27

I've got a nested CloudFormation template which accepts a number of parameters from its root template to configure it. At the moment I'm only passing simple string parameters but now I need to pass a list of S3 bucket ARNs onto the child template.

ChildLambdaStack:
  Type: AWS::CloudFormation::Stack
  Properties:
    Parameters:
      AwsRegion: !Ref AwsRegion
      Environment: !Ref Environment
      Product: !Ref Product
      S3Buckets: "arn:aws:s3:::bucket1,arn:aws:s3:::bucket2"
    TemplateURL: "https://s3.amazonaws.com/child-template.yml"

And then in the child template I have this

AWSTemplateFormatVersion: "2010-09-09"
Description: "Child Lambda"

Parameters:
  AwsRegion:
    Type: String
  Environment:
    Type: String
  Product:
    Type: String
  S3Buckets:
    Type: String

Resources:
  DeployerPolicy:
    Type: AWS::IAM::ManagedPolicy
    Properties:
      PolicyDocument:
        Version: "2012-10-17"
        Statement:
          - Effect: Allow
            Action:
              - s3:PutObject
              - s3:GetObject
              - s3:DeleteObject
              - s3:CreateBucket
              - s3:DeleteBucket
              - s3:ListBucket
              - s3:PutBucketNotification
            Resource:
              - Fn::Split:
                - ","
                - !Ref S3Buckets

My idea is that that list of S3 bucket ARNs that I'm inputting is expanded in the child template like this

Resource:
  - arn:aws:s3:::bucket1
  - arn:aws:s3:::bucket2

But when I run the template in, it just errors out

Syntax errors in policy. (Service: AmazonIdentityManagement; Status Code: 400; Error Code: MalformedPolicyDocument)

I've tried other variations like using a CommaDelimitedList parameter type, but none work. Is there a simple way to pass in a list of strings as a parameter?

Liam Mayfair
  • 493
  • 1
  • 8
  • 8

3 Answers3

23

Because the return value of !Split is A list of string values. I would do it in the following way:

[...]
    Resource: !Split [",", !Ref S3Buckets]
[...]
MaiKaY
  • 4,422
  • 20
  • 28
  • Is this any different to what OP has done? It's inline, but otherwise it calls the same function with the same arguments, right? – arco444 Aug 30 '18 at 11:32
  • 5
    It's not the same. OP added an extra `-` in front of `Fn::Split` which means it passes a list with a value of list with strings. – MaiKaY Aug 30 '18 at 11:43
19

As @MaiKaY points out, the flaw in @Liam Mayfair's code is that Fn::Split is preceded by - which results in a list containing a single element which is a list. The fixed code would look like

...
            Resource:
              Fn::Split:
                - ","
                - !Ref S3Buckets

On a more general note, you must make sure to use a Parameter type of String not CommaDelimitedList when using Fn::Split as it won't split a CommaDelimitedList.

  • If you use CommaDelimitedList with Fn::Split you'll get the error Template error: every Fn::Split object requires two parameters, (1) a string delimiter and (2) a string to be split or a function that returns a string to be split
  • If you use CommaDelimitedList with no Fn::Split you'll get the error Syntax errors in policy
gene_wood
  • 1,960
  • 4
  • 26
  • 39
9

There is actually a much better way. You can use the type List<String> in your CloudFormation Parameters:

# ...
Parameters:
  S3Buckets:
    Type: List<String>
# ...

Then pass the S3 Bucket list just like you did as comma separated values:

# ...
ChildLambdaStack:
  Type: AWS::CloudFormation::Stack
  Properties:
    Parameters:
      AwsRegion: !Ref AwsRegion
      Environment: !Ref Environment
      Product: !Ref Product
      # Next line will be interpreted as a list
      S3Buckets: "arn:aws:s3:::bucket1,arn:aws:s3:::bucket2"
    TemplateURL: "https://s3.amazonaws.com/child-template.yml"
# ...

You can then assume that the type of the referenced Parameter is a list. So instead of:

# ...
Resource: !Split [",", !Ref S3Buckets]
# ...

You can just use:

# ...
Resource: !Ref S3Buckets
# ...
tdy
  • 36,675
  • 19
  • 86
  • 83
nilskch
  • 307
  • 3
  • 10