?
Solved

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

Posted on 2016-11-17
4
Medium Priority
?
150 Views
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.


<#
    report-service-accounts.ps1
    
    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 )

            $serviceList
        }
        catch{
            "Failed to retrieve data from $hostname : $($_.toString())"
        }
    }
    else{
        "$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
    ++$count_servers
    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 }
    processCompletedJobs
}

# 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
processCompletedJobs


# 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>
<html>
<head>
<style>
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} 
</style>
</head>
<body> 
<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>" } )
</body>
</html>"  

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

Open in new window


Thanks in advance.
0
Comment
  • 2
4 Comments
 
LVL 41

Assisted Solution

by:footech
footech earned 400 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.
0
 
LVL 85

Accepted Solution

by:
oBdA earned 1600 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.
<#
.SYNOPSIS
Gets a list of accounts used in services and tasks.
.DESCRIPTION
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.
.PARAMETER Filter
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.
.PARAMETER TaskOnly
Process only tasks.
.INPUTS
System.String
.OUTPUTS
System.Management.Automation.PSCustomObject
.EXAMPLE
Get-ServiceOrTaskAccount
.EXAMPLE
Get-ServiceOrTaskAccount -ComputerName SomeMachine, SomeOtherMachine -ServiceOnly
.EXAMPLE
Get-Content .\servers.txt | Get-ServiceOrTaskAccount -Filter "Domain\Administrator", "Administrator@domain.com"
#>
#requires -Version 3
[CmdletBinding(DefaultParameterSetName="Get_Service_Task")]
Param(
	[Parameter(Mandatory=$false, Position=0, ValueFromPipeline=$True)]
	[string[]]$ComputerName = @($ENV:ComputerName),
	[string[]]$Filter,
	[Parameter(Mandatory=$false, ParameterSetName="Get_Service")]
	[switch]$ServiceOnly,
	[Parameter(Mandatory=$false, ParameterSetName="Get_Task")]
	[switch]$TaskOnly
)
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

1
 
LVL 8

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.
0
 
LVL 8

Author Closing Comment

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

Featured Post

Free Tool: SSL Checker

Scans your site and returns information about your SSL implementation and certificate. Helpful for debugging and validating your SSL configuration.

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.

Question has a verified solution.

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

Windows Server 2003 introduced persistent Volume Shadow Copies and made 2003 a must-do upgrade.  Since then, it's been a must-implement feature for all servers doing any kind of file sharing.
A walk-through example of how to obtain and apply new DID phone numbers to your cloud PBX enabled users that are configured in Office 365. Whether you have 1, 10 or 100+ users in your tenant, it's quite easy to get them phone-enabled and making/rece…
In this video, viewers will be given step by step instructions on adjusting mouse, pointer and cursor visibility in Microsoft Windows 10. The video seeks to educate those who are struggling with the new Windows 10 Graphical User Interface. Change Cu…
Exchange organizations may use the Journaling Agent of the Transport Service to archive messages going through Exchange. However, if the Transport Service is integrated with some email content management application (such as an anti-spam), the admin…

862 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