Public/Invoke-PdqPkgDownload.ps1
<#
.SYNOPSIS Downloads .pdqpkg files. .DESCRIPTION .pdqpkg files are what Deploy downloads when you download a package from the Package Library. You can import these files into Deploy. They will show up as a standard package, not an Auto Download. .INPUTS None. .OUTPUTS System.Management.Automation.PSCustomObject System.Object[] .EXAMPLE Invoke-PdqPkgDownload -PackageName 'Google Chrome Enterprise' Downloads the latest version of Chrome into the current directory. .EXAMPLE Invoke-PdqPkgDownload -PackageName 'Google Chrome Enterprise', 'Mozilla Firefox' -DestinationFolder 'Downloads' Downloads the latest version of Chrome and Firefox into the Downloads directory. .EXAMPLE Invoke-PdqPkgDownload -PackageName 'Google Chrome Enterprise', 'Mozilla Firefox' -PackageVersion '100.0.4896.60', '98.0.2' Downloads specific versions of Chrome and Firefox. .EXAMPLE Invoke-PdqPkgDownload -PackageName '7-Zip', 'Mozilla Firefox', 'iTunes', 'Audacity' -PreviousVersionIndex 1, 0, 2, -1 Downloads the previous version of 7-Zip, the current version of Firefox, a version of iTunes 2 away from current, and the oldest available version of Audacity. .EXAMPLE Invoke-PdqPkgDownload -PackageVersionId 8726, 9102 Downloads version 21.06 of 7-Zip and version 12.12.3.5 of iTunes. #> function Invoke-PdqPkgDownload { [CmdletBinding()] param ( [Parameter(ParameterSetName = 'PackageName', Mandatory = $true)] [Parameter(ParameterSetName = 'PackageVersion', Mandatory = $true)] [Parameter(ParameterSetName = 'PreviousVersionIndex', Mandatory = $true)] # The names of the packages you would like to download from the Package Library. [String[]]$PackageName, [Parameter(ParameterSetName = 'PackageVersion', Mandatory = $true)] # The version numbers of the packages you want to download. # If you use this parameter, you must provide a version for every entry in -PackageName. [String[]]$PackageVersion, [Parameter(ParameterSetName = 'PreviousVersionIndex', Mandatory = $true)] <# A way to select previous versions without having to specify the exact version number. 0 - Current version. 1 - The previous version. 2 - 2 versions ago. etc. -1 - The oldest available version. #> [Int32[]]$PreviousVersionIndex, [Parameter(ParameterSetName = 'PackageVersionId', Mandatory = $true)] # PackageVersionId can be found by using Select-PdqPackageLibraryPackage and examining the PackageVersion property. [UInt32[]]$PackageVersionId, # The folder you would like to save the .pdqpkg files to. [String]$DestinationFolder = $PWD, # A Deploy license in string form. # If you don't use this parameter, your Deploy license will be retrieved from the registry. [String]$License, # How long you want to wait before downloading a fresh copy of the Package Library catalog. [UInt32]$CacheTimeoutSeconds = 600, # The object that Get-PdqPackageLibraryCatalog emits. # Omitting this will cause Get-PdqPackageLibraryCatalog to be called. [XML]$LibraryCatalog ) if ( $PackageVersion ) { if ( $PackageVersion.Count -ne $PackageName.Count ) { throw 'The count of versions does not match the count of names.' } } if ( $PreviousVersionIndex ) { if ( $PreviousVersionIndex.Count -ne $PackageName.Count ) { throw 'The count of indexes does not match the count of names.' } } # WebClient requires an absolute path. $DestinationFolder = (Resolve-Path -Path $DestinationFolder -ErrorAction 'Stop').Path.Trim('\') if ( $License ) { $License = Get-PdqLicense -License $License -Format 'URL' } else { $License = Get-PdqLicense -Product 'Deploy' -Format 'URL' -AllowFreeMode } if ( -not $LibraryCatalog ) { $LibraryCatalog = Get-PdqPackageLibraryCatalog -CacheTimeoutSeconds $CacheTimeoutSeconds } if ( -not $PackageVersionId ) { $PackageEntries = @(Select-PdqPackageLibraryPackage -PackageName $PackageName -LibraryCatalog $LibraryCatalog) for ( $Index = 0; $Index -lt $PackageName.Count; $Index ++ ) { $PackageEntry = $PackageEntries[$Index] $NameOfPackage = $PackageName[$Index] if ( $PackageEntry.PackageVersion.PackageVersionId.Count -eq 0 ) { throw "Unable to find any versions for '$NameOfPackage'. This should never happen." } if ( $PackageVersion ) { $VersionOfPackage = $PackageVersion[$Index] $FoundPackageVersionId = ($PackageEntry.PackageVersion | Where-Object 'VersionNumber' -eq $VersionOfPackage).PackageVersionId if ( -not $FoundPackageVersionId ) { throw "Unable to find a PackageVersionId for '$NameOfPackage' with version '$VersionOfPackage'" } [UInt32[]]$PackageVersionId += $FoundPackageVersionId } elseif ( $PreviousVersionIndex ) { $IndexOfPreviousVersion = $PreviousVersionIndex[$Index] # I would love to use `Select-Object -Index` here, but it doesn't accept negative values :'( [UInt32[]]$PackageVersionId += @($PackageEntry.PackageVersion.PackageVersionId | Sort-Object -Descending)[$IndexOfPreviousVersion] } else { [UInt32[]]$PackageVersionId += $PackageEntry.PackageVersion.PackageVersionId | Sort-Object -Descending | Select-Object -First 1 } } } foreach ( $IdOfPackageVersion in $PackageVersionId ) { <# Dear PDQ.com, The value for AA-Request-Context below is several years old. I'm surprised the API doesn't have replay protection, but I'm glad it doesn't :). Please don't fix it. If you do, here are some suggestions in order of preference: - Make AA-Request-Context optional. It's just telemetry data anyway. - Accept the plaintext form of the data. The full license is being sent anyway, so there's nothing that needs to be encrypted. Also, connections are already over HTTPS. - Accept randomly generated values. I tried it, but the service gave me a 400 error. #> $Params = @{ 'Headers' = @{ 'Subscription-License' = $License 'AA-Request-Context' = 'BCA6FBB167CD558D4197A669ED446879B6D5AC73||KGVuY3J5cHRlZCkAEJ7jh1K/jqfSE/SFteGwZ7XzjNaZcPXt3QGIQX6rIOQqrr0Lj6lmAFRiEN/sWSPwzgamdp/LLWfsAxzGLpMe0vXqNvwxJVCexttDt60QjZ8t4BKC9hkfA1lRO5EKK7ezgAyJqkMxIZIDqYEDcPSV2znGc2kuKlkwMBLXCv9NYt3tBw31LkpnFkg8WBmNq6fdE5JMf/sFCwVa/lUncE5bUJ4wVoWUCOrB9EBXMpGJ/8iqFXTUIeSqdOu6/KSNKtYXWA==' } 'Uri' = "https://library.pdq.com/PackageLibrary?PackageVersionId=$IdOfPackageVersion" } $PackageVersionXml = (Invoke-RestMethod @Params).Package if ( $PackageVersionXml.Count -eq 0 ) { throw "Failed to download XML for PackageVersionId $IdOfPackageVersion" } $FinalName = $PackageVersionXml.Name $FinalVersion = $PackageVersionXml.PackageVersion.VersionNumber $DestinationPath = "$DestinationFolder\$FinalName $FinalVersion" + '.pdqpkg' Write-Verbose "Downloading $FinalName $FinalVersion" [System.Net.WebClient]::new().DownloadFile($PackageVersionXml.DownloadURL, $DestinationPath) # Save the output until the end to avoid scrambling the verbose messages. $Result += @( [PSCustomObject]@{ 'Name' = $FinalName 'Version Number' = $FinalVersion 'Version Name' = $PackageVersionXml.PackageVersion.Version 'Path' = $DestinationPath } ) } $Result } |