Albert Widjaja
asked on
Please help to combine two PowerShell scripts for Computer service account to work properly ?
People,
I need some assistance to modify the below script so it runs with specific filtering on certain OU:
When you execute this line below:
It works and shows the server OS, so how do you combine the two script above to make it running ?
I have tried below but end up in error:
Error: Expressions are only allowed as the first element of a pipeline.
I need some assistance to modify the below script so it runs with specific filtering on certain OU:
<#
Created by: https://www.experts-exchange.com/members/oBdA.html
.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')) {
$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 {
}
When you execute this line below:
Get-ADComputer -Properties * -Filter {Enabled -eq $True -and OperatingSystem -like "*Server*"} -SearchBase "DC=Domain,DC=com" | Where-Object {Test-Connection $_.Name -Count 1 -Quiet} |Select -ExpandProperty Name
It works and shows the server OS, so how do you combine the two script above to make it running ?
I have tried below but end up in error:
$Servers = Get-ADComputer -Properties * -Filter {Enabled -eq $True -and OperatingSystem -like "*Server*"} -SearchBase "DC=domain,DC=com" | Where-Object {Test-Connection $_.Name -Count 1 -Quiet}
$Servers | 'C:\temp\Get-ServiceOrTaskAccount.ps1' -Filter "Domain\Admin*", "Admin*@domain.com" | Export-CSV -Path C:\Result.CSV -NTI
Error: Expressions are only allowed as the first element of a pipeline.
ASKER
David, thanks for the quick reply, so how about the -Filter "Domain\Admin*", "Admin*@domain.com" parameter ?
is it possible to be fitted in the script as you suggested ?
is it possible to be fitted in the script as you suggested ?
just add it as a parameter
ASKER
Somehow it is still not working like the below error:
I have placed the script in the same C:\Scripts\ directory but it is still not working ?
Get-ADComputer : The server has returned the following error: invalid enumeration context.
At line:4 char:12
+ $Servers = Get-ADComputer -Filter {Enabled -eq $True -and OperatingSy ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~ ~~~~~~~~~~ ~~
+ CategoryInfo : NotSpecified: (:) [Get-ADComputer], ADException
+ FullyQualifiedErrorId : ActiveDirectoryServer:0,Microsoft.Ac tiveDirect ory.Manage ment.Comma nds.GetADC omputer
C:\Scripts\Get-ServiceAccounts.ps1 : The term 'C:\Scripts\Get-ServiceAcc ounts.ps1' is not recognized as the name of a cmdlet, function, script file, or operable program. Check the spelling of the name, or if a path was included, verify that the path is correct and try again.
At line:5 char:23
+ $results = $servers | C:\Scripts\Get-ServiceAccounts.ps1
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : ObjectNotFound: (C:\Scripts\Get-ServiceAccounts.ps1: String) [], CommandNotFoundException
+ FullyQualifiedErrorId : CommandNotFoundException
Export-Csv : Cannot bind argument to parameter 'InputObject' because it is null.
At line:6 char:12
+ $results | Export-Csv -NoTypeInformation -Path c:\Scripts\results.csv
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~ ~~~~~~~~~
+ CategoryInfo : InvalidData: (:) [Export-Csv], ParameterBindingValidationException
+ FullyQualifiedErrorId : ParameterArgumentValidationErrorNull NotAllowed ,Microsoft .PowerShel l.Commands .ExportCsv Command
I have placed the script in the same C:\Scripts\ directory but it is still not working ?
I don't know.. perhaps you need to hard code it
Your first command was pretty close.
The main issue is that you enclosed the path to the script in quotes (which isn't a bad thing per se), which turns it into a string, as far as PowerShell is concerned. Since you want to execute it, you need to use the "call" operator "&"
Second issue is that the script doesn't handle wildcards, so you'll have to list out the admin accounts you're looking for
The main issue is that you enclosed the path to the script in quotes (which isn't a bad thing per se), which turns it into a string, as far as PowerShell is concerned. Since you want to execute it, you need to use the "call" operator "&"
Second issue is that the script doesn't handle wildcards, so you'll have to list out the admin accounts you're looking for
$Servers = Get-ADComputer -Properties * -Filter {Enabled -eq $True -and OperatingSystem -like "*Server*"} -SearchBase "DC=domain,DC=com" | Where-Object {Test-Connection $_.Name -Count 1 -Quiet}
$Servers | & 'C:\temp\Get-ServiceOrTaskAccount.ps1' -Filter "Domain\Admin", "Admin@domain.com" | Export-CSV -Path C:\Result.CSV -NTI
ASKER
Cool,
OBDA, can I put the two lines you suggested into the same Script file ?
Let say, at the bottom of the main script file.
OBDA, can I put the two lines you suggested into the same Script file ?
Let say, at the bottom of the main script file.
It's PowerShell, of course you can. Just save them as Whatever.ps1
Or do you want to combine the Get-ServiceOrTaskAccount.p s1 and the AD query into one single script?
Or do you want to combine the Get-ServiceOrTaskAccount.p
ASKER
Yes, that's what I meant.
You wrap the current script into a function, and then just call the function:
Function Get-ServiceOrTaskAccount {
<#
.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"
.NOTES
Created by: https://www.experts-exchange.com/members/oBdA.html
#>
#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')) {
$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 {
}
}
Get-ADComputer -Properties * -Filter {Enabled -eq $True -and OperatingSystem -like "*Server*"} -SearchBase "DC=domain,DC=com" |
Where-Object {Test-Connection $_.Name -Count 1 -Quiet} |
Get-ServiceOrTaskAccount -Filter "Domain\Admin", "Admin@domain.com" |
Export-Csv -NoTypeInformation -Path C:\Result.CSV
ASKER
Obda,
Thanks for the reply, I've customized it like the below:
This is the error:
Thanks for the reply, I've customized it like the below:
$LogfilePath = "C:\Result.CSV"
$OUList = @(
"OU=Site 1,DC=Domain,DC=com"
"OU=Site 2,DC=Domain,DC=com"
)
$UserAccounts = @(
"Domain\Administrator"
"Administrator@Domain.com"
)
$OUList | ForEach {
$OU = $_
Get-ADComputer -Properties * -Filter {Enabled -eq $True -and OperatingSystem -like "*Server*"} -SearchBase $OU -SearchScope Subtree |
Where-Object {Test-Connection $_.Name -Count 1 -Quiet} |
Get-ServiceOrTaskAccount -Filter @UserAccounts |
Export-CSV -Path $LogfilePath -NoTypeInformation
}
Write-Host "Done; results written to '$($LogfilePath)'." -ForegroundColor White
This is the error:
Get-ServiceOrTaskAccount : The input object cannot be bound to any parameters for the command either because the command does not take pipeline input or the input and its properties do
not match any of the parameters that take pipeline input.
At C:\Get-ServiceOrTaskAccount.ps1:146 char:6
+ Get-ServiceOrTaskAccount -Filter @UserAccounts |
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidArgument: (CN=PRDMAIL01-VM,OU=...BS,DC=com,:PSObject) [Get-ServiceOrTaskAccount], ParameterBindingException
+ FullyQualifiedErrorId : InputObjectNotBound,Get-ServiceOrTaskAccount
ASKER CERTIFIED SOLUTION
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
ASKER
Many thanks for the sharing and assistance in this matter ObdA.
It works.
Despite some weird error like in the result attached.
result.csv
It works.
Despite some weird error like in the result attached.
result.csv
Open in new window