Azure Export for Terraform で既存の Azure リソースをエクスポートする

Azure にも少しずつ慣れてきた今日この頃。リソースを管理するのに IaC を導入したい欲が出てきました。
ところが、既に存在するプロジェクトに後から IaC を導入したいというケースってあるはずで、今正に筆者はこうした状況に置かれています。
ということで、Azure リソースを Terraform にエクスポートして管理できるようになるのかやってみます。

# IaC と Terraform

## IaC とは

モダンな開発環境にいる方は恐らく実践されているであろう、コードとしてのインフラストラクチャ(Infrastructure as Code)という考え方のことです。
これまでバージョン管理システムを通してソフトウェアの状態を管理することは一般的に行われてきましたが、パブリック クラウドの隆盛などに伴い、インフラストラクチャもソフトウェアと同じように管理し、それによるメリットを取り込む必要が生じました。
これに直接対応する考え方とプラクティスが IaC であり、後述する Terraform などのツール群により支援されます。

たとえば Microsoft は次のように解説しています。

> コードとしてのインフラストラクチャ (IaC) は、記述型モデルでのインフラストラクチャ (ネットワーク、コンピューティング サービス、データベース、ストレージ、接続トポロジなど) の管理を伴う主な DevOps プラクティスです。IaC を使用すると、チームは変更をより迅速かつ確実に開発してリリースできます。

--- _[Azure / クラウド導入フレームワーク / Ready / コードとしてのインフラストラクチャ](https://learn.microsoft.com/ja-jp/azure/cloud-adoption-framework/ready/considerations/infrastructure-as-code)_

## IaC を実現するツールたち

IaC と一口に言ってもそのポリシーやドメインなどによって色々な切り口が考えられます。
ここでは IaC の実現方法について述べてから、具体的なツールを挙げてみます。

### 2つの IaC

IaC には、大きな分類で2種類の実現方法があると言えます。
それはこれまでも広く使われてきたスクリプトによる自動化である**命令型IaC**と、最近よく使われている**宣言型IaC**です。

- 命令型IaC
> Bash や PowerShell などの言語でスクリプトを記述する必要があります。目的の結果を出すために実行されるコマンドを明示的に指定します。命令型デプロイを使用する場合は、必要に応じて依存関係の順序付け、エラー制御、リソースの更新を管理できます。
- 宣言型IaC
> 環境の外観を指定する定義を記述する必要があります。この定義では、目的の結果を実現する方法ではなく、目的の結果を指定します。ツールでは、現在の状態を調べて対象の状態を比較し、その違いを適用することで、結果を実現する方法を示します。

--- _[Azure / クラウド導入フレームワーク / Ready / コードとしてのインフラストラクチャ > ツール](https://learn.microsoft.com/ja-jp/azure/cloud-adoption-framework/ready/considerations/infrastructure-as-code#tooling)_

### 具体的なツール

ここでは幾つかの具体的なツールを取り上げ、簡単な比較をしてみます。

ツール|概要|IaCタイプ|いいところ|もう少しなところ
---|---|---|---|---
ARM(Azure Resource Manager) テンプレート|インフラストラクチャが記述されているJSONファイル|宣言型|Azure Native であり、ARM への変換等は必要ない|可読性や保守性が低い<br>Azure 専用
[Bicep](https://learn.microsoft.com/ja-jp/azure/azure-resource-manager/bicep/overview?tabs=bicep)|Azure リソースをデプロイするドメイン固有言語 (DSL) です。Bicep ファイルでは、デプロイするインフラストラクチャとそのプロパティを宣言します。|宣言型|ARM テンプレートに比べて遥かに可読性が高く、記述しやすい|Bicep(DSL) の学習コストがかかる<br>Azure 専用
[Terraform](https://www.terraform.io/)|クラウド インフラストラクチャのプロビジョニングおよび管理を行うためのオープン ソースの IaC (コードとしてのインフラストラクチャ) ツールです|宣言型|ARM テンプレートに比べて遥かに可読性が高く、記述しやすい<br>マルチクラウド・オンプレミス対応|Hashicorp Configuration Language(HCL) の学習コストがかかる
[Ansible](https://www.redhat.com/ja/technologies/management/ansible)|RedHatが開発・提供するオープンソースの構成管理ツール|命令型|命令型ではあるが専用の言語を学習する必要はなく参入障壁が少ない|YAML の学習コストがかかる<br>命令型なので取り回しに注意が必要なケースがある

## 何故 Terraform か

筆者的には下記のような点から Terraform がいいなと思っています。

- マルチクラウド対応なので、学習コストの効率がよい
    - Terraform で書ければとりあえず Azure/AWS/GCP などを統一的に扱える 
    - AWS CDK や Bicep ではそれぞれ対応するクラウドが異なるため、クラウドプラットフォームが変わるごとに学習コストがかかる
- Ansible は命令型なので扱いにくい面がある
    - 既存の YAML ファイルからあるリソースを削除したい場合、削除することを明示的に書かないと削除されない
    - Terraform のコードからリソースを削除したら、それを適用するときに当該リソースは削除される
- クラウドベンダーから独立している
    - 特定のベンダー傘下にある企業のプロダクトだった場合、対応内容に優劣がついたり、他のベンダーを排斥するような活動を展開する可能性がある

# Azure Export for Terraform

Azure で Terraform を使い始めるにあたって、既存のリソースがある環境に適用するにはどうすればよいのか調べました。
Microsoft はこのユースケースに対して [Learn/ Azure / 開発者 / Terraform / Azure Export for Terraform の概要](https://learn.microsoft.com/ja-jp/azure/developer/terraform/azure-export-for-terraform/export-terraform-overview) というドキュメントを公開しているので、これを読みつつ実践していきます。

## ドキュメントを読み必要なものを揃える

### インストール

WSL2 上で動作する Ubuntu 22.04 にインストールしていきたいと思います。

#### 前提条件

- Terraform は local backend を使用するものとします。
- terraform コマンドはインストール済みとします。
    筆者の環境では次のように構成しました。
    ```bash
    tfenv --version
    tfenv 3.0.0
    terraform --version
    Terraform v1.5.3
    on linux_amd64
    + provider registry.terraform.io/hashicorp/azurerm v3.65.0
    ```

#### インストール

[Learn / Azure / 開発者 / Terraform / Azure Export for Terraformr の概要 > インストール](https://learn.microsoft.com/ja-jp/azure/developer/terraform/azure-export-for-terraform/export-terraform-overview#installation)
GitHub ページを参照してインストールするようにと書いてあるので実行。

```bash
# run as root
curl -sSL https://packages.microsoft.com/keys/microsoft.asc > /etc/apt/trusted.gpg.d/microsoft.asc
ver=22.04
apt-add-repository https://packages.microsoft.com/ubuntu/${ver}/prod
apt-get install aztfexport
# run as user
aztfexport --version
# aztfexport version v0.13.1(c690456)
# disable telemetry
aztfexport config set telemetry_enabled false
```
無事にインストール完了して、今回はテレメトリの送信も無効にしました。

## 使用方法

今回は LogAnalytics ワークスペース と ストレージアカウントだけが存在するリソース グループをエクスポートしてみます。

### エクスポート

```bash
aztfexport resource-group rg-my-sample-group
terraform init --upgrade
terraform plan
=== < 中略 > ===
No changes. Your infrastructure matches the configuration.

Terraform has compared your real infrastructure against your configuration and found no differences, so no
changes are needed.
```

このように、変更は必要ありませんと出力されていれば、正常にエクスポートできていると判断できます。

### リソースを破棄する場合

エクスポートが正常に成功していれば通常の Terraform の使い方で破棄できます。
今回エクスポートしたリソース グループ全体を破棄する場合は次のコマンドを使います。
※事前に検証する場合は `terraform plan -destroy -out terraform.destroy.tfstate` などしましょう。

```bash
terraform destroy
```

### 制限事項について

Azure Export for Terraform の既知の制限事項について、[Learn / Azure / 開発者 / Terraform / Azure Export for Terraform のしくみ > 制限事項](https://learn.microsoft.com/ja-jp/azure/developer/terraform/azure-export-for-terraform/export-terraform-concepts#limitations)に記載があります。

今回は遭遇しなかったのですが
> AzureRM 内の特定のプロパティは書き込み専用であり、Azure Export for Terraform によって生成されるコードには含まれません

というようなこともあるようです。
この場合は、`terraform plan` してレビュー時に検出して、コードにプロパティ定義を追加するという対応になりそうです。
この状況に遭遇したらやってみたいと思います。

# まとめ

IaC やってみたいけど既存のリソース グループに導入するのハードル高いな！って思っていたら、そういうよくあるニーズはやはり応えられているものですね。
完璧ではないにしても、Azure Export for Terraform を使って始めの一歩を踏み出していきたいと思います。
また、Azure DevOps用プロバイダも定義されているようなので、Azure Pipelines なども管理できそうです。
この辺りの管理がどうあるべきなのかは、プロジェクト等の状況によると思いますが、機会があれば触ってみようと思います。