Public/Push-DeployQueue.ps1

function Push-DeployQueue {
    <#
        .SYNOPSIS
            Enqueue a command for later execution.
        .DESCRIPTION
            Adds a command string to the registry-backed queue. Queue operations are
            protected by a mutex to avoid race conditions on concurrent processes.
        .PARAMETER Value
            The PowerShell command to queue.
        .PARAMETER Unique
            If set, and an identical command is already queued, the operation is not pushed.
        .PARAMETER StartQueue
            If set, the queue runner is triggered immediately after pushing the command.
    #>

    [CmdletBinding()]
    [OutputType([pscustomobject])]
    param(
        [Parameter(Position = 0, Mandatory = $true, ValueFromPipeline = $true)]
        [string]$Value,

        [Parameter(Mandatory = $false)]
        [switch]$Unique,

        [Parameter(Mandatory = $false)]
        [switch]$StartQueue
    )
    Begin {
        Write-Verbose "[$($MyInvocation.MyCommand.Name)] Function started"
        $MutexName = "$($script:DeployQueueConfig.MutexPrefix)_ModifyQueue"
        Write-Verbose "Mutex::New ($MutexName)"
        $Mutex = New-Object System.Threading.Mutex($false, $MutexName) -ErrorAction Stop
    }
    End {
        Write-Verbose "[$($MyInvocation.MyCommand.Name)] Function ended"
        Write-Verbose "Mutex::Dispose"
        $Mutex.Dispose()
    }
    Process {
        Write-Verbose "[$($MyInvocation.MyCommand.Name)] PSBoundParameters: $($PSBoundParameters | Out-String)"
        Write-Verbose "Mutex::Wait"
        if (-not ($Mutex.WaitOne($script:DeployQueueConfig.MutexTimeout))) {
            Write-Error "Error acquiring Mutex (the system is busy with another operation)"
            return
        }
        try {
            $RegistryKey = Join-Path $script:DeployQueueConfig.RegistryRoot 'Queue'
            if (-not (Test-Path $RegistryKey)) {
                New-Item $RegistryKey -Force -ErrorAction Stop | Out-Null
            }
            $Queue = Get-Item $RegistryKey -ErrorAction Stop
            $LastId = 0
            try {
                $LastId = $Queue | Get-ItemPropertyValue -Name LastId -ErrorAction Stop
                $LastId = ([int]$LastId) + 1
            } catch {
                $LastId = 0
            }
            Write-Verbose "LastId: $LastId"

            $DoStoreItem = $true
            if ($Unique) {
                $Items = $Queue | Get-ItemProperty
                if ($Items.PSObject.Properties | Where-Object { $_.Name -match '^\d+$' -and $Items.($_.Name) -eq $Value }) {
                    $DoStoreItem = $false
                }
            }
            if ($DoStoreItem) {
                $NewItem = [PSCustomObject]@{ Name = [int]$LastId; Value = $Value }
                $Queue | Set-ItemProperty -Name $NewItem.Name -Value $NewItem.Value -ErrorAction Stop
                $Queue | Set-ItemProperty -Name LastId -Value ([int]$LastId) -ErrorAction Stop
                return $NewItem
            }
        } finally {
            Write-Verbose "Mutex::Release"
            $Mutex.ReleaseMutex()

            if ($StartQueue) {
                Start-DeployQueueRunner
            }
        }
    }
}