> ## Documentation Index
> Fetch the complete documentation index at: https://notes.kodekloud.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Demo Immutable Infrastructure With CloudFormation

> Demonstrates deploying immutable AWS infrastructure using golden AMIs, Launch Templates, Auto Scaling groups and CloudFormation update and creation policies to replace instances instead of mutating them.

In this lesson we demonstrate immutable infrastructure patterns on AWS using a golden AMI (built via EC2 Image Builder), Launch Templates, Auto Scaling groups (ASGs), and AWS CloudFormation update/creation policies. The goal: deploy new instances from a new AMI rather than mutating running instances in place.

Immutable deployments are valuable because they reduce configuration drift and make rollbacks and auditing simpler. In this demo the AMI contains the application and configuration; user data does only minimal startup tasks (for example, starting a simple web server and exposing instance metadata).

Quick clarification

* Immutable infrastructure: replace instances with new instances built from a new AMI.
* Mutable infrastructure: apply updates directly to running instances.

Here is an example Launch Template (note the AMI, instance type, security group and other instance-level settings):

![A screenshot of the AWS EC2 console showing the Launch Template details page for "devops-020-380-lt" (lt-06a29c7cda6f00cce). It displays the template/version info and instance details like AMI, instance type (t3.micro), security group ID, and owner/creation info.](https://mintcdn.com/kodekloud-c4ac6d9a/deef86LW8w1XLb8X/images/AWS-Certified-DevOps-Engineer-Professional/Domain-1-SDLC-Automation/Demo-Immutable-Infrastructure-With-CloudFormation/aws-ec2-launch-template-t3-micro.jpg?fit=max\&auto=format\&n=deef86LW8w1XLb8X\&q=85\&s=102af95a436543cb9b79bf8cfa8c84d3)

Launch templates often include networking, block device mapping, monitoring, metadata options and (optionally) user data. In an immutable model, bake the application into the AMI and keep user data minimal. Example user data that starts a simple web server and creates a status page showing instance metadata:

```bash theme={null}
#!/bin/bash
# For demo purposes, install and start a simple web server
yum install -y httpd
systemctl enable httpd
systemctl start httpd

# Create a simple page showing instance metadata
INSTANCE_ID=$(curl -s http://169.254.169.254/latest/meta-data/instance-id)
AZ=$(curl -s http://169.254.169.254/latest/meta-data/placement/availability-zone)
VERSION="v1.0.0"

cat > /var/www/html/index.html << EOF
<html>
<head><title>Immutable Infrastructure Demo</title></head>
<body>
<h1>Immutable Infrastructure Demo</h1>
<p><strong>Instance ID:</strong> $INSTANCE_ID</p>
<p><strong>Availability Zone:</strong> $AZ</p>
<p><strong>Application Version:</strong> $VERSION</p>
<p><strong>Launch Template:</strong> devops-020-380-lt</p>
<hr>
<p>This instance was launched from a versioned launch template.</p>
<p>Updates deploy new AMI via instance refresh - no in-place changes.</p>
</body>
</html>
EOF
```

CloudFormation template — structure and key parameters
If you use AWS CloudFormation to provision this stack, declare parameters and resources such as AMI ID, instance type, Launch Template, Auto Scaling group, target group and listener. Below is a condensed, corrected structure used in the demo.

Top of the template — metadata and parameters:

```yaml theme={null}
AWSTemplateFormatVersion: '2010-09-09'
Description: >
  Immutable Infrastructure Demo - Auto Scaling Group with Rolling Update
  and Launch Template versioning for Golden AMI deployments

Parameters:
  AMIId:
    Type: AWS::EC2::Image::Id
    Description: Golden AMI ID for application servers

  Environment:
    Type: String
    AllowedValues:
      - dev
      - test
      - prod
    Default: dev

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

  VPCId:
    Type: AWS::EC2::VPC::Id
    Description: VPC for deployment

  PrivateSubnets:
    Type: List<AWS::EC2::Subnet::Id>
    Description: Private subnets for Auto Scaling Group

  PublicSubnets:
    Type: List<AWS::EC2::Subnet::Id>
    Description: Public subnets for Load Balancer

  MinCapacity:
    Type: Number
  MaxCapacity:
    Type: Number
  DesiredCapacity:
    Type: Number
```

Creating the stack in CloudFormation will show the stack creation progress and events. Example: a stack in CREATE\_IN\_PROGRESS:

![A screenshot of the AWS CloudFormation console showing a list of stacks on the left and details for the selected stack on the right. The selected stack is "devops-020-380-cfn-stack" with status "CREATE\_IN\_PROGRESS."](https://mintcdn.com/kodekloud-c4ac6d9a/Kx09awxrzT0QVPGP/images/AWS-Certified-DevOps-Engineer-Professional/Domain-1-SDLC-Automation/Demo-Immutable-Infrastructure-With-CloudFormation/aws-cloudformation-devops-020-380-creating.jpg?fit=max\&auto=format\&n=Kx09awxrzT0QVPGP\&q=85\&s=e04553c4dc42d29508032c0ce014de90)

[AWS CloudFormation](https://learn.kodekloud.com/user/courses/aws-cloud-formation) property names must be exact. For example, use `DefaultActions` (plural) in an ALB Listener, not `DefaultAction`. A small typo will cause stack creation to fail.

Key resource snippets
Below are the core resource snippets from the CloudFormation template used in the demo. These show how the Launch Template is parameterized with the golden AMI, and how the ASG is configured for rolling updates and creation-time signals.

Launch Template (note UpdateReplacePolicy and DeletionPolicy):

```yaml theme={null}
LaunchTemplate:
  Type: AWS::EC2::LaunchTemplate
  UpdateReplacePolicy: Retain
  DeletionPolicy: Delete
  Properties:
    LaunchTemplateName: !Sub '${AWS::StackName}-lt'
    VersionDescription: !Sub 'AMI: ${AMIId}'
    LaunchTemplateData:
      ImageId: !Ref AMIId
      InstanceType: !Ref InstanceType
      IamInstanceProfile:
        Arn: !GetAtt InstanceProfile.Arn
      SecurityGroupIds:
        - !Ref ApplicationSecurityGroup
      Monitoring:
        Enabled: true
      MetadataOptions:
        HttpTokens: required
        HttpPutResponseHopLimit: 1
        HttpEndpoint: enabled
      TagSpecifications:
        - ResourceType: instance
          Tags:
            - Key: Name
              Value: !Sub '${AWS::StackName}-instance'
            - Key: Environment
              Value: !Ref Environment
            - Key: AMI
              Value: !Ref AMIId
```

Auto Scaling group with rolling update and creation policies:

```yaml theme={null}
AutoScalingGroup:
  Type: AWS::AutoScaling::AutoScalingGroup
  Properties:
    AutoScalingGroupName: !Sub '${AWS::StackName}-asg'
    LaunchTemplate:
      LaunchTemplateId: !Ref LaunchTemplate
      Version: !GetAtt LaunchTemplate.LatestVersionNumber
    MinSize: !Ref MinCapacity
    MaxSize: !Ref MaxCapacity
    DesiredCapacity: !Ref DesiredCapacity
    HealthCheckType: ELB
    HealthCheckGracePeriod: 300
    TargetGroupARNs:
      - !Ref ApplicationTargetGroup
    VPCZoneIdentifier: !Ref PrivateSubnets
    TerminationPolicies:
      - OldestLaunchTemplate
      - OldestInstance
    Tags:
      - Key: Name
        Value: !Sub '${AWS::StackName}-asg'
        PropagateAtLaunch: false
      - Key: Environment
        Value: !Ref Environment
        PropagateAtLaunch: true

  UpdatePolicy:
    AutoScalingRollingUpdate:
      MinInstancesInService: !Ref MinCapacity
      MaxBatchSize: 2
      PauseTime: PT5M
      WaitOnResourceSignals: true
      SuspendProcesses:
        - HealthCheck
        - ReplaceUnhealthy
        - AZRebalance
        - AlarmNotification
        - ScheduledActions

  CreationPolicy:
    ResourceSignal:
      Count: !Ref DesiredCapacity
      Timeout: PT15M
```

Application Target Group and Listener (health check expects HTTP 200 on root path):

```yaml theme={null}
ApplicationTargetGroup:
  Type: AWS::ElasticLoadBalancingV2::TargetGroup
  Properties:
    Name: !Sub '${AWS::StackName}-tg'
    Port: 80
    Protocol: HTTP
    VpcId: !Ref VPCId
    TargetType: instance
    HealthCheckEnabled: true
    HealthCheckPath: /
    HealthCheckProtocol: HTTP
    HealthCheckIntervalSeconds: 30
    HealthCheckTimeoutSeconds: 5
    HealthyThresholdCount: 2
    UnhealthyThresholdCount: 3
    Matcher:
      HttpCode: '200'
    Tags:
      - Key: Name
        Value: !Sub '${AWS::StackName}-tg'

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

cfn-signal and user-data integration
When instances boot, user data can wait for the application to respond and then use cfn-signal to notify CloudFormation that the instance is ready. When embedding this script into CloudFormation, supply the user data with `Fn::Base64` and `Fn::Sub` so CloudFormation substitutes `${AWS::StackName}` and `${AWS::Region}` before the instance runs the script. Also ensure the `--resource` value matches the logical ID of the AutoScalingGroup in your template.

When embedding this script into a CloudFormation template, provide the user data with `Fn::Base64` and `Fn::Sub` (or `!Sub`) so CloudFormation substitutes `${AWS::StackName}` and `${AWS::Region}`. Ensure the `--resource` flag matches your AutoScalingGroup logical ID (for example, `AutoScalingGroup`).

Example user-data snippet that waits for HTTP 200 and then calls cfn-signal:

```bash theme={null}
# Wait for httpd to respond on port 80
MAX_RETRIES=30
RETRY_COUNT=0

while [ $RETRY_COUNT -lt $MAX_RETRIES ]; do
    if [ "$(curl -s -o /dev/null -w '%{http_code}' http://localhost:80/)" = "200" ]; then
        echo "Application is healthy"
        break
    fi
    echo "Waiting for application... (attempt $RETRY_COUNT/$MAX_RETRIES)"
    sleep 10
    RETRY_COUNT=$((RETRY_COUNT + 1))
done

if [ $RETRY_COUNT -eq $MAX_RETRIES ]; then
    echo "Application health check failed"
    /opt/aws/bin/cfn-signal -e 1 \
        --stack "${AWS::StackName}" \
        --resource AutoScalingGroup \
        --region "${AWS::Region}"
    exit 1
fi

/opt/aws/bin/cfn-signal -e 0 \
    --stack "${AWS::StackName}" \
    --resource AutoScalingGroup \
    --region "${AWS::Region}"
```

Inspecting the resulting Auto Scaling groups
After the stack is created you can inspect ASGs in the EC2 console. This demo shows two ASGs with desired capacity 2:

![A screenshot of the AWS EC2 console on the Auto Scaling groups page showing two auto scaling groups (devops-020-380-cfn-stack-asg and devops-020-380-asg) each with desired capacity 2. The left sidebar shows EC2 navigation sections like Elastic Block Store, Network & Security, and Load Balancing.](https://mintcdn.com/kodekloud-c4ac6d9a/deef86LW8w1XLb8X/images/AWS-Certified-DevOps-Engineer-Professional/Domain-1-SDLC-Automation/Demo-Immutable-Infrastructure-With-CloudFormation/aws-ec2-autoscaling-groups-capacity2.jpg?fit=max\&auto=format\&n=deef86LW8w1XLb8X\&q=85\&s=98209f6687cedea4aa68f025502e8812)

Zoom in on the CloudFormation-managed ASG to see the in-service instances (desired capacity 2 in this example):

![Screenshot of the AWS EC2 Auto Scaling Groups console for the "devops-020-380-cfn-stack-asg" group. It shows desired capacity 2 (scaling limits 2–4) and two InService t3.micro instances listed as Healthy.](https://mintcdn.com/kodekloud-c4ac6d9a/Kx09awxrzT0QVPGP/images/AWS-Certified-DevOps-Engineer-Professional/Domain-1-SDLC-Automation/Demo-Immutable-Infrastructure-With-CloudFormation/aws-ec2-asg-devops-020-380.jpg?fit=max\&auto=format\&n=Kx09awxrzT0QVPGP\&q=85\&s=0e82b88c5386efefca72690e9f6aa851)

Replacing instances with a new AMI
You have two common options to replace instances with a new AMI:

* Update CloudFormation with a new Launch Template version and let the `UpdatePolicy` (AutoScalingRollingUpdate) perform rolling replacements.
* Trigger an Auto Scaling group Instance Refresh, which replaces instances to match the current launch template/configuration.

This demo used an instance refresh. The ASG console displays refresh progress:

![Screenshot of the AWS EC2 Auto Scaling "Instance refresh" tab showing a green banner that an instance refresh started successfully. The Active instance refresh panel displays details like the refresh ID, minimum healthy percentage (100%), rolling refresh strategy, and a 300-second instance warmup.](https://mintcdn.com/kodekloud-c4ac6d9a/deef86LW8w1XLb8X/images/AWS-Certified-DevOps-Engineer-Professional/Domain-1-SDLC-Automation/Demo-Immutable-Infrastructure-With-CloudFormation/aws-ec2-instance-refresh-success-300s.jpg?fit=max\&auto=format\&n=deef86LW8w1XLb8X\&q=85\&s=a4fd6ac3bbf5a9f350748385bfc99f4a)

Practical notes and exam tips

* Immutable deployments replace instances (no in-place changes). Mutable deployments modify running instances.
* Use Launch Templates — Launch Configurations are legacy and lack features like versioning.
* In CloudFormation, combine `UpdatePolicy` (AutoScalingRollingUpdate) with `CreationPolicy`/`ResourceSignal` to control update behavior and ensure instances are healthy before proceeding.
* For blue/green patterns you can maintain two ASGs and switch traffic via the ALB; CodeDeploy is the recommended AWS service for exam-style blue/green deployments.
* Use CloudFormation change sets and drift detection to preview and validate updates before applying them.

Quick resource reference table

| Resource Type | Purpose | Example Logical ID |
| - | - | - |
| Launch Template | Defines AMI, instance type, metadata options and tags | `LaunchTemplate` |
| Auto Scaling Group | Manages groups of instances and scaling/refresh behavior | `AutoScalingGroup` |
| Target Group (ALB) | Health checks and routing of HTTP traffic to instances | `ApplicationTargetGroup` |
| Listener (ALB) | Accepts client traffic and forwards to the target group | `HTTPListener` |
| IAM Instance Profile | Provides instance permissions (e.g., SSM, CloudFormation helper scripts) | `InstanceProfile` |

Links and references

* [AWS CloudFormation Documentation](https://docs.aws.amazon.com/cloudformation/index.html)
* [Amazon EC2 Launch Templates](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/ec2-launch-templates.html)
* [EC2 Image Builder](https://docs.aws.amazon.com/image-builder/latest/userguide/what-is-image-builder.html)
* [Auto Scaling Instance Refresh](https://docs.aws.amazon.com/autoscaling/ec2/userguide/asg-instance-refresh.html)
* [Application Load Balancer (ALB) listener and target groups](https://docs.aws.amazon.com/elasticloadbalancing/latest/application/introduction.html)
* [AWS CodeDeploy blue/green deployments](https://docs.aws.amazon.com/codedeploy/latest/userguide/deployment-steps.html)

Summary
Build a golden AMI, reference it in a Launch Template, attach that Launch Template to an Auto Scaling group, and control replacement behavior using CloudFormation UpdatePolicy (rolling updates) or ASG instance refreshes. This approach yields predictable, auditable, and testable immutable infrastructure.

> **[Watch Video](https://learn.kodekloud.com/user/courses/aws-certified-devops-engineer-professional/module/199ecdf9-a58f-4c2d-b9c9-eac36de111cd/lesson/9beb0226-28fa-44ed-85e2-1a808f738e98)**

> **[Practice Lab](https://learn.kodekloud.com/user/courses/aws-certified-devops-engineer-professional/module/199ecdf9-a58f-4c2d-b9c9-eac36de111cd/lesson/97934eb2-0a2b-4408-9b9d-7aec983e2880)**


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.