Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to create some random or unique value in a CloudFormation template?

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}'
like image 615
lexicore Avatar asked Aug 26 '19 15:08

lexicore


4 Answers

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).

EDIT 2022-02-17:

As observed by @teuber789's comment, if you need multiple resources of the same type, e.g.: my-auto-scaling-group-<random_1> and my-auto-scaling-group-<random_2>, this approach won't work as AWS::StackId is the same for whole stack.

like image 177
Tulio Casagrande Avatar answered Nov 12 '22 14:11

Tulio Casagrande


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.

like image 22
Aleksey Balenko Avatar answered Nov 12 '22 14:11

Aleksey Balenko


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

Value: !Select [2, !Split ['/', !Ref AWS::StackId]]
like image 41
owlwalks Avatar answered Nov 12 '22 12:11

owlwalks


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.

like image 38
LiuChang Avatar answered Nov 12 '22 14:11

LiuChang