Search code examples
amazon-web-servicesaws-cloudformationamazon-vpc

Make VPC creation optional


I am trying to make VPC creation conditional in my cloudformation script. For example, if there is a VPC id provided in the parameters, then I want to create all resources in this VPC, otherwise to create a new one.

The problem starts when I want to reuse an existing VPC, but I have no way of deducing the subnets, which I need for one of my resources. So I suppose, I have to provide them as parameters. But if I provide them as parameters, in the case where I want to create a new VPC, it complains because the list of subnet ids is empty and it must be a valid one.

The error is Parameter validation failed: parameter value for parameter name VpcPrivateSubnetIds does not exist. Rollback requested by user. It is impossible to give any dummy value. Any ideas how to achieve this?

Here is my CF script:

VpcId:
  Type: String
  Description: Give the VPC id if you want to use an existing one. Leave empty for creating a new one.

VpcPublicSubnetIds:
  Type: List<AWS::EC2::Subnet::Id>
  Description: List of 3 public SubnetIds for the given VPC. 

VpcPrivateSubnetIds:
  Type: List<AWS::EC2::Subnet::Id>
  Description: List of 3 private SubnetIds for the given VPC. 


Conditions:
  CreateVPC: !Equals [ !Ref VpcId, ""]


Resources: 
  (...)
    Properties:
      PrivateSubnetIds: !If
        - CreateVPC
        - !GetAtt VPCStack.Outputs.PrivateSubnets
        - !Join [',', [!Select [0, !Ref VpcPrivateSubnetIds], !Select [1, !Ref VpcPrivateSubnetIds], !Select [2, !Ref VpcPrivateSubnetIds]]]
      PublicSubnetIds: !If
        - CreateVPC
        - !GetAtt VPCStack.Outputs.PublicSubnets
        - !Join [',', [!Select [0, !Ref VpcPublicSubnetIds], !Select [1, !Ref VpcPublicSubnetIds], !Select [2, !Ref VpcPublicSubnetIds]]]

Solution

  • One solution could be to treat the subnetId parameters as string, which can then be left as empty. (but user will have to manually type in the subnet id list when VPC is present).

    In case the list is not empty (existing VPC to be used), use Cloudformation custom resource lambda to turn string (comma separated) into list & return to cloudformation for use in resource creation. So your stack would look something like

    Parameters:
      VpcId:
        Type: String
        Description: Give the VPC id if you want to use an existing one. Leave empty for creating a new one.
      VpcPublicSubnetIds:
        Type: String
        Description: List of 3 public SubnetIds for the given VPC.
        Default: ''
      VpcPrivateSubnetIds:
        Type: String
        Description: List of 3 private SubnetIds for the given VPC.
        Default: ''
    Conditions:
      CreateVPC: !Equals [ !Ref VpcId, ""]
      CreateList: !Not [!Equals [ !Ref VpcId, ""]]
    Resources:
      CreateList:
        Type: AWS::CloudFormation::CustomResource
        Condition: CreateList
        Properties:
          ServiceToken:<some token>
          Public: !Ref VpcPublicSubnetIds
          Private: !Ref VpcPrivateSubnetIds
      SomeResource:
        Properties:
          PrivateSubnetIds: !If
            - CreateVPC
            - !GetAtt VPCStack.Outputs.PrivateSubnets
            - !GetAtt CreateList.PrivateSubnetIds
          PublicSubnetIds: !If
            - CreateVPC
            - !GetAtt VPCStack.Outputs.PublicSubnets
            - !GetAtt CreateList.PublicSubnetIds
    

    Please note I have not validated this script, so you may have to make some corrections.