Multi-Account Management
Status: PLANNED — This feature is not yet implemented. The
aws_accountstable, theaccount_discoverycollector, and the cross-account collection wrapper (collect_across_accounts) do not exist yet. Currently collectors work with a single configured AWS account. This document is the design specification for future development.
Polo monitors 10-20+ AWS accounts, with new ones added over time. The system discovers which accounts exist, ensures each has the necessary IAM roles, and alerts when an account exists without Polo visibility.
Account Registry: polo.aws_accounts
CREATE TABLE polo.aws_accounts
(
account_id String,
account_name String,
account_email String DEFAULT '',
org_unit String DEFAULT '',
polo_status LowCardinality(String), -- 'active', 'pending_setup', 'no_access', 'excluded'
polo_read_role String DEFAULT '',
polo_action_role String DEFAULT '',
last_successful_scan DateTime64(3),
last_scan_error String DEFAULT '',
account_role LowCardinality(String) DEFAULT '',
owner String DEFAULT '',
discovered_at DateTime64(3),
updated_at DateTime64(3),
_version UInt64
)
ENGINE = ReplacingMergeTree(_version) ORDER BY (account_id);
Status values
| Status | Meaning |
|---|---|
active | Polo has working access, collectors running |
pending_setup | Discovered but roles not yet provisioned |
no_access | Roles exist but AssumeRole fails |
excluded | Intentionally excluded from monitoring |
Account Discovery
Daily collector calls organizations.list_accounts(). For each account:
- Upsert into
aws_accounts - New accounts →
polo_status = 'pending_setup' - Existing accounts → verify
sts.assume_role()still works - Sync into
hierarchy_nodesasnode_type = 'account'
Coverage Monitoring
SELECT account_id, account_name, polo_status, last_scan_error,
dateDiff('hour', last_successful_scan, now()) AS hours_since_scan
FROM polo.aws_accounts FINAL
WHERE polo_status IN ('pending_setup', 'no_access')
OR last_successful_scan < now() - INTERVAL 24 HOUR;
Powers: UI warning banner, Slack notifications, weekly digest section.
IAM Roles
Two roles per target account, trusting the Polo management account:
{env}-PoloReadRole (for collectors)
Read-only: ec2:Describe*, s3:Get*/List*, elasticloadbalancing:Describe*, sagemaker:List*/Describe*, tag:GetResources, ce:Get*, savingsplans:Describe*, cloudwatch:GetMetricData, logs:DescribeLogGroups, organizations:ListAccounts, iam:ListAccountAliases.
polo-action-role (for action executor)
Write, scoped to supported actions only: ec2:TerminateInstances, ec2:StopInstances, ec2:StartInstances, ec2:ModifyInstanceAttribute, ec2:ModifyVolume, ec2:DeleteVolume, ec2:DeleteSnapshot, ec2:ReleaseAddress, ec2:DeleteNatGateway, sagemaker:StopNotebookInstance, s3:DeleteObject, logs:DeleteLogGroup. With a Deny for polo:protected=true tagged resources.
Role Provisioning
Roles are defined as a CloudFormation StackSet template. Deploy to new accounts via StackSet. The UI will show setup instructions for pending_setup accounts. Infrastructure is managed via AWS CDK in infra/polo/.
See ../operations/new-account.md for the step-by-step process.