LearnDevOps
DevOps04. Infrastructure as Code (IaC)·EXPERT·6 min read

14. Cloud IaC

Cloud IaC

TL;DR

  • CloudFormation is AWS-native IaC; CDK is a TypeScript/Python layer that synthesizes to CloudFormation; the real decision is immediacy (AWS-native, fast feature support) versus portability (multi-cloud, single tool).
  • CloudFormation automatically rolls back failed stack updates to the last good state, a meaningfully different safety posture than tools leaving partial changes in place.
  • CDK synthesizes your application-language code to a plain CloudFormation template before deployment; debug against the synthesized template, not the source code, to match what actually failed.
  • CDK Aspects programmatically enforce organizational policy (tags, encryption, resource limits) across stacks; far more reliable than expecting teams to remember standards in code review.
  • AWS-only footprint favors CloudFormation/CDK; genuine multi-cloud or multi-SaaS footprint favors Terraform’s single-tool, cross-provider approach.

Core Concepts

CloudFormation: AWS-native, AWS-managed state

CloudFormation is AWS’s native declarative IaC service. Advantages:

  • Tight AWS integration: brand-new AWS features often available in CloudFormation before Terraform providers catch up (same organization maintains both)
  • AWS-managed state: no separate state file or backend to configure
  • Automatic rollback: failed stack updates roll back to last good state automatically, not leaving partial changes in place

Trade-off: AWS-only. Multi-cloud organizations give up a single unified tool and workflow.

Automatic rollback behavior is operationally significant: when a stack update fails partway through, CloudFormation automatically rolls back applied changes rather than leaving the stack in an inconsistent, half-updated state. This is a meaningfully different failure mode than tools that leave partial changes for manual reconciliation.

AWS CDK: application language → CloudFormation

CDK lets you define infrastructure in real programming languages (TypeScript, Python, Java) instead of verbose JSON/YAML:

// TypeScript CDK
const stack = new cdk.Stack(app, 'MyStack');

// Create 10 S3 buckets with a loop
for (let i = 0; i < 10; i++) {
  new s3.Bucket(stack, `Bucket${i}`, {
    bucketName: `my-bucket-${i}`,
    encryption: s3.BucketEncryption.S3_MANAGED,
  });
}

// Conditional resources
if (config.enableDatabaseBackup) {
  new rds.Database(stack, 'BackupDB', {...});
}

// Classes and reuse
class AppStack extends cdk.Stack {
  constructor(scope, id, props) { ... }
}

Critical for debugging: CDK synthesizes this TypeScript to a plain CloudFormation template before anything deploys. The loop, conditionals, classes — all synthesize away. What actually gets deployed, and what you debug against when it fails, is the synthesized CloudFormation template:

cdk synth  # Generate synthesized CloudFormation
# Output: a plain CloudFormation JSON template

Treating CDK as a black box that skips synthesis is a common debugging mistake.

CDK Aspects: policy enforcement as code

Aspects let platform teams programmatically visit and validate/mutate every construct in a stack (or across many stacks), enforcing organizational policy:

// Enforce required tags on every resource
Aspects.of(stack).add(new RequiredTagsAspect({
  requiredTags: { Environment: true, Owner: true, CostCenter: true }
}));

// Enforce encryption on S3 buckets
Aspects.of(stack).add(new EnforceS3EncryptionAspect());

// Validate resources stay under size limits
Aspects.of(stack).add(new ResourceSizeLimitAspect());

This is the CDK equivalent of a shared Terraform module: write the policy once, have it apply consistently to every team’s infrastructure code. Violations are caught at synthesis time, not in post-hoc code review.

State management in CloudFormation

CloudFormation manages state internally (AWS tracks stack resources). Unlike Terraform, teams don’t have a separate state file to manage. This simplifies operations (no backend configuration needed) but also means AWS controls state backup and recovery, with less flexibility for custom recovery scenarios.

CloudFormation vs CDK vs Terraform

Aspect CloudFormation (JSON/YAML) AWS CDK Terraform
Language JSON/YAML (declarative) TypeScript, Python, etc. (imperative→declarative) HCL (declarative)
State management AWS-managed AWS-managed (via CloudFormation) Explicit state file
Cloud scope AWS only AWS only Multi-cloud (100+ providers)
New AWS feature support Fast (AWS-native) Fast (synthesizes to CF) Slower (depends on provider lag)
Programming abstractions Limited (templating) Full (loops, classes, inheritance) Limited (modules)
Rollback on failure Automatic Automatic (via CF) Manual or via wrapper tool
Reusable patterns CloudFormation modules CDK constructs Terraform modules
Multi-region/account Stack replication CDK stacks in multiple contexts terraform_remote_state + cross-account

Synthesis and Debugging Workflow

# CDK synthesis
cdk synth                              # Generate CloudFormation
cdk synth --json > template.json       # Save to file

# Debugging a failed deployment
cdk deploy MyStack                     # Deploy and fail
# Error message references CloudFormation

# First step: look at the synthesized CloudFormation
cdk synth MyStack | jq '.Resources.SomeResource'
# This is what actually failed, not the TypeScript source

# Common mistake
# ❌ Staring at TypeScript source trying to understand a CloudFormation error
# ✅ Look at the synthesized CloudFormation template instead

Configuration Reference

// CDK stack with organization policy via Aspects
import * as cdk from 'aws-cdk-lib';
import * as s3 from 'aws-cdk-lib/aws-s3';
import { Aspects, IAspect } from 'aws-cdk-lib';
import { IConstruct } from 'constructs';

// Custom Aspect: enforce encryption on S3 buckets
class S3EncryptionAspect implements IAspect {
  visit(node: IConstruct) {
    if (node instanceof s3.CfnBucket) {
      if (!node.serverSideEncryptionConfiguration) {
        node.serverSideEncryptionConfiguration = [{
          serverSideEncryptionByDefault: {
            sseAlgorithm: 'AES256',
          },
        }];
      }
    }
  }
}

// Stack definition
class MyStack extends cdk.Stack {
  constructor(scope: cdk.App, id: string, props?: cdk.StackProps) {
    super(scope, id, props);

    new s3.Bucket(this, 'MyBucket', {
      bucketName: 'my-data-bucket',
      // Encryption will be automatically added by S3EncryptionAspect
    });

    // Apply policy aspects
    Aspects.of(this).add(new S3EncryptionAspect());
    Aspects.of(this).add(new RequiredTagsAspect(['Environment', 'Owner']));
  }
}

// App
const app = new cdk.App();
new MyStack(app, 'MyStack', {
  env: { account: '123456789012', region: 'us-east-1' },
});

Common Pitfalls

  • Pitfall: CloudFormation chosen for a genuinely multi-cloud organization; years later, managing non-AWS SaaS infrastructure through a different tool causes friction. Why: AWS-only limitation wasn’t factored into the long-term decision. Fix: Evaluate actual cloud footprint; if genuine multi-cloud, Terraform’s single-tool approach is more operationally efficient.

  • Pitfall: CDK deployment failure; hours spent debugging TypeScript source instead of the synthesized CloudFormation. Why: Synthesis step wasn’t understood as the critical debugging layer. Fix: Always look at the synthesized CloudFormation template when debugging; this is what actually failed.

  • Pitfall: Tagging and encryption standards enforced only through code review; teams miss them inconsistently. Why: Policy wasn’t programmatically enforced. Fix: Use CDK Aspects to programmatically enforce organizational policy; violations are caught at synthesis time, not left to code review.

  • Pitfall: Stack update fails partway; team expects partial changes to persist, but CloudFormation automatically rolled back. Why: Automatic rollback behavior wasn’t understood. Fix: Document CloudFormation’s automatic-rollback behavior; factor into incident response and recovery procedures.

  • Pitfall: A team doesn’t understand that CDK is a synthesis layer and tries to debug by looking at original TypeScript. Why: CDK’s role as a synthesis tool wasn’t clearly explained. Fix: Emphasize that CDK synthesizes to CloudFormation; debugging happens at the CloudFormation level.

Interview Questions

  • Explain the decision between CloudFormation/CDK and Terraform for an AWS-heavy but multi-SaaS organization. — Tests understanding of the immediacy-versus-portability trade-off.

  • A CDK deployment fails with a cryptic CloudFormation error. Walk me through your debugging approach. — Tests whether synthesized template inspection is understood as the correct layer.

  • Design a mechanism to enforce mandatory tags and S3 encryption across all teams’ CDK infrastructure. — Tests whether CDK Aspects are reached for as the purpose-built solution.

  • What happens when a CloudFormation stack update fails partway through? How is this different from other IaC tools? — Tests understanding of automatic-rollback behavior and its operational implications.

  • Why can CDK support loops and classes in infrastructure definitions while raw CloudFormation cannot? — Tests understanding of synthesis and the transformation from programming language to declarative template.

🔒 Locked
Sign in and unlock the full syllabus to keep reading.
Sign in to continue
Knowledge check
5 scenario questions on this topic
Take the quiz →
Related in 04. Infrastructure as Code (IaC)
10. IaC Fundamentals
EXPERT
11. Terraform
EXPERT
12. OpenTofu
EXPERT
13. Terragrunt
EXPERT