Azure Terrafy を使って既存の Azure リソースを Terraform 化しよう!

[Azure Advent Calendar 2021](https://qiita.com/advent-calendar/2021/azure) 9日目です。

## はじめに

Infrastrucure as Code を始めると、GitHub 上で Pull Request ベースであれば、関連する Issue やなぜこの Pull Request が作られたのか。また誰が作り、誰がマージしたのか？またマージできる人が誰なのかといった事業の根幹となるインフラ部分について、議論の歴史を残しながら技術的意思決定と同時に構築することができます。Infrastructure as Code は、単にインフラの自動化やデプロイのためではなく、根幹となるインフラリソースをチームで議論を残せるかつ監査観点で統制をとる方法として有効な仕組みです。

さて、既存の Azure リソースから Infrastrucure as Code をはじめる場合に Terraform の活用が選択肢の一つしてあると思います。ただ、すでに Azure で運用している場合や他のチームとの連携など含めると、始めるに当たり壁があると感じられることが多いかなと思います。 そこで既存の Azure リソースを Terraform の管理下に置くためのツールとして、[Azure Terrafy](https://github.com/Azure/aztfy) がありますので、このツールについて社内の Hackthon で知り、 Contribute していたのでここの場で紹介します。

## Azure Terrafy とは

Azure Terrafy は、 Resource Group 単位でかつ、ほしいリソースを利用者が選んで `tf` ファイルや `tfstate` ファイルを生成してくれます。既存の Azure リソースを Terraform を始める上で、様々な壁があります。 Azure Terrafy は、次のような困った場合に活躍できそうです。

- 他チームやプロダクトに影響を与えたくない
- 他のリソースがある中で Try & Error がしにくい
- 限定された範囲で portal 上でポチポチして試したものを export して、書き方がわかった状態から管理しているリポジトリにコミットしたい

これは、 Azure Terrafy 自身の Goal が `terraform plan` した際に **no diff** になることを目指して作られており、tf ファイルを利用できるようにするために ARM temaple の依存関係の考慮を含めて生成してくれるからです。
 
## How to install

[Releases](https://github.com/Azure/aztfy/releases) からダウンロードして利用します。( Homebrew などに対応したい。。

その他、利用する前に Terraform の install と [Azure CLI](https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/guides/azure_cli) が必要です。

## Usage

```console
$ aztfy
aztfy [option] <resource group name>

  -o string
        Specify output dir. Default is a dir under the user cache dir, which is named after the resource group name
  -v    Print version
```

リソースグループを指定して実行すると、 README の demo にあるようにユーザー自身でOS の user cache に `tf` と `tfstate` また backup を生成してくれます。

## デモ

以前、[ブログ](https://koudaiii.medium.com/introducing-distributed-load-testing-using-locust-on-aks-4b998f68429d) で書いた AKS を構築する script がある [distributed-load-testing-using-locust-on-aks](https://github.com/koudaiii/distributed-load-testing-using-locust-on-aks) を使って試したいと思います

```console
$ git clone https://github.com/koudaiii/distributed-load-testing-using-locust-on-aks
$ cd distributed-load-testing-using-locust-on-aks
$ script/bootstrap
```

デフォルトだと `DistributedLoadTesting` という名前の Resource Group が作られるようになっています。こちらを実際に aztfy で Terraform 化を行ってみましょう。

```console
$ mkdir sample
$ aztfy -o sample DistributedLoadTesting
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/18843/1cb868e0-c339-a9c5-2ed5-c7b31f43df79.png)

初期化が終わると作られたリソースが表示されます。

```console
   Azure Terrafy 
  
     DistributedLoadTesting   Possible resource type(s): azurerm_kubernetes_cluster_node_pool                                                                                      
                                                                                                                                                                                   
    3 items                                                                                                                                                                        
                                                                                                                                                                                   
    /subscriptions/xxxx-xxxx/resourceGroups/DistributedLoadTesting/providers/Microsoft.ContainerService/managedClusters/LocustOnAKS                     
    (Skip)                                                                                                                                                                         
                                                                                                                                                                                   
  │ /subscriptions/xxx-xxx/resourceGroups/DistributedLoadTesting/providers/Microsoft.ContainerService/managedClusters/LocustOnAKS/agentPools/nodepool1
  │ (Skip)                                                                                                                                                                         
                                                                                                                                                                                   
    /subscriptions/xxx-xxx/resourceGroups/DistributedLoadTesting                                                                                      
    (Skip)                                                                                                                                                                         
                                                                                                                                                                                   
                                                                                                                                                                                   
                                                                              
```

画面下にキー操作の案内があります。

```console
    ↑/k up • ↓/j down • / filter • w save • e show error • r show recommendation • q quit • ? more  
```

キー操作をもとに自身がほしいリソースを移動して、Enter 押すことで書き込めます。**各 Azure リソースの Terraform リソースタイプ** と **任意の名前** を、`<リソースタイプ>.<名前>`（例：`azurerm_linux_virtual_machine.example`）の形で書く必要があります。この場合、 Terraform のリソースタイプについては、 Terraform の公式ページから取ってくる必要があります。または、 `r` を押すと、候補がでてきます。

```
     DistributedLoadTesting   Possible resource type(s): azurerm_kubernetes_cluster_node_pool  # <--- 各 Azure リソースの Terraform リソースタイプ                                                                                   
```

※ユーザーは `r` を押すことで、選択したインポートアイテムに可能なリソースタイプを確認することができますが、これは100%正確というわけではないことに注意してください。(こちらは、 Azure のプロバイダーの Terrafom に依存し、さらに Terraform は ARM Template という Azure のテンプレートに依存するためです。)

```console
   Azure Terrafy 
  
     DistributedLoadTesting                                                                                                                                                        
                                                                                                                                                                                   
    3 items                                                                                                                                                                        
                                                                                                                                                                                   
  │ /subscriptions/xxx-xxx/resourceGroups/DistributedLoadTesting/providers/Microsoft.ContainerService/managedClusters/LocustOnAKS                     
  (Skip)                                                                                                                                              
                                                                                                                                                                                   
    /subscriptions/xxx-xxx/resourceGroups/DistributedLoadTesting/providers/Microsoft.ContainerService/managedClusters/LocustOnAKS/agentPools/nodepool1
    azurerm_kubernetes_cluster_node_pool.sample                                                                                                                                    
                                                                                                                                                                                   
    /subscriptions/xxx-xxx/resourceGroups/DistributedLoadTesting                                                                                      
    azurerm_resource_group.sample                                                                                                                                                  
                                         
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/18843/6e94f096-bf05-605f-a4b7-2f5a53e9f099.png)

出力したいリソースが決まったら `w` を押します。ここから `aztfy` の仕組みですが、その後 `aztfy` は、中で `terraform import` や `terraform add -from-state` を実行し、生成ファイルの準備をします。最後に、ARMテンプレートを活用して、各リソース間に依存関係を解決します。最後に Terraform テンプレートが生成されます。

```console
   Azure Terrafy 
  
  Terraform state and the config are generated at: sample
  
  Press any key to quit
```

生成されたファイルが如何のようにできています。

```console
[2021/12/09 22:06:03] ~/src/github.com/Azure/aztfy (main)(LocustOnAKS)
$ ll sample 
total 48
drwxr-xr-x   9 sakabekodai  staff   288B Dec  9 22:05 .
drwxr-xr-x  22 sakabekodai  staff   704B Dec  9 21:49 ..
drwxr-xr-x   3 sakabekodai  staff    96B Dec  9 21:50 .terraform
-rw-r--r--   1 sakabekodai  staff   1.1K Dec  9 21:50 .terraform.lock.hcl
-rw-------   1 sakabekodai  staff   221B Dec  9 22:05 .terraform.tfstate.lock.info
-rw-r--r--   1 sakabekodai  staff   741B Dec  9 22:05 main.tf
-rw-r--r--   1 sakabekodai  staff   161B Dec  9 21:49 provider.tf
-rw-r--r--   1 sakabekodai  staff   3.3K Dec  9 22:05 terraform.tfstate
-rw-r--r--   1 sakabekodai  staff   2.4K Dec  9 22:05 terraform.tfstate.backup
```

main.tf

```tf
resource "azurerm_kubernetes_cluster_node_pool" "sample" {
  kubernetes_cluster_id = "/subscriptions/xxx-xxxx/resourceGroups/DistributedLoadTesting/providers/Microsoft.ContainerService/managedClusters/LocustOnAKS"
  mode                  = "System"
  name                  = "nodepool1"
  os_disk_type          = "Ephemeral"
  vm_size               = "Standard_DS3_v2"
  depends_on = [
    # Depending on "/subscriptions/xxx-xxx/resourceGroups/DistributedLoadTesting/providers/Microsoft.ContainerService/managedClusters/LocustOnAKS", which is not imported by Terraform.
  ]
}

resource "azurerm_resource_group" "sample" {
  location = "eastus"
  name     = "DistributedLoadTesting"
}
```

provider.tf

```tf
 terraform {
  required_providers {
    azurerm = {
      source = "hashicorp/azurerm"
      version = "2.88.0"
    }
  }
}

provider "azurerm" {
  features {}
}
```

このように選択した部分だけが template 化されております。最後に `terraform plan` で **no diff** を確認します。

```console
$ terraform plan
azurerm_resource_group.sample: Refreshing state... [id=/subscriptions/xxx-xxx/resourceGroups/DistributedLoadTesting]
azurerm_kubernetes_cluster_node_pool.sample: Refreshing state... [id=/subscriptions/xxx-xxx/resourceGroups/DistributedLoadTesting/providers/Microsoft.ContainerService/managedClusters/LocustOnAKS/agentPools/nodepool1]

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.
```

🎉

## さいごに

一連の作業については、[Demo 動画](https://asciinema.org/a/iPTGS6E2CSxpYPtbPQhWmxLdu) でみるとよりイメージがしやすいです。(こちらは README からも見られます) その他制約事項があるようなので、 [README](https://github.com/Azure/aztfy/blob/af3fb15540f5f353dc42e0fe1cc9652ffa8d8f0a/README.md) をご確認ください。 Azure Terrafy は出たばかりで、今後利用者が増えていく中で便利になっていくと思います。一緒に Contribute していきましょう！