Introducing Troposphere: How to create Cloud formation templates programmatically

We will first install the troposphere library:

$ pip install troposphere

Once the installation is done, you can then create a new file called helloworld-cf- template.py .

We will start our file by importing a number of definitions from the troposphere module:

"""Generating CloudFormation template."""

from troposphere import (
  Base64,
  ec2,
  GetAtt,
  Join,
  Output,
  Parameter,
  Ref,
  Template,
)

From a code standpoint, the first thing we will do is initialize a Template variable. By the end of our script, the template will contain the entire description of our infrastructure and we will be able to simply print its output to get our CloudFromation template:

t = Template()

Throughout this book, we will create and run concurrently several CloudFormation templates. To help us identify what’s in a given stack, we have the ability to provide a description. After the creation of the template, add the description as follow:

t.add_description("Effective DevOps in AWS: HelloWorld web application")

When we launched EC2 instances using the web command line interface, we selected which key pair to use in order to gain SSH access to the host. In order to not lose this ability, the first thing our template will have is a parameter to offer the CloudFormation user the ability to select which key pair to use when launching the EC2 instance.

To do that, we are going to create a Parameter object, and initialize it by providing an identifier, a description, a parameter type, a description and a constraint description to help to make the right decision

when we launch the stack. In order for this parameter to exist in our final template, we will also use the add_paramter() function defined in the template class:

t.add_parameter(Parameter(
   "KeyPair",
   Description="Name of an existing EC2 KeyPair to SSH",
   Type="AWS::EC2::KeyPair::KeyName",
   ConstraintDescription="must be the name of an existing EC2 KeyPair.",
))

The next thing we will look at is the security group. We will proceed exactly as we did for our KeyPair parameter.

ApplicationPort = 3000 

t.add_resource(ec2.SecurityGroup(
   "SecurityGroup",
   GroupDescription="Allow SSH and TCP/{} access".format(ApplicationPort),
   SecurityGroupIngress=[
   ec2.SecurityGroupRule(
     IpProtocol="tcp",
     FromPort="22",
     ToPort="22",
     CidrIp="0.0.0.0/0",
   ),
   ec2.SecurityGroupRule(
     IpProtocol="tcp",
     FromPort=ApplicationPort,
     ToPort=ApplicationPort,
     CidrIp="0.0.0.0/0",
  ),
 ],
))

In our next section, we will replace the need to log on to our EC2 instance and install by hand the helloworld.js file and its init scripts. To do so, we will take advantage of the UserData feature that EC2 offers.

When you create an EC2 instance, you have the ability through the UserData optional parameter to provide a set of commands to run once the virtual machine has spawned up.

user_data = Base64(Join('\n', [
 "#!/bin/bash",
 "sudo yum install --enablerepo=epel -y nodejs",
 "wget http://bit.ly/2vESNuc -O /home/ec2-user/helloworld.js",
 "wget http://bit.ly/2vVvT18 -O /etc/init/helloworld.conf",
 "start helloworld"
]))

We will now focus on the main resource of our template, our EC2 instance. The creation of the instance requires providing a name for identifying the resource, an image ID, an instance type, a security group, the keypair to use for the SSH access, and the user data.

In order to keep things simple, we will hard code the AMI ID ( ami-a4c7edb2 ) and instance type ( t2.micro ).

The remaining information needed to create our EC2 instances is the security group information and the keypair name, which we collected previously by defining a parameter and a resource. In CloudFormation, you can reference pre-existing subsections of your template by using the keyword Ref .

In Troposphere, this is done by calling the Ref() function. As before, we will add the resulting output to our template with the help of the add_resource function:

t.add_resource(ec2.Instance(
  "instance",
  ImageId="ami-a4c7edb2",
  InstanceType="t2.micro",
  SecurityGroups=[Ref("SecurityGroup")],
  KeyName=Ref("KeyPair"),
  UserData=user_data,
))

In the last section of our script, we will focus on producing the Outputs section of the template that gets populated when CloudFormation creates a stack.

This selection allows you to print out useful information that was computed during the launch of the stack. In our case, there are two useful pieces information, the URL to access our web application and the public IP address of the instance so that we can SSH into it if we want to.

In order to retrieve such information, CloudFormation uses the function Fn::GetAtt . In troposphere this is translated into using the GetAttr() function:

t.add_output(Output(
  "InstancePublicIp",
  Description="Public IP of our instance.",
  Value=GetAtt(instance, "PublicIp"),
))

t.add_output(Output(
  "WebUrl",
  Description="Application endpoint",
  Value=Join("", [
      "http://", GetAtt(instance, "PublicDnsName"), ":", ApplicationPort
  ]),
))

At that point, we can make our script output the final result of the template we generated:

print t.to_json()

That’s it. You can check this GitHub repo.

Thanks for reading, happy coding.

14 Shares:
You May Also Like