Fundamentos do AWS CDK: Constructs e Stacks
O AWS CDK (Cloud Development Kit) é um framework que permite definir infraestrutura como código usando linguagens de programação familiar. Um Construct é o bloco de construção fundamental — representa um componente de nuvem (EC2, S3, Lambda) ou um agrupamento lógico deles. Você combina Constructs para criar aplicações complexas.
Uma Stack é um container que agrupa Constructs e mapeia para um CloudFormation template. Cada Stack pode ser deployada independentemente. Vamos ver como estruturar isso:
import * as cdk from 'aws-cdk-lib';
import * as s3 from 'aws-cdk-lib/aws-s3';
import * as lambda from 'aws-cdk-lib/aws-lambda';
export class StorageStack extends cdk.Stack {
public readonly bucket: s3.Bucket;
constructor(scope: cdk.App, id: string, props?: cdk.StackProps) {
super(scope, id, props);
// Criando um S3 bucket customizado
this.bucket = new s3.Bucket(this, 'DataBucket', {
versioned: true,
encryption: s3.BucketEncryption.S3_MANAGED,
blockPublicAccess: s3.BlockPublicAccess.BLOCK_ALL,
removalPolicy: cdk.RemovalPolicy.RETAIN,
});
// Lambda para processar arquivos
const processor = new lambda.Function(this, 'Processor', {
runtime: lambda.Runtime.NODEJS_18_X,
handler: 'index.handler',
code: lambda.Code.fromAsset('lambda'),
});
this.bucket.grantReadWrite(processor);
}
}
Observe como exportamos o bucket para reutilização em outras Stacks. Isso é essencial para composição.
Constructs Avançados e Composição
Constructs de alto nível (L3) encapsulam lógica complexa em abstrações reutilizáveis. Em vez de gerenciar IAM, VPC e segurança manualmente, você constrói Constructs customizados que herdam de Construct base.
import * as cdk from 'aws-cdk-lib';
import * as ec2 from 'aws-cdk-lib/aws-ec2';
import * as iam from 'aws-cdk-lib/aws-iam';
interface SecureWebServerProps extends cdk.StackProps {
instanceType: ec2.InstanceType;
machineImage: ec2.IMachineImage;
}
export class SecureWebServer extends cdk.Stack {
constructor(scope: cdk.App, id: string, props: SecureWebServerProps) {
super(scope, id, props);
const vpc = new ec2.Vpc(this, 'VPC', {
maxAzs: 2,
cidrMask: 24,
});
const securityGroup = new ec2.SecurityGroup(this, 'SG', {
vpc,
description: 'Security group for web server',
allowAllOutbound: true,
});
securityGroup.addIngressRule(
ec2.Peer.anyIpv4(),
ec2.Port.tcp(80),
'Allow HTTP'
);
const instance = new ec2.Instance(this, 'Instance', {
vpc,
instanceType: props.instanceType,
machineImage: props.machineImage,
securityGroup,
});
new cdk.CfnOutput(this, 'InstanceIP', {
value: instance.instancePublicIp,
});
}
}
O padrão aqui é aceitar um props interface para configurabilidade. Isso permite que sua Stack seja parametrizada e reutilizável em múltiplos contextos.
CDK Pipelines: CI/CD Declarativo
O CDK Pipelines é o padrão mais poderoso: você define seu pipeline de deployment como código. O pipeline é autossuficiente — ele se autodeploya e sincroniza com seu repositório.
import * as cdk from 'aws-cdk-lib';
import * as codepipeline from 'aws-cdk-lib/aws-codepipeline';
import * as codepipeline_actions from 'aws-cdk-lib/aws-codepipeline-actions';
import { CodePipeline, CodePipelineSource, ShellStep } from 'aws-cdk-lib/pipelines';
export class PipelineStack extends cdk.Stack {
constructor(scope: cdk.App, id: string, props?: cdk.StackProps) {
super(scope, id, props);
const pipeline = new CodePipeline(this, 'Pipeline', {
pipelineName: 'MyAppPipeline',
synth: new ShellStep('Synth', {
input: CodePipelineSource.gitHub('seu-user/seu-repo', 'main', {
authentication: cdk.SecretValue.secretsManager('github-token'),
}),
commands: [
'npm install',
'npm run build',
'npx cdk synth',
],
}),
});
// Estágio de desenvolvimento
const devStage = pipeline.addStage(
new ApplicationStage(this, 'Dev', {
env: { account: '111111111111', region: 'us-east-1' },
})
);
devStage.addPost(
new ShellStep('TestDev', {
commands: [
'npm install',
'npm test',
],
})
);
// Estágio de produção com aprovação manual
const prodStage = pipeline.addStage(
new ApplicationStage(this, 'Prod', {
env: { account: '222222222222', region: 'us-east-1' },
})
);
prodStage.addPre(
new codepipeline_actions.ManualApprovalAction({
actionName: 'ApproveProduction',
}) as any
);
}
}
export class ApplicationStage extends cdk.Stage {
constructor(scope: cdk.Construct, id: string, props?: cdk.StageProps) {
super(scope, id, props);
new StorageStack(this, 'Storage');
new SecureWebServer(this, 'WebServer', {
instanceType: ec2.InstanceType.of(ec2.InstanceClass.T3, ec2.InstanceSize.MICRO),
machineImage: ec2.AmazonLinuxImage.latestAmazonLinux2(),
} as any);
}
}
O pipeline sincroniza automaticamente com seu repositório GitHub. Quando você faz push, o synth executa, gera o CloudFormation e deploya os estágios sequencialmente. Adicione testes, aprovações manuais e validações conforme necessário.
Boas Práticas e Patterns
Separação de Responsabilidades
Organize seu código em múltiplos arquivos: uma Stack por arquivo, Constructs customizados isolados, configurações centralizadas. Isso torna projetos escaláveis.
Contexto e Configuração
Use valores de contexto para parametrizar comportamentos sem hardcode:
const isDev = this.node.tryGetContext('env') === 'dev';
const bucketRetention = isDev ? cdk.RemovalPolicy.DESTROY : cdk.RemovalPolicy.RETAIN;
Validação em Tempo de Síntese
Valide Constructs durante a síntese para falhar rápido:
if (!props.domainName) {
throw new Error('domainName é obrigatório');
}
Conclusão
Dominar AWS CDK exige entender três pilares: Constructs como blocos reutilizáveis com responsabilidades bem definidas, Stacks como unidades de deployment lógicas e compostas, e CDK Pipelines como a ferramenta que automatiza e torna repetível todo o ciclo de infraestrutura-como-código. Comece com Stacks simples, evolua para Constructs customizados de alto nível, e implemente pipelines quando sua infraestrutura crescer. O poder real vem da composição: Constructs dentro de Stacks dentro de Stages dentro de Pipelines.