285 lines
		
	
	
	
		
			6.9 KiB
		
	
	
	
		
			Markdown
		
	
	
	
	
	
			
		
		
	
	
			285 lines
		
	
	
	
		
			6.9 KiB
		
	
	
	
		
			Markdown
		
	
	
	
	
	
---
 | 
						|
tags: [AWS]
 | 
						|
---
 | 
						|
 | 
						|
# AWS SAM
 | 
						|
 | 
						|
SAM stands for **serverless application model**. It is a framework developed by
 | 
						|
AWS to simplify the process of building, deploying and managing serverless
 | 
						|
applications. It provides a concise syntax for defining the components of a
 | 
						|
serverless application, such as
 | 
						|
[Lambda functions](zk/Lambda_programming_model.md),
 | 
						|
[API gateway](/zk/AWS_API_Gateway.md) and database tables.
 | 
						|
 | 
						|
The SAM infrastructure is defined in a YAML file which is then deployed to AWS.
 | 
						|
SAM syntax gets transformed into CloudFormation during the deployment process.
 | 
						|
(CloudFormation is a broader and more robust AWS tool for large, highly
 | 
						|
scaleable infrastructures).
 | 
						|
 | 
						|
## Key features of SAM
 | 
						|
 | 
						|
- Single deployment configuration
 | 
						|
- Integration with development tools
 | 
						|
- Local testing and debugging
 | 
						|
- Built on AWS CloudFormation
 | 
						|
 | 
						|
## Main technologies required
 | 
						|
 | 
						|
### Docker
 | 
						|
 | 
						|
Whilst SAM can be used to create a deployable file for AWS it can also be run as
 | 
						|
a container for local development with Docker.
 | 
						|
 | 
						|
### AWS CLI
 | 
						|
 | 
						|
This is installed using Python and allows you to interact directly with AWS via
 | 
						|
the command-line.
 | 
						|
 | 
						|
### AWS SAM CLI
 | 
						|
 | 
						|
See
 | 
						|
[https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/install-sam-cli.html](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/install-sam-cli.html)
 | 
						|
 | 
						|
## Setting up credentials for the AWS CLI
 | 
						|
 | 
						|
You require an access key for the given
 | 
						|
[IAM user](zk/AWS_User_management_and_roles.md#iam). You should create an IAM
 | 
						|
account specific to the project with bounded permissions.
 | 
						|
 | 
						|
```
 | 
						|
aws configure
 | 
						|
AWS Access Key ID [None]: AK*******
 | 
						|
AWS Secret Access Key [None]: ukp******
 | 
						|
Default region name [None]:
 | 
						|
Default output format [None]:
 | 
						|
```
 | 
						|
 | 
						|
This information can be found in the Security Credentials section of the given
 | 
						|
[IAM](zk/AWS_User_management_and_roles.md#iam) user:
 | 
						|
 | 
						|

 | 
						|
 | 
						|
### Switching between credentials
 | 
						|
 | 
						|
You should set up a different IAM user for each project.
 | 
						|
 | 
						|
You can do this with:
 | 
						|
 | 
						|
```sh
 | 
						|
aws configure --profile <profile-name>
 | 
						|
```
 | 
						|
 | 
						|
This will then ask you to add the credentials for the user.
 | 
						|
 | 
						|
You can switch between different credentials for the user as follows:
 | 
						|
 | 
						|
```sh
 | 
						|
AWS_PROFILE=<profile-name> sam build
 | 
						|
 | 
						|
```
 | 
						|
 | 
						|
## Starting a SAM project
 | 
						|
 | 
						|
First create a directory for your project which will serve as the repository:
 | 
						|
 | 
						|
```sh
 | 
						|
mkdir aws-sam-learning
 | 
						|
cd aws-sam-learning
 | 
						|
```
 | 
						|
 | 
						|
Then we can use the `sam` cli to bootstrap the project:
 | 
						|
 | 
						|
```sh
 | 
						|
sam init --runtime nodejs16.x
 | 
						|
```
 | 
						|
 | 
						|
We can just click through and accept the basic HelloWorld Lambda.
 | 
						|
 | 
						|
This will create the Lambda as well as an API Gateway trigger URL.
 | 
						|
 | 
						|
### `template.yaml`
 | 
						|
 | 
						|
This is autogenerated and details the main constituents of the project. There
 | 
						|
are lots of fields but the most important are the following:
 | 
						|
 | 
						|
```yaml
 | 
						|
HelloWorldFunction:
 | 
						|
  Type: AWS::Serverless::Function
 | 
						|
  Properties:
 | 
						|
    CodeUri: hello-world/
 | 
						|
    Handler: app.lambdaHandler
 | 
						|
    Runtime: nodejs16.x
 | 
						|
    Architectures:
 | 
						|
      - x86_64
 | 
						|
    Events:
 | 
						|
      HelloWorld:
 | 
						|
        Type: Api
 | 
						|
        Properties:
 | 
						|
          Path: /hello
 | 
						|
          Method: get
 | 
						|
```
 | 
						|
 | 
						|
This details the location of the [handler function](/Lambda_handler_function.md)
 | 
						|
which is contained at the path `hello-world/app.js`:
 | 
						|
 | 
						|
```js
 | 
						|
exports.lambdaHandler = async (event, context) => {
 | 
						|
  try {
 | 
						|
    // const ret = await axios(url);
 | 
						|
    response = {
 | 
						|
      statusCode: 200,
 | 
						|
      body: JSON.stringify({
 | 
						|
        message: "hello world",
 | 
						|
        // location: ret.data.trim()
 | 
						|
      }),
 | 
						|
    };
 | 
						|
  } catch (err) {
 | 
						|
    console.log(err);
 | 
						|
    return err;
 | 
						|
  }
 | 
						|
 | 
						|
  return response;
 | 
						|
};
 | 
						|
```
 | 
						|
 | 
						|
It also lists the `get` event that we can use to call API Gateway and trigger
 | 
						|
the Lambda.
 | 
						|
 | 
						|
The full template is below:
 | 
						|
 | 
						|

 | 
						|
 | 
						|
## Adding our own code
 | 
						|
 | 
						|
We will create our own function and API Gateway trigger.
 | 
						|
 | 
						|
We will place our function after the existing `HelloWorldFunction`
 | 
						|
 | 
						|
```yaml
 | 
						|
ClockFunction:
 | 
						|
  Type: AWS::Serverless::Function
 | 
						|
  Properties:
 | 
						|
    CodeUri: clock/
 | 
						|
    Handler: handler.clock
 | 
						|
    Runtime: nodejs16.x
 | 
						|
  Events:
 | 
						|
    ClockApi:
 | 
						|
      Type: Api
 | 
						|
      Properties:
 | 
						|
        Path: /clock
 | 
						|
        Method: get
 | 
						|
```
 | 
						|
 | 
						|
We can test the syntax with:
 | 
						|
 | 
						|
```sh
 | 
						|
sam validate
 | 
						|
```
 | 
						|
 | 
						|
Just like with `HelloWorld`, we will create a directory for this function:
 | 
						|
`clock` and we will initialise it as an `npm` project.
 | 
						|
 | 
						|
```sh
 | 
						|
mkdir clock
 | 
						|
cd clock
 | 
						|
npm init
 | 
						|
```
 | 
						|
 | 
						|
We will use `handler.js` as our root, handler function.
 | 
						|
 | 
						|
We have said in the template file that our `Handler: handler.clock`, therefore
 | 
						|
the main function in the `handler` module should be `clock`:
 | 
						|
 | 
						|
```js
 | 
						|
const moment = require("moment");
 | 
						|
 | 
						|
exports.clock = async (event) => {
 | 
						|
  console.log("Clock function run");
 | 
						|
  const message = moment().format();
 | 
						|
  const response = {
 | 
						|
    statusCode: 200,
 | 
						|
    body: JSON.stringify(message),
 | 
						|
  };
 | 
						|
  return response;
 | 
						|
};
 | 
						|
```
 | 
						|
 | 
						|
The directory structure is as follows:
 | 
						|
 | 
						|

 | 
						|
 | 
						|
When we call the API Gateway path `/clock` with `GET`, our function will be
 | 
						|
triggered.
 | 
						|
 | 
						|
## Deploying the project
 | 
						|
 | 
						|
We will now deploy our project to AWS from the local environment.
 | 
						|
 | 
						|
The process is as follows:
 | 
						|
 | 
						|
1. Build
 | 
						|
2. Package
 | 
						|
3. Deploy
 | 
						|
 | 
						|
### Build
 | 
						|
 | 
						|
We need to install the runtime dependencies for the function. We do this by
 | 
						|
running `sam build`. This ignores test files and development dependencies and
 | 
						|
installs the project dependencies and source files to a temporary subdirectory.
 | 
						|
 | 
						|

 | 
						|
 | 
						|
The build directory is `.aws-sam/build/`. There will be a subdirectory for each
 | 
						|
of our files.
 | 
						|
 | 
						|
### Package
 | 
						|
 | 
						|
As noted, CloudFront handles the deployment of the application. It can only
 | 
						|
receive one file as an input. The packaging process consists in creating that
 | 
						|
single file.
 | 
						|
 | 
						|
The packaging proces will first archive all of the project artefacts into a zip
 | 
						|
file and then upload that to [S3](zk/AWS_S3.md). A reference to this S3 entity
 | 
						|
is then provided to CloudFormation.
 | 
						|
 | 
						|

 | 
						|
 | 
						|
The command is as follows:
 | 
						|
 | 
						|
```sh
 | 
						|
sam package
 | 
						|
  --template-file template.yaml
 | 
						|
  --output-template-file pkg.yml
 | 
						|
  --region eu-west-1
 | 
						|
```
 | 
						|
 | 
						|
This will automatically create a hashed bucket name for you in S3 (I have tried
 | 
						|
to add my own naming but it doesn't comply.)
 | 
						|
 | 
						|
### Local development with Docker
 | 
						|
 | 
						|
In order to work with your application locally without actually sending requests
 | 
						|
to AWS and using credit, you can run a local instance.
 | 
						|
 | 
						|
See [Local AWS Development with SAM](zk/Local_AWS_development_with_SAM.md).
 | 
						|
 | 
						|
### Deploy
 | 
						|
 | 
						|
Once you have packaged the app you can deploy with `sam deploy --guided`. This
 | 
						|
will talk you through the defaults and will deploy the package to
 | 
						|
CloudFormation. In CloudFormation each individual project is called a **stack**.
 | 
						|
 | 
						|
If we then go to Cloud Formation we will see the deployed application.
 | 
						|
 | 
						|

 | 
						|
 | 
						|
## Call the endpoint
 | 
						|
 | 
						|
If we now go to the Lambda console, we will see our function listed, and the API
 | 
						|
Gateway endpoint under `triggers`:
 | 
						|
 | 
						|

 | 
						|
 | 
						|
We can then call this from Postman to check everything is working as it should:
 | 
						|
 | 
						|

 |