Event Source Mapping Missing Required IAM Permissions
TL-PERM-002
What this rule checks
An AWS::Lambda::EventSourceMapping polls an in-template SQS queue, Kinesis stream, or DynamoDB stream but the function execution role lacks the documented required actions (e.g. sqs:ReceiveMessage / DeleteMessage / GetQueueAttributes). The mapping fails to create or the poller fails with AccessDenied.
How to fix it
- 1Grant the poller permissions in CDK: queue.grantConsumeMessages(fn), stream.grantRead(fn), or table.grantStreamRead(fn)
- 2Or attach the matching AWS managed policy: AWSLambdaSQSQueueExecutionRole, AWSLambdaKinesisExecutionRole, or AWSLambdaDynamoDBExecutionRole
- 3The Lambda service polls the source with the function's execution role, so the role itself needs these actions
import * as lambda from 'aws-cdk-lib/aws-lambda';
import * as sqs from 'aws-cdk-lib/aws-sqs';
const queue = new sqs.Queue(this, 'Jobs');
const worker = new lambda.Function(this, 'Worker', {
runtime: lambda.Runtime.NODEJS_20_X,
handler: 'index.handler',
code: lambda.Code.fromInline('exports.handler = async () => {};'),
});
new lambda.CfnEventSourceMapping(this, 'Mapping', {
functionName: worker.functionName,
eventSourceArn: queue.queueArn,
});import * as lambda from 'aws-cdk-lib/aws-lambda';
import { SqsEventSource } from 'aws-cdk-lib/aws-lambda-event-sources';
import * as sqs from 'aws-cdk-lib/aws-sqs';
const queue = new sqs.Queue(this, 'Jobs');
const worker = new lambda.Function(this, 'Worker', {
runtime: lambda.Runtime.NODEJS_20_X,
handler: 'index.handler',
code: lambda.Code.fromInline('exports.handler = async () => {};'),
});
worker.addEventSource(new SqsEventSource(queue));CDK Insights pinpoints the exact file and line in your CDK source for every finding, so you can jump straight to the fix.
Affected resource types
AWS::Lambda::EventSourceMappingAWS::Lambda::FunctionAWS::IAM::RoleIntentional? Suppress this finding
Sometimes a flag is deliberate — a genuinely public endpoint, say. You can dismiss TL-PERM-002 and the reason is kept in the report, not silently hidden.
In .cdk-insights.json:
{
"ignoreRules": [
{ "id": "TL-PERM-002", "reason": "Why this is intentional" }
]
}Or inline in your CDK code:
Validations.of(scope).acknowledge({
id: 'cdk-insights::TL-PERM-002',
reason: 'Why this is intentional',
});Use the rule ID TL-PERM-002 shown above — not the CDK-* ID from SARIF / GitHub code scanning. To dismiss every finding on one construct instead, use ignorePaths. Suppression docs →
Catch this in your stack
$ npx cdk-insights scanCDK Insights runs this and 131+ other rules locally against your synthesised CDK app — free, no account, your code never leaves your machine.