Try Live
Add Docs
Rankings
Pricing
Docs
Install
Theme
Install
Docs
Pricing
More...
More...
Try Live
Rankings
Enterprise
Create API Key
Add Docs
AWS Cloud Development Kit
https://github.com/aws/aws-cdk
Admin
The AWS Cloud Development Kit (AWS CDK) is an open-source framework for defining cloud
...
Tokens:
1,080,166
Snippets:
4,617
Trust Score:
9.6
Update:
2 weeks ago
Context
Skills
Chat
Benchmark
91.1
Suggestions
Latest
Show doc for...
Code
Info
Show Results
Context Summary (auto-generated)
Raw
Copy
Link
# AWS Cloud Development Kit (AWS CDK) The AWS Cloud Development Kit (AWS CDK) is an open-source software development framework for defining cloud infrastructure in code and provisioning it through AWS CloudFormation. It offers a high-level object-oriented abstraction to define AWS resources imperatively using modern programming languages including TypeScript, JavaScript, Python, Java, .NET, and Go. The CDK enables developers to use familiar programming constructs like loops, conditionals, and object-oriented design patterns to create reusable cloud components called constructs, which are composed together into stacks that map directly to CloudFormation stacks. The CDK architecture consists of three main layers: the CDK CLI for interacting with applications, the CDK framework for defining apps/stacks/constructs, and the AWS Construct Library which provides pre-built constructs for all AWS services. Constructs come in three levels: L1 (low-level CFN resources), L2 (high-level abstractions with sensible defaults), and L3 (patterns combining multiple resources). The framework handles asset bundling, environment-aware deployments, cross-stack references, and automatic IAM permission management, allowing developers to focus on business logic rather than CloudFormation syntax. --- ## CDK CLI Installation and Project Initialization The AWS CDK CLI is the command-line tool for interacting with CDK applications. It handles synthesis, deployment, and diffing of CloudFormation templates. ```bash # Install CDK CLI globally via npm npm install -g aws-cdk # Verify installation cdk --version # Initialize a new TypeScript CDK project mkdir my-cdk-app && cd my-cdk-app cdk init sample-app --language=typescript # Initialize with other languages cdk init app --language=python cdk init app --language=java cdk init app --language=csharp cdk init app --language=go # Bootstrap your AWS environment (required once per account/region) cdk bootstrap aws://ACCOUNT-NUMBER/REGION # Synthesize CloudFormation template cdk synth # Deploy stack to AWS cdk deploy # Show differences between deployed stack and local changes cdk diff # List all stacks in the app cdk list # Destroy deployed stack cdk destroy ``` --- ## Creating a CDK App and Stack The `App` is the root of the CDK construct tree, and `Stack` is the unit of deployment that maps to a CloudFormation stack. Every CDK application starts with an App containing one or more Stacks. ```typescript import { App, Stack, StackProps, CfnOutput, Duration, RemovalPolicy } from 'aws-cdk-lib'; import { Construct } from 'constructs'; import * as s3 from 'aws-cdk-lib/aws-s3'; import * as lambda from 'aws-cdk-lib/aws-lambda'; // Define a custom stack class class MyApplicationStack extends Stack { public readonly bucketArn: string; constructor(scope: Construct, id: string, props?: StackProps) { super(scope, id, props); // Create an S3 bucket const bucket = new s3.Bucket(this, 'MyBucket', { versioned: true, removalPolicy: RemovalPolicy.DESTROY, autoDeleteObjects: true, }); // Create a Lambda function const fn = new lambda.Function(this, 'MyFunction', { runtime: lambda.Runtime.NODEJS_LATEST, handler: 'index.handler', code: lambda.Code.fromInline(` exports.handler = async (event) => { console.log('Event:', JSON.stringify(event)); return { statusCode: 200, body: 'Hello from CDK!' }; }; `), timeout: Duration.seconds(30), environment: { BUCKET_NAME: bucket.bucketName, }, }); // Grant Lambda read/write access to the bucket bucket.grantReadWrite(fn); // Export values this.bucketArn = bucket.bucketArn; new CfnOutput(this, 'BucketName', { value: bucket.bucketName }); new CfnOutput(this, 'FunctionArn', { value: fn.functionArn }); } } // Create the CDK app const app = new App(); // Instantiate stacks for different environments new MyApplicationStack(app, 'DevStack', { env: { account: process.env.CDK_DEFAULT_ACCOUNT, region: 'us-east-1' }, tags: { Environment: 'Development' }, }); new MyApplicationStack(app, 'ProdStack', { env: { account: '123456789012', region: 'us-west-2' }, tags: { Environment: 'Production' }, terminationProtection: true, }); app.synth(); ``` --- ## Amazon S3 Bucket The S3 Bucket construct provides a high-level abstraction for creating and configuring Amazon S3 buckets with encryption, versioning, lifecycle rules, and access policies. ```typescript 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 * as kms from 'aws-cdk-lib/aws-kms'; const stack = new cdk.Stack(); // Basic bucket const simpleBucket = new s3.Bucket(stack, 'SimpleBucket'); // KMS-encrypted bucket with custom key const encryptionKey = new kms.Key(stack, 'BucketKey', { enableKeyRotation: true, }); const encryptedBucket = new s3.Bucket(stack, 'EncryptedBucket', { encryption: s3.BucketEncryption.KMS, encryptionKey: encryptionKey, bucketKeyEnabled: true, enforceSSL: true, }); // Bucket with versioning and lifecycle rules const versionedBucket = new s3.Bucket(stack, 'VersionedBucket', { versioned: true, lifecycleRules: [ { id: 'DeleteOldVersions', noncurrentVersionExpiration: cdk.Duration.days(90), noncurrentVersionsToRetain: 3, }, { id: 'TransitionToGlacier', transitions: [ { storageClass: s3.StorageClass.GLACIER, transitionAfter: cdk.Duration.days(180), }, ], }, ], }); // Static website hosting bucket const websiteBucket = new s3.Bucket(stack, 'WebsiteBucket', { websiteIndexDocument: 'index.html', websiteErrorDocument: 'error.html', publicReadAccess: true, blockPublicAccess: s3.BlockPublicAccess.BLOCK_ACLS, removalPolicy: cdk.RemovalPolicy.DESTROY, autoDeleteObjects: true, }); // Add bucket policy encryptedBucket.addToResourcePolicy( new iam.PolicyStatement({ actions: ['s3:GetObject'], resources: [encryptedBucket.arnForObjects('public/*')], principals: [new iam.AnyPrincipal()], conditions: { 'Bool': { 'aws:SecureTransport': 'true' }, }, }) ); // Import existing bucket const existingBucket = s3.Bucket.fromBucketName(stack, 'ImportedBucket', 'my-existing-bucket'); // Access bucket properties console.log('Bucket ARN:', encryptedBucket.bucketArn); console.log('Bucket Name:', encryptedBucket.bucketName); console.log('Website URL:', websiteBucket.bucketWebsiteUrl); ``` --- ## AWS Lambda Function The Lambda Function construct creates serverless functions with automatic IAM role creation, environment configuration, VPC integration, and event source mappings. ```typescript import * as cdk from 'aws-cdk-lib'; import * as lambda from 'aws-cdk-lib/aws-lambda'; import * as logs from 'aws-cdk-lib/aws-logs'; import * as iam from 'aws-cdk-lib/aws-iam'; import * as path from 'path'; const stack = new cdk.Stack(); // Basic Lambda function from inline code const inlineFunction = new lambda.Function(stack, 'InlineFunction', { runtime: lambda.Runtime.NODEJS_LATEST, handler: 'index.handler', code: lambda.Code.fromInline(` exports.handler = async (event) => { return { statusCode: 200, body: JSON.stringify(event) }; }; `), }); // Lambda function from local directory const assetFunction = new lambda.Function(stack, 'AssetFunction', { runtime: lambda.Runtime.PYTHON_3_12, handler: 'main.handler', code: lambda.Code.fromAsset(path.join(__dirname, 'lambda-handler')), timeout: cdk.Duration.minutes(5), memorySize: 512, environment: { LOG_LEVEL: 'INFO', TABLE_NAME: 'my-table', }, logRetention: logs.RetentionDays.ONE_WEEK, tracing: lambda.Tracing.ACTIVE, architecture: lambda.Architecture.ARM_64, }); // Lambda with custom IAM role const customRole = new iam.Role(stack, 'LambdaRole', { assumedBy: new iam.ServicePrincipal('lambda.amazonaws.com'), managedPolicies: [ iam.ManagedPolicy.fromAwsManagedPolicyName('service-role/AWSLambdaBasicExecutionRole'), ], }); const functionWithRole = new lambda.Function(stack, 'FunctionWithRole', { runtime: lambda.Runtime.NODEJS_LATEST, handler: 'index.handler', code: lambda.Code.fromInline('exports.handler = async () => ({})'), role: customRole, }); // Docker-based Lambda function const dockerFunction = new lambda.DockerImageFunction(stack, 'DockerFunction', { code: lambda.DockerImageCode.fromImageAsset(path.join(__dirname, 'docker-handler')), memorySize: 1024, timeout: cdk.Duration.minutes(15), architecture: lambda.Architecture.ARM_64, }); // Add function URL for HTTP access const functionUrl = assetFunction.addFunctionUrl({ authType: lambda.FunctionUrlAuthType.NONE, cors: { allowedOrigins: ['https://example.com'], allowedMethods: [lambda.HttpMethod.GET, lambda.HttpMethod.POST], }, }); // Create function alias and version const version = assetFunction.currentVersion; const alias = new lambda.Alias(stack, 'ProdAlias', { aliasName: 'prod', version: version, provisionedConcurrentExecutions: 5, }); // Grant permissions assetFunction.grantInvoke(new iam.ServicePrincipal('apigateway.amazonaws.com')); new cdk.CfnOutput(stack, 'FunctionUrl', { value: functionUrl.url }); ``` --- ## Amazon DynamoDB Table The DynamoDB TableV2 construct creates global tables with support for replicas, global secondary indexes, auto-scaling, and encryption. TableV2 is recommended for all use cases. ```typescript import * as cdk from 'aws-cdk-lib'; import * as dynamodb from 'aws-cdk-lib/aws-dynamodb'; import * as iam from 'aws-cdk-lib/aws-iam'; const app = new cdk.App(); const stack = new cdk.Stack(app, 'DynamoStack', { env: { region: 'us-west-2' }, }); // Basic table with partition key const simpleTable = new dynamodb.TableV2(stack, 'SimpleTable', { partitionKey: { name: 'pk', type: dynamodb.AttributeType.STRING }, removalPolicy: cdk.RemovalPolicy.DESTROY, }); // Table with sort key and GSI const tableWithGsi = new dynamodb.TableV2(stack, 'TableWithGSI', { partitionKey: { name: 'pk', type: dynamodb.AttributeType.STRING }, sortKey: { name: 'sk', type: dynamodb.AttributeType.STRING }, billing: dynamodb.Billing.onDemand(), pointInTimeRecoverySpecification: { pointInTimeRecoveryEnabled: true, }, globalSecondaryIndexes: [ { indexName: 'GSI1', partitionKey: { name: 'gsi1pk', type: dynamodb.AttributeType.STRING }, sortKey: { name: 'gsi1sk', type: dynamodb.AttributeType.NUMBER }, projectionType: dynamodb.ProjectionType.ALL, }, ], timeToLiveAttribute: 'ttl', }); // Global table with replicas const globalTable = new dynamodb.TableV2(stack, 'GlobalTable', { partitionKey: { name: 'pk', type: dynamodb.AttributeType.STRING }, billing: dynamodb.Billing.provisioned({ readCapacity: dynamodb.Capacity.autoscaled({ maxCapacity: 100 }), writeCapacity: dynamodb.Capacity.autoscaled({ maxCapacity: 100 }), }), replicas: [ { region: 'us-east-1' }, { region: 'eu-west-1', tableClass: dynamodb.TableClass.STANDARD_INFREQUENT_ACCESS, }, ], }); // Provisioned table with auto-scaling const provisionedTable = new dynamodb.TableV2(stack, 'ProvisionedTable', { partitionKey: { name: 'id', type: dynamodb.AttributeType.STRING }, billing: dynamodb.Billing.provisioned({ readCapacity: dynamodb.Capacity.autoscaled({ minCapacity: 5, maxCapacity: 100, targetUtilizationPercent: 70, }), writeCapacity: dynamodb.Capacity.fixed(10), }), tableClass: dynamodb.TableClass.STANDARD_INFREQUENT_ACCESS, contributorInsightsSpecification: { enabled: true }, deletionProtection: true, }); // Grant permissions to Lambda function declare const fn: lambda.Function; simpleTable.grantReadWriteData(fn); tableWithGsi.grantReadData(fn); // Import existing table const importedTable = dynamodb.TableV2.fromTableName(stack, 'ImportedTable', 'existing-table'); // Output table information new cdk.CfnOutput(stack, 'TableArn', { value: simpleTable.tableArn }); new cdk.CfnOutput(stack, 'TableName', { value: simpleTable.tableName }); ``` --- ## Amazon API Gateway REST API The API Gateway construct creates REST APIs with Lambda integrations, authorizers, usage plans, and custom domains. LambdaRestApi provides a simplified interface for Lambda-backed APIs. ```typescript import * as cdk from 'aws-cdk-lib'; import * as apigateway from 'aws-cdk-lib/aws-apigateway'; import * as lambda from 'aws-cdk-lib/aws-lambda'; import * as cognito from 'aws-cdk-lib/aws-cognito'; const stack = new cdk.Stack(); // Create Lambda handler const handler = new lambda.Function(stack, 'ApiHandler', { runtime: lambda.Runtime.NODEJS_LATEST, handler: 'index.handler', code: lambda.Code.fromInline(` exports.handler = async (event) => { return { statusCode: 200, headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ message: 'Hello from API Gateway!' }), }; }; `), }); // Simple Lambda REST API (proxy mode) const lambdaApi = new apigateway.LambdaRestApi(stack, 'LambdaApi', { handler: handler, proxy: true, deployOptions: { stageName: 'prod', throttlingRateLimit: 1000, throttlingBurstLimit: 500, }, }); // Custom REST API with resources and methods const api = new apigateway.RestApi(stack, 'CustomApi', { restApiName: 'My Custom API', description: 'API for managing resources', deployOptions: { stageName: 'v1', loggingLevel: apigateway.MethodLoggingLevel.INFO, dataTraceEnabled: true, }, defaultCorsPreflightOptions: { allowOrigins: apigateway.Cors.ALL_ORIGINS, allowMethods: apigateway.Cors.ALL_METHODS, allowHeaders: ['Content-Type', 'Authorization'], }, }); // Add resources and methods const items = api.root.addResource('items'); items.addMethod('GET', new apigateway.LambdaIntegration(handler)); items.addMethod('POST', new apigateway.LambdaIntegration(handler)); const item = items.addResource('{itemId}'); item.addMethod('GET', new apigateway.LambdaIntegration(handler)); item.addMethod('PUT', new apigateway.LambdaIntegration(handler)); item.addMethod('DELETE', new apigateway.LambdaIntegration(handler)); // Cognito User Pool authorizer const userPool = new cognito.UserPool(stack, 'UserPool'); const authorizer = new apigateway.CognitoUserPoolsAuthorizer(stack, 'Authorizer', { cognitoUserPools: [userPool], }); const secureResource = api.root.addResource('secure'); secureResource.addMethod('GET', new apigateway.LambdaIntegration(handler), { authorizer: authorizer, authorizationType: apigateway.AuthorizationType.COGNITO, }); // Usage plan and API key const plan = api.addUsagePlan('UsagePlan', { name: 'Standard', throttle: { rateLimit: 100, burstLimit: 50, }, quota: { limit: 10000, period: apigateway.Period.MONTH, }, }); const apiKey = api.addApiKey('ApiKey'); plan.addApiKey(apiKey); plan.addApiStage({ stage: api.deploymentStage }); new cdk.CfnOutput(stack, 'ApiUrl', { value: api.url }); ``` --- ## Amazon VPC and EC2 The VPC construct creates virtual private clouds with subnets, NAT gateways, and routing. EC2 constructs provide instances, security groups, and auto-scaling groups. ```typescript 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'; const stack = new cdk.Stack(); // Create VPC with public and private subnets const vpc = new ec2.Vpc(stack, 'MyVpc', { maxAzs: 3, natGateways: 1, subnetConfiguration: [ { name: 'Public', subnetType: ec2.SubnetType.PUBLIC, cidrMask: 24, }, { name: 'Private', subnetType: ec2.SubnetType.PRIVATE_WITH_EGRESS, cidrMask: 24, }, { name: 'Isolated', subnetType: ec2.SubnetType.PRIVATE_ISOLATED, cidrMask: 24, }, ], }); // Create security group const webSg = new ec2.SecurityGroup(stack, 'WebSecurityGroup', { vpc, description: 'Allow HTTP and HTTPS traffic', allowAllOutbound: true, }); webSg.addIngressRule(ec2.Peer.anyIpv4(), ec2.Port.tcp(80), 'Allow HTTP'); webSg.addIngressRule(ec2.Peer.anyIpv4(), ec2.Port.tcp(443), 'Allow HTTPS'); // Create EC2 instance const instance = new ec2.Instance(stack, 'WebServer', { vpc, vpcSubnets: { subnetType: ec2.SubnetType.PRIVATE_WITH_EGRESS }, instanceType: ec2.InstanceType.of(ec2.InstanceClass.T3, ec2.InstanceSize.MICRO), machineImage: ec2.MachineImage.latestAmazonLinux2023(), securityGroup: webSg, keyPair: ec2.KeyPair.fromKeyPairName(stack, 'KeyPair', 'my-key-pair'), }); // Add user data script instance.addUserData( 'yum update -y', 'yum install -y httpd', 'systemctl start httpd', 'systemctl enable httpd', 'echo "Hello from CDK!" > /var/www/html/index.html' ); // Create Application Load Balancer target const userData = ec2.UserData.forLinux(); userData.addCommands( '#!/bin/bash', 'yum update -y', 'yum install -y nginx', 'systemctl start nginx' ); // Auto Scaling Group const asg = new ec2.AutoScalingGroup(stack, 'ASG', { vpc, instanceType: ec2.InstanceType.of(ec2.InstanceClass.T3, ec2.InstanceSize.SMALL), machineImage: ec2.MachineImage.latestAmazonLinux2023(), minCapacity: 1, maxCapacity: 10, desiredCapacity: 2, userData, vpcSubnets: { subnetType: ec2.SubnetType.PRIVATE_WITH_EGRESS }, }); // VPC Endpoints for AWS services vpc.addGatewayEndpoint('S3Endpoint', { service: ec2.GatewayVpcEndpointAwsService.S3, }); vpc.addInterfaceEndpoint('SecretsManagerEndpoint', { service: ec2.InterfaceVpcEndpointAwsService.SECRETS_MANAGER, subnets: { subnetType: ec2.SubnetType.PRIVATE_WITH_EGRESS }, }); // Import existing VPC const existingVpc = ec2.Vpc.fromLookup(stack, 'ExistingVpc', { vpcId: 'vpc-12345678', }); new cdk.CfnOutput(stack, 'VpcId', { value: vpc.vpcId }); new cdk.CfnOutput(stack, 'InstanceId', { value: instance.instanceId }); ``` --- ## IAM Roles and Policies The IAM constructs manage AWS Identity and Access Management resources including roles, policies, users, and groups. CDK automatically creates and manages IAM roles with least-privilege permissions. ```typescript import * as cdk from 'aws-cdk-lib'; import * as iam from 'aws-cdk-lib/aws-iam'; const stack = new cdk.Stack(); // Create IAM Role for Lambda const lambdaRole = new iam.Role(stack, 'LambdaRole', { assumedBy: new iam.ServicePrincipal('lambda.amazonaws.com'), description: 'Role for Lambda function', roleName: 'MyLambdaExecutionRole', maxSessionDuration: cdk.Duration.hours(1), }); // Add managed policies lambdaRole.addManagedPolicy( iam.ManagedPolicy.fromAwsManagedPolicyName('service-role/AWSLambdaBasicExecutionRole') ); // Add inline policy lambdaRole.addToPolicy( new iam.PolicyStatement({ effect: iam.Effect.ALLOW, actions: ['s3:GetObject', 's3:PutObject'], resources: ['arn:aws:s3:::my-bucket/*'], }) ); // Create custom managed policy const customPolicy = new iam.ManagedPolicy(stack, 'CustomPolicy', { managedPolicyName: 'MyCustomPolicy', statements: [ new iam.PolicyStatement({ actions: ['dynamodb:GetItem', 'dynamodb:PutItem', 'dynamodb:Query'], resources: ['arn:aws:dynamodb:*:*:table/MyTable'], }), new iam.PolicyStatement({ actions: ['sqs:SendMessage', 'sqs:ReceiveMessage'], resources: ['arn:aws:sqs:*:*:MyQueue'], conditions: { StringEquals: { 'aws:RequestedRegion': 'us-east-1', }, }, }), ], }); // Role for EC2 instance const ec2Role = new iam.Role(stack, 'EC2Role', { assumedBy: new iam.ServicePrincipal('ec2.amazonaws.com'), }); ec2Role.addManagedPolicy(customPolicy); // Cross-account role const crossAccountRole = new iam.Role(stack, 'CrossAccountRole', { assumedBy: new iam.AccountPrincipal('123456789012'), externalIds: ['my-external-id'], }); // Role with trust policy conditions const conditionalRole = new iam.Role(stack, 'ConditionalRole', { assumedBy: new iam.FederatedPrincipal( 'arn:aws:iam::123456789012:saml-provider/MyProvider', { StringEquals: { 'SAML:aud': 'https://signin.aws.amazon.com/saml', }, }, 'sts:AssumeRoleWithSAML' ), }); // Grant permissions between resources declare const bucket: s3.Bucket; declare const fn: lambda.Function; bucket.grantRead(lambdaRole); bucket.grantReadWrite(fn); // CDK automatically manages fn's role // Import existing role const existingRole = iam.Role.fromRoleArn( stack, 'ExistingRole', 'arn:aws:iam::123456789012:role/ExistingRole', { mutable: false } ); new cdk.CfnOutput(stack, 'RoleArn', { value: lambdaRole.roleArn }); ``` --- ## Amazon SNS and SQS The SNS and SQS constructs create messaging infrastructure for pub/sub and queue-based communication patterns with support for encryption, dead-letter queues, and subscriptions. ```typescript import * as cdk from 'aws-cdk-lib'; import * as sns from 'aws-cdk-lib/aws-sns'; import * as sqs from 'aws-cdk-lib/aws-sqs'; import * as subscriptions from 'aws-cdk-lib/aws-sns-subscriptions'; import * as lambda from 'aws-cdk-lib/aws-lambda'; const stack = new cdk.Stack(); // Create SNS Topic const topic = new sns.Topic(stack, 'NotificationTopic', { displayName: 'Order Notifications', topicName: 'order-notifications', }); // FIFO Topic with content-based deduplication const fifoTopic = new sns.Topic(stack, 'FifoTopic', { topicName: 'orders.fifo', fifo: true, contentBasedDeduplication: true, }); // Create SQS Queue const queue = new sqs.Queue(stack, 'OrderQueue', { queueName: 'order-processing-queue', visibilityTimeout: cdk.Duration.seconds(300), retentionPeriod: cdk.Duration.days(14), encryption: sqs.QueueEncryption.SQS_MANAGED, enforceSSL: true, }); // Dead Letter Queue const dlq = new sqs.Queue(stack, 'DeadLetterQueue', { queueName: 'order-dlq', retentionPeriod: cdk.Duration.days(14), }); const queueWithDlq = new sqs.Queue(stack, 'QueueWithDLQ', { deadLetterQueue: { queue: dlq, maxReceiveCount: 3, }, }); // FIFO Queue const fifoQueue = new sqs.Queue(stack, 'FifoQueue', { queueName: 'orders.fifo', fifo: true, contentBasedDeduplication: true, deduplicationScope: sqs.DeduplicationScope.MESSAGE_GROUP, fifoThroughputLimit: sqs.FifoThroughputLimit.PER_MESSAGE_GROUP_ID, }); // Subscribe queue to topic topic.addSubscription(new subscriptions.SqsSubscription(queue, { rawMessageDelivery: true, })); // Lambda subscription with filter declare const fn: lambda.Function; topic.addSubscription(new subscriptions.LambdaSubscription(fn, { filterPolicy: { orderType: sns.SubscriptionFilter.stringFilter({ allowlist: ['premium', 'express'], }), amount: sns.SubscriptionFilter.numericFilter({ greaterThan: 100, }), }, })); // Email subscription topic.addSubscription(new subscriptions.EmailSubscription('alerts@example.com')); // URL subscription topic.addSubscription(new subscriptions.UrlSubscription('https://example.com/webhook', { protocol: sns.SubscriptionProtocol.HTTPS, })); // Grant permissions topic.grantPublish(fn); queue.grantConsumeMessages(fn); queue.grantSendMessages(fn); // Lambda event source from SQS import * as lambdaEventSources from 'aws-cdk-lib/aws-lambda-event-sources'; fn.addEventSource(new lambdaEventSources.SqsEventSource(queue, { batchSize: 10, maxBatchingWindow: cdk.Duration.minutes(1), reportBatchItemFailures: true, })); new cdk.CfnOutput(stack, 'TopicArn', { value: topic.topicArn }); new cdk.CfnOutput(stack, 'QueueUrl', { value: queue.queueUrl }); ``` --- ## AWS Step Functions The Step Functions construct creates serverless workflows using state machines. Use with aws-stepfunctions-tasks for AWS service integrations. ```typescript import * as cdk from 'aws-cdk-lib'; import * as sfn from 'aws-cdk-lib/aws-stepfunctions'; import * as tasks from 'aws-cdk-lib/aws-stepfunctions-tasks'; import * as lambda from 'aws-cdk-lib/aws-lambda'; const stack = new cdk.Stack(); // Create Lambda functions for the workflow const validateOrder = new lambda.Function(stack, 'ValidateOrder', { runtime: lambda.Runtime.NODEJS_LATEST, handler: 'index.handler', code: lambda.Code.fromInline(` exports.handler = async (event) => { const { orderId, amount } = event; return { orderId, amount, isValid: amount > 0 }; }; `), }); const processPayment = new lambda.Function(stack, 'ProcessPayment', { runtime: lambda.Runtime.NODEJS_LATEST, handler: 'index.handler', code: lambda.Code.fromInline(` exports.handler = async (event) => { return { ...event, paymentStatus: 'completed' }; }; `), }); // Define workflow states const validateTask = new tasks.LambdaInvoke(stack, 'Validate Order', { lambdaFunction: validateOrder, outputPath: '$.Payload', }); const processPaymentTask = new tasks.LambdaInvoke(stack, 'Process Payment', { lambdaFunction: processPayment, outputPath: '$.Payload', }); const waitState = new sfn.Wait(stack, 'Wait for Processing', { time: sfn.WaitTime.duration(cdk.Duration.seconds(10)), }); const successState = new sfn.Succeed(stack, 'Order Completed'); const failState = new sfn.Fail(stack, 'Order Failed', { error: 'ValidationError', cause: 'Order validation failed', }); // Choice state for conditional branching const checkValidation = new sfn.Choice(stack, 'Is Valid?') .when(sfn.Condition.booleanEquals('$.isValid', true), processPaymentTask) .otherwise(failState); // Parallel state for concurrent execution const parallelNotifications = new sfn.Parallel(stack, 'Send Notifications'); parallelNotifications.branch( new sfn.Pass(stack, 'Email Notification') ); parallelNotifications.branch( new sfn.Pass(stack, 'SMS Notification') ); // Map state for iterating over arrays const processItems = new sfn.Map(stack, 'Process Items', { maxConcurrency: 5, itemsPath: '$.items', }); processItems.itemProcessor(new sfn.Pass(stack, 'Process Item')); // Chain the workflow const definition = validateTask .next(checkValidation); processPaymentTask .next(waitState) .next(parallelNotifications) .next(successState); // Create State Machine const stateMachine = new sfn.StateMachine(stack, 'OrderStateMachine', { stateMachineName: 'OrderProcessingWorkflow', definitionBody: sfn.DefinitionBody.fromChainable(definition), timeout: cdk.Duration.minutes(30), tracingEnabled: true, logs: { destination: new logs.LogGroup(stack, 'StateMachineLogs'), level: sfn.LogLevel.ALL, }, }); // Grant permissions stateMachine.grantStartExecution(new iam.ServicePrincipal('events.amazonaws.com')); new cdk.CfnOutput(stack, 'StateMachineArn', { value: stateMachine.stateMachineArn }); ``` --- ## Custom Resources Custom Resources allow executing arbitrary code during CloudFormation deployment for lookups, custom provisioning, or third-party integrations. The Provider framework handles lifecycle events automatically. ```typescript import * as cdk from 'aws-cdk-lib'; import * as lambda from 'aws-cdk-lib/aws-lambda'; import * as cr from 'aws-cdk-lib/custom-resources'; import * as logs from 'aws-cdk-lib/aws-logs'; import { Construct } from 'constructs'; const stack = new cdk.Stack(); // Simple custom resource using AwsCustomResource for SDK calls const getParameter = new cr.AwsCustomResource(stack, 'GetSSMParameter', { onCreate: { service: 'SSM', action: 'getParameter', parameters: { Name: '/my/parameter/name', WithDecryption: true, }, physicalResourceId: cr.PhysicalResourceId.of('SSMParameterValue'), }, policy: cr.AwsCustomResourcePolicy.fromSdkCalls({ resources: cr.AwsCustomResourcePolicy.ANY_RESOURCE, }), logRetention: logs.RetentionDays.ONE_DAY, }); const parameterValue = getParameter.getResponseField('Parameter.Value'); // Custom resource with Lambda handler const onEventHandler = new lambda.Function(stack, 'OnEventHandler', { runtime: lambda.Runtime.NODEJS_LATEST, handler: 'index.handler', code: lambda.Code.fromInline(` exports.handler = async (event) => { console.log('Event:', JSON.stringify(event)); const { RequestType, ResourceProperties } = event; if (RequestType === 'Create' || RequestType === 'Update') { // Perform custom logic return { PhysicalResourceId: 'custom-resource-' + Date.now(), Data: { Result: 'Success', Value: ResourceProperties.InputValue * 2, }, }; } if (RequestType === 'Delete') { // Cleanup logic return { PhysicalResourceId: event.PhysicalResourceId }; } }; `), }); // Provider with async completion handler const isCompleteHandler = new lambda.Function(stack, 'IsCompleteHandler', { runtime: lambda.Runtime.NODEJS_LATEST, handler: 'index.handler', code: lambda.Code.fromInline(` exports.handler = async (event) => { // Check if async operation is complete return { IsComplete: true }; }; `), }); const provider = new cr.Provider(stack, 'CustomResourceProvider', { onEventHandler: onEventHandler, isCompleteHandler: isCompleteHandler, totalTimeout: cdk.Duration.minutes(30), queryInterval: cdk.Duration.seconds(30), logRetention: logs.RetentionDays.ONE_WEEK, }); // Use the provider const customResource = new cdk.CustomResource(stack, 'MyCustomResource', { serviceToken: provider.serviceToken, properties: { InputValue: 42, Timestamp: Date.now(), }, }); // Create reusable custom construct class DatabaseInitializer extends Construct { public readonly initResult: string; constructor(scope: Construct, id: string, props: { databaseArn: string }) { super(scope, id); const initFunction = new lambda.Function(this, 'InitFunction', { runtime: lambda.Runtime.PYTHON_3_12, handler: 'index.handler', code: lambda.Code.fromInline(` import json def handler(event, context): request_type = event['RequestType'] db_arn = event['ResourceProperties']['DatabaseArn'] if request_type in ['Create', 'Update']: # Initialize database return {'PhysicalResourceId': f'db-init-{db_arn}', 'Data': {'Status': 'Initialized'}} return {'PhysicalResourceId': event.get('PhysicalResourceId', 'unknown')} `), }); const initProvider = new cr.Provider(this, 'Provider', { onEventHandler: initFunction, }); const resource = new cdk.CustomResource(this, 'Resource', { serviceToken: initProvider.serviceToken, properties: { DatabaseArn: props.databaseArn }, }); this.initResult = resource.getAttString('Status'); } } new cdk.CfnOutput(stack, 'CustomValue', { value: customResource.getAttString('Value'), }); ``` --- ## Aspects and Tags Aspects allow applying operations across all constructs in a scope, commonly used for tagging, validation, and enforcing compliance rules. Tags propagate automatically to all taggable resources. ```typescript import * as cdk from 'aws-cdk-lib'; import { Construct, IConstruct } from 'constructs'; import * as s3 from 'aws-cdk-lib/aws-s3'; import * as ec2 from 'aws-cdk-lib/aws-ec2'; const app = new cdk.App(); const stack = new cdk.Stack(app, 'MyStack'); // Apply tags to all resources cdk.Tags.of(stack).add('Environment', 'Production'); cdk.Tags.of(stack).add('Project', 'MyProject'); cdk.Tags.of(stack).add('CostCenter', '12345'); // Conditional tagging cdk.Tags.of(stack).add('DataClassification', 'Confidential', { includeResourceTypes: ['AWS::S3::Bucket', 'AWS::RDS::DBInstance'], }); // Exclude specific resources from tagging cdk.Tags.of(stack).add('AutoDelete', 'true', { excludeResourceTypes: ['AWS::DynamoDB::Table'], }); // Custom Aspect for validation class BucketVersioningChecker implements cdk.IAspect { public visit(node: IConstruct): void { if (node instanceof s3.CfnBucket) { if (!node.versioningConfiguration) { cdk.Annotations.of(node).addWarning( 'Bucket versioning is not enabled. Consider enabling for data protection.' ); } } } } // Custom Aspect for enforcing encryption class EncryptionEnforcer implements cdk.IAspect { public visit(node: IConstruct): void { if (node instanceof s3.CfnBucket) { if (!node.bucketEncryption) { node.bucketEncryption = { serverSideEncryptionConfiguration: [{ serverSideEncryptionByDefault: { sseAlgorithm: 'AES256', }, }], }; } } } } // Custom Aspect for adding security groups class SecurityGroupEnforcer implements cdk.IAspect { constructor(private readonly requiredTags: Record<string, string>) {} public visit(node: IConstruct): void { if (cdk.TagManager.isTaggable(node)) { Object.entries(this.requiredTags).forEach(([key, value]) => { node.tags.setTag(key, value); }); } } } // Apply aspects to the stack cdk.Aspects.of(stack).add(new BucketVersioningChecker()); cdk.Aspects.of(stack).add(new EncryptionEnforcer()); cdk.Aspects.of(stack).add(new SecurityGroupEnforcer({ 'Compliance': 'Required', 'ManagedBy': 'CDK', })); // Aspect with priority (for ordering) cdk.Aspects.of(stack).add(new EncryptionEnforcer(), { priority: cdk.AspectPriority.MUTATING, }); cdk.Aspects.of(stack).add(new BucketVersioningChecker(), { priority: cdk.AspectPriority.READONLY, }); // Create resources that will be processed by aspects new s3.Bucket(stack, 'MyBucket'); // Will get encryption added by aspect new ec2.Vpc(stack, 'MyVpc'); ``` --- ## Cross-Stack References and Nested Stacks CDK automatically handles cross-stack references using CloudFormation exports. Nested stacks help organize large applications and avoid CloudFormation resource limits. ```typescript import * as cdk from 'aws-cdk-lib'; import { Construct } from 'constructs'; import * as s3 from 'aws-cdk-lib/aws-s3'; import * as lambda from 'aws-cdk-lib/aws-lambda'; import * as ec2 from 'aws-cdk-lib/aws-ec2'; import * as cfn from 'aws-cdk-lib/aws-cloudformation'; const app = new cdk.App(); // Infrastructure Stack (shared resources) class InfrastructureStack extends cdk.Stack { public readonly vpc: ec2.Vpc; public readonly bucket: s3.Bucket; constructor(scope: Construct, id: string, props?: cdk.StackProps) { super(scope, id, props); this.vpc = new ec2.Vpc(this, 'SharedVpc', { maxAzs: 2, }); this.bucket = new s3.Bucket(this, 'SharedBucket', { versioned: true, }); // Export values for cross-region references new cdk.CfnOutput(this, 'VpcId', { value: this.vpc.vpcId, exportName: 'SharedVpcId', }); } } // Application Stack (consumes shared resources) interface ApplicationStackProps extends cdk.StackProps { vpc: ec2.Vpc; bucket: s3.Bucket; } class ApplicationStack extends cdk.Stack { constructor(scope: Construct, id: string, props: ApplicationStackProps) { super(scope, id, props); const fn = new lambda.Function(this, 'AppFunction', { runtime: lambda.Runtime.NODEJS_LATEST, handler: 'index.handler', code: lambda.Code.fromInline('exports.handler = async () => ({})'), vpc: props.vpc, // CDK creates cross-stack reference automatically environment: { BUCKET_NAME: props.bucket.bucketName, }, }); // Grant permissions across stacks props.bucket.grantReadWrite(fn); } } // Nested Stack for modular organization class DatabaseNestedStack extends cfn.NestedStack { public readonly tableArn: string; constructor(scope: Construct, id: string, props?: cfn.NestedStackProps) { super(scope, id, props); const table = new dynamodb.TableV2(this, 'Table', { partitionKey: { name: 'pk', type: dynamodb.AttributeType.STRING }, }); this.tableArn = table.tableArn; } } class ApiNestedStack extends cfn.NestedStack { constructor(scope: Construct, id: string, props?: cfn.NestedStackProps) { super(scope, id, props); const api = new apigateway.RestApi(this, 'Api'); api.root.addMethod('GET'); } } // Parent stack with nested stacks class ParentStack extends cdk.Stack { constructor(scope: Construct, id: string, props?: cdk.StackProps) { super(scope, id, props); const dbStack = new DatabaseNestedStack(this, 'Database'); const apiStack = new ApiNestedStack(this, 'Api'); // Access nested stack resources new cdk.CfnOutput(this, 'TableArn', { value: dbStack.tableArn, }); } } // Cross-region references class CrossRegionStack extends cdk.Stack { constructor(scope: Construct, id: string, props?: cdk.StackProps) { super(scope, id, { ...props, crossRegionReferences: true, }); } } // Instantiate stacks with dependencies const infraStack = new InfrastructureStack(app, 'InfraStack', { env: { region: 'us-east-1' }, }); const appStack = new ApplicationStack(app, 'AppStack', { env: { region: 'us-east-1' }, vpc: infraStack.vpc, bucket: infraStack.bucket, }); // Explicit dependency appStack.addDependency(infraStack); new ParentStack(app, 'ParentStack'); app.synth(); ``` --- The AWS CDK fundamentally transforms infrastructure management by bringing software engineering practices—version control, code review, testing, and reusability—to cloud infrastructure. The main use cases include defining complex multi-tier applications with automatic IAM permission management, creating reusable construct libraries for organizational standards, implementing CI/CD pipelines for infrastructure, and migrating existing CloudFormation templates to type-safe constructs. The framework excels at reducing boilerplate code while maintaining full access to CloudFormation capabilities when needed. Integration patterns commonly involve using CDK Pipelines for self-mutating deployments, combining CDK with AWS SAM for local Lambda testing, integrating with existing CloudFormation stacks via imports, and using CDK's asset system for bundling Lambda code and Docker images. The construct library's layered architecture (L1/L2/L3) allows teams to work at the appropriate abstraction level—using high-level patterns for rapid development while dropping to low-level resources for fine-grained control. Organizations typically structure CDK applications with shared infrastructure in base stacks, environment-specific configurations via context and stack props, and compliance enforcement through Aspects.