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:DescribeAccountbut 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:DescribeAccountallow 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.
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.
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:
Blind spot creation:
organizations:DescribeAccountis 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.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"onorganizations:*or a wildcard that catchesDescribeAccount. An attacker who has compromised a lower-privileged account in the same OU now knows your SCP is overly broad — a recon signal.Delegated admin privilege confusion: Developers frequently assume that
aws organizations register-delegated-administratorgrants 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.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)
- Check for explicit deny in SCPs — this overrides any IAM allow.
- Check the IAM role in the delegated admin account — does it have
organizations:DescribeAccount? - 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:DescribeAccountmust be called against the management account's Organizations endpoint. If your delegated admin role lives in account111122223333, it needs to assume a role in the management account000000000000that 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 configs into ChatGPT? Generic AI tools log your company's ARNs, DB strings, and private keys. StackEngine is a zero-backend, pure Client-Side WASM utility. Drop your failing config into the sandbox above. We redact your secrets locally in the browser and auto-generate the refactored code using your own API key.
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.