REST APIからAzure OpenAIのネットワークACLを操作する

2024年7月31日掲載

キービジュアル

共通プラットフォーム開発本部の安澤です。今年4月からチームに参画し、主にドキュメントの更新監視ツールなど業務効率化ツールの開発を担当してきました。JavaScriptベースの言語での開発は今回が初めてであり、非常に苦戦しましたが、その分とても楽しい開発経験となりました。

今回の記事では、Azure 側で接続元IPアドレスによるアクセス制限を実施している場合に、Google Apps Script(以下、GAS)からREST APIを経由してAzure OpenAI(以下、AOAI)のネットワークACLを操作する方法について説明します

目次

この記事がおすすめな方
  • パブリックアクセスの全開放を避けつつ、AOAIリソースに対するリクエストツールを開発したい方
  • AOAIの送信元アクセス制御を動的に行いたい方

この記事に含まれる内容

  • GASからAOAIへのリクエスト時にIPアドレス制御をどのように行うか、その方法と課題について説明しています
  • Azure Resource ManagerのREST APIを活用した実装方法を実際のコードを含め紹介しています

背景

Azureの全リージョンに、各リージョンがサポートしているAOAIのモデルをデプロイし、そのモデルにリクエストを投げ、応答時間を計測するアプリの開発を行いました。その中で、AOAIのネットワークACLにGASが使用するIPアドレスを登録することが求められましたが、以下の点を考慮する必要がありました。

  1. AOAIリソースがパブリックアクセス可能な状態はセキュリティ的に問題がある。
  2. GASからのHTTPリクエストの送信元IPアドレスは広範囲のレンジから動的に割り当てられるため、それら全てを許可したままの状態もセキュリティ的に脆弱である。
  3. GASの送信元IPアドレスは一定時間間隔で切り替わる性質がある。そのため、ツール実行開始時点にネットワークACLに追加したとしても、実行時間が長い場合には途中で送信元IPアドレスが切り替わってしまい、そのたびにNSGの開閉を行う必要が生じる。これにより無駄な通信が増加する可能性がある。

これらの要素を総合的に考慮し、実行タイミングのみGoogleが公開しているIPアドレスレンジを全て許可することが適切であると判断し、それに基づいてネットワークACLを設定する方法を選択しました。

AOAIのネットワークACLの操作は現状、Azure portal、PowerShell、または Azure CLI がサポートされています。(詳細はIP ネットワーク ルールの管理をご確認ください) 。そのため、GASなどCLIコマンドが使えない状況で操作をするためには別のアプローチが必要となります。

解決策

Azure Resource ManagerのREST APIを使用してGASからAOAIのネットワークACLを操作する方法で実装を行いました。

GASで使用するIPアドレスの取得について

実装方法で使用するipRangeに関して、Google公式ドキュメントに記載のPythonコードをJavaScriptで書き直したものを使用しています。

実装方法

1. サービスプリンシパルを作成する

Azure Cloud Shellを開き、以下のコードを入力し実行します。これはGASからAOAIのリソースの操作を行うときに利用します。

# サービスプリンシパル名(任意のサービスプリンシパル名に変更)
SP_NAME=gas_response_app

# サービスプリンシパルを作成 
az ad sp create-for-rbac \
 --name ${SP_NAME}

作成時にプロンプトに表示されるJSONは下記のような形式になります。後で使用するのでメモに取っておいてください。

{
  "appId": "<YOUR_APPID>",
  "displayName": "gas_response_app",
  "password": "<YOUR_CLIENT_SECRET>",
  "tenant": "<YOUR_TENANT_ID>"
}

2. サービスプリンシパルにロールを付与する

サービスプリンシパルがAOAIリソースを操作できるように、「Cognitive Services 共同作成者」ロールを付与します。

  • assignee:appIdには1でメモしたjsonの情報を使用する
  • role:公式ドキュメントに記載されているIDを設定する
  • scope:AOAIを管理しているリソースグループを指定する(subscriptionIdはAzure上で確認してください)
# Cognitive Services 共同作成者ロールを設定
az role assignment create \
 --assignee $(appId) \
 --role "25fbc0a9-bd7c-42a3-aa1a-3b75d497ee68" \
 --scope "/subscriptions/$(subscriptionId)/resourceGroups/$(subscriptionId)"

3. GASスクリプトの作成

1. Azure へのアクセストークンを取得する

​​先ほどメモしておいたJSONの情報を使用し、アクセストークンの生成を下記で行います。

function getAzureAccessToken(tenantId, clientId, clientSecret) {
  const tokenUrl = `https://login.microsoftonline.com/${tenantId}/oauth2/v2.0/token`;
  const params = {
    method: "post",
    payload: {
      client_id: clientId,
      scope: `https://management.azure.com/.default`,
      client_secret: clientSecret,
      grant_type: "client_credentials",
    },
  };

  try {
    const response = UrlFetchApp.fetch(tokenUrl, params);
    const token = JSON.parse(response.getContentText());
    return token.access_token;
  } catch (e) {
    Logger.log("Error: " + e.message);
  }
}

2. リクエストを作成する

各リージョンに投げるリクエストを作成します。

  • accessToken: 1の「getAzureAccessToken」関数で取得したアクセストークンを指定する
  • subscriptionId:  AOAIを管理しているサブスクリプションを指定する
  • resourceGroupName: AOAIを管理しているリソースグループを指定する
  • accountName: 作成したAOAIの名前を指定する
  • ipRange: AOAIのネットワークACLに登録したいIPアドレスを指定する(IPアドレスは、[ { value: '8.8.4.0/24' },{ value: '8.8.8.0/24' }...]形式になるようにする)
function createRequestOptions(accessToken, subscriptionId, resourceGroupName, accountName, ipRange) {
  const url = `https://management.azure.com/subscriptions/${subscriptionId}/resourceGroups/${resourceGroupName}/providers/Microsoft.CognitiveServices/accounts/${accountName}?api-version=2021-04-30`;
  const headers = {
    Authorization: "Bearer " + accessToken,
    "Content-Type": "application/json",
  };

  const payload = {
    properties: {
      networkAcls: {
        defaultAction: "Deny",
        ipRules: ipRange,
      },
    },
  };
  const options = {
    method: "PATCH",
    headers: headers,
    payload: JSON.stringify(payload),
    muteHttpExceptions: true,
  };

  return {
    url: url,
    options: options,
  };
}
networkAclsに関して

ipRangeで指定したIPアドレスのみ許可され、その他のアドレスは拒否されます。payloadには、Azure Resource Manager テンプレートの内容を設定します。Azure上のAOAI概要にあるJSONビューから現在のテンプレートの内容を確認することができます。

3. AOAIにリクエストを行い、ネットワークACLを変更する

全リージョンのネットワークACLを変更する必要があるため、GASで提供されているメソッドのUrlFetchApp.fetchAllを使用します。

これは複数の URL への HTTP リクエストを一度に並列で実行することを可能にします。

  • paramsArray: subscriptionId、resourceGroupName、accountNameをJSON形式で渡す

function updateAzureOpenAiIpRange(tenantId, clientId, clientSecret, paramsArray, ipRange) {
  const accessToken = getAzureAccessToken(tenantId, clientId, clientSecret);

  const requests = paramsArray.map(function (account) {
    const subscriptionId = account.subscriptionId;
    const resourceGroupName = account.resourceGroupName;
    const accountName = account.accountName;

    const requestOptions = createRequestOptions(accessToken, subscriptionId, resourceGroupName, accountName, ipRange);
    return requestOptions;
  });

  const fetchRequests = requests.map(function (request) {
    return {
      url: request.url,
      method: request.options.method,
      headers: request.options.headers,
      payload: request.options.payload,
      muteHttpExceptions: request.options.muteHttpExceptions,
    };
  });

  const responses = UrlFetchApp.fetchAll(fetchRequests);
  return responses;
}

「updateAzureOpenAiIpRange」関数の呼び出しを行っている「main」関数では、ネットワークACLが反映されるまでに時間がかかるため、30秒のスリープ処理を追加しています。

おわりに

この記事ではREST APIを利用してAOAIのネットワークACLを操作する方法について詳しく説明しました。

今回の開発を通じて、ドキュメントを読み漁るだけでなく、先輩方とのコミュニケーションを通じてより柔軟に視点を変えることができたと感じました。

この記事が少しでも皆さんの役に立ち、技術の楽しさを感じていただけたら幸いです。これからも新しい発見や成長を楽しみながら取り組んでいきたいと思います。

最後までお読みいただきありがとうございました。

関連サービス

生成AIパッケージ

セキュアなAzure OpenAI Service環境をパッケージとして提供するサービスです。よりスムーズに生成AIの導入を実現することができます。

MSPサービス

MSP(Managed Service Provider)サービスは、お客さまのパブリッククラウドの導入から運用までをトータルでご提供するマネージドサービスです。

Microsoft Azure

Microsoft Azureは、Microsoftが提供するパブリッククラウドプラットフォームです。コンピューティングからデータ保存、アプリケーションなどのリソースを、必要な時に必要な量だけ従量課金で利用することができます。

おすすめの記事

条件に該当するページがございません