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

Jan 13, 2025 - 04:41
 0
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

  1. Define the input parameters you want your condition to evaluate.
  2. Define the condition by using the intrinsic condition functions.
  3. 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!