Skip to content
Tech News
← Back to articles

AWS Secrets Manager Terraform: Least-Privilege Access

read original more articles
Why This Matters

This article highlights a crucial workaround for managing least-privilege access in AWS when IAM policies are overly broad or uncontrollable. By applying resource-based policies directly to secrets, organizations can enforce tighter security controls even in restricted environments, ensuring sensitive data like database passwords are accessible only to authorized applications. This approach is especially relevant for scenarios involving pre-baked roles, cross-account access, or vendor-managed environments, where traditional IAM restrictions are limited.

Key Takeaways

You’ve just been handed access to an AWS account, and the identity you’re given — a role, not a user — already has broad permissions baked in. You didn’t create it. You can’t edit it. You definitely can’t run iam:CreatePolicy to carve out something narrower. And yet the task in front of you is straightforward on paper: store this database password in AWS Secrets Manager, provision it with Terraform, and make sure only the application can read it.

If your instinct is “that’s impossible without controlling IAM,” you’ve been thinking about least privilege in only one dimension. This situation is more common than it looks: AWS Academy Learner Labs hand every student a pre-baked LabRole with a wide identity policy and no iam:CreateRole permission. Cross-account setups often hand you a role assumed from another account that you don’t own. Vendor-managed execution environments — a SaaS platform’s “bring your own AWS account” integration, an SCP-locked landing zone where a central platform team owns every IAM policy — put you in exactly the same spot: you have an identity to work with, and you have zero ability to narrow what that identity is allowed to do at the IAM layer.

This post walks through the pattern that solves that problem for AWS Secrets Manager specifically: instead of fighting the identity-based policy you can’t touch, you attach a resource-based policy directly to the secret. AWS evaluates both layers, and a resource policy scoped tightly enough compensates for an identity policy that’s far too broad. We’ll build this end-to-end with Terraform — creating the secret, writing its value safely, and layering on a resource policy that restricts access to exactly what’s needed — and verify it actually works with both a positive and a negative test.

One thing this post deliberately does not cover: secret rotation. Creating a secret and locking down who can read it is a big enough topic on its own, and rotation with Lambda functions deserves its own post — that’s coming next.

The dual-layer access model: why a resource policy works when the identity policy doesn’t

Every authorization decision in AWS Secrets Manager (and IAM generally) is the result of evaluating two independent policy types for the same request:

Identity-based policy — attached to the principal making the call (a user, role, or federated identity). This is the “what can this identity do” policy. In our scenario, this is LabRole — broad, pre-created, and out of reach.

— attached to the principal making the call (a user, role, or federated identity). This is the “what can this identity do” policy. In our scenario, this is — broad, pre-created, and out of reach. Resource-based policy — attached to the resource being accessed. For Secrets Manager, this is the aws_secretsmanager_secret_policy resource. This is the “who can touch this specific secret” policy, and it’s the one lever we actually control.

AWS evaluates both layers for every request, and the combination logic is simple once you internalize it:

If either layer contains an explicit Deny that matches the request, the request is denied. Full stop — nothing else matters. If neither layer denies, the request is allowed only if at least one layer contains an explicit Allow that matches. Everything not explicitly allowed is implicitly denied.

... continue reading