Solved

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

Posted on 2016-11-17
4
35 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 39

Assisted Solution

by:footech
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.
0
 
LVL 82

Accepted Solution

by:
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.
<#
.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 7

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 7

Author Closing Comment

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

Featured Post

Do You Know the 4 Main Threat Actor Types?

Do you know the main threat actor types? Most attackers fall into one of four categories, each with their own favored tactics, techniques, and procedures.

Join & Write a Comment

Suggested Solutions

Utilizing an array to gracefully append to a list of EmailAddresses
Create and license users in Office 365 in bulk based on a CSV file. A step-by-step guide with PowerShell script examples.
This tutorial will walk an individual through the process of transferring the five major, necessary Active Directory Roles, commonly referred to as the FSMO roles to another domain controller. Log onto the new domain controller with a user account t…
Windows 8 came with a dramatically different user interface known as Metro. Notably missing from that interface was a Start button and Start Menu. Microsoft responded to negative user feedback of the Metro interface, bringing back the Start button a…

707 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

15 Experts available now in Live!

Get 1:1 Help Now