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
pull/60749/head
Klaus Frank 5 years ago committed by Jordan Borean
parent 811153afb1
commit fb2c1d4577

@ -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)"

@ -3,6 +3,53 @@
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) # GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
#Requires -Module Ansible.ModuleUtils.Legacy #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 { try {
Import-Module ActiveDirectory Import-Module ActiveDirectory
@ -24,7 +71,7 @@ $check_mode = Get-AnsibleParam -obj $params -name "_ansible_check_mode" -default
# Module control parameters # Module control parameters
$state = Get-AnsibleParam -obj $params -name "state" -type "str" -default "present" -validateset "present","absent","query" $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" $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_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_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 $user_obj = Get-ADUser -Identity $username -Properties * @extra_args
} }
# Set the password if required If ($password) {
If ($password -and (($new_user -and $update_password -eq "on_create") -or $update_password -eq "always")) { # Don't uncecessary check for working credentials.
$secure_password = ConvertTo-SecureString $password -AsPlainText -Force # Set the password if we need to.
Set-ADAccountPassword -Identity $username -Reset:$true -Confirm:$false -NewPassword $secure_password -WhatIf:$check_mode @extra_args # For new_users there is also no difference between always and when_changed
$user_obj = Get-ADUser -Identity $username -Properties * @extra_args # so we don't need to differentiate between this two states.
$result.password_updated = $true If ($new_user -or ($update_password -eq "always")) {
$result.changed = $true $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 # Configure password policies

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

Loading…
Cancel
Save