Summary: In this article we look at why the ability to manage IAM policies as first class objects is important and provide some example code to illustrate resource duplication that may lead to increased and unnecessary risk.

A couple of weeks back, AWS announced Managed Policies for AWS Identity & Access Management. This promotion of IAM policies to first class objects is important as it prevents duplication of resources that not only result in operational inefficiency, but also have the potential to increase security risk.

I believe that the security benefits of using managed policies is the real winner here. IAM policies are used to control the principal's (user, group, role) access to AWS resources and therefore, the impacts of a misconfigured policy can be significant and should be carefully managed. By replacing in-line policies with centrally managed policies, security teams can develop policies that provide the minimum access permissions required for the principal to perform its duty. When a policy needs to be modified, either as a result of a change in required permissions or in response to a new IAM feature, the centralised policy is modified, reviewed and published. The revised policy immediately affects each principal it is associated with.

If you are curious about what impact the new feature could have on your AWS environment, you may find the following snippet of python code helpful. It lists the number of roles associated with IAM policies of a given name and therefore suggests policies that may be duplicated within your environment.

import boto.iam as iam
from collections import defaultdict

# create a connection to the iam service in the sydney region
conn = iam.connect_to_region(region_name='ap-southeast-2')

def print_role_to_policy_mapping(conn, max):
    # get a list of all iam roles
    iam_roles = conn.list_roles(max_items=max)

    # initialise role_list
    role_list = []

    # populate role_list with defined roles
    for role in iam_roles['list_roles_response']['list_roles_result']['roles']:
        role_list.append(role['role_name'])

    # initialise role dictionary
    role_dict = defaultdict(list)

    # create a dictionary of all the in-line policies (by name) associated with each role
    for role in role_list:
        role_policy_list = conn.list_role_policies(role_name=role)['list_role_policies_response']
        ['list_role_policies_result']['policy_names']

        for role_policy in role_policy_list:
            role_dict[role_policy].append(role)

    for key, array in role_dict.iteritems():
        if len(array) == 1:
            print str(len(array)) + " role is associated with a policy named : " + key
        else:
            print str(len(array)) + " roles are associated with a policy named : " + key

print_role_to_policy_mapping(conn,5)

Clearly there are a number of improvements we can make to this simple snippet of code including error handling, pagination and checking the contents of policies rather than just their name. That being said, I have however found it quite helpful to quickly highlight areas for further investigation.