From Servlerless to AWS SAM

By Marcin Piczkowski Comment
Subscribe

In this post we will demonstrate how to switch Serverless Reservation application to use AWS SAM instead of default Serverless configuration.

Why even bother to use SAM?

Because it is an Amazon specification. Amazon will support it and base development tools on this standard. E.g. they recently released AWS SAM Local beta version, so we can expect more tools like that.

All files mentioned in this post are available on Github.

Thanks to @SAPessi there is now serverless-sam plugin available that makes porting easy.

What we need to do is installing the plugin:

npm install --save-dev serverless-sam

Then add it in configuration. In serverless.yml we have to add:

plugins:
  - serverless-sam

Now you can generete SAM config out of Serverless config with command:

serverless sam export --output ./sam-template-generated.yml

It’s time to test deployment to Cloud Formation using AWS SAM template. We will follow standard AWS CLI procedure:

  1. Package the application
aws cloudformation package \
    --template-file sam-template-generatec.yml \
    --s3-bucket sam-reservation-bucket \
    --output-template-file packaged-sam-template.yaml \
    --profile dev_profile

Another argument output-template-file is used to name the file which will be created based on the input file. There is nothing interesting to know about this file. AWS CLI will enhance the input file with additional information like bucket name.

Argument profile is optional and depends which profile we used in our serverless application and which profiles we have configured in AWS client. In our case we are using a profile named dev_profile so we will use the same one for AWS SAM.

Important to note in the command above is the argument s3-bucket. When we used Serverless to deploy our application it automatically zipped files and deployed to AWS S3 auto-created bucket. E.g in the image below you can see example bucket and content in AWS:

image
AWS S3 bucket used by Serverless

Now when we use SAM we need to specify existing bucket name. If the bucket does not exist you will see message:

Unable to upload artifact /home/marcin/git/cloudly.tech/severless/serverless-reservation/.serverless/serverless-reservation.zip referenced by CodeUri parameter of GraphQl resource.
S3 Bucket does not exist. Execute the command to create a new bucket
aws s3 mb s3://sam-reservation-bucket

Let’s then execute the mentioned command with our AWS profile aws s3 mb s3://sam-reservation-bucket --profile dev_profile Then let’s execute the package command again. It will ZIP the project and upload to AWS bucket.

You can read more about the package command in AWS docs

  1. Deploy the application
aws cloudformation deploy \
    --template-file packaged-sam-template.yaml \
    --stack-name sam-reservation \
    --capabilities CAPABILITY_IAM \
    --profile dev_profile \
    --region us-east-1

The arguments profile and region are needed only if you do not have default profile specified for AWS CLI tool. In our case we have a separate profile used for Serverless called dev_profile and we use us-east-1 region so we will use the same for AWS SAM deployment.

There are a few more argument to explain:

  • template-file is the same argument as in the previous aws cloudformation package command.
  • stack-name is the name of Cloud Formation stack where the app should be deployed. We can either deployed on the existing stack created by Serverless or create a new stack. To see existing stacks you can check AWS web console and go to Cloud Formation service, e.g. this is what I see:
image
Existing Cloud Formation stacks in AWS web console

Let’s start from fresh and deploy to new stack called sam-reservation.

  • capabilities is the list of capabilities that Cloud Formation needs to create the stack. The only valid values are CAPABILITY_IAM and CAPABILITY_NAMED_IAM.

You can read more about the deploy command in AWS docs

At this point you may encounter an error which I did:

Failed to create the changeset: Waiter ChangeSetCreateComplete failed: Waiter encountered a terminal failure state Status: FAILED. Reason: Transform AWS::Serverless-2016-10-31 failed with: Invalid Serverless Application Specification document. Number of errors found: 1. Resource with id [GraphQl] is invalid. Either 'DefinitionUri' or 'DefinitionBody' property is required

This is a known issue which exists in the older version of AWS CLI. If you have this problem please upgrade AWS CLI to the latest version and start again from step 1 above (Package the application)

Now the command should succeed and you should see new Cloud Formation stack created in AWS web console. You can navigate to API Gateway service and check the endpoint URL:

image
AWS API Gateway endpoint URL

However, we are not yet done. Let’s invoke the endpoint:

 curl -X POST -H "Content-Type: application/json" \
  -d 'mutation ExampleMutation {createItem(id: "1", name: "test sam", description: "test sam") {name, createdAt}}' \
   https://483271bxfk.execute-api.us-east-1.amazonaws.com/prod/graphql

The response is:

{"message": "Internal server error"}

It is not very descriptive but we can get more information if we go to AWS web console and try to test API Gateway endpoint there:

image
Test API Gateway

Click on Test button and then just submit form with default inputs. In the logs at the end you would see:

Execution failed due to configuration error: Invalid permissions on Lambda function
Method completed with status: 500

I found a description of similar issue in AWS Github and it seems to be a bug, but easy to solve. You can navigate to API Gateway method execution and edit Lambda Function in Integration Request settings.

image
Fix Lambda permission

Just edit with the same value and save.

It will popup with message about added permission:

image
Confirm edit

Now you can test again APi Gateway and see there is a different error, a new one:

User: arn:aws:sts::811338114639:assumed-role/sam-reservation-GraphQlRole-YWTCNCLLLIM4/sam-reservation-GraphQl-1DOKQAA8K5HS8 is not authorized to perform: dynamodb:PutItem on resource: arn:aws:dynamodb:us-east-1:811338114639:table/Items",

The issue is with Lambda function permissions to DynamoDB which we defined in Serverless yaml file, which was then translated to SAM file by the plugin we used. This section in sam-template-generated.yml is not valid according to AWS format:

      Policies:
        - Effect: Allow
          Action:
            - 'dynamodb:Query'
            - 'dynamodb:Scan'
            - 'dynamodb:GetItem'
            - 'dynamodb:PutItem'
            - 'dynamodb:UpdateItem'
            - 'dynamodb:DeleteItem'
          Resource: 'arn:aws:dynamodb:us-east-1:*:table/Items'

it should be

      Policies:
        - Version: '2012-10-17'
          Statement:
            - Effect: Allow
              Action:
                - 'dynamodb:Query'
                - 'dynamodb:Scan'
                - 'dynamodb:GetItem'
                - 'dynamodb:PutItem'
                - 'dynamodb:UpdateItem'
                - 'dynamodb:DeleteItem'
              Resource: 'arn:aws:dynamodb:us-east-1:*:table/Items'

Please note the missing Statement element in the generated file.

If we fix the file, then repeat commands aws cloudformation package and aws cloudformation deploy, fix the API Gateway permissions as described above, we will be able to invoke the API correctly. Just remember to remove DynamoDB Items table and CloudFormation stack we used before, otherwise you would face naming conflicts. AWS allows to update the existing stack but for sake of simplicity we will not do it this time.

You see that migrating Serverless configuration to AWS SAM was not as straight forward as we would expect. We faced some issues. This shows advantages of using Serverless and its simplicity.

My personal thought is that if I was to use Serverless, I would not go for maintaining both Serverless and SAM configurations at the same time. An advantage of using SAM is that it is Amazon specification and Amazon will continue adding more support and tools for it in the future, some of which may beat Serverless. E.g. recently Amazon has released AWS SAM Local It allows running Lambda functions locally in Docker container. Using it together with DynamoDB Local allows developers to setup testing environment without connection to AWS.

References

comments powered by Disqus