In the AWS world, Infrastructure as code is not a new concept but a hot topic as lot of improvisation had happened in this area.
After working with CloudFormation templates for a while, one can notice several shortcomings that make templates long, clunky, and nigh unreadable. So what are the alternatives from a python developer lens: Troposphere and AWS CDK
The Troposphere python library allows for easier creation of the Aamzon CloudFormation JSON by writing Python code to describe the AWS resources. This effectively allows you to programmatically define your infrastructure without becoming as limited as we are with plain CloudFormation.
Let' install it:
pip install troposphere
The Troposphere team has some great examples in their GitHub repository. I suggest taking a look there and working from their examples.
CloudFormation, is a managed service by AWS: the user must simply write a YAML or JSON file describing all the infrastructure upload it on S3 or directly to Cloudformation and the service will take care of running it safely and statefully.
However, CloudFormation has its own drawbacks: YAML files are often very verbose and difficult to write and debug and do not support advanced logic and loops.
Let’s look at the raw CloudFormation template to create VPC:
Description: AWS CloudFormation Template to create a VPC Parameters: SftpCidr: Description: SftpCidr Type: String Resources: SftpVpc: Properties: CidrBlock: !Ref 'SftpCidr' EnableDnsHostnames: 'true' EnableDnsSupport: 'true' Type: AWS::EC2::VPC RouteTablePrivate: Properties: VpcId: !Ref 'SftpVpc' Type: AWS::EC2::RouteTable PrivateSubnet1: Properties: AvailabilityZone: !Select - 0 - !GetAZs Ref: AWS::Region CidrBlock: !Select - 4 - !Cidr - !GetAtt 'SftpVpc.CidrBlock' - 16 - 8 MapPublicIpOnLaunch: 'false' VpcId: !Ref 'SftpVpc' Type: AWS::EC2::Subnet PrivateSubnet2: Properties: AvailabilityZone: !Select - 1 - !GetAZs Ref: AWS::Region CidrBlock: !Select - 5 - !Cidr - !GetAtt 'SftpVpc.CidrBlock' - 16 - 8 MapPublicIpOnLaunch: 'false' VpcId: !Ref 'SftpVpc' Type: AWS::EC2::Subnet PrivateSubnet3: Properties: AvailabilityZone: !Select - 2 - !GetAZs Ref: AWS::Region CidrBlock: !Select - 6 - !Cidr - !GetAtt 'SftpVpc.CidrBlock' - 16 - 8 MapPublicIpOnLaunch: 'false' VpcId: !Ref 'SftpVpc' Type: AWS::EC2::Subnet SubnetPrivateToRouteTableAttachment1: Properties: RouteTableId: !Ref 'RouteTablePrivate' SubnetId: !Ref 'PrivateSubnet1' Type: AWS::EC2::SubnetRouteTableAssociation SubnetPrivateToRouteTableAttachment2: Properties: RouteTableId: !Ref 'RouteTablePrivate' SubnetId: !Ref 'PrivateSubnet2' Type: AWS::EC2::SubnetRouteTableAssociation SubnetPrivateToRouteTableAttachment3: Properties: RouteTableId: !Ref 'RouteTablePrivate' SubnetId: !Ref 'PrivateSubnet3' Type: AWS::EC2::SubnetRouteTableAssociation
Troposphere is really simple: it is just a Python DSL which maps CloudFormation Entities (all of them!) to Python classes and the other way round. This gives us a very simple way to create a template that looks exactly like we want but is generated through a high level easily maintainable language. Furthermore, Python IDEs will help us fixing problems without even running the YAML template and the compilation step to YAML will break if we create inconsistent references. The python troposphere script which generated the script is the following:
import troposphere.ec2 as vpc template = Template() template.set_description("AWS CloudFormation Template to create a VPC") sftp_cidr = template.add_parameter( Parameter('SftpCidr', Type='String', Description='SftpCidr') ) vpc_sftp = template.add_resource(vpc.VPC( 'SftpVpc', CidrBlock=Ref(sftp_cidr), EnableDnsSupport=True, EnableDnsHostnames=True, )) private_subnet_route_table = template.add_resource(vpc.RouteTable( 'RouteTablePrivate', VpcId=Ref(vpc_sftp) )) for ii in range(3): private_subnet = template.add_resource(vpc.Subnet( 'PrivateSubnet' + str(ii + 1), VpcId=Ref(vpc_sftp), MapPublicIpOnLaunch=False, AvailabilityZone=Select(ii, GetAZs(Ref(AWS_REGION))), CidrBlock=Select(ii + 4, Cidr(GetAtt(vpc_sftp, 'CidrBlock'), 16, 8)) )) private_subnet_attachment = template.add_resource(vpc.SubnetRouteTableAssociation( 'SubnetPrivateToRouteTableAttachment' + str(ii + 1), SubnetId=Ref(private_subnet), RouteTableId=Ref(private_subnet_route_table) )) print(template.to_yaml())
The code is readily readable and understandable even if it was automatically generated by a troposphere based script. As can immediately be seen most of the code is duplicated since we created 3 subnets with relative attachments to a routing table.
Running this script after installing Troposphere (pip install troposphere) will print the CF YAML shown above. As you can see the python code is much more compact and easy to understand. Furthermore, since Troposphere maps all the native cloudformation YAML functions (e.g. Ref, Join, GettAtt, etc.) we don’t even need to learn anything new: every existing CF template can easily be converted in a Troposphere template.
Troposphere is comparable to AWS CloudFormation templates, but in Python - offering the features of a programming language. Troposphere is a very simple way to reap all the advantages of CloudFormation together with the abstraction level provided by a modern programming language and it greatly simplifies CloudFormation code development and deployments.
In next part, I'll explain about AWS CDK.