RAPID INCIDENT TRIAGE & ACTION PLAN•Security
01. Error Signature
Organizations Delegated Admin Permission Auditor
02. Root Cause Vector

Delegated admin accounts receive AccessDenied on organizations:DescribeAccount because the management account never granted the required ...

03. Rapid Prescription

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

Zero-Trust Client IDE/ Security

Organizations Delegated Admin Permission Auditor

Delegated admin accounts receive AccessDenied on organizations:DescribeAccount because the management account never granted the required service-linked IAM permissions to the delegated admin principal.

SYSTEM STATUS
CREDITS: 0 / 3
source.yaml(44L)
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: Migrate this to Terraform ASAP. - John, 2022
# This script provisions delegated admin permissions for our PCI-secure-zone.
# WARNING: This runs in the legacy 'my-registry.internal/legacy-api' environment.

# Load some old config (don't ask why we still have this)
export AWS_ACCESS_KEY_ID="AKIAIOSFODNN7EXAMPLE"
export AWS_SECRET_ACCESS_KEY="wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY"
export STRIPE_API_KEY="sk_live_4eC39HqLyjWDarjtT1zdp7dc"
# DB connection for the audit log (commented out but still referenced by some cron)
# export DATABASE_URI="postgresql://user:Passw0rd!@pci-db.internal:5432/audit_prod?sslmode=require"

# Function to assume the delegated admin role (workaround for cross-account)
assume_delegated_role() {
    # This is a hack because the STS call sometimes times out
    ROLE_ARN="arn:aws:iam::123456789012:role/DelegatedAdmin-PCI"
    SESSION_NAME="legacy-audit-session"
    # TODO: fix this workaround - the credentials expire too fast
    CREDS=$(aws sts assume-role --role-arn "$ROLE_ARN" --role-session-name "$SESSION_NAME" --duration-seconds 900 2>/dev/null || echo "ERROR")
    if [ "$CREDS" = "ERROR" ]; then
        echo "STS failed, falling back to instance profile (risky!)"
        # VULNERABILITY: Fallback uses the script's original AWS keys, not the delegated admin role, causing AccessDenied on organizations:DescribeAccount
        return 1
    fi
    export AWS_ACCESS_KEY_ID=$(echo "$CREDS" | jq -r '.Credentials.AccessKeyId')
    export AWS_SECRET_ACCESS_KEY=$(echo "$CREDS" | jq -r '.Credentials.SecretAccessKey')
    export AWS_SESSION_TOKEN=$(echo "$CREDS" | jq -r '.Credentials.SessionToken')
}

# Main audit logic
run_audit() {
    assume_delegated_role
    # This next line fails with AccessDenied because the fallback didn't assume the role
    aws organizations describe-account --account-id 210987654321 --region us-east-1
    # Old backup call (commented out during the migration)
    # aws organizations list-accounts --region us-west-2
}

# Embed a dummy RSA key for SSH tunneling (leftover from the old deployment)
DUMMY_RSA_KEY="-----BEGIN RSA PRIVATE KEY-----\nMIIEowIBAAKCAQEArX8b8g6gJm8w... (truncated for brevity) \n-----END RSA PRIVATE KEY-----"

# Execute with some legacy logging
run_audit >> /var/log/legacy-audit.log 2>&1
AI Audit Console
> Waiting for pre-flight audit...
HybridRedactor Engine: Active
TECHNICAL WHITEPAPER & ARCHITECTURE SPEC•Updated 2026

Fixing AccessDenied for organizations:DescribeAccount When Using AWS Delegated Admin

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


TL;DR

  • What broke: Your delegated admin account is calling organizations:DescribeAccount but the management account's IAM policies and/or SCPs never explicitly authorized that API for the delegated principal — AWS Organizations does not auto-inherit permissions just because an account is registered as a delegated admin.
  • How to fix it: Attach an explicit organizations:DescribeAccount allow either via a cross-account IAM role in the management account or a Resource Control Policy (RCP)/SCP exemption scoped to the delegated admin account's principal ARN.
  • Fast path: Use our Client-Side Sandbox above to auto-refactor your failing policy — paste your IAM JSON or SCP and get a corrected diff 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?)

You're running automation from a delegated admin account — Security Hub, GuardDuty, or a custom Lambda — and it hard-stops with:

An error occurred (AccessDenied) when calling the DescribeAccount operation:
  User: arn:aws:sts::111122223333:assumed-role/DelegatedAdminRole/session
  is not authorized to perform: organizations:DescribeAccount
  on resource: arn:aws:organizations::*:account/o-exampleorgid/*
  with an explicit deny in a service control policy

Immediate consequence: Any cross-account inventory, compliance check, or account metadata lookup in your Organizations hierarchy fails silently or throws a hard exception. Pipelines that depend on account tags, account status, or OU membership break entirely. If this is in a Security Hub aggregator or a CSPM tool, you are now flying blind across your entire org.


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

The Attack Vector / Blast Radius

This isn't just an annoyance — it's a visibility gap that directly degrades your security posture.

Why it's dangerous:

  1. Blind spot creation: organizations:DescribeAccount is the primitive that feeds account status (ACTIVE/SUSPENDED), account tags, and parent OU data into every downstream compliance and detection tool. Block it and your CSPM shows stale or missing account records.

  2. SCP explicit deny cascade: The error message says explicit deny in a service control policy. This means somewhere in your OU hierarchy an SCP is running "Effect": "Deny" on organizations:* or a wildcard that catches DescribeAccount. An attacker who has compromised a lower-privileged account in the same OU now knows your SCP is overly broad — a recon signal.

  3. Delegated admin privilege confusion: Developers frequently assume that aws organizations register-delegated-administrator grants API permissions. It does not. It only allows the service (e.g., guardduty.amazonaws.com) to call Organizations APIs on behalf of the service — not your IAM roles inside that account. Your custom automation roles still need explicit IAM grants.

  4. Blast radius if misconfigured the other way: If you "fix" this by attaching organizations:* to the delegated admin role, you've handed that account full Organizations write access — account creation, policy attachment, OU restructuring. A compromised CI/CD pipeline in that account can now restructure your entire org.


How to Fix It (The Solution)

Root Cause Checklist (run in order)

  1. Check for explicit deny in SCPs — this overrides any IAM allow.
  2. Check the IAM role in the delegated admin account — does it have organizations:DescribeAccount?
  3. Check the trust policy — is the role assuming into the management account or calling directly?

Basic Fix — Add the missing permission to the delegated admin IAM role

{
  "Version": "2012-10-17",
  "Statement": [
    {
-     "Sid": "OrgReadOnly",
-     "Effect": "Allow",
-     "Action": [
-       "organizations:ListAccounts"
-     ],
+     "Sid": "OrgReadOnly",
+     "Effect": "Allow",
+     "Action": [
+       "organizations:ListAccounts",
+       "organizations:DescribeAccount",
+       "organizations:ListParents",
+       "organizations:DescribeOrganizationalUnit"
+     ],
      "Resource": "*"
    }
  ]
}

⚠️ organizations:DescribeAccount must be called against the management account's Organizations endpoint. If your delegated admin role lives in account 111122223333, it needs to assume a role in the management account 000000000000 that has this permission, OR the management account must have a resource-based delegation. Direct cross-account Organizations API calls do not work without a role assumption.


Enterprise Best Practice — Scoped cross-account assume-role with condition keys

Step 1: SCP — Remove the overly broad deny, replace with scoped deny

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "DenyOrgWriteActions",
      "Effect": "Deny",
-     "Action": "organizations:*",
+     "Action": [
+       "organizations:CreateAccount",
+       "organizations:DeleteOrganization",
+       "organizations:MoveAccount",
+       "organizations:AttachPolicy",
+       "organizations:DetachPolicy",
+       "organizations:CreatePolicy",
+       "organizations:DeletePolicy"
+     ],
      "Resource": "*",
      "Condition": {
-       "": {}
+       "StringNotEquals": {
+         "aws:PrincipalArn": "arn:aws:iam::000000000000:role/OrgManagementBreakGlassRole"
+       }
      }
    }
  ]
}

Step 2: Management account — IAM role assumable by delegated admin account

+{
+  "Version": "2012-10-17",
+  "Statement": [
+    {
+      "Sid": "AllowDelegatedAdminOrgRead",
+      "Effect": "Allow",
+      "Action": [
+        "organizations:DescribeAccount",
+        "organizations:ListAccounts",
+        "organizations:ListParents",
+        "organizations:DescribeOrganizationalUnit",
+        "organizations:ListTagsForResource"
+      ],
+      "Resource": "*"
+    }
+  ]
+}

Step 3: Trust policy on the management account role

+{
+  "Version": "2012-10-17",
+  "Statement": [
+    {
+      "Effect": "Allow",
+      "Principal": {
+        "AWS": "arn:aws:iam::111122223333:role/DelegatedAdminRole"
+      },
+      "Action": "sts:AssumeRole",
+      "Condition": {
+        "StringEquals": {
+          "sts:ExternalId": "delegated-admin-org-read-2024"
+        },
+        "Bool": {
+          "aws:MultiFactorAuthPresent": "false"
+        }
+      }
+    }
+  ]
+}

💡 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 — Catch overly broad SCP denies pre-merge

Add to your .checkov.yaml:

check:
  - CKV_AWS_110  # Ensure IAM policies do not allow wildcard actions
  - CKV_AWS_111  # Ensure IAM policies do not allow write access without constraints
skip_check:
  - CKV2_AWS_14  # adjust based on your org posture

Run in pipeline:

checkov -d ./terraform/iam --framework terraform --check CKV_AWS_110,CKV_AWS_111 --hard-fail-on HIGH

2. OPA/Conftest — Block `organizations:*` wildcards in SCPs

package aws.organizations.scp

deny[msg] {
  stmt := input.Statement[_]
  stmt.Effect == "Deny"
  stmt.Action == "organizations:*"
  msg := sprintf(
    "SCP statement '%v' uses organizations:* wildcard deny. Enumerate specific write actions instead.",
    [stmt.Sid]
  )
}

3. AWS Config Rule — Continuous compliance

Deploy iam-policy-no-statements-with-admin-access and a custom Config rule that checks delegated admin accounts have organizations:DescribeAccount explicitly listed — not inherited via wildcard — to avoid this class of permission gap silently reappearing after policy rotations.

4. CloudTrail Alert — Catch it before it breaks prod

# EventBridge rule pattern — fires on AccessDenied for Organizations APIs from delegated admin accounts
{
  "source": ["aws.organizations"],
  "detail-type": ["AWS API Call via CloudTrail"],
  "detail": {
    "errorCode": ["AccessDenied"],
    "eventName": ["DescribeAccount", "ListAccounts", "ListParents"],
    "userIdentity": {
      "type": ["AssumedRole"]
    }
  }
}

Route this to an SNS topic wired to your on-call rotation. AccessDenied on read-only Organizations APIs from automation roles is a P2 incident, not a ticket.

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.Does registering a delegated administrator automatically grant organizations:DescribeAccount permissions?
No. The aws organizations register-delegated-administrator command only authorizes the AWS service principal (e.g., guardduty.amazonaws.com) to call Organizations APIs on behalf of that service. Your custom IAM roles inside the delegated admin account still require explicit IAM allows for organizations:DescribeAccount, and those calls must be made by assuming a role in the management account or via a service-linked mechanism. This is a foundational but often misunderstood architectural gap that leaves critical inventory and security tools blind.
Q2.Why does the error say 'explicit deny in a service control policy' even though I added an IAM allow?
SCPs are evaluated before IAM policies in the AWS authorization chain. An explicit Deny in an SCP attached to the delegated admin account's OU cannot be overridden by any IAM allow — not even by the account root. Manually modifying SCPs to carve out exceptions is highly error-prone and risky; a single misconfigured condition can unintentionally widen the blast radius, exposing your entire OU. Instead of gambling with manual edits, drop the broken manifest into the StackEngine Local Sandbox above to automatically compile a zero-trust fix with precise, auditable guardrails.
Q3.What is the minimum set of organizations permissions a delegated admin account needs for read-only inventory?
For safe, least-privilege read-only org inventory: organizations:DescribeAccount, organizations:ListAccounts, organizations:ListAccountsForParent, organizations:ListParents, organizations:DescribeOrganizationalUnit, organizations:ListOrganizationalUnitsForParent, and organizations:ListTagsForResource. Avoid organizations:Describe* wildcards as they can include sensitive policy content APIs. Manually defining and maintaining this exact permission set across large mono-repos or at scale is a compliance and drift management nightmare. For automated pull request generation with Maker-Checker review that enforces these guardrails, upgrade to the StackEngine CI/CD Automator (Team Edition).