Unlocking Serverless with AWS Lambda and IAM
As I mentioned earlier we find the code for our two Lambda functions create-user and get-user under their respective folders.
import json
import boto3
import os
client = boto3.client(‘dynamodb’)
table_name = os.getenv(“TABLE_NAME”)
def handler(event, _):
body = json.loads(event[‘body’])
data = client.put_item(
TableName=table_name,
Item={
‘id’: {
‘S’: body[‘id’]
},
‘name’: {
‘S’: body[‘name’]
}
}
)
response = {
‘statusCode’: 200,
‘body’: json.dumps({“id”: body[‘id’], “name”:body[‘name’]}),
‘headers’: {
‘Content-Type’: ‘application/json’,
‘Access-Control-Allow-Origin’: ‘*’
},
}
return response
import json
import boto3
import os
client = boto3.client(‘dynamodb’)
table_name = os.getenv(‘TABLE_NAME’)
def handler(event, _):
data = client.get_item(
TableName=table_name,
Key={
‘id’: {
‘S’: event[‘pathParameters’][‘id’]
}
}
)
response = {
‘statusCode’: 200,
‘body’: json.dumps(data[‘Item’]),
‘headers’: {
‘Content-Type’: ‘application/json’,
‘Access-Control-Allow-Origin’: ‘*’
},
}
return response
But the real core of a serverless project is the template file. This is where all the resources and configuration of your serverless architecture is defined.
AWSTemplateFormatVersion: “2010-09-09”
Transform: AWS::Serverless-2016-10-31
Description: >
user-sam-app
A sample user api
Globals:
Function:
Runtime: python3.9
Timeout: 3
Handler: app.handler
Architectures:
– x86_64
Environment:
Variables:
TABLE_NAME: !Ref UserTable
Resources:
CreateUserFunction:
Type: AWS::Serverless::Function
Properties:
CodeUri: create_user/
Events:
AccountsAPI:
Type: Api
Properties:
Path: /users
Method: post
Policies:
– DynamoDBWritePolicy:
TableName: !Ref UserTable
GetUserFunction:
Type: AWS::Serverless::Function
Properties:
CodeUri: get_user/
Events:
AccountsAPI:
Type: Api
Properties:
Path: /users/{id}
Method: get
Policies:
– DynamoDBReadPolicy:
TableName: !Ref UserTable
UserTable:
Type: AWS::DynamoDB::Table
Properties:
TableName: UserTable
BillingMode: PAY_PER_REQUEST
AttributeDefinitions:
– AttributeName: id
AttributeType: S
KeySchema:
– AttributeName: id
KeyType: HASH
Outputs:
UserAPI:
Description: “API Gateway endpoint URL for Prod stage”
Value: !Sub “https://${ServerlessRestApi}.execute-api.${AWS::Region}.amazonaws.com/Prod/users/”
CreateUserFunction:
Description: “Create User Lambda Function ARN”
Value: !GetAtt CreateUserFunction.Arn
CreateUserFunctionIamRole:
Description: “Implicit IAM Role created for Create User function”
Value: !GetAtt CreateUserFunctionRole.Arn
GetUserFunction:
Description: “Get User Lambda Function ARN”
Value: !GetAtt GetUserFunction.Arn
GetUserFunctionIamRole:
Description: “Implicit IAM Role created for the Get User function”
Value: !GetAtt GetUserFunctionRole.Arn
UserTable:
Value: !Ref UserTable
Description: DynamoDb Table to store users
It can definitely look intimidating at first but lets walk through each property to understand what each is accomplishing for us.
Globals:
Function:
Runtime: python3.9
Timeout: 3
Handler: app.handler
Architectures:
– x86_64
Environment:
Variables:
TABLE_NAME: !Ref UserTable
Under Globals we can define properties for multiple resources at once. In this case each of the properties under Function apply to all Lambda functions defined in this file. Of note is the Handler which specifies the entry point for our functions and the Environment Variable TABLE_NAME which will be available to our functions to determine exactly which DynamboDB table to read and write from.
Under Resources you can define almost any AWS resource! Feel free to experiment!
Here we’ve created our two Lambda Functions with type AWS::Serverless::Function and our DynamoDB Table with type AWS::DynamoDB::Table.
Next, lets zoom in on our create-user Lambda function.
CreateUserFunction:
Type: AWS::Serverless::Function
Properties:
CodeUri: create_user/
Events:
AccountsAPI:
Type: Api
Properties:
Path: /users
Method: post
Policies:
– DynamoDBWritePolicy:
TableName: !Ref UserTable
Starting from the top, the Type AWS::Serverless::Function is actually a unique resource type provided by SAM that will implicitly create resources in order to help us quickly accomplish the Lambda configuration we saw earlier.