diff --git a/changelogs/fragments/78172-allow-force-deletion-of-group.yaml b/changelogs/fragments/78172-allow-force-deletion-of-group.yaml new file mode 100644 index 00000000000..780f19ee22c --- /dev/null +++ b/changelogs/fragments/78172-allow-force-deletion-of-group.yaml @@ -0,0 +1,2 @@ +minor_changes: + - Allow force deletion of a group even when it is the primary group of a user. (https://github.com/ansible/ansible/issues/77849) \ No newline at end of file diff --git a/lib/ansible/modules/group.py b/lib/ansible/modules/group.py index 109a161ac56..4444f158b4f 100644 --- a/lib/ansible/modules/group.py +++ b/lib/ansible/modules/group.py @@ -35,6 +35,13 @@ options: type: str choices: [ absent, present ] default: present + force: + description: + - Whether to delete a group even if it is the primary group of a user. + - Only applicable on platforms which implement a --force flag on the group deletion command. + type: bool + default: false + version_added: "2.15" system: description: - If I(yes), indicates that the group created is a system group. @@ -140,6 +147,7 @@ class Group(object): self.module = module self.state = module.params['state'] self.name = module.params['name'] + self.force = module.params['force'] self.gid = module.params['gid'] self.system = module.params['system'] self.local = module.params['local'] @@ -244,6 +252,31 @@ class Group(object): return info +# =========================================== + +class Linux(Group): + """ + This is a Linux Group manipulation class. This is to apply the '-f' parameter to the groupdel command + + This overrides the following methods from the generic class:- + - group_del() + """ + + platform = 'Linux' + distribution = None + + def group_del(self): + if self.local: + command_name = 'lgroupdel' + else: + command_name = 'groupdel' + cmd = [self.module.get_bin_path(command_name, True)] + if self.force: + cmd.append('-f') + cmd.append(self.name) + return self.execute_command(cmd) + + # =========================================== class SunOS(Group): @@ -596,6 +629,7 @@ def main(): argument_spec=dict( state=dict(type='str', default='present', choices=['absent', 'present']), name=dict(type='str', required=True), + force=dict(type='bool', default=False), gid=dict(type='int'), system=dict(type='bool', default=False), local=dict(type='bool', default=False), @@ -607,6 +641,9 @@ def main(): ], ) + if module.params['force'] and module.params['local']: + module.fail_json(msg='force is not a valid option for local, force=True and local=True are mutually exclusive') + group = Group(module) module.debug('Group instantiated - platform %s' % group.platform) diff --git a/test/integration/targets/group/tasks/tests.yml b/test/integration/targets/group/tasks/tests.yml index f81f570b6f9..eb92cd1df04 100644 --- a/test/integration/targets/group/tasks/tests.yml +++ b/test/integration/targets/group/tasks/tests.yml @@ -345,6 +345,49 @@ # only applicable to Linux, limit further to CentOS where 'lgroupadd' is installed when: ansible_distribution == 'CentOS' + # https://github.com/ansible/ansible/pull/78172 + - block: + - name: Create a group + group: + name: groupdeltest + state: present + + - name: Create user with primary group of groupdeltest + user: + name: groupdeluser + group: groupdeltest + state: present + + - name: Show we can't delete the group usually + group: + name: groupdeltest + state: absent + ignore_errors: true + register: failed_delete + + - name: assert we couldn't delete the group + assert: + that: + - failed_delete is failed + + - name: force delete the group + group: + name: groupdeltest + force: true + state: absent + + always: + - name: Cleanup user + user: + name: groupdeluser + state: absent + + - name: Cleanup group + group: + name: groupdeltest + state: absent + when: ansible_distribution not in ["MacOSX", "Alpine", "FreeBSD"] + # create system group - name: remove group