Reputation: 123
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.
Upvotes: 8
Views: 15669
Reputation: 22532
As @Jason explained, one solution is to refactor your map to work within the 2D limitations of the maps in CloudFormation. There are however, two ways you can work around this:
AWS::LanguageExtensions
transform to your template and use Fn::Join
in Fn:FindInMap
just as you described above.Fn::FindInMap
calls to achieve a "3rd dimension" in your mapUnder solution 1, you'll add the AWS::LanguageExtensions
transform to the root of your template. This will let you use intrinsic functions and other functionalities not included by default in AWS CloudFormation. With this transform, Fn::FindInMap
is enhanced to allow an extended set of nested intrinsic functions as well as an optional field to return a default value. It just so happens that Fn::Join
is one of the functions that is supported by this enhanced version of the Fn::FindInMap
. As such, with the language extension applied, you'll be able to use Fn::Join
as a nested function within Fn::FindInMap
exactly as you wrote in your question.
Under solution 2, the standard Fn::FindInMap
intrinsic function only supports the following nested functions:
Fn::FindInMap
Ref
Using a Join
in this case will give you the slightly cryptic error you post above. However, because you can nest FindInMap
calls, you can achieve a "3rd dimension" 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"
}
]
}
]
}
Upvotes: 7
Reputation: 2105
I suggest you refactor your mappings to avoid the nested Fn::Join.
Mappings" : {
"OrganizationDefaults" : {
"1a" : {
"prod" : "subnet-foobar1",
"qa" : "subnet-foobar2"
},
"1c"
"prod" : "subnet-foobar3",
"qa" : "subnet-foobar4"
}
},
"OrganizationTwo" : {
"1a" : {
"prod" : "subnet-foobar5",
"qa" : "subnet-foobar6"
},
"1c" : {
"prod" : "subnet-foobar7",
"qa" : "subnet-foobar8"
}
},
},
This simplifies your reference.
"SubnetId" : { "Fn::FindInMap" : [ { "Ref" : "OrganizationName" }, { "Ref" : "Member1AZ" }, { "Ref" : "Environment" }]}
Upvotes: 4