Olá pessoal, tudo bem?
Hoje vamos falar sobre mais uma ferramenta que gosto muito e que facilita bastante a criação de aplicações serverless, o AWS Serverless Application Model (AWS SAM).
O que é o SAM?
O AWS SAM é um framework que oferece novos elementos para o CloudFormation específicos para serverless. Na realidade, ele é um superset do mesmo, pois estes novos componentes foram criados utilizando os transforms do CloudFormation.
Além destes novos elementos, o SAM fornece também uma CLI, utilizada para gerenciar todo o ciclo de vida das aplicações, desde a criação até o deploy.
O que há de novo no template?
Para ilustrar algumas diferenças, abaixo segue o exemplo de um template criado usando o SAM.
AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Description: Sample SAM Template for SamExample
Resources:
HelloWorldFunction:
Type: AWS::Serverless::Function
Properties:
CodeUri: hello_world/
Handler: app.lambda_handler
Runtime: python3.7
Events:
HelloWorld:
Type: Api
Properties:
Path: /hello
Method: get
Outputs:
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
O exemplo acima é o template padrão do AWS SAM, criado com o projeto. Por ser simples, facilita a visualização da nova estrutura.
Como podemos ver, a primeira diferença no SAM está na linha 2, o Transform que ele utiliza para gerar o template final do CloudFormation.
Na linha 7, vemos um novo tipo de resource, o AWS::Serverless::Function
. Como veremos em mais detalhes a seguir, todos os tipos do SAM usam o namespace AWS::Serverless::*
.
O restante é praticamente idêntico ao CloudFormation, suportando funções intrínsecas, importvalue, e as demais features do mesmo.
Para entendermos um pouco melhor sobre sua estrutura, abaixo falaremos um pouco sobre cada novo tipo fornecido pelo framework.
Function – AWS::Serverless::Function
HelloWorldFunction:
Type: AWS::Serverless::Function
Properties:
CodeUri: hello_world/
Handler: app.lambda_handler
Runtime: python3.7
Events:
HelloWorld:
Type: Api
Properties:
Path: /hello
Method: get
Como o nome sugere, Function representa Lambda functions. As propriedades que aparecem no exemplo representam:
- CodeUri: O local onde está o código da função Lambda;
- Handler: Quem é o handler que será o ponto de entrada da Lambda;
- Runtime: Qual a runtime da Lambda;
- Events: Aqui podemos definir quem é o gatilho (ou gatilhos) que irá disparar a função. Atualmente o SAM suporta S3, SNS, DynamoDB, Kinesis, SQS, API, Schedule, CloudWatchEvents, CloudWatchLogs, IoTRules, Cognito e Alexa Skills.
No exemplo acima, a Lambda será disparada através de um API Gateway, criada durante o deploy, quando efetuarmos um GET no path /hello
. Se tivermos mais de uma função no template, que tenha API como fonte de eventos, quando fizermos o deploy será criada apenas uma API Gateway contendo todos os endpoints relacionados a estes eventos.
API – AWS::Serverless::API
HelloWorldAPI:
Type: AWS::Serverless::Api
Properties:
StageName: production
DefinitionUri: mydefinition.yml
EndpointConfiguration: REGIONAL
Variables:
ConnectionString: ProductionConnectionString
Se quisermos utilizar o Swagger para definirmos a estrutura da API Gateway, podemos usar o tipo API. No exemplo, as principais propriedades são:
- StageName: Define o nome do estágio da API Gateway;
- DefinitionUri: Uri com a definição do template do swagger;
- EndpointConfiguration: Tipo do endpoint da API (REGIONAL, PRIVATE ou EDGE);
- Variables: São as stage variables do estágio da API Gateway;
SimpleTable – AWS::Serverless::SimpleTable
HelloWorldTable:
Type: AWS::Serverless::SimpleTable
Properties:
PrimaryKey:
Name: productId
Type: String
ProvisionedThroughput:
ReadCapacityUnits: 1
WriteCapacityUnits: 1
TableName: Products
O tipo SimpleTable, é uma versão simplificada de uma tabela do DynamoDB. Simplificada pois é possível definir apenas uma PrimaryKey, e nada mais. Inclusive, podemos remover todo o nodo Properties
, que valores default serão usados para criar a tabela.
LayerVersion – AWS::Serverless::LayerVersion
HelloWorldLayer:
Properties:
LayerName: HelloWorldLayer
ContentUri: 's3://<mybucket>/sam-example-layer.zip'
RetentionPolicy: Delete
Description: Sam example Layer Version
LicenseInfo: 'License information'
CompatibleRuntimes:
- python2.7
- python3.6
- python3.7
LayerVersion representa uma layer que pode ser compartilhada entre as funções Lambda.
Neste componente, podemos evidenciar as propriedades:
- ContentUri: Onde está localizado o conteúdo da layer;
- CompatibleRuntimes: Runtimes que são compatíveis com esta layer;
- RetentionPolicy: Apesar de estar definido como Delete no exemplo, deletando as versões anteriores da layer, o valor padrão é Retain, que faz com que à cada novo deploy, as versões anteriores permaneçam.
Application – AWS::Serverless::Application
HelloWorldApplication:
Type: AWS::Serverless::Application
Properties:
Location:
ApplicationId: 'arn:aws:serverlessrepo:us-east-1:012345678901:applications/sam-application-example'
SemanticVersion: 1.0.0
Parameters:
StringParameter: SAM Test
TimeoutInMinutes: 10
Com o tipo Application, podemos aninhar outras aplicações à aplicação que estamos criando. Estas aplicações podem ser do AWS SAR (Serverless Application Repository) ou podem ser templates armazenados no S3. Este componente é bacana, pois facilita a reutilização de aplicações e componentes criadas por nós e outros desenvolvedores.
Sobre as propriedades mostradas no exemplo:
- Location: Como citei anteriormente, pode ser do SAR ou do S3. No exemplo, apontamos para o SAR. No S3, teríamos algo como:
Location: https://s3.amazonaws.com/<mybucket>/app.yaml
;
- Parameters: Os parâmetros enviados à aplicação;
- TimeoutInMinutes: Após a transformação, o tipo Application resulta em uma nested stack do CloudFormation. Este parâmetro indica o tempo que o CloudFormation irá esperar pela criação desta stack aninhada, antes de cancelar a operação.
Outros elementos importantes
Globals
Globals:
Function:
Timeout: 14
handler: app.lambda_handler
É possível definir um elemento Globals no template, onde definimos propriedades que são comuns a todas as funções Lambdas existentes no template.
No exemplo acima, estamos definindo o timeout e o caminho do handler de forma global.
Policies
Resources:
HelloWorldFunction:
Type: AWS::Serverless::Function
Properties:
CodeUri: hello_world/
Handler: app.lambda_handler
Runtime: python3.7
Policies:
- EC2DescribePolicy: {}
Events:
HelloWorld:
Type: Api
Properties:
Path: /hello
Method: get
O AWS SAM possui diversos templates prontas de policies, para facilitar a construção das aplicações. Ela também permite a utilização de políticas gerenciadas e políticas inline (criadas no template), possibilitando bastante flexibilidade ao gerirmos o controle de permissões.
Para saber mais sobre estas templates, basta clicar aqui para acessar a documentação.
Criando um projeto
Falando um pouco agora sobre o SAM CLI, o primeiro passo é instalarmos o mesmo. A instalação pode ser ser feita seguindo está documentação: Instalando o AWS SAM CLI.
Em seguida, podemos criar um projeto com o comando:
sam init --runtime python3.7 --name sam-example
Basicamente, no comando acima ele irá criar um app chamado sam-example, usando o python3.7. É possível remover todos os argumentos do comando, executando apenas sam init
, e neste caso ele criar uma aplicação chamada sam-app, usando nodejs10.* como runtime.

Sobre a estrutura inicial do projeto, vale comentar um pouco sobre:
- events/*: Eventos de exemplo para serem usados em teste;
- hello_world/*: Contém o handler e os arquivos relacionados à Lambda HelloWorld;
- tests/*: Diretório para incluirmos testes;
- template.yaml: Template do AWS SAM.
Compilando o projeto
Para compilarmos um projeto do SAM, executamos o comando:
sam build
Como produto da compilação, o SAM gera um diretório chamado .aws-sam no root do projeto. Ali ele copia o arquivo de template, e cria um diretório para a função incluindo todas as dependências para executá-la.

Testando as aplicações localmente
Os testes locais são feitos através do comando sam local
. Nele temos algumas opções de execução:
- start-lambda: Inicia a lambda localmente em um endpoint, permitindo que executemos ela através do SDK ou do CLI.
- start-api: Simula uma API, permitindo que efetuemos testes usando HTTP;
- invoke: Executa a lambda localmente, apenas uma vez.
Existe outra opção no comando local
, chamada generate-event
. Esta opção cria exemplos de evento das fontes suportadas pelo SAM (S3, API, Kinesis, etc), para usarmos nos testes.
Publicando a aplicação
Quando a aplicação estiver pronta para ser publicada, precisamos executar dois passos:
1) Empacotar a aplicação
Para empacotar a aplicação, utilizamos a instrução:
sam package --s3-bucket sam-example
A execução deste comando irá gerar um pacote, enviá-lo para o S3 e fazer uma cópia do template, substituindo o sourceUri da função pelo caminho criado no S3. O novo sourceUri ficará similar ao exemplo abaixo (linha 4):
Resources:
HelloWorldFunction:
Properties:
CodeUri: s3://sam-example/80d971cd7ef24874575dd3f2563888c7
Events:
HelloWorld:
Properties:
Method: get
Path: /hello
Type: Api
Handler: app.lambda_handler
Runtime: python3.7
Type: AWS::Serverless::Function
2) Publicar a aplicação
Por fim, para publicarmos a aplicação com o SAM, executamos o comando:
sam deploy --template-file <template-file-location>.yaml --stack-name sam-example
Obrigatoriamente precisamos informar ambos os argumentos:
- template-file: Template gerado pelo comando package (com o sourceUri modificado);
- stack-name: O nome da stack do CloudFormation que será criada/atualizada.
Conclusão
Acredito que o framework mais conhecido e utilizado para este tipo de aplicação seja o Serverless Framework. Contudo, para aqueles que forem trabalhar com AWS, o SAM também é uma excelente opção para iniciar um projeto. Ele possui algumas de features avançadas, como canary deployment (que não comentei neste post, mas você pode acessar aqui), e executa sobre o CloudFormation.
Um abraço, e até a próxima!