Serverless framework

Introduction

Develop, deploy, troubleshoot and secure your serverless applications with radically less overhead and cost by using the Serverless Framework. The Serverless Framework consists of an open source CLI and a hosted dashboard. Together, they provide you with full serverless application lifecycle management.


References

Serverless Framework

https://www.serverless.com/framework/docs/getting-started

Serverless.yml Reference

Including stage parameters, etc.

https://www.serverless.com/framework/docs/providers/aws/guide/serverless.yml

serverless.yml - Dynamically replace config values

https://www.serverless.com/framework/docs/providers/aws/guide/variables


Install

npm install serverless

//npm install [package-name]@[version-number]

//-g = global

//-i = skip the execution of any scripts that are defined in the package.json file 

Upgrade

npm update -g serverless

Initial setup

serverless

Commands

sls client

Deploy static files to, eg S3, using the plugin finch

sls client deploy --stage dev

sls create

sls create --template aws-nodejs

sls deploy

serverless deploy --region eu-west-1 --stage dev

sls dynamo

Install local DynamoDB, which can be use later w/ sls offline start

sls dynamodb install

Migrate database

sls dynamodb migrate

sls info

Service information, endpoints (eg: of a lambda), functions, layers

serverless info

serverless info --region eu-west-1 --stage labs

sls invoke

Invoke a deployed function

sls invoke -f hello

Invoke a non-deployed function

sls invoke local -f hello

sls logs

Retrieve logs of function

sls logs myfunction

sls offline

sls offline start

sls remove

The sls remove command will remove the deployed service, defined in your current working directory, from the provider. 

serverless remove --region eu-west-1 --stage dev


Plugins

serverless-add-api-key

Eg: sixqueue

https://www.serverless.com/plugins/serverless-add-api-key

To create api key and usage pattern (if they don't already exist) and associate them to the Rest Api.

It matches API Key by name, ignores it if it already exists.


serverless-plugin-ifelse

Eg: sixqueue

https://www.serverless.com/plugins/serverless-plugin-ifelse

It allows conditional serverless.yml


serverless-prune-plugin

Eg: sixqueue

https://www.serverless.com/plugins/serverless-prune-plugin

Following deployment, the Serverless Framework does not purge previous versions of functions from AWS, so the number of deployed versions can grow out of hand rather quickly. This plugin allows pruning of all but the most recent version(s) of managed functions from AWS.

Usage, delete all but the n-most recent versions of each function deployed. Versions references by an alias are automatically preserved:

sls prune -n <number of version to keep>


serverless-pseudo-parameters

For serverless version < 2.50.0; it allows to use #{AWS::AccountId}, #{AWS::Region}, etc. in any of your config strings.

https://www.npmjs.com/package/serverless-pseudo-parameters

Install w/ npm:

npm install --save serverless-pseudo-parameters

and add it to the serverless.yml plugins list:

plugins:

  - serverless-pseudo-parameters


serverless-python-requirements

Eg: sixqueue

https://www.serverless.com/plugins/serverless-python-requirements

The plugin will bundle your python dependencies specified in your Pipfile when you run sls deploy.

Add the plugin to package.json and the plugins section of serverless.yml

sls plugin install -n serverless-python-requirements


Variables

SSM Parameter Store

You can reference SSM Parameters as the source of your variables with the ssm:/path/to/param syntax. For example:

${ssm:/${self:provider.stackTags.env}/${self:provider.stackTags.ci}/salesforce_username}

For decrypting SecureString (WithDecryption: true) add ~true. Eg:

${ssm:/${self:provider.stackTags.env}/${self:provider.stackTags.ci}/salesforce_password~true}


Functions

API Gateway gateway

a) If using HTTP API (API Gateway v2), not REST API:

provider:

  environment:

    API_URL: !GetAtt HttpApi.ApiEndpoint

Example:

!Join ['', [!GetAtt HttpApi.ApiEndpoint, '/mylambda']]

b) If using REST API (API Gateway v1), not HTTP API:

provider:

  environment:

    API_URL: !Sub 'https://${ApiGatewayRestApi}.execute-api.${aws:region}.amazonaws.com/${sls:stage}'


Layer: AWS-Parameters-and-Secrets-Lambda-Extension (functions)

In serverless.yml:

provider:

  iamRoleStatements:

    - Effect: "Allow"

      Action:

        - "ssm:GetParameter"

      Resource: arn:aws:ssm:${self:provider.region}:#{AWS::AccountId}:parameter/${self:provider.stackTags.UOCEnv}/${self:provider.stackTags.ci}/*

    - Effect: "Allow"

      Action:

        - "kms:Decrypt"

      Resource: arn:aws:kms:*:#{AWS::AccountId}:key/alias/aws/ssm

and in the functions section:

functions:

  create:

    handler: event.jobs

    layers:

      - arn:aws:lambda:eu-west-1:015030872274:layer:AWS-Parameters-and-Secrets-Lambda-Extension:11

    events:

      - http:

          path: ${self:service}/jobs

          method: post

          cors: true

          private: true

Sample request:

import urllib.parse

import urllib.request


aws_session_token = get_env('AWS_SESSION_TOKEN')

req = urllib.request.Request('http://localhost:2773/systemsmanager/parameters/get?name=' + urllib.parse.quote('/env/ci/cl_id') + '&withDecryption=true')

req.add_header('X-Aws-Parameters-Secrets-Token', aws_session_token)

config = urllib.request.urlopen(req).read().decode('utf-8')

Sample response:

{"Parameter":{"ARN":"arn:aws:ssm:eu-west-1:434374682878:parameter/env/ci/cl_id","DataType":"text","LastModifiedDate":"2024-01-08T16:16:42.673Z","Name":"/env/ci/cl_id","Selector":null,"SourceResult":null,"Type":"String","Value":"MyValue","Version":1},"ResultMetadata":{}}

Resources (Serverless)

Security Group (Resources)

It allows to add CloudFormation resources to the servelss.yml

resources:

  Resources:

    SecurityGroupLambda: #AWS CloudFormation yaml synxtax

      Type: AWS::EC2::SecurityGroup

      Properties:

        GroupDescription: 'Assigned to authorizer lambda and configured on the OAuth server to allow connections from it'

        GroupName: ${self:provider.stackTags.env}-${self:provider.stackTags.ci}-lambda-sg

        SecurityGroupEgress:

          - CidrIp: 0.0.0.0/0

            Description: 'IPv4 allow all outbound traffic, eg to the OAuth server'

            IpProtocol: -1

          - CidrIpv6: ::/0

            Description: 'IPv6 allow all outbound traffic, eg to the OAuth server'

            IpProtocol: -1

        #SecurityGroupIngress: ~

        VpcId:  ${param:vpc_id}

  Outputs:

     SgIdForOauth:

      Description: 'SG id to be configured in the OAuth server to allow connections from this lambda authorizer'

      Value: !Ref SecurityGroupLambda

The id of the created SG can be referenced this way:

      securityGroupIds:

        - Ref: SecurityGroupLambda

The 'output' (the id of the created security group in this case) can be obtained with:

aws cloudformation describe-stacks --stack-name <ci>-<stage> --output text --region eu-west-1 --query "Stacks[0].Outputs[?OutputKey=='SgIdForOauth'].OutputValue"

Python lambda (Serverless)

Version

package.json contains the 'version' in a Serveless package.

It can be obtained from a Python lambda via:

import json


def lambda_handler(event, context):

    with open('package.json', 'r') as f_ver:

        f_ver_dict = json.load(f_ver)

        tech_comp_version = f_ver_dict.get("version")

    logger.info("======== Artifact version: %s ========", tech_comp_version)

Troubleshooting

Deployment bucket has been removed manually. Please recreate it or remove your service and attempt to deploy it again

TLDR: Manually delete the cloud formation stack in AWS console, and then redeploy.