Skip to content

Commit

Permalink
feat(custom-resources): support custom lambda role in provider framew…
Browse files Browse the repository at this point in the history
…ork (#12131)

Added support to pass a custom role to the provider which the lambda will use. Can be used a.o. to pass a permission boundary.

closes #12126


----

*By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license*
  • Loading branch information
Ruben-E authored Jun 7, 2021
1 parent a37108c commit bc01207
Show file tree
Hide file tree
Showing 3 changed files with 74 additions and 2 deletions.
6 changes: 5 additions & 1 deletion packages/@aws-cdk/custom-resources/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,14 +33,18 @@ the actual handler.
```ts
import { CustomResource } from '@aws-cdk/core';
import * as logs from '@aws-cdk/aws-logs';
import * as iam from '@aws-cdk/aws-iam';
import * as cr from '@aws-cdk/custom-resources';

const onEvent = new lambda.Function(this, 'MyHandler', { /* ... */ });

const myRole = new iam.Role(this, 'MyRole', { /* ... */ });

const myProvider = new cr.Provider(this, 'MyProvider', {
onEventHandler: onEvent,
isCompleteHandler: isComplete, // optional async "waiter"
logRetention: logs.RetentionDays.ONE_DAY // default is INFINITE
logRetention: logs.RetentionDays.ONE_DAY, // default is INFINITE
role: myRole, // must be assumable by the `lambda.amazonaws.com` service principal
});

new CustomResource(this, 'Resource1', { serviceToken: myProvider.serviceToken });
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import * as path from 'path';
import * as ec2 from '@aws-cdk/aws-ec2';
import * as iam from '@aws-cdk/aws-iam';
import * as lambda from '@aws-cdk/aws-lambda';
import * as logs from '@aws-cdk/aws-logs';
import { Duration } from '@aws-cdk/core';
Expand Down Expand Up @@ -105,6 +106,15 @@ export interface ProviderProps {
*/
readonly securityGroups?: ec2.ISecurityGroup[];

/**
* AWS Lambda execution role.
*
* The role that will be assumed by the AWS Lambda.
* Must be assumable by the 'lambda.amazonaws.com' service principal.
*
* @default - A default role will be created.
*/
readonly role?: iam.IRole;
}

/**
Expand Down Expand Up @@ -135,6 +145,7 @@ export class Provider extends CoreConstruct implements ICustomResourceProvider {
private readonly vpc?: ec2.IVpc;
private readonly vpcSubnets?: ec2.SubnetSelection;
private readonly securityGroups?: ec2.ISecurityGroup[];
private readonly role?: iam.IRole;

constructor(scope: Construct, id: string, props: ProviderProps) {
super(scope, id);
Expand All @@ -152,6 +163,8 @@ export class Provider extends CoreConstruct implements ICustomResourceProvider {
this.vpcSubnets = props.vpcSubnets;
this.securityGroups = props.securityGroups;

this.role = props.role;

const onEventFunction = this.createFunction(consts.FRAMEWORK_ON_EVENT_HANDLER_NAME);

if (this.isCompleteHandler) {
Expand All @@ -166,7 +179,6 @@ export class Provider extends CoreConstruct implements ICustomResourceProvider {
interval: retry.interval,
maxAttempts: retry.maxAttempts,
});

// the on-event entrypoint is going to start the execution of the waiter
onEventFunction.addEnvironment(consts.WAITER_STATE_MACHINE_ARN_ENV, waiterStateMachine.stateMachineArn);
waiterStateMachine.grantStartExecution(onEventFunction);
Expand Down Expand Up @@ -197,6 +209,7 @@ export class Provider extends CoreConstruct implements ICustomResourceProvider {
vpc: this.vpc,
vpcSubnets: this.vpcSubnets,
securityGroups: this.securityGroups,
role: this.role,
});

fn.addEnvironment(consts.USER_ON_EVENT_FUNCTION_ARN_ENV, this.onEventHandler.functionArn);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import * as path from 'path';
import * as ec2 from '@aws-cdk/aws-ec2';
import * as iam from '@aws-cdk/aws-iam';
import * as lambda from '@aws-cdk/aws-lambda';
import * as logs from '@aws-cdk/aws-logs';
import { Duration, Stack } from '@aws-cdk/core';
Expand Down Expand Up @@ -342,3 +343,57 @@ describe('log retention', () => {
expect(stack).not.toHaveResource('Custom::LogRetention');
});
});

describe('role', () => {
it('uses custom role when present', () => {
// GIVEN
const stack = new Stack();

// WHEN
new cr.Provider(stack, 'MyProvider', {
onEventHandler: new lambda.Function(stack, 'MyHandler', {
code: lambda.Code.fromAsset(path.join(__dirname, './integration-test-fixtures/s3-file-handler')),
handler: 'index.onEvent',
runtime: lambda.Runtime.NODEJS_10_X,
}),
role: new iam.Role(stack, 'MyRole', {
assumedBy: new iam.ServicePrincipal('lambda.amazonaws.com'),
managedPolicies: [iam.ManagedPolicy.fromAwsManagedPolicyName('service-role/AWSLambdaBasicExecutionRole')],
}),
});

// THEN
expect(stack).toHaveResourceLike('AWS::Lambda::Function', {
Role: {
'Fn::GetAtt': [
'MyRoleF48FFE04',
'Arn',
],
},
});
});

it('uses default role otherwise', () => {
// GIVEN
const stack = new Stack();

// WHEN
new cr.Provider(stack, 'MyProvider', {
onEventHandler: new lambda.Function(stack, 'MyHandler', {
code: lambda.Code.fromAsset(path.join(__dirname, './integration-test-fixtures/s3-file-handler')),
handler: 'index.onEvent',
runtime: lambda.Runtime.NODEJS_10_X,
}),
});

// THEN
expect(stack).toHaveResourceLike('AWS::Lambda::Function', {
Role: {
'Fn::GetAtt': [
'MyProviderframeworkonEventServiceRole8761E48D',
'Arn',
],
},
});
});
});

0 comments on commit bc01207

Please sign in to comment.