Permissions Boundaries

There is one slide in the “Security Engineering on AWS” course about Permissions Boundaries. The text is as follows:

A permissions boundary is an advanced feature in which you limit the maximum permissions that a principal can have. These boundaries can be applied to AWS Organizations organizations or to IAM users or roles.

As the IAM administrator, you can define one or more permissions boundaries using managed policies and allow your employee to create a principal with this boundary. The employee can then attach a permissions policy to this principal. However, the effective permissions of the principal are the intersection of the permissions boundary and permissions policy. As a result, the new principal cannot exceed the boundary that you defined.

When you use a policy to set the permissions boundary for a user, it limits the user’s permissions but does not provide permissions on its own.

It might be a challenge to understand the use case and how it is used in practice. There is a helpful blog post here.

Say you have a development team with full access to certain services, but no access to IAM.

Say you want the developer to assign a role to EC2, and you want them to create the role, a policy, attach the policy to the role, and attach the role to an EC2 instance. That is a lot of permissions, and instead we want to keep to the principal of least privilege.

The example in the blog post is lengthy to set up, so I simplify a bit here, and omit the JSON for brevity (see the referenced blog post above for the detailed steps)

The admin does the following tasks:

First the admin creates a boundary policy “DynamoDB_Boundary_Frankfurt“. The policy will allow put, update, delete on any table in the Frankfurt region.. Employees will be required to set this policy as the permission boundary for the roles they create.

Create “Employee_Policy”. This policy will allow the employee to create roles and policies, attach policies to roles, and attach roles to resources. However the admin wants to retain control over the naming conventions of the roles and policies for ease of auditing. The roles and policies must have the prefix MyTestApp. Also, there will be a condition that the above permissions boundary policy must be attached to the role, otherwise the role creation will fail.

Create a role “MyEmployeeRole” and attach the “Employee_Policy”.

Create a policy “Pass_Role_Policy” to allow the employee to pass the roles they create to services such as EC2.

Attach the policy to “MyEmployee_Role”

The Employee does the following tasks:

Create a role “MyTestAppRole”. The employee must provide the permissions boundary “DynamoDB_Boundary_Frankfurt”, otherwise the role creation will fail. The policy will allow EC2 to assume the role.

Create a policy “MyTestApp_DDB_Permissions” allowing all DynamoDB actions on a specific table MyTestApp_DDB_Table.

Attach the policy to “MyTestAppRole”

To summarise, the administrator created a permission boundary allowing DynamoDB put, update, delete on all resources in Frankfurt.

The employee created a policy which allowed all actions on a specific table with no region restriction.

The effective permission is to allow put, update, and delete on the specific table MyTestApp_DDB_Table in the Frankfurt region.


IAM Policies with MFA Conditions

For certain actions like stop or terminate an instance, you might require MFA, while for less potentially destructive actions like describe-instance, you might not require MFA.

For a simple test of this, I used this policy:

    "Version": "2012-10-17",
    "Statement": [
            "Sid": "VisualEditor0",
            "Effect": "Allow",
            "Action": [
            "Resource": "*",
            "Condition": {
                "Bool": {
                    "aws:MultiFactorAuthPresent": "true"
            "Sid": "VisualEditor1",
            "Effect": "Allow",
            "Action": "ec2:DescribeInstances",
            "Resource": "*"

I made 2 users, UserMFA and UserNoMFA, both with console access.

For UserMFA, I enabled MFA using Google Authenticator for iPhone. If you havn’t done this before, create a user, then go to the  Security Credentials tab and you are guided through the process, which involves pointing the iPhone camera at a QR code and entering the two 6-digit codes which appear on the iPhone app.

Attach the policy to the two users.

Log in as UserMFA. The user can see the EC2 instances because they have describe-instance permission, and can stop an instance because they are logged in using MFA.

Log in as UserNoMFA. The user can see the EC2 instances because they have describe-instance permission, but cannot stop an instance because they are not logged in using MFA. They receive the following error:

The same concept can apply to use of CLI and APIs, rather than the console, but that’s another story.


IAM Policy Simulator

Lets say a user sees an Access Denied message when trying to access a resource, for example S3.

This is likely the result of a policy applied to the user, or the users group, or it could be a bucket policy. Or it could be that the user or group has no explicit permissions at all.

One way to troubleshoot the issue is to use the IAM Policy Simulator.

I am already logged in as an admin, and it displays a list of users in my account.

Select the user, and simulate an action, here I select S3 as the service.

For the actions, I select “All Actions” (you can also choose a specific action on a specific bucket) and click Run Simulation.

It will list which actions are allowed or denied. You can drill down on any denied action and it will display the policy and even the section of the policy which is denying access.

In this case the user was in a group with the “AdministratorAccess” policy, but the above policy applied to the user was overriding the Allow.