What I'm trying to achieve is to redirect standard output and standard error from running psexec -nobanner \\1.2.3.4 net localgroup Administrators. When I redirect standard output, the result of the command changes. Something about capturing standard output, in any of the ways I've tried, seems to change the result. I'd like to know why and I'd like to get it working.
In PowerShell, if I run this:
psexec -nobanner \\1.2.3.4 net localgroup Administrators
I see this:
Couldn't access 1.2.3.4:
The trust relationship between this workstation and the primary domain failed.
(Where Couldn't access 1.2.3.4: ends up, I briefly see Connecting to 1.2.3.4... and something else flashes past too quickly to see.)
If I try to capture the output, using this:
$output = psexec.exe -nobanner \\1.2.3.4 net localgroup Administrators
I see:
Couldn't access 1.2.3.4:
The handle is invalid.
(As above, where Couldn't access 1.2.3.4: ends up, I briefly see Connecting to 1.2.3.4....)
I realise that I need to redirect the error stream - that's where I started. But I can't even get the standard output without it changing. This question is about whey the output changes as soon as I try to capture it.
UPDATE
I've just noticed that if I run the same command (that works above in the PowerShell host)
psexec -nobanner \\1.2.3.4 net localgroup Administrators
in PowerShell ISE, I get the same error as below:
psexec : The handle is invalid.
At line:1 char:1
+ psexec -nobanner \\1.2.3.4 net localgroup Administrators
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (The handle is invalid.:String) [], RemoteException
+ FullyQualifiedErrorId : NativeCommandError
Connecting to 1.2.3.4...Couldn't access 1.2.3.4:
Connecting to 1.2.3.4...
So, why does running it in ISE give different output from the normal host?
Other things I've tried:
1. Start-Process
Start-Process -Wait -PSPath 'C:\Windows\PSTools\psexec.exe' -NoNewWindow `
-ArgumentList "-nobanner \\$ip net localgroup Administrators" `
-RedirectStandardError '.\tempError.log' -RedirectStandardOutput '.\tempOutput.log'
'O:'
Get-Content .\tempOutput.log
'E:'
Get-Content .\tempError.log
which gives:
O:
E:
The handle is invalid.
Connecting to 1.2.3.4...
Couldn't access 1.2.3.4:
Connecting to 1.2.3.4...
2. Redirect Standard Output only
psexec -nobanner \\1.2.3.4 net localgroup Administrators > psexec.log
which gives:
Couldn't access 1.2.3.4:
The handle is invalid.
[psexec.log is empty because I'm only redirecting Standard Output and PsExec writes its own messages to Standard Error.]
3. Redirect Standard Error only
I've noticed something else odd about this: if I only redirect Standard Error, it works (PsExec works, the command fails, the output is redirected):
psexec -nobanner \\1.2.3.4 net localgroup Administrators 2> psexec.log
The file psexec.log contains:
psexec : The trust relationship between this workstation and the primary domain failed.
At line:1 char:1
+ psexec -nobanner \\1.2.3.4 net localgroup Administrators 2> ps ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (The trust relat... domain failed.:String) [], RemoteException
+ FullyQualifiedErrorId : NativeCommandError
Connecting to 1.2.3.4...
Couldn't access 1.2.3.4:
Connecting to 1.2.3.4...
4. Redirect all
psexec.exe -nobanner \\1.2.3.4 net localgroup Administrators *>&1 | Set-Variable -Name Output
which gives this:
psexec : The handle is invalid.
At line:1 char:1
+ psexec -nobanner \\1.2.3.4 net localgroup Administrators *>&1 ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (The handle is invalid.:String) [], RemoteException
+ FullyQualifiedErrorId : NativeCommandError
Connecting to 1.2.3.4...Couldn't access 1.2.3.4:
Connecting to 1.2.3.4...
I repeated some of the above using cmd:
5. Redirect Standard Output only, with cmd
cmd /c C:\Windows\PsTools\psexec -nobanner \\1.2.3.4 net localgroup Administrators --% 1> psexec.log
gives:
Couldn't access 1.2.3.4:
The handle is invalid.
(directly to the console).
6. Redirecting Standard Error only, with cmd
cmd /c C:\Windows\PsTools\psexec -nobanner \\1.2.3.4 net localgroup Administrators --% 2> psexec.log
gives (in psexec.log):
The trust relationship between this workstation and the primary domain failed.
Connecting to 1.2.3.4...
Couldn't access 1.2.3.4:
Connecting to 1.2.3.4...
psexec.exe is a simple executable which writes output to stdout (standard output) and stderr (standard error). So, to capture the output use:
psexec.exe > stdout.txt to capture sent to stdout.
psexec.exe 2> sterr.txt to capture output sent to stderr.
psexec.exe > combined.txt 2>&1 to capture both stdout and stderr in a single file.
Interestingly, psexec writes the default message to stderr - usually this would be stdout. So what you see in your shell when running psexec is actually the error output and would need to be captured using 2>.
This returns all the output from myScript.ps1 to the $result variable without all the other junk surrounding PsExec. This is assuming you can get the ps1 file copied to the target machine.
$result = & C:\tools\SysinternalsSuite\PsExec.exe \\$PC -nobanner C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe -executionpolicy bypass -file c:\myScript.ps1 2> $null
In line PS command version
$result = & C:\tools\SysinternalsSuite\PsExec.exe \\$PC -nobanner C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe -executionpolicy bypass -command "get-process" 2> $null
Command line version
$result = & C:\tools\SysinternalsSuite\PsExec.exe \\$PC -nobanner C:\Windows\System32\cmd.exe /c "ipconfig" 2> $null
This is a full script to run and collect report data from remote systems using psexec and runspaces. It is much, much faster than start-job and uses far less memory.
################################################################################################
# PSEXEC_Command_Runspaces
# Uses PSEXEC to run a command on multiple computers. Useful when PS remoting is not enabled
# but you have admin rights.
#
# Requires RSAT tools for the get-adcomputer command. You could import a csv or other method
# to obtain a list of computers instead.
################################################################################################
# Parameters
################################################################################################
#The list of computers to process
$pclist = get-adcomputer -filter "OperatingSystem -eq 'Windows 10 Enterprise' -and Name -like 'RC-*'" -properties DNSHostName | select -ExpandProperty DNSHostName
$Throttle = 500 #number of concurrent runspaces. The higher this is, the more memory is needed for the runspaces. 500 takes less than 1GB for this script.
################################################################################################
# This is the script that will run in each runspace.
################################################################################################
$scriptblock = {
Param (
$nothing, #this empty variable seems to be required because if you pass a single variable, it gets corrupted.
$PC
)
if (test-connection $PC -Count 1 -ea SilentlyContinue) {
# Create script folders on remote computer and copy report script.
md \\$PC\c$\wsapps -ea SilentlyContinue
md \\$PC\C$\wsapps\QA -ea SilentlyContinue
copy 'C:\tools\Powershell\Review Center\PullBIOSandLoggedOnUser.ps1' "\\$pc\c$\wsapps\qa" -Force
# Run ps exec and collect output
# 2> $null gets rid of the "starting service and other junk from the PSexec output
$result = & C:\tools\SysinternalsSuite\PsExec.exe \\$PC -nobanner C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe -executionpolicy bypass -file c:\wsapps\qa\PullBIOSandLoggedOnUser.ps1 2> $null
#remote script file from remote machine. You could also remove folders if apropriate here.
remove-item \\$pc\C$\wsapps\QA\PullBIOSandLoggedOnUser.ps1 -ea SilentlyContinue
#Parse results from single line of output. PS does not return muliple lines of output from PSEXEC when wrapped in a job or runspace.
$parts = $result.split(",")
$outobj = New-Object psobject
$outobj | Add-Member ComputerName $PC
$outobj | Add-Member WindowsVersion ($parts[1].split(":")[1])
$outobj | Add-Member BiosVersion ($parts[2].split(":")[1])
$outobj | Add-Member LoggedOnUser ($parts[3].split(":")[1])
$outobj | Add-Member IPAddress ($parts[4].split(":")[1])
}
else { #report object indicating offline status.
$outobj = New-Object psobject
$outobj | Add-Member ComputerName $PC
$outobj | Add-Member WindowsVersion "Offline"
$outobj | Add-Member BiosVersion "?"
$outobj | Add-Member LoggedOnUser "?"
$outobj | Add-Member IPAddress "?"
}
write-output $outobj
}
################################################################################################
# Main Logic
# Runspaces are much, much faster than start-job and use far less memory
# 260 computers took 4.5GB memory and > 20 minutes to process with start- job
# 260 computers took 260MB memory and < 1 minute to process with runspaces.
################################################################################################
$RunspacePool = [runspacefactory]::CreateRunspacePool(1,$Throttle)
$RunspacePool.Open()
#RSArrayList contains a link to each runspace. Needed to track progress and obtain results later
$RSArrayList = New-Object System.Collections.ArrayList
#Loop through each PC in the list, creating runspaces. The runspace pool is used for multiple parallel spaces with rate control.
foreach ($PC in $PClist) {
$PowerShell = [powershell]::Create()
[void]$PowerShell.AddScript($scriptblock)
[void]$powershell.AddArgument("").AddArgument($PC) #extra argument to avoid single argument corruption bug.
$PowerShell.RunspacePool = $RunspacePool
$ThisRS = New-Object psobject
$ThisRS | Add-Member Computer $PC
$ThisRS | Add-Member PSInstance $PowerShell
$thisRS | Add-Member Space ($PowerShell.BeginInvoke()) #execution starts here.
$RSArrayList += $thisRS
write-host "Adding $PC"
}
################################################################################################
#Progress bar to track when jobs are finished.
write-host "waiting for runspaces to finish"
while (($RSArrayList.space.iscompleted -eq $false).count -gt 0) {
$Done = $RSArrayList.count - ($RSArrayList.space.iscompleted -eq $false).count
if ($Done -eq 0) {$percentComplete = 0}
else {$percentComplete = $Done / $RSArrayList.count * 100}
write-progress -Activity "Waiting for jobs to complete" -Status (($RSArrayList.count - $Done).ToString() + "Left") -PercentComplete $percentComplete
sleep -Seconds 1
}
################################################################################################
#collecting results and creating report object
write-host "Processing Results"
$Report = New-Object System.Collections.ArrayList
foreach ($RS in $RSArrayList) {
$Report += $RS.PSInstance.EndInvoke($RS.Space) #equivilant to "receive-job"
$RS.PSInstance.Dispose() # frees up memory.
}
$Report | ft
This is the report collection script that is run on the remote system
################################################################################################
# Looks up the computer name, Windows Version, BIOS version, logged on user, and IP address of the computer.
# Designed to be called by start-job or runspaces (much faster).
################################################################################################
$computername = $env:COMPUTERNAME
$WindowsVersion = (Get-WmiObject win32_OperatingSystem).BuildNumber.toString()
$BiosVersion = (Get-WmiObject Win32_BIOS).Name
$IPAddress = "No 10 range IP"
$addr = (Get-NetIPAddress -AddressFamily IPv4).ipaddress | where {$_ -like '10.*'}
if ($addr) {$IPAddress = $addr}
$LoggedOnUser = "None"
$quser = (quser.exe 2> $null | select-string "console").line
if ($quser) {$LoggedOnUser = $quser.Substring(1,$quser.IndexOf(" ",1)-1)}
# For whatever reason, PS will not return multiple lines of output from PSexec when run under start-job or runspaces. This was the workaround.
"Computername:$computername,WindowsVersion:$WindowsVersion,BIOSVersion:$BIOSVersion,LoggedOnUser:$LoggedOnUser,IPAddress:$IPAddress"
Related
I have a VM running Windows Server 2012.And some intended services on it. I want to change a configuration file on this VM machine remotely from my desktop pc.
Currently I change this configuration file by mapping the C: drive of the remote server and then changing the file. Now this blocks me from changing multiple servers as I can't map multiple server c: simultaneously to same drive. Also, mapping hundreds of drives wouldn't be ideal.
The way I am changing the file by mapping drive is:
$password = <password text> | ConvertTo-SecureString -asPlainText -Force
$username = "admin"
$credential = New-Object System.Management.Automation.PSCredential($username,$password)
net use Z: \\$ipAddress\C$ <password> /user:admin type 'z:\Program Files\lalaland\Data\Settings.xml'
(Get-Content 'z:\Program Files\lalaland\Data\Settings.xml') | ForEach-Object { $_ -replace $oldString, $newString } | Set-Content 'z:\Program Files\lalaland\Data\Settings.xml'
type 'z:\Program Files\lalaland\Data\Settings.xml'
net use z: /delete
Therefore I searched for a better option and found this script at https://gallery.technet.microsoft.com/scriptcenter/PowerShell-Replace-String-58fbfa85 but it doesn't work for me.
I am using this script as :
.\Replace-StringInFile.ps1 -ComputerName <RemoteComputerHostName> -TargetPath 'C:\Program Files\lalaland\Data' -Fil
eName Settings.xml -Replace $oldString -ReplaceWith $newString -Credential (Get-Credential)
when I run the command credential window pops up asking for username and password. I enter the username and password that I used for mapping the drive, but it throws the following error:
New-PSSession : [<RemoteHostName>] Connecting to remote server <RemoteHostName> failed with the following error message : The user name or password is incorrect. For more information, see the
about_Remote_Troubleshooting Help topic.
At D:\workspace\Replace-StringInFile.ps1:84 char:14
+ ... $Session = New-PSSession -ComputerName $Computer -Credential $Creden ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : OpenError: (System.Manageme....RemoteRunspace:RemoteRunspace) [New-PSSession], PSRemotingTransportException
+ FullyQualifiedErrorId : LogonFailure,PSSessionOpenFailed
what I don't understand is when I map the drive with the same credentials it works fine but using the other script, which internally uses New-PSSession doesn't work.
Any idea ?
I was able to do this using the following cmdlet:
function Edit-RemoteFileStringReplace
{
[cmdletbinding()]
param([Parameter(Mandatory=$true)][ValidateScript({$_ -match [IPAddress]$_ })][string]$Address,
[Parameter(Mandatory=$true)][string]$FilePath,
[Parameter(Mandatory=$true)][string]$Replace,
[Parameter(Mandatory=$true)][string]$ReplaceWith,
[Parameter(Mandatory=$true)][System.Management.Automation.PSCredential]$Credential
)
$DriveLetter=""
$FilePathWithoutDriveLetter=""
$DriveName = $Address.replace('.','x')
$DriveName = "PSDrive_$DriveName"
$DriveLetter = (Get-DriveLetterFromPath -Path $FilePath).Substring(0,1)
$FilePathWithoutDriveLetter = Remove-DriveLetterFromPath -Path $FilePath
$MappedFilePath="$DriveName" + ":$FilePathWithoutDriveLetter"
New-PSDrive -Name $DriveName -PSProvider FileSystem -Root \\$Address\$DriveLetter$ -Credential $Credential -ErrorAction Stop
(Get-Content $MappedFilePath) | ForEach-Object { $_ -replace $Replace, $ReplaceWith } | Set-Content $MappedFilePath
Remove-PSDrive -Name $DriveName
}
I need to remotely run powershell commands on multiple windows servers using SysInternal tools but I tried a lot but it seems not to work . Any help is highly appreciated .
Servers.csv (file content)
10.10.10.100
10.0.0.111
Code :
$List = Import-CSV -Path "C:\Users\javed\Desktop\New\servers.csv"
foreach ($entry in $List) {
if (test-Connection -Cn $($entry.Name) -quiet) {
& C:\Users\javed\Downloads\PsTools\psexec.exe \\$($entry.Name) -u "$($entry.Name)\Admin" -p 'P#ssword' -accepteula cmd /c " HostName >> C:\Users\javed\Desktop\New\Script.log"
} else {
"$computer is not online" >> C:\Users\javed\Desktop\New\Script.log
}
}
Output :
PS C:\Users\javed> C:\Users\javed\Desktop\New\Change-NetworkRoute.ps1
PsExec v2.2 - Execute processes remotely
Copyright (C) 2001-2016 Mark Russinovich
Sysinternals - www.sysinternals.com
psexec.exe : The handle is invalid.
At C:\Users\javed\Desktop\New\Change-NetworkRoute.ps1:26 char:9
+ & C:\Users\javed\Downloads\PsTools\psexec.exe \\$($entry.Name) -u "$(
...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (The handle is invalid.:String) [], Re
moteException
+ FullyQualifiedErrorId : NativeCommandError
Connecting to 10.10.10.100...Couldn't access 10.10.10.100:
Connecting to 10.10.10.100...
PS C:\Users\javed>
Apparently your command line isn't completely evaluated, note that PSExec shows \\$($entry.Name) in the error rather then the actual name.
The correct way to troubleshoot this is to put the first put the command is a variable, show the variable, and then execute it:
$CommandLine = "C:\Users\javed\Downloads\PsTools\psexec.exe \\$($entry.Name) -u ..."
Write-Host $CommandLine # Confirm that this is the command line you expect
if (test-Connection -Cn $($entry.Name) -quiet) {
&$CommandLine
} else {
Anyhow, it is a bad idea to use an external command line as PSExec including credentials to achieve something like this. Instead, I would use WMI to retrieve the actual name of the servers. Something like this:
$SPAdmin = "$($entry.Name)\Admin"
$Password = "P#ssword" | convertto-securestring
$Credential = new-object -typename System.Management.Automation.PSCredential -argumentlist $SPAdmin, $Password
$Computer = Get-WmiObject -Class Win32_Computersystem -ComputerName $entry.Name -Credential $Credential
Write-Host $Computer.Name
We have moved our system alerting over to SCOM 2012 and receive heartbeat alerts when servers go offline. Presently there are approximately 750 servers in the vDC that I am managing. The SCOM 2012 server is in a different untrusted domain.
I have one working script where it puts the servers in Maintenance mode, but its run serially and takes about 40 minutes to put nearly 400 servers in Maintenance Mode. This is a workable solution, but I would like to use the foreach -parallel command to speed it up.
I have my workflow (to use the foreach -parallel command) created and placed in one of the default PowerShell Module locations on the Source and Destination Machines. I've tested the set of commands outside of the workflow on the SCOM server and it runs successfully. When I try to run the command remotely via an Invoke-command, the SCOM commands come back as being unrecognized.
#Get Date for usage in Connection Name
$Date = Get-Date -Format HHmmsss
#combine Name with Date to uniquify
$name = "ScomMM" + $Date
#Collect Servers from WSUS Server
[reflection.assembly]::LoadWithPartialName("Microsoft.Updateservices.Administration") | out-null
$WSUS = [Microsoft.updateservices.administration.adminproxy]::Getupdateserver("ServerName",$false,8530);
$TS = $wsus.getcomputertargetgroups() | ? {($_.name -eq "Group1") -or ($_.Name -eq "Group2")}
$computers = $TS.getcomputertargets() | Select-Object FullDomainName
#Setup Trusted host
invoke-Command -ScriptBlock {(winrm.cmd s winrm/config/client '#{TrustedHosts="*PublicIPAddress*"}')}
Enable-PSRemoting -Force
#Credentials stored in a file
$username = "username"
$password = get-content 'Path' | convertto-securestring
$creds = New-Object -TypeName System.Management.Automation.PSCredential -argumentlist $username,$password
$session = new-PSSession -ComputerName "IPAddress" -Credential $creds -Name $name
#SCOM Commands Module
Import-Module OperationsManager
#Workflow
Import-Module Set-MM
#Run the command remotely
Invoke-Command -Session $session -ScriptBlock {
Import-Module -Name Set-MM
Set-MM -computers $using:computers
}
#Workflow - Stored at C:\Windows\System32\WindowsPowerShell\v1.0\Modules\Set-MM\Set-MM.psm1
Workflow Set-MM
{
Param($computers)
Foreach -Parallel($computer in $computers)
{
Get-SCOMClassInstance -Name $computers.FullDomainName | Start-SCOMMaintenanceMode -EndTime (Get-Date).AddMinutes(6) -Reason PlannedOperatingSystemReconfiguration
}
}
At C:\Windows\system32\WindowsPowerShell\v1.0\Modules\Set-MM\Set-MM.psm1:6 char:3
+ Get-SCOMClassInstance -Name $computers.FullDomainName | Start ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Cannot find the 'Get-SCOMClassInstance' command. If this command is defined as a workflow, ensure it is
defined before the workflow that calls it. If it is a command intended to run directly within Windows
PowerShell (or is not available on this system), place it in an InlineScript: 'InlineScript {
Get-SCOMClassInstance }'
+ CategoryInfo : ParserError: (:) [], ParseException
+ FullyQualifiedErrorId : CommandNotFound
+ PSComputerName : IPAddress
The term 'Set-MM' 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.
+ CategoryInfo : ObjectNotFound: (Set-MM:String) [], CommandNotFoundException
+ FullyQualifiedErrorId : CommandNotFoundException
+ PSComputerName : IPAddress
Get-PSSession | Remove-PSSession
If I use an Inlinescript on the script inside the foreach -Parallel
InlineScript{Get-SCOMClassInstance -Name $Using:computers.FullDomainName | Start-SCOMMaintenanceMode -EndTime (Get-Date).AddMinutes(6) -Reason PlannedOperatingSystemReconfiguration}
I get this for each computer that is attempting to get processed in the workflow:
The term 'Get-SCOMClassInstance' 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.
+ CategoryInfo : ObjectNotFound: (Get-SCOMClassInstance:String) [], CommandNotFoundException
+ FullyQualifiedErrorId : CommandNotFoundException
+ PSComputerName : ServerName
Lets say there is an application with the name exampleService that should be running on Server1. This code works if it is running. However when it's not running, it crashes.
$application = Get-Process -ComputerName Server1 -Name "exampleService"
I get this crash if the application is not running. Is there a more graceful way of finding out if it's not running (without crashing)
Get-Process : Cannot find a process with the name "exampleService". Verify the process name and call the cmdlet again.
At line:1 char:16
+ $application = Get-Process -ComputerName Server1 -Name "exampleService"
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : ObjectNotFound: (Sampler:String) [Get-Process], ProcessCommandException
+ FullyQualifiedErrorId : NoProcessFoundForGivenName,Microsoft.PowerShell.Commands.GetProcessCommand
Also is it possible to launch the application on the Server if it's not running?
The Server is running Windows Server 2012. The PowerShell commands are being run from a Windows 7 64-bit PC.
Look at using -ErrorAction SilentlyContinue to keep that error from displaying. You can use that in an If statement to launch the application if it isn't running.
--Updated to include launching the remote process
If (-NOT (Get-Process -Computername Server1 -name "cmd" -ErrorAction SilentlyContinue)) {
Write-Host "Launch application"
$application = "c:\windows\system32\cmd.exe"
$start = ([wmiclass]"\\Server1\Root\CIMV2:win32_process").Create($application)
}
You could set the ErrorActionto SilentlyContinue (alias for that is -ea 0):
$application = Get-Process -ComputerName Server1 -Name "exampleService" -ea 0
Now you can check $application and start the application if its null.
I only wanted my script to continue on one particular Get-Process error, i.e. process not found. (and I preferred to use a Try/Catch). But I haven't done much powershell and had trouble locating the specific error.
Once I found I could look at FullyQualifiedErrorId and added the following to a general Catch block I located what I was after.
Write-Host ('FullyQualifiedErrorId: ' + $_.FullyQualifiedErrorId);
So as a full example which works for my situation:
Try {
$application = Get-Process -Name "exampleService" -ea Stop #note the -ea Stop is so try catch will fire whatever ErrorAction is configured
} Catch [Microsoft.PowerShell.Commands.ProcessCommandException] {
If ($_.FullyQualifiedErrorId) {
If ($_.FullyQualifiedErrorId -eq "NoProcessFoundForGivenName,Microsoft.PowerShell.Commands.GetProcessCommand"){
Write-Host "Presume not running. That is OK."; # or you could perform start action here
}
}
else {
throw #rethrow other processcommandexceptions
}
} Catch {
# Log details so we can refine catch block above if needed
Write-Host ('Exception Name:' + $_.Exception.GetType().fullname); # what to put in the catch's square brackets
If ($_.FullyQualifiedErrorId) {
Write-Host ('FullyQualifiedErrorId: ' + $_.FullyQualifiedErrorId); #what specific ID to check for
}
throw #rethrow so script stops
}
I am currently writing a PowerShell script to stop a few processes, start a program, and then restart those processes. The problem arises when I feed the processes into an array variable to restart them later.
My code:
$direc = "C:\Program Files (x86)\PathTo\Program.exe";
$arguments = "/theseArguments1", "/theseArguments2";
$processesDefined = #();
$processes = "notepad", "explorer";
ForEach ($i in $processes)
{
$processesDefined += get-process -name $i | select-object path;
get-process -name $i | stop-process -force;
}
start-process -filepath $direc -ArgumentList $arguments -NoNewWindow -wait;
ForEach ($i in $processesDefined)
{
start-process -filepath $i;
}
When debugging the $processesDefined.Count displays 2 and the $processesDefined displays as expected yet when it gets to tjhe time to start the processes I get:
Start-Process : This command cannot be executed due to the error: The system ca
nnot find the file specified.
At D:\Desktop\Aion.ps1:17 char:18
+ start-process <<<< -FilePath $i;
+ CategoryInfo : InvalidOperation: (:) [Start-Process], InvalidOp
erationException
+ FullyQualifiedErrorId : InvalidOperationException,Microsoft.PowerShell.C
ommands.StartProcessCommand
I've done a lot of searching but can't find anything of real help. Any help with this would be greatly appreciated. Thank you.
Output of $processesDefined:
[DBG]: PS D:\Desktop>>> $processesDefined
Path
----
C:\Program Files\Windows Media Player\wmpnetwk.exe
C:\Windows\explorer.exe
_____________________________________________________________
Try doing it this way. The way you are doing it leaves the variable $processesDefined with a table heading (PATH) and this will screw things up.
$processesDefined += (get-process -name $i).Path
This way will just give you the path with no table heading