DEV Community

Cover image for Deploy AWS resources using serverless framework
Rashwan Lazkani
Rashwan Lazkani

Posted on • Edited on

Deploy AWS resources using serverless framework

In this article I will show you how you can deploy your AWS resources using the serverless framework and IaC (Infrastructure as Code).

I will use the AWS services AWS Lambda and Amazon DynamoDB. I will also use the Serverless framework and NodeJS.

I will show you how you can deploy differnt Lambdas using serverless and also how you create a DynamoDB table using serverless.

The project will be a simple ToDo app where we will create the Lambdas for adding and fetching an item.

Note that this article will focus on the serverless.yml file and the code to the Lambdas will not be the focus hence not explained. If you have any comments on those or questions feel free to ask these in the comments.

Prerequisites:

  1. AWS account
  2. AWS CLI installed - here is a guide of how to give your AWS CLI access to your AWS account
  3. Serverless framework installed - guide here

Initialize project

Open terminal and type:

serverless
Enter fullscreen mode Exit fullscreen mode

You will now be prompted with some options, choose:

AWS - Node.js - HTTP API 
Enter fullscreen mode Exit fullscreen mode

Add a name to your project or click enter to select the suggested name.

For the next question, I have chosen n:

Do you want to login/register to Serverless Dashboard? n
Enter fullscreen mode Exit fullscreen mode

For the next question, I have chosen n:

Do you want to deploy now? n
Enter fullscreen mode Exit fullscreen mode

Open the newly created project in an editor, I will be using Visual Studio Code.

Open project

Let's go through what we currently have in our project structure:

  • .gitignore
  • handler.js - our handler with a sample module created
  • README.md
  • serverless.yml - this is the core file that we will build to deploy our services to AWS

serverless.yml

A service is configured via a serverless.yml file where you define your functions, the events that trigger them, and the AWS resources to deploy.

Our current file looks like this:

service: aws-node-http-api-project
frameworkVersion: '3'

provider:
  name: aws
  runtime: nodejs14.x

functions:
  hello:
    handler: handler.hello
    events:
      - httpApi:
          path: /
          method: get
Enter fullscreen mode Exit fullscreen mode

service = what we're calling our service
frameworkVersion = the version of the serverless framework that is used
provider = the cloud provider, runtime for our functions,
functions = current functions, we currently have one in our handler.js
functions.hello = this is where we have added our functions properties such as the handler and the events which triggers the event

Commands that we will be using

These are the commands that we will be using:

  • serverless deploy - Deploy changes
  • serverless deploy -f - Deploy a specific function
  • serverless info - View deployed endpoints and resources
  • serverless invoke - Invoke deployed functions
  • serverless --help - Discover more commands

A tip: you can use the command sls instead of typing serverless. So for example sls deploy

Let's start building our add item function

Start by adding a src folder in your project and remove the current handler.js file.

Inside the src folder add a file called add.js and add the following code:

const { v4 } = require('uuid')
const AWS = require('aws-sdk')

const add = async (event) => {

  const dynamoDB = new AWS.DynamoDB.DocumentClient();

  const { todo } = JSON.parse(event.body);
  const createdAt = new Date().toDateString()
  const id = v4();

  const newTodo = {
    id,
    todo,
    createdAt,
    completed: false
  };

  await dynamoDB.put({
    TableName: 'TodoTable',
    Item: newTodo
  }).promise();

  return {
    statusCode: 200,
    body: JSON.stringify(newTodo)
  };
};

module.exports = {
  handler: add
}
Enter fullscreen mode Exit fullscreen mode

As you can see we have code added to add the item to our DynamoDB database but that is not yet created, but it's okey we'll create it after this step.

Now go to your serverless.yml file and we'll add this new function that we just created.

First of all we want to add the region in the provider section. Add the region you want to create your services in, mine is in region: eu-north-1. Note that the indentation is important in the .yml file and the region has to be in the same indentation as the runtime.

Now go down to the functions section and remove the hello function and start by adding the following indented one tab below the functions:

add: // this is the name of our function
    handler: src/add.handler // our handler which is located in the src folder and is called add.handler
   events: // how we call this function
       - httpApi: // - this dash in .yml means an array which we will use to define multiple parameters
          path: / // no specific path will be used but it could be for example /addItem
          method: post // this is a post
Enter fullscreen mode Exit fullscreen mode

We're now ready to deploy our first Lambda function through IaC. Open the terminal and type:

sls deploy
Enter fullscreen mode Exit fullscreen mode

The deploy can take a minute or two but when it's done you will can an output like the following:

Deploying aws-node-http-api-project to stage dev (eu-north-1)

✔ Service deployed to stack aws-node-http-api-project-dev (104s)

endpoint: POST - https://...execute-api.eu-north-1.amazonaws.com/
functions:
  add: aws-node-http-api-project-dev-add (1.9 kB)

Enter fullscreen mode Exit fullscreen mode

The endpoint is how you reach this Lambda and since we didn't add any specific path then it's enough for this function to do the following to add a new item:

method: POST
endpoint: https://...execute-api.eu-north-1.amazonaws.com/
body: {
           "todo": "My first item"
      }

Enter fullscreen mode Exit fullscreen mode

If you go to your AWS console you should now be able to see your newly created function in the region you created it in.

Let's create our database

Go to your serverless.yml file and add the following below the entire functions section:

resources:
  Resources:
      TodoTable:
        Type: AWS::DynamoDB::Table
        Properties:
          TableName: TodoTable
          BillingMode: PAY_PER_REQUEST
          AttributeDefinitions:
            - AttributeName: id
              AttributeType: S
          KeySchema:
            - AttributeName: id
              KeyType: HASH
Enter fullscreen mode Exit fullscreen mode

What we have done here is the following:

  1. Created a new resource called TodoTable
  2. The type is AWS::DynamoDB::Table
  3. We have added a couple of properties such as TableName and BillingMode
  4. AttributeDefinitions represents an attribute for describing the key schema for the table and indexes
  5. KeySchema specifies the attributes that make up the primary key for a table or an index

You can now deploy your infrastructure by typing:

sls deploy
Enter fullscreen mode Exit fullscreen mode

If you run your add function you will notice that it will not write anything to your table and this is because our functions cannot access the DynamoDB table we've just created and we need to provide an IAM role for this. Go to provider and under region, add the following:

 iamRoleStatements:
    - Effect: Allow
      Action:
        - dynamodb:*
      Resource:
        - <your DynamoDB's ARN that can be accessed from the console>
Enter fullscreen mode Exit fullscreen mode

In this role we're saying that All our functions is allowed to do any actions in our DynamoDB table. Please not that for testing purposes I have added the action dynamodb:*, do not use this when going live with a table.

If you now deploy again and run the POST function you will see that you have an item in your DynamoDB table.

Let's fetch a specific item from our table

In your src folder create a new file called fetch.js and add the following code:

const AWS = require('aws-sdk')

const fetch = async (event) => {

    const dynamoDB = new AWS.DynamoDB.DocumentClient();
    const { id } = event.pathParameters;
    let todo;

    try {
        const result = await dynamoDB.get({
            TableName: 'TodoTable',
            Key: { id }
        }).promise();
        todo = result.Item;
    } catch (error) {
        console.log(error);
    };

    return {
        statusCode: 200,
        body: JSON.stringify(todo)
    };
};

module.exports = {
    handler: fetch
}
Enter fullscreen mode Exit fullscreen mode

Now go to your serverless.yml file and add the following under the add function:

fetch:
    handler: src/fetch.handler
    events:
      - httpApi:
          path: /todo/{id}
          method: get

Enter fullscreen mode Exit fullscreen mode

As you can see it follows the same structure as the add but with the main difference is that here we want to be able to pass in an id so that we can fetch for that in the database.

Run a deploy and you will get the following output:

Deploying aws-node-http-api-project to stage dev (eu-north-1)

✔ Service deployed to stack aws-node-http-api-project-dev (50s)

endpoints:
  POST - https://...execute-api.eu-north-1.amazonaws.com/
  GET - https://...execute-api.eu-north-1.amazonaws.com/todo/{id}
functions:
  add: aws-node-http-api-project-dev-add (2.3 kB)
  fetch: aws-node-http-api-project-dev-fetch (2.3 kB)
Enter fullscreen mode Exit fullscreen mode

As you can see we have two endpoints and two functions. Now you can do the following to access a specific item in your table:

method: GET
endpoint: https://...execute-api.eu-north-1.amazonaws.com/todo/{id}
Enter fullscreen mode Exit fullscreen mode

Deploy

A final word about deploys, right now we have made changes to our serverless.yml file hence we need to make an sls deploy which takes 1-2 minutes but let's say that you only make changes in your add.js file and want to deploy those changes then it's enough with the following which takes a couple of seconds:

sls deploy -f add
Enter fullscreen mode Exit fullscreen mode

Above will only deploy that function.

Summary

IaC is a good way of having a code-based approach which makes it easier to get more done in less time. There is a lot of different providers for using IaC and this was one example.

Any comments, questions or discussions are always welcome!

Top comments (0)