AWS Organizations Discovery
Infracast can auto-discover every account in your AWS Organization without manual per-account configuration. When a new account is added to your org, it's automatically picked up on the next scan run.
This page covers Organizations-mode specifically. For single-account AWS setup, see AWS Discovery.
How It Works
- Infracast assumes a management account role that has
organizations:ListAccountspermission - It enumerates all active member accounts in the org
- For each member account, Infracast fans out and assumes the
InfracastDiscoveryrole (up to 10 accounts concurrently) - Resources from all accounts are discovered and stored in your topology graph, tagged with their originating account
Setup
Step 1: Run the One-Command Setup Script
The easiest approach is the setup script generated by Infracast directly in the UI. It handles everything — management account role creation, StackSet deployment to all member accounts, and verification.
- Navigate to Settings → Connectors → AWS → Add
- Toggle Organization Mode on
- Copy the CloudShell setup script shown in the UI
- Open AWS CloudShell in your management account and paste the script
The script (shown in full below for reference) does three things automatically:
#!/bin/bash
# Infracast AWS Organizations Discovery Setup
# Run this from the MANAGEMENT ACCOUNT in AWS CloudShell
# This deploys the InfracastDiscovery role to ALL member accounts via CloudFormation StackSets
set -e
ROLE_NAME="InfracastDiscovery"
INFRACAST_PRINCIPAL="arn:aws:iam::427297225718:role/vulcan-prod-ecs-task-role"
EXTERNAL_ID="<generated-by-infracast>" # Shown in the UI — do not modify
echo ""
echo "▶ Step 1/3 — Setting up management account role..."
MGMT_ACCOUNT_ID=$(aws sts get-caller-identity --query Account --output text)
cat > /tmp/trust-policy.json << EOF
{
"Version": "2012-10-17",
"Statement": [{
"Sid": "InfracastAssumeRole",
"Effect": "Allow",
"Principal": { "AWS": "${INFRACAST_PRINCIPAL}" },
"Action": "sts:AssumeRole",
"Condition": { "StringEquals": { "sts:ExternalId": "${EXTERNAL_ID}" } }
}]
}
EOF
cat > /tmp/discovery-policy.json << 'POLICY'
{
"Version": "2012-10-17",
"Statement": [{
"Sid": "InfracastDiscoveryReadOnly",
"Effect": "Allow",
"Action": [
"ec2:Describe*", "ec2:Get*", "ec2:List*",
"s3:GetBucket*", "s3:ListAllMyBuckets", "s3:ListBucket",
"iam:Get*", "iam:List*",
"rds:Describe*", "rds:List*",
"eks:Describe*", "eks:List*",
"lambda:List*", "lambda:Get*",
"cloudfront:List*", "cloudfront:Get*",
"route53:List*", "route53:Get*",
"elasticloadbalancing:Describe*",
"autoscaling:Describe*",
"cloudtrail:Describe*", "cloudtrail:Get*", "cloudtrail:List*",
"config:Describe*", "config:Get*", "config:List*",
"securityhub:Get*", "securityhub:List*", "securityhub:Describe*",
"guardduty:Get*", "guardduty:List*",
"access-analyzer:List*", "access-analyzer:Get*",
"organizations:Describe*", "organizations:List*"
],
"Resource": "*"
}]
}
POLICY
POLICY_ARN="arn:aws:iam::${MGMT_ACCOUNT_ID}:policy/InfracastDiscoveryPolicy"
aws iam create-policy --policy-name InfracastDiscoveryPolicy \
--policy-document file:///tmp/discovery-policy.json > /dev/null 2>&1 || true
if aws iam create-role --role-name "$ROLE_NAME" \
--assume-role-policy-document file:///tmp/trust-policy.json > /dev/null 2>&1; then
echo " ✓ Created role $ROLE_NAME"
else
aws iam update-assume-role-policy --role-name "$ROLE_NAME" \
--policy-document file:///tmp/trust-policy.json > /dev/null 2>&1
echo " ✓ Updated trust policy on existing role"
fi
aws iam attach-role-policy --role-name "$ROLE_NAME" --policy-arn "$POLICY_ARN" > /dev/null 2>&1 || true
echo "✔ Step 1 complete — management account: $MGMT_ACCOUNT_ID"
# ── Step 2: Deploy to ALL member accounts via CloudFormation StackSets ──────
echo ""
echo "▶ Step 2/3 — Deploying to member accounts via StackSets..."
aws cloudformation activate-organizations-access > /dev/null 2>&1 || true
# StackSet template deploys InfracastDiscovery role to every member account
aws cloudformation create-stack-set \
--stack-set-name InfracastDiscovery \
--template-body "$(cat /tmp/infracast-stackset-template.json)" \
--capabilities CAPABILITY_NAMED_IAM \
--permission-model SERVICE_MANAGED \
--auto-deployment Enabled=true,RetainStacksOnAccountRemoval=false \
> /dev/null 2>&1 || true
ROOT_ID=$(aws organizations list-roots --query 'Roots[0].Id' --output text)
if aws cloudformation create-stack-instances \
--stack-set-name InfracastDiscovery \
--deployment-targets "OrganizationalUnitIds=$ROOT_ID" \
--regions us-east-1 \
--operation-preferences FailureTolerancePercentage=20,MaxConcurrentPercentage=50 > /dev/null 2>&1; then
echo " ✓ StackSet deploying to all member accounts (~5 minutes)"
fi
echo "✔ Step 2 complete"
# ── Step 3: Verify & Output ──────────────────────────────────────────
echo ""
echo "▶ Step 3/3 — Verifying setup..."
# (trust policy and policy attachment verification)
MGMT_ROLE_ARN="arn:aws:iam::${MGMT_ACCOUNT_ID}:role/${ROLE_NAME}"
echo ""
echo "═══════════════════════════════════════════════════════"
echo " Paste these values into Infracast:"
echo " Management Role ARN: $MGMT_ROLE_ARN"
echo " External ID: $EXTERNAL_ID"
echo " Member Role Name: InfracastDiscovery"
echo " Mode: Organization (all accounts)"
echo "═══════════════════════════════════════════════════════"
The script outputs the Management Role ARN and External ID to paste back into Infracast.
The actual script shown in the Infracast UI has your External ID pre-filled. Use the UI version — do not copy the script from this page directly.
Step 2: Paste Values into Infracast
After running the script, paste the output into the connector form:
| Field | Value |
|---|---|
| Management Role ARN | Output from script (e.g. arn:aws:iam::123456789012:role/InfracastDiscovery) |
| External ID | Output from script (generated by Infracast, stable) |
| Member Role Name | InfracastDiscovery (default, matches what StackSet deployed) |
| Org Exclude Accounts | Optional — comma-separated account IDs to skip (e.g. sandbox) |
Configuration Reference
discovery:
jobs:
- name: aws-org-discovery
plugin: aws
credential: org-management-role
schedule: "0 */6 * * *" # every 6 hours
config:
mode: organizations
management_account_id: "123456789012"
management_role_arn: "arn:aws:iam::123456789012:role/InfracastDiscovery"
member_role_name: InfracastDiscovery
external_id: your-external-id
regions:
- us-east-1
- us-west-2
# Optional: exclude specific accounts
exclude_accounts:
- "111111111111" # sandbox account
Parallel Discovery
Infracast discovers up to 10 member accounts concurrently to minimize total scan time. For an org with 50 accounts, the wall-clock time is roughly the same as scanning 10 accounts sequentially.
Each account's resources are tagged with account_id in the topology so you can filter and group by account in the UI.
New Account Detection
On each scan run, Infracast calls organizations:ListAccounts and automatically includes any new accounts that have the InfracastDiscovery role available — no manual steps needed.
Troubleshooting
AccessDenied on management role
Verify the trust policy in the management account role includes Infracast's collector ARN (arn:aws:iam::427297225718:role/vulcan-prod-ecs-task-role) and the correct External ID. Check for SCPs that might block cross-account sts:AssumeRole.
Member account skipped
Infracast skips accounts where the InfracastDiscovery role doesn't exist or can't be assumed (logs a warning, continues with other accounts). Check that the StackSet deployed successfully:
aws cloudformation list-stack-instances \
--stack-set-name InfracastDiscovery \
--query 'Summaries[?Status!=`CURRENT`]'
External ID mismatch after rotation
If you rotate the External ID in Infracast, you must re-run the setup script from the UI (it has the new External ID pre-filled). The old trust policy in each member account will stop working until updated.
Only management account resources appear
Ensure Organization Mode is toggled on in the connector form. Without it, Infracast treats the connector as a standard single-account job.
For single-account setup, see AWS Discovery. For topology and route discovery, see Route-Aware Topology.