Why Read-Only Is the Only Safe Way to Let AI Near Your AWS Account CloudWise, a cloud cost optimization tool, has detailed its security model for AWS account access, emphasizing that its read-only monitoring role is scoped to specific actions and cannot make any write calls. The company's remediation role, which can make changes, is deliberately separate and includes explicit deny rules to protect critical resources. The design aims to ensure that AI-driven cost optimization cannot accidentally harm a user's AWS environment. 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. 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 https://cloudcostwise.io/security/permissions?utm source=blog&utm medium=hub&utm campaign=read-only-is-the-only-safe-way-to-let-ai-near-your-aws-account , 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. 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 roles secretsmanager:GetSecretValue / PutSecretValue / CreateSecret / UpdateSecret — no access to your secrets, ever cloudtrail:DeleteTrail , cloudtrail:StopLogging , config:DeleteConfigRule , config:StopConfigurationRecorder , guardduty:DeleteDetector — your audit and detection trail can't be turned off s3:DeleteBucket , ec2:DeleteVpc , ec2:DeleteSubnet , ec2:DeleteSecurityGroup , rds:DeleteDBInstance — no deleting the structural stuff that would actually hurt ec2:AuthorizeSecurityGroupIngress/Egress , ec2:RevokeSecurityGroupIngress/Egress , ec2:CreateSecurityGroup — CloudWise cannot open, close, or create a network path kms:CreateKey/CreateGrant/Encrypt/Decrypt/GenerateDataKey , ssm: — no touching encryption or Systems ManagerA 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. 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. So, precisely: Get / Describe / List / BatchGet actions, verifiable line-by-line at 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 https://cloudcostwise.io?utm source=blog&utm medium=hub&utm campaign=read-only-is-the-only-safe-way-to-let-ai-near-your-aws-account takes about five minutes, grants nothing beyond what's described above, and changes nothing without you approving it first.