Why Read-Only Is the Only Safe Way to Let AI Near Your AWS Account

Every AWS cost tool eventually asks you for the same terrifying thing: an IAM role. And every vendor says the same reassuring word about it: "read-only." I want to tell you exactly what that word means when CloudWise says it, because "read-only" gets used loosely enough in this industry that the word alone shouldn't be enough to trust anyone — including us. So this post is the IAM, not the marketing.
The role you actually grant
When you connect an AWS account through CloudWise's one-click setup, you launch exactly one CloudFormation stack: the monitoring stack. There is no button in onboarding that launches anything else — startConnect('monitoring') is the only path the connect flow calls.
That stack's policy (cloudwise-cur-setup-template.yaml) is a bespoke allow-list we wrote and maintain, not the AWS-managed ReadOnlyAccess policy. That distinction matters more than it sounds like it should: ReadOnlyAccess is enormous and vague — it grants read access to almost every AWS service, including ones CloudWise has no reason to ever look at. Our policy is scoped to what a cost scan actually needs, action by action: ec2:DescribeInstances, rds:DescribeDBInstances, s3:ListAllMyBuckets, ce:GetCostAndUsage, compute-optimizer:GetEC2InstanceRecommendations, and so on — read the whole thing at cloudcostwise.io/security/permissions, which renders the live action count straight off the template, not a number we typed into a page and forgot to update. Every single statement in that policy is a Get*, Describe*, List*, or BatchGet* call — plus sts:GetCallerIdentity and iam:SimulatePrincipalPolicy, which CloudWise uses to check what a role can do without ever calling it. There is no Put, Create, Delete, Update, Attach, or Modify verb anywhere in that role's policy. It cannot make a single write call against your account. Not "shouldn't" — cannot; IAM will reject the attempt at the API layer before it reaches any resource.
(One nuance, for the pedants, because I'd rather you catch it than an auditor: the CUR bucket in that same template does have a bucket policy permitting s3:PutObject — granted to AWS's own billingreports.amazonaws.com service principal, not to CloudWise. That's how AWS itself delivers your Cost and Usage Report into a bucket you own. It's AWS writing to you, not us writing to anything.)
Remediation is a different stack, a different role, and a different click
If you want CloudWise to actually fix waste — stop an idle NAT gateway, delete an orphaned snapshot, right-size an instance — that is never part of onboarding. It requires deploying a second, separate CloudFormation stack (CloudWise-Remediation, using cloudwise-remediation-role.yaml) that you reach only from settings/remediation or setup/permissions, deliberately after the point where you've already seen what a read-only scan finds. Nothing in the sign-up or connect flow grants this role. You have to go looking for it.
That role's policy carries an explicit Deny block that overrides everything else, no matter what any other statement in the policy says:
iam:*,organizations:*,sts:*— CloudWise can never touch identity, org structure, or assume other rolessecretsmanager:GetSecretValue/PutSecretValue/CreateSecret/UpdateSecret— no access to your secrets, evercloudtrail:DeleteTrail,cloudtrail:StopLogging,config:DeleteConfigRule,config:StopConfigurationRecorder,guardduty:DeleteDetector— your audit and detection trail can't be turned offs3:DeleteBucket,ec2:DeleteVpc,ec2:DeleteSubnet,ec2:DeleteSecurityGroup,rds:DeleteDBInstance— no deleting the structural stuff that would actually hurtec2:AuthorizeSecurityGroupIngress/Egress,ec2:RevokeSecurityGroupIngress/Egress,ec2:CreateSecurityGroup— CloudWise cannot open, close, or create a network pathkms:CreateKey/CreateGrant/Encrypt/Decrypt/GenerateDataKey*,ssm:*— no touching encryption or Systems Manager
A Deny statement in IAM always wins, regardless of what any Allow statement elsewhere in the same policy — or any other policy — grants. That's not a UI promise. It's how the policy evaluates at the API layer.
You'll also see a small number of Create* actions in that same policy file — things like ec2:RunInstances or rds:CreateDBInstance. Those exist for one purpose: rollback. Before executing an approved action, CloudWise records enough state to reverse it — restart an instance it stopped, recreate a resource it deleted, restore a secret's scheduled deletion. If a fix goes wrong, or you change your mind, there's a way back. That's a narrower and more honest thing than "can create resources," and it's also narrower than "can never create resources" — so I'm not going to round it either direction. Read the actual file if you want the specifics; that's the point of publishing it.
Nothing executes without your click
Here's the part I actually care about you believing: CloudWise proposes fixes. It does not execute them on its own initiative, ever, under any circumstance we ship today.
The execution path (lambdas/remediation_executor/handler.py) checks a status field before it will run a single mutating API call, and that status has to read approved. The only thing that writes approved is a separate approval-gateway Lambda, triggered by a verified, authenticated action from you. There is no code path today that sets an action to approved automatically. If you never click approve, the action sits there, proposed, forever, and nothing happens to your account.
There's also a second, quieter layer under that: every mutating call CloudWise's execution role makes is tagged with a session tag — aws:PrincipalTag/cloudwise-action — that CloudWise itself sets at execution time, and the role's policy can further condition on that tag. I want to be precise about what this is and isn't: it's real defense-in-depth inside our own execution path, not a lever you hold. You don't set that tag; we do. The thing you actually control is upstream of it — the approve click that has to happen before any of this fires at all.
What "read-only" means when we say it
So, precisely:
- Onboarding grants exactly one role, scoped to
Get/Describe/List/BatchGetactions, verifiable line-by-line at cloudcostwise.io/security/permissions. It cannot write to your account. This is the role every CloudWise customer has, whether or not they ever touch remediation. - Remediation is opt-in, separate, and denies the dangerous stuff outright — identity, org structure, secrets, audit trails, network rules, deletion of anything structural — regardless of what else the policy grants.
- Nothing mutates without your explicit approval. Propose, then execute only what you approve. Not "propose, then execute unless you object."
None of that requires you to trust our intentions. It requires you to trust IAM evaluation semantics, which is a much smaller ask, and one you can verify yourself against files we publish rather than a page of prose we wrote about ourselves.
If you want to see what the read-only role actually finds in your account, a free, read-only scan takes about five minutes, grants nothing beyond what's described above, and changes nothing without you approving it first.
Stop wasting money on AWS
CloudWise monitors 45 AWS services and finds waste automatically. Free forever.
Start Free Scan →