powershell invoke-command not working, but psexec does - powershell

I am trying to uninstall an old version of Flash on multiple PCs with a powershell script using the invoke-command cmdlet. The script runs without errors, but flash is never uninstalled. Here is what I am using:
$flash = get-content "C:\11.6.txt"
$flash | Foreach {
if (Test-Connection -ComputerName $_ -Count 1 -ErrorAction SilentlyContinue) {
Invoke-Command -ComputerName $_ -ScriptBlock {Start-Process cmd.exe "/c start /wait MsiExec.exe /X{346137E0-7160-403B-AD21-3FF01D25037B} /qn" -NoNewWindow}
Write-Host "Uninstall from $_ complete."}
else {
Write-Host "$_ is offline"
}
}
If I run the uninstaller via psexec it works fine:
start /wait msiexec.exe /X{346137E0-7160-403B-AD21-3FF01D25037B} /qn
I cannot for the life of me figure out why invoke-command won't work. I am relatively new to PowerShell so it could be something silly, but I can't figure it out.

When it comes to executing code on a remote computer i have never been successful with Invoke-Command. However, PSExec has been nothing but good to me.
Try something like this. I can't test at the moment but let me know if it works for you.
Note: Change the FilePath location to your PSExec.exe location.
Start-Process -FilePath "C:\PSExec.exe" -ArgumentList {"\\$ComputerNameHere CMD.exe '/C start /wait msiexec.exe /X{346137E0-7160-403B-AD21-3FF01D25037B} /qn'"}

Here is the code that ended up working for me:
$flashPuters = Get-Content "C:\11.8.800.txt"
foreach ($computer in $flashPuters)
{
if (test-Connection -ComputerName $computer -Count 1 -ErrorAction SilentlyContinue) {
& 'C:\PSTools\psexec.exe' #("\\$computer", 'msiexec.exe', '/X{BFBC6337-B7B9-4AEE-BC19-CA910EED755D}', '/qn')
"Uninstall complete for $computer" >> C:\11.8.800.log
}else {
"$computer is not online" >> C:\11.8.800.log
}
}
Thank you to #HiTech for helping me!

Related

Any Idea how to uninstall .MSI with a password in the argument parameter?

I am still pretty new to PowerShell and I am trying to create a PS script that uninstalls a program remotely, however it doesn't seem to be working, it runs fine with no error messages, but just doesn't seem to uninstall the app. I am sure I might be doing something completely wrong... Hoping someone can point me in the right direction. Code below:
$Password = "1234"
Invoke-Command -ComputerName TEST `
-ScriptBlock {
$product = Get-WmiObject win32_product | where{$_.name -eq "Program Name"}
$product.IdentifyingNumber
Start-Process "C:\Windows\System32\msiexec.exe" `
-ArgumentList "/x $($product.IdentifyingNumber) PASSWORD=$Password /quiet /noreboot" -Wait }
Because of the strange indentation in your code I didn't see it at first, but the thing is that variable $Password is unknown in the scriptblock, unless you either scope it with using: or send it as parameter in the ArgumentsList parameter of the scriptblock and add a param() block in there.
Also, as commented, only proceed with the Start-Process command if you have checked that the Get-WmiObject call actually returned a valid product object.
Try
Using the using: scope modifier
$Password = "1234"
Invoke-Command -ComputerName TEST -ScriptBlock {
$product = Get-WmiObject win32_product | Where-Object {$_.name -eq "Program Name"}
if ($product) {
$argList = "/x $($product.IdentifyingNumber) PASSWORD=$using:Password /quiet /noreboot"
Start-Process "C:\Windows\System32\msiexec.exe" -ArgumentList $argList -Wait
}
}
or use a param() block in the scriptblock
$Password = "1234"
Invoke-Command -ComputerName TEST -ScriptBlock {
param( $Password )
$product = Get-WmiObject win32_product | Where-Object {$_.name -eq "Program Name"}
if ($product) {
$argList = "/x $($product.IdentifyingNumber) PASSWORD=$Password /quiet /noreboot"
Start-Process "C:\Windows\System32\msiexec.exe" -ArgumentList $argList -Wait
}
} -ArgumentList $Password
You may consider using the newer Get-CimInstance cmdlet instead of Get-WmiObject

How to run script against windows servers WinRM

I am trying to run a script that searches/downloads/installs windows updates on remote computers using WinRM. I am running this script as a domain user with Admin access. However, I get an ACCESS Denied error.
Now, I have the script copied over to the remote servers but I am unable to view output to see whether the script is running or not.
OUTPUT I want to see:
# Continue running on other servers on error
$ErrorActionPreference = "Continue"
# Server list
$servers = Get-Content "C:\Users\admin\Desktop\vm-nonprod.txt"
# Logs
$log = "C:\Users\admin\Desktop\log-nonprod.txt"
# Path to script on server list
$scriptpath = "C:\Patch.ps1"
$results = #()
foreach ($server in $servers) {
try {
$Credential = Import-CliXml -Path "C:\Users\admin\Desktop\admin.Cred"
#New-PSSession -ComputerName $server -Credential $Credential
Invoke-Command -ComputerName $server -Credential $Credential -ScriptBlock {$scriptpath} -ArgumentList "Y" | Out-File -FilePath C:\Users\admin\Desktop\WinPatch.txt
#Invoke-Command -ComputerName $server -Credential hhq\admin -FilePath "C:\Users\admin\Documents\Patch.ps1"
#Copy-Item -Path C:\Users\admin\Documents\Patch.ps1 -Destination 'C:\' -ToSession (New-PSSession –ComputerName $server -Credential $Credential)
}
catch {
Write-Output ("Error running script on remote host: " + $server)
}
}
$results | Export-Csv -NoTypeInformation $log
There's a few issues here.
Does the script exist on the server?
Sounds like yes, you have Patch.ps1 in C:\ on each $server
The scriptblock does not run the script - just prints the variable.
To run it, change {$scriptpath} to {. $scriptpath} or {& $scriptpath}
The variable $scriptpath is not in the scriptblock scope - you will have to pass it in the -ArgumentList
Change: {$scriptpath} -ArgumentList "Y"
____To: {param($p); . $p} -ArgumentList $scriptpath
The argument "Y" is being passed to the scriptbock, not the script. The scriptblock is not looking for it, so this value is being lost.
Assume you want it to be passed to the script - this needs to be done in the scriptblock:
{$scriptpath "Y"}
I would recommend getting rid of Out-File until you are happy with the output in the console.
Putting it all together:
-ScriptBlock {$scriptpath} -ArgumentList "Y" | Out-File -FilePath C:\Users\admin\Desktop\WinPatch.txt
-ScriptBlock {param($p); . $p "Y"} -ArgumentList $scriptpath
I believe you have the wrong Invoke-Command commented out. The one that is running only has the user name hhq\admin in the credential parameter. It might be failing due to that because it would be prompting for the password during run-time.

Run an setup file (.exe) on mutiple machines remotely using powershell

I am trying to run an exe copied to a specified folder on all servers along with an argument list. This however is failing without any errors, file copy is working fine, however remote execution of the setup is failing.
The following is what I am writing.
Kindly suggest, any assistance would be appreciated.
$servers = Get-Content c:\temp\servers.txt
foreach ($server in $servers){
"Processing $server"
Copy-Item -Path "\\$serverA\utility$\Setup.exe" -Destination "\\$server\c$\temp\Setup.exe" -Force
$copy_complete = Test-Path "\\$server\c$\temp\Setup.exe"
if ($copy_complete) {
"copy successful"
Invoke-Command -ComputerName $server -ScriptBlock { Start-Process 'c:\temp\Setup.exe' -ArgumentList '/quiet /noreboot /enable_remote_assistance /Exclude "Smart Tools agent","Profile Manager WMI Plugin","Personal vDisk"' }
"$server completed"
}
else {
"Failed copy, retry manually on $server"
}
}
You could try it with the call operator (&)
Invoke-Command -ComputerName $server -ScriptBlock {
$executable='c:\temp\Setup.exe'
$arguments='/quiet /noreboot /enable_remote_assistance /Exclude "Smart Tools agent","Profile Manager WMI Plugin","Personal vDisk"'
& $executable $arguments
}

Run console EXE remotely with PowerShell and get its process ID and stdout

I use PowerShell to run a console EXE on a remote server and see its output like this:
Invoke-Command -ComputerName %SERVER_NAME% -ScriptBlock { Set-Location "%EXE_DIR%" ; .\MyExe.exe %EXE_ARGS% }
This works, but gives me no way to kill the process, except connecting to the server via RDP. If I could just save its process ID to a file I should be able to kill it using Stop-Process. I tried this:
Invoke-Command -ComputerName %SERVER_NAME% -ScriptBlock { Start-Process -NoNewWindow -PassThru -Wait -WorkingDirectory "%EXE_DIR%" "%EXE_DIR%\MyExe.exe" "%EXE_ARGS%" }
The process runs, but now I don't see its standard output! When I use Start-Process locally (without Invoke-Command) I see the output.
How can I get both the process ID and the standard output/error?
Use a background job. You can either start a local job to invoke a command on a remote host:
$job = Start-Job -ScriptBlock {
Invoke-Command -ComputerName $computer -ScriptBlock {
Set-Location 'C:\some\folder'
& 'C:\path\to\your.exe' $args
} -ArgumentList $args
} -ArgumentList $argument_list
or open a session to a remote host and start a local job there:
Enter-PSSession -Computer $computer
$job = Start-Job -ScriptBlock {
Set-Location 'C:\some\folder'
& 'C:\path\to\your.exe' $args
} -ArgumentList $argument_list
Exit-PSSession
Job output can be received via Receive-Job:
while ($job.HasMoreData) {
Receive-Job $job
Start-Sleep -Milliseconds 100
}
You can terminate it via its StopJob() method:
$job.StopJob()
Remove-Job removes the job from the job list:
Remove-Job $job

Powershell Run Bat File in Remote machine

I modified a function I got from Microsoft forum, it purpose is to run copy a bat file to a remote machine and to run it there. I could see the file being copied over, however it seems not working when I try to call the Invoke-Command to execute the file. Any advice will be appreciated, thank you :)
function Run-BatchFile ($computer, [string]$batLocation)
{
$sessions = New-PSSession -ComputerName $computer -Credential qa\qalab3
Copy-Item -Path $batLocation -Destination "\\$computer\C$\MD5temp" #copy the file locally on the machine where it will be executed
$batfilename = Split-Path -Path $batLocation -Leaf
Invoke-Command -Session $sessions -ScriptBlock {param($batfilename) "cmd.exe /c C:\MD5temp\$batfilename" } -ArgumentList $batfilename -AsJob
$remotejob | Wait-Job #wait for the remote job to complete
Remove-Item -Path "\\$computer\C$\MD5temp\$batfilename" -Force #remove the batch file from the remote machine once job done
Remove-PSSession -Session $sessions #remove the PSSession once it is done
}
Run-BatchFile 192.168.2.207 "D:\MD5Check\test.bat"
You put the commandline you're trying to run in quotes.
Invoke-Command -Session $sessions -ScriptBlock {
param($batfilename)
"cmd.exe /c C:\MD5temp\$batfilename"
} -ArgumentList $batfilename -AsJob
PowerShell will just echo bare strings, not interpret them as command and execute them. You need to use Invoke-Expression for the latter:
Invoke-Command -Session $sessions -ScriptBlock {
param($batfilename)
Invoke-Expression "cmd.exe /c C:\MD5temp\$batfilename"
} -ArgumentList $batfilename -AsJob
or (better) remove the quotes and (optionally) use the call operator:
Invoke-Command -Session $sessions -ScriptBlock {
param($batfilename)
& cmd.exe /c "C:\MD5temp\$batfilename"
} -ArgumentList $batfilename -AsJob