Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

AWS Cloudformation : Fn::Join within a Fn::FindInMap statement?

Trying to use Fn::Join within Fn::FindInMap, as below:

"SubnetId": {
    "Fn::FindInMap": [
        {
            "Ref": "OrganizationName"
        },
        "AZ",
        {
            "Fn::Join": [
                "",
                [
                    {
                        "Ref": "Environment"
                    },
                    {
                        "Ref": "Member1AZ"
                    }
                ]
            ]
        }
    ]
}

The OrganizationName, Environment and Member1AZ are all parameters. Essentially it should hook up to my mappings and produce, for example :

"SubnetId" : { "Fn::FindInMap" : [ "Organization2", "AZ", "prod1c" ]}

However, it does not seem to be taking the output from the Fn::Join as a single entity on the Fn::FindInMap, it validates correctly if I hardcode that section of the template.

A client error (ValidationError) occurred when calling the ValidateTemplate operation: Template error: every Fn::FindInMap object requires three parameters, the map name, map key and the attribute for return value

My Mappings are as follows:

Mappings" : {
      "OrganizationDefaults" : {
            "AZ" : {
                "prod1a" : "subnet-foobar1",
                "qa1a" : "subnet-foobar2",
                "prod1c" : "subnet-foobar3",
                "qa1c" : "subnet-foobar4"
            }
      },
      "OrganizationTwo" : {
            "AZ" : {
                "prod1a" : "subnet-foobar5",
                "qa1a" : "subnet-foobar6",
                "prod1c" : "subnet-foobar7",
                "qa1c" : "subnet-foobar8"
            }
      },
},

Can anyone help on this, or had to do something similar before? I need to use the same template for any organizations listed, so Mappings should solve this for me, if I can get it right.

like image 375
stephencoetzee Avatar asked May 23 '14 10:05

stephencoetzee


People also ask

How do I use the FN () function in AWS CloudFormation?

You can use the Fn::Sub function to substitute supported functions or to substitute as a string for other functions. In your AWS CloudFormation template, you can use Fn::Sub to substitute other supported functions, such as Fn::FindInMap. If you're using a JSON template, substitute Var1Name and Var2Name with a Var1Value and Var2Value. For example:

How do I substitute for findinmap in AWS CloudFormation?

In your AWS CloudFormation template, you can use Fn::Sub to substitute other supported functions, such as Fn::FindInMap. If you're using a JSON template, substitute Var1Name and Var2Name with a Var1Value and Var2Value.

What is FN::join in CloudFormation?

CloudFormation supports a number of intrinsic functions and Fn::Join (or !Join) is often used to construct parameterised names and paths. The Serverless framework, for instance, uses it extensively.

How to join variables in serverless CloudFormation?

Check out the serverless-cloudformation-sub-variables plugin which lets you use Fn::Sub in the serverless.yml. You just need to use # {VariableName} instead of $ {VariableName}. CloudFormation supports a number of intrinsic functions and Fn::Join (or !Join) is often used to construct parameterised names and paths.


1 Answers

Although I agree with @Jason that in your case a refactor of your map layout is the best solution for you, there are cases where the 2D limitations of the maps in CloudFormation can be limiting, so I will post a possible solution here.

As of the date of this post, the Fn::FindInMap intrinsic function only supports the following nested functions:

  • Fn::FindInMap
  • Ref

Using a Join will give you the slightly cryptic error you post above. However, because you can nest FindInMap calls, you can achieve a "3rd dimention" for the map by creating another lookup map:

Mappings" : {
  "OrganizationDefaults" : {
        "AZ" : {
            "prod1a" : "subnet-foobar1",
            "qa1a" : "subnet-foobar2",
            "prod1c" : "subnet-foobar3",
            "qa1c" : "subnet-foobar4"
        }
  },
  "OrganizationTwo" : {
        "AZ" : {
            "prod1a" : "subnet-foobar5",
            "qa1a" : "subnet-foobar6",
            "prod1c" : "subnet-foobar7",
            "qa1c" : "subnet-foobar8"
        }
  },
  "EnvMemberMap" : {
        "prod": {
            "1a" : "prod1a",
            "1c" : "prod1c",
        },
        "qa": {
            "1a" : "qa1a",
            "1c" : "qa1c",
        }    
  }
},

And then perform the map retrieval like this:

"SubnetId": {
    "Fn::FindInMap": [
        {
            "Ref": "OrganizationName"
        },
        "AZ",
        {
            "Fn::FindInMap": [
                "EnvMemberMap",
                {
                    "Ref": "Environment"
                },
                {
                    "Ref": "Member1AZ"
                }
            ]
        }
    ]
}
like image 82
Lee Netherton Avatar answered Sep 18 '22 21:09

Lee Netherton