Back to blog
AWSBest PracticesCloud SecurityIdentity & AccessServerless

Lambda Has an Invalid Execution Role: Detect and Fix Orphaned IAM References

Learn why an AWS Lambda function with a deleted IAM execution role fails silently, how to detect it, fix it with the CLI, and prevent it with IaC and CI/CD.

TL;DR

This check flags Lambda functions whose IAM execution role has been deleted. Without a valid role, the function fails to run and can break entire event-driven pipelines. Fix it by recreating the role or repointing the function to a valid one with aws lambda update-function-configuration.

A Lambda function is only as good as the permissions behind it. Every function assumes an IAM execution role at invocation time, and that role is what grants the function access to CloudWatch Logs, DynamoDB tables, S3 buckets, and anything else it touches. When that role disappears, the function keeps its configuration but loses its ability to execute. The lambda_invalidrole check catches exactly this situation: a function pointing at an execution role ARN that no longer exists in your account.


What this check detects

The check inspects each Lambda function's configured execution role and verifies that the referenced IAM role still exists. The role is stored as an ARN in the function's configuration, something like:

arn:aws:iam::123456789012:role/my-lambda-execution-role

Lambda does not validate the role on every read. You can delete the role and the function configuration will happily keep displaying the stale ARN. The breakage only surfaces when the function is invoked, at which point Lambda fails to assume the role. This check surfaces the problem proactively, before an invocation fails in production.

Note: The execution role is different from the resource-based policy that controls who can invoke a function. The execution role defines what the function itself is allowed to do once it runs. Deleting it does not stop callers from triggering the function, it just guarantees the invocation will error out.


Why it matters

An invalid execution role is a silent failure waiting to happen. Here is what tends to go wrong in practice.

Invocations fail at runtime

When Lambda cannot assume the execution role, the invocation throws an error before your code ever runs. For synchronous callers like API Gateway, the client gets a 5xx response. For asynchronous invocations, Lambda retries and eventually drops the event, often without anyone noticing until data goes missing.

Event sources back up or drop data

Functions wired to SQS, Kinesis, or DynamoDB Streams depend on a working execution role to read from the source. If the role is gone, messages pile up in the queue or stream records age out. A Kinesis stream with a 24 hour retention window can lose data permanently if the consumer is broken longer than that.

Warning: Asynchronous and stream-based failures are easy to miss because they do not return an error to a human-facing client. Configure a dead-letter queue or on-failure destination so dropped events land somewhere you can inspect them.

It usually means broader IAM drift

A deleted execution role rarely happens in isolation. It often points to a Terraform state that has drifted, a half-finished cleanup of a decommissioned project, or someone manually deleting roles to tidy up the IAM console without realizing what depended on them. Where you find one orphaned reference, you tend to find more.


How to fix it

You have two paths: recreate the missing role, or repoint the function at a role that already exists. Pick based on whether the role was deleted by accident or whether the function should be sharing an existing role.

Step 1: Confirm the missing role

Find out which role the function references and confirm it is actually gone.

# Get the configured execution role for the function
aws lambda get-function-configuration \
  --function-name my-function \
  --query 'Role' \
  --output text

# Check whether that role exists (extract the role name from the ARN)
aws iam get-role --role-name my-lambda-execution-role

If get-role returns NoSuchEntity, the role is confirmed missing.

Step 2a: Recreate the role

If the role was deleted by mistake, recreate it with the correct trust policy. Lambda execution roles must trust the Lambda service.

# Create a trust policy file
cat > trust-policy.json <<'EOF'
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": { "Service": "lambda.amazonaws.com" },
      "Action": "sts:AssumeRole"
    }
  ]
}
EOF

# Recreate the role
aws iam create-role \
  --role-name my-lambda-execution-role \
  --assume-role-policy-document file://trust-policy.json

# Attach the basic execution policy for CloudWatch Logs
aws iam attach-role-policy \
  --role-name my-lambda-execution-role \
  --policy-arn arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole

Attach any additional managed or inline policies the function needs to reach its downstream resources. If the function reads from DynamoDB, give it the relevant scoped permissions rather than a broad managed policy.

Danger: Resist the urge to attach AdministratorAccess or AWSLambda_FullAccess just to get things working again. An over-permissioned execution role turns a single compromised function into a path to your whole account. Scope permissions to exactly what the function calls.

Step 2b: Repoint to an existing role

If a suitable role already exists, update the function configuration to use it.

aws lambda update-function-configuration \
  --function-name my-function \
  --role arn:aws:iam::123456789012:role/existing-lambda-role

Step 3: Verify

Invoke the function and confirm it runs cleanly.

aws lambda invoke \
  --function-name my-function \
  --payload '{}' \
  --cli-binary-format raw-in-base64-out \
  response.json

cat response.json

Tip: Newly created IAM roles can take a few seconds to propagate. If an invocation fails immediately after you recreate the role, wait 10 to 15 seconds and try again before assuming something else is wrong.


How to prevent it from happening again

The root cause is almost always an IAM role being deleted independently of the function that uses it. The fix is to manage roles and functions together and to validate references before they reach production.

Define roles and functions in the same IaC

When you define the execution role alongside the function in Terraform or CloudFormation, the dependency graph protects you. Terraform will not let you delete a role that a managed function still references.

resource "aws_iam_role" "lambda_exec" {
  name = "my-lambda-execution-role"

  assume_role_policy = jsonencode({
    Version = "2012-10-17"
    Statement = [{
      Action    = "sts:AssumeRole"
      Effect    = "Allow"
      Principal = { Service = "lambda.amazonaws.com" }
    }]
  })
}

resource "aws_iam_role_policy_attachment" "basic" {
  role       = aws_iam_role.lambda_exec.name
  policy_arn = "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"
}

resource "aws_lambda_function" "this" {
  function_name = "my-function"
  role          = aws_iam_role.lambda_exec.arn
  handler       = "index.handler"
  runtime       = "python3.12"
  filename      = "function.zip"
}

The role = aws_iam_role.lambda_exec.arn reference creates an explicit dependency, so a plan that destroys the role while keeping the function will fail.

Gate on drift in CI/CD

Run terraform plan -detailed-exitcode in your pipeline on a schedule and fail the build when it detects drift. A role that was manually deleted shows up as a planned recreate, which is your early warning.

terraform plan -detailed-exitcode
# exit code 0 = no changes, 1 = error, 2 = drift detected

Catch out-of-band deletions with policy-as-code

Use an SCP or a permissions boundary to restrict who can delete IAM roles, and route role deletions through change control. For accounts where manual cleanup is common, an EventBridge rule on DeleteRole API calls can alert you whenever a role disappears.

Tip: Run the lambda_invalidrole check in Lensix on a schedule rather than only on demand. Orphaned role references appear after the fact, when something else deletes the role, so continuous evaluation catches them within hours instead of when a customer reports an outage.


Best practices

  • One role per function, or per tightly scoped group. Sharing a single execution role across dozens of unrelated functions makes least privilege impossible and turns one deletion into many broken functions.
  • Scope permissions to named resources. Prefer policies that name specific tables, buckets, and queues over wildcard resource grants.
  • Always configure a dead-letter queue or failure destination. This catches dropped events from async and stream invocations so a broken role does not silently lose data.
  • Tag roles with their owning function or service. When someone reviews IAM for cleanup, a clear function:my-function tag tells them not to delete it.
  • Never delete IAM roles by hand in production accounts. Route all changes through IaC so the dependency graph is enforced.
  • Monitor invocation errors. A CloudWatch alarm on the Errors metric surfaces assume-role failures quickly even when no human is watching the client.

An invalid execution role is a small misconfiguration with an outsized blast radius. It costs nothing to fix and almost nothing to prevent, but left unnoticed it can quietly drain a Kinesis stream or fail every API call for hours. Keep roles and functions together in code, watch for drift, and let an automated check flag orphaned references before your users do.