Stuck command in Powershell but not in Cmd.exe - windows

i'm facing a problem, my powershell script have a very strange comportement.
There is a little piece of a big script :
Write-Host "Installation in progress..."
Invoke-Command -Session $session -ThrottleLimit 64 -ScriptBlock {.\XenDesktopServerSetup.exe /QUIET /CONFIGURE_FIREWALL /components $using:componentparam /nosql }
Write-Host "Installation completed"
Sometimes even if the execution of XenDesktopServerSetup.exe is finished my script doesn't pass to the next command Write-Host "Installation completed"
I need to click somewhere in the shell or break the script with CTRL+C to see the end of my script;
I tried to change ThrottleLimit, but i doesn't have effect.
I'm using Powershell V4.0.
There is only one server in $Session.
EDIT :
I tried this command directly on my server, i opened a PS Shell and executed :
.\XenDesktopServerSetup.exe /QUIET /CONFIGURE_FIREWALL /components CONTROLLER /nosql
I had the same problem !
But when i tried the same command in Cmd.exe, it works perfectly and everytime !
How can i execute this command in Cmd.exe ? I want something like this :
Write-Host "Installation in progress..."
Invoke-Command -Session $session -ThrottleLimit 64 -ScriptBlock {Cmd.exe .\XenDesktopServerSetup.exe /QUIET /CONFIGURE_FIREWALL /components $using:componentparam /nosql }
Write-Host "Installation completed"
This shows us that there is a difference between cmd.exe and powershell in how PS make the call of .exe file, but I don't understand at all why it works in cmd.exe but not in powershell
Thanks for your suggestions

Are you sure all of the remote servers are done? You could use the -AsJob parameter and then periodically inspect the state of each job using Get-Job. Retrieve output with Receive-Job. You can even wait for all to finish with a timeout using Wait-Job $jobs -Timeout 120

This is how i resolved it, I tested it a lot and i don't have any issue :
Write-Host "Installation in progress..."
Invoke-Command -Session $session -ScriptBlock {$Components = $using:componentparam}
Invoke-Command -Session $session -ScriptBlock {Invoke-Expression "cmd /C '$IsoLetter\x64\XenDesktop Setup\XenDesktopServerSetup.exe' /QUIET /CONFIGURE_FIREWALL /components $Components /nosql" }
Write-Host "Installation completed"
What do you think? I think the problem is in the .exe file.. there is something wrong with.
But finally i solved it by using Invoke-expression and cmd /C

Related

PowerShell Invoke-Command with Start-Process outputs different result

I have an update script for running the Dell Command Update tool. In short dcu-cli.exe. The thing now is than when i run the same script code on the computer local then everything runs OK but when i run the exact same code in a script with invoke-command(and yes i have full admin rights) than the exitcode is 2 meaning An unknown application error has occurred instead of 0 (everything OK)
It is a very large script so i created a new one to debug this. This is the shorted code:
Invoke-Command -ComputerName "MyComputer" -ScriptBlock {
$ExitCode = 0
#Declare path and arguments
$DcuCliPath = 'C:\Program Files (x86)\Dell\CommandUpdate\dcu-cli.exe'
$DellCommand = "/applyUpdates -autoSuspendBitLocker=enable -outputLog=C:\Dell_Update.log"
#Verify Dell Command | Update exists
If (Test-Path -Path $DcuCliPath) {
$objWMI = Get-WmiObject Win32_ComputerSystem
Write-Host ("Dell Model [{0}]" -f $objWMI.Model.Trim())
$serviceName = "DellClientManagementService"
Write-Host ("Service [{0}] is currently [{1}]" -f $serviceName, (Get-Service $serviceName).Status)
If ((Get-Service $serviceName).Status -eq 'Stopped') {
Start-Service $serviceName
Write-Host "Service [$serviceName] started"
}
#Update the system with the latest drivers
Write-Host "Starting Dell Command | Update tool with arguments [$DellCommand] dcu-cli found at [$DcuCliPath]"
$ExitCode = (Start-Process -FilePath ($DcuCliPath) -ArgumentList ($DellCommand) -PassThru -Wait).ExitCode
Write-Host ("Dell Command | Update tool finished with ExitCode: [$ExitCode] current Win32 ExitCode: [$LastExitCode] Check log for more information: C:\Dell_Update.log")
}
}
When i remove the Invoke-Command -ComputerName "MyComputer" -ScriptBlock { and then copy + run the script local on the PC then the exitcode = 0
What i also noticed than when i run the command via 'Invoke-Command' then there is also no log file created as i passed along in the arguments... So my best guess is something is going wrong with local an remote paths?
So what am i missing? I'm guessing it is something simple but i spend several hours to get this running without any luck...
Try running it this way. You should be able to see any output or error messages. I typically add to the path first rather than using & or start-process.
invoke-command mycomputer {
$env:path += ';C:\Program Files (x86)\Dell\CommandUpdate';
dcu-cli /applyUpdates -autoSuspendBitLocker=enable -outputLog=C:\Dell_Update.log }
Using start-process inside invoke-command seems pretty challenging. I can't even see the output of findstr unless I save it to a file. And if I didn't wait the output would be truncated. By default start-process runs in the background and in another window. There's a -nonewwindow option too but it doesn't help with invoke-command.
invoke-command localhost { # elevated
start-process 'findstr' '/i word c:\users\joe\file1' -wait -RedirectStandardOutput c:\users\joe\out }
#js2010, thanks for your additional help. Unfortunately this didn't helped either.
So i did some more debugging and it turns out it was a bug in the dcu-cli version running on my test machine, DOH...!!
On the test machine version 3.1.1 was running and on another machine version 4.0 was running and that worked fine via remote Powershell. So i looked for the release notes, which i found here: https://www.dell.com/support/kbdoc/000177325/dell-command-update
And as you can see in version 3.1.3 there was this fix:
A problem was solved where dcu-cli.exe was not executed in an external interactive session of PowerShell.

Installing windows updates remotely, using PowerShell. (some PowerCLI)

I am having a problem with a script for remote windows updates.
My goal: update all the windows servers, without using WSUS or SCCM, from PowerShell without having to log into all of them and start the update process.
For my script I am using powercli and PSWindowsUpdate modules.
For test purposes I am using a single host in the $hostname variable.
So here is what I have.
$Cred = Get-Credential
Import-Module PSWindowsUpdate
Invoke-Command -ComputerName $HostName {Import-Module PSWindowsUpdate} -Verbose -Credential $Cred
Invoke-Command -ComputerName $HostName {Add-WUServiceManager -ServiceID 7971f918-a847-4430-9279-4a52d1efe18d -Confirm:$false} -Verbose -Credential $Cred
Invoke-Command -ComputerName $HostName -ScriptBlock {Get-WUInstall -MicrosoftUpdate -IgnoreUserInput -AcceptAll -AutoReboot -Confirm:$FALSE -Verbose | Out-File C:\Setup\PSWindowsUpdate.log } -credential $cred
Get-Content \\$HostName\c$\Setup\PSWindowsUpdate.log
After running the script everything works from the shut down, snapshot, power on but I can't install any updates.
I am getting the error below :
WARNING: To perform some operations you must run an elevated Windows PowerShell console.
WARNING: Can't access to machine "hostName". Try use: winrm qc
As I was searching I see that I can not make any elevation in PowerShell itself and I am reading some posts about running a CMD bat to start PowerShell in elevated rights.
It is not a problem for me to copy to every windows server the update script and have a batch to run it in elevated rights but is this the only way?
I will do the batch solution to see what I can achieve but is there any other solution in the problem I am facing?
Has anyone tried that out?
Thank you very much for your time!
I am also open for any comment on my code or fixes!
If Admin right, elevated prompts are your issue, the following may help you.
PS Code to check if running as an Administrator. If not, It will relaunch as an Administrator.
If (-Not ([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole] "Administrator")) {
$Arguments = "& '" + $MyInvocation.MyCommand.Definition + "'"
Start-Process Powershell -Verb RunAs -ArgumentList $Arguments
Break
}
You will also likely need to modify ConsentPromptBehaviorAdmin at HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System\ in the Registry
-
Here is a simple BAT file that will will get around the PS Execution policy and then launch the PS Script (Assuming they are named the same).
REG ADD "HKLM\SOFTWARE\Microsoft\PowerShell\1\ShellIds\Microsoft.PowerShell" /T REG_SZ /V ExecutionPolicy /D Unrestricted /F
Start PowerShell.exe -Command "& '%~dpn0.ps1'"
Add the end of your PS Script you can use Set-ExecutionPolicy -ExecutionPolicy Restricted -Scope LocalMachine -Force to set the execution policy back to restricted or what you have ti set to.
I ran into this issue when trying to import modules on remote machines using Invoke-Command. There's a security function that doesn't allow you to do it. You might try running the following to see if you're more successful at importing a new module.
$myPsSession = New-PSSession -ComputerName $HostName
Import-Module -Name PSWindowsUpdate -PSSession $myPsSession
I went with the task scheduler solution as it seems easier.
I created a script named WUpdates.ps1
$Timestamp=((Get-Date).ToString('dd_MM_yyyy_HH_mm'))
Import-Module PSWindowsUpdate -Verbose
Add-WUServiceManager -ServiceID 7971f918-a847-4430-9279-4a52d1efe18d -Confirm:$false -Verbose
Get-WUInstall -MicrosoftUpdate -IgnoreUserInput -AcceptAll -AutoReboot -Confirm:$FALSE -Verbose | Format-Table -AutoSize | Out-File C:\Setup\WUpdates\PSWindowsUpdate_$Timestamp.log
and a script to create a schedule task named WinUpdateScheduler.ps1
Unregister-ScheduledTask -TaskName "WindowsUpdates" -Confirm:$False
$Action = New-ScheduledTaskAction -Execute C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe -Argument '-f "C:\Setup\WUpdates\WUpdates.ps1"'
$Trigger = New-ScheduledTaskTrigger -Once -At (get-date).AddSeconds(30)
Register-ScheduledTask -Action $Action -Trigger $Trigger -RunLevel Highest -User system -TaskName "WindowsUpdates" -Description "Running Windows updates."
then in the main script I call the schedule script and have my updates installed.
Invoke-Command -ComputerName $HostName { c:\setup\WUpdates\WinUpdateScheduler.ps1 } -Credential $Cred
If anyone want the full script I can provide it.
As of the Nixphoe and Backin answers, I will check them and I will come back later to comment on them.
Thank you very much for your time.
Invoke-WUInstall -ComputerName Test-1 -Script { ipmo PSWindowsUpdate; Get-WUInstall -AcceptAll | Out-File C:\PSWindowsUpdate.log }
-Confirm:$false –Verbose
https://4sysops.com/archives/install-windows-updates-remotely-with-the-powershell/

Remote Installation of Python using Powershell fails using Invoke-Command

I am trying to use this script to install Python on the remote computer. If I run this file directly on the server. This is the Python_Pip_Proxy_PyWinAuto.ps1 file. It works.
Set-ExecutionPolicy -ExecutionPolicy Unrestricted
Write-Host("Hi")
$installer="C:\temp\python-3.6.2.exe"
& $installer /quiet PrependPath=1 InstallAllUsers=1 TargetDir="C:\Python36"
However if I run the Invoke-Command using the following script to run this remotely on the same server, It print's the Hi message so I know that the file is running but Python doesn't get installed.
# Getting the list of servers from a .txt file to an array #
$SRVListFile = "C:\Scripts\ServerList.txt"
$SRVList = Get-Content $SRVListFile -ErrorAction SilentlyContinue
# Copying the .exe file from a shared location to each server in the array #
# Invoking the .ps1 file which runs natively on each server #
Foreach($computer in $SRVList) {
Get-Service remoteregistry -ComputerName $computer | start-service
Copy-item -Path "E:\Software\python-3.6.2.exe" -Destination \\$computer\c$\temp -Recurse
Copy-item -Path "C:\My Files\Work\Episode 003 - MongoDB Back Up\Python_GUI.py" -Destination \\$computer\c$\temp -Recurse
Invoke-Command -ComputerName $computer -FilePath "C:\My Files\Work\Episode 003 - MongoDB Back Up\Python_Pip_Proxy_PyWinAuto.ps1"
}
What is going wrong. What should I change the code to?
Try using the -scriptblock {Your command here} parameter to execute the command inside the scriptblock parenthesis on the remote computer.
Perhaps you can do it like
$Scriptblock = {
PowerShell -file "C:\My Files\Work\Episode 003 - MongoDB Back Up\Python_Pip_Proxy_PyWinAuto.ps1"
"This is Working" | out-file "C:\Hi.txt"
}
Invoke-Command -ComputerName $computer -Scriptblock $Scriptblock
You might want to remove the Write-Host "Hi" part because that gives the script an interactive nature. If you want to check for execution on remote computer, you can use out-file cmdlet to create a file on the remote computer as an indication.

Starting a .exe file on a remote computer using powershell

Hey everyone I'm attempting to start a exe file on remote systems, we have an agent that runs to connect our website to our database and printers. It hangs occasionally, and requires us to remote in and/or go to the station and restart the application. I've attempted many different solutions on here and none seem to work.
I'm able to kill the process that is running but then it will not reopen the exe file to start it again. Here is the small script so far
################################################
write-host "This will Restart Workbench helper please press the any key to start"
pause
$mycreds = Get-Credential
$computer = Read-Host -prompt "Please Enter Computer Name"
$WBSTART1 = Read-Host -prompt "please enter command"
$pers = New-PSSession -ComputerName $computer -Credential $mycreds
# Gets Computer Name and Kills Workbench Helper #
(Get-WmiObject win32_process -ComputerName $computer -Credential $mycreds | ?{ $_.ProcessName -match "Workbench3.helper" }).Terminate()
# Writes Host countdown to screen #
write-host -ForegroundColor black -NoNewline "Stopping Process"
foreach ($element in 1..10)
{
Write-Host -NoNewline "${element} " -BackgroundColor 'white' - ForegroundColor 'black'
Start-Sleep -Seconds 1
}
Write-Host ''
Write-host "Process Complete"
Start-Process -filepath "C:\Program Files (x86)\Company Security\Workbench3 Helper\WorkBench3.Helper.exe"
# Writes Host countdown to termination Screen #
write-host -ForegroundColor black -NoNewline "Starting Workbench Process"
foreach ($element in 1..10)
{
Write-Host -NoNewline "${element} " -BackgroundColor 'white' -ForegroundColor 'black'
Start-Sleep -Seconds 1
}
Write-Host ''
Write-host "Process Complete"
# Starts Process #
$WBSTART = {start-process -filepath 'C:\Program Files (x86)\FrontPoint Security\Workbench3 Helper\WorkBench3.Helper.exe'}
Invoke-Command –ComputerName $computer -Credential $mycreds -ScriptBlock $WBSTART
#Starts Process# at the end runs with no errors, but nothing happens on the remote machine, killing the process works fine. Any help would be greatly appreciated.
You created a pssession to the remote computer but didn't use it. Your Start-Process was run on the computer hosting the script. You need to use Enter-PSSession since it:
Starts an interactive session with a remote computer.
Once you are in a session then you do not need to run the WMI commands remotely. You can run them like you were running on the server directly. Borrowing from the example on MSDN
The first command uses the Enter-PSSession cmdlet to start an interactive session with Server01, a remote computer. When the session starts, the command prompt changes to include the computer name.
PS C:\> Enter-PSSession -Computer Server01
[Server01]: PS C:\>
The second command gets the Windows PowerShell process and redirects the output to the Process.txt file. The command is submitted to the remote computer, and the file is saved on the remote computer.
[Server01]: PS C:\> Get-Process Powershell > C:\ps-test\Process.txt
Don't forget to call Exit-PSSession when you are done.
Invoke-Command
If you didn't want to use pssessions then you could also you Invoke-Command on the remote machine as well. You already are trying to do this with your last lines of code.
Invoke-Command -ComputerName $computer -ScriptBlock {Start-Process "bagel.exe"}
If you do go down this path be aware of scoping issues and look up how to pass arguments to the scriptblock.
I am not sure why yours is not working. Perhaps you have some errors being suppressed? I would try something simpler for testing. Is your application GUI based? It might not work when run like this. Try something like ipconfig and see what the results are.
I would also consider a while loop with a timeout condition while you are waiting for the process to terminate/start on the remote machine. That was you can account for failures better instead of assuming that 10 seconds is enough.
I think you've a problem with your local/remote execution.
(Get-WmiObject win32_process -ComputerName $computer -Credential $mycreds | ?{ $_.ProcessName -match "Workbench3.helper" }).Terminate()
Above line returns all processes from the remote computer to your local computer. Than you're fitering the returned process-objects on your LOCAL machine, and call Terminate() on your LOCAL machine. I would suggest you use the following line instead:
Invoke-command -session $pers -ScriptBlock { (Get-WmiObject win32_process -ComputerName $computer -Credential $mycreds | ?{ $_.ProcessName -like"*Workbench3.helper*" }).Terminate()}
This should kill the Workbench3 process(es). I used the like operator to make filtering less strict.
After that, you can reuse the session stored in $pers to start the process again.
Invoke-command -session $pers -scriptblock {Start-Process -filepath "C:\Program Files (x86)\Company Security\Workbench3 Helper\WorkBench3.Helper.exe" -Wait
}
I additionally used the -Waitswitch. The switch forces Start-Process to wait for the programm to be started (and return afterwards).
When doing remote operations I would always suggest to perform these operations via Invoke-Commandand not via the -ComputerName parameters of the several commands. Invoke-Command uses WS-MAN via HTTP(S), while the remote-implemenation of commands offering the -ComputerNameparameter differ in implementation from cmdlet to cmdlet. This advantage of invoke-commandmakes it easier to configure e.g. firewalls.
Hope that helps.
Thanks for all your help! So this is what I ended up with to accomplish what I was trying to do, what do you think?
$mycreds = Get-Credential
$computer = Read-Host -prompt "Please Enter Computer Name"
$s = New-PSSession -ComputerName $computer -Credential $mycreds
#### Kills Workbench Helper Proccess ####
Invoke-command -ScriptBlock { (Get-WmiObject win32_process -ComputerName $computer -Credential $mycreds | ?{ $_.ProcessName -like"*Workbench3.helper*" }).Terminate()}
################ Starting Workbench Helper #####################
Invoke-Command $s {$run = (Get-Date).AddMinutes(1) Register-ScheduledTask -TaskName "ZZ_Start WorkBench Helper" -User "domain\user" -InputObject (
(
New-ScheduledTask -Action (
New-ScheduledTaskAction -Execute "C:\Program Files (x86)\Company Security\Workbench3 Helper\WorkBench3.Helper.exe" -Argument (
" "
)
) -Trigger (
New-ScheduledTaskTrigger -Once -At ($run.TimeOfDay.ToString("hh\:mm")) # As a "TimeOfDay" to get 24Hr format
) -Settings (
New-ScheduledTaskSettingsSet -DeleteExpiredTaskAfter 00:00:01 #
)
) | %{ $_.Triggers[0].EndBoundary = $run.AddMinutes(1).ToString('s') ; $_ }
)
}

Problems with running remote daemon (and not only) processes via PowerShell

From my script I want to run some command in remote Windows box. So I googled a little and seems the most popular and somehow standard way to do that is to use PowerShell's Invoke-Command cmdlet which seems to use the same protocol as winrm and winrs. So, bellow are commands I've tried to call from my script (actually I've tried lots of other their modifications as well, but IMO these are enough to illustrate the problem):
PowerShell -Command "$encpass=ConvertTo-SecureString -AsPlainText mypass -Force;$cred = New-Object System.Management.Automation.PSCredential -ArgumentList myuser,$encpass; Invoke-Command -ComputerName REMOTE_COMPUTER_NAME -Credential $cred -ScriptBlock {<fullcommand>};"
PowerShell -Command "$encpass=ConvertTo-SecureString -AsPlainText mypass -Force;$cred = New-Object System.Management.Automation.PSCredential -ArgumentList myuser,$encpass; Invoke-Command -ComputerName REMOTE_COMPUTER_NAME -Credential $cred -ScriptBlock {Start-Process -FilePath <fullexepath> -ArgumentList <arguments> -Wait -NoNewWindow};"
PowerShell -Command "$encpass=ConvertTo-SecureString -AsPlainText mypass -Force;$cred = New-Object System.Management.Automation.PSCredential -ArgumentList myuser,$encpass;$session=new-PSSession -ComputerName "REMOTE_COMPUTER_NAME" -Credential $cred; Invoke-Command -Session $session -ScriptBlock {<fullcommand>};"
NOTE: The script is written in perl, but IMO here the language of the script doesn't matter, so you can suppose that I call the command from batch script, just note, that as commands should run from a script they should not require any interactive actions.
So, I have several problems with these commands, and need help to figure them out. Here they are:
Can't run processes of type configure and run daemon. Namely if I want to run configure_server.pl on remote box (<fullcommand> = "configure_server.pl <arguments>"), which should do some stuff, then run server.exe, it doesn't work, because as soon as configure_server.pl is done, full remote job is being killed including the server.exe which supposed to run as a daemon. (applies to points 1,2,3)
Get wrapped (length of each line is less or equal than 80 chars) standard output and standard error. (applies to point 1,3)
Don't get standard output and standard error. (applies to point 2)
Whew this is a tough one, your kind of all over the place so if I miss what your trying to do completely let me know.
1 . Stop trying to go the remote route. I know it seems like a great idea, but it's not. The only way you can get the process to persist is if you keep the powershell window up on the host computer. As you've probably noticed, you can create processes fine, but they're children of your powershell.exe, and are hosted by the wsmprovhost.exe process on the client computer. Once the parent is gone, all the children are killed
To fix this I suggest using a command line function : Schtasks.exe
schtasks.exe /create /sc ONCE /tn $taskName /tr $command /s $serverName /u $userName /p $pass /ru $userName /rp $pass /st $startTime
This command is going to create a scheduled task to run once and then remove itself after. It even takes computer name, so no remote-access required. You can also do a schtasks.exe /query and check the "Status" for running before you do a schtasks.exe /delete too.
2 . Use the ` (backtick) to specify carry execution to the next line in powershell
3 . I get standard output and error with this function, sometimes not output when executed in a big script, but can always use $? to capture error.
Note: You cannot pass in PSCredentials to this command. I suggest making a GUI that just prompts for userName and and password, without converting to secure string. I try to stay away from remoting in my scripts, since it always just makes everything so slow. Sometimes you do not have a choice, but in this case I believe you do.

Resources