parent
1c611a85ab
commit
5c6e5d4841
5 changed files with 917 additions and 0 deletions
354
lib/ansible/modules/windows/win_domain_group.ps1
Normal file
354
lib/ansible/modules/windows/win_domain_group.ps1
Normal file
|
@ -0,0 +1,354 @@
|
||||||
|
#!powershell
|
||||||
|
# This file is part of Ansible
|
||||||
|
#
|
||||||
|
# (c) 2017, Jordan Borean <jborean93@gmail.com>, and others
|
||||||
|
#
|
||||||
|
# This file is part of Ansible
|
||||||
|
#
|
||||||
|
# Ansible 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.
|
||||||
|
#
|
||||||
|
# Ansible 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 Ansible. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
# WANT_JSON
|
||||||
|
# POWERSHELL_COMMON
|
||||||
|
|
||||||
|
$ErrorActionPreference = "Stop"
|
||||||
|
|
||||||
|
$params = Parse-Args -arguments $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
|
||||||
|
|
||||||
|
$name = Get-AnsibleParam -obj $params -name "name" -type "str" -failifempty $true
|
||||||
|
$display_name = Get-AnsibleParam -obj $params -name "display_name" -type "str"
|
||||||
|
$domain_username = Get-AnsibleParam -obj $params -name "domain_username" -type "str"
|
||||||
|
$domain_password = Get-AnsibleParam -obj $params -name "domain_password" -type "str" -failifempty ($domain_username -ne $null)
|
||||||
|
$description = Get-AnsibleParam -obj $params -name "description" -type "str"
|
||||||
|
$category = Get-AnsibleParam -obj $params -name "category" -type "str" -validateset "distribution","security"
|
||||||
|
$scope = Get-AnsibleParam -obj $params -name "scope" -type "str" -validateset "domainlocal","global","universal"
|
||||||
|
$managed_by = Get-AnsibleParam -obj $params -name "managed_by" -type "str"
|
||||||
|
$attributes = Get-AnsibleParam -obj $params -name "attributes"
|
||||||
|
$organizational_unit = Get-AnsibleParam -obj $params -name "organizational_unit" -type "str" -aliases "ou","path"
|
||||||
|
$state = Get-AnsibleParam -obj $params -name "state" -type "str" -default "present" -validateset "present","absent"
|
||||||
|
$protect = Get-AnsibleParam -obj $params -name "protect" -type "bool"
|
||||||
|
$ignore_protection = Get-AnsibleParam -obj $params -name "ignore_protection" -type "bool" -default $false
|
||||||
|
|
||||||
|
$result = @{
|
||||||
|
changed = $false
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($diff_mode) {
|
||||||
|
$result.diff = @{}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (-not (Get-Module -Name ActiveDirectory -ListAvailable)) {
|
||||||
|
Fail-Json $result "win_domain_group requires the ActiveDirectory PS module to be installed"
|
||||||
|
}
|
||||||
|
Import-Module ActiveDirectory
|
||||||
|
|
||||||
|
$extra_args = @{}
|
||||||
|
if ($domain_username -ne $null) {
|
||||||
|
$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
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
$group = Get-ADGroup -Identity $name -Properties * @extra_args
|
||||||
|
} catch [Microsoft.ActiveDirectory.Management.ADIdentityNotFoundException] {
|
||||||
|
$group = $null
|
||||||
|
} catch {
|
||||||
|
Fail-Json $result "failed to retrieve initial details for group $($name): $($_.Exception.Message)"
|
||||||
|
}
|
||||||
|
if ($state -eq "absent") {
|
||||||
|
if ($group -ne $null) {
|
||||||
|
if ($group.ProtectedFromAccidentalDeletion -eq $true -and $ignore_protection -eq $true) {
|
||||||
|
$group = $group | Set-ADObject -ProtectedFromAccidentalDeletion $false -WhatIf:$check_mode -PassThru @extra_args
|
||||||
|
} elseif ($group.ProtectedFromAccidentalDeletion -eq $true -and $ignore_protection -eq $false) {
|
||||||
|
Fail-Json $result "cannot delete group $name when ProtectedFromAccidentalDeletion is turned on, run this module with ignore_protection=true to override this"
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
$group | Remove-ADGroup -Confirm:$false -WhatIf:$check_mode @extra_args
|
||||||
|
} catch {
|
||||||
|
Fail-Json $result "failed to remove group $($name): $($_.Exception.Message)"
|
||||||
|
}
|
||||||
|
|
||||||
|
$result.changed = $true
|
||||||
|
if ($diff_mode) {
|
||||||
|
$result.diff.prepared = "-[$name]"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
# validate that path is an actual path
|
||||||
|
if ($organizational_unit -ne $null) {
|
||||||
|
try {
|
||||||
|
Get-ADObject -Identity $organizational_unit @extra_args | Out-Null
|
||||||
|
} catch [Microsoft.ActiveDirectory.Management.ADIdentityNotFoundException] {
|
||||||
|
Fail-Json $result "the group path $organizational_unit does not exist, please specify a valid LDAP path"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$diff_text = $null
|
||||||
|
if ($group -ne $null) {
|
||||||
|
# will be overriden later if no change actually occurs
|
||||||
|
$diff_text += "[$name]`n"
|
||||||
|
|
||||||
|
# change the path of the group
|
||||||
|
if ($organizational_unit -ne $null) {
|
||||||
|
$group_cn = $group.CN
|
||||||
|
$existing_path = $group.DistinguishedName -replace "^CN=$group_cn,",''
|
||||||
|
if ($existing_path -ne $organizational_unit) {
|
||||||
|
$protection_disabled = $false
|
||||||
|
if ($group.ProtectedFromAccidentalDeletion -eq $true -and $ignore_protection -eq $true) {
|
||||||
|
$group | Set-ADObject -ProtectedFromAccidentalDeletion $false -WhatIf:$check_mode -PassThru @extra_args | Out-Null
|
||||||
|
$protection_disabled = $true
|
||||||
|
} elseif ($group.ProtectedFromAccidentalDeletion -eq $true -and $ignore_protection -eq $false) {
|
||||||
|
Fail-Json $result "cannot move group $name when ProtectedFromAccidentalDeletion is turned on, run this module with ignore_protection=true to override this"
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
$group = $group | Move-ADObject -Targetpath $organizational_unit -WhatIf:$check_mode -PassThru @extra_args
|
||||||
|
} catch {
|
||||||
|
Fail-Json $result "failed to move group from $existing_path to $($organizational_unit): $($_.Exception.Message)"
|
||||||
|
} finally {
|
||||||
|
if ($protection_disabled -eq $true) {
|
||||||
|
$group | Set-ADObject -ProtectedFromAccidentalDeletion $true -WhatIf:$check_mode -PassThru @extra_args | Out-Null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$result.changed = $true
|
||||||
|
$diff_text += "-DistinguishedName = CN=$group_cn,$existing_path`n+DistinguishedName = CN=$group_cn,$organizational_unit`n"
|
||||||
|
|
||||||
|
if ($protection_disabled -eq $true) {
|
||||||
|
$group | Set-ADObject -ProtectedFromAccidentalDeletion $true -WhatIf:$check_mode @extra_args | Out-Null
|
||||||
|
}
|
||||||
|
# get the group again once we have moved it
|
||||||
|
$group = Get-ADGroup -Identity $name -Properties * @extra_args
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# change attributes of group
|
||||||
|
$extra_scope_change = $null
|
||||||
|
$run_change = $false
|
||||||
|
$set_args = $extra_args.Clone()
|
||||||
|
|
||||||
|
if ($scope -ne $null) {
|
||||||
|
if ($group.GroupScope -ne $scope) {
|
||||||
|
# you cannot from from Global to DomainLocal and vice-versa, we
|
||||||
|
# need to change it to Universal and then finally to the target
|
||||||
|
# scope
|
||||||
|
if ($group.GroupScope -eq "global" -and $scope -eq "domainlocal") {
|
||||||
|
$set_args.GroupScope = "Universal"
|
||||||
|
$extra_scope_change = $scope
|
||||||
|
} elseif ($group.GroupScope -eq "domainlocal" -and $scope -eq "global") {
|
||||||
|
$set_args.GroupScope = "Universal"
|
||||||
|
$extra_scope_change = $scope
|
||||||
|
} else {
|
||||||
|
$set_args.GroupScope = $scope
|
||||||
|
}
|
||||||
|
$run_change = $true
|
||||||
|
$diff_text += "-GroupScope = $($group.GroupScope)`n+GroupScope = $scope`n"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($description -ne $null -and $group.Description -cne $description) {
|
||||||
|
$set_args.Description = $description
|
||||||
|
$run_change = $true
|
||||||
|
$diff_text += "-Description = $($group.Description)`n+Description = $description`n"
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($display_name -ne $null -and $group.DisplayName -cne $display_name) {
|
||||||
|
$set_args.DisplayName = $display_name
|
||||||
|
$run_change = $true
|
||||||
|
$diff_text += "-DisplayName = $($group.DisplayName)`n+DisplayName = $display_name`n"
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($category -ne $null -and $group.GroupCategory -ne $category) {
|
||||||
|
$set_args.GroupCategory = $category
|
||||||
|
$run_change = $true
|
||||||
|
$diff_text += "-GroupCategory = $($group.GroupCategory)`n+GroupCategory = $category`n"
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($managed_by -ne $null) {
|
||||||
|
if ($group.ManagedBy -eq $null) {
|
||||||
|
$set_args.ManagedBy = $managed_by
|
||||||
|
$run_change = $true
|
||||||
|
$diff_text += "+ManagedBy = $managed_by`n"
|
||||||
|
} else {
|
||||||
|
try {
|
||||||
|
$managed_by_object = Get-ADGroup -Identity $managed_by @extra_args
|
||||||
|
} catch [Microsoft.ActiveDirectory.Management.ADIdentityNotFoundException] {
|
||||||
|
try {
|
||||||
|
$managed_by_object = Get-ADUser -Identity $managed_by @extra_args
|
||||||
|
} catch [Microsoft.ActiveDirectory.Management.ADIdentityNotFoundException] {
|
||||||
|
Fail-Json $result "failed to find managed_by user or group $managed_by to be used for comparison"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($group.ManagedBy -ne $managed_by_object.DistinguishedName) {
|
||||||
|
$set_args.ManagedBy = $managed_by
|
||||||
|
$run_change = $true
|
||||||
|
$diff_text += "-ManagedBy = $($group.ManagedBy)`n+ManagedBy = $($managed_by_object.DistinguishedName)`n"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($attributes -ne $null) {
|
||||||
|
$add_attributes = @{}
|
||||||
|
$replace_attributes = @{}
|
||||||
|
foreach ($attribute in $attributes.GetEnumerator()) {
|
||||||
|
$attribute_name = $attribute.Name
|
||||||
|
$attribute_value = $attribute.Value
|
||||||
|
|
||||||
|
$valid_property = [bool]($group.PSobject.Properties.name -eq $attribute_name)
|
||||||
|
if ($valid_property) {
|
||||||
|
$existing_value = $group.$attribute_name
|
||||||
|
if ($existing_value -cne $attribute_value) {
|
||||||
|
$replace_attributes.$attribute_name = $attribute_value
|
||||||
|
$diff_text += "-$attribute_name = $existing_value`n+$attribute_name = $attribute_value`n"
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$add_attributes.$attribute_name = $attribute_value
|
||||||
|
$diff_text += "+$attribute_name = $attribute_value`n"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ($add_attributes.Count -gt 0) {
|
||||||
|
$set_args.Add = $add_attributes
|
||||||
|
$run_change = $true
|
||||||
|
}
|
||||||
|
if ($replace_attributes.Count -gt 0) {
|
||||||
|
$set_args.Replace = $replace_attributes
|
||||||
|
$run_change = $true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($run_change) {
|
||||||
|
try {
|
||||||
|
$group = $group | Set-ADGroup -WhatIf:$check_mode -PassThru @set_args
|
||||||
|
} catch {
|
||||||
|
Fail-Json $result "failed to change group $($name): $($_.Exception.Message)"
|
||||||
|
}
|
||||||
|
$result.changed = $true
|
||||||
|
|
||||||
|
if ($extra_scope_change -ne $null) {
|
||||||
|
try {
|
||||||
|
$group = $group | Set-ADGroup -GroupScope $extra_scope_change -WhatIf:$check_mode -PassThru @extra_args
|
||||||
|
} catch {
|
||||||
|
Fail-Json $result "failed to change scope of group $name to $($scope): $($_.Exception.Message)"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# make sure our diff text is null if no change occured
|
||||||
|
if ($result.changed -eq $false) {
|
||||||
|
$diff_text = $null
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
# validate if scope is set
|
||||||
|
if ($scope -eq $null) {
|
||||||
|
Fail-Json $result "scope must be set when state=present and the group doesn't exist"
|
||||||
|
}
|
||||||
|
|
||||||
|
$diff_text += "+[$name]`n+Scope = $scope`n"
|
||||||
|
$add_args = $extra_args.Clone()
|
||||||
|
$add_args.Name = $name
|
||||||
|
$add_args.GroupScope = $scope
|
||||||
|
|
||||||
|
if ($description -ne $null) {
|
||||||
|
$add_args.Description = $description
|
||||||
|
$diff_text += "+Description = $description`n"
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($display_name -ne $null) {
|
||||||
|
$add_args.DisplayName = $display_name
|
||||||
|
$diff_text += "+DisplayName = $display_name`n"
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($category -ne $null) {
|
||||||
|
$add_args.GroupCategory = $category
|
||||||
|
$diff_text += "+GroupCategory = $category`n"
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($managed_by -ne $null) {
|
||||||
|
$add_args.ManagedBy = $managed_by
|
||||||
|
$diff_text += "+ManagedBy = $managed_by`n"
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($attributes -ne $null) {
|
||||||
|
$add_args.OtherAttributes = $attributes
|
||||||
|
foreach ($attribute in $attributes.GetEnumerator()) {
|
||||||
|
$diff_text += "+$($attribute.Name) = $($attribute.Value)`n"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($organizational_unit -ne $null) {
|
||||||
|
$add_args.Path = $organizational_unit
|
||||||
|
$diff_text += "+Path = $organizational_unit`n"
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
$group = New-AdGroup -WhatIf:$check_mode -PassThru @add_args
|
||||||
|
} catch {
|
||||||
|
Fail-Json $result "failed to create group $($name): $($_.Exception.Message)"
|
||||||
|
}
|
||||||
|
$result.changed = $true
|
||||||
|
}
|
||||||
|
|
||||||
|
# set the protection value
|
||||||
|
if ($protect -ne $null) {
|
||||||
|
if (-not $check_mode) {
|
||||||
|
$group = Get-ADGroup -Identity $name -Properties * @extra_args
|
||||||
|
}
|
||||||
|
$existing_protection_value = $group.ProtectedFromAccidentalDeletion
|
||||||
|
if ($existing_protection_value -eq $null) {
|
||||||
|
$existing_protection_value = $false
|
||||||
|
}
|
||||||
|
if ($existing_protection_value -ne $protect) {
|
||||||
|
$diff_text += @"
|
||||||
|
-ProtectedFromAccidentalDeletion = $existing_protection_value
|
||||||
|
+ProtectedFromAccidentalDeletion = $protect
|
||||||
|
"@
|
||||||
|
|
||||||
|
$group | Set-ADObject -ProtectedFromAccidentalDeletion $protect -WhatIf:$check_mode -PassThru @extra_args
|
||||||
|
$result.changed = $true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($diff_mode -and $diff_text -ne $null) {
|
||||||
|
$result.diff.prepared = $diff_text
|
||||||
|
}
|
||||||
|
|
||||||
|
if (-not $check_mode) {
|
||||||
|
$group = Get-ADGroup -Identity $name -Properties * @extra_args
|
||||||
|
$result.sid = $group.SID.Value
|
||||||
|
$result.description = $group.Description
|
||||||
|
$result.distinguished_name = $group.DistinguishedName
|
||||||
|
$result.display_name = $group.DisplayName
|
||||||
|
$result.name = $group.Name
|
||||||
|
$result.canonical_name = $group.CanonicalName
|
||||||
|
$result.guid = $group.ObjectGUID
|
||||||
|
$result.protected_from_accidental_deletion = $group.ProtectedFromAccidentalDeletion
|
||||||
|
$result.managed_by = $group.ManagedBy
|
||||||
|
$result.group_scope = ($group.GroupScope).ToString()
|
||||||
|
$result.category = ($group.GroupCategory).ToString()
|
||||||
|
|
||||||
|
if ($attributes -ne $null) {
|
||||||
|
$result.attributes = @{}
|
||||||
|
foreach ($attribute in $attributes.GetEnumerator()) {
|
||||||
|
$attribute_name = $attribute.Name
|
||||||
|
$result.attributes.$attribute_name = $group.$attribute_name
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Exit-Json $result
|
218
lib/ansible/modules/windows/win_domain_group.py
Normal file
218
lib/ansible/modules/windows/win_domain_group.py
Normal file
|
@ -0,0 +1,218 @@
|
||||||
|
#!/usr/bin/python
|
||||||
|
# This file is part of Ansible
|
||||||
|
#
|
||||||
|
# Ansible 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.
|
||||||
|
#
|
||||||
|
# Ansible 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 Ansible. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
# this is a windows documentation stub, actual code lives in the .ps1
|
||||||
|
# file of the same name
|
||||||
|
|
||||||
|
ANSIBLE_METADATA = {'metadata_version': '1.0',
|
||||||
|
'status': ['preview'],
|
||||||
|
'supported_by': 'community'}
|
||||||
|
|
||||||
|
|
||||||
|
DOCUMENTATION = r'''
|
||||||
|
---
|
||||||
|
module: win_domain_group
|
||||||
|
version_added: '2.4'
|
||||||
|
short_description: creates, modifies or removes domain groups
|
||||||
|
description:
|
||||||
|
- Creates, modifies or removes groups in Active Directory.
|
||||||
|
- For local groups, use the M(win_group) module instead.
|
||||||
|
options:
|
||||||
|
attributes:
|
||||||
|
description:
|
||||||
|
- A dict of custom LDAP attributes to set on the group.
|
||||||
|
- This can be used to set custom attributes that are not exposed as module
|
||||||
|
parameters, e.g. C(mail).
|
||||||
|
- See the examples on how to format this parameter.
|
||||||
|
category:
|
||||||
|
description:
|
||||||
|
- The category of the group, this is the value to assign to the LDAP
|
||||||
|
C(groupType) attribute.
|
||||||
|
- If a new group is created then C(security) will be used by default.
|
||||||
|
choices: [ distribution, security ]
|
||||||
|
description:
|
||||||
|
description:
|
||||||
|
- The value to be assigned to the LDAP C(description) attribute.
|
||||||
|
display_name:
|
||||||
|
description:
|
||||||
|
- The value to assign to the LDAP C(displayName) attribute.
|
||||||
|
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.
|
||||||
|
domain_password:
|
||||||
|
description:
|
||||||
|
- The password for C(username).
|
||||||
|
ignore_protection:
|
||||||
|
description:
|
||||||
|
- Will ignore the C(ProtectedFromAccidentalDeletion) flag when deleting or
|
||||||
|
moving a group.
|
||||||
|
- The module will fail if one of these actions need to occur and this value
|
||||||
|
is set to no.
|
||||||
|
type: bool
|
||||||
|
default: 'no'
|
||||||
|
managed_by:
|
||||||
|
description:
|
||||||
|
- The value to be assigned to the LDAP C(managedBy) attribute.
|
||||||
|
- This value can be in the forms C(Distinguished Name), C(objectGUID),
|
||||||
|
C(objectSid) or C(sAMAccountName), see examples for more details.
|
||||||
|
name:
|
||||||
|
description:
|
||||||
|
- The name of the group to create, modify or remove.
|
||||||
|
- This value can be in the forms C(Distinguished Name), C(objectGUID),
|
||||||
|
C(objectSid) or C(sAMAccountName), see examples for more details.
|
||||||
|
required: yes
|
||||||
|
organizational_unit:
|
||||||
|
description:
|
||||||
|
- The full LDAP path to create or move the group to.
|
||||||
|
- This should be the path to the parent object to create or move the group
|
||||||
|
to.
|
||||||
|
- See examples for details of how this path is formed.
|
||||||
|
aliases: [ ou, path ]
|
||||||
|
protect:
|
||||||
|
description:
|
||||||
|
- Will set the C(ProtectedFromAccidentalDeletion) flag based on this value.
|
||||||
|
- This flag stops a user from deleting or moving a group to a different
|
||||||
|
path.
|
||||||
|
type: bool
|
||||||
|
scope:
|
||||||
|
description:
|
||||||
|
- The scope of the group.
|
||||||
|
- If C(state=present) and the group doesn't exist then this must be set.
|
||||||
|
choices: [domainlocal, global, universal]
|
||||||
|
state:
|
||||||
|
description:
|
||||||
|
- If C(state=present) this module will ensure the group is created and is
|
||||||
|
configured accordingly.
|
||||||
|
- If C(state=absent) this module will delete the group if it exists
|
||||||
|
default: present
|
||||||
|
choices: [ absent, present ]
|
||||||
|
notes:
|
||||||
|
- This must be run on a host that has the ActiveDirectory powershell module
|
||||||
|
installed.
|
||||||
|
author:
|
||||||
|
- Jordan Borean (@jborean93)
|
||||||
|
'''
|
||||||
|
|
||||||
|
EXAMPLES = r'''
|
||||||
|
- name: ensure the group Cow exists using sAMAccountName
|
||||||
|
win_domain_group:
|
||||||
|
name: Cow
|
||||||
|
scope: global
|
||||||
|
path: OU=groups,DC=ansible,DC=local
|
||||||
|
|
||||||
|
- name: ensure the group Cow does't exist using the Distinguished Name
|
||||||
|
win_domain_group:
|
||||||
|
name: CN=Cow,OU=groups,DC=ansible,DC=local
|
||||||
|
state: absent
|
||||||
|
|
||||||
|
- name: delete group ignoring the protection flag
|
||||||
|
win_domain_group:
|
||||||
|
name: Cow
|
||||||
|
state: absent
|
||||||
|
ignore_protection: yes
|
||||||
|
|
||||||
|
- name: create group with delete protection enabled and custom attributes
|
||||||
|
win_domain_group:
|
||||||
|
name: Ansible Users
|
||||||
|
scope: domainlocal
|
||||||
|
category: security
|
||||||
|
attributes:
|
||||||
|
mail: helpdesk@ansible.com
|
||||||
|
wWWHomePage: www.ansible.com
|
||||||
|
ignore_protection: yes
|
||||||
|
|
||||||
|
- name: change the OU of a group using the SID and ignore the protection flag
|
||||||
|
win_domain_group:
|
||||||
|
name: S-1-5-21-2171456218-3732823212-122182344-1189
|
||||||
|
scope: global
|
||||||
|
organizational_unit: OU=groups,DC=ansible,DC=local
|
||||||
|
ignore_protection: True
|
||||||
|
|
||||||
|
- name: add managed_by user
|
||||||
|
win_domain_group:
|
||||||
|
name: Group Name Here
|
||||||
|
managed_by: Domain Admins
|
||||||
|
'''
|
||||||
|
|
||||||
|
RETURN = r'''
|
||||||
|
attributes:
|
||||||
|
description: Custom attributes that were set by the module. This does not
|
||||||
|
show all the custom attributes rather just the ones that were set by the
|
||||||
|
module.
|
||||||
|
returned: group exists and attributes are set on the module invocation
|
||||||
|
type: dict
|
||||||
|
sample:
|
||||||
|
mail: 'helpdesk@ansible.com'
|
||||||
|
wWWHomePage: 'www.ansible.com'
|
||||||
|
canonical_name:
|
||||||
|
description: The canonical name of the group.
|
||||||
|
returned: group exists
|
||||||
|
type: string
|
||||||
|
sample: ansible.local/groups/Cow
|
||||||
|
category:
|
||||||
|
description: The Group type value of the group, i.e. Security or Distribution.
|
||||||
|
returned: group exists
|
||||||
|
type: string
|
||||||
|
sample: Security
|
||||||
|
description:
|
||||||
|
description: The Description of the group.
|
||||||
|
returned: group exists
|
||||||
|
type: string
|
||||||
|
sample: Group Description
|
||||||
|
display_name:
|
||||||
|
description: The Display name of the group.
|
||||||
|
returned: group exists
|
||||||
|
type: string
|
||||||
|
sample: Users who connect through RDP
|
||||||
|
distinguished_name:
|
||||||
|
description: The full Distinguished Name of the group.
|
||||||
|
returned: group exists
|
||||||
|
type: string
|
||||||
|
sample: CN=Cow,OU=groups,DC=ansible,DC=local
|
||||||
|
group_scope:
|
||||||
|
description: The Group scope value of the group.
|
||||||
|
returned: group exists
|
||||||
|
type: string
|
||||||
|
sample: Universal
|
||||||
|
guid:
|
||||||
|
description: The guid of the group.
|
||||||
|
returned: group exists
|
||||||
|
type: string
|
||||||
|
sample: 512a9adb-3fc0-4a26-9df0-e6ea1740cf45
|
||||||
|
managed_by:
|
||||||
|
description: The full Distinguished Name of the AD object that is set on the
|
||||||
|
managedBy attribute.
|
||||||
|
returned: group exists
|
||||||
|
type: string
|
||||||
|
sample: CN=Domain Admins,CN=Users,DC=ansible,DC=local
|
||||||
|
name:
|
||||||
|
description: The name of the group.
|
||||||
|
returned: group exists
|
||||||
|
type: string
|
||||||
|
sample: Cow
|
||||||
|
protected_from_accidental_deletion:
|
||||||
|
description: Whether the group is protected from accidental deletion.
|
||||||
|
returned: group exists
|
||||||
|
type: bool
|
||||||
|
sample: True
|
||||||
|
sid:
|
||||||
|
description: The Security ID of the group.
|
||||||
|
returned: group exists
|
||||||
|
type: string
|
||||||
|
sample: S-1-5-21-2171456218-3732823212-122182344-1189
|
||||||
|
'''
|
0
test/integration/targets/win_domain_group/aliases
Normal file
0
test/integration/targets/win_domain_group/aliases
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
test_win_domain_group_ldap_base: DC=ansible,DC=local
|
||||||
|
test_win_domain_group_ou_path: OU=ou1,DC=ansible,DC=local
|
||||||
|
test_win_domain_group_name: Moo Cow
|
342
test/integration/targets/win_domain_group/tasks/main.yml
Normal file
342
test/integration/targets/win_domain_group/tasks/main.yml
Normal file
|
@ -0,0 +1,342 @@
|
||||||
|
# this won't run in Ansible's integration tests until we get a domain set up
|
||||||
|
# these are here if someone wants to run the module tests locally on their own
|
||||||
|
# domain.
|
||||||
|
# Requirements:
|
||||||
|
# LDAP Base path set in defaults/main.yml like DC=ansible,DC=local
|
||||||
|
# Custom OU path set in defaults/main.yml like OU=ou1,DC=ansible,DC=local
|
||||||
|
---
|
||||||
|
- name: ensure the test group is deleted before the test
|
||||||
|
win_domain_group:
|
||||||
|
name: '{{test_win_domain_group_name}}'
|
||||||
|
state: absent
|
||||||
|
ignore_protection: True
|
||||||
|
|
||||||
|
- name: fail pass in an invalid path
|
||||||
|
win_domain_group:
|
||||||
|
name: '{{test_win_domain_group_name}}'
|
||||||
|
state: present
|
||||||
|
organizational_unit: OU=fakeou,{{test_win_domain_group_ldap_base}}
|
||||||
|
register: fail_invalid_path
|
||||||
|
failed_when: fail_invalid_path.msg != 'the group path OU=fakeou,' + test_win_domain_group_ldap_base + ' does not exist, please specify a valid LDAP path'
|
||||||
|
|
||||||
|
- name: create group with defaults check
|
||||||
|
win_domain_group:
|
||||||
|
name: '{{test_win_domain_group_name}}'
|
||||||
|
scope: global
|
||||||
|
state: present
|
||||||
|
register: create_default_check
|
||||||
|
check_mode: yes
|
||||||
|
|
||||||
|
- name: get actual group with defaults check
|
||||||
|
win_command: powershell.exe "Import-Module ActiveDirectory; Get-ADGroup -Identity '{{test_win_domain_group_name}}'"
|
||||||
|
register: create_default_actual_check
|
||||||
|
ignore_errors: True
|
||||||
|
|
||||||
|
- name: assert create group with defaults checl
|
||||||
|
assert:
|
||||||
|
that:
|
||||||
|
- create_default_check|changed
|
||||||
|
- create_default_actual_check.rc == 1
|
||||||
|
|
||||||
|
- name: create group with defaults
|
||||||
|
win_domain_group:
|
||||||
|
name: '{{test_win_domain_group_name}}'
|
||||||
|
scope: global
|
||||||
|
state: present
|
||||||
|
register: create_default
|
||||||
|
|
||||||
|
- name: get actual group with defaults
|
||||||
|
win_command: powershell.exe "Import-Module ActiveDirectory; Get-ADGroup -Identity '{{test_win_domain_group_name}}'"
|
||||||
|
register: create_default_actual
|
||||||
|
|
||||||
|
- name: assert create group with defaults
|
||||||
|
assert:
|
||||||
|
that:
|
||||||
|
- create_default|changed
|
||||||
|
- create_default.category == 'Security'
|
||||||
|
- create_default.description == None
|
||||||
|
- create_default.display_name == None
|
||||||
|
- create_default.distinguished_name == 'CN=' + test_win_domain_group_name + ',CN=Users,' + test_win_domain_group_ldap_base
|
||||||
|
- create_default.group_scope == 'Global'
|
||||||
|
- create_default.guid is defined
|
||||||
|
- create_default.managed_by == None
|
||||||
|
- create_default.name == test_win_domain_group_name
|
||||||
|
- create_default.protected_from_accidental_deletion == False
|
||||||
|
- create_default.sid is defined
|
||||||
|
- create_default_actual.rc == 0
|
||||||
|
|
||||||
|
- name: create group with defaults again
|
||||||
|
win_domain_group:
|
||||||
|
name: '{{test_win_domain_group_name}}'
|
||||||
|
scope: global
|
||||||
|
state: present
|
||||||
|
register: create_default_again
|
||||||
|
|
||||||
|
- name: assert create group with defaults again
|
||||||
|
assert:
|
||||||
|
that:
|
||||||
|
- not create_default_again|changed
|
||||||
|
|
||||||
|
- name: remove group check
|
||||||
|
win_domain_group:
|
||||||
|
name: '{{test_win_domain_group_name}}'
|
||||||
|
state: absent
|
||||||
|
register: remove_group_check
|
||||||
|
check_mode: yes
|
||||||
|
|
||||||
|
- name: get actual remove group check
|
||||||
|
win_command: powershell.exe "Import-Module ActiveDirectory; Get-ADGroup -Identity '{{test_win_domain_group_name}}'"
|
||||||
|
register: remove_group_actual_check
|
||||||
|
|
||||||
|
- name: assert remove group check
|
||||||
|
assert:
|
||||||
|
that:
|
||||||
|
- remove_group_check|changed
|
||||||
|
- remove_group_actual_check.rc == 0
|
||||||
|
|
||||||
|
- name: remove group
|
||||||
|
win_domain_group:
|
||||||
|
name: '{{test_win_domain_group_name}}'
|
||||||
|
state: absent
|
||||||
|
register: remove_group
|
||||||
|
|
||||||
|
- name: get actual remove group
|
||||||
|
win_command: powershell.exe "Import-Module ActiveDirectory; Get-ADGroup -Identity '{{test_win_domain_group_name}}'"
|
||||||
|
register: remove_group_actual
|
||||||
|
ignore_errors: True
|
||||||
|
|
||||||
|
- name: assert remove group
|
||||||
|
assert:
|
||||||
|
that:
|
||||||
|
- remove_group|changed
|
||||||
|
- remove_group_actual.rc == 1
|
||||||
|
|
||||||
|
- name: remove group again
|
||||||
|
win_domain_group:
|
||||||
|
name: '{{test_win_domain_group_name}}'
|
||||||
|
state: absent
|
||||||
|
register: remove_group_again
|
||||||
|
|
||||||
|
- name: assert remove group again
|
||||||
|
assert:
|
||||||
|
that:
|
||||||
|
- not remove_group_again|changed
|
||||||
|
|
||||||
|
- name: create non default group check
|
||||||
|
win_domain_group:
|
||||||
|
name: '{{test_win_domain_group_name}}'
|
||||||
|
state: present
|
||||||
|
description: Group Description
|
||||||
|
display_name: Group Display Name
|
||||||
|
managed_by: Domain Admins
|
||||||
|
organizational_unit: '{{test_win_domain_group_ou_path}}'
|
||||||
|
category: distribution
|
||||||
|
scope: domainlocal
|
||||||
|
attributes:
|
||||||
|
mail: test@email.com
|
||||||
|
wWWHomePage: www.google.com
|
||||||
|
protect: True
|
||||||
|
register: create_non_default_check
|
||||||
|
check_mode: yes
|
||||||
|
|
||||||
|
- name: get actual create non default group check
|
||||||
|
win_command: powershell.exe "Import-Module ActiveDirectory; Get-ADGroup -Identity '{{test_win_domain_group_name}}'"
|
||||||
|
register: create_non_default_actual_check
|
||||||
|
ignore_errors: True
|
||||||
|
|
||||||
|
- name: assert create non default group check
|
||||||
|
assert:
|
||||||
|
that:
|
||||||
|
- create_non_default_check|changed
|
||||||
|
- create_non_default_actual_check.rc == 1
|
||||||
|
|
||||||
|
- name: create non default group
|
||||||
|
win_domain_group:
|
||||||
|
name: '{{test_win_domain_group_name}}'
|
||||||
|
state: present
|
||||||
|
description: Group Description
|
||||||
|
display_name: Group Display Name
|
||||||
|
managed_by: Domain Admins
|
||||||
|
organizational_unit: '{{test_win_domain_group_ou_path}}'
|
||||||
|
category: distribution
|
||||||
|
scope: domainlocal
|
||||||
|
attributes:
|
||||||
|
mail: test@email.com
|
||||||
|
wWWHomePage: www.google.com
|
||||||
|
protect: True
|
||||||
|
register: create_non_default
|
||||||
|
|
||||||
|
- name: get actual create non default group
|
||||||
|
win_command: powershell.exe "Import-Module ActiveDirectory; Get-ADGroup -Identity '{{test_win_domain_group_name}}'"
|
||||||
|
register: create_non_default_actual
|
||||||
|
ignore_errors: True
|
||||||
|
|
||||||
|
- name: assert create non default group
|
||||||
|
assert:
|
||||||
|
that:
|
||||||
|
- create_non_default|changed
|
||||||
|
- create_non_default.category == 'Distribution'
|
||||||
|
- create_non_default.description == 'Group Description'
|
||||||
|
- create_non_default.display_name == 'Group Display Name'
|
||||||
|
- create_non_default.distinguished_name == 'CN=' + test_win_domain_group_name + ',' + test_win_domain_group_ou_path
|
||||||
|
- create_non_default.group_scope == 'DomainLocal'
|
||||||
|
- create_non_default.guid is defined
|
||||||
|
- create_non_default.managed_by == 'CN=Domain Admins,CN=Users,' + test_win_domain_group_ldap_base
|
||||||
|
- create_non_default.name == test_win_domain_group_name
|
||||||
|
- create_non_default.protected_from_accidental_deletion == True
|
||||||
|
- create_non_default.sid is defined
|
||||||
|
- create_non_default.attributes.mail == 'test@email.com'
|
||||||
|
- create_non_default.attributes.wWWHomePage == 'www.google.com'
|
||||||
|
- create_non_default_actual.rc == 0
|
||||||
|
|
||||||
|
- name: create non default group again
|
||||||
|
win_domain_group:
|
||||||
|
name: '{{test_win_domain_group_name}}'
|
||||||
|
state: present
|
||||||
|
description: Group Description
|
||||||
|
display_name: Group Display Name
|
||||||
|
managed_by: Domain Admins
|
||||||
|
organizational_unit: '{{test_win_domain_group_ou_path}}'
|
||||||
|
category: distribution
|
||||||
|
scope: domainlocal
|
||||||
|
attributes:
|
||||||
|
mail: test@email.com
|
||||||
|
wWWHomePage: www.google.com
|
||||||
|
register: create_non_default_again
|
||||||
|
|
||||||
|
- name: assert create non default group again
|
||||||
|
assert:
|
||||||
|
that:
|
||||||
|
- not create_non_default_again|changed
|
||||||
|
|
||||||
|
- name: try and move group with protection mode on
|
||||||
|
win_domain_group:
|
||||||
|
name: '{{test_win_domain_group_name}}'
|
||||||
|
state: present
|
||||||
|
organizational_unit: CN=Users,{{test_win_domain_group_ldap_base}}
|
||||||
|
register: fail_move_with_protection
|
||||||
|
failed_when: fail_move_with_protection.msg != 'cannot move group ' + test_win_domain_group_name + ' when ProtectedFromAccidentalDeletion is turned on, run this module with ignore_protection=true to override this'
|
||||||
|
|
||||||
|
- name: modify existing group check
|
||||||
|
win_domain_group:
|
||||||
|
name: '{{test_win_domain_group_name}}'
|
||||||
|
state: present
|
||||||
|
description: New Description
|
||||||
|
display_name: New Display Name
|
||||||
|
managed_by: Administrator
|
||||||
|
organizational_unit: 'CN=Users,{{test_win_domain_group_ldap_base}}'
|
||||||
|
category: security
|
||||||
|
scope: global
|
||||||
|
attributes:
|
||||||
|
mail: anothertest@email.com
|
||||||
|
ignore_protection: True
|
||||||
|
register: modify_existing_check
|
||||||
|
check_mode: yes
|
||||||
|
|
||||||
|
- name: get actual of modify existing group check
|
||||||
|
win_command: powershell.exe "Import-Module ActiveDirectory; (Get-ADGroup -Identity '{{test_win_domain_group_name}}').DistinguishedName"
|
||||||
|
register: modify_existing_actual_check
|
||||||
|
|
||||||
|
- name: assert modify existing group check
|
||||||
|
assert:
|
||||||
|
that:
|
||||||
|
- modify_existing_check|changed
|
||||||
|
- modify_existing_actual_check.stdout == 'CN=' + test_win_domain_group_name + ',' + test_win_domain_group_ou_path + '\r\n'
|
||||||
|
|
||||||
|
- name: modify existing group
|
||||||
|
win_domain_group:
|
||||||
|
name: '{{test_win_domain_group_name}}'
|
||||||
|
state: present
|
||||||
|
description: New Description
|
||||||
|
display_name: New Display Name
|
||||||
|
managed_by: Administrator
|
||||||
|
organizational_unit: CN=Users,{{test_win_domain_group_ldap_base}}
|
||||||
|
category: security
|
||||||
|
scope: global
|
||||||
|
attributes:
|
||||||
|
mail: anothertest@email.com
|
||||||
|
protect: True
|
||||||
|
ignore_protection: True
|
||||||
|
register: modify_existing
|
||||||
|
|
||||||
|
- name: get actual of modify existing group
|
||||||
|
win_command: powershell.exe "Import-Module ActiveDirectory; (Get-ADGroup -Identity '{{test_win_domain_group_name}}').DistinguishedName"
|
||||||
|
register: modify_existing_actual
|
||||||
|
|
||||||
|
- name: assert modify existing group
|
||||||
|
assert:
|
||||||
|
that:
|
||||||
|
- modify_existing|changed
|
||||||
|
- modify_existing.category == 'Security'
|
||||||
|
- modify_existing.description == 'New Description'
|
||||||
|
- modify_existing.display_name == 'New Display Name'
|
||||||
|
- modify_existing.distinguished_name == 'CN=' + test_win_domain_group_name + ',CN=Users,' + test_win_domain_group_ldap_base
|
||||||
|
- modify_existing.group_scope == 'Global'
|
||||||
|
- modify_existing.guid is defined
|
||||||
|
- modify_existing.managed_by == 'CN=Administrator,CN=Users,' + test_win_domain_group_ldap_base
|
||||||
|
- modify_existing.name == test_win_domain_group_name
|
||||||
|
- modify_existing.protected_from_accidental_deletion == True
|
||||||
|
- modify_existing.sid is defined
|
||||||
|
- modify_existing.attributes.mail == 'anothertest@email.com'
|
||||||
|
- modify_existing_actual.stdout == 'CN=' + test_win_domain_group_name + ',CN=Users,' + test_win_domain_group_ldap_base + '\r\n'
|
||||||
|
|
||||||
|
- name: modify existing group again
|
||||||
|
win_domain_group:
|
||||||
|
name: '{{test_win_domain_group_name}}'
|
||||||
|
state: present
|
||||||
|
description: New Description
|
||||||
|
display_name: New Display Name
|
||||||
|
managed_by: Administrator
|
||||||
|
organizational_unit: CN=Users,{{test_win_domain_group_ldap_base}}
|
||||||
|
category: Security
|
||||||
|
scope: global
|
||||||
|
attributes:
|
||||||
|
mail: anothertest@email.com
|
||||||
|
protect: True
|
||||||
|
ignore_protection: True
|
||||||
|
register: modify_existing_again
|
||||||
|
|
||||||
|
- name: assert modify existing group again
|
||||||
|
assert:
|
||||||
|
that:
|
||||||
|
- not modify_existing_again|changed
|
||||||
|
|
||||||
|
- name: fail change managed_by to invalid user
|
||||||
|
win_domain_group:
|
||||||
|
name: '{{test_win_domain_group_name}}'
|
||||||
|
state: present
|
||||||
|
scope: global
|
||||||
|
managed_by: fake user
|
||||||
|
register: fail_invalid_managed_by_user
|
||||||
|
failed_when: fail_invalid_managed_by_user.msg != 'failed to find managed_by user or group fake user to be used for comparison'
|
||||||
|
|
||||||
|
- name: fail delete group with protection mode on
|
||||||
|
win_domain_group:
|
||||||
|
name: '{{test_win_domain_group_name}}'
|
||||||
|
state: absent
|
||||||
|
register: fail_delete_with_protection
|
||||||
|
failed_when: fail_delete_with_protection.msg != 'cannot delete group ' + test_win_domain_group_name + ' when ProtectedFromAccidentalDeletion is turned on, run this module with ignore_protection=true to override this'
|
||||||
|
|
||||||
|
- name: delete group with protection mode on
|
||||||
|
win_domain_group:
|
||||||
|
name: '{{test_win_domain_group_name}}'
|
||||||
|
state: absent
|
||||||
|
ignore_protection: True
|
||||||
|
register: delete_with_force
|
||||||
|
|
||||||
|
- name: get actual delete group with protection mode on
|
||||||
|
win_command: powershell.exe "Import-Module ActiveDirectory; Get-ADGroup -Identity '{{test_win_domain_group_name}}'"
|
||||||
|
register: delete_with_force_actual
|
||||||
|
ignore_errors: True
|
||||||
|
|
||||||
|
- name: assert delete group with protection mode on
|
||||||
|
assert:
|
||||||
|
that:
|
||||||
|
- delete_with_force|changed
|
||||||
|
- delete_with_force_actual.rc == 1
|
||||||
|
|
||||||
|
- name: ensure the test group is deleted after the test
|
||||||
|
win_domain_group:
|
||||||
|
name: '{{test_win_domain_group_name}}'
|
||||||
|
state: absent
|
||||||
|
ignore_protection: True
|
Loading…
Reference in a new issue