Using built-in CloudFormation variables to generate unique resource names
Using AWS::StackId & other pseudo predefined cloudformation variables to generate unique resource names within a CloudFormation Template
Table Of Contents
Today I Explained
When working within AWS, eventually you will encounter a service that needs unique names, and typically you learn this by an error message that sounds similar to:
An error occurred: ServiceLogs - /aws/lambda/myservicesloggroup already exists.
As the message is hinting at, the resource you are attempting to create must have a unique name/identifier, and you are attempting to create it with a name/identifier that is already in-use. This usually leads to the idea of just having a random suffix (or prefix) within your resource name to avoid the name clash, or phrased in another way:
- How can I add a random string in a CloudFormation template?
- Is there a way to create some kind of random value in a CloudFormation template?
- How to add random suffix values to resource names in CloudFormation?
- Is there a way to randomly parameterize CloudFormation resource names?
- How to set semi-random names for resoruces using CloudFormation?
One of the built-in solutions within CloudFormation to this problem is the pseudo predefined cloudformation variables, specifically the AWS::StackId
parameter, as it allows us to source a Uuid like 201c12d0-0721-11ee-b056-02bec084ce62
from the ARN: arn:aws:cloudformation:ca-central-1:123456789012:stack/aws-cfn-unique-resource-names/201c12d0-0721-11ee-b056-02bec084ce62
.
Using something like the AWS::StackId
, it then becomes possible to create a name like Example-02bec084ce62
using something like the following:
MyResource:
Type: AWS::MyService::MyResource
Properties:
Name:
!Join [
"-",
[
"Example",
!Select [4, !Split ["-", !Select [2, !Split ["/", !Ref AWS::StackId]]]],
],
]
Or to summarize the above in a programming language, we are doing something like this:
stackid = "arn:aws:cloudformation:ca-central-1:123456789012:stack/aws-cfn-unique-resource-names/201c12d0-0721-11ee-b056-02bec084ce62"
uuid = stackid.split("/")[2] # Split on `/` to isolate the Uuid, which is the 3rd element in the array
random = uuid.split("-")[-1] # Split the uuid into partitions, grab the last element
name = "-".join(["Example", random]) # Combine the elements into a "-" delimited string
This isn’t the only option for generating a “pseudo” unique name using the pseudo predefined cloudformation variables. Depending on the level of uniqueness you need, it may make sense to leverage some of the other available variables. You can see an example below of these values:
Name | Value |
---|---|
AWSAccountID | 123456789012 |
AWSAccountRegion | ca-central-1 |
StackId | arn:aws:cloudformation:ca-central-1:123456789012:stack/aws-cfn-unique-resource-names/201c12d0-0721-11ee-b056-02bec084ce62 |
StackName | aws-cfn-unique-resource-names |
Uuid | 201c12d0-0721-11ee-b056-02bec084ce62 |
UuidPartition1 | 201c12d0 |
UuidPartition2 | 0721 |
UuidPartition3 | 11ee |
UuidPartition4 | b056 |
UuidPartition5 | 02bec084ce62 |
GeneratedName | Example-02bec084ce62 |
Notes on Uniqueness
- Sometimes you only need regional uniqueness, that is to say, your name is unique within an AWS Region (e.g.
us-east-1
). In which case, the name of the CloudFormation stack is already guaranteed to be unique within your AWS Region (AWS::StackName
). - The combination of the AWS Account ID, AWS Region & StackName can often yield a likely globally unique resource name for resources like S3 Buckets. Although at this length, you’ll need to be wary of the length limits for S3 buckets (63 characters).
Finally, if you think the uniqueness offered by the predefined pseudo variables isn’t appropriate, you can either consider passing in a unique identifier to your CloudFormation stacks as a parameter:
Parameters:
Identifier:
Description: A random identifier
Type: String
or evaluate the use of AWS CloudFormation template macros which behind-the-scenes allow you to invoke Lambda functions that would be responsible for creating your unique values at runtime. This can be accomplished using roughly something like this:
Resources:
TransformFunction:
Type: AWS::Serverless::Function
Properties:
Runtime: python3.9
Handler: index.handler
MemorySize: 128
Timeout: 3
InlineCode: |
import random
def handler(event, context):
response = {
'requestId': event['requestId'],
'status': 'success'
}
try:
param_min = int(event['params']['min'])
param_max = int(event['params']['max'])
response['fragment'] = random.randint(param_min, param_max - 1)
except Exception as e:
response['status'] = 'failure'
response['errorMessage'] = str(e)
return response
Transform:
Type: AWS::CloudFormation::Macro
Properties:
Name: RandomInt
Description: Provides a random integer within the range requested.
FunctionName: !GetAtt TransformFunction.Arn
With the usage for creating a uniquely named bucket soemthing like this:
Resources:
S3Bucket:
Type: AWS::S3::Bucket
Properties:
Tags:
- Key: RandomInt
Value:
'Fn::Transform':
- Name: RandomInt
Parameters:
min: 100000000
max: 200000000