YAML/Cloudformation Error on SLS Deploy

I’ve been running my code offline happily in my local development environment but I wanted to ensure it would deploy correctly to AWS.

When I call sls deploy, I’m getting this error

The CloudFormation template is invalid:

[/Resources/IamRoleLambdaExecution/Type/Policies/0/PolicyDocument/Statement/2/Resource/arn:aws:dynamodb:${opt:region, self:provider.region}:*:table/${self:provider.environment.LISTINGS_TABLE}] 'null' values are not allowed in templates

I’m definitely no expert in this area and basically haven’t a clue what it’s telling me, I have created the YAML template based on examples.

Is there anyone that could shed a light on it for me please?

I believe it’s being thrown from this block

iamRoleStatements:
    - Effect: Allow
      Action:
        - dynamodb:Query
        - dynamodb:Scan
        - dynamodb:GetItem
        - dynamodb:PutItem
        - dynamodb:UpdateItem
        - dynamodb:DeleteItem
      Resource: {"arn:aws:dynamodb:${opt:region, self:provider.region}:*:table/${self:provider.environment.LISTINGS_TABLE}",
                 "arn:aws:dynamodb:${opt:region, self:provider.region}:*:table/${self:provider.environment.BASKETS_TABLE}",
                 "arn:aws:dynamodb:${opt:region, self:provider.region}:*:table/${self:provider.environment.BASKET_LINES_TABLE}"}

I have the tables defined under environment

environment:
    LISTINGS_TABLE: ${self:service}-listings-${opt:stage, self:provider.stage}
    BASKETS_TABLE: ${self:service}-baskets-${opt:stage, self:provider.stage}
    BASKET_LINES_TABLE: ${self:service}-basketlines-${opt:stage, self:provider.stage}

and also under resources

resources:
  Resources:
    ListingsDynamoDbTable:
      Type: AWS::DynamoDB::Table
      DeletionPolicy: Delete      
      Properties:       
        AttributeDefinitions:
          - AttributeName: id
            AttributeType: S
          - AttributeName: creatorContact
            AttributeType: S
          - AttributeName: creatorAccount
            AttributeType: S
        GlobalSecondaryIndexes:
          - IndexName: ContactIndex
            KeySchema:
            - AttributeName: creatorContact
              KeyType: HASH
            Projection:
              ProjectionType: ALL
            ProvisionedThroughput:
              ReadCapacityUnits: 1
              WriteCapacityUnits: 1
          - IndexName: AccountIndex
            KeySchema:
            - AttributeName: creatorAccount
              KeyType: HASH
            Projection:
              ProjectionType: ALL
            ProvisionedThroughput:
              ReadCapacityUnits: 1
              WriteCapacityUnits: 1
        KeySchema:
          - AttributeName: id
            KeyType: HASH  
        ProvisionedThroughput:
          ReadCapacityUnits: 1
          WriteCapacityUnits: 1
        TableName: ${self:provider.environment.LISTINGS_TABLE}

    BasketsDynamoDbTable:
      Type: AWS::DynamoDB::Table
      DeletionPolicy: Delete  
      Properties: 
        AttributeDefinitions:
          - AttributeName: id
            AttributeType: S            
        KeySchema:        
          - AttributeName: id
            KeyType: HASH 
        ProvisionedThroughput:
          ReadCapacityUnits: 1
          WriteCapacityUnits: 1
        TableName: ${self:provider.environment.BASKETS_TABLE}

    BasketLinesDynamoDbTable:
      Type: AWS::DynamoDB::Table
      DeletionPolicy: Delete
      Properties:       
        AttributeDefinitions:
          - AttributeName: id
            AttributeType: S
          - AttributeName: basketHeaderId
            AttributeType: S
        GlobalSecondaryIndexes:
          - IndexName: BasketIndex
            KeySchema:
            - AttributeName: basketHeaderId
              KeyType: HASH
            Projection:
              ProjectionType: ALL
            ProvisionedThroughput:
              ReadCapacityUnits: 1
              WriteCapacityUnits: 1
        KeySchema:
          - AttributeName: id
            KeyType: HASH 
        ProvisionedThroughput:
          ReadCapacityUnits: 1
          WriteCapacityUnits: 1
        TableName: ${self:provider.environment.BASKET_LINES_TABLE}

Are all the variables mentioned specified? Specifically are you defining all of the variables in the provider section of your serverless.yml (which you haven’t included)?

Also, I think the Resource section in your iamRoleStatements should be an array i.e.

iamRoleStatements:
    - Effect: Allow
      Action:
        - dynamodb:Query
        - dynamodb:Scan
        - dynamodb:GetItem
        - dynamodb:PutItem
        - dynamodb:UpdateItem
        - dynamodb:DeleteItem
      Resource:
        - "arn:aws:dynamodb:${opt:region, self:provider.region}:*:table/${self:provider.environment.LISTINGS_TABLE}",
        - "arn:aws:dynamodb:${opt:region, self:provider.region}:*:table/${self:provider.environment.BASKETS_TABLE}",
        - "arn:aws:dynamodb:${opt:region, self:provider.region}:*:table/${self:provider.environment.BASKET_LINES_TABLE}"}

It’s a little confusing (because it’s Resource and not Resources), but that’s how IAM works.

Thank you - it was simply the way I’d defined the Resource block as I suspected but didn’t know how to fix although it was fairly obvious looking at the rest of the YAML but it always is once you’ve done it!

You were absolutely correct that I needed to defined the Resource using that array functionality