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" }
Related
I'm trying to remotely run this Windows Update Assistance Installer .exe and I notice that the .exe closes immediately unless I use the -wait command. However, if I use the -wait command I can't continue my foreach loop for the other computers since it takes hours for an install to finish. If I take the -wait command out, I think it launches then closes immediately.
$computers | % {
{more code...}
Invoke-Command -Session $Session -ScriptBlock {
$msbuild = "C:\windows\temp\Windows10Upgrade9252(21H2).exe"
$Args = '/quietinstall /skipeula /auto upgrade /copylogs'
Start-Process -FilePath $msbuild -ArgumentList $args -Wait
}
}
Run in parallel (within -throttlelimit) with $computers or $sessions as the computername/sessionname. Can also use -asjob. $args might be reserved already. Start-process won't return text output or the exit code without further action, which I've added. You might use scheduled tasks instead.
$sessions = new-pssession $computers
Invoke-Command $sessions {
$msbuild = "C:\windows\temp\Windows10Upgrade9252(21H2).exe"
$myargs = '/quietinstall /skipeula /auto upgrade /copylogs'
$p = start-process -wait $msbuild $myargs -passthru
[pscustomobject]#{exitcode=$p.exitcode]
} # -asjob
exitcode PSComputerName RunspaceId
-------- -------------- ----------
0 localhost 197d26f5-754b-49d3-baf4-2ca8fccacd4c
Other ways to wait for a program to finish: How to tell PowerShell to wait for each command to end before starting the next?
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
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.
As a precursor to running an installation file on several remote servers, I need to update the Powershell setting MaxMemoryPerShellMB. This requires running a PS session as Administrator on the remote server. I have been trying to run Invoke-Command which then runs a ScriptBlock consisting of a Start-Process command which includes the -Verb RunAs parameter. Nothing seems to work, however.
I have tried with various quoting schemes, single, double, triple, but nothing seems to work.
I've tried running the Start-Process from an Enter-PSSession, with the same results.
Following is the code I'm testing now:
$creds = Get-Credential -Username 'DOMAIN\userID' -Message "Enter Username and Password to access the remote servers."
$ScriptBlock = {
Start-Process -FilePath Powershell.exe -ArgumentList """Set-Item WSMan:\localhost\Shell\MaxMemoryPerShellMB 1024""" -Verb RunAs -Wait
}
Invoke-Command -ComputerName testsvr01 -Credential $creds -ScriptBlock $ScriptBlock
I should be able to RDP to the remote server and run Get-Item WSMan:\localhost\Shell and have it show the updated value, but the value isn't changed.
When running the code it pauses for a second when the Invoke-Command runs, but other than that, there is no feedback in Powershell.
On the remote server I see the following two Kerberos errors in the System Event log.
0x19 KDC_ERR_PREAUTH_REQUIRED,
0xd KDC_ERR_BADOPTION
Any help is greatly appreciated.
> powershell.exe -?
...
EXAMPLES
...
PowerShell -Command "& {Get-EventLog -LogName security}"
-Command
...
To write a string that runs a Windows PowerShell command, use the format:
"& {<command>}"
where the quotation marks indicate a string and the invoke operator (&)
causes the command to be executed.
So you could try to call Set-Item in the following way:
$ScriptBlock = {
Start-Process -FilePath Powershell.exe -ArgumentList "-Command"," &{ Set-Item WSMan:\localhost\Shell\MaxMemoryPerShellMB 1024 }" -Verb RunAs -Wait -PassThru
}
$process = Invoke-Command -ComputerName testsvr01 -Credential $creds -ScriptBlock $ScriptBlock
$process.ExitCode
I'm also returning a process object via -PassThru on which you might check the `ExitCode``
Hope that helps
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.