返回 Skill 列表
extension
分类: 开发与工程无需 API Key

aws-cloudformation-ec2

提供用于EC2实例、安全组、IAM角色和负载均衡器的AWS CloudFormation模式。在创建EC2实例、SPOT实例、安全组、适用于EC2的IAM角色、应用负载均衡器(ALB)、目标组以及实现带有参数、输出、映射、条件和跨堆栈引用的模板结构时使用。

person作者: jakexiaohubgithub

AWS CloudFormation EC2 Infrastructure

Overview

Create production-ready EC2 infrastructure using AWS CloudFormation templates. This skill covers EC2 instances (On-Demand and SPOT), Security Groups, IAM roles and instance profiles, Application Load Balancers (ALB), Target Groups, template structure best practices, parameter patterns, and cross-stack references for modular, reusable infrastructure as code.

When to Use

Use this skill when:

  • Creating new EC2 instances (On-Demand or SPOT)
  • Configuring Security Groups for network access control
  • Creating IAM roles and instance profiles for EC2
  • Setting up Application Load Balancers (ALB) with target groups
  • Implementing template Parameters with AWS-specific types
  • Creating Outputs for cross-stack references
  • Organizing templates with Mappings and Conditions
  • Designing reusable, modular CloudFormation templates

Instructions

Follow these steps to create EC2 infrastructure with CloudFormation:

  1. Define Instance Parameters: Specify instance type, AMI ID, and key pair
  2. Configure Security Groups: Create rules for inbound/outbound traffic
  3. Set Up IAM Roles: Define instance profile for AWS access
  4. Add Storage: Configure EBS volumes and mount points
  5. Implement User Data: Add bootstrap scripts for initialization
  6. Configure Monitoring: Enable detailed monitoring if needed
  7. Add ALB Integration: Set up load balancer and target groups
  8. Use Spot Instances: Configure for cost optimization

For complete examples, see the EXAMPLES.md file.

Examples

The following examples demonstrate common EC2 patterns:

Example 1: EC2 Instance with Security Group

EC2Instance:
  Type: AWS::EC2::Instance
  Properties:
    InstanceType: t3.micro
    ImageId: !Ref AmiId
    KeyName: !Ref KeyName
    SecurityGroupIds:
      - !Ref InstanceSecurityGroup
    IamInstanceProfile: !Ref InstanceProfile
    UserData:
      Fn::Base64: |
        #!/bin/bash
        yum update -y
        yum install -y httpd
        systemctl start httpd

Example 2: Security Group Configuration

InstanceSecurityGroup:
  Type: AWS::EC2::SecurityGroup
  Properties:
    GroupName: !Sub "${AWS::StackName}-sg"
    GroupDescription: Security group for EC2 instance
    VpcId: !Ref VpcId
    SecurityGroupIngress:
      - IpProtocol: tcp
        FromPort: 80
        ToPort: 80
        CidrIp: 0.0.0.0/0
      - IpProtocol: tcp
        FromPort: 22
        ToPort: 22
        CidrIp: 10.0.0.0/16

Example 3: IAM Role for EC2

IAMRole:
  Type: AWS::IAM::Role
  Properties:
    RoleName: !Sub "${AWS::StackName}-role"
    AssumeRolePolicyDocument:
      Version: "2012-10-17"
      Statement:
        - Effect: Allow
          Principal:
            Service: ec2.amazonaws.com
          Action: sts:AssumeRole
    ManagedPolicyArns:
      - arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore

InstanceProfile:
  Type: AWS::IAM::InstanceProfile
  Properties:
    InstanceProfileName: !Sub "${AWS::StackName}-profile"
    Roles:
      - !Ref IAMRole

For complete production-ready examples, see EXAMPLES.md.

Quick Start

Basic EC2 Instance with Secure Configuration

AWSTemplateFormatVersion: 2010-09-09
Description: Simple EC2 instance with secure SSH access

Parameters:
  LatestAmiId:
    Type: AWS::SSM::Parameter::Value<AWS::EC2::Image::Id>
    Default: /aws/service/ami-amazon-linux-latest/amzn2-ami-hvm-x86_64-gp2

  InstanceType:
    Type: String
    Default: t3.micro
    AllowedValues:
      - t3.micro
      - t3.small
      - t3.medium

  KeyName:
    Type: AWS::EC2::KeyPair::KeyName
    Description: SSH key pair name

  SshLocation:
    Type: String
    Description: CIDR block for SSH access
    Default: 10.0.0.0/16
    AllowedPattern: ^([0-9]{1,3}\.){3}[0-9]{1,3}/[0-9]{1,2}$

Resources:
  InstanceSecurityGroup:
    Type: AWS::EC2::SecurityGroup
    Properties:
      GroupDescription: Security group for EC2 instance
      VpcId: !Ref VpcId
      SecurityGroupIngress:
        - IpProtocol: tcp
          FromPort: 22
          ToPort: 22
          CidrIp: !Ref SshLocation
        - IpProtocol: tcp
          FromPort: 80
          ToPort: 80
          CidrIp: 0.0.0.0/0

  Ec2Instance:
    Type: AWS::EC2::Instance
    Properties:
      ImageId: !Ref LatestAmiId
      InstanceType: !Ref InstanceType
      KeyName: !Ref KeyName
      SecurityGroupIds:
        - !Ref InstanceSecurityGroup
      SubnetId: !Ref SubnetId
      Tags:
        - Key: Name
          Value: !Sub ${AWS::StackName}-instance

Outputs:
  InstanceId:
    Description: EC2 Instance ID
    Value: !Ref Ec2Instance

  PublicIp:
    Description: Public IP address
    Value: !GetAtt Ec2Instance.PublicIp

EC2 Instance with IAM Role

AWSTemplateFormatVersion: 2010-09-09
Description: EC2 instance with IAM role for S3 access

Resources:
  # IAM Role for EC2 with least privilege permissions
  Ec2Role:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument:
        Version: "2012-10-17"
        Statement:
          - Effect: Allow
            Principal:
              Service: ec2.amazonaws.com
            Action: sts:AssumeRole
      ManagedPolicyArns: []
      Policies:
        - PolicyName: S3WriteAccess
          PolicyDocument:
            Version: "2012-10-17"
            Statement:
              - Effect: Allow
                Action:
                  - s3:PutObject
                  - s3:GetObject
                Resource: !Sub "arn:aws:s3:::${S3BucketName}/*"

  # Instance Profile
  Ec2InstanceProfile:
    Type: AWS::IAM::InstanceProfile
    Properties:
      Roles:
        - !Ref Ec2Role
      InstanceProfileName: !Sub ${AWS::StackName}-profile

  # EC2 Instance
  Ec2Instance:
    Type: AWS::EC2::Instance
    Properties:
      ImageId: !Ref LatestAmiId
      InstanceType: !Ref InstanceType
      IamInstanceProfile: !Ref Ec2InstanceProfile
      # ... other properties

Template Structure

Template Sections Overview

AWS CloudFormation templates are JSON or YAML files with specific sections. Each section serves a purpose in defining your infrastructure.

AWSTemplateFormatVersion: 2010-09-09  # Required - template version
Description: Optional description string  # Optional description

# Section order matters for readability but CloudFormation accepts any order
Mappings: {}       # Static configuration tables
Metadata: {}       # Additional information about resources
Parameters: {}     # Input values for customization
Rules: {}          # Parameter validation rules
Conditions: {}     # Conditional resource creation
Transform: {}      # Macro processing (e.g., AWS::Serverless)
Resources: {}      # AWS resources to create (REQUIRED)
Outputs: {}        # Return values after stack creation

Format Version

The AWSTemplateFormatVersion identifies the template version. Current version is 2010-09-09.

AWSTemplateFormatVersion: 2010-09-09
Description: My CloudFormation Template

Description

Add a description to document the template's purpose. Must appear after the format version.

AWSTemplateFormatVersion: 2010-09-09
Description: >
  This template creates an EC2 instance with a security group
  and IAM role for running web applications. It includes:
  - EC2 instance configuration
  - Security group with HTTP/HTTPS access
  - IAM role with S3 access permissions

Metadata

Use Metadata for additional information about resources or parameters.

Metadata:
  AWS::CloudFormation::Interface:
    ParameterGroups:
      - Label:
          default: EC2 Configuration
        Parameters:
          - InstanceType
          - KeyName
      - Label:
          default: Network Configuration
        Parameters:
          - VpcId
          - SubnetId
    ParameterLabels:
      InstanceType:
        default: EC2 Instance Type
      KeyName:
        default: SSH Key Pair

Resources Section

The Resources section is the only required section. It defines AWS resources to provision.

Resources:
  MyInstance:
    Type: AWS::EC2::Instance
    Properties:
      ImageId: ami-0ff8a95407f89df2f
      InstanceType: t3.micro
      KeyName: my-key-pair
      Tags:
        - Key: Name
          Value: !Sub ${AWS::StackName}-instance

Parameters

Parameter Types

Use AWS-specific parameter types for validation and easier selection in the console.

Parameters:
  VpcId:
    Type: AWS::EC2::VPC::Id
    Description: Select an existing VPC

  SubnetId:
    Type: AWS::EC2::Subnet::Id
    Description: Select a subnet

  SecurityGroupIds:
    Type: List<AWS::EC2::SecurityGroup::Id>
    Description: Select existing security groups

  InstanceType:
    Type: AWS::EC2::InstanceType
    Description: EC2 instance type
    Default: t3.micro
    AllowedValues:
      - t3.micro
      - t3.small
      - t3.medium
      - t3.large

  AmiId:
    Type: AWS::EC2::Image::Id
    Description: Select an AMI

  KeyName:
    Type: AWS::EC2::KeyPair::KeyName
    Description: Select an existing key pair

  AlbArn:
    Type: AWS::ElasticLoadBalancingV2::LoadBalancer::Arn
    Description: Select an ALB

SSM Parameter Types

Reference Systems Manager parameters for dynamic values.

Parameters:
  LatestAmiId:
    Type: AWS::SSM::Parameter::Value<AWS::EC2::Image::Id>
    Description: Latest AMI ID from SSM
    Default: /aws/service/ami-amazon-linux-latest/amzn2-ami-hvm-x86_64-gp2

  LatestAmiIdARM:
    Type: AWS::SSM::Parameter::Value<AWS::EC2::Image::Id>
    Description: Latest ARM AMI ID from SSM
    Default: /aws/service/ami-amazon-linux-latest/amzn2-ami-hvm-arm64-gp2

Parameter Constraints

Add constraints to validate parameter values.

Parameters:
  VpcCidr:
    Type: String
    Description: CIDR block for the VPC
    Default: 10.0.0.0/16
    AllowedPattern: ^([0-9]{1,3}\.){3}[0-9]{1,3}/[0-9]{1,2}$
    ConstraintDescription: Must be a valid CIDR block (x.x.x.x/x)

  InstanceCount:
    Type: Number
    Description: Number of instances to launch
    Default: 1
    MinValue: 1
    MaxValue: 10

  Environment:
    Type: String
    Description: Deployment environment
    Default: development
    AllowedValues:
      - development
      - staging
      - production
    ConstraintDescription: Must be development, staging, or production

  VolumeSize:
    Type: Number
    Description: EBS volume size in GB
    Default: 20
    MinValue: 8
    MaxValue: 1000

Mappings

Use Mappings for static configuration data based on regions or other factors.

Mappings:
  RegionMap:
    us-east-1:
      HVM64: ami-0ff8a95407f89df2f
      HVMG2: ami-0a0c776d80e2a1f3c
    us-west-2:
      HVM64: ami-0a0c776d80e2a1f3c
      HVMG2: ami-0a0c776d80e2a1f3c
    eu-west-1:
      HVM64: ami-0ff8a95407f89df2f
      HVMG2: ami-0a0c776d80e2a1f3c

  EnvironmentConfig:
    development:
      InstanceType: t3.micro
      MinInstances: 1
      MaxInstances: 2
      EnableAutoScaling: false
    staging:
      InstanceType: t3.small
      MinInstances: 1
      MaxInstances: 3
      EnableAutoScaling: false
    production:
      InstanceType: t3.medium
      MinInstances: 2
      MaxInstances: 10
      EnableAutoScaling: true

Resources:
  Instance:
    Type: AWS::EC2::Instance
    Properties:
      ImageId: !FindInMap [RegionMap, !Ref AWS::Region, HVM64]
      InstanceType: !FindInMap [EnvironmentConfig, !Ref Environment, InstanceType]

Conditions

Use Conditions to conditionally create resources based on parameters.

Parameters:
  DeployAlb:
    Type: String
    Default: true
    AllowedValues:
      - true
      - false

  Environment:
    Type: String
    Default: development
    AllowedValues:
      - development
      - staging
      - production

  UseSpotInstance:
    Type: String
    Default: false
    AllowedValues:
      - true
      - false

Conditions:
  ShouldDeployAlb: !Equals [!Ref DeployAlb, true]
  IsProduction: !Equals [!Ref Environment, production]
  ShouldUseSpot: !Equals [!Ref UseSpotInstance, true]
  IsNotDevelopment: !Not [!Equals [!Ref Environment, development]]

Resources:
  ApplicationLoadBalancer:
    Type: AWS::ElasticLoadBalancingV2::LoadBalancer
    Condition: ShouldDeployAlb
    Properties:
      Scheme: internet-facing
      SecurityGroups:
        - !Ref AlbSecurityGroup
      Subnets: !Ref PublicSubnetIds

  ProductionScalingPolicy:
    Type: AWS::AutoScaling::ScalingPolicy
    Condition: IsProduction
    Properties:
      AutoScalingGroupName: !Ref AutoScalingGroup
      PolicyType: TargetTrackingScaling
      TargetTrackingConfiguration:
        PredefinedMetricSpecification:
          PredefinedMetricType: ASGAverageCPUUtilization
        TargetValue: 70.0

Transform

Use Transform for macros like AWS::Serverless for SAM templates.

AWSTemplateFormatVersion: 2010-09-09
Transform: AWS::Serverless-2016-10-31
Description: SAM template for serverless application with EC2

Resources:
  MyFunction:
    Type: AWS::Serverless::Function
    Properties:
      Handler: index.handler
      Runtime: nodejs18.x
      CodeUri: function/

Outputs and Cross-Stack References

Basic Outputs

Outputs:
  InstanceId:
    Description: EC2 Instance ID
    Value: !Ref Ec2Instance

  PublicIp:
    Description: Public IP address
    Value: !GetAtt Ec2Instance.PublicIp

  PrivateIp:
    Description: Private IP address
    Value: !GetAtt Ec2Instance.PrivateIp

  AvailabilityZone:
    Description: Availability Zone
    Value: !GetAtt Ec2Instance.AvailabilityZone

Exporting Values for Cross-Stack References

Export values so other stacks can import them.

Outputs:
  InstanceId:
    Description: EC2 Instance ID for other stacks
    Value: !Ref Ec2Instance
    Export:
      Name: !Sub ${AWS::StackName}-InstanceId

  SecurityGroupId:
    Description: Security Group ID for other stacks
    Value: !Ref InstanceSecurityGroup
    Export:
      Name: !Sub ${AWS::StackName}-SecurityGroupId

  InstanceRoleArn:
    Description: IAM Role ARN for other stacks
    Value: !GetAtt Ec2Role.Arn
    Export:
      Name: !Sub ${AWS::StackName}-InstanceRoleArn

  TargetGroupArn:
    Description: Target Group ARN for other stacks
    Value: !Ref ApplicationTargetGroup
    Export:
      Name: !Sub ${AWS::StackName}-TargetGroupArn

Importing Values in Another Stack

Parameters:
  VpcId:
    Type: AWS::EC2::VPC::Id
    Description: VPC ID from network stack

  SecurityGroupId:
    Type: AWS::EC2::SecurityGroup::Id
    Description: Security Group ID from security stack

  InstanceRoleArn:
    Type: String
    Description: IAM Role ARN from security stack

  # Or use Fn::ImportValue for programmatic access
Resources:
  SecurityGroup:
    Type: AWS::EC2::SecurityGroup
    Properties:
      VpcId: !ImportValue
        Fn::Sub: ${NetworkStackName}-VpcId
      GroupDescription: Security group for application

  Instance:
    Type: AWS::EC2::Instance
    Properties:
      IamInstanceProfile: !ImportValue
        Fn::Sub: ${SecurityStackName}-InstanceRoleArn

Cross-Stack Reference Pattern

Create a dedicated security stack that exports values:

# security-stack.yaml
AWSTemplateFormatVersion: 2010-09-09
Description: Security resources stack

Resources:
  InstanceSecurityGroup:
    Type: AWS::EC2::SecurityGroup
    Properties:
      GroupDescription: Security group for EC2 instances
      VpcId: !Ref VpcId
      SecurityGroupIngress:
        - IpProtocol: tcp
          FromPort: 80
          ToPort: 80
          CidrIp: 0.0.0.0/0

  Ec2Role:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument:
        Version: "2012-10-17"
        Statement:
          - Effect: Allow
            Principal:
              Service: ec2.amazonaws.com
            Action: sts:AssumeRole

Outputs:
  SecurityGroupId:
    Value: !Ref InstanceSecurityGroup
    Export:
      Name: !Sub ${AWS::StackName}-SecurityGroupId

  InstanceRoleArn:
    Value: !GetAtt Ec2Role.Arn
    Export:
      Name: !Sub ${AWS::StackName}-InstanceRoleArn

Application stack imports these values:

# application-stack.yaml
AWSTemplateFormatVersion: 2010-09-09
Description: Application stack that imports from security stack

Parameters:
  SecurityStackName:
    Type: String
    Description: Name of the security stack
    Default: security-stack

Resources:
  Ec2Instance:
    Type: AWS::EC2::Instance
    Properties:
      ImageId: !Ref AmiId
      InstanceType: !Ref InstanceType
      SecurityGroupIds:
        - !ImportValue
          Fn::Sub: ${SecurityStackName}-SecurityGroupId
      IamInstanceProfile: !ImportValue
        Fn::Sub: ${SecurityStackName}-InstanceRoleArn

EC2 Instances

Basic Instance Configuration

Resources:
  Ec2Instance:
    Type: AWS::EC2::Instance
    Properties:
      ImageId: !Ref AmiId
      InstanceType: !Ref InstanceType
      KeyName: !Ref KeyName
      SubnetId: !Ref SubnetId
      SecurityGroupIds:
        - !Ref SecurityGroup
      UserData:
        Fn::Base64: |
          #!/bin/bash
          yum update -y
          yum install -y httpd
          systemctl start httpd
      Tags:
        - Key: Name
          Value: !Sub ${AWS::StackName}-instance
        - Key: Environment
          Value: !Ref EnvironmentName

Instance with Multiple Volumes

Resources:
  Ec2Instance:
    Type: AWS::EC2::Instance
    Properties:
      ImageId: !Ref AmiId
      InstanceType: !Ref InstanceType
      BlockDeviceMappings:
        - DeviceName: /dev/xvda
          Ebs:
            VolumeSize: 20
            DeleteOnTermination: true
            VolumeType: gp3
        - DeviceName: /dev/xvdh
          Ebs:
            VolumeSize: 50
            DeleteOnTermination: false
            VolumeType: gp3
        - DeviceName: /dev/xvdi
          Ebs:
            VolumeSize: 100
            DeleteOnTermination: false
            VolumeType: st1

Instance with Detailed Monitoring

Resources:
  Ec2Instance:
    Type: AWS::EC2::Instance
    Properties:
      ImageId: !Ref AmiId
      InstanceType: !Ref InstanceType
      Monitoring: true
      Metrics:
        CollectionInterval: 60

Instance with Placement

Resources:
  Ec2Instance:
    Type: AWS::EC2::Instance
    Properties:
      ImageId: !Ref AmiId
      InstanceType: !Ref InstanceType
      Placement:
        AvailabilityZone: !Select [0, !GetAZs '']
        GroupName: !Ref PlacementGroup
        Tenancy: default

SPOT Instances

SPOT Fleet

AWSTemplateFormatVersion: 2010-09-09
Description: SPOT Fleet for cost-optimized instances

Parameters:
  MaxPrice:
    Type: Number
    Default: 0.05
    Description: Maximum price per instance hour

Resources:
  SpotFleet:
    Type: AWS::EC2::SpotFleet
    Properties:
      SpotFleetRequestConfigData:
        TargetCapacity: 10
        IamFleetRole: !GetAtt SpotFleetRole.Arn
        LaunchSpecifications:
          - InstanceType: t3.micro
            ImageId: !Ref AmiId
            SubnetId: !Ref SubnetId
            WeightedCapacity: 1
          - InstanceType: t3.small
            ImageId: !Ref AmiId
            SubnetId: !Ref SubnetId
            WeightedCapacity: 2
        AllocationStrategy: lowestPrice
        SpotPrice: !Sub ${MaxPrice}

  SpotFleetRole:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument:
        Version: "2012-10-17"
        Statement:
          - Effect: Allow
            Principal:
              Service: spotfleet.amazonaws.com
            Action: sts:AssumeRole
      Policies:
        - PolicyName: SpotFleetPolicy
          PolicyDocument:
            Version: "2012-10-17"
            Statement:
              - Effect: Allow
                Action:
                  - ec2:DescribeInstances
                  - ec2:DescribeImages
                Resource: "*"

SPOT Instance Request

Resources:
  SpotRequest:
    Type: AWS::EC2::SpotFleet
    Properties:
      SpotFleetRequestConfigData:
        TargetCapacity: 1
        IamFleetRole: !GetAtt SpotFleetRole.Arn
        LaunchSpecifications:
          - InstanceType: t3.medium
            ImageId: !Ref AmiId
            SubnetId: !Ref SubnetId
            KeyName: !Ref KeyName
        Type: persistent

SPOT Instance with Fallback

Resources:
  OnDemandInstance:
    Type: AWS::EC2::Instance
    Condition: IsNotSpot
    Properties:
      ImageId: !Ref AmiId
      InstanceType: !Ref InstanceType

  SpotInstance:
    Type: AWS::EC2::SpotFleet
    Condition: UseSpot
    Properties:
      SpotFleetRequestConfigData:
        TargetCapacity: 1
        IamFleetRole: !GetAtt SpotFleetRole.Arn
        LaunchSpecifications:
          - InstanceType: t3.medium
            ImageId: !Ref AmiId
            SubnetId: !Ref SubnetId

Security Groups

Basic Security Group

Resources:
  WebSecurityGroup:
    Type: AWS::EC2::SecurityGroup
    Properties:
      GroupDescription: Security group for web servers
      VpcId: !Ref VpcId
      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
        - IpProtocol: tcp
          FromPort: 22
          ToPort: 22
          CidrIp: 10.0.0.0/16
      SecurityGroupEgress:
        - IpProtocol: tcp
          FromPort: 443
          ToPort: 443
          CidrIp: 0.0.0.0/0
      Tags:
        - Key: Name
          Value: !Sub ${AWS::StackName}-web-sg

Security Group with Self Reference

Resources:
  AppSecurityGroup:
    Type: AWS::EC2::SecurityGroup
    Properties:
      GroupDescription: Security group for application
      VpcId: !Ref VpcId
      SecurityGroupIngress:
        - IpProtocol: tcp
          FromPort: 8080
          ToPort: 8080
          SourceSecurityGroupId: !Ref LoadBalancerSecurityGroup
      Tags:
        - Key: Name
          Value: !Sub ${AWS::StackName}-app-sg

  DatabaseSecurityGroup:
    Type: AWS::EC2::SecurityGroup
    Properties:
      GroupDescription: Security group for database
      VpcId: !Ref VpcId
      SecurityGroupIngress:
        - IpProtocol: tcp
          FromPort: 5432
          ToPort: 5432
          SourceSecurityGroupId: !Ref AppSecurityGroup
      Tags:
        - Key: Name
          Value: !Sub ${AWS::StackName}-db-sg

Security Group for ALB

Resources:
  AlbSecurityGroup:
    Type: AWS::EC2::SecurityGroup
    Properties:
      GroupDescription: Security group for Application Load Balancer
      VpcId: !Ref VpcId
      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
      SecurityGroupEgress:
        - IpProtocol: tcp
          FromPort: 80
          ToPort: 80
          SourceSecurityGroupId: !Ref AppSecurityGroup
        - IpProtocol: tcp
          FromPort: 443
          ToPort: 443
          SourceSecurityGroupId: !Ref AppSecurityGroup

IAM Roles

EC2 IAM Role with Least Privilege

Resources:
  Ec2Role:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument:
        Version: "2012-10-17"
        Statement:
          - Effect: Allow
            Principal:
              Service: ec2.amazonaws.com
            Action: sts:AssumeRole
      ManagedPolicyArns: []
      Policies:
        - PolicyName: S3Access
          PolicyDocument:
            Version: "2012-10-17"
            Statement:
              - Effect: Allow
                Action:
                  - s3:GetObject
                  - s3:PutObject
                  - s3:DeleteObject
                Resource: !Sub "arn:aws:s3:::${S3BucketName}/*"
        - PolicyName: CloudWatchLogs
          PolicyDocument:
            Version: "2012-10-17"
            Statement:
              - Effect: Allow
                Action:
                  - logs:CreateLogGroup
                  - logs:CreateLogStream
                  - logs:PutLogEvents
                  - logs:DescribeLogStreams
                Resource: !Sub "arn:aws:logs:${AWS::Region}:${AWS::AccountId}:log-group:/ec2/${EnvironmentName}/*"

  Ec2InstanceProfile:
    Type: AWS::IAM::InstanceProfile
    Properties:
      Roles:
        - !Ref Ec2Role
      InstanceProfileName: !Sub ${AWS::StackName}-profile

Role for Systems Manager

Resources:
  SsmRole:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument:
        Version: "2012-10-17"
        Statement:
          - Effect: Allow
            Principal:
              Service: ec2.amazonaws.com
            Action: sts:AssumeRole
      ManagedPolicyArns:
        - arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore
      Policies:
        - PolicyName: S3ReadAccess
          PolicyDocument:
            Version: "2012-10-17"
            Statement:
              - Effect: Allow
                Action:
                  - s3:GetObject
                Resource: !Sub "arn:aws:s3:::${S3BucketName}/*"

Application Load Balancer (ALB)

Basic ALB

Resources:
  ApplicationLoadBalancer:
    Type: AWS::ElasticLoadBalancingV2::LoadBalancer
    Properties:
      Name: !Sub ${AWS::StackName}-alb
      Scheme: internet-facing
      SecurityGroups:
        - !Ref AlbSecurityGroup
      Subnets:
        - !Ref PublicSubnet1
        - !Ref PublicSubnet2
      Type: application

  ApplicationTargetGroup:
    Type: AWS::ElasticLoadBalancingV2::TargetGroup
    Properties:
      Name: !Sub ${AWS::StackName}-tg
      Port: 80
      Protocol: HTTP
      VpcId: !Ref VpcId
      TargetType: instance
      HealthCheckPath: /health
      HealthCheckIntervalSeconds: 30
      HealthCheckTimeoutSeconds: 5
      HealthyThresholdCount: 2
      UnhealthyThresholdCount: 3
      Tags:
        - Key: Name
          Value: !Sub ${AWS::StackName}-tg

  ApplicationListener:
    Type: AWS::ElasticLoadBalancingV2::Listener
    Properties:
      DefaultActions:
        - Type: forward
          TargetGroupArn: !Ref ApplicationTargetGroup
      LoadBalancerArn: !Ref ApplicationLoadBalancer
      Port: 80
      Protocol: HTTP

ALB with HTTPS

Resources:
  ApplicationLoadBalancer:
    Type: AWS::ElasticLoadBalancingV2::LoadBalancer
    Properties:
      Name: !Sub ${AWS::StackName}-alb
      Scheme: internet-facing
      SecurityGroups:
        - !Ref AlbSecurityGroup
      Subnets:
        - !Ref PublicSubnet1
        - !Ref PublicSubnet2

  ApplicationTargetGroup:
    Type: AWS::ElasticLoadBalancingV2::TargetGroup
    Properties:
      Name: !Sub ${AWS::StackName}-tg
      Port: 443
      Protocol: HTTPS
      VpcId: !Ref VpcId
      TargetType: instance
      HealthCheckPath: /health
      HealthCheckProtocol: HTTPS

  ApplicationListener:
    Type: AWS::ElasticLoadBalancingV2::Listener
    Properties:
      DefaultActions:
        - Type: forward
          TargetGroupArn: !Ref ApplicationTargetGroup
      LoadBalancerArn: !Ref ApplicationLoadBalancer
      Port: 443
      Protocol: HTTPS
      Certificates:
        - CertificateArn: !Ref CertificateArn

  ApplicationListenerHttpRedirect:
    Type: AWS::ElasticLoadBalancingV2::Listener
    Properties:
      DefaultActions:
        - Type: redirect
          RedirectConfig:
            Host: "#{host}"
            Path: "/#{path}"
            Port: "443"
            Protocol: "HTTPS"
            Query: "#{query}"
            StatusCode: HTTP_301
      LoadBalancerArn: !Ref ApplicationLoadBalancer
      Port: 80
      Protocol: HTTP

ALB with Multiple Target Groups

Resources:
  ApplicationLoadBalancer:
    Type: AWS::ElasticLoadBalancingV2::LoadBalancer
    Properties:
      Name: !Sub ${AWS::StackName}-alb
      Scheme: internet-facing
      SecurityGroups:
        - !Ref AlbSecurityGroup
      Subnets:
        - !Ref PublicSubnet1
        - !Ref PublicSubnet2

  ApiTargetGroup:
    Type: AWS::ElasticLoadBalancingV2::TargetGroup
    Properties:
      Name: !Sub ${AWS::StackName}-api-tg
      Port: 8080
      Protocol: HTTP
      VpcId: !Ref VpcId
      HealthCheckPath: /actuator/health

  WebTargetGroup:
    Type: AWS::ElasticLoadBalancingV2::TargetGroup
    Properties:
      Name: !Sub ${AWS::StackName}-web-tg
      Port: 80
      Protocol: HTTP
      VpcId: !Ref VpcId
      HealthCheckPath: /health

  ApiListenerRule:
    Type: AWS::ElasticLoadBalancingV2::ListenerRule
    Properties:
      Actions:
        - Type: forward
          TargetGroupArn: !Ref ApiTargetGroup
      Conditions:
        - Field: path-pattern
          Values:
            - /api/*
            - /v1/*
      ListenerArn: !Ref ApplicationListener
      Priority: 10

  WebListenerRule:
    Type: AWS::ElasticLoadBalancingV2::ListenerRule
    Properties:
      Actions:
        - Type: forward
          TargetGroupArn: !Ref WebTargetGroup
      Conditions:
        - Field: path-pattern
          Values:
            - /*
      ListenerArn: !Ref ApplicationListener
      Priority: 100

ALB with Cross-Zone Load Balancing

Resources:
  ApplicationLoadBalancer:
    Type: AWS::ElasticLoadBalancingV2::LoadBalancer
    Properties:
      Name: !Sub ${AWS::StackName}-alb
      Scheme: internet-facing
      SecurityGroups:
        - !Ref AlbSecurityGroup
      Subnets:
        - !Ref PublicSubnet1
        - !Ref PublicSubnet2
      LoadBalancerAttributes:
        - Key: load_balancing.cross_zone.enabled
          Value: true

Complete Example

Full EC2 Stack with ALB

AWSTemplateFormatVersion: 2010-09-09
Description: Complete EC2 stack with ALB, security groups, and IAM role

Parameters:
  EnvironmentName:
    Type: String
    Default: production

  LatestAmiId:
    Type: AWS::SSM::Parameter::Value<AWS::EC2::Image::Id>
    Default: /aws/service/ami-amazon-linux-latest/amzn2-ami-hvm-x86_64-gp2

  InstanceType:
    Type: String
    Default: t3.micro
    AllowedValues:
      - t3.micro
      - t3.small
      - t3.medium
      - t3.large

  VpcCidr:
    Type: String
    Default: 10.0.0.0/16

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

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

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

  # Public Subnets
  PublicSubnet1:
    Type: AWS::EC2::Subnet
    Properties:
      VpcId: !Ref VPC
      CidrBlock: 10.0.1.0/24
      AvailabilityZone: !Select [0, !GetAZs '']
      MapPublicIpOnLaunch: true
      Tags:
        - Key: Name
          Value: !Sub ${EnvironmentName}-public-1
        - Key: SubnetType
          Value: Public

  PublicSubnet2:
    Type: AWS::EC2::Subnet
    Properties:
      VpcId: !Ref VPC
      CidrBlock: 10.0.2.0/24
      AvailabilityZone: !Select [1, !GetAZs '']
      MapPublicIpOnLaunch: true
      Tags:
        - Key: Name
          Value: !Sub ${EnvironmentName}-public-2
        - Key: SubnetType
          Value: Public

  # Security Group for ALB
  AlbSecurityGroup:
    Type: AWS::EC2::SecurityGroup
    Properties:
      GroupDescription: Security group for ALB
      VpcId: !Ref VPC
      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 ${EnvironmentName}-alb-sg
        - Key: Environment
          Value: !Ref EnvironmentName

  # Security Group for EC2
  InstanceSecurityGroup:
    Type: AWS::EC2::SecurityGroup
    Properties:
      GroupDescription: Security group for EC2 instances
      VpcId: !Ref VPC
      SecurityGroupIngress:
        - IpProtocol: tcp
          FromPort: 80
          ToPort: 80
          SourceSecurityGroupId: !Ref AlbSecurityGroup
        - IpProtocol: tcp
          FromPort: 22
          ToPort: 22
          CidrIp: 10.0.0.0/16
      Tags:
        - Key: Name
          Value: !Sub ${EnvironmentName}-instance-sg
        - Key: Environment
          Value: !Ref EnvironmentName

  # IAM Role with specific permissions
  Ec2Role:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument:
        Version: "2012-10-17"
        Statement:
          - Effect: Allow
            Principal:
              Service: ec2.amazonaws.com
            Action: sts:AssumeRole
      ManagedPolicyArns:
        - arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore
      Policies:
        - PolicyName: S3Access
          PolicyDocument:
            Version: "2012-10-17"
            Statement:
              - Effect: Allow
                Action:
                  - s3:GetObject
                  - s3:PutObject
                Resource: !Sub "arn:aws:s3:::${S3BucketName}/*"
        - PolicyName: CloudWatchLogs
          PolicyDocument:
            Version: "2012-10-17"
            Statement:
              - Effect: Allow
                Action:
                  - logs:CreateLogGroup
                  - logs:CreateLogStream
                  - logs:PutLogEvents
                Resource: !Sub "arn:aws:logs:${AWS::Region}:${AWS::AccountId}:log-group:/ec2/${EnvironmentName}/*"
      Tags:
        - Key: Name
          Value: !Sub ${EnvironmentName}-ec2-role

  Ec2InstanceProfile:
    Type: AWS::IAM::InstanceProfile
    Properties:
      Roles:
        - !Ref Ec2Role

  # Application Load Balancer
  ApplicationLoadBalancer:
    Type: AWS::ElasticLoadBalancingV2::LoadBalancer
    Properties:
      Name: !Sub ${EnvironmentName}-alb
      Scheme: internet-facing
      SecurityGroups:
        - !Ref AlbSecurityGroup
      Subnets:
        - !Ref PublicSubnet1
        - !Ref PublicSubnet2
      Type: application

  ApplicationTargetGroup:
    Type: AWS::ElasticLoadBalancingV2::TargetGroup
    Properties:
      Name: !Sub ${EnvironmentName}-tg
      Port: 80
      Protocol: HTTP
      VpcId: !Ref VPC
      TargetType: instance
      HealthCheckPath: /health
      HealthCheckIntervalSeconds: 30
      HealthCheckTimeoutSeconds: 5
      HealthyThresholdCount: 2
      UnhealthyThresholdCount: 3
      Tags:
        - Key: Name
          Value: !Sub ${EnvironmentName}-tg

  ApplicationListener:
    Type: AWS::ElasticLoadBalancingV2::Listener
    Properties:
      DefaultActions:
        - Type: forward
          TargetGroupArn: !Ref ApplicationTargetGroup
      LoadBalancerArn: !Ref ApplicationLoadBalancer
      Port: 80
      Protocol: HTTP

  # EC2 Instance
  Ec2Instance:
    Type: AWS::EC2::Instance
    Properties:
      ImageId: !Ref LatestAmiId
      InstanceType: !Ref InstanceType
      IamInstanceProfile: !Ref Ec2InstanceProfile
      SecurityGroupIds:
        - !Ref InstanceSecurityGroup
      SubnetId: !Ref PublicSubnet1
      UserData:
        Fn::Base64: |
          #!/bin/bash
          yum update -y
          yum install -y httpd
          systemctl start httpd
          systemctl enable httpd
          echo "<h1>Hello from $(hostname)</h1>" > /var/www/html/index.html
      Tags:
        - Key: Name
          Value: !Sub ${EnvironmentName}-instance
        - Key: Environment
          Value: !Ref EnvironmentName

Outputs:
  InstanceId:
    Description: EC2 Instance ID
    Value: !Ref Ec2Instance

  InstancePublicIp:
    Description: EC2 Instance Public IP
    Value: !GetAtt Ec2Instance.PublicIp

  LoadBalancerDnsName:
    Description: ALB DNS Name
    Value: !GetAtt ApplicationLoadBalancer.DNSName

  TargetGroupArn:
    Description: Target Group ARN
    Value: !Ref ApplicationTargetGroup

  SecurityGroupId:
    Description: Instance Security Group ID
    Value: !Ref InstanceSecurityGroup

  RoleArn:
    Description: IAM Role ARN
    Value: !GetAtt Ec2Role.Arn

Best Practices

Use AWS-Specific Parameter Types

Always use AWS-specific parameter types for validation and easier selection.

Parameters:
  VpcId:
    Type: AWS::EC2::VPC::Id
    Description: Select a VPC

  SubnetIds:
    Type: List<AWS::EC2::Subnet::Id>
    Description: Select subnets

  SecurityGroupIds:
    Type: List<AWS::EC2::SecurityGroup::Id>
    Description: Select security groups

  InstanceType:
    Type: AWS::EC2::InstanceType
    Description: EC2 instance type

  KeyName:
    Type: AWS::EC2::KeyPair::KeyName
    Description: Select a key pair

Organize by Lifecycle

Separate resources that change at different rates into different stacks.

# Network stack - rarely changes
AWSTemplateFormatVersion: 2010-09-09
Description: Network infrastructure (VPC, subnets, routes)
Resources:
  VPC: AWS::EC2::VPC
  Subnets: AWS::EC2::Subnet

# Security stack - changes occasionally
AWSTemplateFormatVersion: 2010-09-09
Description: Security resources (IAM roles, security groups)
Resources:
  SecurityGroups: AWS::EC2::SecurityGroup
  Roles: AWS::IAM::Role

# Application stack - changes frequently
AWSTemplateFormatVersion: 2010-09-09
Description: Application resources (EC2 instances, ALB)
Parameters:
  NetworkStackName:
    Type: String
  SecurityStackName:
    Type: String
Resources:
  Instances: AWS::EC2::Instance
  LoadBalancer: AWS::ElasticLoadBalancingV2::LoadBalancer

Use Meaningful Names

Use AWS::StackName and parameters for consistent naming.

Resources:
  Ec2Instance:
    Type: AWS::EC2::Instance
    Properties:
      Tags:
        - Key: Name
          Value: !Sub ${AWS::StackName}-instance
        - Key: Environment
          Value: !Ref EnvironmentName

Use Pseudo Parameters

Use pseudo parameters for region-agnostic templates.

Resources:
  S3Bucket:
    Type: AWS::S3::Bucket
    Properties:
      BucketName: !Sub ${AWS::StackName}-${AWS::AccountId}-${AWS::Region}

  Ec2Instance:
    Type: AWS::EC2::Instance
    Properties:
      Tags:
        - Key: StackName
          Value: !Ref AWS::StackName
        - Key: Region
          Value: !Ref AWS::Region

Validate Before Deployment

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

# Check for syntax errors
aws cloudformation validate-template \
  --template-body file://template.yaml \
  --query 'Description'

# Use cfn-lint for advanced validation
pip install cfn-lint
cfn-lint template.yaml

Stack Policies

Stack policies protect stack resources from unintended updates that could cause service disruption. Apply policies to prevent accidental modifications or deletions of critical resources.

Resources:
  # EC2 instance - allow updates but prevent deletion
  Ec2Instance:
    Type: AWS::EC2::Instance
    DeletionPolicy: Retain
    UpdateReplacePolicy: Retain

  # Database - prevent all updates
  DatabaseInstance:
    Type: AWS::RDS::DBInstance
    DeletionPolicy: Snapshot
    UpdateReplacePolicy: Retain

Stack Policy JSON Example:

{
  "Statement": [
    {
      "Effect": "Allow",
      "Action": "Update:*",
      "Principal": "*",
      "Resource": "*"
    },
    {
      "Effect": "Deny",
      "Action": ["Update:Delete", "Update:Replace"],
      "Principal": "*",
      "Resource": "LogicalId=Ec2Instance"
    },
    {
      "Effect": "Deny",
      "Action": "Update:*",
      "Principal": "*",
      "Resource": "LogicalId=DatabaseInstance"
    }
  ]
}

Apply Stack Policy:

aws cloudformation set-stack-policy \
  --stack-name my-ec2-stack \
  --stack-policy-body file://stack-policy.json

# Or from a file
aws cloudformation set-stack-policy \
  --stack-name my-ec2-stack \
  --stack-policy-url https://s3.amazonaws.com/bucket/policy.json

Termination Protection

Enable termination protection to prevent accidental deletion of production stacks. This is critical for production environments.

# Enable termination protection when creating a stack
aws cloudformation create-stack \
  --stack-name my-ec2-stack \
  --template-body file://template.yaml \
  --enable-termination-protection

# Enable termination protection on existing stack
aws cloudformation update-termination-protection \
  --stack-name my-ec2-stack \
  --enable-termination-protection

# Disable termination protection (use with caution)
aws cloudformation update-termination-protection \
  --stack-name my-ec2-stack \
  --no-enable-termination-protection

# Check if termination protection is enabled
aws cloudformation describe-stacks \
  --stack-name my-ec2-stack \
  --query 'Stacks[0].EnableTerminationProtection'

Best Practices for Termination Protection:

  • Enable on all production stacks
  • Use AWS Organizations SCP to enforce termination protection
  • Review before deleting development stacks
  • Document the process for emergency termination

Drift Detection

Drift detection identifies differences between the actual infrastructure and the CloudFormation template. Regular drift checks ensure compliance and security.

# Detect drift on a stack
aws cloudformation detect-drift \
  --stack-name my-ec2-stack

# Get drift detection status
aws cloudformation describe-stack-drift-detection-status \
  --stack-drift-detection-id abc123

# Get resources that have drifted
aws cloudformation describe-stack-resource-drifts \
  --stack-name my-ec2-stack

# Get detailed drift information for a specific resource
aws cloudformation describe-stack-resource-drifts \
  --stack-name my-ec2-stack \
  --stack-resource-drifts-limit 10

Detect Drift Programmatically:

#!/bin/bash
# detect-drift.sh - Automated drift detection script

STACK_NAME=$1
if [ -z "$STACK_NAME" ]; then
    echo "Usage: $0 <stack-name>"
    exit 1
fi

echo "Detecting drift for stack: $STACK_NAME"

# Start drift detection
DETECTION_ID=$(aws cloudformation detect-drift \
  --stack-name $STACK_NAME \
  --query 'StackId' \
  --output text)

echo "Drift detection started: $DETECTION_ID"

# Wait for drift detection to complete
STATUS="DETECTION_IN_PROGRESS"
while [ "$STATUS" = "DETECTION_IN_PROGRESS" ]; do
    sleep 5
    STATUS=$(aws cloudformation describe-stack-drift-detection-status \
      --stack-drift-detection-id $DETECTION_ID \
      --query 'DetectionStatus' \
      --output text)
    echo "Status: $STATUS"
done

# Get drift status
DRIFT_STATUS=$(aws cloudformation describe-stack-drift-detection-status \
  --stack-drift-detection-id $DETECTION_ID \
  --query 'DriftStatus' \
  --output text)

echo "Drift Status: $DRIFT_STATUS"

if [ "$DRIFT_STATUS" = "DRIFTED" ]; then
    echo "Resources with drift:"
    aws cloudformation describe-stack-resource-drifts \
      --stack-name $STACK_NAME \
      --query 'StackResourceDrifts[].{LogicalId:LogicalResourceId,Status:ResourceDriftStatus,Type:ResourceType}'
else
    echo "No drift detected - stack is in sync with template"
fi

Common Drift Scenarios:

| Drift Type | Description | Action Required | |------------|-------------|-----------------| | MODIFIED | Resource properties changed | Review and update template or revert changes | | DELETED | Resource deleted outside CFN | Recreate via template or import | | ADDED | Resource created outside CFN | Import to stack or delete manually |

Change Sets

Change sets preview the impact of stack changes before execution. Always review change sets in production environments.

# Create a change set
aws cloudformation create-change-set \
  --stack-name my-ec2-stack \
  --change-set-name my-ec2-changeset \
  --template-body file://updated-template.yaml \
  --capabilities CAPABILITY_IAM \
  --change-set-type UPDATE

# List change sets for a stack
aws cloudformation list-change-sets \
  --stack-name my-ec2-stack

# Describe a change set to see the planned changes
aws cloudformation describe-change-set \
  --stack-name my-ec2-stack \
  --change-set-name my-ec2-changeset

# Execute a change set
aws cloudformation execute-change-set \
  --stack-name my-ec2-stack \
  --change-set-name my-ec2-changeset

# Delete a change set if changes are not needed
aws cloudformation delete-change-set \
  --stack-name my-ec2-stack \
  --change-set-name my-ec2-changeset

Change Set Types:

| Type | Description | Use Case | |------|-------------|----------| | UPDATE | Preview changes to existing stack | Modifying existing resources | | CREATE | Preview new stack creation | Creating new stacks from template | | IMPORT | Preview resources to import | Importing existing resources |

Review Change Sets with Filters:

# Get changes affecting specific resource types
aws cloudformation describe-change-set \
  --stack-name my-ec2-stack \
  --change-set-name my-ec2-changeset \
  --query 'Changes[?ResourceChange.ResourceType==`AWS::EC2::Instance`]'

# Get changes with replacement impact
aws cloudformation describe-change-set \
  --stack-name my-ec2-stack \
  --change-set-name my-ec2-changeset \
  --query 'Changes[?ResourceChange.Replacement!=`None`]'

Automated Change Set Review Script:

#!/bin/bash
# review-changeset.sh - Automated change set review

STACK_NAME=$1
CHANGE_SET_NAME=$2
AUTO_APPROVE=false

while getopts "a" opt; do
  case $opt in
    a) AUTO_APPROVE=true ;;
    *) echo "Usage: $0 [-a] <stack-name> <change-set-name>"
       echo "  -a: Auto-approve if no critical changes"
       exit 1 ;;
  esac
done

echo "Reviewing change set: $CHANGE_SET_NAME"
echo "Stack: $STACK_NAME"
echo ""

# Get change set summary
CHANGES=$(aws cloudformation describe-change-set \
  --stack-name $STACK_NAME \
  --change-set-name $CHANGE_SET_NAME \
  --query 'Changes[*].{Type:Type,Resource:ResourceChange.LogicalResourceId,Action:ResourceChange.Action,Replacement:ResourceChange.Replacement}' \
  --output table)

echo "Planned Changes:"
echo "$CHANGES"
echo ""

# Check for critical changes (replacements or deletions)
CRITICAL_CHANGES=$(aws cloudformation describe-change-set \
  --stack-name $STACK_NAME \
  --change-set-name $CHANGE_SET_NAME \
  --query 'Changes[?ResourceChange.Replacement==`True` || ResourceChange.Action==`Remove`]' \
  --output json)

if [ -n "$CRITICAL_CHANGES" ] && [ "$CRITICAL_CHANGES" != "[]" ]; then
    echo "WARNING: Critical changes detected that require manual review:"
    echo "$CRITICAL_CHANGES"
    echo ""
    echo "Please review manually before executing."
    exit 1
fi

if [ "$AUTO_APPROVE" = true ]; then
    echo "No critical changes - auto-executing change set..."
    aws cloudformation execute-change-set \
      --stack-name $STACK_NAME \
      --change-set-name $CHANGE_SET_NAME
    echo "Change set executed successfully."
else
    echo "Review complete. No critical changes detected."
    echo "To execute: aws cloudformation execute-change-set --stack-name $STACK_NAME --change-set-name $CHANGE_SET_NAME"
fi

Related Resources

  • For advanced examples: See EXAMPLES.md
  • For reference: See REFERENCE.md
  • AWS CloudFormation User Guide: https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/
  • AWS EC2 Documentation: https://docs.aws.amazon.com/ec2/
  • AWS ALB Documentation: https://docs.aws.amazon.com/elasticloadbalancing/latest/application/

Constraints and Warnings

Resource Limits

  • Instance Limits: Maximum number of EC2 instances per region varies by account
  • ENI Limits: Each instance type has maximum ENI limits
  • Security Groups: Maximum 500 security groups per VPC
  • EBS Volumes: Maximum number of EBS volumes per account is 20 by default

Instance Constraints

  • Instance Type Availability: Not all instance types available in all regions/AZs
  • Instance Replacement: Changing instance type requires instance replacement
  • Tenancy Changes: Changing between default and dedicated tenancy requires replacement
  • Host Resource Group: Dedicated Hosts have specific allocation and utilization constraints

Storage Constraints

  • EBS Volume Limits: EBS volume size varies by volume type (gp3 up to 16 TB)
  • IOPS Limits: Provisioned IOPS (io1/io2) has specific IOPS per GB limits
  • Snapshot Costs: EBS snapshots incur storage costs even after source volume deletion
  • Throughput: Volume throughput limits vary by volume type and size

Network Constraints

  • ENI Limits: Each instance type has maximum ENI and IP address per ENI limits
  • Public IP Assignment: Public IP addresses are released on instance stop
  • Elastic IP Limits: Each account has limited number of Elastic IPs
  • Security Group Rules: Maximum 60 inbound and 60 outbound rules per security group

Security Constraints

  • Key Pairs: Key pairs cannot be recovered if lost; instance access lost
  • IAM Roles: Instance profile roles cannot be changed after instance creation
  • Password Authentication: Password authentication for EC2 is disabled by default; use key pairs
  • Metadata Service: IMDSv2 should be enabled; IMDSv1 has security vulnerabilities

Cost Considerations

  • Instance Costs: Instances incur costs while running; stopped instances may still incur EBS costs
  • EBS Costs: EBS volumes incur costs per GB-month even if not attached
  • Data Transfer: Data transfer out to internet incurs significant costs
  • Load Balancer Costs: ALBs/ELBs/NLBs incur hourly and per-GB data processing costs
  • Elastic IP: Unattached Elastic IPs incur small hourly costs

Availability Constraints

  • Spot Instances: Spot instances can be terminated with 2-minute notice
  • Reserved Instances: Reserved instances require commitment and have specific term limits
  • Availability Zones: Not all instance types available in all AZs
  • Capacity Limits: On-Demand capacity limits may prevent launching instances during high demand

Additional Files