DEV Community

Paul Eggerling-Boeck
Paul Eggerling-Boeck

Posted on • Originally published at awstip.com on

Deploying an AWS Lambda with SAM

I’ve found that AWS SAM is a simple way to deploy and manage an AWS Lambda function and it’s associated services. In this article, I’ll describe what I learned as I used it for a bare bones deploy of a simple Lambda function.

Graphic showing the words AWS Lambda and two of the AWS icons for Lambda

The AWS Serverless Application Model (SAM) is a framework for building serverless applications at AWS. It provides a simplified way to define the Amazon API Gateway APIs, AWS Lambda functions, and Amazon DynamoDB tables needed by your serverless application. For the purposes of this article, we’ll focus on using SAM solely for AWS Lambda.

With SAM, you can define your serverless application using a YAML or JSON template file. This template is used to specify all the details about your Lambda function, many of the AWS resources it uses (e.g. IAM Roles, Cloudwatch Log Groups, EventBridge triggers, etc.), and the information needed to deploy it to AWS.

SAM also provides a set of command-line tools for packaging, deploying, and managing your serverless applications. These tools simplify the process of deploying your application, managing your AWS resources, and handling updates and rollbacks. GitHub has integrated SAM CLI into GitHub Actions which makes setting up your CI/CD workflows really simple.

Of course, you don’t NEED to use SAM to deploy or manage a Lambda function at AWS. You can create everything I’m about to show you using the AWS Management Console. SAM is probably overkill for the silly little Lambda function we’ll be working with today, but as you start to create more complex Lambdas, using the console is going to become an increasingly bad idea. Here are a few reasons why:

  1. You’re (presumably) a human being. Human beings make mistakes. If you’re defining your Lambda manually using the console, you’re probably going to forget some key steps somewhere along the line.
  2. If you want to create more than a single Lambda function, you have to do all of the (mostly) same work for each one. Sure, each Lambda function may have different requirements and needed resources, but there are a number of common steps that need to be performed every time.
  3. If you’re like me, you’re just experimenting and don’t want to be spending a ton of money on this little experiment. If you set up everything manually, you have to tear it all down manually. Don’t forget to remove anything though or you may end up with a surprise AWS bill at the end of the month. ( HINT: if you’re using SAM to deploy a Lambda, you’ll have an S3 cost if nothing else. Granted, it’ll probably be small, but it will be > $0. So don’t forget to sam delete.)

Now that you have a vague idea of what SAM is and a few good reasons to use it, let’s dive into the details. First I need to spell out a few prerequisites (see the embedded links, or the resource links at the end of the article for help with some of these):

  1. Fork my GitHub repository (medium-simple-java-lambda) and clone it to your workstation
  2. Create AWS account and an IAM user that has an access key created
  3. Install and configure AWS CLI and SAM CLI on your workstation
  4. Configure AWS credentials in your terminal session (i.e. set environment variables)
  5. Create GitHub repository secrets for GitHub Actions to work (see .github/workflows/sam-deploy.yml in the repository for details)
sam build --template-file sam.template.yaml
sam deploy \
  --template-file .aws-sam/build/template.yaml \
  --no-confirm-changeset \
  --no-fail-on-empty-changeset \
  --stack-name simple-java-lambda \
  --s3-bucket <bucket name> \
  --capabilities CAPABILITY_IAM CAPABILITY_NAMED_IAM \
  --region <region name e.g. us-east-1>
Enter fullscreen mode Exit fullscreen mode

SAM is able to detect that this project is set up to build with Gradle, and will run the Gradle build when you execute the sam build command. The sam deploy command will upload the built artifact to S3 into the bucket specified by --s3-bucket which must already exist. Take note that these two commands each take a different template file as a parameter. The sam build command takes the SAM template file and creates a full CloudFormation template which is what the sam deploy command uses. Looking at the command output, we see that SAM deploys the Lambda as part of a full CloudFormation application stack which, not surprisingly, has the name specified by the --stack-name parameter.


CloudFormation events from stack operations (refresh every 0.5 seconds)
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
ResourceStatus ResourceType LogicalResourceId ResourceStatusReason                            
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
CREATE_IN_PROGRESS AWS::IAM::Role SimpleJavaFunctionRole -                                               
CREATE_IN_PROGRESS AWS::IAM::Role SimpleJavaFunctionRole Resource creation Initiated                     
CREATE_COMPLETE AWS::IAM::Role SimpleJavaFunctionRole -                                               
CREATE_IN_PROGRESS AWS::Lambda::Function SimpleJavaFunction -                                               
CREATE_IN_PROGRESS AWS::Lambda::Function SimpleJavaFunction Resource creation Initiated                     
CREATE_COMPLETE AWS::Lambda::Function SimpleJavaFunction -                                               
CREATE_COMPLETE AWS::CloudFormation::Stack simple-java-lambda -                                               
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

Successfully created/updated stack - simple-java-lambda in us-east-1
Enter fullscreen mode Exit fullscreen mode

For me, this is the one downside of using SAM. CloudFormation stacks can take forever to create, update, and delete. But I suppose it’s a decent tradeoff for the simplicity you can achieve. The other thing I’ve learned about CloudFormation stacks is that it’s possible to get them into an invalid state where the whole stack must be deleted in the AWS CloudFormation console because the SAM CLI will not function properly.

Unless you went off script and defined one or more triggers for your shiny new Lambda function, you’ll have to log in to the AWS Lambda console and use the built-in test functionality to see that it returns the string “Hello World!”

To delete your Lambda and ensure that you’re not going to incur any sneaky AWS charges, you can run the sam delete command as shown below. Notice that the SAM CLI deletes the S3 objects that were created earlier as well as the CloudFormation stack itself.

sam delete --stack-name simple-java-lambda
Are you sure you want to delete the stack simple-java-lambda in the region us-east-1? [y/N]: y
  Do you want to delete the template file 389d4cb200cca272c3f9f211fce1afc5.template in S3? [y/N]: y
  - Deleting S3 object with key 481802c173d3f3ef734bf763ade4be88
  - Deleting S3 object with key 389d4cb200cca272c3f9f211fce1afc5.template
  - Deleting Cloudformation stack simple-java-lambda

Deleted successfully
Enter fullscreen mode Exit fullscreen mode

Now that you’ve seen the SAM CLI commands used to build, deploy, and delete a Lambda, Let me show you how I set up GitHub Actions workflows so that I never have to actually type those commands. First the build/deploy workflow:

name: SAM Build/Deploy
on:
  push:
    branches:
      - main
    paths:
      - 'sam.template.yaml'
      - 'src/main/**'
      - 'build.gradle'
jobs:
  build-deploy:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout Code
        uses: actions/checkout@v2
      - name: Set up JDK 8
        uses: actions/setup-java@v3
        with:
          java-version: 8
          distribution: 'adopt'
      - name: Set Up Python
        uses: actions/setup-python@v2
      - name: Set up AWS SAM
        uses: aws-actions/setup-sam@v1
      - name: Set up AWS Credentials
        uses: aws-actions/configure-aws-credentials@v1
        with:
          aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
          aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
          aws-region: ${{ secrets.AWS_REGION }}
      - name: Run SAM Build
        run: sam build -t sam.template.yaml

      - name: Run SAM Deploy
        run: |
          sam deploy \
            --template-file .aws-sam/build/template.yaml \
            --no-confirm-changeset \
            --no-fail-on-empty-changeset \
            --stack-name simple-java-lambda \
            --s3-bucket ${{ secrets.AWS_S3_BUCKET }} \
            --capabilities CAPABILITY_IAM CAPABILITY_NAMED_IAM \
            --region ${{ secrets.AWS_REGION }}
Enter fullscreen mode Exit fullscreen mode

You can see that after some Java and Python setup, we’re leveraging the aws-actions/setup-sam@v1 step so that GitHub can run the sam build and sam deploy commands as the last steps of the workflow. There’s also a delete workflow that runs sam delete instead of sam deploy. Checkout the .github/workflows folder in the GitHub repository.

To be sure, AWS SAM is capable of much more complicated tasks than what I’ve presented here. This is essentially the bare minimum of what you can specify to deploy a serverless application at AWS. Check out the AWS Serverless Application Model documentation for all the details you didn’t get here.

Was this article helpful? Did you learn anything? Did I get it all wrong? Did you find it a waste of time? I’d love to hear any feedback you have and/or help you get past any stumbling blocks you may have encountered along the way!

Resources:


Top comments (0)