AWS CLOUDFORMATION All Together – Part 1

AWS Cloudformation

AWS Cloud Formation has so many reference on Internet and many of them are very helpful. I have tried to compile some of the information scattered all across in a single blog.

AWS Cloud Formation has so many reference on Internet and many of them are very helpful but what I found while going through them is that you have to go through 100s of them to consolidate a single template. So finally decided to put most of things here.   Few important points worth considering while working on complex templates   1) What I learned is that do not try to create complete Stack from a single template because you will end up with a template with 2k lines or more and if the template has some error then it becomes hard to sort it out. So, it's better to divide the Stack in Logical Components then create Cloud Formation Template for them. eg: VPC , Subnets and NAT Gateway DB Subnet Group, RDS, Replica and CloudWatch Alarms,   2) Tags are very important as it helps in filtering the resources for Costs and other   3) Magic of Parameters : Always try to pass variables from Parameters. This helps to make the template Universal.   For start let's create a simple template to launch an EC2 Instance.   The template will be divided in 3 Parts.

  1. Parameters
  2. Resources
  3. Output
  1. Parameters

The Values which you need to give while launching an Instance like VPC ID, AMI, Role, Subnet, Keypair name etc. Keeping these values in Parameters help in making the template universal.

Tip: Always create your AWS Resources with proper tags. Leaving the tagging part for later is time consuming activity.   Types:- "VPC" : { "Description" : "The VPC in which you want to Launch your EC2", "Type" : "AWS::EC2::VPC::Id" }

In Parameters Type, always specify “Existing AWS values” if present.

Existing AWS values that are in the template user's account. You can specify the following AWS-specific types: Few examples are below.   AWS::EC2::AvailabilityZone::Name An Availability Zone, such as us-west-2a. AWS::EC2::Image::Id An Amazon EC2 image ID, such as ami-ff527ecf. Note that the AWS CloudFormation console won't show a drop-down list of values for this parameter type. AWS::EC2::Instance::Id An Amazon EC2 instance ID, such as i-1e731a32. AWS::EC2::KeyPair::KeyName An Amazon EC2 key pair name. AWS::EC2::SecurityGroup::GroupName An EC2-Classic or default VPC security group name, such as my-sg-abc. AWS::EC2::SecurityGroup::Id A security group ID, such as sg-a123fd85. AWS::EC2::Subnet::Id A subnet ID, such as subnet-123a351e. AWS::EC2::Volume::Id An Amazon EBS volume ID, such as vol-3cdd3f56. AWS::EC2::VPC::Id A VPC ID, such as vpc-a123baa3. AWS::Route53::HostedZone::Id An Amazon Route 53 hosted zone ID, such as Z23YXV4OVPL04A.

  1. Resources

The AWS Components which we want to create from the template. So here I am creating EC2 Security Group and EC2 Instance.

  1. Outputs

This the part where you can get the Ids or AWS Resources Values which are created from the template.

So What's the use of this OUTPUTS? Output does not seems to be a important in simple stack creation but if use a Nested Template where 2nd Resource is dependent on the values from 1st Resource then this comes in picture.   Finally here is my template which will create EC2 Security Group and Launch EC2 Instance.

{ 
“AWSTemplateFormatVersion”:“2016-03-28”,
“Description”:“This Template will create EC2 INSTANCE and Security group”,
“Parameters”:{  
  “TagValue1”:{  
    “Description”:“The Project Name “,
    “Type”:“String”
  },
  “TagValue2”:{  
    “Description”:“The Environment name”,
    “Type”:“String”,
    “AllowedValues”:[  
      “Development”,
      ”Staging”,
      ”Production”
    ]
  },
  “TagValue3”:{  
    “Description”:“The EC2 Instance Name”,
    “Type”:“String”
  },
  “TagValue4”:{  
    “Description”:“The Server Name”,
    “Type”:“String”
  },
  “VPC”:{  
    “Description”:“The VPC in which you want to Launch your EC2”,
    “Type”:    “AWS::    EC2::    VPC::Id”
  },
  “AMI”:{  
    “Description”:“The AMI that you’ll use for your EC2”,
    “Type”:    “AWS::    EC2::    Image::Id”
  },
  “IAMROLE”:{  
    “Description”:“The IAM you’ll use for your EC2”,
    “Type”:“String”
  },
  “Subnet”:{  
    “Description”:“The Subnet that you’ll use for your EC2”,
    “Type”:    “AWS::    EC2::    Subnet::Id”
  },
  “KeyPairName”:{  
    “Description”:“Name of an existing Amazon EC2 KeyPair for SSH access to the Web Server”,
    “Type”:    “AWS::    EC2::    KeyPair::KeyName”,
    “Default”:“my-key”
  },
  “InstanceClass”:{  
    “Description”:“EC2 instance type”,
    “Type”:“String”,
    “Default”:“t2.micro”,
    “AllowedValues”:[  
      “t2.micro”,
      ”t2.medium”,
      ”t2.small”,
      ”t2.large”,
      ”m4.large”,
      ”m4.xlarge”,
      ”m4.2xlarge”,
      “m4.4xlarge”,
      ”m4.10xlarge”,
      ”m3.medium”,
      ”m3.large”,
      ”m3.xlarge”,
      ”m3.2xlarge”,
      “c4.large”,
      ”c4.xlarge”,
      ”c4.2xlarge”,
      ”c4.4xlarge”,
      ”c4.8xlarge”,
      ”c3.large”,
      “c3.xlarge”,
      ”c3.2xlarge”,
      ”c3.4xlarge”,
      ”c3.8xlarge”
    ],
    “ConstraintDescription”:“must be a valid EC2 instance type.”
  }
},
“Resources”:{  
  “EC2SecurityGroup”:{  
    “Type”:    “AWS::    EC2::SecurityGroup”,
    “Properties”:{  
      “GroupDescription”:“SecurityGroup”,
      “VpcId”:{  
        “Ref”:“VPC”
      },
      “SecurityGroupIngress”:[  
        {  
          “IpProtocol”:“tcp”,
          “FromPort”:“22”,
          “ToPort”:“22”,
          “CidrIp”:“0.0.0.0/32”
        },
        {  
          “IpProtocol”:“tcp”,
          “FromPort”:“80”,
          “ToPort”:“80”,
          “CidrIp”:“0.0.0.0/32”
        },
        {  
          “IpProtocol”:“tcp”,
          “FromPort”:“443”,
          “ToPort”:“443”,
          “CidrIp”:“0.0.0.0/32”
        }
      ]
    }
  },
  “Ec2Instance”:{  
    “Type”:    “AWS::    EC2::Instance”,
    “Properties”:{  
      “ImageId”:{  
        “Ref”:“AMI”
      },
      “InstanceType”:{  
        “Ref”:“InstanceClass”
      },
      “IamInstanceProfile”:{  
        “Ref”:“IAMROLE”
      },
      “KeyName”:{  
        “Ref”:“KeyPairName”
      },
      “SecurityGroupIds”:[  
        {  
          “Ref”:“EC2SecurityGroup”
        }
      ],
      “SubnetId”:{  
        “Ref”:“Subnet”
      },
      “Tags”:[  
        {  
          “Key”:“Project”,
          “Value”:{  
            “Ref”:“TagValue1”
          }
        },
        {  
          “Key”:“Environment”,
          “Value”:{  
            “Ref”:“TagValue2”
          }
        },
        {  
          “Key”:“Name”,
          “Value”:{  
            “Ref”:“TagValue3”
          }
        },
        {  
          “Key”:“Server”,
          “Value”:{  
            “Ref”:“TagValue4”
          }
        }
      ],
      “Tenancy”:“default”
    }
  }
},
“Outputs”:{  
  “InstanceId”:{  
    “Description”:“InstanceId of the newly created EC2 instance”,
    “Value”:{  
      “Ref”:“Ec2Instance”
    }
  },
  “AZ”:{  
    “Description”:“Availability Zone of the newly created EC2 instance”,
    “Value”:{  
      “Fn::      GetAtt”:[  
        “Ec2Instance”,
        “AvailabilityZone”
      ]
    }
  },
  “PublicIP”:{  
    “Description”:“Public IP address of the newly created EC2 instance”,
    “Value”:{  
      “Fn::      GetAtt”:[  
        “Ec2Instance”,
        “PublicIp”
      ]
    }
  },
  “PrivateIP”:{  
    “Description”:“Private IP address of the newly created EC2 instance”,
    “Value”:{  
      “Fn::      GetAtt”:[  
        “Ec2Instance”,
        “PrivateIp”
      ]
    }
  }
}
}

From CLI:


aws cloudformation create-stack --stack-name MY-FIRST-STACK --template-body file:///file-path.json --parameters ParameterKey=AMI,ParameterValue=ami-xxx ParameterKey=IAMROLE,ParameterValue=my-role ParameterKey=InstanceClass,ParameterValue=t2.micro ParameterKey=KeyPairName,ParameterValue=my-key ParameterKey=Subnet,ParameterValue=sunrt-xxxxx ParameterKey=VPC,ParameterValue=vpc-xxxx ParameterKey=TagValue1,ParameterValue=MyProject ParameterKey=TagValue2,ParameterValue=Developmet ParameterKey=TagValue3,ParameterValue=MyEc2 ParameterKey=TagValue4,ParameterValue=WebServer

Conclusion

AWS CLOUDFORMATION helps you implement Infra as a Code. Here is a blog to describe how you can write a high-quality CloudFormation template.

Related Blogs