<?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: Kevin Gomes</title>
    <description>The latest articles on DEV Community by Kevin Gomes (@kevinfinalboss).</description>
    <link>https://dev.to/kevinfinalboss</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.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F986313%2Fb40b04df-97e9-481c-ad8e-dd730a70de52.jpeg</url>
      <title>DEV Community: Kevin Gomes</title>
      <link>https://dev.to/kevinfinalboss</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/kevinfinalboss"/>
    <language>en</language>
    <item>
      <title>Golden Images com Packer + Ansible: como automatizamos provisionamento e centralizamos configurações</title>
      <dc:creator>Kevin Gomes</dc:creator>
      <pubDate>Thu, 09 Jul 2026 17:43:02 +0000</pubDate>
      <link>https://dev.to/kevinfinalboss/golden-images-com-packer-ansible-como-automatizamos-provisionamento-e-centralizamos-configuracoes-4689</link>
      <guid>https://dev.to/kevinfinalboss/golden-images-com-packer-ansible-como-automatizamos-provisionamento-e-centralizamos-configuracoes-4689</guid>
      <description>&lt;h2&gt;
  
  
  O problema
&lt;/h2&gt;

&lt;p&gt;Toda vez que uma máquina nova precisa subir e o processo depende de alguém lembrar os passos certos, você já tem um problema. Foi exatamente isso que eu vivi trabalhando com EC2 dedicadas: cada instância nova virava uma pequena novela. Subir a máquina, instalar pacote por pacote na mão, ajustar configuração, esquecer algum passo, documentar (às vezes) num lugar que ninguém revisita depois, e torcer para lembrar de fazer tudo igual da próxima vez.&lt;/p&gt;

&lt;p&gt;Isso não é só perda de tempo, é falta de padrão. Duas máquinas que deveriam ser idênticas nunca são de fato idênticas, porque o processo de criação delas nunca foi de fato o mesmo. E quando algo dá errado, não tem como saber com certeza o que está instalado e configurado ali sem entrar na máquina e investigar manualmente.&lt;/p&gt;

&lt;p&gt;A pergunta que me guiou foi simples: como transformar "instalar uma máquina" em algo repetível, versionado e auditável, sem depender de alguém lembrar os passos certos?&lt;/p&gt;

&lt;h2&gt;
  
  
  A solução: Packer + Ansible-local
&lt;/h2&gt;

&lt;p&gt;A combinação que resolveu isso pra mim foi:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Packer para orquestrar o build da imagem (AMI)&lt;/li&gt;
&lt;li&gt;Ansible rodando em modo local (&lt;code&gt;ansible-local&lt;/code&gt;) dentro da própria instância de build, aplicando os playbooks diretamente na máquina, sem precisar de um control node externo fazendo SSH&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;O fluxo básico fica assim:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Packer sobe uma instância temporária a partir de uma AMI base&lt;/li&gt;
&lt;li&gt;Copia os playbooks Ansible para dentro da instância&lt;/li&gt;
&lt;li&gt;Executa o Ansible localmente, aplicando os roles definidos&lt;/li&gt;
&lt;li&gt;Roda os passos de limpeza (remove chaves temporárias, limpa logs, zera histórico de bash, remove credenciais de build)&lt;/li&gt;
&lt;li&gt;Congela tudo numa AMI nova, versionada&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Um exemplo simplificado do template, em HCL2:&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;source&lt;/span&gt; &lt;span class="s2"&gt;"amazon-ebs"&lt;/span&gt; &lt;span class="s2"&gt;"app_base"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;ami_name&lt;/span&gt;      &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"app-golden-{{timestamp}}"&lt;/span&gt;
  &lt;span class="nx"&gt;instance_type&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"t3a.large"&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;"sa-east-1"&lt;/span&gt;
  &lt;span class="nx"&gt;source_ami_filter&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;filters&lt;/span&gt; &lt;span class="p"&gt;=&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;"al2023-ami-*-arm64"&lt;/span&gt;
      &lt;span class="nx"&gt;root-device-type&lt;/span&gt;    &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"ebs"&lt;/span&gt;
      &lt;span class="nx"&gt;virtualization-type&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"hvm"&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="nx"&gt;owners&lt;/span&gt;      &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"amazon"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="nx"&gt;most_recent&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;span class="nx"&gt;ssh_username&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"ec2-user"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;build&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;sources&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"source.amazon-ebs.app_base"&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;"shell"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;inline&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"sudo mkdir -p /etc/ansible/facts.d"&lt;/span&gt;&lt;span class="p"&gt;]&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;"ansible-local"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;playbook_file&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"playbooks/site.yml"&lt;/span&gt;
    &lt;span class="nx"&gt;role_paths&lt;/span&gt;    &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"roles/hardening"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"roles/app-setup"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"roles/config-sync"&lt;/span&gt;&lt;span class="p"&gt;]&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;"shell"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;script&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"scripts/cleanup.sh"&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;O ponto central é que o Ansible não toca a máquina de fora, ele roda dentro dela, como parte do build. Isso elimina a dependência de conectividade SSH externa durante o processo e faz o build funcionar de forma consistente em qualquer conta ou VPC, já que não precisa abrir rota de rede para um control node.&lt;/p&gt;

&lt;h2&gt;
  
  
  Um detalhe que fez diferença: Packer se conectando via SSM
&lt;/h2&gt;

&lt;p&gt;Um ponto que vale a pena registrar é que o builder &lt;code&gt;amazon-ebs&lt;/code&gt; do Packer não depende só de SSH tradicional para se conectar na instância de build. Existe a opção de usar o Session Manager (SSM) como interface de conexão, configurando &lt;code&gt;ssh_interface = "session_manager"&lt;/code&gt; no builder.&lt;/p&gt;

&lt;p&gt;Na prática, o Packer sobe um túnel via SSM até a porta de SSH da instância (por padrão 22) e conversa com ela por dentro desse túnel, sem precisar que a instância tenha uma rota de rede pública nem uma Security Group liberando a porta 22 para o seu IP. Para isso funcionar é necessário:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Anexar um Instance Profile válido com permissão de Systems Manager na instância de build (para o SSM Agent conseguir abrir a sessão)&lt;/li&gt;
&lt;li&gt;Ter o session-manager-plugin instalado na máquina local que está rodando o Packer&lt;/li&gt;
&lt;li&gt;Manter &lt;code&gt;communicator = "ssh"&lt;/code&gt;, já que o SSM aqui só substitui a forma como o túnel é estabelecido, o comunicador continua sendo SSH por baixo dos panos&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Um exemplo de builder usando esse modelo:&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;source&lt;/span&gt; &lt;span class="s2"&gt;"amazon-ebs"&lt;/span&gt; &lt;span class="s2"&gt;"app_base_ssm"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;ami_name&lt;/span&gt;             &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"app-golden-{{timestamp}}"&lt;/span&gt;
  &lt;span class="nx"&gt;instance_type&lt;/span&gt;        &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"m6a.large"&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;"sa-east-1"&lt;/span&gt;
  &lt;span class="nx"&gt;communicator&lt;/span&gt;         &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"ssh"&lt;/span&gt;
  &lt;span class="nx"&gt;ssh_username&lt;/span&gt;         &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"ec2-user"&lt;/span&gt;
  &lt;span class="nx"&gt;ssh_interface&lt;/span&gt;        &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"session_manager"&lt;/span&gt;
  &lt;span class="nx"&gt;iam_instance_profile&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"packer-ssm-instance-profile"&lt;/span&gt;
  &lt;span class="nx"&gt;source_ami_filter&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;filters&lt;/span&gt; &lt;span class="p"&gt;=&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;"al2023-ami-*-arm64"&lt;/span&gt;
      &lt;span class="nx"&gt;root-device-type&lt;/span&gt;    &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"ebs"&lt;/span&gt;
      &lt;span class="nx"&gt;virtualization-type&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"hvm"&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="nx"&gt;owners&lt;/span&gt;      &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"amazon"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="nx"&gt;most_recent&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;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Isso resolveu um incômodo real: builds de imagem deixaram de depender de sub-redes com saída para a internet ou de Security Groups abrindo porta 22 pontualmente durante o processo. A instância de build fica isolada, sem exposição de SSH, e o Packer se conecta por dentro da rede da AWS usando as mesmas permissões IAM que já governam o resto do ambiente. Menos superfície de ataque durante o processo de build, que é justamente o momento em que a máquina está mais "aberta" para receber comandos externos.&lt;/p&gt;

&lt;h2&gt;
  
  
  O pulo do gato: configuração fora da imagem
&lt;/h2&gt;

&lt;p&gt;Aqui está a parte que, na minha visão, separa "automatizei o install" de "resolvi o problema de verdade": a imagem não carrega a configuração da aplicação embutida.&lt;/p&gt;

&lt;p&gt;O Packer builda a AMI com tudo que é estrutural: pacotes, hardening, usuários, estrutura de diretórios, serviços systemd. Mas a configuração específica da aplicação (endpoints, parâmetros de conexão, feature flags, e por aí vai) fica no AWS Systems Manager Parameter Store, como fonte única de verdade.&lt;/p&gt;

&lt;p&gt;Quando a instância sobe de fato em produção, fora do contexto de build, um serviço systemd dispara na inicialização, busca os parâmetros correspondentes ao ambiente/aplicação no Parameter Store e aplica a configuração local:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ini"&gt;&lt;code&gt;&lt;span class="nn"&gt;[Unit]&lt;/span&gt;
&lt;span class="py"&gt;Description&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;Sync config from Parameter Store&lt;/span&gt;
&lt;span class="py"&gt;After&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;network-online.target&lt;/span&gt;
&lt;span class="py"&gt;Wants&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;network-online.target&lt;/span&gt;

&lt;span class="nn"&gt;[Service]&lt;/span&gt;
&lt;span class="py"&gt;Type&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;oneshot&lt;/span&gt;
&lt;span class="py"&gt;ExecStart&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;/opt/app/bin/config-sync.sh&lt;/span&gt;
&lt;span class="py"&gt;RemainAfterExit&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;yes&lt;/span&gt;

&lt;span class="nn"&gt;[Install]&lt;/span&gt;
&lt;span class="py"&gt;WantedBy&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;multi-user.target&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/usr/bin/env bash&lt;/span&gt;
&lt;span class="nb"&gt;set&lt;/span&gt; &lt;span class="nt"&gt;-euo&lt;/span&gt; pipefail

&lt;span class="nv"&gt;APP_ENV&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;cat&lt;/span&gt; /etc/app/environment&lt;span class="si"&gt;)&lt;/span&gt;
&lt;span class="nv"&gt;PREFIX&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"/app/&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;APP_ENV&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/config"&lt;/span&gt;

aws ssm get-parameters-by-path &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--path&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;PREFIX&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--recursive&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--with-decryption&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--query&lt;/span&gt; &lt;span class="s2"&gt;"Parameters[*].[Name,Value]"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--output&lt;/span&gt; text | &lt;span class="k"&gt;while &lt;/span&gt;&lt;span class="nb"&gt;read&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; name value&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do
    &lt;/span&gt;&lt;span class="nv"&gt;key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;basename&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;name&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;
    &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;key&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;=&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;value&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; /etc/app/app.env
  &lt;span class="k"&gt;done

&lt;/span&gt;systemctl restart app.service
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Isso separa claramente duas responsabilidades:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Packer/Ansible cuidam do "o quê está instalado" (imutável, versionado, buildado uma vez)&lt;/li&gt;
&lt;li&gt;Parameter Store cuida do "como está configurado agora" (mutável, centralizado, com revisão de histórico nativa do SSM)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Na prática, isso trouxe ganhos que não eram o objetivo inicial, mas viraram os mais valiosos:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Rastreabilidade de configuração: todo parâmetro tem histórico de mudança no próprio Parameter Store, então dá pra saber quem mudou o quê e quando, sem precisar caçar em log de aplicação&lt;/li&gt;
&lt;li&gt;Configuração centralizada por ambiente: a mesma AMI serve para DEV, UAT e PROD, o que muda é só o path que ela lê no Parameter Store&lt;/li&gt;
&lt;li&gt;Redeploy de configuração sem rebuild de imagem: ajustar um parâmetro não exige gerar uma AMI nova, só reiniciar o serviço de sync (ou deixar o próximo boot pegar)&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  O efeito colateral bom: Disaster Recovery
&lt;/h2&gt;

&lt;p&gt;Esse foi o ponto que só ficou óbvio depois que a arquitetura já estava rodando: uma vez que você tem a golden AMI e a configuração centralizada e desacoplada no Parameter Store, replicar a aplicação para outra AZ ou outra região vira um problema de infraestrutura, não de aplicação.&lt;/p&gt;

&lt;p&gt;O racional é direto:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;A AMI já tem tudo que a aplicação precisa para rodar (pacotes, hardening, serviços)&lt;/li&gt;
&lt;li&gt;A configuração não está embutida na imagem, está no Parameter Store, que também pode ser replicado ou lido cross-region, dependendo do desenho&lt;/li&gt;
&lt;li&gt;Logo, subir a aplicação numa nova AZ ou região é: copiar a AMI para o destino, subir a instância, deixar o &lt;code&gt;config-sync&lt;/code&gt; buscar a configuração certa&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Isso transformou o cenário de DR de "vamos ter que reconstruir isso na mão e torcer para lembrar de tudo" para "isso já é testado e replicável, porque é o mesmo processo usado todo dia para gerar imagem nova". Fizemos inclusive simulações de falha de AZ cobrindo essa camada, e a parte provisionada via Packer foi de longe a mais previsível de recuperar, justamente por não depender de passos manuais.&lt;/p&gt;

&lt;h2&gt;
  
  
  Hardening: CIS Level 1/2 embutido no build
&lt;/h2&gt;

&lt;p&gt;Outro ganho direto de ter o Ansible rodando como parte do processo de build: hardening deixou de ser uma tarefa "depois que a máquina já está no ar" e virou parte do próprio pipeline de criação da imagem.&lt;/p&gt;

&lt;p&gt;Os controles de CIS Benchmark, com Level 1 como baseline e alguns controles de Level 2 aplicados pontualmente onde fazia sentido, viraram uma role Ansible própria, aplicada em todo build:&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;hardening&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;connection&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;local&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="na"&gt;roles&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;role&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;cis-benchmark&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;cis_level&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;
        &lt;span class="na"&gt;cis_al2023_specific&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;Isso significa que toda AMI nova já nasce hardenizada, sem depender de rodar um scanner depois e corrigir desvio por desvio. O hardening é parte do artefato, não um processo paralelo.&lt;/p&gt;

&lt;h2&gt;
  
  
  Migração Amazon Linux 2 → Amazon Linux 2023
&lt;/h2&gt;

&lt;p&gt;A mesma estrutura de Packer + Ansible foi o que viabilizou a migração de AL2 para AL2023 sem drama. Como o pipeline já era declarativo, roles Ansible descrevendo o estado desejado da máquina, e não passos manuais amarrados a uma distro específica, trocar a AMI base de origem e ajustar os pontos de incompatibilidade (principalmente em torno de pacotes renomeados e mudança de cgroups) foi um trabalho localizado, não um retrabalho do zero.&lt;/p&gt;

&lt;p&gt;Isso reforça um ponto que vale destacar: o valor de ter Packer + Ansible não é só economizar tempo hoje, é ter a distro e a versão do sistema operacional como parâmetro do pipeline, não como algo enraizado no processo manual de alguém.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Antes&lt;/th&gt;
&lt;th&gt;Depois&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Instalação manual, sem processo padronizado&lt;/td&gt;
&lt;td&gt;Build automatizado e versionado via Packer&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Conexão de build dependendo de SSH exposto&lt;/td&gt;
&lt;td&gt;Build via SSM, sem exposição de porta 22&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Configuração embutida/ad-hoc na máquina&lt;/td&gt;
&lt;td&gt;Configuração centralizada no Parameter Store, aplicada no boot&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;DR dependente de reconstrução manual&lt;/td&gt;
&lt;td&gt;DR via replicação de AMI + config-sync automático&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Hardening aplicado depois, sob demanda&lt;/td&gt;
&lt;td&gt;Hardening (CIS) embutido no build&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Migração de distro = retrabalho grande&lt;/td&gt;
&lt;td&gt;Migração de distro = ajuste pontual no pipeline&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;O maior aprendizado prático desse case não foi técnico, foi de desenho: a decisão de não colocar a configuração de aplicação dentro da imagem foi o que abriu espaço para os ganhos de DR e centralização. Se a configuração estivesse embutida na AMI, cada mudança de parâmetro exigiria rebuild, e a imagem deixaria de ser "golden" para virar só mais um artefato acoplado a um ambiente específico.&lt;/p&gt;

&lt;p&gt;Packer resolve o "como eu construo isso de forma repetível". Ansible-local resolve o "como eu aplico o estado desejado sem depender de infraestrutura externa durante o build". A conexão via SSM resolve o "como eu faço isso sem expor a instância de build". E o Parameter Store resolve o "como eu mantenho isso configurável sem quebrar a imutabilidade da imagem". Separados, cada um resolve uma fatia do problema. Juntos, o resultado foi bem maior que a soma das partes.&lt;/p&gt;

</description>
      <category>aws</category>
      <category>packer</category>
      <category>automation</category>
    </item>
  </channel>
</rss>
