CloudPanorama

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.

  1. Create the reader role. deploy/cloudformation/reader-role.yaml creates 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-tenant ExternalId. Deploy it as a StackSet across the organisation, or once per account.

  2. 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.

  3. 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

Allow

  • sts:AssumeRole

Condition:

StringEquals:
    sts:ExternalId: ExternalId

AllowSourceIdentityAndTagPropagation

Allow

  • sts:SetSourceIdentity
  • sts:TagSession

Ec2Inventory

Allow · resource *

  • 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

Allow · resource *

  • organizations:ListAccounts
  • organizations:DescribeOrganization

CostExplorerRead

Allow · resource *

  • ce:GetCostAndUsage
  • ce:GetCostAndUsageWithResources

TrustedAdvisorRead

Allow · resource *

  • support:DescribeTrustedAdvisorChecks
  • support:DescribeTrustedAdvisorCheckResult

EksDiscovery

Allow · resource *

  • eks:ListClusters
  • eks:DescribeCluster
  • eks:ListNodegroups
  • eks:DescribeNodegroup

RdsInventory

Allow · resource *

  • rds:DescribeDBInstances

S3Inventory

Allow · resource *

  • s3:ListAllMyBuckets
  • s3:GetAccountPublicAccessBlock
  • s3:GetBucketTagging
  • s3:GetBucketVersioning
  • s3:GetEncryptionConfiguration
  • s3:GetBucketPublicAccessBlock
  • s3:GetBucketPolicyStatus

DynamoDbInventory

Allow · resource *

  • dynamodb:ListTables
  • dynamodb:DescribeTable
  • dynamodb:DescribeContinuousBackups
  • dynamodb:ListTagsOfResource

ElbInventory

Allow · resource *

  • elasticloadbalancing:DescribeLoadBalancers
  • elasticloadbalancing:DescribeTargetGroups
  • elasticloadbalancing:DescribeTargetHealth
  • elasticloadbalancing:DescribeTags

SqsInventory

Allow · resource *

  • sqs:ListQueues
  • sqs:GetQueueAttributes
  • sqs:ListQueueTags

SnsInventory

Allow · resource *

  • sns:ListTopics
  • sns:GetTopicAttributes
  • sns:ListSubscriptionsByTopic
  • sns:ListTagsForResource

ApiGatewayInventory

Allow · resource arn:${AWS::Partition}:apigateway:*::/restapis, arn:${AWS::Partition}:apigateway:*::/restapis/??????????/deployments, arn:${AWS::Partition}:apigateway:*::/apis, arn:${AWS::Partition}:apigateway:*::/apis/??????????/deployments

  • apigateway:GET

BackupInventory

Allow · resource *

  • backup:ListBackupVaults
  • backup:ListTags

AcmInventory

Allow · resource *

  • acm:ListCertificates
  • acm:DescribeCertificate
  • acm:ListTagsForCertificate

KinesisInventory

Allow · resource *

  • kinesis:ListStreams
  • kinesis:DescribeStreamSummary
  • kinesis:ListTagsForStream

ElastiCacheInventory

Allow · resource *

  • elasticache:DescribeCacheClusters
  • elasticache:ListTagsForResource

OpenSearchInventory

Allow · resource *

  • es:ListDomainNames
  • es:ListTags

GlueInventory

Allow · resource *

  • glue:ListJobs
  • glue:GetTags

AthenaInventory

Allow · resource *

  • athena:ListWorkGroups
  • athena:ListTagsForResource

CloudWatchMetricsRead

Allow · resource *

  • 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

Allow

  • sts:AssumeRole

Condition:

StringEquals:
    sts:ExternalId: ExternalId

AllowSourceIdentityAndTagPropagation

Allow

  • sts:SetSourceIdentity
  • sts:TagSession

ListReportPrefix

Allow · resource arn:aws:s3:::${ReportBucket}

  • s3:ListBucket

Condition:

StringLike:
    s3:prefix: ${ReportPrefix}/*

ReadReportObjects

Allow · resource arn:aws:s3:::${ReportBucket}/${ReportPrefix}/*

  • s3:GetObject