Customizable VPCs with CloudFormation Conditions
Using CloudFormation Conditions you can decide which resources to create/configure from your CloudFormation template. In this post, we are going to build a template that can create a VPC with private subnets and a NAT gateway or only public subnets depending on a parameter when creating the CloudFormation stack. Steps to create and use Conditions Define the input parameters you want your condition to evaluate. Define the condition by using the intrinsic condition functions. Declare the condition in resources or outputs you want to create. Let's dive into the example! Define the parameter and condition In the first part of our template, we'll define our parameter and condition. We associate the CreatePrivateResources condition with a value/option from our CreateNatGateway parameter. This enables us to do what has been discussed in the intro above. Parameters: CreateNatGateway: Type: String Description: "Need a NAT Gateway?" AllowedValues: - yes - no Default: yes Conditions: CreatePrivateResources: !Equals - !Ref CreateNatGateway - yes Define the public resources Here, we will define our VPC, internet gateway, public subnets, routes and route tables. These resources will always be created. This is because they will not have any condition associated with them. Resources: VPC: Type: AWS::EC2::VPC Properties: CidrBlock: "10.0.0.0/16" EnableDnsSupport: true EnableDnsHostnames: true Tags: - Key: Name Value: !Sub "${AWS::StackName}-VPC" # Public Resources InternetGateway: Type: AWS::EC2::InternetGateway Properties: Tags: - Key: Name Value: !Sub "${AWS::StackName}-InternetGateway" AttachGateway: Type: AWS::EC2::VPCGatewayAttachment Properties: VpcId: !Ref VPC InternetGatewayId: !Ref InternetGateway PublicSubnet1: Type: AWS::EC2::Subnet Properties: VpcId: !Ref VPC CidrBlock: "10.0.1.0/24" MapPublicIpOnLaunch: true AvailabilityZone: !Select [0, !GetAZs ""] Tags: - Key: Name Value: !Sub "${AWS::StackName}-PublicSubnet1" PublicSubnet2: Type: AWS::EC2::Subnet Properties: VpcId: !Ref VPC CidrBlock: "10.0.2.0/24" MapPublicIpOnLaunch: true AvailabilityZone: !Select [1, !GetAZs ""] Tags: - Key: Name Value: !Sub "${AWS::StackName}-PublicSubnet2" PublicRouteTable: Type: AWS::EC2::RouteTable Properties: VpcId: !Ref VPC Tags: - Key: Name Value: !Sub "${AWS::StackName}-PublicRouteTable" PublicRoute: Type: AWS::EC2::Route Properties: RouteTableId: !Ref PublicRouteTable DestinationCidrBlock: "0.0.0.0/0" GatewayId: !Ref InternetGateway PublicSubnet1RouteTableAssociation: Type: AWS::EC2::SubnetRouteTableAssociation Properties: SubnetId: !Ref PublicSubnet1 RouteTableId: !Ref PublicRouteTable PublicSubnet2RouteTableAssociation: Type: AWS::EC2::SubnetRouteTableAssociation Properties: SubnetId: !Ref PublicSubnet2 RouteTableId: !Ref PublicRouteTable Use the condition We now apply our condition to selected resources which creates a private environment for us, i.e. NatGateway, private subnets and routes etc. If we choose no at the prompt, these will not get created. We also control the display of outputs using the same conditions. # Private Resources NatGateway: Condition: CreatePrivateResources Type: AWS::EC2::NatGateway Properties: AllocationId: !GetAtt EIPNatGateway.AllocationId SubnetId: !Ref PublicSubnet1 Tags: - Key: Name Value: !Sub "${AWS::StackName}-NatGateway" EIPNatGateway: Condition: CreatePrivateResources Type: AWS::EC2::EIP Properties: Domain: vpc Tags: - Key: Name Value: !Sub "${AWS::StackName}-EIPNatGateway" PrivateSubnet1: Condition: CreatePrivateResources Type: AWS::EC2::Subnet Properties: VpcId: !Ref VPC CidrBlock: "10.0.3.0/24" MapPublicIpOnLaunch: false AvailabilityZone: !Select [0, !GetAZs ""] Tags: - Key: Name Value: !Sub "${AWS::StackName}-PrivateSubnet1" PrivateSubnet2: Condition: CreatePrivateResources Type: AWS::EC2::Subnet Properties: VpcId: !Ref VPC CidrBlock: "10.0.4.0/24" MapPublicIpOnLaunch: false AvailabilityZone: !Select [1, !GetAZs ""] Tags: - Key: Name Value: !Sub "${AWS::StackName}-PrivateSubnet2" PrivateRouteTable: Condition: CreatePrivateResources Type: AWS::EC2::RouteTable Properties: VpcId: !Ref VPC Tags: - Key: Name Value: !Sub "${AWS::StackName}-PrivateRouteTable" PrivateRoute: Condition: CreatePrivateResources Type: AWS::EC2::Route Properties: RouteTableId: !Re
Using CloudFormation Conditions you can decide which resources to create/configure from your CloudFormation template.
In this post, we are going to build a template that can create a VPC with private subnets and a NAT gateway or only public subnets depending on a parameter when creating the CloudFormation stack.
Steps to create and use Conditions
- Define the input parameters you want your condition to evaluate.
- Define the condition by using the intrinsic condition functions.
- Declare the condition in resources or outputs you want to create.
Let's dive into the example!
Define the parameter and condition
In the first part of our template, we'll define our parameter and condition.
We associate the CreatePrivateResources condition with a value/option from our CreateNatGateway parameter. This enables us to do what has been discussed in the intro above.
Parameters:
CreateNatGateway:
Type: String
Description: "Need a NAT Gateway?"
AllowedValues:
- yes
- no
Default: yes
Conditions:
CreatePrivateResources: !Equals
- !Ref CreateNatGateway
- yes
Define the public resources
Here, we will define our VPC, internet gateway, public subnets, routes and route tables. These resources will always be created. This is because they will not have any condition associated with them.
Resources:
VPC:
Type: AWS::EC2::VPC
Properties:
CidrBlock: "10.0.0.0/16"
EnableDnsSupport: true
EnableDnsHostnames: true
Tags:
- Key: Name
Value: !Sub "${AWS::StackName}-VPC"
# Public Resources
InternetGateway:
Type: AWS::EC2::InternetGateway
Properties:
Tags:
- Key: Name
Value: !Sub "${AWS::StackName}-InternetGateway"
AttachGateway:
Type: AWS::EC2::VPCGatewayAttachment
Properties:
VpcId: !Ref VPC
InternetGatewayId: !Ref InternetGateway
PublicSubnet1:
Type: AWS::EC2::Subnet
Properties:
VpcId: !Ref VPC
CidrBlock: "10.0.1.0/24"
MapPublicIpOnLaunch: true
AvailabilityZone: !Select [0, !GetAZs ""]
Tags:
- Key: Name
Value: !Sub "${AWS::StackName}-PublicSubnet1"
PublicSubnet2:
Type: AWS::EC2::Subnet
Properties:
VpcId: !Ref VPC
CidrBlock: "10.0.2.0/24"
MapPublicIpOnLaunch: true
AvailabilityZone: !Select [1, !GetAZs ""]
Tags:
- Key: Name
Value: !Sub "${AWS::StackName}-PublicSubnet2"
PublicRouteTable:
Type: AWS::EC2::RouteTable
Properties:
VpcId: !Ref VPC
Tags:
- Key: Name
Value: !Sub "${AWS::StackName}-PublicRouteTable"
PublicRoute:
Type: AWS::EC2::Route
Properties:
RouteTableId: !Ref PublicRouteTable
DestinationCidrBlock: "0.0.0.0/0"
GatewayId: !Ref InternetGateway
PublicSubnet1RouteTableAssociation:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
SubnetId: !Ref PublicSubnet1
RouteTableId: !Ref PublicRouteTable
PublicSubnet2RouteTableAssociation:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
SubnetId: !Ref PublicSubnet2
RouteTableId: !Ref PublicRouteTable
Use the condition
We now apply our condition to selected resources which creates a private environment for us, i.e. NatGateway, private subnets and routes etc. If we choose no at the prompt, these will not get created.
We also control the display of outputs using the same conditions.
# Private Resources
NatGateway:
Condition: CreatePrivateResources
Type: AWS::EC2::NatGateway
Properties:
AllocationId: !GetAtt EIPNatGateway.AllocationId
SubnetId: !Ref PublicSubnet1
Tags:
- Key: Name
Value: !Sub "${AWS::StackName}-NatGateway"
EIPNatGateway:
Condition: CreatePrivateResources
Type: AWS::EC2::EIP
Properties:
Domain: vpc
Tags:
- Key: Name
Value: !Sub "${AWS::StackName}-EIPNatGateway"
PrivateSubnet1:
Condition: CreatePrivateResources
Type: AWS::EC2::Subnet
Properties:
VpcId: !Ref VPC
CidrBlock: "10.0.3.0/24"
MapPublicIpOnLaunch: false
AvailabilityZone: !Select [0, !GetAZs ""]
Tags:
- Key: Name
Value: !Sub "${AWS::StackName}-PrivateSubnet1"
PrivateSubnet2:
Condition: CreatePrivateResources
Type: AWS::EC2::Subnet
Properties:
VpcId: !Ref VPC
CidrBlock: "10.0.4.0/24"
MapPublicIpOnLaunch: false
AvailabilityZone: !Select [1, !GetAZs ""]
Tags:
- Key: Name
Value: !Sub "${AWS::StackName}-PrivateSubnet2"
PrivateRouteTable:
Condition: CreatePrivateResources
Type: AWS::EC2::RouteTable
Properties:
VpcId: !Ref VPC
Tags:
- Key: Name
Value: !Sub "${AWS::StackName}-PrivateRouteTable"
PrivateRoute:
Condition: CreatePrivateResources
Type: AWS::EC2::Route
Properties:
RouteTableId: !Ref PrivateRouteTable
DestinationCidrBlock: "0.0.0.0/0"
NatGatewayId: !Ref NatGateway
PrivateSubnet1RouteTableAssociation:
Condition: CreatePrivateResources
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
SubnetId: !Ref PrivateSubnet1
RouteTableId: !Ref PrivateRouteTable
PrivateSubnet2RouteTableAssociation:
Condition: CreatePrivateResources
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
SubnetId: !Ref PrivateSubnet2
RouteTableId: !Ref PrivateRouteTable
Outputs:
VPCId:
Description: "VPC ID"
Value: !Ref VPC
PublicSubnet1Id:
Description: "Public Subnet 1 ID"
Value: !Ref PublicSubnet1
PublicSubnet2Id:
Description: "Public Subnet 2 ID"
Value: !Ref PublicSubnet2
PrivateSubnet1Id:
Condition: CreatePrivateResources
Description: "Private Subnet 1 ID"
Value: !Ref PrivateSubnet1
PrivateSubnet2Id:
Condition: CreatePrivateResources
Description: "Private Subnet 2 ID"
Value: !Ref PrivateSubnet2
NatGatewayId:
Condition: CreatePrivateResources
Description: "NAT Gateway ID"
Value: !Ref NatGateway
You can now customize your VPC creation just by changing a parameter.
Thanks for reading. Happy Building!