Created
January 17, 2017 09:34
-
-
Save bevinduplessis/1f5134a9897a6d315dafd49051ed645d to your computer and use it in GitHub Desktop.
Get a list of unique usernames being used to run services on a computer
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Function Get-ServiceUsers | |
{ | |
<# | |
.Synopsis | |
Get a list of unique usernames being used to run services on a computer. | |
.DESCRIPTION | |
Get a list of unique user's being used to run services on one or more computers. | |
.PARAMETER ComputerName | |
A single or list of computer names to perform search against. | |
.EXAMPLE | |
PS C:\> Get-ServiceUsers | |
ComputerName : SERVERDB11 | |
Online : True | |
Domain : | |
Username : LocalSystem | |
ServiceName : ProfSvc | |
.EXAMPLE | |
PS C:\> Get-ServiceUsers -ComputerName (Get-ADComputer -Filter { OperatingSystem -like '*Windows Server*'}).Name | Where-Object {$_.Domain -like "DOM*"} | |
ComputerName : SERVERDB11 | |
Online : True | |
Domain : DOM.CORP.COM | |
Username : sqladmin1 | |
ServiceName : ReportServer | |
#> | |
[CmdletBinding()] | |
param( | |
[Parameter(ValueFromPipeline = $True)] | |
[ValidateNotNullOrEmpty()] | |
[Alias('Computers', 'Name')] | |
[string[]] $ComputerName = $ENV:ComputerName, | |
[int] $Threads = $env:NUMBER_OF_PROCESSORS, | |
[switch] $ShowProgress = $True, | |
[switch] $ShowTime = $false, | |
[switch] $ShowOnlineOfflineSummary = $false, | |
[switch]$ShowFullOutput = $True, | |
[int] $Timeout = 300, | |
[int]$RetryCount = 5, | |
[int]$RetryWaitTime = 5, | |
[switch] $DNSLookup | |
) | |
Begin | |
{ | |
Function Get-Result | |
{ | |
[CmdletBinding()] | |
Param( | |
[switch] $Wait | |
) | |
Do | |
{ | |
$More = $false | |
foreach ($Runspace in $Runspaces) | |
{ | |
$StartTime = $RunspaceTimers[$Runspace.ID] | |
if ($Runspace.Handle.IsCompleted) | |
{ | |
$Runspace.PowerShell.EndInvoke($Runspace.Handle) | |
$Runspace.PowerShell.Dispose() | |
$Runspace.PowerShell = $Null | |
$Runspace.Handle = $Null | |
} | |
elseif ($Runspace.Handle -ne $Null) | |
{ | |
$More = $True | |
} | |
if ($Timeout -and $StartTime) | |
{ | |
if ((New-TimeSpan -Start $StartTime).TotalSeconds -ge $Timeout -and $Runspace.PowerShell) | |
{ | |
Write-Warning -Message ('Timeout {0}' -f $Runspace.IObject) | |
$Runspace.PowerShell.Dispose() | |
$Runspace.PowerShell = $Null | |
$Runspace.Handle = $Null | |
} | |
} | |
} | |
if ($More -and $PSBoundParameters['Wait']) | |
{ | |
Start-Sleep -Milliseconds 100 | |
} | |
foreach ($Thread in $Runspaces.Clone()) | |
{ | |
if (-not $Thread.Handle) | |
{ | |
Write-Verbose -Message ('Removing [{0}] from runspaces' -f $Thread.IObject) | |
$Runspaces.Remove($Thread) | |
} | |
} | |
if ($ShowProgress) | |
{ | |
$ProgressSplatting = @{ | |
Activity = 'Checking hosts' | |
Status = 'Processing: {0} of {1} total hosts' -f ($RunspaceCounter - $Runspaces.Count), $RunspaceCounter | |
PercentComplete = ($RunspaceCounter - $Runspaces.Count) / $RunspaceCounter * 100 | |
} | |
Write-Progress @ProgressSplatting | |
} | |
} | |
while ($More -and $PSBoundParameters['Wait']) | |
} | |
if($ComputerName.Count -gt 1) | |
{ | |
Write-Verbose -Message "Processing [$($ComputerName.Count)] hosts" | |
} | |
$CachedErrorActionPreference = 'Stop' | |
$ErrorActionPreference = $CachedErrorActionPreference | |
$StartTime = Get-Date | |
$RunspaceTimers = [HashTable]::Synchronized(@{}) | |
$Output = [HashTable]::Synchronized(@{}) | |
$Runspaces = New-Object -TypeName System.Collections.ArrayList | |
$RunspaceCounter = 0 | |
Write-Verbose -Message 'Creating initial session state' | |
$ISS = [initialsessionstate]::CreateDefault() | |
$ISS.Variables.Add((New-Object -TypeName System.Management.Automation.Runspaces.SessionStateVariableEntry -ArgumentList 'RunspaceTimers', $RunspaceTimers, '')) | |
$ISS.Variables.Add((New-Object -TypeName System.Management.Automation.Runspaces.SessionStateVariableEntry -ArgumentList 'Output', $Output, '')) | |
Write-Verbose -Message 'Creating runspace factory' | |
$RunspacePool = [runspacefactory]::CreateRunspacePool(1, $Threads, $ISS, $Host) | |
$RunspacePool.ApartmentState = 'STA' | |
$RunspacePool.Open() | |
$PsExecScriptBlock = { | |
[CmdletBinding()] | |
param( | |
[int] $ID, | |
[string] $ComputerName, | |
[int]$RetryCount, | |
[int]$RetryWaitTime, | |
[switch] $DNSLookup | |
) | |
Begin | |
{ | |
Function Split-Username | |
{ | |
[CmdletBinding()] | |
param | |
( | |
[string]$Username | |
) | |
if ($Username -like '*\*') | |
{ | |
$Useroutput = [PSCustomObject]@{ | |
domain = $Username.Split('\')[0].ToUpper() | |
username = $Username.Split('\')[1] | |
} | |
} | |
elseif ($Username -like '*@*') | |
{ | |
$Useroutput = [PSCustomObject]@{ | |
domain = $Username.Split('@')[1].ToUpper() | |
username = $Username.Split('@')[0] | |
} | |
} | |
else | |
{ | |
$Useroutput = [PSCustomObject]@{ | |
domain = '' | |
username = $Username | |
} | |
} | |
return $Useroutput | |
} | |
} | |
Process { | |
$RunspaceTimers.$ID = Get-Date | |
if (-not $Output.ContainsKey($ComputerName)) | |
{ | |
$Output[$ComputerName] = New-Object -TypeName PSObject -Property @{ | |
ComputerName = $ComputerName | |
} | |
} | |
if ($DNSLookup) | |
{ | |
Write-Verbose -Message "[$ComputerName] Performing DNS lookup." | |
$ErrorActionPreference = 'SilentlyContinue' | |
$HostEntry = [Net.Dns]::GetHostEntry($ComputerName) | |
$Result = $? | |
$ErrorActionPreference = $CachedErrorActionPreference | |
if ($Result) | |
{ | |
if ($HostEntry.HostName.Split('.')[0] -ieq $ComputerName.Split('.')[0]) | |
{ | |
$IPDns = @($HostEntry | | |
Select-Object -ExpandProperty AddressList | | |
Select-Object -ExpandProperty IPAddressToString) | |
} | |
else | |
{ | |
$IPDns = @(@($HostEntry.HostName) + @($HostEntry.Aliases)) | |
} | |
$Output[$ComputerName] | Add-Member -MemberType NoteProperty -Name 'IP' -Value $IPDns | |
} | |
else | |
{ | |
$Output[$ComputerName] | Add-Member -MemberType NoteProperty -Name 'IP' -Value $Null | |
} | |
} | |
Write-Verbose -Message "Pinging host [$ComputerName]" | |
$Online = $false | |
1..$RetryCount | ForEach-Object -Process { | |
$Param = @{ | |
ComputerName = $ComputerName | |
BufferSize = 1 | |
Count = 1 | |
Quiet = $True | |
} | |
$Online = Test-Connection @Param | |
if($Online) | |
{ | |
$Output[$ComputerName] | Add-Member -MemberType NoteProperty -Name Online -Value $True | |
} | |
else | |
{ | |
$Output[$ComputerName] | Add-Member -MemberType NoteProperty -Name Online -Value $false | |
} | |
} | |
if($Online) | |
{ | |
Write-Verbose -Message 'Attempting to get service users' | |
try | |
{ | |
Write-Verbose -Message "Querying WMI on $ComputerName." | |
$Services = Get-WmiObject -ComputerName $ComputerName -Class win32_service -ErrorAction Stop | Select-Object -Property Name, StartName | |
} | |
catch | |
{ | |
$ErrorText = $Error[0].Exception.Message | |
$ErrorShortText = $ErrorText.ToString().Split('(')[0] | |
Write-Verbose -Message "Could not talk to $ComputerName : $ErrorShortText" | |
Write-Error -Message "Could not talk to $ComputerName : $ErrorText" | |
} | |
finally | |
{ | |
$Error.Clear() | |
} | |
$ServicesUsers = $Services | | |
Select-Object -Property StartName, Name | | |
Where-Object { | |
$_.StartName -ne $Null | |
} | | |
Sort-Object -Property StartName -Unique | |
Write-Verbose -Message "Processing a total of $($ServicesUsers.Count) service users for $ComputerName" | |
foreach ($ServicesUser in $ServicesUsers) | |
{ | |
$Split = Split-Username -Username $ServicesUser.StartName | |
$Username = $Split.Username | |
$Domain = $Split.Domain | |
$Output[$ComputerName] | Add-Member -MemberType NoteProperty -Name 'Domain' -Value $Domain | |
$Output[$ComputerName] | Add-Member -MemberType NoteProperty -Name 'Username' -Value $Username | |
$Output[$ComputerName] | Add-Member -MemberType NoteProperty -Name 'ServiceName' -Value $ServicesUser.Name | |
} | |
} | |
} | |
} | |
} | |
Process | |
{ | |
foreach ($Computer in $ComputerName) | |
{ | |
Write-Verbose -Message "Processing [$Computer]" | |
++$RunspaceCounter | |
$psCMD = [powershell]::Create().AddScript($PsExecScriptBlock) | |
$null = $psCMD.AddParameter('ID', $RunspaceCounter) | |
$null = $psCMD.AddParameter('ComputerName', $Computer) | |
$null = $psCMD.AddParameter('RetryCount', $RetryCount) | |
$null = $psCMD.AddParameter('RetryWaitTime', $RetryWaitTime) | |
$null = $psCMD.AddParameter('DNSLookup', $DNSLookup) | |
$null = $psCMD.AddParameter('Verbose', $Global:VerbosePreference) | |
$psCMD.RunspacePool = $RunspacePool | |
$null = $Runspaces.Add(@{ | |
Handle = $psCMD.BeginInvoke() | |
PowerShell = $psCMD | |
IObject = $Computer | |
ID = $RunspaceCounter | |
}) | |
Get-Result | |
} | |
} | |
End | |
{ | |
Get-Result -Wait | |
if ($ShowProgress) | |
{ | |
Write-Progress -Activity 'Checking if host is online' -Status 'Done' -Completed | |
} | |
Write-Verbose -Message 'Closing runspace pool.' | |
$RunspacePool.Close() | |
Write-Verbose -Message 'Disposing runspace pool.' | |
$RunspacePool.Dispose() | |
[hashtable[]] $Properties = @{ | |
Name = 'ComputerName' | |
Expression = { | |
$_.Name | |
} | |
} | |
if ($DNSLookup) | |
{ | |
$Properties += @{ | |
Name = 'IP' | |
Expression = { | |
$_.Value.'IP' | |
} | |
} | |
} | |
$Properties += @{ | |
Name = 'Online' | |
Expression = { | |
$_.Value.Online | |
} | |
} | |
$Properties += @{ | |
Name = 'Domain' | |
Expression = { | |
$_.Value.Domain | |
} | |
} | |
$Properties += @{ | |
Name = 'Username' | |
Expression = { | |
$_.Value.Username | |
} | |
} | |
$Properties += @{ | |
Name = 'ServiceName' | |
Expression = { | |
$_.Value.ServiceName | |
} | |
} | |
if ($ShowTime) | |
{ | |
Write-Host ' ' | |
Write-Host "Start time: $StartTime" -ForegroundColor Cyan | |
Write-Host "End time: $(Get-Date)" -ForegroundColor Cyan | |
} | |
if($ShowFullOutput) | |
{ | |
Write-Host ' ' | |
$Output.GetEnumerator() | Select-Object -Property $Properties | Sort-Object -Property Online | |
} | |
if($ShowOnlineOfflineSummary) | |
{ | |
[int]$TotalOffline = 0 | |
[int]$TotalOnline = 0 | |
Foreach ($OutputObject in ($Output.GetEnumerator() | Select-Object -Property $Properties)) | |
{ | |
if($OutputObject.Online -eq $false) | |
{ | |
++$TotalOffline | |
} | |
if ($OutputObject.Online -eq $True) | |
{ | |
++$TotalOnline | |
} | |
} | |
Write-Host "Total Offline: " -NoNewLine | |
Write-Host "[$TotalOffline]" -ForegroundColor Red -BackgroundColor Black | |
Write-Host "Total Online: " -NoNewLine | |
Write-Host "[$TotalOnline]" -ForegroundColor Green -BackgroundColor Black | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment