Types of Policies

policy types

Policies are bits of JSON that define who can do what. That almost sounds simple, but some of them have annoying details (examples below, altho that’s not the point of this article).

There’s actually a few different types of policies, some of which you’ll run into often. Here’s some info you need about the most common ones.

The AWS docs are gonna throw these terms around, so you should know them. We'll talk about:

  1. Identity-based policiess
  2. Resource-based policies
  3. The other, more boring, policy types

Identity-Based Policies

These are the most common types of policies - the ones you’re likely most used to.

A common use for a Policy is attaching one to IAM User or Role. IAM Users/Roles are examples of an identity. This is why it’s called an identity-based policy.

Here’s an example of such a policy you might create, and then attach to a user/role:

1{
2 "Version":"2012-10-17",
3 "Statement":[
4 {
5 "Sid":"GetAuthorizationToken",
6 "Effect":"Allow",
7 "Action":[
8 "ecr:GetAuthorizationToken"
9 ],
10 "Resource":"*"
11 },
12 {
13 "Sid":"ManageRepositoryContents",
14 "Effect":"Allow",
15 "Action":[
16 "ecr:BatchCheckLayerAvailability",
17 "ecr:GetDownloadUrlForLayer",
18 "ecr:GetRepositoryPolicy",
19 "ecr:DescribeRepositories",
20 "ecr:ListImages",
21 "ecr:DescribeImages",
22 "ecr:BatchGetImage",
23 "ecr:InitiateLayerUpload",
24 "ecr:UploadLayerPart",
25 "ecr:CompleteLayerUpload",
26 "ecr:PutImage"
27 ],
28 "Resource":"arn:aws:ecr:us-east-1:123456789012:repository/my-repo"
29 }
30 ]
31}

This policy lets us push and pull container images from an ECR repository.

Fun detail! This ECR policy is broken up into 2 statements as you need ecr:GetAuthorizationToken on resource *, otherwise it doesn’t work.

There’s lots of foot-guns like this. I’ll point them out as I go.

Where's the Identity?

As stated, you can create a policy (a bunch of JSON, like the above example), and attach it to an identity, such as an IAM User or Role.

Attaching a policy to an identity gives the identity its permissions.

You may have seen policies with a Principal section in them before. With identity-based policies, we don’t need to define a Principal. The principal is the identity - and the policy is attached directly to it already.

Resource-Based Policies

These are policies you attach to a resource directly.

A resource might be an ECR repository, an S3 bucket, a Secrets Manager secret, or any service that supports Resource-based policies.

Here’s an example policy attached to an SQS queue:

1{
2 "Version": "2012-10-17",
3 "Statement": [
4 {
5 "Sid": "allow-another-account",
6 "Effect": "Allow",
7 "Principal": {
8 "AWS": "098709870987"
9 },
10 "Action": [
11 "sqs:SendMessage",
12 "sqs:ReceiveMessage",
13 "sqs:GetQueueUrl",
14 "sqs:GetQueueAttributes",
15 "sqs:DeleteMessage"
16 ],
17 "Resource": "arn:aws:sqs:us-east-1:123412341234:my-queue",
18 }
19 ]
20}

The important point here is that a resource-based policy is attached to a ressource. We need to tell it who this policy affects. Therefore, we must define a Principal.

The above resource-based policy is on SQS queue “my-queue”. It has a Resource defined but it’s actually redundant - the only resource it’s applied to is the SQS queue the policy is on (which is “my-queue” in account 123412341234).

The examples for SQS resource policies all show a Resource being defined, and it’s a good idea to follow the doc’s lead. A lot of times there are implicit reasons the documentation does this.

For example, the ECR service gives you an obtuse error if you include a Resource section in a resource-based policy! The ECR example docs don’t tell you not to use a Resoure, yet the examples omit them!

Since resource-based policies are attached to a resource, we (often) can get away with having no Resource: ... section, or using Resource: "*" in the Policy JSON. Each service is actually a bit different in this regard, so its not an exact science. As stated above, find documentation examples and use their conventions in that regard.

But that Principal, tho?

Since a Resource is inferred(ish), we instead need to supply who the policy affects. This is the Principal.

The Principal can be a lot of things - an AWS account, a specific Role, an IAM user, and more.

1{
2 "Version": "2012-10-17",
3 "Statement": [
4 {
5 "Sid": "send-message-from-these-principals",
6 "Effect": "Allow",
7 "Principal": {
8 "AWS": [
9 "098709870987",
10 "arn:aws:iam::444455556666:role/some-role-name",
11 "arn:aws:iam::111122223333:user/some-iam-user"
12 ]
13 },
14 "Action": [
15 "sqs:SendMessage",
16 "sqs:GetQueueUrl",
17 "sqs:GetQueueAttributes",
18 ],
19 "Resource": "arn:aws:sqs:us-east-1:123412341234:my-queue",
20 }
21 ]
22}

The above gives the ability for identities in account 098709870987, a specific role in account 444455556666, and a specific IAM user in yet another account 111122223333 some permissions to send messages to this queue.

Note that the above resource-based policy gives other accounts the potential to use this queue.

The principals in the other account actually need their own (identity-based) policy that ALSO states they can send messages to this queue. More on that in other articles.

Last example: we can set Principal: "*" (any principal! from anywhere!) and use Conditions to lock down access in fancier ways!

1{
2 "Version": "2012-10-17",
3 "Statement": [
4 {
5 "Sid": "ou-based-access",
6 "Effect": "Allow",
7 "Principal": {
8 "AWS": "*"
9 },
10 "Action": [
11 "sqs:SendMessage",
12 "sqs:ReceiveMessage",
13 "sqs:GetQueueUrl",
14 "sqs:GetQueueAttributes",
15 "sqs:DeleteMessage"
16 ],
17 "Resource": "arn:aws:sqs:us-east-1:123412341234:my-queue",
18 "Condition": {
19 "ForAnyValue:StringEquals": {
20 "aws:PrincipalOrgPaths": "o-abcabcabcd/r-r00t/ou-abcd-efghijkl/ou-mnop-qrstuvwx/"
21 }
22 }
23 }
24 ]
25}

This is a fun one. The Principal section is effectively ANY principal in any account, globally. However, the Condition locks it down to only allow API requests coming from within your AWS Organization.

This might be a good use case for a Organization RCP, more on that below.

AWS Orgs have the notion of Organizational Units (OU’s), which categorize your multitude of AWS accounts. The above OU path might correspond to AWS accounts within your Some-Org/Root-Acct/Product-Name/Production OU.

Trust Policies

If you've used Roles before, you may be familiar with their trust policies. All roles have a trust policy.

These are actually a form of resource-based policy!

Each role has only one trust policy, and like any resource-based policy, it requires a Principal section.

The Trust Policy determines who is allowed to assume the role (hence the need for a Principal).

1{
2 "Version": "2012-10-17",
3 "Statement": [
4 {
5 "Effect": "Allow",
6 "Action": "sts:AssumeRole",
7 "Principal": {
8 "AWS": "111122223333"
9 },
10 "Condition": {}
11 }
12 ]
13}

The above is an extremely vanilla Trust Policy that allows identities within account 111122223333 the opportunity to assume this role. Likely the trust policy was created on a role within the same account 111122223333 , although you can specify any account there as a Principal.

It’s important to understand that trust policies are separate from the “normal” (identity-based) policies you attach to a role, which give permissions to AWS various services.

Other Policy Types

Finally we have the section of the article where I dump "all those other things" together. It's because they're boring (shocking). They're useful, but they're less in-your-face than the above policy types.

Calling these “security policies” is a bit redundant in this context, but I group these together in my head as having similar-ish use cases:

  • Permission boundaries - You can set the maximum (widest) permissions an identity (Role or User) is allowed to have by setting a permission boundary on the User/Role. These don’t grant permissions but instead define the widest set an identity can have.
  • Organization SCPs - Similar to permission boundaries, but this is called a “service control policies” and are scoped to accounts within an AWS organization’s OU’s. This limits the widest permissions allowed for any User/Role within an account in an organization
  • Organization RCPs - Much less used (and less supported), these are “resource control policies” and they set the widest possible permissions for resources (rather than identities such as users/roles). They’re more like Resource-based policies but are set organization-wide.
  • Access Control Lists - These help you decide which principals can access resources in cross-account situations. For example you may want to ensure that no IAM permissions are created that allow access to principals outside of your AWS organization.

AWS is complex. Sign up for free, useful lessons like this.

Don't miss out

Sign up to learn when new content is released! Courses are in production now.