Windows Server – Check for Configured Service Accounts
This script is modified from components posted by Margin Schvartzman on TechNet.
Copy and paste the script below to a text file and save it as scan_serviceaccts.ps1.
How To Run:
For optimal results, you should run this script in the PowerShell console in Administrator Mode.
.\scan_serviceaccts.ps1 -mode local – This will only scan the local system
.\scan_serviceaccts.ps1 -mode scan – This will retrieve a list of systems that are “Windows Servers” through AD. You will need to run this on a domain controller, or a system that has the AD RSAT components installed.
The script will output results to a folder called ps_results on the C: drive. If the folder does not exist, the script will attempt to create it.
# -----------------------------------------------------------------
# Check Configured Service Accounts
# Created by: Christopher Clai
# Contains elements of Get-ServiceAccountUsage from Martin Schvartzman (http://blogs.technet.com/b/isrpfeplat/)
# -----------------------------------------------------------------
# Version 1.0 (December 5, 2017)
# -----------------------------------------------------------------
#
# Example of running the script:
# .\scan_serviceaccts.ps1 -mode [local|scan] (Choose what method to run the script, local mode or remote)
#
#
# ##### CHANGELOG ########
# Version 1.0
# - Created
#
#
# -----
# DO NOT EDIT ANYTHING BELOW THIS LINE
# -----
# Retrieve parameters if run on a single use.
param (
[Parameter(Mandatory=$True,ValueFromPipeline=$True,ValueFromPipelineByPropertyName=$True,HelpMessage='Local or Remote Mode')]
$mode
)
function Get-ServiceAccountUsage {
Param(
[CmdletBinding(DefaultParametersetName='Explicit')]
[Parameter(ValueFromPipeline=$true, ValueFromPipelineByPropertyName=$true,
HelpMessage = 'The name of the [remote] computer or an array of computer names')]
[Alias('CN','IPAddress','Server','Computer','__SERVER')]
[ValidateNotNullOrEmpty()]
[string[]] $ComputerName=$ENV:COMPUTERNAME,
[Parameter(ParameterSetName='Explicit',
HelpMessage = 'The user account being used on a Service, Scheduled Task or Application Pool')]
[Alias('User','UserName','Account')]
[string] $UserAccount='*',
[Parameter(Mandatory=$true, ParameterSetName='Implicit',
HelpMessage = 'List Services, Scheduled Tasks or Application Pools run by Non System Accounts (LOCALSYSTEM / LOCALSERVICE / NETWOKSERVICE / etc.)')]
[Alias('Implicit','NonDefault','NSA')]
[switch] $NonSystemAccounts,
[System.Management.Automation.Credential()] $Credential = [System.Management.Automation.PSCredential]::Empty
)
begin {
switch ($PsCmdlet.ParameterSetName) {
'Explicit' {
$ServiceFilter = "StartName LIKE '$($UserAccount.Replace('\','\\').Replace('*','%'))'"
$TaskFilter = { $_.'Run As User' -like $UserAccount }
$IIS6Filter = "WAMUserName LIKE '$($UserAccount.Replace('\','\\').Replace('*','%'))'"
$IIS7Filter = "ProcessModel.UserName LIKE '$($UserAccount.Replace('\','\\').Replace('*','%'))'"
break
}
'Implicit' {
$ServiceFilter = "(NOT StartName LIKE '%LocalSystem') AND (NOT StartName LIKE '%LocalService') AND (NOT StartName LIKE '%NetworkService') AND (NOT StartName LIKE 'NT AUTHORITY%')"
$TaskSystemAccounts = 'INTERACTIVE', 'SYSTEM', 'NETWORK SERVICE', 'LOCAL SERVICE', 'Run As User', 'Authenticated Users', 'Users', 'Administrators', 'Everyone', ''
$TaskFilter = { $TaskSystemAccounts -notcontains $_.'Run As User' }
$IIS6Filter = 'AppPoolIdentityType = 3'
$IIS7Filter = 'ProcessModel.IdentityType = 3'
break
}
}
Write-Verbose "Services filter = $ServiceFilter"
Write-Verbose "Tasks filter = `$_.'Run As User' -like $UserAccount"
Write-Verbose "MicrosoftIISv2 filter = $IIS6Filter"
Write-Verbose "WebAdministration filter = $IIS7Filter"
$IsCredSpecified = ($Credential -and ($Credential -ne ([System.Management.Automation.PSCredential]::Empty)))
function Get-xWmiObject {
param(
[string] $ComputerName,
[string] $Namespace = 'root\cimv2',
[string] $Class,
[string] $Filter,
[int] $Timeout = 5
)
try {
$ConnectionOptions = New-Object System.Management.ConnectionOptions
$EnumerationOptions = New-Object System.Management.EnumerationOptions
$ConnectionOptions.Authentication = 'PacketPrivacy'
$timeoutseconds = New-TimeSpan -Seconds $Timeout
$EnumerationOptions.set_timeout($timeoutseconds)
$assembledpath = "\\" + $ComputerName + "\" + $Namespace
$Scope = New-Object System.Management.ManagementScope $assembledpath, $ConnectionOptions
$Scope.Connect()
$querystring = "SELECT * FROM " + $class
if ($Filter) { $querystring += ' WHERE ' + $Filter}
$query = New-Object System.Management.ObjectQuery $querystring
$searcher = New-Object System.Management.ManagementObjectSearcher
$searcher.set_options($EnumerationOptions)
$searcher.Query = $querystring
$searcher.Scope = $Scope
return $searcher.get()
}
catch {
return $null
}
}
}
process {
foreach($Computer in $ComputerName) {
if ($Computer -eq '.') { $Computer = $ENV:COMPUTERNAME }
Write-Verbose 'Building the services parameters hashtable'
$ParamServices = @{
Namespace = 'root\cimv2'
Class = 'Win32_Service'
Filter = $ServiceFilter
ErrorAction = 'SilentlyContinue'
ComputerName = $Computer
}
if ($IsCredSpecified) { $ParamServices.Add('Credential',$Credential) }
Write-Verbose 'Building the scheduled tasks credentials parameters'
$sCreds = $null
if ($IsCredSpecified -ne $false) {
$User = $Credential.UserName
$Password = $Credential.GetNetworkCredential().Password
$sCreds = '/U {0} /P {1} ' -f $User, $Password
}
Write-Verbose 'Building the IIS6 parameters hashtable'
$ParamIIS6 = @{
Namespace = 'root\MicrosoftIISv2'
Class = 'IIsApplicationPoolSetting'
Filter = $IIS6Filter
ErrorAction = 'Stop'
ComputerName = $Computer
Authentication = 'PacketPrivacy'
}
if ($IsCredSpecified) { $ParamIIS6.Add('Credential',$Credential) }
Write-Verbose 'Building the IIS7 parameters hashtable'
$ParamIIS7 = @{
Namespace = 'root\WebAdministration'
Class = 'ApplicationPool'
Filter = $IIS7Filter
ErrorAction = 'Stop'
ComputerName = $Computer
Authentication = 'PacketPrivacy'
}
if ($IsCredSpecified) { $ParamIIS7.Add('Credential',$Credential) }
$LocalMachineAliases = $ENV:COMPUTERNAME,'localhost','127.0.0.1','::1'
if ($LocalMachineAliases -contains $Computer -and $IsCredSpecified) {
Write-Warning "User credentials cannot be used for local connections. Ignoring credentials for all $Computer connections."
$ParamServices.Remove('Credential')
$sCreds = $null
$ParamIIS6.Remove('Credential')
$ParamIIS7.Remove('Credential')
}
Write-Verbose "Checking $Computer's availability"
if (Test-Connection -ComputerName $Computer -Count 1 -Quiet) {
try {
Write-Verbose "Checking for Services on $Computer"
Get-xWmiObject @ParamServices | ForEach-Object {
New-Object -TypeName PSObject -Property @{
ComputerName = $Computer
Type = 'Service'
Name = $_.DisplayName
Account = $_.StartName
} | Select-Object ComputerName, Type, Name, Account
}
}
catch {
Write-Error $_
}
try {
Write-Verbose "Checking for Scheduled Tasks on $Computer"
Invoke-Expression "SCHTASKS /QUERY /S $Computer $sCreds /FO CSV /V" -ErrorAction Stop | ConvertFrom-CSV | Where-Object $TaskFilter | ForEach-Object {
New-Object -TypeName PSObject -Property @{
ComputerName = $Computer
Type = 'Task'
Name = $_.TaskName
Account = $_.'Run As User'
} | Select-Object ComputerName, Type, Name, Account
}
}
catch {
Write-Error 'Error checking Scheduled Tasks configuration'
}
try {
Write-Verbose "Checking for Application Pools on $Computer (using the MicrosoftIISv2 WMI namespace)"
if ($AppPools = Get-xWmiObject @ParamIIS6) {
$AppPools | ForEach-Object {
New-Object -TypeName PSObject -Property @{
ComputerName = $Computer
Type = 'ApplicationPool'
Name = ($_.Name -replace 'W3SVC/APPPOOLS/')
Account = $_.WAMUserName
} | Select-Object ComputerName, Type, Name, Account
}
}
else {
Write-Verbose "Checking for Application Pools on $Computer (using the WebAdministration WMI namespace)"
if($AppPools = Get-xWmiObject @ParamIIS7) {
$AppPools | ForEach-Object {
New-Object -TypeName PSObject -Property @{
ComputerName = $Computer
Type = 'ApplicationPool'
Name = ($_.Name -replace 'W3SVC/APPPOOLS/')
Account = $_.ProcessModel.UserName
} | Select-Object ComputerName, Type, Name, Account
}
}
}
}
catch [System.UnauthorizedAccessException] { Write-Warning "Access denied on $Computer. cannot check Application Pools configuration. Please run elevated, or use credentials with administrative permissions" }
catch { Write-Verbose 'Cannot check Application Pools. NameSpaces MicrosoftIISv2 and WebAdministration do not exist or could not be contacted' }
}
else { Write-Warning "Cannot connect to $Computer" }
}
}
}
# Output Folder Verification
New-Item -ItemType Directory -Force -Path C:\ps_result
# Begin Transcript
Start-Transcript -Path "C:\ps_result\scan_serviceaccts.txt" -Append
# Set Other Variables
$taction = 0
if ($mode -eq "local") {
write-host "Running in local mode..."
$taction++
$servtot = "1"
Get-ServiceAccountUsage -NonSystemAccounts
}
elseif ($mode -eq "scan") {
#We need to retrieve Active Directory
Import-Module ActiveDirectory
#Let's get a list of systems
$servers = Get-ADComputer -Filter 'OperatingSystem -Like "Windows Server*"' | Select Name | Foreach-Object {$_.Name}
#Let's set the count.
$servtot = $servers.count
ForEach ($server in $servers) {
# Output Server Name
Write-Host "Checking $server"
If(Test-Connection -BufferSize 32 -Count 1 -ComputerName $server -Quiet) {
Write-Host "System is Online. Checking configuration..."
Get-ServiceAccountUsage -ComputerName $server -NonSystemAccounts
}
Else {
#System is offline,
Write-Host "System is Offline. Skipping..."
}
#increment the counter
$taction++
}
}
# Write the result
Write-Host $taction "Systems Checked out of " $servtot "Systems."
# Complete