Connect AWS
The reader role, the management account, discover, verify, enable — the exact permission list, rendered from the template that creates the role — and the second role that reads a Cost & Usage Report.
Three steps
Three steps, each of which is a page or a command rather than a manual.
-
Create the reader role.
deploy/cloudformation/reader-role.yamlcreates an IAM role with an exact read-only allowlist (nineteen EC2 describes and two Organizations calls — no wildcards, nothing that returns customer content) trusting this deployment's account under a per-tenantExternalId. Deploy it as a StackSet across the organisation, or once per account. -
Register the management account and get that external id:
panorama admin add-management-account --tenant-slug acme --account-id 111122223333 --name 'Acme management'The external id is generated here, shown once, and inherited by every account discovery registers; put it in the stack's parameter.
-
Discover and enable. In the portal, Admin → Accounts → Discover lists the organisation's accounts through the management role; Verify assumes each account's role and runs every entitled module's cheapest real read — the inventory collectors per region, Spend's Cost Explorer read once for the account — naming the action to grant when one is refused; Enable (with the regions to cover) schedules collection within one cadence.
PUT /admin/accounts/ {accountId}is the same thing over the API.
A worker then registers a run, stores instances in batches, finalizes, and
every status change from then on is a row in the timeline, an alert while it
lasts, a Slack message if a channel is configured, and an entry on the public
page. docs/runbook.md says what to do when any of that stops.
What the reader role may do
CloudPanorama reader role. Grants the platform account read-only access to this account through an assume-role trust that REQUIRES an external ID. Deploy once per account, or once org-wide as a StackSet (see README.md).
The role grants 71 actions in 20 policy statements, every one a read; no wildcard appears anywhere in the template. The remaining 2 statements are the trust policy: only the platform account may assume the role, and only with the external id you were given. The list below is the template's, statement by statement.
AllowPlatformWithExternalId
- sts:AssumeRole
Condition:
StringEquals:
sts:ExternalId: ExternalId
AllowSourceIdentityAndTagPropagation
- sts:SetSourceIdentity
- sts:TagSession
Ec2Inventory
- ec2:DescribeInstances
- ec2:DescribeInstanceStatus
- ec2:DescribeInstanceTypes
- ec2:DescribeVolumes
- ec2:DescribeVolumeStatus
- ec2:DescribeSecurityGroups
- ec2:DescribeSecurityGroupRules
- ec2:DescribeNetworkInterfaces
- ec2:DescribeAddresses
- ec2:DescribeVpcs
- ec2:DescribeSubnets
- ec2:DescribeRouteTables
- ec2:DescribeInternetGateways
- ec2:DescribeNatGateways
- ec2:DescribeVpcEndpoints
- ec2:DescribeVpcPeeringConnections
- ec2:DescribeTags
- ec2:DescribeRegions
- ec2:DescribeAvailabilityZones
OrganizationDiscovery
- organizations:ListAccounts
- organizations:DescribeOrganization
CostExplorerRead
- ce:GetCostAndUsage
- ce:GetCostAndUsageWithResources
TrustedAdvisorRead
- support:DescribeTrustedAdvisorChecks
- support:DescribeTrustedAdvisorCheckResult
EksDiscovery
- eks:ListClusters
- eks:DescribeCluster
- eks:ListNodegroups
- eks:DescribeNodegroup
RdsInventory
- rds:DescribeDBInstances
S3Inventory
- s3:ListAllMyBuckets
- s3:GetAccountPublicAccessBlock
- s3:GetBucketTagging
- s3:GetBucketVersioning
- s3:GetEncryptionConfiguration
- s3:GetBucketPublicAccessBlock
- s3:GetBucketPolicyStatus
DynamoDbInventory
- dynamodb:ListTables
- dynamodb:DescribeTable
- dynamodb:DescribeContinuousBackups
- dynamodb:ListTagsOfResource
ElbInventory
- elasticloadbalancing:DescribeLoadBalancers
- elasticloadbalancing:DescribeTargetGroups
- elasticloadbalancing:DescribeTargetHealth
- elasticloadbalancing:DescribeTags
SqsInventory
- sqs:ListQueues
- sqs:GetQueueAttributes
- sqs:ListQueueTags
SnsInventory
- sns:ListTopics
- sns:GetTopicAttributes
- sns:ListSubscriptionsByTopic
- sns:ListTagsForResource
ApiGatewayInventory
- apigateway:GET
BackupInventory
- backup:ListBackupVaults
- backup:ListTags
AcmInventory
- acm:ListCertificates
- acm:DescribeCertificate
- acm:ListTagsForCertificate
KinesisInventory
- kinesis:ListStreams
- kinesis:DescribeStreamSummary
- kinesis:ListTagsForStream
ElastiCacheInventory
- elasticache:DescribeCacheClusters
- elasticache:ListTagsForResource
OpenSearchInventory
- es:ListDomainNames
- es:ListTags
GlueInventory
- glue:ListJobs
- glue:GetTags
AthenaInventory
- athena:ListWorkGroups
- athena:ListTagsForResource
CloudWatchMetricsRead
- cloudwatch:GetMetricData
- cloudwatch:ListMetrics
The cost report role
CloudPanorama cost report role. Grants the platform account read access to ONE Cost & Usage Report — the objects under one prefix of one bucket — through an assume-role trust that REQUIRES an external ID. A second role beside the reader role on purpose: that one reads inventory metadata and never object content, and this one reads exactly the report this account chose to share, and nothing beside it.
Deploy it once per account that shares a Cost & Usage Report, with the same platform account and external id as the reader role, the report's bucket, and its prefix followed by the report name; enter the RoleArn output beside the report in the cost settings. The reader role itself never gains an object read. Verifying the account then tries these reads under this role and names the ARN a failed one needs.
AllowPlatformWithExternalId
- sts:AssumeRole
Condition:
StringEquals:
sts:ExternalId: ExternalId
AllowSourceIdentityAndTagPropagation
- sts:SetSourceIdentity
- sts:TagSession
ListReportPrefix
- s3:ListBucket
Condition:
StringLike:
s3:prefix: ${ReportPrefix}/*
ReadReportObjects
- s3:GetObject