While creating a Lambda function using Terraform, the operation failed with an AccessDeniedException related to the iam:PassRole permission

We are building an AWS workflow where SNS triggers a Lambda, which pushes messages to SQS, and another Lambda reads from SQS and sends emails using SES. To achieve this, we created custom IAM roles (lambda-sqs-ses-role, etc.) that the Lambda functions should assume.

Error Message -
operation error Lambda: CreateFunction, https response error StatusCode: 403,
api error AccessDeniedException: User: arn:aws:iam:::user/kk_labs_user_448879
is not authorized to perform: iam:PassRole on resource:
arn:aws:iam:::role/lambda-sqs-ses-role_vd
because no identity-based policy allows the iam:PassRole action

Hi @vidya4java,

Are you working in the lab or in the KodeKloud playground? If it’s in the lab, please share the lab link so I can check it.

Try calling the role lambda_execution_role. PassRole permission is granted only to specific named roles.

Hi @raymond.baoly , I am working on the KodeKloud playground for learning purposes and trying out different scenarios where I need to set up infrastructure using Terraform. However, I am encountering an error stating that the user does not have permission to create a role. Kindly assist with this issue

Hi @vidya4java

In that case, please try Alistair’s solution.

@raymond.baoly , I am not sure how to do this. Please share the steps.

Hi Team, Still getting below error

Error: creating Lambda Event Source Mapping (arn:aws:sqs:us-east-1:460059030778:lambda1-to-lambda2-queue): operation error Lambda: CreateEventSourceMapping,

Please find my attached code .

terraform {
required_providers {
aws = {
source = “hashicorp/aws”
version = “~> 5.0”
}
archive = {
source = “hashicorp/archive”
version = “~> 2.0”
}
}
required_version = “>= 1.2.0”
}

provider “aws” {
region = var.aws_region
}

variable “aws_region” {
type = string
default = “us-east-1”
}

variable “ses_source_email” {
type = string
description = “The ‘From’ email address to use with SES. Must be verified if SES is in sandbox.”
}

— Use an existing Lambda execution role —

data “aws_iam_role” “lambda_exec” {
name = “lambda_execution_role” # Must exist with SQS/SES/Logs permissions
}

— SNS Topic —

resource “aws_sns_topic” “incoming” {
name = “sns-to-lambda1-topic”
}

— SQS Queue —

resource “aws_sqs_queue” “processing” {
name = “lambda1-to-lambda2-queue”
visibility_timeout_seconds = 30
message_retention_seconds = 1209600
}

— Allow SNS → SQS —

data “aws_iam_policy_document” “sqs_allow_sns” {
statement {
principals {
type = “Service”
identifiers = [“sns.amazonaws.com”]
}

actions = [
  "sqs:SendMessage",
  "sqs:GetQueueAttributes",
  "sqs:GetQueueUrl"
]

resources = [aws_sqs_queue.processing.arn]

condition {
  test     = "ArnEquals"
  variable = "aws:SourceArn"
  values   = [aws_sns_topic.incoming.arn]
}

}
}

resource “aws_sqs_queue_policy” “allow_sns” {
queue_url = aws_sqs_queue.processing.id
policy = data.aws_iam_policy_document.sqs_allow_sns.json
}

— Lambda1 code (SNS → SQS) —

data “archive_file” “lambda1_zip” {
type = “zip”
output_path = “${path.module}/lambda1.zip”

source {
content = <<-PY
import json
import os
import boto3
import logging

  logger = logging.getLogger()
  logger.setLevel(logging.INFO)

  sqs = boto3.client("sqs")
  SQS_QUEUE_URL = os.environ.get("SQS_QUEUE_URL")

  def lambda_handler(event, context):
      logger.info("Received event: %s", json.dumps(event))
      for record in event.get("Records", []):
          sns = record.get("Sns", {})
          message = sns.get("Message", "")
          logger.info("SNS message: %s", message)
          sqs.send_message(QueueUrl=SQS_QUEUE_URL, MessageBody=message)
      return {"status": "ok"}
PY
filename = "lambda1.py"

}
}

— Lambda2 code (SQS → SES) —

data “archive_file” “lambda2_zip” {
type = “zip”
output_path = “${path.module}/lambda2.zip”

source {
content = <<-PY
import json
import os
import boto3
import logging

  logger = logging.getLogger()
  logger.setLevel(logging.INFO)

  ses = boto3.client("ses")
  SES_SOURCE = os.environ.get("SES_SOURCE_EMAIL")

  def send_email_simple(recipient, subject, body):
      return ses.send_email(
          Source=SES_SOURCE,
          Destination={"ToAddresses": [recipient]},
          Message={
              "Subject": {"Data": subject},
              "Body": {"Text": {"Data": body}}
          }
      )

  def lambda_handler(event, context):
      results = []
      for record in event.get("Records", []):
          body = record.get("body", "")
          try:
              payload = json.loads(body)
              if isinstance(payload, dict) and "to" in payload:
                  recipient = payload["to"]
                  subject = payload.get("subject", "Notification from Lambda2")
                  message_body = payload.get("body", "")
              else:
                  recipient = SES_SOURCE
                  subject = "Notification from Lambda2"
                  message_body = body
          except Exception:
              recipient = SES_SOURCE
              subject = "Notification from Lambda2"
              message_body = body
          try:
              send_email_simple(recipient, subject, message_body)
              results.append({"status": "sent", "recipient": recipient})
          except Exception as e:
              results.append({"status": "failed", "recipient": recipient, "error": str(e)})
      return {"results": results}
PY
filename = "lambda2.py"

}
}

— Lambda1 (SNS → SQS) —

resource “aws_lambda_function” “lambda1” {
function_name = “sns-to-sqs-lambda1”
filename = data.archive_file.lambda1_zip.output_path
source_code_hash = filebase64sha256(data.archive_file.lambda1_zip.output_path)
handler = “lambda1.lambda_handler”
runtime = “python3.9”
role = data.aws_iam_role.lambda_exec.arn

environment {
variables = {
SQS_QUEUE_URL = aws_sqs_queue.processing.url
}
}

timeout = 30
memory_size = 128
}

resource “aws_lambda_permission” “allow_sns” {
statement_id = “AllowExecutionFromSNS”
action = “lambda:InvokeFunction”
function_name = aws_lambda_function.lambda1.function_name
principal = “sns.amazonaws.com
source_arn = aws_sns_topic.incoming.arn
}

resource “aws_sns_topic_subscription” “lambda1_sub” {
topic_arn = aws_sns_topic.incoming.arn
protocol = “lambda”
endpoint = aws_lambda_function.lambda1.arn
}

— Lambda2 (SQS → SES) —

resource “aws_lambda_function” “lambda2” {
function_name = “sqs-to-ses-lambda2”
filename = data.archive_file.lambda2_zip.output_path
source_code_hash = filebase64sha256(data.archive_file.lambda2_zip.output_path)
handler = “lambda2.lambda_handler”
runtime = “python3.9”
role = data.aws_iam_role.lambda_exec.arn

environment {
variables = {
SES_SOURCE_EMAIL = var.ses_source_email
}
}

timeout = 30
memory_size = 256
}

resource “aws_lambda_event_source_mapping” “sqs_to_lambda2” {
event_source_arn = aws_sqs_queue.processing.arn
function_name = aws_lambda_function.lambda2.arn
batch_size = 1
enabled = true
}

— Outputs —

output “sns_topic_arn” {
value = aws_sns_topic.incoming.arn
}

output “sqs_queue_url” {
value = aws_sqs_queue.processing.url
}

output “lambda1_name” {
value = aws_lambda_function.lambda1.function_name
}

output “lambda2_name” {
value = aws_lambda_function.lambda2.function_name
}

You may want to review how to format code in Discourse, which is the software this forum uses, since your code is hopeless garbled :frowning: You want to put your code in code blocks; this guide explains how to do that.

@Alistair_KodeKloud, @raymond.baoly , I want to understand is how to grant a Lambda function the necessary permissions to access services like S3, SQS, and SNS. Could you provide some sample examples?

Blockquote

No, what I want to understand is how to grant a Lambda function the necessary permissions to access services like S3, SQS, and SNS. Could you provide some sample examples?

Valid, well formatted code will help us help you in that regard. It’s hard to figure out what you’re trying to do without that.

Hi Team,

I am trying to set up an event-driven architecture using Terraform. The workflow I want to implement is as follows:

A client application sends an event through a Python program to an SNS topic.

The SNS topic triggers a Lambda function, which processes the event, creates a message, and sends it to an SQS queue.

The SQS queue then invokes another Lambda function, which is responsible for sending a sample email to the client using Amazon SES.

I have been able to build and run this end-to-end flow successfully through manual configuration in the AWS console. However, when I try to automate the same setup using Terraform, I encounter permission issues (IAM roles/policies not allowing the required actions).

Regards
Vidya

Hi Team, I am trying to create user with multiple access like S3 full access, SQS full access etc. but getting below error. Let me know how we can fix this issue.

User: arn:aws:iam::152448202018:user/kk_labs_user_984047

Action: iam:CreateUser

On resource(s): arn:aws:iam::152448202018:user/firstUsers

Context: no identity-based policy allows the action