private/MDT/pwsh/Step-WinPEAppAzCopy.ps1

function Step-WinPEAppAzCopy {
    <#
    .SYNOPSIS
        Downloads and adds the Microsoft AzCopy tool to a mounted WinPE image.
 
    .DESCRIPTION
        Downloads the latest AzCopy v10 for Windows from the official Microsoft
        URI and caches it under WinPEAppsPath\microsoft-azcopy. The AzCopy.exe
        binary is then copied to MountPath\Windows\System32 so it is available
        during WinPE boot.
 
        If AzCopy.exe already exists in System32, this function exits immediately
        without re-downloading.
 
    .PARAMETER AppName
        Display name recorded in $global:BuildMedia.InstalledApps. Default: 'AzCopy'.
 
    .PARAMETER Architecture
        Target architecture for the downloaded binary. Default: $global:BuildMedia.Architecture.
 
    .PARAMETER MountPath
        Path to the directory where the WIM image is mounted.
        Defaults to $global:BuildMedia.MountPath.
 
    .PARAMETER WinPEAppsPath
        Root directory used to cache downloaded WinPE app binaries.
        Defaults to $global:BuildMedia.WinPEAppsPath.
 
    .EXAMPLE
        Step-WinPEAppAzCopy
 
        Downloads (if needed) and installs AzCopy using values from the global BuildMedia hashtable.
 
    .INPUTS
        None. This function does not accept pipeline input.
 
    .OUTPUTS
        None.
 
    .NOTES
        Author: David Segura
        Company: Recast Software
        Called by Invoke-OSDeployMDT during the WIM stage.
        Reference: https://learn.microsoft.com/en-us/azure/storage/common/storage-use-azcopy-v10
    #>

    [CmdletBinding()]
    param (
        [System.String]
        $AppName = 'AzCopy',
        [System.String]
        $Architecture = $global:BuildMedia.Architecture,
        [System.String]
        $MountPath = $global:BuildMedia.MountPath,
        [System.String]
        $WinPEAppsPath = $global:BuildMedia.WinPEAppsPath
    )
    #=================================================
    $Error.Clear()
    Write-Verbose "[$(Get-Date -format s)] Start"
    #=================================================
    Write-Verbose "[$(Get-Date -format s)] Architecture: $Architecture"
    Write-Verbose "[$(Get-Date -format s)] MountPath: $MountPath"
    Write-Verbose "[$(Get-Date -format s)] WinPEAppsPath: $WinPEAppsPath"
    #=================================================
    if (Test-Path -LiteralPath "$MountPath\Windows\System32\AzCopy.exe") {
        Write-Host -ForegroundColor DarkGray "[$(Get-Date -format s)] $AppName already installed at $MountPath\Windows\System32\AzCopy.exe"
        return
    }
    #=================================================
    # Get started with AzCopy
    # https://learn.microsoft.com/en-us/azure/storage/common/storage-use-azcopy-v10?tabs=dnf

    $CacheAzCopy = Join-Path $WinPEAppsPath 'microsoft-azcopy'

    if (-not (Test-Path -Path $CacheAzCopy)) {
        Write-Host -ForegroundColor DarkGray "[$(Get-Date -format s)] Adding cache content $CacheAzCopy"
        New-Item -Path $CacheAzCopy -ItemType Directory -Force | Out-Null
    }
    else {
        Write-Host -ForegroundColor DarkGray "[$(Get-Date -format s)] Source: $CacheAzCopy"
    }

    # Download amd64 version
    if (-not (Test-Path "$CacheAzCopy\amd64")) {
        $Uri = 'https://aka.ms/downloadazcopy-v10-windows'
        $DownloadUri = (Invoke-WebRequest -Uri $Uri -Method Head -ErrorAction SilentlyContinue).BaseResponse.RequestMessage.RequestUri.AbsoluteUri
        if ($DownloadUri) {
            $FileName = if ($DownloadUri -match 'filename%3D([^&]+)') { [System.Uri]::UnescapeDataString($Matches[1]) } else { Split-Path $DownloadUri -Leaf }
            if (-not (Test-Path "$CacheAzCopy\$FileName")) {
                curl.exe --location --output "$CacheAzCopy\$FileName" $DownloadUri
                $DownloadResult = Get-Item -LiteralPath "$CacheAzCopy\$FileName" -ErrorAction SilentlyContinue
                Start-Sleep -Seconds 2
                Expand-Archive -Path $($DownloadResult.FullName) -DestinationPath "$CacheAzCopy\amd64" -Force
            }
        }
    }

    <#
    # arm64
    if (-not (Test-Path "$CacheAzCopy\arm64")) {
        $Uri = 'https://aka.ms/downloadazcopy-v10-windows-arm64'
        $DownloadUri = (Invoke-WebRequest -Uri $Uri -Method Head -ErrorAction SilentlyContinue).BaseResponse.RequestMessage.RequestUri.AbsoluteUri
        if ($DownloadUri) {
            $FileName = if ($DownloadUri -match 'filename%3D([^&]+)') { [System.Uri]::UnescapeDataString($Matches[1]) } else { Split-Path $DownloadUri -Leaf }
            if (-not (Test-Path "$CacheAzCopy\$FileName")) {
                curl.exe --location --output "$CacheAzCopy\$FileName" $DownloadUri
                $DownloadResult = Get-Item -LiteralPath "$CacheAzCopy\$FileName" -ErrorAction SilentlyContinue
                Start-Sleep -Seconds 2
                Expand-Archive -Path $($DownloadResult.FullName) -DestinationPath "$CacheAzCopy\arm64" -Force
            }
        }
    }
    #>


    Get-ChildItem -Path "$CacheAzCopy\$Architecture" -Recurse -Include 'AzCopy.exe' -ErrorAction SilentlyContinue | ForEach-Object {
        Copy-Item $_.FullName -Destination "$MountPath\Windows\System32" -Force

        # Record the installed app
        $global:BuildMedia.InstalledApps += $AppName
    }
    #=================================================
    Write-Verbose "[$(Get-Date -format s)] End"
    #=================================================
}