Skip to content

Commit

Permalink
feat: Support new provider.iam property
Browse files Browse the repository at this point in the history
Resolves: #73
  • Loading branch information
LironEr authored and Enase committed Mar 22, 2021
1 parent 720dc0f commit 6e20297
Show file tree
Hide file tree
Showing 3 changed files with 167 additions and 2 deletions.
26 changes: 26 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,31 @@ functions:

By default, function level `iamRoleStatements` override the provider level definition. It is also possible to inherit the provider level definition by specifying the option `iamRoleStatementsInherit: true`:

**serverless >= v2.24.0**
```yaml
provider:
name: aws
iam:
role:
statements:
- Effect: "Allow"
Action:
- xray:PutTelemetryRecords
- xray:PutTraceSegments
Resource: "*"
...
functions:
func1:
handler: handler.get
iamRoleStatementsInherit: true
iamRoleStatements:
- Effect: "Allow"
Action:
- dynamodb:GetItem
Resource: "arn:aws:dynamodb:${self:provider.region}:*:table/mytable"
```

**serverless < v2.24.0**
```yaml
provider:
name: aws
Expand All @@ -92,6 +117,7 @@ functions:
- dynamodb:GetItem
Resource: "arn:aws:dynamodb:${self:provider.region}:*:table/mytable"
```

The generated role for `func1` will contain both the statements defined at the provider level and the ones defined at the function level.

If you wish to change the default behavior to `inherit` instead of `override` it is possible to specify the following custom configuration:
Expand Down
11 changes: 9 additions & 2 deletions src/lib/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -344,8 +344,15 @@ class ServerlessIamPerFunctionPlugin {
const isInherit = functionObject.iamRoleStatementsInherit
|| (this.defaultInherit && functionObject.iamRoleStatementsInherit !== false);

if (isInherit && !_.isEmpty(this.serverless.service.provider.iamRoleStatements)) { // add global statements
for (const s of this.serverless.service.provider.iamRoleStatements) {
// Since serverless 2.24.0 provider.iamRoleStatements is deprecated
// https://github.com/serverless/serverless/blob/master/CHANGELOG.md#2240-2021-02-16
// Support old & new iam statements by checking if `iam` property exists
const providerIamRoleStatements = this.serverless.service.provider.iam
? this.serverless.service.provider.iam.role?.statements
: this.serverless.service.provider.iamRoleStatements;

if (isInherit && !_.isEmpty(providerIamRoleStatements)) { // add global statements
for (const s of providerIamRoleStatements) {
policyStatements.push(s);
}
}
Expand Down
132 changes: 132 additions & 0 deletions src/test/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -441,4 +441,136 @@ describe('plugin tests', function(this: any) {
});
});

describe('support new provider.iam property', () => {
const getLambdaTestStatements = (): any[] => {
const plugin = new Plugin(serverless);

const compiledResources = serverless.service.provider.compiledCloudFormationTemplate.Resources;
plugin.createRolesPerFunction();
const helloInherit = compiledResources.HelloInheritIamRoleLambdaExecution;
assert.isNotEmpty(helloInherit);

return helloInherit.Properties.Policies[0].PolicyDocument.Statement;
}

it('no global iam and iamRoleStatements properties', () => {
_.set(serverless.service, 'provider.iam', undefined);
_.set(serverless.service, 'provider.iamRoleStatements', undefined);

const statements = getLambdaTestStatements();

assert.isTrue(statements.find((s) => s.Action[0] === 'xray:PutTelemetryRecords') === undefined,
'provider.iamRoleStatements values shouldn\'t exists');
assert.isObject(
statements.find((s) => s.Action[0] === 'dynamodb:GetItem'),
'per function statements imported upon inherit',
);
});

describe('new iam property takes precedence over old iamRoleStatements property', () => {
it('empty iam object', () => {
_.set(serverless.service, 'provider.iam', {});

const statements = getLambdaTestStatements();

assert.isTrue(statements.find((s) => s.Action[0] === 'xray:PutTelemetryRecords') === undefined,
'provider.iamRoleStatements values shouldn\'t exists');
assert.isObject(
statements.find((s) => s.Action[0] === 'dynamodb:GetItem'),
'per function statements imported upon inherit',
);
});

it('no role property', () => {
_.set(serverless.service, 'provider.iam', {
deploymentRole: 'arn:aws:iam::123456789012:role/deploy-role'
});

const statements = getLambdaTestStatements();

assert.isTrue(statements.find((s) => s.Action[0] === 'xray:PutTelemetryRecords') === undefined,
'provider.iamRoleStatements values shouldn\'t exists');
assert.isObject(
statements.find((s) => s.Action[0] === 'dynamodb:GetItem'),
'per function statements imported upon inherit',
);
});

it('role property set to role ARN', () => {
_.set(serverless.service, 'provider.iam', {
role: 'arn:aws:iam::0123456789:role//my/default/path/roleInMyAccount'
});

const statements = getLambdaTestStatements();

assert.isTrue(statements.find((s) => s.Action[0] === 'xray:PutTelemetryRecords') === undefined,
'provider.iamRoleStatements values shouldn\'t exists');
assert.isObject(
statements.find((s) => s.Action[0] === 'dynamodb:GetItem'),
'per function statements imported upon inherit',
);
});

it('role is set without statements', () => {
_.set(serverless.service, 'provider.iam', {
role: {
managedPolicies: ['arn:aws:iam::123456789012:user/*']
}
});

const statements = getLambdaTestStatements();

assert.isTrue(statements.find((s) => s.Action[0] === 'xray:PutTelemetryRecords') === undefined,
'provider.iamRoleStatements values shouldn\'t exists');
assert.isObject(
statements.find((s) => s.Action[0] === 'dynamodb:GetItem'),
'per function statements imported upon inherit',
);
});

it('empty statements', () => {
_.set(serverless.service, 'provider.iam', {
role: {
statements: []
}
});

const statements = getLambdaTestStatements();

assert.isTrue(statements.find((s) => s.Action[0] === 'xray:PutTelemetryRecords') === undefined,
'provider.iamRoleStatements values shouldn\'t exists');
assert.isObject(
statements.find((s) => s.Action[0] === 'dynamodb:GetItem'),
'per function statements imported upon inherit',
);
});
});

it('global iam role statements exists in lambda role statements', () => {
_.set(serverless.service, 'provider.iam', {
role: {
statements: [{
Effect: 'Allow',
Action: [
'ec2:CreateNetworkInterface'
],
Resource: '*'
}]
}
});

const statements = getLambdaTestStatements();

assert.isObject(
statements.find((s) => s.Action[0] === 'ec2:CreateNetworkInterface'),
'global iam role statements exists',
);
assert.isTrue(statements.find((s) => s.Action[0] === 'xray:PutTelemetryRecords') === undefined,
'old provider.iamRoleStatements shouldn\'t exists');
assert.isObject(
statements.find((s) => s.Action[0] === 'dynamodb:GetItem'),
'per function statements imported upon inherit',
);
});
});
});

0 comments on commit 6e20297

Please sign in to comment.