2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

MS365管理ライセンスの棚卸手法

2
Last updated at Posted at 2025-12-18

MS365管理ライセンスをEntra IDグループで管理しているケースで、GUIでユーザ一覧を出力しなくなったのでPowerShellで一覧を取得して、ライセンスを割り当てているものの使用していないユーザを特定する手法を紹介します。ツールを使えばもっとスマートに出来るかもしれませんが、費用をかけず、広く使われているツールを使っています。

以下からの手順はPowerShell7を使用することを前提とし、以下の順序で実行します。

1.初回実行時に下記を実行してGraphモジュールをインストール

Find-Module Microsoft.Graph
Install-Module Microsoft.Graph -Scope CurrentUser -Force
Get-Module Microsoft.Graph* -ListAvailable | ft Name,Version,ModuleBase

2.初回実行時は下記のポリシーを一時適用

Set-ExecutionPolicy -Scope CurrentUser -ExecutionPolicy RemoteSigned

3.利用しているサービスプランの特定(任意のファイル名.ps1で保存)

Connect-MgGraph -Scopes "User.Read.All","Directory.Read.All"

$skus = Get-MgSubscribedSku

$skus | ForEach-Object {
    Write-Host ("SkuPartNumber: {0}" -f $_.SkuPartNumber)
    $_.ServicePlans | ForEach-Object {
        Write-Host ("  - ServicePlanName: {0} | State: {1}" -f $_.ServicePlanName, $_.ProvisioningStatus)
    }
    Write-Host ""
}

4.ライセンスを割り当てているユーザ一覧の出力(任意のファイル名.ps1で保存)
テナントIDと特定したいライセンスのサービスプラン名、出力ファイル名を
各ユーザの環境に合わせて修正して下さい。

テナントIDの確認

Get-MgOrganization

ex) Power BI Proの場合、BI_AZURE_P2 をサービスプラン名として入力
SkuPartNumber: POWER_BI_PRO

  • ServicePlanName: PURVIEW_DISCOVERY | State: Success
  • ServicePlanName: EXCHANGE_S_FOUNDATION | State: Success
  • ServicePlanName: BI_AZURE_P2 | State: Success
# テナントIDの定義
$TenantId = "********-****-****-****-************"

Disconnect-MgGraph -ErrorAction SilentlyContinue
Connect-MgGraph -Scopes "User.Read.All","Directory.Read.All" -TenantId $TenantId

$Downloads = Join-Path $env:USERPROFILE "Downloads"
$Stamp     = Get-Date -Format "yyyyMMdd-HHmm"
$CsvPath   = Join-Path $Downloads ("******-assigned-users-with-details-{0}.csv" -f $Stamp)

# 特定したいライセンスのサービスプラン名
$pbPlans = @("***********")  

# ユーザー一覧取得(部署・会社名・役職を含む)
$users = Get-MgUser -All -Property "Id,DisplayName,UserPrincipalName,Mail,AssignedLicenses,AccountEnabled,Department,CompanyName,JobTitle"

$results = @()
foreach ($u in $users) {
    $details = Get-MgUserLicenseDetail -UserId $u.Id
    foreach ($lic in $details) {
        foreach ($sp in $lic.ServicePlans) {
            if ($pbPlans -contains $sp.ServicePlanName -and ($sp.ProvisioningStatus -in @("Enabled","Success"))) {
                $isDirect = $false
                if ($u.AssignedLicenses -and ($u.AssignedLicenses.SkuId -contains $lic.SkuId)) { $isDirect = $true }

                $results += [PSCustomObject]@{
                    DisplayName       = $u.DisplayName
                    UserPrincipalName = $u.UserPrincipalName
                    Mail              = $u.Mail
                    Department        = $u.Department
                    CompanyName       = $u.CompanyName
                    JobTitle          = $u.JobTitle
                    Id                = $u.Id
                    AccountEnabled    = $u.AccountEnabled
                    Source            = if($isDirect){"Direct"}else{"Group"}
                    SkuPartNumber     = $lic.SkuPartNumber
                    ServicePlanName   = $sp.ServicePlanName
                    Provisioning      = $sp.ProvisioningStatus
                }
            }
        }
    }
}

# 重複排除
$unique = $results | Sort-Object Department, DisplayName, UserPrincipalName, Id -Unique

# CSV出力(部署・会社名・役職を含む)
$unique |
    Select-Object DisplayName,UserPrincipalName,Mail,Department,CompanyName,JobTitle,Id,AccountEnabled,Source,SkuPartNumber,ServicePlanName,Provisioning |
    Export-Csv -Path $CsvPath -NoTypeInformation -Encoding UTF8

# サマリー
$cntAll    = $unique.Count
$cntDirect = ($unique | Where-Object {$_.Source -eq "Direct"}).Count
$cntGroup  = ($unique | Where-Object {$_.Source -eq "Group"}).Count

Write-Host ("CSV exported: {0}" -f $CsvPath) -ForegroundColor Green
Write-Host ("Total unique users (***** ** ***): {0}" -f $cntAll)
Write-Host ("  - Direct assignments : {0}" -f $cntDirect)
Write-Host ("  - Group assignments  : {0}" -f $cntGroup)

5.3と4の実行

ファイルの保存先を指定して以下の様に実行する

cd "C:\Users\ユーザ名\Downloads"
.\***********.ps1

6.認証
MS365管理コンソールの管理者権限があるアカウントで認証します。
認証後にcsvがDownloadsフォルダに出力されます。

7.棚卸

サービスを日々使用しているかどうかは、監査ログを利用しています。
ログイン履歴だとセッションが残っている正しいリストを取得出来ない可能性があるため
サービスで特定されているデータへのアクセス履歴をフィルターしてユーザを一覧にします。
データ量が多い場合は予めアカウントの重複を除いた一覧を作成します。

4.で出力したユーザを監査ログから出力したアカウント一覧と突合します。

excelであれば、以下の関数で突合します。

COUNTIF関数を使って、指定範囲内で特定のデータが何回出現するかをカウントします
その数が1より大きい(2以上)場合に重複と判断する方法で、チェック列を以下の関数
で重複を確認します

=COUNTIF(範囲,検索条件)(ex:=COUNTIF($B$2:$B$10,B2))と入力

結果を見て以下のように判定します。

2を表示:アクセス履歴あり(4.の出力結果と重複するアカウントが監査ログに記録)
1を表示:アクセス履歴無し(4.の出力結果が監査ログに記録無し)

2
1
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
2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?