If you’ve ever wrote AWS CloudFormation template, you probably know that it can be a daunting task. Luckily, it can be much easier, if you use Python’s library “Troposphere”.
Troposphere let’s you create Python objects in place of CloudFormation elements, does some basic validation of your input and generates the JSON template for CloudFormation for you. It is much easier and cleaner to use that writing JSON templates manually.
To install troposphere, just run:
pip install troposphere
A very simple stack that creates a new VPC with private and public subnets in AZs A-C, can look like this:
from troposphere import Ref, Template, Tags, Join from troposphere.ec2 import VPC, Subnet, NetworkAcl, NetworkAclEntry, InternetGateway, \ VPCGatewayAttachment, RouteTable, Route, SubnetRouteTableAssociation, SubnetNetworkAclAssociation VPC_NETWORK = "172.21.0.0/16" VPC_PRIVATE_A = "172.21.1.0/24" VPC_PRIVATE_B = "172.21.2.0/24" VPC_PRIVATE_C = "172.21.3.0/24" VPC_PUBLIC_A = "172.21.128.0/24" VPC_PUBLIC_B = "172.21.129.0/24" VPC_PUBLIC_C = "172.21.130.0/24" t = Template() t.add_description("Stack creating a VPC") vpc = t.add_resource(VPC( "VPC", CidrBlock=VPC_NETWORK, InstanceTenancy="default", EnableDnsSupport=True, EnableDnsHostnames=False, Tags=Tags( Name=Ref("AWS::StackName") ) )) # internet gateway internetGateway = t.add_resource(InternetGateway( "InternetGateway", Tags=Tags( Name=Join("", [Ref("AWS::StackName"), "-gateway"]), ), )) gatewayAttachment = t.add_resource(VPCGatewayAttachment( "InternetGatewayAttachment", InternetGatewayId=Ref(internetGateway), VpcId=Ref(vpc) )) # public routing table publicRouteTable = t.add_resource(RouteTable( "PublicRouteTable", VpcId=Ref(vpc), Tags=Tags( Name=Join("-", [Ref("AWS::StackName"), "public-rt"]), ), )) privateRouteTable = t.add_resource(RouteTable( "PrivateRouteTable", VpcId=Ref(vpc), Tags=Tags( Name=Join("-", [Ref("AWS::StackName"), "private-rt"]), ), )) internetRoute = t.add_resource(Route( "RouteToInternet", DestinationCidrBlock="0.0.0.0/0", GatewayId=Ref(internetGateway), RouteTableId=Ref(publicRouteTable), DependsOn=gatewayAttachment.title )) # private subnetworks subnetPrivateA = t.add_resource(Subnet( "StackPrivateSubnetA", AvailabilityZone=Join("", [Ref("AWS::Region"), "a"]), CidrBlock=VPC_PRIVATE_A, MapPublicIpOnLaunch=False, Tags=Tags( Name=Join("", [Ref("AWS::StackName"), " private subnet A"]), ), VpcId=Ref(vpc) )) t.add_resource(SubnetRouteTableAssociation( "PrivateSubnetARouteTable", RouteTableId=Ref(privateRouteTable), SubnetId=Ref(subnetPrivateA) )) subnetPrivateB = t.add_resource(Subnet( "StackPrivateSubnetB", AvailabilityZone=Join("", [Ref("AWS::Region"), "b"]), CidrBlock=VPC_PRIVATE_B, MapPublicIpOnLaunch=False, Tags=Tags( Name=Join("", [Ref("AWS::StackName"), " private subnet B"]), ), VpcId=Ref(vpc) )) t.add_resource(SubnetRouteTableAssociation( "PrivateSubnetBRouteTable", RouteTableId=Ref(privateRouteTable), SubnetId=Ref(subnetPrivateB) )) subnetPrivateC = t.add_resource(Subnet( "StackPrivateSubnetC", AvailabilityZone=Join("", [Ref("AWS::Region"), "c"]), CidrBlock=VPC_PRIVATE_C, MapPublicIpOnLaunch=False, Tags=Tags( Name=Join("", [Ref("AWS::StackName"), " private subnet C"]), ), VpcId=Ref(vpc) )) t.add_resource(SubnetRouteTableAssociation( "PrivateSubnetCRouteTable", RouteTableId=Ref(privateRouteTable), SubnetId=Ref(subnetPrivateC) )) # public subnetworks subnetPublicA = t.add_resource(Subnet( "StackPublicSubnetA", AvailabilityZone=Join("", [Ref("AWS::Region"), "a"]), CidrBlock=VPC_PUBLIC_A, MapPublicIpOnLaunch=True, Tags=Tags( Name=Join("", [Ref("AWS::StackName"), " public subnet A"]), ), VpcId=Ref(vpc), )) t.add_resource(SubnetRouteTableAssociation( "PublicSubnetARouteTable", RouteTableId=Ref(publicRouteTable), SubnetId=Ref(subnetPublicA) )) subnetPublicB = t.add_resource(Subnet( "StackPublicSubnetB", AvailabilityZone=Join("", [Ref("AWS::Region"), "b"]), CidrBlock=VPC_PUBLIC_B, MapPublicIpOnLaunch=True, Tags=Tags( Name=Join("", [Ref("AWS::StackName"), " public subnet B"]), ), VpcId=Ref(vpc), )) t.add_resource(SubnetRouteTableAssociation( "PublicSubnetBRouteTable", RouteTableId=Ref(publicRouteTable), SubnetId=Ref(subnetPublicB) )) subnetPublicC = t.add_resource(Subnet( "StackPublicSubnetC", AvailabilityZone=Join("", [Ref("AWS::Region"), "c"]), CidrBlock=VPC_PUBLIC_C, MapPublicIpOnLaunch=True, Tags=Tags( Name=Join("", [Ref("AWS::StackName"), " public subnet C"]), ), VpcId=Ref(vpc) )) t.add_resource(SubnetRouteTableAssociation( "PublicSubnetCRouteTable", RouteTableId=Ref(publicRouteTable), SubnetId=Ref(subnetPublicC) )) # network ACL for private subnets privateNetworkAcl = t.add_resource(NetworkAcl( "PrivateNetworkAcl", VpcId=Ref(vpc), Tags=Tags( Name=Join("", [Ref("AWS::StackName"), "-private-nacl"]), ), )) t.add_resource(SubnetNetworkAclAssociation( "PrivateNetworkAAclAss", SubnetId=Ref(subnetPrivateA), NetworkAclId=Ref(privateNetworkAcl) )) t.add_resource(SubnetNetworkAclAssociation( "PrivateNetworkBAclAss", SubnetId=Ref(subnetPrivateB), NetworkAclId=Ref(privateNetworkAcl) )) t.add_resource(SubnetNetworkAclAssociation( "PrivateNetworkCAclAss", SubnetId=Ref(subnetPrivateC), NetworkAclId=Ref(privateNetworkAcl) )) t.add_resource(NetworkAclEntry( "PrivateNetworkAclEntryIngress", CidrBlock=VPC_NETWORK, Egress=False, NetworkAclId=Ref(privateNetworkAcl), Protocol=-1, RuleAction="allow", RuleNumber=200 )) t.add_resource(NetworkAclEntry( "PrivateNetworkAclEntryEgress", CidrBlock=VPC_NETWORK, Egress=True, NetworkAclId=Ref(privateNetworkAcl), Protocol=-1, RuleAction="allow", RuleNumber=200 )) print(t.to_json())
Save this as vpc_stack.py
and run python vpc_stack.py
. Output will be a JSON template that can be used for CloudFormation!
You can easily direct the output to a file, by running python vpc_stack.py > vpc_stack.json
. The JSON file can be uploaded directly to CloudFormation via CLI or AWS Management Console.
To find out more and for full documentation, see troposphere on github: https://github.com/cloudtools/troposphere.
For more examples of CloudFormation stacks with troposphere, check out our github repo: https://github.com/MysteriousCode/cloudformation-examples.
Vijay
Nice article, can you please provide a sample for nested cloud formation template
Maciej Walkowiak
Nice article!
Tiny little typo: python vpc_stack.py > vpc_stack.json rather than python vpc_stack.py > vpc_stack.py π
Paulina Budzon
Good eye! π Fixed.
Atul Kaulgud
Hi Paulina
I would like to prepare for AWS solution architect – professional exam. Could you please share any documents or reference material
Regards
Josephine
What about YAML?!
Paulina Budzon
Josephine, you can just use t.to_yaml instead of t.to_json now with trosposhere π