Things LLMs get wrong about AWS IAM

Like many, I’ve been using coding agents to write code. This includes my precious AWS IAM policies.

I happen to be particular about AWS IAM, a system so useful and so baroque. Coding agents, as proverbial genies in the bottle misinterpreting your wishes to consternating results, often confuse me with their solutions to IAM problems. When I get confused, I get frustrated. A little emotional, even.

Since way back in my writing career, I’ve thought: be the Google search result you want to see. In the LLM era, it seems like the same concept holds - if I help de-confuse the internet, I will de-confuse myself. With this post, I hope to correct the confusing and, in my humblest opinion, wrong IAM patterns that I consistently fought coding agents about. Voilà, my takes:

Deny in identity policies

I’ve seen a lot of over-zealous AWS IAM policies that use Deny. For reference, the pattern is like, an Allow statement with a Deny next to it to “lock things down”:

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "AllowAppAccess",
      "Effect": "Allow",
      "Action": ["s3:GetObject", "s3:PutObject"],
      "Resource": "arn:aws:s3:::app-data/*"
    },
    {
      "Sid": "DenyEverythingElse",
      "Effect": "Deny",
      "NotAction": "s3:*",
      "Resource": "*"
    }
  ]
}

Maybe they’re useful for coding agents, but my eyes prefer fewer tests, fewer comments, and fewer damn Deny statements in my IAM than the stock experience. They’re not load bearing, per se, and they create complexity, a real tradeoff recognized by system cruft haters like myself.

Deny is still useful, though! Deny works great in SCPs/RCPs for “all identities” invariants/ceilings (e.g. don’t disable audit logging). And it’s of course a common tool for carving things out in unusual circumstances, like revoking role sessions via time-based condition. But in identity policies? Think twice.

Permissions boundaries

Permissions boundaries are a curious pattern. They’re like saffron, or something - perfect in the right dish. Which, for permissions boundaries, is constraining delegated IAM authority. This is not just my opinion - AWS blog says:

The predominant use case for permissions boundaries is to limit privileges available to IAM roles created by developers (referred to as delegated administrators in the IAM documentation) who have permissions to create and manage these roles.

I can imagine the kinds of IAM designs that need permissions boundaries (“we own IAM but we need to let other people we don’t trust manage their own IAM roles in our account”). Like most delegation scenarios, they require some abstract thinking, and are tricky to get right.

But coding agents miss the nuance - they use permissions boundaries as just “a way to restrict IAM subjects”, kind of like Deny in identity policies. I think it’s a very familiar pattern for earnest security designs that “sprinkle a little extra security in there”. And most of the time, there’s no harm and some warm feelies from defense-in-depth. But it’s not what permissions boundaries are for!

sts:GetCallerIdentity in policies

OK this one is very ticky-tacky but also my Van Halen test - sts:GetCallerIdentity doesn’t require an explicit permission. When I see Allow for the sts:GetCallerIdentity action in an IAM policy, it’s a clear iykyk moment. It’s one of the few AWS IAM actions that are always allowed and cannot be blocked - sts:GetSessionToken is the only other one I know of, as of this writing.


It’s interesting to primarily review code after writing code for so long. The limited value I can imagine providing in this era is to spot intuitively wrong directions - common areas of confusion. These are a few that felt worth throwing into the collective context.

All writing