Home Resume
Homeβ€Ί Blogβ€Ί AWS Daily Intelligence #13 - Policy Autopilot Read…
AWS Daily Intelligence AWS

Policy Autopilot Reads Your Terraform Plan, Not Your Runtime

A deterministic way to generate the policy your deployment needs. Which is a different policy from the one your application needs, and the distinction is the whole story.

Verified against current vendor documentation on 19 August 2026. Pricing, limits and API behaviour were checked against the official docs on that date. Cloud services change fast — if you are reading this much later, treat the specifics as a starting point and re-check the linked sources.
AWS Daily Intelligence #13

Executive summary

IAM Policy Autopilot is an open-source tool that analyses your code to deterministically create scoped-down IAM policies. It now accepts Terraform plan files, applying that same analysis to produce a policy scoped to the CRUD functions of the resources in the plan. Generated policies reference specific resource ARNs rather than wildcards where feasible. It runs on your machine, and there is no additional cost.

Two words in that description carry most of the weight. Deterministic means this is static analysis, not inference β€” the same input produces the same policy, and you can reason about why each statement is there. Plan means it reads what Terraform intends to create, which is a complete description of your infrastructure and no description at all of your application's behaviour.

So this generates the policy your deployment needs. That is genuinely useful and it is not the policy your workload needs at runtime. Conflating the two is the mistake this post is about, and it matters more this week than most, because the same series covered IAM role manager attaching PowerUserAccess to Lambda roles precisely because runtime permissions cannot be known in advance.

What changed

Policy Autopilot previously analysed application source code: it read your SDK calls and worked out which IAM actions they required. That covers the workload, and leaves out the pipeline that creates the workload's infrastructure.

Terraform plan file support fills that gap. A plan is Terraform's own statement of what it will create, change or destroy, and it is machine-readable. Feeding it to Autopilot produces the policy the deploying principal needs to carry that plan out.

InputWhat it describesPolicy produced for
Application source codeThe SDK calls your workload makesThe workload's execution role
Terraform plan fileThe resources your infrastructure declaresThe deploying principal
Both togetherTerraform definitions cross-referenced with SDK callsResolving concrete ARNs in workload policy

That third row is the pre-existing capability and it is the more interesting of the two ideas. Application code usually knows the shape of a resource it calls but not its ARN, because the ARN is decided by whatever created it. Reading both sides lets the tool close that loop and emit a concrete ARN instead of a wildcard.

Why AWS built this

Least privilege for deployment pipelines is a problem almost everyone solves badly, and for an understandable reason: the pipeline principal must be able to create things that do not exist yet, so there is nothing to point an ARN at when you write the policy.

The result, near-universally, is a deployment role with broad administrative permissions, justified on the grounds that Terraform "needs to be able to make anything". A plan file undercuts that argument, because a plan is precisely the enumeration of what will be made.

The choice of deterministic analysis over inference is the part worth respecting. A generated policy you cannot explain is one nobody will dare narrow later; a deterministic one can be diffed, reviewed and regenerated, and it will not quietly change because a model was updated.

Architecture

Diagram: the deployment policy generated from a Terraform plan against the runtime policy a workload needs, and why only the first is knowable in advance

The tool runs where you run it. It is open source, it is not a service you enrol in, and it does not need access to your account to do the analysis β€” it reads artefacts you already have.

The generated policy is scoped to the CRUD functions of the resources in the plan. That phrasing is precise and worth reading literally: the unit of scoping is the resource in the plan, and the permissions are those needed to create, read, update and delete it. A resource absent from the plan gets no permissions, which is exactly the intent and exactly the limitation.

Where it can, the output names specific ARNs rather than wildcards. That is the difference between a policy that is narrow and one that merely looks narrow, and it is only possible where the ARN is determinable from the inputs.

The trap: two different policies, one word

Everyone involved calls both of these "the IAM policy", and they answer different questions.

Deployment policyRuntime policy
Held byThe pipeline principalThe workload's execution role
AnswersMay I create this infrastructure?May I do this while running?
Knowable in advanceYes β€” the plan enumerates itOften not, which is why broad defaults exist
Autopilot inputTerraform plan fileApplication source code
Lives forThe duration of an applyThe life of the workload

The asymmetry in the third row is the whole reason this feature is possible. A deployment's permission needs are declarable because deployment is a declaration. A workload's are not, because a workload responds to input.

A plan file cannot see runtime behaviour, and no future version will change that. It does not know which bucket a Lambda reads when a particular message arrives, which secret is fetched only on a failure path, or which cross-account role is assumed under load. Those are properties of execution, not of declared infrastructure. Generating a deployment policy from a plan is not progress toward solving the runtime problem β€” it is a different problem, solved.

This matters this week in particular. IAM role manager attaches PowerUserAccess to Lambda roles because, in AWS's own words, the permissions a function needs are not known up front. A team that reads about Policy Autopilot and concludes the runtime problem is now solved will narrow the wrong policy and leave the broad one in place.

The honest framing: Autopilot's plan-file support removes the excuse for an administrator-level pipeline role. It does nothing about an over-broad execution role, and the two are usually owned by different people.

Business value

The deployment role is one of the most over-permissioned principals in a typical AWS estate, and one of the least examined, because narrowing it has always meant hand-enumerating every resource type a pipeline might touch. That is tedious work that goes stale on the next Terraform change.

Generating it from the plan makes it cheap and repeatable. Regenerating on every plan means the policy tracks the infrastructure rather than drifting behind it, which is the property that makes narrowing sustainable rather than a one-off exercise somebody did in 2024.

The ARN resolution is the quieter win. A policy full of Resource: "*" passes review because it works; one naming actual ARNs is meaningfully narrower and, crucially, fails loudly when infrastructure changes shape β€” which is a feature, because that failure is a review prompt.

Security considerations

A generated policy is a starting point, not an attestation. Deterministic analysis guarantees the output follows from the input; it guarantees nothing about whether the input was complete. A plan generated against one workspace, one variable set or one target does not describe what a different invocation will do.

The plan file itself deserves care. A Terraform plan enumerates your infrastructure and can contain values you would not publish. If plan files start flowing into a policy-generation step in CI, that is a new place they are written and possibly retained.

Narrow the pipeline role and you change failure modes. A pipeline that could do anything failed rarely and mysteriously; one scoped to a plan will fail immediately and specifically when someone adds a resource type the policy has not been regenerated for. That is better, and it needs to be expected, or the first such failure gets fixed by widening the policy again.

Do not let a narrow deployment policy stand in for a security review. A pipeline scoped exactly to its plan can still create wide-open resources β€” a public bucket, an over-permissive role β€” if that is what the plan declares. Constraining what the pipeline may create says nothing about what it does create.

Cost considerations

No additional cost. It is an open-source tool running on your machine or in your pipeline, so the only cost is the compute it runs on, which is negligible against a Terraform plan you were already generating.

The saving is labour, and it is the recurring kind. Hand-narrowing a deployment role is a day of work that decays with every infrastructure change; generating it is a step that stays correct.

Operational considerations

Generate from the same plan you apply. A policy derived from a plan that is not the one being executed is a policy for infrastructure nobody is creating. Producing both in the same pipeline step is what makes the guarantee mean anything.

Expect churn. Every meaningful infrastructure change alters the required permissions, so the policy becomes a generated artefact that changes with the code. Treat it like a lockfile: regenerate, review the diff, commit.

The diff is the review surface. The single most useful thing this enables is noticing that a change to some module now requires IAM permissions it did not before. That shows up in the generated policy diff and nowhere else in a Terraform review.

It complements, rather than replaces, IAM Access Analyzer. Autopilot works forward from declared intent before anything runs; Access Analyzer works backward from CloudTrail after things have run. Neither sees what the other sees, and the runtime side is where the unused-access analysis still earns its place.

Tradeoffs

GainCost
The deployment role can finally be scoped to what it deploysOnly to what that plan deploys; it regenerates or it rots
Deterministic, so the output is reviewable and diffableStatic, so it cannot see anything decided at runtime
Specific ARNs rather than wildcards where feasibleTighter policies fail more often, by design
Open source, local, no service to enrol inYou own running it and keeping it in the pipeline
FreeEasy to mistake for a solution to the runtime problem

Implementation guidance

1. Start with the pipeline role, not the workload role. That is what plan-file support addresses, and it is usually the more over-permissioned of the two.

2. Generate the policy in the same step that produces the plan. Two artefacts from one input, so they cannot disagree.

3. Commit the generated policy and review its diff. The diff answers a question no other part of a Terraform review asks: did this change require new permissions?

4. Use the source-code analysis separately for workload roles, and keep the two conversations apart. Different principal, different lifetime, different owner.

5. Keep Access Analyzer on the runtime side. Autopilot reasons about intent before execution; unused-access analysis reasons about behaviour after it. You want both.

6. Decide where plan files live in CI before piping them into a new step, since they describe your infrastructure in detail.

Best practices

  • Say which policy you mean. "The IAM policy" is ambiguous in every conversation where a pipeline and a workload are both present.
  • Treat generated policy as a build artefact β€” regenerated, reviewed and committed, never hand-edited.
  • Do not widen on first failure. A scoped pipeline role failing on a new resource type is the mechanism working; regenerate instead.
  • Prefer specific ARNs and accept the maintenance. A wildcard that never fails is not evidence of a correct policy.
  • Keep runtime least-privilege on its own track. Nothing here narrows an execution role.

Who should adopt, who should wait

Adopt now if you deploy with Terraform and your pipeline principal holds broad permissions β€” which is the common case. It is free, local, and the output is reviewable, so the cost of trying it is one pipeline step.

Particularly worth it for teams already generating plans in CI, where the input exists and the only change is passing it to one more tool.

Less relevant if you deploy with CloudFormation or CDK rather than Terraform, since plan-file support is the new capability here, or if your pipeline role is already narrowed and maintained by hand.

Do not adopt it expecting to fix over-broad execution roles. That is a different problem and this does not touch it.

Key takeaways

  • IAM Policy Autopilot is an open-source tool that deterministically generates scoped-down IAM policies by analysing your code. It runs locally and costs nothing.
  • It now accepts Terraform plan files, producing a policy scoped to the CRUD functions of the resources in that plan.
  • Generated policies name specific resource ARNs rather than wildcards where feasible.
  • The pre-existing capability cross-references Terraform resource definitions with SDK calls in application code to resolve ARNs β€” closing the gap where code knows a resource but not its ARN.
  • This produces the deployment policy, not the runtime policy. Different principal, different question, different lifetime.
  • A deployment's permissions are knowable because deployment is a declaration. A workload's often are not, which is why broad execution-role defaults exist.
  • A plan file cannot see runtime behaviour, and no future version will change that β€” it is a property of the input, not a gap in the tool.
  • Deterministic matters: the policy can be diffed and reviewed, and will not change because a model was updated.
  • Regenerate with every plan. A generated policy that is not regenerated is a hand-written policy with extra steps.
  • Scoping the pipeline says nothing about what the pipeline creates. A narrow deployment role can still deploy a public bucket.

Official AWS references

Comments

How was your experience?
Your feedback helps improve this site.
PoorExcellent