Why this matters
AWS identity is the control plane for almost everything you do in an account. A person or workload signs in, assumes a role, or uses an access key, and every API call is checked against IAM policies. If those policies allow broad actions on all resources, one stolen key or one careless script can change data, open a network path, or delete backups. Least privilege limits that blast radius.
Least privilege does not mean removing admin from everyone and hoping nothing breaks. It means each identity gets the actions and resources it needs for its job, under the conditions you can enforce, for only as long as it needs them. That includes human users, service roles, cross-account roles, CI pipelines, and third-party integrations. The work is part inventory, part policy design, and part review habit.
Tight policies also make reviews easier. When a customer security questionnaire asks who can read production data, you can answer from role names and policy statements instead of guesswork. When an application fails, a narrow policy gives you a clearer signal about which permission is missing. Broad admin policies hide both answers.
Before you start
Pick one AWS account and one application as the first target. Trying to rewrite every identity in an organization at once creates too many changes to test. Confirm that CloudTrail is logging management events and that you can search recent activity. Turn on IAM Access Analyzer in the account and note any existing findings. You will use these tools to see what identities actually do, not what their policy names suggest.
List the identities in scope: IAM users, IAM groups, IAM roles, access keys, and cross-account trust relationships. Add the owner and purpose for each one. Do not skip roles used by Lambda, ECS tasks, EC2 instances, Step Functions, or CI systems. Non-human identities often carry the widest permissions because they were created quickly to make a deployment work.
Agree on guardrails before you edit policies. Decide who can approve an exception, how long an exception lasts, and how you will roll back a change. Use a nonproduction account for the first pass. Check whether AWS Organizations service control policies already restrict the account, because an SCP can block an action even when an identity policy allows it. Know your break-glass path for emergencies, and keep it separate from daily roles.
How to do it
Start with observed activity. A policy built from guesses will be either too broad or too brittle. Use IAM Access Advisor to see which services an identity has used, and use CloudTrail to list the actions and resources behind that use. IAM Access Analyzer can generate a policy from a CloudTrail trail and can flag unused permissions. Cover a full business cycle so monthly reports and quarterly jobs show up.
Write policies with explicit actions and resource ARNs. Replace wildcards in the Action and Resource elements wherever you can name the action or ARN. Add conditions that fit the workload, such as aws:RequestedRegion, aws:SourceAccount, aws:SourceArn, or aws:PrincipalOrgID. For third-party roles, require an external ID and scope the trust policy to the exact principal. Put human access behind IAM Identity Center permission sets, and give workloads roles instead of long-term access keys.
Add guardrails that cap permissions without granting new ones. Permission boundaries limit what an identity policy can allow, which is useful when you delegate role creation. SCPs apply organization-wide limits to member accounts, such as blocking dangerous services or regions you do not use. Resource policies on S3 buckets, KMS keys, and queues also decide access, so review them alongside identity policies. A deny in any applicable policy wins.
Change one identity or one workload at a time. Apply the narrower policy in a test account, run the real job, and read the errors. Fix missing permissions by adding a specific action or ARN, not by restoring a wildcard. Then move to production during a low-risk window and watch CloudTrail for AccessDenied events. Keep a short exception list with an owner and an end date.
- Inventory identities and owners. List IAM users, groups, roles, access keys, and trust policies in the target account. Tag each one with an owner, purpose, and environment so you know what a change will affect.
- Collect real usage data. Use IAM Access Advisor and CloudTrail to see which services, actions, and resources each identity used. Cover a full business cycle so scheduled jobs are included.
- Draft narrow policies. Replace wildcards with explicit actions and resource ARNs. Add conditions such as aws:SourceArn or aws:PrincipalOrgID where they fit the workload.
- Move workloads to roles. Give applications, CI jobs, and third parties IAM roles with scoped trust policies. Remove long-term access keys after the role path works.
- Add boundaries and SCPs. Use permission boundaries for delegated administration and SCPs for organization-wide limits. Remember that these guardrails cap permissions but do not grant them.
- Test one change at a time. Apply the narrower policy in a test account, run the real workload, and read CloudTrail errors. Fix gaps with specific actions, not broad wildcards.
- Review and retire. Check unused access findings, remove stale roles and keys, and set an expiry date for every exception. Repeat the review on a fixed schedule.
How to check it worked
Look at the evidence, not the policy text alone. IAM Access Analyzer unused access findings should shrink for the identities you changed. IAM Access Advisor should show the same services being used with fewer permissions granted. CloudTrail should show successful calls for the workload and no unexpected new actions. A quiet AccessDenied graph is not enough if the workload stopped running.
Run the application through its normal paths: sign-in, read, write, batch job, deployment, and rollback. Check background jobs and scheduled tasks that may run only weekly. Ask the application owner to confirm behavior, and compare error rates before and after. If you see AccessDenied spikes, map each event to a missing action and add only that action. If you see successful calls that the identity should not make, tighten the policy again.
Review trust policies and resource policies as part of the check. A role can have a narrow identity policy and still be assumable by a broad principal. An S3 bucket can grant access that IAM does not. Confirm that permission boundaries and SCPs are attached where you intended, and that break-glass access was not used during the change. Record the review date and the next owner check.
Mistakes to avoid
Do not start with a deny-all policy and wait for production to fail. That approach creates outages and teaches the team to fear least privilege. Start from observed activity, remove clear excess, and iterate. Also avoid replacing one wildcard with another, such as allowing s3:* on all buckets because the app only needs GetObject and PutObject on two prefixes.
Do not ignore non-human identities. Roles for CI, Lambda, containers, and third parties often have the widest reach and the least review. Do not leave old access keys active after moving a workload to roles. Do not treat permission boundaries or SCPs as grants; they only limit. And do not forget resource policies, because a narrow IAM policy can be undone by a bucket policy that allows a broad principal.
Least privilege is a maintenance habit, not a one-time cleanup. Put a review on the calendar, track exceptions with expiry dates, and remove unused roles and keys. If you want an outside read before a customer security review, the Cloud security review at $6,500 fixed price is one option among others.
Frequently asked questions
What is least privilege in AWS IAM?
Least privilege means an AWS identity gets only the actions and resources it needs for its task, under the conditions you set, for as long as it needs them. In IAM, that usually means explicit actions and resource ARNs, scoped trust policies, and roles instead of long-term keys.
Should I use IAM users or IAM roles for least privilege?
Use IAM roles for workloads and for human access through federation or IAM Identity Center. Roles issue temporary credentials and make trust and permission policies easier to scope. Keep IAM users only where a specific system cannot use roles, and remove their access keys when you can.
How do I find unused AWS IAM permissions?
Use IAM Access Analyzer unused access findings, IAM Access Advisor service data, and CloudTrail event history. Compare those results with the policy document, then remove actions and resources that no identity used during a full business cycle.