#004: Azure Developer CLI (azd)を用いたBicepのデプロイ

※本記事は、個人の意見および個人的活動で得た経験を記したものであり、会社を代表するものではありません

# 1. 実施概要

azdを使うことで、デプロイが楽に実行できるようになる。
azd を Github actions でも使えるので、構成ファイルは一元管理できる。
azd により、少なくともトップレベルのプロジェクトフォルダ構成が同じになる。
プロジェクトフォルダ構成が同じというのはいろんなプロジェクトやる上でとっても重要。

- Azureリソースのプロビジョニング
- アプリケーションデプロイ
- CI/CDパイプラインの構成

# 2. azdインストール（mac）

https://learn.microsoft.com/ja-jp/azure/developer/azure-developer-cli/install-azd?tabs=winget-windows,brew-mac,script-linux&pivots=os-mac

macの場合は“homebrew”でインストールできる。

```bash
sato@[17:29:15]:~% brew tap azure/azd && brew install azd
==> Auto-updating Homebrew...
Adjust how often this is run with HOMEBREW_AUTO_UPDATE_SECS or disable with
HOMEBREW_NO_AUTO_UPDATE. Hide these hints with HOMEBREW_NO_ENV_HINTS (see `man brew`).
==> Auto-updated Homebrew!
Updated 3 taps (homebrew/services, homebrew/core and homebrew/cask).
==> New Formulae
cortexso        ftnchek         litmusctl       pug             wcurl
cotila          h26forge        nerdfetch       ryelang
frizbee         kaskade         openbao         soapyhackrf
==> New Casks
blip                                     notchnook
crashplan                                replit
font-sixtyfour-convergence

You have 26 outdated formulae and 3 outdated casks installed.

==> Tapping azure/azd
Cloning into '/usr/local/Homebrew/Library/Taps/azure/homebrew-azd'...
remote: Enumerating objects: 209, done.
remote: Counting objects: 100% (32/32), done.
remote: Compressing objects: 100% (16/16), done.
remote: Total 209 (delta 14), reused 25 (delta 8), pack-reused 177
Receiving objects: 100% (209/209), 31.06 KiB | 3.88 MiB/s, done.
Resolving deltas: 100% (101/101), done.
Tapped 1 formula (17 files, 49.9KB).
==> Fetching azure/azd/azd
==> Downloading https://github.com/Azure/azure-dev/releases/download/azure-dev-c
==> Downloading from https://objects.githubusercontent.com/github-production-rel
######################################################################### 100.0%
==> Installing azd from azure/azd
==> Caveats
The Azure Developer CLI collects usage data and sends that usage data to Microsoft in order to help us improve your experience.
You can opt-out of telemetry by setting the AZURE_DEV_COLLECT_TELEMETRY environment variable to 'no' in the shell you use.

Read more about Azure Developer CLI telemetry: https://github.com/Azure/azure-dev#data-collection

azd may download binaries to ~/.azd/bin and, depending on how azd was used on this machine,
may download binaries to other users' home directories in their .azd/bin directory.
These binaries will need to be removed manually upon uninstall.
To remove such binaries from your home directory, run 'rm -rf ~/.azd/bin'.

==> Summary
🍺  /usr/local/Cellar/azd/1.9.5: 6 files, 30.8MB, built in 5 seconds
==> Running `brew cleanup azd`...
Disable this behaviour by setting HOMEBREW_NO_INSTALL_CLEANUP.
Hide these hints with HOMEBREW_NO_ENV_HINTS (see `man brew`).
sato@[17:30:00]:~% 
```
インストールされているazdコマンドのバージョンを確認。
```bash
sato@[17:33:45]:~% azd version
azd version 1.9.5 (commit cd2b7af9995d358aab33c782614f801ac1997dde)
sato@[17:33:57]:~% 
```

# 3. azdテンプレート

Azure Developer CLI (azd)テンプレートは、以下を含む標準コードリポジトリ。

- サンプルアプリケーションコード
- azd用の構成ファイル
- インフラストラクチャファイル（infraフォルダー＝IaC用）

この後でazdの初期化コマンドで実際にプロジェクトフォルダーを作成する。
プロジェクトフォルダーを作成する方法として、以下２つの方法が選択できる。

1. サンプルコード付きテンプレートでプロジェクトを作成
2. すでにコードが手元にある場合は既存コードをベースとしたプロジェクトを作成

今回はすでに作成済みのAzureのBicepファイルがあることを前提に、一旦上記1で”Minimal”という最小限のフォルダー構成とファイルだけのテンプレートでプロジェクトを初期化し、後で事前に作成していたrg.bicepとvnet.bicepをこちらに統合してみる。

## 3-1. azd初期化

初期化コマンドを実行すると、矢印キーで “Use code in the current directory” と “Select a template” を選択できる。既存コードはないので “Select a template” を選択。

```bash
sato@[6:57:54]:~/proj/azdsimple% azd init

Initializing an app to run on Azure (azd init)

? How do you want to initialize your app?  [Use arrows to move, type to filter]
  Use code in the current directory
> Select a template
```

次にテンプレートの選択を要求される。いろんなテンプレートが同様に矢印キーで選択できるが、ここでは “Minimal” を選択。

```bash
sato@[6:57:54]:~/proj/azdsimple% azd init

Initializing an app to run on Azure (azd init)

? How do you want to initialize your app? Select a template
? Select a project template:  [Use arrows to move, type to filter]
> Minimal

  Deploy Phoenix to Azure
  (Arize-ai/phoenix-on-azure)

  API Center Reference Sample
  (Azure-Samples/APICenter-Reference)

  Event Driven Java Application with Azure Service Bus on Azure Spring Apps
  (Azure-Samples/ASA-Samples-Event-Driven-Application)

  Static React Web App with Java API and PostgreSQL
  (Azure-Samples/ASA-Samples-Web-Application)

  Creative Writing Assistant: Working with Agents using Promptflow (Python Implementation)
  (Azure-Samples/agent-openai-python-prompty)

  Function Calling with Prompty, LangChain and Pinecone
  (Azure-Samples/agent-openai-python-prompty-langchain-pinecone)
```

フォルダー構成と最小限のプロジェクトファイルが作成される。
環境名を聞かれるので、開発環境を想定し “dev” を入力。

```bash
sato@[6:57:54]:~/proj/azdsimple% azd init

Initializing an app to run on Azure (azd init)

? How do you want to initialize your app? Select a template
? Select a project template: Minimal

  (✓) Done: Created a new azure.yaml file
  (✓) Done: Initialized git repository
  (✓) Done: Created minimal project files at: /Users/sato/proj/azdsimple

? Enter a new environment name: [? for help] dev
```

プロジェクトの初期化が完了。

```bash
sato@[6:57:54]:~/proj/azdsimple% azd init

Initializing an app to run on Azure (azd init)

? How do you want to initialize your app? Select a template
? Select a project template: Minimal

  (✓) Done: Created a new azure.yaml file
  (✓) Done: Initialized git repository
  (✓) Done: Created minimal project files at: /Users/sato/proj/azdsimple

? Enter a new environment name: dev

SUCCESS: New project initialized!
You can view the template code in your directory: /Users/sato/proj/azdsimple
Learn more about running 3rd party code on our DevHub: https://aka.ms/azd-third-party-code-notice
sato@[7:06:12]:~/proj/azdsimple% 
```

### 3-1-1. azd最小構成時のファイル・ディレクトリ構成

git関連のディレクトリ・ファイルを除外すると “Minimal” テンプレートのファイルツリーは以下の通り。

```bash
sato@[7:08:28]:~/proj/azdsimple% tree -a -I .git -I .gitignore
.
├── .azure
│   ├── config.json
│   └── dev
│       ├── .env
│       └── config.json
├── azure.yaml
└── infra
    ├── main.bicep
    └── main.parameters.json

4 directories, 6 files
sato@[7:08:34]:~/proj/azdsimple% 
```

## 3-2. bicepファイルをプロジェクトに統合

以前作成済みのrg.bicepとvnet.bicepをこのプロジェクトに統合し、azdコマンドを使ってデプロイできるようにし、かつ以前作成したGithub Actionsも統合・編集して、Github Actionsでもazdコマンドを利用するように変更してみる。

要するに今後は全てazdベースで作成していくことにする。

以前のプロジェクト構成は以下。もともと”infra”ディレクトリが無く、その配下の３フォルダーは直下にあった。中途半端にazdフォルダー構成を真似した直後だった。

```bash
sato@[7:00:44]:~/proj/RagSystem.bak% tree -a -I .git -I .gitignore
.
├── .github
│   └── workflows
│       ├── ShowAzAccount.yml
│       └── readme.md
├── LICENSE
└── infra
    ├── fed-id
    │   ├── create-githubactions-fed-id.sh
    │   ├── githubactions.json
    │   └── readme.md
    ├── resource-group
    │   ├── readme.md
    │   ├── rg.bicep
    │   └── rg.bicepparam
    └── virtual-network
        ├── readme.md
        ├── vnet.bicep
        └── vnet.bicepparam

7 directories, 12 files
sato@[7:01:00]:~/proj/RagSystem.bak% 
```

このフォルダー構成上で”azd init”コマンドを実行した結果が以下。オレンジ色の箇所が追加されたフォルダー＆ファイル。

```bash
sato@[7:02:47]:~/proj/RagSystem% tree -a -I .git -I .gitignore
.
├── .azure
│   ├── config.json
│   └── dev
│       ├── .env
│       └── config.json
├── .github
│   └── workflows
│       ├── ShowAzAccount.yml
│       └── readme.md
├── LICENSE
├── azure.yaml
└── infra
    ├── fed-id
    │   ├── create-githubactions-fed-id.sh
    │   ├── githubactions.json
    │   └── readme.md
    ├── main.bicep
    ├── main.parameters.json
    ├── resource-group
    │   ├── readme.md
    │   ├── rg.bicep
    │   └── rg.bicepparam
    └── virtual-network
        ├── readme.md
        ├── vnet.bicep
        └── vnet.bicepparam

9 directories, 19 files
sato@[7:02:49]:~/proj/RagSystem% 
```

“azd”コマンドでは”main.bicep”が実行されるので、今まで作成した”rg.bicep”と”vnet.bicep”をモジュールとして”main.bicep”で参照、統合する。

### 3-2-1. 環境変数定義と参照方法

azdコマンドの実行直後、.envファイルに定義されている環境変数が読み込まれ、Bicepテンプレート実行中に参照可能になる。

.envファイルは.azure\devフォルダーに保存されている。

```bash
sato@[0:04:21]:~/proj/RagSystem% tree -a -I .git -P .env
.
├── .azure
│   └── dev
│       └── .env <- これね
├── .github
│   └── workflows
└── infra
    ├── fed-id
    ├── resource-group
    └── virtual-network
```

デフォルトでは、”azd init” 時に指定した環境名しか定義されていないが、今回追加でリージョン、システム名を定義し、bicepから参照できるようにする。

”AZURE_SUBSCRIPTION_ID”は自分で設定していないので、一度”up”コマンドを実行すると自動的に追加される項目のようだ。

```bash
AZURE_ENV_NAME="dev"
AZURE_LOCATION="japaneast"
AZURE_SUBSCRIPTION_ID="xxx"
SYSTEM_NAME="RagSystem"
```

ここで定義した環境変数は、bicepファイルから直接参照できず、parameterファイルからのみ参照可能。”main.bicepparameters.json”ファイルでパラメータに環境変数を代入することで、bicepから参照できるようになる。

```bash
{
    "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#",
    "contentVersion": "1.0.0.0",
    "parameters": {
      "SystemName": {
        "value": "${SYSTEM_NAME}" 
      },
      "EnvironmentName": {
        "value": "${AZURE_ENV_NAME}"
      },
      "Location": {
        "value": "${AZURE_LOCATION}"
      }
    }
}
```

### 3-2-2. パラメータファイルをjsonフォーマットからbicepパラメータファイルへ移行

“azd init” 実行時、デフォルトでは”main.bicepparam.json”というjsonフォーマットのパラメータファイルが生成される。せっかくbicep用のパラメータ定義ファイル（”*.bicepparam”という拡張子のファイル）が使えるし、jsonフォーマットよりもシンプルなので、”main.bicepparameters.json”を”main.bicepparam”に移行する。

以下、bicepparamファイルにしたパラメータファイル。

定義自体はすごくシンプルになったが、環境変数の参照方法がイマイチ。。

```bash
using './main.bicep'

param systemName = readEnvironmentVariable('SYSTEM_NAME')
param environmentName = readEnvironmentVariable('AZURE_ENV_NAME')
param location = readEnvironmentVariable('AZURE_LOCATION')
```

### 3-2-3. rg.bicepを統合

“rg.bicep”をモジュールとして”main.bicep”に取り込む。

”rg.bicepparam”ファイルに定義していたパラメータを”main.bicepparam”ファイルに統合し、”rg.bicepparam”ファイルは破棄する。

システム名と環境識別子の組み合わせでリソースグループ名を作成し、tag情報も渡すようにしてみた。

```bash
////////////////////////////////////////////////////////////
// Definitions of common variables for all the resources.

@description('Tags that should be applied to all resources.')
var tags = {
  system: systemName
  env: environmentName
  lastDeployed: deploymentDate
}

@description('The name of the resource group. This is used for specifying the scope of the module of the resources that is created in the resource group.')
var rgName = '${systemName}-${environmentName}'

////////////////////////////////////////////////////////////
// Definitions of the resource group.

@description('Create a Azure Resource Group by bicep template with some tags.')
module ragRg './resource-group/rg.bicep' = {

  scope: subscription()
  name: 'ragRg'

  params: {
    rgName: rgName
    location: location
    tags: tags
  }
}
```

### 3-2-4. 共通変数の定義

変数定義について少し言及。

- “tags”：これから作成する各リソースで共通で参照するので、”module”の外で定義。
- “rgName”：”<システム名>-<環境識別子>としたいので、変数を定義してパラメータ値を組み合わせて自動作成するように変更。

これらは、このリソースグループ内に作成するすべてのリソース共通の変数となり得る。

### 3-2-5. スコープ定義

“rg.bicep”ファイルにもtargetscopeを定義しているが、モジュール参照側にも必要らしいので”subscription”として定義。

”name”は必須プロパティであるため定義を追加。名前はragRgであることがわかれば良いので、モジュール名と同じ文字列を定義した。

### 3-2-6. vnet.bicepを統合

次に、”vnet.bicep”ファイルをモジュールとして”main.bicep”に取り込む。

この仮想ネットワークには、２つのサブネット”AdminSubnet”と”AzureBastionSubnet”をデフォルトで作成している。この２つのサブネットは、閉域網を構成する多くのシステムで共通で利用することになる可能性が高いため、仮想ネットワークのコンポジットモジュールとして定義する。

その他に必要なサブネットがある場合は、今後個別に追加する方針とした。

```bash
module RagVNet './network/vnet.bicep' = {
  scope: resourceGroup(rgName)
  name: 'RagVNet'

  params: {
    location: location
    tags: tags
    
    // for the RagVNet that is a main virtual networkf for the RagSystem.
    ragVNet_name: ragVNet_name
    ragVNet_addressPrefix: ragVNet_addressPrefix
    ragVNet_encryptionEnabled: ragVNet_encryptionEnabled
    ragVNet_ddosProtectionEnabled: ragVNet_ddosProtectionEnabled

    // for the AdminSubnet
    adminSubnet_name: adminSubnet_name
    adminSubnet_addressPrefix: adminSubnet_addressPrefix
    adminSubnet_privateEnabled: adminSubnet_privateEnabled
    adminSubnet_privateEndpointNetworkPolicies: adminSubnet_privateEndpointNetworkPolicies

    // for the BastionSubnet
    bastionSubnet_name: bastionSubnet_name
    bastionSubnet_addressPrefix: bastionSubnet_addressPrefix
    bastionSubnet_privateEnabled: bastionSubnet_privateEnabled
    bastionSubnet_privateEndpointNetworkPolicies: bastionSubnet_privateEndpointNetworkPolicies
  }

  dependsOn: [
    ragRg
  ]
}
```

# 4. 統合結果のソースコード

ここまでの全容は以下を参照されたし。

https://github.com/masatoshisato/RagSystem/tree/v1.0.1/infra

# 5. VSCodeのプラグイン

これもあるみたいなので、イントール。azd関連の言語仕様をVSCodeでチェックできるようになる。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/466853/d609a862-536c-edff-5d7f-0544edeb1b3d.png)