From 71ff77e51fb8f713a8d76c76be9c5636b5e63f47 Mon Sep 17 00:00:00 2001 From: Jordan Borean Date: Mon, 8 Jan 2018 09:38:13 +1000 Subject: [PATCH] win_domain_user: added ability to explicitly set credentials and user when interacting with AD (#34562) --- .../modules/windows/win_domain_group.ps1 | 6 +- .../modules/windows/win_domain_group.py | 20 ++-- .../modules/windows/win_domain_user.ps1 | 111 +++++++++--------- .../modules/windows/win_domain_user.py | 48 +++++--- 4 files changed, 102 insertions(+), 83 deletions(-) diff --git a/lib/ansible/modules/windows/win_domain_group.ps1 b/lib/ansible/modules/windows/win_domain_group.ps1 index e83a6fec4d6..713e5e9ecac 100644 --- a/lib/ansible/modules/windows/win_domain_group.ps1 +++ b/lib/ansible/modules/windows/win_domain_group.ps1 @@ -25,7 +25,7 @@ $organizational_unit = Get-AnsibleParam -obj $params -name "organizational_unit" $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 -$server = Get-AnsibleParam -obj $params -name "server" -type "str" +$domain_server = Get-AnsibleParam -obj $params -name "domain_server" -type "str" $result = @{ changed = $false @@ -46,8 +46,8 @@ if ($domain_username -ne $null) { $credential = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList $domain_username, $domain_password $extra_args.Credential = $credential } -if ($server -ne $null) { - $extra_args.Server = $server +if ($domain_server -ne $null) { + $extra_args.Server = $domain_server } try { diff --git a/lib/ansible/modules/windows/win_domain_group.py b/lib/ansible/modules/windows/win_domain_group.py index b70a28b917d..dd7435d7479 100644 --- a/lib/ansible/modules/windows/win_domain_group.py +++ b/lib/ansible/modules/windows/win_domain_group.py @@ -45,6 +45,13 @@ options: domain_password: description: - The password for C(username). + 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. + version_added: '2.5' ignore_protection: description: - Will ignore the C(ProtectedFromAccidentalDeletion) flag when deleting or @@ -89,13 +96,6 @@ options: - If C(state=absent) this module will delete the group if it exists default: present choices: [ absent, present ] - 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. - version_added: '2.5' notes: - This must be run on a host that has the ActiveDirectory powershell module installed. @@ -146,10 +146,10 @@ EXAMPLES = r''' - name: add group and specify the AD domain services to use for the create win_domain_group: name: Test Group - domain_admin_user: user@CORP.ANSIBLE.COM - domain_admin_password: Password01! + domain_username: user@CORP.ANSIBLE.COM + domain_password: Password01! + domain_server: corp-DC12.corp.ansible.com scope: domainlocal - server: corp-DC12.corp.ansible.com ''' RETURN = r''' diff --git a/lib/ansible/modules/windows/win_domain_user.ps1 b/lib/ansible/modules/windows/win_domain_user.ps1 index c00e89c0e00..c259201639b 100644 --- a/lib/ansible/modules/windows/win_domain_user.ps1 +++ b/lib/ansible/modules/windows/win_domain_user.ps1 @@ -1,23 +1,10 @@ #!powershell # 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 . -# WANT_JSON -# POWERSHELL_COMMON +# 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 } @@ -30,6 +17,8 @@ $result = @{ password_updated = $false } +$ErrorActionPreference = "Stop" + $params = Parse-Args $args -supports_check_mode $true $check_mode = Get-AnsibleParam -obj $params -name "_ansible_check_mode" -default $false @@ -37,6 +26,9 @@ $check_mode = Get-AnsibleParam -obj $params -name "_ansible_check_mode" -default $state = Get-AnsibleParam -obj $params -name "state" -type "str" -default "present" -validateset "present","absent","query" $update_password = Get-AnsibleParam -obj $params -name "update_password" -type "str" -default "always" -validateset "always","on_create" $groups_action = Get-AnsibleParam -obj $params -name "groups_action" -type "str" -default "replace" -validateset "add","remove","replace" +$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) +$domain_server = Get-AnsibleParam -obj $params -name "domain_server" -type "str" # User account parameters $username = Get-AnsibleParam -obj $params -name "name" -type "str" -failifempty $true @@ -75,8 +67,18 @@ If (($password_expired -ne $null) -and ($password_never_expires -ne $null)) { Fail-Json $result "password_expired and password_never_expires are mutually exclusive but have both been set" } +$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 +} +if ($domain_server -ne $null) { + $extra_args.Server = $domain_server +} + try { - $user_obj = Get-ADUser -Identity $username -Properties * + $user_obj = Get-ADUser -Identity $username -Properties * @extra_args } catch { $user_obj = $null @@ -90,64 +92,64 @@ If ($state -eq 'present') { # If the account does not exist, create it If (-not $user_obj) { If ($path -ne $null){ - New-ADUser -Name $username -Path $path -WhatIf:$check_mode + New-ADUser -Name $username -Path $path -WhatIf:$check_mode @extra_args } Else { - New-ADUser -Name $username -WhatIf:$check_mode + New-ADUser -Name $username -WhatIf:$check_mode @extra_args } $new_user = $true $result.changed = $true If ($check_mode) { Exit-Json $result } - $user_obj = Get-ADUser -Identity $username -Properties * + $user_obj = Get-ADUser -Identity $username -Properties * @extra_args } # Set the password if required If ($password -and (($new_user -and $update_password -eq "on_create") -or $update_password -eq "always")) { $secure_password = ConvertTo-SecureString $password -AsPlainText -Force - Set-ADAccountPassword -Identity $username -Reset:$true -Confirm:$false -NewPassword $secure_password -WhatIf:$check_mode - $user_obj = Get-ADUser -Identity $username -Properties * + Set-ADAccountPassword -Identity $username -Reset:$true -Confirm:$false -NewPassword $secure_password -WhatIf:$check_mode @extra_args + $user_obj = Get-ADUser -Identity $username -Properties * @extra_args $result.password_updated = $true $result.changed = $true } # Configure password policies If (($password_never_expires -ne $null) -and ($password_never_expires -ne $user_obj.PasswordNeverExpires)) { - Set-ADUser -Identity $username -PasswordNeverExpires $password_never_expires -WhatIf:$check_mode - $user_obj = Get-ADUser -Identity $username -Properties * + Set-ADUser -Identity $username -PasswordNeverExpires $password_never_expires -WhatIf:$check_mode @extra_args + $user_obj = Get-ADUser -Identity $username -Properties * @extra_args $result.changed = $true } If (($password_expired -ne $null) -and ($password_expired -ne $user_obj.PasswordExpired)) { - Set-ADUser -Identity $username -ChangePasswordAtLogon $password_expired -WhatIf:$check_mode - $user_obj = Get-ADUser -Identity $username -Properties * + Set-ADUser -Identity $username -ChangePasswordAtLogon $password_expired -WhatIf:$check_mode @extra_args + $user_obj = Get-ADUser -Identity $username -Properties * @extra_args $result.changed = $true } If (($user_cannot_change_password -ne $null) -and ($user_cannot_change_password -ne $user_obj.CannotChangePassword)) { - Set-ADUser -Identity $username -CannotChangePassword $user_cannot_change_password -WhatIf:$check_mode - $user_obj = Get-ADUser -Identity $username -Properties * + Set-ADUser -Identity $username -CannotChangePassword $user_cannot_change_password -WhatIf:$check_mode @extra_args + $user_obj = Get-ADUser -Identity $username -Properties * @extra_args $result.changed = $true } # Assign other account settings If (($upn -ne $null) -and ($upn -ne $user_obj.UserPrincipalName)) { - Set-ADUser -Identity $username -UserPrincipalName $upn -WhatIf:$check_mode - $user_obj = Get-ADUser -Identity $username -Properties * + Set-ADUser -Identity $username -UserPrincipalName $upn -WhatIf:$check_mode @extra_args + $user_obj = Get-ADUser -Identity $username -Properties * @extra_args $result.changed = $true } If (($description -ne $null) -and ($description -ne $user_obj.Description)) { - Set-ADUser -Identity $username -description $description -WhatIf:$check_mode - $user_obj = Get-ADUser -Identity $username -Properties * + Set-ADUser -Identity $username -description $description -WhatIf:$check_mode @extra_args + $user_obj = Get-ADUser -Identity $username -Properties * @extra_args $result.changed = $true } If ($enabled -ne $user_obj.Enabled) { - Set-ADUser -Identity $username -Enabled $enabled -WhatIf:$check_mode - $user_obj = Get-ADUser -Identity $username -Properties * + Set-ADUser -Identity $username -Enabled $enabled -WhatIf:$check_mode @extra_args + $user_obj = Get-ADUser -Identity $username -Properties * @extra_args $result.changed = $true } If ((-not $account_locked) -and ($user_obj.LockedOut -eq $true)) { - Unlock-ADAccount -Identity $username -WhatIf:$check_mode - $user_obj = Get-ADUser -Identity $username -Properties * + Unlock-ADAccount -Identity $username -WhatIf:$check_mode @extra_args + $user_obj = Get-ADUser -Identity $username -Properties * @extra_args $result.changed = $true } @@ -158,17 +160,16 @@ If ($state -eq 'present') { } $value = $user_info[$key] If ($value -ne $user_obj.$key) { - $expression = "Set-ADUser -Identity $username -$key '$value'" - If (-not $check_mode) { - Invoke-Expression $expression - } + $set_args = $extra_args.Clone() + $set_args.$key = $value + Set-ADUser -Identity $username -WhatIf:$check_mode @set_args $result.changed = $true - $user_obj = Get-ADUser -Identity $username -Properties * + $user_obj = Get-ADUser -Identity $username -Properties * @extra_args } } # Set additional attributes - $set_args = @{} + $set_args = $extra_args.Clone() $run_change = $false if ($attributes -ne $null) { $add_attributes = @{} @@ -213,11 +214,11 @@ If ($state -eq 'present') { $groups = @() Foreach ($group in $group_list) { - $groups += (Get-ADGroup -Identity $group).DistinguishedName + $groups += (Get-ADGroup -Identity $group @extra_args).DistinguishedName } $assigned_groups = @() - Foreach ($group in (Get-ADPrincipalGroupMembership -Identity $username)) { + Foreach ($group in (Get-ADPrincipalGroupMembership -Identity $username @extra_args)) { $assigned_groups += $group.DistinguishedName } @@ -225,8 +226,8 @@ If ($state -eq 'present') { "add" { Foreach ($group in $groups) { If (-not ($assigned_groups -Contains $group)) { - Add-ADGroupMember -Identity $group -Members $username -WhatIf:$check_mode - $user_obj = Get-ADUser -Identity $username -Properties * + Add-ADGroupMember -Identity $group -Members $username -WhatIf:$check_mode @extra_args + $user_obj = Get-ADUser -Identity $username -Properties * @extra_args $result.changed = $true } } @@ -234,8 +235,8 @@ If ($state -eq 'present') { "remove" { Foreach ($group in $groups) { If ($assigned_groups -Contains $group) { - Remove-ADGroupMember -Identity $group -Members $username -Confirm:$false -WhatIf:$check_mode - $user_obj = Get-ADUser -Identity $username -Properties * + Remove-ADGroupMember -Identity $group -Members $username -Confirm:$false -WhatIf:$check_mode @extra_args + $user_obj = Get-ADUser -Identity $username -Properties * @extra_args $result.changed = $true } } @@ -243,15 +244,15 @@ If ($state -eq 'present') { "replace" { Foreach ($group in $assigned_groups) { If (($group -ne $user_obj.PrimaryGroup) -and -not ($groups -Contains $group)) { - Remove-ADGroupMember -Identity $group -Members $username -Confirm:$false -WhatIf:$check_mode - $user_obj = Get-ADUser -Identity $username -Properties * + Remove-ADGroupMember -Identity $group -Members $username -Confirm:$false -WhatIf:$check_mode @extra_args + $user_obj = Get-ADUser -Identity $username -Properties * @extra_args $result.changed = $true } } Foreach ($group in $groups) { If (-not ($assigned_groups -Contains $group)) { - Add-ADGroupMember -Identity $group -Members $username -WhatIf:$check_mode - $user_obj = Get-ADUser -Identity $username -Properties * + Add-ADGroupMember -Identity $group -Members $username -WhatIf:$check_mode @extra_args + $user_obj = Get-ADUser -Identity $username -Properties * @extra_args $result.changed = $true } } @@ -267,7 +268,7 @@ If ($state -eq 'present') { # Ensure user does not exist try { If ($user_obj) { - Remove-ADUser $user_obj -Confirm:$false -WhatIf:$check_mode + Remove-ADUser $user_obj -Confirm:$false -WhatIf:$check_mode @extra_args $result.changed = $true If ($check_mode) { Exit-Json $result @@ -282,7 +283,7 @@ If ($state -eq 'present') { try { If ($user_obj) { - $user_obj = Get-ADUser -Identity $username -Properties * + $user_obj = Get-ADUser -Identity $username -Properties * @extra_args $result.name = $user_obj.Name $result.firstname = $user_obj.GivenName $result.surname = $user_obj.Surname @@ -303,7 +304,7 @@ try { $result.sid = [string]$user_obj.SID $result.upn = $user_obj.UserPrincipalName $user_groups = @() - Foreach ($group in (Get-ADPrincipalGroupMembership $username)) { + Foreach ($group in (Get-ADPrincipalGroupMembership $username @extra_args)) { $user_groups += $group.name } $result.groups = $user_groups diff --git a/lib/ansible/modules/windows/win_domain_user.py b/lib/ansible/modules/windows/win_domain_user.py index 42194e49a8d..ce076df39cc 100644 --- a/lib/ansible/modules/windows/win_domain_user.py +++ b/lib/ansible/modules/windows/win_domain_user.py @@ -1,20 +1,9 @@ #!/usr/bin/python # -*- coding: utf-8 -*- -# + # 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 . + +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) # this is a windows documentation stub. actual code lives in the .ps1 # file of the same name @@ -155,10 +144,28 @@ options: parameters, e.g. C(telephoneNumber). - See the examples on how to format this parameter. version_added: "2.5" + 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. + version_added: '2.5' + domain_password: + description: + - The password for C(username). + version_added: '2.5' + 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. + version_added: '2.5' notes: - Works with Windows 2012R2 and newer. - If running on a server that is not a Domain Controller, credential - delegation through CredSSP or Kerberos with delegation must be used. + delegation through CredSSP or Kerberos with delegation must be used or the + I(domain_username), I(domain_password) must be set. - Note that some individuals have confirmed successful operation on Windows 2008R2 servers with AD and AD Web Services enabled, but this has not received the same degree of testing as Windows 2012R2. @@ -185,6 +192,17 @@ EXAMPLES = r''' attributes: telephoneNumber: 555-123456 +- name: Ensure user bob is created and use custom credentials to create the user + win_domain_user: + name: bob + firstname: Bob + surname: Smith + password: B0bP4ssw0rd + state: present + domain_username: DOMAIN\admin-account + domain_password: SomePas2w0rd + domain_server: domain@DOMAIN.COM + - name: Ensure user bob is present in OU ou=test,dc=domain,dc=local win_domain_user: name: bob