Skip to main content
Technology & EngineeringAws Services278 lines

Cloudformation

AWS CloudFormation infrastructure-as-code for provisioning and managing AWS resources declaratively

Quick Summary26 lines
You are an expert in AWS CloudFormation for defining, provisioning, and managing AWS infrastructure as code using declarative YAML or JSON templates.

## Key Points

- **Applying updates without change sets** -- Direct `update-stack` in production risks unintended resource replacements and data loss. Always preview changes first.
- **Building a single 500-resource mega-template** -- This exceeds stack limits, slows deployments, and makes rollbacks fragile. Decompose into nested stacks with clear dependency boundaries.
- **Ignoring stack drift** -- Manual console changes that diverge from the template create a shadow infrastructure. Run drift detection regularly and reconcile differences back into templates.
- **Always use change sets** for production updates. Review the changes before executing.
- **Set `DeletionPolicy: Retain`** on stateful resources (databases, S3 buckets) to prevent accidental data loss on stack deletion.
- **Use `!Sub` and `AWS::StackName`** for naming resources to avoid collisions between stacks.
- **Parameterize environments**: Use parameters and conditions so the same template works for dev, staging, and prod.
- **Export outputs** for cross-stack references, but be cautious: exported values create dependencies that block stack deletion.
- **Use `cfn-lint`** to validate templates before deploying: `cfn-lint template.yaml`.
- **Enable termination protection** on production stacks: `aws cloudformation update-termination-protection --enable-termination-protection --stack-name prod`.
- **Keep templates modular** using nested stacks. Separate network, compute, database, and monitoring into reusable templates.
- **Use stack policies** to prevent replacement or deletion of critical resources during updates.

## Quick Example

```bash
aws cloudformation detect-stack-drift --stack-name my-app-prod
aws cloudformation describe-stack-drift-detection-status --stack-drift-detection-id <id>
aws cloudformation describe-stack-resource-drifts --stack-name my-app-prod
```
skilldb get aws-services-skills/CloudformationFull skill: 278 lines
Paste into your CLAUDE.md or agent config

AWS CloudFormation — Cloud Services

You are an expert in AWS CloudFormation for defining, provisioning, and managing AWS infrastructure as code using declarative YAML or JSON templates.

Core Philosophy

Infrastructure belongs in code, not in click-ops. CloudFormation templates are the single source of truth for your AWS environment. Every resource -- from VPCs to Lambda functions -- should be declared in version-controlled templates so that environments are reproducible, auditable, and recoverable. If a resource exists in the console but not in a template, it is technical debt waiting to cause a production incident.

Change sets are your safety net. Never apply changes to production stacks directly. Always create a change set, review the planned modifications (especially replacements and deletions), and only then execute. This discipline catches destructive changes -- like an RDS engine swap that triggers resource replacement -- before they destroy data. Combine change sets with termination protection and stack policies for defense in depth.

Templates should be modular and parameterized. A single monolithic template with 400 resources is unmaintainable. Break infrastructure into nested stacks by concern (networking, compute, data, monitoring) and wire them together with outputs and cross-stack references. Use parameters and conditions so the same templates serve dev, staging, and production without forking.

Anti-Patterns

  • Applying updates without change sets -- Direct update-stack in production risks unintended resource replacements and data loss. Always preview changes first.
  • Hardcoding account IDs, regions, and resource names -- Use pseudo parameters (AWS::AccountId, AWS::Region) and AWS::StackName for dynamic naming. Hardcoded values break multi-account and multi-region deployments.
  • Omitting DeletionPolicy on stateful resources -- Databases, S3 buckets, and encryption keys should have DeletionPolicy: Retain or Snapshot to prevent accidental data destruction on stack deletion.
  • Building a single 500-resource mega-template -- This exceeds stack limits, slows deployments, and makes rollbacks fragile. Decompose into nested stacks with clear dependency boundaries.
  • Ignoring stack drift -- Manual console changes that diverge from the template create a shadow infrastructure. Run drift detection regularly and reconcile differences back into templates.

Overview

CloudFormation lets you model AWS resources in templates that are deployed as stacks. It handles dependency ordering, rollback on failure, drift detection, and change sets for safe updates. Templates declare resources, parameters, outputs, mappings, and conditions. Nested stacks and stack sets support modular and multi-account deployments.

Setup & Configuration

Template Structure

AWSTemplateFormatVersion: "2010-09-09"
Description: My application infrastructure

Parameters:
  Environment:
    Type: String
    AllowedValues: [dev, staging, prod]
    Default: dev
  VpcId:
    Type: AWS::EC2::VPC::Id

Conditions:
  IsProd: !Equals [!Ref Environment, prod]

Mappings:
  RegionConfig:
    us-east-1:
      AMI: ami-0abcdef1234567890
    eu-west-1:
      AMI: ami-0fedcba9876543210

Resources:
  # Resources defined here

Outputs:
  ApiEndpoint:
    Description: API Gateway endpoint URL
    Value: !GetAtt Api.Endpoint
    Export:
      Name: !Sub "${AWS::StackName}-ApiEndpoint"

Deploy a Stack

# Create stack
aws cloudformation create-stack \
  --stack-name my-app-prod \
  --template-body file://template.yaml \
  --parameters ParameterKey=Environment,ParameterValue=prod \
  --capabilities CAPABILITY_NAMED_IAM \
  --tags Key=Team,Value=platform

# Wait for completion
aws cloudformation wait stack-create-complete --stack-name my-app-prod

# Update stack via change set (safe preview)
aws cloudformation create-change-set \
  --stack-name my-app-prod \
  --change-set-name update-v2 \
  --template-body file://template-v2.yaml \
  --parameters ParameterKey=Environment,ParameterValue=prod \
  --capabilities CAPABILITY_NAMED_IAM

aws cloudformation describe-change-set \
  --stack-name my-app-prod \
  --change-set-name update-v2

aws cloudformation execute-change-set \
  --stack-name my-app-prod \
  --change-set-name update-v2

Core Patterns

VPC with Public and Private Subnets

Resources:
  VPC:
    Type: AWS::EC2::VPC
    Properties:
      CidrBlock: 10.0.0.0/16
      EnableDnsSupport: true
      EnableDnsHostnames: true
      Tags:
        - Key: Name
          Value: !Sub "${AWS::StackName}-vpc"

  PublicSubnetA:
    Type: AWS::EC2::Subnet
    Properties:
      VpcId: !Ref VPC
      CidrBlock: 10.0.1.0/24
      AvailabilityZone: !Select [0, !GetAZs ""]
      MapPublicIpOnLaunch: true

  PrivateSubnetA:
    Type: AWS::EC2::Subnet
    Properties:
      VpcId: !Ref VPC
      CidrBlock: 10.0.10.0/24
      AvailabilityZone: !Select [0, !GetAZs ""]

  InternetGateway:
    Type: AWS::EC2::InternetGateway

  AttachGateway:
    Type: AWS::EC2::VPCGatewayAttachment
    Properties:
      VpcId: !Ref VPC
      InternetGatewayId: !Ref InternetGateway

  NatGatewayEIP:
    Type: AWS::EC2::EIP
    DependsOn: AttachGateway

  NatGateway:
    Type: AWS::EC2::NatGateway
    Properties:
      AllocationId: !GetAtt NatGatewayEIP.AllocationId
      SubnetId: !Ref PublicSubnetA

Lambda Function with API Gateway

Resources:
  LambdaExecutionRole:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument:
        Version: "2012-10-17"
        Statement:
          - Effect: Allow
            Principal:
              Service: lambda.amazonaws.com
            Action: sts:AssumeRole
      ManagedPolicyArns:
        - arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole

  MyFunction:
    Type: AWS::Lambda::Function
    Properties:
      FunctionName: !Sub "${AWS::StackName}-handler"
      Runtime: python3.12
      Handler: index.handler
      Role: !GetAtt LambdaExecutionRole.Arn
      Code:
        ZipFile: |
          def handler(event, context):
              return {"statusCode": 200, "body": "Hello"}
      Environment:
        Variables:
          TABLE_NAME: !Ref DynamoTable

  DynamoTable:
    Type: AWS::DynamoDB::Table
    DeletionPolicy: Retain
    Properties:
      TableName: !Sub "${AWS::StackName}-data"
      BillingMode: PAY_PER_REQUEST
      AttributeDefinitions:
        - AttributeName: PK
          AttributeType: S
      KeySchema:
        - AttributeName: PK
          KeyType: HASH

Intrinsic Functions

Resources:
  Bucket:
    Type: AWS::S3::Bucket
    Properties:
      # !Sub - string interpolation
      BucketName: !Sub "${AWS::StackName}-${AWS::Region}-assets"

  # !If - conditional resource properties
  Instance:
    Type: AWS::EC2::Instance
    Properties:
      InstanceType: !If [IsProd, m5.xlarge, t3.micro]
      ImageId: !FindInMap [RegionConfig, !Ref "AWS::Region", AMI]

  # !GetAtt - get attribute from another resource
  # !Ref - reference a parameter or resource
  # !Join, !Split, !Select - string manipulation
  BucketPolicy:
    Type: AWS::S3::BucketPolicy
    Properties:
      Bucket: !Ref Bucket
      PolicyDocument:
        Statement:
          - Effect: Allow
            Principal:
              AWS: !GetAtt LambdaExecutionRole.Arn
            Action: s3:GetObject
            Resource: !Sub "${Bucket.Arn}/*"

Nested Stacks

Resources:
  NetworkStack:
    Type: AWS::CloudFormation::Stack
    Properties:
      TemplateURL: https://s3.amazonaws.com/my-templates/network.yaml
      Parameters:
        Environment: !Ref Environment

  AppStack:
    Type: AWS::CloudFormation::Stack
    DependsOn: NetworkStack
    Properties:
      TemplateURL: https://s3.amazonaws.com/my-templates/app.yaml
      Parameters:
        VpcId: !GetAtt NetworkStack.Outputs.VpcId
        SubnetIds: !GetAtt NetworkStack.Outputs.PrivateSubnetIds

Drift Detection

aws cloudformation detect-stack-drift --stack-name my-app-prod
aws cloudformation describe-stack-drift-detection-status --stack-drift-detection-id <id>
aws cloudformation describe-stack-resource-drifts --stack-name my-app-prod

Best Practices

  • Always use change sets for production updates. Review the changes before executing.
  • Set DeletionPolicy: Retain on stateful resources (databases, S3 buckets) to prevent accidental data loss on stack deletion.
  • Use !Sub and AWS::StackName for naming resources to avoid collisions between stacks.
  • Parameterize environments: Use parameters and conditions so the same template works for dev, staging, and prod.
  • Export outputs for cross-stack references, but be cautious: exported values create dependencies that block stack deletion.
  • Use cfn-lint to validate templates before deploying: cfn-lint template.yaml.
  • Enable termination protection on production stacks: aws cloudformation update-termination-protection --enable-termination-protection --stack-name prod.
  • Keep templates modular using nested stacks. Separate network, compute, database, and monitoring into reusable templates.
  • Use stack policies to prevent replacement or deletion of critical resources during updates.

Common Pitfalls

  • Circular dependencies: Resource A depends on B and B depends on A. Break the cycle with DependsOn restructuring or by splitting into separate stacks.
  • Replacement vs. update: Changing certain properties (e.g., RDS Engine, Lambda FunctionName) causes resource replacement, destroying the old resource. Check the CloudFormation docs for "Update requires: Replacement".
  • Forgetting CAPABILITY_NAMED_IAM: Stacks that create IAM resources require explicit acknowledgment via capabilities.
  • Stack update rollback stuck in UPDATE_ROLLBACK_FAILED: Use continue-update-rollback with --resources-to-skip to skip problematic resources.
  • Template size limits: Template body max 51,200 bytes inline, 460,800 bytes from S3. Use nested stacks for large architectures.
  • Resource limit per stack: 500 resources per stack. Plan modular architecture for large deployments.
  • Hard-coded account IDs and regions: Use AWS::AccountId, AWS::Region pseudo parameters instead.

Install this skill directly: skilldb add aws-services-skills

Get CLI access →