1#!/usr/bin/python 2# Copyright: Ansible Project 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 9ANSIBLE_METADATA = {'metadata_version': '1.1', 10 'status': ['stableinterface'], 11 'supported_by': 'community'} 12 13 14DOCUMENTATION = ''' 15--- 16module: iam 17short_description: Manage IAM users, groups, roles and keys 18description: 19 - Allows for the management of IAM users, user API keys, groups, roles. 20version_added: "2.0" 21options: 22 iam_type: 23 description: 24 - Type of IAM resource 25 choices: ["user", "group", "role"] 26 type: str 27 name: 28 description: 29 - Name of IAM resource to create or identify 30 required: true 31 type: str 32 new_name: 33 description: 34 - When state is update, will replace name with new_name on IAM resource 35 type: str 36 new_path: 37 description: 38 - When state is update, will replace the path with new_path on the IAM resource 39 type: str 40 state: 41 description: 42 - Whether to create, delete or update the IAM resource. Note, roles cannot be updated. 43 required: true 44 choices: [ "present", "absent", "update" ] 45 type: str 46 path: 47 description: 48 - When creating or updating, specify the desired path of the resource. If state is present, 49 it will replace the current path to match what is passed in when they do not match. 50 default: "/" 51 type: str 52 trust_policy: 53 description: 54 - The inline (JSON or YAML) trust policy document that grants an entity permission to assume the role. Mutually exclusive with C(trust_policy_filepath). 55 version_added: "2.2" 56 type: dict 57 trust_policy_filepath: 58 description: 59 - The path to the trust policy document that grants an entity permission to assume the role. Mutually exclusive with C(trust_policy). 60 version_added: "2.2" 61 type: str 62 access_key_state: 63 description: 64 - When type is user, it creates, removes, deactivates or activates a user's access key(s). Note that actions apply only to keys specified. 65 choices: [ "create", "remove", "active", "inactive", "Create", "Remove", "Active", "Inactive"] 66 type: str 67 key_count: 68 description: 69 - When access_key_state is create it will ensure this quantity of keys are present. Defaults to 1. 70 default: 1 71 type: int 72 access_key_ids: 73 description: 74 - A list of the keys that you want impacted by the access_key_state parameter. 75 type: list 76 groups: 77 description: 78 - A list of groups the user should belong to. When update, will gracefully remove groups not listed. 79 type: list 80 password: 81 description: 82 - When type is user and state is present, define the users login password. Also works with update. Note that always returns changed. 83 type: str 84 update_password: 85 default: always 86 choices: ['always', 'on_create'] 87 description: 88 - C(always) will update passwords if they differ. C(on_create) will only set the password for newly created users. 89 type: str 90notes: 91 - 'Currently boto does not support the removal of Managed Policies, the module will error out if your 92 user/group/role has managed policies when you try to do state=absent. They will need to be removed manually.' 93author: 94 - "Jonathan I. Davila (@defionscode)" 95 - "Paul Seiffert (@seiffert)" 96extends_documentation_fragment: 97 - aws 98 - ec2 99''' 100 101EXAMPLES = ''' 102# Basic user creation example 103tasks: 104- name: Create two new IAM users with API keys 105 iam: 106 iam_type: user 107 name: "{{ item }}" 108 state: present 109 password: "{{ temp_pass }}" 110 access_key_state: create 111 loop: 112 - jcleese 113 - mpython 114 115# Advanced example, create two new groups and add the pre-existing user 116# jdavila to both groups. 117task: 118- name: Create Two Groups, Mario and Luigi 119 iam: 120 iam_type: group 121 name: "{{ item }}" 122 state: present 123 loop: 124 - Mario 125 - Luigi 126 register: new_groups 127 128- name: 129 iam: 130 iam_type: user 131 name: jdavila 132 state: update 133 groups: "{{ item.created_group.group_name }}" 134 loop: "{{ new_groups.results }}" 135 136# Example of role with custom trust policy for Lambda service 137- name: Create IAM role with custom trust relationship 138 iam: 139 iam_type: role 140 name: AAALambdaTestRole 141 state: present 142 trust_policy: 143 Version: '2012-10-17' 144 Statement: 145 - Action: sts:AssumeRole 146 Effect: Allow 147 Principal: 148 Service: lambda.amazonaws.com 149 150''' 151RETURN = ''' 152role_result: 153 description: the IAM.role dict returned by Boto 154 type: str 155 returned: if iam_type=role and state=present 156 sample: { 157 "arn": "arn:aws:iam::A1B2C3D4E5F6:role/my-new-role", 158 "assume_role_policy_document": "...truncated...", 159 "create_date": "2017-09-02T14:32:23Z", 160 "path": "/", 161 "role_id": "AROAA1B2C3D4E5F6G7H8I", 162 "role_name": "my-new-role" 163 } 164roles: 165 description: a list containing the name of the currently defined roles 166 type: list 167 returned: if iam_type=role and state=present 168 sample: [ 169 "my-new-role", 170 "my-existing-role-1", 171 "my-existing-role-2", 172 "my-existing-role-3", 173 "my-existing-role-...", 174 ] 175''' 176 177import json 178import traceback 179 180try: 181 import boto.exception 182 import boto.iam 183 import boto.iam.connection 184except ImportError: 185 pass # Taken care of by ec2.HAS_BOTO 186 187from ansible.module_utils.basic import AnsibleModule 188from ansible.module_utils.ec2 import (HAS_BOTO, boto_exception, connect_to_aws, ec2_argument_spec, 189 get_aws_connection_info) 190 191 192def _paginate(func, attr): 193 ''' 194 paginates the results from func by continuously passing in 195 the returned marker if the results were truncated. this returns 196 an iterator over the items in the returned response. `attr` is 197 the name of the attribute to iterate over in the response. 198 ''' 199 finished, marker = False, None 200 while not finished: 201 res = func(marker=marker) 202 for item in getattr(res, attr): 203 yield item 204 205 finished = res.is_truncated == 'false' 206 if not finished: 207 marker = res.marker 208 209 210def list_all_groups(iam): 211 return [item['group_name'] for item in _paginate(iam.get_all_groups, 'groups')] 212 213 214def list_all_users(iam): 215 return [item['user_name'] for item in _paginate(iam.get_all_users, 'users')] 216 217 218def list_all_roles(iam): 219 return [item['role_name'] for item in _paginate(iam.list_roles, 'roles')] 220 221 222def list_all_instance_profiles(iam): 223 return [item['instance_profile_name'] for item in _paginate(iam.list_instance_profiles, 'instance_profiles')] 224 225 226def create_user(module, iam, name, pwd, path, key_state, key_count): 227 key_qty = 0 228 keys = [] 229 try: 230 user_meta = iam.create_user( 231 name, path).create_user_response.create_user_result.user 232 changed = True 233 if pwd is not None: 234 pwd = iam.create_login_profile(name, pwd) 235 if key_state in ['create']: 236 if key_count: 237 while key_count > key_qty: 238 keys.append(iam.create_access_key( 239 user_name=name).create_access_key_response. 240 create_access_key_result. 241 access_key) 242 key_qty += 1 243 else: 244 keys = None 245 except boto.exception.BotoServerError as err: 246 module.fail_json(changed=False, msg=str(err)) 247 else: 248 user_info = dict(created_user=user_meta, password=pwd, access_keys=keys) 249 return (user_info, changed) 250 251 252def delete_dependencies_first(module, iam, name): 253 changed = False 254 # try to delete any keys 255 try: 256 current_keys = [ck['access_key_id'] for ck in 257 iam.get_all_access_keys(name).list_access_keys_result.access_key_metadata] 258 for key in current_keys: 259 iam.delete_access_key(key, name) 260 changed = True 261 except boto.exception.BotoServerError as err: 262 module.fail_json(changed=changed, msg="Failed to delete keys: %s" % err, exception=traceback.format_exc()) 263 264 # try to delete login profiles 265 try: 266 login_profile = iam.get_login_profiles(name).get_login_profile_response 267 iam.delete_login_profile(name) 268 changed = True 269 except boto.exception.BotoServerError as err: 270 error_msg = boto_exception(err) 271 if 'Login Profile for User ' + name + ' cannot be found.' not in error_msg: 272 module.fail_json(changed=changed, msg="Failed to delete login profile: %s" % err, exception=traceback.format_exc()) 273 274 # try to detach policies 275 try: 276 for policy in iam.get_all_user_policies(name).list_user_policies_result.policy_names: 277 iam.delete_user_policy(name, policy) 278 changed = True 279 except boto.exception.BotoServerError as err: 280 error_msg = boto_exception(err) 281 if 'must detach all policies first' in error_msg: 282 module.fail_json(changed=changed, msg="All inline policies have been removed. Though it appears" 283 "that %s has Managed Polices. This is not " 284 "currently supported by boto. Please detach the policies " 285 "through the console and try again." % name) 286 module.fail_json(changed=changed, msg="Failed to delete policies: %s" % err, exception=traceback.format_exc()) 287 288 # try to deactivate associated MFA devices 289 try: 290 mfa_devices = iam.get_all_mfa_devices(name).get('list_mfa_devices_response', {}).get('list_mfa_devices_result', {}).get('mfa_devices', []) 291 for device in mfa_devices: 292 iam.deactivate_mfa_device(name, device['serial_number']) 293 changed = True 294 except boto.exception.BotoServerError as err: 295 module.fail_json(changed=changed, msg="Failed to deactivate associated MFA devices: %s" % err, exception=traceback.format_exc()) 296 297 return changed 298 299 300def delete_user(module, iam, name): 301 changed = delete_dependencies_first(module, iam, name) 302 try: 303 iam.delete_user(name) 304 except boto.exception.BotoServerError as ex: 305 module.fail_json(changed=changed, msg="Failed to delete user %s: %s" % (name, ex), exception=traceback.format_exc()) 306 else: 307 changed = True 308 return name, changed 309 310 311def update_user(module, iam, name, new_name, new_path, key_state, key_count, keys, pwd, updated): 312 changed = False 313 name_change = False 314 if updated and new_name: 315 name = new_name 316 try: 317 current_keys = [ck['access_key_id'] for ck in 318 iam.get_all_access_keys(name).list_access_keys_result.access_key_metadata] 319 status = [ck['status'] for ck in 320 iam.get_all_access_keys(name).list_access_keys_result.access_key_metadata] 321 key_qty = len(current_keys) 322 except boto.exception.BotoServerError as err: 323 error_msg = boto_exception(err) 324 if 'cannot be found' in error_msg and updated: 325 current_keys = [ck['access_key_id'] for ck in 326 iam.get_all_access_keys(new_name).list_access_keys_result.access_key_metadata] 327 status = [ck['status'] for ck in 328 iam.get_all_access_keys(new_name).list_access_keys_result.access_key_metadata] 329 name = new_name 330 else: 331 module.fail_json(changed=False, msg=str(err)) 332 333 updated_key_list = {} 334 335 if new_name or new_path: 336 c_path = iam.get_user(name).get_user_result.user['path'] 337 if (name != new_name) or (c_path != new_path): 338 changed = True 339 try: 340 if not updated: 341 user = iam.update_user( 342 name, new_user_name=new_name, new_path=new_path).update_user_response.response_metadata 343 else: 344 user = iam.update_user( 345 name, new_path=new_path).update_user_response.response_metadata 346 user['updates'] = dict( 347 old_username=name, new_username=new_name, old_path=c_path, new_path=new_path) 348 except boto.exception.BotoServerError as err: 349 error_msg = boto_exception(err) 350 module.fail_json(changed=False, msg=str(err)) 351 else: 352 if not updated: 353 name_change = True 354 355 if pwd: 356 try: 357 iam.update_login_profile(name, pwd) 358 changed = True 359 except boto.exception.BotoServerError: 360 try: 361 iam.create_login_profile(name, pwd) 362 changed = True 363 except boto.exception.BotoServerError as err: 364 error_msg = boto_exception(str(err)) 365 if 'Password does not conform to the account password policy' in error_msg: 366 module.fail_json(changed=False, msg="Password doesn't conform to policy") 367 else: 368 module.fail_json(msg=error_msg) 369 370 try: 371 current_keys = [ck['access_key_id'] for ck in 372 iam.get_all_access_keys(name).list_access_keys_result.access_key_metadata] 373 status = [ck['status'] for ck in 374 iam.get_all_access_keys(name).list_access_keys_result.access_key_metadata] 375 key_qty = len(current_keys) 376 except boto.exception.BotoServerError as err: 377 error_msg = boto_exception(err) 378 if 'cannot be found' in error_msg and updated: 379 current_keys = [ck['access_key_id'] for ck in 380 iam.get_all_access_keys(new_name).list_access_keys_result.access_key_metadata] 381 status = [ck['status'] for ck in 382 iam.get_all_access_keys(new_name).list_access_keys_result.access_key_metadata] 383 name = new_name 384 else: 385 module.fail_json(changed=False, msg=str(err)) 386 387 new_keys = [] 388 if key_state == 'create': 389 try: 390 while key_count > key_qty: 391 new_keys.append(iam.create_access_key( 392 user_name=name).create_access_key_response.create_access_key_result.access_key) 393 key_qty += 1 394 changed = True 395 396 except boto.exception.BotoServerError as err: 397 module.fail_json(changed=False, msg=str(err)) 398 399 if keys and key_state: 400 for access_key in keys: 401 if key_state in ('active', 'inactive'): 402 if access_key in current_keys: 403 for current_key, current_key_state in zip(current_keys, status): 404 if key_state != current_key_state.lower(): 405 try: 406 iam.update_access_key(access_key, key_state.capitalize(), user_name=name) 407 changed = True 408 except boto.exception.BotoServerError as err: 409 module.fail_json(changed=False, msg=str(err)) 410 else: 411 module.fail_json(msg="Supplied keys not found for %s. " 412 "Current keys: %s. " 413 "Supplied key(s): %s" % 414 (name, current_keys, keys) 415 ) 416 417 if key_state == 'remove': 418 if access_key in current_keys: 419 try: 420 iam.delete_access_key(access_key, user_name=name) 421 except boto.exception.BotoServerError as err: 422 module.fail_json(changed=False, msg=str(err)) 423 else: 424 changed = True 425 426 try: 427 final_keys, final_key_status = \ 428 [ck['access_key_id'] for ck in 429 iam.get_all_access_keys(name). 430 list_access_keys_result. 431 access_key_metadata],\ 432 [ck['status'] for ck in 433 iam.get_all_access_keys(name). 434 list_access_keys_result. 435 access_key_metadata] 436 except boto.exception.BotoServerError as err: 437 module.fail_json(changed=changed, msg=str(err)) 438 439 for fk, fks in zip(final_keys, final_key_status): 440 updated_key_list.update({fk: fks}) 441 442 return name_change, updated_key_list, changed, new_keys 443 444 445def set_users_groups(module, iam, name, groups, updated=None, 446 new_name=None): 447 """ Sets groups for a user, will purge groups not explicitly passed, while 448 retaining pre-existing groups that also are in the new list. 449 """ 450 changed = False 451 452 if updated: 453 name = new_name 454 455 try: 456 orig_users_groups = [og['group_name'] for og in iam.get_groups_for_user( 457 name).list_groups_for_user_result.groups] 458 remove_groups = [ 459 rg for rg in frozenset(orig_users_groups).difference(groups)] 460 new_groups = [ 461 ng for ng in frozenset(groups).difference(orig_users_groups)] 462 except boto.exception.BotoServerError as err: 463 module.fail_json(changed=changed, msg=str(err)) 464 else: 465 if len(orig_users_groups) > 0: 466 for new in new_groups: 467 iam.add_user_to_group(new, name) 468 for rm in remove_groups: 469 iam.remove_user_from_group(rm, name) 470 else: 471 for group in groups: 472 try: 473 iam.add_user_to_group(group, name) 474 except boto.exception.BotoServerError as err: 475 error_msg = boto_exception(err) 476 if ('The group with name %s cannot be found.' % group) in error_msg: 477 module.fail_json(changed=False, msg="Group %s doesn't exist" % group) 478 479 if len(remove_groups) > 0 or len(new_groups) > 0: 480 changed = True 481 482 return (groups, changed) 483 484 485def create_group(module=None, iam=None, name=None, path=None): 486 changed = False 487 try: 488 iam.create_group( 489 name, path).create_group_response.create_group_result.group 490 except boto.exception.BotoServerError as err: 491 module.fail_json(changed=changed, msg=str(err)) 492 else: 493 changed = True 494 return name, changed 495 496 497def delete_group(module=None, iam=None, name=None): 498 changed = False 499 try: 500 iam.delete_group(name) 501 except boto.exception.BotoServerError as err: 502 error_msg = boto_exception(err) 503 if ('must delete policies first') in error_msg: 504 for policy in iam.get_all_group_policies(name).list_group_policies_result.policy_names: 505 iam.delete_group_policy(name, policy) 506 try: 507 iam.delete_group(name) 508 except boto.exception.BotoServerError as err: 509 error_msg = boto_exception(err) 510 if ('must delete policies first') in error_msg: 511 module.fail_json(changed=changed, msg="All inline policies have been removed. Though it appears" 512 "that %s has Managed Polices. This is not " 513 "currently supported by boto. Please detach the policies " 514 "through the console and try again." % name) 515 else: 516 module.fail_json(changed=changed, msg=str(error_msg)) 517 else: 518 changed = True 519 else: 520 module.fail_json(changed=changed, msg=str(error_msg)) 521 else: 522 changed = True 523 return changed, name 524 525 526def update_group(module=None, iam=None, name=None, new_name=None, new_path=None): 527 changed = False 528 try: 529 current_group_path = iam.get_group( 530 name).get_group_response.get_group_result.group['path'] 531 if new_path: 532 if current_group_path != new_path: 533 iam.update_group(name, new_path=new_path) 534 changed = True 535 if new_name: 536 if name != new_name: 537 iam.update_group(name, new_group_name=new_name, new_path=new_path) 538 changed = True 539 name = new_name 540 except boto.exception.BotoServerError as err: 541 module.fail_json(changed=changed, msg=str(err)) 542 543 return changed, name, new_path, current_group_path 544 545 546def create_role(module, iam, name, path, role_list, prof_list, trust_policy_doc): 547 changed = False 548 iam_role_result = None 549 instance_profile_result = None 550 try: 551 if name not in role_list: 552 changed = True 553 iam_role_result = iam.create_role(name, 554 assume_role_policy_document=trust_policy_doc, 555 path=path).create_role_response.create_role_result.role 556 557 if name not in prof_list: 558 instance_profile_result = iam.create_instance_profile(name, path=path) \ 559 .create_instance_profile_response.create_instance_profile_result.instance_profile 560 iam.add_role_to_instance_profile(name, name) 561 else: 562 instance_profile_result = iam.get_instance_profile(name).get_instance_profile_response.get_instance_profile_result.instance_profile 563 except boto.exception.BotoServerError as err: 564 module.fail_json(changed=changed, msg=str(err)) 565 else: 566 updated_role_list = list_all_roles(iam) 567 iam_role_result = iam.get_role(name).get_role_response.get_role_result.role 568 return changed, updated_role_list, iam_role_result, instance_profile_result 569 570 571def delete_role(module, iam, name, role_list, prof_list): 572 changed = False 573 iam_role_result = None 574 instance_profile_result = None 575 try: 576 if name in role_list: 577 cur_ins_prof = [rp['instance_profile_name'] for rp in 578 iam.list_instance_profiles_for_role(name). 579 list_instance_profiles_for_role_result. 580 instance_profiles] 581 for profile in cur_ins_prof: 582 iam.remove_role_from_instance_profile(profile, name) 583 try: 584 iam.delete_role(name) 585 except boto.exception.BotoServerError as err: 586 error_msg = boto_exception(err) 587 if ('must detach all policies first') in error_msg: 588 for policy in iam.list_role_policies(name).list_role_policies_result.policy_names: 589 iam.delete_role_policy(name, policy) 590 try: 591 iam_role_result = iam.delete_role(name) 592 except boto.exception.BotoServerError as err: 593 error_msg = boto_exception(err) 594 if ('must detach all policies first') in error_msg: 595 module.fail_json(changed=changed, msg="All inline policies have been removed. Though it appears" 596 "that %s has Managed Polices. This is not " 597 "currently supported by boto. Please detach the policies " 598 "through the console and try again." % name) 599 else: 600 module.fail_json(changed=changed, msg=str(err)) 601 else: 602 changed = True 603 604 else: 605 changed = True 606 607 for prof in prof_list: 608 if name == prof: 609 instance_profile_result = iam.delete_instance_profile(name) 610 except boto.exception.BotoServerError as err: 611 module.fail_json(changed=changed, msg=str(err)) 612 else: 613 updated_role_list = list_all_roles(iam) 614 return changed, updated_role_list, iam_role_result, instance_profile_result 615 616 617def main(): 618 argument_spec = ec2_argument_spec() 619 argument_spec.update(dict( 620 iam_type=dict(required=True, choices=['user', 'group', 'role']), 621 groups=dict(type='list', default=None, required=False), 622 state=dict(required=True, choices=['present', 'absent', 'update']), 623 password=dict(default=None, required=False, no_log=True), 624 update_password=dict(default='always', required=False, choices=['always', 'on_create']), 625 access_key_state=dict(default=None, required=False, choices=[ 626 'active', 'inactive', 'create', 'remove', 627 'Active', 'Inactive', 'Create', 'Remove']), 628 access_key_ids=dict(type='list', default=None, required=False), 629 key_count=dict(type='int', default=1, required=False), 630 name=dict(default=None, required=False), 631 trust_policy_filepath=dict(default=None, required=False), 632 trust_policy=dict(type='dict', default=None, required=False), 633 new_name=dict(default=None, required=False), 634 path=dict(default='/', required=False), 635 new_path=dict(default=None, required=False) 636 ) 637 ) 638 639 module = AnsibleModule( 640 argument_spec=argument_spec, 641 mutually_exclusive=[['trust_policy', 'trust_policy_filepath']], 642 ) 643 644 if not HAS_BOTO: 645 module.fail_json(msg='This module requires boto, please install it') 646 647 state = module.params.get('state').lower() 648 iam_type = module.params.get('iam_type').lower() 649 groups = module.params.get('groups') 650 name = module.params.get('name') 651 new_name = module.params.get('new_name') 652 password = module.params.get('password') 653 update_pw = module.params.get('update_password') 654 path = module.params.get('path') 655 new_path = module.params.get('new_path') 656 key_count = module.params.get('key_count') 657 key_state = module.params.get('access_key_state') 658 trust_policy = module.params.get('trust_policy') 659 trust_policy_filepath = module.params.get('trust_policy_filepath') 660 key_ids = module.params.get('access_key_ids') 661 662 if key_state: 663 key_state = key_state.lower() 664 if any([n in key_state for n in ['active', 'inactive']]) and not key_ids: 665 module.fail_json(changed=False, msg="At least one access key has to be defined in order" 666 " to use 'active' or 'inactive'") 667 668 if iam_type == 'user' and module.params.get('password') is not None: 669 pwd = module.params.get('password') 670 elif iam_type != 'user' and module.params.get('password') is not None: 671 module.fail_json(msg="a password is being specified when the iam_type " 672 "is not user. Check parameters") 673 else: 674 pwd = None 675 676 if iam_type != 'user' and (module.params.get('access_key_state') is not None or 677 module.params.get('access_key_id') is not None): 678 module.fail_json(msg="the IAM type must be user, when IAM access keys " 679 "are being modified. Check parameters") 680 681 if iam_type == 'role' and state == 'update': 682 module.fail_json(changed=False, msg="iam_type: role, cannot currently be updated, " 683 "please specify present or absent") 684 685 # check if trust_policy is present -- it can be inline JSON or a file path to a JSON file 686 if trust_policy_filepath: 687 try: 688 with open(trust_policy_filepath, 'r') as json_data: 689 trust_policy_doc = json.dumps(json.load(json_data)) 690 except Exception as e: 691 module.fail_json(msg=str(e) + ': ' + trust_policy_filepath) 692 elif trust_policy: 693 try: 694 trust_policy_doc = json.dumps(trust_policy) 695 except Exception as e: 696 module.fail_json(msg=str(e) + ': ' + trust_policy) 697 else: 698 trust_policy_doc = None 699 700 region, ec2_url, aws_connect_kwargs = get_aws_connection_info(module) 701 702 try: 703 if region: 704 iam = connect_to_aws(boto.iam, region, **aws_connect_kwargs) 705 else: 706 iam = boto.iam.connection.IAMConnection(**aws_connect_kwargs) 707 except boto.exception.NoAuthHandlerFound as e: 708 module.fail_json(msg=str(e)) 709 710 result = {} 711 changed = False 712 713 try: 714 orig_group_list = list_all_groups(iam) 715 716 orig_user_list = list_all_users(iam) 717 718 orig_role_list = list_all_roles(iam) 719 720 orig_prof_list = list_all_instance_profiles(iam) 721 except boto.exception.BotoServerError as err: 722 module.fail_json(msg=err.message) 723 724 if iam_type == 'user': 725 been_updated = False 726 user_groups = None 727 user_exists = any([n in [name, new_name] for n in orig_user_list]) 728 if user_exists: 729 current_path = iam.get_user(name).get_user_result.user['path'] 730 if not new_path and current_path != path: 731 new_path = path 732 path = current_path 733 734 if state == 'present' and not user_exists and not new_name: 735 (meta, changed) = create_user( 736 module, iam, name, password, path, key_state, key_count) 737 keys = iam.get_all_access_keys(name).list_access_keys_result.\ 738 access_key_metadata 739 if groups: 740 (user_groups, changed) = set_users_groups( 741 module, iam, name, groups, been_updated, new_name) 742 module.exit_json( 743 user_meta=meta, groups=user_groups, keys=keys, changed=changed) 744 745 elif state in ['present', 'update'] and user_exists: 746 if update_pw == 'on_create': 747 password = None 748 if name not in orig_user_list and new_name in orig_user_list: 749 been_updated = True 750 name_change, key_list, user_changed, new_key = update_user( 751 module, iam, name, new_name, new_path, key_state, key_count, key_ids, password, been_updated) 752 if new_key: 753 user_meta = {'access_keys': list(new_key)} 754 user_meta['access_keys'].extend( 755 [{'access_key_id': key, 'status': value} for key, value in key_list.items() if 756 key not in [it['access_key_id'] for it in new_key]]) 757 else: 758 user_meta = { 759 'access_keys': [{'access_key_id': key, 'status': value} for key, value in key_list.items()]} 760 761 if name_change and new_name: 762 orig_name = name 763 name = new_name 764 if isinstance(groups, list): 765 user_groups, groups_changed = set_users_groups( 766 module, iam, name, groups, been_updated, new_name) 767 if groups_changed == user_changed: 768 changed = groups_changed 769 else: 770 changed = True 771 else: 772 changed = user_changed 773 if new_name and new_path: 774 module.exit_json(changed=changed, groups=user_groups, old_user_name=orig_name, 775 new_user_name=new_name, old_path=path, new_path=new_path, keys=key_list, 776 created_keys=new_key, user_meta=user_meta) 777 elif new_name and not new_path and not been_updated: 778 module.exit_json( 779 changed=changed, groups=user_groups, old_user_name=orig_name, new_user_name=new_name, keys=key_list, 780 created_keys=new_key, user_meta=user_meta) 781 elif new_name and not new_path and been_updated: 782 module.exit_json( 783 changed=changed, groups=user_groups, user_name=new_name, keys=key_list, key_state=key_state, 784 created_keys=new_key, user_meta=user_meta) 785 elif not new_name and new_path: 786 module.exit_json( 787 changed=changed, groups=user_groups, user_name=name, old_path=path, new_path=new_path, 788 keys=key_list, created_keys=new_key, user_meta=user_meta) 789 else: 790 module.exit_json( 791 changed=changed, groups=user_groups, user_name=name, keys=key_list, created_keys=new_key, 792 user_meta=user_meta) 793 794 elif state == 'update' and not user_exists: 795 module.fail_json( 796 msg="The user %s does not exist. No update made." % name) 797 798 elif state == 'absent': 799 if user_exists: 800 try: 801 set_users_groups(module, iam, name, '') 802 name, changed = delete_user(module, iam, name) 803 module.exit_json(deleted_user=name, changed=changed) 804 805 except Exception as ex: 806 module.fail_json(changed=changed, msg=str(ex)) 807 else: 808 module.exit_json( 809 changed=False, msg="User %s is already absent from your AWS IAM users" % name) 810 811 elif iam_type == 'group': 812 group_exists = name in orig_group_list 813 814 if state == 'present' and not group_exists: 815 new_group, changed = create_group(module=module, iam=iam, name=name, path=path) 816 module.exit_json(changed=changed, group_name=new_group) 817 elif state in ['present', 'update'] and group_exists: 818 changed, updated_name, updated_path, cur_path = update_group( 819 module=module, iam=iam, name=name, new_name=new_name, 820 new_path=new_path) 821 822 if new_path and new_name: 823 module.exit_json(changed=changed, old_group_name=name, 824 new_group_name=updated_name, old_path=cur_path, 825 new_group_path=updated_path) 826 827 if new_path and not new_name: 828 module.exit_json(changed=changed, group_name=name, 829 old_path=cur_path, 830 new_group_path=updated_path) 831 832 if not new_path and new_name: 833 module.exit_json(changed=changed, old_group_name=name, 834 new_group_name=updated_name, group_path=cur_path) 835 836 if not new_path and not new_name: 837 module.exit_json( 838 changed=changed, group_name=name, group_path=cur_path) 839 840 elif state == 'update' and not group_exists: 841 module.fail_json( 842 changed=changed, msg="Update Failed. Group %s doesn't seem to exist!" % name) 843 844 elif state == 'absent': 845 if name in orig_group_list: 846 removed_group, changed = delete_group(module=module, iam=iam, name=name) 847 module.exit_json(changed=changed, delete_group=removed_group) 848 else: 849 module.exit_json(changed=changed, msg="Group already absent") 850 851 elif iam_type == 'role': 852 role_list = [] 853 if state == 'present': 854 changed, role_list, role_result, instance_profile_result = create_role( 855 module, iam, name, path, orig_role_list, orig_prof_list, trust_policy_doc) 856 elif state == 'absent': 857 changed, role_list, role_result, instance_profile_result = delete_role( 858 module, iam, name, orig_role_list, orig_prof_list) 859 elif state == 'update': 860 module.fail_json( 861 changed=False, msg='Role update not currently supported by boto.') 862 module.exit_json(changed=changed, roles=role_list, role_result=role_result, 863 instance_profile_result=instance_profile_result) 864 865 866if __name__ == '__main__': 867 main() 868