Append to a Static List in a YAML CloudFormation Template

When writing CloudFormation stack templates, I sometimes need to create a list combining things defined at runtime and static values.

Imagine you have a template that contains a mapping, which enumerates IAM roles by environment. You want to grant permission to these roles as well as one or more Lambda execution roles. Can you create a list comprised of the static values defined in your map with references to roles created as part of your stack?

The FindInMap intrinsic function returns a set when the mapped value is a list, such as in our example. The Join function creates a string composed of the elements in the set separated by a given value.

You may perform a join on a set returned from the FindInMap function, returning a string composed of the elements in the set delimited by comma. You can then join the comma delimited string with a list of values. This second list can include references to resources created in the template.

!Join
  - ","
    - - !Join [",", !FindInMap ["MyMap", "foo", "thing"]]
    - !Ref "Thinger"

The following shows a CloudFormation stack template using this technique juxtaposition to an instance of the provisioned resource..

AWS CloudFormation Append Value to List
You’re seeing a role definition in a CloudFormation stack template shown juxtaposition to an instance of the resource provisioned. The role’s definition includes a list of ARNs. The ARNs are a combination of a static list provided by a mapping, and an execution role for a Lambda. The provisioned role reflects the complete list.

Notice the provisioned resource is a superset of the two lists. The following is the complete template:

Description: Sample Stack
Parameters:
  Thinger:
    Type: "String"
    Default: "arn:aws:s3:::f2c9"
Mappings:
  MyMap:
    foo:
      thing:
        - "arn:aws:s3:::0b50"
        - "arn:aws:s3:::e256"
        - "arn:aws:s3:::4159"
      thang:
        - "arn:aws:s3:::8199"
        - "arn:aws:s3:::d9f1"
        - "arn:aws:s3:::bc2b"
    bar:
      thing:
        - "arn:aws:s3:::bd69"
        - "arn:aws:s3:::eb00"
        - "arn:aws:s3:::0f55"
      thang:
        - "arn:aws:s3:::5ebc"
        - "arn:aws:s3:::4ccb"
        - "arn:aws:s3:::85c2"
Resources:
  Something:
    Type: "AWS::IAM::Role"
    Properties:
      AssumeRolePolicyDocument:
        Version: "2012-10-17"
        Statement:
          - Effect: "Allow"
            Principal:
              Service:
                - "lambda.amazonaws.com"
            Action: "sts:AssumeRole"
      Policies:
        - PolicyName: ExecuteSubmitFilePolicy
          PolicyDocument:
            Version: "2012-10-17"
            Statement:
              - Effect: Allow
                Action:
                  - logs:CreateLogGroup
                  - logs:CreateLogStream
                  - logs:PutLogEvents
                Resource: !Split
                  - ","
                  - !Join
                    - ","
                    - - !Join [",", !FindInMap ["MyMap", "foo", "thing"]]
                      - !Ref "Thinger"
Outputs:
  UnifiedList:
    Value: !Join
      - ","
      - - !Join [",", !FindInMap ["MyMap", "foo", "thing"]]
        - !Ref "Thinger"

The utility of this technique is debatable. That said, it’s a useful pattern for joining two sets in a CloudFormation stack template.