Using CDK to create an IoT CfnTopicRule that can invoke a lambda via 'aws_lambda' function

0

I am using the following form of a Topic Rule SQL statement:

SELECT aws_lambda("<<functionArn>>", *) as output FROM '<<topic>>'

But the function never gets called (CloudWatch log group never gets a new stream created), and the result is an empty object (I republish the result to another IoT Core topic.

If I enable logging for IoT, the logs show the following error:

Received an error while making a request to Lambda: ErrorType: Client, ErrorCode: AccessDeniedException, StatusCode: 403

The topic rule is constructed as:

        const rule = new CfnTopicRule(this, 'LoriotIngest', {
            topicRulePayload: {
                sql: `
                    SELECT 
                        aws_lambda("<<functionArn>>", *) as payload 
                    FROM 
                        '$aws/things/+/shadow/update/accepted'
                `,
                actions: [
                    {
                        republish: {
                            topic: 'loriot/ingest',
                            roleArn: topicRuleRole.roleArn,
                            qos: 0,
                        }
                    },
                ],
            }
        })

The role for the topic rule is constructed as:

        const topicRuleRole = new Role(this, 'TopicRuleRole', {
            assumedBy: new ServicePrincipal(ServicePrincipals.IOT),
        })

The role is applied to the republish action, and is having another policy statement added to give is iot:Publish rights, and messages are being republished as the MQTT client shows them arriving (when I exclude the aws_lambda call).

It seems that its not possible to provide the CfnTopicRule with a role that grants it lambda:InvokeFunction rights - the props don't take a role, nor is one exposed.

Other questions seem to be working from the CLI, and so this perhaps is an ommission in the CfnTopicRule API which isn't taking account of calling functions, but there seem to be plenty of examples so I must be overlooking something.

The intention of the lambda function is to do payload decoding, without needing to go through another publish step. I've found samples that show the implementation of a payload decoder, but not the topic rule configuration.

3 Answers
0

You may need to grant permission for the AWS IoT service to invoke your Lambda function. Use the add-permission command to add a permission statement to your function's resource-based policy.

Reference : https://docs.aws.amazon.com/lambda/latest/dg/services-iot.html

If you are using CDK, you can perhaps try something like this below

// Grant permission for AWS IoT to invoke the Lambda function
const iotServicePrincipal = new iam.ServicePrincipal('iot.amazonaws.com');
lambdaFunction.grantInvoke(iotServicePrincipal);

The rule action must have permission to receive the original topic and publish the new topic.

The policies that authorize the rule to receive message data and republish it are specific to the topics used. If you change the topic used to republish the message data, you must update the rule action's role to update its policy to match the current topic.

If you suspect this is the problem, edit the Republish rule action and create a new role. New roles created by the rule action receive the authorizations necessary to perform these actions

profile pictureAWS
EXPERT
answered 10 months ago
  • Agreed, that was missing and seems to have silenced the AWSIotV2 error messages, but the function still appears not to be called (the topic rule republishes but the lambda result is missing, although I can select other fields and see them republished). The function does not create a log group, log stream or log entries, but does when I 'test' it from the lambda console. This suggests that the topic rule might have a problem with the aws_lambda() call, but isn't treating it as an error (perhaps it just sees the result as null/undefined and therefore doesn't include it in the output). I'll post a screenshot of the lambda permissions, which shows that it has its own logs:* actions and a policy statement for IoT that allows lambda:InvokeFunction.

0

Hi Dave Meehan,

you must add the lambda:InvokeFunction permission to the Lambda function: https://docs.aws.amazon.com/iot/latest/developerguide/iot-sql-functions.html#iot-func-aws-lambda

Cheers,
Philipp

AWS
EXPERT
answered 10 months ago
0

This shows the configuration of the lambda permissions. I beleive this is consistent with the documentation.

Enter image description here

Here is the topic rule (aws iot get-topic-rule ...)

{
    "ruleArn": "arn:aws:iot:eu-west-1:<<REDACTED>>:rule/Dev<<REDACTED>>AppLoriotIngestPayloadParserRule_81f3ecff",
    "rule": {
        "ruleName": "Dev<<REDACTED>>AppLoriotIngestPayloadParserRule_81f3ecff",
        "sql": "SELECT aws_lamdba('arn:aws:lambda:eu-west-1:<<REDACTED>>:function:Dev<<REDACTED>>AppLoriotIngestPayloadParser_96078392', *) as event.response, topic(3) as event.eui FROM '$aws/things/+/shadow/update/accepted' ",
        "createdAt": "2023-07-14T13:39:24+01:00",
        "actions": [
            {
                "republish": {
                    "roleArn": "arn:aws:iam::<<REDACTED>>:role/Dev<<REDACTED>>AppLoriotIngestPayloadParserRuleRepublish_3a5eba09",
                    "topic": "loriot/ingest",
                    "qos": 0,
                    "headers": {}
                }
            }
        ],
        "ruleDisabled": true,
        "awsIotSqlVersion": "2016-03-23",
        "errorAction": {
            "republish": {
                "roleArn": "arn:aws:iam::<<REDACTED>>:role/Dev<<REDACTED>>AppLoriotIngestPayloadParserRuleError_ff712474",
                "topic": "errors",
                "qos": 0,
                "headers": {}
            }
        }
    }
}
answered 10 months ago

You are not logged in. Log in to post an answer.

A good answer clearly answers the question and provides constructive feedback and encourages professional growth in the question asker.

Guidelines for Answering Questions