Cloud Security & Compliance • 3 min read
IAM Hardening for AWS Lambda: Least Privilege in Practice
How to scope Lambda execution roles to exact actions and resources, use conditions and permission boundaries, validate policies with Access Analyzer, and handle secrets safely.
By CryptoAgile Labs Editorial Team • Published July 2026 • Last updated October 10, 2026
Serverless functions are quick to build, which tempts teams to attach broad permissions "just to make it work." Those permissions then survive into production. A function with a wildcard policy turns any code-execution bug or compromised dependency into account-wide access. This guide shows how to build tight roles for AWS Lambda and keep them tight.
The zero-trust mindset
"Zero trust" here means no implicit trust from network position or from the fact that a call is internal. Every function authenticates as its own identity and is allowed only the specific actions it needs on the specific resources it uses. Assume any single function can be compromised and design so the damage is contained.
1. One role per function
Sharing a single role across many functions guarantees it ends up with the union of all their permissions. Create a dedicated execution role for each function (or each tightly related group). Name roles clearly so audit reviews are easy.
2. Specific actions on specific resources
Replace wildcards with exact actions and ARNs.
Too broad:
{ "Effect": "Allow", "Action": "dynamodb:*", "Resource": "*" }Scoped:
{
"Effect": "Allow",
"Action": ["dynamodb:GetItem", "dynamodb:PutItem", "dynamodb:UpdateItem"],
"Resource": "arn:aws:dynamodb:us-west-1:123456789012:table/api-keys"
}Apply the same approach to S3 (bucket and prefix), SES (specific identities), Secrets Manager (specific secret ARNs) and KMS (specific key ARNs). A function that only reads one secret should not be able to list or read all secrets.
3. Add conditions
Conditions narrow when a permission applies. Useful examples:
aws:SourceArnandaws:SourceAccounton resource policies, so only your API Gateway can invoke a function (preventing the confused-deputy problem).dynamodb:LeadingKeysto restrict access to items for a specific partition key, which supports per-tenant isolation.aws:PrincipalOrgIDto limit cross-account access to your organization.ses:FromAddressto restrict which sender address a function may use.
4. Separate who can deploy from what the function can do
Developers who deploy functions do not need the permissions the function itself has. Use a permissions boundary to cap the maximum permissions any role created by a team can have, and service control policies at the organization level to block dangerous actions entirely (for example disabling CloudTrail, or leaving approved regions). Boundaries prevent privilege escalation through creating a new, more powerful role.
5. Start from usage, then reduce
If you are unsure what a function needs, use IAM Access Analyzer. It can generate a policy from CloudTrail activity, and it reports unused permissions and external access. A workable approach is to run in a non-production account with broader permissions briefly, generate a policy from observed calls, review it by hand, and apply it. Revisit regularly, since code changes.
6. Handle secrets properly
Do not hardcode credentials. Lambda environment variables are encrypted at rest, but they are visible in plaintext to any principal with permission to read the function configuration, and they can appear in logs and error output. For sensitive values, store them in AWS Secrets Manager or Systems Manager Parameter Store (SecureString), grant the function read access to that single secret, fetch it at cold start and cache it in memory. Enable rotation where possible. See Secrets Management.
7. Protect the entry points
Authorization at the API layer matters as much as IAM inside it. Use authorizers or JWT validation on API Gateway routes, throttle per client, and validate input. Review the OWASP API Security Top 10 for what to test.
8. Monitor and alert
- Enable CloudTrail in all regions and send logs to a protected bucket.
- Alert on sensitive events: policy changes, role creation, access key creation, use of the root account.
- Use Access Analyzer findings and unused-access reports as a recurring review item.
Review checklist
- No
*in Action or Resource without a documented reason. - One execution role per function.
- Resource policies restrict invocation to named sources with conditions.
- Secrets in a secrets manager, not environment variables.
- Permissions boundary and organization policies in place.
- Access Analyzer reviewed on a schedule.
- Logging and alerting enabled for IAM changes.
These practices also produce the access-control evidence auditors request. See SOC 2 Evidence Automation.
Sources and further reading
Related articles
This article is for educational purposes and is not professional security, legal or compliance advice. Spotted an error or an out-of-date detail? Email us a correction. See our editorial policy.