0
votes

I've been working on a website that shows my resume and also has a visitor count on it while using AWS Services. I've gotten a "hard coded" way but now would like to be able to deploy the API Gateway, Lambda code, and DynamoDB all in one go if I want to change things. The SAM CLI template seems to be perfect for it. I've scrounged together some code off the internet and from my own but it hasn't quite come together completely still. The lambda function is triggered by an APIGateway and then will access the visitor count in DynamoDB, add 1 to it and then both send it to the website and then store the new number in DynamoDB.

I realized my old Lambda function will not work as the DynamoDB names will change with the SAM Template. Is there a way to use the global variables and implement that into my Lambda function? I'm also realizing I created an additional column to store the count in my current dynamoDB table called 'visits' and am not sure how to add a column in the SAM Template?

template.yaml

AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Description: >
  resume_backend

  Sample SAM Template for resume_backend

# More info about Globals: https://github.com/awslabs/serverless-application-model/blob/master/docs/globals.rst
Globals:
  Function:
  Runtime: python3.8 # language used at runtime
  Timeout: 180 # timeout for a given lambda function execution
  Environment:
      Variables: # these will be important later
          DYNAMO_TABLE: !Ref DynamoVisitorTable
          DB_ENDPOINT: http://dynamodb.us-east-1.amazonaws.com
          REGION_NAME: us-east-1


Resources:
  VisitorCountFunction:
    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: visitorCount/
      Handler: app.lambda_handler
      Policies: AmazonDynamoDBFullAccess # default IAM policy 
      Runtime: python3.8
      Events:
        # Api:
        #   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: /visitorCount
        #     Method: GET
        cors: true
        ApiGatewayApi:
          Type: AWS::Serverless::Api
          Properties:
            StageName: Prod
            Cors:
              AllowMethods: "'POST, GET'"
              AllowHeaders: "'X-Forwarded-For'"
              AllowOrigin: "'www.example.com'"
              MaxAge: "'600'"
              AllowCredentials: True
              Path: /visitorCount
              Method: GET


  DynamoVisitorTable:
    Type: AWS::Serverless::SimpleTable # if you want to define a more complex table, use AWS::DynamoDB::Table
    TableName: websiteVisitorTable
    PrimaryKey:
        Name: webPageCounted
        Type: S
    ProvisionedThroughput:
        ReadCapacityUnit: 5
        WriteCapacityUnits: 5
    Tags:
        AppType: Serverless

lambda_function: app.py

##
##aws SDK for python
import boto3

def lambda_handler(event, context):
    TABLE_NAME="websiteCounter"
    KEYPAIR = {'visitsMain'}
    client = boto3.resource("dynamodb")
    table = client.Table(TABLE_NAME)
    
    response = table.get_item(
        Key={
            "visitsMain":"mainPage",
        }
        )
    item = response['Item']
    table.update_item(
        Key={
            "visitsMain":"mainPage",
        },
        UpdateExpression='SET visits = :val1',
        ExpressionAttributeValues={
            ':val1': item['visits'] + 1
        }
    )
    return (item['visits'] + 1)

2

2 Answers

0
votes

As the comment in the template suggests, you should use Serverless syntax rather than SAM for this part. The difference is that SAM is a simplification of Serverless, it works well for small stacks or stateless functions, but it can only get you so far. The good thing is that you can mix Serverless statements right into your SAM template, since SAM compiles down to Serverless anyway.

These docs will help you define the table exactly as you want it. But after your first deployment, you'll need to add a condition to check if the table exists, otherwise further deployments will fail as they will try to create the table again. To avoid this complexity, some people will argue to create two different templates, one for the tables, and another for the functions. Either way though, you'll probably want to define your tables in the Serverless syntax and make sure to add a policy to the functions to allow them to access the correct table.

To take a step back, since this is for your resume, you should consider using the AWS CDK instead of SAM/Serverless since this is really the latest in infrastructure definition and is vastly superior to text templates, in my opinion (TypeScript yay!). Also, the tutorials, examples and resources are much better. The AWS CDK is however AWS specific, whereas Serverless is platform agnostic, but I don't consider that a strong enough argument for not using it.

0
votes

Did you check if the lambda function has access to DynamoDB?

Here's is an interesting article how to assig permissions. It can be done either manually or within the SAM template:

Link: https://aws.amazon.com/premiumsupport/knowledge-center/lambda-sam-template-permissions/

Syntax inside the SAM template:

Resources:
  MyFunction:
    Type: 'AWS::Serverless::Function'
    Properties:
      Handler: index.handler
      Runtime: nodejs8.10
      CodeUri: 's3://my-bucket/function.zip'
      Policies:
      # Give DynamoDB Full Access to your Lambda Function
      - AmazonDynamoDBFullAccess