Skip to content
Permalink
Browse files

add support for customizing the Lambda function IAM role policy. Closes

  • Loading branch information...
tj committed May 9, 2018
1 parent b62768f commit 9f4facaf37138f1eb6640bbbda91cf0ac7873973
Showing with 111 additions and 23 deletions.
  1. +26 −1 config/lambda.go
  2. +28 −0 config/lambda_test.go
  3. +30 −0 docs/04-configuration.md
  4. +27 −22 platform/lambda/lambda.go
@@ -1,6 +1,26 @@
package config

import "errors"
import (
"errors"
)

// defaultPolicy is the default function role policy.
var defaultPolicy = IAMPolicyStatement{
"Effect": "Allow",
"Resource": "*",
"Action": []string{
"logs:CreateLogGroup",
"logs:CreateLogStream",
"logs:PutLogEvents",
"ssm:GetParametersByPath",
"ec2:CreateNetworkInterface",
"ec2:DescribeNetworkInterfaces",
"ec2:DeleteNetworkInterface",
},
}

// IAMPolicyStatement configuration.
type IAMPolicyStatement map[string]interface{}

// Lambda configuration.
type Lambda struct {
@@ -15,6 +35,9 @@ type Lambda struct {

// Runtime of the function.
Runtime string `json:"runtime"`

// Policy of the function role.
Policy []IAMPolicyStatement `json:"policy"`
}

// Default implementation.
@@ -27,6 +50,8 @@ func (l *Lambda) Default() error {
l.Runtime = "nodejs8.10"
}

l.Policy = append(l.Policy, defaultPolicy)

return nil
}

@@ -12,3 +12,31 @@ func TestLambda(t *testing.T) {
assert.Equal(t, 0, c.Timeout, "timeout")
assert.Equal(t, 512, c.Memory, "timeout")
}

func TestLambda_Policy(t *testing.T) {
t.Run("defaults", func(t *testing.T) {
c := &Lambda{}
assert.NoError(t, c.Default(), "default")
assert.Len(t, c.Policy, 1)
assert.Equal(t, defaultPolicy, c.Policy[0])
})

t.Run("specified", func(t *testing.T) {
c := &Lambda{
Policy: []IAMPolicyStatement{
{
"Effect": "Allow",
"Resource": "*",
"Action": []string{
"s3:List*",
"s3:Get*",
},
},
},
}

assert.NoError(t, c.Default(), "default")
assert.Len(t, c.Policy, 2)
assert.Equal(t, defaultPolicy, c.Policy[1])
})
}
@@ -95,6 +95,7 @@ The following Lambda-specific settings are available:
- `role` – IAM role ARN, defaulting to the one Up creates for you
- `memory` – Function memory in mb (Default `512`, Min `128`, Max `1536`)
- `runtime` – Function runtime (Default `nodejs8.10`)
- `policy` – IAM function policy statement(s)

For example:

@@ -114,6 +115,35 @@ Lambda `memory` also scales the CPU, if your app is slow, or for cases such as l

Note: Changes to Lambda configuration do not require a `up stack apply`, just deploy and these changes are picked up!

### IAM Policy

Up uses IAM policies to grant access to resources within your AWS account such as DynamoDB or S3.

To add additional permissions add one or more IAM policy statements to the `policy` array, in the following example we permit DynamoDB item reading, updating, and deleting.

```json
{
"name": "myapp",
"lambda": {
"memory": 1024,
"policy": [
{
"Effect": "Allow",
"Resource": "*",
"Action": [
"dynamodb:Get*",
"dynamodb:List*",
"dynamodb:PutItem",
"dynamodb:DeleteItem"
]
}
]
}
}
```

Deploy to update the IAM function role permissions.

## Hook Scripts

Up provides "hooks" which are commands invoked at certain points within the deployment workflow for automating builds, linting and so on. The following hooks are available:
@@ -3,6 +3,7 @@ package lambda

import (
"bytes"
"encoding/json"
"fmt"
"io"
"io/ioutil"
@@ -28,6 +29,7 @@ import (
"github.com/pkg/errors"

"github.com/apex/up"
"github.com/apex/up/config"
"github.com/apex/up/internal/proxy/bin"
"github.com/apex/up/internal/shim"
"github.com/apex/up/internal/util"
@@ -69,26 +71,6 @@ var apiGatewayAssumePolicy = `{
]
}`

// policy for the lambda function.
var functionPolicy = `{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Resource": "*",
"Action": [
"logs:CreateLogGroup",
"logs:CreateLogStream",
"logs:PutLogEvents",
"ssm:GetParametersByPath",
"ec2:CreateNetworkInterface",
"ec2:DescribeNetworkInterfaces",
"ec2:DeleteNetworkInterface"
]
}
]
}`

// TODO: aggregate progress report for N regions or distinct progress bars
// TODO: refactor with another region-scoped struct to clean this up

@@ -775,11 +757,16 @@ func (p *Platform) createRole() error {
func (p *Platform) updateRole(c *iam.IAM) error {
name := p.roleName()

policy, err := p.functionPolicy()
if err != nil {
return errors.Wrap(err, "creating function policy")
}

log.Debug("updating role policy")
_, err := c.PutRolePolicy(&iam.PutRolePolicyInput{
_, err = c.PutRolePolicy(&iam.PutRolePolicyInput{
PolicyName: &name,
RoleName: &name,
PolicyDocument: &functionPolicy,
PolicyDocument: &policy,
})

return err
@@ -931,6 +918,24 @@ func (p *Platform) getAccountID() string {
return strings.Split(p.config.Lambda.Role, ":")[4]
}

// functionPolicy returns the IAM function role policy.
func (p *Platform) functionPolicy() (string, error) {
policy := struct {
Version string
Statement []config.IAMPolicyStatement
}{
Version: "2012-10-17",
Statement: p.config.Lambda.Policy,
}

b, err := json.MarshalIndent(policy, "", " ")
if err != nil {
return "", err
}

return string(b), nil
}

// isCreatingRole returns true if the role has not been created.
func isCreatingRole(err error) bool {
return err != nil && strings.Contains(err.Error(), "role defined for the function cannot be assumed by Lambda")

0 comments on commit 9f4faca

Please sign in to comment.
You can’t perform that action at this time.