The Gap Between Knowing and Doing

Every AWS engineer knows what least privilege means. Grant only the permissions a workload needs to do its job, and nothing more. The principle is simple, the consequences of ignoring it are well-documented, and it appears in every security framework, certification guide, and architecture review checklist that exists. None of this has stopped the most common IAM finding in cloud security audits from being roles and users with far broader permissions than their actual usage requires.

The gap is not ignorance — it is friction. Writing a policy that grants exactly the right permissions for a specific workload is tedious, requires accurate knowledge of what API calls that workload makes, and gets broken by updates. The permissive path — a managed policy like AdministratorAccess or a wildcard action set — is faster, always works, and rarely causes an immediate visible problem. Least privilege fails in practice not because engineers do not understand it, but because the permissive path is easier to reach for under delivery pressure. The solution is to make the correct path easier than the permissive one.

Let Access Analyzer Do the Hard Work

IAM Access Analyzer's policy generation feature addresses the hardest part of least-privilege implementation: knowing what permissions a workload actually uses. Rather than guessing at the right set of actions, you run the workload in a staging environment with a permissive policy, let CloudTrail collect API call logs for a representative period, then generate a policy from those logs. The result is a policy that reflects actual usage, not an engineer's estimate of usage.

This is not a one-time operation. Workload behaviour changes — new features, new integrations, new infrastructure — and the generated policy needs to be regenerated periodically or when significant changes are deployed. Building the generation step into the staging pipeline, so that a new policy is proposed as part of every major deployment, keeps the policy current without requiring sustained manual discipline. The engineering cost of running Access Analyzer on CloudTrail logs is trivial compared to the cost of a privilege escalation incident.

Delegation Without Risk: Permission Boundaries

A common constraint in larger organisations is that the platform team owns IAM, which means application teams cannot create the roles their workloads need without raising a ticket and waiting. The alternative — granting developers iam:CreateRole — creates an obvious privilege escalation path: a developer who can create any role can create a role with AdministratorAccess and assume it. Permission boundaries resolve this by acting as a ceiling on what any role or user can do, regardless of what policies are attached to them.

A permission boundary defined by the platform team limits what permissions a developer-created role can have, even if the developer attaches an overly permissive policy. The developer has the autonomy to create roles for their workloads without platform team involvement; the platform team retains control over the maximum privilege ceiling. This is the correct model for delegating IAM management without creating an unbounded escalation risk, and it scales to organisations where centralised IAM management becomes a bottleneck.

Resource Conditions as a Force Multiplier

IAM condition keys allow a policy to restrict an action not just by what it is but by where and how it operates. An S3 policy that uses a wildcard action set — s3:* — is permissive on the action dimension but can be tightly scoped by adding conditions that restrict it to a specific bucket ARN prefix, a specific region, or resources that carry a specific tag. The same pattern applies across most AWS services: actions that look broad in isolation become targeted when paired with resource-level conditions.

Tagging strategies deserve particular mention here. Teams that implement consistent resource tagging for cost allocation — tagging every resource with the owning team, environment, and workload — have already done most of the work needed to use those tags as access control boundaries. A policy that restricts actions to resources tagged with the workload's own identifier is a durable least-privilege control that does not break when ARNs change. The cost-allocation tagging work pays back in security without additional effort.

The Role-Per-Service Pattern

The shared-role anti-pattern — one IAM role used by multiple distinct services or workloads — is widespread and genuinely dangerous. When a role serves multiple workloads, its permissions are the union of what all of them need, which is always broader than what any individual workload needs. More importantly, a compromise of any one workload using a shared role grants the attacker access to whatever all the other workloads can do.

The alternative is a dedicated role per workload, with permissions scoped to exactly that workload's requirements. The objection is usually role sprawl — too many roles to manage. This is a tooling problem, not an architectural problem. CDK and Terraform both make it straightforward to define a role alongside the workload it belongs to, so that the role is created, scoped, and destroyed as part of the workload's lifecycle. Role sprawl managed through IaC is not sprawl — it is correct isolation with an audit trail.

Making Least Privilege the Default Path

The tools described above — Access Analyzer, permission boundaries, resource conditions, per-workload roles — are not security theatre. They are mechanisms that shift the economics of the decision: making least privilege the path of least resistance rather than the path of most friction. When generating a scoped policy from CloudTrail logs takes less time than arguing about which managed policy to use, engineers will generate the scoped policy. When developers can create roles within a permission boundary without raising tickets, they will create dedicated roles instead of reusing shared ones.

Durable security controls are the ones that travel with the workflow. A policy review in a quarterly audit catches nothing that a continuous policy generation step in a deployment pipeline would not have caught months earlier. The goal is not to make engineers more security-conscious — it is to make the correct choice the natural one.