From 30b25d53d2f5b199d4653a60fb374d6ad6116fe1 Mon Sep 17 00:00:00 2001 From: Andrew Saraceni Date: Wed, 30 Jan 2019 20:48:49 -0500 Subject: [PATCH] Add "pure" state functionality for win_group_membership (#51298) * add pure state functionality for win_group_membership * fixing typos in docs * fix syntax for adding removed array depending on state * remove trailing whitespace from docs * fix issue in testing pure (again) * adding note for pure being added in Ansible 2.8 --- .../modules/windows/win_group_membership.ps1 | 42 +++++++++- .../modules/windows/win_group_membership.py | 20 +++-- .../win_group_membership/tasks/tests.yml | 80 ++++++++++++++++++- 3 files changed, 132 insertions(+), 10 deletions(-) diff --git a/lib/ansible/modules/windows/win_group_membership.ps1 b/lib/ansible/modules/windows/win_group_membership.ps1 index 5d6dce2a59..8afbe8b5af 100644 --- a/lib/ansible/modules/windows/win_group_membership.ps1 +++ b/lib/ansible/modules/windows/win_group_membership.ps1 @@ -92,15 +92,16 @@ $check_mode = Get-AnsibleParam -obj $params -name "_ansible_check_mode" -type "b $name = Get-AnsibleParam -obj $params -name "name" -type "str" -failifempty $true $members = Get-AnsibleParam -obj $params -name "members" -type "list" -failifempty $true -$state = Get-AnsibleParam -obj $params -name "state" -type "str" -default "present" -validateset "present","absent" +$state = Get-AnsibleParam -obj $params -name "state" -type "str" -default "present" -validateset "present","absent","pure" $result = @{ changed = $false name = $name } -if ($state -eq "present") { +if ($state -in @("present", "pure")) { $result.added = @() -} elseif ($state -eq "absent") { +} +if ($state -in @("absent", "pure")) { $result.removed = @() } @@ -112,9 +113,13 @@ if (!$group) { } $current_members = Get-GroupMember -Group $group +$pure_members = @() foreach ($member in $members) { $group_member = Test-GroupMember -GroupMember $member + if ($state -eq "pure") { + $pure_members += $group_member + } $user_in_group = $false foreach ($current_member in $current_members) { @@ -127,7 +132,7 @@ foreach ($member in $members) { $member_sid = "WinNT://{0}" -f $group_member.sid try { - if ($state -eq "present" -and !$user_in_group) { + if ($state -in @("present", "pure") -and !$user_in_group) { if (!$check_mode) { $group.Add($member_sid) $result.added += $group_member.account_name @@ -145,6 +150,35 @@ foreach ($member in $members) { } } +if ($state -eq "pure") { + # Perform removals for existing group members not defined in $members + $current_members = Get-GroupMember -Group $group + + foreach ($current_member in $current_members) { + $user_to_remove = $true + foreach ($pure_member in $pure_members) { + if ($pure_member.sid -eq $current_member.sid) { + $user_to_remove = $false + break + } + } + + $member_sid = "WinNT://{0}" -f $current_member.sid + + try { + if ($user_to_remove) { + if (!$check_mode) { + $group.Remove($member_sid) + $result.removed += $current_member.account_name + } + $result.changed = $true + } + } catch { + Fail-Json -obj $result -message $_.Exception.Message + } + } +} + $final_members = Get-GroupMember -Group $group if ($final_members) { diff --git a/lib/ansible/modules/windows/win_group_membership.py b/lib/ansible/modules/windows/win_group_membership.py index d6f97d0a34..bff6d710b9 100644 --- a/lib/ansible/modules/windows/win_group_membership.py +++ b/lib/ansible/modules/windows/win_group_membership.py @@ -35,8 +35,11 @@ options: state: description: - Desired state of the members in the group. + - C(pure) was added in Ansible 2.8. + - When C(state) is C(pure), only the members specified will exist, + and all other existing members not specified are removed. type: str - choices: [ absent, present ] + choices: [ absent, present, pure ] default: present seealso: - module: win_domain_group @@ -62,6 +65,13 @@ EXAMPLES = r''' - DOMAIN\TestGroup - NT AUTHORITY\SYSTEM state: absent + +- name: Ensure only a domain user exists in a local group + win_group_membership: + name: Remote Desktop Users + members: + - DOMAIN\TestUser + state: pure ''' RETURN = r''' @@ -71,14 +81,14 @@ name: type: str sample: Administrators added: - description: A list of members added when C(state) is C(present); this is - empty if no members are added. + description: A list of members added when C(state) is C(present) or + C(pure); this is empty if no members are added. returned: success and C(state) is C(present) type: list sample: ["SERVERNAME\\NewLocalAdmin", "DOMAIN\\TestUser"] removed: - description: A list of members removed when C(state) is C(absent); this is - empty if no members are removed. + description: A list of members removed when C(state) is C(absent) or + C(pure); this is empty if no members are removed. returned: success and C(state) is C(absent) type: list sample: ["DOMAIN\\TestGroup", "NT AUTHORITY\\SYSTEM"] diff --git a/test/integration/targets/win_group_membership/tasks/tests.yml b/test/integration/targets/win_group_membership/tasks/tests.yml index f6ff8d8718..44a94a0473 100644 --- a/test/integration/targets/win_group_membership/tasks/tests.yml +++ b/test/integration/targets/win_group_membership/tasks/tests.yml @@ -239,4 +239,82 @@ - remove_another_user_from_group_again.changed == false - remove_another_user_from_group_again.removed == [] - remove_another_user_from_group_again.members == [] - when: not in_check_mode \ No newline at end of file + when: not in_check_mode + + +- name: Setup users for pure testing + win_group_membership: + <<: *wgm_present + members: + - "{{ admin_account_name }}" + - NT AUTHORITY\NETWORK SERVICE + + +- name: Define users as pure + win_group_membership: &wgm_pure + <<: *wgm_present + state: pure + register: define_users_as_pure + +- name: Test define_users_as_pure (normal mode) + assert: + that: + - define_users_as_pure.changed == true + - define_users_as_pure.added == ["{{ ansible_hostname }}\\{{ win_local_user }}", "NT AUTHORITY\\SYSTEM"] + - define_users_as_pure.removed == ["NT AUTHORITY\\NETWORK SERVICE"] + - define_users_as_pure.members == ["{{ ansible_hostname }}\\{{ admin_account_name }}", "{{ ansible_hostname }}\\{{ win_local_user }}", "NT AUTHORITY\\SYSTEM"] + when: not in_check_mode + +- name: Test define_users_as_pure (check-mode) + assert: + that: + - define_users_as_pure.changed == true + - define_users_as_pure.added == [] + - define_users_as_pure.removed == [] + - define_users_as_pure.members == [] + when: in_check_mode + + +- name: Define users as pure (again) + win_group_membership: *wgm_pure + register: define_users_as_pure_again + +- name: Test define_users_as_pure_again (normal mode) + assert: + that: + - define_users_as_pure_again.changed == false + - define_users_as_pure_again.added == [] + - define_users_as_pure_again.removed == [] + - define_users_as_pure_again.members == ["{{ ansible_hostname }}\\{{ admin_account_name }}", "{{ ansible_hostname }}\\{{ win_local_user }}", "NT AUTHORITY\\SYSTEM"] + when: not in_check_mode + + +- name: Define different syntax users as pure + win_group_membership: + <<: *wgm_pure + members: + - '{{ ansible_hostname }}\{{ admin_account_name }}' + - '.\{{ win_local_user }}' + register: define_different_syntax_users_as_pure + +- name: Test define_different_syntax_users_as_pure (normal mode) + assert: + that: + - define_different_syntax_users_as_pure.changed == true + - define_different_syntax_users_as_pure.added == [] + - define_different_syntax_users_as_pure.removed == ["NT AUTHORITY\\SYSTEM"] + - define_different_syntax_users_as_pure.members == ["{{ ansible_hostname }}\\{{ admin_account_name }}", "{{ ansible_hostname }}\\{{ win_local_user }}"] + when: not in_check_mode + +- name: Test define_different_syntax_users_as_pure (check-mode) + assert: + that: + - define_different_syntax_users_as_pure.changed == true + - define_different_syntax_users_as_pure.added == [] + - define_different_syntax_users_as_pure.removed == [] + - define_different_syntax_users_as_pure.members == [] + when: in_check_mode + + +- name: Teardown remaining pure users + win_group_membership: *wgm_absent \ No newline at end of file