Meet AWS SAM CLI: sam init

Eric Johnson
12 min readAug 6, 2019

Welcome to the first installment of “Meet AWS SAM CLI”. In this series I will provide simple instructions on the most common use cases of AWS SAM CLI and how it can help you as a developer move faster and be more efficient in your development process.

The AWS SAM CLI is a command line tool that operates on an AWS SAM template and application code. With the AWS SAM CLI:

  • Initialize a serverless application from a template.
  • Invoke and debug a Lambda function locally.
  • Create a deployment package for your serverless application.
  • Deploy your serverless application to the AWS cloud.

To install the SAM CLI please refer to https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/serverless-sam-cli-install.html. For this post I will be using SAM CLI v0.18.0.

The init command

The first command I am going to look at is “sam init”. To get started, let’s run the basic init command and see what happens:

sam init

SAM CLI will set up a basic serverless “hello-world” application under a folder called sam-app. This application consists of the following structure and defaults to a runtime of node.js v8.10.

sam-app
|-- README.md <- The instructions file
|-- event.json <- API Gateway event payload
|-- hello-world <- Source code for Lambda function
| |-- app.js <- Lambda function code
| |-- package.json <- NodeJS dependencies
| `-- tests <- Unit tests
| `-- unit
| `-- test-handler.js
`-- template.yaml <- SAM Template

While this might be enough to get started, SAM CLI can do much more to help you create the perfect starting point for your serverless application. To discover more, let’s run the init command again but this time with the help flag.

sam init --help

This command will output a description of the Usage, Common usage, and Options available. I am going to skip the Common usage examples for now and cover them a bit further down. For now, take a look at the Usage and Options. For the Usage, the following is displayed.

“Initialize a serverless application with a SAM template, folder structure for your Lambda functions, connected to an event source such as APIs, S3 Buckets or DynamoDB Tables. This application includes everything you need to get started with serverless and eventually grow into a production scale application.

This command can initialize a boilerplate serverless app. If you want to create your own template as well as use a custom location please take a look at our official documentation.”

For the Options, the following items are displayed.

-l, --location

Possible Values: TEXT

Description: Template location (git, mercurial, http(s), zip, path).

-r, --runtime

Possible Values: nodejs , nodejs6.10, nodejs8.10, nodejs10.x, dotnet, dotnetcore, dotnetcore1.0, dotnetcore2.0, dotnetcore2.1, go, go1.x, python, python2.7, python3.6, python3.7, java, java, ruby, ruby2.5

Description: Lambda Runtime of your app.

-d, --dependency-manager

Possible Values: npm, pip, bundler, cli-package, maven gradle

Description: Dependency manager of your Lambda runtime.

-o, --output-dir

Possible Values: PATH

Description: Where to output the initialized app into.

-n, --name

Possible Values: TEXT

Description: Name of your project to be generated as a folder.

--no-input

Description: Disable prompting and accept default values defined template config.

--debug

Description: Turn on debug logging to print debug message generated by SAM CLI.

-h, --help

Description: Show this message and exit.

Options explained

Some of these options are self-explanatory, however, I want to dig in a bit and see if I can surprise you with some SAM capabilities you might be unaware of.

The runtime option

-r, --runtime

With the runtime option I can tell SAM CLI to generate an application template in my preferred programming runtime. As for runtime families, I have Node, Dotnet, Go, Python, Java, and Ruby. Additionally, you can see version options in each of the runtimes as well. There are some generics for each family of runtimes, as well as some specifics. I have listed the runtimes again with a legend to show which are default and which I have to call specifically.

While nodejs8.10 is the default if you do not pass a runtime to init, each of the languages also has a default. For example, passing dotnet, dotenetcore will default to dotnetcore2.1.

The dependency-manager option

-d, --dependency-manager

I might not use the dependency-manager option all the time, but comes in handy when I want to choose a different dependency-manager for my serverless application that is not the default. For example, if I am building a serverless application with the Java runtime I would use the following command:

sam init -r java

The simple application structure that is generated looks like this:

sam-app
|-- HelloWorldFunction
| |-- pom.xml
| `-- src
| |-- main
| | `-- java
| | `-- helloworld
| | |-- App.java
| | `-- GatewayResponse.java
| `-- test
| `-- java
| `-- helloworld
| `-- AppTest.java
|-- README.md
`-- template.yaml

Notice the pom.xml file in the root of the HelloWorldFunction. This file is used by the Maven to handle dependencies for a Java application. Therefore, I now know that Maven is the default dependency-manager for Java. Let’s say I want to use Gradle to manage my application dependencies instead:

sam init -r java -d gradle

Now when I look at my application structure, I see it has changed.

sam-app
|-- HelloWorldFunction
| |-- build.gradle
| |-- gradle
| | `-- wrapper
| | |-- gradle-wrapper.jar
| | `-- gradle-wrapper.properties
| |-- gradlew
| |-- gradlew.bat
| `-- src
| |-- main
| | `-- java
| | `-- helloworld
| | |-- App.java
| | `-- GatewayResponse.java
| `-- test
| `-- java
| `-- helloworld
| `-- AppTest.java
|-- README.md
`-- template.yaml

Instead of the pom.xml file, I see several Gradle files that allow me to use Gradle as a dependency manager.

Again, this is not an option I will use much as I am generally a Node.js developer. However, it offers a great deal of flexibility when my language of choice might have more than one dependency manager.

The output-directory option

-o, --output-directory

This option comes in handy if I need to create an app in a location other than the current directory. For example, I have a directory called project1 that is empty. I can run the following command:

sam init -o ./project1

After this, I get the following structure:

project1
`-- sam-app
|-- README.md
|-- event.json
|-- hello-world
| |-- app.js
| |-- package.json
| `-- tests
| `-- unit
| `-- test-handler.js
`-- template.yaml

I now have an application that exists in the project1 directory. However, I also notice that it is under a folder called sam-app. This is the default application name given to all applications by the SAM CLI. In the name option we will look at how to change that.

The name option

-n, --name

This option allows me to name the application. The name I choose will be the name of the directory containing the application as well as in the description in the template.yaml file.

sam init -n my-app

This will render the following structure.

my-app
|-- README.md
|-- event.json
|-- hello-world
| |-- app.js
| |-- package.json
| `-- tests
| `-- unit
| `-- test-handler.js
`-- template.yaml

Also, the template.yaml file will have the following entry.

Description: >  my-app  Sample SAM Template for my-app

Now I want to re-run my command from the output option section.

sam init -o ./project1 -n my-app

And now I have this.

project1
`-- my-app
|-- README.md
|-- event.json
|-- hello-world
| |-- app.js
| |-- package.json
| `-- tests
| `-- unit
| `-- test-handler.js
`-- template.yaml

Using the output-dir and name options, I can now specify the output location and the name of the application folder.

The location option

-l, --location

This option is probably one of the biggest surprises in the SAM CLI as it is useful and somewhat misunderstood. This option is helpful when creating new applications as well as when modifying current applications. I want to break this into two parts to help explain more clearly.

Creating a new application
As we have seen before, we can use sam init to create new applications from templates that are managed by AWS. However, it is also possible for me to create my own templates as starters for applications. I am going to create a basic application to use an example.

sam init -n my-example-app

Here is the resulting template structure.

my-example-app
|-- README.md
|-- event.json
|-- hello-world
| |-- app.js
| |-- package.json
| `-- tests
| `-- unit
| `-- test-handler.js
`-- template.yaml

I also want to take a look at the template.yaml file to see what the AWS managed application template looks like.

AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Description: >
my-example-app
Sample SAM Template for my-example-app# More info about Globals: https://github.com/awslabs/serverless-application-model/blob/master/docs/globals.rst
Globals:
Function:
Timeout: 3
Resources:
HelloWorldFunction:
Type: AWS::Serverless::Function # More info about Function Resource: https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md#awsserverlessfunction
Properties:
CodeUri: hello-world/
Handler: app.lambdaHandler
Runtime: nodejs8.10
Events:
HelloWorld:
Type: Api # More info about API Event Source: https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md#api
Properties:
Path: /hello
Method: get
Outputs:
# ServerlessRestApi is an implicit API created out of Events key under Serverless::Function
# Find out more about other implicit resources you can reference within SAM
# https://github.com/awslabs/serverless-application-model/blob/master/docs/internals/generated_resources.rst#api
HelloWorldApi:
Description: "API Gateway endpoint URL for Prod stage for Hello World function"
Value: !Sub "https://${ServerlessRestApi}.execute-api.${AWS::Region}.amazonaws.com/Prod/hello/"
HelloWorldFunction:
Description: "Hello World Lambda Function ARN"
Value: !GetAtt HelloWorldFunction.Arn
HelloWorldFunctionIamRole:
Description: "Implicit IAM Role created for Hello World function"
Value: !GetAtt HelloWorldFunctionRole.Arn

There is a lot of helpful information in here. However, I am pretty familiar with the template and I don’t need all the comments. There are also a few changes I make in the template every time I create a new application.

Here is an example of a basic, single-function template that I like to start with.

AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Description: My serverless application
Globals:
Function:
Timeout: 3
Handler: app.lambdaHandler
Runtime: nodejs8.10
Tracing: Active
Layers:
- !FindInMap [RegionMap, !Ref "AWS::Region", layer]
Mappings:
RegionMap:
us-east-1:
layer: arn:aws:lambda:us-east-1:98765432100:layer:aws-sdk:6
us-west-2:
layer: arn:aws:lambda:us-west-2:98765432100:layer:aws-sdk:6
eu-central-1:
layer: arn:aws:lambda:eu-central-1:98765432100:layer:aws-sdk:2
Resources:
ServiceFunction:
Type: AWS::Serverless::Function
Properties:
CodeUri: service/
Events:
Service:
Type: Api
Properties:
Path: /
Method: get
Outputs:
ServiceApi:
Description: "API Gateway endpoint URL for Prod stage for service function"
Value: !Sub "https://${ServerlessRestApi}.execute-api.${AWS::Region}.amazonaws.com/Prod/"

Some key changes are:

  1. I removed the comments.
  2. I added a Mappings to load a common layer I use in my Node development (AWS SDK, X-Ray).
  3. Moved many of the function parameters into the Globals area.
  4. Renamed the function to be more applicable to my needs.
  5. Removed some of the outputs that I do not care about.

In addition to the changes to the template.yaml file, I also updated the directory structure according to the references in the template, modified the README.md to reflect the relevant changes, and removed some files that I do not use. I can continue to make these changes manually every time I create a new application, or I can take advantage of the location option in SAM CLI.

In order to make the templates usable by SAM CLI, they must be formatted in the cookiecutter configuration. Cookiecutter is a CLI that allows you to create projects from templates. As a developer, I can setup a common file structure, dependencies, language, and other settings to use as a starting point for my projects. See https://cookiecutter.readthedocs.io/en/latest/index.html for more information on cookiecutter.

Using the cookiecutter format, I have built a template based on my changes that I will demonstrate as an example. My template can be found at https://github.com/singledigit/sam-template-node. In the cookiecutter template, my directory structure looks like this:

sam-template-node
|-- README.md
|-- cookiecutter.json
|-- license.txt
`-- {{cookiecutter.project_slug}}
|-- service
| |-- app.js
| |-- package.json
| `-- tests
| `-- unit
| `-- test-handler.js
`-- template.yaml

To make use of this template I can use the location option:

sam init -l gh:singledigit/sam-template-node

When prompted for a project_name, I will enter “My New App”. For the project_slug I will enter “my-new-app” which should also be the default value. The resulting directory structure looks like this:

my-new-app
|-- service
| |-- app.js
| |-- package.json
| `-- tests
| `-- unit
| `-- test-handler.js
`-- template.yaml

I now see that my project has been created from everything in the {{cookiecutter.project_slug}} directory from above. And the folder has been named from my project_slug value. I now look inside my new template.yaml file and it looks like this.

AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Description: My serverless application
Globals:
Function:
Timeout: 3
Handler: app.lambdaHandler
Runtime: nodejs8.10
Tracing: Active
Layers:
- !FindInMap [RegionMap, !Ref "AWS::Region", layer]
Mappings:
RegionMap:
us-east-1:
layer: arn:aws:lambda:us-east-1:98765432100:layer:aws-sdk:6
us-west-2:
layer: arn:aws:lambda:us-west-2:98765432100:layer:aws-sdk:6
eu-central-1:
layer: arn:aws:lambda:eu-central-1:98765432100:layer:aws-sdk:2
Resources:
ServiceFunction:
Type: AWS::Serverless::Function
Properties:
CodeUri: service/
Events:
Service:
Type: Api
Properties:
Path: /
Method: get
Outputs:
ServiceApi:
Description: "API Gateway endpoint URL for Prod stage for service function"
Value: !Sub "https://${ServerlessRestApi}.execute-api.${AWS::Region}.amazonaws.com/Prod/"

Now I have a simple starting point for all my Node.js applications that have the starting file structure I want and the included Lambda layers that I need.

Modifying a current application

Not only can I create a new application from a cookiecutter template, I can also add a cookiecutter template to an existing application. A great example of this is the “Cookiecutter Pipeline for SAM based Serverless Apps” which can be found at https://github.com/aws-samples/cookiecutter-aws-sam-pipeline.

With this template I can, very simply, add a continuous integration and continuous delivery (CI/CD) pipeline to my current application I just created. From the root of the my-new-app directory, I am going to run the following.

sam init -l gh:aws-samples/cookiecutter-aws-sam-pipeline

When I run this, I will be prompted for a new project name and asked to select between GitHub and CodeCommit. I entered the project cicd and selected GitHub as my repository. After completing these steps, I will examine the directory structure of my application for changes.

my-new-app
|-- Pipeline-Instructions.md
|-- buildspec.yaml
|-- pipeline-sample.png
|-- pipeline.yaml
|-- service
| |-- app.js
| |-- package.json
| `-- tests
| `-- unit
| `-- test-handler.js
`-- template.yaml

I notice that four new files have been added to my application:

  1. Pipeline-Instructions.md: instructions on deploying the new CICD pipeline.
  2. buildspec.yml: the build commands for the pipeline to run when building my application.
  3. pipeline-sample.png: a graphical representation of my new CICD pipeline.
  4. pipeline.yaml: the infrastructure for my new CICD pipeline stored as code.

To enable the CI/CD pipeline, all I need to do is follow the instructions in the Pipeline-Instructions.md file. This involves setting a few parameters and creating the CloudFormation stack. If you are interested in more on the CI/CD cookiecutter template and how to use it, please refer to this Start Right video.

That’s it! By using the location option, I was able to create a new application from my own custom template and add a CI/CD from an aws-sample cookiecutter template. Using the cookiecutter configuration you can build your own starter templates as well.

What locations can I use?
Before I move on, I want to talk about the locations that are valid for this option. In my example I used GitHub, but it is also possible to use local files or folders, and online files. When formatting my location string, it is important to note that Cookiecutter knows abbreviations for GitHub (gh), Bitbucket (bb), and GitLab (gl) projects, but you can also give it the full URL to any repository.

Repositories
GitHUb

sam init --location gh:aws-samples/cookiecutter-aws-sam-pythonsam init --location https://github.com/aws-samples/cookiecutter-aws-sam-pythonsam init --location git+ssh://git@github.com/aws-samples/cookiecutter-aws-sam-python.git

BitBucket (git or mercurial (hg))

sam init --location https://bitbucket.org/repo/template-namesam init --location hg+ssh://hg@bitbucket.org/repo/template-name

Files
Local zip file

sam init --location /path/to/template.zip

Remote zip file

sam init --location https://example.com/path/to/template.zip

Local folder

sam init --location /path/to/template/folder

To learn more about location options, refer back to the sam init — help in the terminal and read the usage section, or get a full explanation from the CookieCutter documentation.

The no-input option

--no-input

This option directly relates to the location option. When calling a cookiecutter template, the author of the template will have defaults for prompted values. If I pass the –no-input flag to sam init, then the defaults will be used without prompting for values.

The debug option

--debug

Passing the –debug flag to sam init will cause SAM CLI to output debug information pertaining to the CLI itself and not my application.

The help option

-h, --help

Passing the –help flag to sam init will display the help message we generated at the beginning of this post.

Conclusion

AWS SAM CLI is designed to help developers move quickly and efficiently in serverless application development and sam init is where it all starts. Using options for runtime, name, and location allows me to quickly create a starting point for my application without having to manually build from scratch. I can get even more specific to my preferences by using cookiecutter templates. Hopefully, you have seen how SAM CLI can get you up and running quickly and efficiently with sam init in the SAM CLI, and are ready to start coding more efficiently with SAM!

Happy coding!

Background image credit: Gerd Altmann from Pixabay

--

--

Eric Johnson

Christian, husband, dad of 5, musician, Senior Developer Advocate — Serverless for @AWScloud . Opinions are my own. #Serverless #ServerlessForEveryone