AI coding agents invent IAM JSON with the confidence of a junior who discovered ChatGPT and the blast radius of a senior who forgot to open the simulator. Wildcards, missing Condition blocks, and Resource: "*" on s3:* show up dressed as “least privilege.” Access Analyzer plus the policy simulator are the gates—not vibes.
⚡ TL;DR: Treat model-emitted IAM as untrusted input. Run Access Analyzer (policy validation + external access findings), simulate every statement against deny/allow fixtures, and only then attach via CI. Reject
*Actions/Resources unless an allowlisted exception ticket exists. Pair with Cursor Rules for AWS CDK and AI-Written Terraform Plan Diff Gates.
Never attach raw model JSON
// scripts/iam-ai-gate.ts
import { IAMClient, SimulatePrincipalPolicyCommand } from "@aws-sdk/client-iam";
import { AccessAnalyzerClient, ValidatePolicyCommand } from "@aws-sdk/client-accessanalyzer";
export type AiPolicyDraft = {
policyDocument: string;
roleName: string;
ticket: string;
};
const FORBIDDEN = [
/"Action"\s*:\s*"\*"/,
/"Action"\s*:\s*\[["']\*["']\]/,
/"Resource"\s*:\s*"\*"/,
/"NotAction"/,
];
export function assertNoWildcards(doc: string) {
for (const re of FORBIDDEN) {
if (re.test(doc)) throw new Error(`forbidden_iam_pattern:${re}`);
}
}
export async function validateWithAnalyzer(doc: string) {
const aa = new AccessAnalyzerClient({});
const out = await aa.send(new ValidatePolicyCommand({
policyDocument: doc,
policyType: "IDENTITY_POLICY",
}));
const errors = (out.findings ?? []).filter((f) => f.findingType === "ERROR");
if (errors.length) throw new Error(JSON.stringify(errors));
return out.findings ?? [];
}
✅ Analyzer + regex denylist before any PutRolePolicy.
❌ aws iam put-role-policy from an agent shell with no simulation.
Simulate the happy path and the deny path
const iam = new IAMClient({});
export async function simulateMustAllow(roleArn: string, actions: string[], resources: string[]) {
const r = await iam.send(new SimulatePrincipalPolicyCommand({
PolicySourceArn: roleArn,
ActionNames: actions,
ResourceArns: resources,
}));
for (const e of r.EvaluationResults ?? []) {
if (e.EvalDecision !== "allowed") {
throw new Error(`expected_allow:${e.EvalActionName}:${e.EvalDecision}`);
}
}
}
export async function simulateMustDeny(roleArn: string, actions: string[], resources: string[]) {
const r = await iam.send(new SimulatePrincipalPolicyCommand({
PolicySourceArn: roleArn,
ActionNames: actions,
ResourceArns: resources,
}));
for (const e of r.EvaluationResults ?? []) {
if (e.EvalDecision === "allowed") {
throw new Error(`expected_deny_got_allow:${e.EvalActionName}`);
}
}
}
// Fixture: agent proposed s3:GetObject on payments bucket — must allow
// Same role must DENY s3:DeleteBucket on org-security bucket
Encode fixtures next to the policy in the PR so reviewers see intent, not only JSON. For CDK-generated policies from agents, see Secure AI Sandboxes.
CI gate shape
# .github/workflows/iam-ai-gate.yml
name: iam-ai-gate
on:
pull_request:
paths: ["infra/iam/**", "**/iam-policies/**"]
jobs:
analyze:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: pnpm tsx scripts/iam-ai-gate.ts --dir infra/iam
- name: Fail on Access Analyzer ERROR findings
run: test ! -s analyzer-errors.json
Feedback loop into the generator
Feed Analyzer findings back into the next model prompt as structured negatives—same spirit as banned-API allowlists in Agent Tool Allowlists—paste Analyzer findings into the agent system prompt as hard bans.
DENIED_PATTERNS:
- Action "*"
- Resource "*" on iam:*, kms:*, s3:*
- Missing aws:PrincipalOrgID on cross-account trusts
Closing checklist
- [ ] Access Analyzer
ValidatePolicyreturns zero ERROR findings - [ ] Policy simulator has allow fixtures and deny fixtures
- [ ] Wildcards blocked unless exception ticket referenced in PR body
- [ ] Attach only from CI role, never from laptop long-lived keys
- [ ] CloudTrail alarm on
PutRolePolicy/AttachRolePolicyoutside the pipeline - [ ] Agent tool that mutates IAM requires human dual-control (see AI On-Call Copilots)
Related reading
- Cursor Rules for AWS CDK: Stop AI From Inventing IAM Wildcards
- AI-Written Terraform: Plan Diff Gates Before Any Apply Runs
- Agent Tool Allowlists: Least Privilege for Filesystem and Shell Access
- Cross-Account Bedrock Access: Platform Teams Without Shared Keys
Last updated on September 11, 2026
Discover more from CheatCoders
Subscribe to get the latest posts sent to your email.
