<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Marcos Vilela</title>
    <description>The latest articles on DEV Community by Marcos Vilela (@marcos_vile).</description>
    <link>https://dev.to/marcos_vile</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F2315917%2F2b4dcd53-2153-4239-b273-5c867b7d1e25.png</url>
      <title>DEV Community: Marcos Vilela</title>
      <link>https://dev.to/marcos_vile</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/marcos_vile"/>
    <language>en</language>
    <item>
      <title>Shared Workflows: minha experiência definindo pipelines reutilizáveis</title>
      <dc:creator>Marcos Vilela</dc:creator>
      <pubDate>Tue, 03 Mar 2026 19:53:52 +0000</pubDate>
      <link>https://dev.to/marcos_vile/shared-workflows-minha-experiencia-definindo-pipelines-reutilizaveis-74b</link>
      <guid>https://dev.to/marcos_vile/shared-workflows-minha-experiencia-definindo-pipelines-reutilizaveis-74b</guid>
      <description>&lt;p&gt;Nos últimos meses trabalhei na definição de um modelo padrão de shared workflows para projetos de backend Node.js e recursos de infraestruturas implantados na AWS. Neste artigo descrevo a motivação, decisões de arquitetura, trechos de código e aprendizados que considero úteis para quem quer adotar um modelo similar.&lt;/p&gt;

&lt;h2&gt;
  
  
  Problema e motivação
&lt;/h2&gt;

&lt;p&gt;Em organizações com múltiplos times e repositórios, cada projeto costuma inventar seu pipeline (lint, test, build, deploy). Isso gera retrabalho, inconsistências de segurança e custo de manutenção alto. Minha meta foi criar um conjunto de workflows reutilizáveis — fáceis de configurar por repositórios consumidores — que reduzissem duplicação e padronizassem boas práticas.&lt;/p&gt;

&lt;h2&gt;
  
  
  Abordagem
&lt;/h2&gt;

&lt;p&gt;Optei por usar reusable workflows do GitHub Actions (&lt;code&gt;on: workflow_call&lt;/code&gt;) e separar claramente responsabilidades, exemplo:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;CI&lt;/strong&gt;: lint, install, cache, unit/integration tests, security scan (yarn audit).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Infra CI&lt;/strong&gt;: Terraform fmt/init/plan (shared-terraform-ci).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;CD&lt;/strong&gt;: build/push para ECR, deploy blue/green em ECS (shared-backend-deploy-ecs).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Release&lt;/strong&gt;: criação automática de branch de release e PR para &lt;code&gt;main&lt;/code&gt; (shared-release-workflow).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Essa separação facilita reaproveitamento: um repositório pode chamar apenas o CI shared, ou o deploy shared, ou ambos através de wrappers.&lt;/p&gt;

&lt;h2&gt;
  
  
  Trechos de consumo (exemplos)
&lt;/h2&gt;

&lt;p&gt;Consumo do shared CI num projeto:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;jobs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;ci&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;./.github/workflows/shared-backend-ci.yml&lt;/span&gt;
    &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;working_directory&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;app&lt;/span&gt;
      &lt;span class="na"&gt;node_version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;20'&lt;/span&gt;
      &lt;span class="na"&gt;enable_security_scan&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
    &lt;span class="na"&gt;secrets&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;GITHUB_TOKEN&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.GITHUB_TOKEN }}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Wrapper que executa deploy em &lt;code&gt;staging&lt;/code&gt; e, em seguida, chama o workflow de release se o deploy teve sucesso:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;jobs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;deploy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;./.github/workflows/shared-backend-deploy-ecs.yml&lt;/span&gt;
    &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;staging&lt;/span&gt;
      &lt;span class="na"&gt;tf_backend_bucket&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;my-staging-state&lt;/span&gt;
      &lt;span class="na"&gt;tf_var_file&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;envs/staging/variables.tfvars&lt;/span&gt;
    &lt;span class="na"&gt;secrets&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;GITHUB_TOKEN&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.GITHUB_TOKEN }}&lt;/span&gt;

  &lt;span class="na"&gt;promote&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;needs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;deploy&lt;/span&gt;
    &lt;span class="na"&gt;if&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ needs.deploy.result == 'success' }}&lt;/span&gt;
    &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;./.github/workflows/shared-release.yml&lt;/span&gt;
    &lt;span class="na"&gt;secrets&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;GITHUB_TOKEN&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.GITHUB_TOKEN }}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Decisões arquiteturais chave
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Reusable workflows via &lt;code&gt;workflow_call&lt;/code&gt; favorecem versão e rollback centralizado. Consumidores apontam para uma tag (&lt;code&gt;@v1&lt;/code&gt;) no repositório de shared workflows.&lt;/li&gt;
&lt;li&gt;Segreguei permissões por job (princípio de menor privilégio): jobs de lint/test usam &lt;code&gt;contents: read&lt;/code&gt;; jobs de release/deploy recebem permissões mais elevadas apenas quando estritamente necessário.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Aprendizados e recomendações
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Parametrização é essencial.&lt;/strong&gt; Trabalhei com &lt;code&gt;working_directory&lt;/code&gt;, &lt;code&gt;app_path&lt;/code&gt;, &lt;code&gt;image_tag&lt;/code&gt; e outros inputs para permitir que projetos com monorepos ou estruturas diferentes reutilizem os mesmos workflows.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Teste local e validação estática.&lt;/strong&gt; Ferramentas como actionlint, shellcheck e checkov evitaram regressões e problemas de segurança antes do merge.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Documente uso e exemplos.&lt;/strong&gt; Consumidores adotam shared workflows muito mais rápido quando encontram exemplos práticos e um README conciso com nomes de secrets esperados.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Tenha um piloto.&lt;/strong&gt; Validar o modelo em um projeto piloto permitiu ajustar inputs e detectar diferenças (por exemplo, onde o monorepo precisava de um &lt;code&gt;working_directory&lt;/code&gt; diferente).&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Riscos &amp;amp; mitigação
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Diferenças entre repositórios&lt;/strong&gt;: mitigação com inputs configuráveis e exemplos detalhados.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Permissões excessivas&lt;/strong&gt;: mitigação com revisão de permissões por job e uso de roles para operações AWS.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Dependência central&lt;/strong&gt;: documentar SLA de alterações e versionar via tags semânticas para evitar breaking changes.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Conclusão
&lt;/h2&gt;

&lt;p&gt;Criar um modelo de shared workflows traz retorno rápido em escala: menos esforço duplicado, pipelines padronizados e melhorias de segurança centralizadas. A chave do sucesso foi manter o design simples, parametrizável e bem documentado, além de validar por meio de um piloto real.&lt;/p&gt;

&lt;p&gt;Se você estiver começando, recomendo iniciar pelo shared CI (lint/test) e, em seguida, evoluir para um shared deploy com validações de infraestrutura (Terraform) e deploys controlados.&lt;/p&gt;

</description>
      <category>githubactions</category>
      <category>cicd</category>
      <category>devops</category>
      <category>braziliandevs</category>
    </item>
    <item>
      <title>[Boost]</title>
      <dc:creator>Marcos Vilela</dc:creator>
      <pubDate>Tue, 03 Mar 2026 19:53:00 +0000</pubDate>
      <link>https://dev.to/marcos_vile/-1pp8</link>
      <guid>https://dev.to/marcos_vile/-1pp8</guid>
      <description>&lt;div class="ltag__link"&gt;
  &lt;a href="/marcos_vile" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__pic"&gt;
      &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F2315917%2F2b4dcd53-2153-4239-b273-5c867b7d1e25.png" alt="marcos_vile"&gt;
    &lt;/div&gt;
  &lt;/a&gt;
  &lt;a href="https://dev.to/marcos_vile/reestudando-sua-infraestrutura-324p" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__content"&gt;
      &lt;h2&gt;Reestudando sua infraestrutura&lt;/h2&gt;
      &lt;h3&gt;Marcos Vilela ・ Jan 27&lt;/h3&gt;
      &lt;div class="ltag__link__taglist"&gt;
        &lt;span class="ltag__link__tag"&gt;#security&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#iac&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#vulnerabilityanalysis&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#devops&lt;/span&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/a&gt;
&lt;/div&gt;


</description>
      <category>security</category>
      <category>iac</category>
      <category>vulnerabilityanalysis</category>
      <category>devops</category>
    </item>
    <item>
      <title>Template de testes de API com K6</title>
      <dc:creator>Marcos Vilela</dc:creator>
      <pubDate>Tue, 24 Feb 2026 12:18:31 +0000</pubDate>
      <link>https://dev.to/marcos_vile/template-de-testes-de-api-com-k6-2k3d</link>
      <guid>https://dev.to/marcos_vile/template-de-testes-de-api-com-k6-2k3d</guid>
      <description>&lt;p&gt;Escrevi um template para servir de modelo para escrita de testes de performance para testar APIs REST com k6, integrando Prometheus e Grafana localmente e adicionando utilitários reutilizáveis para autenticação, checks, thresholds e geração de relatórios. A seguir descrevo as decisões que tomei, exemplos de código e como rodar localmente e no CI.&lt;/p&gt;

&lt;h2&gt;
  
  
  Por que escolhi o k6
&lt;/h2&gt;

&lt;p&gt;Escolhi o k6 por ser leve, confiável e por permitir escrever testes em JavaScript com boa legibilidade. Ele integra bem com observability (Prometheus/Grafana) e funciona tanto via CLI quanto em container — ideal para rodar localmente ou em pipelines de CI.&lt;/p&gt;

&lt;h2&gt;
  
  
  Estrutura do projeto
&lt;/h2&gt;

&lt;p&gt;Organizei o repositório com foco em reutilização e clareza:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;tests/&lt;/code&gt; — scripts organizados por tipo (smoke, load, stress, soak).&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;lib/&lt;/code&gt; — utilitários e helpers:

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;lib/common/&lt;/code&gt; — &lt;code&gt;auth.js&lt;/code&gt;, &lt;code&gt;checks.js&lt;/code&gt;, &lt;code&gt;generators.js&lt;/code&gt;, &lt;code&gt;thresholds.js&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;lib/config/loader.js&lt;/code&gt; — carregador de configurações (.env, env vars e arquivos JSON)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;lib/utils/&lt;/code&gt; — &lt;code&gt;logger.js&lt;/code&gt;, &lt;code&gt;reporter.js&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;
&lt;code&gt;docker-compose.yml&lt;/code&gt; — stack local com &lt;code&gt;k6&lt;/code&gt;, &lt;code&gt;prometheus&lt;/code&gt; e &lt;code&gt;grafana&lt;/code&gt;
&lt;/li&gt;

&lt;li&gt;
&lt;code&gt;.env.example&lt;/code&gt; — exemplo mínimo de variáveis locais&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;Essa divisão mantém os testes pequenos e delega lógica compartilhada para &lt;code&gt;lib/&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Configuração e precedência de variáveis
&lt;/h2&gt;

&lt;p&gt;Implementei uma lógica simples de carregamento de configuração em &lt;code&gt;lib/config/loader.js&lt;/code&gt;:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Variáveis passadas via CLI/CI (&lt;code&gt;__ENV&lt;/code&gt;) têm prioridade;&lt;/li&gt;
&lt;li&gt;Em seguida, eu leio o &lt;code&gt;.env&lt;/code&gt; local;&lt;/li&gt;
&lt;li&gt;Por fim, valores padrões vindos de &lt;code&gt;config/staging.json&lt;/code&gt; ou &lt;code&gt;config/production.json&lt;/code&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Trecho simplificado:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// lib/config/loader.js (trecho)&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;currentEnv&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;__ENV&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ENVIRONMENT&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;dotEnv&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ENVIRONMENT&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;staging&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;configFile&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`../../config/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;currentEnv&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;.json`&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;config&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;assign&lt;/span&gt;&lt;span class="p"&gt;({},&lt;/span&gt; &lt;span class="nx"&gt;configFile&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;BASE_URL&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;__ENV&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;BASE_URL&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;dotEnv&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;BASE_URL&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;configFile&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;BASE_URL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;USERNAME&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;__ENV&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;USERNAME&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;dotEnv&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;USERNAME&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;configFile&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;USERNAME&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;PASSWORD&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;__ENV&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;PASSWORD&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;dotEnv&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;PASSWORD&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;configFile&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;PASSWORD&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;AUTH_TOKEN&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;__ENV&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;AUTH_TOKEN&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;dotEnv&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;AUTH_TOKEN&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;configFile&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;AUTH_TOKEN&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;Nota: &lt;code&gt;__ENV&lt;/code&gt; é a forma padrão do k6 para acessar variáveis de ambiente passadas na execução.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Autenticação reutilizável
&lt;/h2&gt;

&lt;p&gt;Para endpoints protegidos criei &lt;code&gt;lib/common/auth.js&lt;/code&gt;. A função &lt;code&gt;authenticate()&lt;/code&gt; tenta:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Retornar &lt;code&gt;AUTH_TOKEN&lt;/code&gt; se presente (útil para debug);&lt;/li&gt;
&lt;li&gt;Caso contrário, fazer login com &lt;code&gt;USERNAME&lt;/code&gt;/&lt;code&gt;PASSWORD&lt;/code&gt; e retornar o token.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Uso típico em um teste:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;authenticate&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;../../lib/common/auth.js&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;setup&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;token&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;authenticate&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;token&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A função valida a resposta (checks) e chama &lt;code&gt;fail()&lt;/code&gt; em caso de erro para interromper o teste quando a autenticação falha.&lt;/p&gt;

&lt;h2&gt;
  
  
  Checks e thresholds
&lt;/h2&gt;

&lt;p&gt;Criei helpers para checks (&lt;code&gt;lib/common/checks.js&lt;/code&gt;) e thresholds (&lt;code&gt;lib/common/thresholds.js&lt;/code&gt;) para padronizar asserts e SLOs:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Exemplo de test (smoke)&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;http&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;k6/http&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;config&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;../../lib/config/loader.js&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;standardChecks&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;../../lib/common/checks.js&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;options&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;vus&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;duration&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;10s&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;thresholds&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;http_req_failed&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;rate&amp;lt;0.01&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="na"&gt;http_req_duration&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;p(95)&amp;lt;500&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nf"&gt;function &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;http&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;BASE_URL&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/health`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;standardChecks&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;is200&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Os thresholds possibilitam que o job do CI falhe automaticamente quando os SLOs não são atendidos.&lt;/p&gt;

&lt;h2&gt;
  
  
  Relatórios
&lt;/h2&gt;

&lt;p&gt;Integrei o &lt;code&gt;k6-reporter&lt;/code&gt; para gerar &lt;code&gt;summary.html&lt;/code&gt; e &lt;code&gt;summary.json&lt;/code&gt;. No &lt;code&gt;lib/utils/reporter.js&lt;/code&gt; exporto &lt;code&gt;handleSummary&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;htmlReport&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://raw.githubusercontent.com/benc-uk/k6-reporter/main/dist/bundle.js&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;handleSummary&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;summary.html&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;htmlReport&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;summary.json&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;E nos testes eu apenas faço:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;handleSummary&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;../../lib/utils/reporter.js&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;handleSummary&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Observability: Prometheus + Grafana
&lt;/h2&gt;

&lt;p&gt;Para observability local usei &lt;code&gt;docker-compose.yml&lt;/code&gt; com k6, Prometheus e Grafana. Configurei o k6 para enviar métricas via remote write para o Prometheus, e ativei &lt;code&gt;remote-write-receiver&lt;/code&gt; no contêiner do Prometheus.&lt;/p&gt;

&lt;p&gt;Execução local recomendada:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker-compose up &lt;span class="nt"&gt;-d&lt;/span&gt;
&lt;span class="c"&gt;# rodar um teste (container k6 já monta o repositório):&lt;/span&gt;
docker-compose run &lt;span class="nt"&gt;--rm&lt;/span&gt; k6 run tests/smoke/01-health-check.js
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Após subir a stack você pode abrir Grafana (&lt;code&gt;http://localhost:3000&lt;/code&gt;) e Prometheus (&lt;code&gt;http://localhost:9090&lt;/code&gt;).&lt;/p&gt;

&lt;h2&gt;
  
  
  Integração com CI (GitHub Actions)
&lt;/h2&gt;

&lt;p&gt;Usei a &lt;code&gt;grafana/k6-action&lt;/code&gt; para executar scripts no CI. Exemplo de step:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Run K6 Smoke Test&lt;/span&gt;
  &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;grafana/k6-action@v0.2.0&lt;/span&gt;
  &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;filename&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;tests/smoke/01-health-check.js&lt;/span&gt;
  &lt;span class="na"&gt;env&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;BASE_URL&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.BASE_URL }}&lt;/span&gt;
    &lt;span class="na"&gt;ENVIRONMENT&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;staging&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Recomendo rodar &lt;code&gt;smoke&lt;/code&gt; em PRs e &lt;code&gt;load/stress&lt;/code&gt; em jobs separados ou agendados para evitar impacto em ambientes de produção.&lt;/p&gt;

&lt;h2&gt;
  
  
  Boas práticas que segui
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Não commitar segredos (&lt;code&gt;.env&lt;/code&gt; está no &lt;code&gt;.gitignore&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;Reutilizar helpers em &lt;code&gt;lib/&lt;/code&gt; para diminuir duplicação.&lt;/li&gt;
&lt;li&gt;Definir thresholds para transformar métricas em critérios de sucesso/falha.&lt;/li&gt;
&lt;li&gt;Manter testes idempotentes para que possam ser executados várias vezes sem efeitos colaterais.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Problemas que enfrentei e como resolvi
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Leitura de &lt;code&gt;.env&lt;/code&gt;: implementei parsing que aceita aspas e ignora comentários.&lt;/li&gt;
&lt;li&gt;Autenticação: incluí suporte a &lt;code&gt;AUTH_TOKEN&lt;/code&gt; estático para facilitar debug sem chamadas de login.&lt;/li&gt;
&lt;li&gt;Observability: configurei Prometheus para aceitar remote write do k6 no ambiente local.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Exemplo mínimo pronto para copiar
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// tests/smoke/01-health-check.js&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;http&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;k6/http&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;config&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;../../lib/config/loader.js&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;standardChecks&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;../../lib/common/checks.js&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;options&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;vus&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;duration&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;10s&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nf"&gt;function &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;http&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;BASE_URL&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/health`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;standardChecks&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;is200&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Conclusão
&lt;/h2&gt;

&lt;p&gt;Criar este template me ajudou a padronizar a forma como escrevemos e executamos testes de carga: fica mais fácil rodar localmente com observability, sendo assim para cada projeto, inicio um novo repositório a partir desse template, posso utilizar os testes padrões para validação e escrever novos testes logicamente, tendo assim mais flexibilidade, robustez, escala e padronização, além de poder, integrar/utilizar o K6 nas pipelines de CI/CD.&lt;/p&gt;

</description>
      <category>k6</category>
      <category>performance</category>
      <category>platformengineer</category>
      <category>devops</category>
    </item>
    <item>
      <title>Como integrei testes de carga com K6, GitHub Actions e OpenTelemetry</title>
      <dc:creator>Marcos Vilela</dc:creator>
      <pubDate>Tue, 10 Feb 2026 11:24:58 +0000</pubDate>
      <link>https://dev.to/marcos_vile/como-integrei-testes-de-carga-com-k6-github-actions-e-opentelemetry-23eh</link>
      <guid>https://dev.to/marcos_vile/como-integrei-testes-de-carga-com-k6-github-actions-e-opentelemetry-23eh</guid>
      <description>&lt;p&gt;Neste artigo, compartilho minha experiência construindo uma infraestrutura de testes de carga gerida de forma eficiente com GitHub Actions e observável via integração com OpenTelemetry (OTel). O objetivo era garantir que o desempenho da nossa API pudesse ser monitorado facilmente dentro dos pipelines de CI/CD.&lt;/p&gt;

&lt;h2&gt;
  
  
  O Problema
&lt;/h2&gt;

&lt;p&gt;Precisávamos executar testes de desempenho regularmente contra nossos ambientes de staging e produção sem intervenção manual. Além disso, as métricas geradas por esses testes precisavam ficar visíveis nos nossos dashboards centralizados do Grafana, que usam o Amazon Managed Prometheus (AMP) alimentado por um OpenTelemetry Collector.&lt;/p&gt;

&lt;h2&gt;
  
  
  Por que o K6?
&lt;/h2&gt;

&lt;p&gt;Escolhi o K6 porque permite escrever testes em JavaScript, tornando-os acessíveis para toda a equipe. É leve, compatível com containers e tem suporte nativo para exportar métricas para OpenTelemetry.&lt;/p&gt;

&lt;h2&gt;
  
  
  Estrutura do caso de uso
&lt;/h2&gt;

&lt;p&gt;Estruturei o repositório para lidar com diferentes tipos de testes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Smoke Tests&lt;/strong&gt;: Verificações rápidas de saúde executadas em pull requests.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Load Tests&lt;/strong&gt;: Baselines de desempenho padrão executadas em agendamentos.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Stress &amp;amp; Soak Tests&lt;/strong&gt;: Testes mais agressivos para identificar pontos de ruptura.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Aqui está um exemplo simplificado de como estruturei um teste básico usando checks e thresholds:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;http&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;k6/http&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;check&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;k6&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;options&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;thresholds&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;http_req_duration&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;p(95)&amp;lt;2000&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="c1"&gt;// 95% das requisições devem estar abaixo de 2s&lt;/span&gt;
        &lt;span class="na"&gt;http_req_failed&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;rate&amp;lt;0.01&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;     &lt;span class="c1"&gt;// Menos de 1% de falha&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nf"&gt;function &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;http&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://api.staging.example.com/health&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="nf"&gt;check&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;status é 200&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Passo 1: Implementação no GitHub Actions
&lt;/h2&gt;

&lt;p&gt;O núcleo da automação fica no GitHub Actions. Criei um workflow que permite execução agendada (cron) e acionamento manual (&lt;code&gt;workflow_dispatch&lt;/code&gt;) para ambientes específicos.&lt;/p&gt;

&lt;p&gt;Um desafio específico foi garantir que o binário do K6 estivesse corretamente instalado e disponível no PATH. Usar a action oficial &lt;code&gt;grafana/setup-k6-action&lt;/code&gt; foi a solução mais confiável.&lt;/p&gt;

&lt;p&gt;Aqui está a configuração essencial do arquivo de workflow &lt;code&gt;.github/workflows/load-tests.yml&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;K6 Load Tests&lt;/span&gt;

&lt;span class="na"&gt;on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;schedule&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;cron&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;0&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;0&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;*&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;*&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;5'&lt;/span&gt; &lt;span class="c1"&gt;# Toda sexta-feira à meia-noite&lt;/span&gt;
    &lt;span class="na"&gt;workflow_dispatch&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;inputs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
                &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;choice&lt;/span&gt;
                &lt;span class="na"&gt;options&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; 
                    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;staging&lt;/span&gt;
                    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;production&lt;/span&gt;
            &lt;span class="na"&gt;test_type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
                &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;choice&lt;/span&gt;
                &lt;span class="na"&gt;options&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; 
                    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;smoke&lt;/span&gt;
                    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;load&lt;/span&gt;

&lt;span class="na"&gt;jobs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;run-tests&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;
        &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@v4&lt;/span&gt;

            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Setup k6&lt;/span&gt;
                &lt;span class="s"&gt;uses&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="s"&gt;grafana/setup-k6-action@v1&lt;/span&gt;

            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Run K6 Test&lt;/span&gt;
                &lt;span class="s"&gt;run&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="s"&gt;k6 run --out opentelemetry tests/${{ inputs.test_type }}/test.js&lt;/span&gt;
                &lt;span class="s"&gt;env&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;
                    &lt;span class="na"&gt;K6_OTEL_EXPORTER_TYPE&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;http&lt;/span&gt;
                    &lt;span class="na"&gt;K6_OTEL_HTTP_EXPORTER_ENDPOINT&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ vars.OTEL_COLLECTOR_ENDPOINT }}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Passo 2: Integração com OpenTelemetry
&lt;/h2&gt;

&lt;p&gt;Esta foi a parte mais crítica da configuração. Integrar o K6 com nosso OpenTelemetry Collector exigiu configurações específicas para garantir que as métricas viajassem do runner do GitHub Action para nosso collector e, finalmente, para o Prometheus.&lt;/p&gt;

&lt;p&gt;O suporte nativo do K6 ao OTel é excelente, mas nuances de configuração importam.&lt;/p&gt;

&lt;h3&gt;
  
  
  Configuração do protocolo
&lt;/h3&gt;

&lt;p&gt;Precisei definir explicitamente o tipo do exporter para &lt;code&gt;http&lt;/code&gt; porque o collector escuta na porta 4318 para payloads HTTP/OTLP. Por padrão, algumas ferramentas assumem gRPC (porta 4317).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;env&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;K6_OTEL_EXPORTER_TYPE&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;http&lt;/span&gt;
    &lt;span class="na"&gt;K6_OTEL_HTTP_EXPORTER_ENDPOINT&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ vars.OTEL_COLLECTOR_ENDPOINT }}&lt;/span&gt;
    &lt;span class="na"&gt;K6_OTEL_EXPORTER_INSECURE&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A variável de endpoint geralmente é algo como &lt;code&gt;seu-domínio.com:4318&lt;/code&gt;. O K6 adiciona o protocolo/caminho correto se configurado apropriadamente, mas aprendi que &lt;code&gt;K6_OTEL_HTTP_EXPORTER_ENDPOINT&lt;/code&gt; é a variável específica quando o tipo é &lt;code&gt;http&lt;/code&gt;, enquanto &lt;code&gt;K6_OTEL_EXPORTER_ENDPOINT&lt;/code&gt; costuma ser usada para gRPC ou defaults genéricos.&lt;/p&gt;

&lt;h3&gt;
  
  
  Atributos de recurso e filtragem
&lt;/h3&gt;

&lt;p&gt;Em um ambiente de observabilidade compartilhado, taguear suas métricas é obrigatório. Nosso collector descarta qualquer métrica que não contenha um &lt;code&gt;client_id&lt;/code&gt; específico. Resolvi isso injetando &lt;code&gt;K6_OTEL_RESOURCE_ATTRIBUTES&lt;/code&gt; diretamente no pipeline:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;K6_OTEL_RESOURCE_ATTRIBUTES&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;client_id=test3,environment=${{ matrix.environment }},service_name=k6-load-tests&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Isso garante que, ao consultar &lt;code&gt;k6_http_req_duration&lt;/code&gt; no Grafana, eu possa filtrar exatamente pela execução do teste e ambiente.&lt;/p&gt;

&lt;h2&gt;
  
  
  Passo 3: Simulação local com Act
&lt;/h2&gt;

&lt;p&gt;Testar workflows do GitHub Actions cometendo e dando push com mensagens "fix ci" é tedioso. Integrei o &lt;code&gt;act&lt;/code&gt; para simular o ambiente do GitHub Actions localmente.&lt;/p&gt;

&lt;p&gt;Criando um arquivo simples de payload de evento &lt;code&gt;dispatch-event.json&lt;/code&gt;, pude verificar o comportamento exato da injeção de variáveis de ambiente e da execução do comando K6 sem sair do terminal:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;act workflow_dispatch &lt;span class="nt"&gt;-e&lt;/span&gt; .github/dispatch-event.json &lt;span class="nt"&gt;-W&lt;/span&gt; .github/workflows/load-tests.yml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Isso economizou horas de depuração, especialmente ao ajustar as variáveis de ambiente do OTel.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusão
&lt;/h2&gt;

&lt;p&gt;Ao combinar K6, GitHub Actions e OpenTelemetry, implementei um sistema onde o desempenho é monitorado como a saúde da aplicação.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Automação:&lt;/strong&gt; Não há mais execução manual dos testes de carga.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Visibilidade:&lt;/strong&gt; Métricas estão no mesmo dashboard que as métricas da aplicação.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Depuração:&lt;/strong&gt; Testes locais com &lt;code&gt;act&lt;/code&gt; garantem que o pipeline permaneça estável.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Se for montar testes de carga, recomendo priorizar o aspecto de observabilidade desde cedo. Ver gráficos em tempo real de Virtual Users (VUs) e Duração de Requisições ao lado do uso de CPU do servidor oferece insights que um relatório local isolado nunca proporciona.&lt;/p&gt;

</description>
      <category>k6</category>
      <category>githubactions</category>
      <category>prometheus</category>
      <category>opentelemetry</category>
    </item>
    <item>
      <title>Economia não é sorte, é automação. Veja como monitorei custos na AWS com Terraform.</title>
      <dc:creator>Marcos Vilela</dc:creator>
      <pubDate>Tue, 03 Feb 2026 12:09:13 +0000</pubDate>
      <link>https://dev.to/marcos_vile/economia-nao-e-sorte-e-automacao-veja-como-monitorei-custos-na-aws-com-terraform-39fe</link>
      <guid>https://dev.to/marcos_vile/economia-nao-e-sorte-e-automacao-veja-como-monitorei-custos-na-aws-com-terraform-39fe</guid>
      <description>&lt;div class="ltag__link"&gt;
  &lt;a href="/marcos_vile" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__pic"&gt;
      &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F2315917%2F2b4dcd53-2153-4239-b273-5c867b7d1e25.png" alt="marcos_vile"&gt;
    &lt;/div&gt;
  &lt;/a&gt;
  &lt;a href="https://dev.to/marcos_vile/automatizando-finops-na-aws-como-construimos-um-modulo-de-monitoramento-de-custos-com-terraform-e-3da3" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__content"&gt;
      &lt;h2&gt;Automatizando FinOps na AWS: Como construímos um módulo de Monitoramento de Custos com Terraform e incident.io&lt;/h2&gt;
      &lt;h3&gt;Marcos Vilela ・ Feb 3&lt;/h3&gt;
      &lt;div class="ltag__link__taglist"&gt;
        &lt;span class="ltag__link__tag"&gt;#finops&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#aws&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#terraform&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#iac&lt;/span&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/a&gt;
&lt;/div&gt;


</description>
      <category>finops</category>
      <category>aws</category>
      <category>terraform</category>
      <category>iac</category>
    </item>
    <item>
      <title>A /home encheu? Como usei Block Storage para expandir o armazenamento sem dor.</title>
      <dc:creator>Marcos Vilela</dc:creator>
      <pubDate>Tue, 03 Feb 2026 12:07:59 +0000</pubDate>
      <link>https://dev.to/marcos_vile/a-home-encheu-como-usei-block-storage-para-expandir-o-armazenamento-sem-dor-55c1</link>
      <guid>https://dev.to/marcos_vile/a-home-encheu-como-usei-block-storage-para-expandir-o-armazenamento-sem-dor-55c1</guid>
      <description>&lt;p&gt;

&lt;/p&gt;
&lt;div class="ltag__link--embedded"&gt;
  &lt;div class="crayons-story "&gt;
  &lt;a href="https://dev.to/marcos_vile/como-expandi-o-armazenamento-da-minha-pasta-home-com-block-storage-4cjc" class="crayons-story__hidden-navigation-link"&gt;Como expandi o armazenamento da minha pasta /home com Block Storage&lt;/a&gt;


  &lt;div class="crayons-story__body crayons-story__body-full_post"&gt;
    &lt;div class="crayons-story__top"&gt;
      &lt;div class="crayons-story__meta"&gt;
        &lt;div class="crayons-story__author-pic"&gt;

          &lt;a href="/marcos_vile" class="crayons-avatar  crayons-avatar--l  "&gt;
            &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F2315917%2F2b4dcd53-2153-4239-b273-5c867b7d1e25.png" alt="marcos_vile profile" class="crayons-avatar__image"&gt;
          &lt;/a&gt;
        &lt;/div&gt;
        &lt;div&gt;
          &lt;div&gt;
            &lt;a href="/marcos_vile" class="crayons-story__secondary fw-medium m:hidden"&gt;
              Marcos Vilela
            &lt;/a&gt;
            &lt;div class="profile-preview-card relative mb-4 s:mb-0 fw-medium hidden m:inline-block"&gt;
              
                Marcos Vilela
                
              
              &lt;div id="story-author-preview-content-2070920" class="profile-preview-card__content crayons-dropdown branded-7 p-4 pt-0"&gt;
                &lt;div class="gap-4 grid"&gt;
                  &lt;div class="-mt-4"&gt;
                    &lt;a href="/marcos_vile" class="flex"&gt;
                      &lt;span class="crayons-avatar crayons-avatar--xl mr-2 shrink-0"&gt;
                        &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F2315917%2F2b4dcd53-2153-4239-b273-5c867b7d1e25.png" class="crayons-avatar__image" alt=""&gt;
                      &lt;/span&gt;
                      &lt;span class="crayons-link crayons-subtitle-2 mt-5"&gt;Marcos Vilela&lt;/span&gt;
                    &lt;/a&gt;
                  &lt;/div&gt;
                  &lt;div class="print-hidden"&gt;
                    
                      Follow
                    
                  &lt;/div&gt;
                  &lt;div class="author-preview-metadata-container"&gt;&lt;/div&gt;
                &lt;/div&gt;
              &lt;/div&gt;
            &lt;/div&gt;

          &lt;/div&gt;
          &lt;a href="https://dev.to/marcos_vile/como-expandi-o-armazenamento-da-minha-pasta-home-com-block-storage-4cjc" class="crayons-story__tertiary fs-xs"&gt;&lt;time&gt;Nov 6 '24&lt;/time&gt;&lt;span class="time-ago-indicator-initial-placeholder"&gt;&lt;/span&gt;&lt;/a&gt;
        &lt;/div&gt;
      &lt;/div&gt;

    &lt;/div&gt;

    &lt;div class="crayons-story__indention"&gt;
      &lt;h2 class="crayons-story__title crayons-story__title-full_post"&gt;
        &lt;a href="https://dev.to/marcos_vile/como-expandi-o-armazenamento-da-minha-pasta-home-com-block-storage-4cjc" id="article-link-2070920"&gt;
          Como expandi o armazenamento da minha pasta /home com Block Storage
        &lt;/a&gt;
      &lt;/h2&gt;
        &lt;div class="crayons-story__tags"&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/beginners"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;beginners&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/linux"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;linux&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/blockstorage"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;blockstorage&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/braziliandevs"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;braziliandevs&lt;/a&gt;
        &lt;/div&gt;
      &lt;div class="crayons-story__bottom"&gt;
        &lt;div class="crayons-story__details"&gt;
          &lt;a href="https://dev.to/marcos_vile/como-expandi-o-armazenamento-da-minha-pasta-home-com-block-storage-4cjc" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left"&gt;
            &lt;div class="multiple_reactions_aggregate"&gt;
              &lt;span class="multiple_reactions_icons_container"&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/sparkle-heart-5f9bee3767e18deb1bb725290cb151c25234768a0e9a2bd39370c382d02920cf.svg" width="18" height="18"&gt;
                  &lt;/span&gt;
              &lt;/span&gt;
              &lt;span class="aggregate_reactions_counter"&gt;2&lt;span class="hidden s:inline"&gt; reactions&lt;/span&gt;&lt;/span&gt;
            &lt;/div&gt;
          &lt;/a&gt;
            &lt;a href="https://dev.to/marcos_vile/como-expandi-o-armazenamento-da-minha-pasta-home-com-block-storage-4cjc#comments" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left flex items-center"&gt;
              Comments


              &lt;span class="hidden s:inline"&gt;Add Comment&lt;/span&gt;
            &lt;/a&gt;
        &lt;/div&gt;
        &lt;div class="crayons-story__save"&gt;
          &lt;small class="crayons-story__tertiary fs-xs mr-2"&gt;
            4 min read
          &lt;/small&gt;
            
              &lt;span class="bm-initial"&gt;
                

              &lt;/span&gt;
              &lt;span class="bm-success"&gt;
                

              &lt;/span&gt;
            
        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;

&lt;/div&gt;




</description>
      <category>beginners</category>
      <category>linux</category>
      <category>blockstorage</category>
      <category>braziliandevs</category>
    </item>
    <item>
      <title>A /home encheu? Como usei Block Storage para expandir o armazenamento sem dor</title>
      <dc:creator>Marcos Vilela</dc:creator>
      <pubDate>Tue, 03 Feb 2026 12:06:56 +0000</pubDate>
      <link>https://dev.to/marcos_vile/a-home-encheu-como-usei-block-storage-para-expandir-o-armazenamento-sem-dor-1ahk</link>
      <guid>https://dev.to/marcos_vile/a-home-encheu-como-usei-block-storage-para-expandir-o-armazenamento-sem-dor-1ahk</guid>
      <description>&lt;p&gt;

&lt;/p&gt;
&lt;div class="ltag__link--embedded"&gt;
  &lt;div class="crayons-story "&gt;
  &lt;a href="https://dev.to/marcos_vile/como-expandi-o-armazenamento-da-minha-pasta-home-com-block-storage-4cjc" class="crayons-story__hidden-navigation-link"&gt;Como expandi o armazenamento da minha pasta /home com Block Storage&lt;/a&gt;


  &lt;div class="crayons-story__body crayons-story__body-full_post"&gt;
    &lt;div class="crayons-story__top"&gt;
      &lt;div class="crayons-story__meta"&gt;
        &lt;div class="crayons-story__author-pic"&gt;

          &lt;a href="/marcos_vile" class="crayons-avatar  crayons-avatar--l  "&gt;
            &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F2315917%2F2b4dcd53-2153-4239-b273-5c867b7d1e25.png" alt="marcos_vile profile" class="crayons-avatar__image"&gt;
          &lt;/a&gt;
        &lt;/div&gt;
        &lt;div&gt;
          &lt;div&gt;
            &lt;a href="/marcos_vile" class="crayons-story__secondary fw-medium m:hidden"&gt;
              Marcos Vilela
            &lt;/a&gt;
            &lt;div class="profile-preview-card relative mb-4 s:mb-0 fw-medium hidden m:inline-block"&gt;
              
                Marcos Vilela
                
              
              &lt;div id="story-author-preview-content-2070920" class="profile-preview-card__content crayons-dropdown branded-7 p-4 pt-0"&gt;
                &lt;div class="gap-4 grid"&gt;
                  &lt;div class="-mt-4"&gt;
                    &lt;a href="/marcos_vile" class="flex"&gt;
                      &lt;span class="crayons-avatar crayons-avatar--xl mr-2 shrink-0"&gt;
                        &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F2315917%2F2b4dcd53-2153-4239-b273-5c867b7d1e25.png" class="crayons-avatar__image" alt=""&gt;
                      &lt;/span&gt;
                      &lt;span class="crayons-link crayons-subtitle-2 mt-5"&gt;Marcos Vilela&lt;/span&gt;
                    &lt;/a&gt;
                  &lt;/div&gt;
                  &lt;div class="print-hidden"&gt;
                    
                      Follow
                    
                  &lt;/div&gt;
                  &lt;div class="author-preview-metadata-container"&gt;&lt;/div&gt;
                &lt;/div&gt;
              &lt;/div&gt;
            &lt;/div&gt;

          &lt;/div&gt;
          &lt;a href="https://dev.to/marcos_vile/como-expandi-o-armazenamento-da-minha-pasta-home-com-block-storage-4cjc" class="crayons-story__tertiary fs-xs"&gt;&lt;time&gt;Nov 6 '24&lt;/time&gt;&lt;span class="time-ago-indicator-initial-placeholder"&gt;&lt;/span&gt;&lt;/a&gt;
        &lt;/div&gt;
      &lt;/div&gt;

    &lt;/div&gt;

    &lt;div class="crayons-story__indention"&gt;
      &lt;h2 class="crayons-story__title crayons-story__title-full_post"&gt;
        &lt;a href="https://dev.to/marcos_vile/como-expandi-o-armazenamento-da-minha-pasta-home-com-block-storage-4cjc" id="article-link-2070920"&gt;
          Como expandi o armazenamento da minha pasta /home com Block Storage
        &lt;/a&gt;
      &lt;/h2&gt;
        &lt;div class="crayons-story__tags"&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/beginners"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;beginners&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/linux"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;linux&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/blockstorage"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;blockstorage&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/braziliandevs"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;braziliandevs&lt;/a&gt;
        &lt;/div&gt;
      &lt;div class="crayons-story__bottom"&gt;
        &lt;div class="crayons-story__details"&gt;
          &lt;a href="https://dev.to/marcos_vile/como-expandi-o-armazenamento-da-minha-pasta-home-com-block-storage-4cjc" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left"&gt;
            &lt;div class="multiple_reactions_aggregate"&gt;
              &lt;span class="multiple_reactions_icons_container"&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/sparkle-heart-5f9bee3767e18deb1bb725290cb151c25234768a0e9a2bd39370c382d02920cf.svg" width="18" height="18"&gt;
                  &lt;/span&gt;
              &lt;/span&gt;
              &lt;span class="aggregate_reactions_counter"&gt;2&lt;span class="hidden s:inline"&gt; reactions&lt;/span&gt;&lt;/span&gt;
            &lt;/div&gt;
          &lt;/a&gt;
            &lt;a href="https://dev.to/marcos_vile/como-expandi-o-armazenamento-da-minha-pasta-home-com-block-storage-4cjc#comments" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left flex items-center"&gt;
              Comments


              &lt;span class="hidden s:inline"&gt;Add Comment&lt;/span&gt;
            &lt;/a&gt;
        &lt;/div&gt;
        &lt;div class="crayons-story__save"&gt;
          &lt;small class="crayons-story__tertiary fs-xs mr-2"&gt;
            4 min read
          &lt;/small&gt;
            
              &lt;span class="bm-initial"&gt;
                

              &lt;/span&gt;
              &lt;span class="bm-success"&gt;
                

              &lt;/span&gt;
            
        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;

&lt;/div&gt;




</description>
      <category>beginners</category>
      <category>linux</category>
      <category>blockstorage</category>
      <category>braziliandevs</category>
    </item>
    <item>
      <title>Automatizando FinOps na AWS: Como construímos um módulo de Monitoramento de Custos com Terraform e incident.io</title>
      <dc:creator>Marcos Vilela</dc:creator>
      <pubDate>Tue, 03 Feb 2026 12:03:52 +0000</pubDate>
      <link>https://dev.to/marcos_vile/automatizando-finops-na-aws-como-construimos-um-modulo-de-monitoramento-de-custos-com-terraform-e-3da3</link>
      <guid>https://dev.to/marcos_vile/automatizando-finops-na-aws-como-construimos-um-modulo-de-monitoramento-de-custos-com-terraform-e-3da3</guid>
      <description>&lt;p&gt;Gerenciar custos em nuvem e um desafio constante para equipes de engenharia. A disciplina de FinOps busca trazer visibilidade e accountability para esses gastos, mas sem as ferramentas certas, e fácil ser surpreendido pela fatura no final do mês.&lt;/p&gt;

&lt;p&gt;Neste artigo, compartilho a experiência e os detalhes técnicos da criação de um módulo Terraform reutilizável para automatizar alertas de orçamento na AWS, integrando diretamente com ferramentas de resposta a incidentes (neste caso, o incident.io), eliminando intermediários desnecessários como funções Lambda para esse fim específico.&lt;/p&gt;

&lt;h2&gt;
  
  
  O Problema
&lt;/h2&gt;

&lt;p&gt;Precisávamos de uma maneira padronizada de criar "guardrails" financeiros para novos projetos. Toda nova conta ou ambiente (staging, production) na AWS deveria nascer com um orçamento definido e um canal de alerta configurado.&lt;/p&gt;

&lt;p&gt;Os requisitos eram:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Definir um limite mensal em dólares.&lt;/li&gt;
&lt;li&gt;Alertar quando o gasto real atingir certas porcentagens (ex: 80%, 100%).&lt;/li&gt;
&lt;li&gt;Alertar quando a previsão (forecast) indicar que o orçamento será estourado.&lt;/li&gt;
&lt;li&gt;Enviar esses alertas para nossa plataforma de gerenciamento de incidentes.&lt;/li&gt;
&lt;li&gt;Ser seguro (dados criptografados em repouso).&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  A Arquitetura Simplificada
&lt;/h2&gt;

&lt;p&gt;Inicialmente, considerou-se usar AWS Lambda para processar os alertas do AWS Budgets e formatar o JSON para o webhook. No entanto, percebemos que poderíamos simplificar a arquitetura utilizando a capacidade nativa do SNS de realizar chamadas HTTPS (Webhooks).&lt;/p&gt;

&lt;p&gt;O fluxo ficou assim:&lt;br&gt;
&lt;code&gt;AWS Budgets&lt;/code&gt; -&amp;gt; &lt;code&gt;SNS Topic (Criptografado)&lt;/code&gt; -&amp;gt; &lt;code&gt;HTTP POST&lt;/code&gt; -&amp;gt; &lt;code&gt;Incident.io&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Essa abordagem reduz a complexidade operacional (menos código para manter) e o custo.&lt;/p&gt;
&lt;h2&gt;
  
  
  A Implementação com Terraform
&lt;/h2&gt;

&lt;p&gt;Abaixo, detalho as partes cruciais do módulo, focando em como resolvemos os desafios de segurança e flexibilidade.&lt;/p&gt;
&lt;h3&gt;
  
  
  1. Segurança Primeiro: Criptografia KMS
&lt;/h3&gt;

&lt;p&gt;O AWS SNS suporta criptografia de dados (Server-Side Encryption). Para ambientes corporativos, isso não é opcional. No entanto, usar uma chave KMS gerenciada pelo cliente (CMK) requer politicas de acesso especificas para permitir que o serviço de AWS Budgets (que é um serviço global) chame um recurso regional e use a chave para criptografar a mensagem antes de enviá-la ao tópico.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="nx"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_kms_key"&lt;/span&gt; &lt;span class="s2"&gt;"cost_alerts_key"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;description&lt;/span&gt;             &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"KMS key for encrypting FinOps SNS topics"&lt;/span&gt;
  &lt;span class="nx"&gt;deletion_window_in_days&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;
  &lt;span class="nx"&gt;enable_key_rotation&lt;/span&gt;     &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;

  &lt;span class="nx"&gt;policy&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;jsonencode&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="nx"&gt;Version&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"2012-10-17"&lt;/span&gt;
    &lt;span class="nx"&gt;Statement&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
      &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;Sid&lt;/span&gt;    &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Enable IAM User Permissions"&lt;/span&gt;
        &lt;span class="nx"&gt;Effect&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Allow"&lt;/span&gt;
        &lt;span class="nx"&gt;Principal&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="nx"&gt;AWS&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"arn:aws:iam::${local.account_id}:root"&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="nx"&gt;Action&lt;/span&gt;   &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"kms:*"&lt;/span&gt;
        &lt;span class="nx"&gt;Resource&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"*"&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;Sid&lt;/span&gt;    &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Allow AWS Budgets to use the key"&lt;/span&gt;
        &lt;span class="nx"&gt;Effect&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Allow"&lt;/span&gt;
        &lt;span class="nx"&gt;Principal&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="nx"&gt;Service&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"budgets.amazonaws.com"&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="nx"&gt;Action&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
          &lt;span class="s2"&gt;"kms:GenerateDataKey*"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="s2"&gt;"kms:Decrypt"&lt;/span&gt;
        &lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="nx"&gt;Resource&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"*"&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Observe a permissão explicita para &lt;code&gt;budgets.amazonaws.com&lt;/code&gt;. Sem isso, o orçamento falha silenciosamente ao tentar publicar no tópico.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. O Tópico SNS e a política de Publicação
&lt;/h3&gt;

&lt;p&gt;O tópico SNS atua como o roteador. Além de criar o tópico, precisamos de uma &lt;code&gt;aws_sns_topic_policy&lt;/code&gt; para autorizar o serviço de orçamento a publicar nele.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="nx"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_sns_topic"&lt;/span&gt; &lt;span class="s2"&gt;"cost_alerts"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;name_prefix&lt;/span&gt;       &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"${local.name_prefix}-cost-alerts-"&lt;/span&gt;
  &lt;span class="nx"&gt;kms_master_key_id&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws_kms_key&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cost_alerts_key&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;arn&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_sns_topic_policy"&lt;/span&gt; &lt;span class="s2"&gt;"default"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;arn&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws_sns_topic&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cost_alerts&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;arn&lt;/span&gt;
  &lt;span class="nx"&gt;policy&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;jsonencode&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="nx"&gt;Version&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"2012-10-17"&lt;/span&gt;
    &lt;span class="nx"&gt;Statement&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
      &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;Sid&lt;/span&gt;    &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"AWSBudgets-Publish"&lt;/span&gt;
        &lt;span class="nx"&gt;Effect&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Allow"&lt;/span&gt;
        &lt;span class="nx"&gt;Principal&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="nx"&gt;Service&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"budgets.amazonaws.com"&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="nx"&gt;Action&lt;/span&gt;   &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"SNS:Publish"&lt;/span&gt;
        &lt;span class="nx"&gt;Resource&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws_sns_topic&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cost_alerts&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;arn&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  3. Flexibilidade com Dynamic Blocks
&lt;/h3&gt;

&lt;p&gt;Um dos maiores desafios ao criar módulos reutilizáveis e atender as diferentes necessidades de notificação de cada time. Alguns querem alertas aos 50%, outros apenas aos 100%.&lt;/p&gt;

&lt;p&gt;Utilizamos o recurso &lt;code&gt;dynamic&lt;/code&gt; do Terraform para gerar as configurações de notificação com base em uma lista de objetos fornecida via variável.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="nx"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_budgets_budget"&lt;/span&gt; &lt;span class="s2"&gt;"cost_budget"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;name&lt;/span&gt;              &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"${local.name_prefix}-budget"&lt;/span&gt;
  &lt;span class="nx"&gt;budget_type&lt;/span&gt;       &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"COST"&lt;/span&gt;
  &lt;span class="nx"&gt;limit_amount&lt;/span&gt;      &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;limit_amount&lt;/span&gt;
  &lt;span class="nx"&gt;limit_unit&lt;/span&gt;        &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;limit_unit&lt;/span&gt;
  &lt;span class="nx"&gt;time_period_start&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"2024-01-01_00:00"&lt;/span&gt;
  &lt;span class="nx"&gt;time_unit&lt;/span&gt;         &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;time_unit&lt;/span&gt;

  &lt;span class="c1"&gt;# Bloco dinamico para notificacoes&lt;/span&gt;
  &lt;span class="nx"&gt;dynamic&lt;/span&gt; &lt;span class="s2"&gt;"notification"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;for_each&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;notifications&lt;/span&gt;
    &lt;span class="nx"&gt;content&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;comparison_operator&lt;/span&gt;       &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;notification&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;comparison_operator&lt;/span&gt;
      &lt;span class="nx"&gt;threshold&lt;/span&gt;                 &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;notification&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;threshold&lt;/span&gt;
      &lt;span class="nx"&gt;threshold_type&lt;/span&gt;            &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;notification&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;threshold_type&lt;/span&gt;
      &lt;span class="nx"&gt;notification_type&lt;/span&gt;         &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;notification&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;notification_type&lt;/span&gt;
      &lt;span class="nx"&gt;subscriber_sns_topic_arns&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;aws_sns_topic&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cost_alerts&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;arn&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Isso permite que o consumidor do module configure alertas de forma declarativa e simples no arquivo &lt;code&gt;.tfvars&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="nx"&gt;notifications&lt;/span&gt; &lt;span class="err"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;comparison_operator&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"GREATER_THAN"&lt;/span&gt;
    &lt;span class="nx"&gt;threshold&lt;/span&gt;           &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;80&lt;/span&gt;
    &lt;span class="nx"&gt;threshold_type&lt;/span&gt;      &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"PERCENTAGE"&lt;/span&gt;
    &lt;span class="nx"&gt;notification_type&lt;/span&gt;   &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"ACTUAL"&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;comparison_operator&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"GREATER_THAN"&lt;/span&gt;
    &lt;span class="nx"&gt;threshold&lt;/span&gt;           &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;
    &lt;span class="nx"&gt;threshold_type&lt;/span&gt;      &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"PERCENTAGE"&lt;/span&gt;
    &lt;span class="nx"&gt;notification_type&lt;/span&gt;   &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"FORECASTED"&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  4. Integração via Webhook (A Parte "Chata")
&lt;/h3&gt;

&lt;p&gt;A integração final e feita via uma assinatura SNS com protocolo HTTPS:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="nx"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_sns_topic_subscription"&lt;/span&gt; &lt;span class="s2"&gt;"incident_io"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;topic_arn&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws_sns_topic&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cost_alerts&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;arn&lt;/span&gt;
  &lt;span class="nx"&gt;protocol&lt;/span&gt;  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"https"&lt;/span&gt; &lt;span class="c1"&gt;# Detectado dinamicamente no codigo real&lt;/span&gt;
  &lt;span class="nx"&gt;endpoint&lt;/span&gt;  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;incident_io_webhook_url&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;O Desafio da Confirmação da Assinatura:&lt;/strong&gt;&lt;br&gt;
Diferente de Lambda ou SQS, endpoints HTTP/HTTPS requerem uma confirmação de assinatura. O SNS envia um POST inicial com uma URL de confirmação.&lt;br&gt;
Como estamos usando uma ferramenta de terceiros (incident.io), não temos controle direto para clicar programaticamente nesse link.&lt;/p&gt;

&lt;p&gt;A solução adotada foi processual:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;O Terraform aplica a infraestrutura.&lt;/li&gt;
&lt;li&gt;O estado da assinatura fica como "PendingConfirmation".&lt;/li&gt;
&lt;li&gt;O administrador acessa os logs do incident.io, localiza a mensagem de &lt;code&gt;SubscriptionConfirmation&lt;/code&gt; e clica no link manualmente.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Embora não seja 100% automatizado (zero-touch), e um compromisso aceitável para uma configuração que ocorre apenas uma vez por ambiente.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusão
&lt;/h2&gt;

&lt;p&gt;Criar módulos de infraestrutura como código para FinOps e essencial para escalar o uso da nuvem de forma responsável. Ao centralizar a lógica de orçamentos, criptografia e notificações em um único modulo, garantimos que todos os ambientes seguem as melhores praticas de segurança e observabilidade financeira.&lt;/p&gt;

&lt;p&gt;A escolha de remover camadas intermediarias (como Lambdas) simplificou a stack, tornando-a mais robusta e fácil de manter a longo prazo.&lt;/p&gt;

</description>
      <category>finops</category>
      <category>aws</category>
      <category>terraform</category>
      <category>iac</category>
    </item>
    <item>
      <title>Reestudando sua infraestrutura</title>
      <dc:creator>Marcos Vilela</dc:creator>
      <pubDate>Tue, 27 Jan 2026 17:30:47 +0000</pubDate>
      <link>https://dev.to/marcos_vile/reestudando-sua-infraestrutura-324p</link>
      <guid>https://dev.to/marcos_vile/reestudando-sua-infraestrutura-324p</guid>
      <description>&lt;p&gt;Recentemente, parei para rever/revisar o IaC de infra/redes e sua infraestrutura aplicada, de um ecossistema de uma fintech (que chamaremos aqui de &lt;em&gt;GlobalPay&lt;/em&gt;). O objetivo era avaliar a postura de segurança de seus apps diferentes que compõem o produto principal.&lt;/p&gt;

&lt;p&gt;O que parecia ser uma varredura de rotina revelou uma lição valiosa: &lt;strong&gt;a segurança não é apenas um problema técnico, mas um desafio de governança e automação&lt;/strong&gt; e também, revisão.&lt;/p&gt;

&lt;p&gt;Aqui estão os principais aprendizados para quem trabalha com DevOps, Cloud e Segurança de Aplicações que pude tirar reestudando meu próprio código.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. O perigo dos Templates de IaC Inadequados
&lt;/h2&gt;

&lt;p&gt;Um dos achados mais curiosos foi que &lt;strong&gt;nos apps analisados&lt;/strong&gt; apresentavam exatamente a mesma falha: a fuga de nomes internos de Load Balancers de &lt;em&gt;staging&lt;/em&gt; e regiões da no cabeçalho &lt;code&gt;Location&lt;/code&gt; de redirecionamentos HTTP.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;O aprendizado:&lt;/strong&gt; Quando usamos ferramentas como Terraform ou CloudFormation, um erro no template original é replicado em escala. Adversários usam essas informações para mapear ambientes de teste, que geralmente possuem controles de segurança menos rigorosos.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. A Inconsistência é o seu maior inimigo
&lt;/h2&gt;

&lt;p&gt;Durante a minha análise, encontramos um cenário de "dois mundos". Enquanto um app apresentava uma postura exemplar (o nosso &lt;em&gt;benchmark&lt;/em&gt;), com &lt;strong&gt;HSTS, CSP e X-Frame-Options&lt;/strong&gt; configurados corretamente, os apps adjacentes — teoricamente mais sensíveis — não possuíam poucos desses controles.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A lição:&lt;/strong&gt; A segurança não pode ser seletiva. A falta de uma &lt;strong&gt;baseline mínima obrigatória&lt;/strong&gt; centralizada faz com que cada time de desenvolvimento implemente sua própria versão de "segurança", criando buracos previsíveis no perímetro.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Vulnerabilidades "Clássicas" ainda derrubam gigantes
&lt;/h2&gt;

&lt;p&gt;Identificamos uma vulnerabilidade ao ataque &lt;strong&gt;Slowloris DoS&lt;/strong&gt; (CVE-2007-6750) em um dos endpoints. É uma falha de 2009 que ainda assombra ambientes que não configuram corretamente os &lt;em&gt;timeouts&lt;/em&gt; de conexão em seus Load Balancers ou WAFs.&lt;/p&gt;

&lt;p&gt;Além disso, o &lt;strong&gt;Bypass de Rate Limit&lt;/strong&gt; no endpoint de autenticação foi classificado como crítico. Sem um limite rigoroso, a aplicação fica exposta a ataques de &lt;strong&gt;Account Takeover (ATO)&lt;/strong&gt; via &lt;em&gt;Credential Stuffing&lt;/em&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Hardening de Borda: Mais que apenas HTTPS
&lt;/h2&gt;

&lt;p&gt;Muitos desenvolvedores acreditam que subir um certificado SSL/TLS é o suficiente. No entanto, a análise revelou que:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;CORS Permissivo:&lt;/strong&gt; Configurações de &lt;code&gt;access-control-allow-origin: *&lt;/code&gt; (wildcard) em APIs financeiras permitem que qualquer site malicioso faça requisições em nome de usuários autenticados.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Fingerprinting de Tecnologia:&lt;/strong&gt; Manter o cabeçalho &lt;code&gt;X-Powered-By: Express&lt;/code&gt; facilita a vida do atacante, que pode buscar exploits específicos para aquela versão do framework.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Segurança de Cabeçalhos:&lt;/strong&gt; A ausência de cabeçalhos críticos (como CSP para evitar XSS e HSTS para forçar HTTPS) foi uma falha abrangente.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  5. Do Manual para o "Security as Code"
&lt;/h2&gt;

&lt;p&gt;A maior conclusão deste relato não foi sobre quais portas estavam abertas, mas sobre a ausência de &lt;strong&gt;validações automáticas em pipelines de CI/CD&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Se tivesse implementado verificações de segurança automatizadas, seria impossível um app chegar à produção sem os cabeçalhos obrigatórios ou com políticas de CORS abertas.&lt;/p&gt;

&lt;h3&gt;
  
  
  Conclusão: O Projeto Arquitetônico Digital
&lt;/h3&gt;

&lt;p&gt;A infraestrutura de uma empresa é como um &lt;strong&gt;edifício residencial&lt;/strong&gt;. Não adianta o andar de investimentos ter portas blindadas se o andar de gestão de contas foi construído com janelas que não trancam.&lt;/p&gt;

&lt;p&gt;A solução definitiva para os problemas encontrados não é consertar cada domínio manualmente, mas atualizar o &lt;strong&gt;único projeto arquitetônico digital (os templates de IaC)&lt;/strong&gt; e garantir que cada novo andar construído siga o mesmo padrão de segurança máxima desde o alicerce.&lt;/p&gt;

</description>
      <category>security</category>
      <category>iac</category>
      <category>vulnerabilityanalysis</category>
      <category>devops</category>
    </item>
    <item>
      <title>Produtividade no Terminal: O Poder dos Aliases no Linux</title>
      <dc:creator>Marcos Vilela</dc:creator>
      <pubDate>Tue, 13 Jan 2026 11:46:53 +0000</pubDate>
      <link>https://dev.to/marcos_vile/produtividade-no-terminal-o-poder-dos-aliases-no-linux-13bm</link>
      <guid>https://dev.to/marcos_vile/produtividade-no-terminal-o-poder-dos-aliases-no-linux-13bm</guid>
      <description>&lt;p&gt;Se você utiliza a linha de comando diariamente, já deve ter percebido que passamos boa parte do tempo digitando os mesmos comandos repetidamente. Seja para limpar o cache do Docker, realizar um deploy ou formatar uma string, a repetição é a inimiga da eficiência. É aqui que entram os aliases do Linux.&lt;/p&gt;

&lt;h2&gt;
  
  
  O que são Aliases?
&lt;/h2&gt;

&lt;p&gt;De forma simplificada, um alias é um apelido ou um atalho para um comando mais longo e complexo. Eles funcionam como substituições de texto no seu shell (Bash ou Zsh). Em vez de digitar uma sequência de 50 caracteres, você pode definir uma palavra de quatro letras que executa exatamente a mesma função.&lt;/p&gt;

&lt;h3&gt;
  
  
  Por que utilizar?
&lt;/h3&gt;

&lt;p&gt;Redução de erros de digitação: Comandos complexos com muitas flags são propensos a erros.&lt;/p&gt;

&lt;h3&gt;
  
  
  Padronização: Você pode criar um fluxo de trabalho padrão para sua equipe ou para seus diferentes projetos.
&lt;/h3&gt;

&lt;h3&gt;
  
  
  Velocidade: A economia de segundos em cada comando se traduz em horas de produtividade ao final de um mês.
&lt;/h3&gt;

&lt;h2&gt;
  
  
  Exemplos simples mas práticos de uso
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Gerenciamento de Infraestrutura
Quem trabalha com Docker sabe que resíduos de contêineres e redes podem consumir gigabytes de memória rapidamente. Um alias de limpeza pode ser um grande aliado, o que costumo utilizar:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;alias docker-prune-all="docker system prune --all --volumes -f"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Normalização de Strings
Muitas vezes precisamos renomear arquivos removendo espaços, acentos e transformando tudo em minúsculas. Uma função no seu arquivo de perfil pode automatizar isso, esse me ajuda a normalizar frases que posso utilizar na criação de branchs:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;normaliza() {
  echo "$*" | iconv -f utf8 -t ascii//TRANSLIT | tr "[:upper:]" "[:lower:]" | sed "s/ /-/g"
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Lembretes de Workflow
Se o seu processo de merge e deploy envolve muitos passos manuais, você pode criar um alias que apenas imprime o passo a passo na tela, servindo como um guia rápido, como um &lt;code&gt;man&lt;/code&gt; de algum software/função:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;alias deploy-help='echo -e "1. git pull\n2. npm run build\n3. dep deploy stage"'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Como configurar
&lt;/h2&gt;

&lt;p&gt;Para tornar seus aliases permanentes, você deve adicioná-los ao arquivo de configuração do seu shell, geralmente o &lt;code&gt;.bashrc&lt;/code&gt; ou &lt;code&gt;.zshrc&lt;/code&gt;, localizados na pasta raiz do seu usuário (~/).&lt;/p&gt;

&lt;p&gt;Uma boa prática é manter seus aliases em um arquivo separado chamado &lt;code&gt;.bash_aliases&lt;/code&gt; e apenas o chamar dentro do seu arquivo principal. Isso mantém suas configurações organizadas e fáceis de transportar para outras máquinas.&lt;/p&gt;

&lt;p&gt;Aliases não são apenas sobre digitar menos, são sobre criar um ambiente de trabalho que se adapta às suas necessidades. Ao identificar padrões no seu dia a dia e automatizá-los, você libera espaço mental para focar no que realmente importa: a solução dos problemas e o desenvolvimento de código de qualidade.&lt;/p&gt;

</description>
      <category>linux</category>
      <category>debian</category>
      <category>devops</category>
      <category>terminal</category>
    </item>
    <item>
      <title>Como Estruturei um Template de AWS Lambda com Terraform e GitHub Actions</title>
      <dc:creator>Marcos Vilela</dc:creator>
      <pubDate>Tue, 25 Nov 2025 13:30:26 +0000</pubDate>
      <link>https://dev.to/marcos_vile/como-estruturei-um-template-de-aws-lambda-com-terraform-e-github-actions-5hd1</link>
      <guid>https://dev.to/marcos_vile/como-estruturei-um-template-de-aws-lambda-com-terraform-e-github-actions-5hd1</guid>
      <description>&lt;p&gt;Recentemente, desenvolvi um template reutilizável para aplicações AWS Lambda em Python, integrando infraestrutura como código via Terraform e automação de CI/CD com GitHub Actions. Neste artigo, compartilho minha experiência, decisões de arquitetura e exemplos práticos para quem deseja acelerar projetos serverless na AWS.&lt;/p&gt;

&lt;h2&gt;
  
  
  Motivação
&lt;/h2&gt;

&lt;p&gt;A necessidade de padronizar e acelerar entregas de funções Lambda me levou a criar um template que pudesse ser facilmente adaptado para diferentes projetos. O objetivo era garantir segurança, rastreabilidade e facilidade de manutenção, sem depender de nomes de empresas ou clientes.&lt;br&gt;
Ao longo do tempo, percebi que cada projeto serverless traz desafios recorrentes: integração com múltiplos triggers, controle de ambientes, versionamento de código, automação de deploy e governança de permissões. Centralizar essas soluções em um template robusto foi essencial para evitar retrabalho e garantir consistência. A busca por automação e segurança guiou cada decisão técnica.&lt;/p&gt;
&lt;h2&gt;
  
  
  Estrutura do Projeto
&lt;/h2&gt;

&lt;p&gt;A estrutura que utilizei foi a seguinte:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;.&lt;/span&gt;
├── app/
│   └── handler.py              &lt;span class="c"&gt;# Código da Lambda&lt;/span&gt;
├── infra/
│   └── terraform/
│       ├── main.tf             &lt;span class="c"&gt;# Infra principal&lt;/span&gt;
│       ├── variables.tf        &lt;span class="c"&gt;# Variáveis&lt;/span&gt;
│       ├── outputs.tf          &lt;span class="c"&gt;# Outputs&lt;/span&gt;
│       ├── version.tf          &lt;span class="c"&gt;# Provider e backend&lt;/span&gt;
│       └── envs/
│           ├── prod/
│           │   └── terraform.tfvars
│           └── staging/
│               └── terraform.tfvars
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Lambda Handler Genérico
&lt;/h2&gt;

&lt;p&gt;Implementei um handler que detecta automaticamente o tipo de evento recebido (HTTP, EventBridge, SNS, S3) e responde de forma adequada. Veja um exemplo simplificado:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;logging&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;datetime&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;datetime&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;timezone&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;typing&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Dict&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Any&lt;/span&gt;

&lt;span class="n"&gt;logger&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;logging&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getLogger&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setLevel&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;logging&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;INFO&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;lambda_handler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Dict&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Any&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Any&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;Dict&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Any&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
    &lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;info&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Lambda invocada - Request ID: %s&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;aws_request_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;info&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Evento recebido: %s&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dumps&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;default&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

    &lt;span class="n"&gt;response_data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;message&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Hello World!&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;timestamp&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;datetime&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;timezone&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;utc&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;isoformat&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;request_id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;aws_request_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;event_type&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;get_event_type&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;function_name&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;function_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;function_version&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;function_version&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;memory_limit_mb&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;memory_limit_in_mb&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;info&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Resposta: %s&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dumps&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;response_data&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nf"&gt;is_http_event&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;statusCode&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;headers&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Content-Type&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;application/json&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;body&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dumps&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;response_data&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;statusCode&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;body&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;response_data&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Infraestrutura como Código com Terraform
&lt;/h2&gt;

&lt;p&gt;Utilizei módulos oficiais da AWS para Lambda, API Gateway, EventBridge, SNS e S3. A configuração de variáveis permite habilitar ou desabilitar triggers conforme o ambiente. Exemplo de variáveis para produção:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="nx"&gt;aws_region&lt;/span&gt;   &lt;span class="err"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"us-east-1"&lt;/span&gt;
&lt;span class="nx"&gt;environment&lt;/span&gt;  &lt;span class="err"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"prod"&lt;/span&gt;
&lt;span class="nx"&gt;project_name&lt;/span&gt; &lt;span class="err"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"my-lambda-app"&lt;/span&gt;
&lt;span class="nx"&gt;lambda_runtime&lt;/span&gt;     &lt;span class="err"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"python3.12"&lt;/span&gt;
&lt;span class="nx"&gt;lambda_timeout&lt;/span&gt;     &lt;span class="err"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;60&lt;/span&gt;
&lt;span class="nx"&gt;lambda_memory_size&lt;/span&gt; &lt;span class="err"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;512&lt;/span&gt;
&lt;span class="nx"&gt;enable_eventbridge&lt;/span&gt; &lt;span class="err"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
&lt;span class="nx"&gt;enable_api_gateway&lt;/span&gt; &lt;span class="err"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
&lt;span class="nx"&gt;enable_function_url&lt;/span&gt; &lt;span class="err"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A detecção automática de Lambda Layers foi feita via local no Terraform:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="nx"&gt;locals&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;layers_path&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"${path.module}/../../app/layers"&lt;/span&gt;
  &lt;span class="nx"&gt;layer_files&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;fileset&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;local&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;layers_path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"*.zip"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="nx"&gt;auto_detected_layers&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;for&lt;/span&gt; &lt;span class="nx"&gt;file&lt;/span&gt; &lt;span class="nx"&gt;in&lt;/span&gt; &lt;span class="nx"&gt;local&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;layer_files&lt;/span&gt; &lt;span class="err"&gt;:&lt;/span&gt;
    &lt;span class="nx"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;file&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;".zip"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;""&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;layer_name&lt;/span&gt;          &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;file&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;".zip"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;""&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="nx"&gt;description&lt;/span&gt;         &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Auto-detected layer from ${file}"&lt;/span&gt;
      &lt;span class="nx"&gt;compatible_runtimes&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;lambda_runtime&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
      &lt;span class="nx"&gt;source_path&lt;/span&gt;         &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;file&lt;/span&gt;
      &lt;span class="nx"&gt;skip_destroy&lt;/span&gt;        &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="nx"&gt;all_layers&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;merge&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nx"&gt;local&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;auto_detected_layers&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;for&lt;/span&gt; &lt;span class="nx"&gt;layer&lt;/span&gt; &lt;span class="nx"&gt;in&lt;/span&gt; &lt;span class="nx"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;lambda_layers&lt;/span&gt; &lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;layer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;layer_name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;layer&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  CI/CD com GitHub Actions
&lt;/h2&gt;

&lt;p&gt;Implementei pipelines para validação, lint, scan de segurança, deploy em staging e produção, além de automação de releases e rollback. O workflow principal para produção segue este padrão:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;push&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;branches&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;main&lt;/span&gt;

&lt;span class="na"&gt;jobs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;validate-lambda&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;
    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Checkout code&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@v5&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Setup Python&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/setup-python@v5&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Check Python syntax&lt;/span&gt;
        &lt;span class="na"&gt;working-directory&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;app&lt;/span&gt;
  &lt;span class="c1"&gt;# ... outros jobs para lint, scan, terraform, release&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Boas Práticas Adotadas
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Separação clara entre ambientes (staging/prod)&lt;/li&gt;
&lt;li&gt;Uso de variáveis para habilitar/desabilitar recursos&lt;/li&gt;
&lt;li&gt;Detecção automática de Lambda Layers&lt;/li&gt;
&lt;li&gt;Templates de PR e rollback para padronizar processos&lt;/li&gt;
&lt;li&gt;Outputs detalhados para facilitar integrações&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Conclusão
&lt;/h2&gt;

&lt;p&gt;Esse template acelerou significativamente o desenvolvimento de funções Lambda, garantindo padronização e segurança. Recomendo fortemente a abordagem para quem busca agilidade e governança em projetos serverless na AWS.&lt;/p&gt;

&lt;p&gt;O código completo está disponível em repositórios privados, mas os exemplos acima podem ser adaptados para qualquer contexto. Se quiser discutir mais sobre arquitetura serverless, estou à disposição para trocar ideias.&lt;/p&gt;

</description>
      <category>lambda</category>
      <category>aws</category>
      <category>terraform</category>
      <category>githubactions</category>
    </item>
    <item>
      <title>Como Provisionar Infraestrutura AWS MediaConvert com Terraform</title>
      <dc:creator>Marcos Vilela</dc:creator>
      <pubDate>Tue, 25 Nov 2025 13:28:05 +0000</pubDate>
      <link>https://dev.to/marcos_vile/como-provisionar-infraestrutura-aws-mediaconvert-com-terraform-1lg</link>
      <guid>https://dev.to/marcos_vile/como-provisionar-infraestrutura-aws-mediaconvert-com-terraform-1lg</guid>
      <description>&lt;h2&gt;
  
  
  Introdução
&lt;/h2&gt;

&lt;p&gt;Recentemente, trabalhei em um projeto que envolvia o processamento de vídeos em escala na nuvem. Precisávamos de uma solução robusta para converter vídeos automaticamente, e o AWS MediaConvert se mostrou perfeito para isso. No entanto, configurar toda a infraestrutura necessária – filas, roles IAM, templates de job e logs – pode ser complexo. Decidi usar Terraform para automatizar tudo, criando uma infraestrutura como código que pudesse ser reutilizada em diferentes ambientes.&lt;/p&gt;

&lt;p&gt;Neste artigo, vou compartilhar minha experiência implementando essa infraestrutura. Vou explicar passo a passo como provisionar os recursos essenciais do MediaConvert usando Terraform, desde a configuração inicial até o deploy. O foco é em uma abordagem prática, com exemplos de código e dicas para evitar armadilhas comuns.&lt;/p&gt;

&lt;h2&gt;
  
  
  Por Que Terraform para Infraestrutura de MediaConvert?
&lt;/h2&gt;

&lt;p&gt;Antes de mergulhar no código, vamos entender o contexto. O AWS MediaConvert é um serviço gerenciado para transcodificação de vídeos. Ele suporta formatos variados e pode processar jobs em paralelo usando filas. Para integrá-lo com uma aplicação, como uma função Lambda que detecta uploads de vídeos no S3, precisamos de:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Uma fila MediaConvert para gerenciar jobs.&lt;/li&gt;
&lt;li&gt;Um role IAM com permissões para acessar buckets S3 e escrever logs no CloudWatch.&lt;/li&gt;
&lt;li&gt;Um template de job que define as configurações de transcodificação (resolução, bitrate, etc.).&lt;/li&gt;
&lt;li&gt;Logs para monitoramento e debugging.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Fazer isso manualmente via console AWS é possível, mas propenso a erros e difícil de versionar. Com Terraform, tudo fica declarativo, versionado no Git e replicável em ambientes como staging e produção.&lt;/p&gt;

&lt;h2&gt;
  
  
  Estrutura do Projeto
&lt;/h2&gt;

&lt;p&gt;Organizei o projeto da seguinte forma:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;infra/terraform/
├── main.tf                        # Recursos principais
├── variables.tf                   # Declarações de variáveis
├── outputs.tf                     # Outputs para integração
├── version.tf                     # Configuração do provider
├── mediaconvert-template.json     # Template de job
└── envs/
    ├── staging/
    │   └── variables.tfvars
    └── prod/
        └── variables.tfvars
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Essa estrutura permite gerenciar múltiplos ambientes com arquivos de variáveis específicos. O template JSON define as configurações de vídeo, como resolução 720p, codec H.264 e bitrate variável.&lt;/p&gt;

&lt;h2&gt;
  
  
  Pré-requisitos
&lt;/h2&gt;

&lt;p&gt;Antes de começar, certifique-se de ter:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Terraform instalado.&lt;/li&gt;
&lt;li&gt;AWS CLI configurado com credenciais válidas.&lt;/li&gt;
&lt;li&gt;Buckets S3 existentes para input e output de vídeos (o Terraform não cria buckets, apenas configura permissões).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Para configurar o backend S3 do Terraform (para armazenar o estado remotamente), edite o &lt;code&gt;version.tf&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="nx"&gt;terraform&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;backend&lt;/span&gt; &lt;span class="s2"&gt;"s3"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;bucket&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"meu-bucket-tfstate"&lt;/span&gt;
    &lt;span class="nx"&gt;key&lt;/span&gt;    &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"infra/mediaconvert.tfstate"&lt;/span&gt;
    &lt;span class="nx"&gt;region&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"us-east-1"&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nx"&gt;required_providers&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;aws&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;source&lt;/span&gt;  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"hashicorp/aws"&lt;/span&gt;
      &lt;span class="nx"&gt;version&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"&amp;gt;= 4.40.0, &amp;lt;= 5.55.0"&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nx"&gt;required_version&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"&amp;gt;= 1.6.0"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Definindo as Variáveis
&lt;/h2&gt;

&lt;p&gt;No &lt;code&gt;variables.tf&lt;/code&gt;, declarei todas as variáveis necessárias. Isso inclui região AWS, nomes de buckets, status da fila e configurações de aceleração. Por exemplo:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="nx"&gt;variable&lt;/span&gt; &lt;span class="s2"&gt;"aws_region"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;description&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"AWS region where resources will be created"&lt;/span&gt;
  &lt;span class="nx"&gt;type&lt;/span&gt;        &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;string&lt;/span&gt;
  &lt;span class="nx"&gt;default&lt;/span&gt;     &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"us-east-1"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;variable&lt;/span&gt; &lt;span class="s2"&gt;"s3_input_bucket"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;description&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"S3 bucket name for input videos"&lt;/span&gt;
  &lt;span class="nx"&gt;type&lt;/span&gt;        &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;string&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;variable&lt;/span&gt; &lt;span class="s2"&gt;"queue_status"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;description&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"MediaConvert queue status (ACTIVE or PAUSED)"&lt;/span&gt;
  &lt;span class="nx"&gt;type&lt;/span&gt;        &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;string&lt;/span&gt;
  &lt;span class="nx"&gt;default&lt;/span&gt;     &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"ACTIVE"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Para staging, criei um arquivo &lt;code&gt;envs/staging/variables.tfvars&lt;/code&gt; com valores específicos:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="nx"&gt;aws_region&lt;/span&gt;   &lt;span class="err"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"us-east-1"&lt;/span&gt;
&lt;span class="nx"&gt;environment&lt;/span&gt;  &lt;span class="err"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"staging"&lt;/span&gt;
&lt;span class="nx"&gt;project_name&lt;/span&gt; &lt;span class="err"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"mediaconvert-infra"&lt;/span&gt;

&lt;span class="nx"&gt;s3_input_bucket&lt;/span&gt;  &lt;span class="err"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"meu-bucket-staging-input"&lt;/span&gt;
&lt;span class="nx"&gt;s3_output_bucket&lt;/span&gt; &lt;span class="err"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"meu-bucket-staging-output"&lt;/span&gt;
&lt;span class="nx"&gt;queue_status&lt;/span&gt;     &lt;span class="err"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"ACTIVE"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Isso permite customizar por ambiente sem alterar o código principal.&lt;/p&gt;

&lt;h2&gt;
  
  
  Provisionando os Recursos Principais
&lt;/h2&gt;

&lt;p&gt;No &lt;code&gt;main.tf&lt;/code&gt;, comecei configurando o provider AWS com tags padrão:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="nx"&gt;provider&lt;/span&gt; &lt;span class="s2"&gt;"aws"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;region&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;aws_region&lt;/span&gt;

  &lt;span class="nx"&gt;default_tags&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;Project&lt;/span&gt;     &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"MediaConvert Infra"&lt;/span&gt;
    &lt;span class="nx"&gt;Environment&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;environment&lt;/span&gt;
    &lt;span class="nx"&gt;ManagedBy&lt;/span&gt;   &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Terraform"&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Em seguida, criei o role IAM para o MediaConvert. Esse role precisa de permissões para ler do bucket de input, escrever no de output e publicar logs. Usei policy documents para definir as permissões granularmente:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="s2"&gt;"aws_iam_policy_document"&lt;/span&gt; &lt;span class="s2"&gt;"mediaconvert_assume_role"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;statement&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;actions&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"sts:AssumeRole"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="nx"&gt;principals&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;type&lt;/span&gt;        &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Service"&lt;/span&gt;
      &lt;span class="nx"&gt;identifiers&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"mediaconvert.amazonaws.com"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_iam_role"&lt;/span&gt; &lt;span class="s2"&gt;"mediaconvert_role"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;name&lt;/span&gt;               &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"${var.project_name}-role-${var.environment}"&lt;/span&gt;
  &lt;span class="nx"&gt;assume_role_policy&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;aws_iam_policy_document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;mediaconvert_assume_role&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;json&lt;/span&gt;
  &lt;span class="nx"&gt;description&lt;/span&gt;        &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Service role for AWS MediaConvert"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Anexei políticas para acesso S3 e operações MediaConvert. Para a fila, foi simples:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="nx"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_media_convert_queue"&lt;/span&gt; &lt;span class="s2"&gt;"main"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;name&lt;/span&gt;   &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"${var.project_name}-queue-${var.environment}"&lt;/span&gt;
  &lt;span class="nx"&gt;status&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;queue_status&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;O template de job foi o mais desafiador, pois o provider Terraform não suporta nativamente. Usei um &lt;code&gt;null_resource&lt;/code&gt; com &lt;code&gt;local-exec&lt;/code&gt; para criar via AWS CLI:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="nx"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"null_resource"&lt;/span&gt; &lt;span class="s2"&gt;"mediaconvert_job_template"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;provisioner&lt;/span&gt; &lt;span class="s2"&gt;"local-exec"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;command&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"aws mediaconvert create-job-template --name ${var.project_name}-job-template-${var.environment} --settings file://${var.job_template_json_path}"&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nx"&gt;depends_on&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;aws_media_convert_queue&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;main&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Isso garante que o template seja criado após a fila. Por fim, adicionei um log group no CloudWatch para monitoramento.&lt;/p&gt;

&lt;h2&gt;
  
  
  Outputs para Integração
&lt;/h2&gt;

&lt;p&gt;No &lt;code&gt;outputs.tf&lt;/code&gt;, exportei os ARNs e nomes necessários para que a função Lambda possa criar jobs:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="nx"&gt;output&lt;/span&gt; &lt;span class="s2"&gt;"mediaconvert_queue_arn"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;description&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"ARN of the MediaConvert queue"&lt;/span&gt;
  &lt;span class="nx"&gt;value&lt;/span&gt;       &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws_media_convert_queue&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;main&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;arn&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;output&lt;/span&gt; &lt;span class="s2"&gt;"mediaconvert_role_arn"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;description&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"ARN of the MediaConvert IAM role"&lt;/span&gt;
  &lt;span class="nx"&gt;value&lt;/span&gt;       &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws_iam_role&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;mediaconvert_role&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;arn&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Isso facilita a integração com outros componentes da aplicação.&lt;/p&gt;

&lt;h2&gt;
  
  
  Deploy e Validação
&lt;/h2&gt;

&lt;p&gt;Para aplicar em staging:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cd &lt;/span&gt;infra/terraform
terraform init
terraform plan &lt;span class="nt"&gt;-var-file&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;envs/staging/variables.tfvars
terraform apply &lt;span class="nt"&gt;-var-file&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;envs/staging/variables.tfvars
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Sempre valide com &lt;code&gt;terraform plan&lt;/code&gt; antes de aplicar. Para produção, use o arquivo &lt;code&gt;prod/variables.tfvars&lt;/code&gt; e considere adicionar aprovação manual nos workflows de CI/CD.&lt;/p&gt;

&lt;h2&gt;
  
  
  Lições Aprendidas
&lt;/h2&gt;

&lt;p&gt;Durante a implementação, aprendi que:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Buckets S3 devem existir antes do Terraform; caso contrário, as políticas IAM falham.&lt;/li&gt;
&lt;li&gt;O template JSON precisa ser validado manualmente, pois erros só aparecem no runtime.&lt;/li&gt;
&lt;li&gt;Use workspaces Terraform para isolar ambientes e evitar conflitos de estado.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Essa abordagem tornou o deploy previsível e escalável. Se você está trabalhando com processamento de mídia na AWS, recomendo fortemente o Terraform para gerenciar essa infraestrutura.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusão
&lt;/h2&gt;

&lt;p&gt;Provisionar infraestrutura MediaConvert com Terraform não só automatiza o processo como também o torna versionável e replicável. Comecei com scripts manuais e migrei para IaC, o que reduziu erros e acelerou deploys. Se você tiver dúvidas ou quiser ver o código completo, confira o repositório no GitHub. Estou curioso para ouvir suas experiências com MediaConvert!&lt;/p&gt;

</description>
      <category>aws</category>
      <category>mediaconvert</category>
      <category>terraform</category>
      <category>deploy</category>
    </item>
  </channel>
</rss>
