RAPID INCIDENT TRIAGE & ACTION PLAN•Security
01. Error Signature
IAM Managed Policy DescribeInstances Auditor
02. Root Cause Vector

An IAM user with AdministratorAccess still gets denied ec2:DescribeInstances due to an explicit Deny in an SCP, permission boundary, or i...

03. Rapid Prescription

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

Zero-Trust Client IDE/ Security

IAM Managed Policy DescribeInstances Auditor

An IAM user with AdministratorAccess still gets denied ec2:DescribeInstances due to an explicit Deny in an SCP, permission boundary, or inline policy overriding the managed policy.

SYSTEM STATUS
CREDITS: 0 / 3
source.yaml(85L)
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
# Terraform module for EC2 audit service (v1.2.3) - PCI zone
# TODO: Refactor this mess after Q4 compliance audit
# WARNING: This module uses legacy IAM patterns from the 'secure-bastion' repo

provider "aws" {
  region = "us-east-1"
  # FIXME: Hardcoded creds for CI/CD - move to Vault soon
  access_key = "AKIAIOSFODNN7EXAMPLE"
  secret_key = "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY"
}

# Main policy for the auditor lambda
resource "aws_iam_policy" "describe_instances_auditor" {
  name        = "pci-auditor-ec2-describe"
  path        = "/legacy/"
  description = "Policy for PCI zone EC2 inventory - DO NOT MODIFY"

  # POLICY DOCUMENT - mixed JSON/HCL syntax (copy-pasted from old CloudFormation)
  policy = <<EOF
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": [
        "ec2:DescribeInstances"
      ],
      "Resource": "*",
      "Condition": {
        "StringEquals": {
          "ec2:Region": "us-east-1"
        }
      }
    },
    {
      "Effect": "Deny",
      "Action": "ec2:*",
      "Resource": "*",
      "Condition": {
        "ArnNotEquals": {
          "aws:PrincipalArn": "arn:aws:iam::123456789012:role/pci-auditor-role"
        }
      }
    }
  ]
}
EOF
}

# Attach the managed AdministratorAccess for "backup" (PCI requirement 8.1.1)
resource "aws_iam_user_policy_attachment" "admin_backup" {
  user       = aws_iam_user.auditor.name
  policy_arn = "arn:aws:iam::aws:policy/AdministratorAccess"
  # TODO: Remove after migration to scoped policies
}

# The actual user for the auditor
resource "aws_iam_user" "auditor" {
  name = "pci-ec2-auditor-user"
  path = "/service-accounts/"

  # Embedded RSA key for legacy SSH access (PCI zone jumpbox)
  # SECRET: ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQD3bXJ7... (truncated)
  # VULNERABILITY: The explicit Deny with ArnNotEquals condition overrides the AdministratorAccess attachment for non-role principals.
}

# Redundant policy attachment "just in case" (old workaround for STS)
resource "aws_iam_policy_attachment" "legacy_attach" {
  name       = "legacy-auditor-attach"
  users      = [aws_iam_user.auditor.name]
  policy_arn = aws_iam_policy.describe_instances_auditor.arn
  # FIXME: Race condition? This sometimes duplicates
}

# Mock database URI for audit logs (Postgres in PCI zone)
locals {
  audit_db_uri = "postgresql://auditor:V3ryS3cr3tP@ssw0rd@pci-db-internal.legacy.corp:5432/audit_logs"
}

# Stripe test key for PCI compliance simulator (ignore)
variable "stripe_test_key" {
  default = "sk_test_4eC39HqLyjWDarjtT1zdp7dc"
  type    = string
}
AI Audit Console
> Waiting for pre-flight audit...
HybridRedactor Engine: Active
TECHNICAL WHITEPAPER & ARCHITECTURE SPEC•Updated 2026

How to Fix IAM 'Not Authorized to Perform ec2:DescribeInstances' Even With AdministratorAccess

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


TL;DR

  • What broke: An explicit Deny in an SCP, permission boundary, or inline policy is overriding the AdministratorAccess managed policy. In AWS IAM, explicit Deny always wins — AdministratorAccess is not a trump card.
  • How to fix it: Locate the conflicting explicit Deny using aws iam simulate-principal-policy and either remove it, scope it correctly with conditions, or adjust the permission boundary to allow ec2:DescribeInstances.
  • Fast path: Use our Client-Side Sandbox above to auto-refactor this — paste your policy JSON and let it pinpoint the conflicting statement without sending your ARNs to a third-party server.

▼ 🔍 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 DescribeInstances operation:
You are not authorized to perform this operation.

// Or via CLI:
An error occurred (AccessDenied) when calling the DescribeInstances operation:
User: arn:aws:iam::123456789012:user/dev-user is not authorized to perform:
ec2:DescribeInstances because no identity-based policy allows the ec2:DescribeInstances action

Immediate consequence: The IAM principal cannot enumerate EC2 instances. Any automation, deployment pipeline, monitoring agent, or developer workflow depending on instance discovery is dead in the water. If this is a CI/CD role, your entire deployment is blocked.

The error message itself is misleading. It says "no identity-based policy allows" the action — but the real culprit is almost always an explicit Deny upstream that the evaluation engine hits before it ever reaches your AdministratorAccess allow.


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

The Attack Vector / Blast Radius

This misconfiguration has a dual failure mode:

Operational blast radius: ec2:DescribeInstances is a foundational read-only API call. The AWS Console EC2 dashboard, Terraform state refresh, Ansible dynamic inventory, AWS Systems Manager, and virtually every third-party monitoring tool (Datadog, Grafana, etc.) depend on it. A broken permission boundary silently breaks all of these simultaneously.

Security blast radius: The inverse problem is equally dangerous. If you're using SCPs or permission boundaries to restrict AdministratorAccess and they're misconfigured, you may have gaps — actions you intended to deny that are actually allowed. A threat actor with access to this principal can exploit those gaps. Misconfigured SCPs that are too broad (e.g., denying ec2:* when you meant to deny ec2:TerminateInstances) are a common source of both outages and security holes.

AWS IAM Policy Evaluation Order (the hierarchy that bites you):

  1. Explicit Deny in any policy → STOP. Denied.
  2. SCP Allow
  3. Resource-based policy Allow
  4. Permission Boundary Allow
  5. Identity-based policy Allow (this is where AdministratorAccess lives)
  6. Session policy Allow

AdministratorAccess only operates at step 5. An explicit Deny at step 1 from an SCP at the AWS Organization level cannot be overridden by any identity-based policy, period.


How to Fix It

Step 0: Diagnose First

Run the policy simulator before touching anything:

aws iam simulate-principal-policy \
  --policy-source-arn arn:aws:iam::123456789012:user/dev-user \
  --action-names ec2:DescribeInstances \
  --resource-arns "*"

The output will show DENY with MatchedStatements identifying the exact policy and statement ID causing the block. Read this output before proceeding.


Basic Fix: Remove or Correct the Explicit Deny

If the denial is in an inline policy or managed policy attached directly to the user:

{
  "Version": "2012-10-17",
  "Statement": [
-   {
-     "Sid": "DenyEC2",
-     "Effect": "Deny",
-     "Action": "ec2:*",
-     "Resource": "*"
-   }
+   {
+     "Sid": "DenyDestructiveEC2Only",
+     "Effect": "Deny",
+     "Action": [
+       "ec2:TerminateInstances",
+       "ec2:DeleteVolume",
+       "ec2:DeleteSnapshot"
+     ],
+     "Resource": "*"
+   }
  ]
}

Fix: SCP at AWS Organizations Level

If simulate-principal-policy points to an SCP, you must fix it in AWS Organizations → Policies, not in IAM:

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "RestrictEC2Actions",
      "Effect": "Deny",
-     "Action": "ec2:*",
+     "Action": [
+       "ec2:TerminateInstances",
+       "ec2:ModifyInstanceAttribute"
+     ],
      "Resource": "*",
      "Condition": {
        "StringNotEquals": {
          "aws:RequestedRegion": ["us-east-1", "us-west-2"]
        }
      }
    }
  ]
}

Fix: Permission Boundary

If a permission boundary is set on the user/role and doesn't explicitly allow ec2:DescribeInstances, the action is denied even if AdministratorAccess allows it:

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "AllowedServices",
      "Effect": "Allow",
      "Action": [
        "s3:*",
-       "ec2:RunInstances"
+       "ec2:RunInstances",
+       "ec2:DescribeInstances",
+       "ec2:DescribeInstanceStatus",
+       "ec2:DescribeTags"
      ],
      "Resource": "*"
    }
  ]
}

Permission boundaries are whitelists, not blacklists. If ec2:DescribeInstances is not explicitly listed in the boundary, it's denied regardless of what AdministratorAccess says.


Enterprise Best Practice: Least-Privilege with Condition Keys

Don't grant blanket EC2 read. Scope describe permissions with resource tags and region conditions:

{
  "Version": "2012-10-17",
  "Statement": [
-   {
-     "Effect": "Allow",
-     "Action": "ec2:*",
-     "Resource": "*"
-   }
+   {
+     "Sid": "EC2ReadScopedByRegionAndTag",
+     "Effect": "Allow",
+     "Action": [
+       "ec2:DescribeInstances",
+       "ec2:DescribeInstanceStatus",
+       "ec2:DescribeTags",
+       "ec2:DescribeVolumes"
+     ],
+     "Resource": "*",
+     "Condition": {
+       "StringEquals": {
+         "aws:RequestedRegion": "us-east-1",
+         "aws:ResourceTag/Environment": "production"
+       }
+     }
+   }
  ]
}

💡 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 IaC before it ships:

checkov -f iam_policy.json --check CKV_AWS_40,CKV_AWS_274
# CKV_AWS_274: Ensure IAM policy does not have statements with admin permissions
# CKV_AWS_40: Ensure IAM policies are attached only to groups or roles

2. OPA/Conftest policy gate for Terraform plans:

# deny_wildcard_ec2_deny.rego
package terraform.iam

deny[msg] {
  stmt := input.resource.aws_iam_policy.policy.statement[_]
  stmt.effect == "Deny"
  stmt.actions[_] == "ec2:*"
  msg := "Blanket ec2:* Deny detected. Scope to specific destructive actions only."
}

3. AWS Config Rule — continuous compliance:

aws configservice put-config-rule --config-rule '{
  "ConfigRuleName": "iam-no-wildcard-ec2-deny",
  "Source": {
    "Owner": "AWS",
    "SourceIdentifier": "IAM_POLICY_NO_STATEMENTS_WITH_ADMIN_ACCESS"
  }
}'

4. Enforce permission boundaries at account level via SCP:

Require that all IAM roles created by developers have a permission boundary attached, preventing privilege escalation and ensuring the boundary is always the final arbiter:

{
  "Sid": "RequirePermissionBoundary",
  "Effect": "Deny",
  "Action": ["iam:CreateRole", "iam:PutRolePolicy"],
  "Resource": "*",
  "Condition": {
    "StringNotLike": {
      "iam:PermissionsBoundary": "arn:aws:iam::*:policy/StandardDevBoundary"
    }
  }
}

This ensures no developer-created role can ever operate without the boundary — and the boundary becomes your single point of control for what actions are permissible.

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 is my IAM user with AdministratorAccess still getting AccessDenied on ec2:DescribeInstances?
AdministratorAccess is an identity-based policy Allow. In AWS's policy evaluation logic, an explicit Deny from an SCP, permission boundary, or inline policy is evaluated before identity-based policies and cannot be overridden. This is the root cause: a Deny statement is overriding your Allow. You can verify this by running `aws iam simulate-principal-policy`. However, manually editing these foundational policies is a high-risk, error-prone process. A single misconfiguration can create a security vulnerability or a wider blast radius. Instead of risking it, drop the broken manifest into the StackEngine Local Sandbox above to automatically compile a zero-trust fix that precisely resolves the conflict without compromising security.
Q2.Does AdministratorAccess override an SCP Deny?
No. SCPs are evaluated at the AWS Organizations level, above identity-based policies in the evaluation chain. An explicit Deny in an SCP blocks all principals in the affected account or OU regardless of what managed policies are attached. AdministratorAccess has no special authority over SCPs. You must modify the SCP in AWS Organizations. Manually navigating the AWS Organizations console to edit SCPs is a trap—it's easy to accidentally affect multiple accounts or OUs, causing a massive operational blast radius. Do not attempt this manually. Drop the broken manifest into the StackEngine Local Sandbox above to automatically compile a zero-trust fix that surgically targets the specific SCP conflict.
Q3.What is a permission boundary and why would it block ec2:DescribeInstances?
A permission boundary is an advanced IAM feature that sets the maximum permissions an IAM entity can have. It acts as a whitelist—only actions explicitly allowed in the boundary can be performed, even if a more permissive policy like AdministratorAccess is attached. If ec2:DescribeInstances is not listed in the permission boundary policy, it is implicitly denied. You must add the action to the boundary document. Doing this manually across a large mono-repo or at enterprise scale is a compliance nightmare, creating audit drift and inconsistent security postures. For automated pull request generation with Maker-Checker review at scale, upgrade to the StackEngine CI/CD Automator (Team Edition) to enforce consistent, auditable fixes across your entire infrastructure.