Functions/Get-WSLocalUser.ps1
|
function Get-WSLocalUser { <# .NOTES Author: Skyler Hart Created: 2021-02-27 22:05:08 Last Edit: 2021-02-28 21:05:57 Keywords: Requires: -RunAsAdministrator .LINK https://wanderingstag.github.io #> [CmdletBinding()] param( [Parameter( Mandatory=$false, Position=0 )] [Alias('Host','Computer','CN')] [string[]]$ComputerName = "$env:COMPUTERNAME", [Parameter()] [int32]$MaxThreads = 5, [Parameter()] $SleepTimer = 200, [Parameter()] $MaxResultTime = 1200, [Parameter()] [string]$Output = $null ) Begin { $config = $Global:WSToolsConfig $ScriptWD = $config.ScriptWD $ISS = [system.management.automation.runspaces.initialsessionstate]::CreateDefault() $RunspacePool = [runspacefactory]::CreateRunspacePool(1, $MaxThreads, $ISS, $Host) $RunspacePool.Open() $Code = { [CmdletBinding()] Param ( [Parameter( Mandatory=$true, Position=0 )] [string]$comp, [Parameter( Mandatory=$true, Position=1 )] [string]$Output ) $info = @() try { $Role = (Get-WmiObject -ComputerName $comp -Class Win32_ComputerSystem -Property DomainRole -ErrorAction Stop).DomainRole } catch { $info = [PSCustomObject]@{ ComputerName = $comp DatePulled = $Null Description = $Null Disabled = $Null LastLogin = $Null Locked = $Null PasswordExpired = $Null PasswordLastSet = $Null PasswordLastSetDays = $Null PasswordNeverExpires = $Null PasswordNotChangable = $Null PasswordNotRequired = $Null Status = "Comm Error" User = $Null }#new object } if ($Role -match "4|5") { $info = [PSCustomObject]@{ ComputerName = $comp DatePulled = $Null Description = $Null Disabled = $Null LastLogin = $Null Locked = $Null PasswordExpired = $Null PasswordLastSet = $Null PasswordLastSetDays = $Null PasswordNeverExpires = $Null PasswordNotChangable = $Null PasswordNotRequired = $Null Status = "Domain Controller - no local users" User = $Null }#new object }#if DC elseif ($Role -match "0|1|2|3") { $UI = ([ADSI]"WinNT://$comp").Children | Where-Object {$_.SchemaClassName -eq 'User'} $td = Get-Date $info = foreach ($user in $UI) { $uflags = $user.UserFlags[0] $flags = New-Object System.Collections.ArrayList switch ($uflags) { ($uflags -BOR 0x0002) {[void]$flags.Add('Disabled')} ($uflags -BOR 0x0010) {[void]$flags.Add('Locked')} ($uflags -BOR 0x0020) {[void]$flags.Add('PwdNotReq')} ($uflags -BOR 0x0040) {[void]$flags.Add('PwdNotChangable')} ($uflags -BOR 0x10000) {[void]$flags.Add('NeverExp')} ($uflags -BOR 0x800000) {[void]$flags.Add('PwdExp')} } $List = $flags -join ', ' [int32]$pa = 0 $pa = $user.PasswordAge[0] $pls = ((Get-Date).AddSeconds(-($pa))) $plsd = (New-TimeSpan -Start $pls -End (Get-Date)).Days [PSCustomObject]@{ ComputerName = $comp DatePulled = $td Description = $user.Description[0] Disabled = if ($List -match "Disabled") {$true} else {$false} LastLogin = (($user.LastLogin[0]).DateTime) Locked = if ($List -match "Locked") {$true} else {$false} PasswordExpired = if ($List -match "PwdExp") {$true} else {$false} PasswordLastSet = $pls PasswordLastSetDays = $plsd PasswordNeverExpires = if ($List -match "NeverExp") {$true} else {$false} PasswordNotChangable = if ($List -match "PwdNotChangable") {$true} else {$false} PasswordNotRequired = if ($List -match "PwdNotReq") {$true} else {$false} Status = "Connected" User = $user.Name[0] }#new object }#foreach user on computer }#not DC if (Test-Path $Output) { $info | Select-Object ComputerName,Status,User,Description,Disabled,LastLogin,Locked,PasswordExpired,PasswordLastSet,PasswordLastSetDays,PasswordNeverExpires,PasswordNotChangable,PasswordNotRequired,DatePulled | Export-Csv $Output -NoTypeInformation -Append } else { $info | Select-Object ComputerName,Status,User,Description,Disabled,LastLogin,Locked,PasswordExpired,PasswordLastSet,PasswordLastSetDays,PasswordNeverExpires,PasswordNotChangable,PasswordNotRequired,DatePulled | Export-Csv $Output -NoTypeInformation } }#end code block $Jobs = @() } Process { if ([string]::IsNullOrWhiteSpace($Output)) { if (!(Test-Path $ScriptWD)) {mkdir $ScriptWD} $Output = $ScriptWD + "\WS_LocalUser.csv" } Write-Progress -Activity "Preloading threads" -Status "Starting Job $($jobs.count)" ForEach ($Object in $ComputerName){ $PowershellThread = [powershell]::Create().AddScript($Code) $PowershellThread.AddArgument($Object.ToString()) | out-null $PowershellThread.AddArgument($Output.ToString()) | out-null $PowershellThread.RunspacePool = $RunspacePool $Handle = $PowershellThread.BeginInvoke() $Job = "" | Select-Object Handle, Thread, object $Job.Handle = $Handle $Job.Thread = $PowershellThread $Job.Object = $Object.ToString() $Jobs += $Job } } End { $ResultTimer = Get-Date While (@($Jobs | Where-Object {$Null -ne $_.Handle}).count -gt 0) { $Remaining = "$($($Jobs | Where-Object {$_.Handle.IsCompleted -eq $False}).object)" If ($Remaining.Length -gt 60){ $Remaining = $Remaining.Substring(0,60) + "..." } Write-Progress ` -Activity "Waiting for Jobs - $($MaxThreads - $($RunspacePool.GetAvailableRunspaces())) of $MaxThreads threads running" ` -PercentComplete (($Jobs.count - $($($Jobs | Where-Object {$_.Handle.IsCompleted -eq $False}).count)) / $Jobs.Count * 100) ` -Status "$(@($($Jobs | Where-Object {$_.Handle.IsCompleted -eq $False})).count) remaining - $remaining" ForEach ($Job in $($Jobs | Where-Object {$_.Handle.IsCompleted -eq $True})){ $Job.Thread.EndInvoke($Job.Handle) $Job.Thread.Dispose() $Job.Thread = $Null $Job.Handle = $Null $ResultTimer = Get-Date } If (($(Get-Date) - $ResultTimer).totalseconds -gt $MaxResultTime){ Write-Error "Child script appears to be frozen, try increasing MaxResultTime" Exit } Start-Sleep -Milliseconds $SleepTimer } $RunspacePool.Close() | Out-Null $RunspacePool.Dispose() | Out-Null } } |