Win domain group membership module (#52556)

* Add new win_domain_group_membership module.

* Add support for diff mode.

* Do not assign variable which is never used.

* Add documentation for the `domain_*` options.

* Let ansible handle the exceptions.

The test if the group exists is useless as the first action on the 
groups fails with the same error message if it does not exist.

* Add comments why we need the try/catch

* Rework diff handling.

Just return before/after state and let ansible do the working out of the
diff.

* Minor cleanups according to PR

* Switch from Get-AdUser/Group to Get-AdObject

so we can add/remove service accounts, or computers too.

* Cleanup PowerShell code
pull/53828/head
Marius Rieder 5 years ago committed by Jordan Borean
parent a043570579
commit 22fb4c858a

@ -0,0 +1,124 @@
#!powershell
# Copyright: (c) 2019, Marius Rieder <marius.rieder@scs.ch>
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
#Requires -Module Ansible.ModuleUtils.Legacy
try {
Import-Module ActiveDirectory
}
catch {
Fail-Json -obj @{} -message "win_domain_group_membership requires the ActiveDirectory PS module to be installed"
}
$params = Parse-Args $args -supports_check_mode $true
$check_mode = Get-AnsibleParam -obj $params -name "_ansible_check_mode" -type "bool" -default $false
$diff_mode = Get-AnsibleParam -obj $params -name "_ansible_diff" -type "bool" -default $false
# Module control parameters
$state = Get-AnsibleParam -obj $params -name "state" -type "str" -default "present" -validateset "present","absent","pure"
$domain_username = Get-AnsibleParam -obj $params -name "domain_username" -type "str"
$domain_password = Get-AnsibleParam -obj $params -name "domain_password" -type "str" -failifempty ($null -ne $domain_username)
$domain_server = Get-AnsibleParam -obj $params -name "domain_server" -type "str"
# Group Membership parameters
$name = Get-AnsibleParam -obj $params -name "name" -type "str" -failifempty $true
$members = Get-AnsibleParam -obj $params -name "members" -type "list" -failifempty $true
# Filter ADObjects by ObjectClass
$ad_object_class_filter = "(ObjectClass -eq 'user' -or ObjectClass -eq 'group' -or ObjectClass -eq 'computer' -or ObjectClass -eq 'msDS-ManagedServiceAccount')"
$extra_args = @{}
if ($null -ne $domain_username) {
$domain_password = ConvertTo-SecureString $domain_password -AsPlainText -Force
$credential = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList $domain_username, $domain_password
$extra_args.Credential = $credential
}
if ($nul -ne $domain_server) {
$extra_args.Server = $domain_server
}
$result = @{
changed = $false
added = [System.Collections.Generic.List`1[String]]@()
removed = [System.Collections.Generic.List`1[String]]@()
}
if ($diff_mode) {
$result.diff = @{}
}
$members_before = Get-AdGroupMember -Identity $name @extra_args
$pure_members = [System.Collections.Generic.List`1[String]]@()
foreach ($member in $members) {
$group_member = Get-ADObject -Filter "SamAccountName -eq '$member' -and $ad_object_class_filter" -Properties objectSid, sAMAccountName
if (!$group_member) {
Fail-Json -obj $result "Could not find domain user, group, service account or computer named $member"
}
if ($state -eq "pure") {
$pure_members.Add($group_member.objectSid)
}
$user_in_group = $false
foreach ($current_member in $members_before) {
if ($current_member.sid -eq $group_member.objectSid) {
$user_in_group = $true
break
}
}
if ($state -in @("present", "pure") -and !$user_in_group) {
Add-ADGroupMember -Identity $name -Members $group_member -WhatIf:$check_mode @extra_args
$result.added.Add($group_member.SamAccountName)
$result.changed = $true
} elseif ($state -eq "absent" -and $user_in_group) {
Remove-ADGroupMember -Identity $name -Members $group_member -WhatIf:$check_mode @extra_args -Confirm:$False
$result.removed.Add($group_member.SamAccountName)
$result.changed = $true
}
}
if ($state -eq "pure") {
# Perform removals for existing group members not defined in $members
$current_members = Get-AdGroupMember -Identity $name @extra_args
foreach ($current_member in $current_members) {
$user_to_remove = $true
foreach ($pure_member in $pure_members) {
if ($pure_member -eq $current_member.sid) {
$user_to_remove = $false
break
}
}
if ($user_to_remove) {
Remove-ADGroupMember -Identity $name -Members $current_member -WhatIf:$check_mode @extra_args -Confirm:$False
$result.removed.Add($current_member.SamAccountName)
$result.changed = $true
}
}
}
$final_members = Get-AdGroupMember -Identity $name @extra_args
if ($final_members) {
$result.members = [Array]$final_members.SamAccountName
} else {
$result.members = @()
}
if ($diff_mode -and $result.changed) {
$result.diff.before = $members_before.SamAccountName | Out-String
if (!$check_mode) {
$result.diff.after = [Array]$final_members.SamAccountName | Out-String
} else {
$after = [System.Collections.Generic.List`1[String]]$result.members
$result.removed | ForEach-Object { $after.Remove($_) > $null }
$after.AddRange($result.added)
$result.diff.after = $after | Out-String
}
}
Exit-Json -obj $result

@ -0,0 +1,117 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
# Copyright: (c) 2017, Andrew Saraceni <andrew.saraceni@gmail.com>
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
ANSIBLE_METADATA = {'metadata_version': '1.1',
'status': ['preview'],
'supported_by': 'community'}
DOCUMENTATION = r'''
---
module: win_domain_group_membership
version_added: "2.8"
short_description: Manage Windows domain group membership
description:
- Allows the addition and removal of domain users
and domain groups from/to a domain group.
options:
name:
description:
- Name of the domain group to manage membership on.
type: str
required: yes
members:
description:
- A list of members to ensure are present/absent from the group.
- The given names must be a SmaAccountName of a user, group, service account, or computer
type: list
required: yes
state:
description:
- Desired state of the members in the group.
- 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, pure ]
default: present
domain_username:
description:
- The username to use when interacting with AD.
- If this is not set then the user Ansible used to log in with will be
used instead when using CredSSP or Kerberos with credential delegation.
type: str
domain_password:
description:
- The password for I(username).
type: str
domain_server:
description:
- Specifies the Active Directory Domain Services instance to connect to.
- Can be in the form of an FQDN or NetBIOS name.
- If not specified then the value is based on the domain of the computer
running PowerShell.
type: str
seealso:
- module: win_domain_user
- module: win_domain_group
author:
- Marius Rieder (@jiuka)
'''
EXAMPLES = r'''
- name: Add a domain user/group to a domain group
win_domain_group_membership:
name: Foo
members:
- Bar
state: present
- name: Remove a domain user/group from a domain group
win_domain_group_membership:
name: Foo
members:
- Bar
state: absent
- name: Ensure only a domain user/group exists in a domain group
win_domain_group_membership:
name: Foo
members:
- Bar
state: pure
- name: Add a computer to a domain group
win_domain_group_membership:
name: Foo
members:
- DESKTOP$
state: present
'''
RETURN = r'''
name:
description: The name of the target domain group.
returned: always
type: str
sample: Domain-Admins
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) or C(pure)
type: list
sample: ["UserName", "GroupName"]
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) or C(pure)
type: list
sample: ["UserName", "GroupName"]
members:
description: A list of all domain group members at completion; this is empty
if the group contains no members.
returned: success
type: list
sample: ["UserName", "GroupName"]
'''
Loading…
Cancel
Save