Private/Send-IntuneContentFile.ps1

function Send-IntuneContentFile {
    <#
    .SYNOPSIS
        Uploads a file to the Azure Storage SAS URI of a Win32 app content file in blocks.

    .DESCRIPTION
        Uses the Blob service Put Block (PUT <sas>&comp=block&blockid=<id>) and Put Block List
        (PUT <sas>&comp=blocklist) operations:
        https://learn.microsoft.com/rest/api/storageservices/put-block
        https://learn.microsoft.com/rest/api/storageservices/put-block-list
        Block IDs are Base64 strings of equal length. When RenewAfterSeconds have passed, the SAS URI
        is renewed with the renewUpload action
        (https://learn.microsoft.com/graph/api/intune-apps-mobileappcontentfile-renewupload).
    #>

    [CmdletBinding()]
    [OutputType([void])]
    param (
        [Parameter(Mandatory)]
        [string]$Path,

        [Parameter(Mandatory)]
        [string]$SasUri,

        [Parameter(Mandatory)]
        [string]$FileUri,

        [ValidateRange(1, 100)]
        [int]$ChunkSizeMB = 6,

        [int]$RenewAfterSeconds = 420,

        [int]$PollIntervalSeconds = 5
    )

    $ChunkSize = $ChunkSizeMB * 1024 * 1024
    $BlockIds = New-Object -TypeName System.Collections.Generic.List[string]
    $Timer = [System.Diagnostics.Stopwatch]::StartNew()
    $Stream = [System.IO.File]::OpenRead($Path)
    try {
        $Buffer = New-Object -TypeName byte[] -ArgumentList $ChunkSize
        $Index = 0
        while (($Read = $Stream.Read($Buffer, 0, $ChunkSize)) -gt 0) {
            if ($Timer.Elapsed.TotalSeconds -ge $RenewAfterSeconds) {
                Write-Verbose 'Renewing the Azure Storage SAS URI.'
                $null = Invoke-MgGraphRequest -Method POST -Uri "$FileUri/renewUpload" -ErrorAction Stop
                $SasUri = (Wait-IntuneContentFileState -FileUri $FileUri -State azureStorageUriRenewalSuccess -PollIntervalSeconds $PollIntervalSeconds).azureStorageUri
                $Timer.Restart()
            }
            if ($Read -eq $ChunkSize) {
                $Chunk = $Buffer
            }
            else {
                $Chunk = New-Object -TypeName byte[] -ArgumentList $Read
                [System.Array]::Copy($Buffer, $Chunk, $Read)
            }
            $BlockId = [System.Convert]::ToBase64String([System.Text.Encoding]::ASCII.GetBytes($Index.ToString('000000')))
            Write-Verbose "Uploading block $Index ($Read bytes)."
            $null = Invoke-WebRequest -Method Put -Uri "$SasUri&comp=block&blockid=$([System.Uri]::EscapeDataString($BlockId))" -Body $Chunk -ContentType 'application/octet-stream' -UseBasicParsing -ErrorAction Stop
            $BlockIds.Add($BlockId)
            $Index++
        }
    }
    finally {
        $Stream.Dispose()
    }

    $BlockList = '<?xml version="1.0" encoding="utf-8"?><BlockList>' + (($BlockIds | ForEach-Object { "<Latest>$_</Latest>" }) -join '') + '</BlockList>'
    $null = Invoke-WebRequest -Method Put -Uri "$SasUri&comp=blocklist" -Body $BlockList -ContentType 'application/xml' -UseBasicParsing -ErrorAction Stop
}