1#!/usr/local/bin/python3.8
2# This file is part of Ansible
3# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
4
5from __future__ import (absolute_import, division, print_function)
6__metaclass__ = type
7
8
9DOCUMENTATION = '''
10---
11module: iam_policy_info
12version_added: 1.0.0
13short_description: Retrieve inline IAM policies for users, groups, and roles
14description:
15     - Supports fetching of inline IAM policies for IAM users, groups and roles.
16options:
17  iam_type:
18    description:
19      - Type of IAM resource you wish to retrieve inline policies for.
20    required: yes
21    choices: [ "user", "group", "role"]
22    type: str
23  iam_name:
24    description:
25      - Name of IAM resource you wish to retrieve inline policies for. In other words, the user name, group name or role name.
26    required: yes
27    type: str
28  policy_name:
29    description:
30      - Name of a specific IAM inline policy you with to retrieve.
31    required: no
32    type: str
33
34author:
35    - Mark Chappell (@tremble)
36
37extends_documentation_fragment:
38- amazon.aws.aws
39- amazon.aws.ec2
40
41'''
42
43EXAMPLES = '''
44- name: Describe all inline IAM policies on an IAM User
45  community.aws.iam_policy_info:
46    iam_type: user
47    iam_name: example_user
48
49- name: Describe a specific inline policy on an IAM Role
50  community.aws.iam_policy_info:
51    iam_type: role
52    iam_name: example_role
53    policy_name: example_policy
54
55'''
56RETURN = '''
57policies:
58    description: A list containing the matching IAM inline policy names and their data
59    returned: success
60    type: complex
61    contains:
62        policy_name:
63            description: The Name of the inline policy
64            returned: success
65            type: str
66        policy_document:
67            description: The JSON document representing the inline IAM policy
68            returned: success
69            type: list
70policy_names:
71    description: A list of matching names of the IAM inline policies on the queried object
72    returned: success
73    type: list
74all_policy_names:
75    description: A list of names of all of the IAM inline policies on the queried object
76    returned: success
77    type: list
78'''
79
80try:
81    import botocore
82except ImportError:
83    pass
84
85from ansible_collections.amazon.aws.plugins.module_utils.core import AnsibleAWSModule
86from ansible_collections.amazon.aws.plugins.module_utils.core import is_boto3_error_code
87from ansible_collections.amazon.aws.plugins.module_utils.ec2 import AWSRetry
88
89
90class Policy:
91
92    def __init__(self, client, name, policy_name):
93        self.client = client
94        self.name = name
95        self.policy_name = policy_name
96        self.changed = False
97
98    @staticmethod
99    def _iam_type():
100        return ''
101
102    def _list(self, name):
103        return {}
104
105    def list(self):
106        return self._list(self.name).get('PolicyNames', [])
107
108    def _get(self, name, policy_name):
109        return '{}'
110
111    def get(self, policy_name):
112        return self._get(self.name, policy_name)['PolicyDocument']
113
114    def get_all(self):
115        policies = list()
116        for policy in self.list():
117            policies.append({"policy_name": policy, "policy_document": self.get(policy)})
118        return policies
119
120    def run(self):
121        policy_list = self.list()
122        ret_val = {
123            'changed': False,
124            self._iam_type() + '_name': self.name,
125            'all_policy_names': policy_list
126        }
127        if self.policy_name is None:
128            ret_val.update(policies=self.get_all())
129            ret_val.update(policy_names=policy_list)
130        elif self.policy_name in policy_list:
131            ret_val.update(policies=[{
132                "policy_name": self.policy_name,
133                "policy_document": self.get(self.policy_name)}])
134            ret_val.update(policy_names=[self.policy_name])
135        return ret_val
136
137
138class UserPolicy(Policy):
139
140    @staticmethod
141    def _iam_type():
142        return 'user'
143
144    def _list(self, name):
145        return self.client.list_user_policies(aws_retry=True, UserName=name)
146
147    def _get(self, name, policy_name):
148        return self.client.get_user_policy(aws_retry=True, UserName=name, PolicyName=policy_name)
149
150
151class RolePolicy(Policy):
152
153    @staticmethod
154    def _iam_type():
155        return 'role'
156
157    def _list(self, name):
158        return self.client.list_role_policies(aws_retry=True, RoleName=name)
159
160    def _get(self, name, policy_name):
161        return self.client.get_role_policy(aws_retry=True, RoleName=name, PolicyName=policy_name)
162
163
164class GroupPolicy(Policy):
165
166    @staticmethod
167    def _iam_type():
168        return 'group'
169
170    def _list(self, name):
171        return self.client.list_group_policies(aws_retry=True, GroupName=name)
172
173    def _get(self, name, policy_name):
174        return self.client.get_group_policy(aws_retry=True, GroupName=name, PolicyName=policy_name)
175
176
177def main():
178    argument_spec = dict(
179        iam_type=dict(required=True, choices=['user', 'group', 'role']),
180        iam_name=dict(required=True),
181        policy_name=dict(default=None, required=False),
182    )
183
184    module = AnsibleAWSModule(argument_spec=argument_spec, supports_check_mode=True)
185
186    args = dict(
187        client=module.client('iam', retry_decorator=AWSRetry.jittered_backoff()),
188        name=module.params.get('iam_name'),
189        policy_name=module.params.get('policy_name'),
190    )
191    iam_type = module.params.get('iam_type')
192
193    try:
194        if iam_type == 'user':
195            policy = UserPolicy(**args)
196        elif iam_type == 'role':
197            policy = RolePolicy(**args)
198        elif iam_type == 'group':
199            policy = GroupPolicy(**args)
200
201        module.exit_json(**(policy.run()))
202    except is_boto3_error_code('NoSuchEntity') as e:
203        module.exit_json(changed=False, msg=e.response['Error']['Message'])
204    except (botocore.exceptions.ClientError, botocore.exceptions.BotoCoreError) as e:  # pylint: disable=duplicate-except
205        module.fail_json_aws(e)
206
207
208if __name__ == '__main__':
209    main()
210