Make win_domain_user idempotent for password changes (#58383)

* Make win_domain_user idempotent for passwordchanges

* Add changelog fragment

* Use test-credentials function from win_user.

* Split domain from username

* Update win_domain_user.ps1

* Fix ci

* Update win_domain_user.ps1

Fix ci

* Implement review

* Logic cleanup and remove securestring

* Fix typo

* fix syntax

fix syntax

* Use AD object instead of user input as requested by review

* migrate to Ansible.AccessToken
This commit is contained in:
Klaus Frank 2019-08-18 22:17:41 +02:00 committed by Jordan Borean
parent 811153afb1
commit fb2c1d4577
3 changed files with 73 additions and 13 deletions

View file

@ -0,0 +1,3 @@
---
minor_changes:
- "win_domain_user - Allow to only set password when it actually changed (https://github.com/ansible/ansible/issues/58246)"

View file

@ -3,6 +3,53 @@
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
#Requires -Module Ansible.ModuleUtils.Legacy
#AnsibleRequires -CSharpUtil Ansible.AccessToken
Function Test-Credential {
param(
[String]$Username,
[String]$Password,
[String]$Domain = $null
)
if (($Username.ToCharArray()) -contains [char]'@') {
# UserPrincipalName
$Domain = $null # force $Domain to be null, to prevent undefined behaviour, as a domain name is already included in the username
} elseif (($Username.ToCharArray()) -contains [char]'\') {
# Pre Win2k Account Name
$Username = ($Username -split '\')[0]
$Domain = ($Username -split '\')[1]
} else {
# No domain provided, so maybe local user, or domain specified separately.
}
try {
$handle = [Ansible.AccessToken.TokenUtil]::LogonUser($Username, $Domain, $Password, "Network", "Default")
$handle.Dispose()
return $true
} catch [Ansible.AccessToken.Win32Exception] {
# following errors indicate the creds are correct but the user was
# unable to log on for other reasons, which we don't care about
$success_codes = @(
0x0000052F, # ERROR_ACCOUNT_RESTRICTION
0x00000530, # ERROR_INVALID_LOGON_HOURS
0x00000531, # ERROR_INVALID_WORKSTATION
0x00000569 # ERROR_LOGON_TYPE_GRANTED
)
$failed_codes = @(
0x0000052E, # ERROR_LOGON_FAILURE
0x00000532 # ERROR_PASSWORD_EXPIRED
)
if ($_.Exception.NativeErrorCode -in $failed_codes) {
return $false
} elseif ($_.Exception.NativeErrorCode -in $success_codes) {
return $true
} else {
# an unknown failure, reraise exception
throw $_
}
}
}
try {
Import-Module ActiveDirectory
@ -24,7 +71,7 @@ $check_mode = Get-AnsibleParam -obj $params -name "_ansible_check_mode" -default
# Module control parameters
$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"
$update_password = Get-AnsibleParam -obj $params -name "update_password" -type "str" -default "always" -validateset "always","on_create","when_changed"
$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 ($null -ne $domain_username)
@ -105,13 +152,25 @@ If ($state -eq 'present') {
$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 @extra_args
$user_obj = Get-ADUser -Identity $username -Properties * @extra_args
$result.password_updated = $true
$result.changed = $true
If ($password) {
# Don't uncecessary check for working credentials.
# Set the password if we need to.
# For new_users there is also no difference between always and when_changed
# so we don't need to differentiate between this two states.
If ($new_user -or ($update_password -eq "always")) {
$set_new_credentials = $true
} elseif ($update_password -eq "when_changed") {
$set_new_credentials = -not (Test-Credential -Username $user_obj.UserPrincipalName -Password $password)
} else {
$set_new_credentials = $false
}
If ($set_new_credentials) {
$secure_password = ConvertTo-SecureString $password -AsPlainText -Force
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

View file

@ -73,13 +73,11 @@ options:
type: str
update_password:
description:
- C(always) will update passwords if they differ.
- C(always) will always update passwords.
- C(on_create) will only set the password for newly created users.
- Note that C(always) will always report an Ansible status of 'changed'
because we cannot determine whether the new password differs from
the old password.
- C(when_changed) will only set the password when changed (added in ansible 2.9).
type: str
choices: [ always, on_create ]
choices: [ always, on_create, when_changed ]
default: always
password_expired:
description: