aws-sam-cli: `sam deploy` fails when using SSM Parameter for a Lambda Layer ARN

Description:

When you have a template that is set up using the following structure:

...
Parameters:
  MyLayerParameter:
    Description: The SSM parameter name of the ARN of the Lambda Layer shared across components.
    Type: AWS::SSM::Parameter::Value<String>
....
Resources:
  HelloWorld:
    Type: AWS::Serverless::Function
    Properties:
      Runtime: python3.7
      Handler: index.handler
      CodeUri: ./src/hello_world
      Layers: [ !Ref MyLayerParameter ]

This is perfectly deployable using aws cloudformation deploy however sam deploy will raise the following error:

Error: /environment/service/MyLayerArn is an Invalid Layer Arn.

So my guess is that sam expects the Layer to be an ARN (which is fair) but the type of the parameter is: AWS::SSM::Parameter::Value<String> so, in this case, it should first resolve the SSM Parameter (or ignore it at all)

It looks similar to #1069 but there the build fails on the resolution of the parameter and a workaround is to provide a “dummy” ARN that would cause the build to run. This cannot be used with the deploy command because we actually want to point to the actual SSM Parameter.

Steps to reproduce:

  • Store the LayerARN in a parameter
  • Deploy a stack with passing the SSM Parameter path and not the ARN

Observed result:

2020-12-11 13:34:05,177 | 27 resources found in the template 2020-12-11 13:34:05,178 | Sending Telemetry: {‘metrics’: [{‘commandRun’: {‘awsProfileProvided’: False, ‘debugFlagProvided’: True, ‘region’: ‘eu-west-1’, ‘commandName’: ‘sam deploy’, ‘duration’: 1375, ‘exitReason’: ‘InvalidLayerVersionArn’, ‘exitCode’: 1, ‘requestId’: ‘d7ae766c-2183-4b8b-a690-4eeb47ee2382’, ‘installationId’: ‘e741b3c2-9523-4a05-821d-de8ee1f419de’, ‘sessionId’: ‘3f2f89cc-9c45-48b3-b58a-9443de63e98a’, ‘executionEnvironment’: ‘CLI’, ‘pyversion’: ‘3.7.7’, ‘samcliVersion’: ‘1.13.2’}}]} 2020-12-11 13:34:05,884 | HTTPSConnectionPool(host=‘aws-serverless-tools-telemetry.us-west-2.amazonaws.com’, port=443): Read timed out. (read timeout=0.1) Error: /foo/core/ModelsLambdaLayerArn is an Invalid Layer Arn.

Expected result:

I would expect that if the parameter is an SSM Parameter that is being resolved by CloudFormation, either sam does this before evaluation as well or it would skip the check when performing a deploy.

Additional environment details (Ex: Windows, Mac, Amazon Linux etc)

  1. OS: Mac OS X 11.0.1
  2. sam --version: SAM CLI, version 1.13.2

About this issue

  • Original URL
  • State: open
  • Created 4 years ago
  • Reactions: 5
  • Comments: 15 (1 by maintainers)

Most upvoted comments

A bit of a stupid workaround but I found the most elegant way was simply to add a default value to the parameter that is a known layer that exists

Parameters:
  MyLayerParameter:
    Description: The SSM parameter name of the ARN of the Lambda Layer shared across components.
    Type: AWS::SSM::Parameter::Value<String>
    Default: ArnOfExistingLayer

In case anyone is looking for a workaround: my solution is to lookup the value from SSM elsewhere in the build process and pass the value in using --parameter-overrides

Workaround Example

For example, here’s a makefile using this method for both the build and the deploy steps

SHARED_LAYER := $(shell  aws ssm get-parameter --name '/path/to/lambda/arn' --query 'Parameter.Value' --output text)

deploy: .aws-sam/build/template.yaml
    sam deploy --template .aws-sam/build/template.yaml --parameter-overrides LambdaArnParam=${SHARED_LAYER}

.aws-sam/build/template.yaml:
    sam build --parameter-overrides LambdaArnParam=${SHARED_LAYER

Note that you’ll have to change the Parameter type to a simple String

Hi @Nr18 Here is another yet “dirty” workaround that works for me. It is a rather flexible, then proposed by @RobertoC27.

  1. A separate template with Layer definition:

     Resources:
    
       UtilsLayer:
         Type: AWS::Serverless::LayerVersion
         Properties:
           LayerName: shared-layer
           ContentUri: ...
           ...
     
     Outputs:
    
       LambdaUtilsLayer:
         Description: Lambda layer with shared resources
         Value: !Ref UtilsLayer
       LambdaUtilsLayerVersionNumber:
         Description: Lambda layer Version Number
         # Get the Layer Version Number
         Value: !Select [7, !Split [":", !Ref UtilsLayer]]
    
  2. Master template with nested Stacks definition (including LambdaLayerStack):

     ...
     Resources:
     
       LambdaLayerStack:
         Type: AWS::CloudFormation::Stack
         Properties:
            TemplateURL: ...
     
       AuthService:
         Type: AWS::CloudFormation::Stack
         Properties:
           TemplateURL: ...
           Parameters:
             ...
             UtilsLayerVersionNumber: !GetAtt LambdaLayerStack.Outputs.LambdaUtilsLayerVersionNumber
             ...
         DependsOn:
           - LambdaLayerStack
     ...
    
  3. Lambda layer “consumer” (Service template):

     ...
     Parameters:
       ...
       UtilsLayerVersionNumber:
         Type: String
         # Default value MUST be present
         Default: 1
         ...
       Resources:
         ...
         FoorBarFunction:
           Type: AWS::Serverless::Function
           Properties:
             ...
             Layers:
               - !Sub arn:aws:lambda:${AWS::Region}:${AWS::AccountId}:layer:shared-layer:${UtilsLayerVersionNumber}
            ...
    

The name of Lambda Layer can also be grabbed from splitting results and passed as parameter

Yes, I initially went that route as well, you can see it in the issue history me closing and re-opening the ticket 😉 I actually linked them together in this post: https://github.com/aws/aws-sam-cli/issues/1069#issuecomment-744546634

The major difference between them is #1069 is all about not being able to build and there is a workaround for that, this bug does not have a workaround and cannot use the workaround of #1069, unfortunately…

So I am ok with closing this one if the context and use case is added to #1069 otherwise we can keep them separate, but then the duplicate label should be removed.