diff --git a/README-role.md b/README-role.md index b8e3b655..351d2b03 100644 --- a/README-role.md +++ b/README-role.md @@ -230,6 +230,8 @@ Example playbook to ensure that different members are not associated with a role - User Administrators service: - service01 + sysaccount: + - my-app action: member state: absent ``` @@ -253,7 +255,8 @@ Variable | Description | Required `host` | List of hosts to be assigned or not assigned to the role. | no `hostgroup` | List of hostgroups to be assigned or not assigned to the role. | no `service` | List of services to be assigned or not assigned to the role. | no -`action` | Work on role or member level. It can be on of `member` or `role` and defaults to `role`. | no +`sysaccount` | List of sysaccounts to be assigned or not assigned to the role. | no +`action` | Work on role or member level. It can be one of `member` or `role` and defaults to `role`. | no `state` | The state to ensure. It can be one of `present`, `absent`, default: `present`. | no @@ -261,3 +264,4 @@ Authors ======= Rafael Jeffman +Thomas Woerner diff --git a/README-sysaccount.md b/README-sysaccount.md new file mode 100644 index 00000000..6c412e77 --- /dev/null +++ b/README-sysaccount.md @@ -0,0 +1,196 @@ +Sysaccount module +============ + +Description +----------- + +The sysaccount module allows to ensure presence and absence of system accounts. + +Features +-------- + +* Sysaccount management + + +Supported FreeIPA Versions +-------------------------- + +FreeIPA versions 4.4.0 and up are supported by the ipasysaccount module. + + +Requirements +------------ + +**Controller** +* Ansible version: 2.15+ + +**Node** +* Supported FreeIPA version (see above) + + +Usage +===== + +Example inventory file + +```ini +[ipaserver] +ipaserver.test.local +``` + + +Example playbook to make sure sysaccount "my-app" is present with random password: + +```yaml +--- +- name: Playbook to manage IPA sysaccount. + hosts: ipaserver + become: false + + tasks: + - name: Ensure sysaccount "my-app" is present with random password + ipasysaccount: + ipaadmin_password: SomeADMINpassword + name: my-app + random: true + register: result + + - name: Print generated random password + debug: + var: result.sysaccount.randompassword + +``` + + +Example playbook to make sure sysaccount "my-app" is present with given password: + +```yaml +--- +- name: Playbook to manage IPA sysaccount. + hosts: ipaserver + become: false + + tasks: + - name: Ensure sysaccount "my-app" is present with given password + ipasysaccount: + ipaadmin_password: SomeADMINpassword + name: my-app + password: SomeAPPpassword +``` + + +Example playbook to make sure sysaccount "my-app" is absent: + +```yaml +--- +- name: Playbook to manage IPA sysaccount. + hosts: ipaserver + become: false + + tasks: + - name: Ensure sysaccount "my-app" is absent + ipasysaccount: + ipaadmin_password: SomeADMINpassword + name: my-app + state: absent +``` + +Example playbook to ensure existing sysaccount my-app is privileged + +```yaml +--- +- name: Playbook to manage IPA sysaccount. + hosts: ipaserver + become: false + + tasks: + - name: Ensure existing sysaccount my-app is privileged + ipasysaccount: + ipaadmin_password: SomeADMINpassword + name: my-app + privileged: true +``` + +Example playbook to ensure existing sysaccount my-app is not privileged + +```yaml +--- +- name: Playbook to manage IPA sysaccount. + hosts: ipaserver + become: false + + tasks: + - name: Ensure existing sysaccount my-app is not privileged + ipasysaccount: + ipaadmin_password: SomeADMINpassword + name: my-app + privileged: false +``` + +Example playbook to ensure existing sysaccount my-app is disabled + +```yaml +--- +- name: Playbook to manage IPA sysaccount. + hosts: ipaserver + become: false + + tasks: + - name: Ensure existing sysaccount my-app is disabled + ipasysaccount: + ipaadmin_password: SomeADMINpassword + name: my-app + state: disabled +``` + +Example playbook to ensure existing sysaccount my-app is enabled + +```yaml +--- +- name: Playbook to manage IPA sysaccount. + hosts: ipaserver + become: false + + tasks: + - name: Ensure existing sysaccount my-app is enabled + ipasysaccount: + ipaadmin_password: SomeADMINpassword + name: my-app + state: enabled +``` + + +Variables +--------- + +Variable | Description | Required +-------- | ----------- | -------- +`ipaadmin_principal` | The admin principal is a string and defaults to `admin` | no +`ipaadmin_password` | The admin password is a string and is required if there is no admin ticket available on the node | no +`ipaapi_context` | The context in which the module will execute. Executing in a server context is preferred. If not provided context will be determined by the execution environment. Valid values are `server` and `client`. | no +`ipaapi_ldap_cache` | Use LDAP cache for IPA connection. The bool setting defaults to true. (bool) | no +`name` \| `login` | The list of sysaccount name strings - internally uid. (list of strings) | yes +`description` | A description for the sysaccount. (string) | no +`privileged` | Allow password updates without reset. This flag is not replicated. It is needed to set privileged on all servers, where it is needed. (bool) | no +`random` | Generate a random user password. (bool) | no +`password` \| `userpassword` | Set the password. (string) | no +`update_password` | Set password for a sysaccount in present state only on creation or always. It can be one of `always` or `on_create` and defaults to `always`. | no +`state` | The state to ensure. It can be one of `present`, `absent`, 'enabled', 'disabled', default: `present`. | no + + +Return Values +============= + +There are only return values if a random passwords has been generated. + +Variable | Description | Returned When +-------- | ----------- | ------------- +`sysaccount` | Sysaccount dict (dict)
Options: | Always +  | `randompassword` - The generated random password | If random is yes and sysaccount did not exist or update_password is yes + + + +Authors +======= + +Thomas Woerner diff --git a/README.md b/README.md index 8e8c64a8..7558b23a 100644 --- a/README.md +++ b/README.md @@ -50,6 +50,7 @@ Features * Modules for sudocmd management * Modules for sudocmdgroup management * Modules for sudorule management +* Modules for sysaccount management * Modules for topology management * Modules for trust management * Modules for user management @@ -465,6 +466,7 @@ Modules in plugin/modules * [ipasudocmd](README-sudocmd.md) * [ipasudocmdgroup](README-sudocmdgroup.md) * [ipasudorule](README-sudorule.md) +* [ipasysaccount](README-sysaccount.md) * [ipatopologysegment](README-topology.md) * [ipatopologysuffix](README-topology.md) * [ipatrust](README-trust.md) diff --git a/playbooks/sysaccount/sysaccount-absent.yml b/playbooks/sysaccount/sysaccount-absent.yml new file mode 100644 index 00000000..e0f19fb2 --- /dev/null +++ b/playbooks/sysaccount/sysaccount-absent.yml @@ -0,0 +1,11 @@ +--- +- name: Sysaccount example + hosts: ipaserver + become: false + + tasks: + - name: Ensure sysaccount my-app is absent + ipasysaccount: + ipaadmin_password: SomeADMINpassword + name: my-app + state: absent diff --git a/playbooks/sysaccount/sysaccount-disabled.yml b/playbooks/sysaccount/sysaccount-disabled.yml new file mode 100644 index 00000000..cd59fd03 --- /dev/null +++ b/playbooks/sysaccount/sysaccount-disabled.yml @@ -0,0 +1,11 @@ +--- +- name: Sysaccount example + hosts: ipaserver + become: false + + tasks: + - name: Ensure sysaccount my-app is disabled + ipasysaccount: + ipaadmin_password: SomeADMINpassword + name: my-app + state: disabled diff --git a/playbooks/sysaccount/sysaccount-enabled.yml b/playbooks/sysaccount/sysaccount-enabled.yml new file mode 100644 index 00000000..353be207 --- /dev/null +++ b/playbooks/sysaccount/sysaccount-enabled.yml @@ -0,0 +1,11 @@ +--- +- name: Sysaccount example + hosts: ipaserver + become: false + + tasks: + - name: Ensure sysaccount my-app is enabled + ipasysaccount: + ipaadmin_password: SomeADMINpassword + name: my-app + state: enabled diff --git a/playbooks/sysaccount/sysaccount-present.yml b/playbooks/sysaccount/sysaccount-present.yml new file mode 100644 index 00000000..52306ba4 --- /dev/null +++ b/playbooks/sysaccount/sysaccount-present.yml @@ -0,0 +1,11 @@ +--- +- name: Sysaccount example + hosts: ipaserver + become: false + + tasks: + - name: Ensure sysaccount my-app is present with random password + ipasysaccount: + ipaadmin_password: SomeADMINpassword + name: my-app + random: true diff --git a/playbooks/sysaccount/sysaccount-privileged.yml b/playbooks/sysaccount/sysaccount-privileged.yml new file mode 100644 index 00000000..cca53fe7 --- /dev/null +++ b/playbooks/sysaccount/sysaccount-privileged.yml @@ -0,0 +1,11 @@ +--- +- name: Sysaccount example + hosts: ipaserver + become: false + + tasks: + - name: Ensure sysaccount my-app is privileged + ipasysaccount: + ipaadmin_password: SomeADMINpassword + name: my-app + privileged: true diff --git a/playbooks/sysaccount/sysaccount-unprivileged.yml b/playbooks/sysaccount/sysaccount-unprivileged.yml new file mode 100644 index 00000000..397b9962 --- /dev/null +++ b/playbooks/sysaccount/sysaccount-unprivileged.yml @@ -0,0 +1,11 @@ +--- +- name: Sysaccount example + hosts: ipaserver + become: false + + tasks: + - name: Ensure sysaccount my-app is not privileged + ipasysaccount: + ipaadmin_password: SomeADMINpassword + name: my-app + privileged: false diff --git a/plugins/modules/iparole.py b/plugins/modules/iparole.py index bf449839..09c407ab 100644 --- a/plugins/modules/iparole.py +++ b/plugins/modules/iparole.py @@ -85,6 +85,11 @@ options: type: list elements: str required: false + sysaccount: + description: List of sysaccounts. + type: list + elements: str + required: false action: description: Work on role or member level. type: str @@ -177,7 +182,7 @@ def check_parameters(module): "description", "user", "group", "host", "hostgroup", - "service", + "service", "sysaccount", "privilege", ] @@ -225,7 +230,7 @@ def ensure_absent_state(module, name, action, res_find): {"privilege": del_list}]) member_args = {} - for key in ['user', 'group', 'hostgroup']: + for key in ['user', 'group', 'hostgroup', 'sysaccount']: _members = module.params_get_lowercase(key) if _members: del_list = gen_intersection_list( @@ -335,7 +340,7 @@ def ensure_role_with_members_is_present(module, name, res_find, action): add_members = {} del_members = {} - for key in ["user", "group", "hostgroup"]: + for key in ["user", "group", "hostgroup", "sysaccount"]: _members = module.params_get_lowercase(key) if _members is not None: add_list, del_list = gen_add_del_lists( @@ -437,6 +442,8 @@ def create_module(): default=None), service=dict(required=False, type='list', elements="str", default=None), + sysaccount=dict(required=False, type='list', elements="str", + default=None), # state action=dict(type="str", default="role", @@ -467,8 +474,15 @@ def main(): state = ansible_module.params_get("state") action = ansible_module.params_get("action") names = ansible_module.params_get("name") + sysaccount = ansible_module.params_get("sysaccount") commands = [] + has_sysaccount_member = ansible_module.ipa_command_param_exists( + "role_add_member", "sysaccount") + if not has_sysaccount_member and sysaccount is not None: + ansible_module.fail_json( + msg="sysaccount members are not supported by your IPA version") + for name in names: cmds = role_commands_for_name(ansible_module, state, action, name) commands.extend(cmds) diff --git a/plugins/modules/ipasysaccount.py b/plugins/modules/ipasysaccount.py new file mode 100644 index 00000000..f050396d --- /dev/null +++ b/plugins/modules/ipasysaccount.py @@ -0,0 +1,309 @@ +# -*- coding: utf-8 -*- + +# Authors: +# Thomas Woerner +# +# Copyright (C) 2025 Red Hat +# see file 'COPYING' for use and warranty information +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +from __future__ import (absolute_import, division, print_function) + +__metaclass__ = type + +ANSIBLE_METADATA = { + "metadata_version": "1.0", + "supported_by": "community", + "status": ["preview"], +} + +DOCUMENTATION = """ +--- +module: ipasysaccount +short_description: Manage FreeIPA system account +description: Manage FreeIPA system account +extends_documentation_fragment: + - ipamodule_base_docs + - ipamodule_base_docs.delete_continue +options: + name: + description: The list of sysaccount name strings (internally uid). + required: true + type: list + elements: str + aliases: ["login"] + description: + description: A description for the sysaccount. + type: str + required: false + privileged: + description: Allow password updates without reset. + type: bool + required: false + random: + description: Generate a random user password. + required: false + type: bool + password: + description: Set the user password. + required: false + type: str + aliases: ["userpassword"] + update_password: + description: + Set password for a sysaccount in present state only on creation or always + type: str + choices: ["always", "on_create"] + required: false + state: + description: The state to ensure. + choices: ["present", "absent", "enabled", "disabled"] + default: present + type: str +author: + - Thomas Woerner (@t-woerner) +""" + +EXAMPLES = """ +# Ensure sysaccount my-app is present +- ipasysaccount: + ipaadmin_password: SomeADMINpassword + name: my-app + random: true + +# Ensure sysaccount my-app is absent +- ipasysaccount: + ipaadmin_password: SomeADMINpassword + name: my-app + state: absent + +# Ensure existing sysaccount my-app is privileged +- ipasysaccount: + ipaadmin_password: SomeADMINpassword + name: my-app + privileged: true + +# Ensure existing sysaccount my-app is not privileged +- ipasysaccount: + ipaadmin_password: SomeADMINpassword + name: my-app + privileged: false + +# Ensure existing sysaccount my-app is disabled +- ipasysaccount: + ipaadmin_password: SomeADMINpassword + name: my-app + state: disabled + +# Ensure existing sysaccount my-app is enabled +- ipasysaccount: + ipaadmin_password: SomeADMINpassword + name: my-app + state: enabled +""" + +RETURN = """ +sysaccount: + description: Sysaccount dict with random password + returned: | + If random is yes and user sysaccount not exist or update_password is yes + type: dict + contains: + randompassword: + description: The generated random password + type: str +""" + + +from ansible.module_utils.ansible_freeipa_module import \ + IPAAnsibleModule, compare_args_ipa, ipalib_errors +from ansible.module_utils import six + +if six.PY3: + unicode = str + + +def find_sysaccount(module, name): + """Find if a sysaccount with the given name already exist.""" + try: + _result = module.ipa_command("sysaccount_show", name, {"all": True}) + except ipalib_errors.NotFound: + # An exception is raised if sysaccount name is not found. + return None + return _result["result"] + + +def gen_args(description, random, privileged, password): + _args = {} + if description is not None: + _args["description"] = description + if random is not None: + _args["random"] = random + if privileged is not None: + _args["privileged"] = privileged + if password is not None: + _args["userpassword"] = password + return _args + + +# pylint: disable=unused-argument +def result_handler(module, result, command, name, args, exit_args, errors): + if "random" in args and command in ["sysaccount_add", "sysaccount_mod"] \ + and "randompassword" in result["result"]: + exit_args["randompassword"] = \ + result["result"]["randompassword"] + + +def main(): + ansible_module = IPAAnsibleModule( + argument_spec=dict( + # general + name=dict(type="list", elements="str", required=True, + aliases=["login"]), + # present + description=dict(required=False, type='str', default=None), + random=dict(required=False, type='bool', default=None), + privileged=dict(required=False, type='bool', default=None), + password=dict(required=False, type='str', + aliases=["userpassword"], default=None), + + # mod + update_password=dict(type='str', default=None, no_log=False, + choices=['always', 'on_create']), + + # state + state=dict(type="str", default="present", + choices=["present", "absent", "enabled", "disabled"]), + ), + supports_check_mode=True, + ipa_module_options=["delete_continue"], + mutually_exclusive=[["random", "password"]] + ) + + ansible_module._ansible_debug = True + + # Get parameters + + # general + names = ansible_module.params_get("name") + + # present + description = ansible_module.params_get("description") + random = ansible_module.params_get("random") + privileged = ansible_module.params_get("privileged") + password = ansible_module.params_get("password") + + # mod + update_password = ansible_module.params_get("update_password") + + # absent + delete_continue = ansible_module.params_get("delete_continue") + + # state + state = ansible_module.params_get("state") + + # Check parameters + + invalid = [] + + if state == "present" and len(names) != 1: + ansible_module.fail_json( + msg="Only one sysaccount can be added at a time.") + + if state in ["absent", "enabled", "disabled"]: + if len(names) < 1: + ansible_module.fail_json(msg="No name given.") + invalid = ["description", "random", "privileged", "password"] + + ansible_module.params_fail_used_invalid(invalid, state) + + # Init + + changed = False + exit_args = {} + + # Connect to IPA API + with ansible_module.ipa_connect(): + + if not ansible_module.ipa_command_exists("sysaccount_add"): + ansible_module.fail_json( + msg=("Managing sysaccounts is not supported by your " + "IPA version") + ) + + commands = [] + for name in names: + # Make sure sysaccount exists + res_find = find_sysaccount(ansible_module, name) + + # Create command + if state == "present": + + # Generate args + args = gen_args(description, random, privileged, password) + + # Found the sysaccount + if res_find is not None: + # Ignore password and random with + # update_password == on_create + if update_password == "on_create": + if "userpassword" in args: + del args["userpassword"] + if "random" in args: + del args["random"] + # if using "random:false" password should not be + # generated. + if not args.get("random", True): + del args["random"] + + # For all settings is args, check if there are + # different settings in the find result. + # If yes: modify + if not compare_args_ipa(ansible_module, args, + res_find): + commands.append([name, "sysaccount_mod", args]) + else: + commands.append([name, "sysaccount_add", args]) + + elif state == "absent": + if res_find is not None: + commands.append( + [name, "sysaccount_del", {"continue": delete_continue}] + ) + + elif state == "enabled": + if res_find is not None and res_find["nsaccountlock"]: + commands.append([name, "sysaccount_enable", {}]) + + elif state == "disabled": + if res_find is not None and not res_find["nsaccountlock"]: + commands.append([name, "sysaccount_disable", {}]) + + else: + ansible_module.fail_json(msg="Unkown state '%s'" % state) + + # Execute commands + + changed = ansible_module.execute_ipa_commands( + commands, result_handler, keeponly=["randompassword"], + exit_args=exit_args) + + # Done + + ansible_module.exit_json(changed=changed, sysaccount=exit_args) + + +if __name__ == "__main__": + main() diff --git a/tests/role/test_role_sysaccount_member.yml b/tests/role/test_role_sysaccount_member.yml new file mode 100644 index 00000000..1b415cac --- /dev/null +++ b/tests/role/test_role_sysaccount_member.yml @@ -0,0 +1,161 @@ +--- +- name: Test sysaccount + hosts: "{{ ipa_test_host | default('ipaserver') }}" + # It is normally not needed to set "become" to "true" for a module test. + # Only set it to true if it is needed to execute commands as root. + become: false + # Enable "gather_facts" only if "ansible_facts" variable needs to be used. + gather_facts: false + module_defaults: + ipaprivilege: + ipaadmin_password: SomeADMINpassword + ipaapi_context: "{{ ipa_context | default(omit) }}" + iparole: + ipaadmin_password: SomeADMINpassword + ipaapi_context: "{{ ipa_context | default(omit) }}" + ipasysaccount: + ipaadmin_password: SomeADMINpassword + ipaapi_context: "{{ ipa_context | default(omit) }}" + + tasks: + + - name: Verify if role sysaccount member tests are possible + ansible.builtin.shell: + cmd: | + echo SomeADMINpassword | kinit -c {{ krb5ccname }} admin > /dev/null + RESULT=$(KRB5CCNAME={{ krb5ccname }} ipa role-add-member --help) + kdestroy -A -c {{ krb5ccname }} > /dev/null + echo $RESULT + vars: + krb5ccname: "__check_ipa_role_add_member__" + register: check_role_add_member + + - name: Execute tests + when: '"sysaccounts" in check_role_add_member.stdout' + block: + + # CLEANUP TEST ITEMS + + - name: Ensure sysaccount my-app is absent + ipasysaccount: + name: my-app + state: absent + + - name: Ensure role "my-app role" is absent + iparole: + name: my-app role + state: absent + + - name: Ensure privilege "my-app password change privilege" is absent + ipaprivilege: + name: my-app password change privilege + state: absent + + # CREATE TEST ITEMS + + - name: Ensure privilege "my-app password change privilege" is present + ipaprivilege: + name: my-app password change privilege + permission: + - "System: Change User password" + register: result + failed_when: not result.changed or result.failed + + # TESTS + + - name: Ensure sysaccount my-app is present with random password + ipasysaccount: + name: my-app + random: true + register: result + failed_when: not result.changed or result.failed + + - name: Ensure role "my-app role" is present with sysaccount member my-app + iparole: + name: my-app role + sysaccount: my-app + privilege: my-app password change privilege + register: result + failed_when: not result.changed or result.failed + + - name: Ensure role "my-app role" is present with sysaccount member my-app, again + iparole: + name: my-app role + sysaccount: my-app + privilege: my-app password change privilege + register: result + failed_when: result.changed or result.failed + + - name: Ensure role my-app role does not have sysaccount member my-app + iparole: + name: my-app role + sysaccount: my-app + action: member + state: absent + register: result + failed_when: not result.changed or result.failed + + - name: Ensure role my-app role does not have sysaccount member my-app, again + iparole: + name: my-app role + sysaccount: my-app + action: member + state: absent + register: result + failed_when: result.changed or result.failed + + - name: Ensure role my-app role has sysaccount member my-app + iparole: + name: my-app role + sysaccount: my-app + action: member + register: result + failed_when: not result.changed or result.failed + + - name: Ensure role my-app role has sysaccount member my-app, again + iparole: + name: my-app role + sysaccount: my-app + action: member + register: result + failed_when: result.changed or result.failed + + - name: Ensure role my-app role has zero sysaccount members + iparole: + name: my-app role + sysaccount: [] + register: result + failed_when: not result.changed or result.failed + + - name: Ensure role my-app role has zero sysaccount members, again + iparole: + name: my-app role + sysaccount: [] + register: result + failed_when: result.changed or result.failed + + - name: Ensure role my-app role does not have sysaccount member my-app, again + iparole: + name: my-app role + sysaccount: my-app + action: member + state: absent + register: result + failed_when: result.changed or result.failed + + # CLEANUP TEST ITEMS + + - name: Ensure sysaccount my-app is absent + ipasysaccount: + name: my-app + state: absent + + - name: Ensure role my-app role is absent + iparole: + name: my-app role + state: absent + + - name: Ensure privilege "my-app password change privilege" is absent + ipaprivilege: + name: my-app password change privilege + state: absent diff --git a/tests/sysaccount/test_sysaccount.yml b/tests/sysaccount/test_sysaccount.yml new file mode 100644 index 00000000..f0e037f9 --- /dev/null +++ b/tests/sysaccount/test_sysaccount.yml @@ -0,0 +1,150 @@ +--- +- name: Test sysaccount + hosts: "{{ ipa_test_host | default('ipaserver') }}" + # It is normally not needed to set "become" to "true" for a module test. + # Only set it to true if it is needed to execute commands as root. + become: false + # Enable "gather_facts" only if "ansible_facts" variable needs to be used. + gather_facts: false + module_defaults: + ipasysaccount: + ipaadmin_password: SomeADMINpassword + ipaapi_context: "{{ ipa_context | default(omit) }}" + + tasks: + + - name: Verify sysaccount tests are possible + ansible.builtin.shell: + cmd: | + echo SomeADMINpassword | kinit -c {{ krb5ccname }} admin > /dev/null + RESULT=$(KRB5CCNAME={{ krb5ccname }} ipa sysaccount-add --help) + kdestroy -A -c {{ krb5ccname }} > /dev/null + echo $RESULT + vars: + krb5ccname: "__check_ipa_sysaccount_add__" + register: check_sysaccount_add + + - name: Execute tests + when: '"ipa: ERROR: unknown command" not in check_sysaccount_add.stderr' + block: + + # CLEANUP TEST ITEMS + + - name: Ensure sysaccount my-app is absent + ipasysaccount: + name: my-app + state: absent + + # CREATE TEST ITEMS + + # TESTS + + - name: Ensure sysaccount my-app is present with random password + ipasysaccount: + name: my-app + random: true + register: result + failed_when: not result.changed or + result.sysaccount.randompassword is not defined or + result.failed + + - name: Ensure sysaccount my-app is present, again with updated random password and update_password always + ipasysaccount: + name: my-app + random: true + register: result2 + failed_when: not result2.changed or + result2.sysaccount.randompassword is not defined or + result2.sysaccount.randompassword == result.sysaccount.randompassword or + result2.failed + + - name: Ensure sysaccount my-app is present, again with random password and update_password on_create + ipasysaccount: + name: my-app + random: true + update_password: on_create + register: result + failed_when: not result2.changed or + result.sysaccount.randompassword is defined or + result.failed + + # more tests here + + - name: Ensure sysaccount my-app is disabled + ipasysaccount: + name: my-app + state: disabled + register: result + failed_when: not result.changed or result.failed + + - name: Ensure sysaccount my-app is disabled, again + ipasysaccount: + name: my-app + state: disabled + register: result + failed_when: result.changed or result.failed + + - name: Ensure sysaccount my-app is enabled + ipasysaccount: + name: my-app + state: enabled + register: result + failed_when: not result.changed or result.failed + + - name: Ensure sysaccount my-app is enabled, again + ipasysaccount: + name: my-app + state: enabled + register: result + failed_when: result.changed or result.failed + + - name: Ensure sysaccount my-app is privileged + ipasysaccount: + name: my-app + privileged: true + register: result + failed_when: not result.changed or result.failed + + - name: Ensure sysaccount my-app is privileged, again + ipasysaccount: + name: my-app + privileged: true + register: result + failed_when: result.changed or result.failed + + # ADDITIONAL TEST HERE? + + - name: Ensure sysaccount my-app is not privileged + ipasysaccount: + name: my-app + privileged: false + register: result + failed_when: not result.changed or result.failed + + - name: Ensure sysaccount my-app is not privileged, again + ipasysaccount: + name: my-app + privileged: false + register: result + failed_when: result.changed or result.failed + + - name: Ensure sysaccount my-app is absent + ipasysaccount: + name: my-app + state: absent + register: result + failed_when: not result.changed or result.failed + + - name: Ensure sysaccount my-app is absent again + ipasysaccount: + name: my-app + state: absent + register: result + failed_when: result.changed or result.failed + + # CLEANUP TEST ITEMS + + - name: Ensure sysaccount my-app is absent + ipasysaccount: + name: my-app + state: absent diff --git a/tests/sysaccount/test_sysaccount_client_context.yml b/tests/sysaccount/test_sysaccount_client_context.yml new file mode 100644 index 00000000..be8541d7 --- /dev/null +++ b/tests/sysaccount/test_sysaccount_client_context.yml @@ -0,0 +1,40 @@ +--- +- name: Test sysaccount + hosts: ipaclients, ipaserver + # It is normally not needed to set "become" to "true" for a module test. + # Only set it to true if it is needed to execute commands as root. + become: false + # Enable "gather_facts" only if "ansible_facts" variable needs to be used. + gather_facts: false + + tasks: + - name: Include FreeIPA facts. + ansible.builtin.include_tasks: ../env_freeipa_facts.yml + + # Test will only be executed if host is not a server. + - name: Execute with server context in the client. + ipasysaccount: + ipaadmin_password: SomeADMINpassword + ipaapi_context: server + name: ThisShouldNotWork + register: result + failed_when: not (result.failed and result.msg is regex("No module named '*ipaserver'*")) + when: ipa_host_is_client + +# Import basic module tests, and execute with ipa_context set to 'client'. +# If ipaclients is set, it will be executed using the client, if not, +# ipaserver will be used. +# +# With this setup, tests can be executed against an IPA client, against +# an IPA server using "client" context, and ensure that tests are executed +# in upstream CI. + +- name: Test sysaccount using client context, in client host. + import_playbook: test_sysaccount.yml + when: groups['ipaclients'] + vars: + ipa_test_host: ipaclients + +- name: Test sysaccount using client context, in server host. + import_playbook: test_sysaccount.yml + when: groups['ipaclients'] is not defined or not groups['ipaclients']