简体   繁体   中英

How to import existing lambda from arn and add SQS as event source? Amplify

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? 在此处输入图像描述

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.


NB You can delete queue.grantConsumeMessages(lambdaRole) and queue.grantConsumeMessages(fn) . They are redundant. fn.addEventSource(eventSource) makes the grant under the hood.

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
)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM