Sessão 004 — CDK v2: setup, bootstrap e estrutura de projeto
Duração estimada: 60 minutos
Pré-requisitos: session-003 — CloudFormation: changesets, drift detection e stack policies
Objetivo
Ao final, você conseguirá inicializar um projeto CDK v2 em TypeScript ou Python, executar cdk bootstrap em uma conta/região, entender o que o bootstrap provisiona (S3 bucket, ECR, IAM roles), e executar cdk synth, cdk diff e cdk deploy em um stack simples.
Contexto
[FATO] O AWS CDK (Cloud Development Kit) é um framework open-source lançado em 2019 que permite definir infraestrutura em linguagens de programação de propósito geral (TypeScript, Python, Java, Go, C#). O CDK não substitui o CloudFormation — ele gera CloudFormation como artefato de saída. Todo cdk deploy termina em um changeset do CloudFormation sendo executado.
[CONSENSO] A principal vantagem do CDK sobre CloudFormation puro não é apenas sintaxe — é abstração com escape hatch. Você escreve menos para casos comuns (L2 constructs cuidam de permissões, logs, e defaults seguros automaticamente), mas sempre pode descer para o nível do CloudFormation quando precisar de controle total. Essa é a proposta central do modelo de constructs L1/L2/L3, que a sessão 005 cobre em profundidade.
[FATO] CDK v2 foi lançado em dezembro de 2021. A principal diferença em relação ao v1 é que todos os constructs de serviços AWS foram consolidados em um único pacote (aws-cdk-lib) em vez de pacotes separados por serviço. CDK v1 chegou ao end-of-support em junho de 2023. Use sempre v2.
Conceitos principais
1. Modelo mental: CDK é um compilador de infraestrutura
O CDK transforma código em CloudFormation. O fluxo completo é:
Seu código (TypeScript/Python)
│
▼ cdk synth
Cloud Assembly (cdk.out/)
├── MeuStack.template.json ← CloudFormation template gerado
├── asset.XXXX/ ← arquivos locais a fazer upload (Lambda code, etc.)
└── manifest.json ← metadados do assembly
│
▼ cdk deploy
Upload assets → S3/ECR (bucket do bootstrap)
Cria/atualiza stack CloudFormation via changeset
│
▼
Recursos AWS criados
Quando um deploy falha, o erro real está no CloudFormation — não no CDK. Por isso conhecer CloudFormation (sessões 002-003) é pré-requisito essencial.
2. Instalação e pré-requisitos
[FATO] O CDK CLI é um pacote npm. O runtime mínimo é Node.js 18.x (mesmo que você escreva em Python ou Go — o CLI é sempre Node.js).
# Verificar versão do Node
node --version # precisa ser >= 18.x
# Instalar o CDK CLI globalmente
npm install -g aws-cdk
# Verificar versão instalada
cdk --version # ex: 2.176.0 (build xxxxxxx)
# Para Python: instalar a lib no projeto (não no sistema)
pip install aws-cdk-lib constructs
[CONSENSO] Instalar o CDK CLI globalmente (-g) é conveniente para uso local, mas em pipelines CI/CD prefira instalar localmente no projeto (npm install aws-cdk) para fixar a versão. A versão do CLI e da aws-cdk-lib no package.json devem ser compatíveis — diferenças de versão entre CLI e lib podem causar warnings ou erros de synth.
3. Estrutura de um projeto CDK v2
Inicializar um novo projeto:
mkdir meu-projeto && cd meu-projeto
# TypeScript (mais comum em exemplos oficiais)
cdk init app --language typescript
# Python
cdk init app --language python
Estrutura gerada para TypeScript:
meu-projeto/
├── bin/
│ └── meu-projeto.ts ← Entry point: instancia o App e os Stacks
├── lib/
│ └── meu-projeto-stack.ts ← Definição dos constructs (onde você trabalha)
├── test/
│ └── meu-projeto.test.ts ← Testes com assertions (sessão 008)
├── cdk.json ← Configuração do CDK CLI
├── package.json
├── tsconfig.json
└── node_modules/
Estrutura gerada para Python:
meu-projeto/
├── app.py ← Entry point
├── meu_projeto/
│ ├── __init__.py
│ └── meu_projeto_stack.py ← Definição dos constructs
├── tests/
├── cdk.json
├── requirements.txt
└── .venv/ ← virtualenv (criar manualmente: python -m venv .venv)
O entry point (bin/meu-projeto.ts):
import * as cdk from 'aws-cdk-lib';
import { MeuProjetoStack } from '../lib/meu-projeto-stack';
const app = new cdk.App();
new MeuProjetoStack(app, 'MeuProjetoStack', {
env: {
account: process.env.CDK_DEFAULT_ACCOUNT,
region: process.env.CDK_DEFAULT_REGION,
},
});
[FATO] CDK_DEFAULT_ACCOUNT e CDK_DEFAULT_REGION são variáveis de ambiente populadas automaticamente pelo CDK CLI com base no perfil AWS ativo. Hardcodar account/region é possível mas não recomendado — impede reusar o mesmo código em múltiplas contas.
A stack (lib/meu-projeto-stack.ts):
import * as cdk from 'aws-cdk-lib';
import * as s3 from 'aws-cdk-lib/aws-s3';
import { Construct } from 'constructs';
export class MeuProjetoStack extends cdk.Stack {
constructor(scope: Construct, id: string, props?: cdk.StackProps) {
super(scope, id, props);
// Construct L2 — nível de abstração alto
new s3.Bucket(this, 'MeuBucket', {
versioned: true,
encryption: s3.BucketEncryption.S3_MANAGED,
removalPolicy: cdk.RemovalPolicy.DESTROY, // equivalente a DeletionPolicy
autoDeleteObjects: true,
});
}
}
Hierarquia de constructs:
App ← raiz de tudo
└── Stack (MeuProjetoStack)
└── Bucket (MeuBucket)
└── BucketPolicy (gerado automaticamente pelo L2)
Cada nó na árvore tem um ID único dentro do pai. O CDK usa esse path para gerar logical IDs no CloudFormation: MeuProjetoStackMeuBucketXXXXXXXX.
4. Bootstrap — o que é e o que provisiona
Por que o bootstrap é necessário:
Quando o CDK faz deploy de assets (código Lambda, imagens Docker, arquivos locais), ele precisa de um lugar para armazená-los antes de passar para o CloudFormation. O bootstrap cria essa infraestrutura de suporte.
[FATO] O cdk bootstrap implanta uma stack CloudFormation chamada CDKToolkit na conta/região. Esta stack provisiona:
CDKToolkit stack
├── S3 Bucket (cdk-xxxxxxxx-assets-ACCOUNT-REGION)
│ └── Armazena templates sintetizados e assets locais
│ Versionado e com lifecycle policy de 365 dias
│
├── ECR Repository (cdk-xxxxxxxx-container-assets-ACCOUNT-REGION)
│ └── Armazena imagens Docker para Lambda/ECS
│
└── IAM Roles (6 roles criadas por padrão)
├── cdk-xxxxxxxx-lookup-role-ACCOUNT-REGION
│ └── Permite ao CDK CLI fazer lookups (VPCs, AMIs, etc.)
├── cdk-xxxxxxxx-file-publishing-role-ACCOUNT-REGION
│ └── Upload de assets para o S3
├── cdk-xxxxxxxx-image-publishing-role-ACCOUNT-REGION
│ └── Push de imagens para o ECR
├── cdk-xxxxxxxx-deploy-role-ACCOUNT-REGION
│ └── Criação/atualização de stacks CloudFormation
├── cdk-xxxxxxxx-cfn-exec-role-ACCOUNT-REGION
│ └── Role que o CloudFormation assume para criar recursos
└── cdk-xxxxxxxx-readonly-role-ACCOUNT-REGION (v2 recente)
└── Permissões de leitura para diff e synth
Executando o bootstrap:
# Bootstrap na conta/região atual (usa o perfil padrão)
cdk bootstrap
# Bootstrap explicitando conta e região
cdk bootstrap aws://123456789012/us-east-1
# Bootstrap com perfil específico
cdk bootstrap --profile prod aws://123456789012/us-east-1
# Bootstrap multi-região (executar uma vez por região)
cdk bootstrap aws://123456789012/us-east-1
cdk bootstrap aws://123456789012/sa-east-1
# Ver o que seria criado sem executar
cdk bootstrap --show-template
Bootstrap qualifier:
[FATO] O qualifier é um identificador de 9 caracteres (padrão: hnb659fds) que faz parte dos nomes dos recursos do bootstrap. Ele permite ter múltiplos bootstraps na mesma conta/região — útil quando times diferentes precisam de isolamento de assets.
# Bootstrap com qualifier customizado
cdk bootstrap --qualifier meutime
# No cdk.json, referenciar o qualifier
{
"@aws-cdk/core:bootstrapQualifier": "meutime"
}
Quando re-executar o bootstrap:
O bootstrap tem um número de versão. Ao atualizar o CDK CLI para uma versão maior, pode ser necessário atualizar o bootstrap. O CDK avisa durante o cdk deploy se a versão do bootstrap está desatualizada:
❌ This CDK deployment requires bootstrap stack version '6', found '4'.
Please run 'cdk bootstrap'.
Rodar cdk bootstrap numa conta/região já bootstrapada é seguro — ele atualiza os recursos existentes sem recriar.
5. Os comandos principais do CDK CLI
cdk synth — compilar para CloudFormation:
# Sintetizar todos os stacks
cdk synth
# Sintetizar um stack específico
cdk synth MeuProjetoStack
# Ver o template gerado no terminal (sem salvar em cdk.out/)
cdk synth --quiet
# Output em cdk.out/ por padrão
ls cdk.out/
# MeuProjetoStack.template.json
# manifest.json
# tree.json
cdk synth é o equivalente a "compilar" — transforma código em CloudFormation. Ele detecta erros de configuração antes de qualquer chamada à AWS.
cdk diff — ver o que vai mudar:
# Diff entre o código local e a stack deployada
cdk diff
# Diff de um stack específico
cdk diff MeuProjetoStack
# Diff contra um template salvo (sem stack deployada)
cdk diff --template cdk.out/MeuProjetoStack.template.json
cdk diff é o equivalente ao changeset da sessão anterior — mas calculado localmente, sem criar nada na AWS. É mais rápido que um changeset real, mas menos preciso para recursos com valores resolvidos em runtime.
cdk deploy — implantar:
# Deploy com aprovação interativa de IAM changes
cdk deploy
# Deploy sem aprovação (útil em pipelines)
cdk deploy --require-approval never
# Deploy de múltiplos stacks
cdk deploy Stack1 Stack2
# Deploy de todos os stacks
cdk deploy "*"
# Deploy com hotswap (para desenvolvimento — evita CloudFormation para mudanças em Lambda)
cdk deploy --hotswap
# Deploy e mostrar os outputs da stack
cdk deploy --outputs-file outputs.json
[FATO] cdk deploy --hotswap detecta mudanças que são apenas em código de Lambda ou definições de ECS e as aplica diretamente (sem CloudFormation), reduzindo o tempo de feedback de ~2 minutos para ~10 segundos. Nunca use hotswap em produção — ele pode deixar o estado do CloudFormation dessincronizado com o estado real.
cdk destroy — destruir a stack:
cdk destroy MeuProjetoStack
# Pede confirmação interativa
cdk destroy --force MeuProjetoStack
# Sem confirmação
Diagrama do fluxo de comandos:
Código CDK
│
├─ cdk synth ──► cdk.out/ (CloudFormation templates + assets)
│
├─ cdk diff ──► Comparação local vs. stack deployada (sem chamadas AWS)
│
├─ cdk deploy
│ ├── cdk synth (implícito)
│ ├── Upload assets → S3/ECR (via roles do bootstrap)
│ ├── Cria changeset CloudFormation
│ ├── Mostra IAM changes para aprovação (se houver)
│ └── Executa changeset → aguarda conclusão
│
└─ cdk destroy
├── Cria changeset de deleção
└── Executa → deleta todos os recursos (respeitando DeletionPolicy)
6. cdk.json — configuração do projeto
O cdk.json é o arquivo de configuração do CDK CLI para o projeto. Todo cdk executado na pasta lê esse arquivo.
{
"app": "npx ts-node --prefer-ts-exts bin/meu-projeto.ts",
"watch": {
"include": ["**"],
"exclude": ["README.md", "cdk*.json", "**/*.d.ts", "**/*.js", "tsconfig.json",
"package*.json", ".git/", "node_modules/", "cdk.out/"]
},
"context": {
"@aws-cdk/aws-lambda:recognizeLayerVersion": true,
"@aws-cdk/core:checkSecretUsage": true,
"@aws-cdk/aws-iam:minimizePolicies": true,
"@aws-cdk/aws-s3:serverAccessLogsUseBucketPolicy": true
}
}
Campos principais:
app → comando para executar o entry point (compilador/runtime)
watch → arquivos a monitorar para cdk watch (hot reload em dev)
context → feature flags e valores de contexto persistidos
[FATO] Os valores em context que começam com @aws-cdk/ são feature flags. Eles controlam comportamentos que mudaram entre versões do CDK e permitem migração gradual. Projetos novos criados com cdk init recebem automaticamente o conjunto de feature flags da versão atual.
O que vai no gitignore:
cdk.out/ ← sempre ignorar (artefato de build)
cdk.context.json ← DEPENDE (veja sessão 012 para a discussão completa)
node_modules/ ← sempre ignorar
.venv/ ← sempre ignorar (Python)
Exemplo prático
Criar um projeto CDK do zero e fazer o primeiro deploy:
# 1. Criar e inicializar o projeto
mkdir demo-cdk && cd demo-cdk
cdk init app --language typescript
npm install
# 2. Bootstrap (uma vez por conta/região)
cdk bootstrap --profile dev
# 3. Editar lib/demo-cdk-stack.ts
cat > lib/demo-cdk-stack.ts << 'EOF'
import * as cdk from 'aws-cdk-lib';
import * as s3 from 'aws-cdk-lib/aws-s3';
import * as iam from 'aws-cdk-lib/aws-iam';
import { Construct } from 'constructs';
export class DemoCdkStack extends cdk.Stack {
constructor(scope: Construct, id: string, props?: cdk.StackProps) {
super(scope, id, props);
const bucket = new s3.Bucket(this, 'DemoBucket', {
versioned: true,
encryption: s3.BucketEncryption.S3_MANAGED,
blockPublicAccess: s3.BlockPublicAccess.BLOCK_ALL,
removalPolicy: cdk.RemovalPolicy.DESTROY,
autoDeleteObjects: true,
});
const appRole = new iam.Role(this, 'AppRole', {
assumedBy: new iam.ServicePrincipal('lambda.amazonaws.com'),
managedPolicies: [
iam.ManagedPolicy.fromAwsManagedPolicyName('service-role/AWSLambdaBasicExecutionRole'),
],
});
// L2 cuida das permissões — gera a policy correta automaticamente
bucket.grantReadWrite(appRole);
// Outputs
new cdk.CfnOutput(this, 'BucketName', { value: bucket.bucketName });
new cdk.CfnOutput(this, 'AppRoleArn', { value: appRole.roleArn });
}
}
EOF
# 4. Sintetizar — ver o CloudFormation gerado
cdk synth
# 5. Ver o que vai ser criado (diff contra stack inexistente)
cdk diff --profile dev
# 6. Deploy
cdk deploy --profile dev
# 7. Ver o CloudFormation template gerado (para comparar com o que você escreveria)
cat cdk.out/DemoCdkStack.template.json | jq '.Resources | keys'
# 8. Comparar o que o L2 gerou vs o que você escreveria manualmente
# O bucket.grantReadWrite(appRole) gera automaticamente uma IAM Policy
# com as permissões corretas — sem precisar escrever o ARN do bucket
cat cdk.out/DemoCdkStack.template.json | jq '.Resources | to_entries[] | select(.value.Type == "AWS::IAM::Policy")'
Armadilhas comuns
1. Esquecer de re-executar o bootstrap após atualização do CDK
Após npm install -g aws-cdk@latest, se a nova versão exige uma versão mais recente do bootstrap, o próximo cdk deploy falha com a mensagem de versão incompatível. A solução é simples (cdk bootstrap), mas confunde quem não sabe por que está falhando.
2. RemovalPolicy.DESTROY em produção
O CDK usa RemovalPolicy.RETAIN como padrão para a maioria dos recursos stateful (S3, RDS, DynamoDB). Mudar para DESTROY faz o cdk destroy deletar o recurso e seus dados. Em projetos criados a partir de tutoriais, é comum esquecer esse valor copiado de exemplos de desenvolvimento ao promover para produção.
3. Logical ID instável — o rename problem
O CDK deriva o logical ID do CloudFormation a partir do path de constructs na árvore (Stack > Construct > Resource). Se você renomear um construct ou mover um recurso na hierarquia, o logical ID muda — e o CloudFormation interpreta isso como "deletar o antigo e criar um novo". Para recursos stateful (RDS, S3 com dados), isso é destrutivo. A solução é usar overrideLogicalId() para fixar o ID quando necessário.
// Fixar o logical ID para evitar substituição acidental ao refatorar
const cfnBucket = bucket.node.defaultChild as s3.CfnBucket;
cfnBucket.overrideLogicalId('MeuBucketEstavel');
Exercício de reflexão
Você está migrando uma stack CloudFormation existente para CDK. O template original tem um bucket S3 com logical ID AppBucket. Ao escrever o código CDK, você cria o bucket assim:
new s3.Bucket(this, 'AppBucket', { versioned: true });
O CDK vai gerar um logical ID diferente do original (algo como AppBucketXXXXXXXX com hash). Se você fizer cdk deploy sobre a stack existente, o CloudFormation vai tentar criar um novo bucket e deletar o original.
Quais são as duas abordagens para resolver esse problema? Para cada uma, descreva os passos concretos e os trade-offs. Considere também: por que o CDK adiciona esse hash ao logical ID e qual problema ele resolve na vida normal de um projeto CDK.
Recursos para aprofundar
Setup e primeiro projeto:
- Getting started with the AWS CDK — pré-requisitos, instalação, e o tutorial hello-world com TypeScript e Python.
Bootstrap em profundidade:
- AWS CDK bootstrapping — lista todos os recursos criados, versões do bootstrap, e quando re-executar.
CLI reference:
- AWS CDK CLI reference — todos os comandos com flags, incluindo --hotswap, --watch, --outputs-file e opções de aprovação.
CDK Workshop (prático):
- CDK Workshop — tutorial hands-on com TypeScript ou Python que cobre App → Stack → Constructs → Pipeline em ~2 horas. Recomendado para consolidar essa sessão na prática.