When working with AWS CloudFormation, we often encounter situations where the same composed string value needs to be referenced across multiple resources. A common example is when you need to construct naming conventions using parameters like ProjectName
and Environment
that get used in tags, hostnames, and other resource identifiers.
The most straightforward method is using CloudFormation's intrinsic functions directly in each resource definition:
"HostName": {
"Fn::Join": ["-", [
{"Ref": "ProjectName"},
{"Ref": "Environment"},
"web-server"
]]
}
While this works, it becomes cumbersome to maintain when the same string composition appears in multiple places.
One effective solution is to use the Mappings
section to define reusable string patterns:
"Mappings": {
"NamingConventions": {
"BaseName": {
"Pattern": {"Fn::Join": ["-", [
{"Ref": "ProjectName"},
{"Ref": "Environment"}
]]}
}
}
}
Then reference it using:
{"Fn::FindInMap": ["NamingConventions", "BaseName", "Pattern"]}
For AWS resource naming, consider this parameter pattern approach:
"Parameters": {
"ResourcePrefix": {
"Type": "String",
"Default": "",
"Description": "Will be auto-generated if empty",
"AllowedPattern": ".*"
}
},
"Conditions": {
"CreateResourcePrefix": {"Fn::Equals": [{"Ref": "ResourcePrefix"}, ""]}
},
"Resources": {
"ResourcePrefixGenerator": {
"Type": "AWS::SSM::Parameter",
"Condition": "CreateResourcePrefix",
"Properties": {
"Name": "/cloudformation/prefix",
"Type": "String",
"Value": {"Fn::Join": ["-", [
{"Ref": "ProjectName"},
{"Ref": "Environment"}
]]}
}
}
}
For advanced scenarios, consider a Lambda-backed custom resource:
"Resources": {
"StringGenerator": {
"Type": "Custom::StringGenerator",
"Properties": {
"ServiceToken": {"Fn::GetAtt": ["StringGeneratorLambda", "Arn"]},
"ProjectName": {"Ref": "ProjectName"},
"Environment": {"Ref": "Environment"}
}
},
"ExampleResource": {
"Type": "AWS::EC2::Instance",
"Properties": {
"Tags": [{
"Key": "Name",
"Value": {"Fn::GetAtt": ["StringGenerator", "ConstructedName"]}
}]
}
}
}
For enterprise-scale templates, CloudFormation macros can process your template and inject the repeated strings:
"Transformations": {
"NameGeneratorMacro": {
"ProjectName": {"Ref": "ProjectName"},
"Environment": {"Ref": "Environment"}
}
}
Then in your resources, you can simply reference !GetGeneratedName
or similar macro-generated values.
When working with AWS CloudFormation, I frequently encounter situations where I need to construct the same composite string multiple times across a template. A common example is combining parameters like ProjectName
and Environment
to form resource identifiers or tags.
\"Fn::Join\": [\"-\", [{\"Ref\":\"ProjectName\"}, {\"Ref\":\"Environment\"}]]
Repeating this Fn::Join
operation throughout the template becomes tedious and creates maintenance challenges.
One effective approach is to use the Mappings
section to define reusable string combinations:
\"Mappings\": {
\"StringCombinations\": {
\"ProjectEnv\": {
\"Value\": {\"Fn::Join\": [\"-\", [{\"Ref\":\"ProjectName\"}, {\"Ref\":\"Environment\"}]]}
}
}
}
Then reference it elsewhere in your template:
\"Tags\": [
{
\"Key\": \"Name\",
\"Value\": {\"Fn::FindInMap\": [\"StringCombinations\", \"ProjectEnv\", \"Value\"]}
}
]
For more complex scenarios, consider a custom resource that generates the string values:
\"Resources\": {
\"NamingGenerator\": {
\"Type\": \"Custom::StringGenerator\",
\"Properties\": {
\"ServiceToken\": {\"Fn::GetAtt\": [\"LambdaFunction.Arn\"]},
\"ProjectName\": {\"Ref\": \"ProjectName\"},
\"Environment\": {\"Ref\": \"Environment\"}
}
}
}
Then reference the output:
\"HostName\": {\"Fn::GetAtt\": [\"NamingGenerator\", \"ProjectEnvString\"]}
- Centralize all reusable strings in your template's
Mappings
section - Use consistent naming conventions for mapping keys
- Document each composite string's purpose in the template metadata
- Consider parameterizing the separator characters (like '-' or '_')
Here's a complete example for naming EC2 instances:
{
\"AWSTemplateFormatVersion\": \"2010-09-09\",
\"Parameters\": {
\"ProjectName\": {\"Type\": \"String\"},
\"Environment\": {\"Type\": \"String\"}
},
\"Mappings\": {
\"NamingConvention\": {
\"BaseName\": {
\"Value\": {\"Fn::Join\": [\"-\", [
{\"Ref\": \"ProjectName\"},
{\"Ref\": \"Environment\"},
{\"Fn::Select\": [\"2\", {\"Fn::Split\": [\"-\", {\"Ref\": \"AWS::StackName\"}]}]}
]]}
}
}
},
\"Resources\": {
\"WebServer\": {
\"Type\": \"AWS::EC2::Instance\",
\"Properties\": {
\"Tags\": [{
\"Key\": \"Name\",
\"Value\": {\"Fn::Join\": [\"-\", [
{\"Fn::FindInMap\": [\"NamingConvention\", \"BaseName\", \"Value\"]},
\"webserver\"
]]}
}]
}
}
}
}