diff --git a/packages/aws-cdk-lib/aws-events-targets/lib/ecs-task.ts b/packages/aws-cdk-lib/aws-events-targets/lib/ecs-task.ts index d17ef04e17e20..6498273a2007f 100644 --- a/packages/aws-cdk-lib/aws-events-targets/lib/ecs-task.ts +++ b/packages/aws-cdk-lib/aws-events-targets/lib/ecs-task.ts @@ -277,10 +277,18 @@ export class EcsTask implements events.IRuleTarget { } private createEventRolePolicyStatements(): iam.PolicyStatement[] { + // check if there is a taskdefinition revision (arn will end with : followed by digits) included in the arn already + let needsRevisionWildcard = false; + if ( !cdk.Token.isUnresolved(this.taskDefinition.taskDefinitionArn)) { + const revisionAtEndPattern = /:[0-9]+$/; + const hasRevision = revisionAtEndPattern.test(this.taskDefinition.taskDefinitionArn); + needsRevisionWildcard = !hasRevision; + } + const policyStatements = [ new iam.PolicyStatement({ actions: ['ecs:RunTask'], - resources: [this.taskDefinition.taskDefinitionArn], + resources: [`${this.taskDefinition.taskDefinitionArn}${needsRevisionWildcard ? ':*' : ''}`], conditions: { ArnEquals: { 'ecs:cluster': this.cluster.clusterArn }, }, diff --git a/packages/aws-cdk-lib/aws-events-targets/test/ecs/event-rule-target.test.ts b/packages/aws-cdk-lib/aws-events-targets/test/ecs/event-rule-target.test.ts index ebc88f0f91e9a..d6a6c857e2914 100644 --- a/packages/aws-cdk-lib/aws-events-targets/test/ecs/event-rule-target.test.ts +++ b/packages/aws-cdk-lib/aws-events-targets/test/ecs/event-rule-target.test.ts @@ -9,6 +9,7 @@ import * as iam from '../../../aws-iam'; import * as sqs from '../../../aws-sqs'; import * as cdk from '../../../core'; import * as targets from '../../lib'; +import { EcsTask } from '../../lib'; let stack: cdk.Stack; let vpc: ec2.Vpc; @@ -1095,3 +1096,100 @@ test.each([ ], }); }); + +test('Non-imported TaskDefinition role is targeting by Ref', () => { + const taskDefinition = new ecs.FargateTaskDefinition(stack, 'TaskDef'); + taskDefinition.addContainer('TheContainer', { + image: ecs.ContainerImage.fromRegistry('henk'), + }); + + const rule = new events.Rule(stack, 'Rule', { + schedule: events.Schedule.rate(cdk.Duration.hours(1)), + }); + + rule.addTarget( + new EcsTask({ + cluster: cluster, + taskDefinition: taskDefinition, + }), + ); + + const policyMatch = Match.objectLike({ + PolicyDocument: { + Statement: Match.arrayWith([ + Match.objectLike({ + Action: 'ecs:RunTask', + Resource: { + Ref: 'TaskDef54694570', + }, + }), + ]), + }, + }); + const template = Template.fromStack(stack); + template.hasResource('AWS::IAM::Policy', { Properties: policyMatch }); +}); + +test('Imported task definition without revision adds wildcard to policy resource', () => { + const taskDefinition = ecs.FargateTaskDefinition.fromFargateTaskDefinitionAttributes(stack, 'TaskDefImport', { + taskDefinitionArn: 'arn:aws:ecs:us-west-2:012345678901:task-definition/MyTask', + taskRole: iam.Role.fromRoleArn(stack, 'RoleImport', 'arn:aws:iam::012345678901:role/MyTaskRole'), + networkMode: ecs.NetworkMode.AWS_VPC, + }); + + const rule = new events.Rule(stack, 'Rule', { + schedule: events.Schedule.rate(cdk.Duration.hours(1)), + }); + + rule.addTarget( + new EcsTask({ + cluster: cluster, + taskDefinition: taskDefinition, + }), + ); + + const policyMatch = Match.objectLike({ + PolicyDocument: { + Statement: Match.arrayWith([ + Match.objectLike({ + Action: 'ecs:RunTask', + Resource: `${taskDefinition.taskDefinitionArn}:*`, + }), + ]), + }, + }); + const template = Template.fromStack(stack); + template.hasResource('AWS::IAM::Policy', { Properties: policyMatch }); +}); + +test('Imported task definition with revision uses original arn for policy resource', () => { + const taskDefinition = ecs.FargateTaskDefinition.fromFargateTaskDefinitionAttributes(stack, 'TaskDefImport', { + taskDefinitionArn: 'arn:aws:ecs:us-west-2:012345678901:task-definition/MyTask:1', + taskRole: iam.Role.fromRoleArn(stack, 'RoleImport', 'arn:aws:iam::012345678901:role/MyTaskRole'), + networkMode: ecs.NetworkMode.AWS_VPC, + }); + + const rule = new events.Rule(stack, 'Rule', { + schedule: events.Schedule.rate(cdk.Duration.hours(1)), + }); + + rule.addTarget( + new EcsTask({ + cluster: cluster, + taskDefinition: taskDefinition, + }), + ); + + const policyMatch = Match.objectLike({ + PolicyDocument: { + Statement: Match.arrayWith([ + Match.objectLike({ + Action: 'ecs:RunTask', + Resource: taskDefinition.taskDefinitionArn, + }), + ]), + }, + }); + const template = Template.fromStack(stack); + template.hasResource('AWS::IAM::Policy', { Properties: policyMatch }); +}); \ No newline at end of file