In AWS IAM, when should a permission live in an identity-based policy attached to a user or role, and when does it have to live in a resource-based policy attached to the resource itself?
answer
- two ends of the same request
- one of them names the caller explicitly
- some callers have no role you own
- a rule that must bind everyone
- not every service offers both
basics
~20 sIdentity-based policies are the default and scale per principal. A resource-based policy is required when the caller is outside the account, when an AWS service principal must be granted access, or when a rule must apply to every caller of that one resource.
solid answer
~50 sIdentity-based policies attach to a user, group or role and answer "what may this principal do", so they are the natural home for a workload's permissions — one role per workload, its permissions travelling with it. A resource-based policy lives on the resource, names a `Principal`, and is the only place some grants can be expressed. You need one in three situations. First, when the caller lives in another AWS account: the resource's own policy has to extend access to that external principal. Second, when an AWS service principal such as `s3.amazonaws.com` or `events.amazonaws.com` must act on the resource — there is no role of yours to attach a policy to. Third, when you want a rule that binds every caller of that resource, such as a bucket policy denying requests where `aws:SecureTransport` is false. Not every service supports resource-based policies — S3, SQS, SNS, Lambda, KMS, Secrets Manager, ECR and a handful of others do; EC2 and DynamoDB tables do not.
code
json · 13 lines{
"Version": "2012-10-17",
"Statement": [{
"Sid": "AllowInvokeFromEventBridgeRule",
"Effect": "Allow",
"Principal": { "Service": "events.amazonaws.com" },
"Action": "lambda:InvokeFunction",
"Resource": "arn:aws:lambda:eu-west-1:123456789012:function:processor",
"Condition": {
"ArnLike": { "aws:SourceArn": "arn:aws:events:eu-west-1:123456789012:rule/nightly" }
}
}]
}go deeper
Know that permissions are normally attached to the role a workload runs as, and that some resources such as S3 buckets and SQS queues carry their own policy too.
Be able to name the cases that force a resource policy — an external account, an AWS service principal, public access — and say which services actually support one.
Show the design judgment: identity policies carry workload permissions, resource policies carry perimeter guarantees that must hold for callers you do not control.
Own where the boundary is drawn account-wide: which guarantees are mandated as resource-policy controls, how they are kept from drifting, and what that costs in review overhead.
## Two places a permission can live AWS permissions can be written from either end of the request. An **identity-based policy** is attached to an IAM user, group or role and reads "this principal may call these actions on these resources". A **resource-based policy** is attached to the resource itself, contains a `Principal` element, and reads "this resource extends these actions to these principals". They are the same JSON grammar pointed in opposite directions. ## Default to the identity For everyday work, permissions belong on the identity. A service gets a role, the role carries exactly the actions that service needs, and the permissions move with the workload when it is redeployed or copied to another environment. This scales: one policy describes one workload's needs regardless of how many resources it touches, and the whole permission set for an application is readable in one place. If you write permissions resource-first, a single application's access ends up scattered across dozens of resource documents, and nobody can answer "what can this service do" without enumerating the account. ## When the resource has to carry it There are grants an identity policy simply cannot express. **The caller is outside your account.** You cannot attach a policy to somebody else's role. If a principal in another account is to touch your queue or your bucket, the grant must be written on your resource, naming that principal. (Their side must also permit the call; the mechanics of how the two sides combine is its own subject.) **An AWS service is the caller.** When S3 publishes an event notification to an SNS topic, or EventBridge invokes a Lambda function, the caller is an AWS service principal, not a role you own. The only place to grant it is the target's own resource policy. This is why `lambda:AddPermission` exists at all: it writes a statement into the function's resource-based policy so that a named service or account may invoke it. **Anonymous or broad access.** A resource policy can name `"Principal": "*"` — the only way to make an object publicly readable, and correspondingly the thing to check first when auditing a bucket. **A rule that must bind everyone.** A resource policy is a single choke point for the resource. `{"Effect": "Deny", "Principal": "*", "Action": "s3:*", "Resource": "...", "Condition": {"Bool": {"aws:SecureTransport": "false"}}}` on a bucket applies to every caller, including principals whose identity policies you do not control. Expressing the same rule identity-side would mean editing every role that can reach the bucket, and hoping nobody adds a new one. ## Not every service offers the choice Resource-based policies exist only where the service implements them. S3 buckets and access points, SQS queues, SNS topics, Lambda functions, KMS keys, Secrets Manager secrets, ECR repositories, EFS file systems, API Gateway APIs, CloudWatch Logs resource policies and a growing list of others have them. EC2 instances, DynamoDB tables and most other resources do not — for those, all access control is identity-side plus conditions. A role's **trust policy** is technically a resource-based policy on the role, which is why it has a `Principal` element while the role's permission policies do not. ```json { "Version": "2012-10-17", "Statement": [{ "Sid": "DenyPlaintextTraffic", "Effect": "Deny", "Principal": "*", "Action": "s3:*", "Resource": ["arn:aws:s3:::reports", "arn:aws:s3:::reports/*"], "Condition": { "Bool": { "aws:SecureTransport": "false" } } }] } ``` ## How to answer the question in practice Ask who the caller is. If it is a principal you own inside the account, put the permission on its role and stop. If it is a foreign account, a service principal, or the general public, the resource must speak. If it is a rule you want true for *all* callers of that resource, the resource must speak even when you also own every caller — because the guarantee should not depend on remembering to edit each identity. In a mature account you see both: identity policies carrying the day-to-day grants, and short, sharp resource policies carrying the perimeter rules and the cross-boundary grants.
- Why can you not solve the AWS-service-invokes-my-function case with an identity policy?Because the caller is an AWS service principal, not an identity in your account. There is no user or role of yours to attach a policy to, so the only document that can carry the grant is the function's own resource-based policy — which is exactly what `lambda:AddPermission` writes into.
- Which resource-based policy statement would you look for first when auditing a bucket?Any statement whose `Principal` is `"*"` without a narrowing condition — that is anonymous access, and it is the only way a bucket becomes public. Next, statements naming an account ARN you do not recognise, and any `Action` wildcard that includes write or policy-modifying operations.
- Is a role's trust policy an identity policy or a resource policy?A resource policy. It is attached to the role, names a `Principal`, and controls who may assume it — the same grammar as a bucket policy. The role's permission policies are the identity-based half and never contain a `Principal` element.
saying these in an interview costs you the question
- Believes every service supports resource-based policies
- Puts routine same-account grants on resource policies
- Thinks a resource policy overrides the identity policy by design
- Cannot name a grant only a resource policy can express
- Confuses a role's trust policy with its permission policies