Public/Measure-PdqDeploymentStartDelay.ps1

<#
.SYNOPSIS
Calculates how long it took for each deployment in PDQ Deploy to start.

.NOTES
This is a script I wrote when I was working at PDQ.
There was a bug that caused deployments to take a long time to start.
That bug was fixed, but I figured I may as well publish this script in case it is useful again in the future.

.INPUTS
None.

.OUTPUTS
System.Management.Automation.PSCustomObject
System.Object[]

.EXAMPLE
Measure-PdqDeploymentStartDelay
Reads the PDQ Deploy database from the default location, then outputs how long each deployment took to start.
#>

function Measure-PdqDeploymentStartDelay {

    [CmdletBinding()]
    param (
        # The path to the currently active database will be retrieved by default.
        # You can use this parameter if you wish to run this function against a different database.
        [String]$DatabasePath
    )

    $DeploymentComputers = @{}
    $TargetCounts = @{}

    Try {

        $CloseConnection = Open-PdqSqlConnection -Product 'Deploy' -DatabasePath $DatabasePath

        $DeploymentsQuery = 'SELECT DeploymentId, Created FROM Deployments ORDER BY DeploymentId DESC;'
        $Deployments = Invoke-SqlQuery -Query $DeploymentsQuery -ConnectionName 'Deploy'

        # $TargetCounts is a hashtable whose Key is DeploymentId,
        # and Value is the Started value of the target that started first.
        # This is faster than filtering every record from DeploymentComputers in each loop.
        $DeploymentComputersQuery = 'SELECT DeploymentId, Started FROM DeploymentComputers;'
        Invoke-SqlQuery -Query $DeploymentComputersQuery -ConnectionName 'Deploy' -Stream | ForEach-Object {

            # I had to go with DepId instead of DeploymentId because I was getting a really strange error.
            # "Cannot overwrite variable DeploymentId because the variable has been optimized."
            [UInt64]$DepId = $_.DeploymentId

            $TargetCounts.$DepId ++
        
            # -as accepts null values, casting to [DateTime] does not.
            $Started = $_.Started -as 'DateTime'
            if ( $_.Started ) {

                # Look for the lowest (earliest) Started value, or the first Started value of this deployment.
                if ( ($Started -lt $DeploymentComputers.$DepId) -or (-not $DeploymentComputers.$DepId) ) {

                    $DeploymentComputers.$DepId = $Started

                }
            
            }

        }

    } Finally {

        Close-PdqSqlConnection -Product 'Deploy' -CloseConnection $CloseConnection

    }

    ForEach ( $Deployment in $Deployments ) {

        [UInt64]$DeploymentId = $Deployment.DeploymentId
        [DateTime]$DeploymentStart = $Deployment.Created
        $FirstComputer = $DeploymentComputers.$DeploymentId

        # Make sure at least 1 record was found.
        if ( -not $FirstComputer ) {

            Write-Verbose "No viable records for DeploymentId $DeploymentId"
            Continue

        }

        $Delay = ($FirstComputer - $DeploymentStart).TotalSeconds

        [PSCustomObject]@{
            DeploymentId = $DeploymentId
            TargetCount  = $TargetCounts.$DeploymentId
            Delay        = $Delay
        }

    }

}