Azure OpenAI ServiceリソースをTerraformで作成する

## はじめに
[Azure OpenAI Service](https://azure.microsoft.com/ja-jp/products/cognitive-services/openai-service)を活用することで、様々な生成AIをセキュアに利用できます。

また、TerraformではAzureの様々なリソースの作成・変更をコードで構築でき、Azure OpenAI ServiceについてもTerraformで構築することができます。

そこで、今回はネットワーク制限（IP制限）を含めてAzure OpenAI ServiceリソースをTerraformで作成する方法をご紹介します。

## 前提
* Azureサブスクリプションの適用およびAzure OpenAI Serviceの利用申請は既に完了しているものとします。
    * Azure OpenAI Serviceの利用申請については下記ドキュメントをご参照ください。
        * https://learn.microsoft.com/ja-jp/azure/cognitive-services/openai/overview
* Terraformのインストールおよび環境構築は完了しているものとします。
    * Terraformのインストールについては下記ドキュメントをご参照ください。
        * https://learn.hashicorp.com/tutorials/terraform/install-cli
* Terraformでのデプロイ対象のリソースグループは既に作成済みであるものとします。

## TerraformでのAzure OpenAI Serviceリソースの定義

### azurermプロバイダーの定義
まずTerraformでAzureリソースを構成するには、`main.tf`で`azurerm`プロバイダーを定義する必要があります。

参考：https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs

```main.tf
terraform {
  # Azure Provider
  required_providers {
    azurerm = {
      source  = "hashicorp/azurerm"
      version = ">= 3.59.0"
    }
  }
}

# Configure the Azure Provider
provider "azurerm" {
  features {}
}
```

### Azure OpenAI Serviceアカウントの定義
Azure OpenAI Serviceのリソースを定義するには、`azurerm_cognitive_account`で定義します。

参考：https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/cognitive_account

設定項目と概要は下記の通りです。

* `name`: OpenAIリソース名
* `resource_group_name`: リソースグループ名
* `location`: リージョン
    * 本ドキュメントでは東日本を指定しています。
    * Azure OpenAI Serviceが提供されているリージョンは、[こちら](https://azure.microsoft.com/ja-jp/explore/global-infrastructure/products-by-region/?products=cognitive-services)で確認できます。
* `kind`: `OpenAI`（固定値）
* `sku_name`: `S0`（固定値）
* `custom_subdomain_name`: 任意のAzure OpenAI Serviceドメイン名
    * リソース作成後、OpenAIエンドポイントは`https://<任意のドメイン>.openai.azure.com/`となります。
* `network_acls`: ネットワークアクセス制御
    * `default_action`: `Deny`
        * ip_rulesで指定したアドレス以外からのアクセスを拒否します。
    * `ip_rules`: Azure OpenAI Serviceのアクセスを許可するIPアドレス一覧
        * ネットワークアクセス制御は必須の設定ではありませんが、こちらが無いとインターネット上どこからでもAzure OpenAI Serviceにアクセスできてしまうため、IP制限等でアクセス制限しておくことをおすすめします。

```main.tf
# Azure OpenAI Service Account
resource "azurerm_cognitive_account" "openai_sample" {
  name                  = "openai-acc"
  resource_group_name   = "my-resource-group"
  location              = "japaneast"
  kind                  = "OpenAI"
  sku_name              = "S0"
  custom_subdomain_name = "<任意のドメイン>"
  network_acls {
    default_action = "Deny"
    ip_rules       = ["xx.xx.xx.xx", "xx.xx.xx.xx"] # 許可するIPの一覧
  }
}
```

### Azure OpenAI Serviceデプロイメントの定義
上記のAzure OpenAI Serviceアカウントだけではチャット機能を使うことはできず、先ほど作成したアカウントに対し[モデルをデプロイ](https://learn.microsoft.com/ja-jp/azure/cognitive-services/openai/how-to/create-resource?pivots=web-portal#deploy-a-model)する必要があります。

こちらは、`azurerm_cognitive_deployment`で定義します。

参考：https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/cognitive_deployment

設定項目と概要は下記の通りです。

* `name`: デプロイ名
* `cognitive_account_id`: Azure OpenAI ServiceアカウントのID
    * 上記で定義したAzure OpenAI Service Accountと紐づくように、`azurerm_cognitive_account.<account名>.id`とします。
* `model`: モデル
    * `format`: `OpenAI`（固定値）
    * `name`: モデルの種類
    * `version`: バージョン
        * modelの`name`および`version`では、Azure OpenAI Serviceで利用するモデルの種類とバージョンを指定します。
        * モデルの種類やバージョンは、[こちらのドキュメント](https://learn.microsoft.com/ja-jp/azure/cognitive-services/openai/concepts/models)をご参照ください。
        * 本ドキュメントでは、`gpt-35-turbo`の最新バージョン`0613`を指定しています。
* `scale`
   * `type`: `Standard`（固定値）


```tf
# Azure OpenAI Service Deployment
resource "azurerm_cognitive_deployment" "chat" {
  name                 = "my-chat-model"
  cognitive_account_id = azurerm_cognitive_account.openai_sample.id
  model {
    format  = "OpenAI"
    name    = "gpt-35-turbo"
    version = "0613"
  }
  scale {
    type = "Standard"
  }
}
```


## リソースの確認
`terraform plan`および`terraform apply`でリソースの作成が完了したら、Azure PortalでAzure OpenAI Serviceが作成されています。

リソースの概要の「探索」からAzure OpenAI Studioにアクセスしてみましょう。

まず「デプロイ」を確認すると、Terraformで作成したモデルがデプロイされています。

![1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/398865/66d4fbf7-ee0a-c03d-be8f-1905ef105fdd.png)

また、「チャット」で先ほどデプロイしたモデルでチャットを行えることが確認できます。

![2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/398865/7e0ee3ec-0a64-43ef-a0b2-375d65f88851.png)

また、アクセス元のIPアドレスをTerraformで定義したアドレス以外でチャットしようとすると`Access denied due to Virtual Network/Firewall rules.`が返りIP制限も反映されていることが確認できます。

（IP制限の詳細についてはAzure Portalで作成したAzure OpenAI Serviceリソースの「ネットワーク」から確認できます。）

![3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/398865/6cfaa805-6caf-23ac-0196-4e79b4f2a878.png)

## サンプルコード
```main.tf
terraform {
  # Azure Provider
  required_providers {
    azurerm = {
      source  = "hashicorp/azurerm"
      version = ">= 3.59.0"
    }
  }
}

# Configure the Azure Provider
provider "azurerm" {
  features {}
}

# Azure OpenAI Service Account
resource "azurerm_cognitive_account" "openai_sample" {
  name                  = "openai-acc"
  resource_group_name   = "my-resource-group"
  location              = "japaneast"
  kind                  = "OpenAI"
  sku_name              = "S0"
  custom_subdomain_name = "<任意のドメイン>"
  network_acls {
    default_action = "Deny"
    ip_rules       = ["xx.xx.xx.xx", "xx.xx.xx.xx"]
  }
}

# Azure OpenAI Service Deployment
resource "azurerm_cognitive_deployment" "chat" {
  name                 = "my-chat-model"
  cognitive_account_id = azurerm_cognitive_account.openai_sample.id
  model {
    format  = "OpenAI"
    name    = "gpt-35-turbo"
    version = "0613"
  }
  scale {
    type = "Standard"
  }
}
```

## まとめ
今回はTerraformを使い、Azure OpenAI Serviceのリソース作成およびモデルのデプロイを行いました。

また、今回ご紹介したIP制限以外にも、プライベートエンドポイント接続などセキュアにAzure OpenAI Serviceを利用できる環境をTerraformで定義することができます。

## 関連リンク
* [azurerm_cognitive_account](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/cognitive_account)
* [azurerm_cognitive_deployment](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/cognitive_deployment)