Managed vs Inline Policies
In AWS IAM (Identity and Access Management, the service that controls who can do what), a policy is the same JSON (JavaScript Object Notation) document no matter where it lives — but how you store and attach it makes a huge difference to how maintainable your account is. AWS gives you three storage choices: AWS-managed, customer-managed, and inline. Picking the wrong one is one of the most common reasons permissions become a tangled mess that nobody can audit. This page explains the three kinds, shows how to create and attach each, and tells you exactly which to reach for.
The three kinds of policy
The difference is not what the policy says — it is who owns it and whether it can be reused.
| Kind | Who maintains it | Reusable across identities? | Versioned | Best for |
|---|---|---|---|---|
| AWS-managed | AWS | Yes | Yes (by AWS) | Quick start, common roles, broad job functions |
| Customer-managed | You | Yes — attach to many users, groups, roles | Yes (up to 5 versions) | Almost everything in your own account |
| Inline | You | No — embedded in exactly one identity | No | Strict one-off bindings only |
An AWS-managed policy is written and kept up to date by AWS. When AWS adds a new action to a service, it can update the policy for you. You see these with names like AmazonS3ReadOnlyAccess or AdministratorAccess. You attach them but you cannot edit them.
A customer-managed policy is one you create. It is a standalone object with its own ARN (Amazon Resource Name, the unique ID AWS gives every resource). You can attach the same policy to many users, groups, and roles, and AWS keeps a version history so you can roll back.
An inline policy is a policy you embed directly inside a single user, group, or role. It has no ARN of its own and exists only as part of that one identity. It is a 1:1 relationship: the policy and the identity are welded together.
The big gotcha with inline policies
This is the part that bites teams later. An inline policy lives inside its identity, so:
- It is deleted automatically when you delete the user or role. There is no copy left behind, and no warning that a permission just vanished.
- It cannot be reused. If three roles need the same permission, you copy-paste the same JSON three times. Fix a typo in one and the others still have it.
- It is invisible to central auditing. Tools that list “all policies in the account” only see managed policies. Inline policies hide inside each identity, so they quietly grant permissions that never show up in your policy inventory.
Warning. Inline policies are where forgotten, over-broad permissions hide. Because they do not appear in the central Policies list and disappear with their identity, a security review can easily miss them. Treat every inline policy as technical debt and reach for a customer-managed policy instead.
When to use which
- Customer-managed — your default. Use these for nearly all permissions you define yourself. One named policy, attached wherever needed, versioned, and visible in the Policies list. Change it once and every attached identity updates.
- AWS-managed — for speed and common patterns. Great when getting started or when a policy maps cleanly to a job function (for example,
ReadOnlyAccessfor an auditor). The trade-off: AWS-managed policies are often broader than you need, and AWS may widen them over time, so do not rely on them for least privilege on sensitive resources. - Inline — rare, strict one-off bindings. Use only when a permission must be tied to exactly one identity and must die with it — for example, a single-purpose role that should never share its policy with anything else. Even then, a well-named customer-managed policy is usually cleaner.
Creating an AWS-managed attachment
You do not create AWS-managed policies — you attach the ones AWS provides.
Console steps:
- Open the IAM console at
https://console.aws.amazon.com/iam/. - Choose Users (or Roles), then select the identity.
- Choose Add permissions → Attach policies directly.
- Search for
AmazonS3ReadOnlyAccess, tick it, and choose Add permissions.
CLI equivalent (AWS CLI v2):
aws iam attach-user-policy \
--user-name dev-anita \
--policy-arn arn:aws:iam::aws:policy/AmazonS3ReadOnlyAccess
Note the ARN starts with arn:aws:iam::aws:policy/ — the aws account ID marks it as AWS-managed. This command prints nothing on success.
Creating a customer-managed policy (recommended)
First create the standalone policy, then attach it anywhere you like.
aws iam create-policy \
--policy-name ReadReportsBucket \
--policy-document file://read-reports.json
Output:
{
"Policy": {
"PolicyName": "ReadReportsBucket",
"PolicyId": "ANPA0A1B2C3D4E5F6G7H8",
"Arn": "arn:aws:iam::123456789012:policy/ReadReportsBucket",
"DefaultVersionId": "v1",
"AttachmentCount": 0,
"CreateDate": "2026-06-15T10:50:00+00:00"
}
}
This time the ARN contains your account ID (123456789012), marking it customer-managed. Now attach it to as many identities as you need:
aws iam attach-role-policy \
--role-name report-reader \
--policy-arn arn:aws:iam::123456789012:policy/ReadReportsBucket
When you need to change it, create a new version instead of editing in place — AWS keeps up to five versions so you can roll back:
aws iam create-policy-version \
--policy-arn arn:aws:iam::123456789012:policy/ReadReportsBucket \
--policy-document file://read-reports-v2.json \
--set-as-default
In the Console, the equivalent is Policies → Create policy, then attach it from each identity’s Add permissions screen.
Creating an inline policy (and why to think twice)
An inline policy is written straight onto one identity with put-*-policy:
aws iam put-role-policy \
--role-name report-reader \
--policy-name OneOffSnsPublish \
--policy-document file://sns-publish.json
This command prints nothing on success. Notice there is no ARN — the policy now only exists inside report-reader. List it with:
aws iam list-role-policies --role-name report-reader
Output:
{
"PolicyNames": [
"OneOffSnsPublish"
]
}
That list-role-policies call is the only way to see this policy. It will never appear under aws iam list-policies or in the Console’s Policies list — which is exactly why inline policies hide permissions.
In the Console, an inline policy is created from an identity’s page via Add permissions → Create inline policy.
Cost note
All three kinds of policy are free. IAM itself never costs anything — you only pay for the underlying resources the permissions let you use. So there is no cost reason to favour inline policies; the right choice is purely about maintainability and auditability.
Best Practices
- Make customer-managed policies your default for everything you author — they are reusable, versioned, and visible to audits.
- Use AWS-managed policies to move fast or cover common job functions, but verify they are not broader than the task needs before trusting them on sensitive resources.
- Avoid inline policies except for strict one-off permissions that must live and die with a single identity.
- Give policies clear, purpose-based names (
ReadReportsBucket, notPolicy1) so the Policies list reads like documentation. - Prefer creating a new policy version over editing in place, so you keep a rollback path.
- Periodically run
aws iam list-role-policies/list-user-policiesacross identities to surface hidden inline policies you may have forgotten.