Sessão 003 — CloudFormation: changesets, drift detection e stack policies
Duração estimada: 60 minutos
Pré-requisitos: session-002 — CloudFormation: stacks, templates, parameters, outputs, Ref/GetAtt
Objetivo
Ao final, você conseguirá criar e revisar um changeset antes de aplicar, detectar drift em recursos fora do ciclo CloudFormation, e configurar uma stack policy para proteger recursos críticos de substituição acidental.
Contexto
[CONSENSO] O risco principal em operações de IaC em produção não é o deploy inicial — é a atualização. Um aws cloudformation deploy em produção pode deletar um banco de dados, substituir um security group com todas as suas regras, ou trocar o nome de uma fila SQS (perdendo mensagens em trânsito) se o engenheiro não entender o comportamento de update de cada tipo de recurso.
Esta sessão cobre as três ferramentas que existem para mitigar esse risco: changesets (para ver o que vai mudar antes de mudar), drift detection (para saber quando o mundo real divergiu do template), e stack policies (para impedir que certas mudanças aconteçam mesmo que sejam solicitadas).
[FATO] Changesets e stack policies são recursos do próprio CloudFormation. Drift detection é uma operação assíncrona que pode levar minutos em stacks grandes e não é executada automaticamente — você precisa acionar explicitamente.
Conceitos principais
1. Update behaviors — o que pode acontecer com um recurso ao atualizar
Antes de entender changesets, é preciso entender o que o CloudFormation pode fazer com um recurso durante um update. Cada propriedade de cada tipo de recurso tem um "update behavior" documentado.
[FATO] Existem três categorias:
Update with No Interruption
→ CloudFormation atualiza o recurso sem interromper a operação
→ O recurso mantém seu physical ID
→ Ex: mudar tags de um EC2, mudar descrição de um Security Group
Update with Some Interruption
→ CloudFormation atualiza o recurso com interrupção temporária
→ O recurso mantém seu physical ID
→ Ex: mudar o tipo de instância de um EC2 (reboot necessário)
Replacement
→ CloudFormation cria um NOVO recurso, atualiza as referências,
e deleta o recurso antigo
→ O recurso recebe um NOVO physical ID
→ Ex: mudar o nome de um bucket S3, mudar o engine de um RDS,
mudar a VPC de um Security Group
Por que Replacement é o mais perigoso:
Estado anterior: Após Replacement:
AppBucket (bucket-prod-123) → AppBucket (bucket-prod-456) [novo]
bucket-prod-123 [deletado]
Se DeletionPolicy = Delete: todos os objetos são perdidos
Se DeletionPolicy = Retain: bucket antigo permanece órfão na conta
[FATO] Para identificar o update behavior de uma propriedade específica, consulte a documentação do recurso na coluna "Update requires" de cada propriedade. Não existe regra universal — depende do serviço e da propriedade.
Diagrama dos update behaviors:
Update da stack
│
├─ Propriedade com "No Interruption"
│ └─ Recurso atualizado in-place, sem impacto
│
├─ Propriedade com "Some Interruption"
│ └─ Recurso reiniciado, downtime mínimo
│
└─ Propriedade com "Replacement"
└─ Novo recurso criado → referências atualizadas → antigo deletado
⚠️ Physical ID muda, dados podem ser perdidos
2. Changesets — ver antes de fazer
Um changeset é um plano de execução que o CloudFormation calcula sem aplicar nada. Você cria, revisa e só então decide se executa.
Fluxo completo via CLI:
# 1. Criar o changeset (não aplica nada)
aws cloudformation create-change-set \
--stack-name minha-stack \
--change-set-name meu-changeset-$(date +%Y%m%d%H%M) \
--template-body file://template.yaml \
--parameters ParameterKey=Env,ParameterValue=prod \
--capabilities CAPABILITY_NAMED_IAM
# 2. Aguardar o changeset ficar pronto (status: CREATE_COMPLETE)
aws cloudformation wait change-set-create-complete \
--stack-name minha-stack \
--change-set-name meu-changeset-20260506
# 3. Revisar o changeset
aws cloudformation describe-change-set \
--stack-name minha-stack \
--change-set-name meu-changeset-20260506 \
--query 'Changes[*].ResourceChange.[Action,LogicalResourceId,ResourceType,Replacement]' \
--output table
# 4a. Executar (se aprovado)
aws cloudformation execute-change-set \
--stack-name minha-stack \
--change-set-name meu-changeset-20260506
# 4b. Cancelar (se não aprovado)
aws cloudformation delete-change-set \
--stack-name minha-stack \
--change-set-name meu-changeset-20260506
Lendo o output do describe-change-set:
O campo mais importante é Changes[].ResourceChange. Cada item tem:
{
"Action": "Modify", // Add | Modify | Remove
"LogicalResourceId": "AppDB",
"ResourceType": "AWS::RDS::DBInstance",
"Replacement": "True", // True | False | Conditional
"Scope": ["Properties"],
"Details": [
{
"Target": {
"Attribute": "Properties",
"Name": "DBInstanceClass",
"RequiresRecreation": "Always" // Never | Conditional | Always
},
"ChangeSource": "DirectModification"
}
]
}
O campo Replacement e seus valores:
False → nenhuma propriedade modificada exige substituição
True → pelo menos uma propriedade exige substituição (certeza)
Conditional → substituição depende de outras condições avaliadas em runtime
(ex: quando uma propriedade muda para um valor específico)
[FATO] Conditional é o mais traiçoeiro — o CloudFormation não consegue determinar em tempo de planejamento se haverá substituição. Trate Conditional como True ao revisar changesets em produção.
Comparando aws cloudformation deploy vs changeset manual:
aws cloudformation deploy
└── cria changeset internamente
└── executa automaticamente
└── você não vê o changeset antes da execução
└── --no-execute-changeset cria o changeset mas NÃO executa
aws cloudformation create-change-set + execute-change-set
└── fluxo explícito — você controla cada etapa
└── recomendado em pipelines de produção com aprovação humana
Múltiplos changesets numa stack:
[FATO] Uma stack pode ter múltiplos changesets pendentes simultaneamente, mas só um pode ser executado — ao executar, o CloudFormation deleta todos os outros changesets da stack automaticamente (eles ficam obsoletos após a execução).
3. Drift detection — quando o mundo real diverge do template
Drift acontece quando alguém modifica um recurso fora do CloudFormation — via console, CLI direta, ou outra ferramenta. O template diz uma coisa, o recurso está em outro estado.
Acionando drift detection:
# Detectar drift em toda a stack (operação assíncrona)
aws cloudformation detect-stack-drift \
--stack-name minha-stack
# Retorna: { "StackDriftDetectionId": "abc-123" }
# Acompanhar o status da detecção
aws cloudformation describe-stack-drift-detection-status \
--stack-drift-detection-id abc-123
# DetectionStatus: DETECTION_IN_PROGRESS | DETECTION_COMPLETE | DETECTION_FAILED
# Ver o resultado por recurso
aws cloudformation describe-stack-resource-drifts \
--stack-name minha-stack \
--stack-resource-drift-filter-status DRIFTED \
--query 'StackResourceDrifts[*].[LogicalResourceId,ResourceType,StackResourceDriftStatus]' \
--output table
# Detectar drift num recurso específico
aws cloudformation detect-stack-resource-drift \
--stack-name minha-stack \
--logical-resource-id AppBucket
Status de drift por recurso:
IN_SYNC → propriedades conferem com o template
DRIFTED → pelo menos uma propriedade difere do template
NOT_CHECKED → recurso não suporta drift detection ou não foi verificado
DELETED → recurso foi deletado fora do CloudFormation
O que o drift detection verifica:
[FATO] O CloudFormation compara apenas as propriedades explicitamente definidas no template com o estado atual do recurso. Propriedades não declaradas no template (que ficam com valores default do serviço) não são verificadas.
# Template declara apenas BucketName e VersioningConfiguration
AppBucket:
Type: AWS::S3::Bucket
Properties:
BucketName: meu-bucket
VersioningConfiguration:
Status: Enabled
# Alguém adicionou uma política de lifecycle via console
# → drift detection VAI detectar o lifecycle como "added" (não estava no template)
# → drift detection NÃO vai reportar a ausência de CORS (nunca foi declarado)
Limitações importantes do drift detection — [FATO]:
1. Nem todos os tipos de recurso suportam drift detection
(verifique a lista em "Resources that support import and drift detection operations")
2. A detecção é assíncrona e pode levar vários minutos em stacks grandes
3. Drift detection NÃO corrige o drift — apenas reporta
Para corrigir: ou atualiza o recurso manualmente para o estado do template,
ou atualiza o template para refletir o estado atual
4. Não existe drift detection contínua nativa — você precisa agendar via EventBridge
ou usar AWS Config com a regra `cloudformation-stack-drift-detection-check`
5. Stacks com status REVIEW_IN_PROGRESS não podem ter drift detectado
Diagrama do ciclo de drift:
Template Recurso Real
───────── ────────────
VersioningConfiguration: Enabled
VersioningConfiguration: Enabled ← IN_SYNC
[Alguém vai no console e desativa o versioning]
VersioningConfiguration: Enabled
VersioningConfiguration: Suspended ← DRIFTED
Opções para resolver:
A) Reativar o versioning no console/CLI → IN_SYNC
B) Atualizar o template para Suspended e fazer deploy → IN_SYNC
C) Fazer deploy do template original (sem mudar nada) → CloudFormation
detecta a diferença e reativa o versioning como parte do update
4. Stack policies — proteção declarativa contra atualizações destrutivas
Uma stack policy é um documento JSON que define quais ações de update são permitidas em quais recursos. Uma vez definida, toda atualização da stack passa pela policy antes de ser executada.
Estrutura da policy:
{
"Statement": [
{
"Effect": "Allow" | "Deny",
"Principal": "*",
"Action": [ "Update:Modify", "Update:Replace", "Update:Delete", "Update:*" ],
"Resource": "LogicalResourceId/NomeDoRecurso" | "*",
"Condition": { ... } // opcional
}
]
}
As quatro Actions possíveis:
Update:Modify → atualização que não substitui nem deleta
Update:Replace → atualização que substitui o recurso (Replacement = True)
Update:Delete → remoção do recurso do template
Update:* → qualquer atualização (wildcard)
Comportamento padrão — [FATO]:
Sem stack policy: todos os recursos podem ser atualizados sem restrição.
Com stack policy: todos os recursos ficam protegidos por padrão (Deny Update:*). Você precisa declarar explicitamente o que é permitido.
Exemplo prático — proteger RDS e SQS, liberar o resto:
{
"Statement": [
{
"Effect": "Allow",
"Principal": "*",
"Action": "Update:*",
"Resource": "*"
},
{
"Effect": "Deny",
"Principal": "*",
"Action": ["Update:Replace", "Update:Delete"],
"Resource": "LogicalResourceId/ProductionDatabase"
},
{
"Effect": "Deny",
"Principal": "*",
"Action": ["Update:Replace", "Update:Delete"],
"Resource": "LogicalResourceId/OrdersQueue"
}
]
}
[FATO] Deny sempre prevalece sobre Allow quando há sobreposição de statements. A lógica é idêntica ao IAM.
Aplicando e gerenciando a policy:
# Aplicar a policy ao criar a stack
aws cloudformation create-stack \
--stack-name minha-stack \
--template-body file://template.yaml \
--stack-policy-body file://stack-policy.json
# Aplicar a uma stack existente
aws cloudformation set-stack-policy \
--stack-name minha-stack \
--stack-policy-body file://stack-policy.json
# Ver a policy atual
aws cloudformation get-stack-policy \
--stack-name minha-stack
# Override temporário para atualizar um recurso protegido
# (a policy original volta a valer automaticamente após o update)
aws cloudformation update-stack \
--stack-name minha-stack \
--template-body file://template.yaml \
--stack-policy-during-update-body file://override-policy.json
Override temporário — política de emergência:
// override-policy.json — permite substituição apenas do RDS
{
"Statement": [
{
"Effect": "Allow",
"Principal": "*",
"Action": "Update:*",
"Resource": "LogicalResourceId/ProductionDatabase"
}
]
}
[FATO] O override via --stack-policy-during-update-body é aplicado apenas durante aquele update específico. Após a conclusão do update (sucesso ou rollback), a policy original é restaurada automaticamente.
5. Stack termination protection — complemento às stack policies
Stack policy protege recursos de updates. Termination protection protege a stack inteira de ser deletada.
# Habilitar
aws cloudformation update-termination-protection \
--stack-name minha-stack \
--enable-termination-protection
# Verificar
aws cloudformation describe-stacks \
--stack-name minha-stack \
--query 'Stacks[0].EnableTerminationProtection'
# Desabilitar (necessário antes de deletar)
aws cloudformation update-termination-protection \
--stack-name minha-stack \
--no-enable-termination-protection
Diferença entre as proteções:
Termination protection → impede DELETE da stack inteira
Stack policy → impede UPDATE destrutivo de recursos específicos
DeletionPolicy: Retain → comportamento do recurso quando a stack é deletada
As três são complementares e independentes.
Exemplo prático
Cenário: você tem uma stack de produção com um RDS e precisa atualizar o tipo de instância — uma operação de Some Interruption. Você quer garantir que não vai acidentalmente substituir o banco.
# 1. Ver o estado atual da stack
aws cloudformation describe-stacks \
--stack-name prod-stack \
--query 'Stacks[0].[StackStatus,EnableTerminationProtection]'
# 2. Verificar se há drift antes de qualquer operação
aws cloudformation detect-stack-drift --stack-name prod-stack
# aguardar...
aws cloudformation describe-stack-resource-drifts \
--stack-name prod-stack \
--stack-resource-drift-filter-status DRIFTED \
--output table
# Se houver drift, avaliar antes de prosseguir
# 3. Criar changeset com a mudança de instance class
aws cloudformation create-change-set \
--stack-name prod-stack \
--change-set-name update-rds-class-$(date +%Y%m%d) \
--template-body file://template.yaml \
--parameters \
ParameterKey=DBInstanceClass,ParameterValue=db.t3.large \
--capabilities CAPABILITY_NAMED_IAM
aws cloudformation wait change-set-create-complete \
--stack-name prod-stack \
--change-set-name update-rds-class-20260506
# 4. Revisar: verificar se Replacement é False (esperado para mudança de class)
aws cloudformation describe-change-set \
--stack-name prod-stack \
--change-set-name update-rds-class-20260506 \
--query 'Changes[*].ResourceChange.[Action,LogicalResourceId,Replacement,Scope]' \
--output table
# Saída esperada:
# Action | LogicalResourceId | Replacement | Scope
# Modify | ProductionDB | False | ['Properties']
# ✅ Replacement = False → seguro prosseguir
# 5. Executar após confirmação
aws cloudformation execute-change-set \
--stack-name prod-stack \
--change-set-name update-rds-class-20260506
# 6. Acompanhar eventos em tempo real
aws cloudformation describe-stack-events \
--stack-name prod-stack \
--query 'StackEvents[0:10].[Timestamp,LogicalResourceId,ResourceStatus,ResourceStatusReason]' \
--output table
Armadilhas comuns
1. Confiar em Replacement: False sem verificar RequiresRecreation nos Details
O campo Replacement no nível do resource change é um agregado. Para entender qual propriedade específica está causando a mudança — e se ela poderia causar replacement em cenários diferentes — você precisa olhar Changes[].ResourceChange.Details[].Target.RequiresRecreation. Um changeset pode mostrar Replacement: False hoje e Replacement: True amanhã se o valor da propriedade mudar.
2. Achar que drift detection cobre todos os recursos
Vários tipos de recurso não suportam drift detection — entre eles alguns recursos Lambda, recursos de serviços mais novos, e recursos customizados (AWS::CloudFormation::CustomResource). Antes de confiar num relatório de drift limpo, verifique quais recursos da stack estão marcados como NOT_CHECKED no output.
3. Stack policy bloqueando o próprio rollback
[FATO] Se uma stack policy nega Update:Replace num recurso, e um update falha e tenta fazer rollback criando uma nova versão do recurso, o rollback também pode ser bloqueado pela policy — deixando a stack em UPDATE_ROLLBACK_FAILED. Para sair desse estado: aws cloudformation continue-update-rollback com --resources-to-skip para pular o recurso problemático.
Exercício de reflexão
Você mantém uma stack de produção com os seguintes recursos: um RDS PostgreSQL, uma fila SQS, um bucket S3 e um ALB. Um colega cria uma stack policy que apenas nega Update:Replace e Update:Delete para o RDS e a fila SQS, e permite Update:* para tudo mais.
Três meses depois, alguém muda o BucketName do S3 no template (uma operação de Replacement) e faz deploy. O bucket é substituído e os dados são perdidos.
Onde a proteção falhou? Reescreva a stack policy para cobrir esse caso sem bloquear operações legítimas de manutenção nos outros recursos. Considere também: o que precisaria ser feito no template além da stack policy para garantir proteção em múltiplas camadas?
Recursos para aprofundar
Changesets:
- Update CloudFormation stacks using change sets — cobre o ciclo completo: criação, visualização e execução, com os campos do output explicados.
Update behaviors:
- Understand update behaviors of stack resources — referência dos três comportamentos com exemplos de recursos e propriedades para cada categoria.
Drift detection:
- Detect unmanaged configuration changes to stacks and resources with drift detection — inclui a lista de recursos suportados e o formato detalhado do output de drift por recurso.
Stack policies:
- Prevent updates to stack resources — estrutura da policy JSON, as quatro actions disponíveis, e como fazer override temporário.