8
votes

I have been using the !Sub function in my CloudFormation Yaml templates just fine. And when used it as an object property value it works for me

Object:
  Property1: !Sub some-value-with-a-${variable}-in-it

The value of variable gets replaced as expected.

However, I can't figure out how to use the !Sub function in an element of a string array

Array:
  - !Sub some-value-with-a-${variable}-in-it

That array element just gets ignored.

I am trying this in the context of a SAM template creating a AWS::Serverless::Function type resource. The Policies property can take an array of strings:

lambda:
  Type: AWS::Serverless::Function
  Properties:
    CodeUri: api
    FunctionName: !Sub api-${MyStageName}
    Handler: Lambda:Api.Function::HandleAsync
    Runtime: dotnetcore1.0
    Policies: 
    - AWSLambdaBasicExecutionRole
    - !Sub arn:aws:iam::${AWS::AccountId}:policy/some-policy
    - arn:aws:iam::123456789:policy/another-policy

The !Sub function works in the FunctionName property in this example. But I only end up with 2 Policies attached to my generated role - AWSLambdaBasicExecutionRole and arn:aws:iam::123456789:policy/another-policy. The one including the !Sub function gets ignored.

I have tried options like putting the function on a new line:

Array:
  - 
    !Sub some value with a ${variable} in it

Can anyone help?

Update

@Tom Melo pointed out that this is not a array problem so I have adjusted my question.

Further investigation has revealed it is not a Cloud Formation issue exactly, but very specific to the AWS::Serverless::Function resource type, and the Policies property within in. I suspect it has something to do with the fact that the Policies property is so flexible in what it can accept. It can accept strings referring to policy names or Arns, and it can also accept policy documents describing new policy. I suspect this means it is not able to support the functions.

2
There should not be a difference in using !Sub with an array item or a mapping value. This seems to be a bug and should be reported as such. I'd try to quote the scalar after !Sub; maybe it's a parser bug and if so, that could be a workaround.flyx
Thanks for the response. Quotes didn't work so I will try logging a bugAndy McCluggage

2 Answers

5
votes

Apparently, there's nothing wrong with !Sub function in a array of elements.

I have tried to create the following stack on Cloudformation and it worked:

AWSTemplateFormatVersion: '2010-09-09'
Description: 'IAM Roles Template'

Parameters:
  ArnBase:
    Type: String
    Default: arn:aws:iam::aws:policy/
  AWSLambdaFullAccess:
    Type: String
    Default: AWSLambdaFullAccess
  AmazonSESFullAccess:
    Type: String
    Default: AmazonSESFullAccess    

Resources:

  LambdaRole:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument:
        Version: "2012-10-17"
        Statement:
          Effect: Allow
          Principal:
            Service: lambda.amazonaws.com
          Action: sts:AssumeRole          
      Policies:
        -
          PolicyName: CloudFormationFullAccess
          PolicyDocument:
            Version: "2012-10-17"
            Statement:
              -
                Effect: "Allow"
                Action: "cloudformation:*"
                Resource: "*"    
      ManagedPolicyArns:        
        - !Sub ${ArnBase}${AWSLambdaFullAccess}        
        - !Sub ${ArnBase}${AmazonSESFullAccess}
        - !Sub arn:aws:iam::${AWS::AccountId}:policy/CustomAmazonGlacierReadOnlyAccess

That should work using SAM either...

0
votes

Workaround

The AWS::Serverless::Function resource type supports several ways of configuring access.

  1. Refer to policies directly via the Policies property and have the framework create a role that has those policies.
  2. The Role property can refer to a role which already contains policies.

I was using option 1, but option 2 proves to be a way around the issues with !Sub function.

Creating a role explicitly with the policies we want using the AWS::IAM::Role means we can use the !Sub function within the ManagedPolicyArns property. For example

role:
  Type: AWS::IAM::Role
  Properties:
    ...
    ManagedPolicyArns:
      - !Sub arn:aws:iam::${AWS::AccountId}:policy/some-policy
    ...
lambda:
  Type: AWS::Serverless::Function
  Properties:
    ...
    Role: !GetAtt role.Arn
    ...