Secretの期限切れから解放されるためにmanaged identityを使いたい

このエントリは2025/04/24現在の情報に基づいています。将来の機能追加や変更に伴い、記載事項からの乖離が発生する可能性があります。

問い合わせ

Azure OpenAI Serviceをオンプレミスから利用している人から以下のような問い合わせがあった。

LangChain (4JではなくPython) で作成したオンプレミスアプリからAzure OpenAI Service (以下、AOAI) を呼び出している。Microsoft Entra ID (以下、Entra ID) にアプリを登録し、シークレットを作成して、AOAIに対するRBACを構成している。最近このシークレットがまもなく有効期限を迎える旨のメールが届いた。ただ、現在使っているSubscriptionが参加するEntra IDの管理者権限がないため、シークレットの新規作成が出来ない状態である。そこで

  1. Entra IDの権限を割り当ててもらって、シークレットを作成できるようにするにはどうしたらよいか?
  2. 今後の手間を考慮し、可能ならシークレットを意識しなくてよいようにしたい。この場合、どのような選択肢が考えられるか?

アプリ登録とシークレット

IdPであるEntra IDにアプリを登録して、シークレットを作成し、当該シークレットとClient IDを使ってBearerトークンを取得する必要がある。

アプリケーションを Microsoft Entra ID に追加する方法と理由 / How and why applications are added to Microsoft Entra ID
https://learn.microsoft.com/entra/identity-platform/how-applications-are-added

で、このシークレットが期限切れを迎えるのでどうしたものか、ということのよう。Portalを確認すると、シークレットは再作成はできないので、新規にシークレットを追加するしかない。ただ彼らには新規作成の権限がないので、詰んだ状況である、ということのよう。

まずやるべきは

権限がないと始まらない。Subscriptionの権限ではなく、Entra IDの権限が必要なので、以下のいずれかを実行してもらう必要がある。

  • Application AdminもしくはCloud Adminのロールを付与(実際には管理者に割り当ててもらう)
  • もしくは、Application Developerロールを付与した上で、登録されたアプリのOwnerとして登録(実際には管理者に割り当ててもらう)

これでシークレットを作成できるようになる。前者のほうが簡単ではあるが、権限が強いので注意が必要。

シークレットの管理はしたくない

続いて、シークレットの管理をしなくて済むようにするにはどうしたらよいか、というお題。これはオンプレミス(もしくは3rd party cloud)からのアクセスでも使える、User assigned managed identityの一択である。

ユーザー割り当てマネージド ID の管理 / Manage user-assigned managed identities
https://learn.microsoft.com/entra/identity/managed-identities-azure-resources/how-manage-user-assigned-managed-identities

これを使い、AOAIのRBACを構成しておく。

Azure OpenAI Service のロールベースのアクセス制御 / Role-based access control for Azure OpenAI Service
https://learn.microsoft.com/azure/ai-services/openai/how-to/role-based-access-control

Chat completion APIを使うだけなら、Cognitive Services OpenAI ユーザー (Cognitive Services OpenAI User) ロールを割り当てればOK。

試してみる

LangChainで実際に動作確認してみる。サンプルでは同期APIを使っている。

1) 環境変数

利用にあたって、以下の環境変数

AZURE_CLIENT_ID
AZURE_TENANT_ID

を定義しておく。その他、LangChainのために

AZURE_OPENAI_ENDPOINT
AZURE_OPENAI_DEPLOYMENT_NAME
OPENAI_API_VERSION

を定義しておく。

2) 認証

Javaなどと同様、DefaultAzureCredentialを使う。

default_credential = DefaultAzureCredential(
    managed_identity_client_id=os.environ['AZURE_CLIENT_ID’]
)

token = default_credential.get_token(
    'https://cognitiveservices.azure.com/.default',
    tenant_id=os.environ['AZURE_TENANT_ID']
).token

user assigned managed identityを使っているので、明示的にIDのClient IDを指定する必要がある(system assigned managed identityであれば不要だが、今回はオンプレミスからの実行なので、system assigned managed identityは使えない)。

また、bearer token取得時にはスコープ以外にtenant_idを指定しているが、これは通常であれば環境変数に指定しているはずなので、本来は指定不要。ただし、他のEntra IDテナントも使っているような場合は明示的に指定しておくことを推奨する。

3) AOAIへの接続

LangChainでAOAIにアクセスする場合、AzureChatOpenAIAzureAIChatCompletionsModelの2種類が利用できる。前者は、OpenAI SDK、後者はAzure AI Foundry SDKで実装されている。

AzureChatOpenAI
https://python.langchain.com/api_reference/openai/chat_models/langchain_openai.chat_models.azure.AzureChatOpenAI.html
AzureAIChatCompletionsModel
https://python.langchain.com/api_reference/azure_ai/chat_models/langchain_azure_ai.chat_models.inference.AzureAIChatCompletionsModel.html

今回は前者を使う。AzureChatOpenAIのお作法はドキュメントの通りだが、azure_ad_tokenの部分だけ通常のAPI Keyの場合と異なる点に注意が必要。

model = AzureChatOpenAI(
    azure_endpoint=os.environ['AZURE_OPENAI_ENDPOINT'],
    model_name=os.environ['AZURE_OPENAI_DEPLOYMENT_NAME'],
    openai_api_version=os.environ['OPENAI_API_VERSION'],
    azure_ad_token=token,
)

messages = [
    SystemMessage('Translate the following from English into Spanish:'),
    HumanMessage('hi!'),
]

response = model.invoke(messages)
if response:
    print(response.content)  # ¡Hola!
else:
    print('No response received.')

これでおしまい。

動作確認

今回はPython 3.12で動作確認した。

(.venv) $ python sample.py 
¡Hola!

コメントを残す

このサイトはスパムを低減するために Akismet を使っています。コメントデータの処理方法の詳細はこちらをご覧ください