Import-PSSession causes memory leak in Provisioning System

Problem

When using the Import-PSSession PowerShell command in a provider function, a memory leak occurs.

The provisioning system service consumes an increasing amount of memory and may eventually experience out-of-memory errors.

Solution

Running the provider function as a background job will ensure that the memory consumed by the Import-PSSession command is released when the background job completes.

The following is an example of a very simple provider function that outputs the alias of a specific mailbox identity, and will cause a memory leak.

Function causing memory leak

function Get-Office365MailboxAlias
{
    param(
        [parameter(Mandatory = $true)]
        [string]$Identity,

        [parameter(Mandatory = $true)]
        [string]$OutputVariableName
    )

    $cred = Get-PFCredential -Name O365
    $session = New-PSSession -ConfigurationName Microsoft.Exchange -ConnectionUri "https://outlook.office365.com/powershell" -Credential $cred -Authentication Basic -AllowRedirection -ErrorAction Stop -WarningAction SilentlyContinue
    $import = Import-PSSession $session -ErrorAction Stop -WarningAction SilentlyContinue -DisableNameChecking
    $mailbox = Get-Mailbox -Identity $Identity
    Get-PSSession | Remove-PSSession
    @{$OutputVariableName=$mailbox.Alias}
}

Below is the same function, now using a background job to work around the issue.

Function using Start-Job (no memory leak)

function Get-Office365MailboxAlias
{
    param(
        [parameter(Mandatory = $true)]
        [string]$Identity,

        [parameter(Mandatory = $true)]
        [string]$OutputVariableName
    )

    $job = Start-Job -ArgumentList $Identity,$OutputVariableName -ScriptBlock {
        $Identity = $args[0]
        $OutputVariableName = $args[1]

        $cred = Get-PFCredential -Name O365
        $session = New-PSSession -ConfigurationName Microsoft.Exchange -ConnectionUri "https://outlook.office365.com/powershell" -Credential $cred -Authentication Basic -AllowRedirection -ErrorAction Stop -WarningAction SilentlyContinue
        $import = Import-PSSession $session -ErrorAction Stop -WarningAction SilentlyContinue -DisableNameChecking
        $mailbox = Get-Mailbox -Identity $Identity
        Get-PSSession | Remove-PSSession
        @{$OutputVariableName=$mailbox.Alias}
    }

    Wait-Job $job | out-null
    Receive-Job $job
}

Note how the input parameters are passed in the -ArgumentList parameter to the background job. The job then retrieves them from the $args array.

Also note the use of the Wait-Job command to wait for the job to complete, and Receive-Job to replicate the output from the job to the pipeline.