New module: Add module to collect Amazon AWS IAM user info (cloud/amazon/iam_user_info) (#23382)
* AWS: new module iam_user_info Signed-off-by: psharkey <psharkey@cleo.com> Signed-off-by: Abhijeet Kasurde <akasurde@redhat.com> Rename from iam_user_facts to iam_user_info. Rename and target 2.10. Fixing docs. * Adding iam_user_info integration test. Removing unnecessary tasks. Fixing yamllint failure test/integration/targets/iam_user_info/defaults/main.yml:5:1: empty-lines: too many blank lines (1 > 0). * name paramter is optional * Switch to use AnsibleAWSModule. * Convert to using fail_json_aws * Rework asserts to inspect ARN. * Move integration tests from iam_user_info to iam_user. * Fix pep8 problems. * ec2_argument_spec not needed with AnsibleAWSModule. * Switch to use helper in AnsibleAWSModule. * Add iam_user_info to the aws group. * Add support for pagination and backoff. * Check improper parameter usage first. * Adding test cases for multiple users. * Rmoving unneeded line. * Remove unneeded imports. * Switch to catch BotoCoreError. * Adding tests for exception coverage. * Compare user info directly with values from created user.
This commit is contained in:
parent
334d2ce764
commit
ee91714eb2
6 changed files with 428 additions and 0 deletions
|
@ -412,6 +412,8 @@ groupings:
|
|||
- aws
|
||||
iam_user:
|
||||
- aws
|
||||
iam_user_info:
|
||||
- aws
|
||||
kinesis_stream:
|
||||
- aws
|
||||
lambda:
|
||||
|
|
185
lib/ansible/modules/cloud/amazon/iam_user_info.py
Normal file
185
lib/ansible/modules/cloud/amazon/iam_user_info.py
Normal file
|
@ -0,0 +1,185 @@
|
|||
#!/usr/bin/python
|
||||
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright: (c) 2018, Ansible Project
|
||||
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
|
||||
from __future__ import absolute_import, division, print_function
|
||||
__metaclass__ = type
|
||||
|
||||
|
||||
ANSIBLE_METADATA = {
|
||||
'metadata_version': '1.1',
|
||||
'status': ['preview'],
|
||||
'supported_by': 'community'
|
||||
}
|
||||
|
||||
|
||||
DOCUMENTATION = '''
|
||||
---
|
||||
module: iam_user_info
|
||||
short_description: Gather IAM user(s) facts in AWS
|
||||
description:
|
||||
- This module can be used to gather IAM user(s) facts in AWS.
|
||||
version_added: "2.10"
|
||||
author:
|
||||
- Constantin Bugneac (@Constantin07)
|
||||
- Abhijeet Kasurde (@Akasurde)
|
||||
options:
|
||||
name:
|
||||
description:
|
||||
- The name of the IAM user to look for.
|
||||
required: false
|
||||
type: str
|
||||
group:
|
||||
description:
|
||||
- The group name name of the IAM user to look for. Mutually exclusive with C(path).
|
||||
required: false
|
||||
type: str
|
||||
path:
|
||||
description:
|
||||
- The path to the IAM user. Mutually exclusive with C(group).
|
||||
- If specified, then would get all user names whose path starts with user provided value.
|
||||
required: false
|
||||
default: '/'
|
||||
type: str
|
||||
requirements:
|
||||
- botocore
|
||||
- boto3
|
||||
extends_documentation_fragment:
|
||||
- aws
|
||||
- ec2
|
||||
'''
|
||||
|
||||
EXAMPLES = r'''
|
||||
# Note: These examples do not set authentication details, see the AWS Guide for details.
|
||||
# Gather facts about "test" user.
|
||||
- name: Get IAM user facts
|
||||
iam_user_info:
|
||||
name: "test"
|
||||
|
||||
# Gather facts about all users in the "dev" group.
|
||||
- name: Get IAM user facts
|
||||
iam_user_info:
|
||||
group: "dev"
|
||||
|
||||
# Gather facts about all users with "/division_abc/subdivision_xyz/" path.
|
||||
- name: Get IAM user facts
|
||||
iam_user_info:
|
||||
path: "/division_abc/subdivision_xyz/"
|
||||
'''
|
||||
|
||||
RETURN = r'''
|
||||
iam_users:
|
||||
description: list of maching iam users
|
||||
returned: success
|
||||
type: complex
|
||||
contains:
|
||||
arn:
|
||||
description: the ARN of the user
|
||||
returned: if user exists
|
||||
type: str
|
||||
sample: "arn:aws:iam::156360693172:user/dev/test_user"
|
||||
create_date:
|
||||
description: the datetime user was created
|
||||
returned: if user exists
|
||||
type: str
|
||||
sample: "2016-05-24T12:24:59+00:00"
|
||||
password_last_used:
|
||||
description: the last datetime the password was used by user
|
||||
returned: if password was used at least once
|
||||
type: str
|
||||
sample: "2016-05-25T13:39:11+00:00"
|
||||
path:
|
||||
description: the path to user
|
||||
returned: if user exists
|
||||
type: str
|
||||
sample: "/dev/"
|
||||
user_id:
|
||||
description: the unique user id
|
||||
returned: if user exists
|
||||
type: str
|
||||
sample: "AIDUIOOCQKTUGI6QJLGH2"
|
||||
user_name:
|
||||
description: the user name
|
||||
returned: if user exists
|
||||
type: str
|
||||
sample: "test_user"
|
||||
'''
|
||||
|
||||
from ansible.module_utils.aws.core import AnsibleAWSModule
|
||||
from ansible.module_utils.ec2 import camel_dict_to_snake_dict, AWSRetry
|
||||
|
||||
try:
|
||||
import botocore
|
||||
from botocore.exceptions import BotoCoreError, ClientError
|
||||
except ImportError:
|
||||
pass # caught by AnsibleAWSModule
|
||||
|
||||
|
||||
@AWSRetry.exponential_backoff()
|
||||
def list_iam_users_with_backoff(client, operation, **kwargs):
|
||||
paginator = client.get_paginator(operation)
|
||||
return paginator.paginate(**kwargs).build_full_result()
|
||||
|
||||
|
||||
def list_iam_users(connection, module):
|
||||
|
||||
name = module.params.get('name')
|
||||
group = module.params.get('group')
|
||||
path = module.params.get('path')
|
||||
|
||||
params = dict()
|
||||
iam_users = []
|
||||
|
||||
if not group and not path:
|
||||
if name:
|
||||
params['UserName'] = name
|
||||
try:
|
||||
iam_users.append(connection.get_user(**params)['User'])
|
||||
except (ClientError, BotoCoreError) as e:
|
||||
module.fail_json_aws(e, msg="Couldn't get IAM user info for user %s" % name)
|
||||
|
||||
if group:
|
||||
params['GroupName'] = group
|
||||
try:
|
||||
iam_users = list_iam_users_with_backoff(connection, 'get_group', **params)['Users']
|
||||
except (ClientError, BotoCoreError) as e:
|
||||
module.fail_json_aws(e, msg="Couldn't get IAM user info for group %s" % group)
|
||||
if name:
|
||||
iam_users = [user for user in iam_users if user['UserName'] == name]
|
||||
|
||||
if path and not group:
|
||||
params['PathPrefix'] = path
|
||||
try:
|
||||
iam_users = list_iam_users_with_backoff(connection, 'list_users', **params)['Users']
|
||||
except (ClientError, BotoCoreError) as e:
|
||||
module.fail_json_aws(e, msg="Couldn't get IAM user info for path %s" % path)
|
||||
if name:
|
||||
iam_users = [user for user in iam_users if user['UserName'] == name]
|
||||
|
||||
module.exit_json(iam_users=[camel_dict_to_snake_dict(user) for user in iam_users])
|
||||
|
||||
|
||||
def main():
|
||||
argument_spec = dict(
|
||||
name=dict(),
|
||||
group=dict(),
|
||||
path=dict(default='/')
|
||||
)
|
||||
|
||||
module = AnsibleAWSModule(
|
||||
argument_spec=argument_spec,
|
||||
mutually_exclusive=[
|
||||
['group', 'path']
|
||||
],
|
||||
supports_check_mode=True
|
||||
)
|
||||
|
||||
connection = module.client('iam')
|
||||
|
||||
list_iam_users(connection, module)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
3
test/integration/targets/iam_user/aliases
Normal file
3
test/integration/targets/iam_user/aliases
Normal file
|
@ -0,0 +1,3 @@
|
|||
cloud/aws
|
||||
iam_user_info
|
||||
unsupported
|
7
test/integration/targets/iam_user/defaults/main.yml
Normal file
7
test/integration/targets/iam_user/defaults/main.yml
Normal file
|
@ -0,0 +1,7 @@
|
|||
---
|
||||
test_group: '{{ resource_prefix }}-group'
|
||||
test_path: '/'
|
||||
test_user: '{{ test_users[0] }}'
|
||||
test_users:
|
||||
- '{{ resource_prefix }}-user-a'
|
||||
- '{{ resource_prefix }}-user-b'
|
3
test/integration/targets/iam_user/meta/main.yml
Normal file
3
test/integration/targets/iam_user/meta/main.yml
Normal file
|
@ -0,0 +1,3 @@
|
|||
dependencies:
|
||||
- prepare_tests
|
||||
- setup_ec2
|
228
test/integration/targets/iam_user/tasks/main.yml
Normal file
228
test/integration/targets/iam_user/tasks/main.yml
Normal file
|
@ -0,0 +1,228 @@
|
|||
---
|
||||
- name: set up aws connection info
|
||||
module_defaults:
|
||||
group/aws:
|
||||
aws_access_key: "{{ aws_access_key }}"
|
||||
aws_secret_key: "{{ aws_secret_key }}"
|
||||
security_token: "{{ security_token | default(omit) }}"
|
||||
region: "{{ aws_region }}"
|
||||
block:
|
||||
|
||||
- name: ensure improper usage of parameters fails gracefully
|
||||
iam_user_info:
|
||||
path: '{{ test_path }}'
|
||||
group: '{{ test_group }}'
|
||||
ignore_errors: yes
|
||||
register: iam_user_info_path_group
|
||||
- assert:
|
||||
that:
|
||||
- iam_user_info_path_group is failed
|
||||
- 'iam_user_info_path_group.msg == "parameters are mutually exclusive: group|path"'
|
||||
|
||||
- name: ensure exception handling fails as expected
|
||||
iam_user_info:
|
||||
region: 'bogus'
|
||||
path: ''
|
||||
ignore_errors: yes
|
||||
register: iam_user_info
|
||||
- assert:
|
||||
that:
|
||||
- iam_user_info is failed
|
||||
- '"user" in iam_user_info.msg'
|
||||
|
||||
- name: ensure exception handling fails as expected with group
|
||||
iam_user_info:
|
||||
region: 'bogus'
|
||||
group: '{{ test_group }}'
|
||||
ignore_errors: yes
|
||||
register: iam_user_info
|
||||
- assert:
|
||||
that:
|
||||
- iam_user_info is failed
|
||||
- '"group" in iam_user_info.msg'
|
||||
|
||||
- name: ensure exception handling fails as expected with default path
|
||||
iam_user_info:
|
||||
region: 'bogus'
|
||||
ignore_errors: yes
|
||||
register: iam_user_info
|
||||
- assert:
|
||||
that:
|
||||
- iam_user_info is failed
|
||||
- '"path" in iam_user_info.msg'
|
||||
|
||||
- name: ensure ansible user exists
|
||||
iam_user:
|
||||
name: '{{ test_user }}'
|
||||
state: present
|
||||
register: iam_user
|
||||
|
||||
- name: ensure the info used to validate other tests is valid
|
||||
set_fact:
|
||||
test_iam_user: '{{ iam_user.iam_user.user }}'
|
||||
- assert:
|
||||
that:
|
||||
- 'test_iam_user.arn.startswith("arn:aws:iam")'
|
||||
- 'test_iam_user.arn.endswith("user/" + test_user )'
|
||||
- test_iam_user.create_date is not none
|
||||
- test_iam_user.path == '{{ test_path }}'
|
||||
- test_iam_user.user_id is not none
|
||||
- test_iam_user.user_name == '{{ test_user }}'
|
||||
|
||||
- name: get info on IAM user(s)
|
||||
iam_user_info:
|
||||
register: iam_user_info
|
||||
- assert:
|
||||
that:
|
||||
- iam_user_info.iam_users | length != 0
|
||||
|
||||
- name: get info on IAM user(s) with name
|
||||
iam_user_info:
|
||||
name: '{{ test_user }}'
|
||||
register: iam_user_info
|
||||
- debug: var=iam_user_info
|
||||
- assert:
|
||||
that:
|
||||
- iam_user_info.iam_users | length == 1
|
||||
- iam_user_info.iam_users[0].arn == test_iam_user.arn
|
||||
- iam_user_info.iam_users[0].create_date == test_iam_user.create_date
|
||||
- iam_user_info.iam_users[0].path == test_iam_user.path
|
||||
- iam_user_info.iam_users[0].user_id == test_iam_user.user_id
|
||||
- iam_user_info.iam_users[0].user_name == test_iam_user.user_name
|
||||
|
||||
- name: get info on IAM user(s) on path
|
||||
iam_user_info:
|
||||
path: '{{ test_path }}'
|
||||
name: '{{ test_user }}'
|
||||
register: iam_user_info
|
||||
- assert:
|
||||
that:
|
||||
- iam_user_info.iam_users | length == 1
|
||||
- iam_user_info.iam_users[0].arn == test_iam_user.arn
|
||||
- iam_user_info.iam_users[0].create_date == test_iam_user.create_date
|
||||
- iam_user_info.iam_users[0].path == test_iam_user.path
|
||||
- iam_user_info.iam_users[0].user_id == test_iam_user.user_id
|
||||
- iam_user_info.iam_users[0].user_name == test_iam_user.user_name
|
||||
|
||||
- name: ensure group exists
|
||||
iam_group:
|
||||
name: '{{ test_group }}'
|
||||
users:
|
||||
- '{{ test_user }}'
|
||||
state: present
|
||||
register: iam_group
|
||||
|
||||
- name: get info on IAM user(s) in group
|
||||
iam_user_info:
|
||||
group: '{{ test_group }}'
|
||||
name: '{{ test_user }}'
|
||||
register: iam_user_info
|
||||
- assert:
|
||||
that:
|
||||
- iam_user_info.iam_users | length == 1
|
||||
- iam_user_info.iam_users[0].arn == test_iam_user.arn
|
||||
- iam_user_info.iam_users[0].create_date == test_iam_user.create_date
|
||||
- iam_user_info.iam_users[0].path == test_iam_user.path
|
||||
- iam_user_info.iam_users[0].user_id == test_iam_user.user_id
|
||||
- iam_user_info.iam_users[0].user_name == test_iam_user.user_name
|
||||
|
||||
- name: remove user from group
|
||||
iam_group:
|
||||
name: '{{ test_group }}'
|
||||
purge_users: True
|
||||
users: []
|
||||
state: present
|
||||
register: iam_group
|
||||
|
||||
- name: get info on IAM user(s) after removing from group
|
||||
iam_user_info:
|
||||
group: '{{ test_group }}'
|
||||
name: '{{ test_user }}'
|
||||
register: iam_user_info
|
||||
|
||||
- name: assert empty list of users for group are returned
|
||||
assert:
|
||||
that:
|
||||
- iam_user_info.iam_users | length == 0
|
||||
|
||||
- name: ensure ansible users exist
|
||||
iam_user:
|
||||
name: '{{ item }}'
|
||||
state: present
|
||||
with_items: '{{ test_users }}'
|
||||
|
||||
- name: get info on multiple IAM user(s)
|
||||
iam_user_info:
|
||||
register: iam_user_info
|
||||
- assert:
|
||||
that:
|
||||
- iam_user_info.iam_users | length != 0
|
||||
|
||||
- name: ensure multiple user group exists with single user
|
||||
iam_group:
|
||||
name: '{{ test_group }}'
|
||||
users:
|
||||
- '{{ test_user }}'
|
||||
state: present
|
||||
register: iam_group
|
||||
|
||||
- name: get info on IAM user(s) in group
|
||||
iam_user_info:
|
||||
group: '{{ test_group }}'
|
||||
register: iam_user_info
|
||||
- assert:
|
||||
that:
|
||||
- iam_user_info.iam_users | length == 1
|
||||
|
||||
- name: add all users to group
|
||||
iam_group:
|
||||
name: '{{ test_group }}'
|
||||
users: '{{ test_users }}'
|
||||
state: present
|
||||
register: iam_group
|
||||
|
||||
- name: get info on multiple IAM user(s) in group
|
||||
iam_user_info:
|
||||
group: '{{ test_group }}'
|
||||
register: iam_user_info
|
||||
- assert:
|
||||
that:
|
||||
- iam_user_info.iam_users | length == test_users | length
|
||||
|
||||
- name: purge users from group
|
||||
iam_group:
|
||||
name: '{{ test_group }}'
|
||||
purge_users: True
|
||||
users: []
|
||||
state: present
|
||||
register: iam_group
|
||||
|
||||
- name: ensure info is empty for empty group
|
||||
iam_user_info:
|
||||
group: '{{ test_group }}'
|
||||
register: iam_user_info
|
||||
- assert:
|
||||
that:
|
||||
- iam_user_info.iam_users | length == 0
|
||||
|
||||
- name: get info on IAM user(s) after removing from group
|
||||
iam_user_info:
|
||||
group: '{{ test_group }}'
|
||||
register: iam_user_info
|
||||
|
||||
- name: assert empty list of users for group are returned
|
||||
assert:
|
||||
that:
|
||||
- iam_user_info.iam_users | length == 0
|
||||
|
||||
always:
|
||||
- name: remove group
|
||||
iam_group:
|
||||
name: '{{ test_group }}'
|
||||
state: absent
|
||||
|
||||
- name: remove ansible users
|
||||
iam_user:
|
||||
name: '{{ item }}'
|
||||
state: absent
|
||||
with_items: '{{ test_users }}'
|
Loading…
Reference in a new issue