

# CloudFormation walkthroughs
<a name="walkthroughs"></a>

This documentation provides a collection of walkthroughs designed to give you hands-on practice with stack deployments.
+ [Refer to resource outputs in another CloudFormation stack](walkthrough-crossstackref.md) – This walkthrough shows you how to reference outputs from one CloudFormation stack within another stack. Instead of including all resources in a single stack, you can create related AWS resources in separate stacks to create more modular and reusable templates.
+ [Deploy applications on Amazon EC2](deploying.applications.md) – Learn how to use CloudFormation to automatically install, configure, and start up your application on Amazon EC2 instances. This way, you can easily duplicate deployments and update existing installations without connecting directly to the instances.
+ [Update a CloudFormation stack](updating.stacks.walkthrough.md) – Walk through a simple progression of updates to a running stack with CloudFormation.
+ [Create a scaled and load-balanced application](walkthrough-autoscaling.md) – Discover how to use CloudFormation to create a scalable and load-balanced application. This walkthrough covers creating an Auto Scaling group, a load balancer, and other related resources to ensure your application can handle varying traffic loads and maintain high availability.
+ [Peer with a VPC in another AWS account](peer-with-vpc-in-another-account.md) – This walkthrough guides you through the process of creating a Virtual Private Cloud (VPC) peering connection between two VPCs in different AWS accounts. VPC peering helps you route traffic between the VPCs and access resources as if they were part of the same network.
+ [Perform ECS blue/green deployments through CodeDeploy using CloudFormation](blue-green.md) – Discover how to use CloudFormation to perform AWS CodeDeploy blue/green deployments on Amazon ECS. Blue/green deployments are a way to update your applications or services with minimal downtime.

# Refer to resource outputs in another CloudFormation stack
<a name="walkthrough-crossstackref"></a>

This walkthrough shows you how to reference outputs from one CloudFormation stack within another stack to create more modular and reusable templates. 

Instead of including all resources in a single stack, you create related AWS resources in separate stacks. Then, you can refer to required resource outputs from other stacks. By restricting cross-stack references to outputs, you control the parts of a stack that are referenced by other stacks.

For example, you might have a network stack with a VPC, a security group, and a subnet for public web applications, and a separate public web application stack. To ensure that the web applications use the security group and subnet from the network stack, you create a cross-stack reference that allows the web application stack to reference resource outputs from the network stack. With a cross-stack reference, owners of the web application stacks don't need to create or maintain networking rules or assets.

To create a cross-stack reference, use the `Export` output field to flag the value of a resource output for export. Then, use the `Fn::ImportValue` intrinsic function to import the value. For more information, see [Get exported outputs from a deployed CloudFormation stack](using-cfn-stack-exports.md).

**Note**  
CloudFormation is a free service. However, you are charged for the AWS resources that you include in your stacks at the current rate for each one. For more information about AWS pricing, see [the detail page for each product](http://aws.amazon.com).

**Topics**
+ [Use a sample template to create a network stack](#walkthrough-crossstackref-create-vpc-stack)
+ [Use a sample template to create a web application stack](#walkthrough-crossstackref-create-ec2-stack)
+ [Verify the stack works as designed](#walkthrough-crossstackref-verify)
+ [Troubleshoot AMI mapping errors](#walkthrough-crossstackref-troubleshooting-ami)
+ [Clean up your resources](#walkthrough-crossstackref-clean-up)

## Use a sample template to create a network stack
<a name="walkthrough-crossstackref-create-vpc-stack"></a>

Before you begin this walkthrough, check that you have IAM permissions to use all of the following services: Amazon VPC, Amazon EC2, and CloudFormation.

The network stack contains the VPC, security group, and subnet that you will use in the web application stack. In addition to these resources, the network stack creates an Internet gateway and routing tables to enable public access.

You must create this stack before you create the web application stack. If you create the web application stack first, it won't have a security group or subnet.

The stack template is available from the following URL: [https://s3.amazonaws.com/cloudformation-examples/user-guide/cross-stack/SampleNetworkCrossStack.template](https://s3.amazonaws.com/cloudformation-examples/user-guide/cross-stack/SampleNetworkCrossStack.template). To see the resources that the stack will create, choose the link, which opens the template. In the `Outputs` section, you can see the networking resources that the sample template exports. The names of the exported resources are prefixed with the stack's name in case you export networking resources from other stacks. When users import networking resources, they can specify from which stack the resources are imported.

**To create the network stack**

1. Sign in to the AWS Management Console and open the CloudFormation console at [https://console.aws.amazon.com/cloudformation](https://console.aws.amazon.com/cloudformation/).

1. On the **Stacks** page, choose **Create stack** at top right, and then choose **With new resources (standard)**.

1. Choose **Choose an existing template**, and in the **Specify template** section, choose **Amazon S3 URL**.

1. For **Amazon S3 URL**, paste the following URL: **https://s3.amazonaws.com/cloudformation-examples/user-guide/cross-stack/SampleNetworkCrossStack.template**.

1. Choose **Next**.

1. For **Stack name**, type **SampleNetworkCrossStack**, and then choose **Next**.
**Note**  
Record the name of this stack. You'll need the stack name when you launch the web application stack.

1. Choose **Next**. For this walkthrough, you don't need to add tags or specify advanced settings.

1. Ensure that the stack name and template URL are correct, and then choose **Create stack**.

   It might take several minutes for CloudFormation to create your stack. Wait until all resources have been successfully created before proceeding to create the web application stack.

1. To monitor progress, view the stack events. For more information, see [Monitor stack progress](monitor-stack-progress.md).

## Use a sample template to create a web application stack
<a name="walkthrough-crossstackref-create-ec2-stack"></a>

The web application stack creates an EC2 instance that uses the security group and subnet from the network stack. 

You must create this stack in the same AWS Region as the network stack.

The stack template is available from the following URL: [https://s3.amazonaws.com/cloudformation-examples/user-guide/cross-stack/SampleWebAppCrossStack.template](https://s3.amazonaws.com/cloudformation-examples/user-guide/cross-stack/SampleWebAppCrossStack.template). To see the resources that the stack will create, choose the link, which will open the template. In the `Resources` section, view the EC2 instance's properties. You can see how the networking resources are imported from another stack by using the `Fn::ImportValue` function.

**To create the web application stack**

1. From the **Stacks** page, choose **Create stack** at top right, and then choose **With new resources (standard)**.

1. Choose **Choose an existing template**, and in the **Specify template** section, choose **Amazon S3 URL**.

1. For **Amazon S3 URL**, paste the following URL: **https://s3.amazonaws.com/cloudformation-examples/user-guide/cross-stack/SampleWebAppCrossStack.template**.

1. Choose **Next**.

1. For **Stack name**, type **SampleWebAppCrossStack**. In the **Parameters** section, use the default value for the **NetworkStackName** parameter, and then choose **Next**.

   The sample template uses the parameter value to specify from which stack to import values.

1. Choose **Next**. For this walkthrough, you don't need to add tags or specify advanced settings.

1. Ensure that the stack name and template URL are correct, and then choose **Create stack**.

   It might take several minutes for CloudFormation to create your stack.

## Verify the stack works as designed
<a name="walkthrough-crossstackref-verify"></a>

After the stack has been created, view its resources and note the instance ID. For more information on viewing stack resources, see [View stack information from the CloudFormation console](cfn-console-view-stack-data-resources.md).

To verify the instance's security group and subnet, view the instance's properties in the [Amazon EC2 console](https://console.aws.amazon.com/ec2/). If the instance uses the security group and subnet from the `SampleNetworkCrossStack` stack, you have successfully created a cross-stack reference.

Use the console to view the stack outputs and the example website URL to verify that the web application is running. For more information, see [View stack information from the CloudFormation console](cfn-console-view-stack-data-resources.md).

## Troubleshoot AMI mapping errors
<a name="walkthrough-crossstackref-troubleshooting-ami"></a>

If you receive the error `Template error: Unable to get mapping for AWSRegionArch2AMI::[region]::HVM64`, the template doesn't include an AMI mapping for your AWS Region. Instead of updating the mapping, we recommend using Systems Manager public parameters to dynamically reference the latest AMIs:

1. Download the `SampleWebAppCrossStack` template to your local machine from: [https://s3.amazonaws.com/cloudformation-examples/user-guide/cross-stack/SampleWebAppCrossStack.template](https://s3.amazonaws.com/cloudformation-examples/user-guide/cross-stack/SampleWebAppCrossStack.template).

1. Delete the entire `AWSRegionArch2AMI` mapping section.

1. Add the following Systems Manager parameter:

   ```
   "LatestAmiId": {
     "Description": "The latest Amazon Linux 2 AMI from the Parameter Store",
       "Type": "AWS::SSM::Parameter::Value<AWS::EC2::Image::Id>",
       "Default": "/aws/service/ami-amazon-linux-latest/amzn2-ami-hvm-x86_64-gp2"
     }
   ```

1. Replace the existing `ImageId` reference:

   ```
   "ImageId": { "Fn::FindInMap": [ "AWSRegionArch2AMI", { "Ref": "AWS::Region" } , "HVM64" ] }, 
   ```

   With:

   ```
   "ImageId": { "Ref": "LatestAmiId" },
   ```

   This parameter automatically resolves to the latest Amazon Linux 2 AMI for the region where you deploy the stack. 

   For other Linux distributions, use the appropriate parameter path. For more information, see [Discovering public parameters in Parameter Store](https://docs.aws.amazon.com/systems-manager/latest/userguide/parameter-store-finding-public-parameters.html) in the *AWS Systems Manager User Guide*.

1. Upload the modified template to an S3 bucket in your account:

   ```
   aws s3 cp SampleWebAppCrossStack.template s3://amzn-s3-demo-bucket/
   ```

1. When creating the stack, specify your S3 template URL instead of the example URL.

## Clean up your resources
<a name="walkthrough-crossstackref-clean-up"></a>

To ensure that you are not charged for unwanted services, delete the stacks.

**To delete the stacks**

1. In the CloudFormation console, choose the `SampleWebAppCrossStack` stack.

1. Choose **Actions**, and then choose **Delete stack**.

1. In the confirmation message, choose **Delete**.

1. After the stack has been deleted, repeat the same steps for the `SampleNetworkCrossStack` stack.
**Note**  
Wait until CloudFormation completely deletes the `SampleWebAppCrossStack` stack. If the EC2 instance is still running in the VPC, CloudFormation won't delete the VPC in the `SampleNetworkCrossStack` stack.

# Deploy applications on Amazon EC2
<a name="deploying.applications"></a>

You can use CloudFormation to automatically install, configure, and start applications on Amazon EC2 instances. Doing so enables you to easily duplicate deployments and update existing installations without connecting directly to the instance, which can save you a lot of time and effort.

CloudFormation includes a set of helper scripts (`cfn-init`, `cfn-signal`, `cfn-get-metadata`, and `cfn-hup`) that are based on `cloud-init`. You call these helper scripts from your CloudFormation templates to install, configure, and update applications on Amazon EC2 instances that are in the same template. For more information, see the [CloudFormation helper scripts reference](https://docs.aws.amazon.com/AWSCloudFormation/latest/TemplateReference/cfn-helper-scripts-reference.html) in the *CloudFormation Template Reference Guide*.

In the [getting started tutorial](gettingstarted.walkthrough.md), you created a simple web server using `UserData` with a basic bash script. While this worked for a simple "Hello World" page, real applications often need more sophisticated configuration, including:
+ Multiple software packages installed in the correct order.
+ Complex configuration files created with specific content.
+ Services started and configured to run automatically.
+ Error handling and validation of the setup process.

CloudFormation's helper scripts provide a more robust and maintainable way to configure EC2 instances compared to basic bash scripts in `UserData`. The `cfn-init` helper script reads configuration data from your template's metadata and applies it systematically to your instance.

In this tutorial, you'll learn how to use the `cfn-init` helper script and monitor the bootstrapping process.

**Note**  
CloudFormation is free, but you'll be charged for the Amazon EC2 resources you create. However, if you're new to AWS, you can take advantage of the [Free Tier](https://aws.amazon.com/free/) to minimize or eliminate costs during this learning process.

**Topics**
+ [Prerequisites](#bootstrapping-tutorial-prerequisites)
+ [Understanding bootstrap concepts](#bootstrapping-tutorial-understand-concepts)
+ [Start with a simple bootstrap example](#bootstrapping-tutorial-simple-example)
+ [Adding files and commands](#bootstrapping-tutorial-add-complexity)
+ [Adding network security](#bootstrapping-tutorial-security-group)
+ [The complete bootstrap template](#bootstrapping-tutorial-complete-template)
+ [Create the stack using the console](#bootstrapping-tutorial-create-stack)
+ [Monitor the bootstrap process](#bootstrapping-tutorial-validate-bootstrap)
+ [Test the bootstrapped web server](#bootstrapping-tutorial-test-web-server)
+ [Troubleshooting bootstrap issues](#bootstrapping-tutorial-troubleshooting)
+ [Clean up resources](#bootstrapping-tutorial-clean-up)
+ [Next steps](#bootstrapping-tutorial-next-steps)

## Prerequisites
<a name="bootstrapping-tutorial-prerequisites"></a>
+ You must have completed the [Creating your first stack](gettingstarted.walkthrough.md) tutorial or have equivalent experience with CloudFormation basics.
+ You must have access to an AWS account with an IAM user or role that has permissions to use Amazon EC2 and CloudFormation, or administrative user access.
+ You must have a Virtual Private Cloud (VPC) that has access to the internet. This tutorial template requires a default VPC, which comes automatically with newer AWS accounts. If you don't have a default VPC, or if it was deleted, see the troubleshooting section in the [Creating your first stack](gettingstarted.walkthrough.md) tutorial for alternative solutions.

## Understanding bootstrap concepts
<a name="bootstrapping-tutorial-understand-concepts"></a>

Let's understand the key concepts that make bootstrapping work before creating the template.

### The `cfn-init` helper script
<a name="bootstrapping-tutorial-cfn-init-overview"></a>

CloudFormation provides Python helper scripts that you can use to install software and start services on an Amazon EC2 instance. The `cfn-init` script reads resource metadata from your template and applies the configuration to your instance.

The process works as follows:

1. You define the configuration in the `Metadata` section of your EC2 resource.

1. You call `cfn-init` from the `UserData` script.

1. `cfn-init` reads the metadata and applies the configuration.

1. Your instance is configured according to your specifications.

### Metadata structure
<a name="bootstrapping-tutorial-metadata-structure"></a>

The configuration is defined in a specific structure within your EC2 instance.

```
Resources:
  EC2Instance:
    Type: AWS::EC2::Instance
    Metadata:                       # Metadata section for the resource
      AWS::CloudFormation::Init:    # Required key that cfn-init looks for
        config:                     # Configuration name (you can have multiple)
          packages:                 # Install packages
          files:                    # Create files
          commands:                 # Run commands
          services:                 # Start/stop services
```

The `cfn-init` script processes these sections in a specific order: packages, groups, users, sources, files, commands, and then services.

## Start with a simple bootstrap example
<a name="bootstrapping-tutorial-simple-example"></a>

Let's start with a minimal bootstrap example that just installs and starts Apache.

```
Resources:
  EC2Instance:
    Type: AWS::EC2::Instance
    Metadata:
      AWS::CloudFormation::Init:
        config:
          packages:                 # Install Apache web server
            yum:
              httpd: []
          services:                 # Start Apache and enable it to start on boot
            systemd:
              httpd:
                enabled: true
                ensureRunning: true
    Properties:
      ImageId: !Ref LatestAmiId
      InstanceType: !Ref InstanceType
      UserData: !Base64             # Script that runs when instance starts
        Fn::Sub: |
          #!/bin/bash
          yum install -y aws-cfn-bootstrap
          /opt/aws/bin/cfn-init -v --stack ${AWS::StackName} --resource EC2Instance --region ${AWS::Region}
```

This simple example demonstrates the core concepts:
+ `packages` section installs the `httpd` package using yum. This works on Amazon Linux and other Linux distributions that use yum.
+ `services` section ensures `httpd` starts and runs automatically.
+ `UserData` installs the latest bootstrap tools and calls `cfn-init`.

## Adding files and commands
<a name="bootstrapping-tutorial-add-complexity"></a>

Now, let's enhance our example by adding a custom web page and a log file in the `/var/log` directory on the EC2 instance.

### Creating files
<a name="bootstrapping-tutorial-files-section"></a>

The `files` section allows you to create files on the instance with specific content. The vertical pipe (`|`) allows you to pass a literal block of text (HTML code) as the content of the file (`/var/www/html/index.html`).

```
files:
  /var/www/html/index.html:
    content: |
      <body>
        <h1>Congratulations, you have successfully launched the AWS CloudFormation sample.</h1>
      </body>
```

### Running commands
<a name="bootstrapping-tutorial-commands-section"></a>

The `commands` section lets you run shell commands during the bootstrap process. This command creates a log file at `/var/log/welcome.txt` on the EC2 instance. To view it, you need an Amazon EC2 key pair to use for SSH access and an IP address range that can be used to SSH to the instance (not covered here).

```
commands:
  createWelcomeLog:
    command: "echo 'cfn-init ran successfully!' > /var/log/welcome.txt"
```

## Adding network security
<a name="bootstrapping-tutorial-security-group"></a>

Since we're setting up a web server, we need to allow web traffic (HTTP) to reach our EC2 instance. To do this, we'll create a security group that allows incoming traffic on port 80 from your IP address. EC2 instances also need to send traffic out to the internet, for example, to install package updates. By default, security groups allow all outgoing traffic. We'll then associate this security group with our EC2 instance using the `SecurityGroupIds` property.

```
WebServerSecurityGroup:
  Type: AWS::EC2::SecurityGroup
  Properties:
    GroupDescription: Allow HTTP access from my IP address
    SecurityGroupIngress:
      - IpProtocol: tcp
        Description: HTTP
        FromPort: 80
        ToPort: 80
        CidrIp: !Ref MyIP
```

## The complete bootstrap template
<a name="bootstrapping-tutorial-complete-template"></a>

Now, let's put all the pieces together. Here's the complete template that combines all the concepts we've discussed.

```
AWSTemplateFormatVersion: 2010-09-09
Description: Bootstrap an EC2 instance with Apache web server using cfn-init

Parameters:
  LatestAmiId:
    Description: The latest Amazon Linux 2 AMI from the Parameter Store
    Type: AWS::SSM::Parameter::Value<AWS::EC2::Image::Id>
    Default: '/aws/service/ami-amazon-linux-latest/amzn2-ami-hvm-x86_64-gp2'

  InstanceType:
    Description: EC2 instance type
    Type: String
    Default: t2.micro
    AllowedValues:
      - t3.micro
      - t2.micro
    ConstraintDescription: must be a valid EC2 instance type.

  MyIP:
    Description: Your IP address in CIDR format (e.g. 203.0.113.1/32)
    Type: String
    MinLength: 9
    MaxLength: 18
    Default: 0.0.0.0/0
    AllowedPattern: '^(\d{1,3}\.){3}\d{1,3}\/\d{1,2}$'
    ConstraintDescription: must be a valid IP CIDR range of the form x.x.x.x/x.

Resources:
  WebServerSecurityGroup:
    Type: AWS::EC2::SecurityGroup
    Properties:
      GroupDescription: Allow HTTP access from my IP address
      SecurityGroupIngress:
        - IpProtocol: tcp
          Description: HTTP
          FromPort: 80
          ToPort: 80
          CidrIp: !Ref MyIP

  WebServer:
    Type: AWS::EC2::Instance
    Metadata:
      AWS::CloudFormation::Init:
        config:
          packages:
            yum:
              httpd: []
          files:
            /var/www/html/index.html:
              content: |
                <body>
                  <h1>Congratulations, you have successfully launched the AWS CloudFormation sample.</h1>
                </body>
          commands:
            createWelcomeLog:
              command: "echo 'cfn-init ran successfully!' > /var/log/welcome.txt"
          services:
            systemd:
              httpd:
                enabled: true
                ensureRunning: true
    Properties:
      ImageId: !Ref LatestAmiId
      InstanceType: !Ref InstanceType
      SecurityGroupIds:
        - !Ref WebServerSecurityGroup
      UserData: !Base64
        Fn::Sub: |
          #!/bin/bash
          yum install -y aws-cfn-bootstrap
          /opt/aws/bin/cfn-init -v --stack ${AWS::StackName} --resource WebServer --region ${AWS::Region}
      Tags:
        - Key: Name
          Value: Bootstrap Tutorial Web Server

Outputs:
  WebsiteURL:
    Value: !Sub 'http://${WebServer.PublicDnsName}'
    Description: EC2 instance public DNS name
```

## Create the stack using the console
<a name="bootstrapping-tutorial-create-stack"></a>

The following procedure involves uploading the sample stack template from a file. Open a text editor on your local machine and add the template. Save the file with the name `samplelinux2stack.template`.

**To launch the stack template**

1. Sign in to the AWS Management Console and open the CloudFormation console at [https://console.aws.amazon.com/cloudformation](https://console.aws.amazon.com/cloudformation/).

1. Choose **Create stack**, **With new resources (standard)**.

1. Under **Specify template**, choose **Upload a template file**, **Choose file** to upload the `samplelinux2stack.template` file. 

1. Choose **Next**.

1. On the **Specify stack details** page, type **BootstrapTutorialStack** as the stack name.

1. Under **Parameters**, do the following.
   + **LatestAmiId**: Leave the default value.
   + **InstanceType**: Choose either **t2.micro** or **t3.micro** for the EC2 instance type.
   + **MyIP**: Enter your public IP address with a `/32` suffix.

1. Choose **Next** twice, then **Submit** to create the stack.

## Monitor the bootstrap process
<a name="bootstrapping-tutorial-validate-bootstrap"></a>

Bootstrap processes take longer than simple EC2 launches because additional software is being installed and configured.

**To monitor bootstrap progress**

1. In the CloudFormation console, select your stack and open the **Events** tab.

1. Watch for the `WebServer CREATE_IN_PROGRESS` event. The bootstrap process begins after the instance launches.

1. The bootstrap process typically takes a few minutes. You'll see `WebServer CREATE_COMPLETE` when it's finished.

If you want to see what's happening during the bootstrap process, you can check the instance logs.

**To view bootstrap logs (optional)**

1. Open the [EC2 console](https://console.aws.amazon.com/ec2/) and find your instance.

1. Select the instance, and then choose **Actions**, **Monitor and troubleshoot**, **Get system log** to see the bootstrap progress.

1. If you don't see the logs immediately, wait and refresh the page. 

## Test the bootstrapped web server
<a name="bootstrapping-tutorial-test-web-server"></a>

When your stack shows `CREATE_COMPLETE`, test your web server.

**To test the web server**

1. In the CloudFormation console, go to the **Outputs** tab for your stack.

1. Click on the **WebsiteURL** value to open your web server in a new tab.

1. You should see your custom web page with the message `Congratulations, you have successfully launched the AWS CloudFormation sample`.

**Note**  
If the page doesn't load immediately, wait a minute and try again. The bootstrap process may still be completing even after the stack shows `CREATE_COMPLETE`.

## Troubleshooting bootstrap issues
<a name="bootstrapping-tutorial-troubleshooting"></a>

If your bootstrap process fails or your web server isn't working, here are common issues and solutions.

### Common issues
<a name="bootstrapping-tutorial-common-issues"></a>
+ **Stack creation fails** – Check the **Events** tab for specific error messages.
+ **Web server not accessible** – Verify your IP address is correct in the `MyIP` parameter. Remember to include `/32` at the end.
+ **Bootstrap process fails** – The instance may launch but `cfn-init` fails. Check the system logs as described in the monitoring section.

## Clean up resources
<a name="bootstrapping-tutorial-clean-up"></a>

To avoid ongoing charges, you can clean up by deleting the stack and its resources. 

**To delete the stack and its resources**

1. Open the [CloudFormation console](https://console.aws.amazon.com/cloudformation/).

1. On the **Stacks** page, select the option next to the name of the stack you created (**BootstrapTutorialStack**) and then choose **Delete**.

1. When prompted for confirmation, choose **Delete**.

1. Monitor the progress of the stack deletion process on the **Event** tab. The status for **BootstrapTutorialStack** changes to `DELETE_IN_PROGRESS`. When CloudFormation completes the deletion of the stack, it removes the stack from the list.

## Next steps
<a name="bootstrapping-tutorial-next-steps"></a>

Congratulations\$1 You've successfully learned how to bootstrap EC2 instances with CloudFormation. You now understand:
+ How to use `cfn-init` helper scripts
+ How to structure metadata for bootstrapping
+ How to install packages, create files, run commands, and manage services
+ How to monitor for bootstrap issues

To continue learning:
+ Learn how to update a running stack and use the `cfn-hup` helper script. For more information, see [Update a CloudFormation stack](updating.stacks.walkthrough.md).
+ Learn how to bootstrap a Windows stack. For more information, see [Bootstrapping Windows-based CloudFormation stacks](cfn-windows-stacks-bootstrapping.md).
+ Explore more complex bootstrap scenarios with multiple configuration sets. For more information, see [cfn-init](https://docs.aws.amazon.com/AWSCloudFormation/latest/TemplateReference/cfn-init.html) and [AWS::CloudFormation::Init](https://docs.aws.amazon.com/AWSCloudFormation/latest/TemplateReference/aws-resource-init.html) in the *CloudFormation Template Reference Guide*.
+ Learn about `cfn-signal` for reporting bootstrap completion status. For more information, see [cfn-signal](https://docs.aws.amazon.com/AWSCloudFormation/latest/TemplateReference/cfn-signal.html) in the *CloudFormation Template Reference Guide*.

# Update a CloudFormation stack
<a name="updating.stacks.walkthrough"></a>

**Note**  
This tutorial builds on concepts from the [Deploy applications on Amazon EC2](deploying.applications.md) tutorial. If you haven't completed that tutorial, we recommend doing so first to understand EC2 bootstrapping with CloudFormation.

This topic demonstrates a simple progression of updates to a running stack. We will walk through the following steps:

1. **Create the initial stack** – Create a stack using a base Amazon Linux 2 AMI, installing the Apache Web Server and a simple PHP application using the CloudFormation helper scripts.

1. **Update the application** – Update one of the files in the application and deploy the software using CloudFormation.

1. **Add a key pair** – Add an Amazon EC2 key pair to the instance, and then update the security group to allow SSH access to the instance.

1. **Update the instance type** – Change the instance type of the underlying Amazon EC2 instance.

1. **Update the AMI** – Change the Amazon Machine Image (AMI) for the Amazon EC2 instance in your stack.

**Note**  
CloudFormation is free, but you'll be charged for the Amazon EC2 resources you create. However, if you're new to AWS, you can take advantage of the [Free Tier](https://aws.amazon.com/free/) to minimize or eliminate costs during this learning process.

**Topics**
+ [Step 1: Create the initial stack](#update-stack-initial-stack)
+ [Step 2: Update the application](#update-stack-update-application)
+ [Step 3: Add SSH access with a key pair](#update-stack-add-key-pair)
+ [Step 4: Update the instance type](#update-stack-update-instance-type)
+ [Step 5: Update the AMI](#update-stack-update-ami)
+ [Availability and impact considerations](#update.walkthrough.impact)
+ [Related resources](#update.walkthrough.related)

## Step 1: Create the initial stack
<a name="update-stack-initial-stack"></a>

We'll begin by creating a stack that we can use throughout the rest of this topic. We have provided a simple template that launches a single instance PHP web application hosted on the Apache Web Server and running on an Amazon Linux 2 AMI.

The Apache Web Server, PHP, and the simple PHP application are all installed by the CloudFormation helper scripts that are installed by default on the Amazon Linux 2 AMI. The following template snippet shows the metadata that describes the packages and files to install, in this case the Apache Web Server and the PHP infrastructure from the Yum repository for the Amazon Linux 2 AMI. The snippet also shows the `Services` section, which ensures that the Apache Web Server is running. 

```
WebServerInstance:
  Type: AWS::EC2::Instance
  Metadata:
    AWS::CloudFormation::Init:
      config:
        packages:
          yum:
            httpd: []
            php: []
        files:
          /var/www/html/index.php:
            content: |
              <?php
              echo '<h1>Hello World!</h1>';
              ?>
            mode: '000644'
            owner: apache
            group: apache
        services:
          systemd:
            httpd:
              enabled: true
              ensureRunning: true
```

The application itself is a "Hello World" example that's entirely defined within the template. For a real-world application, the files may be stored on Amazon S3, GitHub, or another repository and referenced from the template. CloudFormation can download packages (such as RPMs or RubyGems), and reference individual files and expand `.zip` and `.tar` files to create the application artifacts on the Amazon EC2 instance.

The template enables and configures the `cfn-hup` daemon to listen for changes to the configuration defined in the metadata for the Amazon EC2 instance. By using the `cfn-hup` daemon, you can update application software, such as the version of Apache or PHP, or you can update the PHP application file itself from CloudFormation. The following snippet from the same Amazon EC2 resource in the template shows the pieces necessary to configure `cfn-hup` to call `cfn-init` every two minutes to notice and apply updates to the metadata. Otherwise, the `cfn-init` only runs once at start up.

```
files:
  /etc/cfn/cfn-hup.conf:
    content: !Sub |
      [main]
      stack=${AWS::StackId}
      region=${AWS::Region}
      # The interval used to check for changes to the resource metadata in minutes. Default is 15
      interval=2
    mode: '000400'
    owner: root
    group: root
  /etc/cfn/hooks.d/cfn-auto-reloader.conf:
    content: !Sub |
      [cfn-auto-reloader-hook]
      triggers=post.update
      path=Resources.WebServerInstance.Metadata.AWS::CloudFormation::Init
      action=/opt/aws/bin/cfn-init -s ${AWS::StackId} -r WebServerInstance --region ${AWS::Region}
      runas=root
services:
  systemd:
    cfn-hup:
      enabled: true
      ensureRunning: true
      files:
        - /etc/cfn/cfn-hup.conf
        - /etc/cfn/hooks.d/cfn-auto-reloader.conf
```

To complete the stack, in the `Properties` section of the Amazon EC2 instance definition, the `UserData` property contains the `cloud-init` script that calls `cfn-init` to install the packages and files. For more information, see the [CloudFormation helper scripts reference](https://docs.aws.amazon.com/AWSCloudFormation/latest/TemplateReference/cfn-helper-scripts-reference.html) in the *CloudFormation Template Reference Guide*. The template also creates an Amazon EC2 security group.

```
AWSTemplateFormatVersion: 2010-09-09

Parameters:
  LatestAmiId:
    Description: The latest Amazon Linux 2 AMI from the Parameter Store
    Type: AWS::SSM::Parameter::Value<AWS::EC2::Image::Id>
    Default: '/aws/service/ami-amazon-linux-latest/amzn2-ami-hvm-x86_64-gp2'

  InstanceType:
    Description: WebServer EC2 instance type
    Type: String
    Default: t3.micro
    AllowedValues:
      - t3.nano
      - t3.micro
      - t3.small
      - t3.medium
      - t3a.nano
      - t3a.micro
      - t3a.small
      - t3a.medium
      - m5.large
      - m5.xlarge
      - m5.2xlarge
      - m5a.large
      - m5a.xlarge
      - m5a.2xlarge
      - c5.large
      - c5.xlarge
      - c5.2xlarge
      - r5.large
      - r5.xlarge
      - r5.2xlarge
      - r5a.large
      - r5a.xlarge
      - r5a.2xlarge
    ConstraintDescription: must be a valid EC2 instance type.
    
Resources:
  WebServerInstance:
    Type: AWS::EC2::Instance
    Properties:
      ImageId: !Ref LatestAmiId
      InstanceType: !Ref InstanceType
      SecurityGroupIds:
        - !Ref WebServerSecurityGroup
      UserData:
        Fn::Base64: !Sub |
          #!/bin/bash -xe
          # Get the latest CloudFormation package
          yum update -y aws-cfn-bootstrap
          # Run cfn-init
          /opt/aws/bin/cfn-init -v --stack ${AWS::StackName} --resource WebServerInstance --region ${AWS::Region} || error_exit 'Failed to run cfn-init'        
          # Start up the cfn-hup daemon to listen for changes to the EC2 instance metadata
          /opt/aws/bin/cfn-hup || error_exit 'Failed to start cfn-hup'
          # Signal success or failure
          /opt/aws/bin/cfn-signal -e $? --stack ${AWS::StackName} --resource WebServerInstance --region ${AWS::Region}
    Metadata:
      AWS::CloudFormation::Init:
        config:
          packages:
            yum:
              httpd: []
              php: []
          files:
            /var/www/html/index.php:
              content: |
                <?php
                echo "<h1>Hello World!</h1>";
                ?>
              mode: '000644'
              owner: apache
              group: apache
            /etc/cfn/cfn-hup.conf:
              content: !Sub |
                [main]
                stack=${AWS::StackId}
                region=${AWS::Region}
                # The interval used to check for changes to the resource metadata in minutes. Default is 15
                interval=2
              mode: '000400'
              owner: root
              group: root
            /etc/cfn/hooks.d/cfn-auto-reloader.conf:
              content: !Sub |
                [cfn-auto-reloader-hook]
                triggers=post.update
                path=Resources.WebServerInstance.Metadata.AWS::CloudFormation::Init
                action=/opt/aws/bin/cfn-init -s ${AWS::StackId} -r WebServerInstance --region ${AWS::Region}
                runas=root
          services:
            systemd:
              httpd:
                enabled: true
                ensureRunning: true
              cfn-hup:
                enabled: true
                ensureRunning: true
                files:
                  - /etc/cfn/cfn-hup.conf
                  - /etc/cfn/hooks.d/cfn-auto-reloader.conf
    CreationPolicy:
      ResourceSignal:
        Timeout: PT5M

  WebServerSecurityGroup:
    Type: AWS::EC2::SecurityGroup
    Properties:
      GroupDescription: Enable HTTP access via port 80
      SecurityGroupIngress:
        - IpProtocol: tcp
          FromPort: 80
          ToPort: 80
          CidrIp: 0.0.0.0/0

Outputs:
  WebsiteURL:
    Value: !Sub 'http://${WebServerInstance.PublicDnsName}'
    Description: URL of the web application
```

**To launch a stack from this template**

1. Copy the template and save it locally on your system as a text file. Note the location because you'll need to use the file in a subsequent step.

1. Sign in to the AWS Management Console and open the CloudFormation console at [https://console.aws.amazon.com/cloudformation](https://console.aws.amazon.com/cloudformation/).

1. Choose **Create stack, With new resources (standard)**.

1. Choose **Choose an existing template**.

1. Under **Specify template**, choose **Upload a template file** and browse to the file that you created in the first step, and then choose **Next**.

1. On the **Specify stack details** page, enter **UpdateTutorial** as the stack name.

1. Under **Parameters**, keep all parameters the same and choose **Next** twice.

1. On the **Review and create** screen, choose **Submit**.

After the status of your stack is `CREATE_COMPLETE`, the **Outputs** tab will display the URL of your website. If you choose the value of the `WebsiteURL` output, you will see your new PHP application working.

## Step 2: Update the application
<a name="update-stack-update-application"></a>

Now that we have deployed the stack, let's update the application. We'll make a simple change to the text that's printed out by the application. To do so, we'll add an echo command to the index.php file as shown in this template snippet:

```
files:
  /var/www/html/index.php:
    content: |
      <?php
      echo "<h1>Hello World!</h1>";
      echo "<p>This is an updated version of our application.</p>";
      ?>
    mode: '000644'
    owner: apache
    group: apache
```

Use a text editor to manually edit the template file that you saved locally.

Now, update the stack.

**To update the stack with your updated template**

1. In the CloudFormation console, select your **UpdateTutorial** stack.

1. Choose **Update, Make a direct update**.

1. Choose **Replace existing template**.

1. Under **Specify template**, choose **Upload a template file** and upload your modified template file, and then choose **Next**.

1. On the **Specify stack details** page, keep all parameters the same and choose **Next** twice.

1. On the **Review** page, review the changes. Under **Changes**, you should see that CloudFormation will update the `WebServerInstance` resource.

1. Choose **Submit**.

When your stack is in the `UPDATE_COMPLETE` state, you can choose the `WebsiteURL` output value again to verify that the changes to your application have taken effect. The `cfn-hup` daemon runs every 2 minutes, so it may take up to 2 minutes for the application to change once the stack has been updated.

To see the set of resources that were updated, go to the CloudFormation console. On the **Events** tab, look at the stack events. In this particular case, the metadata for the Amazon EC2 instance `WebServerInstance` was updated, which caused CloudFormation to also reevaluate the other resources (`WebServerSecurityGroup`) to ensure that there were no other changes. None of the other stack resources were modified. CloudFormation will update only those resources in the stack that are affected by any changes to the stack. Such changes can be direct, such as property or metadata changes, or they can be due to dependencies or data flows through `Ref`, `GetAtt`, or other intrinsic template functions. For more information, see [Intrinsic function reference](https://docs.aws.amazon.com/AWSCloudFormation/latest/TemplateReference/intrinsic-function-reference.html).

This simple update illustrates the process. However, you can make much more complex changes to the files and packages that are deployed to your Amazon EC2 instances. For example, you might decide that you need to add MySQL to the instance, along with PHP support for MySQL. To do so, simply add the additional packages and files along with any additional services to the configuration and then update the stack to deploy the changes.

```
packages:
  yum:
    httpd: []
    php: []
    mysql: []
    php-mysql: []
    mysql-server: []
    mysql-libs: []

  ...

services:
  systemd:
    httpd:
      enabled: true
      ensureRunning: true
    cfn-hup:
      enabled: true
      ensureRunning: true
      files:
        - /etc/cfn/cfn-hup.conf
        - /etc/cfn/hooks.d/cfn-auto-reloader.conf
    mysqld:
      enabled: true
      ensureRunning: true
```

You can update the CloudFormation metadata to update to new versions of the packages used by the application. In the previous examples, the version property for each package is empty, indicating that `cfn-init` should install the latest version of the package.

```
packages:
  yum:
    httpd: []
    php: []
```

You can optionally specify a version string for a package. If you change the version string in subsequent update stack calls, the new version of the package will be deployed. Here's an example of using version numbers for RubyGems packages. Any package that supports versioning can have specific versions.

```
packages:
  rubygems:
    mysql: []
    rubygems-update:
      - "1.6.2"
    rake:
      - "0.8.7"
    rails:
      - "2.3.11"
```

## Step 3: Add SSH access with a key pair
<a name="update-stack-add-key-pair"></a>

You can also update a resource in the template to add properties that weren't originally specified in the template. To illustrate that, we'll add an Amazon EC2 key pair to an existing EC2 instance and then open up port 22 in the Amazon EC2 security group so that you can use Secure Shell (SSH) to access the instance.

**To add SSH access to an existing Amazon EC2 instance**

1. Add two additional parameters to the template to pass in the name of an existing Amazon EC2 key pair and SSH location.

   ```
   Parameters:
     KeyName:
       Description: Name of an existing EC2 KeyPair to enable SSH access to the instance
       Type: AWS::EC2::KeyPair::KeyName
       ConstraintDescription: must be the name of an existing EC2 KeyPair.
   
     SSHLocation:
       Description: The IP address that can be used to SSH to the EC2 instances in CIDR format (e.g. 203.0.113.1/32)
       Type: String
       MinLength: 9
       MaxLength: 18
       Default: 0.0.0.0/0
       AllowedPattern: '^(\d{1,3}\.){3}\d{1,3}\/\d{1,2}$'
       ConstraintDescription: must be a valid IP CIDR range of the form x.x.x.x/x.
   ```

1. Add the `KeyName` property to the Amazon EC2 instance.

   ```
   WebServerInstance:
     Type: AWS::EC2::Instance
     Properties:
       ImageId: !Ref LatestAmiId
       InstanceType: !Ref InstanceType
       KeyName: !Ref KeyName
       SecurityGroupIds:
         - !Ref WebServerSecurityGroup
   ```

1. Add port 22 and the SSH location to the ingress rules for the Amazon EC2 security group.

   ```
   WebServerSecurityGroup:
     Type: AWS::EC2::SecurityGroup
     Properties:
       GroupDescription: Enable HTTP access via port 80 and SSH access via port 22
       SecurityGroupIngress:
         - IpProtocol: tcp
           FromPort: 80
           ToPort: 80
           CidrIp: 0.0.0.0/0
         - IpProtocol: tcp
           FromPort: 22
           ToPort: 22
           CidrIp: !Ref SSHLocation
   ```

1. Update the stack using the same steps as explained in [Step 2: Update the application](#update-stack-update-application).

## Step 4: Update the instance type
<a name="update-stack-update-instance-type"></a>

Now let's demonstrate how to update the underlying infrastructure by changing the instance type.

The stack we have built so far uses a t3.micro Amazon EC2 instance. Let's suppose that your newly created website is getting more traffic than a t3.micro instance can handle, and now you want to move to an m5.large Amazon EC2 instance type. If the architecture of the instance type changes, the instance must be created with a different AMI. However, both the t3.micro and m5.large use the same CPU architectures and run Amazon Linux 2 (x86\$164) AMIs . For more information, see [Compatibility for changing the instance type](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/resize-limitations.html) in the *Amazon EC2 User Guide*.

Let's use the template that we modified in the previous step to change the instance type. Because `InstanceType` was an input parameter to the template, we don't need to modify the template; we can change the value of the parameter on the **Specify stack details** page.

**To update the stack with a new parameter value**

1. In the CloudFormation console, select your **UpdateTutorial** stack.

1. Choose **Update, Make a direct update**.

1. Choose **Use existing template**, and then choose **Next**.

1. On the **Specify stack details** page, change the value of the **InstanceType** text box from `t3.micro` to `m5.large`. Then, choose **Next** twice.

1. On the **Review** page, review the changes. Under **Changes**, you should see that CloudFormation will update the `WebServerInstance` resource.

1. Choose **Submit**.

You can dynamically change the instance type of an EBS-backed Amazon EC2 instance by starting and stopping the instance. CloudFormation tries to optimize the change by updating the instance type and restarting the instance, so the instance ID doesn't change. When the instance is restarted, however, the public IP address of the instance does change. To ensure that the Elastic IP address is bound correctly after the change, CloudFormation will also update the Elastic IP address. You can see the changes in the CloudFormation console on the **Events** tab.

To check the instance type from the AWS Management Console, open the Amazon EC2 console, and locate your instance there.

## Step 5: Update the AMI
<a name="update-stack-update-ami"></a>

Now let's update our stack to use Amazon Linux 2023, which is the next generation of Amazon Linux. 

Updating the AMI is a major change that requires replacing the instance. We can't simply start and stop the instance to modify the AMI; CloudFormation considers this a change to an immutable property of the resource. In order to make a change to an immutable property, CloudFormation must launch a replacement resource, in this case a new Amazon EC2 instance running the new AMI. 

Let's look at how we might update our stack template to use Amazon Linux 2023. The key changes include updating the AMI parameter and changing from `yum` to `dnf` package manager.

```
AWSTemplateFormatVersion: 2010-09-09

Parameters:
  LatestAmiId:
    Description: The latest Amazon Linux 2023 AMI from the Parameter Store
    Type: AWS::SSM::Parameter::Value<AWS::EC2::Image::Id>
    Default: '/aws/service/ami-amazon-linux-latest/al2023-ami-kernel-6.1-x86_64'

  InstanceType:
    Description: WebServer EC2 instance type
    Type: String
    Default: t3.micro
    AllowedValues:
      - t3.nano
      - t3.micro
      - t3.small
      - t3.medium
      - t3a.nano
      - t3a.micro
      - t3a.small
      - t3a.medium
      - m5.large
      - m5.xlarge
      - m5.2xlarge
      - m5a.large
      - m5a.xlarge
      - m5a.2xlarge
      - c5.large
      - c5.xlarge
      - c5.2xlarge
      - r5.large
      - r5.xlarge
      - r5.2xlarge
      - r5a.large
      - r5a.xlarge
      - r5a.2xlarge
    ConstraintDescription: must be a valid EC2 instance type.

  KeyName:
    Description: Name of an existing EC2 KeyPair to enable SSH access to the instance
    Type: AWS::EC2::KeyPair::KeyName
    ConstraintDescription: must be the name of an existing EC2 KeyPair.

  SSHLocation:
    Description: The IP address that can be used to SSH to the EC2 instances in CIDR format (e.g. 203.0.113.1/32)
    Type: String
    MinLength: 9
    MaxLength: 18
    Default: 0.0.0.0/0
    AllowedPattern: '^(\d{1,3}\.){3}\d{1,3}\/\d{1,2}$'
    ConstraintDescription: must be a valid IP CIDR range of the form x.x.x.x/x.
    
Resources:
  WebServerInstance:
    Type: AWS::EC2::Instance
    Properties:
      ImageId: !Ref LatestAmiId
      InstanceType: !Ref InstanceType
      KeyName: !Ref KeyName
      SecurityGroupIds:
        - !Ref WebServerSecurityGroup
      UserData:
        Fn::Base64: !Sub |
          #!/bin/bash -xe
          # Get the latest CloudFormation package
          dnf update -y aws-cfn-bootstrap
          # Run cfn-init
          /opt/aws/bin/cfn-init -v --stack ${AWS::StackName} --resource WebServerInstance --region ${AWS::Region} || error_exit 'Failed to run cfn-init'        
          # Start up the cfn-hup daemon to listen for changes to the EC2 instance metadata
          /opt/aws/bin/cfn-hup || error_exit 'Failed to start cfn-hup'
          # Signal success or failure
          /opt/aws/bin/cfn-signal -e $? --stack ${AWS::StackName} --resource WebServerInstance --region ${AWS::Region}
    Metadata:
      AWS::CloudFormation::Init:
        config:
          packages:
            dnf:
              httpd: []
              php: []
          files:
            /var/www/html/index.php:
              content: |
                <?php
                echo "<h1>Hello World!</h1>";
                echo "<p>This is an updated version of our application.</p>";
                echo "<p>Running on Amazon Linux 2023!</p>";
                ?>
              mode: '000644'
              owner: apache
              group: apache
            /etc/cfn/cfn-hup.conf:
              content: !Sub |
                [main]
                stack=${AWS::StackId}
                region=${AWS::Region}
                # The interval used to check for changes to the resource metadata in minutes. Default is 15
                interval=2
              mode: '000400'
              owner: root
              group: root
            /etc/cfn/hooks.d/cfn-auto-reloader.conf:
              content: !Sub |
                [cfn-auto-reloader-hook]
                triggers=post.update
                path=Resources.WebServerInstance.Metadata.AWS::CloudFormation::Init
                action=/opt/aws/bin/cfn-init -s ${AWS::StackId} -r WebServerInstance --region ${AWS::Region}
                runas=root
          services:
            systemd:
              httpd:
                enabled: true
                ensureRunning: true
              cfn-hup:
                enabled: true
                ensureRunning: true
                files:
                  - /etc/cfn/cfn-hup.conf
                  - /etc/cfn/hooks.d/cfn-auto-reloader.conf
    CreationPolicy:
      ResourceSignal:
        Timeout: PT5M

  WebServerSecurityGroup:
    Type: AWS::EC2::SecurityGroup
    Properties:
      GroupDescription: Enable HTTP access via port 80 and SSH access via port 22
      SecurityGroupIngress:
        - IpProtocol: tcp
          FromPort: 80
          ToPort: 80
          CidrIp: 0.0.0.0/0
        - IpProtocol: tcp
          FromPort: 22
          ToPort: 22
          CidrIp: !Ref SSHLocation

Outputs:
  WebsiteURL:
    Value: !Sub 'http://${WebServerInstance.PublicDnsName}'
    Description: URL of the web application
```

Update the stack using the same steps as explained in [Step 2: Update the application](#update-stack-update-application).

After the new instance is running, CloudFormation updates the other resources in the stack to point to the new resource. When all new resources are created, the old resource is deleted, a process known as `UPDATE_CLEANUP`. This time, you will notice that the instance ID and application URL of the instance in the stack has changed as a result of the update. The events in the **Event** table contain a description "Requested update has a change to an immutable property and hence creating a new physical resource" to indicate that a resource was replaced.

Alternatively: If you have application code written into the AMI that you want to update, you can use the same stack update mechanism to update the AMI to load your new application.

**To update the AMI with custom application code**

1. Create your new AMI containing your application or operating system changes. For more information, see [Create an Amazon EBS-backed AMI](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/creating-an-ami-ebs.html) in the *Amazon EC2 User Guide*.

1. Update your template to incorporate the new AMI ID.

1. Update the stack using the same steps as explained in [Step 2: Update the application](#update-stack-update-application).

When you update the stack, CloudFormation detects that the AMI ID has changed, and then it triggers a stack update in the same way as we initiated the one above.

## Availability and impact considerations
<a name="update.walkthrough.impact"></a>

Different properties have different impacts on the resources in the stack. You can use CloudFormation to update any property; however, before you make any changes, you should consider these questions:

1. How does the update affect the resource itself? For example, updating an alarm threshold will render the alarm inactive during the update. As we have seen, changing the instance type requires that the instance be stopped and restarted. CloudFormation uses the update or modify actions for the underlying resources to make changes to resources. To understand the impact of updates, you should check the documentation for the specific resources.

1. Is the change mutable or immutable? Some changes to resource properties, such as changing the AMI on an Amazon EC2 instance, aren't supported by the underlying services. In the case of mutable changes, CloudFormation will use the Update or Modify type APIs for the underlying resources. For immutable property changes, CloudFormation will create new resources with the updated properties and then link them to the stack before deleting the old resources. Although CloudFormation tries to reduce the down time of the stack resources, replacing a resource is a multistep process, and it will take time. During stack reconfiguration, your application will not be fully operational. For example, it may not be able to serve requests or access a database.

## Related resources
<a name="update.walkthrough.related"></a>

For more information about using CloudFormation to start applications and on integrating with other configuration and deployment services such as Puppet and Opscode Chef, see the following whitepapers:
+ [Bootstrapping applications via CloudFormation](https://s3.amazonaws.com/cloudformation-examples/BoostrappingApplicationsWithAWSCloudFormation.pdf)
+ [Integrating CloudFormation with Opscode Chef](https://s3.amazonaws.com/cloudformation-examples/IntegratingAWSCloudFormationWithOpscodeChef.pdf)
+ [Integrating CloudFormation with Puppet](https://s3.amazonaws.com/cloudformation-examples/IntegratingAWSCloudFormationWithPuppet.pdf)

# Create a scaled and load-balanced application
<a name="walkthrough-autoscaling"></a>

For this walkthrough, you create a stack that helps you set up a scaled and load-balanced application. The walkthrough provides a sample template that you use to create the stack. The example template provisions an Auto Scaling group, an Application Load Balancer, security groups that control traffic to the load balancer and to the Auto Scaling group, and an Amazon SNS notification configuration to publish notifications about scaling activities. 

This template creates one or more Amazon EC2 instances and an Application Load Balancer. You will be billed for the AWS resources used if you create a stack from this template. 

## Full stack template
<a name="example-templates-autoscaling-full-stack-template"></a>

Let's start with the template.

**YAML**

```
AWSTemplateFormatVersion: 2010-09-09
Parameters:
  InstanceType:
    Description: The EC2 instance type
    Type: String
    Default: t3.micro
    AllowedValues:
      - t3.micro
      - t3.small
      - t3.medium
  KeyName:
    Description: Name of an existing EC2 key pair to allow SSH access to the instances
    Type: AWS::EC2::KeyPair::KeyName
  LatestAmiId:
    Description: The latest Amazon Linux 2 AMI from the Parameter Store
    Type: AWS::SSM::Parameter::Value<AWS::EC2::Image::Id>
    Default: '/aws/service/ami-amazon-linux-latest/amzn2-ami-hvm-x86_64-gp2'
  OperatorEmail:
    Description: The email address to notify when there are any scaling activities
    Type: String
  SSHLocation:
    Description: The IP address range that can be used to SSH to the EC2 instances
    Type: String
    MinLength: 9
    MaxLength: 18
    Default: 0.0.0.0/0
    ConstraintDescription: must be a valid IP CIDR range of the form x.x.x.x/x.
  Subnets:
    Type: 'List<AWS::EC2::Subnet::Id>'
    Description: At least two public subnets in different Availability Zones in the selected VPC
  VPC:
    Type: AWS::EC2::VPC::Id
    Description: A virtual private cloud (VPC) that enables resources in public subnets to connect to the internet
Resources:
  ELBSecurityGroup:
    Type: AWS::EC2::SecurityGroup
    Properties:
      GroupDescription: ELB Security Group
      VpcId: !Ref VPC
      SecurityGroupIngress:
      - IpProtocol: tcp
        FromPort: 80
        ToPort: 80
        CidrIp: 0.0.0.0/0
  EC2SecurityGroup:
    Type: AWS::EC2::SecurityGroup
    Properties:
      GroupDescription: EC2 Security Group
      VpcId: !Ref VPC
      SecurityGroupIngress:
      - IpProtocol: tcp
        FromPort: 80
        ToPort: 80
        SourceSecurityGroupId:
          Fn::GetAtt:
          - ELBSecurityGroup
          - GroupId
      - IpProtocol: tcp
        FromPort: 22
        ToPort: 22
        CidrIp: !Ref SSHLocation
  EC2TargetGroup:
    Type: AWS::ElasticLoadBalancingV2::TargetGroup
    Properties:
      HealthCheckIntervalSeconds: 30
      HealthCheckProtocol: HTTP
      HealthCheckTimeoutSeconds: 15
      HealthyThresholdCount: 5
      Matcher:
        HttpCode: '200'
      Name: EC2TargetGroup
      Port: 80
      Protocol: HTTP
      TargetGroupAttributes:
      - Key: deregistration_delay.timeout_seconds
        Value: '20'
      UnhealthyThresholdCount: 3
      VpcId: !Ref VPC
  ALBListener:
    Type: AWS::ElasticLoadBalancingV2::Listener
    Properties:
      DefaultActions:
        - Type: forward
          TargetGroupArn: !Ref EC2TargetGroup
      LoadBalancerArn: !Ref ApplicationLoadBalancer
      Port: 80
      Protocol: HTTP
  ApplicationLoadBalancer:
    Type: AWS::ElasticLoadBalancingV2::LoadBalancer
    Properties:
      Scheme: internet-facing
      Subnets: !Ref Subnets
      SecurityGroups:
        - !GetAtt ELBSecurityGroup.GroupId
  LaunchTemplate:
    Type: AWS::EC2::LaunchTemplate
    Properties: 
      LaunchTemplateName: !Sub ${AWS::StackName}-launch-template
      LaunchTemplateData:
        ImageId: !Ref LatestAmiId
        InstanceType: !Ref InstanceType
        KeyName: !Ref KeyName
        SecurityGroupIds: 
          - !Ref EC2SecurityGroup
        UserData:
          Fn::Base64: !Sub |
            #!/bin/bash
            yum update -y
            yum install -y httpd
            systemctl start httpd
            systemctl enable httpd
            echo "<h1>Hello World!</h1>" > /var/www/html/index.html
  NotificationTopic:
    Type: AWS::SNS::Topic
    Properties:
      Subscription:
        - Endpoint: !Ref OperatorEmail
          Protocol: email
  WebServerGroup:
    Type: AWS::AutoScaling::AutoScalingGroup
    Properties:
      LaunchTemplate:
        LaunchTemplateId: !Ref LaunchTemplate
        Version: !GetAtt LaunchTemplate.LatestVersionNumber
      MaxSize: '3'
      MinSize: '1'
      NotificationConfigurations:
        - TopicARN: !Ref NotificationTopic
          NotificationTypes: ['autoscaling:EC2_INSTANCE_LAUNCH', 'autoscaling:EC2_INSTANCE_LAUNCH_ERROR', 'autoscaling:EC2_INSTANCE_TERMINATE', 'autoscaling:EC2_INSTANCE_TERMINATE_ERROR']
      TargetGroupARNs:
        - !Ref EC2TargetGroup
      VPCZoneIdentifier: !Ref Subnets
```

**JSON**

```
{
  "AWSTemplateFormatVersion":"2010-09-09",
  "Parameters":{
    "InstanceType":{
      "Description":"The EC2 instance type",
      "Type":"String",
      "Default":"t3.micro",
      "AllowedValues":[
        "t3.micro",
        "t3.small",
        "t3.medium"
      ]
    },
    "KeyName":{
      "Description":"Name of an existing EC2 key pair to allow SSH access to the instances",
      "Type":"AWS::EC2::KeyPair::KeyName"
    },
    "LatestAmiId":{
      "Description":"The latest Amazon Linux 2 AMI from the Parameter Store",
      "Type":"AWS::SSM::Parameter::Value<AWS::EC2::Image::Id>",
      "Default":"/aws/service/ami-amazon-linux-latest/amzn2-ami-hvm-x86_64-gp2"
    },
    "OperatorEmail":{
      "Description":"The email address to notify when there are any scaling activities",
      "Type":"String"
    },
    "SSHLocation":{
      "Description":"The IP address range that can be used to SSH to the EC2 instances",
      "Type":"String",
      "MinLength":9,
      "MaxLength":18,
      "Default":"0.0.0.0/0",
      "ConstraintDescription":"Must be a valid IP CIDR range of the form x.x.x.x/x."
    },
    "Subnets":{
      "Type":"List<AWS::EC2::Subnet::Id>",
      "Description":"At least two public subnets in different Availability Zones in the selected VPC"
    },
    "VPC":{
      "Type":"AWS::EC2::VPC::Id",
      "Description":"A virtual private cloud (VPC) that enables resources in public subnets to connect to the internet"
    }
  },
  "Resources":{
    "ELBSecurityGroup":{
      "Type":"AWS::EC2::SecurityGroup",
      "Properties":{
        "GroupDescription":"ELB Security Group",
        "VpcId":{
          "Ref":"VPC"
        },
        "SecurityGroupIngress":[
          {
            "IpProtocol":"tcp",
            "FromPort":80,
            "ToPort":80,
            "CidrIp":"0.0.0.0/0"
          }
        ]
      }
    },
    "EC2SecurityGroup":{
      "Type":"AWS::EC2::SecurityGroup",
      "Properties":{
        "GroupDescription":"EC2 Security Group",
        "VpcId":{
          "Ref":"VPC"
        },
        "SecurityGroupIngress":[
          {
            "IpProtocol":"tcp",
            "FromPort":80,
            "ToPort":80,
            "SourceSecurityGroupId":{
              "Fn::GetAtt":[
                "ELBSecurityGroup",
                "GroupId"
              ]
            }
          },
          {
            "IpProtocol":"tcp",
            "FromPort":22,
            "ToPort":22,
            "CidrIp":{
              "Ref":"SSHLocation"
            }
          }
        ]
      }
    },
    "EC2TargetGroup":{
      "Type":"AWS::ElasticLoadBalancingV2::TargetGroup",
      "Properties":{
        "HealthCheckIntervalSeconds":30,
        "HealthCheckProtocol":"HTTP",
        "HealthCheckTimeoutSeconds":15,
        "HealthyThresholdCount":5,
        "Matcher":{
          "HttpCode":"200"
        },
        "Name":"EC2TargetGroup",
        "Port":80,
        "Protocol":"HTTP",
        "TargetGroupAttributes":[
          {
            "Key":"deregistration_delay.timeout_seconds",
            "Value":"20"
          }
        ],
        "UnhealthyThresholdCount":3,
        "VpcId":{
          "Ref":"VPC"
        }
      }
    },
    "ALBListener":{
      "Type":"AWS::ElasticLoadBalancingV2::Listener",
      "Properties":{
        "DefaultActions":[
          {
            "Type":"forward",
            "TargetGroupArn":{
              "Ref":"EC2TargetGroup"
            }
          }
        ],
        "LoadBalancerArn":{
          "Ref":"ApplicationLoadBalancer"
        },
        "Port":80,
        "Protocol":"HTTP"
      }
    },
    "ApplicationLoadBalancer":{
      "Type":"AWS::ElasticLoadBalancingV2::LoadBalancer",
      "Properties":{
        "Scheme":"internet-facing",
        "Subnets":{
          "Ref":"Subnets"
        },
        "SecurityGroups":[
          {
            "Fn::GetAtt":[
              "ELBSecurityGroup",
              "GroupId"
            ]
          }
        ]
      }
    },
    "LaunchTemplate":{
      "Type":"AWS::EC2::LaunchTemplate",
      "Properties":{
        "LaunchTemplateName":{
          "Fn::Sub":"${AWS::StackName}-launch-template"
        },
        "LaunchTemplateData":{
          "ImageId":{
            "Ref":"LatestAmiId"
          },
          "InstanceType":{
            "Ref":"InstanceType"
          },
          "KeyName":{
            "Ref":"KeyName"
          },
          "SecurityGroupIds":[
            {
              "Ref":"EC2SecurityGroup"
            }
          ],
          "UserData":{
            "Fn::Base64":{
              "Fn::Join":[
                "",
                [
                  "#!/bin/bash\n",
                  "yum update -y\n",
                  "yum install -y httpd\n",
                  "systemctl start httpd\n",
                  "systemctl enable httpd\n",
                  "echo \"<h1>Hello World!</h1>\" > /var/www/html/index.html"
                ]
              ]
            }
          }
        }
      }
    },
    "NotificationTopic":{
      "Type":"AWS::SNS::Topic",
      "Properties":{
        "Subscription":[
          {
            "Endpoint":{
              "Ref":"OperatorEmail"
            },
            "Protocol":"email"
          }
        ]
      }
    },
    "WebServerGroup":{
      "Type":"AWS::AutoScaling::AutoScalingGroup",
      "Properties":{
        "LaunchTemplate":{
          "LaunchTemplateId":{
            "Ref":"LaunchTemplate"
          },
          "Version":{
            "Fn::GetAtt":[
              "LaunchTemplate",
              "LatestVersionNumber"
            ]
          }
        },
        "MaxSize":"3",
        "MinSize":"1",
        "NotificationConfigurations":[
          {
            "TopicARN":{
              "Ref":"NotificationTopic"
            },
            "NotificationTypes":[
              "autoscaling:EC2_INSTANCE_LAUNCH",
              "autoscaling:EC2_INSTANCE_LAUNCH_ERROR",
              "autoscaling:EC2_INSTANCE_TERMINATE",
              "autoscaling:EC2_INSTANCE_TERMINATE_ERROR"
            ]
          }
        ],
        "TargetGroupARNs":[
          {
            "Ref":"EC2TargetGroup"
          }
        ],
        "VPCZoneIdentifier":{
          "Ref":"Subnets"
        }
      }
    }
  }
}
```

## Template walkthrough
<a name="example-templates-autoscaling-description"></a>

The first part of this template specifies the `Parameters`. Each parameter must be assigned a value at runtime for CloudFormation to successfully provision the stack. Resources specified later in the template reference these values and use the data.
+ `InstanceType`: The type of EC2 instance that Amazon EC2 Auto Scaling provisions. If not specified, a default of `t3.micro` is used.
+ `KeyName`: An existing EC2 key pair to allow SSH access to the instances.
+ `LatestAmiId`: The Amazon Machine Image (AMI) for the instances. If not specified, your instances are launched with an Amazon Linux 2 AMI, using an AWS Systems Manager public parameter maintained by AWS. For more information, see [Finding public parameters](https://docs.aws.amazon.com/systems-manager/latest/userguide/parameter-store-finding-public-parameters.html) in the *AWS Systems Manager User Guide*.
+ `OperatorEmail`: The email address where you want to send scaling activity notifications.
+ `SSHLocation`: The IP address range that can be used to SSH to the instances.
+ `Subnets`: At least two public subnets in different Availability Zones. 
+ `VPC`: A virtual private cloud (VPC) in your account that enables resources in public subnets to connect to the internet. 
**Note**  
You can use the default VPC and default subnets to allow instances to access the internet. If using your own VPC, make sure that it has a subnet mapped to each Availability Zone of the Region you are working in. At minimum, you must have two public subnets available to create the load balancer.

The next part of this template specifies the `Resources`. This section specifies the stack resources and their properties.

[https://docs.aws.amazon.com/AWSCloudFormation/latest/TemplateReference/aws-resource-ec2-securitygroup.html](https://docs.aws.amazon.com/AWSCloudFormation/latest/TemplateReference/aws-resource-ec2-securitygroup.html) resource `ELBSecurityGroup` 
+ `SecurityGroupIngress` contains a TCP ingress rule that allows access from *all IP addresses* ("CidrIp" : "0.0.0.0/0") on port 80.

[https://docs.aws.amazon.com/AWSCloudFormation/latest/TemplateReference/aws-resource-ec2-securitygroup.html](https://docs.aws.amazon.com/AWSCloudFormation/latest/TemplateReference/aws-resource-ec2-securitygroup.html) resource `EC2SecurityGroup` 
+ `SecurityGroupIngress` contains two ingress rules: 1) a TCP ingress rule that allows SSH access (port 22) from the IP address range that you provide for the `SSHLocation` input parameter and 2) a TCP ingress rule that allows access from the load balancer by specifying the load balancer's security group. The [GetAtt](resources-section-structure.md#resource-properties-getatt) function is used to get the ID of the security group with the logical name `ELBSecurityGroup`.

[AWS::ElasticLoadBalancingV2::TargetGroup](https://docs.aws.amazon.com/AWSCloudFormation/latest/TemplateReference/aws-resource-elasticloadbalancingv2-targetgroup.html) resource `EC2TargetGroup`
+ `Port`, `Protocol`, and `HealthCheckProtocol` specify the EC2 instance port (80) and protocol (HTTP) that the `ApplicationLoadBalancer` routes traffic to and that Elastic Load Balancing uses to check the health of the EC2 instances.
+ `HealthCheckIntervalSeconds` specifies that the EC2 instances have an interval of 30 seconds between health checks. The `HealthCheckTimeoutSeconds` is defined as the length of time Elastic Load Balancing waits for a response from the health check target (15 seconds in this example). After the timeout period lapses, Elastic Load Balancing marks that EC2 instance's health check as unhealthy. When an EC2 instance fails three consecutive health checks (`UnhealthyThresholdCount`), Elastic Load Balancing stops routing traffic to that EC2 instance until that instance has five consecutive healthy health checks (`HealthyThresholdCount`). At that point, Elastic Load Balancing considers the instance healthy and begins routing traffic to the instance again.
+ `TargetGroupAttributes` updates the deregistration delay value of the target group to 20 seconds. By default, Elastic Load Balancing waits 300 seconds before completing the deregistration process.

[AWS::ElasticLoadBalancingV2::Listener](https://docs.aws.amazon.com/AWSCloudFormation/latest/TemplateReference/aws-resource-elasticloadbalancingv2-listener.html) resource `ALBListener`
+ `DefaultActions` specifies the port that the load balancer listens to, the target group where the load balancer forwards requests, and the protocol used to route requests.

[AWS::ElasticLoadBalancingV2::LoadBalancer](https://docs.aws.amazon.com/AWSCloudFormation/latest/TemplateReference/aws-resource-elasticloadbalancingv2-loadbalancer.html) resource `ApplicationLoadBalancer`
+ `Subnets` takes the value of the `Subnets` input parameter as the list of public subnets where the load balancer nodes will be created.
+ `SecurityGroup` gets the ID of the security group that acts as a virtual firewall for your load balancer nodes to control incoming traffic. The [GetAtt](resources-section-structure.md#resource-properties-getatt) function is used to get the ID of the security group with the logical name `ELBSecurityGroup`.

[AWS::EC2::LaunchTemplate](https://docs.aws.amazon.com/AWSCloudFormation/latest/TemplateReference/aws-resource-ec2-launchtemplate.html) resource `LaunchTemplate`
+ `ImageId` takes the value of the `LatestAmiId` input parameter as the AMI to use.
+ `KeyName` takes the value of the `KeyName` input parameter as the EC2 key pair to use.
+ `SecurityGroupIds` gets the ID of the security group with the logical name `EC2SecurityGroup` that acts as a virtual firewall for your EC2 instances to control incoming traffic.
+ `UserData` is a configuration script that runs after the instance is up and running. In this example, the script installs Apache and creates an index.html file.

[AWS::SNS::Topic](https://docs.aws.amazon.com/AWSCloudFormation/latest/TemplateReference/aws-resource-sns-topic.html) resource `NotificationTopic`
+ `Subscription` takes the value of the `OperatorEmail` input parameter as the email address for the recipient of the notifications when there are any scaling activities. 

[AWS::AutoScaling::AutoScalingGroup](https://docs.aws.amazon.com/AWSCloudFormation/latest/TemplateReference/aws-resource-autoscaling-autoscalinggroup.html) resource `WebServerGroup`
+ `MinSize` and `MaxSize` set the minimum and maximum number of EC2 instances in the Auto Scaling group.
+ `TargetGroupARNs` takes the ARN of the target group with the logical name `EC2TargetGroup`. As this Auto Scaling group scales, it automatically registers and deregisters instances with this target group.
+ `VPCZoneIdentifier` takes the value of the `Subnets` input parameter as the list of public subnets where the EC2 instances can be created.

## Step 1: Launch the stack
<a name="example-templates-autoscaling-launch-stack"></a>

Before you launch the stack, check that you have AWS Identity and Access Management (IAM) permissions to use all of the following services: Amazon EC2, Amazon EC2 Auto Scaling, AWS Systems Manager, Elastic Load Balancing, Amazon SNS, and CloudFormation. 

The following procedure involves uploading the sample stack template from a file. Open a text editor on your local machine and add one of the templates. Save the file with the name `sampleloadbalancedappstack.template`.

**To launch the stack template**

1. Sign in to the AWS Management Console and open the CloudFormation console at [https://console.aws.amazon.com/cloudformation](https://console.aws.amazon.com/cloudformation/).

1. Choose **Create stack**, **With new resources (standard)**.

1. Under **Specify template**, choose **Upload a template file**, **Choose file** to upload the `sampleloadbalancedappstack.template` file. 

1. Choose **Next**.

1. On the **Specify stack details** page, type the stack name (for example, **SampleLoadBalancedAppStack**).

1. Under **Parameters**, review the parameters for the stack and provide values for all parameters that don't have default values, including **OperatorEmail**, **SSHLocation**, **KeyName**, **VPC**, and **Subnets**.

1. Choose **Next** twice.

1. On the **Review** page, review and confirm the settings.

1. Choose **Submit**.

   You can view the status of the stack in the CloudFormation console in the **Status** column. When CloudFormation has successfully created the stack, you receive a status of **CREATE\$1COMPLETE**.
**Note**  
After you create the stack, you must confirm the subscription before the email address can start to receive notifications. For more information, see [Get Amazon SNS notifications when your Auto Scaling group scales](https://docs.aws.amazon.com/autoscaling/ec2/userguide/ec2-auto-scaling-sns-notifications.html) in the *Amazon EC2 Auto Scaling User Guide*.

## Step 2: Clean up your sample resources
<a name="example-templates-autoscaling-clean-up"></a>

To make sure that you aren't charged for unused sample resources, delete the stack.

**To delete the stack**

1. In the CloudFormation console, select the **SampleLoadBalancedAppStack** stack.

1. Choose **Delete**.

1. In the confirmation message, choose **Delete stack**.

   The status for **SampleLoadBalancedAppStack** changes to **DELETE\$1IN\$1PROGRESS**. When CloudFormation completes the deletion of the stack, it removes the stack from the list.

Use the sample template from this walkthrough to build your own stack templates. For more information, see [Tutorial: Set up a scaled and load-balanced application](https://docs.aws.amazon.com/autoscaling/ec2/userguide/tutorial-ec2-auto-scaling-load-balancer.html) in the *Amazon EC2 Auto Scaling User Guide*.

# Peer with a VPC in another AWS account
<a name="peer-with-vpc-in-another-account"></a>

You can peer with a Virtual Private Cloud (VPC) in another AWS account by using [https://docs.aws.amazon.com/AWSCloudFormation/latest/TemplateReference/aws-resource-ec2-vpcpeeringconnection.html](https://docs.aws.amazon.com/AWSCloudFormation/latest/TemplateReference/aws-resource-ec2-vpcpeeringconnection.html). This creates a networking connection between two VPCs that enables you to route traffic between them so they can communicate as if they were within the same network. A VPC peering connection can help facilitate data access and data transfer.

To establish a VPC peering connection, you need to authorize two separate AWS accounts within a single CloudFormation stack.

For more information about VPC peering and its limitations, see the [Amazon VPC Peering Guide](https://docs.aws.amazon.com/vpc/latest/peering/). 

## Prerequisites
<a name="peer-with-vpc-in-another-account-prerequisites"></a>

1. You need a peer VPC ID, a peer AWS account ID, and a [cross-account access role](https://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles_common-scenarios_aws-accounts.html) for the peering connection. 
**Note**  
This walkthrough refers to two accounts: First is an account that allows cross-account peering (the *accepter account*). Second is an account that requests the peering connection (the *requester account*).

1. To accept the VPC peering connection, the cross-account access role must be assumable by you. The resource behaves the same way as a VPC peering connection resource in the same account. For information about how an IAM administrator grants permissions to assume the cross-account role, see [Grant a user permissions to switch roles](https://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles_use_permissions-to-switch.html) in the *IAM User Guide*.

## Step 1: Create a VPC and a cross-account role
<a name="step-1-create-vpc-and-cross-account-role"></a>

In this step, you'll create the VPC and role in the *accepter account*.

**To create a VPC and a cross-account access role**

1. Sign in to the AWS Management Console and open the CloudFormation console at [https://console.aws.amazon.com/cloudformation](https://console.aws.amazon.com/cloudformation/).

1. From the **Stacks** page, choose **Create stack** at top right, and then choose **With new resources (standard)**.

1. For **Prerequisite - Prepare template**, choose **Choose an existing template** and then **Upload a template file**, **Choose file**.

1. Open a text editor on your local machine and add one of the following templates. Save the file and return to the console to select it as the template file.  
**Example JSON**  

   ```
   {
     "AWSTemplateFormatVersion": "2010-09-09",
     "Description": "Create a VPC and an assumable role for cross account VPC peering.",
     "Parameters": {
       "PeerRequesterAccountId": {
         "Type": "String"
       }
     },
     "Resources": {
       "vpc": {
         "Type": "AWS::EC2::VPC",
         "Properties": {
           "CidrBlock": "10.1.0.0/16",
           "EnableDnsSupport": false,
           "EnableDnsHostnames": false,
           "InstanceTenancy": "default"
         }
       },
       "peerRole": {
         "Type": "AWS::IAM::Role",
         "Properties": {
           "AssumeRolePolicyDocument": {
             "Statement": [
               {
                 "Principal": {
                   "AWS": {
                     "Ref": "PeerRequesterAccountId"
                   }
                 },
                 "Action": [
                   "sts:AssumeRole"
                 ],
                 "Effect": "Allow"
               }
             ]
           },
           "Path": "/",
           "Policies": [
             {
               "PolicyName": "root",
               "PolicyDocument": {
                 "Version": "2012-10-17",		 	 	 
                 "Statement": [
                   {
                     "Effect": "Allow",
                     "Action": "ec2:AcceptVpcPeeringConnection",
                     "Resource": "*"
                   }
                 ]
               }
             }
           ]
         }
       }
     },
     "Outputs": {
       "VPCId": {
         "Value": {
           "Ref": "vpc"
         }
       },
       "RoleARN": {
         "Value": {
           "Fn::GetAtt": [
             "peerRole",
             "Arn"
           ]
         }
       }
     }
   }
   ```  
**Example YAML**  

   ```
   AWSTemplateFormatVersion: 2010-09-09
   Description: Create a VPC and an assumable role for cross account VPC peering.
   Parameters:
     PeerRequesterAccountId:
       Type: String
   Resources:
     vpc:
       Type: AWS::EC2::VPC
       Properties:
         CidrBlock: 10.1.0.0/16
         EnableDnsSupport: false
         EnableDnsHostnames: false
         InstanceTenancy: default
     peerRole:
       Type: AWS::IAM::Role
       Properties:
         AssumeRolePolicyDocument:
           Statement:
             - Principal:
                 AWS: !Ref PeerRequesterAccountId
               Action:
                 - 'sts:AssumeRole'
               Effect: Allow
         Path: /
         Policies:
           - PolicyName: root
             PolicyDocument:
               Version: 2012-10-17 		 	 	 
               Statement:
                 - Effect: Allow
                   Action: 'ec2:AcceptVpcPeeringConnection'
                   Resource: '*'
   Outputs:
     VPCId:
       Value: !Ref vpc
     RoleARN:
       Value: !GetAtt 
         - peerRole
         - Arn
   ```

1. Choose **Next**.

1. Give the stack a name (for example, **VPC-owner**), and then enter the AWS account ID of the *requester account* in the **PeerRequesterAccountId** field.

1. Accept the defaults, and then choose **Next**.

1. Choose **I acknowledge that CloudFormation might create IAM resources**, and then choose **Create stack**.

## Step 2: Create a template that includes `AWS::EC2::VPCPeeringConnection`
<a name="step-2-create-template-for-vpc-peering-connection-owner"></a>

Now that you've created the VPC and cross-account role, you can peer with the VPC using another AWS account (the *requester account*).

**To create a template that includes the [AWS::EC2::VPCPeeringConnection](https://docs.aws.amazon.com/AWSCloudFormation/latest/TemplateReference/aws-resource-ec2-vpcpeeringconnection.html) resource**

1. Go back to the CloudFormation console home page.

1. From the **Stacks** page, choose **Create stack** at top right, and then choose **With new resources (standard)**.

1. For **Prerequisite - Prepare template**, choose **Choose an existing template** and then **Upload a template file**, **Choose file**.

1. Open a text editor on your local machine and add one of the following templates. Save the file and return to the console to select it as the template file.  
**Example JSON**  

   ```
   {
     "AWSTemplateFormatVersion": "2010-09-09",
     "Description": "Create a VPC and a VPC Peering connection using the PeerRole to accept.",
     "Parameters": {
       "PeerVPCAccountId": {
         "Type": "String"
       },
       "PeerVPCId": {
         "Type": "String"
       },
       "PeerRoleArn": {
         "Type": "String"
       }
     },
     "Resources": {
       "vpc": {
         "Type": "AWS::EC2::VPC",
         "Properties": {
           "CidrBlock": "10.2.0.0/16",
           "EnableDnsSupport": false,
           "EnableDnsHostnames": false,
           "InstanceTenancy": "default"
         }
       },
       "vpcPeeringConnection": {
         "Type": "AWS::EC2::VPCPeeringConnection",
         "Properties": {
           "VpcId": {
             "Ref": "vpc"
           },
           "PeerVpcId": {
             "Ref": "PeerVPCId"
           },
           "PeerOwnerId": {
             "Ref": "PeerVPCAccountId"
           },
           "PeerRoleArn": {
             "Ref": "PeerRoleArn"
           }
         }
       }
     },
     "Outputs": {
       "VPCId": {
         "Value": {
           "Ref": "vpc"
         }
       },
       "VPCPeeringConnectionId": {
         "Value": {
           "Ref": "vpcPeeringConnection"
         }
       }
     }
   }
   ```  
**Example YAML**  

   ```
   AWSTemplateFormatVersion: 2010-09-09
   Description: Create a VPC and a VPC Peering connection using the PeerRole to accept.
   Parameters:
     PeerVPCAccountId:
       Type: String
     PeerVPCId:
       Type: String
     PeerRoleArn:
       Type: String
   Resources:
     vpc:
       Type: AWS::EC2::VPC
       Properties:
         CidrBlock: 10.2.0.0/16
         EnableDnsSupport: false
         EnableDnsHostnames: false
         InstanceTenancy: default
     vpcPeeringConnection:
       Type: AWS::EC2::VPCPeeringConnection
       Properties:
         VpcId: !Ref vpc
         PeerVpcId: !Ref PeerVPCId
         PeerOwnerId: !Ref PeerVPCAccountId
         PeerRoleArn: !Ref PeerRoleArn
   Outputs:
     VPCId:
       Value: !Ref vpc
     VPCPeeringConnectionId:
       Value: !Ref vpcPeeringConnection
   ```

1. Choose **Next**.

1. Give the stack a name (for example, **VPC-peering-connection**).

1. Accept the defaults, and then choose **Next**.

1. Choose **I acknowledge that CloudFormation might create IAM resources**, and then choose **Create stack**.

## Create a template with a highly restrictive policy
<a name="create-template-with-highly-restrictive-policy"></a>

You might want to create a highly restrictive policy for peering your VPC with another AWS account. 

The following example template shows how to change the VPC peer owner template (the *accepter account* created in Step 1 above) so that it's more restrictive.

**Example JSON**  

```
{
  "AWSTemplateFormatVersion":"2010-09-09",
  "Description":"Create a VPC and an assumable role for cross account VPC peering.",
  "Parameters":{
    "PeerRequesterAccountId":{
      "Type":"String"
    }
  },
  "Resources":{
    "peerRole":{
      "Type":"AWS::IAM::Role",
      "Properties":{
        "AssumeRolePolicyDocument":{
          "Statement":[
            {
              "Action":[
                "sts:AssumeRole"
              ],
              "Effect":"Allow",
              "Principal":{
                "AWS":{
                  "Ref":"PeerRequesterAccountId"
                }
              }
            }
          ]
        },
        "Path":"/",
        "Policies":[
          {
            "PolicyDocument":{
              "Statement":[
                {
                  "Action":"ec2:acceptVpcPeeringConnection",
                  "Effect":"Allow",
                  "Resource":{
                    "Fn::Sub":"arn:aws:ec2:${AWS::Region}:${AWS::AccountId}:vpc/${vpc}"
                  }
                },
                {
                  "Action":"ec2:acceptVpcPeeringConnection",
                  "Condition":{
                    "StringEquals":{
                      "ec2:AccepterVpc":{
                        "Fn::Sub":"arn:aws:ec2:${AWS::Region}:${AWS::AccountId}:vpc/${vpc}"
                      }
                    }
                  },
                  "Effect":"Allow",
                  "Resource":{
                    "Fn::Sub":"arn:aws:ec2:${AWS::Region}:${AWS::AccountId}:vpc-peering-connection/*"
                  }
                }
              ],
              "Version":"2012-10-17" 		 	 	 
            },
            "PolicyName":"root"
          }
        ]
      }
    },
    "vpc":{
      "Type":"AWS::EC2::VPC",
      "Properties":{
        "CidrBlock":"10.1.0.0/16",
        "EnableDnsHostnames":false,
        "EnableDnsSupport":false,
        "InstanceTenancy":"default"
      }
    }
  },
  "Outputs":{
    "RoleARN":{
      "Value":{
        "Fn::GetAtt":[
          "peerRole",
          "Arn"
        ]
      }
    },
    "VPCId":{
      "Value":{
        "Ref":"vpc"
      }
    }
  }
}
```

**Example YAML**  

```
AWSTemplateFormatVersion: 2010-09-09
Description: Create a VPC and an assumable role for cross account VPC peering.
Parameters:
  PeerRequesterAccountId:
    Type: String
Resources:
  peerRole:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument:
        Statement:
          - Action:
              - 'sts:AssumeRole'
            Effect: Allow
            Principal:
              AWS:
                Ref: PeerRequesterAccountId
      Path: /
      Policies:
        - PolicyDocument:
            Statement:
              - Action: 'ec2:acceptVpcPeeringConnection'
                Effect: Allow
                Resource:
                  'Fn::Sub': 'arn:aws:ec2:${AWS::Region}:${AWS::AccountId}:vpc/${vpc}'
              - Action: 'ec2:acceptVpcPeeringConnection'
                Condition:
                  StringEquals:
                    'ec2:AccepterVpc':
                      'Fn::Sub': 'arn:aws:ec2:${AWS::Region}:${AWS::AccountId}:vpc/${vpc}'
                Effect: Allow
                Resource:
                  'Fn::Sub': >-
                    arn:aws:ec2:${AWS::Region}:${AWS::AccountId}:vpc-peering-connection/*
            Version: 2012-10-17 		 	 	 
          PolicyName: root
  vpc:
    Type: AWS::EC2::VPC
    Properties:
      CidrBlock: 10.1.0.0/16
      EnableDnsHostnames: false
      EnableDnsSupport: false
      InstanceTenancy: default
Outputs:
  RoleARN:
    Value:
      'Fn::GetAtt':
        - peerRole
        - Arn
  VPCId:
    Value:
      Ref: vpc
```

To access the VPC, you can use the same requester template as in Step 2 above.

For more information, see [Identity and access management for VPC peering](https://docs.aws.amazon.com/vpc/latest/peering/security-iam.html) in the *Amazon VPC Peering Guide*.

# Perform ECS blue/green deployments through CodeDeploy using CloudFormation
<a name="blue-green"></a>

To update an application running on Amazon Elastic Container Service (Amazon ECS), you can use a CodeDeploy blue/green deployment strategy. This strategy helps minimize interruptions caused by changing application versions. 

In a blue/green deployment, you create a new application environment (referred to as *green*) alongside your current, live environment (referred to as *blue*). This allows you to monitor and test the green environment before routing live traffic from the blue environment to the green environment. After the green environment is serving live traffic, you can safely terminate the blue environment.

To perform CodeDeploy blue/green deployments on ECS using CloudFormation, you include the following information in your stack template:
+ A `Hooks` section that describes a `AWS::CodeDeploy::BlueGreen` hook.
+  A `Transform` section that specifies the `AWS::CodeDeployBlueGreen` transform.

The following topics guide you through setting up a CloudFormation template for a blue/green deployment on ECS.

**Topics**
+ [About blue/green deployments](about-blue-green-deployments.md)
+ [Considerations when managing ECS blue/green deployments using CloudFormation](blue-green-considerations.md)
+ [`AWS::CodeDeploy::BlueGreen` hook syntax](blue-green-hook-syntax.md)
+ [Blue/green deployment template example](blue-green-template-example.md)

# About blue/green deployments
<a name="about-blue-green-deployments"></a>

This topic provides an overview of how performing blue/green deployments with CloudFormation works. It also explains how to prepare your CloudFormation template for blue/green deployments.

**Topics**
+ [How it works](#blue-green-how-it-works)
+ [Resource updates that initiate green deployments](#blue-green-resources)
+ [Preparing your template](#blue-green-setup)
+ [Modeling your blue/green deployment](#blue-green-required)
+ [Change sets](#blue-green-changesets)
+ [Monitoring stack events](#blue-green-events)
+ [IAM permissions](#blue-green-iam)

## How it works
<a name="blue-green-how-it-works"></a>

When using CloudFormation to perform ECS blue/green deployments through CodeDeploy, you start by creating a stack template that defines the resources for both your blue and green application environments, including specifying the traffic routing and stabilization settings to use. Next, you create a stack from that template. This generates your blue (current) application. CloudFormation only creates the blue resources during stack creation. Resources for a green deployment aren't created until they're required.

Then, if in a future stack update you update the task definition or task set resources in your blue application, CloudFormation does the following:
+ Generates all the necessary green application environment resources
+ Shifts the traffic based on the specified traffic routing parameters
+ Deletes the blue resources

If an error occurs at any point before the green deployment is successful and finalized, CloudFormation rolls the stack back to its state before the entire green deployment was initiated.

## Resource updates that initiate green deployments
<a name="blue-green-resources"></a>

When you perform a stack update that updates certain properties of specific ECS resources, CloudFormation initiates a green deployment process. The resources that initiate this process are:
+ [https://docs.aws.amazon.com/AWSCloudFormation/latest/TemplateReference/aws-resource-ecs-taskdefinition.html](https://docs.aws.amazon.com/AWSCloudFormation/latest/TemplateReference/aws-resource-ecs-taskdefinition.html)
+ [https://docs.aws.amazon.com/AWSCloudFormation/latest/TemplateReference/aws-resource-ecs-taskset.html](https://docs.aws.amazon.com/AWSCloudFormation/latest/TemplateReference/aws-resource-ecs-taskset.html) 

However, if the updates to these resources don't involve property changes that require replacement, a green deployment won't be initiated. For more information, see [Understand update behaviors of stack resources](using-cfn-updating-stacks-update-behaviors.md).

It's important to note that you can't combine updates to the above resources with updates to other resources in the same stack update operation. If you need to update both the listed resources and other resources within the same stack, you have two options:
+ Perform two separate stack update operations: one that includes only the updates to the above resources, and a separate stack update that includes changes to any other resources.
+ Remove the `Transform` and `Hooks` sections from your template and then perform the stack update. In this case, CloudFormation won't perform a green deployment.

## Preparing your template to perform ECS blue/green deployments
<a name="blue-green-setup"></a>

To enable blue/green deployments on your stack, include the following sections in your stack template before performing a stack update.
+ Add a reference to the `AWS::CodeDeployBlueGreen` transform to your template:

  ```
  "Transform": [
    "AWS::CodeDeployBlueGreen"
  ],
  ```
+ Add a `Hooks` section that invokes the `AWS::CodeDeploy::BlueGreen` hook and specifies the properties for your deployment. For more information, see [`AWS::CodeDeploy::BlueGreen` hook syntax](blue-green-hook-syntax.md).
+ In the `Resources` section, define the blue and green resources for your deployment.

You can add these sections when you first create the template (that's, before creating the stack itself), or you can add them to an existing template before performing a stack update. If you specify the blue/green deployment for a new stack, CloudFormation only creates the blue resources during stack creation — resources for the green deployment aren't created until they're required during a stack update.

## Modeling your blue/green deployment using CloudFormation resources
<a name="blue-green-required"></a>

To perform CodeDeploy blue/green deployment on ECS, your CloudFormation template needs to include the resources that model your deployment, such as an Amazon ECS service and load balancer. For more details on what these resources represent, see [Before you begin an Amazon ECS deployment](https://docs.aws.amazon.com/codedeploy/latest/userguide/deployment-steps-ecs.html#deployment-steps-prerequisites-ecs) in the *AWS CodeDeploy User Guide*.


| Requirement | Resource | Required/Optional | Initiates blue/green deployment if replaced? | 
| --- | --- | --- | --- | 
| Amazon ECS cluster | [https://docs.aws.amazon.com/AWSCloudFormation/latest/TemplateReference/aws-resource-ecs-cluster.html](https://docs.aws.amazon.com/AWSCloudFormation/latest/TemplateReference/aws-resource-ecs-cluster.html) | Optional. The default cluster can be used. | No | 
| Amazon ECS service | [https://docs.aws.amazon.com/AWSCloudFormation/latest/TemplateReference/aws-resource-ecs-service.html](https://docs.aws.amazon.com/AWSCloudFormation/latest/TemplateReference/aws-resource-ecs-service.html) | Required. | No | 
| Application or Network Load Balancer | [https://docs.aws.amazon.com/AWSCloudFormation/latest/TemplateReference/aws-properties-ecs-service-loadbalancer.html](https://docs.aws.amazon.com/AWSCloudFormation/latest/TemplateReference/aws-properties-ecs-service-loadbalancer.html) | Required. | No | 
| Production listener | [https://docs.aws.amazon.com/AWSCloudFormation/latest/TemplateReference/aws-resource-elasticloadbalancingv2-listener.html](https://docs.aws.amazon.com/AWSCloudFormation/latest/TemplateReference/aws-resource-elasticloadbalancingv2-listener.html) | Required. | No | 
| Test listener  | [https://docs.aws.amazon.com/AWSCloudFormation/latest/TemplateReference/aws-resource-elasticloadbalancingv2-listener.html](https://docs.aws.amazon.com/AWSCloudFormation/latest/TemplateReference/aws-resource-elasticloadbalancingv2-listener.html) | Optional. | No | 
| Two target groups | [https://docs.aws.amazon.com/AWSCloudFormation/latest/TemplateReference/aws-resource-elasticloadbalancingv2-targetgroup.html](https://docs.aws.amazon.com/AWSCloudFormation/latest/TemplateReference/aws-resource-elasticloadbalancingv2-targetgroup.html) | Required. | No | 
| Amazon ECS task definition  | [https://docs.aws.amazon.com/AWSCloudFormation/latest/TemplateReference/aws-resource-ecs-taskdefinition.html](https://docs.aws.amazon.com/AWSCloudFormation/latest/TemplateReference/aws-resource-ecs-taskdefinition.html) | Required. | Yes | 
| Container for your Amazon ECS application | [https://docs.aws.amazon.com/AWSCloudFormation/latest/TemplateReference/aws-properties-ecs-taskdefinition-containerdefinition.html#cfn-ecs-taskdefinition-containerdefinition-name](https://docs.aws.amazon.com/AWSCloudFormation/latest/TemplateReference/aws-properties-ecs-taskdefinition-containerdefinition.html#cfn-ecs-taskdefinition-containerdefinition-name) | Required. | No | 
| Port for your replacement task set | [https://docs.aws.amazon.com/AWSCloudFormation/latest/TemplateReference/aws-properties-ecs-taskdefinition-portmapping.html#cfn-ecs-taskdefinition-portmapping-containerport](https://docs.aws.amazon.com/AWSCloudFormation/latest/TemplateReference/aws-properties-ecs-taskdefinition-portmapping.html#cfn-ecs-taskdefinition-portmapping-containerport) | Required. | No | 

## Change sets
<a name="blue-green-changesets"></a>

We strongly recommend that you create a change set before performing a stack update that will initiate a green deployment. This allows you to see the actual changes that will be made to your stack before performing stack update. Be aware that resource changes may not be listed in the order in which they will be performed during the stack update. For more information, see [Update CloudFormation stacks using change sets](using-cfn-updating-stacks-changesets.md).

## Monitoring stack events
<a name="blue-green-events"></a>

You can view the stack events generated at each step of the ECS deployment on the **Events** tab of the **Stack** page, and using the AWS CLI. For more information, see [Monitor stack progress](monitor-stack-progress.md).

## IAM permissions for blue/green deployments
<a name="blue-green-iam"></a>

In order for CloudFormation to successfully perform the blue-green deployments, you must have the following CodeDeploy permissions:
+ `codedeploy:Get*`
+ `codedeploy:CreateCloudFormationDeployment`

For more information, see [Actions, resources, and condition keys for CodeDeploy](https://docs.aws.amazon.com/service-authorization/latest/reference/list_awscodedeploy.html) in the *Service Authorization Reference*.

# Considerations when managing ECS blue/green deployments using CloudFormation
<a name="blue-green-considerations"></a>

The process of using CloudFormation to perform your ECS blue/green deployments through CodeDeploy is different from a standard ECS deployment using just CodeDeploy. For a detailed understanding of these differences, see [Differences between Amazon ECS blue/green deployments through CodeDeploy and CloudFormation](https://docs.aws.amazon.com/codedeploy/latest/userguide/deployments-create-ecs-cfn.html#differences-ecs-bg-cfn) in the *AWS CodeDeploy User Guide*. 

When managing your blue/green deployment using CloudFormation, there are certain limitations and considerations to keep in mind:
+ Only updates to certain resources will initiate a green deployment. For more information, see [Resource updates that initiate green deployments](about-blue-green-deployments.md#blue-green-resources).
+ You can't include updates to resources that initiate green deployments and updates to other resources in the same stack update. For more information, see [Resource updates that initiate green deployments](about-blue-green-deployments.md#blue-green-resources).
+ You can only specify a single ECS service as the deployment target.
+ Parameters whose values are obfuscated by CloudFormation can't be updated by CodeDeploy during a green deployment, and will lead to an error and stack update failure. These include:
  + Parameters defined with the `NoEcho` attribute.
  + Parameters that use dynamic references to retrieve their values from external services. For more information about dynamic references, see [Get values stored in other services using dynamic references](dynamic-references.md).
+ To cancel a green deployment that's still in progress, cancel the stack update in CloudFormation, not CodeDeploy or ECS. For more information, see [Cancel a stack update](using-cfn-stack-update-cancel.md). After an update has finished, you can't cancel it. However, you can update a stack again with any previous settings.
+ The following CloudFormation features are not currently supported for templates that define ECS blue/green deployments:
  + Declaring [outputs](outputs-section-structure.md) or using [Fn::ImportValue](https://docs.aws.amazon.com/AWSCloudFormation/latest/TemplateReference/intrinsic-function-reference-importvalue.html) to import values from other stacks.
  + Importing resources. For more information about importing resources, see [Import AWS resources into a CloudFormation stack](import-resources.md).
  + Using the `AWS::CodeDeploy::BlueGreen` hook in a template that includes nested stack resources. For more information about nested stacks, see [Split a template into reusable pieces using nested stacks](using-cfn-nested-stacks.md).
  + Using the `AWS::CodeDeploy::BlueGreen` hook in a nested stack.

# `AWS::CodeDeploy::BlueGreen` hook syntax
<a name="blue-green-hook-syntax"></a>

The following syntax describes the structure of an `AWS::CodeDeploy::BlueGreen` hook for ECS blue/green deployments.

## Syntax
<a name="cfn-blue-green-hook-syntax"></a>

```
"Hooks": {
  "Logical ID": {
    "Type": "AWS::CodeDeploy::BlueGreen",
    "Properties": {
      "TrafficRoutingConfig": {
        "Type": "Traffic routing type",
        "TimeBasedCanary": {
          "StepPercentage": Integer,
          "BakeTimeMins": Integer
        },
        "TimeBasedLinear": {
          "StepPercentage": Integer,
          "BakeTimeMins": Integer
        }
      },
      "AdditionalOptions": {"TerminationWaitTimeInMinutes": Integer},
      "LifecycleEventHooks": {
        "BeforeInstall": "FunctionName",
        "AfterInstall": "FunctionName",
        "AfterAllowTestTraffic": "FunctionName",
        "BeforeAllowTraffic": "FunctionName",
        "AfterAllowTraffic": "FunctionName"
      },
      "ServiceRole": "CodeDeployServiceRoleName",
      "Applications": [
        {
          "Target": {
            "Type": "AWS::ECS::Service",
            "LogicalID": "Logical ID of AWS::ECS::Service"
          },
          "ECSAttributes": {
            "TaskDefinitions": [
              "Logical ID of AWS::ECS::TaskDefinition (Blue)",
              "Logical ID of AWS::ECS::TaskDefinition (Green)"
            ],
            "TaskSets": [
              "Logical ID of AWS::ECS::TaskSet (Blue)",
              "Logical ID of AWS::ECS::TaskSet (Green)"
            ],
            "TrafficRouting": {
              "ProdTrafficRoute": {
                "Type": "AWS::ElasticLoadBalancingV2::Listener",
                "LogicalID": "Logical ID of AWS::ElasticLoadBalancingV2::Listener (Production)"
              },
              "TestTrafficRoute": {
                "Type": "AWS::ElasticLoadBalancingV2::Listener",
                "LogicalID": "Logical ID of AWS::ElasticLoadBalancingV2::Listener (Test)"
              },
              "TargetGroups": [
                "Logical ID of AWS::ElasticLoadBalancingV2::TargetGroup (Blue)",
                "Logical ID of AWS::ElasticLoadBalancingV2::TargetGroup (Green)"
              ]
            }
          }
        }
      ]
    }
  }
}
```

## Properties
<a name="cfn-blue-green-hook-properties"></a>

Logical ID (also called *logical name*)  
The logical ID of a hook declared in the `Hooks` section of the template. The logical ID must be alphanumeric (A-Za-z0-9) and unique within the template.  
*Required*: Yes    
`Type`  
The type of hook. `AWS::CodeDeploy::BlueGreen`  
*Required*: Yes  
`Properties`  
Properties of the hook.  
*Required*: Yes    
`TrafficRoutingConfig`  
Traffic routing configuration settings.  
*Required*: No  
The default configuration is time-based canary traffic shifting, with a 15% step percentage and a five minute bake time.    
`Type`  
The type of traffic shifting used by the deployment configuration.  
Valid values: AllAtOnce \$1 TimeBasedCanary \$1 TimeBasedLinear  
*Required*: Yes    
`TimeBasedCanary`  
Specifies a configuration that shifts traffic from one version of the deployment to another in two increments.  
*Required*: Conditional: If you specify `TimeBasedCanary` as the traffic routing type, you must include the `TimeBasedCanary` parameter.    
`StepPercentage`  
The percentage of traffic to shift in the first increment of a `TimeBasedCanary` deployment. The step percentage must be 14% or greater.  
*Required*: No  
`BakeTimeMins`  
The number of minutes between the first and second traffic shifts of a `TimeBasedCanary` deployment.  
*Required*: No  
`TimeBasedLinear`  
Specifies a configuration that shifts traffic from one version of the deployment to another in equal increments, with an equal number of minutes between each increment.  
*Required*: Conditional: If you specify `TimeBasedLinear` as the traffic routing type, you must include the `TimeBasedLinear` parameter.    
`StepPercentage`  
The percentage of traffic that's shifted at the start of each increment of a `TimeBasedLinear` deployment. The step percentage must be 14% or greater.  
*Required*: No  
`BakeTimeMins`  
The number of minutes between each incremental traffic shift of a `TimeBasedLinear` deployment.  
*Required*: No  
`AdditionalOptions`  
Additional options for the blue/green deployment.  
*Required*: No    
`TerminationWaitTimeInMinutes`  
Specifies time to wait, in minutes, before terminating the blue resources.  
*Required*: No  
`LifecycleEventHooks`  
Use lifecycle event hooks to specify a Lambda function that CodeDeploy can call to validate a deployment. You can use the same function or a different one for deployment lifecyle events. Following completion of the validation tests, the Lambda `AfterAllowTraffic` function calls back CodeDeploy and delivers a result of `Succeeded` or `Failed`. For more information, see [AppSpec 'hooks' section](https://docs.aws.amazon.com/codedeploy/latest/userguide/reference-appspec-file-structure-hooks.html) in the *AWS CodeDeploy User Guide.*  
*Required*: No    
`BeforeInstall`  
Function to use to run tasks before the replacement task set is created.  
*Required*: No  
`AfterInstall`  
Function to use to run tasks after the replacement task set is created and one of the target groups is associated with it.  
*Required*: No  
`AfterAllowTestTraffic`  
Function to use to run tasks after the test listener serves traffic to the replacement task set.  
*Required*: No  
`BeforeAllowTraffic`  
Function to use to run tasks after the second target group is associated with the replacement task set, but before traffic is shifted to the replacement task set.  
*Required*: No  
`AfterAllowTraffic`  
Function to use to run tasks after the second target group serves traffic to the replacement task set.  
*Required*: No  
`ServiceRole`  
The execution role for CloudFormation to use to perform the blue-green deployments. For a list of the necessary permissions, see [IAM permissions for blue/green deployments](about-blue-green-deployments.md#blue-green-iam).  
*Required*: No  
`Applications`  
Specifies properties of the Amazon ECS application.  
*Required*: Yes    
`Target`  
  
*Required*: Yes    
`Type`  
The type of the resource.  
*Required*: Yes  
`LogicalID`  
The logical id of the resource.  
*Required*: Yes  
`ECSAttributes`  
The resources that represent the various requirements of your Amazon ECS application deployment.  
*Required*: Yes    
`TaskDefinitions`  
The logical ID of the [https://docs.aws.amazon.com/AWSCloudFormation/latest/TemplateReference/aws-resource-ecs-taskdefinition.html](https://docs.aws.amazon.com/AWSCloudFormation/latest/TemplateReference/aws-resource-ecs-taskdefinition.html) resource to run the Docker container that contains your Amazon ECS application.  
*Required*: Yes  
`TaskSets`  
The logical IDs of the [https://docs.aws.amazon.com/AWSCloudFormation/latest/TemplateReference/aws-resource-ecs-taskset.html](https://docs.aws.amazon.com/AWSCloudFormation/latest/TemplateReference/aws-resource-ecs-taskset.html) resources to use as task sets for the application.  
*Required*: Yes  
`TrafficRouting`  
Specifies resources used for traffic routing.  
*Required*: Yes    
`ProdTrafficRoute`  
The listener to be used by your load balancer to direct traffic to your target groups.  
*Required*: Yes    
`Type`  
The type of the resource. `AWS::ElasticLoadBalancingV2::Listener`  
*Required*: Yes  
`LogicalID`  
The logical ID of the resource.  
*Required*: Yes  
`TestTrafficRoute`  
The listener to be used by your load balancer to direct traffic to your target groups.  
*Required*: Yes    
`Type`  
The type of the resource. `AWS::ElasticLoadBalancingV2::Listener`  
*Required*: Yes  
`LogicalID`  
The logical ID of the resource.  
*Required*: No  
`TargetGroups`  
Logical ID of resources to use as target groups to route traffic to the registered target.  
*Required*: Yes

# Blue/green deployment template example
<a name="blue-green-template-example"></a>

The following example template sets up a CodeDeploy blue/green deployment on ECS, with a traffic routing progress of 15 percent per step and a stabilization period of 5 minutes between each step. 

Creating a stack with the template will provision the initial configuration of the deployment. If you then made any changes to properties in the `BlueTaskSet` resource that require that resource be replaced, CloudFormation will then initiate a green deployment as part of the stack update.

## JSON
<a name="blue-green-template-example.json"></a>

```
{
  "AWSTemplateFormatVersion":"2010-09-09",
  "Parameters":{
    "Vpc":{ "Type":"AWS::EC2::VPC::Id" },
    "Subnet1":{ "Type":"AWS::EC2::Subnet::Id" },
    "Subnet2":{ "Type":"AWS::EC2::Subnet::Id" }
  },
  "Transform":[ "AWS::CodeDeployBlueGreen" ],
  "Hooks":{
    "CodeDeployBlueGreenHook":{
      "Type":"AWS::CodeDeploy::BlueGreen",
      "Properties":{
        "TrafficRoutingConfig":{
          "Type":"TimeBasedCanary",
          "TimeBasedCanary":{
            "StepPercentage":15,
            "BakeTimeMins":5
          }
        },
        "Applications":[
          {
            "Target":{
              "Type":"AWS::ECS::Service",
              "LogicalID":"ECSDemoService"
            },
            "ECSAttributes":{
              "TaskDefinitions":[ "BlueTaskDefinition","GreenTaskDefinition" ],
              "TaskSets":[ "BlueTaskSet","GreenTaskSet" ],
              "TrafficRouting":{
                "ProdTrafficRoute":{
                  "Type":"AWS::ElasticLoadBalancingV2::Listener",
                  "LogicalID":"ALBListenerProdTraffic"
                },
                "TargetGroups":[ "ALBTargetGroupBlue","ALBTargetGroupGreen" ]
              }
            }
          }
        ]
      }
    }
  },
  "Resources":{
    "ExampleSecurityGroup":{
      "Type":"AWS::EC2::SecurityGroup",
      "Properties":{
        "GroupDescription":"Security group for ec2 access",
        "VpcId":{ "Ref":"Vpc" },
        "SecurityGroupIngress":[
          {
            "IpProtocol":"tcp",
            "FromPort":80,
            "ToPort":80,
            "CidrIp":"0.0.0.0/0"
          },
          {
            "IpProtocol":"tcp",
            "FromPort":8080,
            "ToPort":8080,
            "CidrIp":"0.0.0.0/0"
          },
          {
            "IpProtocol":"tcp",
            "FromPort":22,
            "ToPort":22,
            "CidrIp":"0.0.0.0/0"
          }
        ]
      }
    },
    "ALBTargetGroupBlue":{
      "Type":"AWS::ElasticLoadBalancingV2::TargetGroup",
      "Properties":{
        "HealthCheckIntervalSeconds":5,
        "HealthCheckPath":"/",
        "HealthCheckPort":"80",
        "HealthCheckProtocol":"HTTP",
        "HealthCheckTimeoutSeconds":2,
        "HealthyThresholdCount":2,
        "Matcher":{ "HttpCode":"200" },
        "Port":80,
        "Protocol":"HTTP",
        "Tags":[{ "Key":"Group","Value":"Example" }],
        "TargetType":"ip",
        "UnhealthyThresholdCount":4,
        "VpcId":{ "Ref":"Vpc" }
      }
    },
    "ALBTargetGroupGreen":{
      "Type":"AWS::ElasticLoadBalancingV2::TargetGroup",
      "Properties":{
        "HealthCheckIntervalSeconds":5,
        "HealthCheckPath":"/",
        "HealthCheckPort":"80",
        "HealthCheckProtocol":"HTTP",
        "HealthCheckTimeoutSeconds":2,
        "HealthyThresholdCount":2,
        "Matcher":{ "HttpCode":"200" },
        "Port":80,
        "Protocol":"HTTP",
        "Tags":[{ "Key":"Group","Value":"Example" }],
        "TargetType":"ip",
        "UnhealthyThresholdCount":4,
        "VpcId":{ "Ref":"Vpc" }
      }
    },
    "ExampleALB":{
      "Type":"AWS::ElasticLoadBalancingV2::LoadBalancer",
      "Properties":{
        "Scheme":"internet-facing",
        "SecurityGroups":[{ "Ref":"ExampleSecurityGroup" }],
        "Subnets":[{ "Ref":"Subnet1" },{ "Ref":"Subnet2" }],
        "Tags":[{ "Key":"Group","Value":"Example" }],
        "Type":"application",
        "IpAddressType":"ipv4"
      }
    },
    "ALBListenerProdTraffic":{
      "Type":"AWS::ElasticLoadBalancingV2::Listener",
      "Properties":{
        "DefaultActions":[
          {
            "Type":"forward",
            "ForwardConfig":{
              "TargetGroups":[
                {
                  "TargetGroupArn":{ "Ref":"ALBTargetGroupBlue" },
                  "Weight":1
                }
              ]
            }
          }
        ],
        "LoadBalancerArn":{ "Ref":"ExampleALB" },
        "Port":80,
        "Protocol":"HTTP"
      }
    },
    "ALBListenerProdRule":{
      "Type":"AWS::ElasticLoadBalancingV2::ListenerRule",
      "Properties":{
        "Actions":[
          {
            "Type":"forward",
            "ForwardConfig":{
              "TargetGroups":[
                {
                  "TargetGroupArn":{ "Ref":"ALBTargetGroupBlue" },
                  "Weight":1
                }
              ]
            }
          }
        ],
        "Conditions":[
          {
            "Field":"http-header",
            "HttpHeaderConfig":{
              "HttpHeaderName":"User-Agent",
              "Values":[ "Mozilla" ]
            }
          }
        ],
        "ListenerArn":{ "Ref":"ALBListenerProdTraffic" },
        "Priority":1
      }
    },
    "ECSTaskExecutionRole":{
      "Type":"AWS::IAM::Role",
      "Properties":{
        "AssumeRolePolicyDocument":{
          "Version": "2012-10-17",		 	 	 
          "Statement":[
            {
              "Sid":"",
              "Effect":"Allow",
              "Principal":{
                "Service":"ecs-tasks.amazonaws.com"
              },
              "Action":"sts:AssumeRole"
            }
          ]
        },
        "ManagedPolicyArns":[ "arn:aws:iam::aws:policy/service-role/AmazonECSTaskExecutionRolePolicy" ]
      }
    },
    "BlueTaskDefinition":{
      "Type":"AWS::ECS::TaskDefinition",
      "Properties":{
        "ExecutionRoleArn":{
          "Fn::GetAtt":[ "ECSTaskExecutionRole","Arn" ]
        },
        "ContainerDefinitions":[
          {
            "Name":"DemoApp",
            "Image":"nginxdemos/hello:latest",
            "Essential":true,
            "PortMappings":[
              {
                "HostPort":80,
                "Protocol":"tcp",
                "ContainerPort":80
              }
            ]
          }
        ],
        "RequiresCompatibilities":[ "FARGATE" ],
        "NetworkMode":"awsvpc",
        "Cpu":"256",
        "Memory":"512",
        "Family":"ecs-demo"
      }
    },
    "ECSDemoCluster":{
      "Type":"AWS::ECS::Cluster",
      "Properties":{}
    },
    "ECSDemoService":{
      "Type":"AWS::ECS::Service",
      "Properties":{
        "Cluster":{ "Ref":"ECSDemoCluster" },
        "DesiredCount":1,
        "DeploymentController":{ "Type":"EXTERNAL" }
      }
    },
    "BlueTaskSet":{
      "Type":"AWS::ECS::TaskSet",
      "Properties":{
        "Cluster":{ "Ref":"ECSDemoCluster" },
        "LaunchType":"FARGATE",
        "NetworkConfiguration":{
          "AwsVpcConfiguration":{
            "AssignPublicIp":"ENABLED",
            "SecurityGroups":[{ "Ref":"ExampleSecurityGroup" }],
            "Subnets":[{ "Ref":"Subnet1" },{ "Ref":"Subnet2" }]
          }
        },
        "PlatformVersion":"1.4.0",
        "Scale":{
          "Unit":"PERCENT",
          "Value":100
        },
        "Service":{ "Ref":"ECSDemoService"},
        "TaskDefinition":{ "Ref":"BlueTaskDefinition" },
        "LoadBalancers":[
          {
            "ContainerName":"DemoApp",
            "ContainerPort":80,
            "TargetGroupArn":{ "Ref":"ALBTargetGroupBlue" }
          }
        ]
      }
    },
    "PrimaryTaskSet":{
      "Type":"AWS::ECS::PrimaryTaskSet",
      "Properties":{
        "Cluster":{ "Ref":"ECSDemoCluster" },
        "Service":{ "Ref":"ECSDemoService" },
        "TaskSetId":{ "Fn::GetAtt":[ "BlueTaskSet","Id" ]
        }
      }
    }
  }
}
```

## YAML
<a name="blue-green-template-example.yaml"></a>

```
AWSTemplateFormatVersion: 2010-09-09
Parameters:
  Vpc:
    Type: AWS::EC2::VPC::Id
  Subnet1:
    Type: AWS::EC2::Subnet::Id
  Subnet2:
    Type: AWS::EC2::Subnet::Id
Transform:
  - 'AWS::CodeDeployBlueGreen'
Hooks:
  CodeDeployBlueGreenHook:
    Type: AWS::CodeDeploy::BlueGreen
    Properties:
      TrafficRoutingConfig:
        Type: TimeBasedCanary
        TimeBasedCanary:
          StepPercentage: 15
          BakeTimeMins: 5
      Applications:
        - Target:
            Type: AWS::ECS::Service
            LogicalID: ECSDemoService
          ECSAttributes:
            TaskDefinitions:
              - BlueTaskDefinition
              - GreenTaskDefinition
            TaskSets:
              - BlueTaskSet
              - GreenTaskSet
            TrafficRouting:
              ProdTrafficRoute:
                Type: AWS::ElasticLoadBalancingV2::Listener
                LogicalID: ALBListenerProdTraffic
              TargetGroups:
                - ALBTargetGroupBlue
                - ALBTargetGroupGreen
Resources:
  ExampleSecurityGroup:
    Type: AWS::EC2::SecurityGroup
    Properties:
      GroupDescription: Security group for ec2 access
      VpcId: !Ref Vpc
      SecurityGroupIngress:
        - IpProtocol: tcp
          FromPort: 80
          ToPort: 80
          CidrIp: 0.0.0.0/0
        - IpProtocol: tcp
          FromPort: 8080
          ToPort: 8080
          CidrIp: 0.0.0.0/0
        - IpProtocol: tcp
          FromPort: 22
          ToPort: 22
          CidrIp: 0.0.0.0/0
  ALBTargetGroupBlue:
    Type: AWS::ElasticLoadBalancingV2::TargetGroup
    Properties:
      HealthCheckIntervalSeconds: 5
      HealthCheckPath: /
      HealthCheckPort: '80'
      HealthCheckProtocol: HTTP
      HealthCheckTimeoutSeconds: 2
      HealthyThresholdCount: 2
      Matcher:
        HttpCode: '200'
      Port: 80
      Protocol: HTTP
      Tags:
        - Key: Group
          Value: Example
      TargetType: ip
      UnhealthyThresholdCount: 4
      VpcId: !Ref Vpc
  ALBTargetGroupGreen:
    Type: AWS::ElasticLoadBalancingV2::TargetGroup
    Properties:
      HealthCheckIntervalSeconds: 5
      HealthCheckPath: /
      HealthCheckPort: '80'
      HealthCheckProtocol: HTTP
      HealthCheckTimeoutSeconds: 2
      HealthyThresholdCount: 2
      Matcher:
        HttpCode: '200'
      Port: 80
      Protocol: HTTP
      Tags:
        - Key: Group
          Value: Example
      TargetType: ip
      UnhealthyThresholdCount: 4
      VpcId: !Ref Vpc
  ExampleALB:
    Type: AWS::ElasticLoadBalancingV2::LoadBalancer
    Properties:
      Scheme: internet-facing
      SecurityGroups:
        - !Ref ExampleSecurityGroup
      Subnets:
        - !Ref Subnet1
        - !Ref Subnet2
      Tags:
        - Key: Group
          Value: Example
      Type: application
      IpAddressType: ipv4
  ALBListenerProdTraffic:
    Type: AWS::ElasticLoadBalancingV2::Listener
    Properties:
      DefaultActions:
        - Type: forward
          ForwardConfig:
            TargetGroups:
              - TargetGroupArn: !Ref ALBTargetGroupBlue
                Weight: 1
      LoadBalancerArn: !Ref ExampleALB
      Port: 80
      Protocol: HTTP
  ALBListenerProdRule:
    Type: AWS::ElasticLoadBalancingV2::ListenerRule
    Properties:
      Actions:
        - Type: forward
          ForwardConfig:
            TargetGroups:
              - TargetGroupArn: !Ref ALBTargetGroupBlue
                Weight: 1
      Conditions:
        - Field: http-header
          HttpHeaderConfig:
            HttpHeaderName: User-Agent
            Values:
              - Mozilla
      ListenerArn: !Ref ALBListenerProdTraffic
      Priority: 1
  ECSTaskExecutionRole:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument:
        Version: 2012-10-17
        Statement:
          - Sid: ''
            Effect: Allow
            Principal:
              Service: ecs-tasks.amazonaws.com
            Action: 'sts:AssumeRole'
      ManagedPolicyArns:
        - 'arn:aws:iam::aws:policy/service-role/AmazonECSTaskExecutionRolePolicy'
  BlueTaskDefinition:
    Type: AWS::ECS::TaskDefinition
    Properties:
      ExecutionRoleArn: !GetAtt 
        - ECSTaskExecutionRole
        - Arn
      ContainerDefinitions:
        - Name: DemoApp
          Image: 'nginxdemos/hello:latest'
          Essential: true
          PortMappings:
            - HostPort: 80
              Protocol: tcp
              ContainerPort: 80
      RequiresCompatibilities:
        - FARGATE
      NetworkMode: awsvpc
      Cpu: '256'
      Memory: '512'
      Family: ecs-demo
  ECSDemoCluster:
    Type: AWS::ECS::Cluster
    Properties: {}
  ECSDemoService:
    Type: AWS::ECS::Service
    Properties:
      Cluster: !Ref ECSDemoCluster
      DesiredCount: 1
      DeploymentController:
        Type: EXTERNAL
  BlueTaskSet:
    Type: AWS::ECS::TaskSet
    Properties:
      Cluster: !Ref ECSDemoCluster
      LaunchType: FARGATE
      NetworkConfiguration:
        AwsVpcConfiguration:
          AssignPublicIp: ENABLED
          SecurityGroups:
            - !Ref ExampleSecurityGroup
          Subnets:
            - !Ref Subnet1
            - !Ref Subnet2
      PlatformVersion: 1.4.0
      Scale:
        Unit: PERCENT
        Value: 100
      Service: !Ref ECSDemoService
      TaskDefinition: !Ref BlueTaskDefinition
      LoadBalancers:
        - ContainerName: DemoApp
          ContainerPort: 80
          TargetGroupArn: !Ref ALBTargetGroupBlue
  PrimaryTaskSet:
    Type: AWS::ECS::PrimaryTaskSet
    Properties:
      Cluster: !Ref ECSDemoCluster
      Service: !Ref ECSDemoService
      TaskSetId: !GetAtt 
        - BlueTaskSet
        - Id
```