• Status: Solved
  • Priority: Medium
  • Security: Public
  • Views: 187
  • Last Modified:

Modifying PowerShell to list service account from custom list & OU ?

Hi All,

Can anyone here please assist me in how to modify the below Powershell script so that I can get the list of the service account

Since it is returning the error:

Cannot validate argument on parameter 'ComputerName'. The argument is null or empty. Provide an argument that is not null or empty, and then try the command again.
At line:54 char:9
+         $data = Receive-Job $job
+         ~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : InvalidData: (:) [Test-Connection], ParameterBindingValidationException
    + FullyQualifiedErrorId : ParameterArgumentValidationError,Microsoft.PowerShell.Commands.TestConnectionCommand
    + PSComputerName        : localhost

This is the script that I have found but not working since I need to customize the source of the servers.

    Service account report script
    developed by Gleb Yourchenko
    Version 1

    Reads service configuration from all Windows servers 
    in the current domain and generates report listing all 
    domain accounts used as service logon account.
    Version history: 
        04.03.2016 First release


$reportFile = "c:\temp\report-serviceaccounts.html"
$ErrorActionPreference = "Stop"
$maxThreads = 10
$currentDomain = $env:USERDOMAIN.ToUpper()
$serviceAccounts = @{}
[string[]]$warnings = @() 

$readServiceAccounts = {
# scriptblock to retrieve service list form a remote machine

    param( $hostname )
    if ( Test-Connection -ComputerName $hostname -Count 3 -Quiet ){
        try {
            $serviceList = @( gwmi -Class Win32_Service -ComputerName WHERE StartName <> 'LocalSystem'" | ? { $_.StartName -notlike 'NT AUTHORITY*' -and $_.StartName -notlike 'NT SERVICE*' $hostname -Property Name,StartName,SystemName -ErrorAction Stop )

            "Failed to retrieve data from $hostname : $($_.toString())"
        "$hostname is unreachable"

function processCompletedJobs(){
# reads service list from completed jobs, 
# updates $serviceAccount table and removes completed job

    $jobs = Get-Job -State Completed
    foreach( $job in $jobs ) {

        $data = Receive-Job $job 
        Remove-Job $job 
        if ( $data.GetType() -eq [Object[]] ){
            $serviceList = $data | ? { $_.StartName.toUpper().StartsWith( $currentDomain )}
            foreach( $service in $serviceList ){
                $account = $service.StartName
                $occurance = "`"$($service.Name)`" service on $($service.SystemName)" 
                if ( $script:serviceAccounts.Contains( $account ) ){
                    $script:serviceAccounts.Item($account) += $occurance
                else {
                    $script:serviceAccounts.Add( $account, @( $occurance ) ) 
        elseif ( $data.GetType() -eq [String] ) {
            $script:warnings += $data
            Write-warning $data

#################    MAIN   #########################

Import-Module ActiveDirectory

# read computer accounts from current domain
Write-Progress -Activity "Retrieving server list from ActiveDirectory" -Status "Processing..." -PercentComplete 0 
$serverList = Get-Content -Path C:\temp\IP.txt
	#  Get-ADComputer -Properties Name  -Filter {Enabled -eq $True -and OperatingSystem -like "*Windows Server*"} -SearchBase "OU=Servers,DC=domain,DC=com" |
	#  Where-Object {$_.DistinguishedName -notlike "*OU=Decommissioned Servers,OU=Servers,DC=domain,DC=com"}

# start data retrieval job for each server in the list
# use up to $maxThreads threads
$count_servers = 0
foreach( $server in $serverList ){
    Start-Job -ScriptBlock $readServiceAccounts -Name "read_$($server.cn)" -ArgumentList $server.dnshostname | Out-Null
    Write-Progress -Activity "Retrieving data from servers" -Status "Processing..." -PercentComplete ( $count_servers * 100 / $serverList.Count )
    while ( ( Get-Job -State Running).count -ge $maxThreads ) { Start-Sleep -Seconds 3 }

# process remaining jobs 
Write-Progress -Activity "Retrieving data from servers" -Status "Waiting for background jobs to complete..." -PercentComplete 100
Wait-Job -State Running -Timeout 30  | Out-Null
Get-Job -State Running | Stop-Job

# prepare data table for report
Write-Progress -Activity "Generating report" -Status "Please wait..." -PercentComplete 0
$accountTable = @()
foreach( $serviceAccount in $serviceAccounts.Keys )  {
    foreach( $occurance in $serviceAccounts.item($serviceAccount)  ){
        $row = new-object psobject
        Add-Member -InputObject $row -MemberType NoteProperty -Name "Account" -Value $serviceAccount
        Add-Member -InputObject $row -MemberType NoteProperty -Name "Usage" -Value $occurance
        $accountTable  += $row

# create report
$report = "
<!DOCTYPE html>
TABLE{border-width: 1px;border-style: solid;border-color: black;border-collapse: collapse;white-space:nowrap;} 
TH{border-width: 1px;padding: 4px;border-style: solid;border-color: black} 
TD{border-width: 1px;padding: 2px 10px;border-style: solid;border-color: black} 
<H1>Service account report for $currentDomain domain</H1> 
$($serverList.count) servers processed. Discovered $($serviceAccounts.count) service accounts.
<H2>Discovered service accounts</H2>
$( $accountTable | Sort Account | ConvertTo-Html Account, Usage -Fragment )
<H2>Warning messages</H2> 
$( $warnings | % { "<p>$_</p>" } )

Write-Progress -Activity "Generating report" -Status "Please wait..." -Completed
$report  | Set-Content $reportFile -Force 
Invoke-Expression $reportFile 

Open in new window

Thanks in advance.
Senior IT System Engineer
Senior IT System Engineer
  • 3
2 Solutions
Just mentioning the first things I see.  I haven't tested.
Doesn't look like your line numbers above line up with what's mentioned in the error text.

Line 31 won't work (looks like you put the computername in the wrong place.  I think this filter statement does all you want.
$serviceList = @( gwmi -Class Win32_Service -ComputerName $hostname -Filter "StartName <> 'LocalSystem' AND NOT StartName LIKE 'NT AUTHORITY\\%' AND NOT StartName LIKE 'NT SERVICE\\%'" -Property Name,StartName,SystemName -ErrorAction Stop )

Open in new window

Line 94 doesn't work with line 85.  You would need to read in a .CSV (Import-Csv) with the properties that you're trying to use in 94.
Try this; save as Get-ServiceOrTaskAccount.ps1 or Whatever.ps1. Covers both services and tasks (or only one). It accepts a string array of server names as input, either as argument or by pipeline. It will (try to) filter out default accounts, or you can pass your own filter. Output is an array of custom PS objects with 5 properties, ComputerName, Type, Name, Account, Exception. Exception will have the error if a server couldn't be queried. You can pipe the output to Export-Csv or do whatever else PS allows you to do with pipeline output.
Gets a list of accounts used in services and tasks.
Gets a list of accounts used in services and tasks. Default Windows accounts will be filtered out.
.PARAMETER ComputerName
A list of computer names to query.
You can pipe strings to this argument.
A list of strings with account names to look for; overrides the default filter.
Note that accounts may show up as DnsDomainName\User, NetBiosDomainName\User, or user@DnsDomainName.
.PARAMETER ServiceOnly
Process only services.
Process only tasks.
Get-ServiceOrTaskAccount -ComputerName SomeMachine, SomeOtherMachine -ServiceOnly
Get-Content .\servers.txt | Get-ServiceOrTaskAccount -Filter "Domain\Administrator", "Administrator@domain.com"
#requires -Version 3
	[Parameter(Mandatory=$false, Position=0, ValueFromPipeline=$True)]
	[string[]]$ComputerName = @($ENV:ComputerName),
	[Parameter(Mandatory=$false, ParameterSetName="Get_Service")]
	[Parameter(Mandatory=$false, ParameterSetName="Get_Task")]
Begin {
	$IgnoreAccounts = @()
	ForEach ($Name In ('NT AUTHORITY\LocalService', 'NT AUTHORITY\LocalSystem', 'NT Authority\NetworkService', 'NT AUTHORITY\SYSTEM')) {
		$IgnoreAccounts += $Name
		$IgnoreAccounts += $Name.Split('\')
	ForEach ($Sid In ('S-1-5-18', 'S-1-5-19', 'S-1-5-20', 'S-1-5-80-0')) {
		$PrincipalSID = New-Object -TypeName System.Security.Principal.SecurityIdentifier -ArgumentList $Sid
		$Principal = $PrincipalSID.Translate([System.Security.Principal.NTAccount])
		$IgnoreAccounts += $Sid
		$IgnoreAccounts += $Principal.Value
		$IgnoreAccounts += $Principal.Value.Split('\')
	$IgnoreAccounts = $IgnoreAccounts | Sort-Object -Unique
	$Result = [ordered]@{
		'ComputerName' = $Null
		'Type' = $Null
		'Name' = $Null
		'Account' = $Null
		'Exception' = $Null
	If ($Filter) {
		Write-Warning "Will only list objects running with the following accounts:`r`n'$($Filter -join "', '")'"
	} Else {
		Write-Warning "Will list objects NOT running with the following accounts:`r`n'$($IgnoreAccounts -join "', '")'"
	Function Select-Account([System.Collections.Specialized.OrderedDictionary]$Result) {
		$Skip = $False
		If ($Filter) {
			If ($Filter -notcontains $Result['Account']) {
				$Skip = $True
		} Else {
			If ($IgnoreAccounts -contains $Result['Account']) {
				$Skip = $True
		If ($Skip) {
			Write-Verbose -Message "[$($Result['ComputerName'])] Ignored $($Result['Type']) '$($Result['Name'])', account '$($Result['Account'])'."
		} Else {
			New-Object -TypeName PSObject -Property $Result
Process {
	ForEach ($Computer In $ComputerName) {
		$Result['ComputerName'] = $Computer
		If ($PsCmdlet.ParameterSetName.Contains('Service')) {
			$Result['Type'] = 'Service'
			Try {
				ForEach ($Service In (Get-WmiObject -Query "Select DisplayName, StartName From Win32_Service" -ComputerName $Computer -ErrorAction Stop)) {
					$Result['Name'] = $Service.DisplayName
					$Result['Account'] = $Service.StartName
					Select-Account -Result $Result
			} Catch {
				$Result['Name'] = $Null
				$Result['Account'] = $Null
				$Result['Exception'] = $_.Exception.Message
				New-Object -TypeName PSObject -Property $Result
				$Result['Exception'] = $Null
		If ($PsCmdlet.ParameterSetName.Contains('Task')) {
			$Result['Type'] = 'Task'
			$Output = schtasks.exe /s $Computer /xml ONE 2>&1
			If ($LastExitCode -eq 0) {
				[xml]$xml = $Output -replace 'xmlns=', '_xmlns='
				ForEach ($Node In $xml.SelectNodes("Tasks/Task/Principals/Principal/UserId")) {
					$Result['Name'] = $Node.SelectSingleNode('../../../preceding-sibling::comment()[1]').InnerText.Trim()
					$Result['Account'] = $Node.'#text'
					Select-Account -Result $Result
			} Else {
				$Result['Name'] = $Null
				$Result['Account'] = $Null
				$Result['Exception'] = $Output -join " "
				New-Object -TypeName PSObject -Property $Result
				$Result['Exception'] = $Null
End {

Open in new window

Senior IT System EngineerIT ProfessionalAuthor Commented:
Whoa, OBDA
So that script you posted will look for service and task scheduler running with some user account :-)

That's exceeding my expectations.
I'll try it when I'm back in the office.
Senior IT System EngineerIT ProfessionalAuthor Commented:
Many thanksfor the suggestion and the solution guys !
Senior IT System EngineerIT ProfessionalAuthor Commented:
OK, so how to run that script again ?

Get-ADComputer -Properties * -Filter {Enabled -eq $True -and OperatingSystem -like "*Server*"} -SearchBase "OU=Servers,DC=Domain,DC=com" | Where-Object {Test-Connection $_.Name -Count 1 -Quiet} | 'H:\Powershell scripts\Get-ServiceOrTaskAccount.ps1' -Filter "Domain\Admin*", "Admin*@domain.com"

Open in new window

because I got this error: Expressions are only allowed as the first element of a pipeline.
Question has a verified solution.

Are you are experiencing a similar issue? Get a personalized answer when you ask a related question.

Have a better answer? Share it in a comment.

Join & Write a Comment

Featured Post

Free Tool: Port Scanner

Check which ports are open to the outside world. Helps make sure that your firewall rules are working as intended.

One of a set of tools we are providing to everyone as a way of saying thank you for being a part of the community.

  • 3
Tackle projects and never again get stuck behind a technical roadblock.
Join Now