From a8b8197f99602d015718262fc383e82535b7bf11 Mon Sep 17 00:00:00 2001 From: Cory Hall <43035978+corymhall@users.noreply.github.com> Date: Fri, 9 Sep 2022 09:38:18 -0400 Subject: [PATCH] fix(lambda-event-sources): cannot add sqs event source to an imported function (#21970) If an SQS event sources is added to an imported function it will throw an error if the function is not imported with an IAM role. This PR updates the logic to only attempt to add permissions to the principal if the role exists, otherwise it will add a warning indicating that permissions were not added. fixes #12607 ---- ### All Submissions: * [ ] Have you followed the guidelines in our [Contributing guide?](https://github.com/aws/aws-cdk/blob/main/CONTRIBUTING.md) ### Adding new Unconventional Dependencies: * [ ] This PR adds new unconventional dependencies following the process described [here](https://github.com/aws/aws-cdk/blob/main/CONTRIBUTING.md/#adding-new-unconventional-dependencies) ### New Features * [ ] Have you added the new feature to an [integration test](https://github.com/aws/aws-cdk/blob/main/INTEGRATION_TESTS.md)? * [ ] Did you use `yarn integ` to deploy the infrastructure and generate the snapshot (i.e. `yarn integ` without `--dry-run`)? *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license* --- .../aws-lambda-event-sources/lib/sqs.ts | 11 +- .../aws-lambda-event-sources/test/sqs.test.ts | 109 ++++++++++++++++++ 2 files changed, 118 insertions(+), 2 deletions(-) diff --git a/packages/@aws-cdk/aws-lambda-event-sources/lib/sqs.ts b/packages/@aws-cdk/aws-lambda-event-sources/lib/sqs.ts index fb416a727655e..105a7faf66766 100644 --- a/packages/@aws-cdk/aws-lambda-event-sources/lib/sqs.ts +++ b/packages/@aws-cdk/aws-lambda-event-sources/lib/sqs.ts @@ -1,6 +1,6 @@ import * as lambda from '@aws-cdk/aws-lambda'; import * as sqs from '@aws-cdk/aws-sqs'; -import { Duration, Names, Token } from '@aws-cdk/core'; +import { Duration, Names, Token, Annotations } from '@aws-cdk/core'; export interface SqsEventSourceProps { /** @@ -84,7 +84,14 @@ export class SqsEventSource implements lambda.IEventSource { }); this._eventSourceMappingId = eventSourceMapping.eventSourceMappingId; - this.queue.grantConsumeMessages(target); + // only grant access if the lambda function has an IAM role + // otherwise the IAM module will throw an error + if (target.role) { + this.queue.grantConsumeMessages(target); + } else { + Annotations.of(target).addWarning(`Function '${target.node.path}' was imported without an IAM role `+ + `so it was not granted access to consume messages from '${this.queue.node.path}'`); + } } /** diff --git a/packages/@aws-cdk/aws-lambda-event-sources/test/sqs.test.ts b/packages/@aws-cdk/aws-lambda-event-sources/test/sqs.test.ts index e84e2c87e9f17..24d9dd307e14a 100644 --- a/packages/@aws-cdk/aws-lambda-event-sources/test/sqs.test.ts +++ b/packages/@aws-cdk/aws-lambda-event-sources/test/sqs.test.ts @@ -1,7 +1,9 @@ import { Template } from '@aws-cdk/assertions'; +import * as iam from '@aws-cdk/aws-iam'; import * as lambda from '@aws-cdk/aws-lambda'; import * as sqs from '@aws-cdk/aws-sqs'; import * as cdk from '@aws-cdk/core'; +import { App } from '@aws-cdk/core'; import * as sources from '../lib'; import { TestFunction } from './test-function'; @@ -282,8 +284,115 @@ describe('SQSEventSource', () => { Template.fromStack(stack).hasResourceProperties('AWS::Lambda::EventSourceMapping', { 'FunctionResponseTypes': ['ReportBatchItemFailures'], }); + }); + + test('warning added if lambda function imported without role', () => { + const app = new App(); + const stack = new cdk.Stack(app); + const fn = lambda.Function.fromFunctionName(stack, 'Handler', 'testFunction'); + const q = new sqs.Queue(stack, 'Q'); + + // WHEN + fn.addEventSource(new sources.SqsEventSource(q)); + const assembly = app.synth(); + + const messages = assembly.getStackArtifact(stack.artifactId).messages; + + // THEN + expect(messages.length).toEqual(1); + expect(messages[0]).toMatchObject({ + level: 'warning', + id: '/Default/Handler', + entry: { + data: expect.stringMatching(/Function 'Default\/Handler' was imported without an IAM role/), + }, + }); + // THEN + Template.fromStack(stack).resourceCountIs('AWS::Lambda::EventSourceMapping', 1); + Template.fromStack(stack).resourceCountIs('AWS::IAM::Policy', 0); + }); + test('policy added to imported function role', () => { + // GIVEN + const stack = new cdk.Stack(); + const fn = lambda.Function.fromFunctionAttributes(stack, 'Handler', { + functionArn: stack.formatArn({ + service: 'lambda', + resource: 'function', + resourceName: 'testFunction', + }), + role: iam.Role.fromRoleName(stack, 'Role', 'testFunctionRole'), + }); + const q = new sqs.Queue(stack, 'Q'); + + // WHEN + fn.addEventSource(new sources.SqsEventSource(q)); + + // THEN + Template.fromStack(stack).hasResourceProperties('AWS::IAM::Policy', { + 'PolicyDocument': { + 'Statement': [ + { + 'Action': [ + 'sqs:ReceiveMessage', + 'sqs:ChangeMessageVisibility', + 'sqs:GetQueueUrl', + 'sqs:DeleteMessage', + 'sqs:GetQueueAttributes', + ], + 'Effect': 'Allow', + 'Resource': { + 'Fn::GetAtt': [ + 'Q63C6E3AB', + 'Arn', + ], + }, + }, + ], + 'Version': '2012-10-17', + }, + 'Roles': ['testFunctionRole'], + }); + + Template.fromStack(stack).hasResourceProperties('AWS::Lambda::EventSourceMapping', { + 'EventSourceArn': { + 'Fn::GetAtt': [ + 'Q63C6E3AB', + 'Arn', + ], + }, + 'FunctionName': { + 'Fn::Select': [ + 6, + { + 'Fn::Split': [ + ':', + { + 'Fn::Join': [ + '', + [ + 'arn:', + { + 'Ref': 'AWS::Partition', + }, + ':lambda:', + { + 'Ref': 'AWS::Region', + }, + ':', + { + 'Ref': 'AWS::AccountId', + }, + ':function/testFunction', + ], + ], + }, + ], + }, + ], + }, + }); }); test('adding filter criteria', () => {