Intune Win32アプリ　巨大アプリの更新をPowershellで実行する

# はじめに

Intune Win32アプリでは、最大で30GBまでのアプリの登録が可能です。とはいえ大きなサイズのアプリの登録はアップロードの箇所で相当な時間がかかることから、手作業での実行はきついです。
MSのGraph Powershellのサンプルを利用して、より効率的にアップロードする方法がないか調べてみました。


# 利用できるスクリプト
こちらにあるスクリプトを利用すると、Win32アプリの登録から更新までいろいろできるようです、使い勝手もいろいろと改善されているようです。

https://github.com/MSEndpointMgr/IntuneWin32App

一方、本家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アプリは事前に空の中身で登録しておきます。

その後、サンプルの関数を呼び出す形でこのように実行してみます。

```PowerShell:LargeSizeupdate.ps1
# ここでサンプルのファイルを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が確認できます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1015969/ce2a952e-1507-428e-ae91-a3293d5ce976.png)

そして実行してみると、数々のトラブルが。。1つずつ解決していきます。

## トラブルその1

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1015969/6565a67b-82a3-4aff-b30f-6fa58719582d.png)

これは簡単ですね、Uint32ではファイルサイズが収まらないのでしょう、Uint32の箇所をUint64に変更して解決です。

```PowerShell:Win32_Application_Update.ps1

		# 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

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1015969/0df9097b-ae04-4afc-8282-71bc5bf0f644.png)

更新後少しの間はアップロードが進行したのですが。。途中でこのようなエラーです。403とはこれいかに。。
前述のIntuneWIn32Appなどを参考にさせていただいたところ、Win32アプリの実体はAzure Blobにアップロードしているようなのですが、そのSASに期限があるようです。アップロード中でも定期的に更新しないとこのようなエラーが出るようです。そこで、UploadFileToAzureStorage関数を改良します、SASのRenewalをロジックとして入れました。450000 msec = 7.5分毎に更新しています。

```PowerShell:Win32_Application_Update.ps1
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更新を入れると、こんな風に定期的に更新がされる為か、アップロードも順調でした。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1015969/2becc848-d028-4a54-b16d-0451aafc347f.png)

ところが最後の最後で失敗です。。400エラーとは

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1015969/1b413b30-1e0c-479b-9ca7-6bb56f34a7ae.png)

これだけはよくわからずだったのですが、試しに最後のUpdateの前に、スリープを180秒=3分いれてみると、なんと動作します。

```PowerShell:Win32_Application_Update.ps1

		# 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
        
```

ようやく正常に動作が完了するようになりましたが、アップロード時間はかかりますね。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1015969/6b9f95aa-b115-40dc-ad95-5be5846f052c.png)


MSさんのサンプルもここまで大きなサイズのアップロードは考慮していなかったようですね。

更新したファイルはこちらです。Pull requestも出してみました。

https://github.com/keitanak/mggraph-intune-samples/blob/LargeUpload/LOB_Application/Win32_Application_Update.ps1

2025/5/6追記、Pull Requestですが無事Mergeされました