TIL - Using CloudFormation cross stack references

Today I learned how to use cross-stack references in AWS CloudFormation.

Long story short: I needed to add permissions to a staging lambda function.

Looking at the CloudFormation docs for lambda permissions, we get the following:

permission:
  Type: AWS::Lambda::Permission
  Properties:
    Action: lambda:InvokeFunction
    FunctionName: !Ref FunctionName
    Principal: 29302903920

My project has two stacks, defined in two templates: one for staging and one for production.

The problem I had was that I wanted to dynamically populate the Principal field using the ARN of an IAM role I had created in the production stack.

I didn’t want to copy and paste the role’s ARN everywhere, so I looked around for an elegant solution and discovered cross-stack references.

The solution

The solution is very simple and is divided in 3 steps:

  1. Export the desired value in the Outputs section of the production stack
  2. Create a variable in the Parameters section of the staging stack
  3. Retrieve the value at runtime and pass it to the staging template when deploying

Step 1: Export the value

In the production stack add the following block to the bottom of the template file:

Outputs:
  NgnrLogRoleArn:
    Value: !GetAtt NgnrApiLogsRole.Arn
    Description: Arn for the cloudwatch logs role

Step 2: Create a variable in the staging stack

In the staging template, add a new variable under Parameters

Parameters:
  NgnrLogRoleArn:
    Type: string

then reference it in the permission block

permission:
  Type: AWS::Lambda::Permission
  Properties:
    Action: lambda:InvokeFunction
    FunctionName: !Ref FunctionName
    Principal: !Ref NgnrLogRoleArn

Step 3: Retrieve the value dynamically with the CLI

To retrieve the output value of a stack, we can use the describe-stacks command

aws cloudFormation describe-stacks --stack-name stack-name --query "Stacks[0].Outputs[?OutputKey=='NgnrLogRoleArn'].OutputValue" --output text

This will get the metadata for the given stack-name then filter the results based on the given condition

In plain English:

For the first item in the Stacks list, look at the Outputs list. Give me the one whose key is NgnrLogRoleArn and output the corresponding value in plain text.

Deploy the stack

You can now use the above command to fetch whatever value you need and dynamically pass it to the stack. Putting everything together we get the following deploy command:

aws cloudformation deploy --template-file staging.infra.cf.yaml --stack-name ngnr-club-staging --region us-east-1 --capabilities CAPABILITY_NAMED_IAM --parameter-overrides NgnrLogRoleArn=(aws cloudformation describe-stacks --stack-name ngnr-club --query "Stacks[0].Outputs[?OutputKey=='NgnrLogRoleArn'].OutputValue" --output=text)

Thanks for coming to my TED talk.