Skip to content

Commit 6e20297

Browse files
LironErEnase
authored andcommitted
feat: Support new provider.iam property
Resolves: #73
1 parent 720dc0f commit 6e20297

File tree

3 files changed

+167
-2
lines changed

3 files changed

+167
-2
lines changed

README.md

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,31 @@ functions:
7272

7373
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`:
7474

75+
**serverless >= v2.24.0**
76+
```yaml
77+
provider:
78+
name: aws
79+
iam:
80+
role:
81+
statements:
82+
- Effect: "Allow"
83+
Action:
84+
- xray:PutTelemetryRecords
85+
- xray:PutTraceSegments
86+
Resource: "*"
87+
...
88+
functions:
89+
func1:
90+
handler: handler.get
91+
iamRoleStatementsInherit: true
92+
iamRoleStatements:
93+
- Effect: "Allow"
94+
Action:
95+
- dynamodb:GetItem
96+
Resource: "arn:aws:dynamodb:${self:provider.region}:*:table/mytable"
97+
```
98+
99+
**serverless < v2.24.0**
75100
```yaml
76101
provider:
77102
name: aws
@@ -92,6 +117,7 @@ functions:
92117
- dynamodb:GetItem
93118
Resource: "arn:aws:dynamodb:${self:provider.region}:*:table/mytable"
94119
```
120+
95121
The generated role for `func1` will contain both the statements defined at the provider level and the ones defined at the function level.
96122

97123
If you wish to change the default behavior to `inherit` instead of `override` it is possible to specify the following custom configuration:

src/lib/index.ts

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -344,8 +344,15 @@ class ServerlessIamPerFunctionPlugin {
344344
const isInherit = functionObject.iamRoleStatementsInherit
345345
|| (this.defaultInherit && functionObject.iamRoleStatementsInherit !== false);
346346

347-
if (isInherit && !_.isEmpty(this.serverless.service.provider.iamRoleStatements)) { // add global statements
348-
for (const s of this.serverless.service.provider.iamRoleStatements) {
347+
// Since serverless 2.24.0 provider.iamRoleStatements is deprecated
348+
// https://github.com/serverless/serverless/blob/master/CHANGELOG.md#2240-2021-02-16
349+
// Support old & new iam statements by checking if `iam` property exists
350+
const providerIamRoleStatements = this.serverless.service.provider.iam
351+
? this.serverless.service.provider.iam.role?.statements
352+
: this.serverless.service.provider.iamRoleStatements;
353+
354+
if (isInherit && !_.isEmpty(providerIamRoleStatements)) { // add global statements
355+
for (const s of providerIamRoleStatements) {
349356
policyStatements.push(s);
350357
}
351358
}

src/test/index.test.ts

Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -441,4 +441,136 @@ describe('plugin tests', function(this: any) {
441441
});
442442
});
443443

444+
describe('support new provider.iam property', () => {
445+
const getLambdaTestStatements = (): any[] => {
446+
const plugin = new Plugin(serverless);
447+
448+
const compiledResources = serverless.service.provider.compiledCloudFormationTemplate.Resources;
449+
plugin.createRolesPerFunction();
450+
const helloInherit = compiledResources.HelloInheritIamRoleLambdaExecution;
451+
assert.isNotEmpty(helloInherit);
452+
453+
return helloInherit.Properties.Policies[0].PolicyDocument.Statement;
454+
}
455+
456+
it('no global iam and iamRoleStatements properties', () => {
457+
_.set(serverless.service, 'provider.iam', undefined);
458+
_.set(serverless.service, 'provider.iamRoleStatements', undefined);
459+
460+
const statements = getLambdaTestStatements();
461+
462+
assert.isTrue(statements.find((s) => s.Action[0] === 'xray:PutTelemetryRecords') === undefined,
463+
'provider.iamRoleStatements values shouldn\'t exists');
464+
assert.isObject(
465+
statements.find((s) => s.Action[0] === 'dynamodb:GetItem'),
466+
'per function statements imported upon inherit',
467+
);
468+
});
469+
470+
describe('new iam property takes precedence over old iamRoleStatements property', () => {
471+
it('empty iam object', () => {
472+
_.set(serverless.service, 'provider.iam', {});
473+
474+
const statements = getLambdaTestStatements();
475+
476+
assert.isTrue(statements.find((s) => s.Action[0] === 'xray:PutTelemetryRecords') === undefined,
477+
'provider.iamRoleStatements values shouldn\'t exists');
478+
assert.isObject(
479+
statements.find((s) => s.Action[0] === 'dynamodb:GetItem'),
480+
'per function statements imported upon inherit',
481+
);
482+
});
483+
484+
it('no role property', () => {
485+
_.set(serverless.service, 'provider.iam', {
486+
deploymentRole: 'arn:aws:iam::123456789012:role/deploy-role'
487+
});
488+
489+
const statements = getLambdaTestStatements();
490+
491+
assert.isTrue(statements.find((s) => s.Action[0] === 'xray:PutTelemetryRecords') === undefined,
492+
'provider.iamRoleStatements values shouldn\'t exists');
493+
assert.isObject(
494+
statements.find((s) => s.Action[0] === 'dynamodb:GetItem'),
495+
'per function statements imported upon inherit',
496+
);
497+
});
498+
499+
it('role property set to role ARN', () => {
500+
_.set(serverless.service, 'provider.iam', {
501+
role: 'arn:aws:iam::0123456789:role//my/default/path/roleInMyAccount'
502+
});
503+
504+
const statements = getLambdaTestStatements();
505+
506+
assert.isTrue(statements.find((s) => s.Action[0] === 'xray:PutTelemetryRecords') === undefined,
507+
'provider.iamRoleStatements values shouldn\'t exists');
508+
assert.isObject(
509+
statements.find((s) => s.Action[0] === 'dynamodb:GetItem'),
510+
'per function statements imported upon inherit',
511+
);
512+
});
513+
514+
it('role is set without statements', () => {
515+
_.set(serverless.service, 'provider.iam', {
516+
role: {
517+
managedPolicies: ['arn:aws:iam::123456789012:user/*']
518+
}
519+
});
520+
521+
const statements = getLambdaTestStatements();
522+
523+
assert.isTrue(statements.find((s) => s.Action[0] === 'xray:PutTelemetryRecords') === undefined,
524+
'provider.iamRoleStatements values shouldn\'t exists');
525+
assert.isObject(
526+
statements.find((s) => s.Action[0] === 'dynamodb:GetItem'),
527+
'per function statements imported upon inherit',
528+
);
529+
});
530+
531+
it('empty statements', () => {
532+
_.set(serverless.service, 'provider.iam', {
533+
role: {
534+
statements: []
535+
}
536+
});
537+
538+
const statements = getLambdaTestStatements();
539+
540+
assert.isTrue(statements.find((s) => s.Action[0] === 'xray:PutTelemetryRecords') === undefined,
541+
'provider.iamRoleStatements values shouldn\'t exists');
542+
assert.isObject(
543+
statements.find((s) => s.Action[0] === 'dynamodb:GetItem'),
544+
'per function statements imported upon inherit',
545+
);
546+
});
547+
});
548+
549+
it('global iam role statements exists in lambda role statements', () => {
550+
_.set(serverless.service, 'provider.iam', {
551+
role: {
552+
statements: [{
553+
Effect: 'Allow',
554+
Action: [
555+
'ec2:CreateNetworkInterface'
556+
],
557+
Resource: '*'
558+
}]
559+
}
560+
});
561+
562+
const statements = getLambdaTestStatements();
563+
564+
assert.isObject(
565+
statements.find((s) => s.Action[0] === 'ec2:CreateNetworkInterface'),
566+
'global iam role statements exists',
567+
);
568+
assert.isTrue(statements.find((s) => s.Action[0] === 'xray:PutTelemetryRecords') === undefined,
569+
'old provider.iamRoleStatements shouldn\'t exists');
570+
assert.isObject(
571+
statements.find((s) => s.Action[0] === 'dynamodb:GetItem'),
572+
'per function statements imported upon inherit',
573+
);
574+
});
575+
});
444576
});

0 commit comments

Comments
 (0)