ABAC

AWS ABAC: IAM Tags, Policy Examples and Best Practices

AWS attribute-based access control (ABAC) is an IAM authorization strategy that makes access decisions from attributes. In AWS, those attributes are usually tags on a principal, a session, a resource or a request. Tags do not grant access by themselves; an IAM policy must evaluate them.

A common pattern is simple: allow an action when a principal’s access-project tag matches the resource’s access-project tag. Implementing it safely also requires control over where attributes come from, who may change authorization tags, which service actions support each condition key and what happens when a required tag is missing.

What is AWS ABAC?

AWS ABAC can replace many resource-specific permissions with a smaller number of reusable policies. Instead of adding every new resource ARN to a project role, you can write a policy that applies whenever trusted principal and resource tags match.

The four building blocks are:

Building block AWS mechanism and purpose
Principal attribute aws:PrincipalTag/key reads the caller’s project.
Resource attribute aws:ResourceTag/key, or a service-specific key, identifies the resource’s project.
Request attribute aws:RequestTag/key and aws:TagKeys constrain tags supplied during creation or modification.
Authorization rule An IAM policy Condition compares the principal and resource project values.

The AWS IAM ABAC documentation describes the same core model. Two qualifications matter:

  • a matching condition makes one policy statement applicable; it does not override an explicit deny, permissions boundary, session policy or AWS Organizations control;
  • condition-key and resource-level permission support varies by AWS service, action and resource type.

ABAC is authorization, not authentication. IAM first has to know which principal is making the request. It then evaluates that principal’s request against the applicable policies.

How an AWS ABAC decision works

An AWS ABAC decision begins before IAM evaluates a policy. The value used in the decision has to enter the request context from a trusted source.

  1. A directory, identity provider, IAM role or workload supplies an attribute such as access-project=orion.
  2. AWS makes that value available as a principal or session tag.
  3. The target AWS resource has a corresponding resource tag, also access-project=orion.
  4. An IAM policy compares the resource tag with ${aws:PrincipalTag/access-project}.
  5. IAM combines that statement with every other applicable policy type and returns an allow or deny decision.

The trust boundary starts with this supply chain. If a user can choose their own project tag, or can retag a production resource, a correctly written comparison can still authorize the wrong access.

The final decision also follows the normal IAM policy evaluation logic. Requests start implicitly denied. An applicable allow can grant access, but an explicit deny wins. Service control policies, resource control policies, permissions boundaries and session policies can further limit the effective permission set.

AWS ABAC condition keys

AWS ABAC policies compare attributes in the request context with condition operators. Principal, resource and request tags serve different roles, and their availability must be verified for every service action and resource type.

These global condition keys appear frequently in tag-based policies:

Condition key Role in the decision
aws:PrincipalTag/access-project Reads a tag on the requesting principal or session to select the caller’s authorized project.
aws:ResourceTag/access-project Reads a tag on the target resource, where supported, to require the same project.
aws:RequestTag/access-project Reads a tag supplied in the current API request to constrain resource creation or modification.
aws:TagKeys Reads all tag-key names supplied in the request to limit which keys a caller may set.

AWS services can also expose service-specific keys such as ec2:ResourceTag/access-project or secretsmanager:ResourceTag/access-project. Do not substitute one form for another by intuition. Check the action and resource type in the AWS Service Authorization Reference.

Use operators that match the key’s data type. Principal, resource and request tag values are single-valued strings in the request context, while aws:TagKeys is multivalued. That is why StringEquals fits a project-tag comparison and ForAllValues:StringEquals can constrain a set of submitted tag keys.

There is a subtle missing-key trap. AWS documents that ForAllValues can evaluate true when its context key is absent. If a tag must be present, add an explicit Null check rather than assuming the set operator proves presence. The IAM condition-operator reference explains this behavior.

How to implement ABAC in AWS

Treat ABAC as an authorization system, not a collection of independent tags.

1. Define a small authorization vocabulary

Start with attributes that have a clear owner and stable meaning. Examples include project, environment, business unit or data classification. Avoid making a person’s email address or display name the core authorization key; those values are harder to govern and may change for reasons unrelated to access.

For each key, document:

  • the authoritative source;
  • the allowed values and case convention;
  • which principal and resource types receive it;
  • who may create, update or remove it;
  • how quickly a source change reaches a new AWS session;
  • the behavior when the value is missing.

One or two governed attributes are easier to test than a large taxonomy whose combinations no one can enumerate.

2. Confirm service and action support

List the exact AWS actions and resource types the workload needs. Then check the relevant service page in the Service Authorization Reference for:

  • whether the action supports resource-level permissions;
  • which resource ARN form it accepts;
  • which global or service-specific condition keys are available;
  • whether creating a tagged resource requires an additional tagging permission;
  • which list or account-level actions cannot be scoped to the same resource.

Do this before writing the policy. A condition that is unavailable for an action does not become valid because another action in the same service supports it.

3. Choose the attribute path

For workforce access, AWS IAM Identity Center can pass selected user attributes into AWS accounts as session tags. The values can come from the Identity Center identity store or, with an external identity provider, from SAML assertions.

For direct federation or role assumption, AWS Security Token Service can accept session tags through AssumeRole, AssumeRoleWithSAML and AssumeRoleWithWebIdentity. Workload roles can also carry IAM role tags.

The choice determines who controls the value and when a change becomes effective. Document it as part of the policy, not as an identity-team implementation detail.

4. AWS ABAC policy example: match principal and resource tags

Begin with a small action set and one resource type. The following identity-based policy adapts the official AWS Secrets Manager ABAC tutorial into a narrower example. It allows a principal to read and describe Secrets Manager secrets only when both sides have the same non-null access-project value:

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "ReadSecretsForMatchingProject",
      "Effect": "Allow",
      "Action": [
        "secretsmanager:GetSecretValue",
        "secretsmanager:DescribeSecret"
      ],
      "Resource": "arn:aws:secretsmanager:<region>:<account-id>:secret:*",
      "Condition": {
        "StringEquals": {
          "aws:ResourceTag/access-project": "${aws:PrincipalTag/access-project}"
        },
        "Null": {
          "aws:PrincipalTag/access-project": "false",
          "aws:ResourceTag/access-project": "false"
        }
      }
    }
  ]
}

Replace the placeholders with your Region and account ID. The example intentionally omits ListSecrets, so it will not power a secret-list workflow. List actions require a separate design review because they do not behave like access to one tagged resource; do not silently add a broad list allow. The example also avoids secretsmanager:*; a policy that automatically inherits every future service action is difficult to call least privilege.

The policy uses a principal tag as a policy variable. IAM substitutes the caller’s value into the string comparison. The Null conditions make the intended fail-closed behavior explicit when either required tag is absent.

If a secret uses a customer-managed KMS key, this Secrets Manager policy is not sufficient by itself. GetSecretValue also requires separately scoped kms:Decrypt permission. Creating a secret with a customer-managed key requires kms:GenerateDataKey and kms:Decrypt; keep those KMS permissions in a separately reviewed statement. AWS documents these requirements in Secrets Manager encryption and key permissions.

5. Tag-on-create policy example

Reading matching resources is only half of the design. If creators can omit or invent the authorization tag, resources can become inaccessible or land in the wrong access domain.

This separate statement allows a principal to create a Secrets Manager secret with tags only when the submitted project tag matches the principal’s project. The Secrets Manager CreateSecret API requires secretsmanager:TagResource permission when the request includes tags, so both actions appear here. The statement permits only the required key plus an optional Name key:

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "CreateSecretForOwnProject",
      "Effect": "Allow",
      "Action": [
        "secretsmanager:CreateSecret",
        "secretsmanager:TagResource"
      ],
      "Resource": "arn:aws:secretsmanager:<region>:<account-id>:secret:*",
      "Condition": {
        "StringEquals": {
          "aws:RequestTag/access-project": "${aws:PrincipalTag/access-project}"
        },
        "ForAllValues:StringEquals": {
          "aws:TagKeys": [
            "access-project",
            "Name"
          ]
        },
        "Null": {
          "aws:PrincipalTag/access-project": "false",
          "aws:RequestTag/access-project": "false"
        }
      }
    }
  ]
}

ForAllValues:StringEquals limits the submitted keys; it does not prove that access-project was supplied. The Null check provides that presence requirement.

Do not treat this as a complete production mutation policy. Because the statement grants TagResource, it can also match a later standalone request that retags a secret to the caller’s project. Any other policy that grants TagResource or UntagResource can create the same bypass. Put creation behind a dedicated provisioning role or workflow, and separately prevent ordinary principals from changing or removing protected authorization tags. AWS publishes an SCP pattern for protecting authorization tags; adapt and test it against your administrative model rather than copying it blindly.

AWS Organizations tag policies can standardize allowed keys and values. Basic compliance enforcement does not require a missing tag on every direct resource-creation path, while required-tag-key enforcement applies only to supported infrastructure-as-code workflows. Use an SCP or another tested provisioning control for direct API guardrails. AWS documents these tag-policy enforcement boundaries.

Principal tags, session tags and IAM Identity Center

A principal tag may come from an IAM user or role tag, or from a session tag attached to temporary credentials. Session tags are useful when a directory owns workforce attributes, but they add trust-policy and lifecycle requirements.

AWS STS requires the role trust policy to permit sts:TagSession when a caller passes session tags. AWS lets you pass up to 50 session tags, with one value per tag. Session tags apply only to the current session, and they continue through role chaining only when the keys are marked transitive. A new session tag overrides a role tag with the same key regardless of character case. These details are documented in Pass session tags in AWS STS.

For IAM Identity Center, decide whether each access-control value comes from the Identity Center identity store or directly from the external IdP’s SAML assertion. If the same attribute is configured in Identity Center and supplied in SAML, the configured identity-store mapping takes precedence. That precedence can explain a correct-looking IdP value that never appears in the AWS session.

Newly issued role sessions pick up changed session attributes; existing temporary credentials retain the attributes with which they were issued until the session expires. IAM Identity Center provides a procedure for responding to active AWS account sessions, including an SCP deny step that blocks use of role sessions that can remain active for up to the configured maximum. Include session duration, reauthentication, immediate-deny handling and role chaining in the change-propagation design, especially for project transfers and departures.

Test the allowed and denied cases

Do not ship an ABAC policy after proving only that the happy path works. Create at least two principals and two resources with different project values, then record the expected decision for each case.

Test Expected result
Principal orion reads resource orion Allowed by the matching statement
Principal orion reads resource atlas The boundary passes only if the overall request is denied; if allowed, identify the broader grant
Principal has no access-project tag The boundary passes only if the overall request is denied; if allowed, identify the broader grant
Resource has no access-project tag The boundary passes only if the overall request is denied; if allowed, identify the broader grant
Principal tries to create resource atlas The boundary passes only if the overall request is denied; if allowed, identify the broader grant
Principal tries to change the protected project tag Must be blocked by the separately implemented tag-mutation guardrail

Use IAM Access Analyzer policy validation to catch syntax, action, ARN and condition-key findings. Use the IAM policy simulator for fast decision checks with explicit context values, but then test in a non-production AWS account. The simulator does not verify that a service action actually supports every supplied global condition key, and it does not evaluate resource control policies. Its result can therefore differ from a live request.

The final test should include the whole effective policy set: identity policies, resource policies, permissions boundaries, session policies and relevant AWS Organizations controls. A matching ABAC statement may still return an overall deny, while a separate broad allow may make a test pass for the wrong reason.

Common AWS ABAC failure modes

AWS ABAC failures usually come from the attribute supply chain, unsupported condition keys, session behavior or another policy layer—not from the tag comparison alone. Start with the caller’s actual session and the target resource instead of the intended directory state.

Symptom What to inspect
A matching user is denied Missing session tag, mismatched tag value or case, duplicate tag keys differing only by case, unsupported condition key, stale session or a higher-level deny
An unrelated user is allowed Broader identity/resource policy, mutable authorization tag or missing boundary/SCP
Role assumption fails Trust policy lacks sts:TagSession, tag limit exceeded or packed session size exceeded
Access disappears after role chaining Required session tag was not marked transitive
IdP value is ignored IAM Identity Center mapping of the same attribute takes precedence
Resource creation is denied Required request tag is missing, unapproved tag key supplied or action needs additional permissions
Tag policy reports look clean but untagged resources exist The tag policy does not evaluate or block that untagged creation path
One service works and another fails The second action/resource does not support the same condition key or ARN scoping

When troubleshooting, inspect the actual caller session rather than the intended directory record. Then examine the resource tags, the action’s supported condition keys and every policy layer that contributes to the result. CloudTrail can show the principal/session context and the request that occurred; it does not by itself prove that your policy design covers every denied case.

AWS ABAC vs RBAC

AWS RBAC assigns permissions through roles and policies; AWS ABAC adds conditions that select resources from trusted attributes. Use RBAC for stable action sets, ABAC for repeated resource boundaries, and a hybrid when roles should define operations while tags limit their scope.

AWS ABAC and RBAC solve different administration problems and are commonly combined.

Model Best fit and main risk
RBAC Best for stable job functions and a manageable role set. Main risk: role or policy proliferation as resources grow.
ABAC Best for repeated project, environment or ownership boundaries across many resources. Main risk: attribute and tag-governance failure.
Hybrid Best when roles define allowed actions and tags constrain the resources those actions reach. Main risk: more interactions to test and explain.

For example, a Developer role can define the permitted EC2 actions while Project=Orion limits those actions to Orion resources. The role answers “which operations?” and the tag condition answers “on which resources?”

ABAC is not automatically more secure or simpler. It reduces policy duplication only when the attribute model is smaller, clearer and better governed than the permissions it replaces. For a broader model-selection framework, see RBAC vs ABAC: differences and when to use each.

AWS ABAC best practices

  • Use a trusted attribute source. Users should not be able to self-assign a project, environment or clearance value that grants access.
  • Keep authorization keys few and explicit. Record exact case, allowed values, owners and missing-value behavior.
  • Separate access tags from descriptive tags. A useful cost-allocation label should not become an authorization boundary accidentally.
  • Constrain tag creation and mutation. Check aws:RequestTag, aws:TagKeys, tag actions and the administrative path that can override them.
  • Verify support for every action. Recheck the Service Authorization Reference when adding a service, resource type or operation.
  • Prefer named actions to broad wildcards. Review new permissions intentionally instead of inheriting them when a service expands.
  • Test negative cases and the full policy set. A deny can come from another policy layer, and an unexpected allow can hide behind a broader grant.
  • Model session freshness. Directory changes affect new sessions; role chains need the correct transitive-tag behavior.
  • Validate continuously. Run IAM Access Analyzer checks in the policy delivery path and test policies in an isolated account before production.
  • Monitor tag drift. Inventory missing, noncompliant and mutable authorization tags; do not rely on a single compliance report.

AWS ABAC is not application-level tenant authorization

AWS IAM ABAC protects AWS actions and resources. Your SaaS application still needs its own authorization decision for questions such as whether a user in Tenant A may read Invoice B, edit a workspace or list customer records.

It is possible to include a trusted tenant identifier in an AWS principal tag and use IAM to isolate tenant-partitioned resources. AWS publishes a SaaS tenant-isolation example using ABAC and IAM. But a tag comparison does not automatically validate application membership, active tenant context, resource ownership or list filtering. Those controls still need an explicit design.

The two layers can use related attributes, but they answer different questions:

Authorization layer Decision path
AWS IAM ABAC Directory or IdP → principal or session attribute → AWS request context → IAM condition → AWS action and resource
Application authorization Authenticated user → tenant membership and active tenant context → application decision → tenant-owned product resource

These are related authorization layers, not the same decision. The application must preserve tenant context when it translates a product request into an AWS request.

See the authorization architecture guide for the application decision model and the general ABAC guide for subject, resource, action and environment attributes outside AWS. Frontegg’s Authorization and Entitlements is the product route for customer-facing application access control; it does not replace AWS IAM policy design.

AWS ABAC implementation checklist

Before rollout, verify that:

  • every authorization attribute has one named source and owner;
  • tag keys, values and case are standardized;
  • each action/resource pair supports the condition keys in the policy;
  • missing principal, resource and request tags fail closed;
  • callers cannot assign or change protected tags outside an approved path;
  • trust policies permit only the required session tags;
  • required tags survive approved role chains;
  • positive and cross-project negative tests both pass;
  • broader policies do not bypass the ABAC condition;
  • explicit denies, boundaries and Organizations controls behave as intended;
  • IAM Access Analyzer validation runs before deployment;
  • session freshness and incident rollback are documented;
  • AWS resource authorization and SaaS tenant authorization are tested separately.

Record where every decision attribute came from, who can change it, which AWS operations honor it and which denied tests prove the boundary before treating the policy as ready for production.

Looking to take your User Management to the next level?
Sign up. It's free