From b96efd8aaa143845b9fe315a9ee1e8398c4d83c2 Mon Sep 17 00:00:00 2001 From: Ayush Goyal Date: Thu, 1 Oct 2020 04:37:22 +0530 Subject: [PATCH] feat(events-targets): option to provide an existing role to use with the StepFunctions State Machine target (#10551) Add option to use existing role for SfnStateMachine closes #8255 ---- *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license* --- .../aws-events-targets/lib/state-machine.ts | 20 ++++- .../test/stepfunctions/statemachine.test.ts | 82 +++++++++++++++++++ 2 files changed, 98 insertions(+), 4 deletions(-) diff --git a/packages/@aws-cdk/aws-events-targets/lib/state-machine.ts b/packages/@aws-cdk/aws-events-targets/lib/state-machine.ts index 5fa41ffae689f..acffea0eea6f4 100644 --- a/packages/@aws-cdk/aws-events-targets/lib/state-machine.ts +++ b/packages/@aws-cdk/aws-events-targets/lib/state-machine.ts @@ -13,13 +13,28 @@ export interface SfnStateMachineProps { * @default the entire EventBridge event */ readonly input?: events.RuleTargetInput; + + /** + * The IAM role to be assumed to execute the State Machine + * + * @default - a new role will be created + */ + readonly role?: iam.IRole; } /** * Use a StepFunctions state machine as a target for Amazon EventBridge rules. */ export class SfnStateMachine implements events.IRuleTarget { + private readonly role: iam.IRole; + constructor(public readonly machine: sfn.IStateMachine, private readonly props: SfnStateMachineProps = {}) { + if (props.role) { + props.role.grant(new iam.ServicePrincipal('events.amazonaws.com')); + } + // no statements are passed because we are configuring permissions by using grant* helper below + this.role = props.role ?? singletonEventRole(machine, []); + machine.grantStartExecution(this.role); } /** @@ -31,10 +46,7 @@ export class SfnStateMachine implements events.IRuleTarget { return { id: '', arn: this.machine.stateMachineArn, - role: singletonEventRole(this.machine, [new iam.PolicyStatement({ - actions: ['states:StartExecution'], - resources: [this.machine.stateMachineArn], - })]), + role: this.role, input: this.props.input, targetResource: this.machine, }; diff --git a/packages/@aws-cdk/aws-events-targets/test/stepfunctions/statemachine.test.ts b/packages/@aws-cdk/aws-events-targets/test/stepfunctions/statemachine.test.ts index 1617fb59c3b87..3b50ef569f004 100644 --- a/packages/@aws-cdk/aws-events-targets/test/stepfunctions/statemachine.test.ts +++ b/packages/@aws-cdk/aws-events-targets/test/stepfunctions/statemachine.test.ts @@ -1,5 +1,6 @@ import '@aws-cdk/assert/jest'; import * as events from '@aws-cdk/aws-events'; +import * as iam from '@aws-cdk/aws-iam'; import * as sfn from '@aws-cdk/aws-stepfunctions'; import * as cdk from '@aws-cdk/core'; import * as targets from '../../lib'; @@ -27,4 +28,85 @@ test('State machine can be used as Event Rule target', () => { }, ], }); + expect(stack).toHaveResourceLike('AWS::IAM::Role', { + AssumeRolePolicyDocument: { + Statement: [ + { + Action: 'sts:AssumeRole', + Effect: 'Allow', + Principal: { + Service: 'events.amazonaws.com', + }, + }, + ], + }, + }); + expect(stack).toHaveResourceLike('AWS::IAM::Policy', { + PolicyDocument: { + Statement: [ + { + Action: 'states:StartExecution', + Effect: 'Allow', + Resource: { + Ref: 'SM934E715A', + }, + }, + ], + }, + }); +}); + +test('Existing role can be used for State machine Rule target', () => { + // GIVEN + const stack = new cdk.Stack(); + const rule = new events.Rule(stack, 'Rule', { + schedule: events.Schedule.rate(cdk.Duration.minutes(1)), + }); + const role = new iam.Role(stack, 'Role', { + assumedBy: new iam.ServicePrincipal('events.amazonaws.com'), + }); + const stateMachine = new sfn.StateMachine(stack, 'SM', { + definition: new sfn.Wait(stack, 'Hello', { time: sfn.WaitTime.duration(cdk.Duration.seconds(10)) }), + role, + }); + + // WHEN + rule.addTarget(new targets.SfnStateMachine(stateMachine, { + input: events.RuleTargetInput.fromObject({ SomeParam: 'SomeValue' }), + })); + + // THEN + expect(stack).toHaveResourceLike('AWS::Events::Rule', { + Targets: [ + { + Input: '{"SomeParam":"SomeValue"}', + }, + ], + }); + expect(stack).toHaveResourceLike('AWS::IAM::Role', { + AssumeRolePolicyDocument: { + Statement: [ + { + Action: 'sts:AssumeRole', + Effect: 'Allow', + Principal: { + Service: 'events.amazonaws.com', + }, + }, + ], + }, + }); + expect(stack).toHaveResourceLike('AWS::IAM::Policy', { + PolicyDocument: { + Statement: [ + { + Action: 'states:StartExecution', + Effect: 'Allow', + Resource: { + Ref: 'SM934E715A', + }, + }, + ], + }, + }); });