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

Leave a Comment

Your email address will not be published. Required fields are marked *