👪

Microsoft Entra IDのユーザとグループ棚卸し用のコマンドを作ったので紹介

2024/05/30に公開

こんにちは。イオンスマートテクノロジー株式会社(AST)でSREチームの林 aka もりはやです。

当社はメインのクラウドサービスとしてAzureを採用しており、Azureおよび関連するツール群のID管理をMicrosoft Entra ID(以降はEntra ID)にて行なっています。(正確にはまだまだEntra IDによるSSO/SCIM未対応のツールも残っており移行を検討中です)

Entra IDは大変多機能ですが、本記事ではコア機能のユーザ管理(User management)においてグループと各グループに所属するユーザの棚卸しを行なった際に組み立てたコマンドを紹介します。

なお、Entra IDの多機能さについてはドキュメントをご覧ください。
https://learn.microsoft.com/en-us/entra/identity/

TL;DR

  • 新しいプロダクト用に新しいEntra IDのグループを作成する要件が出てきた
  • 既存のグループから乖離する形にはしたくなかったため棚卸しを一度やりたい
  • GUIでは大変なためAzure CLIでコマンドを組み立てた

背景

当社はイオンのトータルアプリiAEONの開発を中心に、複数のプロダクト開発チームが様々な取り組みを日々行なっています。

ある日、一つのプロダクト開発チームから「新しいチーム用に、Azureリソースに対して適切な権限を用意してほしい」との依頼が来ました。

Azure上で各種リソースに権限を付与する場合、RBACと呼ばれる考え方で権限を割り当てることになり、考慮するのはざっくり以下です。(なおRBACを発展させたABACという権限管理もありますがここでは割愛します)

  • 4つの対象(セキュリティプリンシパル( (ユーザー、グループ、サービス プリンシパル、またはマネージド ID) )
  • 4つのスコープ (管理グループ、サブスクリプション、リソース グループ、リソース)
  • 割り当てるロール(ビルトインの既存のもの、ユーザカスタム)

https://learn.microsoft.com/ja-jp/azure/role-based-access-control/overview

今回の依頼は「新しいチーム用」の権限のため、ユーザの集合であるグループを作成し、そのグループに対して適切な権限を持つロールを割り当てていくことになります。

新しいグループを作成するにあたり以下のような観点で棚卸しをすることにしました。

  • 既存グループの名前と違和感が無いようにしたい(ネーミングルールの基本方針はあるが)
  • 今回の新しいチームは、実は既存のグループが流用できたりしないか(メンバーが同じとか)
  • ついでに不要なグループおよびユーザの所属があるのではないか

GUIから確認するのは大変

グループの一覧だけなら、AzureのPortal -> Entra ID -> Groups -> Download groups でCSVファイルとして簡単に取得することができます。

しかし、グループとユーザの所属状況をGUIから簡単に取得することはできなさそうでした。

対策としてCLIコマンドを組み立てる

そこでAzure CLIを利用してコマンドを組み立てることにしました。
テキストに出力ができれば、豊富なCLIコマンドによる集計等が便利だからです。

前提となる環境

本記事の環境は以下の通りです。基本的なコマンドとオプションを利用していますが環境差異によって動作しない場合があります。

  • OS: macOS Sonoma 14.4.1
  • zsh: 5.9
  • az: 以下を参照
$ az --version
azure-cli                         2.61.0

core                              2.61.0
telemetry                          1.1.0

Extensions:
account                            0.2.5
azure-devops                       1.0.1
ssh                                2.0.3

Dependencies:
msal                              1.28.0
azure-mgmt-resource               23.1.1

コマンド1: グループ名とメンバーを表示(グループ名昇順)

このコマンドはグループ名を # My-Group-Name として表示した後で、所属するメンバーを順に表示します。

主な用途:人間が目視で確認する時に、グループ名で検索してメンバー一覧を確認する

$ az ad group list | jq -r '.[].displayName' | sort | while read -r GROUP
do
  echo "# ${GROUP}"
  az ad group member list --group "${GROUP}" | jq  -c '.[] | {displayName: .displayName , mail: .mail}'
done

以下は出力例です。名前(displayName)だけではユーザを判別できないケースがあるためメール(mail)も表示しています。

# My-Group-01
{"displayName":"User 01","mail":"user01@example.com"}
{"displayName":"User 02","mail":"user02@example.com"}
# My-Group-02
{"displayName":"User 11","mail":"user11@example.com"}
{"displayName":"User 12","mail":"user12@example.com"}
...

コマンド2: グループ名とメンバー数を表示(グループ名昇順)

このコマンドはグループ名と、そのグループに所属するユーザの数を表示します。

主な用途:グループと所属ユーザ数を把握する

$ az ad group list | jq -r '.[].displayName' | sort | while read -r GROUP
do
  echo -n "${GROUP}:"
  az ad group member list --group "${GROUP}" | jq  -c '.[] | {displayName: .displayName , mail: .mail}' | wc -l | tr -d ' '
done

以下は出力例です。 : の後に空白を入れるのかはお好みです。

My-Group-01:3
My-Group-02:32
My-Group-03:0
...

コマンド3: グループ名とメンバー数を表示(メンバー数で昇順)

ほぼコマンド2と同じですが、このコマンドはグループ名と、そのグループに所属するユーザの数を所属ユーザ数が多い順で表示します。なお while の表示が終わった後に sort するため、前2つのコマンドと違い出力が最後にまとまるため表示が遅く感じられます。

主な用途:所属ユーザが0の未使用のグループや、人数が多すぎて分割した方が良いグループを洗い出す

$ az ad group list | jq -r '.[].displayName' | while read -r GROUP
do
  echo -n "${GROUP}:"
  az ad group member list --group "${GROUP}" | jq  -c '.[] | {displayName: .displayName , mail: .mail}' | wc -l | tr -d ' '
done | sort -t: -k2 -n

以下は出力例です。 : の後に空白を入れるのかはお好みです。

My-Group-03:0
My-Group-01:3
My-Group-02:32
...

コマンド4: グループ名とメンバーを1行で表示(グループ名で昇順)

コマンド1の結果を一行にまとめるコマンドがこれです。

主な用途:テキストに出力しておき、ユーザ名をキーにそのユーザがどのグループに出力しているかを確認する

$ az ad group list | jq -r '.[].displayName' | sort | while read -r GROUP
do
  echo "# ${GROUP}"
  az ad group member list --group "${GROUP}" | jq  -c '.[] | {displayName: .displayName , mail: .mail}'
done | tr -d '\n' | sed -e 's/#/\n#/g'

以下は出力例です。このままでは読みづらいため、テキストへリダイレクトで出力しておき簡易的なDBとして扱います。

# My-Group-01{"displayName":"User 01","mail":"user01@example.com"}{"displayName":"User 02","mail":"user02@example.com"}
# My-Group-02{"displayName":"User 11","mail":"user11@example.com"}{"displayName":"User 12","mail":"user12@example.com"}{"displayName":"U...
...

以下のようにテキストに出力しておき、ユーザ名をキーとして grep をすることで、そのユーザが所属するグループの一覧を取得できます。 cut はグループ名だけを表示して見やすくするために追加しています。

$ grep user01 <output.txt> | cut -d '{' -f 1
# My-Group-01
# My-Group-04
# My-Group-08
...

コマンドの解説

簡単に使用したコマンドについて解説します。

  • az ad group list : Entra IDのグループの一覧を取得します
  • az ad group member list --group <GroupName> : --group で指定したグループに所属するユーザ一覧を取得します
  • jq : az ad group の結果がJSONでリターンされるため、欲しい情報をフィルタするために利用します(細かなフィルタ条件については割愛)
  • sort : 結果を並び替えます
    • -t: : フィールドの区切り文字として : を利用します(デフォルトは空白)
    • -k2 : 2つ目のフィールドをキーに並び替えます
    • -n : 数字として並べ替えます(デフォルトは文字順)
  • wc -l : 行数をカウントして、メンバー数を表示します
  • while : 一個前のコマンドの結果を利用して、ループ処理をします
    • 当初は for で行なっていましたが、グループ名に空白を持つものがあったため利用しました。今回一番工夫したところがここです
  • tr -d '\n' : 改行を削除し全てのテキストをまとめて一行にします
  • sed -e 's/#/\n#/g' : tr によって一行になったテキストを # の前に改行を入れて整形します
  • grep : テキストファイルからキーワードを含む行を抜き出して表示します。 -E オプションで正規表現も組み合わせてトラブルシュートに使うことも多いです

正直に言ってシェル芸感があります。特に tr -d '\n' | sed -e 's/#/\n#/g' のところは sed だけで表現できそうですし、他ももっとシンプルに書けるかもしれませんが、試行錯誤しつつもサクッと組み立てた結果がこちらです。

終わりに

以上、Entra IDのグループと所属するユーザを棚卸しする便利コマンドについて紹介しました。つい慣れているAzure CLIおよびシェルコマンド群を利用してしまいましたが、今後はAzure Resource Graphも活用したいと考え入門中です。それでは皆様もEnjoy Azure!

AEON TECH HUB

Discussion