なぜMicrosoft Graphは沼なのか
本記事は、Japan Digital Design Advent Calendar 2024 の12日目の記事になります。
三菱UFJフィナンシャル・グループ(以下MUFG)の戦略子会社であるJapan Digital Design(以下JDD)SecurityTeamの杉野太紀( 杉fat )です。
本記事の目的
この記事では、Microsoft365で社内セキュリティを構成しているときに避けては通れないMicrosoft Graph (以下 Graph)が沼である理由について書きます。
わざわざMicrosoft製品をやり玉に挙げ、沼である・・・つまり使いづらいポイントを列挙することは、個人の心情としては気が進まないものの、この度ブログとした目的は、「Graphを触っている方々の不安を少しでも和らげたい」というものです。
おそらくGraphを業務で利用する用途は、多くの場合効率化による時間削減だと思います。しかし、Graphは後述する沼要素により、想定よりも多くの時間を「調査」に費やしてしまい、最悪の場合、何もできずに終わってしまう危険性があります。
本記事ではGraphの沼要素を明らかにし、できるだけ丁寧に解説することで、同じ境遇になった時に「あーそれあるよね!」と、安心して沼にハマっていただきつつ、上司に調査業務の困難さを説明する一助になることを目指して書きました。
また記事の後半では、どうすればできるだけ沼を避けられるか―Graphとうまくやっていけるのかについて、私が少しずつ気づき始めたことをお伝えしようと思います。
読者の方へのお願い
この記事の記載内容はすべて未完成であり、実のところ私自身もいまだに沼から抜け出せない状況が続いています。読者の方の優しいコメントやSNS上でのゆるい議論によって、私を含めた沼の住人や新たに挑む探検家たちが、よりクリアな視点を持って作業にとりかかれる社会になることを願っています。
Microsoft Graphについて
ここで簡単にGraphについてご紹介したいと思います。
Graphは簡単に言うと Microsoft365 関連の各種操作をAPIで操作できるようにする仕組み全般を指します。詳細は公式ドキュメント(Microsoft Graph の概要 - Microsoft Graph | Microsoft Learn)を参照ください。
この仕組みを使うことによって、自分たちで作成したアプリケーションなどに、Microsoft365の操作(参照、登録など)を組み込むことが可能になります。しかし、多くの人がGraphに触れるシーンは、PowershellでEntraIDやAzure上の資産を操作するときではないかと思います。
というのも、これまでPowershellにAzureADやMSOnlineなどに用意されていた専用モジュールが、2024年4月に非推奨となってしまいました。その移行先としてGraphがアナウンスされています(参考:重要なお知らせ: Azure AD PowerShell および MSOnline PowerShell モジュールの廃止 | Japan Azure Identity Support Blog )。
AzureADやMSOnlineはまだ使えますが、今後のことを考えると、PowershellでMicrosoft365を操作するには、原則Graphを使う必要があります。
上記の想定により、というより私の現在の利用用途のため、以降の内容はGraphをPowershellで操作することを前提とした沼要素となります。
Microsoft Graphの沼要素
できることが多すぎて沼
まずこれまでの解説でお気づきの通り、Graphは大体の場合、ユーザーが「やりたいこと」に対して「できること」が膨大すぎます。
Graphは、EntraIDのユーザーやグループをはじめ、Onedrive上のファイル、Outlookの予定表など幅広いオブジェクトをサポートしています。コマンドリファレンス(https://learn.microsoft.com/en-us/powershell/module/microsoft.graph.applications/?view=graph-powershell-1.0)を見ると膨大な量のコマンドが紹介されています。
では、早速 Microsoft.Graph モジュールを Install-module コマンドでインストールしてコマンド量を確認してみましょう。
> (Get-Command -Module Microsoft.Graph*).Count
9326
なんと9326個もあることがわかりました。
当社の環境をコマンドで支配したいと思い立ち、そのまま無邪気にImportしようとすると、、、
> Import-Module Microsoft.Graph
Import-Module : Function Get-MgDirectoryAttributeSet cannot be created because function capacity 4096 has been exceeded
for this scope.
なんと、セッションで許容できるコマンドの最大値 4096 に抵触していまい、インポートすることができません。
・・・これだけだと仕様が詰んでいるように見えますが、実は Microsoft.Graphは38個のサブモジュールの集合体になっています。Importは必要なものを適宜指定して利用するのが一般的な使い方です。
例えばEntraIDの登録ユーザーを取得する Get-MgUser コマンドを利用したい場合は Microsoft.Graph.Users モジュールをインポートして使います。
コマンド長すぎて沼
多機能であるが故の別の問題として、コマンドの重複を避けるために文字数が多くなるという難点も発生します。
試しにコマンドを長いものから順番に並べてみましょう。
> Get-Command | Sort-Object { $_.Name.Length } -Descending | Select-Object Name, @{Name='Length'; Expression={$_.Name.Length}} -First 5
Name Length
---- ------
Remove-MgIdentityAuthenticationEventFlowAsOnGraphAPretributeCollectionExternalUserSelfServiceSignUpAttributeIdentityUserFlowAttributeByRef 138
Get-MgIdentityAuthenticationEventFlowOnAuthenticationMethodLoadStartAsOnAuthenticationMethodLoadStartExternalUserSelfServiceSignUp 130
Remove-MgIdentityAuthenticationEventFlowAsOnAuthenticationMethodLoadStartExternalUserSelfServiceSignUpIdentityProviderBaseByRef 127
Remove-MgIdentityAuthenticationEventFlowAsOnAuthenticationMethodLoadStartExternalUserSelfServiceSignUpIdentityProviderByRef 123
New-MgIdentityAuthenticationEventFlowAsOnAuthenticationMethodLoadStartExternalUserSelfServiceSignUpIdentityProviderByRef 120
一番長いコマンドは
Remove-MgIdentityAuthenticationEventFlowAsOnGraphAPretributeCollectionExternalUserSelfServiceSignUpAttributeIdentityUserFlowAttributeByRef
で、138文字あることがわかりました。このコマンドのリファレンス(https://learn.microsoft.com/en-us/powershell/module/microsoft.graph.identity.signins/remove-mgidentityauthenticationeventflowasongraphapretributecollectionexternaluserselfservicesignupattributeidentityuserflowattributebyref?view=graph-powershell-1.0)を参照すると、その解説は
Delete ref of navigation property attributes for identity
(訳)ID のナビゲーション プロパティ属性の ref を削除する
とのことです。ちなみにこの解説は、スペースを入れて57文字です。
権限の考え方が独特で沼
おそらく、Graphを利用し始めた人が最初に躓く問題は「権限を持ってるはずなのに、必要なコマンドを実行できない」「権限不足といわれる」なのではないかと思います。
これはGraphの設計思想によるものです。
前述の通り、Graphは、「Microsoft365 関連の各種操作をAPIで操作できるようにする仕組み」です。したがってコマンドはAPI操作であり、実行権限はあくまでAPIに持たせる必要があります。
そのAPIは、Entra上にアプリケーションとして自分で作ってあげる必要があります。
この思想は、従来のEntraID (AzureAD) やActiveDirectoryを利用してきた人が抱いている「俺の権限=コマンドでできること」という概念とは大きく異なるため、最初は混乱を招きます。
しかしこの沼は、その時のエラーにも表れます。
エラーがわかりにくくて沼
上記の流れで、権限が不足していた時に出てくるエラーの例が以下です。
> Get-MgUser
Get-MgUser : Insufficient privileges to complete the operation.
Status: 403 (Forbidden)
ErrorCode: Authorization_RequestDenied
Date: 2024-11-30T15:23:02
Headers:
Transfer-Encoding : chunked
Vary : Accept-Encoding
Strict-Transport-Security : max-age=xxxxxxxx
request-id : xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
client-request-id : xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
x-ms-ags-diagnostic : {"ServerInfo":{"DataCenter":"Japan West","Slice":"E","Ring":"3","ScaleUnit":"000","Role
Instance":"OooOOOO000000o0"}}
x-ms-resource-unit : 2
Cache-Control : no-cache
Date : Sat, 30 Nov 2024 15:23:01 GMT
At line:1 char:1
+ Get-MgUser
+ ~~~~~~~~~~
+ CategoryInfo : InvalidOperation: ({ ConsistencyLe... , Headers = }:<>f__AnonymousType41`9) [Get-MgUser
_List], Exception
+ FullyQualifiedErrorId : Authorization_RequestDenied,Microsoft.Graph.PowerShell.Cmdlets.GetMgUser_List
2行目だけなら権限不足だとわかるのですが、3行目の 403 (Forbidden) が権限不足だと認識させるのを邪魔します。
特に上述の通り実行者自身は「自分の権限で見れるはず」と思っており、しかも色々試行錯誤している中でこのエラーメッセージに出会うことを想定すると、丸腰では素直に「権限が足りない」と認識することが難しいのではないかと思います。
これは、やはりGraphがAPIを呼び出す仕組みであるためで、例えばリソースがない時は404エラー、 内部でエラーが発生すれば500エラーといった風に、コマンドのエラーをHTTPのノリで返してきます。
この仕様は、ActiveDirectoryの管理をこれまで行ってきた人にとってはあまりユーザーフレンドリーとはいえません。
Web画面と感覚ズレてて沼
さて、いきなり個別具体的な話であり、また私個人の問題で申し訳ないのですが、現時点で私が知る限り、Graphを用いて、アクセスパッケージに紐づいてるリソースの一覧を出力することはできません。
しかしこれは、Web版のAzureConsoleであれば、、、むしろアクセスパッケージの一覧を確認すれば管理者でなくてもすぐ確認できる情報です。
一般的にPowerShellでコマンド操作をしたいときは、これらのWeb操作を自動化・効率化したい場合が殆どなので、「GUIでできることがCUIでできない」は結構痛手です。そして当然できないことはDocsに書いてないので、「できない」事実に気づくのは、これまでに紹介した沼に散々ハマった後に徐々に明らかになってきます。
そもそもGraphの管理単位が、Users、Group、Teams、予定表などのオブジェクト単位で区分けされていて、我々が日頃業務で利用するEntraID、Teams、Sharepointといった製品ごとのイメージで整理されていません。
EntraIDに限らず、またCUIであるかAPI呼び出しであるかに限らず、通常こういった操作はGUI操作との結びつきを学習しながら実装していくのが一般的な開発の流れであると考えると、やはり少し不親切なのではないかと思います。
Beta版の存在が色々怖くて沼
さて、上記の沼をGoogleやChatGPTをお供に沼を探検していくと、Graphのコマンドには Mg で始まるものと、 MgBeta で始まるものの、大きく2種類存在することに気づき始めます。
例えば、こちらのMicrosoft公式の記事(https://learn.microsoft.com/ja-jp/azure/active-directory/governance/entitlement-management-access-package-resources#add-resource-roles-to-an-access-package-with-microsoft-powershell)では以下のようなコマンドが出現します。
Get-MgBetaEntitlementManagementAccessPackageCatalog
これら、 MgBetaで始まっているコマンド一式はベータ版( v2.1.0 )で、まだプレビュー状態のコマンドです。ドキュメント(https://learn.microsoft.com/ja-jp/graph/versioning-and-support)には以下のような記述があり、利用には少し不安が残ります。
ベータ版には随時 API の重大な変更と廃止が発生することがあります。 運用アプリケーションでのベータ API の使用はサポートされていません。ベータ版の機能が現行バージョンに採用される保証はありません。
しかし、例えば権限管理系のコマンドでは、ベータ版のほうが出力が充実しており、非ベータは出力が貧弱で実質使えないものもあります。これは、上述の通りMicrosoft公式の記事でさえ、ベータ版の利用を手順に盛り込んでいる状況からも見て取れます。
したがって、用途によっては今後のアップデートに怯えながら運用する選択肢もとらなければならないかもしれません。
ベータ系の各コマンドのリファレンスはこちら(Microsoft Graph PowerShell documentation | Microsoft Learn)で、 Microsoft.Graph.Beta コマンドレットをインストール、インポートすると利用できます。
できるだけ沼らないために
さて、ここまでGraphの沼要素についてお話してまいりましたが、もちろんMicrosoft社も私たちを沼に招き入れようとして設計したとは思えません。
うまく使えばきっと、うまく使えるはずです。
ここからは、私がこれまでのGraphを使ってきて気づいてきた、沼にできるだけハマらない方法をお伝えしようと思います。
アプリを作成して、適切な権限を割り当てる
まず、この点は早々にあきらめることです。
面倒くさくとも、アプリの作成はほかの部署の管轄で依頼が必要であったとしても、Graphはアプリを作成しないことにはスタート地点に立てません。
ちなみに、アプリに User.Read.All の様な名前の権限を割り当てる方法として、「委任」と「アプリ」の二種類があります。
この違い、ざっくりいうと以下の通りです。
委任 → 利用者の権限で認証する
アプリ → シークレットとか証明書で認証する
とりあえず動きを見たい人は「委任」、もう仕上がってきて運用に落とし込みたい人は「アプリ」で権限を割り当てるとよさそうです。
「Microsoft GraphはEntraやAzureと違う会社の製品」だと思って使う
なぜGraphは沼なのか、、、の究極の答えのような気がしますが「利用者は同じ会社の製品だと思っている」ことがGraphの利用をより難しくしている一因のように思います。
したがって、これまでの経験は一度忘れて、むしろ全く違う会社の製品を無理やりMicrosoft環境に適用しているぐらいのノリで利用すると、意外に順調に作業が捗るのではないかと思っています。具体的には
リファレンスをよく見る
エラー内容は最初から最後までよく見る
のような、まったく知らない製品を触るような、初心に返って操作をすることが重要な気がします。
調べる系コマンドを身に着けておく
最後に、Graph沼を探検するにあたって私がよく利用しているコマンドをご紹介して終わりにしたいと思います。
今のセッションで使える権限を確認する
(Get-MgContext).Scopes
指定のフレーズを含むコマンドを調べる
Get-Command -Module Microsoft.Graph.Identity.Governance -Name *AccessPackage*
コマンドの実行に必要な権限を調べる(以下の例では Get-MgUser に必要な権限を表示します)
Find-MgGraphCommand -Command Get-MgUser | Select-Object -ExpandProperty Permissions
さいごに
ここまで、Graphの沼についてご紹介してまいりました。
同じ沼にハマっている方や、これからGraphいじろうと思って、たまたまこの記事にぶつかってしまった方々の一助になれたなら、私としてはとても幸せです。
Microsoftのセキュリティソリューションが膨大かつ複雑であるが故に、それをコントロールするGraphもまた複雑ということかと思いますが、うまく使いこなせると非常に強力なツールになるものだと信じて、私も日々業務に励んでおります。
最後に、とても辛そうな記事に見えるかもしれませんが、筆者自身はこういう作業が嫌いではなく、その証拠に現在も埼玉の低沼地帯に居住しており、その前は鷺沼に住んでいたことを付け加えておきます。
最後までご覧いただきありがとうございました。
Japan Digital Design株式会社では、一緒に働いてくださる仲間を募集中です。カジュアル面談も実施しておりますので下記リンク先からお気軽にお問合せください。
この記事に関するお問い合わせはこちら
Technology & Development Div.
Taiki Sugino