Skip to content

Commit

Permalink
Merge pull request #2584 from serverless/add-custom-per-function-roles
Browse files Browse the repository at this point in the history
Add custom per function roles
  • Loading branch information
eahefnawy committed Nov 8, 2016
2 parents 823cd68 + 66be13d commit dce44fc
Show file tree
Hide file tree
Showing 14 changed files with 637 additions and 52 deletions.
6 changes: 3 additions & 3 deletions docs/providers/aws/guide/functions.md
Original file line number Diff line number Diff line change
Expand Up @@ -160,18 +160,18 @@ functions:
memorySize: 512
```

You can also use an existing IAM role by adding your IAM Role ARN in the `iamRoleARN` property. For example:
You can also use an existing IAM role by adding your IAM Role ARN in the `role` property. For example:

```yml
# serverless.yml
service: new-service
provider:
name: aws
iamRoleARN: arn:aws:iam::YourAccountNumber:role/YourIamRole
role: arn:aws:iam::YourAccountNumber:role/YourIamRole
```

Support for separate IAM Roles per function is coming soon.
See the documentation about [IAM](./iam.md) for function level IAM roles.

## VPC Configuration

Expand Down
289 changes: 289 additions & 0 deletions docs/providers/aws/guide/iam.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,289 @@
<!--
title: Serverless Framework - AWS Lambda Guide - IAM
menuText: IAM
menuOrder: 12
description: How to set up the different roles on a service and function level
layout: Doc
-->

<!-- DOCS-SITE-LINK:START automatically generated -->
### [Read this on the main serverless docs site](https://www.serverless.com/framework/docs/providers/aws/guide/iam)
<!-- DOCS-SITE-LINK:END -->

# Defining IAM Rights

Serverless provides no-configuration rights provisioning by default.
However you can always define roles on a service or function level if you need to.

## Default Role Management

The default rights provisioning approach requires no configuration and defines a role that is shared by all of the Lambda functions in your service. A policy is also created and is attached to the generated role. Any additional specific rights are added to the role by defining provider level `iamRoleStatements` that will be merged into the generated policy.

### Adding Custom IAM Role Statements to the Default Policy

By default, your Lambda functions will be provided with the right to create and write to CloudWatch logs. Further, if you have specified VPC security groups and subnets for your lambdas to use then the EC2 rights necessary to attach to the VPC via an ENI will be added into the default IAM policy.

If you want to give permission to your functions to access certain resources on your AWS account, you can add custom IAM role statements to your service by adding the statements in the `iamRoleStatements` array in the `provider` object. As those statements will be merged into the CloudFormation template you can use `Join`, `Ref` or any other CloudFormation method or feature. You're also able to either use YAML for defining the statement (including the methods) or use embedded JSON if you prefer it. Here's an example that uses all of these:

```yml
service: new-service

provider:
name: aws
iamRoleStatements:
- Effect: 'Allow'
Action:
- 's3:ListBucket'
Resource:
Fn::Join:
- ''
- - 'arn:aws:s3:::'
- Ref: ServerlessDeploymentBucket
- Effect: "Allow"
Action:
- "s3:PutObject"
Resource:
Fn::Join:
- ''
- - 'arn:aws:s3:::'
- Ref: ServerlessDeploymentBucket
```
On deployment, all these statements will be added to the policy that is applied to the IAM role that is assumed by your Lambda functions.
## Custom Role Management
**WARNING:** You need to take care of the overall role setup as soon as you define custom roles.
That means that `iamRoleStatements` you've defined on the `provider` level won't be applied anymore. Furthermore you need to provide the corresponding permissions for your Lambdas `logs` and [`stream`](../events/streams.md) events.

Serverless empowers you to define custom roles and apply them to your functions on a provider or individual function basis. To do this you must declare a `role` attribute at the level at which you would like the role to be applied.

Defining it on the provider will make the role referenced by the `role` value the default role for any Lambda without its own `role` declared. This is to say that defining a `role` attribute on individual functions will override any provider level declared role. If every function within your service has a role assigned to it (either via provider level `role` declaration, individual declarations, or a mix of the two) then the default role and policy will not be generated and added to your Cloud Formation Template.

The `role` attribute can have a value of the logical name of the role, the ARN of the role, or an object that will resolve in the ARN of the role. The declaration `{ function: { role: 'myRole' } }` will result in `{ 'Fn::GetAtt': ['myRole', 'Arn'] }`. You can of course just declare an ARN like so `{ function: { role: 'an:aws:arn:xxx:*:*' } }`. This use case is primarily for those who must create their roles and / or policies via a means outside of Serverless.

Here are some examples of using these capabilities to specify Lambda roles.

### Provide a single role for all lambdas (via each form of declaration)

```yml
service: new-service
provider:
name: aws
# declare one of the following...
role: myDefaultRole # must validly reference a role defined in the service
role: arn:aws:iam::0123456789:role//my/default/path/roleInMyAccount # must validly reference a role defined in your account
role: # must validly resolve to the ARN of a role you have the rights to use
Fn::GetAtt:
- myRole
- Arn
functions:
func0: # will assume 'myDefaultRole'
... # does not define role
func1: # will assume 'myDefaultRole'
... # does not define role
resources:
Resources:
myDefaultRole:
Type: AWS::IAM::Role
Properties:
Path: /my/default/path
RoleName: MyDefaultRole
AssumeRolePolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Principal:
Service:
- lambda.amazonaws.com
Action: sts:AssumeRole
Policies:
- PolicyName: myPolicyName
PolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow # note that these rights are given in the default policy and are required if you want logs out of your lambda(s)
Action:
- logs:CreateLogGroup
- logs:CreateLogStream
- logs:PutLogEvents
Resource: arn:aws:logs:${region}:${accountId}:log-group:/aws/lambda/*:*:*
- Effect: "Allow"
Action:
- "s3:PutObject"
Resource:
Fn::Join:
- ""
- - "arn:aws:s3:::"
- "Ref" : "ServerlessDeploymentBucket"
```

### Provide individual roles for each Lambda

```yml
service: new-service
provider:
name: aws
... # does not define role
functions:
func0:
role: myCustRole0
...
func1:
role: myCustRole1
...
resources:
Resources:
myCustRole0:
Type: AWS::IAM::Role
Properties:
Path: /my/cust/path
RoleName: MyCustRole0
AssumeRolePolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Principal:
Service:
- lambda.amazonaws.com
Action: sts:AssumeRole
Policies:
- PolicyName: myPolicyName
PolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Action:
- logs:CreateLogGroup
- logs:CreateLogStream
- logs:PutLogEvents
Resource: arn:aws:logs:${region}:${accountId}:log-group:/aws/lambda/*:*:*
- Effect: Allow
Action:
- ec2:CreateNetworkInterface
- ec2:DescribeNetworkInterfaces
- ec2:DetachNetworkInterface
- ec2:DeleteNetworkInterface
Resource: "*"
myCustRole1:
Type: AWS::IAM::Role
Properties:
Path: /my/cust/path
RoleName: MyCustRole1
AssumeRolePolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Principal:
Service:
- lambda.amazonaws.com
Action: sts:AssumeRole
Policies:
- PolicyName: myPolicyName
PolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow # note that these rights are given in the default policy and are required if you want logs out of your lambda(s)
Action:
- logs:CreateLogGroup
- logs:CreateLogStream
- logs:PutLogEvents
Resource: arn:aws:logs:${region}:${accountId}:log-group:/aws/lambda/*:*:*
- Effect: "Allow"
Action:
- "s3:PutObject"
Resource:
Fn::Join:
- ""
- - "arn:aws:s3:::"
- "Ref" : "ServerlessDeploymentBucket"
```

### Provide a default role for all Lambdas except those overriding the default

```yml
service: new-service
provider:
name: aws
role: myDefaultRole
functions:
func0:
role: myCustRole0
...
func1:
... # does not define role
resources:
Resources:
myDefaultRole:
Type: AWS::IAM::Role
Properties:
Path: /my/default/path
RoleName: MyDefaultRole
AssumeRolePolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Principal:
Service:
- lambda.amazonaws.com
Action: sts:AssumeRole
Policies:
- PolicyName: myPolicyName
PolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow # note that these rights are given in the default policy and are required if you want logs out of your lambda(s)
Action:
- logs:CreateLogGroup
- logs:CreateLogStream
- logs:PutLogEvents
Resource: arn:aws:logs:${region}:${accountId}:log-group:/aws/lambda/*:*:*
- Effect: "Allow"
Action:
- "s3:PutObject"
Resource:
Fn::Join:
- ""
- - "arn:aws:s3:::"
- "Ref" : "ServerlessDeploymentBucket"
myCustRole0:
Type: AWS::IAM::Role
Properties:
Path: /my/cust/path
RoleName: MyCustRole0
AssumeRolePolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Principal:
Service:
- lambda.amazonaws.com
Action: sts:AssumeRole
Policies:
- PolicyName: myPolicyName
PolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Action:
- logs:CreateLogGroup
- logs:CreateLogStream
- logs:PutLogEvents
Resource: arn:aws:logs:${region}:${accountId}:log-group:/aws/lambda/*:*:*
- Effect: Allow
Action:
- ec2:CreateNetworkInterface
- ec2:DescribeNetworkInterfaces
- ec2:DetachNetworkInterface
- ec2:DeleteNetworkInterface
Resource: "*"
```
2 changes: 1 addition & 1 deletion docs/providers/aws/guide/plugins.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<!--
title: Serverless Framework - AWS Lambda Guide - Plugins
menuText: Plugins
menuOrder: 12
menuOrder: 13
description: How to install and create Plugins to extend or overwrite the functionality of the Serverless Framework
layout: Doc
-->
Expand Down
4 changes: 2 additions & 2 deletions docs/providers/aws/guide/workflow.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<!--
title: Serverless Framework Guide - AWS Lambda - Workflow
menuText: Workflow
menuOrder: 13
menuOrder: 14
description: A guide and cheatsheet containing CLI commands and workflow recommendations.
layout: Doc
-->
Expand Down Expand Up @@ -67,4 +67,4 @@ serverless invoke function -f [FUNCTION NAME] -s [STAGE NAME] -r [REGION NAME] -
Open up a separate tab in your console and stream all logs for a specific Function using this command.
```
serverless logs -f [FUNCTION NAME] -s [STAGE NAME] -r [REGION NAME]
```
```
31 changes: 17 additions & 14 deletions lib/plugins/aws/deploy/compile/events/stream/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -113,20 +113,23 @@ class AwsCompileStreamEvents {
};
}

// update the PolicyDocument statements
const statement = this.serverless.service.provider.compiledCloudFormationTemplate
.Resources
.IamPolicyLambdaExecution
.Properties
.PolicyDocument
.Statement;

this.serverless.service.provider.compiledCloudFormationTemplate
.Resources
.IamPolicyLambdaExecution
.Properties
.PolicyDocument
.Statement = statement.concat([streamStatement]);
// update the PolicyDocument statements (if default policy is used)
if (this.serverless.service.provider.compiledCloudFormationTemplate
.Resources.IamPolicyLambdaExecution) {
const statement = this.serverless.service.provider.compiledCloudFormationTemplate
.Resources
.IamPolicyLambdaExecution
.Properties
.PolicyDocument
.Statement;

this.serverless.service.provider.compiledCloudFormationTemplate
.Resources
.IamPolicyLambdaExecution
.Properties
.PolicyDocument
.Statement = statement.concat([streamStatement]);
}

const newStreamObject = {
[`${normalizedFunctionName}EventSourceMapping${
Expand Down
Loading

0 comments on commit dce44fc

Please sign in to comment.