AWS IAM "ReadOnlyAccess" Managed Policy Is Too Permissive (For Us)

taking away some read-only permissions that Amazon allows

Amazon has created an IAM Managed Policy named ReadOnlyAccess, which grants read-only access to active resources on most AWS services. At Campus Explorer, we depend on this convenient managed policy for our read-only roles.

However, our use of “read-only” doesn’t quite match up with the choices that Amazon made when creating this policy. This isn’t to say that Amazon is wrong, just that our concept of “read-only” differs slightly from Amazon’s.

The biggest difference is that we want our read-only roles to be able to see the architecture of our AWS systems and what resources are active, but we would prefer that the role not be able to read sensitive data from DynamoDB, S3, Kinesis, SQS queue messages, CloudFormation template parameters, and the like.

For example, third party services often ask for a ReadOnlyAccess role to allow them to analyze your AWS account and provide helpful tips on how to improve security or cost control. This sounds good, but do you really want them to be reading messages from Kinesis streams and SQS queues or downloading the contents of S3 objects?

To better protect our data when creating read-only roles, we not only attach the ReadOnlyAccess managed policy from Amazon, but we also attach our own DenyData managed policy that uses Deny statements to take away a number of the previously allowed permissions.


These are our business’ current rules as compiled by sysadmin extraordinaire, Jennine Townsend. Your business needs may differ. Feel free to tighten or loosen as you see fit. The goal here is to let Amazon do most of the work in managing the ReadOnlyAccess policy, but tweak it a bit to fit your particular situation.

Using the aws-cli, you can create a supplemental managed policy with a command like:

policy_arn=$(aws iam create-policy \
  --policy-name "$policy_name" \
  --description 'Use in combination with Amazon managed ReadOnlyAccess policy.' \
  --policy-document '{
      "Version": "2012-10-17",
      "Statement": [
              "Sid": "DenyData",
              "Effect": "Deny",
              "Action": [
              "Resource": "*"
  }' \
  --output text \
  --query 'Policy.Arn')
echo policy_arn=$policy_arn

Attach the managed policy to your existing role, group, or user.

# role
role_name="readonly" # Replace with your role name
aws iam attach-role-policy \
  --role-name "$role_name" \
  --policy-arn "$policy_arn"

# group
group_name="readonly" # Replace with your group name
aws iam attach-user-policy \
  --group-name "$group_name" \
  --policy-arn "$policy_arn" 

# user
user_name="bilbo" # Replace with your user name
aws iam attach-user-policy \
  --user-name "$user_name" \
  --policy-arn "$policy_arn"


If you created the above managed policy and wish to remove it, then detach from any users, groups, roles you had attached it to:

aws iam detach-role-policy \
  --role-name "$role_name" \
  --policy-arn "$policy_arn"

aws iam detach-user-policy \
  --gropu-name "$group_name" \
  --policy-arn "$policy_arn" 

aws iam detach-user-policy \
  --user-name "$user_name" \
  --policy-arn "$policy_arn"

and delete the managed policy itself:

aws iam delete-policy \
  --policy-arn "$policy_arn"

[Update 2018-03-21: Add in Jennine’s latest DenyData specifications and simplify policy name.]