ユーザーに付与されているMicrosoftライセンスを確認するスクリプト

Microsoft 365テナントの移行作業を実施する中でMicrosoftライセンス(以下、ライセンス)の切替作業を行った際、先立って各ユーザーに付与されているライセンスの洗い出しを行いました。

各ユーザーのすべてのライセンスを確認するため一つ一つ確認するには時間がかかることもあり、対象ユーザーをCSVファイルに記載して読み込ませることで一括で出力可能なスクリプトを作成したので、本記事にまとめてみました。

インプット用CSVファイルの作成

以下の内容をまとめ、Excelファイルに起票します。

UserPrincipalName:アカウント名

起票した後、拡張子を「.csv」として任意のファイル名で保存します。

スクリプトの作成・実行

以下の内容でスクリプト(.ps1)ファイルを作成し、実行します。

param(
    [string]$InputCsvPath = "C:\temp\LicenseCheck\Input\users.csv",
    [string]$OutputFolder = "C:\temp\LicenseCheck\Output"
)

# 0) 出力フォルダを作成
if (-not (Test-Path -Path $OutputFolder)) {
    New-Item -ItemType Directory -Path $OutputFolder -Force | Out-Null
}

# 1) Graph接続
try {
    Connect-MgGraph -Scopes "User.Read.All","Organization.Read.All" -ErrorAction Stop
} catch {
    throw "Graphへの接続に失敗しました。`$($_.Exception.Message)`a"
}

# assignedByGroupId を取りたい場合は beta推奨
try { Select-MgProfile -Name "beta" } catch { }

# 2) 入力CSV
if (-not (Test-Path -Path $InputCsvPath)) {
    throw "入力CSVが見つかりません: $InputCsvPath"
}
$inputRows = Import-Csv -Path $InputCsvPath

# 3) SKU辞書(SkuId -> SkuPartNumber)
$tenantSkus = Get-MgSubscribedSku
$skuMap = @{}
foreach ($sku in $tenantSkus) {
    $skuMap[$sku.SkuId] = $sku.SkuPartNumber
}

# 4) グループ名キャッシュ
$groupNameCache = @{}
function Resolve-GroupName {
    param([string]$GroupId)
    if ([string]::IsNullOrWhiteSpace($GroupId)) { return $null }
    if ($groupNameCache.ContainsKey($GroupId)) { return $groupNameCache[$GroupId] }
    try {
        $g = Get-MgGroup -GroupId $GroupId -ErrorAction Stop
        $groupNameCache[$GroupId] = $g.DisplayName
        return $g.DisplayName
    } catch {
        $groupNameCache[$GroupId] = $null
        return $null
    }
}

# 5) 出力用
$summaryRows = New-Object System.Collections.Generic.List[object]
$errorRows   = New-Object System.Collections.Generic.List[object]

# 6) 進捗
$index = 0
$total = $inputRows.Count

foreach ($row in $inputRows) {
    $index++
    Write-Progress -Activity "ユーザーのライセンス取得中" -Status "$index / $total" -PercentComplete (($index/$total)*100)

    $upn = $row.UserPrincipalName
    $oid = $row.ObjectId

    # ユーザー解決(UPN優先)
    $user = $null
    try {
        if ($upn -and $upn.Trim() -ne "") {
            $user = Get-MgUser -UserId $upn -Property "id,displayName,userPrincipalName" -ErrorAction Stop
        } elseif ($oid -and $oid.Trim() -ne "") {
            $user = Get-MgUser -UserId $oid -Property "id,displayName,userPrincipalName" -ErrorAction Stop
        } else {
            throw "UserPrincipalName か ObjectId のどちらかを指定してください。"
        }
    } catch {
        $errorRows.Add([PSCustomObject]@{
            InputUserPrincipalName = $upn
            InputObjectId          = $oid
            ErrorMessage           = $_.Exception.Message
        })
        continue
    }

    # ライセンス詳細(SKU単位のみ使用)
    $licDetails = $null
    try {
        $licDetails = Get-MgUserLicenseDetail -UserId $user.Id -ErrorAction Stop
    } catch {
        $errorRows.Add([PSCustomObject]@{
            InputUserPrincipalName = $upn
            InputObjectId          = $oid
            ErrorMessage           = "Get-MgUserLicenseDetail失敗: $($_.Exception.Message)"
        })
        continue
    }

    if (-not $licDetails) {
        # ライセンス無しユーザーも1行だけ出す(SKU列は空)
        $summaryRows.Add([PSCustomObject]@{
            UserPrincipalName = $user.UserPrincipalName
            DisplayName       = $user.DisplayName
            SkuId             = ""
            SkuPartNumber     = ""
            AssignedBy        = ""
        })
        continue
    }

    foreach ($lic in $licDetails) {
        $skuId = $lic.SkuId
        $partNumber = $skuMap[$skuId]
        if (-not $partNumber) { $partNumber = "" }

        # グループ割り当て判定
        $assignedByGroupId = $lic.AdditionalProperties["assignedByGroupId"]
        $assignedLabel = if ($assignedByGroupId) {
            $gName = Resolve-GroupName -GroupId $assignedByGroupId
            if ($gName) { "Group:$gName" } else { "Group:$assignedByGroupId" }
        } else {
            "Direct"
        }

        # ユーザー×SKU の1行のみ追加
        $summaryRows.Add([PSCustomObject]@{
            UserPrincipalName = $user.UserPrincipalName
            DisplayName       = $user.DisplayName
            SkuId             = $skuId
            SkuPartNumber     = $partNumber
            AssignedBy        = $assignedLabel
        })
    }

    Start-Sleep -Milliseconds 50
}

# 7) 出力
$stamp = Get-Date -Format "yyyyMMdd-HHMM"
$summaryFile = Join-Path $OutputFolder "M365-User-License-Summary-$stamp.csv"
$errorFile   = Join-Path $OutputFolder "M365-User-License-Errors-$stamp.csv"

$summaryRows | Export-Csv -Path $summaryFile -NoTypeInformation -Encoding UTF8
$errorRows   | Export-Csv -Path $errorFile   -NoTypeInformation -Encoding UTF8

Write-Host "出力完了:"
Write-Host "サマリ: $summaryFile"

CSVのパスについて、「$inputCsvPath」ではファイルを配置した場所に合わせて指定し、「$OutputFolder」では任意のパスを指定してください。

また、スクリプトの保存時、エンコードは「UTF-8(BOM付き)」を選択してください。

尚、スクリプト実行時にMicrosoft 365のサインインが求められる場合は、グローバル管理者などの権限を持ったアカウントでサインインします。以下の画面が表示された場合は「承諾」をクリックしてください。

結果の確認

スクリプトの中の「$OutputFolder」で指定したパスに、作成結果のファイルが生成されます。

取得できたユーザーのライセンス情報は、「M365-User-License-Summary-<日付>.csv」に出力されます。

ファイルを開くと以下の通り一覧が出力されており、「SkuPartNumber」列より付与されているライセンスを確認することが可能です。

取得できなかったユーザーについては、「M365-User-License-Errors-$stamp.csv」に出力されます。

ファイルを開くと以下の通り取得できなかったユーザーの一覧が出力され、「ErrorMessage」列に取得できなかった理由が記載されます。

※画像では該当するユーザーが見つからなかった旨のエラーが記載されています

おわりに

Microsoft 365テナントに付与されているライセンスを確認するスクリプトを紹介しました。

スクリプト一つで特定のユーザーのライセンス情報を確認できるので、手段の一つとして活用していただけますと幸いです。

執筆担当者プロフィール
平林 昌幸

平林 昌幸(日本ビジネスシステムズ株式会社)

通信サービス本部所属。趣味は野球観戦と旅行。 Microsoft 365やセキュリティに関する備忘を投稿していきます。

担当記事一覧