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
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