[Last Call] Learn about multicloud storage options and how to improve your company's cloud strategy. Register Now

x
?
Solved

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

Posted on 2016-11-17
4
Medium Priority
?
134 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
[X]
Welcome to Experts Exchange

Add your voice to the tech community where 5M+ people just like you are talking about what matters.

  • Help others & share knowledge
  • Earn cash & points
  • Learn & ask questions
  • 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

Looking for the Wi-Fi vendor that's right for you?

We know how difficult it can be to evaluate Wi-Fi vendors, so we created this helpful Wi-Fi Buyer's Guide to help you find the Wi-Fi vendor that's right for your business! Download the guide and get started on our checklist today!

Question has a verified solution.

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

This process allows computer passwords to be managed and secured without using LAPS. This is an improvement on an existing process, enhanced to store password encrypted, instead of clear-text files within SQL
IF you are either unfamiliar with rootkits, or want to know more about them, read on ....
Microsoft Active Directory, the widely used IT infrastructure, is known for its high risk of credential theft. The best way to test your Active Directory’s vulnerabilities to pass-the-ticket, pass-the-hash, privilege escalation, and malware attacks …
Finding and deleting duplicate (picture) files can be a time consuming task. My wife and I, our three kids and their families all share one dilemma: Managing our pictures. Between desktops, laptops, phones, tablets, and cameras; over the last decade…

650 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