What is AWS CDK?
AWS CDK (Cloud Development Kit) is a framework that lets you define AWS infrastructure using programming languages (TypeScript, Python, Java, etc.) instead of writing CloudFormation YAML/JSON directly.
Key concept: CDK is a code generator - it converts your code into CloudFormation templates, then deploys them using CloudFormation service.
What is CloudFormation?
AWS CloudFormation is the underlying AWS service that actually creates and manages your infrastructure resources.
Two deployment methods:
- CloudFormation Stacks: Deploy to single account/region
- CloudFormation StackSets: Deploy same template to multiple accounts/regions
How CDK Works: Complete Technical Flow
1. CDK Synthesis (Local)
Your CDK Code (TypeScript/Python)
↓
cdk synth
↓
CloudFormation Template (JSON/YAML)
↓
Saved to cdk.out/
What happens: CDK reads your code and generates CloudFormation templates locally. No AWS interaction yet.
2. CDK Bootstrap (One-time Setup)
cdk bootstrap aws://ACCOUNT-ID/REGION
Creates in your AWS account:
- S3 bucket:
cdk-hnb659fds-assets-{account}-{region}(stores Lambda code, files) - ECR repository: For Docker images
- IAM roles: For deployment
IAM Roles Created:
cdk-hnb659fds-cfn-exec-role-{account}-{region}
↳ CloudFormation assumes this to create resources
↳ Has AdministratorAccess by default
cdk-hnb659fds-deploy-role-{account}-{region}
↳ CDK uses this to trigger deployments
cdk-hnb659fds-file-publishing-role-{account}-{region}
↳ Uploads assets to S3
cdk-hnb659fds-image-publishing-role-{account}-{region}
↳ Pushes Docker images to ECR
cdk-hnb659fds-lookup-role-{account}-{region}
↳ Queries existing AWS resources during synth
3. CDK Deploy
Step 1: Upload assets to S3
↓
Step 2: CDK calls CloudFormation CreateStack/UpdateStack API
↓
Step 3: CloudFormation assumes cfn-exec-role
↓
Step 4: CloudFormation creates your resources (Lambda, S3, KMS, etc.)
Critical: CDK doesn’t create resources directly - CloudFormation does. CDK orchestrates the process.
CloudFormation Stacks vs StackSets
CloudFormation Stacks (Single Account/Region)
Use case: Deploy infrastructure to one account in one region
# Deploy using CDK
cdk deploy
# Or deploy CloudFormation template directly
aws cloudformation create-stack \
--stack-name my-stack \
--template-body file://template.yaml \
--region us-east-1
Architecture:
┌─────────────────────────────────────┐
│ AWS Account: 111111111111 │
│ │
│ ┌───────────────────────────────┐ │
│ │ CloudFormation Stack │ │
│ │ Region: us-east-1 │ │
│ │ │ │
│ │ Resources: │ │
│ │ - Lambda Function │ │
│ │ - S3 Bucket │ │
│ │ - KMS Key │ │
│ └───────────────────────────────┘ │
└─────────────────────────────────────┘
CloudFormation StackSets (Multi-Account/Region)
Use case: Deploy same infrastructure to multiple accounts/regions simultaneously
# Create StackSet
aws cloudformation create-stack-set \
--stack-set-name my-stack-set \
--template-body file://template.yaml
# Deploy to multiple accounts/regions
aws cloudformation create-stack-instances \
--stack-set-name my-stack-set \
--accounts 111111111111 222222222222 333333333333 \
--regions us-east-1 eu-west-1 ap-northeast-1
Architecture:
┌──────────────────────────────────────────────────────────────┐
│ Management Account: 999999999999 │
│ │
│ ┌────────────────────────────────────────────────────────┐ │
│ │ CloudFormation StackSet │ │
│ │ (Template stored here) │ │
│ └────────────────────────────────────────────────────────┘ │
└──────────────────────────────────────────────────────────────┘
│
┌──────────────────┼──────────────────┐
↓ ↓ ↓
┌───────────────┐ ┌───────────────┐ ┌───────────────┐
│ Account: 1111 │ │ Account: 2222 │ │ Account: 3333 │
│ │ │ │ │ │
│ Stack Instance│ │ Stack Instance│ │ Stack Instance│
│ us-east-1 │ │ us-east-1 │ │ us-east-1 │
│ │ │ │ │ │
│ - Lambda │ │ - Lambda │ │ - Lambda │
│ - S3 │ │ - S3 │ │ - S3 │
│ - KMS │ │ - KMS │ │ - KMS │
└───────────────┘ └───────────────┘ └───────────────┘
IAM Roles: Who Does What?
Regular CDK Deploy (Single Account)
You (AWS credentials)
↓
CDK CLI
↓ (uses)
cdk-hnb659fds-deploy-role
↓ (calls CloudFormation API)
CloudFormation Service
↓ (assumes)
cdk-hnb659fds-cfn-exec-role
↓ (creates)
Your Resources (Lambda, S3, KMS, etc.)
Key role: cdk-hnb659fds-cfn-exec-role needs permissions for all resources you’re creating (has AdministratorAccess by default).
CloudFormation StackSets (Multi-Account)
Roles you must create manually:
In Management Account:
AWSCloudFormationStackSetAdministrationRole
↳ Trust: CloudFormation service
↳ Permission: sts:AssumeRole to execution role in member accounts
In Each Member Account:
AWSCloudFormationStackSetExecutionRole
↳ Trust: Management account
↳ Permission: AdministratorAccess (or specific permissions like KMS)
Deployment flow:
You (in management account)
↓
aws cloudformation create-stack-instances
↓ (uses)
AWSCloudFormationStackSetAdministrationRole
↓ (assumes role in each member account)
AWSCloudFormationStackSetExecutionRole (in member account)
↓ (CloudFormation creates resources)
Your Resources in member account
Converting CDK to StackSets
Important: CDK doesn’t have native StackSets support. You must do this manually:
Step 1: Generate CloudFormation Template
# Synthesize CDK to CloudFormation
cdk synth > template.yaml
Step 2: Create StackSet from Template
# Create StackSet
aws cloudformation create-stack-set \
--stack-set-name my-cdk-stack-set \
--template-body file://template.yaml \
--capabilities CAPABILITY_IAM
# Deploy to multiple accounts
aws cloudformation create-stack-instances \
--stack-set-name my-cdk-stack-set \
--accounts 111111111111 222222222222 \
--regions us-east-1 eu-west-1
Why this is complex:
- You lose CDK’s deployment automation
- Must manually create IAM roles in every target account
- No automatic updates when you change CDK code
Modern Alternatives: CDK Pipelines
Better approach for multi-account CDK deployment: Use CDK Pipelines.
What is CDK Pipelines?
A CDK construct that creates a CI/CD pipeline to automatically deploy your CDK app to multiple accounts/regions.
Architecture:
┌─────────────────────────────────────────────────────────────┐
│ Pipeline Account: 999999999999 │
│ │
│ Git Repository (GitHub/CodeCommit) │
│ ↓ │
│ ┌──────────────────────────────────────────────────────┐ │
│ │ CodePipeline │ │
│ │ │ │
│ │ Source → Synth → Deploy Dev → Deploy Prod │ │
│ └──────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────┘
│ │
↓ ↓
┌───────────────────┐ ┌───────────────────┐
│ Dev Account: 1111 │ │ Prod Account: 2222│
│ │ │ │
│ CDK Stack │ │ CDK Stack │
│ - Lambda │ │ - Lambda │
│ - S3 │ │ - S3 │
│ - KMS │ │ - KMS │
└───────────────────┘ └───────────────────┘
CDK Pipelines IAM Roles
In Pipeline Account:
CodePipelineRole
↳ Orchestrates pipeline stages
CodeBuildRole
↳ Runs cdk synth and cdk deploy
In Each Target Account (created automatically by CDK):
cdk-hnb659fds-deploy-role-{account}-{region}
↳ Trust: Pipeline account
↳ Allows pipeline to deploy to this account
cdk-hnb659fds-cfn-exec-role-{account}-{region}
↳ CloudFormation uses this to create resources
Deployment flow:
Git Push
↓
CodePipeline (PipelineRole)
↓
CodeBuild (CodeBuildRole)
↓ (assumes)
deploy-role in target account
↓
CloudFormation (assumes cfn-exec-role)
↓
Creates resources in target account
Minimal CDK Pipelines Example
import { pipelines } from 'aws-cdk-lib';
import { Stack, StackProps, Stage } from 'aws-cdk-lib';
// Define your application stage
class MyAppStage extends Stage {
constructor(scope: Construct, id: string, props?: StageProps) {
super(scope, id, props);
// Your stacks here
new MyStack(this, 'MyStack');
}
}
// Define the pipeline
class PipelineStack extends Stack {
constructor(scope: Construct, id: string, props?: StackProps) {
super(scope, id, props);
const pipeline = new pipelines.CodePipeline(this, 'Pipeline', {
synth: new pipelines.ShellStep('Synth', {
input: pipelines.CodePipelineSource.gitHub('owner/repo', 'main'),
commands: [
'npm ci',
'npx cdk synth'
]
})
});
// Deploy to dev account
pipeline.addStage(new MyAppStage(this, 'Dev', {
env: { account: '111111111111', region: 'us-east-1' }
}));
// Deploy to prod account
pipeline.addStage(new MyAppStage(this, 'Prod', {
env: { account: '222222222222', region: 'us-east-1' }
}));
}
}
What happens:
- You push code to GitHub
- CodePipeline automatically triggers
- Runs
cdk synthto generate templates - Deploys to dev account
- Deploys to prod account
AWS Organizations Integration
AWS Organizations manages multiple AWS accounts in a hierarchy.
Organization Structure
Management Account (root)
├── OU: Production
│ ├── Account: prod-app (111111111111)
│ └── Account: prod-data (222222222222)
└── OU: Development
├── Account: dev (333333333333)
└── Account: staging (444444444444)
Key Features
1. Centralized Account Management
- Create new AWS accounts programmatically
- Consolidated billing (one bill for all accounts)
2. Service Control Policies (SCPs) Enforce rules across all accounts:
{
"Version": "2012-10-17",
"Statement": [{
"Effect": "Deny",
"Action": "kms:ScheduleKeyDeletion",
"Resource": "*"
}]
}
This prevents anyone in child accounts from deleting KMS keys, even with admin permissions.
3. StackSets with Auto-Deployment
# Deploy to all accounts in an OU
aws cloudformation create-stack-set \
--stack-set-name security-baseline \
--template-body file://template.yaml \
--deployment-targets OrganizationalUnitIds=ou-xxxx \
--auto-deployment Enabled=true
When you create a new account in that OU, the StackSet automatically deploys to it.
Organizations IAM Roles
OrganizationAccountAccessRole (created automatically):
Created in: Every member account
Trust: Management account
Permission: AdministratorAccess
Purpose: Allow management account to access member accounts
Usage:
# From management account, assume role in member account
aws sts assume-role \
--role-arn arn:aws:iam::111111111111:role/OrganizationAccountAccessRole \
--role-session-name my-session
Comparison: Deployment Methods
| Method | Use Case | Automation | Cross-Account Setup | Best For |
|---|---|---|---|---|
| CDK Deploy | Single account/region | Manual (cdk deploy) | N/A | Simple deployments |
| StackSets | Multiple accounts/regions | Manual (AWS CLI) | Manual IAM roles | One-time deployments |
| CDK Pipelines | Multiple accounts/regions | Automatic (Git push) | Automatic | Continuous deployment |
| Organizations + StackSets | All accounts in OU | Automatic (new accounts) | Manual IAM roles | Enterprise baseline |
When to Use What?
Single account deployment:
- Use:
cdk deploy - Simple, fast, no extra setup
Multiple accounts, continuous deployment:
- Use: CDK Pipelines
- Automated, Git-based workflow
- Best for application deployments
Multiple accounts, one-time setup:
- Use: CloudFormation StackSets
- Good for security baselines, compliance policies
- Manual but powerful
Enterprise with many accounts:
- Use: AWS Organizations + StackSets
- Centralized management
- Auto-deploy to new accounts
KMS Permissions in Multi-Account Scenarios
Regular CDK Deploy
Role that needs KMS permissions: cdk-hnb659fds-cfn-exec-role
Already has AdministratorAccess by default, so KMS permissions included.
StackSets
Role that needs KMS permissions: AWSCloudFormationStackSetExecutionRole (in each member account)
You must add KMS permissions manually:
{
"Version": "2012-10-17",
"Statement": [{
"Effect": "Allow",
"Action": [
"kms:CreateKey",
"kms:DescribeKey",
"kms:PutKeyPolicy",
"kms:TagResource"
],
"Resource": "*"
}]
}
CDK Pipelines
Role that needs KMS permissions: cdk-hnb659fds-cfn-exec-role (in each target account)
Created automatically by CDK bootstrap with AdministratorAccess.
Key Takeaways
- CDK generates CloudFormation templates - it doesn’t create resources directly
- CloudFormation service creates resources - using IAM roles you configure
- StackSets = same template to many accounts - requires manual IAM role setup
- CDK Pipelines = modern multi-account deployment - automated, Git-based
- Organizations = account management - centralized control, SCPs, auto-deployment
- For KMS in multi-account: Ensure CloudFormation execution role has KMS permissions
Verification Commands
# Check if CDK is bootstrapped
aws cloudformation describe-stacks \
--stack-name CDKToolkit \
--region us-east-1
# List CDK IAM roles
aws iam list-roles \
--query 'Roles[?contains(RoleName, `cdk-`)].RoleName'
# Check CloudFormation stacks
aws cloudformation list-stacks \
--stack-status-filter CREATE_COMPLETE UPDATE_COMPLETE
# Check if Organizations is enabled
aws organizations describe-organization
# List StackSets
aws cloudformation list-stack-sets