CertNote/ Commute Study Notes
Roadmap
KOEN
CLF-C02 · FoundationalCloud Practitioner - Foundational
DVA-C02 · AssociateDeveloper - Associate
SAA-C03 · AssociateSolutions Architect - Associate
SOA-C02 · AssociateCloudOps Engineer - Associate
SAP-C02 · ProfessionalSolutions Architect - Professional
DOP-C02 · ProfessionalDevOps Engineer - Professional
  • Week 1
    • 1.DevOps as an Operating Model: The Five Axes of CALMS and the Truth DORA Proved Through Measurement
    • 2.Well-Architected Framework: Rereading It Through the Six Lenses of DevOps
    • 3.The AWS DevOps Tool Map: The Code* Series and the Real Picture Beyond It
    • 4.Multi-Account Strategy: The Real Picture of Organizations, Control Tower, and IAM Identity Center
    • 5.Week 1 Wrap-Up: Cementing the DevOps Thinking Frame with Scenarios
  • Week 2
  • Week 3
  • Week 4
  • Week 5
  • Week 6
  • Week 7
  • Week 8
    • 1.CloudFormation Advanced: Nested·Cross-Stack and the Deep Story of Modularization
    • 2.StackSets: The Deep Story of Deploying IaC to Thousands of Accounts
    • 3.Custom Resource·Hooks·Change Set: CloudFormation's Extension and Validation Mechanisms
    • 4.CDK·CDK Pipelines·Terraform: Deep Comparison of Modern IaC Tools and Self-Evolving Pipelines
    • 5.Week 8 Integrated Scenarios: Where IaC Tools Meet Inside a Single Incident
  • Week 9
  • Week 10
  • Week 11
  • Week 12
  • Week 13
  • Week 14
  • Week 15
  • Week 16
Linux Master 2 · 2급KAIT Linux Master - Level 2
SCS-C03 · SpecialtySecurity - Specialty
Linux Master 1 · 1급KAIT Linux Master - Level 1
MLA-C01 · AssociateMachine Learning Engineer - Associate
AIF-C01 · FoundationalAI Practitioner - Foundational
DEA-C01 · AssociateData Engineer - Associate
MLS-C01 · SpecialtyMachine Learning - Specialty
SQLD · 개발자SQL Developer (SQLD)

© CertNote · 주간랩스

  • About
  • Changelog
  • Contact
  • Terms
  • Privacy
← DOP-C02/Week 8/Day 4

Based on the DOP-C02 exam guide·Report an error

DOP-C02· ProWeek 8 · Day 4~33 min read

Day 4 - CDK·CDK Pipelines·Terraform: Deep Comparison of Modern IaC Tools and Self-Evolving Pipelines

When CloudFormation templates exceed 3000 lines, developers repeatedly ask the same question. "Is this really code?" YAML has no functions, no variable scope, weak autocomplete, and applying the same pattern to 50 microservices leaves only copy-paste. 2018's AWS CDK (Cloud Development Kit) addresses exactly this limit. Define infrastructure in generic languages like TypeScript/Python/Java/Go/.NET and cdk synth synthesizes ordinary CloudFormation templates. SAM applied macros atop CloudFormation; CDK climbs one more level — treating language itself as an abstraction tool.

Today we explore why Construct has L1/L2/L3 layers, what trust chain cdk bootstrap creates and its security meaning, why CDK Pipelines' self-mutation is a game-changer, operational patterns when Terraform and CDK coexist, CDKTF's position, and how CDK Aspects handle cross-cutting concerns. CDK direct exam questions are moderate but CDK Pipelines + Multi-Account scenarios frequently appear.

CDK's Essence — "Synthesizing" Infrastructure with Code

Initially CDK seems identical to Pulumi — both define infrastructure with generic languages. A decisive difference exists: CDK executes code to generate CloudFormation templates (JSON/YAML), then passes that template to the CFN engine for deployment. Pulumi interprets its own code graph directly, calling AWS APIs itself.

This difference determines entire operational model. CDK inherits all CFN governance assets (StackSets, Drift Detection, ChangeSet, Service Catalog, Config compliance) automatically. Pulumi uses its own separate governance tools. So large AWS-only organizations prefer CDK, while multi-cloud environments favor Pulumi or Terraform.

CDK code (TypeScript)
     │ cdk synth
     ▼
CloudFormation template (YAML/JSON)
     │ cdk deploy
     ▼
CloudFormation engine (AWS managed)
     │ Drift / ChangeSet / StackSet / Service Catalog available
     ▼
Real AWS resources

Due to this structure, CDK appears as single source of truth but actually has two abstraction stages. First, from CDK code to Construct tree (in-memory object graph). Second, from Construct tree to CFN template (synth). So cdk diff previews require understanding both stages — some changes are code-level intent, others are post-synth CFN changes.

💡 Related Theory: CDK's synthesis model mirrors LLVM compiler's IR (Intermediate Representation) pattern. High-level languages (C++/Rust) compile to IR (LLVM bitcode), IR translates to each target (x86, ARM). CDK compiles TypeScript to IR (Construct tree), IR transforms to CFN template. This two-stage approach enables cdktf (Construct tree → Terraform HCL), cdk8s (Construct tree → Kubernetes manifest) synthesis to different backends. One abstraction scales to multiple backends.

Construct Levels L1/L2/L3 — Three Abstraction Stairs

CDK's most powerful design decision separates Construct levels. Same S3 bucket expressible at three abstraction tiers.

// L1 — 1:1 CloudFormation mapping (Cfn prefix)
new s3.CfnBucket(this, 'Raw', {
  bucketName: 'my-bucket',
  bucketEncryption: {
    serverSideEncryptionConfiguration: [{
      serverSideEncryptionByDefault: { sseAlgorithm: 'AES256' }
    }]
  }
});
 
// L2 — Sensible defaults + methods (most common)
const bucket = new s3.Bucket(this, 'Data', {
  encryption: s3.BucketEncryption.S3_MANAGED,
  versioned: true,
  blockPublicAccess: s3.BlockPublicAccess.BLOCK_ALL,
  lifecycleRules: [{
    transitions: [{
      storageClass: s3.StorageClass.INTELLIGENT_TIERING,
      transitionAfter: cdk.Duration.days(30),
    }],
  }],
});
bucket.grantRead(myLambda);          // IAM policy auto-created
bucket.addEventNotification(...);    // S3 event + Lambda permission auto-handled
 
// L3 — Pattern (multiple resources bundled)
new ecs_patterns.ApplicationLoadBalancedFargateService(this, 'Web', {
  taskImageOptions: { image: ecs.ContainerImage.fromRegistry('nginx') },
  publicLoadBalancer: true,
  desiredCount: 3,
});
// Auto-creates: VPC + Subnets + IGW + NAT + ECS Cluster + Fargate Service +
//              Task Definition + ALB + Target Group + Security Groups + IAM Roles
LevelAbstractionCode VolumeLearning CurveControl
L1 (Cfn*)CFN 1:1HighLow (CFN-level)Maximum
L2Sensible defaults + helperMediumMediumHigh
L3 (patterns)Multiple resources bundledLowLow (simple API)Low (black box)

In practice use L2 by default, deploy L1 for security-critical parts (IAM, KMS), extract reusable patterns as custom L3. External L3 like ApplicationLoadBalancedFargateService accelerates PoC but prod needs control of internals (security group ranges, ALB logs, WAF attachment) usually requiring custom Constructs.

🎯 Scenario: "Team deploys 50 microservices with same pattern (Lambda + DynamoDB + API Gateway + CloudWatch Alarm + X-Ray). Security policy change should auto-propagate to all." — Answer is custom L3 Construct (StandardLambdaApiPattern). All services use it, security policy changes in one place → all 50 services auto-update on next deploy. CFN requires module patterns or Nested Stacks with worse code reuse.

CDK Bootstrap — The Hidden Trust Chain

cdk bootstrap runs once per new account·region before using CDK. Superficially "create S3 bucket, ECR repo," actually establishes all trust relationships for CDK operations, a security infrastructure.

cdk bootstrap aws://111111111111/ap-northeast-2

This single line creates:

  • S3 Asset Bucket (cdk-hnb659fds-assets-111-ap-northeast-2): Lambda zip, CFN templates, Docker metadata storage
  • ECR Repository (cdk-hnb659fds-container-assets-111-ap-northeast-2): Docker images
  • 5 IAM Roles:
    • cdk-hnb659fds-deploy-role-*: Calls deploy commands
    • cdk-hnb659fds-cfn-exec-role-*: CFN uses for execution
    • cdk-hnb659fds-file-publishing-role-*: S3 file uploads
    • cdk-hnb659fds-image-publishing-role-*: ECR image push
    • cdk-hnb659fds-lookup-role-*: Query AWS (VPC IDs)
  • CloudFormation Stack CDKToolkit: Defines above resources

Core design is permission separation. CFN execution role (cfn-exec-role) has strongest permissions (AdministratorAccess default), but only CloudFormation service assumes it. Deploy role that people/pipelines assume has narrower permissions — can't create resources directly, only via CloudFormation.

# Cross-account: Tooling trusts Dev/Prod to bootstrap
cdk bootstrap aws://DEV-ACCT/ap-northeast-2 \
  --trust TOOLING-ACCT \
  --trust-for-lookup TOOLING-ACCT \
  --cloudformation-execution-policies arn:aws:iam::aws:policy/AdministratorAccess

--trust TOOLING-ACCT sets trust policy so Dev account's deploy role can assume Tooling account's ID. This enables CDK Pipelines from one Tooling account deploying to multiple environment accounts.

🔍 Deeper: bootstrap has versions — CdkBootstrap SSM Parameter stores version number, cdk deploy checks for compatibility. Old bootstrap can't use new features (e.g., CFN Bucket KMS encryption enhancements). AWS regularly updates bootstrap versions, so operational standards include "quarterly cdk bootstrap re-run."

⚠️ Pitfall: bootstrap's cfn-exec-role defaulting to AdministratorAccess frequently criticized from security perspective. Prod should use --cloudformation-execution-policies specifying narrower policies (company standard PowerUser + explicit deny). Otherwise any CDK code change deploys to prod with full permissions, defeating change control.

CDK Pipelines — The True Meaning of self-mutation

2020 CDK Pipelines launch's biggest differentiator was self-mutation. Ordinary CodePipeline separates definition (pipeline structure) from execution; changing the pipeline itself requires separate tools (CloudFormation, Terraform). CDK Pipelines' pipeline includes itself as first Stage, auto-reflecting code changes on next run.

const pipeline = new CodePipeline(this, 'Pipeline', {
  pipelineName: 'MyApp',
  crossAccountKeys: true,
  synth: new ShellStep('Synth', {
    input: CodePipelineSource.connection('my-org/app', 'main', {
      connectionArn: 'arn:aws:codestar-connections:...',
    }),
    commands: ['npm ci', 'npm test', 'npx cdk synth'],
  }),
  selfMutation: true,  // default
});
 
pipeline.addStage(new AppStage(this, 'Dev', {
  env: { account: 'DEV-ACCT', region: 'ap-northeast-2' },
}));
 
pipeline.addStage(new AppStage(this, 'Prod', {
  env: { account: 'PRD-ACCT', region: 'ap-northeast-2' },
}), {
  pre: [new ManualApprovalStep('ApproveProd')],
  post: [new ShellStep('SmokeTest', {
    commands: ['curl -fSs https://api.example.com/health'],
  })],
});

Pipeline's actual Stage order:

1. Source (GitHub/CodeCommit pull)
2. Build (cdk synth → CFN template + Asset)
3. UpdatePipeline  ← self-mutating step
   (Pipeline def change → pipeline self-updates)
4. PublishAssets (Lambda zip → S3, Docker → ECR)
5. Dev (CFN deploy to DEV-ACCT)
6. ManualApproval
7. Prod (CFN deploy to PRD-ACCT)
8. SmokeTest

Stage 3 is critical. If developer adds new Stage (e.g., Staging), next push's UpdatePipeline auto-adds Staging Stage to pipeline, and subsequent runs execute new Stage. Pipeline definition changes need no separate tools.

💡 Related Theory: self-mutation follows unix make bootstrap or Lisp meta-circular evaluator (self-modifying code). System defines and evolves itself. Tekton Trigger Templates, ArgoCD App-of-Apps, Spinnaker Managed Pipeline Templates are variations of same pattern. CDK Pipelines most cleanly implements GitOps's core (pipelines as code) as AWS native tool.

🎯 Scenario: "Company adds 5 new customer sandbox environments weekly, operations manually updates pipeline." — Answer is CDK Pipelines + CDK code dynamically generating environments (for loop or external config file). Git commit adding environments → self-mutation auto-adds to pipeline. General CodePipeline + CFN need separate tools or manual steps.

crossAccountKeys — Auto KMS Key Management for Multi-Account

CDK Pipelines multi-account deployments require crossAccountKeys: true. Unknown what this automates causes permission setup accidents.

CodePipeline stores artifacts (build results, source) in S3, multi-account scenarios mean that S3 object needs reading by other account's CFN execution role. KMS-encrypted object requires KMS Key Policy allowing other account decryption. crossAccountKeys: true auto-creates:

  1. New KMS CMK in Tooling account
  2. KMS Key Policy auto-adds all target accounts' cfn-exec-role permissions
  3. S3 artifact bucket SSE-KMS setting
  4. Cross-account trust chain enables decryption

crossAccountKeys: false (default) uses AWS-managed KMS key, which can't share across accounts, causing multi-account deploy failure. This pitfall's "yesterday prod deployment worked, today adding Prod account causes KMS error" scenario frequently tests.

⚠️ Pitfall: crossAccountKeys: true creates customer-managed CMK with extra cost ($1/month/key). Toggling on/off means existing artifacts become unreadable, requiring careful decision. If multi-account is possible, start with enabled as standard.

CDK Aspects — Injecting Cross-Cutting Concerns into Tree

CDK Aspects are IaC's Aspect-Oriented Programming (AOP). Tree-wide traversal applies cross-cutting processing to all nodes.

import { Aspects, IAspect, IConstruct, CfnResource } from 'aws-cdk-lib';
 
class EnforceTags implements IAspect {
  visit(node: IConstruct) {
    if (node instanceof CfnResource) {
      node.tags.setTag('Environment', this.env);
      node.tags.setTag('CostCenter', this.costCenter);
      node.tags.setTag('Owner', 'platform-team');
    }
  }
  constructor(private env: string, private costCenter: string) {}
}
 
class EnforceEncryption implements IAspect {
  visit(node: IConstruct) {
    if (node instanceof s3.CfnBucket) {
      if (!node.bucketEncryption) {
        Annotations.of(node).addError('S3 buckets must have encryption');
      }
    }
    if (node instanceof rds.CfnDBInstance) {
      if (!node.storageEncrypted) {
        Annotations.of(node).addError('RDS must have storage encryption');
      }
    }
  }
}
 
Aspects.of(app).add(new EnforceTags('prod', 'cc-001'));
Aspects.of(app).add(new EnforceEncryption());

Annotations.addError fails synth, blocking build. Where Hooks block pre-deployment, Aspects block pre-synth, earlier in CI pipeline so feedback is faster.

Validation ToolTimingDoes
CDK Aspectscdk synth timeCDK code stage policy
cdk-nagcdk synth timeAWS Solutions/HIPAA/NIST ruleset bundles
cfn-lintPost-synth CFN timeTemplate syntax/best practice
CFN HookDeploy timePre-provision blocking (proactive)
Config RulesPost-deployEvaluation/alert/auto-fix (reactive)

📚 Case Study: 2022 SaaS applied cdk-nag full ruleset (NIST 800-53 Rev5) wholesale, build failed with 1200 violations. Instead of all-at-once enforcement: (1) warn-only ruleset initial → baseline, (2) top 5 impact rules enforce → 6-month gradual fix, (3) stage-wise enforce. Graceful governance adoption is standard.

Terraform and CDK Coexistence — CDKTF's Position

Real large organizations don't use one tool only. AWS workloads use CDK, multi-cloud or SaaS providers (GitHub, Datadog, Snowflake) use Terraform. Handling both:

  1. Physical separation: Directory/team split tooling, cross-reference via SSM Parameter Store.
  2. CDKTF: CDK syntax synthesizing Terraform HCL. CDK abstractions + Terraform Provider ecosystem.
  3. Terraform CDK + Provider: Some teams use Terraform-first while adopting CDK syntax.
// CDKTF — Generate Terraform from CDK syntax
import { App, TerraformStack, S3Backend } from 'cdktf';
import { AwsProvider, s3, datadog } from '@cdktf/provider-aws';
 
class HybridStack extends TerraformStack {
  constructor(scope: Construct, id: string) {
    super(scope, id);
    new AwsProvider(this, 'aws', { region: 'ap-northeast-2' });
    new DatadogProvider(this, 'dd', { apiKey: '...' });
 
    const bucket = new s3.S3Bucket(this, 'b', { bucket: 'cdktf-example' });
    new datadog.Monitor(this, 'm', {
      name: `s3-${bucket.bucket}-errors`,
      query: 'sum:s3.errors{bucket:cdktf-example} > 10',
    });
 
    new S3Backend(this, { bucket: 'tfstate', key: 'app/state.tfstate', region: 'ap-northeast-2' });
  }
}

CDKTF advantage: combine CDK code reusability with Terraform Provider breadth. Disadvantage: CDK camp prefers CFN-native speed, Terraform camp sees HCL as standard; CDKTF awkwardly middle. Rare on tests but frequent in practice.

ToolAWS DepthMulti-cloudState ManagementTest Weight
CloudFormationMaximum❌AWS managedVery high
CDKMaximum (CFN synth)❌AWS managedMedium
SAMServerless focused❌AWS managed (CFN)High
TerraformHigh✅tfstate (user responsibility)Low
CDKTFHigh✅tfstateVery low
PulumiHigh✅Pulumi Cloud or selfAlmost none

🔍 Deeper: Terraform tfstate's user responsibility is operations' biggest difference. tfstate records all actual resource state; corruption means Terraform sees "never saw this" → duplicate create or delete attempts. Operations standard: (1) S3 + DynamoDB lock for remote storage, (2) S3 versioning + object lock, (3) per-environment workspace or backend. CDK/CFN delegate this burden to AWS.

Summary

Today's picture has five parts. First, CDK synthesizes (generates) templates, inheriting all CFN governance (StackSets, Drift, ChangeSet) — LLVM IR pattern at two abstraction stages. Second, Construct L1/L2/L3 are three stairs where L2 is standard + custom L3 extraction. Third, bootstrap establishes trust infrastructure; cfn-exec-role strength is prod security core. Fourth, CDK Pipelines self-mutation

is GitOps's "pipelines as code," multi-account requires crossAccountKeys: true. Fifth, Aspects + cdk-nag (synth-time) + Hooks (deploy-time) + Config Rules (post-deploy) form three-layer validation.

Next we see CDK expand to entire AWS ecosystem in Week 9 summary, then Week 10 dives into monitoring.

📝 Practice Questions

Click a choice to reveal the answer and explanation.

Question 1

What is the key difference between CDK and Pulumi?

Question 2

What is the most recommended practical pattern for CDK construct levels?

Question 3

What is the security significance of the IAM role structure that cdk bootstrap creates?

Question 4

What does self-mutation mean in CDK Pipelines?

Question 5

Why is crossAccountKeys: true required for multi-account deployments with CDK Pipelines?

Question 6

What is the most accurate use case for CDK Aspects?

Question 7

What is the recommended strategy when introducing CDK Aspects or cdk-nag rule sets to prod?

📌 Today's Summary

The five key takeaways from today are as follows. First, CDK uses a code → CFN template synthesis model and automatically reuses CFN governance assets (the LLVM IR pattern). Second, construct L1/L2/L3 are three steps of abstraction, and in practice the approach is L2 by default plus extracting your own L3. Third, the five IAM roles of cdk bootstrap follow the principle of separation of privilege, and cfn-exec needs to be narrowed in prod. Fourth, CDK Pipelines self-mutation is the AWS implementation of the GitOps principle "the pipeline is code too," and multi-account setups require crossAccountKeys: true. Fifth, the three-tier verification layer of Aspects + cdk-nag (synth time) + Hooks (deployment time) + Config rules (after deployment), with phased rollout, is the standard for introducing governance.

PreviousCustom Resource·Hooks·Change Set: CloudFormation's Extension and Validation MechanismsWeek 8 · Day 3Next Week 8 Integrated Scenarios: Where IaC Tools Meet Inside a Single IncidentWeek 8 · Day 5

Official references

  • DOP-C02 official exam page(opens in a new tab)
  • AWS KMS documentation(opens in a new tab)
  • AWS CloudFormation documentation(opens in a new tab)
  • Amazon S3 documentation(opens in a new tab)
  • AWS IAM documentation(opens in a new tab)
  • AWS Lambda documentation(opens in a new tab)

On this page

  • CDK's Essence — "Synthesizing" Infrastructure with Code
  • Construct Levels L1/L2/L3 — Three Abstraction Stairs
  • CDK Bootstrap — The Hidden Trust Chain
  • CDK Pipelines — The True Meaning of self-mutation
  • crossAccountKeys — Auto KMS Key Management for Multi-Account
  • CDK Aspects — Injecting Cross-Cutting Concerns into Tree
  • Terraform and CDK Coexistence — CDKTF's Position
  • Summary
  • Practice Questions
  • Today's Summary