How to Return a List from FindInMap in AWS CloudFormation for Multi-AZ ELB Setup


2 views

When configuring Elastic Load Balancers in CloudFormation, we often need to reference pre-existing subnets from different Availability Zones. The common approach using Fn::FindInMap works perfectly for single values, but becomes problematic when we need to return a list of subnet IDs for multi-AZ deployment.

Here's what typically fails:

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

This structure creates a nested list (list containing another list) rather than the flat list of strings that ELB expects.

Solution 1: Using Fn::Split with Delimiter

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

With corresponding mapping:

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

Solution 2: Using Fn::Select with List

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

This approach requires your mapping to be structured as:

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

For cleaner templates, consider using Fn::Join with Fn::GetAZs when creating new subnets:

"Subnets": [
  {"Fn::Select": ["0", {"Fn::GetAZs": ""}]},
  {"Fn::Select": ["1", {"Fn::GetAZs": ""}]}
]

This automatically selects subnets from different AZs without hardcoding values.

For complex scenarios, you might create a Lambda-backed custom resource that returns the subnet list:

"Resources": {
  "SubnetResolver": {
    "Type": "Custom::SubnetList",
    "Properties": {
      "ServiceToken": "arn:aws:lambda:...",
      "Env": {"Ref": "Env"}
    }
  },
  "ELB": {
    "Properties": {
      "Subnets": {"Fn::GetAtt": ["SubnetResolver", "SubnetList"]}
    }
  }
}

When troubleshooting, use Fn::Sub to inspect values:

"Outputs": {
  "SubnetDebug": {
    "Value": {"Fn::Sub": "${AWSEnv2PublicSubnets.${Env}.subList}"}
  }
}

This helps verify your mapping structure before implementing the full solution.


When working with AWS CloudFormation's Fn::FindInMap intrinsic function, many developers encounter difficulties when trying to return a list of values. The common error Value of property Subnets must be of type List of String appears because CloudFormation expects a direct list but receives a nested structure.

For Elastic Load Balancers in multi-AZ environments, you typically need to specify multiple subnets. The natural approach would be to store these subnet IDs in a Mapping and retrieve them, but the standard implementation fails:

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

To properly return a list from FindInMap, you need to use a combination of Fn::Join and Fn::Split functions:

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

Your mapping should then be modified to use comma-separated values:

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

For more complex scenarios, you might consider creating a custom Lambda function that returns the subnet list:

"Subnets": {
  "Fn::GetAtt": ["SubnetResolver", "Subnets"]
},
"SubnetResolver": {
  "Type": "Custom::SubnetList",
  "Properties": {
    "ServiceToken": "arn:aws:lambda...",
    "Environment": {"Ref": "Env"}
  }
}
  • Always validate subnet IDs before template deployment
  • Consider using AWS Systems Manager Parameter Store for dynamic values
  • Test your template with different environment configurations
  • Use CloudFormation linting tools to catch syntax issues early

Remember that FindInMap doesn't natively support list returns. Other mistakes include:

  • Forgetting to update all environment mappings when adding new subnets
  • Using different AZs for subnets in the same ELB
  • Not accounting for region differences in subnet IDs