Intune 管理デバイスの「デバイス カテゴリ」を PowerShell(Microsoft Graph)で変更していると、割り当てはできるのに「未割り当て(Unassigned)」へ戻せないことがあります。本記事では、deviceCategoryId=null の PATCH が効かない理由と、$ref を PUT で更新して確実に戻す方法を解説します。
起きている現象:deviceCategoryId を null にしても「未割り当て」に戻らない
Microsoft Intune の管理デバイス(managedDevices)に対して、PowerShell から Microsoft Graph を叩いて「デバイス カテゴリ(Device category)」を変更していると、次のような挙動に遭遇することがあります。
| 操作 | 期待 | 実際 |
|---|---|---|
| カテゴリを特定の値に割り当てる(PATCH) | 指定したカテゴリに変わる | 変わる |
カテゴリを「未割り当て」に戻すため deviceCategoryId = null で PATCH | Unassigned になる | エラーは出ないのに変わらない |
この状況だと「そもそも null への設定はサポートされていないのでは?」「API のバグ?」「権限不足?」など、切り分けが難しくなります。結論から言うと、managedDevices に対して deviceCategoryId=null を PATCH する方法では “未割り当て(Unassigned)” に戻らないケースがあるため、別の更新方法が必要です。
結論:未割り当てに戻すなら「deviceCategory の参照($ref)」を更新する
この問題の回避策(実務上の解決策)はシンプルで、次の 2 点を押さえれば OK です。
- 「未割り当て」は null ではなく、専用のカテゴリ ID(0 の GUID)として扱う
- managedDevice の deviceCategory を PATCH するのではなく、
/deviceCategory/$refを PUT で付け替える
具体的には、Unassigned のカテゴリ ID として、次の “ゼロ GUID” を使って参照を更新します。
00000000-0000-0000-0000-000000000000
なぜ null PATCH が効かないのか:managedDevices の「値」ではなく「参照」で管理されるため
Intune のデバイス カテゴリは、直感的には「managedDevice のプロパティ(deviceCategoryId)を差し替えれば良い」と思いがちです。しかし実装上は、managedDevice が deviceCategory を“参照(リレーション)”として保持している側面が強く、更新も “参照の更新” として扱うほうが安定します。
ここがポイントです。
- PATCH は「プロパティの値」を更新するのが得意(例:deviceName、notes など)
- $ref は「参照(関連付け)」を更新する(例:A を B に紐づける/外す)
- 「未割り当て」は「参照が空(null)」というより、“未割り当てというカテゴリ”への参照として扱われることがある
そのため、deviceCategoryId = null の PATCH はエラーにならないにもかかわらず、バックエンド側で“参照の更新”として解釈されず、結果的にカテゴリが変わらないという現象が起きます。
「未割り当て(Unassigned)」の扱い:null ではなく “0 の GUID”
Intune の運用では「未割り当て」という状態が重要です。例えば、カテゴリを条件に自動化したり、ヘルプデスクが目視で分類したり、配布アプリや構成を “カテゴリ起点” で考えるケースもあります。
ここで押さえておきたいのが、未割り当ての表現です。
| 画面上の見え方 | Graph 更新での考え方 | 指定する ID |
|---|---|---|
| Unassigned(未割り当て) | 専用カテゴリへの参照として扱う | 00000000-0000-0000-0000-000000000000 |
| 任意のカテゴリ(例:Sales / Kiosk / VIP) | そのカテゴリの GUID への参照 | deviceCategories の GUID |
つまり、“Unassigned に戻す” とは「null にする」ではなく「ゼロ GUID に参照を張り替える」という発想に切り替えるのがコツです。
推奨手順:/deviceCategory/$ref を PUT で更新する
実装の肝は、managedDevice に対して次のエンドポイントを叩くことです(beta 例)。
PUT /deviceManagement/managedDevices/{DeviceId}/deviceCategory/$ref- Body に
@odata.idで参照先カテゴリを渡す
リクエストのイメージは次のとおりです。
{
"@odata.id": "https://graph.microsoft.com/beta/deviceManagement/deviceCategories/00000000-0000-0000-0000-000000000000"
}
ここで指定する GUID を任意カテゴリの GUID にすれば「割り当て」、ゼロ GUID にすれば「未割り当て」になります。“参照を付け替える” という 1 本のやり方で統一できるのが運用上とても強いポイントです。
スクリプト例:回答で提示された方式($ref を PUT 更新)
まずは、質問で提示された「旧系(Connect-MSGraph / Invoke-MSGraphRequest)」の書き方に近い形です。ポイントは、deviceCategory/$ref を PUT していることと、Unassigned にゼロ GUID を使っていることです。
function Unassign-DeviceCategory {
param(
[Parameter(Mandatory)][string]$DeviceID,
[Parameter(Mandatory)][string]$DeviceCategory
)
$body = @{
"@odata.id" = "https://graph.microsoft.com/beta/deviceManagement/deviceCategories/$DeviceCategory"
}
Invoke-MSGraphRequest -HttpMethod PUT `
-Url "deviceManagement/managedDevices/$DeviceID/deviceCategory/`$ref" `
-Content $body
}
$DeviceID = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
$DeviceCategory = "00000000-0000-0000-0000-000000000000" # Unassigned
Unassign-DeviceCategory -DeviceID $DeviceID -DeviceCategory $DeviceCategory
この方式は「未割り当てに戻したい」という目的に対して、最短で効く実装です。
現行寄せの実装例:Connect-MgGraph / Invoke-MgGraphRequest で書く
現在の運用では、Microsoft Graph PowerShell SDK(Connect-MgGraph)に寄せておくと、モジュール統一・認証統一・CI/CD 連携がしやすくなります。やっていることは同じで、$ref を PUT 更新する点が核心です。
以下は、「任意カテゴリの割り当て」も「未割り当て」も同一関数で処理できる形にした例です。
# 事前:Connect-MgGraph で認証(委任 or アプリ)
# 例(委任):
# Connect-MgGraph -Scopes "DeviceManagementManagedDevices.ReadWrite.All"
function Set-IntuneDeviceCategoryRef {
param(
[Parameter(Mandatory)][string]$ManagedDeviceId,
[Parameter(Mandatory)][string]$DeviceCategoryId,
[ValidateSet("beta","v1.0")][string]$ApiVersion = "beta"
)
$uri = "https://graph.microsoft.com/$ApiVersion/deviceManagement/managedDevices/$ManagedDeviceId/deviceCategory/`$ref"
$bodyObject = @{
"@odata.id" = "https://graph.microsoft.com/$ApiVersion/deviceManagement/deviceCategories/$DeviceCategoryId"
}
$json = $bodyObject | ConvertTo-Json -Depth 5
try {
Invoke-MgGraphRequest -Method PUT -Uri $uri -Body $json -ContentType "application/json"
Write-Host "OK: deviceCategory ref updated. DeviceId=$ManagedDeviceId CategoryId=$DeviceCategoryId"
}
catch {
Write-Error "NG: deviceCategory ref update failed. $($_.Exception.Message)"
throw
}
}
# Unassigned(未割り当て)に戻す
$ManagedDeviceId = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
$UnassignedCatId = "00000000-0000-0000-0000-000000000000"
Set-IntuneDeviceCategoryRef -ManagedDeviceId $ManagedDeviceId -DeviceCategoryId $UnassignedCatId -ApiVersion "beta"
「未割り当てに戻す」専用関数を用意したい場合は、ラッパーにすると読みやすくなります。
function Unassign-IntuneDeviceCategory {
param(
[Parameter(Mandatory)][string]$ManagedDeviceId,
[ValidateSet("beta","v1.0")][string]$ApiVersion = "beta"
)
$UnassignedCatId = "00000000-0000-0000-0000-000000000000"
Set-IntuneDeviceCategoryRef -ManagedDeviceId $ManagedDeviceId -DeviceCategoryId $UnassignedCatId -ApiVersion $ApiVersion
}
反映確認のコツ:deviceCategoryDisplayName を見る
更新後の確認は、managedDevice を取得して deviceCategoryDisplayName を見るのが分かりやすいです。反映にタイムラグがある場合もあるので、短い間隔で何度も叩くより、少し待ってから再取得するほうが運用は安定します。
# 例:managedDevice のカテゴリ表示名を確認
$device = Get-MgDeviceManagementManagedDevice -ManagedDeviceId $ManagedDeviceId
$device.deviceCategoryDisplayName
もし取得結果に表示名が出ない/空に見える場合は、取得項目や API バージョン差、同期タイミングの影響もあるため、次の「切り分けチェック」も合わせて実施すると確実です。
切り分けチェック:まずここを確認すると迷子になりにくい
そもそも “DeviceId” はどの ID を使っているか
Intune を触っていると、似たような ID が複数登場します。誤った ID を渡すと 404 になったり、別物を更新しようとして失敗したりします。
| 名称 | よくある表示・取得元 | 用途 | 注意点 |
|---|---|---|---|
| Intune managedDeviceId | Graph の /deviceManagement/managedDevices の id | 今回の更新対象 | これが必要。Azure AD の deviceObjectId と別物 |
| Azure AD device objectId | Entra ID(旧 Azure AD)のデバイスオブジェクト | ディレクトリ操作、動的グループなど | managedDevices のパスに渡しても動かないことが多い |
カテゴリ更新に使うのは、原則として managedDeviceId です。まず、対象デバイスを /deviceManagement/managedDevices から引けているか確認してください。
カテゴリ ID(DeviceCategoryId)を取り違えていないか
カテゴリの GUID は /deviceManagement/deviceCategories(deviceCategories)で一覧できます。自作カテゴリなら一覧に載りますが、Unassigned は特別扱いのため、一覧に見えない/見え方が環境で異なることがあります。その場合でも、ゼロ GUID を指定して $ref を更新する方式で運用できるケースがあります。
# デバイスカテゴリの一覧(見える範囲)
Get-MgDeviceManagementDeviceCategory -All | Select-Object Id, DisplayName
どの API バージョンを使うべきか:beta と v1.0 の考え方
サンプルでは beta を例にしていますが、運用の基本は 可能な限り v1.0 を優先し、どうしても v1.0 に必要な機能がない場合にのみ beta を使うのが安全です。
| 項目 | v1.0 | beta |
|---|---|---|
| 安定性 | 高い(運用向き) | 変化しやすい(検証向き) |
| 機能の早さ | 遅いことがある | 新機能が先に来る |
| 今回のポイント | 環境によっては $ref 更新が可能 | サンプル通りに試しやすい |
まず beta で動作確認し、問題なければ v1.0 を試し、動いたほうを採用するという進め方が現場では堅いです。
必要な権限・スコープ:書き込みができないと “無言で反映しない” に見えることがある
$ref 更新に限らず、Intune の managedDevices に書き込みを行うには適切な権限が必要です。権限不足は 403 で明確に落ちることもありますが、運用状況やエラーハンドリングによっては「失敗が見えづらい」形になります。
最低限の目安としては次のイメージです(どちらを使うかは運用設計次第)。
| 方式 | 代表的な権限(例) | 向いているケース | 注意点 |
|---|---|---|---|
| 委任権限(ユーザーとして実行) | DeviceManagementManagedDevices.ReadWrite.All | 管理者が手動実行、少量変更 | 実行者のロール・条件付きアクセスの影響を受ける |
| アプリ権限(サービスとして実行) | DeviceManagementManagedDevices.ReadWrite.All(アプリ)など | 自動化(Azure Automation / Functions) | アプリに強い権限が付与されるため、管理と監査が重要 |
権限が正しい前提でも、反映確認は必ず行い、「成功ログが出た=反映済み」とは決めつけないのが安全です。
トラブルシューティング:反映しない/エラーになるときの見方
よくあるつまずきと対処
| 症状 | 主な原因 | 対処 |
|---|---|---|
| null PATCH は成功するがカテゴリが変わらない | null が “未割り当て” として処理されない | $ref を PUT 更新に切り替える(ゼロ GUID) |
| 404 Not Found | ManagedDeviceId の取り違え(Entra の deviceId を渡している等) | managedDevices から対象を引き、id を使う |
| 403 Forbidden | 権限・ロール不足、条件付きアクセス | スコープ/アプリ権限/Intune RBAC を見直す |
| 400 Bad Request | URI の $ref エスケープ不備、Body が JSON でない | 文字列中の `$ref、ConvertTo-Json、ContentType を確認 |
| 反映が遅い | Intune 側の同期・キャッシュ | 少し待って再取得(過剰リトライは避ける) |
PowerShell で “失敗を見える化” する
自動化で一番危ないのは「失敗しているのに成功扱いで処理が進む」ことです。try/catch を入れて例外を握りつぶさない、ログを残す、更新後に再取得して状態を検証する、といった基本を徹底すると事故が減ります。
# 更新→再取得で検証する例(簡易)
Set-IntuneDeviceCategoryRef -ManagedDeviceId $ManagedDeviceId -DeviceCategoryId $UnassignedCatId -ApiVersion "beta"
Start-Sleep -Seconds 3
$after = Get-MgDeviceManagementManagedDevice -ManagedDeviceId $ManagedDeviceId
Write-Host "After: " $after.deviceCategoryDisplayName
運用の注意:カテゴリ更新を安定させるための小技
“変更が必要なときだけ更新する” でスロットリングと無駄を減らす
大量台数に対してカテゴリ更新を回す場合、同じ値を何度も PUT すると無駄が増えます。取得したカテゴリ表示名(またはカテゴリ参照)を見て、差分があるときだけ更新する設計が現場では効きます。
# ざっくり差分チェック(表示名ベースの例)
$targetDisplay = "Unassigned"
$current = Get-MgDeviceManagementManagedDevice -ManagedDeviceId $ManagedDeviceId
if ($current.deviceCategoryDisplayName -ne $targetDisplay) {
Unassign-IntuneDeviceCategory -ManagedDeviceId $ManagedDeviceId -ApiVersion "beta"
} else {
Write-Host "Skip: already Unassigned"
}
更新対象が “Intune に存在している状態” かを確認する
Enroll 直後、ワイプ直後、退役直前など、managedDevices 側の状態が不安定なタイミングでは更新が通りづらいことがあります。以下のような運用ルールを作ると詰まりにくくなります。
- 対象デバイスが managedDevices に出現していることを確認してからカテゴリ更新を行う
- 更新が通らない端末は「一定時間後に再実行」するが、短時間での連打は避ける
- 失敗端末を CSV に吐き、手動フォローできる逃げ道を用意する
“未割り当てに戻す” 操作は監査・誤操作防止もセットで
カテゴリを未割り当てに戻す操作は、意図せず実行すると現場の分類が崩れてトラブルになりがちです。自動化する場合は、次の工夫が効きます。
- 対象条件を明確にする(例:端末名プレフィックス、特定グループ所属など)
- 実行前後の状態をログに残す(ManagedDeviceId、端末名、旧カテゴリ、新カテゴリ、実行者、日時)
- dry-run(実行せず一覧化)モードを用意する
補足:PATCH と $ref の使い分けを “設計ルール” にする
今回のように「値を null にする」ことで状態を戻そうとすると、API の内部実装や仕様差分に引っ張られて詰まりやすくなります。運用を安定させるなら、次のようにルール化すると理解が揃いやすいです。
| 対象 | 推奨更新方法 | 理由 |
|---|---|---|
| managedDevice の単純な値(例:メモ、タグ的な値) | PATCH | 値の更新として素直に扱える |
| 他リソースへの紐づけ(例:deviceCategory のような関連) | $ref の PUT(参照更新) | 関連の更新として一貫し、反映のブレが減る |
「Intune のデバイスカテゴリを未割り当てに戻したい」という目的に対しては、“null を入れる” ではなく “参照を付け替える”が安定解です。
まとめ:Intune デバイスカテゴリを未割り当てに戻す最短解
deviceCategoryId = nullの PATCH では Unassigned に戻らないケースがある- Unassigned は ゼロ GUID(00000000-0000-0000-0000-000000000000)として扱う
- 解決策は
/deviceCategory/$refを PUT で更新し、参照をゼロ GUID に付け替える - 実務では 更新後に再取得して反映確認し、権限・ID 取り違え・同期遅延をセットでチェックする
「割り当てはできるのに未割り当てに戻らない」というハマりどころは、手元のスクリプトが悪いというより、更新すべき対象が “値” ではなく “参照” だったことが原因であるケースが多いです。$ref 更新をベースに実装を統一すると、運用も自動化も一気に安定します。

コメント