AutoGineer summary
Written by AutoGineer from the skill’s own files, not by the author
This skill provides comprehensive support for the AWS CDK, covering the full lifecycle of infrastructure-as-code development. It includes best practices for construct patterns, environment bootstrapping, compliance enforcement, drift detection, and safe refactoring. The skill also offers detailed troubleshooting workflows for common CLI, CloudFormation, and deployment errors, ensuring developers can maintain stable and secure AWS environments using TypeScript or Python.
What it does
- Guides the authoring of AWS infrastructure using L1 and L2 CDK constructs.
- Provides procedures for bootstrapping AWS accounts and regions for CDK usage.
- Enforces security and compliance policies using cdk-nag integration.
- Detects and resolves configuration drift between live resources and CDK templates.
- Offers systematic troubleshooting for deployment failures and asset management issues.
- Details safe refactoring patterns to prevent accidental resource replacement.
When to use it
- When initializing new AWS infrastructure projects in TypeScript or Python.
- When planning complex stack architectures or managing cross-stack references.
- When diagnosing deployment errors or stuck CloudFormation stacks.
- When integrating security compliance checks into the CI/CD pipeline.
- When importing existing AWS resources into a CDK-managed stack.
- When refactoring existing infrastructure without causing data loss or resource replacement.
Advantages
- Reduces risk of accidental resource replacement during stack refactoring.
- Ensures infrastructure compliance through automated policy enforcement.
- Provides clear, actionable steps for resolving complex deployment deadlocks.
- Standardizes project structure and dependency management for CDK apps.
- Offers safe, documented patterns for handling stateful resources and S3 buckets.
Usage examples
- “How do I safely remove a cross-stack reference without breaking my deployment?”
- The agent explains the three-deploy process: weakening the reference strength to WEAK before final removal to avoid the deadly embrace deadlock.
- “My stack is stuck in UPDATE_ROLLBACK_FAILED. How do I fix it?”
- The agent provides commands to use cdk rollback or cdk rollback --orphan to recover the stack state.
- “How can I ensure my CDK app follows AWS best practices?”
- The agent instructs on installing and applying cdk-nag rule packs like AwsSolutionsChecks as an Aspect to the application.
- “I need to import an existing S3 bucket into my CDK stack. What is the process?”
- The agent outlines the cdk import workflow and the necessary steps to align the existing resource with the CDK template.
Install
Download the skill as a ZIP:
https://mp-staging.autogineer.com/agent-skills/aws/aws-cdk/downloadUnzip it into the cross-client skills folder of your project:
mkdir -p .agents/skills && unzip aws-cdk.zip -d .agents/skills
Documentation by AWS
AWS CDK
Overview
Domain expertise for CDK construct authoring, deployment workflows, compliance, drift, importing resources, safe refactoring, and troubleshooting CDK CLI / CloudFormation errors.
When NOT to use: Raw CloudFormation YAML/JSON. SAM. Terraform/Pulumi. CI/CD beyond CDK Pipelines. Use builtin knowledge or specialized skills for these.
Critical Warnings
Deadly embrace: Removing a cross-stack reference deadlocks deployment (Export ... cannot be deleted as it is in use by ...). Preferred fix: weaken the reference first — CrossStackReferences.of($RESOURCE).produce(ReferenceStrength.BOTH) then WEAK, then remove (three deploys). Legacy fallback: two-deploy this.exportValue() recipe. See troubleshooting-deployment.
Construct ID changes cause replacement: Renaming/moving a construct changes its logical ID → CloudFormation replaces the resource (data loss for stateful resources). Always cdk diff before deploy. See refactor-and-prevent-replacement.
UPDATE_ROLLBACK_FAILED: Stack is stuck. Fix with cdk rollback $STACK or cdk rollback $STACK --orphan <LogicalId>. Express mode stacks are the exception — they cannot be rolled back at all. See troubleshooting-deployment.
Hotswap and express mode are development-only: --hotswap / --hotswap-fallback bypass CloudFormation and create drift on purpose; --express reports success before resources stabilize and disables automatic rollback. You MUST NOT use either in production. A failed --express deployment cannot be rolled back — recover by rolling forward with another --express deploy. See fast-deployments.
Non-empty S3 buckets persist after destroy: You MUST set both removalPolicy: DESTROY and autoDeleteObjects: true. Versioned buckets are worse — delete markers persist even after apparent deletion.
Common Workflows
| Task | Quick Command | Details |
|---|---|---|
| Bootstrap | cdk bootstrap aws://$ACCOUNT/$REGION | bootstrap-and-project-setup |
| New TS project | cdk init app --language typescript — use tsx, eslint-plugin-awscdk | bootstrap-and-project-setup |
| New Python project | cdk init app --language python — pin deps, use virtualenv | bootstrap-and-project-setup |
| Deploy | cdk synth --strict → cdk diff → cdk deploy | Always diff before deploy to production |
| Fast dev iteration | cdk deploy --hotswap-fallback, cdk watch, or cdk deploy --express — dev only, never production | fast-deployments |
| cdk-nag | Aspects.of(app).add(new AwsSolutionsChecks()) | compliance-and-drift |
| Drift | cdk drift $STACK (use --fail in CI) | compliance-and-drift |
| Import resource | cdk import (interactive or --resource-mapping for CI), cdk deploy --import-existing-resources | import-and-migrate |
| Refactor safely | cdk refactor --unstable=refactor — no property changes in same deploy | refactor-and-prevent-replacement |
Fast Deployments — Hotswap vs Express (dev only)
Both trade safety for speed and you MUST NOT use either in production.
Choosing between them: Use --hotswap / --hotswap-fallback for the fastest loop when you work mostly with hotswappable resources and drift does not matter. Use --express when drift is unacceptable, or your resources are not hotswappable.
Recovery workflows (hotswap drift via --revert-drift, rolling a failed --express deploy forward), the hotswappable-resource rules, and IAM/monitoring enforcement of the prod prohibition are all in the full guide: fast-deployments.
Troubleshooting
| Error | Cause → Fix |
|---|---|
| DeployFailed / DeploymentError | CDK error isn't the root cause. cdk deploy $STACK --verbose, then cdk --unstable=diagnose diagnose $STACK (CLI ≥ 2.1120.0); else aws cloudformation describe-events --stack-name $STACK --filters FailedEvents=true — the first _FAILED event is the cause. Details |
| NoCredentials / ExpiredToken / AssumeRoleFailed | aws sts get-caller-identity + cdk doctor. Expired SSO, missing env, missing sts:AssumeRole. Details |
| Asset errors (CannotFindAsset, FailedToBundleAsset, AssetBuildFailed, AssetPublishFailed) | Path wrong, Docker not running, or bootstrap bucket perms. Use path.join(__dirname, ...). Details |
| AppRequired | Add "app": "npx tsx bin/my-app.ts" to cdk.json. Details |
| AnnotationErrors | Fix the underlying issue; suppress with NagSuppressions only as last resort. Details |
| ConcurrentReadLock / ConcurrentWriteLock | rm -rf cdk.out then re-run. Parallel CI: --output ./cdk.out.$BUILD_ID. Details |
| BootstrapVersionValidation | Re-bootstrap. Match --qualifier everywhere. Details |
| DependencyCycle | Extract shared resource into third stack or use SSM for late-binding. Details |
| UnresolvedAccount | Set explicit env: { account, region } on stack. Commit cdk.context.json. Details |
| NoStacksMatched | CDK uses logical ID (2nd constructor arg), not CFN name. cdk list to find IDs. Details |
| Cannot find module (synth time) | Run npx tsc --noEmit, check cdk.json app path matches tsconfig.json outDir, delete stale .js files. Python: activate venv. Details |
| V1 import paths / duplicate aws-cdk-lib | V1 @aws-cdk/* imports, wrong Construct import, duplicate lib copies in monorepos. Details |
| Lambda Cannot find module (runtime) | Wrong handler value, missing AWS SDK v3 migration, Python deps not bundled. Details |
| API Gateway multi-stage conflicts | Set deploy: false on RestApi, create Deployment and Stage explicitly. Details |
Change didn't deploy under --hotswap | Changes to non-hotswappable resources are silently ignored and only logged — the command still reports success. Read the output; use --hotswap-fallback to force a CloudFormation deployment instead. Details |
Failed --express deploy / can't roll back | Express mode cannot use the Rollback Stack API, and a standard deploy MUST NOT be used to recover it. Roll forward: fix the cause, then cdk deploy $STACK --express. Details |
| Unexpected drift on a dev stack | Hotswap and cdk watch create drift by design. Until reverted, the live resources — not CloudFormation's records — are authoritative. Reconcile with cdk deploy $STACK --revert-drift, which uses Drift Aware Changesets to bring live resources in line with the template (updates reality to match desired state; does NOT rewrite CF records to match drifted resources). Details |
Construct Patterns
Prefer L2. Use L1 with Mixins/Facades when L2 lacks a property. Escape hatches: node.defaultChild → addPropertyOverride. See construct-patterns.
Additional Resources
Search AWS documentation for "CDK Developer Guide", "CDK API Reference" and "CDK Pipelines" respectively
Security Considerations
OIDC for CI/CD credentials (no static keys)
--custom-permissions-boundaryon bootstrapgrant*()for inter-resource IAMcdk-nag+--strictin CIStateful resources in own stack with
terminationProtection: trueCommit
cdk.context.json
Files
- ATTRIBUTION.mdAdded by AutoGineer879 B
- LICENSELicence file added by AutoGineer9.9 KiB
- NOTICELicence file added by AutoGineer67 B
- SKILL.md8.7 KiB
- references/bootstrap-and-project-setup.md8.0 KiB
- references/compliance-and-drift.md5.2 KiB
- references/construct-patterns.md10.4 KiB
- references/fast-deployments.md13.4 KiB
- references/import-and-migrate.md6.3 KiB
- references/refactor-and-prevent-replacement.md6.2 KiB
- references/troubleshooting-credentials.md5.8 KiB
- references/troubleshooting-deployment.md12.9 KiB
- references/troubleshooting-synth.md8.8 KiB
- references/v1-to-v2-migration.md3.2 KiB
references/construct-patterns.md
Construct Patterns
Table of Contents
Overview
Scope and Construct IDs
Choosing Construct Levels
Mixing L1 and L2
Cross-Stack References
Creating Custom Constructs
Testing CDK Infrastructure
Overview
This reference covers construct selection, composition, cross-stack wiring, and testing patterns. It provides decision frameworks for choosing construct levels, mixing them safely, passing references across stacks, building custom constructs, and verifying infrastructure with assertions.
Scope and Construct IDs
Every construct is created with new SomeConstruct(scope, id, props?). The first two arguments are not interchangeable boilerplate — misusing them is a common review finding.
Scope (first argument)
Inside a construct's constructor, you MUST pass this as the scope of child constructs — NOT the incoming scope argument:
// ❌ INCORRECT — child parented to the wrong node
export class MyConstruct extends Construct {
constructor(scope: Construct, id: string) {
super(scope, id);
new ChildConstruct(scope, 'Child'); // wrong: uses 'scope'
}
}
// ✅ CORRECT
export class MyConstruct extends Construct {
constructor(scope: Construct, id: string) {
super(scope, id);
new ChildConstruct(this, 'Child'); // 'this' is the parent
}
}Passing this makes the child a child of the current construct, which is almost always the intent. A scope other than this is legitimate only when it comes from a function parameter or a local variable used to group constructs (e.g. in App or helper/test stacks).
Construct ID (second argument)
The construct ID is the locally unique identifier within a scope. The IDs between a CloudFormation resource and its containing Stack are concatenated into the resource's logical ID — and changing a logical ID replaces the resource.
Construct IDs SHOULD be short and to the point.
You SHOULD NOT interpolate variables into a construct ID: if the variable's value changes, the logical ID changes and the resource is replaced. Legitimate exceptions (constructs created in a loop; an intentional, conditional replacement) MUST be annotated with a comment explaining why.
Construct IDs only need to be unique within their scope. They SHOULD NOT repeat project, region, or stage names already present higher in the construct tree.
Choosing Construct Levels
You SHOULD prefer L2 constructs as the default choice. They provide sensible defaults, grant methods, and metric helpers.
Decision tree
| Need | Construct type |
|---|---|
| Pure logic, no AWS resource | Plain TypeScript/Python class |
| Single resource with stricter defaults | Extend the L2 class (is-a) |
| Composition of multiple resources | Extend Construct (has-a) — this is an L3 |
| Organization-wide policy enforcement | Aspect |
When L1 is viable
L1 (Cfn*) constructs are acceptable when no L2 exists or when you need a property the L2 does not expose. You SHOULD combine L1 with Mixins, Facades, or I<Resource>Ref interfaces to retain type safety and grant support.
When using L3
L3 constructs provision multiple resources behind a single API. You MUST read what they provision (check the source or cdk synth output) before using them in production. Hidden resources may have cost, security, or operational implications.
When an L2 doesn't expose a property
Use this escalation ladder — prefer the first option that works:
Cfn<Resource>PropsMixin (preferred) — type-safe, applied via
.with():import { CfnBucketPropsMixin } from '@aws-cdk/cfn-property-mixins/aws-s3'; new s3.Bucket(this, 'Bucket').with(new CfnBucketPropsMixin({ analyticsConfigurations: [{ id: 'full', prefix: '' }], }));addPropertyOverride— untyped, string-keyed last resort:const cfnBucket = bucket.node.defaultChild as s3.CfnBucket; cfnBucket.addPropertyOverride('AnalyticsConfigurations', [{ Id: 'full', Prefix: '' }]);
Mixing L1 and L2
When a stack contains both L1 and L2 constructs, you MUST use I<Resource>Ref interfaces and Facades to bridge them — do not pass L1 property types to L2 props or vice versa, as their types are not interchangeable.
I<Resource>Ref interfaces (e.g. IBucketRef)
Both L1 and L2 constructs implement I<Resource>Ref-style interfaces (e.g., IBucketRef, IFunctionRef). Use these interfaces as prop types to accept either level:
interface MyProps {
readonly bucket: s3.IBucketRef;
}Facades for L1 grants
L1 constructs lack grant*() methods. You SHOULD wrap them with fromCfn<Resource>() or from<Resource>Attributes() to get an L2 interface:
const cfnTable = new dynamodb.CfnTable(this, 'Table', { /* ... */ });
const table = dynamodb.Table.fromTableArn(this, 'TableRef', cfnTable.attrArn);
table.grantReadData(myFunction);Escalation ladder
When you need to customize a resource, follow this order (least invasive first):
L2 prop — use the built-in property if available.
Mixin — add behavior via a helper function.
Cfn<Resource>PropsMixin— type-safe L1 prop injection.node.defaultChild— access the underlying L1 construct.addPropertyOverride— override arbitrary CloudFormation properties.
You SHOULD exhaust each level before moving to the next.
Cross-Stack References
Same app, same region
Pass construct references via stack props. CDK automatically creates CloudFormation exports and imports:
interface ConsumerProps extends cdk.StackProps {
readonly bucket: s3.IBucket;
}
class ConsumerStack extends cdk.Stack {
constructor(scope: Construct, id: string, props: ConsumerProps) {
super(scope, id, props);
props.bucket.grantRead(myFunction);
}
}Cross-region or cross-account (same app)
Enable crossRegionReferences: true on the consuming stack and use explicit physical names on shared resources:
new ConsumerStack(app, 'Consumer', {
env: { account: process.env.CDK_DEFAULT_ACCOUNT, region: process.env.CDK_DEFAULT_REGION },
crossRegionReferences: true,
bucket: producerStack.bucket,
});Different apps
When stacks are in different CDK apps, automatic exports do not work. You MUST use one of:
CfnOutput+Fn.importValue:// Producer app new cdk.CfnOutput(this, 'BucketArn', { value: bucket.bucketArn, exportName: '$EXPORT_NAME' }); // Consumer app const arn = cdk.Fn.importValue('$EXPORT_NAME');SSM Parameter Store for decoupled lookups.
Fixing cycles
If cross-stack references create a dependency cycle, you MUST extract the shared resource into a third stack so that dependencies flow one way.
Creating Custom Constructs
Extend L2 (is-a)
Use when you want a single resource with stricter defaults:
export class SecureBucket extends s3.Bucket {
constructor(scope: Construct, id: string, props?: s3.BucketProps) {
super(scope, id, {
encryption: s3.BucketEncryption.S3_MANAGED,
blockPublicAccess: s3.BlockPublicAccess.BLOCK_ALL,
enforceSSL: true,
...props,
});
}
}Compose L3 (has-a)
Use when you combine multiple resources behind a single API:
export class ApiWithQueue extends Construct {
public readonly queue: sqs.Queue;
constructor(scope: Construct, id: string) {
super(scope, id);
this.queue = new sqs.Queue(this, 'Queue');
// ... additional resources
}
}Stable logical IDs
The default child ID determines the CloudFormation logical ID. You MUST NOT change construct IDs after deployment — this causes resource replacement. Use the Default child ID convention for the primary resource in an L3.
Escape via defaultChild
When extending an L2, you can access the underlying CFN resource:
const cfn = this.node.defaultChild as s3.CfnBucket;
cfn.addPropertyOverride('$PROPERTY_PATH', '$VALUE');Testing CDK Infrastructure
Fine-grained assertions
Use Template.fromStack() to assert on specific resources:
const template = Template.fromStack(myStack);
template.hasResourceProperties('AWS::SQS::Queue', {
VisibilityTimeout: 300,
});Partial matching
Use Match.* helpers for flexible assertions:
template.hasResourceProperties('AWS::Lambda::Function', {
Runtime: Match.stringLikeRegexp('nodejs'),
Environment: Match.objectLike({
Variables: Match.objectLike({
TABLE_NAME: Match.anyValue(),
}),
}),
});Snapshot tests
Capture the full template and compare against a stored baseline:
expect(template.toJSON()).toMatchSnapshot();You SHOULD use snapshot tests to detect unintended drift but MUST NOT rely on them as the sole testing strategy — they are brittle and hard to review.
Logical ID stability tests
Assert that critical resource logical IDs remain stable to prevent accidental replacement:
template.hasResource('AWS::DynamoDB::Table', {
// Verifying the resource exists with this logical ID
});Integration tests
Use @aws-cdk/integ-tests-alpha for tests that deploy real infrastructure:
const integ = new IntegTest(app, 'MyIntegTest', {
testCases: [myStack],
});
integ.assertions
.awsApiCall('DynamoDB', 'DescribeTable', { TableName: '$TABLE_NAME' })
.assertAtPath('Table.TableStatus', ExpectedResult.stringLikeRegexp('ACTIVE'));Integration tests SHOULD be run in a dedicated test account. They MUST NOT run against production.
Application best practices
Make decisions at synth time — use explicit
envto enable synth-time logic.Use generated physical names (CDK default) unless cross-stack or cross-app references require explicit names.
Set explicit
removalPolicyandlogRetentionon every resource.Separate stateful resources (databases, buckets) into their own stack.
Commit
cdk.context.jsonto version control for reproducible synth.Use
grant*()methods for IAM instead of hand-written policy statements.
Requirements and compatibility
Works with: Claude Code, Claude apps, OpenAI Codex, GitHub Copilot, Cursor, Gemini CLI, Kiro, OpenCode, Goose, Amp, Roo Code, JetBrains Junie, Factory Droid, Devin (ex-Windsurf), Mistral Vibe, OpenHands, Letta
“Works with” means the tool reads the open Agent Skills format; a skill written for one tool may use features others lack.
Needs: Needs Python, Needs Node.js
Security
What our scanner noticed· Medium risk
- References to credential files
- Instructions that use the network
A static scan is not a guarantee. Skills are third-party content: review a skill before you let an agent use it.
Attribution and licence
- Published by
- AWS
- Publisher
- AWS (Official)
- Source
- aws/agent-toolkit-for-aws · skills/core-skills/aws-cdk · commit 0a218f8
- Licence
- Apache License 2.0 (redistribution allowed)
- LICENSE (added by AutoGineer from LICENSE)
- NOTICE (added by AutoGineer from NOTICE)
- ATTRIBUTION.md (written by AutoGineer)
- Synced
- Also distributed in
- aws/agent-toolkit-for-aws
AutoGineer is not affiliated with or endorsed by AWS.
Download ZIP
The ZIP holds the skill’s files, its licence files and an ATTRIBUTION.md. Download aws-cdk.zip