Dissecting Serverless Stacks (I)

Thumbnail

Dissecting Serverless Stacks (I)

This post establishes the base for a small series on how to create Serverless based Lambdas which can be deployed in environments without IAM privileges or where the sls command cannot be used at all.

As you are probably aware, the Serverless Framework is one of the best established ways of writing serverless architectures and deploy them on e.g. AWS. It provides a handy way to have everything related to a project centralized, be it the source code or the CloudFormation/IAM resources. With easy commands like sls deploy you can then create or update your code in a consistent fashion.

Depending on your type of organization you might hit some problems, though. It is good practice to not have the same person create security privileges and use them, also called “Separation of Duties”. As all Lambda functions on AWS need their share of IAM, this makes deploying with the built-in methods hard. One would need a way to still have everything in one project but cater to this particular organizational structure.

It can get even worse. After you basically finished your project, it turns out you won’t have any AWS permissions at all and need to hand it over to somebody else for deployment. And, that is not too far-fetched, that person does not have any interest in installing the sls command but insists on getting some CloudFormation YAML files and a ZIP with the code.

With some tweaks, it is possible to have one SLS project but offer all three deployment styles: - all-in-one, the standard SLS way - iam-plus-sls, for a CloudFormation stack but still using SLS as a developer - bits-and-pieces, for handing over to another party.

This blog post series will explain how to get there, so let’s begin.

Starting off and Cleaning up

For our journey, we will talk about a small SLS project and concentrate on the serverless.yml file as most problems can be solved within this file. As the following posts will use a syntax slightly different to most of these files I have seen in the wild, this post will simply introduce our point to start with and tidy it up for the things to come.

As I am notoriously promoting Ruby, this will be a Ruby based Lambda.

service: fancy

provider:
  name: aws
  runtime: ruby2.5
  region: eu-west-1
  iamRoleStatements:
    - Effect: Allow
      Action: s3:GetObject
      Resource: 'arn:aws:s3:::importbucket/*'

package:
  exclude:
    - package*.json
    - node_modules/**

functions:
  fancy:
    description: Our fancy example Lambda
    handler: src/handler.main

As you can see, we have the usual iamRoleStatements block in here which will be used for all functions we are definiing in our project. Coming from a lot of trainings where I promote the Least Privilege Principle, I find this mildly irritating and would suggest reworking this. The SLS documentation specifically talks about the possibility to attach policies directly to the functions, which has to be done by adding it into the resources section:

service: fancy

provider:
  name: aws
  runtime: ruby2.5
  region: eu-west-1

package:
  exclude:
    - package*.json
    - node_modules/**

functions:
  fancy:
    description: Our fancy example Lambda
    handler: src/handler.main
    role: fancyRole
    
resources:
  Resources:
    fancyRole:
      Type: AWS::IAM::Role
      Properties:
        RoleName: MyfancyRole
        AssumeRolePolicyDocument:
          Version: '2012-10-17'
          Statement:
            - Effect: Allow
              Principal:
                Service:
                  - lambda.amazonaws.com
              Action: sts:AssumeRole
        Policies:
          - PolicyName: allowS3
            PolicyDocument:
              Version: '2012-10-17'
              Statement:
                - Effect: Allow
                  Action:
                    - logs:CreateLogGroup
                    - logs:CreateLogStream
                    - logs:PutLogEvents
                - Effect: Allow
                  Action: s3:GetObject
                  Resource: 'arn:aws:s3:::importbucket/*'

Be careful though! While the common iamRoleStatements shortcut automatically adds the ability to our Lambdas to write logs, this syntax does not. Hence, we have to include the logs: statements here.

This does not look very convenient, but I promise to make things much cleaner in the next post