💡

サービス プリンシパルが作成できなかった時に確認したこと

2024/07/24に公開

サービス プリンシパル (Service Principal) を作成すると、権限不足によるエラーが発生することがあります。たとえば、Azure CLI コマンドだと、以下のようなエラーを見ることがあります。

az ad sp create-for-rbac --role Reader --scopes "/subscriptions/$subid"

# エラー パターン 1:
# Directory permission is needed for the current user to register the application. For how to configure, please refer 'https://docs.microsoft.com/azure/azure-resource-manager/resource-group-create-service-principal-portal'. Original error: Insufficient privileges to complete the operation.
#
# エラー パターン 2:
# Insufficient privileges to complete the operation.

先日、自分も同じ事象に遭遇して少しハマってしまったので、その時に得た知見とやったことをシェアしたいと思います。

Entra テナント上でアプリケーションが作成できるか

サービスプリンシパルの作成には、Entra テナントでのアプリケーションの作成が伴います。つまり、Graph API における Application.ReadWrite.OwnedBy の権限が必要です。

作業アカウントで当該権限が付与されている場合、次のコマンドが成功するはずです。

# Azure CLI
az ad sp create-for-rbac

# Azure PowerShell
New-AzADServicePrincipal

これらのコマンドが失敗する場合は、Entra テナントの管理者に依頼して、Application.ReadWrite.OwnedBy|All の権限を作業アカウント (サービスプリンシパルの作成に使用するアカウント) に付与してもらいます。

  • ここでいう管理者とは、Entra ID の管理ポータルや Graph API を通して、他のユーザーの役割を付与したり変更できるユーザーです。特権ロール管理者 に相当するユーザーが該当しますが、わからなければグローバル管理者に依頼するのが無難です。
  • 作業アカウントがユーザーの場合、Graph API の権限ではなくロールを割り当てることになります。「アプリケーション開発者」か「アプリケーション管理者」を選択します。
  • 作業アカウントがアプリケーション (e.g. サービスプリンシパル) の場合、Graph API のアクセス許可を追加します。
作業アカウント OwnedBy を付与する場合 All を付与する場合
ユーザー アプリケーション開発者 ロールを割り当てる アプリケーション管理者 ロールを割り当てる
アプリケーション Application.ReadWrite.OwnedBy を Graph API のアクセス許可に追加する Application.ReadWrite.All を Graph API のアクセス許可に追加する

具体的な作業方法に関しては、以下を参考にしてください。アクセス許可の追加では、管理者の同意を忘れないようにしましょう。

トークンのキャッシュを消してみる

Azure CLI と Azure PowerShell は、Entra テナントから発行されたトークンをキャッシュする機構を持っています。そのため、古いトークンを使い続けることにより、テナント側の権限変更がコマンドで反映されないことがあります。

次の図は、az ad sp create-for-rbac を実行した時、ローカルのトークンを使う場合とそうでない場合で処理が分岐する様子を示しています。左側がローカルのトークンを再利用した場合のデバッグログ、右側が再利用せず新たにトークンを発行している場合のデバッグログです。


左: トークンを再利用、右: 新しく access token を発行

ローカルのトークンキャッシュを削除するには、次のコマンドが利用できます。

# Azure CLI
az account clear

# Azure PowerShell
Clear-AzAccount

ちなみに、トークンが使いまわされていることは、次のコマンドを実行すれば分かります。ログアウト/再ログインの前後で、ローカルに保存されているトークンは変化しません。

# Azure CLI
az account get-access-token
az logout && az login # --service-principal ...
az account get-access-token

# Azure PowerShell
Get-AzAccessToken
Logout-AzAccount && Connect-AzAccount # -ServicePrincipal ...
Get-AzAccessToken

デバッグ オプションを試す

Azure CLI と Azure PowerShell では、すべてのコマンドにデバッグのための情報を出力するスイッチ パラメータがあります (--debug-Debug)。

# Azure CLI
az ad sp create-for-rbac --debug

# Azure PowerShell
New-AzADServicePrincipal -Debug

権限周りで操作がこけたときは、直前にどんな API を叩いていたか (リソース対象、メソッド、リクエスト内容) が解決のカギを握ることが多いので、それらを確認するために重宝します。

新しい環境で実行する

環境固有の要素を排除するために、複数の環境で同じコマンドをすることは有効です。前述したトークン キャッシュのような落とし穴を回避できます。

手軽に実験できる環境としては、Azure Cloud Shell がおすすめです。特に、セッションごとに新しい環境を作ってくれるエフェメラル セッションを使うのが良いです(Web 開発で Incognito モードを使うのと似てる)[1]

https://learn.microsoft.com/ja-jp/azure/cloud-shell/get-started/ephemeral?tabs=azurecli

個人的にはローカルのコンテナ環境を使うことも多いです。Docker の場合は、次のイメージが Microsoft から公式に配布されていて、最新のバージョンのコマンドラインツールが使用できます。

docker run --rm -it mcr.microsoft.com/azure-cli
docker run --rm -it mcr.microsoft.com/azure-powershell pwsh
脚注
  1. エフェメラル セッション内で cat /proc/1/cmdline とか cat /proc/1/cgroup 実行すると、セッションを作るたびに新しくコンテナをポコポコ立ててくれてそうな雰囲気を感じられます。 ↩︎

Discussion