Skip to main content

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.

tip

This page covers Organizations-mode specifically. For single-account AWS setup, see AWS Discovery.

How It Works

  1. Infracast assumes a management account role that has organizations:ListAccounts permission
  2. It enumerates all active member accounts in the org
  3. For each member account, Infracast fans out and assumes the InfracastDiscovery role (up to 10 accounts concurrently)
  4. 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.

  1. Navigate to Settings → Connectors → AWS → Add
  2. Toggle Organization Mode on
  3. Copy the CloudShell setup script shown in the UI
  4. 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.

note

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:

FieldValue
Management Role ARNOutput from script (e.g. arn:aws:iam::123456789012:role/InfracastDiscovery)
External IDOutput from script (generated by Infracast, stable)
Member Role NameInfracastDiscovery (default, matches what StackSet deployed)
Org Exclude AccountsOptional — comma-separated account IDs to skip (e.g. sandbox)

Configuration Reference

infracast.yaml (organizations mode)
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.