Create and Manage AWS IAM Policy Using Ansible
Tags: aws, ansible, cloud, automation
Published: 2022-03-30 08:13:20

Since Part 2, I've mentioned policies such as what's the best practice to attach policy and more. Now, we will discuss Identity-based policy which consists of 2 categories: Managed policy and Inline policy.

The managed policy is divided into 2 categories: AWS-managed policy and customer-managed policy. AWS-managed policy is a policy that AWS already provided and a customer-managed policy is the custom policy created by the customer itself based on what they need and it will be created as the new policy as well as the name.

Inline policy is a policy that you attach directly to an identity. It's a 1:1 trust relationship. When you delete the user, the inline policy will go along with it. This is not the best practice but here I'll just show you that we can do it with ansible.

For the IAM Inline Policy, we use `community.aws.iam_policy` module. For the IAM Managed Policy, we use `community.aws.iam_managed_policy` module.

## Inline Policy

Create JSON file:

```json
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "iam:ListUsers",
                "iam:ListRoles"
            ],
            "Resource": "*"
        }
    ]
}
```

Task:

```yaml
    - name: create inline policy
      community.aws.iam_policy:
        iam_type: user
        iam_name: "{{ item.user }}"
        policy_name: "{{ item.policy }}"
        state: present
        policy_json: "{{ item.template }}"
      loop: 
        - { user: "{{ user6 }}", policy: IAMListUsers_Roles, template: "{{ lookup('template', 'inline_policy.json.j2') }}" }
      tags:
        - iam_policy_new_inline
```

Before we run the playbook, we need an IAM user to be used. I'll create one more along with the access key. Here are the updated tasks:

```yaml
    - name: create user
      community.aws.iam_user:
        name: "{{ item }}"
        state: present
      loop: 
#        - "{{ user1 }}"
#        - "{{ user2 }}"
#        - "{{ user3 }}"
#        - "{{ user4 }}"
        - "{{ user6 }}"
      tags:
        - iam_user_new ### create user + pass + key
        - iam_user_only ### create user only
        - iam_user_key ### create user + key
        - iam_user_pass ### create user + pass

    - name: create user's key
      community.aws.iam_access_key:
        user_name: "{{ item }}"
        state: present
      loop: 
#        - { name: "{{ user1 }}" }
#        - { name: "{{ user2 }}" }
#        - { name: "{{ user3 }}" }
#        - { name: "{{ user4 }}" }
#        - { name: "{{ user5 }}" }
        - { name: "{{ user6 }}" }
      tags:
        - iam_user_new
        - iam_user_key
        - iam_user_key_only
```

```bash
$ ansible-playbook -i host.yml iam.yml -t iam_user_key

PLAY [iam] *********************************************************************

TASK [create user] *************************************************************
changed: [localhost] => (item=name6)

TASK [create user's key] *******************************************************
changed: [localhost] => (item={'name': 'name6'})
```

Then, now we're ready to run the playbook:

```bash
$ ansible-playbook -i host.yml iam.yml -t iam_policy_new_inline

PLAY [iam] *********************************************************************

TASK [create inline policy] ****************************************************
changed: [localhost] => (item={'user': 'name6', 'policy': 'IAMListUsers_Roles', 'template': {'Version': '2012-10-17', 'Statement': [{'Effect': 'Allow', 'Action': ['iam:ListUsers', 'iam:ListRoles'], 'Resource': '*'}]}})
```

Check if the policy works! (Please set up the new user as a secondary IAM user first by running `aws configure --profile name6` on the localhost. The access key and secret access key are stored in the file named `key_list.txt`)

```bash
$ aws iam list-users --profile name6 | grep UserName
            "UserName": "name5",
            "UserName": "name3",
            "UserName": "name6",
            "UserName": "name1",
            "UserName": "name4",
            "UserName": "name2",

$ aws iam list-roles --profile name6 | grep RoleName
            "RoleName": "aws-ec2-spot-fleet-tagging-role",
            "RoleName": "AWSServiceRoleForAmazonElasticFileSystem",
            "RoleName": "AWSServiceRoleForSupport",
            "RoleName": "AWSServiceRoleForTrustedAdvisor",
            "RoleName": "EC2DemoRole",
            "RoleName": "IAM",
            "RoleName": "IAM_Policy",

$ aws iam list-groups --profile name6

An error occurred (AccessDenied) when calling the ListGroups operation: User: arn:aws:iam::0123456789:user/name6 is not authorized to perform: iam:ListGroups on resource: arn:aws:iam::0123456789:group/
```

As we can see, user `name6` is only allowed to list users and roles as mentioned in the inline policy document.

## Managed Policy

Create JSON file:

```json
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": "iam:GetUser",
            "Resource": "*"
        }
    ]
}
```

Task:

```yaml
    - name: create managed policy
      community.aws.iam_managed_policy:
        policy_name: "{{ item.name }}"
        policy: "{{ item.policy }}"
        state: present
      loop: 
        - { name: IAMGetUser_Only, policy: "{{ lookup('template', 'managed_policy.json.j2') }}" }
      tags:
        - iam_policy_new_managed
```

Run the playbook:

```bash
$ ansible-playbook -i host.yml iam.yml -t iam_policy_new_managed

PLAY [iam] *********************************************************************

TASK [create managed policy] ***************************************************
changed: [localhost] => (item={'name': 'IAMGetUser_Only', 'policy': {'Version': '2012-10-17', 'Statement': [{'Effect': 'Allow', 'Action': 'iam:GetUser', 'Resource': '*'}]}})
```

The task above only creates a managed policy. To attach it to an IAM group and user, I'll use the same task as before. I just need to add new value and comment on the existing one, should look like this:

```yaml
    - name: create group and add existing users as members
      community.aws.iam_group:
        name: "{{ item.name }}"
        state: present
        users: "{{ item.members }}"
      loop: 
#        - { name: "{{ group1 }}", members: ["{{ user1 }}","{{ user2 }}"] }
        - { name: "{{ group1 }}", members: "{{ user4 }}" }
      tags:
        - iam_group_new_members

    - name: create a user and attach a managed policy
      community.aws.iam_user:
        name: "{{ item.name }}"
        managed_policies: "{{ item.policy }}"
        state: present
      loop: 
#        - { name: "{{ user5 }}", policy: arn:aws:iam::aws:policy/IAMFullAccess }
        - { name: "{{ user3 }}", policy: arn:aws:iam::0123456789:policy/IAMGetUser_Only } 
      tags:
        - iam_user_new_policy

    - name: create group + attach managed policy
      community.aws.iam_group:
        name: "{{ item.name }}"
        managed_policies: "{{ item.policy }}"
        state: present
      loop: 
#        - { name: "{{ group2 }}", policy: arn:aws:iam::aws:policy/IAMReadOnlyAccess }
        - { name: "{{ group1 }}", policy: arn:aws:iam::0123456789:policy/IAMGetUser_Only }
      tags:
        - iam_group_new_policy
```

Then, I'll run the existing playbook with multiple tags.

```bash
$ ansible-playbook -i host.yml iam.yml -t "iam_user_new_policy, iam_group_new_policy, iam_group_new_members"

PLAY [iam] *********************************************************************

TASK [create group and add existing users as members] **************************
changed: [localhost] => (item={'name': 'developer', 'members': 'name4'})

TASK [create a user and attach a managed policy] *******************************
changed: [localhost] => (item={'name': 'name3', 'policy': 'arn:aws:iam::0123456789:policy/IAMGetUser_Only'})

TASK [create group + attach managed policy] ************************************
changed: [localhost] => (item={'name': 'developer', 'policy': 'arn:aws:iam::0123456789:policy/IAMGetUser_Only'})
```

The tasks above attach policy directly to user `name3` and to group developer, also add user `name4` into the group. So, the user `name3` and all developer group members have the same policy that is `IAMGetUser_Only`. Check if the policy works:

```bash
$ aws iam get-user --user-name name1 --profile name3 | grep UserName
        "UserName": "name1",

$ aws iam get-user --user-name name1 --profile name4 | grep UserName
        "UserName": "name1",

$ aws iam list-users --profile name3

An error occurred (AccessDenied) when calling the ListUsers operation: User: arn:aws:iam::0123456789:user/name3 is not authorized to perform: iam:ListUsers on resource: arn:aws:iam::0123456789:user/

$ aws iam list-users --profile name4

An error occurred (AccessDenied) when calling the ListUsers operation: User: arn:aws:iam::0123456789:user/name4 is not authorized to perform: iam:ListUsers on resource: arn:aws:iam::0123456789:user/
```

As we can see, the users can get operation but not for the list.

So, we already reached the end of the IAM section. In the next part, we will delete all the things we just created from Part 2 till Part 5. It's optional but in case you need it, I'll show you. Let's move to the last part of this series!

**References:**

* [https://docs.ansible.com/ansible/latest/collections/community/aws/iam\_policy\_module.html](https://docs.ansible.com/ansible/latest/collections/community/aws/iam_policy_module.html)
    
* [https://docs.ansible.com/ansible/latest/collections/community/aws/iam\_managed\_policy\_module.html](https://docs.ansible.com/ansible/latest/collections/community/aws/iam_managed_policy_module.html)