Creating CloudFormation templates

In this section, we present the recipe to create an AWS CloudFormation template to specify the AWS resources (and their properties). AWS CloudFormation provides editors to help you to edit your CloudFormation templates. These editors are available for both Visual Studio (AWS Toolkit for Visual Studio) and Eclipse (AWS Toolkit for Eclipse) environments. Based on your application-specific requirements, you will need to define parameters, mappings, resources, and output in the template. Image ID, instance type, VPC ID, subnet IDs, a name for the instance, and key/pair names are the required parameters to run this recipe. You can store this template file in a local folder or in S3 storage. Before creating the stack from the template, you should validate the template using CLI.

How to do it…

Here, we present the specific commands to use for defining, validating, and then creating an AWS cloud stack. As an example, we will be creating the CloudFormation template for an autoscaled web application. This application scenario will require us to define an Elastic Load Balancer (ELB), launching configurations, and autoscaling groups, cloud watch alarms, and security groups. We will also include commands to retrieve the AWS resource descriptions, get access to the stack events, and send event notifications to an SNS topic. Create the stack by performing the following steps:

  1. Create a template file (let's name the file webserverautoscaling.json) with the JSON string specified as follows:
    {
      "AWSTemplateFormatVersion" : "2010-09-09",
      "Description" : "AWS CloudFormation template for web server auto scaling deployment.",
      "Parameters" : {
        "ImageId" : {
          "Description" : "AMI id.",
          "Type" : "String",
          "Default" : "ami-1e604a4c"
        },
        "InstanceType" : {
          "Description" : "Tomcat EC2 instance type.",
          "Type" : "String",
          "Default" : "m3.medium",
          "AllowedValues" : ["t2.small", "t2.medium", "m1.small", "m1.medium", "m3.medium","m3.large"]
        },
        "VpcId" : {
            "Type" : "String",
            "Description" : "VPC id.",
            "Default" : "vpc-0214e967"
        },
        "Subnets" : {
          "Type" : "CommaDelimitedList",
          "Default" : "subnet-0240b575,subnet-5314c936",
          "Description" : "Subnet id's list."
        },
        "KeyPairName" : {
          "Description" : "EC2 Key Pair to allow SSH access to the instances.",
          "Type" : "String",
          "Default" : "ApacheServerKeyPair"
        },
        "NameForNewInstances" : {
          "Description" : "Name for the instances creating in Auto Scaling Group.",
          "Type" : "String",
          "Default" : "apacheserver-asg-instance"
        }
      },
    
      "Mappings" : {
         
      },
    
      "Resources" : {
        "SecurityGroup": {
            "Type": "AWS::EC2::SecurityGroup",
            "Properties": {
                "GroupDescription": "Enable port 80 access from anywhere.",
                "VpcId" : { "Ref" : "VpcId" },
                "SecurityGroupIngress": [{
                                     "IpProtocol": "tcp",
                                     "FromPort": "80" ,
                                     "ToPort": "80" ,
                                     "CidrIp" : "0.0.0.0/0"
                                    }]
                        }
        },
        "ElasticLoadBalancer" : {
            "Type" : "AWS::ElasticLoadBalancing::LoadBalancer",
            "Properties" : {
                "Subnets" : { "Ref" : "Subnets" },
                "SecurityGroups" : [ { "Ref" : "SecurityGroup" } ],
                "Listeners" : [ {
                    "LoadBalancerPort" : "80",
                    "InstancePort" : "80" ,
                    "Protocol" : "HTTP"
                }],
                "HealthCheck" : {
                    "Target" : "HTTP:80/",
                    "HealthyThreshold" : "3",
                    "UnhealthyThreshold" : "5",
                    "Interval" : "30",
                    "Timeout" : "5"
                }
            }
        },
        "WebServerAutoScalingGroup" : {
          "Type" : "AWS::AutoScaling::AutoScalingGroup",
          "Properties" : {
            "AvailabilityZones" : { "Fn::GetAZs" : "" },
            "VPCZoneIdentifier" : { "Ref" : "Subnets" },
            "LaunchConfigurationName" : { "Ref" : "LaunchConfig" },
            "MinSize" : "1",
            "MaxSize" : "4",
            "LoadBalancerNames" : [ { "Ref" : "ElasticLoadBalancer" } ],
            "HealthCheckGracePeriod" : 300,
            "HealthCheckType" : "ELB",
            "Tags" : [{"Key" : "Name", "Value" : { "Ref" : "NameForNewInstances" },"PropagateAtLaunch" : true}]
          }
        },
    
        "LaunchConfig" : {
          "Type" : "AWS::AutoScaling::LaunchConfiguration",
          "Properties" : {
            "KeyName" : { "Ref" : "KeyPairName" },
            "ImageId" : { "Ref" : "ImageId" },
            "InstanceType" : { "Ref" : "InstanceType" },
            "AssociatePublicIpAddress" : true,
            "SecurityGroups" : [ { "Ref" : "SecurityGroup" } ]
          }
        },
    
        "WebServerScaleUpPolicy" : {
          "Type" : "AWS::AutoScaling::ScalingPolicy",
          "Properties" : {
            "AdjustmentType" : "ChangeInCapacity",
            "AutoScalingGroupName" : { "Ref" : "WebServerAutoScalingGroup" },
            "Cooldown" : "60",
            "ScalingAdjustment" : "1"
          }
        },
        "WebServerScaleDownPolicy" : {
          "Type" : "AWS::AutoScaling::ScalingPolicy",
          "Properties" : {
            "AdjustmentType" : "ChangeInCapacity",
            "AutoScalingGroupName" : { "Ref" : "WebServerAutoScalingGroup" },
            "Cooldown" : "60",
            "ScalingAdjustment" : "-1"
          }
        },
    
        "CPUAlarmHigh": {
         "Type": "AWS::CloudWatch::Alarm",
         "Properties": {
            "AlarmDescription": "Scale-up if CPU > 60% for 10 minutes",
            "MetricName": "CPUUtilization",
            "Namespace": "AWS/EC2",
            "Statistic": "Average",
            "Period": "300",
            "EvaluationPeriods": "2",
            "Threshold": "60",
            "AlarmActions": [ { "Ref": "WebServerScaleUpPolicy" } ],
            "Dimensions": [
              {
                "Name": "AutoScalingGroupName",
                "Value": { "Ref": "WebServerAutoScalingGroup" }
              }
            ],
            "ComparisonOperator": "GreaterThanThreshold"
          }
        },
        "CPUAlarmLow": {
         "Type": "AWS::CloudWatch::Alarm",
         "Properties": {
            "AlarmDescription": "Scale-down if CPU < 60% for 10 minutes",
            "MetricName": "CPUUtilization",
            "Namespace": "AWS/EC2",
            "Statistic": "Average",
            "Period": "300",
            "EvaluationPeriods": "2",
            "Threshold": "60",
            "AlarmActions": [ { "Ref": "WebServerScaleDownPolicy" } ],
            "Dimensions": [
              {
                "Name": "AutoScalingGroupName",
                "Value": { "Ref": "WebServerAutoScalingGroup" }
              }
            ],
            "ComparisonOperator": "LessThanThreshold"
          }
        }
      },
      "Outputs": {
            "URL": {
                "Value": {
                    "Fn::Join": ["", ["http://", {"Fn::GetAtt": ["ElasticLoadBalancer", "DNSName" ]}]]
                },
                "Description" : "Elastic Load Balancer DNS name."
            }
        }
    }
  2. Validate the template by running the following command to check the syntax of the template:
    $ aws cloudformation validate-template
    --template-body file://F:\webserverautoscaling.json
    
  3. Execute the following command to create a stack called webserverautoscaling using the webserverautoscaling.json file:
    $ aws cloudformation create-stack 
    --stack-name webserverautoscaling
    --template-body file://F:\ webserverautoscaling.json
    --parameters ParameterKey=NameForNewInstances,ParameterValue=apacheserver-asg-pr-instance    
    
  4. Execute the following command to get all the resources descriptions for the stack:
    $ aws cloudformation describe-stack-resources 
    --stack-name webserverautoscaling
    
  5. Execute the following command to get the list of events for the stack:
    $ aws cloudformation describe-stack-events 
    --stack-name webserverautoscaling
    

How it works…

In the first step for creating a stack, we use a JSON-based template to declaratively specify the required AWS resources and their properties. In addition, we can also specify the dependencies between the resources, for example, we may want the instances to come up after the load balancer. We also use the template to specify data flows between the resources, for example, passing the database connection strings to the instances.

A template is divided into multiple sections—resources, parameters, mappings, and outputs. The resources section contains the list of all the resources and their specific properties required for the application. These include EC2 instances, RDS databases, security groups, and many more. The parameters section helps you specify the parameters that can be customized at the stack creation time. The parameter section in the template can help you set appropriate values for different environments, for example, you may want to have different number of instances for your test and production stacks, or you may choose to disable the Multi-AZ parameter for the RDS service in your testing environment. The mappings section allows you to perform conditional setting of properties, for example, you may want to have region-specific values for the properties. The final section is the outputs section. This section is used to return important data to the user based on the resources created. For example, we may want to pass the URL for a newly created website to the user. The output values get shown in the console.

We set the version number and provide a simple description for our template. In addition, we declaratively define the image, instance types, VPC, subnets, and so on, in the parameters section. For example, for the InstanceType parameter, we have a description for the parameter, the data type of the parameter is a string, the default value is m3.medium, and the allowed values for the parameter are specified as a list of instance types allowed.

Mappings in templates are like switch statements. Using the Fn::FindInMap function, you can get the values from the mappings by giving the key as input. We have not specified any mappings in our example. However, this section can be used to define mappings between region names and specific AMI IDs. For example, the region could be the one where the CloudFromation template is being executed. This region information can be referenced through a AWS::Region pseudo-parameter. The Fn::FindInMap function will then use the region name to get the appropriate AMI ID.

The AWS resources are defined in the resource section of the CloudFormation template. You can add one or more properties to the specified resources. Each resource has a logical name that you can use to reference the resource in the template. We have also declared an IAM security group, ELB, launch configuration, autoscaling group, and CloudWatch alarm resources. Each resource has its own set of properties to be specified. For example, in the case of the security group resource, we specify the type of resource and the ingress rules. We use the Ref function at several places, as we do not know the physical ID of the resource that will be created. This function will return the value of the property based on the actual physical ID of the resource that is created. The CloudFormation service displays the actual values of the resources as specified in the outputs section in the template after the stack is successfully created. We use the Join function to concatenate strings. Here, we concatenate the http:// string with the DNS name. The DNS name is retrieved using the GetAtt function on the appropriate resource.

In the next step, we validate the template, which includes checks for structure and API usage, JSON syntax, presence of circular dependencies, and so on. Validating the template can help save you a lot of time. However, if AWS CloudFormation fails to create the stack, then you can use the CloudFormation console to view the failure event and understand the reason for it. For debugging EC2 provisioning failures, you can view the cloud-init and cfn logs, or publish these logs to CloudWatch and view them in the AWS Management Console.

After creating and validating the template, you have to provide the template file path to the create-stack command to create the stack using the specified template. While creating the stack, we can pass specific parameters in the parameters section, for example, apacheserver-asg-pr-instance is the autoscaling group for our production environment.

The next command is very helpful in verifying the stack you just created. The response of the describe-stack command includes the stack name and ID, resource names, physical IDs, resource types, current status of the resources, and the descriptions of the resources. Finally, we execute the describe-stack-events command to retrieve all the stack-related events for our stack.

There's more…

Instead of executing the create-stack command, as specified in step 4, you can execute the following command to sends the stack event notifications to a Simple Notification Service (SNS) topic. Execute the following command to send the stack event notifications to a SNS topic called Test.

$ aws cloudformation create-stack 
--stack-name webserverautoscaling
--template-body file://F:\ webserverautoscaling.json
--parameters ParameterKey=NameForNewInstances,ParameterValue=apacheserver-asg-pr-instance    
--notification-arns 
"arn:aws:sns:ap-southeast-1:968336292411:Test"

Before running this command, you will need to create an SNS topic called Test. You can subscribe to this topic to receive e-mail notifications.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset