Connect AWS to Xplorr: Access Keys and IAM Roles for Cloud Cost Data
Connect AWS to Xplorr
Section titled “Connect AWS to Xplorr”Xplorr reads your AWS costs through the Cost Explorer API and your resources through read-only describe calls. It never writes anything to your account: no resource is created, modified or deleted, and every permission below is a read.
There are two ways to connect, and which one you want depends on how many accounts you have.
| Best for | What Xplorr stores | |
|---|---|---|
| Access keys | One account, or a few unrelated ones | The access key pair for that account, encrypted |
| IAM role | Managing many accounts, typically as an MSP or a group with several AWS accounts | One set of your keys for the organization, plus each account’s role name |
Prefer Terraform or CloudFormation? See Set it up with code.
Before you start
Section titled “Before you start”- Cost Explorer must be enabled in the account. Open Billing → Cost Explorer in the AWS console once; it takes up to 24 hours to populate the first time. Without it every cost call returns an error.
- You need the member or admin role in Xplorr. Viewers cannot connect accounts. Whoever connects an account owns it and controls who else in the organization can see it.
- AWS cost data lags by up to 24 hours. Today’s spend is not final until tomorrow. This is AWS’s own refresh rate, not Xplorr’s.
Method 1: Access keys
Section titled “Method 1: Access keys”The simplest path, and the right one unless you are connecting many accounts.
-
Create a read-only IAM user
In the AWS console, go to IAM → Users → Create user. Give it a name such as
xplorr-readonly. Do not give it console access. -
Attach permissions
Create a customer managed policy from the least privilege policy below and attach it to the user. It holds exactly the calls Xplorr makes.
If you would rather use an AWS managed policy,
ReadOnlyAccesson its own also covers every call Xplorr makes, but it is much broader: it grants read access across almost every AWS service.Do not attach
CostExplorerFullAccess. It grantsce:*, which includes write actions (creating and deleting anomaly monitors, cost categories and reports, and changing Cost Explorer preferences) that Xplorr never needs. -
Create an access key
Open the user → Security credentials → Create access key → choose Third-party service. Copy the access key ID and the secret; the secret is shown once.
-
Connect it in Xplorr
In Xplorr go to Infrastructure → Cloud Accounts and click Connect account. Choose AWS, then Access keys.
Fill in:
-
Account name: whatever you want to call it in Xplorr
-
AWS account ID: the 12 digit number in the top right of the AWS console
-
Credentials (JSON): the Fill template button gives you the shape:
{"accessKeyId": "AKIAIOSFODNN7EXAMPLE","secretAccessKey": "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY","region": "us-east-1"}
Click Test connection first if you want to check the keys without saving anything. Then click Connect.
-
Method 2: IAM role
Section titled “Method 2: IAM role”Use this when you manage several AWS accounts and would rather not create a key pair in each one. You save one set of keys for your Xplorr organization, and Xplorr assumes a role in each account using them.
-
Create an IAM user for Xplorr to act as
In the account that will do the assuming (usually your own management account), create an IAM user named
xplorr-assumer, with no console access. Instead of the read-only policy, attach a policy that only allows it to assume the roles you are about to create:{"Version": "2012-10-17","Statement": [{"Effect": "Allow","Action": "sts:AssumeRole","Resource": "arn:aws:iam::*:role/xplorr-readonly"}]}Create an access key for it.
-
Save those keys once in Xplorr
In Connect account → AWS → IAM role, enter the access key ID and secret, then click Save base keys. Xplorr checks them against AWS before storing them, and they are reused for every account you connect this way.
You can change or remove them later from the same place: the banner shows when they last changed, with Replace keys and Remove beside it.
-
Create the role in each account you want to connect
In each target account, create a role named
xplorr-readonly(or any name, as long as it matches thests:AssumeRoleresource in step 1 and the role name you give Xplorr; role names are case sensitive) with:-
Trust relationship: the
xplorr-assumeruser from step 1{"Version": "2012-10-17","Statement": [{"Effect": "Allow","Principal": {"AWS": "arn:aws:iam::YOUR-ACCOUNT-ID:user/xplorr-assumer"},"Action": "sts:AssumeRole"}]}Replace
YOUR-ACCOUNT-IDwith the account holding the user from step 1. -
Permissions: the least privilege policy below, or the broader AWS managed
ReadOnlyAccess. NotCostExplorerFullAccess, which includes write actions.
-
-
Optional: add an external ID
If you add an external ID to the role’s trust policy, enter the same value in Xplorr. It is genuinely optional here, and you choose the value; Xplorr does not generate one. It is most useful when the assuming user is shared.
-
Connect the account in Xplorr
Enter the account name, the AWS account ID of the target account, and the role name. Xplorr builds the ARN for you and shows it under the field so you can check it before connecting.
The IAM policy
Section titled “The IAM policy”This is the complete list of what Xplorr calls, and the policy we recommend.
Every action here corresponds to a call in the code, and nothing Xplorr calls
is missing from it. The AWS managed ReadOnlyAccess policy also includes all
of these actions, if you prefer a managed policy and accept its much wider
read access. Do not use CostExplorerFullAccess: it grants ce:*, write
actions included.
{ "Version": "2012-10-17", "Statement": [ { "Sid": "CostAndCommitments", "Effect": "Allow", "Action": [ "ce:GetCostAndUsage", "ce:GetCostAndUsageWithResources", "ce:GetReservationCoverage", "ce:GetReservationUtilization", "ce:GetReservationPurchaseRecommendation", "ce:GetSavingsPlansCoverage", "ce:GetSavingsPlansUtilizationDetails", "ce:GetSavingsPlansPurchaseRecommendation" ], "Resource": "*" }, { "Sid": "Commitments", "Effect": "Allow", "Action": [ "savingsplans:DescribeSavingsPlans", "ec2:DescribeReservedInstances", "rds:DescribeReservedDBInstances", "elasticache:DescribeReservedCacheNodes" ], "Resource": "*" }, { "Sid": "Credits", "Effect": "Allow", "Action": [ "billing:GetCredits", "billing:GetCreditAllocationHistory" ], "Resource": "*" }, { "Sid": "Inventory", "Effect": "Allow", "Action": [ "ec2:DescribeRegions", "ec2:DescribeInstances", "ec2:DescribeVolumes", "ec2:DescribeVpcs", "ec2:DescribeSubnets", "ec2:DescribeSecurityGroups", "ec2:DescribeNatGateways", "ec2:DescribeInternetGateways", "elasticloadbalancing:DescribeLoadBalancers", "tag:GetResources" ], "Resource": "*" }, { "Sid": "IdleDetection", "Effect": "Allow", "Action": ["cloudwatch:GetMetricStatistics"], "Resource": "*" }, { "Sid": "Pricing", "Effect": "Allow", "Action": ["pricing:GetProducts"], "Resource": "*" }, { "Sid": "Invoices", "Effect": "Allow", "Action": ["invoicing:ListInvoiceSummaries"], "Resource": "*" }, { "Sid": "AuditLog", "Effect": "Allow", "Action": ["cloudtrail:LookupEvents"], "Resource": "*" } ]}What each group is for:
| Group | Used by |
|---|---|
| CostAndCommitments | All cost figures, and coverage and utilisation for reservations and Savings Plans |
| Commitments | Listing what you actually own, which Cost Explorer does not report in full |
| Inventory | The resource list, and network costs by VPC, NAT gateway and load balancer. tag:GetResources is the Resource Groups Tagging API, which is how Xplorr finds resources across every service without a client per service |
| IdleDetection | CPU and attachment metrics behind idle EC2 and unattached EBS recommendations. Note this is GetMetricStatistics, not GetMetricData |
| Pricing | On-demand rates for sizing recommendations |
| Invoices | Reconciling Xplorr’s totals against your actual invoice |
| AuditLog | Every sync makes one cloudtrail:LookupEvents call (the last hour, at most one event) to record whether the CloudTrail audit log is readable, and Cloud Accounts shows a warning on the account when it is not. Anomaly root cause reads these management events to show who changed what before a spend spike |
If you omit a group, the feature it powers reports an error rather than silently showing nothing.
Optional: FOCUS and carbon exports
Section titled “Optional: FOCUS and carbon exports”If you use the FOCUS billing export or the AWS carbon footprint export, Xplorr
reads those from S3 and needs s3:GetObject and s3:ListBucket on those
buckets. Neither is required for normal cost data.
Set it up with code
Section titled “Set it up with code”The public Xplorrio/cloud-onboarding repository, release v0.0.1,
creates the IAM role method for you: the xplorr-readonly role with the least
privilege policy above and, in one account, the IAM user xplorr-assumer (no
password, no access key) that assumes it. It then prints the values the
Connect account form asks for. The console shows the same quick starts under
Set up with code on the Connect account form, pre-filled with the role
name, region and external ID you typed.
provider "aws" { region = "us-east-1"}
module "xplorr" { source = "git::https://github.com/Xplorrio/cloud-onboarding.git//aws/terraform?ref=v0.0.1"
role_name = "xplorr-readonly" region = "us-east-1" # iam_external_id = "..." # optional; generate one with: openssl rand -hex 16}
output "xplorr_connect_form" { value = module.xplorr.xplorr_connect_form}Apply it with credentials for the account you connect, then print the form values:
terraform initterraform applyterraform output -json xplorr_connect_formNo input is required in the default customer_principal trust mode: the
module reads the account ID from the credentials you apply with, and
role_name and region default to xplorr-readonly and us-east-1. The
module README lists every input and has examples for a
first account, additional accounts and an AWS Organization. A
Terragrunt wrapper is in the same repository.
git clone --depth 1 --branch v0.0.1 https://github.com/Xplorrio/cloud-onboarding.git cloud-onboardingcd cloud-onboarding/aws/cloudformation
aws cloudformation deploy \ --stack-name xplorr-readonly \ --template-file role.yaml \ --capabilities CAPABILITY_NAMED_IAM \ --parameter-overrides TrustMode=customer_principal \ RoleName=xplorr-readonly Region=us-east-1
aws cloudformation describe-stacks --stack-name xplorr-readonly \ --query 'Stacks[0].Outputs' --output tableAdd IamExternalId="$(openssl rand -hex 16)" to the parameter overrides for
an external ID. You can also upload role.yaml under CloudFormation >
Create stack > Upload a template file in the console. For every member
account of an AWS Organization, deploy stackset.yaml after role.yaml; the
CloudFormation README has the steps.
Then, in Connect account, choose IAM role and fill in:
| Xplorr field | Terraform output | CloudFormation output |
|---|---|---|
| IAM user access key ID and secret access key | Once per Xplorr organization: create an access key for xplorr-assumer in IAM > Users > xplorr-assumer > Security credentials > Create access key > Third-party service, then Save base keys | The same |
| AWS account ID | aws_account_id | AwsAccountId |
| Role name | role_name | RoleName |
| External ID | iam_external_id (leave blank when null) | IamExternalId (leave blank when not shown) |
| Region | region | Region |
No template creates the access key, so the secret never lands in Terraform
state or a stack. The role_arn (RoleArn) output matches the ARN the form
shows under Role name.
For more accounts, create the user in one account only. In every other
account create just the role, trusting that user (create_user = false with
trusted_principal_arns, or CreateUser=false with TrustedPrincipalArn),
and let the user assume it (user_assumable_role_arns or
AdditionalAssumableRoleArns where the user lives). Every template also has
an xplorr_principal trust mode; it needs keyless onboarding, which is not
available yet, so keep the default.
What happens when you click Connect
Section titled “What happens when you click Connect”- Xplorr validates the credentials immediately, before saving anything. It has 20 seconds; past that you get an error saying the account was not added and nothing was saved.
- If the account is already connected, you get a clear message saying so rather than a duplicate.
- On success the account is saved and the first sync starts, backfilling 90 days so last month is complete on the day you connect.
- The first sync usually takes 10 to 30 minutes. You get an email when it finishes.
The status column shows Active once the account is connected. A failed sync shows Error with the reason underneath it.
AWS Organizations management accounts
Section titled “AWS Organizations management accounts”Cost Explorer called with an AWS Organizations management (payer) account’s credentials answers for the whole organization: every member account’s spend is in it. So a management account connected on its own shows the cost of the whole organization, which is what you want if you connect only that one.
If you also connect some member accounts separately, in the same Xplorr organization, their cost would otherwise be counted twice. Xplorr prevents that:
- A management account’s data leaves out every other AWS account connected in your Xplorr organization, so a connected member’s cost is counted once, under its own account. This applies to costs, network costs, resource costs, Spot spend, Savings Plans and reservation coverage and utilization, and FOCUS export rows.
- Members that are not connected separately stay inside the management account’s cost.
- An account marked inactive is not synced, so its cost stays inside the management account rather than disappearing.
- A management only or member only setup sends the same requests as before.
The exclusion matches on the AWS account ID you entered for each account, so enter it as the 12 digit number, without dashes.
Cloud Accounts shows a note under a management account, for example AWS Organizations management account: its cost includes 3 linked accounts not connected separately. 2 linked accounts connected separately are excluded to avoid double counting. Data Health marks it Management account.
Checking the total against your AWS bill
Section titled “Checking the total against your AWS bill”Every night Xplorr compares each account’s stored total with the total AWS
reports for the same scope, from one ce:GetCostAndUsage request (daily,
unblended cost, not grouped), for the month to date and the previous month.
For a management account that request leaves out the separately connected
members, the same way the sync does; a member account is filtered to itself.
The result appears on Data Health
as Matches bill, Bill mismatch with the gap, or Bill not checked
with the reason. It needs no permission beyond the policy above.
Because of the exclusion, a management account’s total in Xplorr, and on Data Health, is lower than the organization’s full bill in the AWS console by exactly the connected members’ spend. Add the members’ own totals to compare with the full bill.
Who can do what
Section titled “Who can do what”| Action | Who |
|---|---|
| Connect an account | Member or admin |
| Sync, edit or delete an account | An organization admin, or whoever connected it |
| Save or rotate the organization’s base AWS keys | Admin |
| See an account’s data | Admins see everything; members and viewers see only accounts they have been granted |
Troubleshooting
Section titled “Troubleshooting”“No base AWS credentials configured for this organization” You chose the IAM role method without saving the organization keys first. Go back to step 2 of Method 2.
“Account did not answer within 20 seconds, so the account was not added” AWS did not respond in time. Nothing was saved, so retrying is safe. If it keeps happening, check that Cost Explorer is enabled.
Cost data is empty after a successful connection
Cost Explorer is enabled but not yet populated. It can take 24 hours on first
enable. Verify the policy includes ce:GetCostAndUsage.
Recommendations are missing but costs work
Usually a missing cloudwatch:GetMetricStatistics or tag:GetResources.
Idle-resource detection and the resource sweep need those specifically.
The account row asks you to add cloudtrail:LookupEvents
The last sync was refused the CloudTrail audit log. Add the AuditLog group
to the policy; the warning clears after the next sync. Costs and everything
else keep syncing without it.
Invoices do not reconcile
invoicing:ListInvoiceSummaries is missing, or the account is a member
account and the invoice belongs to the payer.
Does Xplorr ever write to my AWS account? No. Every call listed above is a read, and there are no others.
How often does it sync? Once a day by default. You can sync any account on demand from its row in the accounts table.
Does this work for AWS GovCloud or China regions? Not currently. Both use separate partitions with their own endpoints.
Can I connect a member account rather than the payer? Yes, but it only sees its own costs. Connect the payer account for organization-wide figures, or connect each account separately. You can also connect both: members connected separately are left out of the management account’s cost, so nothing is counted twice (see AWS Organizations management accounts).
A note on where this is going
Section titled “A note on where this is going”Today the IAM role method assumes roles as you, using the keys of an IAM user in your account that you save with Xplorr. Keyless onboarding is coming soon: your role will trust Xplorr directly, with an external ID Xplorr generates, and Xplorr will store no credential of yours at all. It is not available yet, so do not create a role that trusts any account other than your own. This page will give the exact trust policy when it ships.
When that lands, both methods will work at once and existing connections will keep syncing. Re-creating a role to trust Xplorr directly will be a deliberate step you take when convenient, not a break.