Finding Services Using non-System Accounts With PowerShell
In March of 2006, I authored this blog post, Finding Services Using non-System Accounts, with the script being written in VBscript.
Two years later, most of my environment is now in PowerShell. So, I've decided to update that post with the PowerShell equivalent.
The VBscript version of the utility was 155 lines long. The PowerShell version is 89 lines long, and the PowerShell version corrects a couple of bugs that were present in the VBscript version (for example, if a service's startup account was specified with an administrator account specified as a UPN, the VBscript would not detect the account as an administrator account).
The PowerShell version was created by doing almost a line-for-line translation of the VBscript utility; only a few things are PowerShell optimized. For example, the "real PowerShell" way to do the checkExclusions() function would be for $arrExclude to be a hash array (associative array). Once that change is made, there is no need for the checkExclusions() function at all - you simply index the array with $account to tell whether or not the account is a member of the array. That would save another 15 lines of code right there. But I didn't know that when I translated the utility. :-)
On the other hand, instead of reading the file line by line, I did optimize that loop by using the PowerShell get-content cmdlet.
The "listofcomputers.txt" file remains in the same format. A small example look like this:
#
# comment lines
#
.
win2003-dc
win2008-exch
win2003-scom
Where any line beginning with a '#' is a comment. A '.' indicates the current computer. All other lines contain a computer name. It may be a short-name or a long (fully-qualified) name. Output (in my environment) for the above input looks like this:
PS C:\Scripts> ./check-services
Checking computer .
Account administrator@essential.local; Service Themes; Caption Themes
Account .\administrator; Service WmiApSrv; Caption WMI Performance Adapter
Checking computer win2003-dc
Checking computer win2008-exch
Get-WmiObject : The RPC server is unavailable. (Exception from HRESULT: 0x800706BA)
At C:\Scripts\Check-Services.ps1:30 char:17
+ $results = gwmi <<<< win32_service -computer $strComputer -property name, startname, caption
Error occurred
Checking computer win2003-scom
Get-WmiObject : The RPC server is unavailable. (Exception from HRESULT: 0x800706BA)
At C:\Scripts\Check-Services.ps1:30 char:17
+ $results = gwmi <<<< win32_service -computer $strComputer -property name, startname, caption
Error occurred
Processing complete.
Total computers processed: . . 4
Total Administrator services: 2
Total special services: . . . 2
Total comment lines: . . . . . 3
Total errors: . . . . . . . . 2
PS C:\Scripts>
Note the detailed error messages. PowerShell gives you those "for free" by default. In this case, Win2008-Exch has had the SCW (Security Configuration Wizard) run on it to lock it down - it doesn't allow remote management. I need to fix that. :-) Also in this case, Win2003-SCOM is turned off. The errors are the same because WMI can't know the reason that a computer does not respond.
So, without further ado, here is the script:
Param ([string]$strFile = "listofcomputers.txt")
$arrExclude = "NT AUTHORITY\LocalService",
"LocalSystem",
".\ASPNET",
"NT AUTHORITY\NETWORK SERVICE",
"NT AUTHORITY\NetworkService"
$script:iAdmin = $script:iTot = $script:iCount = $script:iError = 0
function checkExclusions([string]$strval)
{
foreach ($val in $arrExclude)
{
if ($val.ToLower() -eq $strval)
{
return $true
}
}
return $false
}
function checkServicesOnComputer([string]$strComputer)
{
$iExcluded = $iIncluded = 0
" "; "Checking computer $strComputer"
trap { "Error occurred"; $script:iError++; continue; }
$results = gwmi win32_service -computer $strComputer -property name, startname, caption
foreach ($result in $results)
{
$account = $result.StartName.ToLower()
if (checkExclusions $account)
{
$iExcluded++;
}
else
{
$iIncluded++;
$adminR = "\administrator"; ## admin-from-the-right
if (($account.Length -ge $adminR.Length) -and
($account.SubString($account.Length - $adminR.Length) -eq $adminR))
{
$script:iAdmin++;
}
$adminL = "administrator@"; ## admin-from-the-left
if (($account.Length -ge $adminL.Length) -and
($account.SubString(0, $adminL.Length) -eq $adminL))
{
$script:iAdmin++;
}
"Account $account; Service " + $result.Name + "; Caption " + $result.Caption
}
}
$script:iTot += $iIncluded;
}
function doProcess([string]$filename)
{
$computers = get-content $filename
foreach ($computer in $computers)
{
if ($computer.SubString(0, 1) -eq "#")
{
$script:iComment++;
}
else
{
checkServicesOnComputer $computer;
$script:iCount++;
}
}
}
#
# Main
#
doProcess $strFile
" "
"Processing complete."
"Total computers processed: . . $script:iCount"
"Total Administrator services: $script:iAdmin"
"Total special services: . . . $script:iTot"
"Total comment lines: . . . . . $script:iComment"
"Total errors: . . . . . . . . $script:iError"
Until next time...
I hope you've enjoyed this posting. If there are topics you would like me to cover, please send me an e-mail or leave me a message in the forums.