Lesson 229 · AWS Learning Path

AWS 229: Deploy a stack with CloudFormation and AWS CDK, then inspect drift

· Published · 8 min read

Labelled process diagram for AWS 229: CDK source to Synthesized template and diff to CloudFormation stack to Drift, reconciliation, and destroy evidence, with decision, proof and rejection evidence.

Why this lesson matters

AWS CDK does not replace CloudFormation. CDK source builds a construct tree and synthesizes a CloudFormation template. CloudFormation creates, updates, detects drift in, and deletes the stack. An architect must inspect every boundary instead of treating cdk deploy as magic.

This pinned JavaScript project creates only a seven-day CloudWatch Logs log group and a non-secret Systems Manager Parameter Store string. You will synthesize, inspect, optionally deploy, create reversible drift, reconcile it through source, and prove cleanup.

What you will be able to do

  • Distinguish an app, construct, CDK stack, template, asset, CloudFormation

stack, and deployed resource.

  • Explain L1, L2, and L3 constructs, context, lookups, and unresolved tokens.
  • Pin dependencies and run assertions before AWS access.
  • Inspect bootstrap roles, trust, policies, S3/ECR stores, and ownership.
  • Compare synth, diff, change sets, stack events, and drift detection.
  • Adopt or restore drift through reviewed source and prove exact cleanup.

Safety and approval boundary

  • Local tests and synthesis create no AWS resource. Live work needs account-owner

approval and an exact account/Region check before every mutation.

  • Use a normal role, never root or keys in files. The lab parameter is non-secret;

String is not a secret store.

  • Never bootstrap automatically or delete CDKToolkit; it is shared,

security-sensitive infrastructure.

  • Never hide security changes with --require-approval never.
  • Use only context environment=lab, dev, or test; the app rejects others.
  • Redact account IDs and full ARNs from shared evidence.

The complete mental model

lockfile + JavaScript + context
              -> CDK app/construct tree
              -> cdk synth
              -> template + manifest + any assets
              -> tests/review/cdk diff
              -> CloudFormation change set/events
              -> Logs log group + SSM parameter
              -> drift -> source reconciliation -> destroy

There is one nw-p11-cdk-lab application stack. “CDK stack” is its source model; “CloudFormation stack” is its deployed lifecycle - not a second copy.

TermMeaningReview focus
AppTop-level CDK programcontext, account/Region, stack instances
ConstructComponent in the treedefaults and generated child resources
L1Near-direct CloudFormation resource (Cfn*)explicit properties
L2Intent-based AWS resource abstractiondefaults and helpers
L3/patternMulti-resource architectureall transitive resources/IAM
TokenDeployment-time valueresulting Ref, Fn::GetAtt, pseudo parameter
ContextSynthesis input/cachereproducibility and secret risk
AssetUploaded file/containerhash, store, retention, ownership
Cloud assemblycdk.out templates/manifestsexact deployable output
DriftSupported actual property differs from expected templateproperty differences/time

Context/lookups can make synthesis environment-dependent. Commit only reviewed, non-secret context when reproducibility needs it. This project has no lookup, asset, IAM resource, network, compute, bucket, repository, or public endpoint.

Download and verify the reviewed project

Download the complete locked project, or inspect package.json, the lockfile, stack source, and assertions.

Pins: CDK CLI 2.1141.0, aws-cdk-lib 2.269.0, constructs 10.8.1. The CLI and library now have independent release numbers. Use Node.js 22+ and the lockfile-controlled local CLI:

tar -xzf nw-p11-cdk-drift.tar.gz
cd p11-cdk-drift
node --version
npm ci
npm test
npx cdk --version
npx cdk synth --quiet

Do not use sudo npm to bypass permissions. Inspect cdk.out/nw-p11-cdk-lab.template.json and require:

  • one log group /nw/p11/cdk/lab/application, retention 7;
  • one Standard SSM String parameter /nw/p11/cdk/lab/owner;
  • Delete deletion and update-replacement policies on both data resources;
  • exact outputs and project/environment/cleanup tags;
  • no IAM, network, compute, S3, ECR, public endpoint, or secret.

AWS::CDK::Metadata may also appear; it is not application data. Assertions prove selected invariants, not permissions, quotas, runtime behavior, or an absence of unexpected resources outside their checks - review still matters.

Bootstrap is a separate security decision

Modern bootstrapping can create an asset bucket, ECR repository, IAM roles, an SSM version parameter, and optionally a KMS key. Role trust and execution policy can grant broad cross-account deployment power. Inspect first:

export AWS_DEFAULT_REGION="ap-south-1"
aws sts get-caller-identity --output json
aws cloudformation describe-stacks --stack-name CDKToolkit --output json
aws cloudformation get-template --stack-name CDKToolkit \
  --template-stage Processed --output json
aws cloudformation list-stack-resources --stack-name CDKToolkit --output table

If absent/incompatible, stop. Bootstrap only through the platform owner's approved trust, policies, boundary, qualifier, encryption, and termination protection design. This lesson does not authorize bootstrap or its deletion.

Optional live deployment

1. Freeze target and preflight

export AWS_DEFAULT_REGION="ap-south-1"
export CDK_DEFAULT_REGION="$AWS_DEFAULT_REGION"
export CDK_DEFAULT_ACCOUNT="$(aws sts get-caller-identity --query Account --output text)"
aws sts get-caller-identity --output json
aws configure list
npm test
npx cdk synth --quiet
npx cdk diff nw-p11-cdk-lab

Privately match the account with approval. Save the template/diff; reject any unexplained replacement, IAM broadening, public access, or extra resource.

2. Deploy and inspect CloudFormation

npx cdk deploy nw-p11-cdk-lab --require-approval broadening \
  --outputs-file cdk-outputs.json
aws cloudformation describe-stacks --stack-name nw-p11-cdk-lab --output json
aws cloudformation describe-stack-events --stack-name nw-p11-cdk-lab \
  --max-items 50 --output table
aws cloudformation list-stack-resources --stack-name nw-p11-cdk-lab --output table

Require CREATE_COMPLETE, exact resources/outputs/tags, and no failed event. A successful CLI exit does not prove workload read/write behavior.

log_group="$(jq -r '."nw-p11-cdk-lab".LogGroupName' cdk-outputs.json)"
parameter_name="$(jq -r '."nw-p11-cdk-lab".OwnerParameterName' cdk-outputs.json)"
aws logs describe-log-groups --log-group-name-prefix "$log_group" --output json
aws ssm get-parameter --name "$parameter_name" \
  --query 'Parameter.[Name,Type,Value,Version]' --output table

Require retention 7 and parameter String/NitWings-P11. Preserve output names.

Create and detect reversible drift

Drift compares supported actual properties with CloudFormation's expected template. It is asynchronous; unsupported/unmodelled state can still differ.

aws logs put-retention-policy --log-group-name "$log_group" --retention-in-days 14
drift_id="$(aws cloudformation detect-stack-drift \
  --stack-name nw-p11-cdk-lab --query StackDriftDetectionId --output text)"
while true; do
  status="$(aws cloudformation describe-stack-drift-detection-status \
    --stack-drift-detection-id "$drift_id" --query DetectionStatus --output text)"
  printf '%s %s\n' "$(date -u +%FT%TZ)" "$status"
  case "$status" in DETECTION_COMPLETE) break;; DETECTION_FAILED) exit 1;; esac
  sleep 10
done
aws cloudformation describe-stack-resource-drifts \
  --stack-name nw-p11-cdk-lab \
  --stack-resource-drift-status-filters MODIFIED DELETED NOT_CHECKED --output json

Require log-group MODIFIED, expected 7, actual 14. A console refresh before detection completes is not evidence.

Reconcile through source

Ordinary CDK diff compares desired templates; unchanged source may show no change while drift detection sees changed actual state.

  1. Change ONE_WEEK to TWO_WEEKS and assertion 7 to 14.
  2. Test, synth, diff, review, deploy. This adopts 14 as desired state.
  3. Detect again and require IN_SYNC.
  4. Restore source/assertion to one week/7; test, synth, diff, deploy.
  5. Detect once more; require IN_SYNC and actual retention 7.

Do not leave console state disagreeing with source. Decide which state is correct, review it in source, and let the owner tool converge it.

Diagnose failures from evidence

SymptomInspectResponse
Synth failsNode, lock, trace, contextFix local runtime/source; do not deploy
Bootstrap errorassembly, Region, qualifier, CDKToolkitStop; do not bootstrap by guess
Cannot assume deploy rolecaller, trust, policy, SCP, boundaryFix identity path; do not grant admin
Rollbackearliest failed stack event, CloudTrailDiagnose quota/name/policy/hook/service cause
Empty diff after manual changedrift result and synthesized templateRecognize desired-vs-actual boundary
Drift UNKNOWNdetection status/reasonDo not claim IN_SYNC
Destroy failsdelete events and physical IDsCheck deny, dependency, policy, Region/account
Stack gone/resource remainsoutput, tags, policy, inventoryProve ownership before deletion

Save events before retries; later symptoms can hide the first failure.

Cleanup and negative proof

Restore the canonical one-week source first and save evidence outside cdk.out.

npx cdk destroy nw-p11-cdk-lab
aws cloudformation list-stacks --stack-status-filter DELETE_COMPLETE \
  --query 'StackSummaries[?StackName==`nw-p11-cdk-lab`].[StackName,StackStatus]' \
  --output table
aws logs describe-log-groups --log-group-name-prefix "$log_group" --output json
aws ssm get-parameters --names "$parameter_name" \
  --query '[Parameters,InvalidParameters]' --output json

Require DELETE_COMPLETE, no exact log-group match, and the exact parameter in InvalidParameters. “Stack does not exist” from describe-stacks is expected after deletion. Leave CDKToolkit intact.

Cost and no-create path

CDK and ordinary CloudFormation add no separate fee, but created services do. CloudWatch Logs can charge for ingestion, storage, queries, delivery, and archive; this lab writes no events. Parameter Store Standard allowance and advanced/API charges differ. Bootstrap S3/ECR/KMS can retain charges. Check current Region pricing and use a cleanup timer.

Without AWS permission: run locked Node 22 tests/synth; inventory the template; draw bootstrap trust; explain metadata, expected-7/actual-14 drift, adoption/restoration, misleading unchanged diff, failures, and exact cleanup. Label it static evidence - it does not prove live account permissions/execution.

Lesson acceptance

Submit runtime/package/lock versions, passing assertions, template inventory, redacted target/bootstrap evidence (or no-create label), reviewed diff/events, initial state, drift ID and property difference, adoption/restoration/final IN_SYNC, and exact deletion/negative inventory. Reject unpinned-global-only work, unauthorized bootstrap, incomplete detection, console-only repair, missing cleanup proof, or a claim of live execution from static files.

Knowledge check

  1. Does CDK deploy beside CloudFormation? No; it synthesizes CloudFormation,

which owns deployed lifecycle.

  1. Why can diff be empty after manual change? It compares desired templates;

drift detection compares supported actual properties with expected state.

  1. Why is CDKToolkit sensitive? It can contain shared asset stores and

trusted deployment roles with substantial authority.

  1. Adoption versus restoration? Adoption changes desired source to actual;

restoration changes actual state back to reviewed source intent.

  1. Cleanup proof? DELETE_COMPLETE and exact negative inventory for both

output-named resources, while preserving shared bootstrap infrastructure.

Official sources

Advertisement