Reputation: 719
I'm trying to create an SNS topic that an SQS queue subscribes to which acts as an event source for a Lambda function. I'm trying to do this with the amplify cdk integration. However, there seems to be some problem when trying to reference the function which results in a permission problem.
CREATE_FAILED fetchMetadataSqsEventSourcesqsqueue2144E8FE AWS::Lambda::EventSourceMapping Fri May 06 2022 17:20:15 GMT+0200 (Central European Summer Time) Resource handler returned message: "Invalid request provided: The provided execution role does not have permissions to call ReceiveMessage on SQS (Service: Lambda, Status Code: 400, Request ID: 2b3147b0-8f59-4c35-8f0f-b7c29a45f139, Extended Request ID: null)" (RequestToken: c03cf5fb-283b-6d83-93c0-f7ee018338cd, HandlerErrorCode: InvalidRequest)
Here's my code
import * as AmplifyHelpers from "@aws-amplify/cli-extensibility-helper"
import * as iam from "@aws-cdk/aws-iam"
import * as lambda from "@aws-cdk/aws-lambda"
import { SqsEventSource } from "@aws-cdk/aws-lambda-event-sources"
import * as sns from "@aws-cdk/aws-sns"
import * as subs from "@aws-cdk/aws-sns-subscriptions"
import * as sqs from "@aws-cdk/aws-sqs"
import * as cdk from "@aws-cdk/core"
import { Duration } from "@aws-cdk/core"
import { AmplifyDependentResourcesAttributes } from "../../types/amplify-dependent-resources-ref"
export class cdkStack extends cdk.Stack {
constructor(
scope: cdk.Construct,
id: string,
props?: cdk.StackProps,
amplifyResourceProps?: AmplifyHelpers.AmplifyResourceProps
) {
super(scope, id, props)
/* Do not remove - Amplify CLI automatically injects the current deployment environment in this input parameter */
new cdk.CfnParameter(this, "env", {
type: "String",
description: "Current Amplify CLI env name",
})
/* AWS CDK code goes here - learn more: https://docs.aws.amazon.com/cdk/latest/guide/home.html */
// Example 1: Set up an SQS queue with an SNS topic
const amplifyProjectInfo = AmplifyHelpers.getProjectInfo()
const sqsQueueResourceNamePrefix = `sqs-queue-${amplifyProjectInfo.projectName}`
const queue = new sqs.Queue(this, "sqs-queue", {
queueName: `${sqsQueueResourceNamePrefix}-${cdk.Fn.ref("env")}`,
visibilityTimeout: Duration.seconds(30), // default,
receiveMessageWaitTime: Duration.seconds(20), // default
})
// 👇create sns topic
const snsTopicResourceNamePrefix = `sns-topic-${amplifyProjectInfo.projectName}`
const topic = new sns.Topic(this, "sns-topic", {
topicName: `${snsTopicResourceNamePrefix}-${cdk.Fn.ref("env")}`,
})
// 👇 subscribe queue to topic
topic.addSubscription(new subs.SqsSubscription(queue))
new cdk.CfnOutput(this, "snsTopicArn", {
value: topic.topicArn,
description: "The arn of the SNS topic",
})
const dependencies: AmplifyDependentResourcesAttributes =
AmplifyHelpers.addResourceDependency(
this,
amplifyResourceProps.category,
amplifyResourceProps.resourceName,
[
{
category: "function", // api, auth, storage, function, etc.
resourceName: "fetchMetadata", // find the resource at "amplify/backend/<category>/<resourceName>"
} /* add more dependencies as needed */,
]
)
const fetchMetadataFnArn = cdk.Fn.ref(
dependencies.function.fetchMetadata.Arn
)
const lambdaRole = new iam.Role(this, "Role", {
assumedBy: new iam.ServicePrincipal("lambda.amazonaws.com"),
description: "Example role...",
})
queue.grantConsumeMessages(lambdaRole)
let fn = lambda.Function.fromFunctionAttributes(this, "fetchMetadata", {
role: lambdaRole,
functionArn: fetchMetadataFnArn,
})
queue.grantConsumeMessages(fn)
const eventSource = new SqsEventSource(queue)
fn.addEventSource(eventSource)
}
}
Here's a snippet of the generated CloudFormation code, it seems like there might be an issue with the arn?
Upvotes: 0
Views: 2039
Reputation: 604
The ideal approach to reference the ARN of the lambda function created via Amplify into CDK is to use the below approach assuming you are using AWS CDK v2
const importFunction:AmplifyDependentResourcesAttributes = AmplifyHelpers.addResourceDependency(this,
amplifyResourceProps.category,
amplifyResourceProps.resourceName,
[
{category: 'function', resourceName: <Name of the function>},
]
);
const arn=cdk.Fn.ref(retVal.function.<function_name>.Arn);
This part should replace below part in your code to get the ARN
cdk.Fn.ref(
dependencies.function.fetchMetadata.Arn
)
Upvotes: 1
Reputation: 25739
You are close, but have 1 or 2 problems. CDK requires two ARNs from the imported Lambda: (1) the Function execution Role's ARN and (2) the Function ARN. You provide them with lambda.Function.fromFunctionAttributes
.
(1) Function execution Role ARN: You definitely have a problem here. You need a reference the imported Lambda's existing execution Role
via its ARN. You have created a new Role
, which is not going to work. Instead, "import" the existing role with iam.Role.fromRoleArn
A typical way to get a Role ARN is to export it as a CloudFormation output and import it into cdkStack
:
const fn = lambda.Function.fromFunctionAttributes(this, `FetchMetadata`, {
role: iam.Role.fromRoleArn(
this,
'ImportedRole',
cdk.Fn.importValue('NameOfStackExportWithTheRoleARN')
),
functionArn: fetchMetadataFnArn,
});
If the imported Lambda was not created with CloudFormation, a SSM Parameter Store Parameter would be a way to pass cdkStack
the Role's ARN. Or hardcode it.
(2) Function ARN: You may have a problem here. I am not familiar with the Amplify helpers. You can diagnose whether fetchMetadataFnArn
is correctly resolving by temporarily adding a CfnOutput
to cdkStack
:
new cdk.CfnOutput(this, 'JustCheckingWhetherTheFunctionArnIsReolvingOK', {
value: fetchMetadataFnArn,
});
CDK prints these outputs to the console at deploy-time. Check to see if the ARN is what you expect.
Once you get the imported ARNs right, CDK can create the AWS::SQS::QueuePolicy
and AWS::Lambda::EventSourceMapping
it needs to wire the Lambda to the Queue.
N.B. You can delete queue.grantConsumeMessages(lambdaRole)
and queue.grantConsumeMessages(fn)
. They are redundant. fn.addEventSource(eventSource)
makes the grant under the hood.
Upvotes: 2