AWS CDK skill for Codex

Infrastructure code that never replaces your database. Codex structures CDK apps with stateful and stateless stacks apart, explicit environments and typed per-stage config. It treats logical ID changes as a data-loss risk, reads every cdk diff for replacements and IAM widening before a deploy, and writes assertion tests plus cdk-nag checks for the properties that matter instead of snapshot tests nobody reads.

  • Cloud Platforms
  • $aws-cdk
  • 197 lines
  • 2 files
  • ~2,300 tokens
  • MIT
  • Updated

Install

curl -fsSL codexskills.dev/install.sh \  | sh -s -- aws-cdk

Installs to ~/.agents/skills/aws-cdk/ for every project on this machine.

104-line POSIX script. No sudo, no telemetry. Read install.sh

Use it in Codex

Start a prompt with $aws-cdk, or pick it from /skills. You can also just describe the task: Codex reads the description below and loads the playbook when your request matches.

What Codex reads before loading it

Builds and maintains AWS CDK v2 apps in TypeScript or Python - stack and construct design, environment-aware config, cdk synth/diff/deploy, bootstrap, cdk-nag checks and assertion tests with Template.fromStack - without causing accidental resource replacement. Use when writing or refactoring CDK stacks, adding an L2/L3 construct, reviewing a cdk diff, fixing a failed deploy or bootstrap error, splitting stacks, or adding unit tests for infrastructure code.

Use it when

  • Writing or refactoring AWS CDK stacks and constructs in TypeScript or Python
  • Reviewing a cdk diff before a production deploy
  • Adding unit tests or cdk-nag policy checks to infrastructure code
  • Debugging bootstrap, asset bundling or cross-stack export errors

Use something else when

  • The project is a SAM template for a serverless app: use aws-sam
  • The infrastructure is written in Terraform modules: use terraform-modules

How to check the result

Tests and cdk synth pass offline, and cdk diff shows no replacement of stateful resources and no unexpected IAM or security group changes.

The mistake it prevents

Renaming or moving a construct changes its logical ID, so CloudFormation deletes and recreates the resource, including databases and buckets.

~/.agents/skills/aws-cdk/SKILL.md

--- name: aws-cdk description: Builds and maintains AWS CDK v2 apps in TypeScript or Python - stack and construct design, environment-aware config, cdk synth/diff/deploy,… license: MIT ---

Done well means: cdk synth is deterministic and offline, every deploy is preceded by a cdk diff a human could read, stateful resources can never be replaced or deleted by a refactor, and the infrastructure has tests that fail when a security property regresses.

Before changing anything

  1. Read AGENTS.md, cdk.json, package.json / pyproject.toml and the lockfile. Note the app command in cdk.json, the aws-cdk-lib version and the CLI version (npx cdk --version). Since 2025 the CLI is versioned separately (2.1000+) from aws-cdk-lib; a CLI older than the library fails with a cloud assembly schema error.
  2. Find the entry point (bin/*.ts or app.py) and list stacks: npx cdk ls.
  3. Check whether cdk.context.json is committed. It should be: it pins lookups (Vpc.fromLookup, AMIs, AZs) so synth is reproducible and works without network.
  4. Run npx cdk synth --quiet before editing so you know the baseline is green.

Synth needs no AWS credentials unless a lookup is missing from context. diff, deploy and bootstrap need network and credentials; in a Codex sandbox with network off, stop after synth and tests and hand the deploy step to the user.

Project layout default

bin/app.ts                 # wires stacks to environments, nothing else
lib/stacks/network-stack.ts
lib/stacks/data-stack.ts   # stateful: databases, buckets, queues
lib/stacks/service-stack.ts
lib/constructs/            # reusable L3 constructs, no Stack subclasses
test/                      # assertion tests per stack
  • Separate stateful from stateless stacks. Stateless stacks can be destroyed and recreated; stateful ones get terminationProtection: true.
  • Pass resources between stacks as constructor props (typed references), not by Fn.importValue strings.
  • Put config in a typed object per environment, not in scattered this.node.tryGetContext calls.
// bin/app.ts
const app = new cdk.App();
const envs = {
  staging: { account: '111111111111', region: 'eu-west-1', minTasks: 1 },
  prod:    { account: '222222222222', region: 'eu-west-1', minTasks: 3 },
} as const;
const stage = (app.node.tryGetContext('stage') ?? 'staging') as keyof typeof envs;
const cfg = envs[stage];

const data = new DataStack(app, `Data-${stage}`, { env: cfg, terminationProtection: stage === 'prod' });
new ServiceStack(app, `Service-${stage}`, { env: cfg, table: data.table, minTasks: cfg.minTasks });

Always set env explicitly. Environment-agnostic stacks cannot do lookups and silently get two AZs.

Construct level decision

NeedUse
Standard resource with sane defaultsL2 (s3.Bucket, lambda.Function, ecs.FargateService)
A property the L2 does not exposeL2 plus escape hatch: (bucket.node.defaultChild as s3.CfnBucket).addPropertyOverride(...)
Brand-new service with no L2 yetL1 Cfn* construct, wrapped in your own L3
A pattern repeated across stacksYour own L3 construct extending Construct
AWS Solutions / patterns librariesOnly after reading what they create; they hide IAM and networking choices

Prefer grant* methods (table.grantReadWriteData(fn), bucket.grantRead(role)) over hand-written PolicyStatements: they scope to the resource ARN and include KMS key grants.

Logical IDs and replacement - the main trap

A resource’s CloudFormation logical ID is derived from its construct path. Renaming a construct ID, moving it into another construct, or moving it to another stack produces a new logical ID, and CloudFormation deletes the old resource and creates a new one. For a database or bucket that is data loss.

Hard rules:

  • Every stateful resource gets removalPolicy: RemovalPolicy.RETAIN (or SNAPSHOT for RDS) in production. The DESTROY + autoDeleteObjects combination is for ephemeral dev stacks only.
  • Before renaming or moving a construct, run cdk diff and look for [-] / [+] pairs or requires replacement on stateful types. If it appears, keep the old ID or pin it:
(table.node.defaultChild as dynamodb.CfnTable).overrideLogicalId('OrdersTable8A3F2C1B');
  • Do not hardcode physical names (bucketName, tableName, functionName) unless an external system requires them: a named resource cannot be replaced without a manual rename, so property changes that need replacement fail.
  • Cross-stack references create CloudFormation exports. An export in use cannot be removed or changed; deleting the consumer reference first, deploying, then removing the producer output is a two-deploy dance. Use this.exportValue(...) in the producer to keep an export alive during that transition.

Deploy workflow

  • npm test (or pytest) passes
  • npx cdk synth --quiet passes with no warnings you have not read
  • npx cdk diff <Stack> reviewed: IAM changes, security group changes, replacements
  • User confirmed the diff for any non-dev environment
  • npx cdk deploy <Stack> --require-approval broadening
  • Post-deploy check (stack status, smoke test)
npx cdk bootstrap aws://111111111111/eu-west-1          # once per account/region
npx cdk diff Service-prod --context stage=prod
npx cdk deploy Service-prod --context stage=prod --require-approval broadening
npx cdk deploy --all --concurrency 3 --context stage=staging

Never deploy to production, run cdk destroy, or pass --require-approval never against a shared account without the user’s explicit go-ahead after they have seen the diff. cdk watch and --hotswap bypass CloudFormation and leave drift: dev stacks only.

Testing

Use fine-grained assertions for properties that matter (encryption, public access, IAM scope). Snapshot tests catch everything and therefore get blindly updated; keep at most one per stack.

import { App } from 'aws-cdk-lib';
import { Template, Match } from 'aws-cdk-lib/assertions';
import { DataStack } from '../lib/stacks/data-stack';

test('bucket blocks public access and enforces TLS', () => {
  const app = new App();
  const stack = new DataStack(app, 'T', { env: { account: '1', region: 'eu-west-1' } });
  const t = Template.fromStack(stack);

  t.hasResourceProperties('AWS::S3::Bucket', {
    PublicAccessBlockConfiguration: { BlockPublicAcls: true, RestrictPublicBuckets: true },
  });
  t.hasResourceProperties('AWS::S3::BucketPolicy', {
    PolicyDocument: { Statement: Match.arrayWith([
      Match.objectLike({ Effect: 'Deny', Condition: { Bool: { 'aws:SecureTransport': 'false' } } }),
    ]) },
  });
  t.hasResource('AWS::DynamoDB::Table', { DeletionPolicy: 'Retain' });
});

Python equivalent: from aws_cdk.assertions import Template, Match, Template.from_stack(stack).has_resource_properties(...).

Add policy checks with cdk-nag as an Aspect so violations fail synth:

import { Aspects } from 'aws-cdk-lib';
import { AwsSolutionsChecks, NagSuppressions } from 'cdk-nag';
Aspects.of(app).add(new AwsSolutionsChecks({ verbose: true }));
NagSuppressions.addStackSuppressions(stack, [
  { id: 'AwsSolutions-IAM4', reason: 'AWSLambdaBasicExecutionRole is acceptable for logs' },
]);

Every suppression needs a reason a reviewer would accept. Installing cdk-nag needs network; ask before adding dependencies.

Common failures

SymptomCauseFix
SSM parameter /cdk-bootstrap/hnb659fds/version not foundAccount/region not bootstrappedcdk bootstrap aws://ACCOUNT/REGION (user confirms)
Cloud assembly schema version mismatchCLI older than aws-cdk-libUpgrade the aws-cdk CLI package
Export ... cannot be deleted as it is in useRemoved a cross-stack reference in one stepKeep the export with exportValue, deploy, then remove
... already exists on createHardcoded physical name or retained old resourceDrop the explicit name or import the resource with cdk import
Stack stuck UPDATE_ROLLBACK_FAILEDResource could not roll backaws cloudformation continue-update-rollback --stack-name X (optionally --resources-to-skip), only with user approval
Lookup works locally, fails in CIcdk.context.json not committedCommit it; CI should not need lookup permissions
Asset bundling failsNo Docker and no local esbuild for NodejsFunctionAdd esbuild as a dev dependency

Gotchas

  • Vpc default creates one NAT gateway per AZ. Set natGateways deliberately; for dev consider 0 with VPC endpoints.
  • lambda.Function creates a log group with infinite retention unless you pass a logGroup with retention.
  • Feature flags in cdk.json change synthesized output. Do not delete them during upgrades; new projects get the current recommended set from cdk init.
  • Tokens are unresolved at synth time: bucket.bucketName is ${Token[...]} in TypeScript, so never branch on its string value.
  • cdk diff against a stack deployed outside CDK, or edited in the console, will not show the drift. Run CloudFormation drift detection when in doubt.

Output

When reporting a change, give: stacks affected, the cdk diff summary (resources added, modified, replaced, IAM/security group changes), tests added or updated, and the exact deploy command left for the user to run.

What the zip contains

SKILL.md plus the interface file below. Nothing runs on install.

agents/openai.yamlgenerated from this page

Gives the Codex app a display name, a one-line description and a starter prompt for this skill.

interface:
  display_name: "AWS CDK"
  short_description: "Infrastructure code that never replaces your database"
  default_prompt: "$aws-cdk Split this CDK stack so the database lives in its own stack without recreating it"

About this skill

Written and maintained by the Codex Skills editors to the same editorial standard as every skill here: a selective description, a real workflow, known pitfalls and a way to verify the work. First published , last updated . MIT licensed. Something wrong or out of date? Tell us.

esc
Just launched

Codex Skills Pro is live

79 premium skills for Codex, each with scripts, references and an evidence report that proves the result.

Bought one by one$882.21
All 79 skills$49
94% off

That's $0.62 a skill, paid once.

Get all 79 for $49Or pick single skills from $4.99 →

Secure checkout by Creem