PageRenderTime 33ms CodeModel.GetById 13ms app.highlight 16ms RepoModel.GetById 1ms app.codeStats 0ms

/lib/ansible/modules/windows/win_user.ps1

https://github.com/debfx/ansible
Powershell | 311 lines | 269 code | 29 blank | 13 comment | 46 complexity | 0ee4e8bd5d96ca4c8484398a39987fb5 MD5 | raw file
  1#!powershell
  2
  3# Copyright: (c) 2014, Paul Durivage <paul.durivage@rackspace.com>
  4# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
  5
  6#Requires -Module Ansible.ModuleUtils.Legacy
  7
  8########
  9$ADS_UF_PASSWD_CANT_CHANGE = 64
 10$ADS_UF_DONT_EXPIRE_PASSWD = 65536
 11$LOGON32_LOGON_NETWORK = 3
 12$LOGON32_PROVIDER_DEFAULT = 0
 13
 14$adsi = [ADSI]"WinNT://$env:COMPUTERNAME"
 15
 16function Get-User($user) {
 17    $adsi.Children | where {$_.SchemaClassName -eq 'user' -and $_.Name -eq $user }
 18    return
 19}
 20
 21function Get-UserFlag($user, $flag) {
 22    If ($user.UserFlags[0] -band $flag) {
 23        $true
 24    }
 25    Else {
 26        $false
 27    }
 28}
 29
 30function Set-UserFlag($user, $flag) { 
 31    $user.UserFlags = ($user.UserFlags[0] -BOR $flag)
 32}
 33
 34function Clear-UserFlag($user, $flag) {
 35    $user.UserFlags = ($user.UserFlags[0] -BXOR $flag)
 36}
 37
 38function Get-Group($grp) {
 39    $adsi.Children | where { $_.SchemaClassName -eq 'Group' -and $_.Name -eq $grp }
 40    return
 41}
 42
 43Function Test-LocalCredential {
 44    param([String]$Username, [String]$Password)
 45
 46    $platform_util = @'
 47using System;
 48using System.Runtime.InteropServices;
 49
 50namespace Ansible
 51{
 52    public class WinUserPInvoke
 53    {
 54        [DllImport("advapi32.dll", SetLastError = true)]
 55        public static extern bool LogonUser(
 56            string lpszUsername,
 57            string lpszDomain,
 58            string lpszPassword,
 59            UInt32 dwLogonType,
 60            UInt32 dwLogonProvider,
 61            out IntPtr phToken);
 62
 63        [DllImport("kernel32.dll", SetLastError = true)]
 64        public static extern bool CloseHandle(
 65            IntPtr hObject);
 66    }
 67}
 68'@
 69
 70    $original_tmp = $env:TMP
 71    $env:TMP = $_remote_tmp
 72    Add-Type -TypeDefinition $platform_util
 73    $env:TMP = $original_tmp
 74
 75    $handle = [IntPtr]::Zero
 76    $logon_res = [Ansible.WinUserPInvoke]::LogonUser($Username, $null, $Password,
 77        $LOGON32_LOGON_NETWORK, $LOGON32_PROVIDER_DEFAULT, [Ref]$handle)
 78
 79    if ($logon_res) {
 80        $valid_credentials = $true
 81        [Ansible.WinUserPInvoke]::CloseHandle($handle) > $null
 82    } else {
 83        $err_code = [System.Runtime.InteropServices.Marshal]::GetLastWin32Error()
 84        # following errors indicate the creds are correct but the user was
 85        # unable to log on for other reasons, which we don't care about
 86        $success_codes = @(
 87            0x0000052F,  # ERROR_ACCOUNT_RESTRICTION
 88            0x00000530,  # ERROR_INVALID_LOGON_HOURS
 89            0x00000531,  # ERROR_INVALID_WORKSTATION
 90            0x00000569  # ERROR_LOGON_TYPE_GRANTED
 91        )
 92
 93        if ($err_code -eq 0x0000052E) {
 94            # ERROR_LOGON_FAILURE - the user or pass was incorrect
 95            $valid_credentials = $false
 96        } elseif ($err_code -in $success_codes) {
 97            $valid_credentials = $true
 98        } else {
 99            # an unknown failure, raise an Exception for this
100            $win32_exp = New-Object -TypeName System.ComponentModel.Win32Exception -ArgumentList $err_code
101            $err_msg = "LogonUserW failed: $($win32_exp.Message) (Win32ErrorCode: $err_code)"
102            throw New-Object -TypeName System.ComponentModel.Win32Exception -ArgumentList $err_code, $err_msg
103        }
104    }
105
106    return $valid_credentials
107}
108
109########
110
111$params = Parse-Args $args;
112$_remote_tmp = Get-AnsibleParam $params "_ansible_remote_tmp" -type "path" -default $env:TMP
113
114$result = @{
115    changed = $false
116};
117
118$username = Get-AnsibleParam -obj $params -name "name" -type "str" -failifempty $true
119$fullname = Get-AnsibleParam -obj $params -name "fullname" -type "str"
120$description = Get-AnsibleParam -obj $params -name "description" -type "str"
121$password = Get-AnsibleParam -obj $params -name "password" -type "str"
122$state = Get-AnsibleParam -obj $params -name "state" -type "str" -default "present" -validateset "present","absent","query"
123$update_password = Get-AnsibleParam -obj $params -name "update_password" -type "str" -default "always" -validateset "always","on_create"
124$password_expired = Get-AnsibleParam -obj $params -name "password_expired" -type "bool"
125$password_never_expires = Get-AnsibleParam -obj $params -name "password_never_expires" -type "bool"
126$user_cannot_change_password = Get-AnsibleParam -obj $params -name "user_cannot_change_password" -type "bool"
127$account_disabled = Get-AnsibleParam -obj $params -name "account_disabled" -type "bool"
128$account_locked = Get-AnsibleParam -obj $params -name "account_locked" -type "bool"
129$groups = Get-AnsibleParam -obj $params -name "groups"
130$groups_action = Get-AnsibleParam -obj $params -name "groups_action" -type "str" -default "replace" -validateset "add","remove","replace"
131
132If ($account_locked -ne $null -and $account_locked) {
133    Fail-Json $result "account_locked must be set to 'no' if provided"
134}
135
136If ($groups -ne $null) {
137    If ($groups -is [System.String]) {
138        [string[]]$groups = $groups.Split(",")
139    }
140    ElseIf ($groups -isnot [System.Collections.IList]) {
141        Fail-Json $result "groups must be a string or array"
142    }
143    $groups = $groups | ForEach { ([string]$_).Trim() } | Where { $_ }
144    If ($groups -eq $null) {
145        $groups = @()
146    }
147}
148
149$user_obj = Get-User $username
150
151If ($state -eq 'present') {
152    # Add or update user
153    try {
154        If (-not $user_obj) {
155            $user_obj = $adsi.Create("User", $username)
156            If ($password -ne $null) {
157                $user_obj.SetPassword($password)
158            }
159            $user_obj.SetInfo()
160            $result.changed = $true
161        }
162        ElseIf (($password -ne $null) -and ($update_password -eq 'always')) {
163            # ValidateCredentials will fail if either of these are true- just force update...
164            If($user_obj.AccountDisabled -or $user_obj.PasswordExpired) {
165                $password_match = $false
166            }
167            Else {
168                try {
169                    $password_match = Test-LocalCredential -Username $username -Password $password
170                } catch [System.ComponentModel.Win32Exception] {
171                    Fail-Json -obj $result -message "Failed to validate the user's credentials: $($_.Exception.Message)"
172                }
173            }
174
175            If (-not $password_match) {
176                $user_obj.SetPassword($password)
177                $result.changed = $true
178            }
179        }
180        If (($fullname -ne $null) -and ($fullname -ne $user_obj.FullName[0])) {
181            $user_obj.FullName = $fullname
182            $result.changed = $true
183        }
184        If (($description -ne $null) -and ($description -ne $user_obj.Description[0])) {
185            $user_obj.Description = $description
186            $result.changed = $true
187        }
188        If (($password_expired -ne $null) -and ($password_expired -ne ($user_obj.PasswordExpired | ConvertTo-Bool))) {
189            $user_obj.PasswordExpired = If ($password_expired) { 1 } Else { 0 }
190            $result.changed = $true
191        }
192        If (($password_never_expires -ne $null) -and ($password_never_expires -ne (Get-UserFlag $user_obj $ADS_UF_DONT_EXPIRE_PASSWD))) {
193            If ($password_never_expires) {
194                Set-UserFlag $user_obj $ADS_UF_DONT_EXPIRE_PASSWD
195            }
196            Else {
197                Clear-UserFlag $user_obj $ADS_UF_DONT_EXPIRE_PASSWD
198            }
199            $result.changed = $true
200        }
201        If (($user_cannot_change_password -ne $null) -and ($user_cannot_change_password -ne (Get-UserFlag $user_obj $ADS_UF_PASSWD_CANT_CHANGE))) {
202            If ($user_cannot_change_password) {
203                Set-UserFlag $user_obj $ADS_UF_PASSWD_CANT_CHANGE
204            }
205            Else {
206                Clear-UserFlag $user_obj $ADS_UF_PASSWD_CANT_CHANGE
207            }
208            $result.changed = $true
209        }
210        If (($account_disabled -ne $null) -and ($account_disabled -ne $user_obj.AccountDisabled)) {
211            $user_obj.AccountDisabled = $account_disabled
212            $result.changed = $true
213        }
214        If (($account_locked -ne $null) -and ($account_locked -ne $user_obj.IsAccountLocked)) {
215            $user_obj.IsAccountLocked = $account_locked
216            $result.changed = $true
217        }
218        If ($result.changed) {
219            $user_obj.SetInfo()
220        }
221        If ($null -ne $groups) {
222            [string[]]$current_groups = $user_obj.Groups() | ForEach { $_.GetType().InvokeMember("Name", "GetProperty", $null, $_, $null) }
223            If (($groups_action -eq "remove") -or ($groups_action -eq "replace")) {
224                ForEach ($grp in $current_groups) {
225                    If ((($groups_action -eq "remove") -and ($groups -contains $grp)) -or (($groups_action -eq "replace") -and ($groups -notcontains $grp))) {
226                        $group_obj = Get-Group $grp
227                        If ($group_obj) {
228                            $group_obj.Remove($user_obj.Path)
229                            $result.changed = $true
230                        }
231                        Else {
232                            Fail-Json $result "group '$grp' not found"
233                        }
234                    }
235                }
236            }
237            If (($groups_action -eq "add") -or ($groups_action -eq "replace")) {
238                ForEach ($grp in $groups) {
239                    If ($current_groups -notcontains $grp) {
240                        $group_obj = Get-Group $grp
241                        If ($group_obj) {
242                            $group_obj.Add($user_obj.Path)
243                            $result.changed = $true
244                        }
245                        Else {
246                            Fail-Json $result "group '$grp' not found"
247                        }
248                    }
249                }
250            }
251        }
252    }
253    catch {
254        Fail-Json $result $_.Exception.Message
255    }
256}
257ElseIf ($state -eq 'absent') {
258    # Remove user
259    try {
260        If ($user_obj) {
261            $username = $user_obj.Name.Value
262            $adsi.delete("User", $user_obj.Name.Value)
263            $result.changed = $true
264            $result.msg = "User '$username' deleted successfully"
265            $user_obj = $null
266        } else {
267            $result.msg = "User '$username' was not found"
268        }
269    }
270    catch {
271        Fail-Json $result $_.Exception.Message
272    }
273}
274
275try {
276    If ($user_obj -and $user_obj -is [System.DirectoryServices.DirectoryEntry]) {
277        $user_obj.RefreshCache()
278        $result.name = $user_obj.Name[0]
279        $result.fullname = $user_obj.FullName[0]
280        $result.path = $user_obj.Path
281        $result.description = $user_obj.Description[0]
282        $result.password_expired = ($user_obj.PasswordExpired | ConvertTo-Bool)
283        $result.password_never_expires = (Get-UserFlag $user_obj $ADS_UF_DONT_EXPIRE_PASSWD)
284        $result.user_cannot_change_password = (Get-UserFlag $user_obj $ADS_UF_PASSWD_CANT_CHANGE)
285        $result.account_disabled = $user_obj.AccountDisabled
286        $result.account_locked = $user_obj.IsAccountLocked
287        $result.sid = (New-Object System.Security.Principal.SecurityIdentifier($user_obj.ObjectSid.Value, 0)).Value
288        $user_groups = @()
289        ForEach ($grp in $user_obj.Groups()) {
290            $group_result = @{
291                name = $grp.GetType().InvokeMember("Name", "GetProperty", $null, $grp, $null)
292                path = $grp.GetType().InvokeMember("ADsPath", "GetProperty", $null, $grp, $null)
293            }
294            $user_groups += $group_result;
295        }
296        $result.groups = $user_groups
297        $result.state = "present"
298    }
299    Else {
300        $result.name = $username
301        if ($state -eq 'query') {
302            $result.msg = "User '$username' was not found"
303        }
304        $result.state = "absent"
305    }
306}
307catch {
308    Fail-Json $result $_.Exception.Message
309}
310
311Exit-Json $result