7
votes

Is there a way to create some kind of random or unique value in a CloudFormation template?

Why I need this. In our templates we have a number of custom-named resources, for instance AWS::AutoScaling::LaunchConfiguration with specified LaunchConfigurationName or AWS::AutoScaling::AutoScalingGroup with specified AutoScalingGroupName.
When updating stacks, we often get the following error:

CloudFormation cannot update a stack when a custom-named resource requires replacing. Rename some-stack-launch-configuration and update the stack again.

We don't want to rename resources just because we need to update them.
We also don't want to drop custom names in our resources. We won't mind however having some random suffix in our custom names.

With a "random generator" the solution might look something like:

  MyAutoScalingGroup:
    Type: 'AWS::AutoScaling::AutoScalingGroup'
    Properties:
      AutoScalingGroupName: !Sub 'my-auto-scaling-group-${AWS::Random}'
5

5 Answers

5
votes

If you just need a random ID (no passwords, no fancy requirements), the way I'd recommend is using a portion of AWS::StackId, which is in the following format:

arn:aws:cloudformation:us-west-2:123456789012:stack/teststack/51af3dc0-da77-11e4-872e-1234567db123

So in order to get the last portion, you would need two splits, e.g.:

      AutoScalingGroupName:
        Fn::Join:
          - '-'
          - - my-auto-scaling-group
            - Fn::Select:
                - 4
                - Fn::Split:
                    - '-'
                    - Fn::Select:
                        - 2
                        - Fn::Split:
                            - /
                            - Ref: AWS::StackId

Equivalent shorter syntax:

AutoScalingGroupName: !Join ['-', ['my-auto-scaling-group', !Select [4, !Split ['-', !Select [2, !Split ['/', !Ref AWS::StackId]]]]]]

Meaning:

  1. Start with AWS::StackId, e.g.: arn:aws:cloudformation:us-west-2:123456789012:stack/teststack/51af3dc0-da77-11e4-872e-1234567db123
  2. Split on / and select 2th portion (0-indexed): 51af3dc0-da77-11e4-872e-1234567db123
  3. Split on - and select 4th portion (0-indexed): 1234567db123
  4. Join with your fixed portion name: my-auto-scaling-group-1234567db123.

Advantages: I prefer this way than creating a CustomResource, because for large AWS environments and many stacks, you might end up with several lambdas, making governance a bit harder.

Disadvantages: It's more verbose (Fn::Join, Fn::Select, and Fn::Split).

2
votes

I think you need to create a Lambda function to do this.

Here's a GitHub project cloudformation-random-string, which has a Lambda function and a simple tutorial.

Here's another tutorial Generate Passwords in AWS CloudFormation Template.

You can refer to the Lambda function above and make it work for you.

2
votes

In my opinion, the most elegant way to implement such logic (if you don't want to rename resources) is to use Cloudformation Macros. They're like a custom resource, but you call them implicitly during template transformation. So, I will try to provide some example, but you can investigate more in AWS Documentation.

First of all, you create the function (with all required permissions and so on) that will do the magic (something like LiuChang mentioned).

Then, you should create a macro from this Function:

Resources:
    Macro:
      Type: AWS::CloudFormation::Macro
      Properties:
        Name: <MacroName>
        Description: <Your description>
        FunctionName: <Function ARN>

And then use this Macro in your resources definition:

MyAutoScalingGroup:
    Type: 'AWS::AutoScaling::AutoScalingGroup'
    Properties:
      AutoScalingGroupName: 
        'Fn::Transform':
         - Name: <MacroName>
           Parameters:
             InputString: <Input String>
             ...<Some other parameters like operation type or you can skip this>

Also, to use macros, you should specify the CAPABILITY_AUTO_EXPAND capability during stack creation/updation.

And that's it. It should just work, but of course one of the drawbacks of this approach - you should maintain additional lambda function.

1
votes

this is similar to https://stackoverflow.com/a/67162053/2660313 but shorter:

Value: !Select [2, !Split ['/', !Ref AWS::StackId]]
0
votes

I'm using the AWS Java SDK to run some Stack Update command.

I generate random value using Java, then I pass it as parameter.