PowerShell Studio: How to prevent Powershell cmdlets from hanging in GUI apps

We have seen issues reported in our support forum where Azure commands are not terminating, or more likely their cmdlets are not returning in a GUI environment. This is due to the Powershell pipeline being held up by the GUI, which could prevent Powershell from processing internal messages and events thus causing a hang when a cmdlet waits for these triggers.

AzureRM cmdlets are meant to be executed from a Powershell console. The fact that we can extend this to a Windows application is a wonderful idea. Now we are indeed thinking out-of-the-box!

What’s Going On?

Windows applications are synchronous—the user must wait until they complete. Unfortunately, as explained previously there will be occasions when event-driven tasks may not complete without any warning.

When working with Azure commands everything happens in the cloud. There is the possibility that executed commands may have completed on Azure without you knowing.

Fortunately, there is a convenient way to overcome this issue.

Asynchronous – Way To Go!

The asynchronous method involves submitting the task as a background job to be executed. When working from the GUI, as soon as the job is submitted, the control returns back to the GUI.

PowerShell provides some cmdlets for submitting jobs in the background:

Start-Job
Get-Job
Receive-Job

A number of the Azure cmdlets have the ‘-AsJob‘ parameter which can be used to submit Azure commands as individual jobs to be processed in the background.

Here are some AzureRM cmdlets with the ‘-AsJob‘ parameter:

New-AzureRmStorageAccount
Remove-AzureRmResourceGroup
New-AzureRmVM
Stop-AzureRmVM
Start-AzureRmVM

One thing to remember—submitting individual jobs will require the necessary logic to track them.

Building Code

The following is a basic console example of how we can accomplish creating a job and tracking its progress:

  • Prepare the scriptblock for the job to stop an AzureRm VM:
## - Create Scriptblock for the job:
$StopVmScript1 = {

## - SignOn to Azure:
Import-AzureRmContext -Path "c:\Temp\WinPS_AsubRMprofile.json";

## - Run Azure cmdlet with AsJob parameter:
$AzQryVMStop1 = Stop-AzureRMVM `
-ResourceGroupName "JaxItProCamp2018Resources" `
-Name 'Win2K16VM1' `
-Force;
};
  • Send the job for background execution and track the status:
## - Send/Start job in background with a jobname:
Start-Job -ScriptBlock $StopVmScript1 -Name "bg_StopVM1Script";

## - Loop to check for The Stop-AzureRM cmdlet job completion:
do {
Start-Sleep -Seconds 2;
}
while ((Get-Job).State -ne 'Completed');

## - Get background job results:
Get-job -Name "bg_StopVM1Script" | Format-List | Out-File -FilePath 'C:\Temp\GetBackgroundJobResults.txt';

This code can be tested and debugged at the Powershell console before being copied to the Windows Button control event. This code is just a starting point and can be enhanced.

Code GUI Implementation

Implementing this code in a GUI application will require more changes depending on the application requirements. In this test we only have two controls in the form:

  1. The Button Control is used to prepare the Azure task and submit the job to execute in the background.
  2. The RichTextBox Control is used to post the job progress and result.

Here is a sample implementation of the Powershell code in a Windows Button Control named “$buttonAzStopVMJob_Click“:

$buttonStopVMJob_Click={
#TODO: Place custom script here
$buttonStopVMJob.Enabled = $false;

## - Create Scriptblock for the job:
$StopVmScript1 = {

## - Sign-On to Azure:
Import-AzureRmContext -Path 'c:\Temp\WinPS_AsubRMprofile.json' | Out-Null;

## - Run Azure cmdlet with AsJob parameter:
Stop-AzureRMVM -ResourceGroupName "GlobalAzureBootCampResources" `
-Name 'Win2K16VM1' `
-Force;
}

## - Send/Start job in background with a jobname:
$RichTextBoxStatus.AppendText("`r`nStart Job to Stop VM!`r`n");
Start-Job -ScriptBlock $StopVmScript1 -Name "bgStopVM1Script";

## - Loop to check for The Stop-AzureRM cmdlet job completion:
do {
$RichTextBoxStatus.AppendText("Process Stopping VM!`r`n");
Start-Sleep -Seconds 20;
}
while ((Get-Job -Name "bgStopVM1Script").State -ne 'Completed');

## - Enable button when background job is done:
$buttonStopVMJob.Enabled = $true;
$RichTextBoxStatus.AppendText("Process Completed - VM Stopped!`r`n");

## - Get and Save to the file the background job results:
Get-job -Name "bgStopVM1Script" | Format-List | Out-File -FilePath 'C:\Temp\GetBackgroundJobResults.txt';
Start-Sleep -Seconds 2;

## - Display the background job results:
$RichTextBoxStatus.AppendText("`r`nDisplaying Asynch Job Results:`r`n");
$RichTextBoxStatus.AppendText((Get-Content -Path 'C:\Temp\GetBackgroundJobResults.txt' | Out-String -Width 1000));
}

This code will create one job result:

  1. The background job results from the submitted Scriptblock.

The results of this asynchronous process will be displayed in the GUI form under the RichTextBox object:

Note: PowerShell Studio has a “Job Tracker” control set to manage jobs within your GUI.

Summary

This sample code provides the foundation for you to start thinking about how to prepare any Azure task to take advantage of background processing while the Azure task simultaneously processes smoothly. Now you can take it to the next level!

You can download the sample Windows GUI here: AzStopVMJob_sample.

Stay tuned for a future blog article where we will discuss SAPIEN’s JobTracker helper functions in the wizard template “Grid Job” form.

Related Articles

Feedback

As always, if you have any ideas, comments, or feedback, please visit our feedback forum and reference this post.

 

Max Trinidad is a Technology Evangelist at SAPIEN Technologies Inc., and a Microsoft PowerShell MVP. You can reach him at maxt@sapien.com