はじめに
Intune Win32アプリでは、最大で30GBまでのアプリの登録が可能です。とはいえ大きなサイズのアプリの登録はアップロードの箇所で相当な時間がかかることから、手作業での実行はきついです。
MSのGraph Powershellのサンプルを利用して、より効率的にアップロードする方法がないか調べてみました。
利用できるスクリプト
こちらにあるスクリプトを利用すると、Win32アプリの登録から更新までいろいろできるようです、使い勝手もいろいろと改善されているようです。
一方、本家MSさんのGraph SDK powershellの中では以下のようなサンプルが公開されています。
https://github.com/microsoft/mggraph-intune-samples
https://github.com/microsoft/mggraph-intune-samples/tree/main/LOB_Application
https://github.com/microsoft/mggraph-intune-samples/blob/main/LOB_Application/Win32_Application_Update.ps1
なるべくならばMSさんの公式のものを利用してみたくなることから、このサンプルで実験をしてみます。
テストと改修
テスト的に8GBほどのinstall.intunewinファイルを事前に作成しておきます。
また、Win32アプリは事前に空の中身で登録しておきます。
その後、サンプルの関数を呼び出す形でこのように実行してみます。
# ここでサンプルのファイルをdot sourceしておき、関数を利用できるようにします。
. ".\Win32_Application_Update.ps1"
# これはGraph使う前に必ず必要なおまじないです。認証をします。
Connect-MgGraph
# あらかじめ登録したWin32アプリのIDと作成済のintunewinファイルのパスを指定します。
Invoke-Win32AppUpdate -AppId "2bdc5c92-cc6b-477d-9ee2-7b2624136910" -UpdateAppContentOnly $true -SourceFile ".\install.intunewin"
Win32アプリのIDはブラウザ上で以下のように確認も可能です。
対象アプリを開いた状態で、アドレスバーに長いIDが確認できます。
そして実行してみると、数々のトラブルが。。1つずつ解決していきます。
トラブルその1
これは簡単ですね、Uint32ではファイルサイズが収まらないのでしょう、Uint32の箇所をUint64に変更して解決です。
# Upload the file to Azure Storage
Write-Host "Uploading the file to Azure Storage..." -ForegroundColor Yellow
$file = WaitForFileProcessing $fileUri "AzureStorageUriRequest"
[UInt64]$BlockSizeMB = 4
UploadFileToAzureStorage $file.azureStorageUri $IntuneWinFile $BlockSizeMB $fileUri
トラブルその2
更新後少しの間はアップロードが進行したのですが。。途中でこのようなエラーです。403とはこれいかに。。
前述のIntuneWIn32Appなどを参考にさせていただいたところ、Win32アプリの実体はAzure Blobにアップロードしているようなのですが、そのSASに期限があるようです。アップロード中でも定期的に更新しないとこのようなエラーが出るようです。そこで、UploadFileToAzureStorage関数を改良します、SASのRenewalをロジックとして入れました。450000 msec = 7.5分毎に更新しています。
function UploadFileToAzureStorage($sasUri, $filepath, $blockSizeMB, $fileUri) {
# Chunk size in MiB
$chunkSizeInBytes = (1024 * 1024 * $blockSizeMB)
# Read the whole file and find the total chunks.
#[byte[]]$bytes = Get-Content $filepath -Encoding byte;
# Using ReadAllBytes method as the Get-Content used alot of memory on the machine
$fileStream = [System.IO.File]::OpenRead($filepath)
$chunks = [Math]::Ceiling($fileStream.Length / $chunkSizeInBytes)
# Upload each chunk.
$ids = @()
$cc = 1
$chunk = 0
# Start the timer for SAS URI renewal
$SASRenewalTimer = [System.Diagnostics.Stopwatch]::StartNew()
while ($fileStream.Position -lt $fileStream.Length) {
$id = [System.Convert]::ToBase64String([System.Text.Encoding]::ASCII.GetBytes($chunk.ToString("0000")))
$ids += $id
$size = [Math]::Min($chunkSizeInBytes, $fileStream.Length - $fileStream.Position)
$body = New-Object byte[] $size
$fileStream.Read($body, 0, $size) > $null
$totalBytes += $size
Write-Progress -Activity "Uploading File to Azure Storage" -Status "Uploading chunk $cc of $chunks" -PercentComplete ($cc / $chunks * 100)
$cc++
UploadAzureStorageChunk $sasUri $id $body | Out-Null
$chunk++
# Ensure the SAS Uri is renewed
if ($SASRenewalTimer.ElapsedMilliseconds -ge 450000) {
Write-Host -Message "SAS Uri renewal is required, attempting to renew"
$RenewSASURIRequest = Invoke-MgGraphRequest -uri "$($fileUri)/renewUpload" -Method "POST" -Body "{}"
$Stage = "AzureStorageUriRenewal"
do {
$GraphRequest = Invoke-MgGraphRequest -uri $fileUri -Method "GET"
switch ($GraphRequest.uploadState) {
"$($Stage)Pending" {
Write-Host -Message "Intune service request for operation '$($Stage)' is in pending state, sleeping for 10 seconds"
Start-Sleep -Seconds 10
}
"$($Stage)Failed" {
Write-Host -Message "Intune service request for operation '$($Stage)' failed"
throw
}
"$($Stage)TimedOut" {
Write-Host -Message "Intune service request for operation '$($Stage)' timed out"
throw
}
}
}
until ($GraphRequest.uploadState -like "$($Stage)Success")
Write-Host -Message "Intune service request for operation '$($Stage)' was successful with uploadState: $($GraphRequest.uploadState)"
$SASRenewalTimer.Restart()
}
}
# Stop timer
$SASRenewalTimer.Stop()
$fileStream.Close()
Write-Progress -Completed -Activity "Uploading File to Azure Storage"
# Finalize the upload.
FinalizeAzureStorageUpload $sasUri $ids | Out-Null
}
トラブルその3
SAS更新を入れると、こんな風に定期的に更新がされる為か、アップロードも順調でした。
ところが最後の最後で失敗です。。400エラーとは
これだけはよくわからずだったのですが、試しに最後のUpdateの前に、スリープを180秒=3分いれてみると、なんと動作します。
# Commit the file to the service
Invoke-MgCommitDeviceAppManagementMobileAppMicrosoftGraphWin32LobAppContentVersionFile -MobileAppId $mobileAppId -MobileAppContentId $ContentVersionId -MobileAppContentFileId $ContentVersionFileId -BodyParameter $params
# Wait for the file to be processed
Write-Host "Waiting for the file to be processed..." -ForegroundColor Yellow
$file = WaitForFileProcessing $fileUri "CommitFile"
Start-Sleep -Seconds 180
# Check if we are only updating the content
if ($UpdateAppContentOnly) {
$params = @{
"@odata.type" = "#microsoft.graph.win32LobApp"
committedContentVersion = "$ContentVersionId"
}
}
else {
$params = $mobileAppBody
$params.committedContentVersion = "$ContentVersionId"
}
$params = $params | ConvertTo-Json
# Update the application with the new content version
Write-Host "Updating the application with the new content version..." -ForegroundColor Yellow
Update-MgDeviceAppManagementMobileApp -MobileAppId $mobileAppId -BodyParameter $params
ようやく正常に動作が完了するようになりましたが、アップロード時間はかかりますね。
MSさんのサンプルもここまで大きなサイズのアップロードは考慮していなかったようですね。
更新したファイルはこちらです。Pull requestも出してみました。
2025/5/6追記、Pull Requestですが無事Mergeされました