I'm working through security audits and keep finding roles with permissions like s3:* or AdministratorAccess, often added simply to get a deployment working before a deadline. The frustrating part is that narrowing permissions is usually possible with tools such as IAM Access Analyzer, but debugging missing permissions can be slow and confusing—especially with indirect dependencies, CloudFormation or CDK bootstrapping, KMS-encrypted resources, policy size limits, and environment-specific resource names.
I recently reviewed a web portal whose Lambda roles were scoped to specific DynamoDB resources and S3 prefixes, which showed that careful permissions are achievable. At the same time, some experienced engineers are still deploying internal tools with broad EC2 or administrative access "just in case."
What processes, tooling, or guardrails have worked for your teams? How do you keep development convenient while ensuring staging and production use genuinely least-privilege roles, without making engineers resent the security process?
5 Answers
AWS permissions can be genuinely difficult to narrow down. Some access is indirect, such as KMS operations behind an encrypted S3 object, and some behavior only appears in rare error paths or through assumed identities. Logs may also contain calls from abandoned implementation attempts, while generated policies can include environment-specific ARNs that do not transfer cleanly to staging or production. Access Analyzer and CloudTrail are useful starting points, but they still require human review and proper parameterization.
A lot of this is a developer-experience and incentives problem, not simply laziness. When every missing permission means another Terraform or pipeline cycle, people optimize for getting the deployment finished. Make the safe path faster: provide reusable IaC constructs, clear policy templates, pre-deployment validation, and automated checks that explain exactly what needs changing. Guardrails work better when they remove guesswork instead of just blocking people.
A fast pre-production check that predicts permission failures would get much more buy-in than asking developers to discover them through repeated deployments.
The temporary wildcard is especially dangerous because it usually becomes permanent once the application is working and nobody wants to risk breaking it.
Training and feedback still matter. Many engineers know that a wildcard makes an error disappear, but they do not know how to identify the specific action, resource, or condition they actually need. Give them examples, documentation that lists required permissions for common integrations, and automated policy reviews using tools such as IaC scanners. The goal should be to make the secure option the easiest option, rather than relying on people to remember cleanup after a stressful deployment.
Centralizing all IAM work is not automatically the answer. A central team can become a bottleneck and may respond by creating one huge policy instead of several focused ones. A better model is shared ownership: security defines standards and guardrails, platform teams provide safe building blocks, and application teams own the business context. Reusable CDK or Terraform modules can grant access to the exact resource—for example, a queue construct granting only the required read permissions—without making every developer hand-write policy documents.
Ownership and incentives matter. Managers should be accountable for overprivileged roles, while engineers should have a supported path for requesting narrowly scoped access.
A practical compromise is to allow broader access in isolated development accounts, with the risk clearly understood, while making staging and production match the intended restricted model. Use permission boundaries, organization policies, and service control policies to cap the maximum possible access so that a wildcard policy cannot become unrestricted administrative power. Enforce the restricted version in CI before it reaches a shared or production account.
The environments should be equivalent where possible, especially when infrastructure is managed as code. If development is intentionally looser, the difference needs to be explicit and tested so it does not hide production failures.

The tools are helpful for discovering observed access, but they cannot reliably cover untested exception paths or infer every resource relationship. Treat generated policies as drafts, not final answers.