Sessão 009 — CDK Pipelines: bootstrap cross-account, OIDC connection e estrutura da pipeline
Duração estimada: 60 minutos
Pré-requisitos: session-008 — CDK: Testing com assertions
Objetivo
Ao final, você conseguirá executar cdk bootstrap nas contas de deploy com trust policy apontando para a conta da pipeline, configurar a conexão com GitHub via AWS CodeStar Connections (OIDC — sem PAT), e criar uma CDK Pipeline com Source + Build + UpdatePipeline stages, entendendo o que cada stage faz antes do primeiro deploy real de aplicação.
Contexto
[FATO] CDK Pipelines é um construct de alto nível (aws-cdk-lib/pipelines) que usa AWS CodePipeline como motor de execução. Ele adiciona uma camada de abstração opinionada sobre o CodePipeline, com duas funcionalidades centrais que o tornam diferente de uma pipeline comum: self-mutation (a pipeline atualiza a si mesma antes de qualquer deploy de aplicação) e multi-account deployment nativo (a pipeline roda numa conta e deploya em outras).
[CONSENSO] Para organizações com múltiplas contas AWS (dev, staging, prod), CDK Pipelines é o caminho mais direto para implementar um pipeline de CD completo usando apenas CDK — sem precisar configurar manualmente CodeBuild projects, IAM roles cross-account ou pipelines de múltiplos estágios no console.
Conceitos principais
1. As contas envolvidas — a topologia multi-account
Uma CDK Pipeline sempre tem pelo menos dois tipos de conta:
┌─────────────────────────────────────────────────────────────┐
│ CONTA TOOLS / PIPELINE (onde a pipeline roda) │
│ ┌──────────────────────────────────────┐ │
│ │ CodePipeline │ │
│ │ Source → Build → UpdatePipeline │ │
│ │ → [Stages da aplicação] │ │
│ │ │ │
│ │ CodeBuild (synth + deploy) │ │
│ │ S3 (artifacts) │ │
│ │ ECR (imagens) │ │
│ └──────────────────────────────────────┘ │
│ │ │ │ │
└───────────┼───────────┼───────────┼─────────────────────────┘
│ assume role │ assume role
▼ ▼
┌──────────────────┐ ┌──────────────────┐
│ CONTA DEV │ │ CONTA PROD │
│ (deploy target) │ │ (deploy target) │
│ │ │ │
│ CDKToolkit │ │ CDKToolkit │
│ (bootstrapped │ │ (bootstrapped │
│ com --trust) │ │ com --trust) │
└──────────────────┘ └──────────────────┘
A conta tools precisa ser capaz de assumir roles nas contas de deploy. Isso é configurado no bootstrap de cada conta de deploy via --trust.
2. Bootstrap cross-account — a sequência correta
O bootstrap padrão (cdk bootstrap) cria a CDKToolkit stack com roles que só a própria conta pode assumir. Para CDK Pipelines multi-account, você precisa modificar esse comportamento.
Passo 1 — Bootstrap da conta tools (pipeline):
# A conta tools não precisa de --trust especial
# mas precisa do bootstrap moderno
cdk bootstrap aws://PIPELINE_ACCOUNT_ID/us-east-1 \
--profile pipeline
Passo 2 — Bootstrap das contas de deploy com trust:
# Conta de dev — confia na conta da pipeline para fazer deploys
cdk bootstrap aws://DEV_ACCOUNT_ID/us-east-1 \
--profile dev \
--trust PIPELINE_ACCOUNT_ID \
--cloudformation-execution-policies arn:aws:iam::aws:policy/AdministratorAccess
# Conta de prod — mesma coisa
cdk bootstrap aws://PROD_ACCOUNT_ID/us-east-1 \
--profile prod \
--trust PIPELINE_ACCOUNT_ID \
--cloudformation-execution-policies arn:aws:iam::aws:policy/AdministratorAccess
O que --trust faz na prática:
[FATO] O --trust PIPELINE_ACCOUNT_ID modifica o trust policy das IAM roles do CDKToolkit na conta de deploy para incluir a conta da pipeline como principal autorizado:
// Role: cdk-XXXX-deploy-role-DEV_ACCOUNT-us-east-1
// Trust Policy gerada pelo --trust:
{
"Statement": [
{
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam::PIPELINE_ACCOUNT_ID:root"
},
"Action": "sts:AssumeRole"
}
]
}
Isso permite que o CodeBuild rodando na conta da pipeline faça sts:AssumeRole na conta de deploy e execute o CloudFormation lá.
--cloudformation-execution-policies — o que é e por que importa:
[FATO] Esta flag define as políticas IAM que a role de execução do CloudFormation (a que cria os recursos de fato) terá na conta de deploy. AdministratorAccess é conveniente mas permissivo — para produção, considere criar uma policy customizada que só permite os recursos que sua aplicação usa.
# Versão mais restrita para prod (exemplo)
cdk bootstrap aws://PROD_ACCOUNT_ID/us-east-1 \
--profile prod \
--trust PIPELINE_ACCOUNT_ID \
--cloudformation-execution-policies arn:aws:iam::PROD_ACCOUNT_ID:policy/CdkDeployPolicy
Adicionando trust a um bootstrap existente:
[FATO] Ao re-executar cdk bootstrap com --trust, você deve listar todos os accounts que devem ter trust — não apenas os novos. Accounts não listados no novo comando perdem o trust.
# Re-bootstrap adicionando uma segunda conta de pipeline (ex: após migração)
cdk bootstrap aws://DEV_ACCOUNT_ID/us-east-1 \
--profile dev \
--trust PIPELINE_ACCOUNT_ID_1 \
--trust PIPELINE_ACCOUNT_ID_2 \ # ← novo; PIPELINE_ACCOUNT_ID_1 deve ser repetido
--cloudformation-execution-policies arn:aws:iam::aws:policy/AdministratorAccess
3. CodeStar Connections — GitHub sem PAT
[FATO] A forma legacy de integrar CodePipeline com GitHub usava um Personal Access Token (PAT) armazenado no Secrets Manager. A forma moderna usa AWS CodeStar Connections, que implementa OAuth via uma AWS App instalada no GitHub — sem nenhum token estático.
Por que CodeStar Connections é preferível:
PAT (legado) CodeStar Connections (moderno)
───────────────────────────────── ────────────────────────────────
Token estático com validade OAuth gerenciado pela AWS
Precisa rotacionar manualmente Sem rotação necessária
Armazenado no Secrets Manager Sem segredo a gerenciar
Permissões amplas por repositório Permissões por instalação da App
Webhook manual ou polling Webhook automático via App
Criando a connection (só pode ser feito via Console ou CLI — não via CDK):
# Etapa 1: criar a connection (fica em PENDING até ser autorizada)
aws codestar-connections create-connection \
--provider-type GitHub \
--connection-name minha-org-github \
--profile pipeline
# Retorna: { "ConnectionArn": "arn:aws:codestar-connections:us-east-1:..." }
# Etapa 2: autorizar no console
# Console → Developer Tools → Connections → [sua connection] → Update pending connection
# → Conecta com sua conta GitHub → autoriza a AWS App
# Status muda de PENDING → AVAILABLE
[FATO] A autorização da connection obrigatoriamente passa pelo Console — não existe forma de fazer isso via CLI ou CDK. Isso é intencional: a AWS exige interação humana para autorizar acesso OAuth ao GitHub.
Verificar que a connection está disponível:
aws codestar-connections get-connection \
--connection-arn arn:aws:codestar-connections:us-east-1:ACCOUNT:connection/UUID \
--profile pipeline \
--query 'Connection.ConnectionStatus'
# "AVAILABLE" ← pronto para uso
# "PENDING" ← ainda precisa ser autorizada no console
4. Estrutura da CDK Pipeline — os stages automáticos
Uma CDK Pipeline mínima tem 3 stages gerados automaticamente antes de qualquer stage de aplicação:
GitHub CodePipeline (conta tools)
│ │
│ push para main │
├──────────────────────────────►
│ ┌────▼──────────┐
│ │ Source │ Baixa o código do GitHub
│ │ │ via CodeStar Connection
│ └────┬──────────┘
│ │
│ ┌────▼──────────┐
│ │ Build │ npm ci + cdk synth
│ │ (Synth) │ Gera o cloud assembly
│ └────┬──────────┘
│ │
│ ┌────▼──────────┐
│ │ UpdatePipeline│ Aplica mudanças na
│ │ (Self-Mutate) │ própria pipeline
│ └────┬──────────┘
│ │
│ ┌─────────▼──────────┐
│ │ Assets │ Upload S3/ECR
│ └─────────┬──────────┘
│ │
│ ┌─────────▼──────────┐
│ │ Stage: Dev │ Deploy nas contas
│ │ Stage: Prod │ de aplicação
│ └────────────────────┘
O que cada stage automático faz:
Source:
- Baixa o código do repositório GitHub via CodeStar Connection
- Armazena o artefato no S3 (artifact bucket da pipeline)
- Triggado por push no branch configurado
Build (Synth):
- Roda npm ci (ou equivalente) para instalar dependências
- Executa cdk synth para gerar o cloud assembly (cdk.out/)
- O cloud assembly é o artefato que alimenta todos os stages seguintes
UpdatePipeline (Self-Mutate):
- Compara a definição da pipeline no cloud assembly com a pipeline atual
- Se a pipeline mudou: aplica as mudanças e reinicia a execução do zero
- Se não mudou: avança para os stages de aplicação
5. Self-mutation — o mecanismo que muda tudo
Self-mutation é a característica mais importante e mais confusa do CDK Pipelines.
[FATO] Quando você muda o código da pipeline (adiciona um stage, muda um ShellStep, etc.) e faz push, o ciclo é:
Push para main
│
▼
Source → Build → UpdatePipeline
│
Pipeline mudou?
│ │
Sim Não
│ │
▼ ▼
Pipeline Continua
se atualiza para os
e REINICIA stages de
do zero aplicação
│
▼
Source → Build → UpdatePipeline
│
Agora igual
│
▼
Assets → Dev → Prod
Implicações práticas do self-mutation:
-
A primeira execução nunca deploya a aplicação — ela apenas atualiza a pipeline (que acabou de ser criada) e reinicia. A segunda execução é que deploya.
-
Mudanças na pipeline são atômicas — você não pode ter "a pipeline em produção está na versão antiga enquanto eu testo a versão nova". Toda mudança passa pela pipeline antes de chegar à aplicação.
-
Debugging fica mais lento — para testar uma mudança na definição da pipeline, você precisa fazer push, esperar a pipeline rodar, ver o resultado. Não tem como testar localmente.
-
selfMutation: falsepara desenvolvimento — durante o desenvolvimento inicial da pipeline, você pode desabilitar o self-mutation para iterar mais rápido. Mas nunca em produção.
6. Criando a pipeline — código completo
// lib/pipeline-stack.ts
import * as cdk from 'aws-cdk-lib';
import * as pipelines from 'aws-cdk-lib/pipelines';
import { Construct } from 'constructs';
import { ApplicationStage } from './application-stage';
export class PipelineStack extends cdk.Stack {
constructor(scope: Construct, id: string, props: cdk.StackProps) {
super(scope, id, props);
// 1. Fonte: GitHub via CodeStar Connection
const source = pipelines.CodePipelineSource.connection(
'minha-org/meu-repo', // owner/repo no GitHub
'main', // branch
{
connectionArn: 'arn:aws:codestar-connections:us-east-1:PIPELINE_ACCOUNT:connection/UUID',
triggerOnPush: true, // padrão: true
}
);
// 2. Synth step: instala deps e executa cdk synth
const synth = new pipelines.ShellStep('Synth', {
input: source,
commands: [
'npm ci', // instala dependências (lockfile)
'npm run build', // compila TypeScript (se necessário)
'npx cdk synth', // gera o cloud assembly
],
// primaryOutputDirectory: 'cdk.out', // padrão quando cdk synth é o último comando
});
// 3. Definição da pipeline
const pipeline = new pipelines.CodePipeline(this, 'Pipeline', {
pipelineName: 'MeuAppPipeline',
synth,
// CodeBuild image para o synth (padrão: aws/codebuild/standard:7.0)
codeBuildDefaults: {
buildEnvironment: {
buildImage: codebuild.LinuxBuildImage.STANDARD_7_0,
},
},
// Self-mutation habilitado (padrão: true)
selfMutation: true,
// Publicar assets em paralelo (mais rápido para múltiplos assets)
publishAssetsInParallel: true,
// Docker necessário? (se algum asset usa Docker)
dockerEnabledForSynth: false,
dockerEnabledForSelfMutation: false,
});
// 4. Adicionar stages de aplicação
pipeline.addStage(new ApplicationStage(this, 'Dev', {
env: { account: 'DEV_ACCOUNT_ID', region: 'us-east-1' },
}));
// Prod com aprovação manual antes do deploy
pipeline.addStage(new ApplicationStage(this, 'Prod', {
env: { account: 'PROD_ACCOUNT_ID', region: 'us-east-1' },
}), {
pre: [new pipelines.ManualApprovalStep('ApproveProductionDeploy')],
});
}
}
// bin/app.ts
const app = new cdk.App();
// A pipeline stack roda na conta tools
new PipelineStack(app, 'PipelineStack', {
env: { account: 'PIPELINE_ACCOUNT_ID', region: 'us-east-1' },
});
Primeiro deploy — o bootstrap da pipeline:
# 1. Bootstrap de todas as contas (uma vez)
cdk bootstrap aws://PIPELINE_ACCOUNT_ID/us-east-1 --profile pipeline
cdk bootstrap aws://DEV_ACCOUNT_ID/us-east-1 \
--profile dev \
--trust PIPELINE_ACCOUNT_ID \
--cloudformation-execution-policies arn:aws:iam::aws:policy/AdministratorAccess
cdk bootstrap aws://PROD_ACCOUNT_ID/us-east-1 \
--profile prod \
--trust PIPELINE_ACCOUNT_ID \
--cloudformation-execution-policies arn:aws:iam::aws:policy/AdministratorAccess
# 2. Deploy inicial da pipeline stack (só precisa rodar uma vez)
cdk deploy PipelineStack --profile pipeline
# A partir daqui, todo push para main atualiza e executa a pipeline automaticamente
7. O que acontece antes do primeiro deploy de aplicação
Sequência detalhada na primeira execução após o cdk deploy PipelineStack:
Execução #1 (após cdk deploy PipelineStack):
Source: Baixa o código do GitHub
Build: npm ci + cdk synth → gera cloud assembly
UpdatePipeline: Compara pipeline atual vs cloud assembly
→ A pipeline foi criada pelo cdk deploy manual
→ Cloud assembly tem a mesma definição
→ Sem mudanças → avança
Assets: Faz upload dos assets para S3/ECR
Dev: Deploya ApplicationStage na conta dev ✅
Approve: Aguarda aprovação manual
Prod: Deploya ApplicationStage na conta prod ✅
───────────────────────────────────────────────────────────────
Depois, você faz uma mudança na pipeline (ex: adiciona um stage):
push para main
Execução #2:
Source: Baixa o novo código
Build: cdk synth com o novo stage
UpdatePipeline: Pipeline mudou → aplica mudança → REINICIA
Execução #3 (após restart):
Source → Build → UpdatePipeline (sem mudança agora)
Assets → Dev → Approve → Prod
Exemplo prático — verificando a pipeline após deploy
# Ver o status da pipeline
aws codepipeline get-pipeline-state \
--name MeuAppPipeline \
--profile pipeline \
--query 'stageStates[*].{Stage:stageName,Status:latestExecution.status}' \
--output table
# Ver os logs do Synth step
aws codebuild batch-get-builds \
--ids $(aws codepipeline get-pipeline-state \
--name MeuAppPipeline \
--query 'stageStates[?stageName==`Build`].actionStates[0].latestExecution.externalExecutionId' \
--output text) \
--query 'builds[0].logs.deepLink'
# Forçar nova execução manualmente
aws codepipeline start-pipeline-execution \
--name MeuAppPipeline \
--profile pipeline
# Ver a definição atual da pipeline
aws codepipeline get-pipeline \
--name MeuAppPipeline \
--profile pipeline \
| jq '.pipeline.stages[].name'
Armadilhas comuns
1. Connection em status PENDING — pipeline trava no Source
Se você criar a connection via CLI mas esquecer de autorizá-la no Console, a pipeline vai travar no stage Source com erro Connection is not authorized. Verifique sempre o status da connection antes de criar a pipeline.
2. Bootstrap sem --trust — deploy falha com AccessDenied
Se você esquecer o --trust PIPELINE_ACCOUNT_ID no bootstrap das contas de deploy, a pipeline vai rodar com sucesso até o stage de deploy e falhar com AccessDenied ao tentar assumir a role na conta de destino. O erro não é óbvio — ele aparece nos logs do CodeBuild como falha de sts:AssumeRole.
3. Self-mutation e desenvolvimento da pipeline — loop confuso
Durante o desenvolvimento inicial, cada mudança na pipeline exige um push, espera pelo Source + Build + UpdatePipeline, e reinício. Se a pipeline está quebrando no Synth, você fica num loop: push → falha → fix → push → falha. Para sair mais rápido: teste o cdk synth localmente antes de fazer push, e use selfMutation: false temporariamente enquanto está construindo a pipeline pela primeira vez.
4. dockerEnabledForSynth: false com assets Docker
Se algum dos seus Lambda assets usa DockerImageFunction ou PythonFunction com bundling em Docker, o Synth step precisa de Docker. Sem dockerEnabledForSynth: true, o synth vai falhar com erro de Docker não disponível. Habilitar Docker no CodeBuild aumenta o tempo de startup do build.
Exercício de reflexão
Você tem uma CDK Pipeline rodando na conta tools que deploya em dev e prod. A pipeline está funcionando bem. Então você precisa adicionar um terceiro ambiente staging (nova conta) entre dev e prod.
Descreva a sequência exata de passos necessários — incluindo quais comandos rodar em qual conta, em qual ordem, e o que acontece com a pipeline durante essa transição. Em particular: quando a pipeline precisa ser pausada? Existe risco de downtime em dev ou prod durante a adição do novo stage? O que acontece se você adicionar o stage no código e fazer push sem ter feito o bootstrap da conta staging antes?
Recursos para aprofundar
CDK Pipelines:
- Continuous integration and delivery (CI/CD) using CDK Pipelines — guia completo com exemplos de multi-account, self-mutation e ShellSteps.
Bootstrap cross-account:
- Bootstrap your environment for use with the AWS CDK — opções --trust e --cloudformation-execution-policies com exemplos de comandos.
CodeStar Connections:
- GitHub connections — processo de criação e autorização da connection no Console, incluindo os requisitos de permissão IAM para criar connections.