コード化していなくても諦めるな！terraformerを使って既存環境のインフラコード化 ！！

本記事は[サムザップ #1 Advent Calendar 2020](https://qiita.com/advent-calendar/2020/sumzap1)の4日目の記事です。

# はじめに

SREの島田です。
弊社ではインフラのコード管理に[terraform](https://www.terraform.io/)というツールを使用しているのですが、既に構成管理を使用しないで作成された環境とかってよくありますよね。

- 昔ながらの手動で作成されたリソース
- terraformとは別の構成管理で作成されたリソース
- 手動で作成した方が楽なリソース
- 前任者が構築していて、どのように構築したのか分からないリソース

今回の記事ではそのような既存のリソースを簡単にインポートし、インフラコード化するやり方をご紹介致します。
ここでは実際に[datadog](https://www.datadoghq.com/ja/)プロバイダーを例として取りあえげます。
※ datadogのdashboardは見た目も重要なので、最初は手作業で構築することが多いため

# terraform import

まずはじめに思い浮かぶのが、 `terraform import` です。

とりあえず、main.tfとprovider.tfを用意します。
main.tfの中身は適当で `required` な項目だけ用意しておきます。

```
$ tree
.
├── main.tf
└── provider.tf
```

main.tf

```
resource "datadog_dashboard" "my_dashbord" {
  title       = "test"
  layout_type = "ordered"
  widget {}
}
```

provider.tf

```
provider "datadog" {
  api_key = var.api_key
  app_key = var.app_key
}
```

ファイルが用意できたら、環境変数にキー情報をセットし、`terraform init` をして初期化します。
その後 `terraform import`を行えばインポート完了です。
datadogのダッシュボードのリソースIDはURLから取得できます。(下記の画像の赤枠部分)
![スクリーンショット 2020-12-01 15.31.32.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/210637/7ae510cf-5c36-1028-afcd-137b5598f18d.png)


```
$ terraform import datadog_dashboard.my_dashbord [リソースID]
```
実行して下記のように出力されれば成功！！

```
datadog_dashboard.my_dashbord: Importing from ID "[リソースID]"...
datadog_dashboard.my_dashbord: Import prepared!
  Prepared datadog_dashboard for import
datadog_dashboard.my_dashbord: Refreshing state... [id=[リソースID]]

Import successful!

The resources that were imported are shown above. These resources are now in
your Terraform state and will henceforth be managed by Terraform.
``` 

`terraform.tfstate`ファイルができているのが確認できると思います。
※ 実行前にある場合は中身が更新されます。

```
$ tree
.
├── main.tf
├── provider.tf
└── terraform.tfstate
```

中身を確認すると先程インポートしたリソースの情報が、保存されていることが確認できると思います。
ここでリソースの情報がインポートできたので、`terraform plan`を実行してみましょう。

```
$ terraform plan
Refreshing Terraform state in-memory prior to plan...
The refreshed state will be used to calculate this plan, but will not be
persisted to local or remote state storage.

datadog_dashboard.my_dashbord: Refreshing state... [id=[リソースID]]

------------------------------------------------------------------------

An execution plan has been generated and is shown below.
Resource actions are indicated with the following symbols:
  ~ update in-place

Terraform will perform the following actions:

  # datadog_dashboard.my_dashbord will be updated in-place
  ~ resource "datadog_dashboard" "my_dashbord" {

~~ 差分表示 省略 ~~

Plan: 0 to add, 1 to change, 0 to destroy.

------------------------------------------------------------------------

Note: You didn't specify an "-out" parameter to save this plan, so Terraform
can't guarantee that exactly these actions will be performed if
"terraform apply" is subsequently run.

```

こんな感じで現在の適当に作ったmain.tfの情報と、importしたリソースの情報の差分が表示されます。
この差分がなくなるようにmain.tfを修正すれば、既存リソースの取り込み完了です！！

....

ちょっとまて！ 流石に差分取り込むのしんどいだろ！！
と思わずツッコミたくなりますよね。私も漏れなくツッコミました。
何がimportだと、全然簡単に取り込めないやん。って感じですよね。

そうなんです、`terraform import` だけだと肝心のtfファイルを(今は)自動生成してくれません。
そこで使用するのが今回紹介したい `terraformer`というツールです！！ 


# terraformer

terraformerとはgoogleが作成した Terraformのtfファイルを自動生成してくれるCLI toolです。
さすが天下のgoogleさま、流石です！！

ということで早速使ってみましょう。

### インストール

mac環境だとbrewが使えるので簡単です。

```
$ brew install terraformer
$ terraformer version
Terraformer v0.8.9
```

### 実行

実行も簡単です。対応するリソースはGitHubのREADME.mdを確認下さい。

```
$ terraformer import datadog --resources=dashboard --filter=dashboard=[リソースID] --api-key=${api_key} --app-key=${app_key}
```

importに成功するとホームに`generated`ディレクトリが作成され、それ以下に`dashboard.tf`が作成されます。

```
$ tree
.
├── generated
│   └── datadog
│       └── dashboard
│           ├── dashboard.tf
│           ├── outputs.tf
│           ├── provider.tf
│           └── terraform.tfstate
├── main.tf
├── provider.tf
└── terraform.tfstate
```

ディレクトリの構造とかも`--path-pattern`オプションで自由に変更できるので、これだけで既存のリソースをすべてコード化することが可能ですね :blush:

```
Import current state to Terraform configuration from a provider

Usage:
   import [provider] [flags]
   import [provider] [command]

Available Commands:
  list        List supported resources for a provider

Flags:
  -b, --bucket string         gs://terraform-state
  -c, --connect                (default true)
  -С, --compact                (default false)
  -x, --excludes strings      firewalls,networks
  -f, --filter strings        compute_firewall=id1:id2:id4
  -h, --help                  help for google
  -O, --output string         output format hcl or json (default "hcl")
  -o, --path-output string     (default "generated")
  -p, --path-pattern string   {output}/{provider}/ (default "{output}/{provider}/{service}/")
      --projects strings
  -z, --regions strings       europe-west1, (default [global])
  -r, --resources strings     firewall,networks or * for all services
  -s, --state string          local or bucket (default "local")
  -v, --verbose               verbose mode

Use " import [provider] [command] --help" for more information about a command.
```

### その他

どうでしょうか、既存リソースのtfファイルがあっという間に出来上がります。
コード化されていないプロジェクトでも導入を考えるのに十分なツールではないでしょうか。

考察
 - **リソースのID値の参照がハードコーディングされてしまう**
 　　- この点だけは注意が必要です。基本的にはterraformのdataリソースなどに置き換えましょう。
 - **terraform 0.13をサポートされている**
 　　- 以前利用した際は0.12対応されていなかったのですが、サポートされたみたいですね。
 - **ツールの更新が盛んに行われている**
 　　- terraformの更新やプロバイダー更新も追従しているようなので助かります。 

# まとめ

今回は、インフラリソースのコード化によく利用されるterraformのtfファイルを、既存のリソースから自動生成することができる`terraformer`を実際に使用しながら紹介しました。

datadogのdashboardを例に上げましたが、[AWS](https://aws.amazon.com/jp)や[GCP](https://cloud.google.com/gcp/?hl=ja)などよく使われるプロバイダーにも対応しています。
インフラリソースのコード化がまだできていないというプロジェクトが、こちらの記事をキッカケに挑戦していただけたら幸いです！

# 参考
[terraform import](https://www.terraform.io/docs/import/index.html)
[terraformer GitHub](https://github.com/GoogleCloudPlatform/terraformer)
[Terraformerを使ってTerraformに既存インフラのリソースをインポートする](https://beyondjapan.com/blog/2020/05/terraformer-import-existing-infrastructure/)

---

明日は @morimoto_kazunori の記事です。