01.The Principle of Least Privilege
Every IAM entity — user, role, Lambda function — should have only the permissions it needs to do its job, nothing more. Start with zero permissions and add explicitly. Never attach AdministratorAccess to application roles. A compromised Lambda with admin permissions can destroy your entire AWS account.
02.Use Roles, Never Long-Lived Keys
IAM access keys are long-lived credentials that never expire unless you rotate them manually. They get committed to git, hardcoded in config files, and leaked in logs. Use IAM roles with STS for temporary credentials instead. EC2 instances, Lambda functions, and ECS tasks should all assume roles — never use keys.
- ▸Lambda: attach execution role, access via SDK automatically
- ▸EC2: attach instance profile, credentials via metadata service
- ▸Cross-account: use role assumption with ExternalId
- ▸CI/CD: use OIDC federation, not long-lived keys
03.Resource-Level Permissions
Never write s3:GetObject on Resource: '*'. Always scope permissions to specific resources. Use ARN patterns to grant access only to the specific buckets, queues, or functions your service needs. This limits blast radius if credentials are compromised.
{
"Effect": "Allow",
"Action": ["s3:GetObject", "s3:PutObject"],
"Resource": [
"arn:aws:s3:::my-app-uploads/*",
"arn:aws:s3:::my-app-clean/*"
]
}04.Enable CloudTrail Everywhere
CloudTrail logs every API call made in your AWS account — who called what, when, from where. Enable it in all regions, store logs in a separate audit account with write-once permissions. Set up EventBridge rules to alert on sensitive actions like IAM changes, root account login, or security group modifications.