diff --git a/README-automember.md b/README-automember.md index 42fc4aca3758a7fb26d871a099ccc2ffa3111c30..f5e075ff6f80e439460fdb756e224c1b20e4913c 100644 --- a/README-automember.md +++ b/README-automember.md @@ -104,13 +104,74 @@ Example playbook to add an inclusive condition to an existing rule ipaadmin_password: SomeADMINpassword name: "My domain hosts" description: "my automember condition" - automember_tye: hostgroup + automember_type: hostgroup action: member inclusive: - key: fqdn expression: ".*.mydomain.com" ``` +Example playbook to ensure group membership for all users has been rebuilt + +```yaml +- name: Playbook to ensure group membership for all users has been rebuilt + hosts: ipaserver + become: yes + gather_facts: no + tasks: + - ipaautomember: + ipaadmin_password: SomeADMINpassword + automember_type: group + state: rebuilt +``` + +Example playbook to ensure group membership for given users has been rebuilt + + +```yaml +- name: Playbook to ensure group membership for given users has been rebuilt + hosts: ipaserver + become: yes + gather_facts: no + tasks: + - ipaautomember: + ipaadmin_password: SomeADMINpassword + users: + - user1 + - user2 + state: rebuilt +``` + +Example playbook to ensure hostgroup membership for all hosts has been rebuilt + +```yaml +- name: Playbook to ensure hostgroup membership for all hosts has been rebuilt + hosts: ipaserver + become: yes + gather_facts: no + tasks: + - ipaautomember: + ipaadmin_password: SomeADMINpassword + automember_type: hostgroup + state: rebuilt +``` + +Example playbook to ensure hostgroup membership for given hosts has been rebuilt + +```yaml +- name: Playbook to ensure hostgroup membership for given hosts has been rebuilt + hosts: ipaserver + become: yes + gather_facts: no + tasks: + - ipaautomember: + ipaadmin_password: SomeADMINpassword + hosts: + - host1.mydomain.com + - host2.mydomain.com + state: rebuilt +``` + Variables --------- @@ -129,11 +190,15 @@ Variable | Description | Required `automember_type` | Grouping to which the rule applies. It can be one of `group`, `hostgroup`. | yes `inclusive` | List of dictionaries in the format of `{'key': attribute, 'expression': inclusive_regex}` | no `exclusive` | List of dictionaries in the format of `{'key': attribute, 'expression': exclusive_regex}` | no +`users` | Users to rebuild membership for. | no +`hosts` | Hosts to rebuild membership for. | no +`no_wait` | Don't wait for rebuilding membership. | no `action` | Work on automember or member level. It can be one of `member` or `automember` and defaults to `automember`. | no -`state` | The state to ensure. It can be one of `present`, `absent`, default: `present`. | no +`state` | The state to ensure. It can be one of `present`, `absent`, 'rebuilt'. default: `present`. | no Authors ======= Mark Hahl +Thomas Woerner diff --git a/playbooks/automember/automember-group-membership-all-users-rebuilt.yml b/playbooks/automember/automember-group-membership-all-users-rebuilt.yml new file mode 100644 index 0000000000000000000000000000000000000000..5cfd14379a5f2e16773fc647820f44fffb42547c --- /dev/null +++ b/playbooks/automember/automember-group-membership-all-users-rebuilt.yml @@ -0,0 +1,10 @@ +--- +- name: Automember group membership for all users rebuilt example + hosts: ipaserver + become: true + tasks: + - name: Ensure group automember rule admins is present + ipaautomember: + ipaadmin_password: SomeADMINpassword + automember_type: group + state: rebuilt diff --git a/playbooks/automember/automember-group-membership-users-rebuilt.yml b/playbooks/automember/automember-group-membership-users-rebuilt.yml new file mode 100644 index 0000000000000000000000000000000000000000..df9e54a657a2feba6f15aa3f2dfe12ee09c11701 --- /dev/null +++ b/playbooks/automember/automember-group-membership-users-rebuilt.yml @@ -0,0 +1,12 @@ +--- +- name: Automember group membership for given users rebuilt example + hosts: ipaserver + become: true + tasks: + - name: Ensure group membership for given users has been rebuilt + ipaautomember: + ipaadmin_password: SomeADMINpassword + users: + - user1 + - user2 + state: rebuilt diff --git a/playbooks/automember/automember-hostgroup-membership-all-hosts-rebuilt.yml b/playbooks/automember/automember-hostgroup-membership-all-hosts-rebuilt.yml new file mode 100644 index 0000000000000000000000000000000000000000..f2ad5a49c9ee4ef943b41cd4b3656dc12a77ba50 --- /dev/null +++ b/playbooks/automember/automember-hostgroup-membership-all-hosts-rebuilt.yml @@ -0,0 +1,10 @@ +--- +- name: Automember hostgroup membership for all hosts rebuilt example + hosts: ipaserver + become: true + tasks: + - name: Ensure hostgroup membership for all hosts has been rebuilt + ipaautomember: + ipaadmin_password: SomeADMINpassword + automember_type: hostgroup + state: rebuilt diff --git a/playbooks/automember/automember-hostgroup-membership-hosts-rebuilt.yml b/playbooks/automember/automember-hostgroup-membership-hosts-rebuilt.yml new file mode 100644 index 0000000000000000000000000000000000000000..82e6c7f8f7d96056609db19b8121a0ba94eb8479 --- /dev/null +++ b/playbooks/automember/automember-hostgroup-membership-hosts-rebuilt.yml @@ -0,0 +1,12 @@ +--- +- name: Automember hostgroup membership for given hosts rebuilt example + hosts: ipaserver + become: true + tasks: + - name: Ensure hostgroup membership for given hosts has been rebuilt + ipaautomember: + ipaadmin_password: SomeADMINpassword + hosts: + - host1.mydomain.com + - host2.mydomain.com + state: rebuilt diff --git a/plugins/modules/ipaautomember.py b/plugins/modules/ipaautomember.py index b99879af650332aef3f762dc097b992b22fa51b4..cd2aac6882be672e0bf4a659031d05707bc5313c 100644 --- a/plugins/modules/ipaautomember.py +++ b/plugins/modules/ipaautomember.py @@ -79,6 +79,17 @@ options: description: The expression of the regex type: str required: true + users: + description: Users to rebuild membership for. + type: list + required: false + hosts: + description: Hosts to rebuild membership for. + type: list + required: false + no_wait: + description: Don't wait for rebuilding membership. + type: bool action: description: Work on automember or member level default: automember @@ -86,10 +97,11 @@ options: state: description: State to ensure default: present - choices: ["present", "absent"] + choices: ["present", "absent", "rebuilt"] author: - Mark Hahl - Jake Reynolds + - Thomas Woerner """ EXAMPLES = """ @@ -116,12 +128,39 @@ EXAMPLES = """ - ipaautomember: ipaadmin_password: SomeADMINpassword name: "My domain hosts" - automember_tye: hostgroup + automember_type: hostgroup action: member inclusive: - key: fqdn expression: ".*.mydomain.com" +# Ensure group membership for all users has been rebuilt +- ipaautomember: + ipaadmin_password: SomeADMINpassword + automember_type: group + state: rebuilt + +# Ensure group membership for given users has been rebuilt +- ipaautomember: + ipaadmin_password: SomeADMINpassword + users: + - user1 + - user2 + state: rebuilt + +# Ensure hostgroup membership for all hosts has been rebuilt +- ipaautomember: + ipaadmin_password: SomeADMINpassword + automember_type: hostgroup + state: rebuilt + +# Ensure hostgroup membership for given hosts has been rebuilt +- ipaautomember: + ipaadmin_password: SomeADMINpassword + hosts: + - host1.mydomain.com + - host2.mydomain.com + state: rebuilt """ RETURN = """ @@ -133,10 +172,10 @@ from ansible.module_utils.ansible_freeipa_module import ( ) -def find_automember(module, name, grouping): +def find_automember(module, name, automember_type): _args = { "all": True, - "type": grouping + "type": automember_type } try: @@ -146,13 +185,13 @@ def find_automember(module, name, grouping): return _result["result"] -def gen_condition_args(grouping, +def gen_condition_args(automember_type, key, inclusiveregex=None, exclusiveregex=None): _args = {} - if grouping is not None: - _args['type'] = grouping + if automember_type is not None: + _args['type'] = automember_type if key is not None: _args['key'] = key if inclusiveregex is not None: @@ -163,13 +202,23 @@ def gen_condition_args(grouping, return _args -def gen_args(description, grouping): +def gen_rebuild_args(automember_type, rebuild_users, rebuild_hosts, no_wait): + _args = {"no_wait": no_wait} + if automember_type is not None: + _args['type'] = automember_type + if rebuild_users is not None: + _args["users"] = rebuild_users + if rebuild_hosts is not None: + _args["hosts"] = rebuild_hosts + return _args + + +def gen_args(description, automember_type): _args = {} if description is not None: _args["description"] = description - if grouping is not None: - _args['type'] = grouping - + if automember_type is not None: + _args['type'] = automember_type return _args @@ -208,14 +257,15 @@ def main(): ), elements="dict", required=False), name=dict(type="list", aliases=["cn"], - default=None, required=True), + default=None, required=False), description=dict(type="str", default=None), automember_type=dict(type='str', required=False, choices=['group', 'hostgroup']), + no_wait=dict(type="bool", default=None), action=dict(type="str", default="automember", choices=["member", "automember"]), state=dict(type="str", default="present", - choices=["present", "absent", "rebuild"]), + choices=["present", "absent", "rebuilt"]), users=dict(type="list", default=None), hosts=dict(type="list", default=None), ), @@ -228,6 +278,8 @@ def main(): # general names = ansible_module.params_get("name") + if names is None: + names = [] # present description = ansible_module.params_get("description") @@ -236,6 +288,9 @@ def main(): inclusive = ansible_module.params_get("inclusive") exclusive = ansible_module.params_get("exclusive") + # no_wait for rebuilt + no_wait = ansible_module.params_get("no_wait") + # action action = ansible_module.params_get("action") # state @@ -250,12 +305,29 @@ def main(): # Check parameters invalid = [] - if state != "rebuild": - invalid = ["rebuild_hosts", "rebuild_users"] + if state in ["rebuilt"]: + invalid = ["name", "description", "exclusive", "inclusive"] - if not automember_type and state != "rebuild": + if action == "member": ansible_module.fail_json( - msg="'automember_type' is required unless state: rebuild") + msg="'action=member' is not usable with state '%s'" % state) + + if state == "rebuilt": + if automember_type == "group" and rebuild_hosts is not None: + ansible_module.fail_json( + msg="state %s: hosts can not be set when type is '%s'" % + (state, automember_type)) + if automember_type == "hostgroup" and rebuild_users is not None: + ansible_module.fail_json( + msg="state %s: users can not be set when type is '%s'" % + (state, automember_type)) + + else: + invalid = ["users", "hosts", "no_wait"] + + if not automember_type: + ansible_module.fail_json( + msg="'automember_type' is required.") ansible_module.params_fail_used_invalid(invalid, state, action) @@ -392,16 +464,14 @@ def main(): 'automember_remove_condition', condition_args]) - elif state == "rebuild": - if automember_type: - commands.append([None, 'automember_rebuild', - {"type": automember_type}]) - if rebuild_users: - commands.append([None, 'automember_rebuild', - {"users": rebuild_users}]) - if rebuild_hosts: - commands.append([None, 'automember_rebuild', - {"hosts": rebuild_hosts}]) + if len(names) == 0: + if state == "rebuilt": + args = gen_rebuild_args(automember_type, rebuild_users, + rebuild_hosts, no_wait) + commands.append([None, 'automember_rebuild', args]) + + else: + ansible_module.fail_json(msg="Invalid operation") # Execute commands diff --git a/tests/automember/test_automember_client_context.yml b/tests/automember/test_automember_client_context.yml index d8c5658dd1f82bdec1e5ef75fac85ae9ddf39127..2cb99e1e0c7cc103c22df8451809c27b3a634c9b 100644 --- a/tests/automember/test_automember_client_context.yml +++ b/tests/automember/test_automember_client_context.yml @@ -14,7 +14,8 @@ ipaadmin_password: SomeADMINpassword ipaapi_context: server name: ThisShouldNotWork - state: rebuild + automember_type: group + state: rebuilt register: result failed_when: not (result.failed and result.msg is regex("No module named '*ipaserver'*")) when: ipa_host_is_client diff --git a/tests/automember/test_automember_rebuilt.yml b/tests/automember/test_automember_rebuilt.yml new file mode 100644 index 0000000000000000000000000000000000000000..4458e202766f736bc500401eacb8c3eaebfbee4d --- /dev/null +++ b/tests/automember/test_automember_rebuilt.yml @@ -0,0 +1,155 @@ +--- +- name: Test automember rebuilt + hosts: "{{ ipa_test_host | default('ipaserver') }}" + become: true + + tasks: + + # SET FACTS + + - name: Get Domain from server name + set_fact: + ipaserver_domain: "{{ ansible_facts['fqdn'].split('.')[1:] | + join ('.') }}" + when: ipaserver_domain is not defined + + # CLEANUP TEST ITEMS + + - name: Ensure user testuser is absent + ipauser: + ipaadmin_password: SomeADMINpassword + ipaapi_context: "{{ ipa_context | default(omit) }}" + name: testuser + state: absent + + - name: Ensure host testhost is absent + ipahost: + ipaadmin_password: SomeADMINpassword + ipaapi_context: "{{ ipa_context | default(omit) }}" + name: "{{ 'testhost.' + ipaserver_domain }}" + state: absent + + # CREATE TEST ITEMS + + - name: Ensure user testuser is present + ipauser: + ipaadmin_password: SomeADMINpassword + ipaapi_context: "{{ ipa_context | default(omit) }}" + name: testuser + first: Test + last: User + register: result + failed_when: not result.changed or result.failed + + - name: Ensure host testhost is present + ipahost: + ipaadmin_password: SomeADMINpassword + ipaapi_context: "{{ ipa_context | default(omit) }}" + name: "{{ 'testhost.' + ipaserver_domain }}" + force: yes + reverse: no + register: result + failed_when: not result.changed or result.failed + + # TESTS + + - name: Ensure group membership has been rebuilt + ipaautomember: + ipaadmin_password: SomeADMINpassword + ipaapi_context: "{{ ipa_context | default(omit) }}" + automember_type: group + state: rebuilt + register: result + failed_when: not result.changed or result.failed + + - name: Ensure group membership has been rebuilt no_wait + ipaautomember: + ipaadmin_password: SomeADMINpassword + ipaapi_context: "{{ ipa_context | default(omit) }}" + automember_type: group + no_wait: yes + state: rebuilt + register: result + failed_when: not result.changed or result.failed + + - name: Ensure group membership for given users has been rebuilt + ipaautomember: + ipaadmin_password: SomeADMINpassword + ipaapi_context: "{{ ipa_context | default(omit) }}" + users: + - testuser + state: rebuilt + register: result + failed_when: not result.changed or result.failed + + - name: Ensure hostgroup membership for given hosts has been rebuilt + ipaautomember: + ipaadmin_password: SomeADMINpassword + ipaapi_context: "{{ ipa_context | default(omit) }}" + hosts: + - "{{ 'testhost.' + ipaserver_domain }}" + state: rebuilt + register: result + failed_when: not result.changed or result.failed + + - name: Ensure group membership for given users has been rebuilt with type group + ipaautomember: + ipaadmin_password: SomeADMINpassword + ipaapi_context: "{{ ipa_context | default(omit) }}" + automember_type: group + users: + - testuser + state: rebuilt + register: result + failed_when: not result.changed or result.failed + + - name: Ensure hostgroup membership for given hosts has been rebuilt with type hostgroup + ipaautomember: + ipaadmin_password: SomeADMINpassword + ipaapi_context: "{{ ipa_context | default(omit) }}" + automember_type: hostgroup + hosts: + - "{{ 'testhost.' + ipaserver_domain }}" + state: rebuilt + register: result + failed_when: not result.changed or result.failed + + - name: Ensure group membership rebuild fails with hosts + ipaautomember: + ipaadmin_password: SomeADMINpassword + ipaapi_context: "{{ ipa_context | default(omit) }}" + automember_type: group + hosts: + - "{{ 'testhost.' + ipaserver_domain }}" + state: rebuilt + register: result + failed_when: not result.failed or + "hosts can not be set when type is 'group'" not in result.msg + + - name: Ensure hostgroup membership rebuild fails with users + ipaautomember: + ipaadmin_password: SomeADMINpassword + ipaapi_context: "{{ ipa_context | default(omit) }}" + automember_type: hostgroup + users: + - testuser + state: rebuilt + register: result + failed_when: not result.failed or + "users can not be set when type is 'hostgroup'" not in result.msg + + # CLEANUP TEST ITEMS + + - name: Ensure user testuser is absent + ipauser: + ipaadmin_password: SomeADMINpassword + ipaapi_context: "{{ ipa_context | default(omit) }}" + name: testuser + state: absent + + - name: Ensure host testhost is absent + ipahost: + ipaadmin_password: SomeADMINpassword + ipaapi_context: "{{ ipa_context | default(omit) }}" + name: "{{ 'testhost.' + ipaserver_domain }}" + state: absent