AWS CloudFormation
User Guide (API Version 2010-05-15)
« PreviousNext »
View the PDF for this guide.Go to the AWS Discussion Forum for this product.Go to the Kindle Store to download this guide in Kindle format.Did this page help you?  Yes | No |  Tell us about it...

Walkthrough: Updating a Stack

With AWS CloudFormation, you can update the properties for resources in your existing stacks. These changes can range from simple configuration changes, such as updating the alarm threshold on a CloudWatch alarm, to more complex changes, such as updating the Amazon Machine Image (AMI) running on an Amazon EC2 instance. Many of the AWS resources in a template can be updated, and we continue to add support for more.

This section walks through a simple progression of updates of a running stack. It shows how the use of templates makes it possible to use a version control system for the configuration of your AWS infrastructure, just as you use version control for the software you are running. We will walk through the following steps:

  1. Create the Initial Stack—create a stack using a base Amazon Linux AMI, installing the Apache Web Server and a simple PHP application using the AWS CloudFormation helper scripts.

  2. Update the Application—update one of the files in the application and deploy the software using AWS CloudFormation.

  3. Update the Instance Type—change the instance type of the underlying Amazon EC2 instance.

  4. Update the AMI on an Amazon EC2 instance—change the Amazon Machine Image (AMI) for the Amazon EC2 instance in your stack.

  5. Add a Key Pair to an Instance—add an Amazon EC2 key pair to the instance, and then update the security group to allow SSH access to the instance.

  6. Change the Stack's Resources—add and remove resources from the stack, converting it to an auto-scaled, load-balanced application by updating the template.

A Simple Application

We'll begin by creating a stack that we can use throughout the rest of this section. 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 AMI.

The Apache Web Server, PHP, and the simple PHP application are all installed by the AWS CloudFormation helper scripts that are installed by default on the Amazon Linux 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 AMI. The snippet also shows the Services section, which ensures that the Apache Web Server is running. In the Properties section of the Amazon EC2 instance definition, the UserData property contains the CloudInit script that calls cfn-init to install the packages and files.

  "WebServerHost": {
    "Type" : "AWS::EC2::Instance",
    "Metadata" : {
      "AWS::CloudFormation::Init" : {
        "config" : {
          "packages" : {
            "yum" : {
              "httpd"             : [],
              "php"               : []
            }
          },

          "files" : {

            "/var/www/html/index.php" : {
              "content" : { "Fn::Join" : ["", [
                "<?php\n",
                "echo '<h1>AWS CloudFormation sample PHP application</h1>';\n",
                "echo '<p>", { "Ref" : "WelcomeMessage" }, "</p>';\n",
                "?>\n"
              ]]},
              "mode"    : "000644",
              "owner"   : "apache",
              "group"   : "apache"
            },
          },

          :

          "services" : {
            "sysvinit" : {
              "httpd"    : { "enabled" : "true", "ensureRunning" : "true" },
              "sendmail" : { "enabled" : "false", "ensureRunning" : "false" }
            }
          }
        }
      }
    },

    "Properties": {
      :
      "UserData"       : { "Fn::Base64" : { "Fn::Join" : ["", [
        "#!/bin/bash\n",
        "yum update -y aws-cfn-bootstrap\n",

        :

        "# Install the simple web page\n",
        "/opt/aws/bin/cfn-init -s ", { "Ref" : "AWS::StackId" }, " -r WebServerHost ",
        "         --region ", { "Ref" : "AWS::Region" }, " || error_exit 'Failed to run cfn-init'\n",  
        :
      ]]}}
    }
  },     

The application itself is a very simple two-line "Hello, World" example that is 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. AWS CloudFormation can download packages (such as RPMs or RubyGems), as well as 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 AWS CloudFormation. The following snippet from the same EC2 resource in the template shows the pieces necessary to configure cfn-hup to call cfn-init to update the software if any changes to the metadata are detected:

  "WebServerHost": {
    "Type" : "AWS::EC2::Instance",
    "Metadata" : {
      "AWS::CloudFormation::Init" : {
        "config" : {

            :

          "files" : {

            :

            "/etc/cfn/cfn-hup.conf" : {
              "content" : { "Fn::Join" : ["", [
                "[main]\n",
                "stack=", { "Ref" : "AWS::StackName" }, "\n",
                "region=", { "Ref" : "AWS::Region" }, "\n"
              ]]},
              "mode"    : "000400",
              "owner"   : "root",
              "group"   : "root"
            },

            "/etc/cfn/hooks.d/cfn-auto-reloader.conf" : {
              "content": { "Fn::Join" : ["", [
                "[cfn-auto-reloader-hook]\n",
                "triggers=post.update\n",
                "path=Resources.WebServerHost.Metadata.AWS::CloudFormation::Init\n",
                "action=/opt/aws/bin/cfn-init -s ", { "Ref" : "AWS::StackId" }, " -r WebServerHost ",
                " --region     ", { "Ref" : "AWS::Region" }, "\n",  
                "runas=root\n"
              ]]}
            }
          },
          :
    },

    "Properties": {

         :

      "UserData"       : { "Fn::Base64" : { "Fn::Join" : ["", [

        :

        "# Start up the cfn-hup daemon to listen for changes to the Web Server metadata\n",
        "/opt/aws/bin/cfn-hup || error_exit 'Failed to start cfn-hup'\n",  

        :
      ]]}}
    }
  },     

To complete the stack, the template creates an Amazon EC2 security group, an elastic IP so that we have a consistent IP address to reference the application, and a CloudWatch alarm that triggers if the CPU on the instance reaches a threshold. Here's the complete template, which you can also download or reference at https://s3.amazonaws.com/cloudformation-templates-us-east-1/UpdateTutorial+Part1.template .

{
  "AWSTemplateFormatVersion" : "2010-09-09",

  "Description" : "AWS CloudFormation Sample Template UpdateTutorial Part 1: Sample template that can be used to test EC2 updates. **WARNING** This template creates an Amazon Ec2 Instance. You will be billed for the AWS resources used if you create a stack from this template.",
  
  "Parameters" : {
          
    "WebServerInstanceType" : {
      "Description" : "WebServer EC2 instance type",
      "Type" : "String",
      "Default" : "m1.small",
      "AllowedValues" : [ "t1.micro","m1.small","m1.medium","m1.large","m1.xlarge","m2.xlarge","m2.2xlarge","m2.4xlarge","m3.xlarge","m3.2xlarge","c1.medium","c1.xlarge","cc1.4xlarge","cc2.8xlarge","cg1.4xlarge"],
      "ConstraintDescription" : "must be a valid EC2 instance type."
    }
  },
  
  "Mappings" : {
    "AWSInstanceType2Arch" : {
      "t1.micro"    : { "Arch" : "32" },
      "m1.small"    : { "Arch" : "32" },
      "m1.medium"   : { "Arch" : "64" },
      "m1.large"    : { "Arch" : "64" },
      "m1.xlarge"   : { "Arch" : "64" },
      "m2.xlarge"   : { "Arch" : "64" },
      "m2.2xlarge"  : { "Arch" : "64" },
      "m2.4xlarge"  : { "Arch" : "64" },
      "m3.xlarge"   : { "Arch" : "64" },
      "m3.2xlarge"  : { "Arch" : "64" },
      "c1.medium"   : { "Arch" : "64" },
      "c1.xlarge"   : { "Arch" : "64" },
      "cc1.4xlarge" : { "Arch" : "64HVM" },
      "cc2.8xlarge" : { "Arch" : "64HVM" },
      "cg1.4xlarge" : { "Arch" : "64HVM" }
    },

    "AWSRegionArch2AMI" : {
      "us-east-1"      : { "32" : "ami-31814f58", "64" : "ami-1b814f72", "64HVM" : "ami-0da96764" },
      "us-west-2"      : { "32" : "ami-38fe7308", "64" : "ami-30fe7300", "64HVM" : "NOT_YET_SUPPORTED" },
      "us-west-1"      : { "32" : "ami-11d68a54", "64" : "ami-1bd68a5e", "64HVM" : "NOT_YET_SUPPORTED" },
      "eu-west-1"      : { "32" : "ami-973b06e3", "64" : "ami-953b06e1", "64HVM" : "NOT_YET_SUPPORTED" },
      "ap-southeast-1" : { "32" : "ami-b4b0cae6", "64" : "ami-beb0caec", "64HVM" : "NOT_YET_SUPPORTED" },
      "ap-southeast-2" : { "32" : "ami-b3990e89", "64" : "ami-bd990e87", "64HVM" : "NOT_YET_SUPPORTED" },
      "ap-northeast-1" : { "32" : "ami-0644f007", "64" : "ami-0a44f00b", "64HVM" : "NOT_YET_SUPPORTED" },
      "sa-east-1"      : { "32" : "ami-3e3be423", "64" : "ami-3c3be421", "64HVM" : "NOT_YET_SUPPORTED" }
    }
  },
    
  "Resources" : {   

    "WebServerSecurityGroup" : {
      "Type" : "AWS::EC2::SecurityGroup",
      "Properties" : {
        "GroupDescription" : "Enable HTTP",
        "SecurityGroupIngress" : [
          {"IpProtocol" : "tcp", "FromPort" : "80", "ToPort" : "80", "CidrIp" : "0.0.0.0/0"}
        ]
      }      
    },  

    "Endpoint" : {
      "Type" : "AWS::EC2::EIP",
      "Properties" : {
        "InstanceId" : { "Ref" : "WebServerHost" }
      }
    },
      
    "WebServerHost": {  
      "Type" : "AWS::EC2::Instance",
      "Metadata" : {
        "Comment" : "Install a simple PHP application",
        "AWS::CloudFormation::Init" : {
          "config" : {
            "packages" : {
              "yum" : {
                "httpd"             : [],
                "php"               : []
              }
            },

            "files" : {

              "/var/www/html/index.php" : {
                "content" : { "Fn::Join" : ["", [
                  "<?php\n",
                  "echo '<h1>AWS CloudFormation sample PHP application</h1>';\n",
                  "?>\n"
                ]]},
                "mode"    : "000644",
                "owner"   : "apache",
                "group"   : "apache"
              },


              "/etc/cfn/cfn-hup.conf" : {
                "content" : { "Fn::Join" : ["", [
                  "[main]\n",
                  "stack=", { "Ref" : "AWS::StackId" }, "\n",
                  "region=", { "Ref" : "AWS::Region" }, "\n"
                ]]},
                "mode"    : "000400",
                "owner"   : "root",
                "group"   : "root"
              },

              "/etc/cfn/hooks.d/cfn-auto-reloader.conf" : {
                "content": { "Fn::Join" : ["", [
                  "[cfn-auto-reloader-hook]\n",
                  "triggers=post.update\n",
                  "path=Resources.WebServerHost.Metadata.AWS::CloudFormation::Init\n",
                  "action=/opt/aws/bin/cfn-init -s ", { "Ref" : "AWS::StackId" }, " -r WebServerHost ",
                                                   " --region     ", { "Ref" : "AWS::Region" }, "\n",
                  "runas=root\n"
                ]]}
              }
            },

            "services" : {
              "sysvinit" : {
                "httpd"    : { "enabled" : "true", "ensureRunning" : "true" },
                "sendmail" : { "enabled" : "false", "ensureRunning" : "false" }
              }
            }
          }
        }
      },

      "Properties": {
        "ImageId" : { "Fn::FindInMap" : [ "AWSRegionArch2AMI", { "Ref" : "AWS::Region" }, { "Fn::FindInMap" : [ "AWSInstanceType2Arch", { "Ref" : "WebServerInstanceType" }, "Arch" ] } ] },
        "InstanceType" : { "Ref" : "WebServerInstanceType" },
        "SecurityGroups" : [ {"Ref" : "WebServerSecurityGroup"} ],
        "UserData"       : { "Fn::Base64" : { "Fn::Join" : ["", [
          "#!/bin/bash\n",
          "yum update -y aws-cfn-bootstrap\n",

          "# Helper function\n",
          "function error_exit\n",
          "{\n",
          "  /opt/aws/bin/cfn-signal -e 1 -r \"$1\" '", { "Ref" : "WebServerWaitHandle" }, "'\n",
          "  exit 1\n",
          "}\n",

          "# Install the simple web page\n",
          "/opt/aws/bin/cfn-init -s ", { "Ref" : "AWS::StackId" }, " -r WebServerHost ",
          "         --region ", { "Ref" : "AWS::Region" }, " || error_exit 'Failed to run cfn-init'\n",

          "# Start up the cfn-hup daemon to listen for changes to the Web Server metadata\n",
          "/opt/aws/bin/cfn-hup || error_exit 'Failed to start cfn-hup'\n",

          "# All done so signal success\n",
          "/opt/aws/bin/cfn-signal -e 0 -r \"WebServer setup complete\" '", { "Ref" : "WebServerWaitHandle" }, "'\n"
        ]]}}        
      }
    },

    "WebServerWaitHandle" : {
      "Type" : "AWS::CloudFormation::WaitConditionHandle"
    },

    "WebServerWaitCondition" : {
      "Type" : "AWS::CloudFormation::WaitCondition",
      "DependsOn" : "WebServerHost",
      "Properties" : {
        "Handle" : {"Ref" : "WebServerWaitHandle"},
        "Timeout" : "300"
      }
    }
  },
  
  "Outputs" : {
    "WebsiteURL" : {
      "Value" : { "Fn::Join" : ["", ["http://", { "Ref" : "Endpoint" } ]] },
      "Description" : "Application URL"
    }
  }
}

This example uses a single EC2 instance and Elastic IP address, but you can use the same mechanisms on more complex solutions that make use of Elastic Load Balancers and Auto Scaling groups to manage a collection of application servers. There are, however, some special considerations for Auto Scaling groups. For more information, see Updating Auto Scaling Groups.

Create the Initial Stack

For the purposes of this example, we’ll use the AWS Management Console to create an initial stack from the sample template.

Caution

Completing this procedure will deploy live AWS services. You will be charged the standard usage rates as long as these services are running.

To create the stack from the AWS Management Console

  1. Download the template at https://s3.amazonaws.com/cloudformation-templates-us-east-1/UpdateTutorial+Part1.template and save it in a safe place on your system. Note the location because you'll need to use the file in a subsequent step.

  2. Log in to the AWS CloudFormation console at https://console.aws.amazon.com/cloudformation .

  3. Click Create New Stack.

  4. In the Create New Stack wizard, on the Select Template screen, type UpdateTutorial in the Name field. On the same page, select Upload a template to Amazon S3 and browse to the file that you downloaded in the first step, and then click Next.

  5. On the Specify Parameters screen, in the Web Server Instance Type box, type t1.micro. Then click Next.

  6. On the Options screen, click Next.

  7. On the Review screen, verify that all the settings are as you want them, and then click Create.

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

Update the Application

Now that we have deployed the stack, let's update the application. We'll make a simple change to the text that is 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:

"WebServerHost": {
      "Type" : "AWS::EC2::Instance",
      "Metadata" : {
        "AWS::CloudFormation::Init" : {
          "config" : {
              :

            "files" : {

              "/var/www/html/index.php" : {
                "content" : { "Fn::Join" : ["", [
                  "<?php\n",
                  "echo '<h1>AWS CloudFormation sample PHP application</h1>';\n",
                  "echo 'Updated version via UpdateStack';\n ",
                  "?>\n"
                ]]},
                "mode"    : "000644",
                "owner"   : "apache",
                "group"   : "apache"
              },

              :

      }
    },
      

You can manually edit the template you downloaded previously, or download the updated template at https://s3.amazonaws.com/cloudformation-templates-us-east-1/UpdateTutorial+Part2.template.

Now, we'll update the stack.

To update the stack from the AWS Management Console

  1. Log in to the AWS CloudFormation console, at: https://console.aws.amazon.com/cloudformation.

  2. On the AWS CloudFormation dashboard, click the stack you created previously, and then click Update Stack.

  3. In the Update Stack wizard, on the Select Template screen, select Upload a template to Amazon S3, select the modified template, and then click Next.

  4. On the Options screen, click Next.

  5. Click Next because the stack doesn't have a stack policy. All resources can be updated without an overriding policy.

  6. On the Review screen, verify that all the settings are as you want them, and then click Update.

If you update the stack from the AWS Management Console, you will notice that the parameters that were used to create the initial stack are prepopulated on the Parameters page of the Update Stack wizard. If you use the aws cloudformation update-stack command, be sure to type in the same values for the parameters that you used originally to create the stack.

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

To see the set of resources that were updated, go to the AWS CloudFormation console. On the Events tab, look at the stack events. In this particular case, the metadata for the Amazon EC2 instance WebServerHost was updated, which caused AWS CloudFormation to also reevaluate the Elastic IP address and the WaitCondition resource to ensure that there were no changes that affected the update. None of the other stack resources were modified. AWS 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.

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 may 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. In the following template snippet, the changes are highlighted in red:

    "WebServerHost": {
      "Type" : "AWS::EC2::Instance",
      "Metadata" : {
        "Comment" : "Install a simple PHP application",
        "AWS::CloudFormation::Init" : {
          "config" : {
            "packages" : {
              "yum" : {
                "httpd"             : [],
                "php"               : [],
                "php-mysql"         : [],
                "mysql-server"      : [],
                "mysql-devel"       : [],
                "mysql-libs"        : [],
                "mysql"             : []
              }
            },

            :

            "services" : {
              "sysvinit" : {
                "httpd"    : { "enabled" : "true", "ensureRunning" : "true" },
                "mysqld"   : { "enabled" : "true", "ensureRunning" : "true" },
                "sendmail" : { "enabled" : "false", "ensureRunning" : "false" }
              }
            }
          }
        }
      },

      "Properties": {
           :
      }
    }
      

You can also use UpdateStack, along with 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"]
    }
    }

Updating Auto Scaling Groups

If you are using Auto Scaling groups in your template, as opposed to Amazon EC2 instance resources, updating the application will work in exactly the same way; however, AWS CloudFormation does not provide any synchronization or serialization across the EC2 instances in an Auto Scaling group. The cfn-hup daemon on each host will run independently and update the application on its own schedule. When you use cfn-hup to update the on-instance configuration, each instance will run the cfn-hup hooks on its own schedule; there is no coordination between the instances in the stack. You should consider the following:

  • If the cfn-hup changes run on all EC2 instances in the Auto Scaling group at the same time, your service may be unavailable during the update.

  • If the cfn-hup changes run at different times, old and new versions of the software may be running at the same.

Changing Resource Properties

With AWS CloudFormation, you can change the properties of an existing resource in the stack. The following sections describe various updates that solve specific problems; however, any property of any resource that supports updating in the stack can be modified as necessary.

Update the Instance Type

The stack we have built so far uses a t1.micro Amazon EC2 instance. Let's suppose that your newly created website is getting more traffic than a t1.micro instance can handle, and now you want to move to an m1.small EC2 instance type. If the architecture of the instance type changes from 32 bit to 64 bit, the instance will be created with a different AMI. If you check out the mappings in the template, you will see that both the t1.micro and m1.small are 32 bit architectures, and they use the same base Amazon Linux AMI.

"Mappings" : {
    "AWSInstanceType2Arch" : {
      "t1.micro"    : { "Arch" : "32" },
      "m1.small"    : { "Arch" : "32" },
      "m1.medium"   : { "Arch" : "64" },
      "m1.large"    : { "Arch" : "64" },
      "m1.xlarge"   : { "Arch" : "64" },
      "m2.xlarge"   : { "Arch" : "64" },
      "m2.2xlarge"  : { "Arch" : "64" },
      "m2.4xlarge"  : { "Arch" : "64" },
      "m3.xlarge"   : { "Arch" : "64" },
      "m3.2xlarge"  : { "Arch" : "64" },
      "c1.medium"   : { "Arch" : "64" },
      "c1.xlarge"   : { "Arch" : "64" },
      "cc1.4xlarge" : { "Arch" : "64HVM" },
      "cc2.8xlarge" : { "Arch" : "64HVM" },
      "cg1.4xlarge" : { "Arch" : "64HVM" }
    },

    "AWSRegionArch2AMI" : {
      "us-east-1"      : { "32" : "ami-31814f58", "64" : "ami-1b814f72", "64HVM" : "ami-0da96764" },
      "us-west-2"      : { "32" : "ami-38fe7308", "64" : "ami-30fe7300", "64HVM" : "NOT_YET_SUPPORTED" },
      "us-west-1"      : { "32" : "ami-11d68a54", "64" : "ami-1bd68a5e", "64HVM" : "NOT_YET_SUPPORTED" },
      "eu-west-1"      : { "32" : "ami-973b06e3", "64" : "ami-953b06e1", "64HVM" : "NOT_YET_SUPPORTED" },
      "ap-southeast-1" : { "32" : "ami-b4b0cae6", "64" : "ami-beb0caec", "64HVM" : "NOT_YET_SUPPORTED" },
      "ap-southeast-2" : { "32" : "ami-b3990e89", "64" : "ami-bd990e87", "64HVM" : "NOT_YET_SUPPORTED" },
      "ap-northeast-1" : { "32" : "ami-0644f007", "64" : "ami-0a44f00b", "64HVM" : "NOT_YET_SUPPORTED" },
      "sa-east-1"      : { "32" : "ami-3e3be423", "64" : "ami-3c3be421", "64HVM" : "NOT_YET_SUPPORTED" }
    }
  }

Let's use the template that we modified in the previous section to change the instance type. Because InstanceType was an input parameter to the template, we don't need to modify the template; we can simply change the value of the parameter in the Stack Update wizard, on the Specify Parameters page.

To update the stack from the AWS Management Console

  1. Log in to the AWS CloudFormation console at https://console.aws.amazon.com/cloudformation.

  2. On the AWS CloudFormation dashboard, click the stack you created previously, and then click Update Stack.

  3. In the Update Stack wizard, on the Select Template screen, select Use existing template, and then click Next.

    The Specify Parameters page appears with the parameters that were used to create the initial stack are pre-populated in the Specify Parameters section.

  4. Change the value of the WebServerInstanceType text box from m1.small to t1.micro. Then, click Next.

  5. On the Options screen, click Next.

  6. Click Next because the stack doesn't have a stack policy. All resources can be updated without an overriding policy.

  7. On the Review screen, verify that all the settings are as you want them, and then click Update.

You can dynamically change the instance type of an EBS-backed Amazon EC2 instance by starting and stopping the instance. AWS CloudFormation tries to optimize the change by updating the instance type and restarting the instance, so the instance ID does not 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, AWS CloudFormation will also update the Elastic IP address. You can see the changes in the AWS 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.

Update the AMI on an Amazon EC2 instance

Now let's look at how we might change the Amazon Machine Image (AMI) running on the instance. We will trigger the AMI change by updating the stack to use a new EC2 instance type, such as m1.large, which is a 64-bit instance type.

As in the previous section, we’ll use our existing template to change the instance type used by our example stack. In the Stack Update wizard, on the Specify Parameters page, change the value of the Web Server Instance Type.

In this case, we cannot simply start and stop the instance to modify the AMI; AWS CloudFormation considers this a change to an immutable property of the resource. In order to make a change to an immutable property, AWS CloudFormation must launch a replacement resource, in this case a new Amazon EC2 instance running the new AMI.

After the new instance is running, AWS CloudFormation updates the other resources in the stack, such as the Elastic IP address, 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 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.

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 for an instance on your stack

  1. Create your new AMIs containing your application or operating system changes. For more information, go to Creating Your Own AMIs in the Amazon EC2 User Guide for Linux Instances.

  2. Update your template to incorporate the new AMI IDs.

  3. Update the stack, either from the AWS Management Console as explained in Update the Application or by using the AWS command aws cloudformation update-stack.

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

Update the Amazon EC2 Launch Configuration for an Auto Scaling Group

If you are using Auto Scaling groups rather than EC2 instances, the process of updating the running instances is a little different. With Auto Scaling resources, the configuration of the EC2 instances, such as the instance type or the AMI Id is encapsulated in the Auto Scaling launch configuration. You can make changes to the launch configuration in the same way as we made changes to the EC2 instance resources in the previous sections. However, changing the launch configuration does not impact any of the running EC2 instances in the Auto Scaling group. An updated launch configuration applies only to new instances that are created after the update.

If you want to propagate the change to your launch configuration across all the instances in your Auto Scaling group, you can use the Auto Scaling as-terminate-instance-in-auto-scaling-group command line tool to replace each instance as follows:

as-terminate-instance-in-auto-scaling-group <instance_id> --no-decrement-desired-capacity

For more information about the Auto Scaling command line tools, go to Using the Command Line Tools in the Auto Scaling Developer Guide. After the instance is terminated, Auto Scaling will replace it with one that uses the new AMI. Instance replacements are not instantaneous, and it may take some time to register the new instance with Elastic Load Balancing and any other affected services. Take care not to leave your group under capacity during an upgrade.

Adding Resource Properties

So far, we've looked at changing existing properties of a resource in a template. You can also add properties that were not 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.

Add a Key Pair to an 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" : {
    
        "WebServerKeyName" : {
          "Description" : "Name of an existing Amazon EC2 key pair for SSH access",
          "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",
          "AllowedPattern": "(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})/(\\d{1,2})",
          "ConstraintDescription": "must be a valid IP CIDR range of the form x.x.x.x/x."
        }  
        :
      },
    

  2. Add the KeyName property to the Amazon EC2 instance.

              "WebServerHost": {
              "Type" : "AWS::EC2::Instance",
              :
              "Properties": {
              :
              "KeyName" : { "Ref" : "WebServerKeyName" },
              :
              }
              },
    
  3. 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 and SSH",
            "SecurityGroupIngress" : [
              {"IpProtocol" : "tcp", "FromPort" : "22", "ToPort" : "22", "CidrIp" : { "Ref" : "SSHLocation"}}
              {"IpProtocol" : "tcp", "FromPort" : "80", "ToPort" : "80", "CidrIp" : 
            ]
          }
        },    

  4. Update the stack, either from the AWS Management Console as explained in Update the Application or by using the AWS command aws cloudformation update-stack.

You can download or reference the updated template at https://s3.amazonaws.com/cloudformation-templates-us-east-1/UpdateTutorial+Part3.template.

Change the Stack's Resources

Since application needs can change over time, AWS CloudFormation allows you to change the set of resources that make up the stack. To demonstrate, we’ll take the single instance application from Adding Resource Properties and convert it to an auto-scaled, load-balanced application by updating the stack.

To begin, you can manually edit the template you downloaded previously, or download the updated template at https://s3.amazonaws.com/cloudformation-templates-us-east-1/UpdateTutorial+Part3.template.

This will create a simple, single instance PHP application using an Elastic IP address. We'll now turn the application into a highly available, auto-scaled, load balanced application by changing its resources during an update.

  1. Remove the Elastic IP address resource from the template.

    "Endpoint" : {
      "Type" : "AWS::EC2::EIP",
      "Properties" : {
        "InstanceId" : { "Ref" : "WebServerHost" }
      }
    },             
  2. Add an Elastic Load Balancer resource.

    "ElasticLoadBalancer" : {
      "Type" : "AWS::ElasticLoadBalancing::LoadBalancer",
      "Properties" : {
        "AvailabilityZones" : { "Fn::GetAZs" : "" },
        "Listeners" : [ {
          "LoadBalancerPort" : "80",
          "InstancePort" : "80",
          "Protocol" : "HTTP"
        } ],
        "HealthCheck" : {
          "Target" : "HTTP:80/",
          "HealthyThreshold" : "3",
          "UnhealthyThreshold" : "5",
          "Interval" : "30",
          "Timeout" : "5"
        }
      }
    },             
  3. Convert the EC2 instance in the template into an Auto Scaling Launch Configuration. The properties are identical, so we only need to change the type name from:

    "WebServerHost": {
      "Type" : "AWS::EC2::Instance",

    to:

    "WebServerConfig": {
      "Type" : "AWS::AutoScaling::LaunchConfiguration",

    For clarity in the template, I’ve also changed the name of the resource from WebServerHost to WebServerConfig, so you’ll need to update the resource name referenced by cfn-init and cfn-hup (just search for WebServerHost and replace it with WebServerConfig).

  4. Add an Auto Scaling Group resource.

    "WebServerGroup" : {
      "Type" : "AWS::AutoScaling::AutoScalingGroup",
      "Properties" : {
        "AvailabilityZones" : { "Fn::GetAZs" : ""},
        "LaunchConfigurationName" : { "Ref" : "WebServerConfig" },
        "MinSize" : "1",
        "MaxSize" : "3",
        "LoadBalancerNames" : [ { "Ref" : "ElasticLoadBalancer" } ]
      }
    },             
  5. Update the Security Group definition to lock down the traffic to the instances from the load balancer.

    "WebServerSecurityGroup" : {
      "Type" : "AWS::EC2::SecurityGroup",
      "Properties" : {
        "GroupDescription" : "Enable SSH access and HTTP from the load balancer only",
        "SecurityGroupIngress" : [{
          "IpProtocol" : "tcp",
          "FromPort" : "22",
          "ToPort" : "22",
          "CidrIp" : { "Ref" : "SSHLocation"}}
        }, {
          "IpProtocol" : "tcp",
          "FromPort" : "80",
          "ToPort" : "80",
          "SourceSecurityGroupOwnerId" : {"Fn::GetAtt" :
             ["ElasticLoadBalancer", "SourceSecurityGroup.OwnerAlias"]},
          "SourceSecurityGroupName" : {"Fn::GetAtt" :
             ["ElasticLoadBalancer", "SourceSecurityGroup.GroupName"]}
        }]
      }
    },             
  6. Update the Outputs to return the DNS Name of the Elastic Load Balancer as the location of the application from:

    "WebsiteURL" : {
      "Value" : { "Fn::Join" : ["", ["http://", {"Ref":"Endpoint" }]]},
      "Description" : "Application URL"
    }              

    to:

    "WebsiteURL" : {
      "Value" : { "Fn::Join" : ["", ["http://", 
          { "Fn::GetAtt" : [ "ElasticLoadBalancer", "DNSName" ]}]]},
      "Description" : "Application URL"
    }              

You can download or reference the complete template at: https://s3.amazonaws.com/cloudformation-templates-us-east-1/UpdateTutorialPart5.template.

If you use this template to update the stack, you will convert your simple, single instance application into a highly available, multi-AZ, auto-scaled and load balanced application. Only the resources that need to be updated will be altered, so had there been any data stores for this application, the data would have remained intact. Now, you can use AWS CloudFormation to grow or enhance your stacks as your requirements change.

Availability and Impact Considerations

Different properties have different impacts on the resources in the stack. You can use AWS 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. AWS 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.

  2. Is the change mutable or immutable? Some changes to resource properties, such as changing the AMI on an Amazon EC2 instance, are not supported by the underlying services. In the case of mutable changes, AWS CloudFormation will use the Update or Modify type APIs for the underlying resources. For immutable property changes, AWS CloudFormation will create new resources with the updated properties and then link them to the stack before deleting the old resources. Although AWS 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

For more information about using AWS CloudFormation to start applications and on integrating with other configuration and deployment services such as Puppet and Opscode Chef, see the following whitepapers:

The template used throughout this section is a "Hello, World" PHP application. The template library also has an Amazon ElastiCache sample template that shows how to integrate a PHP application with ElasticCache using cfn-hup and cfn-init to respond to changes in the Amazon ElastiCache Cache Cluster configuration, all of which can be performed by Update Stack.