functions/Get-UdeNuget.ps1
|
<# .SYNOPSIS Gets UDE NuGet packages for a specified environment. .DESCRIPTION This function retrieves the UDE NuGet packages for a specified environment. Uses the same DeveloperTools service as the Power Platform Tools for Visual Studio extension. The NuGet packages were previously available from LCS, and are now served from developertools.powerplatform.microsoft.com. .PARAMETER EnvironmentId The ID of the environment that you want to work against. Supports wildcard patterns. Can be either the environment name or the environment GUID. .PARAMETER Path The path to the directory where the NuGet packages will be saved. Defaults to "C:\Temp\d365bap.tools\UdeNugets". .PARAMETER Packages The types of NuGet packages to retrieve. Can be one or more of the following values: "All", "CompilerPackage", "Platform", "ApplicationSuite", "Application1", "Application2". Defaults to "All". .PARAMETER Download Instructs the function to download the NuGet packages to the specified path. .PARAMETER ClearNugetPackages Instructs the function to clear the existing extracted NuGet packages before extracting. Use with caution as it will delete existing files. Can be useful when the extraction has failed previously and you want to ensure a clean state for the extraction. .EXAMPLE PS C:\> Get-UdeNuget -EnvironmentId "env-123" This will retrieve the UDE NuGet packages for the specified environment ID without downloading them. .EXAMPLE PS C:\> Get-UdeNuget -EnvironmentId "env-123" -Download This will download the UDE NuGet packages for the specified environment ID to the default path. .EXAMPLE PS C:\> Get-UdeNuget -EnvironmentId "env-123" -Download -Packages "CompilerPackage","Platform" This will download only the CompilerPackage and Platform UDE NuGet packages for the specified environment ID to the default path. .EXAMPLE PS C:\> Get-UdeNuget -EnvironmentId "env-123" -Download -ClearNugetPackages This will download the UDE NuGet packages for the specified environment ID to the default path. It will clear the existing extracted NuGet packages before extracting, ensuring a clean state for the extraction. .NOTES Author: Mötz Jensen (@Splaxi) #> function Get-UdeNuget { [CmdletBinding()] [OutputType('System.Object[]')] param ( [Parameter (Mandatory = $true, ValueFromPipelineByPropertyName = $true)] [Alias("PpacEnvId")] [string] $EnvironmentId, [string] $Path = "C:\Temp\d365bap.tools\UdeNugets", [ValidateSet('All', 'CompilerPackage', 'Platform', 'ApplicationSuite', 'Application1', 'Application2')] [string[]] $Packages = 'All', [switch] $Download, [switch] $ClearNugetPackages ) begin { Add-Type -AssemblyName System.IO.Compression.FileSystem $executable = Get-PSFConfigValue -FullName "d365bap.tools.path.azcopy" $packageMap = [ordered]@{ "CompilerPackage" = @{ PackageId = "Microsoft.Dynamics.AX.Platform.CompilerPackage"; DisplayName = "Compiler Tools" } "Platform" = @{ PackageId = "Microsoft.Dynamics.AX.Platform.DevALM.BuildXpp"; DisplayName = "Platform Build Reference" } "ApplicationSuite" = @{ PackageId = "Microsoft.Dynamics.AX.ApplicationSuite.DevALM.BuildXpp"; DisplayName = "ApplicationSuite Build Reference" } "Application1" = @{ PackageId = "Microsoft.Dynamics.AX.Application1.DevALM.BuildXpp"; DisplayName = "Application 1 Build Reference" } "Application2" = @{ PackageId = "Microsoft.Dynamics.AX.Application2.DevALM.BuildXpp"; DisplayName = "Application 2 Build Reference" } } $colPackageTypes = @() if ($Packages -eq 'All') { $colPackageTypes = @($packageMap.Keys) } else { $colPackageTypes = @($Packages) } } process { if (Test-PSFFunctionInterrupt) { return } $envObj = Get-UnifiedEnvironment -EnvironmentId $EnvironmentId | Select-Object -First 1 if ($null -eq $envObj) { $messageString = "Could not find environment with Id <c='em'>$EnvironmentId</c>. Please verify the Id and try again, or list available environments using <c='em'>Get-UnifiedEnvironment</c>. Consider using wildcards if needed." Write-PSFMessage -Level Important -Message $messageString Stop-PSFFunction -Message "Stopping because environment was NOT found based on the id." ` -Exception $([System.Exception]::new($($messageString -replace '<[^>]+>', ''))) return } $build = $envObj.PpacProvApp $buildDashed = $build.Replace(".", "-") if ($Download) { $downloadDir = "$Path\$build" New-Item -Path $downloadDir ` -ItemType Directory ` -Force ` -WarningAction SilentlyContinue > $null } $baseUri = $envObj.PpacEnvUri + "/" #! Very important to have the trailing slash $secureToken = (Get-AzAccessToken -ResourceUrl $baseUri -AsSecureString).Token $tokenWebApiValue = ConvertFrom-SecureString -AsPlainText -SecureString $secureToken $headers = @{ "Correlationid" = [guid]::NewGuid().ToString() "Dataverseenvironmenturi" = $envObj.PpacEnvUri "Authorization" = "Bearer $($tokenWebApiValue)" "Odata-Maxversion" = "4.0" "Odata-Version" = "4.0" "Accept" = "application/json" } $colNugets = @( foreach ($package in $colPackageTypes) { $packageId = $packageMap[$package].PackageId $displayName = $packageMap[$package].DisplayName $localUri = "https://developertools.powerplatform.microsoft.com/api/clientmetadata/nugetpackage?packageName=$([uri]::EscapeDataString($packageId))&version=$buildDashed" $sasUri = Invoke-RestMethod -Method Get ` -Uri $localUri ` -Headers $headers $uriLeaf = Split-Path $sasUri -Leaf $fileName = $uriLeaf.Split("?")[0] $packageVersion = $fileName.Replace("$packageId.", "").Replace(".nupkg", "") [PsCustomObject][Ordered]@{ "Package" = $package "PackageId" = $packageId "DisplayName" = $displayName "Build" = $build "PackageVersion" = $packageVersion "Uri" = $sasUri } | Select-PSFObject -TypeName "D365Bap.Tools.UdeNuget" ` -Property * } ) if (-not $Download) { $colNugets return } if (-not [System.IO.File]::Exists($executable)) { $messageString = "AzCopy executable not found at <c='em'>$executable</c>. Please install AzCopy using <c='em'>Invoke-BapInstallAzCopy</c> or configure the path using <c='em'>Set-BapAzCopyPath</c>." Write-PSFMessage -Level Important -Message $messageString Stop-PSFFunction -Message "Stopping because AzCopy executable was not found." -Exception $([System.Exception]::new($($messageString -replace '<[^>]+>', ''))) return } Write-PSFMessage -Level Important -Message "Will start the download of the NuGet packages:" $retryCount = 0 $maxRetries = 5 do { $retryCount++ foreach ($nugetObj in $colNugets) { $uriQuery = Split-Path $nugetObj.Uri -Leaf $fileName = $uriQuery.Split("?")[0] $outputPath = Join-Path $downloadDir $fileName $nugetObj | Add-Member -NotePropertyName "Path" -NotePropertyValue $outputPath -Force if ([System.IO.Path]::Exists($outputPath)) { Write-PSFMessage -Level Important -Message " - Skipping <c='em'>$fileName</c> as it already <c='em'>exists</c>" continue } Write-PSFMessage -Level Important -Message " - <c='em'>$fileName</c>" $azCopyOut = & $executable copy "$($nugetObj.Uri)" "$outputPath" --overwrite=true 2>&1 Write-PSFMessage -Level Verbose -Message ($azCopyOut -join [Environment]::NewLine) } } while ( ($colNugets | ` Where-Object { -not [System.IO.File]::Exists($_.Path) }) ` -and $retryCount -lt $maxRetries ) foreach ($nugetObj in $colNugets) { if (-not [System.IO.File]::Exists($nugetObj.Path)) { Write-PSFMessage -Level Important -Message "File <c='em'>$($nugetObj.Path)</c> does not exist. It seems the download failed." Stop-PSFFunction -Message "Stopping because at least one file download failed." ` -Exception $([System.Exception]::new("File $($nugetObj.Path) does not exist. Download failed.")) continue } Unblock-File -Path $nugetObj.Path } if (Test-PSFFunctionInterrupt) { return } # Extract each .nupkg (a zip) to a version folder next to the PackagesLocalDirectory $pathNugetsRoot = "$env:LOCALAPPDATA\Microsoft\Dynamics365\$build\NuGets" New-Item -Path $pathNugetsRoot ` -ItemType Directory ` -Force ` -WarningAction SilentlyContinue > $null foreach ($nugetObj in $colNugets) { $extractDirName = [System.IO.Path]::GetFileNameWithoutExtension($nugetObj.Path) $extractPath = Join-Path $pathNugetsRoot $extractDirName $nugetObj | Add-Member -NotePropertyName "ExtractedPath" -NotePropertyValue $extractPath -Force if ([System.IO.Path]::Exists($extractPath)) { if (-not $ClearNugetPackages) { Write-PSFMessage -Level Important -Message "The extracted folder for <c='em'>$extractDirName</c> already exists. If you want to re-extract, please run this command again with the switch <c='em'>-ClearNugetPackages</c>." continue } Remove-Item -Path $extractPath -Recurse -Force } Write-PSFMessage -Level Important -Message "Will extract <c='em'>$extractDirName</c>..." [IO.Compression.ZipFile]::ExtractToDirectory($nugetObj.Path, $extractPath, $true) Write-PSFMessage -Level Important -Message "Extraction of <c='em'>$extractDirName</c> completed..." } [GC]::Collect() [GC]::WaitForPendingFinalizers() $colNugets } end { } } |