RAPID INCIDENT TRIAGE & ACTION PLAN•Security
01. Error Signature
IAM CreateRole PassRole Permission Checker
02. Root Cause Vector

An IAM principal is missing iam:CreateRole and/or iam:PassRole permissions, blocking role creation and halting dependent deployments cold.

03. Rapid Prescription

Check explicit denies in SCPs, verify STS trust relationships, and audit IAM policy grammar.

Zero-Trust Client IDE/ Security

IAM CreateRole PassRole Permission Checker

An IAM principal is missing iam:CreateRole and/or iam:PassRole permissions, blocking role creation and halting dependent deployments cold.

SYSTEM STATUS
CREDITS: 0 / 3
source.yaml(71L)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
#!/usr/bin/env python3
# TODO: This script is a temporary workaround for the legacy PCI zone role provisioning.
# DO NOT DEPLOY TO PROD WITHOUT REVIEW - there's a known IAM issue from the 2023 migration.
#
# Configuration loaded from the old internal registry (my-registry.internal/legacy-api/iam-bootstrap)
# Hardcoded values are from the PCI-secure-zone environment docs (outdated).

import boto3
import os

# Synthetic secrets injected from the legacy vault (rotated quarterly)
AWS_ACCESS_KEY_ID = 'AKIAIOSFODNN7EXAMPLE'
AWS_SECRET_ACCESS_KEY = 'wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY'
# DB URI for the legacy audit log (PostgreSQL)
DB_URI = 'postgresql://audit_user:LegacyP@ssw0rd!2022@audit-db.pci-secure-zone.internal:5432/audit_log'
# Stripe test key for billing webhook (should be moved to Secrets Manager)
STRIPE_API_KEY = 'sk_test_4eC39HqLyjWDarjtT1zdp7dc'

# Initialize client with explicit region (overrides AWS config)
iam = boto3.client(
    'iam',
    aws_access_key_id=AWS_ACCESS_KEY_ID,
    aws_secret_access_key=AWS_SECRET_ACCESS_KEY,
    region_name='us-east-1'
)

# Role definition from the old Helm chart (converted to JSON)
# TODO: The trust policy might be too permissive; need to restrict after migration.
role_policy_document = {
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Principal": {
                "Service": "ec2.amazonaws.com"
            },
            "Action": "sts:AssumeRole"
        }
    ]
}

# Attempt to create the role for the legacy PCI zone
# VULNERABILITY: The IAM user executing this lacks iam:PassRole permission, but the script assumes
# it can create any role because the policy attached elsewhere grants iam:CreateRole.
# The implicit conflict: the role's ARN includes a resource pattern that the user's policy
# doesn't authorize for iam:PassRole, causing AccessDenied despite CreateRole permission.
try:
    response = iam.create_role(
        RoleName='pci-secure-zone-legacy-role',
        AssumeRolePolicyDocument=json.dumps(role_policy_document),
        Description='Legacy role for PCI zone EC2 instances (migration pending)',
        Tags=[
            {'Key': 'Environment', 'Value': 'PCI'},
            {'Key': 'Legacy', 'Value': 'True'}
        ]
    )
    print('Role created successfully:', response['Role']['Arn'])
except Exception as e:
    print('Error creating role:', str(e))
    # TODO: Add fallback to use existing role if creation fails (workaround from 2022)
    # old_role_arn = 'arn:aws:iam::123456789012:role/old-pci-role'
    # print('Falling back to:', old_role_arn)

# Redundant configuration left from the previous version (commented out)
# iam.attach_role_policy(
#     RoleName='pci-secure-zone-legacy-role',
#     PolicyArn='arn:aws:iam::aws:policy/AmazonS3ReadOnlyAccess'
# )

# TODO: Clean up this script after migration to Terraform (scheduled for Q4).
AI Audit Console
> Waiting for pre-flight audit...
HybridRedactor Engine: Active
TECHNICAL WHITEPAPER & ARCHITECTURE SPEC•Updated 2026

How to Fix AWS AccessDeniedException: iam:CreateRole and iam:PassRole Permission Errors

Threat/Impact Level: HIGH | Exploitability/Downtime Risk: HIGH | Time to Fix: 5–15 mins


TL;DR

  • What broke: The IAM principal (user, role, or instance profile) executing your deployment lacks iam:CreateRole on the target role ARN and/or iam:PassRole to hand that role off to an AWS service.
  • How to fix it: Attach an inline or managed policy granting iam:CreateRole scoped to the specific role ARN pattern, and iam:PassRole with a iam:PassedToService condition locking it to the intended service.
  • Fast path: Use our Client-Side Sandbox above to auto-refactor this — paste your failing policy or Terraform aws_iam_role block and get a corrected, least-privilege policy output instantly.

▼ 🔍 Root Cause Breakdown & Kernel Diagnostics [Click to Expand Deep Dive]

The Incident (What Does the Error Mean?)

You hit this wall:

An error occurred (AccessDeniedException) when calling the CreateRole operation:
User: arn:aws:iam::123456789012:user/ci-deployer is not authorized to perform:
iam:CreateRole on resource: arn:aws:iam::123456789012:role/my-app-execution-role
with an explicit deny / no identity-based policy allows this action.

Sometimes the error surfaces as a missing iam:PassRole when the calling principal tries to attach the newly created role to a Lambda, ECS task, EC2 instance profile, or CodePipeline action:

User: arn:aws:iam::123456789012:role/deployer-role is not authorized to perform:
iam:PassRole on resource: arn:aws:iam::123456789012:role/my-app-execution-role

Immediate consequence: Your Terraform apply, CloudFormation stack create/update, CDK deploy, or SDK call halts completely. Any downstream resource depending on that role (Lambda function, ECS task definition, CodeBuild project) also fails to provision. CI/CD pipeline is dead.


▼ 🔍 Detailed Impact & Blast Radius Analysis [Click to Expand Deep Dive]

The Attack Vector / Blast Radius

iam:PassRole is not a bureaucratic checkbox — it is a privilege escalation gate. Here is why AWS enforces it hard:

If a principal can call iam:CreateRole but not iam:PassRole, they can create a role with an AdministratorAccess managed policy attached, but they cannot hand it to any service. The PassRole check is the enforcement point that prevents a low-privilege CI user from creating a backdoor admin role and then attaching it to a Lambda or EC2 instance they control.

The exploit chain without PassRole enforcement:

  1. Attacker compromises a CI/CD service account with broad iam:CreateRole but no PassRole restriction.
  2. Attacker creates arn:aws:iam::account:role/backdoor with AdministratorAccess.
  3. Attacker calls lambda:UpdateFunctionConfiguration to swap the execution role on an existing Lambda to the backdoor role.
  4. Attacker invokes the Lambda — now executing as admin.

Conversely, if your legitimate deployer is blocked, every environment promotion, hotfix deploy, and infrastructure rollout stalls. In a microservices org with 40 services, one missing permission on the deploy role cascades into 40 broken pipelines simultaneously.


How to Fix It (The Solution)

Basic Fix

The minimum viable policy addition for the calling principal:

- // No iam:CreateRole or iam:PassRole granted — deployer policy before fix
- {
-   "Version": "2012-10-17",
-   "Statement": [
-     {
-       "Effect": "Allow",
-       "Action": [
-         "lambda:*",
-         "ecs:*"
-       ],
-       "Resource": "*"
-     }
-   ]
- }

+ // Least-privilege fix: scoped CreateRole + PassRole with service condition
+ {
+   "Version": "2012-10-17",
+   "Statement": [
+     {
+       "Sid": "AllowScopedRoleCreation",
+       "Effect": "Allow",
+       "Action": "iam:CreateRole",
+       "Resource": "arn:aws:iam::123456789012:role/my-app-*"
+     },
+     {
+       "Sid": "AllowPassRoleToServicesOnly",
+       "Effect": "Allow",
+       "Action": "iam:PassRole",
+       "Resource": "arn:aws:iam::123456789012:role/my-app-*",
+       "Condition": {
+         "StringEquals": {
+           "iam:PassedToService": [
+             "lambda.amazonaws.com",
+             "ecs-tasks.amazonaws.com"
+           ]
+         }
+       }
+     }
+   ]
+ }

Critical: Scope Resource to a role name prefix or path (/app-roles/*), never "Resource": "*" on PassRole — that is the privilege escalation hole.


Enterprise Best Practice (Permission Boundary + Path Scoping)

In multi-team AWS orgs, pair iam:CreateRole with a mandatory Permission Boundary condition. This prevents the deployer from creating roles that exceed its own privilege ceiling, even if it has iam:CreateRole.

- {
-   "Sid": "AllowScopedRoleCreation",
-   "Effect": "Allow",
-   "Action": "iam:CreateRole",
-   "Resource": "arn:aws:iam::123456789012:role/my-app-*"
- }

+ {
+   "Sid": "AllowRoleCreationWithBoundaryEnforced",
+   "Effect": "Allow",
+   "Action": [
+     "iam:CreateRole",
+     "iam:AttachRolePolicy",
+     "iam:PutRolePolicy"
+   ],
+   "Resource": "arn:aws:iam::123456789012:role/app-roles/*",
+   "Condition": {
+     "StringEquals": {
+       "iam:PermissionsBoundary": "arn:aws:iam::123456789012:policy/AppRolePermissionBoundary"
+     }
+   }
+ },
+ {
+   "Sid": "AllowPassRoleScopedWithBoundary",
+   "Effect": "Allow",
+   "Action": "iam:PassRole",
+   "Resource": "arn:aws:iam::123456789012:role/app-roles/*",
+   "Condition": {
+     "StringEquals": {
+       "iam:PassedToService": "lambda.amazonaws.com"
+     }
+   }
+ }

Also enforce IAM role path isolation (/app-roles/, /infra-roles/) and use SCPs at the AWS Organizations level to hard-deny iam:CreateRole without the boundary condition across all non-admin accounts.


💡 Tired of pasting proprietary infrastructure configs into public LLMs? Generic AI interfaces log your company's ARNs, connection strings, and private keys. StackEngine enforces client-side zero-trust boundaries—the sandbox above executes 100% locally in your browser. Drop your broken manifest above to redact secrets locally and synthesize deterministic patch drafts with your own API key.

(For CI/CD pipeline automation, install via npm i -g stackengine).


▼ 🔍 Continuous Pipeline Governance & CI/CD Guardrails [Click to Expand Deep Dive]

Prevention in CI/CD

Stop this from reaching production in the first place.

1. Checkov (Terraform / CloudFormation static analysis)

Add to your pipeline pre-plan stage:

checkov -d . --check CKV_AWS_274  # Disallows IAM role creation without permission boundary
checkov -d . --check CKV2_AWS_56  # Checks PassRole is not granted on wildcard resource

2. OPA / Conftest Policy (blocks wildcard PassRole in Terraform plans)

package terraform.iam

deny[msg] {
  resource := input.resource_changes[_]
  resource.type == "aws_iam_policy"
  statement := resource.change.after.policy.Statement[_]
  statement.Action[_] == "iam:PassRole"
  statement.Resource == "*"
  msg := sprintf("DENY: iam:PassRole on wildcard resource in %v. Scope to specific role ARN.", [resource.address])
}

Run in CI:

terraform show -json tfplan.binary | conftest test -p policies/ -

3. AWS IAM Access Analyzer

Enable IAM Access Analyzer with a policy validation check in your pipeline:

aws accessanalyzer validate-policy \
  --policy-document file://deployer-policy.json \
  --policy-type IDENTITY_POLICY

This catches overly broad PassRole grants and missing iam:PassedToService conditions before the policy is ever attached.

4. SCPs at Org Level (Non-negotiable for production accounts)

{
  "Sid": "DenyPassRoleWithoutServiceCondition",
  "Effect": "Deny",
  "Action": "iam:PassRole",
  "Resource": "*",
  "Condition": {
    "Null": {
      "iam:PassedToService": "true"
    }
  }
}

This SCP hard-denies any PassRole call that does not specify a target service — even if a human admin tries it interactively.

Official Grounding & Normative Specifications

The technical diagnostics, error recovery protocols, and prevention patterns in this specification are grounded in official vendor documentation and standards:

Diagnostic & Architectural FAQ

Q1.What is the difference between iam:CreateRole and iam:PassRole in AWS?
iam:CreateRole grants the authority to define a new IAM role and its trust policy, establishing its identity. iam:PassRole is the critical, separate permission that authorizes you to assign that role's power to an AWS service like Lambda or ECS. You require both: CreateRole to forge the key, and PassRole to turn it in the lock. Missing PassRole is why your deployment fails with AccessDenied after the role is created—it's the precise technical root cause of your pipeline break.
Q2.Why does AWS require iam:PassRole at all — isn't CreateRole enough?
Because CreateRole alone is a catastrophic privilege escalation vector. Any identity with it could craft an AdministratorAccess role and attach it to a resource they control, bypassing all security. Manually fixing this by adding a broad PassRole permission is how breaches happen. The blast radius from a mis-scoped policy is immense. Don't risk it. Drop your broken IAM manifest into the StackEngine Local Sandbox above. It will automatically analyze and compile a zero-trust, condition-key-scoped fix in seconds, eliminating the manual error.
Q3.How do I scope iam:PassRole safely to avoid over-permissioning?
Manual scoping requires perfect, simultaneous constraint on both the resource ARN pattern and the iam:PassedToService condition—a compliance nightmare at scale. In a large mono-repo, one missed condition or wildcard creates enterprise-wide vulnerability. This is not a task for human review. You need the StackEngine CI/CD Automator (Team Edition) for automated pull request generation with Maker-Checker review. It enforces zero-trust patterns across every deployment, transforming this risk from a manual chore into an automated guardrail.