IAM Permissions in Detail
Every permission the Wring role carries, what it is used for, and what is explicitly denied.
This page documents the exact IAM footprint of the Wring Base stack (CloudFormation template v2.2) — the stack you launch when connecting an account. It is the full version of the access model: every allowed action mapped to the platform function that uses it, and everything that is permanently denied.
The Base stack is pure cost & usage reading. Optional capabilities — such as security checks or Wring Bedrock — ship as separate stacks with their own narrowly scoped roles, listed in the access model; you always review a level's permissions before launching it.
What the stack creates
| Component | Name / pattern | Purpose |
|---|---|---|
| IAM role | WringCostAccess-{id} | Cross-account role assumed by Wring |
| Allow policy | WringAccess (inline) | Four audited read-only permissions (below) |
| Deny policy | WringDataProtectionPolicy-{id} | Always active — blocks sensitive data access |
| EventBridge rule | wring-callback-{id} | Notifies Wring of stack lifecycle events |
| IAM role | wring-eb-sqs-{id} | Lets EventBridge deliver those events to Wring's event bus (events:PutEvents on that one bus, nothing else) |
Stacks created before September 2026 also contain a one-time
Custom::WringStackNotification deploy-completion ping and deliver events to
a Wring queue instead of the event bus; both variants keep working.
The trust relationship
Only Wring's platform account can assume the role, and only when it supplies your per-customer external ID — the standard protection against confused-deputy attacks. The role has no login profile and no access keys; access happens exclusively through short-lived STS sessions (15–60 minutes), each tagged and visible in your CloudTrail.
{
"Version": "2012-10-17",
"Statement": [{
"Effect": "Allow",
"Principal": { "AWS": "arn:aws:iam::{WRING_ACCOUNT_ID}:root" },
"Action": ["sts:AssumeRole", "sts:TagSession"],
"Condition": {
"StringEquals": { "sts:ExternalId": "{YOUR_EXTERNAL_ID}" }
}
}]
}Allowed: four read-only actions
Every action maps to a named platform function — nothing is granted "just in case".
Billing & cost data
| Action | Used for |
|---|---|
ce:GetCostAndUsage | Daily cost synchronization, savings calculation, connection validation |
organizations:DescribeOrganization | Detect whether the account belongs to an AWS Organization |
organizations:ListAccounts | Discover member accounts during onboarding (management accounts only) |
Cost & Usage Report files
| Action | Used for |
|---|---|
s3:GetObject | Read Cost & Usage Report files — restricted to CUR bucket naming patterns |
S3 access is limited to Cost & Usage Report buckets. Report files are read directly by key; the role cannot even list bucket contents, and all other S3 object reads are explicitly denied (next section).
Denied: the data protection policy
A permanent explicit-DENY policy is always attached. In AWS IAM, an explicit deny always wins over any allow — so even a misconfigured future allow statement could not reach this data:
| Category | Denied actions |
|---|---|
| Secrets Manager | secretsmanager:GetSecretValue, DescribeSecret |
| SSM Parameters | ssm:GetParameter, GetParameters, GetParametersByPath |
| Database data | rds-data:ExecuteStatement, BatchExecuteStatement · dynamodb:GetItem, BatchGetItem, Query, Scan |
| CloudWatch Logs | logs:GetLogEvents, FilterLogEvents, GetLogRecord, StartQuery, GetQueryResults |
| Source code | codecommit:GetFile, GetBlob, GetDifferences, GetCommit, GitPull · codeartifact:GetPackageVersionAsset, ReadFromRepository |
| User identities | cognito-idp:AdminGetUser, ListUsers, AdminListGroupsForUser · cognito-sync:QueryRecords · iam:GetUser, ListUsers, ListAccessKeys |
| Instance access | ec2-instance-connect:SendSSHPublicKey · ssm:StartSession, SendCommand |
| Queues & streams | sqs:ReceiveMessage · kinesis:GetRecords, GetShardIterator · firehose:GetRecord · kafka:FetchTopic |
Additionally, s3:GetObject is denied for every bucket that does not
match Cost & Usage Report naming patterns — application data in S3 is
unreachable regardless of any other policy.
The stack callback
So your account activates automatically, an EventBridge rule notifies Wring when CloudFormation finishes:
| Property | Value |
|---|---|
| Watches | Only stacks whose name starts with Wring-, in the account and Region the stack was created in |
| Events | Create/update/delete completions and failures |
| Delivery | Wring's EventBridge event bus, via a role that can only events:PutEvents on that bus |
| Region | Any — the bus accepts events from every AWS Region, so you can create the stack wherever your policies allow |
The event carries no secret: it is the unmodified CloudFormation status event, and AWS itself stamps the sending account. Before Wring activates an account it verifies that the new role trusts Wring under exactly your external ID — a role created for a different ID, or one without the ID condition, is refused. | Integrity | Events carry a per-customer signature; unsigned events are discarded |
No AWS managed policies
Earlier template versions attached AWS's managed AWSBillingReadOnlyAccess
policy, which covers the entire billing console surface (payment methods, tax
registrations, budgets, …). A least-privilege audit removed it: the explicit
actions above cover everything the platform actually calls.
Both template revisions since then have narrowed the Base stack. The one grant ever added to it after that audit — read-only CloudWatch metrics, for the Bedrock usage dashboard — no longer lives here either: it moved into its own opt-in level, so an account that doesn't use Bedrock never grants it.
Design principles
| Principle | Implementation |
|---|---|
| Audited least privilege | Every action maps to a named platform function |
| Explicit deny | Data protection policy cannot be overridden by any allow |
| External ID | Required on every cross-account role assumption |
| Read-only | No write, modify, or delete permission exists anywhere in the role |
| No standing access | No users, passwords, or access keys — only short-lived STS sessions |
| Audit trail | Every role assumption and API call appears in your CloudTrail |
| No instance access | SSH, SSM sessions, and remote command execution are explicitly denied |
| Revocable by you | Deleting the CloudFormation stack removes all access instantly |
Questions about a specific permission? Ask [email protected] — we're happy to walk your security team through the template line by line.