You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
ansible/test/support/windows-integration/plugins/modules/win_user.ps1

274 lines
10 KiB
PowerShell

#!powershell
# Copyright: (c) 2014, Paul Durivage <paul.durivage@rackspace.com>
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
#AnsibleRequires -CSharpUtil Ansible.AccessToken
#Requires -Module Ansible.ModuleUtils.Legacy
########
$ADS_UF_PASSWD_CANT_CHANGE = 64
$ADS_UF_DONT_EXPIRE_PASSWD = 65536
$adsi = [ADSI]"WinNT://$env:COMPUTERNAME"
function Get-User($user) {
$adsi.Children | Where-Object {$_.SchemaClassName -eq 'user' -and $_.Name -eq $user }
return
}
function Get-UserFlag($user, $flag) {
If ($user.UserFlags[0] -band $flag) {
$true
}
Else {
$false
}
}
function Set-UserFlag($user, $flag) {
$user.UserFlags = ($user.UserFlags[0] -BOR $flag)
}
function Clear-UserFlag($user, $flag) {
$user.UserFlags = ($user.UserFlags[0] -BXOR $flag)
}
function Get-Group($grp) {
$adsi.Children | Where-Object { $_.SchemaClassName -eq 'Group' -and $_.Name -eq $grp }
return
}
Function Test-LocalCredential {
param([String]$Username, [String]$Password)
try {
$handle = [Ansible.AccessToken.TokenUtil]::LogonUser($Username, $null, $Password, "Network", "Default")
$handle.Dispose()
$valid_credentials = $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
)
if ($_.Exception.NativeErrorCode -eq 0x0000052E) {
# ERROR_LOGON_FAILURE - the user or pass was incorrect
$valid_credentials = $false
} elseif ($_.Exception.NativeErrorCode -in $success_codes) {
$valid_credentials = $true
} else {
# an unknown failure, reraise exception
throw $_
}
}
return $valid_credentials
}
########
$params = Parse-Args $args;
$result = @{
changed = $false
};
$username = Get-AnsibleParam -obj $params -name "name" -type "str" -failifempty $true
$fullname = Get-AnsibleParam -obj $params -name "fullname" -type "str"
$description = Get-AnsibleParam -obj $params -name "description" -type "str"
$password = Get-AnsibleParam -obj $params -name "password" -type "str"
$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"
$password_expired = Get-AnsibleParam -obj $params -name "password_expired" -type "bool"
$password_never_expires = Get-AnsibleParam -obj $params -name "password_never_expires" -type "bool"
$user_cannot_change_password = Get-AnsibleParam -obj $params -name "user_cannot_change_password" -type "bool"
$account_disabled = Get-AnsibleParam -obj $params -name "account_disabled" -type "bool"
$account_locked = Get-AnsibleParam -obj $params -name "account_locked" -type "bool"
$groups = Get-AnsibleParam -obj $params -name "groups"
$groups_action = Get-AnsibleParam -obj $params -name "groups_action" -type "str" -default "replace" -validateset "add","remove","replace"
If ($null -ne $account_locked -and $account_locked) {
Fail-Json $result "account_locked must be set to 'no' if provided"
}
If ($null -ne $groups) {
If ($groups -is [System.String]) {
[string[]]$groups = $groups.Split(",")
}
ElseIf ($groups -isnot [System.Collections.IList]) {
Fail-Json $result "groups must be a string or array"
}
$groups = $groups | ForEach-Object { ([string]$_).Trim() } | Where-Object { $_ }
If ($null -eq $groups) {
$groups = @()
}
}
$user_obj = Get-User $username
If ($state -eq 'present') {
# Add or update user
try {
If (-not $user_obj) {
$user_obj = $adsi.Create("User", $username)
If ($null -ne $password) {
$user_obj.SetPassword($password)
}
$user_obj.SetInfo()
$result.changed = $true
}
ElseIf (($null -ne $password) -and ($update_password -eq 'always')) {
# ValidateCredentials will fail if either of these are true- just force update...
If($user_obj.AccountDisabled -or $user_obj.PasswordExpired) {
$password_match = $false
}
Else {
try {
$password_match = Test-LocalCredential -Username $username -Password $password
} catch [System.ComponentModel.Win32Exception] {
Fail-Json -obj $result -message "Failed to validate the user's credentials: $($_.Exception.Message)"
}
}
If (-not $password_match) {
$user_obj.SetPassword($password)
$result.changed = $true
}
}
If (($null -ne $fullname) -and ($fullname -ne $user_obj.FullName[0])) {
$user_obj.FullName = $fullname
$result.changed = $true
}
If (($null -ne $description) -and ($description -ne $user_obj.Description[0])) {
$user_obj.Description = $description
$result.changed = $true
}
If (($null -ne $password_expired) -and ($password_expired -ne ($user_obj.PasswordExpired | ConvertTo-Bool))) {
$user_obj.PasswordExpired = If ($password_expired) { 1 } Else { 0 }
$result.changed = $true
}
If (($null -ne $password_never_expires) -and ($password_never_expires -ne (Get-UserFlag $user_obj $ADS_UF_DONT_EXPIRE_PASSWD))) {
If ($password_never_expires) {
Set-UserFlag $user_obj $ADS_UF_DONT_EXPIRE_PASSWD
}
Else {
Clear-UserFlag $user_obj $ADS_UF_DONT_EXPIRE_PASSWD
}
$result.changed = $true
}
If (($null -ne $user_cannot_change_password) -and ($user_cannot_change_password -ne (Get-UserFlag $user_obj $ADS_UF_PASSWD_CANT_CHANGE))) {
If ($user_cannot_change_password) {
Set-UserFlag $user_obj $ADS_UF_PASSWD_CANT_CHANGE
}
Else {
Clear-UserFlag $user_obj $ADS_UF_PASSWD_CANT_CHANGE
}
$result.changed = $true
}
If (($null -ne $account_disabled) -and ($account_disabled -ne $user_obj.AccountDisabled)) {
$user_obj.AccountDisabled = $account_disabled
$result.changed = $true
}
If (($null -ne $account_locked) -and ($account_locked -ne $user_obj.IsAccountLocked)) {
$user_obj.IsAccountLocked = $account_locked
$result.changed = $true
}
If ($result.changed) {
$user_obj.SetInfo()
}
If ($null -ne $groups) {
[string[]]$current_groups = $user_obj.Groups() | ForEach-Object { $_.GetType().InvokeMember("Name", "GetProperty", $null, $_, $null) }
If (($groups_action -eq "remove") -or ($groups_action -eq "replace")) {
ForEach ($grp in $current_groups) {
If ((($groups_action -eq "remove") -and ($groups -contains $grp)) -or (($groups_action -eq "replace") -and ($groups -notcontains $grp))) {
$group_obj = Get-Group $grp
If ($group_obj) {
$group_obj.Remove($user_obj.Path)
$result.changed = $true
}
Else {
Fail-Json $result "group '$grp' not found"
}
}
}
}
If (($groups_action -eq "add") -or ($groups_action -eq "replace")) {
ForEach ($grp in $groups) {
If ($current_groups -notcontains $grp) {
$group_obj = Get-Group $grp
If ($group_obj) {
$group_obj.Add($user_obj.Path)
$result.changed = $true
}
Else {
Fail-Json $result "group '$grp' not found"
}
}
}
}
}
}
catch {
Fail-Json $result $_.Exception.Message
}
}
ElseIf ($state -eq 'absent') {
# Remove user
try {
If ($user_obj) {
$username = $user_obj.Name.Value
$adsi.delete("User", $user_obj.Name.Value)
$result.changed = $true
$result.msg = "User '$username' deleted successfully"
$user_obj = $null
} else {
$result.msg = "User '$username' was not found"
}
}
catch {
Fail-Json $result $_.Exception.Message
}
}
try {
If ($user_obj -and $user_obj -is [System.DirectoryServices.DirectoryEntry]) {
$user_obj.RefreshCache()
$result.name = $user_obj.Name[0]
$result.fullname = $user_obj.FullName[0]
$result.path = $user_obj.Path
$result.description = $user_obj.Description[0]
$result.password_expired = ($user_obj.PasswordExpired | ConvertTo-Bool)
$result.password_never_expires = (Get-UserFlag $user_obj $ADS_UF_DONT_EXPIRE_PASSWD)
$result.user_cannot_change_password = (Get-UserFlag $user_obj $ADS_UF_PASSWD_CANT_CHANGE)
$result.account_disabled = $user_obj.AccountDisabled
$result.account_locked = $user_obj.IsAccountLocked
$result.sid = (New-Object System.Security.Principal.SecurityIdentifier($user_obj.ObjectSid.Value, 0)).Value
$user_groups = @()
ForEach ($grp in $user_obj.Groups()) {
$group_result = @{
name = $grp.GetType().InvokeMember("Name", "GetProperty", $null, $grp, $null)
path = $grp.GetType().InvokeMember("ADsPath", "GetProperty", $null, $grp, $null)
}
$user_groups += $group_result;
}
$result.groups = $user_groups
$result.state = "present"
}
Else {
$result.name = $username
if ($state -eq 'query') {
$result.msg = "User '$username' was not found"
}
$result.state = "absent"
}
}
catch {
Fail-Json $result $_.Exception.Message
}
Exit-Json $result