This is how you make your AWS CDK pipeline build and deploy itself, and it’s way more powerful than just updating your app.
Imagine this: you’ve got a CDK stack deploying your app. Now, you want to update that CDK stack itself. Normally, you’d have to manually bootstrap your CDK environment or run a separate pipeline to update the CDK. But with a self-mutating pipeline, the pipeline itself contains the CDK code that defines its own infrastructure. When you push changes to your CDK code, the pipeline triggers, and as part of its execution, it updates the very infrastructure that’s running it.
Let’s see this in action. We’ll use AWS CodePipeline and CodeBuild, orchestrated by CDK.
Here’s a simplified CDK snippet for the pipeline. Notice how CodePipeline is defined within the CDK app that also defines the application stacks.
from aws_cdk import (
core as cdk,
aws_codepipeline as codepipeline,
aws_codepipeline_actions as actions,
aws_codecommit as codecommit,
aws_codebuild as codebuild,
aws_iam as iam,
)
class SelfMutatingPipelineStack(cdk.Stack):
def __init__(self, scope: cdk.Construct, id: str, **kwargs) -> None:
super().__init__(scope, id, **kwargs)
# 1. Source: CodeCommit repository
repo = codecommit.Repository(
self, "MySelfMutatingRepo",
repository_name="my-self-mutating-cdk-repo"
)
# 2. Artifacts bucket (if not using default)
# artifact_bucket = s3.Bucket(self, "PipelineArtifactBucket", versioned=True)
# 3. Pipeline definition
pipeline = codepipeline.Pipeline(
self, "MyPipeline",
pipeline_name="my-cdk-self-mutating-pipeline",
# artifact_bucket=artifact_bucket, # Uncomment if using a specific bucket
role=iam.Role(
self, "PipelineRole",
assumed_by=iam.ServicePrincipal("codepipeline.amazonaws.com"),
managed_policies=[
iam.ManagedPolicy.from_aws_managed_policy_name("AdministratorAccess") # For simplicity, use a more restricted role in production!
]
)
)
# 4. Source Stage
source_output = codepipeline.Artifact("SourceOutput")
pipeline.add_stage(
stage_name="Source",
actions=[
actions.CodeCommitSourceAction(
action_name="SourceCheckout",
repository=repo,
output=source_output,
branch_name="main", # Or your primary branch
)
]
)
# 5. Build Stage (CDK Synth)
synth_output = codepipeline.Artifact("SynthOutput")
pipeline.add_stage(
stage_name="Build",
actions=[
actions.CodeBuildAction(
action_name="CDKSynth",
input_artifacts=[source_output],
output_artifacts=[synth_output],
project=codebuild.Project(
self, "CDKSynthProject",
build_spec=codebuild.BuildSpec.from_source_filename("buildspec.yml"),
environment=codebuild.BuildEnvironment(
privileged=True, # Needed for Docker in Docker if CDK uses it, or for certain build envs
build_image=codebuild.LinuxImage.STANDARD_5_0
),
# Granting permissions for CDK to deploy
role=iam.Role(
self, "CodeBuildCDKDeployRole",
assumed_by=iam.ServicePrincipal("codebuild.amazonaws.com"),
managed_policies=[
iam.ManagedPolicy.from_aws_managed_policy_name("AdministratorAccess") # Again, restrict in production!
]
)
)
)
]
)
# 6. Deploy Stage (CDK Deploy)
# This is the crucial part: the deploy action uses the synth output
pipeline.add_stage(
stage_name="Deploy",
actions=[
actions.CodeBuildAction(
action_name="CDKDeploy",
input_artifacts=[synth_output],
project=codebuild.Project(
self, "CDKDeployProject",
build_spec=codebuild.BuildSpec.from_source_filename("deployspec.yml"),
environment=codebuild.BuildEnvironment(
privileged=True,
build_image=codebuild.LinuxImage.STANDARD_5_0
),
# This role needs permissions to deploy your CDK application's resources
role=iam.Role(
self, "CodeBuildCDKDeployExecutionRole",
assumed_by=iam.ServicePrincipal("codebuild.amazonaws.com"),
managed_policies=[
iam.ManagedPolicy.from_aws_managed_policy_name("AdministratorAccess") # Restrict!
]
)
)
)
]
)
# In your main app.py:
# app = cdk.App()
# SelfMutatingPipelineStack(app, "SelfMutatingPipelineStack")
# app.synth()
And the buildspec.yml (for synth):
version: 0.2
phases:
install:
runtime-versions:
nodejs: 18
commands:
- echo "Installing CDK..."
- npm install -g aws-cdk
- cdk bootstrap # Important for the first run or if bootstrap needs updating
build:
commands:
- echo "Synthesizing CloudFormation template..."
- cdk synth
artifacts:
files:
- '**/*'
base-directory: 'cdk.out'
And deployspec.yml (for deploy):
version: 0.2
phases:
install:
runtime-versions:
nodejs: 18
commands:
- echo "Installing CDK..."
- npm install -g aws-cdk
- echo "Performing CDK deploy..."
# The CDK deploy command will use the CloudFormation template generated by cdk synth
# The --ci flag tells CDK to assume it's running in a CI/CD environment
# The --profile and --region can be inferred from the CodeBuild environment
- cdk deploy --ci --force # --force is useful but use with caution
- echo "CDK deploy completed."
The magic happens in how the Deploy stage’s CodeBuild project takes the SynthOutput artifact. This artifact contains the CloudFormation template generated by cdk synth in the Build stage. The CDKDeployProject then executes cdk deploy against this template. Since the CDKDeployProject’s role has permissions to deploy cloud resources, it can update the pipeline stack itself.
The core problem this solves is automating the management of your infrastructure as code. Instead of manual cdk bootstrap or cdk deploy commands run from your local machine or a separate, fixed CI/CD setup, your pipeline becomes a living entity. You push a change to your CDK code (e.g., add a new service, change a security group rule), and the pipeline recognizes this change, synthesizes the new CloudFormation, and then deploys that new CloudFormation, updating itself.
The most surprising thing is that the aws-cdk CLI itself is designed to be idempotent and CI/CD-friendly. When you run cdk deploy --ci, it looks for existing CloudFormation stacks and updates them. It doesn’t need special "self-update" commands; it just performs its standard deployment routine, but the context in which it’s running (CodeBuild within a CodePipeline) provides the necessary permissions and artifacts. The cdk bootstrap command is crucial for setting up the necessary S3 buckets and CloudFormation service roles that CDK uses to track deployments, and it needs to be run once initially, or if the bootstrap assets themselves change.
The next concept you’ll want to explore is implementing more sophisticated deployment strategies like blue/green deployments or canary releases directly within your CDK pipeline, leveraging CodeDeploy.