Public/Start-PdqDeployment.ps1

<#
.SYNOPSIS
A wrapper for "PDQDeploy.exe Deploy" that adds extra features.
 
.INPUTS
None.
 
.OUTPUTS
System.Management.Automation.PSCustomObject
System.Object[]
 
.EXAMPLE
Start-PdqDeployment -Package 'Google Chrome Enterprise' -IgnoreVersion -Target 'CEO-PC'
Deploy Chrome to CEO-PC.
 
.EXAMPLE
Start-PdqDeployment -Folder 'AutoCAD' -Collection 'AutoCAD (Not Installed)' -Wait
Deploy all of the packages in the AutoCAD folder to the 'AutoCAD (Not Installed)' collection and wait until the deployments finish.
#>


function Start-PdqDeployment {

    [CmdletBinding()]
    param (
        # The names of the packages you would like to deploy.
        [String[]]$Package,

        # The list of computers you would like to deploy to.
        [String[]]$Target,

        # If you use this parameter, all of the packages in the folders you specify will be deployed.
        [String[]]$Folder,

        # The collections you would like to retrieve targets from.
        [String[]]$Collection,

        # Ignore the version number in Auto Download package names.
        [Switch]$IgnoreVersion,

        # Wait for the deployments to finish before returning.
        [Switch]$Wait,

        # The number of milliseconds you would like to wait for between database queries.
        [Int32]$SleepMilliseconds = 1000,

        # The credentials you would like to use for the deployment.
        [String]$UserName,

        # The name of the Notification you would like to send when the deployment finishes.
        [String]$NotificationName,

        # Ignores your Target Filters for this deployment.
        [Switch]$OverrideTargetFilters,

        # For each target, use the credentials that are configured as the Scan User in Inventory.
        [Switch]$UseScanUserCredentials,

        # 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
    )

    # The PDQ CLI EXEs must be run as an admin.
    $AdminState = $true
    try {

        $AdminState = [Security.Principal.WindowsPrincipal]::new([Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltinRole]::Administrator)

    } catch {

        throw 'Unable to determine elevation level. Please restart PowerShell as an administrator.'

    }

    if ( $AdminState -eq $false ) {

        throw 'This function must be run as an administrator.'

    }

    try {

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

        if ( $IgnoreVersion ) {

            $NewPackageNames = @()
            
            foreach ( $PackageIterator in $Package ) {

                $QueryParameters = @{
                    'DatabasePath' = $DatabasePath
                    'Product'      = 'Deploy'
                    'Query'        = "SELECT Name FROM Packages WHERE Name LIKE '$PackageIterator %' AND IsAutoDownload = 1;"
                    'QueryType'    = 'Scalar'
                }
                $PackageName = Invoke-PdqSqlQuery @QueryParameters

                if ( $PackageName ) {

                    $NewPackageNames += $PackageName

                } else {

                    $NewPackageNames += $PackageIterator

                }

            }

            $Package = $NewPackageNames

        }

        foreach ( $FolderIterator in $Folder ) {

            $QueryParameters = @{
                'DatabasePath' = $DatabasePath
                'Product'      = 'Deploy'
                'Query'        = "SELECT FolderId FROM Folders WHERE Path = '$FolderIterator';"
                'QueryType'    = 'Scalar'
            }
            $FolderId = Invoke-PdqSqlQuery @QueryParameters

            $QueryParameters = @{
                'DatabasePath' = $DatabasePath
                'Product'      = 'Deploy'
                'Stream'       = $true
                'Query'        = "SELECT Path FROM Packages WHERE FolderId = $FolderId;"
            }
            $Package += (Invoke-PdqSqlQuery @QueryParameters).Path

        }

        if ( -not $Package ) {

            throw 'You must provide -Package and/or -Folder.'

        }

        foreach ( $CollectionIterator in $Collection ) {

            $Target += PDQInventory.exe GetCollectionComputers "$CollectionIterator"

        }

        if ( -not $Target ) {

            throw 'You must provide -Target and/or -Collection.'

        }

        $SharedDeploymentParameters = '-Targets {0}' -f ($Target -join ' ')
        if ( $UserName ) {

            $SharedDeploymentParameters += ' -UserName "{0}"' -f $UserName

        }
        if ( $NotificationName ) {

            $SharedDeploymentParameters += ' -NotificationName "{0}"' -f $NotificationName

        }
        if ( $OverrideTargetFilters ) {

            $SharedDeploymentParameters += ' -OverrideTargetFilters'

        }
        if ( $UseScanUserCredentials ) {

            $SharedDeploymentParameters += ' -UseScanUserCredentials'

        }

        $DeploymentIDs = New-Object System.Collections.Generic.List[Int32]

        foreach ( $PackageIterator in $Package ) {

            $DeploymentParameters = 'PDQDeploy.exe Deploy -Package "{0}" {1} 2>&1' -f $PackageIterator, $SharedDeploymentParameters
            Write-Verbose $DeploymentParameters

            try {

                $DeploymentOutput = Invoke-Expression -Command $DeploymentParameters -ErrorAction 'Stop'

                $DeploymentId = [Int32]($DeploymentOutput[1] -split ':')[1].Trim()
                $DeploymentIDs.Add($DeploymentId)
                
                [PSCustomObject]@{
                    'ID'          = $DeploymentId
                    'Package'     = ($DeploymentOutput[2] -split ':', 2)[1].Trim()
                    'TargetCount' = [Int32]($DeploymentOutput[3] -split ':')[1].Trim()
                }

            } catch {

                Write-Error $DeploymentOutput

            }

        }

        if ( $Wait ) {

            Wait-PdqDeployment -Id $DeploymentIDs -SleepMilliseconds $SleepMilliseconds

        }

    } finally {

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

    }

}