gpupdate with powershell - powershell

I want to perform a gpupdate as an -Action of a System.IO.FileSystemWatcher instance but gpupdate occasionally hangs completely when being called from PowerShell. I have tried the following way to handle an inresponsive gpupdate but it does not work as expected - the whole script still hangs when gpupdate goes wrong.
$command = "/target:computer"
$gp = Start-Process -FilePath gpupdate `
-ArgumentList $command -NoNewWindow -PassThru
wait-process -inputobject $gp -timeout 10
if( $gp.HasExited -ne $true )
{
stop-process -inputobject $gp -force
$errcode = 99
}
else
{
$errcode = $gp.exitcode
}
My questions are
Has anyone experienced similar problems with gpupdate being called from PowerShell (v2); what could be the reason for that?
Why does the above code snippet not do what I expect it to do (i.e. why does the wait-process-command not really shutdown the gpupdate process when the latter is inresponsive)?
UPDATE:
I have tried adding the /wait Parameter but it did not change anything. I have also tried the following code:
$gp = Invoke-WmiMethod -ComputerName "localhost"`
-EnableAllPrivileges`
-Path win32_process -Name "create"`
-ArgumentList "gpupdate /target:Computer /wait:5"
with $gp.ReturnValue as $errcode, but the problem remains. I still dont get a response from the script after several runs and the subscriber stops firing.
After trying the whole thing with start-job and wait-job which did not solve it either, I am at my wit's end.
Is there a better option to perform this task?
Is it worth to read up on powershell error handling?

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.

How to change priority of process in powershell, if it runs with -Wait parameter?

I am using a scheduled PowerShell script to start the same process with various arguments many times in a loop. Process is very heavy on computations and runs for a long time, and I have thousands of iterations. I noticed, that since it's a scheduled script, it runs with BelowNormal priority. And even if I bump it, any spawned processes are still BelowNormal. So, since I run this process in a loop thousands of times, I need to use -Wait like this:
foreach($list in $lists){
Start-Process -FilePath $path_to_EXE -ArgumentList $list -NoNewWindow -Wait
}
Now, I want to bump the priority, and one way I found was to do this:
($MyProcess = Start-Process -FilePath $path_to_EXE -ArgumentList $list -NoNewWindow -Wait -PassThru).PriorityClass = [System.Diagnostics.ProcessPriorityClass]::AboveNormal
However, it doesn't work, because it needs to wait until process finishes, then execute whatever is outside of parenthesis: (...).PriorityClass = ...
I can't let it proceed with the loop and spawn thousands of processes, I need to run one at a time, but how do I tell it to bump the priority, and then wait?
You can do the act of waiting within your code, by way of Wait-Process:
foreach($list in $lists) {
$MyProcess = Start-Process -FilePath $path_to_EXE -ArgumentList $list -NoNewWindow -PassThru
$MyProcess.PriorityClass = [System.Diagnostics.ProcessPriorityClass]::AboveNormal
$Myprocess | Wait-Process
}
The object returned from Start-Process also has a .WaitForExit() method:
foreach($list in $lists) {
$MyProcess = Start-Process -FilePath $path_to_EXE -ArgumentList $list -NoNewWindow -PassThru
$MyProcess.PriorityClass = [System.Diagnostics.ProcessPriorityClass]::AboveNormal
$Myprocess.WaitForExit()
}

Wait for remote Powershell script to finish

I'm creating a powershell script to import Dynamics NAV Application Objects to my Dynamics NAV 2018 database.
Microsoft is providing a PS cmdlet - Import-NAVApplicationObjects - to do so, but I have trouble to wait for the command to finish.
I have a calling script which does the following
$PSSession = New-PSSession -ComputerName $TargetMachine -Credential $MyCredential
Invoke-Command -Session $PSSession -ArgumentList $Database_Name_user, $MyCredential -ScriptBlock {
$process = Start-Process powershell.exe -Verb RunAs -Wait -PassThru -ArgumentList "-File `"C:\Users\User\Desktop\Database\Import1.ps1`" $args[0]"
$process.WaitForExit()
if ($process.ExitCode -ne 0) {
throw $process.StandardError.ReadToEnd()
}
}
The script Import1.ps1 on my $TargetMachine looks like this
param(
[String]$Database_Name_user
)
try {
$AllFiles = "C:\Users\User\Documents\AllFiles.txt"
$Modules = "C:\GIT\Loading Components\NAVModuleImport.ps1"
$OutputBuffer = import-module $Modules
Import-NAVApplicationObject -Path $AllFiles -DatabaseServer "SQLSRV001" -DatabaseName $Database_Name_user -SynchronizeSchemaChanges "No" -ImportAction "Overwrite" -Confirm:$false | Out-Null
}
catch{
$_ | Out-File "C:\Users\User\Documents\Import1.txt"
}
The file AllFiles.txt has a size of 220 MB and contains more than 7700 Dynamics NAV Application Objects (tables, pages, codeunits and so on).
When I launch the script which executes Import-NAVApplicationObject directly from the remote computer stored in $TargetMachine everything works smootly and the process takes up to 10 to 15 minutes, as expected.
When calling the script as shown in the first code example the output stops for a minute and then says everything is done.
Any help is appreciated, thank you in advance.
Edit: Solution
I noticed that my scripts as shown are working, the Import-NAVApplicationObjects cmdlet just failed.
When I elevate the powershell process on the remote computer and run the import, the cmdlet tried to authenticate as NT-Authority\Anonymous to the database.
Then I passed the credentials of the user that opens the remote PSSession to Import1.ps1 and used the parameters -UserName and -Password of the import cmdlet.
Sadly this process failed again.
Now I tried some things with the user I want to use for authenticating, changing passwords etc and it worked! The password contained a semicolon ; and apparently the import cmdlet was not happy with that.
So my simple solution is: Removing the semicolon from the password.

Code in Job's Script Block after Start-Process Does not Execute

When I create a automation script with PowerShell 5.1, I got an issue – in a script block of a job, the code after Start-Process will not get chance to execute. Here’s a simple repro:
Step 1 >> Prepare a .cmd file for Start-Process, the code in callee.cmd is:
#echo off
echo "Callee is executing ..."
exit /B 0
Step 2 >> Prepare the PowerShell code,
$scriptBlock = {
$res = Start-Process -FilePath "cmd.exe" -Wait -PassThru -NoNewWindow -ArgumentList "/c .\callee.cmd"
throw "ERROR!"
}
$job = Start-Job -ScriptBlock $scriptBlock
Wait-Job $job
Receive-Job $job
Write-Host($job.State)
Step 3 >> Run the PowerShell script, the output on screen is:
Id Name PSJobTypeName State HasMoreData Location Command
-- ---- ------------- ----- ----------- -------- -------
1 Job1 BackgroundJob Completed True localhost ...
Completed
The expected value should be “Failed”.
Does my code have problem or I’m using jobs in a wrong way?
Start-Job run job in separate PowerShell process in so-called server mode. In this mode PowerShell job process use standard input and output streams to exchange messages with the parent process.
-NoNewWindow parameter of Start-Process cmdlet instruct it to connect spawned console child process to the standard streams of its parent.
Thus, using Start-Process -NoNewWindow inside of PowerShell job, you connect spawned cmd.exe process to the same streams, which PowerShell job process use to exchange messages with its own parent process.
Now, when spawned cmd.exe write something into its standard output stream, it disturb normal message exchange between PowerShell job process and its parent, which leads to some unexpected behavior.
PetSerAl gave a great explanation of why it happens but it took me while to find a proper solution.
First thing - as some people mentioned don't use -NoNewWindow, use -WindowStyle Hidden instead.
Second, output results to file and handle them in your script block. There are two parameters, one for output and another for errors -RedirectStandardOutput and -RedirectStandardError.
For some reasons I was sure that first one will handle all output and my code worked well if there were no errors, but was failing with no output in case of exceptions in script block.
I created a function that also handles process status result:
function RunProcesSafe($pathToExe, $ArgumentList)
{
Write-Host "starting $pathToExe $ArgumentList"
$logFile = Join-Path $env:TEMP ([guid]::NewGuid())
$errorLogFile = Join-Path $env:TEMP ([guid]::NewGuid())
try
{
Write-Host "starting $pathToExe $ArgumentList"
$proc = Start-Process "$pathToExe" -Wait -PassThru -RedirectStandardError $errorLogFile -RedirectStandardOutput $logFile -WindowStyle Hidden -ArgumentList $ArgumentList
$handle = $proc.Handle
$proc.WaitForExit();
if ($proc.ExitCode -ne 0) {
Write-Host "FAILED with code:"+$proc.ExitCode
Throw "$_ exited with status code $($proc.ExitCode)"
}
}
Finally
{
Write-Host (Get-Content -Path $logFile)
Write-Host (Get-Content -Path $errorLogFile)
Remove-Item -Path $logFile -Force
Remove-Item -Path $errorLogFile -Force
}
}

"Start-Process -NoNewWindow" within a Start-Job?

I'm having issues using Start-Process within a Start-Job, specifically when using -NoNewWindow. For example, this test code:
Start-Job -scriptblock {
Start-Process cmd -NoNewWindow -Wait -ArgumentList '/c', 'echo' | out-null
Start-Process cmd # We'll never get here
}
get-job | wait-job | receive-job
get-job | remove-job
Returns the following error, that apparently google hasnt heard of:
Receive-Job : There is an error processing data from the background process. Error reported: Cannot process an element with node type "Text". Only Element and EndElement node types are supported.
If I remove the -NoNewWindow everything works just fine. Am I doing something silly, or is there no way to start jobs containing Start-Process -NoNewWindow? Any good alternatives?
A little late, but for people still having issues with this specific error message, one fix for this example is to use -WindowStyle Hidden instead of -NoNewWindow, I've had -NoNewWindow appear to get ignored a lot of the time and cause it's own problems.
But for this specific error that seems to come from using Start-Process with various executables, I have found the solution that seems to work consistently is by redirecting the output, as it is the output that comes back appears to cause the problem. Unfortunately though that does result in writing to a temporary file and cleaning it up.
As an example;
Start-Job -ScriptBlock {
# Create a temporary file to redirect output to.
[String]$temporaryFilePath = [System.IO.Path]::GetTempFileName()
[HashTable]$parmeters = #{
'FilePath' = 'cmd';
'Wait' = $true;
'ArgumentList' = #('/c', 'echo');
'RedirectStandardOutput' = $temporaryFilePath;
}
Start-Process #parmeters | Out-Null
Start-Process -FilePath cmd
# Clean up the temporary file.
Remove-Item -Path $temporaryFilePath
}
Get-Job | Wait-Job | Receive-Job
Get-Job | Remove-Job
Hopefully this helps.