Ask a coding agent for an AWS policy and you will usually get something that runs on the first try. That is the problem. The policy works, the task launches, and nobody reads the one line that quietly turns a worker agent into an account administrator. For Australian teams putting Claude Code to work against AWS, that line is almost always the same: iam:PassRole paired with a wildcard resource.
What the PassRole trap actually is
ECS tasks, Lambda functions and many other AWS services run as an IAM role. To start one, the calling identity needs permission to hand that role over, which is what iam:PassRole grants. So far this is normal and necessary. An execution role that launches ECS tasks genuinely needs iam:PassRole. The danger is not the action. The danger is the resource it is scoped to.
When an agent writes a Resource value of "*" next to iam:PassRole, it is saying this identity may pass any role in the account to a service. An agent that can pass any role can start a task running as your most privileged admin role, then do whatever that role allows. That is no longer a permission. It is an escalation path, and a compromised or prompt-injected agent will walk straight down it.
The cost of getting this wrong is not abstract. A single over-scoped role is how a contained incident becomes an account-wide one. Cleaning up after a cloud breach in Australia routinely runs past $45,000 once you count forensics, customer notification and lost engineering time, and a serious data exposure under the Privacy Act can cost a mid-sized firm well over $2.3M when penalties and remediation are added together. The wildcard is cheap to write and expensive to live with.
How to scope it properly
The fix is not to remove iam:PassRole. It is to constrain it to exactly the role the task needs, passable only to the service that needs it. Five changes turn a dangerous statement into a safe one:
Name the role explicitly. Replace the wildcard resource with the full ARN of the one execution role the task uses, so no other role can be passed.
Pin the service. Add an iam:PassedToService condition set to ecs-tasks.amazonaws.com so the role cannot be passed anywhere else.
Enumerate actions. List the specific actions the agent needs instead of service-level wildcards such as iam:* or s3:*.
Add explicit denies. Block the obvious escalation moves, such as attaching policies or creating access keys, even if nothing currently grants them.
Write down the blast radius. End every policy with a short note answering one question: if this agent were fully compromised right now, what is the worst it could do?
Scoped this way, the blast radius drops from the whole account to whatever that single worker role can touch. The task still launches. The escalation path is gone.
Why multi-agent systems make this non-negotiable
A single agent with a tight role is manageable. The risk compounds the moment you run agents that spawn other agents. A sub-agent inherits whatever its parent role allows, and recursive systems chain permissions that no human ever sat down and reviewed. One loose iam:PassRole near the top of that tree is inherited by everything beneath it.
This is why the question to ask is per-agent, not per-system. For each execution role, assume that exact agent is fully compromised and decide whether you can live with the answer. Scope until you can. If you cannot describe the blast radius of an agent in a single sentence, the role is still too broad.
Make the agent write the safe version first
The most reliable place to fix this is before the policy is ever written. Jonathan Ballard packaged the discipline above as a free Claude Code skill that makes the agent produce the scoped policy by default: it enumerates actions instead of wildcarding them, adds explicit escalation denies, and ends each policy with a blast-radius note. Before-and-after fixtures and a scored evaluation ship in the repository, so you can check the behaviour rather than trust it.
This is the practical case for leading with Claude. The same agent that writes your infrastructure can be told to write it safely, with least privilege and scoped credentials as the starting point rather than a later clean-up. Used that way, security review shifts from catching bad policies after the fact to rarely needing to catch them at all.
A five-minute self-audit
If you are already running Claude Code or any agent against AWS, you can check your exposure today. Look for the exact pattern that causes the trouble and fix each instance you find:
Search every role policy for a Resource value of "*" sitting next to iam:PassRole. Each match is a candidate escalation path.
For each one, confirm whether the task needs to pass exactly one role, then replace the wildcard with that role's ARN.
Add the iam:PassedToService condition and re-test the task. A correctly scoped policy still works.
Record who owns each agent role and when it was last reviewed, so the audit is repeatable rather than a one-off.
For an APRA-regulated business in Sydney or a fintech reporting to AUSTRAC, this kind of documented, least-privilege control is also what an auditor expects to see. Getting it right for safety reasons gets you most of the way to getting it right for compliance reasons too.
AI agents are only as safe as the credentials you hand them. If your team is building with Claude Code and wants the IAM groundwork done properly from the start, we help Australian businesses set up scoped, auditable agent permissions. You can book a brainstorm session and we will walk through your current agent roles together.



