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

Posted on 2016-11-17
Last Modified: 2016-11-20
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_$($" -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.
  • 2
LVL 39

Assisted Solution

footech earned 100 total points
ID: 41892494
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.
LVL 83

Accepted Solution

oBdA earned 400 total points
ID: 41892757
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", ""
#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


Author Comment

by:Senior IT System Engineer
ID: 41892808
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.

Author Closing Comment

by:Senior IT System Engineer
ID: 41895177
Many thanksfor the suggestion and the solution guys !

Featured Post

Does Powershell have you tied up in knots?

Managing Active Directory does not always have to be complicated.  If you are spending more time trying instead of doing, then it's time to look at something else. For nearly 20 years, AD admins around the world have used one tool for day-to-day AD management: Hyena. Discover why

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

Synchronize a new Active Directory domain with an existing Office 365 tenant
How to record audio from input sources to your PC – connected devices, connected preamp to record vinyl discs, streaming media, that play through your audio card: Vista, Windows 7, Windows 8, Windows 8.1 and Windows 10 – both 32 bit & 64.
The viewer will learn how to successfully create a multiboot device using the SARDU utility on Windows 7. Start the SARDU utility: Change the image directory to wherever you store your ISOs, this will prevent you from having 2 copies of an ISO wit…
This Micro Tutorial hows how you can integrate  Mac OSX to a Windows Active Directory Domain. Apple has made it easy to allow users to bind their macs to a windows domain with relative ease. The following video show how to bind OSX Mavericks to …

920 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

12 Experts available now in Live!

Get 1:1 Help Now