domains / devops / cloudformation-deploy / cloudformation

cloudformation

Skill card

combined

The copies overlap on core CloudFormation stack operations, but each has material steps the others lack. BagelHole and sickn33 are near-identical (sickn33 is a mirror of BagelHole) and focus on a production web template with ASG/ELB, cfn-lint, and termination protection. itsmostafa adds distinct material: Stack Sets, a troubleshooting section, and specific resource patterns (Lambda, VPC, DynamoDB) not present in the others. Crowning one would hide the itsmostafa-specific patterns and troubleshooting guidance.

Repositories

RepositoryPath
BagelHole/DevOps-Security-Agent-Skillsinfrastructure/cloud-aws/cloudformation/SKILL.md
itsmostafa/aws-agent-skillsskills/cloudformation/SKILL.md
sickn33/agentic-awesome-skillsplugins/agentic-awesome-skills-claude/skills/cloudformation/SKILL.md

Other copies

RoleRepositoryPath
peerBagelHole/DevOps-Security-Agent-Skillsinfrastructure/cloud-aws/cloudformation/SKILL.md
peeritsmostafa/aws-agent-skillsskills/cloudformation/SKILL.md
peersickn33/agentic-awesome-skillsplugins/agentic-awesome-skills-claude/skills/cloudformation/SKILL.md
adaptersickn33/agentic-awesome-skillsplugins/agentic-awesome-skills/skills/cloudformation/SKILL.md
adaptersickn33/agentic-awesome-skillsskills/cloudformation/SKILL.md

Combined skill

---
name: cloudformation
description: Deploy AWS resources with CloudFormation templates. Create stacks, use nested stacks, implement drift detection, and manage multi-account deployments with Stack Sets. Use when deploying AWS-native IaC.
license: MIT
metadata:
  author: devops-skills
  version: "1.0"
---

# CloudFormation

Deploy AWS infrastructure with native CloudFormation templates, change sets, nested stacks, drift detection, and Stack Sets.

## When to Use This Skill

- Deploying AWS resources using AWS-native Infrastructure as Code
- Creating repeatable, parameterized infrastructure templates
- Managing multi-environment deployments (dev, staging, prod) with the same template
- Implementing safe deployments with change sets and rollback protection
- Detecting and remediating configuration drift
- Organizing large infrastructure into nested stacks
- Deploying stacks across multiple accounts and regions with Stack Sets
- Exporting/importing values between stacks

## Prerequisites

- AWS CLI v2 installed and configured
- IAM permissions: `cloudformation:*`, plus permissions for all resources in the template
- (Optional) `cfn-lint` installed for template validation (`pip install cfn-lint`)
- S3 bucket for storing templates larger than 51,200 bytes

## Core Concepts

- **Templates**: JSON or YAML files defining AWS resources with Parameters, Mappings, Conditions, Resources, and Outputs
- **Stacks**: Collection of resources managed as a single unit
- **Change Sets**: Preview changes before executing updates
- **Stack Sets**: Deploy stacks across multiple accounts and regions
- **Nested Stacks**: Organize large infrastructure into modular sub-templates

## Template Structure

```yaml
AWSTemplateFormatVersion: '2010-09-09'
Description: Production web application infrastructure

Metadata:
  AWS::CloudFormation::Interface:
    ParameterGroups:
      - Label: { default: "Environment" }
        Parameters: [Environment, InstanceType]
      - Label: { default: "Network" }
        Parameters: [VpcId, SubnetIds]

Parameters:
  Environment:
    Type: String
    AllowedValues: [dev, staging, prod]
    Default: dev

  InstanceType:
    Type: String
    Default: t3.micro
    AllowedValues: [t3.micro, t3.small, t3.medium, t3.large]

  VpcId:
    Type: AWS::EC2::VPC::Id
    Description: VPC to deploy into

  SubnetIds:
    Type: List<AWS::EC2::Subnet::Id>
    Description: Subnets for the application

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

Mappings:
  RegionAMI:
    us-east-1:
      AL2023: ami-0abcdef1234567890
    us-west-2:
      AL2023: ami-0fedcba9876543210

Resources:
  SecurityGroup:
    Type: AWS::EC2::SecurityGroup
    Properties:
      GroupDescription: !Sub '${Environment}-web-sg'
      VpcId: !Ref VpcId
      SecurityGroupIngress:
        - IpProtocol: tcp
          FromPort: 443
          ToPort: 443
          CidrIp: 0.0.0.0/0
      Tags:
        - Key: Name
          Value: !Sub '${Environment}-web-sg'

  LaunchTemplate:
    Type: AWS::EC2::LaunchTemplate
    Properties:
      LaunchTemplateName: !Sub '${Environment}-web'
      LaunchTemplateData:
        ImageId: !FindInMap [RegionAMI, !Ref 'AWS::Region', AL2023]
        InstanceType: !If [IsProd, t3.large, !Ref InstanceType]
        MetadataOptions:
          HttpTokens: required
        SecurityGroupIds:
          - !Ref SecurityGroup

  AutoScalingGroup:
    Type: AWS::AutoScaling::AutoScalingGroup
    Properties:
      AutoScalingGroupName: !Sub '${Environment}-web-asg'
      LaunchTemplate:
        LaunchTemplateId: !Ref LaunchTemplate
        Version: !GetAtt LaunchTemplate.LatestVersionNumber
      MinSize: !If [IsProd, 2, 1]
      MaxSize: !If [IsProd, 10, 3]
      DesiredCapacity: !If [IsProd, 4, 1]
      VPCZoneIdentifier: !Ref SubnetIds
      TargetGroupARNs:
        - !Ref TargetGroup
      HealthCheckType: ELB
      HealthCheckGracePeriod: 300
      Tags:
        - Key: Name
          Value: !Sub '${Environment}-web'
          PropagateAtLaunch: true
    UpdatePolicy:
      AutoScalingRollingUpdate:
        MinInstancesInService: !If [IsProd, 2, 0]
        MaxBatchSize: 1
        PauseTime: PT5M
        WaitOnResourceSignals: true
        SuspendProcesses:
          - HealthCheck
          - ReplaceUnhealthy
          - AZRebalance
          - AlarmNotification
          - ScheduledActions

  TargetGroup:
    Type: AWS::ElasticLoadBalancingV2::TargetGroup
    Properties:
      Name: !Sub '${Environment}-web-tg'
      Port: 8080
      Protocol: HTTP
      VpcId: !Ref VpcId
      TargetType: instance
      HealthCheckPath: /health
      HealthCheckIntervalSeconds: 30
      HealthyThresholdCount: 2
      UnhealthyThresholdCount: 3

Outputs:
  SecurityGroupId:
    Description: Web security group ID
    Value: !Ref SecurityGroup
    Export:
      Name: !Sub '${Environment}-WebSecurityGroup'

  AutoScalingGroupName:
    Description: ASG name
    Value: !Ref AutoScalingGroup
    Export:
      Name: !Sub '${Environment}-WebASG'
```

## Common Resource Patterns

### Lambda Function

```yaml
Resources:
  LambdaFunction:
    Type: AWS::Lambda::Function
    Properties:
      FunctionName: !Sub '${AWS::StackName}-function'
      Runtime: python3.12
      Handler: index.handler
      Role: !GetAtt LambdaRole.Arn
      Code:
        ZipFile: |
          def handler(event, context):
              return {'statusCode': 200, 'body': 'Hello'}
      Environment:
        Variables:
          ENVIRONMENT: !Ref Environment

  LambdaRole:
    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
```

### VPC with Subnets

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

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

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

  InternetGateway:
    Type: AWS::EC2::InternetGateway

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

  PublicRouteTable:
    Type: AWS::EC2::RouteTable
    Properties:
      VpcId: !Ref VPC

  PublicRoute:
    Type: AWS::EC2::Route
    DependsOn: AttachGateway
    Properties:
      RouteTableId: !Ref PublicRouteTable
      DestinationCidrBlock: 0.0.0.0/0
      GatewayId: !Ref InternetGateway

  PublicSubnet1RouteTableAssociation:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      SubnetId: !Ref PublicSubnet1
      RouteTableId: !Ref PublicRouteTable
```

### DynamoDB Table

```yaml
Resources:
  OrdersTable:
    Type: AWS::DynamoDB::Table
    Properties:
      TableName: !Sub '${AWS::StackName}-orders'
      AttributeDefinitions:
        - AttributeName: PK
          AttributeType: S
        - AttributeName: SK
          AttributeType: S
      KeySchema:
        - AttributeName: PK
          KeyType: HASH
        - AttributeName: SK
          KeyType: RANGE
      BillingMode: PAY_PER_REQUEST
```

## Stack Operations

```bash
# Validate a template
aws cloudformation validate-template --template-body file://template.yaml

# Lint with cfn-lint (catches more issues)
cfn-lint template.yaml

# Create a stack
aws cloudformation create-stack \
  --stack-name production-web \
  --template-body file://template.yaml \
  --parameters \
    ParameterKey=Environment,ParameterValue=prod \
    ParameterKey=VpcId,ParameterValue=vpc-abc123 \
    ParameterKey=SubnetIds,ParameterValue="subnet-aaa\\,subnet-bbb" \
  --capabilities CAPABILITY_IAM CAPABILITY_NAMED_IAM \
  --tags Key=Environment,Value=production Key=Team,Value=platform \
  --enable-termination-protection \
  --on-failure ROLLBACK

# Wait for stack creation
aws cloudformation wait stack-create-complete --stack-name production-web

# Describe stack status and outputs
aws cloudformation describe-stacks \
  --stack-name production-web \
  --query "Stacks[0].{Status:StackStatus,Outputs:Outputs}" \
  --output table

# List stack resources
aws cloudformation list-stack-resources --stack-name production-web \
  --query "StackResourceSummaries[].{Logical:LogicalResourceId,Physical:PhysicalResourceId,Type:ResourceType,Status:ResourceStatus}" \
  --output table

# Update a stack
aws cloudformation update-stack \
  --stack-name production-web \
  --template-body file://template.yaml \
  --parameters ParameterKey=Environment,ParameterValue=prod

# Delete a stack
aws cloudformation delete-stack --stack-name dev-web
aws cloudformation wait stack-delete-complete --stack-name dev-web
```

## Change Sets (Safe Updates)

```bash
# Create a change set to preview changes before applying
aws cloudformation create-change-set \
  --stack-name my-stack \
  --change-set-name my-changes \
  --template-body file://template.yaml \
  --parameters ParameterKey=Environment,ParameterValue=prod

# Describe changes
aws cloudformation describe-change-set \
  --stack-name my-stack \
  --change-set-name my-changes

# Execute change set
aws cloudformation execute-change-set \
  --stack-name my-stack \
  --change-set-name my-changes
```

## Stack Sets (Multi-Account/Region)

```bash
# Create a stack set
aws cloudformation create-stack-set \
  --stack-set-name my-stack-set \
  --template-body file://template.yaml \
  --parameters ParameterKey=Environment,ParameterValue=prod \
  --capabilities CAPABILITY_IAM

# Create instances in specific accounts/regions
aws cloudformation create-stack-instances \
  --stack-set-name my-stack-set \
  --regions us-east-1 us-west-2 \
  --accounts 111111111111 222222222222

# List stack instances
aws cloudformation list-stack-instances --stack-set-name my-stack-set
```

## Drift Detection

```bash
# Detect drift in a stack
aws cloudformation detect-stack-drift --stack-name production-web

# Wait for drift detection to complete
aws cloudformation wait stack-drift-detection-complete --stack-name production-web

# Describe drift details
aws cloudformation describe-stack-resource-drifts --stack-name production-web
```

## Troubleshooting

- **Template validation errors**: Run `cfn-lint` for detailed syntax and best-practice checks
- **Stack creation failures**: Check `describe-stack-events` for the specific resource that failed
- **Permission denied**: Ensure IAM role has `cloudformation:*` plus permissions for all resources in the template
- **Drift detected**: Use `detect-stack-drift` and `describe-stack-resource-drifts` to identify and remediate changes
- **Change set conflicts**: Delete the existing change set before creating a new one with the same name

## Best Practices

- Use `cfn-lint` in CI/CD pipelines to catch template issues early
- Enable termination protection on production stacks
- Use change sets for all production updates
- Tag all resources with Environment, Team, and CostCenter
- Use nested stacks to keep templates under 51,200 bytes
- Export/import values between stacks instead of hardcoding IDs
- Use Stack Sets for multi-account and multi-region deployments