13

I'm using CloudFormation to deploy an ELB to a pre-existing VPC which has pre-existing Subnets. I've listed the subnets in a Mappings section of the template and select the mapping based on the Environment parameter. This works fine if the Mapping is for a single value, but I want the Mapping to return a list as I want the ELB to be multi-AZ. I've tried various combinations of the example below, but I always get the same error Value of property Subnets must be of type List of String. Is there a way that FindInMap can return a list of values?

Example ELB Config

"ElasticLoadBalancerAPI": {
            "Properties": {
                "ConnectionDrainingPolicy": {
                    "Enabled": true,
                    "Timeout": 30
                },
                "ConnectionSettings": {
                    "IdleTimeout": 60
                },
                "CrossZone": "false",
                "Subnets" : [
                    {
                        "Fn::FindInMap": [
                            "AWSEnv2PublicSubnets",
                            {
                                "Ref": "Env"
                            },
                            "subList"
                        ]
                    }
                ],
                "SecurityGroups" : [ { "Ref" : "LoadBalancerSecurityGroup" }],
etc...

Example Parameters

"Parameters": {
  "Env": {
            "Description": "Environment",
            "Type": "String",
            "Default": "DEV",
            "AllowedValues": [
                "DEV",
                "TEST"
            ]
        }
}

Example Mappings

"Mappings": {
  "AWSEnv2PublicSubnets": {
            "DEV": {
                "subList": ["subnet-1111","subnet-2222","subnet-3333"]
            },
            "TEST": {
                "subList": ["subnet-4444"]
            }
        }
}
user3188040
  • 297
  • 1
  • 3
  • 8

2 Answers2

15

Using the Split function works:

JSON

"Mappings": {
   "AWSEnv2PublicSubnets": {
        "DEV": {
            "subList": "subnet-1111,subnet-2222,subnet-3333"
        }
    }
}

then:

"Subnets" : { 
  "Fn::Split" : [
    ",", 
    { "Fn::FindInMap": [
      "AWSEnv2PublicSubnets",
      { "Ref": "Env" },
      "subList"
    ] }
  ]
}

YAML

Mappings: 
  AWSEnv2PublicSubnets:
    DEV:
      subList: subnet-1111,subnet-2222,subnet-3333

then:

Subnets: !Split [",", !FindInMap [ AWSEnv2PublicSubnets, !Ref Env, subList] ]

I used a comma (,) as my separator character, but you can use anything you want so long as it's not also used as a part of the value.

gregmac
  • 1,459
  • 3
  • 18
  • 27
  • I did manage to get it working with an answer almost identical. I had the FindInMap inside a set of braces. But I'll mark yours as the correct answer. `"Mappings" : { "AWSEnv2PublicSubnets": { "DEV": { "subList" : "subnet-1111,subnet-2222,subnet-3333" }, "TEST": { "subList" : "subnet-4444,subnet-5555" } } }` Then the Subnet setting was like `"Subnets" : { "Fn::Split" : [ ",", { "Fn::FindInMap" : [ "AWSEnv2PublicSubnets", { "Ref" : "Env" }, "subList" ]} ] }` – user3188040 Apr 27 '17 at 09:23
  • @user3188040 Ah sorry, I think I just messed up the JSON syntax. I only use YAML for CF templates, so just provided a JSON answer since you asked in JSON. – gregmac Apr 27 '17 at 17:37
9

I think Mappings value supports List type (at least as of now).

https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/mappings-section-structure.html

The Mappings section consists of the key name Mappings. The keys in mappings must be literal strings. The values can be String or List types.

Here is the example written in YAML.

Parameters:
  Env:
    Type: String
    AllowedValues: [dev, qa, prod]

Mappings:
  Environment:
    dev:
      Groups:
        - Developer
        - QA
    qa:
      Groups:
        - Developer
        - QA
    prod:
      Groups:
        - Operations

Resources:
  Policy:
    Type: AWS::IAM::ManagedPolicy
    Properties:
      ManagedPolicyName: iam-manage-role
      PolicyDocument:
        Version: 2012-10-17
        Statement:
          - Effect: Allow
            Action:
              - iam:*Role*
            Resource: "*"
      Groups: !FindInMap [Environment, !Ref Env, Groups]
onelaview
  • 191
  • 1
  • 3
  • This didn't work for me. I got the following error: `Syntax errors in policy. (Service: AmazonIdentityManagement; Status Code: 400; Error Code: MalformedPolicyDocument; ....` – jones-chris May 01 '20 at 14:59
  • `Error Code: MalformedPolicyDocument` - most likely you put indentation wrong somewhere in you yaml? – onelaview May 02 '20 at 07:17