Receive-Job output is empty sometimes

Ask your Windows PowerShell-related questions, including questions on cmdlet development!
Forum rules
Do not post any licensing information in this forum.

Any code longer than three lines should be added as code using the 'Select Code' dropdown menu or attached as a file.
Locked
User avatar
Hardware
Posts: 2
Joined: Wed Dec 31, 2014 5:19 am

Receive-Job output is empty sometimes

Post by Hardware » Thu Feb 22, 2018 7:59 am

Hi everyone,

I need some help with the Job Tracker Control Set integrated in Powershell Studio 2018. I use a job to encrypt removable USB drives with Bitlocker. The job script works well but sometimes, the Receive-Job cmdlet in CompletedScript doesn't return any data, the $output value is null.

Some code :

Code: Select all

Function Start-DriveEncryption {

Param (
    [Parameter(Mandatory = $true)]
    [Hashtable]$args
)

$StatusBar.Text = "Enabling encryption"
Write-Log -message "Enabling encryption" -component $script:component

Add-JobTracker -Name 'DriveEncryption' 
-JobScript {
    Param ([Hashtable]$params)
    
    $output = New-Object PSObject -Property @{
        exitCode			  = 0
        errorMessage		  = ""
        encryptionPercentage  = 0
        volumeStatus		  = ""
        protectionStatus	  = ""
        backupArgsList	      = $params['backupArgsList']
    }
    
    Try
    {
        $securestring = ConvertTo-SecureString $params['password'] -AsPlainText -Force -ErrorAction Stop
        Enable-BitLocker -MountPoint $params['letter'] -EncryptionMethod Aes256 -UsedSpaceOnly -PasswordProtector -Password $securestring -ErrorAction Stop
        $output.encryptionPercentage = Get-BitLockerVolume -MountPoint $params['letter'] | Select-Object -ExpandProperty EncryptionPercentage -ErrorAction Stop
        
        while (-not ($output.encryptionPercentage -eq 100))
        {
            # Sends the encryption progress to UI
            $output
            
            Start-Sleep -Seconds 3
            $bitLockerVolumeInformation = Get-BitLockerVolume -MountPoint $params['letter'] -ErrorAction Stop
            $output.encryptionPercentage = $bitLockerVolumeInformation.EncryptionPercentage
            $output.volumeStatus = $bitLockerVolumeInformation.VolumeStatus
            $output.protectionStatus = $bitLockerVolumeInformation.ProtectionStatus
        }
        
        return $output
    }
    Catch
    {
        $output.exitCode = 1
        $output.errorMessage = $_.Exception.Message
        return $output
    }
}
-UpdateScript {
    Param ($Job)
    $output = Receive-Job -Job $Job | Select-Object -Last 1
    
    if ($output.encryptionPercentage -is [float])
    {
        $LabelProtectionStatus.ForeColor = "DarkOrange"
        $LabelProtectionStatus.Text = "Activation in progress - $($output.encryptionPercentage)%"
    }
    
    Animate-Button
}
-CompletedScript {
    Param ($Job)
    $BTNBackup.ImageIndex = -1
    $output = Receive-Job -Job $Job | Select-Object -Last 1
    
    if ($output.exitCode -ne 0)
    {
        $BTNBackup.Enabled = $true
        $FoldersTable.Enabled = $true
        $TextBoxBitlockerPassword.Enabled = $true
        $StatusBar.Text = "An error occurred while activating Bitlocker."
        $LabelProtectionStatus.ForeColor = "Red"
        $LabelProtectionStatus.Text = "Erreur"
        Show-Error -message "An error occurred : $($output.errorMessage)"
        return $false
    }
    else
    {
        if ($output.volumeStatus -eq [Microsoft.BitLocker.Structures.BitLockerVolumeStatus]::FullyEncrypted -and `
            $output.protectionStatus -eq [Microsoft.BitLocker.Structures.BitLockerVolumeProtectionStatus]::On)
        {
            $LabelProtectionStatus.ForeColor = "Green"
            $LabelProtectionStatus.Text = "Active"
            
            Start-Backup -args $output.backupArgsList
        }
        else
        {
            $LabelProtectionStatus.ForeColor = "Red"
            $LabelProtectionStatus.Text = "Error"
            $message = "The volume has not been fully encrypted."
            $StatusBar.Text = $message
            Show-Error -message $message
            return $false
        }
    }
}
-ArgumentList $args

} # Function Start-DriveEncryption
For the moment, i haven't been able to reproduce this problem in debug mode. But when the Receive-Job output isn't empty, here is the result:

Image

Get-Job -Name DriveEncryption

Code: Select all

Id     Name            PSJobTypeName   State         HasMoreData     Location             Command
--     ----            -------------   -----         -----------     --------             -------
1      DriveEncryption BackgroundJob   Completed     True            localhost            ...
Receive-Job -Name "DriveEncryption" -Keep

Code: Select all

encryptionPercentage : 100
exitCode             : 0
protectionStatus     : On
volumeStatus         : FullyEncrypted
backupArgsList       : {sources, logspath, destination, nni}
errorMessage         :
RunspaceId           : 45fb2b4a-70b0-49e7-b34c-509832f773f5
Get-Job -Name DriveEncryption | Select *

Code: Select all

State         : Completed
HasMoreData   : True
StatusMessage :
Location      : localhost
Command       : ................
JobStateInfo  : Completed
Finished      : System.Threading.ManualResetEvent
InstanceId    : c8b4a25c-e7fa-4f65-9386-453e05846fe0
Id            : 1
Name          : DriveEncryption
ChildJobs     : {Job2}
PSBeginTime   : 22/02/2018 14:46:01
PSEndTime     : 22/02/2018 14:48:26
PSJobTypeName : BackgroundJob
Output        : {}
Error         : {}
Progress      : {}
Verbose       : {}
Debug         : {}
Warning       : {}
Information   : {}
(Get-Job -Name DriveEncryption).ChildJobs

Code: Select all

Id     Name            PSJobTypeName   State         HasMoreData     Location             Command
--     ----            -------------   -----         -----------     --------             -------
2      Job2                            Completed     True            localhost            ...
Get-Job -Name Job2 | select *

Code: Select all

State         : Completed
StatusMessage :
HasMoreData   : True
Location      : localhost
Runspace      : System.Management.Automation.RemoteRunspace
Debugger      : System.Management.Automation.RemotingJobDebugger
IsAsync       : True
Command       : ..............
JobStateInfo  : Completed
Finished      : System.Threading.ManualResetEvent
InstanceId    : 9f43fa66-2203-4c5e-ad73-cb5024e9acbe
Id            : 2
Name          : Job2
ChildJobs     : {}
PSBeginTime   : 22/02/2018 14:46:01
PSEndTime     : 22/02/2018 14:48:26
PSJobTypeName :
Output        : {D:, @{encryptionPercentage=99; exitCode=0; protectionStatus=; volumeStatus=; backupArgsList=System.Collections.Hashtable; errorMessage=;
                PSComputerName=localhost; RunspaceId=45fb2b4a-70b0-49e7-b34c-509832f773f5; PSShowComputerName=False}, @{encryptionPercentage=99; exitCode=0;
                protectionStatus=Off; volumeStatus=EncryptionInProgress; backupArgsList=System.Collections.Hashtable; errorMessage=; PSComputerName=localhost;
                RunspaceId=45fb2b4a-70b0-49e7-b34c-509832f773f5; PSShowComputerName=False}, @{encryptionPercentage=99; exitCode=0; protectionStatus=Off;
                volumeStatus=EncryptionInProgress; backupArgsList=System.Collections.Hashtable; errorMessage=; PSComputerName=localhost;
                RunspaceId=45fb2b4a-70b0-49e7-b34c-509832f773f5; PSShowComputerName=False}...}
Error         : {}
Progress      : {parent = -1 id = 0 act = Préparation des modules à la première utilisation. stat =   cur =  pct = -1 sec = -1 type = Completed, parent = -1
                id = 0 act = Préparation des modules à la première utilisation. stat =   cur =  pct = -1 sec = -1 type = Completed, parent = -1 id = 0 act =
                Préparation des modules à la première utilisation. stat =   cur =  pct = -1 sec = -1 type = Completed, parent = -1 id = 0 act = Préparation
                des modules à la première utilisation. stat =   cur =  pct = -1 sec = -1 type = Completed...}
Verbose       : {}
Debug         : {}
Warning       : {}
Information   : {}
Help would be appreciated :)

User avatar
jvierra
Posts: 12258
Joined: Tue May 22, 2007 9:57 am
Contact:

Re: Receive-Job output is empty sometimes

Post by jvierra » Thu Feb 22, 2018 9:03 am

Nice job. Here is a more reliable way of coding this:

Code: Select all

Function Start-DriveEncryption {

    Param (
        [Parameter(Mandatory = $true)]
        [Hashtable]$args
    )

    $StatusBar.Text = "Enabling encryption"
    Write-Log -message "Enabling encryption" -component $script:component

    Add-JobTracker -Name 'DriveEncryption' 
    -JobScript {
        Param ([Hashtable]$params)
        
        $output = New-Object PSObject -Property @{
            exitCode			  = 0
            errorMessage		  = ""
            encryptionPercentage  = 0
            volumeStatus		  = ""
            protectionStatus	  = ""
            backupArgsList	      = $params['backupArgsList']
        }
        
        Try{
            $securestring = ConvertTo-SecureString $params['password'] -AsPlainText -Force -ErrorAction Stop
            Enable-BitLocker -MountPoint $params['letter'] -EncryptionMethod Aes256 -UsedSpaceOnly -PasswordProtector -Password $securestring -ErrorAction Stop        
            while ($true){
                # Sends the encryption progress to UI
                sleep 1
                $bitLockerVolumeInformation = Get-BitLockerVolume -MountPoint $params['letter'] -ErrorAction Stop
                $output.encryptionPercentage = $bitLockerVolumeInformation.EncryptionPercentage
                $output.volumeStatus = $bitLockerVolumeInformation.VolumeStatus
                $output.protectionStatus = $bitLockerVolumeInformation.ProtectionStatus
                $output
                # this ends the loop and the job
                if($output.encryptionPercentage -eq 100){break}
            }
        }
        Catch{
            $output.exitCode = 1
            $output.errorMessage = $_.Exception.Message
            $output
        }
    }
    -UpdateScript {
        Param ($Job)
        Receive-Job -Job $Job |
            ForEach-Object{
                $LabelProtectionStatus.ForeColor = "DarkOrange"
                $LabelProtectionStatus.Text = "Activation in progress - $($output.encryptionPercentage)%"
            }
        Animate-Button
    }
    -CompletedScript {
        Param ($Job)
        $BTNBackup.ImageIndex = -1
        $output = Receive-Job -Job $Job | Select-Object -Last 1
        
        if ($output.exitCode -ne 0){
            $BTNBackup.Enabled = $true
            $FoldersTable.Enabled = $true
            $TextBoxBitlockerPassword.Enabled = $true
            $StatusBar.Text = "An error occurred while activating Bitlocker."
            $LabelProtectionStatus.ForeColor = "Red"
            $LabelProtectionStatus.Text = "Erreur"
            Show-Error -message "An error occurred : $($output.errorMessage)"
        }else {
            if ($output.volumeStatus -eq [Microsoft.BitLocker.Structures.BitLockerVolumeStatus]::FullyEncrypted -and `
                $output.protectionStatus -eq [Microsoft.BitLocker.Structures.BitLockerVolumeProtectionStatus]::On)
            {
                $LabelProtectionStatus.ForeColor = "Green"
                $LabelProtectionStatus.Text = "Active"
                
                Start-Backup -args $output.backupArgsList
            }else{
                $LabelProtectionStatus.ForeColor = "Red"
                $LabelProtectionStatus.Text = "Error"
                $message = "The volume has not been fully encrypted."
                $StatusBar.Text = $message
                Show-Error -message $message
            }
        }
    }
    -ArgumentList $args

} # Function Start-DriveEncryption
You should avoid using "return" in most functions. "return" from multiple location can make the code unpredictable and prone to hard to fin errors. Use it only when you want to "abandon" a script. "returning" from a loop in a job will terminate the job prematurely.

You should never use "$args" as a variable. It is system defined and managed variable. I di not replace your usage here because I do not know how you are calling the function. I recommend that you change the name.

Once you get this working you can replace the "select ... -Last 1" if you like. I removed it to force enumeration of all output.

Without going out to grab a fresh USB I cannot test this now but this approach should prevent empty returns and unexpected returns.

User avatar
Hardware
Posts: 2
Joined: Wed Dec 31, 2014 5:19 am

Re: Receive-Job output is empty sometimes

Post by Hardware » Thu Feb 22, 2018 9:48 am

jvierra wrote:
Thu Feb 22, 2018 9:03 am
You should avoid using "return" in most functions. "return" from multiple location can make the code unpredictable and prone to hard to fin errors. Use it only when you want to "abandon" a script. "returning" from a loop in a job will terminate the job prematurely.

You should never use "$args" as a variable. It is system defined and managed variable. I di not replace your usage here because I do not know how you are calling the function. I recommend that you change the name.
Great tips.

I will try your changes, I'll let you know. Thank you for your quick reply ;)

Locked