0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Microsoft Graph / PowerShell で Windows 10・Office 2021 残存台数を棚卸しする【Intune/Entra 連携】

0
Posted at

2026年10月13日に Office LTSC 2021 のサポート完全終了Windows 10 ESU Year 1 終了(Year 2 は倍額の $122/台)、Windows Server 2012/R2 ESU 最終終了 という3つの節目が重なります。

この記事では、Microsoft Graph PowerShell SDK を使って Intune/Entra ID に登録されたデバイスの Windows バージョンと Office バージョンを棚卸しするスクリプトを解説します。情シス担当者がゼロから手動で調べるのではなく、スクリプト1本で CSV 出力まで完結させることを目標とします。


前提条件

要件 内容
PowerShell 7.4 以上推奨(Windows PowerShell 5.1 でも動作可)
Microsoft Graph PowerShell SDK Microsoft.Graph モジュール v2.x
Entra ID ロール Device.Read.AllDeviceManagementManagedDevices.Read.All
Intune デバイスが Intune に登録済みであること

モジュールのインストール

# Microsoft Graph PowerShell SDK のインストール
Install-Module -Name Microsoft.Graph -Scope CurrentUser -Force

# バージョン確認
Get-Module Microsoft.Graph -ListAvailable | Select-Object Name, Version

Part 1:Entra ID 登録デバイスから Windows 10 端末を抽出する

Entra ID(旧 Azure AD)に登録されたすべてのデバイスを取得し、OS バージョンで絞り込む方法です。Intune 未登録の BYOD や Azure AD 参加のみの端末も含まれます。

スクリプト:Windows 10 端末の一覧取得

<#
.SYNOPSIS
    Entra ID 登録デバイスから Windows 10 端末を抽出して CSV に出力する
.DESCRIPTION
    Microsoft Graph API の /devices エンドポイントを使用する
    必要スコープ: Device.Read.All
.NOTES
    実行前に Connect-MgGraph を行うこと
#>

[CmdletBinding()]
param(
    [string]$OutputPath = ".\win10-devices-$(Get-Date -Format 'yyyyMMdd').csv"
)

# --- 認証 ---
Connect-MgGraph -Scopes "Device.Read.All" -NoWelcome

Write-Host "デバイス情報を取得中..." -ForegroundColor Cyan

# Entra ID に登録されたすべての Windows デバイスを取得(ページング対応)
$allDevices = Get-MgDevice -Filter "operatingSystem eq 'Windows'" -All -Property `
    DisplayName, `
    OperatingSystem, `
    OperatingSystemVersion, `
    ApproximateLastSignInDateTime, `
    AccountEnabled, `
    DeviceId, `
    TrustType, `
    ProfileType

Write-Host "取得デバイス総数: $($allDevices.Count)" -ForegroundColor Green

# Windows 10 の判定:バージョンが "10.0.1" で始まり "10.0.22" 未満
# Windows 10 の最終ビルドは 10.0.19045.x (22H2)
# Windows 11 は 10.0.22000 以上
$win10Devices = $allDevices | Where-Object {
    $osVer = $_.OperatingSystemVersion
    if ([string]::IsNullOrEmpty($osVer)) { return $false }
    # Windows 10 は 10.0.10240 〜 10.0.19045 の範囲
    $buildNumber = ($osVer -split '\.')[2] -as [int]
    return ($buildNumber -ge 10240 -and $buildNumber -le 19045)
}

Write-Host "Windows 10 デバイス数: $($win10Devices.Count)" -ForegroundColor Yellow

# 結果を整形
$results = $win10Devices | Select-Object `
    @{N='デバイス名'; E={$_.DisplayName}},
    @{N='OSバージョン'; E={$_.OperatingSystemVersion}},
    @{N='最終サインイン'; E={
        if ($_.ApproximateLastSignInDateTime) {
            [DateTime]$_.ApproximateLastSignInDateTime | Get-Date -Format 'yyyy-MM-dd HH:mm'
        } else { '不明' }
    }},
    @{N='有効'; E={$_.AccountEnabled}},
    @{N='デバイスID'; E={$_.DeviceId}},
    @{N='参加形態'; E={$_.TrustType}}

# CSV 出力
$results | Export-Csv -Path $OutputPath -Encoding UTF8BOM -NoTypeInformation
Write-Host "CSV 出力完了: $OutputPath" -ForegroundColor Green

# サマリー表示
Write-Host "`n=== Windows OS バージョン別集計 ===" -ForegroundColor Cyan
$allDevices | Group-Object {
    $ver = $_.OperatingSystemVersion
    if ([string]::IsNullOrEmpty($ver)) { return '不明' }
    $build = ($ver -split '\.')[2] -as [int]
    switch ($true) {
        ($build -ge 22000) { 'Windows 11'; break }
        ($build -ge 10240) { 'Windows 10'; break }
        default            { "その他 (Build $build)" }
    }
} | Sort-Object Name | Format-Table Name, Count -AutoSize

Disconnect-MgGraph

Part 2:Intune 管理デバイスから詳細情報を取得する

Intune 登録デバイスからは、OS バージョンに加えてインストール済みアプリ情報も取得できます。ただしアプリ一覧の取得は API の制限により端末ごとの個別取得になるため、台数が多い場合は時間がかかります。

スクリプト:Intune デバイス棚卸し + OS 集計

<#
.SYNOPSIS
    Intune 管理デバイスの Windows バージョンを棚卸しして CSV に出力する
.DESCRIPTION
    Microsoft Graph API の /deviceManagement/managedDevices エンドポイントを使用
    必要スコープ: DeviceManagementManagedDevices.Read.All
#>

[CmdletBinding()]
param(
    [string]$OutputPath = ".\intune-inventory-$(Get-Date -Format 'yyyyMMdd').csv"
)

# --- 認証 ---
Connect-MgGraph -Scopes "DeviceManagementManagedDevices.Read.All" -NoWelcome

Write-Host "Intune 管理デバイスを取得中..." -ForegroundColor Cyan

# Intune 登録デバイスを全件取得
$managedDevices = Get-MgDeviceManagementManagedDevice -All -Property `
    DeviceName, `
    OperatingSystem, `
    OsVersion, `
    LastSyncDateTime, `
    ComplianceState, `
    EnrolledDateTime, `
    UserDisplayName, `
    UserPrincipalName, `
    Manufacturer, `
    Model, `
    SerialNumber, `
    Id

Write-Host "Intune 管理デバイス総数: $($managedDevices.Count)" -ForegroundColor Green

# Windows デバイスのみ絞り込み
$windowsDevices = $managedDevices | Where-Object { $_.OperatingSystem -eq 'Windows' }

# バージョン分類関数
function Get-WindowsCategory {
    param([string]$OsVersion)
    if ([string]::IsNullOrEmpty($OsVersion)) { return '不明' }
    
    # OsVersion の形式: "10.0.19045.4529" など
    $parts = $OsVersion -split '\.'
    if ($parts.Count -lt 3) { return '不明' }
    
    $build = $parts[2] -as [int]
    switch ($true) {
        ($build -ge 22000) { return 'Windows 11' }
        ($build -eq 19045) { return 'Windows 10 22H2(最終版)' }
        ($build -ge 10240 -and $build -lt 19045) { return 'Windows 10(旧バージョン)' }
        default { return "不明 (Build $build)" }
    }
}

# 結果整形
$results = $windowsDevices | Select-Object `
    @{N='デバイス名';     E={$_.DeviceName}},
    @{N='OSバージョン';   E={$_.OsVersion}},
    @{N='Windowsカテゴリ'; E={Get-WindowsCategory $_.OsVersion}},
    @{N='最終同期';       E={
        if ($_.LastSyncDateTime) {
            [DateTime]$_.LastSyncDateTime | Get-Date -Format 'yyyy-MM-dd HH:mm'
        } else { '未同期' }
    }},
    @{N='コンプライアンス'; E={$_.ComplianceState}},
    @{N='ユーザー名';     E={$_.UserDisplayName}},
    @{N='UPN';           E={$_.UserPrincipalName}},
    @{N='メーカー';       E={$_.Manufacturer}},
    @{N='モデル';         E={$_.Model}},
    @{N='シリアル番号';   E={$_.SerialNumber}},
    @{N='登録日';         E={
        if ($_.EnrolledDateTime) {
            [DateTime]$_.EnrolledDateTime | Get-Date -Format 'yyyy-MM-dd'
        } else { '不明' }
    }}

$results | Export-Csv -Path $OutputPath -Encoding UTF8BOM -NoTypeInformation
Write-Host "CSV 出力完了: $OutputPath" -ForegroundColor Green

# 集計表示
Write-Host "`n=== Windows バージョン別台数 ===" -ForegroundColor Cyan
$results | Group-Object 'Windowsカテゴリ' | Sort-Object Count -Descending |
    Format-Table Name, Count -AutoSize

Write-Host "`n=== コンプライアンス状態別 ===" -ForegroundColor Cyan
$results | Group-Object 'コンプライアンス' | Sort-Object Count -Descending |
    Format-Table Name, Count -AutoSize

Disconnect-MgGraph

Part 3:Intune でインストール済み Office バージョンを確認する

Intune 管理下のデバイスでは、Graph API 経由でインストール済みアプリを取得できます。ただし /deviceAppManagement/managedDevices/{id}/detectedApps エンドポイントは1デバイスごとの取得となるため、件数が多い場合はバッチ処理が必要です。

<#
.SYNOPSIS
    Intune デバイスから Office 2021 インストール台数を集計する
.NOTES
    デバイス数が多い場合は -MaxDevices で上限を設定して段階的に実行すること
    必要スコープ: DeviceManagementManagedDevices.Read.All
                 DeviceManagementApps.Read.All
#>

[CmdletBinding()]
param(
    [int]$MaxDevices    = 50,   # 一度に処理するデバイス数の上限(API負荷軽減)
    [string]$OutputPath = ".\office-inventory-$(Get-Date -Format 'yyyyMMdd').csv"
)

Connect-MgGraph -Scopes `
    "DeviceManagementManagedDevices.Read.All",
    "DeviceManagementApps.Read.All" `
    -NoWelcome

$managedDevices = Get-MgDeviceManagementManagedDevice `
    -Filter "operatingSystem eq 'Windows'" `
    -Top $MaxDevices `
    -Property DeviceName, Id, UserDisplayName

Write-Host "対象デバイス数: $($managedDevices.Count)(上限: $MaxDevices)" -ForegroundColor Cyan

$officeResults = [System.Collections.Generic.List[PSCustomObject]]::new()
$counter = 0

foreach ($device in $managedDevices) {
    $counter++
    Write-Progress -Activity "アプリ情報取得中" `
        -Status "$counter / $($managedDevices.Count): $($device.DeviceName)" `
        -PercentComplete (($counter / $managedDevices.Count) * 100)
    
    try {
        # デバイスの検出済みアプリを取得
        $detectedApps = Get-MgDeviceManagementManagedDeviceDetectedApp `
            -ManagedDeviceId $device.Id `
            -All
        
        # Office 2021 / Office LTSC 2021 を含むアプリを抽出
        $officeApps = $detectedApps | Where-Object {
            $_.DisplayName -match 'Office' -and (
                $_.DisplayName -match '2021' -or
                $_.DisplayName -match 'LTSC'
            )
        }
        
        if ($officeApps) {
            foreach ($app in $officeApps) {
                $officeResults.Add([PSCustomObject]@{
                    デバイス名    = $device.DeviceName
                    ユーザー      = $device.UserDisplayName
                    アプリ名      = $app.DisplayName
                    バージョン    = $app.Version
                    要対応        = '対象(Office 2021)'
                })
            }
        } else {
            # Office 系アプリが見つからない、または Microsoft 365 Apps の場合
            $m365Apps = $detectedApps | Where-Object {
                $_.DisplayName -match 'Microsoft 365' -or
                ($_.DisplayName -match 'Office' -and $_.DisplayName -notmatch '2021|2019|2016|2013')
            }
            $officeResults.Add([PSCustomObject]@{
                デバイス名 = $device.DeviceName
                ユーザー   = $device.UserDisplayName
                アプリ名   = if ($m365Apps) { $m365Apps[0].DisplayName } else { '未検出' }
                バージョン = if ($m365Apps) { $m365Apps[0].Version } else { '-' }
                要対応     = if ($m365Apps) { '対象外(M365 Apps)' } else { '要確認' }
            })
        }
    } catch {
        $officeResults.Add([PSCustomObject]@{
            デバイス名 = $device.DeviceName
            ユーザー   = $device.UserDisplayName
            アプリ名   = 'エラー'
            バージョン = '-'
            要対応     = "取得失敗: $($_.Exception.Message)"
        })
    }
}

Write-Progress -Completed -Activity "完了"

$officeResults | Export-Csv -Path $OutputPath -Encoding UTF8BOM -NoTypeInformation
Write-Host "`nCSV 出力完了: $OutputPath" -ForegroundColor Green

Write-Host "`n=== Office 対応状況サマリー ===" -ForegroundColor Cyan
$officeResults | Group-Object '要対応' | Sort-Object Count -Descending |
    Format-Table Name, Count -AutoSize

Disconnect-MgGraph

Part 4:3スクリプトの実行順序と運用のポイント

推奨実行順序

1. Part 1(Entra ID 全デバイス)
   └─ 全社 Windows 端末の全体像を掴む

2. Part 2(Intune 詳細)
   └─ 詳細なバージョン・コンプライアンス状態を確認

3. Part 3(Office バージョン)
   └─ Office 2021 対象台数を特定

Intune 未登録デバイスへの対応

Intune に登録されていないデバイスは、これらのスクリプトでは検出されません。BYOD デバイスや古い端末が登録されていない場合は、Active Directory の Get-ADComputer や WSUS のレポート機能を補完的に使用してください。

# Active Directory から Windows 10 端末を抽出する補完スクリプト
# 必要: RSAT ActiveDirectory モジュール
Import-Module ActiveDirectory

$adWin10 = Get-ADComputer -Filter {
    OperatingSystem -like "*Windows 10*"
} -Properties Name, OperatingSystem, OperatingSystemVersion, LastLogonDate |
    Select-Object Name, OperatingSystem, OperatingSystemVersion,
    @{N='最終ログオン'; E={$_.LastLogonDate | Get-Date -Format 'yyyy-MM-dd'}}

$adWin10 | Export-Csv -Path ".\ad-win10-$(Get-Date -Format 'yyyyMMdd').csv" `
    -Encoding UTF8BOM -NoTypeInformation

Write-Host "AD 上の Windows 10 端末: $($adWin10.Count) 台"

棚卸し後の対応

棚卸しで現状が把握できたら、次のアクションに移ります:

  • Windows 10 端末:TPM 2.0 確認 → Windows 11 アップグレード可否判定 → PC更新計画策定
  • Office 2021:Microsoft 365 Apps(サブスク)または Office LTSC 2024(買い切り)への移行計画
  • Windows Server 2012(別スクリプトが必要):Azure 移行 / オンプレ更改の判断

情シス365 では、こうした棚卸しの代行から移行計画策定・実行支援まで、中小企業向けに提供しています。スクリプトの実行環境がない、Intune を導入していないというケースでも対応可能です。


まとめ

スクリプト 取得情報 必要ロール
Part 1 Entra ID 全デバイス + OS バージョン Device.Read.All
Part 2 Intune デバイス + 詳細バージョン DeviceManagementManagedDevices.Read.All
Part 3 インストール済み Office バージョン DeviceManagementManagedDevices.Read.All + DeviceManagementApps.Read.All

2026年10月13日まで約4か月。まず棚卸しから始めることで、対応の優先順位と必要コストを正確に把握できます。


著者:亀田 英佑
株式会社BTNコンサルティング 代表取締役 / 情シス365 運営
中小企業の IT リスク管理・アウトソーシングを専門とする。

無料相談 → https://meetings-na2.hubspot.com/e-kameta

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?