🥂

Cloud RunからAzure OpenAIを呼び出す(Google Cloud→Azureの認証をIAMで通す)

2024/09/09に公開

以前書いた記事のAzure版になります。
https://zenn.dev/ncdc/articles/a59403b5799d06

はじめに

前回、Google CloudのCloud RunからAWSのBedrockを呼び出してアプリに組み込んだので、今回はAzure OpenAIを呼び出してGoogle Cloud上でGPT-4oを使います。

AWSの時と同様に、セキュリティを考慮してAPIキーなどという危険物には頼らずIAMで認証を通します。

IAMで、Google Cloud → Azureの認証を通す

方針

AzureとGoogle Cloudは別サービスですので、認証を突破しない限り利用する事はできません。
一番単純なのはAzure OpenAIのAPIキーを発行して使用することですが、セキュリティリスクが高いのでよろしくないですよね。アクセスキーお漏らしでセキュリティ事故になっている会社が毎月のように発生している昨今の状態を考えるとAPIキーの利用は大きなリスクを感じてしまいます。
ではどうするかですが、Google CloudのIAMに対してAzureのIAMを紐づければよいわけです。AWSでは出来たのだからAzureでもできるでしょう。と思ってやってみたら出来ました。

やってみた

Azure側でアプリを登録する

  • AzureのコンソールからEntraID(旧AzureAD)のページを開き、左のメニューから「アプリの登録」を選びます。

  • 新規作成は、とりあえず適当に名前だけつけて登録すます。

  • 登録されたアプリを開き、左のメニューから「証明書とシークレット」を選択します。証明書でもシークレットでもなく「フェデレーション資格情報」タブを選び「資格情報の追加」を選択します。

  • 資格情報を以下のように入力して保存します。

    • フェデレーション資格情報のシナリオ:「その他の発行者」
    • 発行者:「https://accounts.google.com
    • サブジェクト識別子:Cloud Runに紐づけられているサービスアカウントの「一意のID」
    • 名前: 好きな名前
    • 説明: 任意(空でもよい)
    • 対象ユーザー: 既定値のままが良さそうなので「api://AzureADTokenExchange」
  • EntraIDを離れてAzure OpenAIに移動します。GoogleCloudから呼び出したいOpenAIのサービスの「アクセス制御 (IAM)」を開き、先程作成したアプリに「Cognitive Service OpenAI User」の権限を付与します。

Azure側の設定は以上です。

認証用のコードを書く

続いて、認証情報を取得するためのコードを書いていきます。
Google CloudのメータデータサーバーからIDトークンを取得するまでは、AWSでやった時と同じです。一応参考用に公式ドキュメントを張っておきます。
https://cloud.google.com/docs/authentication/get-id-token?hl=ja

その後、Azure SDKのClientAssertionCredentialクラスを使って認証情報を取得します。
https://learn.microsoft.com/ja-jp/python/api/azure-identity/azure.identity.clientassertioncredential?view=azure-python

必要なライブラリのインストール

azure-identitygoogle-authをインストールします。

rye add azure-identity google-auth
rye sync

Azureの認証情報を取得

Google CloudのメータデータサーバーからIDトークンを取得しそのIDトークンからAzure(EntraID)の認証情報を取得する関数を書きます。

from google.auth import compute_engine
import google.auth.transport.requests as grequests
from azure.identity import ClientAssertionCredential

def get_azure_creds(app_user_uri: str, tenant_id: str, client_id: str) -> ClientAssertionCredential | None:
    '''
    Google CloudのメタデータサーバーからIDトークンを取得するし、
    Azure OpenAIへのaccess tokenを取得する
    '''
    def _get_google_token() -> str:
        '''Google CloudのIDトークンを取得'''
        request = grequests.Request()
        g_credentials = compute_engine.IDTokenCredentials(
            request=request,
            target_audience=app_user_uri,
            use_metadata_identity_endpoint=True
        )
        g_credentials.refresh(request)
        return g_credentials.token

    try:
        # AzureのCrendetialを取得
        az_creds = ClientAssertionCredential(
            tenant_id=tenant_id,
            client_id=client_id,
            func=_get_google_token,
        )
        return az_creds
    except Exception as e:
        print(f"Error: {e}")
        return None

実際に使う時は次のように呼び出します。AZURE_APP_USER_URIには、先ほど作成したEntraIDのアプリの対象ユーザーつまり「api://AzureADTokenExchange」を設定してください。AZURE_TENANT_IDAZURE_CLIENT_IDはそれぞれアプリのテナントIDとクライアントIDです。

from pydantic.v1 import SecretStr
from langchain_openai import AzureChatOpenAI

az_creds = get_azure_creds(
    app_user_uri=AZURE_APP_USER_URI,
    tenant_id=AZURE_TENANT_ID,
    client_id=AZURE_CLIENT_ID,
)
az_token: SecretStr = SecretStr(az_creds.get_token(
    "https://cognitiveservices.azure.com/.default"
).token if az_creds else "")
llm = AzureChatOpenAI(
    azure_ad_token=az_token,
    ... # その他の引数を指定
)

azure_ad_tokenstrで入れても動くのですが、何故かpydantic.v1.SecretStrが型指定されているので従いました。なんで今どきv2でなくてv1なのでしょうね?

実行結果

ということで、なんとか無事にCloudRunからOpenAIを実行することが出来できたので、AzureとGoogle CloudとAWSの生成AIを同時に実行してみました。

同様の方法で、 CloudRun → OpenAI 以外のサービスでも Google Cloud → Azure の認証に応用出来るかと思われます。

ただし、Azureには何故かIAMで認証出来ないサービスもあるので注意が必要です。
https://zenn.dev/ncdc/articles/7a4d2e750d75b1

NCDCエンジニアブログ

Discussion