When AWS services are wired together, the part that usually breaks first is not the business logic. It is the permission model.

A Lambda can run but fail to publish to SNS. An SNS subscription can exist but still fail to deliver to SQS. API Gateway can be configured correctly and still return a permission error because Lambda never granted it invoke access.

The easiest way to reason about this is to stop thinking about permissions in the abstract and follow one concrete flow end to end.

In this example, the system looks like this:

Architecture overview

  • API Gateway receives order requests.
  • API Gateway invokes an Order Lambda.
  • The Order Lambda writes order state to DynamoDB.
  • The Order Lambda publishes an event to SNS.
  • SNS fans the event out to SQS queues.
  • Consumer Lambdas poll those queues and write audit records to another DynamoDB table.

The permission model by hop

The cleanest way to summarize the whole system is by looking at each hop separately.

1. API Gateway -> Order Lambda

  • acting service: API Gateway
  • policy type: Lambda resource policy

API Gateway integration with the Order Lambda

Lambda resource-based policy allowing API Gateway invoke

2. Order Lambda -> Orders DynamoDB table, SNS topic

  • acting principal: Order Lambda execution role
  • policy type: IAM identity policy

Order Lambda execution role Order Lambda execution role

The execution role needs identity-based permissions for the downstream services it calls, such as dynamodb:PutItem and sns:Publish.

3. SNS topic -> SQS queue

  • acting service: SNS
  • policy type: SQS queue resource policy

This hop usually needs two separate pieces:

  • the SNS subscription that connects the topic to the queue
  • the SQS queue policy that explicitly allows the SNS topic to send messages

SNS subscription from topic to queue

SQS queue policy allowing SNS delivery

SNS subscription filter policy

4. Consumer Lambda -> SQS queue, Audit DynamoDB table

  • acting principal: consumer Lambda execution role
  • policy type: IAM identity policy

Lambda polls SQS through the event source mapping, and the function role still needs permission for the work done after receipt, such as writing to the audit DynamoDB table.

Consumer Lambda execution role

Consumer Lambda role policy

SQS trigger on consumer Lambda

Permission Matrix

From To Principal or Service Policy Type Actions Resource
API Gateway Order Lambda apigateway.amazonaws.com Lambda resource policy lambda:InvokeFunction Order Lambda
Order Lambda Orders table Order Lambda role IAM identity policy dynamodb:PutItem, dynamodb:UpdateItem Orders table
Order Lambda SNS topic Order Lambda role IAM identity policy sns:Publish Orders topic
SNS topic Consumer queue sns.amazonaws.com SQS queue policy sqs:SendMessage Consumer queue
Consumer Lambda Consumer queue Consumer Lambda role IAM identity policy sqs:ReceiveMessage, sqs:DeleteMessage, sqs:GetQueueAttributes, sqs:ChangeMessageVisibility Consumer queue
Consumer Lambda Audit table Consumer Lambda role IAM identity policy dynamodb:PutItem Audit table
Lambda functions CloudWatch Logs each Lambda role IAM identity policy logs:CreateLogGroup, logs:CreateLogStream, logs:PutLogEvents each function’s log group