# AWS Cloudformation

> "Creates Infrastructure as Code templates with CloudFormation for reproducible" versioned, automated deployments of entire AWS infrastructure stacks.

- Skill: `paulpas/aws-cloudformation` (Agent Skill)
- Install (CLI): `npx skillmds@latest add paulpas/aws-cloudformation`
- Raw SKILL.md: https://api.skillmd.com/api/skills/paulpas/aws-cloudformation/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: DevOps & Infra
- License: MIT
- Author: paulpas (https://skillmd.com/u/paulpas)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/paulpas/aws-cloudformation

---





# CloudFormation

Design and deploy complete AWS infrastructure as code using templates, enabling version control, repeatable deployments, and automated stack management.

## TL;DR Checklist

- [ ] Use YAML format for templates (more readable than JSON)
- [ ] Parameterize templates for reusability across environments
- [ ] Use Outputs to expose important resource values
- [ ] Implement change sets before updating production stacks
- [ ] Version control all templates in Git
- [ ] Use DependsOn for explicit resource dependencies
- [ ] Implement rollback on update failure
- [ ] Create separate templates for modularity (nested stacks)
- [ ] Use stack policies to prevent accidental resource deletion
- [ ] Tag all resources for cost allocation and management

---

## When to Use

Use CloudFormation when:

- Infrastructure needs to be reproducible across environments
- Requiring version control for infrastructure changes
- Automating infrastructure provisioning and updates
- Building reusable templates for common architectures
- Implementing infrastructure as code (IaC) practices
- Coordinating complex resource dependencies

---

## When NOT to Use

Avoid CloudFormation when:

- Building one-off infrastructure (manual console is faster)
- Requiring rapid prototyping (AWS CDK may be better)
- Managing third-party services (use Terraform instead)
- Working with non-AWS resources exclusively

---

## Purpose and Use Cases

**Primary Purpose:** Provide declarative infrastructure as code enabling version-controlled, repeatable, automated deployments of entire AWS infrastructure stacks.

**Common Use Cases:**

1. **Environment Templates** — Dev, staging, production environments from same template
2. **Disaster Recovery** — Rebuild entire infrastructure in secondary region
3. **Multi-Account Deployments** — Deploy stack across multiple AWS accounts
4. **Blue-Green Deployments** — Create new stack, test, then switch traffic
5. **Infrastructure Automation** — CI/CD pipelines deploy infrastructure changes
6. **Compliance and Governance** — Enforce architecture standards via templates

---

## Architecture Design Patterns

### Pattern 1: Modular Multi-AZ Web Application Stack

```yaml
AWSTemplateFormatVersion: '2010-09-09'
Description: Production web application stack with VPC, RDS, and ALB

Metadata:
  AWS::CloudFormation::Interface:
    ParameterGroups:
      - Label:
          default: Network Configuration
        Parameters:
          - VPCCidr
          - Environment
          - EnableEncryption
      - Label:
          default: Database Configuration
        Parameters:
          - DBInstanceClass
          - DBAllocatedStorage

Parameters:
  Environment:
    Type: String
    Default: production
    AllowedValues:
      - development
      - staging
      - production
    Description: Environment name

  VPCCidr:
    Type: String
    Default: 10.0.0.0/16
    Description: CIDR block for VPC

  DBInstanceClass:
    Type: String
    Default: db.r6i.2xlarge
    Description: Database instance type
    AllowedValues:
      - db.t3.medium
      - db.r6i.xlarge
      - db.r6i.2xlarge
      - db.r6i.4xlarge

  DBAllocatedStorage:
    Type: Number
    Default: 100
    MinValue: 20
    MaxValue: 1000
    Description: Database storage in GB

  EnableEncryption:
    Type: String
    Default: 'true'
    AllowedValues:
      - 'true'
      - 'false'
    Description: Enable encryption at rest

Conditions:
  IsProduction: !Equals [!Ref Environment, production]
  ShouldEncrypt: !Equals [!Ref EnableEncryption, 'true']

Resources:
  # ==================== VPC ====================
  ProductionVPC:
    Type: AWS::EC2::VPC
    Properties:
      CidrBlock: !Ref VPCCidr
      EnableDnsHostnames: true
      EnableDnsSupport: true
      Tags:
        - Key: Name
          Value: !Sub '${Environment}-vpc'

  # Public Subnets
  PublicSubnet1:
    Type: AWS::EC2::Subnet
    Properties:
      VpcId: !Ref ProductionVPC
      CidrBlock: !Select [0, !Cidr [!Ref VPCCidr, 6, 8]]
      AvailabilityZone: !Select [0, !GetAZs '']
      MapPublicIpOnLaunch: true
      Tags:
        - Key: Name
          Value: !Sub '${Environment}-public-subnet-1'

  PublicSubnet2:
    Type: AWS::EC2::Subnet
    Properties:
      VpcId: !Ref ProductionVPC
      CidrBlock: !Select [1, !Cidr [!Ref VPCCidr, 6, 8]]
      AvailabilityZone: !Select [1, !GetAZs '']
      MapPublicIpOnLaunch: true
      Tags:
        - Key: Name
          Value: !Sub '${Environment}-public-subnet-2'

  # Private Subnets
  PrivateSubnet1:
    Type: AWS::EC2::Subnet
    Properties:
      VpcId: !Ref ProductionVPC
      CidrBlock: !Select [2, !Cidr [!Ref VPCCidr, 6, 8]]
      AvailabilityZone: !Select [0, !GetAZs '']
      Tags:
        - Key: Name
          Value: !Sub '${Environment}-private-subnet-1'

  PrivateSubnet2:
    Type: AWS::EC2::Subnet
    Properties:
      VpcId: !Ref ProductionVPC
      CidrBlock: !Select [3, !Cidr [!Ref VPCCidr, 6, 8]]
      AvailabilityZone: !Select [1, !GetAZs '']
      Tags:
        - Key: Name
          Value: !Sub '${Environment}-private-subnet-2'

  # Database Subnets
  DBSubnet1:
    Type: AWS::EC2::Subnet
    Properties:
      VpcId: !Ref ProductionVPC
      CidrBlock: !Select [4, !Cidr [!Ref VPCCidr, 6, 8]]
      AvailabilityZone: !Select [0, !GetAZs '']
      Tags:
        - Key: Name
          Value: !Sub '${Environment}-db-subnet-1'

  DBSubnet2:
    Type: AWS::EC2::Subnet
    Properties:
      VpcId: !Ref ProductionVPC
      CidrBlock: !Select [5, !Cidr [!Ref VPCCidr, 6, 8]]
      AvailabilityZone: !Select [1, !GetAZs '']
      Tags:
        - Key: Name
          Value: !Sub '${Environment}-db-subnet-2'

  # Internet Gateway
  InternetGateway:
    Type: AWS::EC2::InternetGateway
    Properties:
      Tags:
        - Key: Name
          Value: !Sub '${Environment}-igw'

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

  # NAT Gateways
  NATGatewayEIP1:
    Type: AWS::EC2::EIP
    DependsOn: AttachGateway
    Properties:
      Domain: vpc
      Tags:
        - Key: Name
          Value: !Sub '${Environment}-nat-eip-1'

  NATGateway1:
    Type: AWS::EC2::NatGateway
    Properties:
      AllocationId: !GetAtt NATGatewayEIP1.AllocationId
      SubnetId: !Ref PublicSubnet1

  # ==================== SECURITY ====================
  ALBSecurityGroup:
    Type: AWS::EC2::SecurityGroup
    Properties:
      GroupDescription: ALB security group
      VpcId: !Ref ProductionVPC
      SecurityGroupIngress:
        - IpProtocol: tcp
          FromPort: 80
          ToPort: 80
          CidrIp: 0.0.0.0/0
        - IpProtocol: tcp
          FromPort: 443
          ToPort: 443
          CidrIp: 0.0.0.0/0
      Tags:
        - Key: Name
          Value: !Sub '${Environment}-alb-sg'

  AppSecurityGroup:
    Type: AWS::EC2::SecurityGroup
    Properties:
      GroupDescription: Application security group
      VpcId: !Ref ProductionVPC
      SecurityGroupIngress:
        - IpProtocol: tcp
          FromPort: 8080
          ToPort: 8080
          SourceSecurityGroupId: !Ref ALBSecurityGroup
      Tags:
        - Key: Name
          Value: !Sub '${Environment}-app-sg'

  DBSecurityGroup:
    Type: AWS::EC2::SecurityGroup
    Properties:
      GroupDescription: Database security group
      VpcId: !Ref ProductionVPC
      SecurityGroupIngress:
        - IpProtocol: tcp
          FromPort: 5432
          ToPort: 5432
          SourceSecurityGroupId: !Ref AppSecurityGroup
      Tags:
        - Key: Name
          Value: !Sub '${Environment}-db-sg'

  # ==================== DATABASE ====================
  DBSubnetGroup:
    Type: AWS::RDS::DBSubnetGroup
    Properties:
      DBSubnetGroupDescription: Database subnet group
      SubnetIds:
        - !Ref DBSubnet1
        - !Ref DBSubnet2
      Tags:
        - Key: Name
          Value: !Sub '${Environment}-db-subnet-group'

  Database:
    Type: AWS::RDS::DBInstance
    DeletionPolicy: Snapshot
    Properties:
      DBInstanceIdentifier: !Sub '${Environment}-db'
      Engine: postgres
      EngineVersion: '14.7'
      DBInstanceClass: !Ref DBInstanceClass
      AllocatedStorage: !Ref DBAllocatedStorage
      StorageType: gp3
      StorageEncrypted: !If [ShouldEncrypt, true, false]
      MasterUsername: admin
      MasterUserPassword: !Sub '{{resolve:secretsmanager:${Environment}/db-password:SecretString:password}}'
      DBSubnetGroupName: !Ref DBSubnetGroup
      VPCSecurityGroups:
        - !Ref DBSecurityGroup
      MultiAZ: !If [IsProduction, true, false]
      BackupRetentionPeriod: !If [IsProduction, 30, 7]
      PreferredBackupWindow: '03:00-04:00'
      PreferredMaintenanceWindow: 'sun:04:00-sun:05:00'
      DeletionProtection: !If [IsProduction, true, false]
      Tags:
        - Key: Name
          Value: !Sub '${Environment}-database'

Outputs:
  VPCId:
    Description: VPC ID
    Value: !Ref ProductionVPC
    Export:
      Name: !Sub '${Environment}-VPC-ID'

  PrivateSubnet1Id:
    Description: Private subnet 1 ID
    Value: !Ref PrivateSubnet1
    Export:
      Name: !Sub '${Environment}-PrivateSubnet1-ID'

  PrivateSubnet2Id:
    Description: Private subnet 2 ID
    Value: !Ref PrivateSubnet2
    Export:
      Name: !Sub '${Environment}-PrivateSubnet2-ID'

  DatabaseEndpoint:
    Description: Database endpoint address
    Value: !GetAtt Database.Endpoint.Address
    Export:
      Name: !Sub '${Environment}-Database-Endpoint'

  DBSecurityGroupId:
    Description: Database security group ID
    Value: !Ref DBSecurityGroup
    Export:
      Name: !Sub '${Environment}-DBSecurityGroup-ID'

  ALBSecurityGroupId:
    Description: ALB security group ID
    Value: !Ref ALBSecurityGroup
    Export:
      Name: !Sub '${Environment}-ALBSecurityGroup-ID'
```

**Key Elements:**
- Parameterized for environment reusability
- Conditional resources (production vs. development)
- Multi-AZ high availability
- Exports for cross-stack references
- Comprehensive Outputs for integration
- Clear resource naming and tagging

### Pattern 2: Change Set and Stack Policy for Safety

```yaml
# Create change set before updating stack
aws cloudformation create-change-set \
  --stack-name prod-stack \
  --change-set-name prod-stack-update-$(date +%s) \
  --template-body file://template.yaml \
  --parameters ParameterKey=Environment,ParameterValue=production \
  --change-set-type UPDATE

# Review change set
aws cloudformation describe-change-set \
  --stack-name prod-stack \
  --change-set-name prod-stack-update-1234567890

# Execute change set
aws cloudformation execute-change-set \
  --stack-name prod-stack \
  --change-set-name prod-stack-update-1234567890

# Stack Policy to prevent accidental deletion
{
  "Statement": [
    {
      "Effect": "Deny",
      "Principal": "*",
      "Action": [
        "cloudformation:Delete",
        "cloudformation:Update"
      ],
      "Resource": "LogicalResourceId/Database",
      "Condition": {
        "StringNotLike": {
          "aws:PrincipalArn": "arn:aws:iam::123456789012:role/admin"
        }
      }
    },
    {
      "Effect": "Allow",
      "Principal": "*",
      "Action": "cloudformation:Update",
      "Resource": "*"
    }
  ]
}
```

**Key Elements:**
- Change sets for safe updates (review before execute)
- Stack policies for protection
- Rollback on failure automatic
- CloudTrail logs all stack operations

### Pattern 3: Nested Stacks for Modularity

```yaml
# Parent stack references nested stacks
Resources:
  NetworkingStack:
    Type: AWS::CloudFormation::Stack
    Properties:
      StackName: !Sub '${Environment}-networking'
      TemplateURL: https://s3.amazonaws.com/cf-templates/networking.yaml
      Parameters:
        VPCCidr: !Ref VPCCidr
        Environment: !Ref Environment

  DatabaseStack:
    Type: AWS::CloudFormation::Stack
    DependsOn: NetworkingStack
    Properties:
      StackName: !Sub '${Environment}-database'
      TemplateURL: https://s3.amazonaws.com/cf-templates/database.yaml
      Parameters:
        VPCId: !GetAtt NetworkingStack.Outputs.VPCId
        DBSubnetGroup: !GetAtt NetworkingStack.Outputs.DBSubnetGroupName

  ApplicationStack:
    Type: AWS::CloudFormation::Stack
    DependsOn: DatabaseStack
    Properties:
      StackName: !Sub '${Environment}-application'
      TemplateURL: https://s3.amazonaws.com/cf-templates/application.yaml
      Parameters:
        VPCId: !GetAtt NetworkingStack.Outputs.VPCId
        DatabaseEndpoint: !GetAtt DatabaseStack.Outputs.DatabaseEndpoint

Outputs:
  ApplicationURL:
    Value: !GetAtt ApplicationStack.Outputs.ApplicationURL
```

**Key Elements:**
- Modular nested stacks for separation of concerns
- DependsOn for ordered stack creation
- Cross-stack outputs via GetAtt
- Reusable templates for common infrastructure

---

## Integration Approaches

### 1. Integration with CodePipeline

CloudFormation + CodePipeline enables:
- Automated infrastructure deployments
- Change sets in CI/CD pipeline
- Approval workflows before production
- Rollback on pipeline failures

### 2. Integration with Service Catalog

Service Catalog + CloudFormation:
- Self-service infrastructure provisioning
- Governance via approved templates
- End-user simplicity with pre-configured options
- Cost tracking per product

### 3. Integration with StackSets

StackSets enable:
- Deploy stack to multiple accounts/regions
- Centralized management
- Automatic updates across deployment targets
- Role-based permissions

### 4. Integration with Systems Manager

Systems Manager + CloudFormation:
- Document-based automation
- Parameter store for template values
- Automation runbooks for post-deployment
- Configuration management

---

## Common Pitfalls

### ❌ Pitfall 1: Hardcoded Values in Templates

**Problem:** Values not reusable; must edit template for each environment.

**Solution:**
- Use Parameters for all environment-specific values
- Use Pseudo Parameters for account/region
- Parameterize instance types, storage, retention

### ❌ Pitfall 2: Tight Coupling Between Stacks

**Problem:** Stacks depend on internal resource IDs; hard to update independently.

**Solution:**
- Use Outputs and cross-stack references
- Decouple stacks functionally (networking, database, app)
- Use nested stacks for logical grouping

### ❌ Pitfall 3: No Change Set Review

**Problem:** Update stack directly; unexpected changes cause outages.

**Solution:**
- Always use change sets for production
- Review changes before execution
- Enable termination protection for critical stacks

### ❌ Pitfall 4: Unversioned Templates

**Problem:** Lose history of infrastructure changes; rollback difficult.

**Solution:**
- Version control all templates in Git
- Use commit messages to document changes
- Tag releases in Git matching stack updates

### ❌ Pitfall 5: Resource Deletion Protection Missing

**Problem:** Accidental resource deletion causes data loss.

**Solution:**
- Set DeletionPolicy: Snapshot for databases
- Enable DeletionProtection on production resources
- Use stack policies to prevent changes

### ❌ Pitfall 6: No Outputs Defined

**Problem:** Stack created but outputs hidden; hard to use resources.

**Solution:**
- Define all important resource IDs in Outputs
- Export outputs for cross-stack references
- Document output purposes

---

## Best Practices Summary

| Category | Best Practice |
|---|---|
| **Design** | Modular nested stacks; separate concerns |
| **Parameters** | Parameterize all environment-specific values |
| **Updates** | Use change sets; review before execution |
| **Safety** | Stack policies; termination protection; DeletionPolicy |
| **Version Control** | Git version all templates and parameters |
| **Documentation** | Clear Metadata; parameter descriptions; Outputs |

---

## Related Skills

| Skill | Purpose |
|---|---|
| `cncf-aws-iam` | IAM roles for CloudFormation |
| `cncf-aws-ec2` | EC2 resources in templates |
| `cncf-aws-rds` | Database resources in templates |
| `cncf-aws-vpc` | VPC and networking in templates |
---

## Core Workflow

1. **Assess Requirements** — Understand the use case, scale, integration needs, and existing infrastructure. **Checkpoint:** Document requirements, constraints, and success criteria.

2. **Design Architecture** — Plan component interactions, data flow, and deployment strategy using cloud-native best practices. **Checkpoint:** Verify the architecture addresses all requirements and follows CNCF conventions.

3. **Implement & Configure** — Create manifests, configurations, and deployment scripts. Include resource limits, health checks, and observability hooks. **Checkpoint:** Validate all YAML against schema and test in a staging environment.

4. **Deploy & Monitor** — Apply manifests to the cluster, verify component health, and confirm observability is working. **Checkpoint:** Confirm all pods/services are running, probes passing, and metrics/alerts configured.

---

## Constraints

### MUST DO
- Include at least one complete working YAML manifest example
- Note when content is auto-generated vs. manually verified
- Reference relevant CNCF project documentation

### MUST NOT DO
- Deploy manifests without testing in a staging environment first
- Use deprecated API versions (e.g., apps/v1beta1)
- Omit resource limits and requests in Kubernetes manifests

