<?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: Leonan Viana</title>
    <description>The latest articles on DEV Community by Leonan Viana (@leonanviana).</description>
    <link>https://dev.to/leonanviana</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%2F1400591%2Fd43cf160-46bd-4d68-9ed4-fc54257db191.jpg</url>
      <title>DEV Community: Leonan Viana</title>
      <link>https://dev.to/leonanviana</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/leonanviana"/>
    <language>en</language>
    <item>
      <title>ECS Fargate com Service Discovery usando AWS Cloud Map e Terraform</title>
      <dc:creator>Leonan Viana</dc:creator>
      <pubDate>Sun, 22 Mar 2026 23:25:15 +0000</pubDate>
      <link>https://dev.to/leonanviana/ecs-fargate-com-service-discovery-usando-aws-cloud-map-e-terraform-54p2</link>
      <guid>https://dev.to/leonanviana/ecs-fargate-com-service-discovery-usando-aws-cloud-map-e-terraform-54p2</guid>
      <description>&lt;p&gt;Toda vez que preciso conectar dois serviços ECS Fargate, alguém no time sugere um ALB. Funciona, mas pagar ~$16/mês fixo pra resolver tráfego interno entre containers na mesma VPC é dinheiro jogado fora.&lt;/p&gt;

&lt;p&gt;Nesse projeto montei uma alternativa usando AWS Cloud Map: dois serviços Fargate em subnets privadas, sem IP público, sem ALB. O serviço interno se registra no Cloud Map e o Nginx resolve o nome via &lt;code&gt;proxy_pass&lt;/code&gt; — tudo dentro da VPC, tudo via Terraform.&lt;/p&gt;

&lt;p&gt;O código está no GitHub.&lt;br&gt;
&lt;/p&gt;
&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://assets.dev.to/assets/github-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/leonanviana" rel="noopener noreferrer"&gt;
        leonanviana
      &lt;/a&gt; / &lt;a href="https://github.com/leonanviana/terraform-ecs-fargate-service-discovery" rel="noopener noreferrer"&gt;
        terraform-ecs-fargate-service-discovery
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      Terraform example: ECS Fargate services communicating via AWS Cloud Map service discovery, with Nginx as reverse proxy — no ALB, no public IPs
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;
&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;terraform-ecs-fargate-service-discovery&lt;/h1&gt;
&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;a href="https://github.com/leonanviana/terraform-ecs-fargate-service-discovery#portugu%C3%AAs" rel="noopener noreferrer"&gt;Português&lt;/a&gt; | &lt;a href="https://github.com/leonanviana/terraform-ecs-fargate-service-discovery#english" rel="noopener noreferrer"&gt;English&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Português&lt;/h2&gt;
&lt;/div&gt;
&lt;p&gt;Exemplo em Terraform demonstrando o uso do &lt;strong&gt;AWS Cloud Map Service Discovery&lt;/strong&gt; com &lt;strong&gt;ECS Fargate&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Todos os serviços rodam em subnets privadas, sem IP público. O serviço interno se registra em um namespace DNS privado do Cloud Map (&lt;code&gt;myapp.internal&lt;/code&gt;), e o Nginx resolve esse nome via &lt;code&gt;proxy_pass&lt;/code&gt; para rotear o tráfego internamente na VPC.&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h3 class="heading-element"&gt;Arquitetura&lt;/h3&gt;
&lt;/div&gt;
&lt;div class="snippet-clipboard-content notranslate position-relative overflow-auto"&gt;&lt;pre class="notranslate"&gt;&lt;code&gt;VPC (privada)
    │
    ▼
 [Nginx - ECS Fargate]  ← subnet privada, sem IP público
    │
    │  proxy_pass via DNS do Cloud Map
    └──► internal-tool.myapp.internal → [Internal Tool - ECS Fargate]
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div class="markdown-heading"&gt;
&lt;h3 class="heading-element"&gt;Estrutura&lt;/h3&gt;

&lt;/div&gt;
&lt;div class="snippet-clipboard-content notranslate position-relative overflow-auto"&gt;
&lt;pre class="notranslate"&gt;&lt;code&gt;stage/
├── main.tf                    # Providers Terraform, backend
├── vpc.tf                     # Módulo VPC (subnets, NAT Gateway)
├── locals.tf                  # Prefixo e tags comuns
├── variables.tf               # Todas as variáveis de entrada
├── cloudmap.tf                # Namespace Cloud Map + registros de service discovery
├── cluster.tf                 # Cluster ECS
├── cluster-sg.tf              # Security groups e regras
├── service-nginx.tf           #&lt;/code&gt;&lt;/pre&gt;…&lt;/div&gt;
&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/leonanviana/terraform-ecs-fargate-service-discovery" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;





&lt;h2&gt;
  
  
  Arquitetura
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;VPC (privada)
│
▼
[Nginx — ECS Fargate]        ← subnet privada, sem IP público
│
│  proxy_pass via DNS do Cloud Map
│
└──► internal-tool.myapp.internal → [Internal Tool — ECS Fargate]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;O Nginx é a única porta de entrada. O &lt;code&gt;internal-tool&lt;/code&gt; nunca expõe IP público — o Cloud Map registra o IP da task automaticamente e o Nginx resolve esse nome sem precisar de configuração adicional a cada deploy.&lt;/p&gt;




&lt;h2&gt;
  
  
  Estrutura dos arquivos
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;stage/
├── main.tf                    # Providers, backend, módulo VPC
├── locals.tf                  # Prefixo e tags comuns
├── variables.tf               # Variáveis de entrada
├── cloudmap.tf                # Namespace DNS + service discovery
├── cluster.tf                 # Cluster ECS
├── cluster-sg.tf              # Security groups
├── service-nginx.tf           # ECS service do Nginx
├── task-nginx.tf              # Task definition do Nginx
├── service-internal-tool.tf   # ECS service da ferramenta interna
├── task-internal-tool.tf      # Task definition da ferramenta interna
└── templates/
    ├── nginx-json.tpl
    └── internal-tool-json.tpl
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Prefiro um arquivo por recurso. Quando o projeto cresce, você não fica caçando &lt;code&gt;aws_ecs_service&lt;/code&gt; enterrado no meio de um &lt;code&gt;main.tf&lt;/code&gt; com 400 linhas.&lt;/p&gt;




&lt;h2&gt;
  
  
  1. Namespace DNS privado com Cloud Map
&lt;/h2&gt;

&lt;p&gt;O &lt;code&gt;cloudmap.tf&lt;/code&gt; cria a zona DNS &lt;code&gt;myapp.internal&lt;/code&gt;, privada à VPC:&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_service_discovery_private_dns_namespace"&lt;/span&gt; &lt;span class="s2"&gt;"ecs"&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;"myapp.internal"&lt;/span&gt;
  &lt;span class="nx"&gt;vpc&lt;/span&gt;         &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;vpc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;vpc_id&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;"Private DNS namespace for myapp ECS services"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Nenhum tráfego sai pra internet. É um DNS interno só pra dentro da VPC.&lt;/p&gt;

&lt;p&gt;O registro do serviço define como os IPs das tasks são publicados:&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_service_discovery_service"&lt;/span&gt; &lt;span class="s2"&gt;"internal_tool"&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;"internal-tool"&lt;/span&gt;

  &lt;span class="nx"&gt;dns_config&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;namespace_id&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws_service_discovery_private_dns_namespace&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ecs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;

    &lt;span class="nx"&gt;dns_records&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;ttl&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;type&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"A"&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="nx"&gt;routing_policy&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"MULTIVALUE"&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nx"&gt;health_check_custom_config&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;&lt;code&gt;MULTIVALUE&lt;/code&gt; faz o Cloud Map retornar todos os IPs de tasks ativas quando consultado. Com mais de uma task rodando, o Nginx distribui o tráfego entre elas sem configuração extra.&lt;/p&gt;




&lt;h2&gt;
  
  
  2. Serviço interno com registro no Cloud Map
&lt;/h2&gt;

&lt;p&gt;O &lt;code&gt;service-internal-tool.tf&lt;/code&gt; tem dois blocos que fazem o serviço aparecer no DNS:&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_ecs_service"&lt;/span&gt; &lt;span class="s2"&gt;"internal_tool"&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.prefix}-internal-tool"&lt;/span&gt;
  &lt;span class="nx"&gt;cluster&lt;/span&gt;         &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws_ecs_cluster&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;id&lt;/span&gt;
  &lt;span class="nx"&gt;task_definition&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws_ecs_task_definition&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;task_internal_tool&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;
  &lt;span class="nx"&gt;launch_type&lt;/span&gt;     &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"FARGATE"&lt;/span&gt;
  &lt;span class="nx"&gt;desired_count&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;app_count&lt;/span&gt;

  &lt;span class="nx"&gt;network_configuration&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;security_groups&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_security_group&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;internal_tool_sg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="nx"&gt;subnets&lt;/span&gt;          &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;vpc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;private_subnets&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="nx"&gt;assign_public_ip&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="c1"&gt;# Service Connect — sidecar proxy do ECS com métricas embutidas&lt;/span&gt;
  &lt;span class="nx"&gt;service_connect_configuration&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;enabled&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;namespace&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws_service_discovery_private_dns_namespace&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ecs&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="c1"&gt;# Registra o IP da task no Cloud Map&lt;/span&gt;
  &lt;span class="c1"&gt;# Nginx resolve: internal-tool.myapp.internal → IP da task&lt;/span&gt;
  &lt;span class="nx"&gt;service_registries&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;registry_arn&lt;/span&gt;   &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws_service_discovery_service&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;internal_tool&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;container_name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"myapp-internal-tool"&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nx"&gt;lifecycle&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;ignore_changes&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;desired_count&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nx"&gt;force_new_deployment&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;O bloco &lt;code&gt;service_registries&lt;/code&gt; conecta o ECS ao Cloud Map. Quando uma task sobe, o ECS registra o IP dela no namespace DNS. Quando desce ou é substituída num deploy, o registro atualiza sozinho.&lt;/p&gt;

&lt;p&gt;O &lt;code&gt;service_connect_configuration&lt;/code&gt; habilita o sidecar proxy do ECS — métricas de latência e erro entre serviços sem instrumentar o código da aplicação.&lt;/p&gt;




&lt;h2&gt;
  
  
  3. Nginx como proxy reverso
&lt;/h2&gt;

&lt;p&gt;O Nginx resolve &lt;code&gt;internal-tool.myapp.internal&lt;/code&gt; via &lt;code&gt;proxy_pass&lt;/code&gt; porque está na mesma VPC. Não precisa se registrar no Cloud Map.&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_ecs_service"&lt;/span&gt; &lt;span class="s2"&gt;"nginx"&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.prefix}-nginx"&lt;/span&gt;
  &lt;span class="nx"&gt;cluster&lt;/span&gt;         &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws_ecs_cluster&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;id&lt;/span&gt;
  &lt;span class="nx"&gt;task_definition&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws_ecs_task_definition&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;task_nginx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;
  &lt;span class="nx"&gt;launch_type&lt;/span&gt;     &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"FARGATE"&lt;/span&gt;
  &lt;span class="nx"&gt;desired_count&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;app_count&lt;/span&gt;

  &lt;span class="nx"&gt;network_configuration&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;security_groups&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_security_group&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;nginx_ecs_sg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="nx"&gt;subnets&lt;/span&gt;          &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;vpc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;private_subnets&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]]&lt;/span&gt;
    &lt;span class="nx"&gt;assign_public_ip&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="nx"&gt;force_new_deployment&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;assign_public_ip = false&lt;/code&gt; nos dois serviços. Nenhum dos dois é acessível diretamente da internet.&lt;/p&gt;




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

&lt;ul&gt;
&lt;li&gt;Conta AWS com permissões para ECS, ECR e VPC&lt;/li&gt;
&lt;li&gt;Bucket S3 criado — atualize o nome no backend em &lt;code&gt;main.tf&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;IAM role &lt;code&gt;ecsTaskExecutionRole&lt;/code&gt; com as managed policies do ECS&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Como usar
&lt;/h2&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;stage/
terraform init
terraform plan
terraform apply
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Cloud Map vs ALB — quando vale a troca
&lt;/h2&gt;

&lt;p&gt;ALB faz sentido quando você precisa expor um serviço publicamente, quer roteamento por path/host, ou precisa de SSL termination gerenciado. Pra tráfego interno entre containers na mesma VPC, você paga pela infraestrutura sem usar a maior parte do que ela oferece.&lt;/p&gt;

&lt;p&gt;Cloud Map cobra por instância registrada e por consultas DNS. Em workloads moderados, a diferença de custo é relevante. Você também elimina um hop de rede — a latência entre os serviços cai.&lt;/p&gt;

&lt;p&gt;A contrapartida: sem ALB você perde os health checks dele e o SSL termination. Pra comunicação interna isso raramente importa — o Service Connect já traz métricas e os health checks do ECS cuidam das tasks.&lt;/p&gt;




&lt;p&gt;Se você usa ECS Fargate com múltiplos serviços internos e ainda não conhecia o Cloud Map, vale testar. O repositório tem tudo pronto pra subir num &lt;code&gt;terraform apply&lt;/code&gt;.&lt;/p&gt;

</description>
      <category>aws</category>
      <category>cloudmap</category>
      <category>terraform</category>
      <category>ecs</category>
    </item>
    <item>
      <title>Checkov Scan para Terraform com Azure Pipelines</title>
      <dc:creator>Leonan Viana</dc:creator>
      <pubDate>Sun, 22 Mar 2026 21:39:09 +0000</pubDate>
      <link>https://dev.to/leonanviana/checkov-scan-para-terraform-com-azure-pipelines-3lo8</link>
      <guid>https://dev.to/leonanviana/checkov-scan-para-terraform-com-azure-pipelines-3lo8</guid>
      <description>&lt;p&gt;Esse post mostra como usar o Checkov para escanear seu IaC Terraform dentro de uma esteira do Azure Pipelines. O objetivo é ter um step dedicado ao scan de segurança antes de qualquer &lt;code&gt;plan&lt;/code&gt; ou &lt;code&gt;apply&lt;/code&gt; chegar no ambiente.&lt;/p&gt;

&lt;h2&gt;
  
  
  O que é o Checkov?
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://www.checkov.io/" rel="noopener noreferrer"&gt;Checkov&lt;/a&gt; é uma ferramenta de análise estática para infraestrutura como código. Ela verifica configurações de recursos cloud e aponta misconfigurations antes que elas sejam provisionadas. Suporta Terraform, CloudFormation, Kubernetes, Helm, ARM Templates, Serverless e AWS CDK.&lt;/p&gt;




&lt;h2&gt;
  
  
  Etapa 1 — Criando o template do Checkov
&lt;/h2&gt;

&lt;p&gt;Como vamos usar Azure Pipelines, faz sentido isolar as tasks do Checkov em um template reutilizável. O arquivo fica em:&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="s"&gt;.azuredevops/templates/terraform-build-checkov.yml&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;O template usa duas variáveis predefinidas do Azure DevOps para instalar o Checkov no runner de execução:&lt;/p&gt;


&lt;div class="crayons-card c-embed text-styles text-styles--secondary"&gt;
    &lt;div class="c-embed__content"&gt;
        &lt;div class="c-embed__cover"&gt;
          &lt;a href="https://learn.microsoft.com/en-us/azure/devops/pipelines/build/variables?view=azure-devops&amp;amp;amp%3Btabs=yaml&amp;amp;amp%3Bsource=post_page-----045f6dd5ab9e---------------------------------------" class="c-link align-middle" rel="noopener noreferrer"&gt;
            &lt;img alt="" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Flearn.microsoft.com%2Fen-us%2Fmedia%2Fopen-graph-image.png" height="auto" class="m-0"&gt;
          &lt;/a&gt;
        &lt;/div&gt;
      &lt;div class="c-embed__body"&gt;
        &lt;h2 class="fs-xl lh-tight"&gt;
          &lt;a href="https://learn.microsoft.com/en-us/azure/devops/pipelines/build/variables?view=azure-devops&amp;amp;amp%3Btabs=yaml&amp;amp;amp%3Bsource=post_page-----045f6dd5ab9e---------------------------------------" rel="noopener noreferrer" class="c-link"&gt;
            Predefined variables - Azure Pipelines | Microsoft Learn
          &lt;/a&gt;
        &lt;/h2&gt;
          &lt;p class="truncate-at-3"&gt;
            A comprehensive list of all available predefined variables
          &lt;/p&gt;
        &lt;div class="color-secondary fs-s flex items-center"&gt;
          learn.microsoft.com
        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;$(Agent.ToolsDirectory)&lt;/code&gt; — diretório de ferramentas do agente&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;$(Agent.OS)&lt;/code&gt; — sistema operacional do agente
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;parameters&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;CheckovVersion&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;string&lt;/span&gt;
    &lt;span class="na"&gt;default&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&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;WorkingDir&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;string&lt;/span&gt;
    &lt;span class="na"&gt;default&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;.'&lt;/span&gt;

&lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;

  &lt;span class="c1"&gt;# Cache do Checkov para evitar reinstalação a cada run&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;task&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Cache@2&lt;/span&gt;
    &lt;span class="na"&gt;displayName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Restore checkov $(CheckovVersion) from cache&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;checkov_restore_cache&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;key&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;"checkov&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;$(CheckovVersion)"&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;$(Agent.OS)'&lt;/span&gt;
      &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;$(Agent.ToolsDirectory)/checkov&lt;/span&gt;
      &lt;span class="na"&gt;cacheHitVar&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;CACHE_RESTORED&lt;/span&gt;

  &lt;span class="c1"&gt;# Instala o Checkov apenas se o cache não existir&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;task&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;CmdLine@2&lt;/span&gt;
    &lt;span class="na"&gt;displayName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Install checkov $(CheckovVersion)&lt;/span&gt;
    &lt;span class="na"&gt;condition&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;and(succeeded(), ne(variables.CACHE_RESTORED, 'true'))&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;targetType&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;inline'&lt;/span&gt;
      &lt;span class="na"&gt;script&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
        &lt;span class="s"&gt;CHECKOV_DIR=${AGENT_TOOLSDIRECTORY}/checkov&lt;/span&gt;
        &lt;span class="s"&gt;mkdir -p $CHECKOV_DIR&lt;/span&gt;
        &lt;span class="s"&gt;python3 -m venv $CHECKOV_DIR&lt;/span&gt;
        &lt;span class="s"&gt;source $CHECKOV_DIR/bin/activate&lt;/span&gt;
        &lt;span class="s"&gt;pip3 install checkov==${{ parameters.CheckovVersion }}&lt;/span&gt;
        &lt;span class="s"&gt;echo "##vso[task.prependpath]$CHECKOV_DIR/bin"&lt;/span&gt;

  &lt;span class="c1"&gt;# Adiciona o Checkov ao PATH do agente&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;task&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;CmdLine@2&lt;/span&gt;
    &lt;span class="na"&gt;displayName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Add checkov to PATH&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;targetType&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;inline'&lt;/span&gt;
      &lt;span class="na"&gt;script&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
        &lt;span class="s"&gt;CHECKOV_DIR=${AGENT_TOOLSDIRECTORY}/checkov&lt;/span&gt;
        &lt;span class="s"&gt;echo "##vso[task.prependpath]$CHECKOV_DIR/bin"&lt;/span&gt;

  &lt;span class="c1"&gt;# Executa o scan no diretório Terraform informado&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;task&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;CmdLine@2&lt;/span&gt;
    &lt;span class="na"&gt;displayName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Run checkov&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;targetType&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;inline'&lt;/span&gt;
      &lt;span class="na"&gt;script&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;checkov --directory "${{ parameters.WorkingDir }}" --framework terraform&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Por que usar cache?&lt;/strong&gt; O Checkov tem algumas dependências Python que levam tempo para instalar. Com a task &lt;code&gt;Cache@2&lt;/code&gt;, a instalação só acontece uma vez por versão — nas runs seguintes o agente restaura direto do cache.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Etapa 2 — Configurando a Pipeline principal
&lt;/h2&gt;

&lt;p&gt;Na raiz do repositório, o &lt;code&gt;azure-pipelines.yaml&lt;/code&gt; referencia o template criado acima. A variável &lt;code&gt;$(System.DefaultWorkingDirectory)&lt;/code&gt; aponta para o diretório dos arquivos Terraform.&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;trigger&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="na"&gt;include&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;paths&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;exclude&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;**/*.md'&lt;/span&gt;

&lt;span class="na"&gt;pool&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;vmImage&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;variables&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;CheckovVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;2.3.110'&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;task&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;TerraformTaskV4@4&lt;/span&gt;
    &lt;span class="na"&gt;displayName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Terraform Init&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;provider&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;aws'&lt;/span&gt;
      &lt;span class="na"&gt;command&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;init'&lt;/span&gt;
      &lt;span class="na"&gt;workingDirectory&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;$(System.DefaultWorkingDirectory)/tf'&lt;/span&gt;

  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;template&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;.azuredevops/templates/terraform-build-checkov.yaml&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;O exemplo acima tem apenas o &lt;code&gt;Init&lt;/code&gt; e o template do Checkov para manter a demonstração simples. Na prática, você vai querer adicionar &lt;code&gt;Validate&lt;/code&gt;, &lt;code&gt;TfLint&lt;/code&gt;, &lt;code&gt;Plan&lt;/code&gt; e &lt;code&gt;Apply&lt;/code&gt; conforme a maturidade da sua esteira.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Etapa 3 — Executando e interpretando o resultado
&lt;/h2&gt;

&lt;p&gt;Ao rodar a pipeline, o log vai mostrar:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Quantos recursos foram verificados&lt;/li&gt;
&lt;li&gt;Quantos checks passaram, falharam ou foram pulados (skipped)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="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%2Farticles%2Fmpnzrzd4tvdnajz5kelj.png" class="article-body-image-wrapper"&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%2Farticles%2Fmpnzrzd4tvdnajz5kelj.png" alt=" "&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Se um check falhar, a pipeline quebra.&lt;/strong&gt; Esse é o comportamento esperado — a ideia é bloquear o deploy até que a configuração seja corrigida ou explicitamente ignorada.&lt;/p&gt;




&lt;h2&gt;
  
  
  Como fazer skip de um check específico
&lt;/h2&gt;

&lt;p&gt;Quando um item é avaliado como falso positivo ou está dentro de uma exceção aceitável para o contexto, você pode declarar o skip diretamente no bloco do resource 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;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_eks_cluster"&lt;/span&gt; &lt;span class="s2"&gt;"cluster-eks"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

  &lt;span class="c1"&gt;#checkov:skip=CKV_AWS_39: "Ensure Amazon EKS public endpoint disabled"&lt;/span&gt;
  &lt;span class="c1"&gt;#checkov:skip=CKV_AWS_37: "Ensure Amazon EKS control plane logging enabled for all log types"&lt;/span&gt;
  &lt;span class="c1"&gt;#checkov:skip=CKV_AWS_38: "Ensure Amazon EKS public endpoint not accessible to 0.0.0.0/0"&lt;/span&gt;
  &lt;span class="c1"&gt;#checkov:skip=CKV_AWS_58: "Ensure EKS Cluster has Secrets Encryption Enabled"&lt;/span&gt;

  &lt;span class="c1"&gt;# ... restante da configuração&lt;/span&gt;

&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;O comentário segue o padrão &lt;code&gt;#checkov:skip=&amp;lt;CHECK_ID&amp;gt;: "&amp;lt;justificativa&amp;gt;"&lt;/code&gt;. A justificativa é opcional mas recomendada para rastreabilidade — especialmente em auditorias.&lt;/p&gt;

&lt;p&gt;&lt;a href="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%2Farticles%2Fqu5entt41r48ui3iai6t.png" class="article-body-image-wrapper"&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%2Farticles%2Fqu5entt41r48ui3iai6t.png" alt=" "&gt;&lt;/a&gt;&lt;/p&gt;




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

&lt;p&gt;Com esse template, o scan de segurança vira parte do ciclo normal de desenvolvimento. Qualquer misconfiguration no Terraform é bloqueada antes de chegar no &lt;code&gt;plan&lt;/code&gt;, o que reduz bastante o risco de provisionar recursos fora de conformidade.&lt;/p&gt;

&lt;p&gt;O Checkov suporta os seguintes frameworks:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Terraform e Terraform Plan&lt;/li&gt;
&lt;li&gt;CloudFormation&lt;/li&gt;
&lt;li&gt;Kubernetes&lt;/li&gt;
&lt;li&gt;ARM Templates&lt;/li&gt;
&lt;li&gt;Serverless&lt;/li&gt;
&lt;li&gt;Helm&lt;/li&gt;
&lt;li&gt;AWS CDK&lt;/li&gt;
&lt;/ul&gt;




</description>
      <category>checkov</category>
      <category>terraform</category>
      <category>azuredevops</category>
      <category>cicd</category>
    </item>
    <item>
      <title>Adicionando Headers no Nginx com Ansible</title>
      <dc:creator>Leonan Viana</dc:creator>
      <pubDate>Mon, 08 Apr 2024 02:25:56 +0000</pubDate>
      <link>https://dev.to/leonanviana/adicionando-headers-no-nginx-com-ansible-42p0</link>
      <guid>https://dev.to/leonanviana/adicionando-headers-no-nginx-com-ansible-42p0</guid>
      <description>&lt;p&gt;Como automatizar a inserção de security headers no Nginx usando Ansible — backup, remoção de duplicatas, incremento via AWK e restart em múltiplos hosts de uma vez.&lt;/p&gt;

&lt;p&gt;Num projeto recente eu precisava adicionar security headers no Nginx de vários hosts ao mesmo tempo. Fazer isso na mão, host por host, estava fora de questão — qualquer mudança futura viraria uma dor de cabeça. Resolvi automatizar com Ansible, e foi mais simples do que esperava.&lt;/p&gt;

&lt;p&gt;O repositório tá disponível no GitHub caso queira já ir direto ao código.&lt;/p&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://assets.dev.to/assets/github-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/leonanviana" rel="noopener noreferrer"&gt;
        leonanviana
      &lt;/a&gt; / &lt;a href="https://github.com/leonanviana/add-headers-nginx" rel="noopener noreferrer"&gt;
        add-headers-nginx
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;
&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;add-headers-nginx&lt;/h1&gt;
&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;🇧🇷 &lt;a href="https://github.com/leonanviana/add-headers-nginx#portugu%C3%AAs" rel="noopener noreferrer"&gt;Português&lt;/a&gt; | 🇺🇸 &lt;a href="https://github.com/leonanviana/add-headers-nginx#english" rel="noopener noreferrer"&gt;English&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;a rel="noopener noreferrer nofollow" href="https://camo.githubusercontent.com/278a3e7e3e82c210e5ffc2710eafb402dd4f8a4b0b7cc37361b43c42875434f6/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f6e67696e782d2532333030393633392e7376673f7374796c653d666f722d7468652d6261646765266c6f676f3d6e67696e78266c6f676f436f6c6f723d7768697465"&gt;&lt;img src="https://camo.githubusercontent.com/278a3e7e3e82c210e5ffc2710eafb402dd4f8a4b0b7cc37361b43c42875434f6/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f6e67696e782d2532333030393633392e7376673f7374796c653d666f722d7468652d6261646765266c6f676f3d6e67696e78266c6f676f436f6c6f723d7768697465" alt="Nginx"&gt;&lt;/a&gt;
&lt;a rel="noopener noreferrer nofollow" href="https://camo.githubusercontent.com/7fc743422d30abe8f2ffeb93521f588ea209f2fc5faaf861dbb70a56380986e7/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f416e7369626c652d3030303030303f7374796c653d666f722d7468652d6261646765266c6f676f3d616e7369626c65266c6f676f436f6c6f723d7768697465"&gt;&lt;img src="https://camo.githubusercontent.com/7fc743422d30abe8f2ffeb93521f588ea209f2fc5faaf861dbb70a56380986e7/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f416e7369626c652d3030303030303f7374796c653d666f722d7468652d6261646765266c6f676f3d616e7369626c65266c6f676f436f6c6f723d7768697465" alt="Ansible"&gt;&lt;/a&gt;
&lt;a rel="noopener noreferrer nofollow" href="https://camo.githubusercontent.com/13ecf8308dd447edcef2bafd36de23b6539b35f24c18be96fd53d12241ec7db0/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f4c696e75782d4643433632343f7374796c653d666f722d7468652d6261646765266c6f676f3d6c696e7578266c6f676f436f6c6f723d626c61636b"&gt;&lt;img src="https://camo.githubusercontent.com/13ecf8308dd447edcef2bafd36de23b6539b35f24c18be96fd53d12241ec7db0/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f4c696e75782d4643433632343f7374796c653d666f722d7468652d6261646765266c6f676f3d6c696e7578266c6f676f436f6c6f723d626c61636b" alt="Linux"&gt;&lt;/a&gt;
&lt;a rel="noopener noreferrer nofollow" href="https://camo.githubusercontent.com/4f745a63a32ff4a0acb50d7491c55d0b26ec04cd57fb798c44be59262b350e5a/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f4465765365634f70732d3030303030303f7374796c653d666f722d7468652d6261646765266c6f676f3d6861636b746865626f78266c6f676f436f6c6f723d7768697465"&gt;&lt;img src="https://camo.githubusercontent.com/4f745a63a32ff4a0acb50d7491c55d0b26ec04cd57fb798c44be59262b350e5a/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f4465765365634f70732d3030303030303f7374796c653d666f722d7468652d6261646765266c6f676f3d6861636b746865626f78266c6f676f436f6c6f723d7768697465" alt="Security"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Português&lt;/h2&gt;
&lt;/div&gt;
&lt;p&gt;Playbooks Ansible para adição e remoção de security headers no Nginx de forma automatizada e idempotente.&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h3 class="heading-element"&gt;Por que security headers?&lt;/h3&gt;
&lt;/div&gt;
&lt;p&gt;Security headers protegem aplicações web contra ataques como XSS, clickjacking e MIME sniffing. São exigidos em auditorias de segurança e compliance (OWASP, PCI-DSS).&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h3 class="heading-element"&gt;Estrutura&lt;/h3&gt;

&lt;/div&gt;
&lt;div class="snippet-clipboard-content notranslate position-relative overflow-auto"&gt;&lt;pre class="notranslate"&gt;&lt;code&gt;add-headers-nginx/
├── nginx.conf                      # Exemplo de configuração Nginx
└── playbooks/
    ├── headers_nginx.txt           # Headers a serem inseridos
    ├── add_headers_nginx.yml       # Playbook para adicionar headers
    └── remove_headers_nginx.yml    # Playbook para remover headers
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div class="markdown-heading"&gt;
&lt;h3 class="heading-element"&gt;Pré-requisitos&lt;/h3&gt;

&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;Ansible instalado&lt;/li&gt;
&lt;li&gt;Acesso sudo ao host alvo&lt;/li&gt;
&lt;li&gt;Nginx instalado&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="markdown-heading"&gt;
&lt;h3 class="heading-element"&gt;Como usar&lt;/h3&gt;

&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;1. Configure os paths no playbook conforme seu ambiente:&lt;/strong&gt;&lt;/p&gt;
&lt;div class="highlight highlight-source-yaml notranslate position-relative overflow-auto js-code-highlight"&gt;
&lt;pre&gt;&lt;span class="pl-ent"&gt;vars&lt;/span&gt;:
  &lt;span class="pl-ent"&gt;PATH_ADD_HEADERS_TXT&lt;/span&gt;: &lt;span class="pl-s"&gt;/seu/path/playbooks/headers_nginx.txt&lt;/span&gt;
  &lt;span class="pl-ent"&gt;PATH_NGINX_CONF&lt;/span&gt;: &lt;span class="pl-s"&gt;/etc/nginx/nginx.conf&lt;/span&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;2. Adicionar headers:&lt;/strong&gt;&lt;/p&gt;
&lt;div class="highlight highlight-source-shell notranslate position-relative overflow-auto js-code-highlight"&gt;
&lt;pre&gt;ansible-playbook playbooks/add_headers_nginx.yml&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;3. Remover headers:&lt;/strong&gt;&lt;/p&gt;
&lt;div class="highlight highlight-source-shell notranslate position-relative overflow-auto js-code-highlight"&gt;
&lt;pre&gt;ansible-playbook playbooks/remove_headers_nginx.yml&lt;/pre&gt;

&lt;/div&gt;
&lt;div class="markdown-heading"&gt;
&lt;h3 class="heading-element"&gt;O que o playbook faz&lt;/h3&gt;

&lt;/div&gt;
&lt;ol&gt;
&lt;li&gt;Faz backup do &lt;code&gt;nginx.conf&lt;/code&gt; (duas cópias de segurança)&lt;/li&gt;
&lt;li&gt;Remove headers existentes para evitar duplicação&lt;/li&gt;
&lt;li&gt;Injeta os novos headers após a diretiva &lt;code&gt;gzip_vary on&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Mantém…&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/leonanviana/add-headers-nginx" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;





&lt;h2&gt;
  
  
  O problema
&lt;/h2&gt;

&lt;p&gt;A aplicação web precisava de uma lista de headers específicos para não quebrar em certas páginas:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;Strict-Transport-Security&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Content-Security-Policy&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;X-XSS-Protection&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;X-Frame-Options&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;X-Content-Type-Options&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Referrer-Policy&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Permissions-Policy&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Com muitos hosts e a possibilidade de ajustes frequentes nos valores, qualquer processo manual ia escalar mal. Ansible resolve isso.&lt;/p&gt;




&lt;h2&gt;
  
  
  Etapa 1 — arquivo de headers
&lt;/h2&gt;

&lt;p&gt;Crio um arquivo &lt;code&gt;headers_nginx.txt&lt;/code&gt; com todas as diretivas &lt;code&gt;add_header&lt;/code&gt; que quero inserir:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight nginx"&gt;&lt;code&gt;&lt;span class="k"&gt;add_header&lt;/span&gt; &lt;span class="s"&gt;Strict-Transport-Security&lt;/span&gt; &lt;span class="s"&gt;'max-age=63072000&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;includeSubDomains&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;preload'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;add_header&lt;/span&gt; &lt;span class="s"&gt;Content-Security-Policy&lt;/span&gt; &lt;span class="s"&gt;"connect-src&lt;/span&gt; &lt;span class="s"&gt;'self'&lt;/span&gt; &lt;span class="s"&gt;www.google-analytics.com&lt;/span&gt; &lt;span class="s"&gt;..."&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;add_header&lt;/span&gt; &lt;span class="s"&gt;X-XSS-Protection&lt;/span&gt; &lt;span class="s"&gt;"1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;mode=block"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;add_header&lt;/span&gt; &lt;span class="s"&gt;X-Frame-Options&lt;/span&gt; &lt;span class="s"&gt;DENY&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;add_header&lt;/span&gt; &lt;span class="s"&gt;X-Content-Type-Options&lt;/span&gt; &lt;span class="s"&gt;nosniff&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;add_header&lt;/span&gt; &lt;span class="s"&gt;Referrer-Policy&lt;/span&gt; &lt;span class="s"&gt;"strict-origin"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;add_header&lt;/span&gt; &lt;span class="s"&gt;Permissions-Policy&lt;/span&gt; &lt;span class="s"&gt;"geolocation=(self),midi=(self),..."&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Esse arquivo é a fonte da verdade. Quando precisar mudar um header, edito aqui e rodo a playbook de novo — sem tocar no &lt;code&gt;nginx.conf&lt;/code&gt; manualmente.&lt;/p&gt;




&lt;h2&gt;
  
  
  Etapa 2 — a Playbook
&lt;/h2&gt;

&lt;p&gt;A playbook tem seis jobs em sequência:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Backup&lt;/strong&gt; do &lt;code&gt;nginx.conf&lt;/code&gt; — duas formas, pelo &lt;code&gt;fetch&lt;/code&gt; e pelo &lt;code&gt;cp&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Remoção&lt;/strong&gt; das linhas &lt;code&gt;add_header&lt;/code&gt; existentes — evita duplicidade&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Remoção&lt;/strong&gt; das linhas &lt;code&gt;add_header&lt;/code&gt; comentadas com &lt;code&gt;#&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Limpeza&lt;/strong&gt; de linhas em branco que ficam após a remoção&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Inserção&lt;/strong&gt; dos novos headers via AWK&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Restart&lt;/strong&gt; do Nginx
&lt;/li&gt;
&lt;/ol&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;Adicionar Headers no Nginx&lt;/span&gt;
  &lt;span class="na"&gt;hosts&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;localhost&lt;/span&gt;
  &lt;span class="na"&gt;become&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;yes&lt;/span&gt;
  &lt;span class="na"&gt;become_method&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;sudo&lt;/span&gt;

  &lt;span class="na"&gt;vars&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;PATH_ADD_HEADERS_TXT&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/Users/leonanviana/Repos/add_headers_nginx/playbooks/headers_nginx.txt&lt;/span&gt;
    &lt;span class="na"&gt;PATH_NGINX_CONF&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/opt/homebrew/etc/nginx/nginx.conf&lt;/span&gt;

  &lt;span class="na"&gt;tasks&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;Fazer backup do nginx.conf via fetch&lt;/span&gt;
      &lt;span class="na"&gt;fetch&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;src&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/opt/homebrew/etc/nginx/nginx.conf&lt;/span&gt;
        &lt;span class="na"&gt;dest&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/var/tmp/&lt;/span&gt;
        &lt;span class="na"&gt;flat&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;yes&lt;/span&gt;
      &lt;span class="na"&gt;become&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&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;Fazer backup do nginx.conf via cp&lt;/span&gt;
      &lt;span class="na"&gt;shell&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;cp "{{ PATH_NGINX_CONF }}" /var/tmp/default_nginx_bkp&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;Remover linhas com add_header&lt;/span&gt;
      &lt;span class="na"&gt;lineinfile&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&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;PATH_NGINX_CONF&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;}}"&lt;/span&gt;
        &lt;span class="na"&gt;state&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;absent&lt;/span&gt;
        &lt;span class="na"&gt;regexp&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;^(\s*add_header\s.*;)$'&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;Remover linhas add_header comentadas com&lt;/span&gt; &lt;span class="c1"&gt;#&lt;/span&gt;
      &lt;span class="na"&gt;lineinfile&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&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;PATH_NGINX_CONF&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;}}"&lt;/span&gt;
        &lt;span class="na"&gt;state&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;absent&lt;/span&gt;
        &lt;span class="na"&gt;regexp&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;^(\s*#add_header\s.*;)$'&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;Remover linhas em branco&lt;/span&gt;
      &lt;span class="na"&gt;replace&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&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;PATH_NGINX_CONF&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;}}"&lt;/span&gt;
        &lt;span class="na"&gt;regexp&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;^\s*$'&lt;/span&gt;
        &lt;span class="na"&gt;replace&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&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;Inserir headers via AWK&lt;/span&gt;
      &lt;span class="na"&gt;shell&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="s"&gt;awk '/gzip_vary on;/ {print; system("cat \"{{ PATH_ADD_HEADERS_TXT }}\""); next} 1'&lt;/span&gt;
        &lt;span class="s"&gt;"{{ PATH_NGINX_CONF }}" &amp;gt; tmpfile &amp;amp;&amp;amp; mv tmpfile "{{ PATH_NGINX_CONF }}"&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;Restart Nginx&lt;/span&gt;
      &lt;span class="na"&gt;service&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;nginx&lt;/span&gt;
        &lt;span class="na"&gt;state&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;restarted&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Como o AWK funciona aqui
&lt;/h2&gt;

&lt;p&gt;O job de inserção merece atenção. Ele usa &lt;code&gt;awk&lt;/code&gt; para localizar a string &lt;code&gt;gzip_vary on;&lt;/code&gt; dentro do &lt;code&gt;nginx.conf&lt;/code&gt; como ponto de ancoragem. Quando encontra essa linha, imprime ela e logo em seguida injeta todo o conteúdo do &lt;code&gt;headers_nginx.txt&lt;/code&gt;. O resultado vai para um arquivo temporário, que substitui o original se tudo correr bem.&lt;/p&gt;

&lt;p&gt;Você pode trocar &lt;code&gt;gzip_vary on;&lt;/code&gt; por qualquer outra string que já exista no seu &lt;code&gt;nginx.conf&lt;/code&gt; — o comportamento é o mesmo.&lt;/p&gt;




&lt;h2&gt;
  
  
  Fluxo de execução
&lt;/h2&gt;

&lt;p&gt;Com tudo configurado, o fluxo fica assim:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;code&gt;nginx.conf&lt;/code&gt; sem nenhum &lt;code&gt;add_header&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Playbook roda — backup, limpeza, inserção, restart&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;nginx.conf&lt;/code&gt; com todos os headers no lugar certo&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Se precisar remover os headers depois, criei uma segunda playbook específica para isso — sem precisar editar o arquivo na mão.&lt;/p&gt;




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

&lt;p&gt;Parece simples porque é. O ganho real aparece quando você tem dezenas de hosts: um único &lt;code&gt;ansible-playbook&lt;/code&gt; propaga a mudança em todos ao mesmo tempo, com backup automático e sem risco de duplicidade.&lt;/p&gt;

&lt;p&gt;O código tá no GitHub — pull requests são bem-vindos.&lt;/p&gt;

</description>
      <category>devops</category>
      <category>ansible</category>
      <category>automation</category>
      <category>nginx</category>
    </item>
  </channel>
</rss>
