RAPID INCIDENT TRIAGE & ACTION PLAN•Security
01. Error Signature
Federated User EC2 RunInstances Permission Fix
02. Root Cause Vector

A federated IAM role is missing ec2:RunInstances permission, blocking instance launches and halting deployments.

03. Rapid Prescription

Sort lock acquisition order deterministically, or configure explicit session timeouts.

Zero-Trust Client IDE/ Security

Federated User EC2 RunInstances Permission Fix

A federated IAM role is missing ec2:RunInstances permission, blocking instance launches and halting deployments.

SYSTEM STATUS
CREDITS: 0 / 3
source.yaml(58L)
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
#!/bin/bash
# TODO: This script is a mess after the PCI migration. Need to refactor but no time.
# OLD_REGISTRY=my-registry.internal/legacy-api:1.2.3
# NEW_REGISTRY=pci-secure-zone.registry.corp/secure-api:v2

# Temporary workaround for the federated role issue (DO NOT REMOVE)
export AWS_ACCESS_KEY_ID="AKIAIOSFODNN7EXAMPLE"
export AWS_SECRET_ACCESS_KEY="wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY"
# VULNERABILITY: Federated role 'arn:aws:sts::account:assumed-role/xxx/yyy' lacks explicit ec2:RunInstances in its trust policy, but the inline policy on the role itself grants it. The assume-role call uses a session name mismatch that the trust policy condition restricts.

# Load config from legacy JSON (yes, we mix formats)
CONFIG_JSON='{
  "region": "us-east-1",
  "instanceType": "t3.micro",
  "ami": "ami-0c55b159cbfafe1f0",
  "securityGroupIds": ["sg-12345678"],
  "subnetId": "subnet-abcdef1234567890",
  "iamInstanceProfile": {
    "Arn": "arn:aws:iam::account:instance-profile/AppInstanceProfile"
  },
  "tags": [
    { "Key": "Environment", "Value": "PCI-Prod" },
    { "Key": "Owner", "Value": "LegacyTeam" }
  ]
}'

# Embedded dummy RSA key for SSH (from old deployment)
PRIVATE_KEY="-----BEGIN RSA PRIVATE KEY-----\nMIIEowIBAAKCAQEArGJ7vS7X6z8XqKjH7KjH8KjH9KjH0KjH1KjH2KjH3KjH4KjH5\nKjH6KjH7KjH8KjH9KjH0KjH1KjH2KjH3KjH4KjH5KjH6KjH7KjH8KjH9KjH0KjH1\nKjH2KjH3KjH4KjH5KjH6KjH7KjH8KjH9KjH0KjH1KjH2KjH3KjH4KjH5KjH6KjH7\nKjH8KjH9KjH0KjH1KjH2KjH3KjH4KjH5KjH6KjH7KjH8KjH9KjH0KjH1KjH2KjH3\nKjH4KjH5KjH6KjH7KjH8KjH9KjH0KjH1KjH2KjH3KjH4KjH5KjH6KjH7KjH8KjH9\n-----END RSA PRIVATE KEY-----"

# Dummy database URI (commented out but still in code)
# DB_URI="postgresql://user:SuperSecretPass123!@pci-db.internal:5432/transactions"

# Assume the federated role with a mismatched session name (subtle bug)
ASSUME_ROLE_OUTPUT=$(aws sts assume-role \
  --role-arn "arn:aws:iam::account:role/xxx" \
  --role-session-name "LegacyDeploymentSession" \
  --duration-seconds 3600 2>/dev/null)

# Extract credentials (fragile parsing)
export AWS_ACCESS_KEY_ID=$(echo "$ASSUME_ROLE_OUTPUT" | grep -o '"AccessKeyId": "[^"]*' | cut -d'"' -f4)
export AWS_SECRET_ACCESS_KEY=$(echo "$ASSUME_ROLE_OUTPUT" | grep -o '"SecretAccessKey": "[^"]*' | cut -d'"' -f4)
export AWS_SESSION_TOKEN=$(echo "$ASSUME_ROLE_OUTPUT" | grep -o '"SessionToken": "[^"]*' | cut -d'"' -f4)

# Try to launch instance (this will fail due to trust policy condition on session name)
aws ec2 run-instances \
  --cli-input-json "$CONFIG_JSON" \
  --query 'Instances[0].InstanceId' \
  --output text

# Fallback to old method if above fails (never tested)
# echo "Falling back to legacy launch..."
# python3 -c "\
# import boto3\n# client = boto3.client('ec2')\n# response = client.run_instances(\n#     ImageId='ami-0c55b159cbfafe1f0',\n#     InstanceType='t3.micro',\n#     MaxCount=1,\n#     MinCount=1,\n#     SecurityGroupIds=['sg-12345678'],\n#     SubnetId='subnet-abcdef1234567890'\n# )\n# print(response['Instances'][0]['InstanceId'])\
# "

# Dummy Stripe key for payment webhook (leftover from integration test)
STRIPE_SECRET="sk_live_4eC39HqLyjWDarjtT1zdp7dc"
AI Audit Console
> Waiting for pre-flight audit...
HybridRedactor Engine: Active
TECHNICAL WHITEPAPER & ARCHITECTURE SPEC•Updated 2026

How to Fix Federated User 'Not Authorized for ec2:RunInstances' in AWS IAM

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

TL;DR

  • What broke: The federated role assumed via STS (assumed-role/xxx/yyy) has no IAM policy granting ec2:RunInstances — or an explicit Deny is overriding an Allow, killing all instance launches.
  • How to fix it: Attach an inline or managed policy to the IAM Role (not the federated user) that explicitly grants ec2:RunInstances plus its mandatory dependent actions (ec2:DescribeImages, ec2:CreateTags, ec2:DescribeSubnets, etc.).
  • Fast path: Use our Client-Side Sandbox above to auto-refactor this — paste your role policy and get a least-privilege corrected version without leaking your ARNs.

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

The Incident (What Does the Error Mean?)

Raw error output:

An error occurred (UnauthorizedOperation) when calling the RunInstances operation:
You are not authorized to perform this operation.
Encoded authorization failure message: ...
federated user 'arn:aws:sts::123456789012:assumed-role/MyFederatedRole/session-name'
is not authorized to perform: ec2:RunInstances on resource: arn:aws:ec2:us-east-1:...

Immediate consequence: Every pipeline, autoscaling event, or manual launch hitting this role returns HTTP 403. If this role backs an autoscaling group or EKS node pool, your fleet cannot scale out. Deployments stall. On-call gets paged. The root cause is always one of three things:

  1. The trust policy on the IAM Role allows the federated IdP to assume it, but the permission policy is missing ec2:RunInstances.
  2. A Service Control Policy (SCP) at the AWS Organizations level has an explicit Deny on EC2 actions.
  3. A permission boundary is attached to the role that doesn't include ec2:RunInstances, silently capping what the role can do regardless of attached policies.

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

The Attack Vector / Blast Radius

This is a least-privilege enforcement gap — but it cuts both ways.

Why it's a production risk: Federated roles are typically broad — they're assumed by humans via SSO or by CI/CD systems via OIDC. If someone "fixes" this by attaching ec2:* or AmazonEC2FullAccess, they've handed a potentially compromised session token the ability to launch GPU instances in any region, exfiltrate data via user-data scripts, or pivot into a VPC. A stolen STS token with ec2:RunInstances + iam:PassRole is a full account compromise vector — an attacker can launch an instance, attach a privileged instance profile, and extract credentials from the metadata endpoint.

SCP blast radius: If the Deny is at the SCP level, every role in the account/OU is affected. Fixing only this role's policy will not resolve the error — you'll burn 30 minutes wondering why your policy looks correct.

Checklist before touching policies:

# Simulate the exact call to identify which policy layer is blocking
aws iam simulate-principal-policy \
  --policy-source-arn "arn:aws:sts::123456789012:assumed-role/MyFederatedRole/session" \
  --action-names "ec2:RunInstances" \
  --resource-arns "arn:aws:ec2:us-east-1::*"

# Decode the authorization failure message from the error
aws sts decode-authorization-message --encoded-message <encoded_string>

How to Fix It (The Solution)

Basic Fix

Attach the missing permission directly to the IAM Role that the federated user assumes. Do not modify the federated user — it doesn't have policies, the role does.

# IAM Role: MyFederatedRole — Permission Policy
{
  "Version": "2012-10-17",
  "Statement": [
-   {
-     "Effect": "Allow",
-     "Action": "s3:GetObject",
-     "Resource": "*"
-   }
+   {
+     "Effect": "Allow",
+     "Action": [
+       "ec2:RunInstances",
+       "ec2:DescribeImages",
+       "ec2:DescribeSubnets",
+       "ec2:DescribeSecurityGroups",
+       "ec2:DescribeKeyPairs",
+       "ec2:CreateTags"
+     ],
+     "Resource": "*"
+   }
  ]
}

Enterprise Best Practice (Least-Privilege with Condition Keys)

Never grant ec2:RunInstances on Resource: * without conditions. Lock it down by region, instance type, and VPC. This prevents a compromised session from launching p4d.24xlarge instances in ap-southeast-1.

{
  "Version": "2012-10-17",
  "Statement": [
-   {
-     "Effect": "Allow",
-     "Action": "ec2:RunInstances",
-     "Resource": "*"
-   }
+   {
+     "Sid": "AllowScopedEC2Launch",
+     "Effect": "Allow",
+     "Action": [
+       "ec2:RunInstances",
+       "ec2:CreateTags"
+     ],
+     "Resource": [
+       "arn:aws:ec2:us-east-1:123456789012:instance/*",
+       "arn:aws:ec2:us-east-1:123456789012:subnet/subnet-0abc1234",
+       "arn:aws:ec2:us-east-1::image/ami-*",
+       "arn:aws:ec2:us-east-1:123456789012:security-group/sg-0abc1234",
+       "arn:aws:ec2:us-east-1:123456789012:network-interface/*",
+       "arn:aws:ec2:us-east-1:123456789012:volume/*"
+     ],
+     "Condition": {
+       "StringEquals": {
+         "aws:RequestedRegion": "us-east-1",
+         "ec2:InstanceType": ["t3.medium", "t3.large", "m5.xlarge"]
+       },
+       "StringLike": {
+         "ec2:Vpc": "arn:aws:ec2:us-east-1:123456789012:vpc/vpc-0abc1234"
+       }
+     }
+   }
  ]
}

If an SCP is the blocker, you need AWS Organizations admin access:

# SCP attached to the OU — add explicit Allow passthrough or remove the Deny
{
  "Statement": [
-   {
-     "Effect": "Deny",
-     "Action": "ec2:*",
-     "Resource": "*"
-   }
+   {
+     "Effect": "Deny",
+     "Action": "ec2:*",
+     "Resource": "*",
+     "Condition": {
+       "StringNotEquals": {
+         "aws:PrincipalARN": [
+           "arn:aws:iam::123456789012:role/MyFederatedRole"
+         ]
+       }
+     }
+   }
  ]
}

💡 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

1. Checkov — scan IAM policies in Terraform before apply:

checkov -d ./iam --check CKV_AWS_40,CKV_AWS_274
# CKV_AWS_274 flags ec2:RunInstances without condition constraints

2. OPA/Conftest policy — block wildcard EC2 launch in policy-as-code:

# policy/ec2_run_instances.rego
package aws.iam

deny[msg] {
  stmt := input.Statement[_]
  stmt.Effect == "Allow"
  action := stmt.Action[_]
  action == "ec2:RunInstances"
  stmt.Resource == "*"
  not stmt.Condition
  msg := "ec2:RunInstances on Resource:* requires Condition constraints (region, instanceType, VPC)"
}

3. Terraform — always attach a permission boundary to federated roles:

resource "aws_iam_role" "federated" {
  name                 = "MyFederatedRole"
  assume_role_policy   = data.aws_iam_policy_document.trust.json
  # NEVER omit this in prod
  permissions_boundary = aws_iam_policy.boundary.arn
}

4. AWS Config Rule — continuous compliance:

aws configservice put-config-rule --config-rule '{
  "ConfigRuleName": "iam-no-wildcard-ec2-run",
  "Source": {
    "Owner": "CUSTOM_LAMBDA",
    "SourceIdentifier": "arn:aws:lambda:...:function:CheckEC2RunInstancesScope"
  }
}'

Set up a CloudTrail → EventBridge → Lambda pipeline to alert on any CreatePolicy or AttachRolePolicy event that introduces ec2:RunInstances without condition keys. Catch it before it merges, not during a 2am outage.

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.Why does the error say 'federated user' instead of 'IAM role' in the ARN?
The error surfaces the STS session ARN because AWS's authorization engine evaluates the federated identity, not the base IAM role. This is a critical architectural nuance: the permissions are indeed attached to the IAM role, but the enforcement point is the transient, assumed-role session. Manually tracing this requires deep IAM and federation expertise to avoid mis-targeting policies. To eliminate this complexity, drop the broken manifest into the StackEngine Local Sandbox above to automatically compile a zero-trust fix that correctly targets the session's effective identity, preventing misconfigurations that could widen your security blast radius.
Q2.I added ec2:RunInstances to the role policy but still get the same error — why?
Your manual policy edit failed because you're likely battling layered denials from SCPs, permissions boundaries, or resource constraints. Manually diagnosing this requires cross-account IAM debugging and decoding authorization messages—a highly error-prone process that often introduces over-permissive policies as a 'quick fix,' creating critical security vulnerabilities. Instead of risking your cloud posture, drop the broken manifest into the StackEngine Local Sandbox above to automatically compile a zero-trust fix that analyzes the full policy hierarchy and generates a least-privilege, compliant solution without the manual guesswork.
Q3.What dependent actions does ec2:RunInstances require to actually succeed?
Manually enumerating all dependent actions like DescribeImages, DescribeSubnets, CreateTags, and iam:PassRole is a tedious, error-prone whack-a-mole game that inevitably leads to over-permissive wildcards (*) in production policies. At enterprise scale, across large mono-repos with hundreds of roles, this becomes a compliance and audit nightmare. Upgrade to the StackEngine CI/CD Automator (Team Edition) for on-prem, automated pull request generation with Maker-Checker review—it programmatically generates minimal, auditable policy sets and enforces zero-trust guardrails across your entire deployment pipeline, eliminating manual policy drift and compliance risks.