Skip to main content
Back
DateRead10 min

AWS IAM Permission Boundaries Explained: What They Do and How to Set Them Up

Key Takeaways

  • A permissions boundary is a managed IAM policy that sets a ceiling on what an IAM user or role can ever do, but it doesn't grant permissions on its own. Effective permissions are always the intersection of the boundary and any attached identity-based policies.
  • The primary use case for permissions boundaries is delegated IAM administration: letting developers create and manage their own roles without being able to create roles more privileged than themselves, which closes the most common privilege escalation path in AWS.
  • Permissions boundaries sit in a specific layer of AWS policy evaluation, distinct from SCPs, resource-based policies, and identity-based policies. Each has a different scope and a different job, and conflating them is where gaps tend to form.
  • Permissions boundaries work well as coarse-grained ceilings on developer-created roles. Fine-grained resource-level least privilege belongs in identity-based policies, and org-wide controls belong in SCPs.

What a Permissions Boundary Is

A permissions boundary is a managed IAM policy attached to an IAM user or role that sets the maximum permissions that entity can ever have. It doesn't grant access on its own. It sets the ceiling on what any attached identity-based policy can actually allow.

The effective permissions of any IAM entity with a boundary attached are always the intersection of what the boundary allows and what the attached identity-based policies allow. If either side doesn't permit an action, the action is denied.

Permissions boundaries have been around long enough that most engineers have encountered the term, but the mental model most people arrive at first is wrong. That tends to produce one of two outcomes: misconfigured boundaries that don't hold, or teams avoiding the feature entirely and leaving a meaningful privilege escalation risk unaddressed.

Where It Sits in the Policy Hierarchy

The AWS policy evaluation layers stacked from service control policies down to session policies, resolving to effective permissions.

AWS evaluates several policy types in sequence before allowing or denying an API call. Permissions boundaries are evaluated alongside SCPs and session policies, with effective permissions determined by the intersection across all applicable types. Unlike SCPs, which apply account- or OU-wide, a permissions boundary only constrains the specific IAM entity it's attached to.

Policy TypeWho Attaches ItWhat It DoesScope
Service Control Policy (SCP)AWS Organizations adminSets the maximum permissions available across an entire account or OUAccount or OU
Permissions boundaryIAM admin (on developer-created entities)Caps the maximum permissions a specific user or role can haveSingle IAM entity
Identity-based policyIAM admin or developerGrants or denies permissions to a specific user or roleSingle IAM entity
Resource-based policyResource ownerGrants principals access to a specific resourceSingle resource
Session policyPassed programmatically at role assumptionLimits permissions for a specific temporary sessionTemporary session

Two relationships in this table matter more than others for day-to-day use.

SCPs and permissions boundaries look similar on the surface. Both set ceilings on what an identity can do, but they operate at different layers. An SCP applies to every identity in an account or organizational unit, regardless of what IAM policies they have attached individually. A permissions boundary applies to a specific user or role. SCPs are the right tool for organization-wide guardrails. Permissions boundaries are the right tool for governing what delegated IAM administrators can create within an account.

Resource-based policies are a separate evaluation path entirely. A permissions boundary on an IAM role doesn't constrain what a resource-based policy can grant to that role. If an S3 bucket policy allows cross-account access to a principal, that access goes through regardless of what boundaries are attached to the principal on the identity side. This matters if you're designing isolation that involves cross-account scenarios, and it's a gap worth planning around explicitly rather than discovering during an incident review.

What It Can and Can't Do

A permissions boundary does one thing: it sets a ceiling. A few things follow from that.

It doesn't grant permissions. Attaching a boundary that includes S3 actions to a role with no identity-based policy attached produces zero effective permissions. The boundary limits what the attached policies can allow. It doesn't replace them.

It can't be exceeded by the entity it's attached to. If a developer has permission to attach policies to a role, they can still attach AdministratorAccess. But if that role has a permissions boundary that only allows S3 and DynamoDB actions, the effective permissions remain S3 and DynamoDB, regardless of what else gets attached.

It doesn't apply to resource-based policies. Cross-account access granted through an S3 bucket policy, KMS key policy, or SQS queue policy isn't constrained by the permissions boundary on the calling principal. If you're designing cross-account isolation, plan for this gap explicitly.

It can be removed, unless you prevent it. If a developer's identity policy includes iam:PutRolePermissionsBoundary or iam:DeleteRolePermissionsBoundary without restriction, they can remove or replace their own boundary. Preventing that requires an explicit Deny on those actions inside the boundary policy itself.

A Working Example

The canonical use case is a developer who needs to create IAM roles for the services their application uses, including Lambda functions, ECS tasks, and application service accounts, without being able to create roles that can reach outside the application's approved scope.

Step 1: Create the boundary policy

This policy defines the ceiling for any role the developer creates. It specifies which AWS services the application ecosystem legitimately needs, and includes an explicit Deny on the actions that would let anyone remove or modify the boundary after the fact.

json
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "AllowApplicationServices",
      "Effect": "Allow",
      "Action": [
        "s3:GetObject",
        "s3:PutObject",
        "s3:ListBucket",
        "dynamodb:GetItem",
        "dynamodb:PutItem",
        "dynamodb:Query",
        "dynamodb:Scan",
        "lambda:InvokeFunction",
        "logs:CreateLogGroup",
        "logs:CreateLogStream",
        "logs:PutLogEvents"
      ],
      "Resource": "*"
    },
    {
      "Sid": "DenyBoundaryModification",
      "Effect": "Deny",
      "Action": [
        "iam:DeleteRolePermissionsBoundary",
        "iam:DeleteUserPermissionsBoundary",
        "iam:PutRolePermissionsBoundary",
        "iam:PutUserPermissionsBoundary"
      ],
      "Resource": "*"
    }
  ]
}

Step 2: Require the boundary as a condition on role creation

This is the step most implementations miss. The developer's own identity policy needs a condition on iam:CreateRole that makes the boundary a prerequisite for the call to succeed. Without this condition, a developer can create a role without any boundary attached, and the ceiling never applies. The iam:PermissionsBoundary condition key lets you specify exactly which boundary policy must be present, so there's no ambiguity about which policy qualifies.

json
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "AllowRoleCreationWithBoundary",
      "Effect": "Allow",
      "Action": [
        "iam:CreateRole",
        "iam:PutRolePolicy",
        "iam:AttachRolePolicy"
      ],
      "Resource": "*",
      "Condition": {
        "StringEquals": {
          "iam:PermissionsBoundary": "arn:aws:iam::123456789012:policy/AppDeveloperBoundary"
        }
      }
    },
    {
      "Sid": "AllowRoleRead",
      "Effect": "Allow",
      "Action": [
        "iam:GetRole",
        "iam:ListRoles",
        "iam:ListRolePolicies",
        "iam:ListAttachedRolePolicies"
      ],
      "Resource": "*"
    }
  ]
}

Step 3: Attach the boundary to the developer's own role

The boundary also needs to be attached to the developer's own IAM role, not just to the roles they create. This closes the loop: the developer can't exceed the boundary themselves. The roles they create don't inherit the boundary automatically, though. That only happens because the iam:PermissionsBoundary condition from Step 2 forces them to attach it at creation time.

Via the AWS CLI:

bash
aws iam put-role-permissions-boundary \
  --role-name AppDeveloperRole \
  --permissions-boundary arn:aws:iam::123456789012:policy/AppDeveloperBoundary

In IaC (Terraform), set the permissions_boundary argument on the aws_iam_role resource to the boundary policy ARN. In CloudFormation, use the PermissionsBoundary property on AWS::IAM::Role. With all three pieces in place, developers can create application roles, attach policies, and build freely. Neither they nor any role they create can reach beyond what the boundary allows, even if someone attaches AdministratorAccess directly.

Limits, Quotas, and Key Reference

Flag for re-verification quarterly against the AWS IAM quotas documentation. Limits and condition-key lists are the fields most likely to go stale.

FieldValueNotes
Permissions boundaries per entity1Only one boundary can be attached to a user or role at a time; attaching a new one replaces the existing one
Supported policy typesCustomer-managed or AWS-managed policies onlyInline policies cannot be used as permissions boundaries
Maximum managed policy document size6,144 charactersApplies to the boundary policy document itself
Account scopeSame account onlyA permissions boundary policy must exist in the same account as the IAM entity it's attached to
Supported entity typesIAM users and rolesPermissions boundaries cannot be attached to IAM groups
Condition key for requiring boundary at role creationiam:PermissionsBoundaryUsed in identity policies to require a specific boundary ARN at role or user creation time; see Step 2 of the worked example above

Best Practices

Apply boundaries to developer-created roles, not to developers themselves. AWS recommends using permissions boundaries to constrain what developers' created roles can do, rather than as the primary mechanism for restricting developer identities directly. There are better tools for that, including identity-based policies and SCPs. Boundaries on developer-created roles are what close the privilege escalation path. Using them as the primary control on developer identities adds complexity without the same security payoff.

Always include a Deny on boundary modification actions. Without an explicit Deny on iam:PutRolePermissionsBoundary and iam:DeleteRolePermissionsBoundary inside the boundary policy itself, a developer with sufficient IAM permissions can simply remove or replace the boundary after the fact, and the enforcement evaporates. This Deny belongs in every boundary policy you write.

Use the iam:PermissionsBoundary condition key as a gate on role creation. The boundary only holds if it's required at creation time. If a developer's identity policy allows iam:CreateRole without a condition key enforcing the boundary ARN, they can create roles without any boundary attached. The condition forces the boundary to be present before the API call succeeds.

Keep boundary policies scoped to what the application actually needs. A boundary that includes every AWS service the team might ever want is a boundary in name only. Start from what the application's workloads legitimately require and be deliberate about adding to it. The narrower the boundary, the more meaningful the ceiling.

Treat permissions boundaries as part of your account architecture, not individual role configuration. Boundaries defined and enforced at the account or OU level through IaC tooling or deployment pipelines stay consistent as teams and accounts scale. Boundaries applied role by role by individual engineers drift. This is the same erosion pattern described in the architecture of intent: the intended design is clear at the moment of definition, but the environment changes faster than the controls do, and the architecture becomes harder to read over time.

Common Mistakes

Confusing a wildcard resource with a permissions grant. A boundary statement with "Resource": "*" doesn't grant access to all resources. The wildcard means the boundary doesn't restrict by resource ARN. It doesn't grant access to anything. Actual access still requires a matching grant in an identity-based policy.

Using a boundary as the only control on a role. A permissions boundary with no identity-based policy attached produces zero effective permissions. Both have to be present and aligned. The boundary limits; the identity-based policy grants.

Assuming boundaries apply to resource-based policies. They don't. Cross-account access granted through a resource-based policy, including S3 bucket policies, KMS key policies, and SQS queue policies, isn't constrained by the permissions boundary attached to the calling principal. If you're designing isolation that involves cross-account access patterns, plan for this explicitly.

Forgetting to block boundary removal. If a developer's identity policy allows iam:PutRolePermissionsBoundary without restriction, they can replace a boundary with a permissive one. The DenyBoundaryModification statement in the boundary policy itself closes this, and it's easier to include from the start than to audit for retroactively.

Widening boundary policies under deployment pressure. A deployment fails because the boundary is too narrow. The fastest fix is to add the missing action to the boundary policy. Done once deliberately, that's fine. Done repeatedly without review, it's how a well-scoped boundary becomes a boundary that allows almost everything. Treat boundary policy changes with the same review discipline as any other security-relevant policy change.

Frequently Asked Questions

What's the difference between a permissions boundary and an SCP? Both set ceilings, but at different scopes. SCPs apply to every identity in an account or OU. A permissions boundary applies only to the specific user or role it's attached to. See the policy hierarchy section above for a full comparison.

Does a permissions boundary grant any access by itself? No. A permissions boundary alone grants nothing. Effective permissions are always the intersection of the boundary and any attached identity-based policies. Both have to allow an action for it to succeed.

Can a developer remove their own permissions boundary? Only if their identity-based policy allows iam:DeleteUserPermissionsBoundary or iam:PutRolePermissionsBoundary without restriction. The standard pattern is to include a Deny on those actions inside the boundary policy itself, which prevents anyone from swapping or removing the boundary on any role they can reach.

Do permissions boundaries apply to resource-based policies? No. Resource-based policies are evaluated separately. If an S3 bucket policy grants a cross-account principal access, that access path isn't constrained by the permissions boundary attached to that principal on the IAM side.

Can I use an inline policy as a permissions boundary? No. AWS only supports customer-managed and AWS-managed policies as permissions boundaries. Inline policies can't be used as a boundary.

When should I use a permissions boundary vs. a narrow identity policy? Use a permissions boundary when you're delegating IAM role creation to developers or automation and you want a ceiling on what those created roles can ever do. Use a narrow identity policy when you need precise, resource-level least privilege on a specific role. The two tools solve different problems and often work together in the same environment.

Permission Boundary vs. IAM Policy. A decision-framework page covering when each tool is the right answer and where teams most often get the distinction wrong.

AWS Service Control Policies (SCPs). The organization-level complement to permissions boundaries. Where permissions boundaries govern individual roles, SCPs set the ceiling for every identity in an account or OU.

Resource Control Policies (RCPs). A newer AWS control type that enforces constraints from the resource side rather than the identity side. Understanding where RCPs fit relative to permissions boundaries matters for anyone designing account-level isolation.

Cloud Security Guardrails. Native's overview of how preventive controls, including permissions boundaries, fit into a broader architectural enforcement model across AWS, Azure, Google Cloud, and OCI.

If you're working through how permissions boundaries fit into your broader IAM architecture, Native can help. Schedule a demo.

Ready to enforce secure-by-design?