Search code examples
amazon-web-servicesaws-cloudformationaws-cloudformation-macro

How to pass a Ref to a CloudFormation Macro using Fn::Transform?


I'm trying to create a macro that takes the ID of another CloudFormation resource as an input parameter, but it isn't working for some reason. I found documentation (not from the official AWS documentation) saying that this should work: link

I've trimmed down my CloudFormation template to the bare minimum to try to figure out if I could get it working, but even so it's still not working:

Resources:
  TestMacroLambdaFunction:
    Type: AWS::Lambda::Function
    Properties:
      ...

  TestMacro:
    Type: AWS::CloudFormation::Macro
    Properties:
      Name: TestMacro
      FunctionName:
        Fn::GetAtt:
          - TestMacroLambdaFunction
          - Arn

  TestFirstS3Bucket:
    Type: AWS::S3::Bucket
    Properties:
      BucketName: "MyTestBucket123"

  TestSecondS3Bucket:
    DependsOn: TestMacro
    Type: AWS::S3::Bucket
    Properties:
      BucketName:
        Fn::Transform:
          Name: TestMacro
          Parameters:
            MyStaticParameter: "hello"
            MyDynamicParameter:
              Ref: TestFirstS3Bucket

The change set fails to get created - the macro function is never even getting invoked! This is the error message:

The value of parameter MyDynamicParameter under transform TestMacro must resolve to a string, number, boolean or a list of any of these.

I get the same error regardless of wheter I try using Fn:GetAtt or Ref... So how can you pass variables into CloudFormation macros?


Solution

  • After further investigation, it seems there's a good reason why you can't reference Ref or GetAtt in CloudFormation macros -- the macros are evaluated before the stack update runs, so it can't possibly reference resources which may not even exist yet (and certainly haven't been updated yet).

    The solution recommended to me by the CloudFormation team was to instead pass the resource names instead of their IDs, for example:

      TestSecondS3Bucket:
        DependsOn: TestMacro
        Type: AWS::S3::Bucket
        Properties:
          BucketName:
            Fn::Transform:
              Name: TestMacro
              Parameters:
                MyStaticParameter: "hello"
                MyDynamicParameter: "TestFirstS3Bucket"
    

    Then your macro code can call DescribeStackResource on the stack with the resource name to find whether the resource already exists, and if so what its physical resource ID is.