Least privilege is the principle that a role should be able to do exactly what it needs and nothing more. It is the difference between a leaked key being a shrug and being a catastrophe.
Start from zero
Do not start from an AWS managed policy like AmazonS3FullAccess and try to trim it. Start from an empty policy and add only what breaks when you run the workload.
Scope three things
- Actions — list specific operations, not
s3:*. - Resources — name the exact ARNs, not
"*". - Conditions — constrain by region, tag, or source when you can.
{
"Effect": "Allow",
"Action": ["s3:GetObject"],
"Resource": "arn:aws:s3:::reports-prod/*",
"Condition": {
"StringEquals": { "aws:RequestedRegion": "us-east-1" }
}
}
This role can read report objects, in one bucket, in one region. Nothing else.
Use deny as a guardrail
Because an explicit Deny always wins, a single deny statement can enforce an invariant across an account — for example, deny any S3 action over plain HTTP.
Review, don't guess
- Turn on CloudTrail and let the workload run.
- Use IAM Access Analyzer to generate a policy from real activity.
- Prune generously — unused permissions are pure risk.
Paste your policy into the workspace and ask "remove unnecessary permissions" to get a tightened version you can diff against the original.