Powershell Start-Process "PsExec" does not print results - powershell

I'm starting a process with PsExec in the script and I want the results to appear on the screen.
The command runs without problem. There is no error message And no result message either.
How can I start the process in the same window. The -NoNewWindow argument just hides the newly opened window. It doesn't print the result to Powershell.
$PSExec = "C:\Windows\System32\PsExec.exe"
$hostname = Read-Host -Prompt 'Hostname or IP Adress'
$command1 = 'cmd /c "net stop issuser"'
$command2 = 'cmd /c "net start issuser"'
Start-Process -Wait -Filepath "$PSExec" -ArgumentList "\\$hostname $command1" -NoNewWindow
Start-Process -Wait -Filepath "$PSExec" -ArgumentList "\\$hostname $command2" -NoNewWindow
Thanks.

getting somthing back: add the parameter -passthru to start-process.
But I think why do you use psexec and net start/stop - welcome to powershell:
$hosts = #("hostA","hostB")
$code = {
try {
$null = Stop-Service -Name issuer -ErrorAction:stop
$stopOperation='success'
}
Catch {
$stopOperation='failed'
$stopException=$_
}
try {
$startOperation = start-service -Name issuer -ErrorAction:stop
$startOperation='success'
}
Catch {
$startOperation='failed'
$startException=$_
}
$attrsht = #{
Name=Issuer
stopOperation=$stopOperation
stopOperationExecption=$stopException
startOperation=$startOperation
startOperationException=$startException
}
return New-Object -typename psobject -Property $attrsht
}
$result = invoke-command -computername $hosts -ScriptBlock $code
ok that makes much sense, but it is what it is ;-) alternatively:
Invoke-CimMethod -Query "select name from win32_service where name = 'issuer'" -MethodName startservice -ComputerName $host
Invoke-CimMethod -Query "select name from win32_service where name = 'issuer'" -MethodName stopservice -ComputerName $host
Hehe, sorry the cim cmdlets are also using WinRm behind the scenes. Back to the old/outdated wmi:
(Get-WmiObject -Query "select name from win32_service where name = 'issuer'" -ComputerName $host).startservice()
(Get-WmiObject -Query "select name from win32_service where name = 'issuer'" -ComputerName $host).stopservice()

Note:
You stated that use of psexec is a must in your case - see the next section, which applies to calling any external console application from PowerShell.
In cases where PowerShell remoting is available or can be set up, you could use Invoke-Command, which, like psexec, relays the output from remotely executed commands:
# Add -ErrorAction Stop to abort the script if stderr output is received.
# To act on process exit codes, examine $LASTEXITCODE in the script block.
Invoke-Command -ComputerName $hostname { net stop issuser; net start issuser }
More simply, you can use PowerShell's Restart-Service cmdlet:
Invoke-Command -ComputerName $hostname { Restart-Service issuser}
In order to invoke console applications synchronously in the current window, with their stdout and stderr streams connected to PowerShell's, invoke them directly - do not use Start-Process:
Start-Process cannot directly return a command's output (you can only redirect to files, with -RedirectStandardOutput and -RedirectStandardError).
See this answer for more information about direct invocation vs. Start-Process.
Therefore:
# Directly executes psexec and outputs its results (stdout and stderr).
# Note:
# -replace '"', '\"' is unfortunately needed up to a least PowerShell 7.2.x
& $PSExec \\$hostname ($command1 -replace '"', '\"')
& $PSExec \\$hostname ($command2 -replace '"', '\"')
Note: You could combine the net stop and net start commands into a single cmd /c invocation and therefore a single psexec call.
Note:
Since your executable path is specified as a variable, you must use &, the call operator for invocation; the same would apply if the path were quoted - see this answer for more information.
Unfortunately, -replace '"', '\"', which escapes embedded " characters as \", is necessary up to at least PowerShell 7.2.x, due to a long-standing bug - see this answer. (Note that your particular commands don't strictly need embedded " characters.)

Why don't you use PowerShell Remoting instead of PSExec for this?
That way your command would be something like this
Invoke-Command -ComputerName $hostname -ScriptBlock { Stop-Service iisuser -PassThru }
Invoke-Command -ComputerName $hostname -ScriptBlock { Start-Service iisuser -PassThru }
Of course you have to have enabled PowerShell remoting for that, but capturing output will be much easier from remote machines to your machine.
If you really want to use PSExec, you should use Invoke-Expression instead of Start-Process.
For example, starting remote notepad process:
Invoke-Expression -Command 'psexec -i \\server01 notepad'
Your command would be something like this then
Invoke-Expression -Command "psexec \\$hostname $command1"
This will open a new window and stop the service, but after is finishes it will close the window. If you want to capture the output you need to redirect that to a text file you could do something like this
Invoke-Expression -Command "psexec \\$hostname $command2" 2> C:\Scripts\output2.txt

Related

Powershell Invoke Command is not working for Remote Machine and did not returning any error message

I am trying to install my MSI package on the remote machine. Below is the script I am using. In this script, The invoke command execution is not returning any error or success message.but the copy command is working fine. anyone help to find the issue.
param([String]$MSILocation,[String]$RemoteComputerName,[String]$UserName,[String]$Password,[String]$Port,[String]$AccessKey,[String]$Protocol)
$ServeAdminPassword= $Password | ConvertTo-SecureString -AsPlainText -Force
$cred=New-Object System.Management.Automation.PSCredential($UserName,$ServeAdminPassword)
$hostname = hostname
$session = New-PSSession -ComputerName $RemoteComputerName -Credential $cred
$cpy = Copy-Item $MSILocation -Destination "C:\Users\Administrator\Desktop\" -ToSession $session
try
{
$result = Invoke-Command -Session $session -ScriptBlock {
param($hostname,$Port,$Protocol)
$value = msiexec /i "C:\Users\Administrator\Desktop\software.msi" SERVERNAME=$hostname PORTNO=$Port PROTOCOL=$Protocol /qb /forcerestart
calc.exe
} -ArgumentList $hostname,$Port,$Protocol | Select-Object value
}
catch
{
$result = $error[0]
echo $result
}
msiexec is unusual in that it runs asynchronously by default, so you need to explicitly wait for its termination in order to learn its exit code.
The exit code is never returned via output (stdout); instead, depending on invocation technique, you must query a Process object's .ExitCode property (when you use Start-Process -Wait -PassThru) or use the automatic $LASTEXITCODE variable (when you use the technique shown below).
A simple way to force synchronous execution and have the exit code be reflected in $LASTEXITCODE is to call via cmd /c:
# Call msiexec and wait for its termination.
cmd /c "msiexec /i C:\Users\Administrator\Desktop\software.msi SERVERNAME=$hostname PORTNO=$Port PROTOCOL=$Protocol /qb /forcerestart"
$LASTEXITCODE # Output msiexec's exit code.
See this answer for details.

Power shell Invoke remote script is not working

I am trying to invoke a remote bat file from my local machine that should start and run the bat in a remote machine terminal. My script is able to connect and show me the output. However, it is invoking the remote bat file but waiting on my screen with bat file output. and my idea is the bat file should invoke and running in the remote machine rather than showing the output on my terminal local terminal. What should be the way here?
loop{
Invoke-Command -ComputerName $computer -credential $cred -ErrorAction Stop -ScriptBlock { Invoke-Expression -Command:"cmd.exe /c 'C:\apacheserver.bat'" }
}
From what I understand you want the .bat file to not show you the result. If so, you should do it with the Out-Null or by redirecting STDOUT and STDERR to NULL.
IE: Invoke-Expression -Command:"cmd.exe /c 'C:\apacheserver.bat'" | Out-Null
If I'm understanding correctly, you want to suppress the output from the .bat file inside your local console? If so, redirecting to some form of $null is the way to go. You also shouldn't need Invoke-Expression as Invoke-Command can run the file directly:
Invoke-Command -ComputerName $computer -Credential $cred -ErrorAction Stop -Scriptblock {
cmd.exe /c 'C:\apacheserver.bat' | Out-Null
}
You could also use > $null instead of Out-Null if you prefer.
If you want to redirect the output of the Invoke-Command call to the remote console instead, that kind of defeats the purpose of the cmdlet. You could try redirecting the console output to a file on the remote computer if you want a local record:
$remoteFile = '\\server\C$\Path\To\Output.txt'
Invoke-Command -ComputerName $computer -Credential $cred -ErrorAction Stop -Scriptblock {
cmd.exe /c 'C:\apacheserver.bat' > $remoteFile
}
If I understand you, you want to run the script in the background and not wait for it:
Invoke-Command $computer -Credential $cred { start-process C:\apacheserver.bat }

Trying to run commands remote with powershell but not luck

I'm looking for help to run commands on remote computers regarding a line with mcafee agent to get it command run remotely.
$Machines = Get-Content -Path "C:\server_list.txt"
foreach ($computer in $Machines){
Write-host "Executing Events on $computer" -b "yellow" -foregroundcolor "red"
$command = Start-Process -NoNewWindow -FilePath "C:\Program Files\McAfee\Agent\cmdagent.exe" -ArgumentList "/e /l C:\temp"
Invoke-Command -ComputerName $computer -ScriptBlock {$command}
}
When I executed this command run locally but not remotely.
I'm looking for help here I have not full experience but I'm started automating some task on my job.
Please suggest some tips
I really appreciate it
Thanks
$command = Start-Process -NoNewWindow -FilePath "C:\Program Files\McAfee\Agent\cmdagent.exe" -ArgumentList "/e /l C:\temp"
This doesn't define a command for later execution with Invoke-Command, it instantly executes the Start-Process command, which is not your intent, and it is the reason it runs locally.
To fix that, you'd have to define it as a script block ({ ... }):
$command = { Start-Proces ... }, and then pass it as-is to Invoke-Command's -ScriptBlock parameter (Invoke-Command -ComputerName $computer -ScriptBlock $command) (don't enclose it in { ... } again).
Additionally, I suggest taking advantage of Invoke-Command's ability to target multiple computers at once, in parallel, and to avoid using Start-Process for synchronous invocation of an external program in the same window.
To put it all together:
$machines = Get-Content -Path "C:\server_list.txt"
Write-host "Executing Events on the following computers: $machines" -b "yellow" -foregroundcolor "red"
# Define the command as a script block, which is a piece of PowerShell code
# you can execute on demand later.
# In it, execute cmdagent.exe *directly*, not via Start-Process.
$command = { & "C:\Program Files\McAfee\Agent\cmdagent.exe" /e /l C:\temp }
# Invoke the script block on *all* computers in parallel, with a single
# Invoke-Command call.
Invoke-Command -ComputerName $machines -ScriptBlock $command
Note the need to use &, the call operator, to invoke the cmdagent.exe executable, because its path is quoted (of necessity, due to containing spaces).
Alternatively, you can define the script block directly in the Invoke-Command call:
Invoke-Command -ComputerName $machines -ScriptBlock {
& "C:\Program Files\McAfee\Agent\cmdagent.exe" /e /l C:\temp
}
A notable pitfall when targeting remote computers is that you cannot directly reference local variables in the (remotely executing) script block, and must instead explicitly refer to them via the $using: scope; e.g., $using:someLocalVar instead of $someLocalVar - see this answer for more information.
The problem is that $command is only valid for your local session - if you try to use it in a remote session, $command is $null and does nothing. Additionally, you are actually assigning $command whatever Start-Process returns, not what you want the command to be.
Just put the command in the script block (and while you're at it you can run this on every machine with a single command without having to loop over each one synchronously):
Invoke-Command -ComputerName $Machines -ScriptBlock { Start-Process -NoNewWindow -FilePath "C:\Program Files\McAfee\Agent\cmdagent.exe" -ArgumentList "/e /l C:\temp" }

Powershell - capture output from Invoke-Command running exe on remote machine

I need to configure the auditing policy on a collection of remote servers. I'm trying to use the Invoke-Command commandlet to run auditpol.exe on each server. The issue is that I can't seem to capture any output from the auditpol command.
I tried the obvious (assigning the result of Invoke-Command to a string):
> $command = "Start-Process -FilePath `"auditpol.exe`" -ArgumentList `"/set`", `"/subcategory`", `"```"File System```"`", `"/success:enable`""
> $command
"auditpol.exe" -ArgumentList "/set", "/subcategory", "`"File System`"", "/success:enable"
> $out = Invoke-Command -ComputerName MyServer -ScriptBlock {$command}
> $out
>
But $out is empty.
I also tried the method detailed in this MSDN blog using Wait-Job and Receive-Job. The results are somewhat promising, but inconclusive:
> $command = "Start-Process -FilePath `"auditpol.exe`" -ArgumentList `"/set`", `"/subcategory`", `"```"File System```"`", `"/success:enable`""
> $command
"auditpol.exe" -ArgumentList "/set", "/subcategory", "`"File System`"", "/success:enable"
> $job = Invoke-Command -ComputerName MyServer -ScriptBlock {$command} -AsJob
> Wait-Job $job
Id Name State HasMoreData Location Command
-- ---- ----- ----------- -------- -------
3 Job3 Completed True MyServer $command
> $output = Receive-Job $job
> $output
>
I was hoping that I would be able to capture the actual output from auditpol.exe using Receive-Job, but as indicated above, that doesn't seem to be the case.
I do get some information from Wait-Job. According to the Microsoft documentation of Wait-Job State=Completed should indicate that the operation was successful, but I'm not entirely convinced that it really has visibility into whether or not the auditpol operation was successful or not. Any advice would be greatly appreciated!
To run a console program synchronously and with its stdout and stderr output available for capture invoke it directly - do not use Start-Process (whether you run that program locally or remotely, via Invoke-Command):
$out = Invoke-Command -ComputerName MyServer -ScriptBlock {
auditpol.exe /set /subcategory 'File System' /success:enable
}
If you also want to capture stderr output, append 2>&1 to the auditpol.exe call.
If your script block is stored in local variable $command (as a [scriptblock] instance, not as a string), simply pass it directly to -ScriptBlock:
# Create a script block (a piece of code that can be executed on demand later)
# and store it in a (local) variable.
# Note that if you were to use any variable references inside the block,
# they would refer to variables on the remote machine if the block were to be
# executed remotely.
$command = { auditpol.exe /set /subcategory 'File System' /success:enable }
# Pass the script block to Invoke-Command for remote execution.
$out = Invoke-Command -ComputerName MyServer -ScriptBlock $command
As for what you tried:
$out = Invoke-Command -ComputerName MyServer -ScriptBlock {$command}
You're passing a script-block literal ({ ... }) that, when it is executed on the target computer, references a variable named $command.
Generally, simply referencing a variable outputs its value - it doesn't execute anything.
More importantly, however, $command is a local variable, which the remotely executing script block cannot see, so referencing the there-uninitialized $command variable will effectively yield $null.
In short: your Invoke-Command call does nothing and returns $null.

Executing batch file in the remote machines using powershell as a background job

All I am trying to do is to execute a batch file in remote machines as a job.I have batch file located in all machines inside C:\Users\temp folder.Here is my code
$batFile = "test.bat"
foreach($m in $machine)
{
Invoke-Command -ComputerName $m -ScriptBlock{param($batFile) & cmd.exe /c "C:\Users\temp\$batFile"} -Authentication negotiate -Credential $cred -ArgumentList $batFile -AsJob
}
But I keep getting
The expression after '&' in a pipeline element produced an object that was not valid. It must result in a command name, a script
block, or a CommandInfo object
I tried using $using:batFile inside ScriptBlock as well with no success. Can anyone suggest me what I might be doing wrong? I am using powershell version 4.
do a trace on that,
Invoke-Command -ComputerName $m -ScriptBlock{param($batFile) Trace-Command NativeCommandParameterBinder -Expression { & cmd.exe /c "C:\Users\temp\$batFile"}} -Authentication negotiate -Credential $cred -ArgumentList $batFile -AsJob
and Try using Invoke-Expression as a workaround instead of &
invoke-Expression "cmd.exe /c 'C:\Users\temp\$batFile'"
Regards,
Kvprasoon