'Passing parameters of type List<AWS::EC2::Subnet::Id> to nested CloudFormation template

I'm trying to nest a CloudFormation template into another by using the AWS::CloudFormation::Stack resource type. The nested template has a parameter of type List<AWS::EC2::Subnet::Id>.

Individually, the nested template runs just fine. But when embedding AWS Console says Encountered unsupported property ELBSubnetList.

Changing the parameter's type to String/CommaSeparated list seems to be a workaround, as dicussed here but I'd loose the fancy UI when creating the template interactivly via AWS Console.

Do you have any idea how to pass the list of subnet ids as a paramter?

This is the embedded template:

{
    "AWSTemplateFormatVersion" : "2010-09-09",
    "Parameters" : {
        "ELBSubnetList" : {
            "Type" : "List<AWS::EC2::Subnet::Id>",
            "Description" : "Subnet List for Elastic Loadbalancer"
        },
        "ELBSecurityGroupList": {
            "Type": "List<AWS::EC2::SecurityGroup::Id>",
            "Description": "Security Group List for Elastic Loadbalancer"
        }
    },
    "Resources" : {
        "ELB" : {
            "Type" : "AWS::ElasticLoadBalancing::LoadBalancer",
            "Properties" : {
                "Subnets": { "Ref": "ELBSubnetList" },
                "CrossZone" : "true",
                "SecurityGroups": { "Ref": "ELBSecurityGroupList" },
                "LBCookieStickinessPolicy" : [ {
                    "PolicyName" : "CookieBasedPolicy",
                    "CookieExpirationPeriod" : "30"
                }],
                "Listeners" : [ {
                    "LoadBalancerPort" : "80",
                    "InstancePort" : "80",
                    "Protocol" : "HTTP",
                    "PolicyNames" : [ "CookieBasedPolicy" ]
                } ],
                "HealthCheck" : {
                    "Target" : "HTTP:80/wordpress/wp-admin/install.php",
                    "HealthyThreshold" : "2",
                    "UnhealthyThreshold" : "5",
                    "Interval" : "10",
                    "Timeout" : "5"
                }
            }
        }
    }
}

And the template that embedds:

{
    "AWSTemplateFormatVersion" : "2010-09-09",
    "Parameters": {
        "ChildTemplate": {
            "Type": "String",
            "Default": "https://s3.eu-central-1.amazonaws.com/cf-templates-xxxxxxxxxxx-eu-central-1/sample_child.template"
        },
        "ELBSubnetList" : {
            "Type" : "List<AWS::EC2::Subnet::Id>",
            "Description" : "Subnet List for Elastic Loadbalancer"
        },
        "ELBSecurityGroupList": {
            "Type": "List<AWS::EC2::SecurityGroup::Id>",
            "Description": "Security Group List for Elastic Loadbalancer"
        }
    },
    "Resources": {
        "Child": {
            "Type": "AWS::CloudFormation::Stack",
            "Properties": {
                "TemplateURL": { "Ref": "ChildTemplate" },
                "Parameters": {
                    "ELBSubnetList": { "Ref": "ELBSubnetList" },
                    "ELBSecurityGroupList": { "Ref": "ELBSecurityGroupList" }
                }
            }
        }
    }
}


Solution 1:[1]

Lists can be converted into Strings and vice versa. So the working invocation is

{
    "AWSTemplateFormatVersion" : "2010-09-09",
    "Parameters": {
        "ChildTemplate": {
            "Type": "String",
            "Default": "https://s3.eu-central-1.amazonaws.com/cf-templates-xxxxxxxxxxx-eu-central-1/sample_child.template"
        },
        "ELBSubnetList" : {
            "Type" : "List<AWS::EC2::Subnet::Id>",
            "Description" : "Subnet List for Elastic Loadbalancer"
        },
        "ELBSecurityGroupList": {
            "Type": "List<AWS::EC2::SecurityGroup::Id>",
            "Description": "Security Group List for Elastic Loadbalancer"
        }
    },
    "Resources": {
        "Child": {
            "Type": "AWS::CloudFormation::Stack",
            "Properties": {
                "TemplateURL": { "Ref": "ChildTemplate" },
                "Parameters": {
                    "ELBSubnetList": {"Fn::Join": [",", { "Ref": "ELBSubnetList" }]},
                    "ELBSecurityGroupList": {"Fn::Join": [",", { "Ref": "ELBSecurityGroupList" }]}
                }
            }
        }
    }
}

Solution 2:[2]

Successfully built in YAML using the following excerpt:

Parameters:
  pSubnetIDs:
    Description: The array of Subnet IDs for the Subnet group
    Type: List<AWS::EC2::Subnet::Id>
Resources:
  rDBSubnetGroup:
    Type: "AWS::RDS::DBSubnetGroup"
    Properties: 
      DBSubnetGroupDescription: The subnet group for the RDS instance
      SubnetIds: !Ref pSubnetIDs

I tried a bunch of variations of !Join and !Ref with no success. Turns out it is just straightforward !Ref of the list.

Solution 3:[3]

To convert a list of SubnetIds to a list of Strings, use a combination of both JOIN and SPLIT.

TLDR;
In YAML, add

!Split [',', !Join [',', !Ref SubnetIds]]

The full answer is 2 parts.

Part 1: JOIN

Here SubnetIds is a list of type Subnet.Id. JOIN will combine all IDs to be one string. For example, a list of subnet ids JOINed with , as the delimiter will be as follows:

[abc, def, hij] => "abc,def,hij".

Part 2: SPLIT

Now let's take the output from part 1, and SPLIT on the delimiter ,.

"abc,def,hij" => ["abc", "def", "hij"]


Here is an example of my use case with creating a Scheduled Task:

AWSTemplateFormatVersion: '2010-09-09'
Parameters
  SubnetIds:
    Type: List<AWS::EC2::Subnet::Id>
    Description: Select at least two subnets in your selected VPC.

  ScheduleTask:
    Type: AWS::Events::Rule
    Properties:
      Description: !Sub 'Trigger Sitemap Generation according to the specified schedule'
      ScheduleExpression: !Ref CronExpression
      State: ENABLED
      Targets:
      - Id: 'targetId'
        Arn: !GetAtt ECSCluster.Arn
        RoleArn: !GetAtt ECSEventsRole.Arn
        EcsParameters:
          TaskDefinitionArn: !Ref TaskDefinition
          TaskCount: 1
          LaunchType: 'FARGATE'
          PlatformVersion: 'LATEST'
          NetworkConfiguration:
            AwsVpcConfiguration:
              AssignPublicIp: ENABLED
              SecurityGroups:
                - !Ref SecurityGroup
              Subnets: !Split [',', !Join [',', !Ref SubnetIds]]

Solution 4:[4]

In YAML you need to "split" the Subnets by using Select. For example with two subnets :

!Join [",", [!Select [0, !Ref Subnets], !Select [1, !Ref Subnets]]]

Solution 5:[5]

Maybe help to another person: but for me works this:

In child template:

SubnetIds:
    Description: Choose which subnets should be deployed to
    Type: List<AWS::EC2::Subnet::Id>

but in the parent template

    SubnetIds:
        Description: Choose which subnets should be deployed to
        Type: CommaDelimitedList
        
        
    Stack:
      Type: AWS::CloudFormation::Stack
      Properties:
        TemplateURL: {s3}
        Parameters:
            SubnetIds: !Join [',', !Ref SubnetIds]

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Stephan
Solution 2 molz
Solution 3 d_coder
Solution 4 Simon M.
Solution 5 Marco Guerrero Alfaro