terraformerを使って複数のDatadog Organizationを統合する

#概要
監視、モニタリングで使用している複数のDatadogの環境（Organization）がある。
これらを１つにOrganizationをまとめて管理しやすくしたい。

でも、、、全部手作業でやるのは~~めんどくs~~工数が掛かる！
そこで`terraform`を使って一気にできたらいいなぁなんて思ってやってみました。


#前提条件
Orgaqnizationは２つあり、以下のダッシュボードのみを持つ。
記事簡略化のためダッシュボード以外のリソースは持たないことにする。

また、各Organizationでは共通のメトリクスが取得できているものとする。
既存Datadogリソースのtf化については以下の方法が考えられた。

1. 手動で全部書く
2. `terraform import`でリソースを取得し、tfファイルに記載する
3. `terraformer`というインポートツールを使用して自動インポートする

結果、3の`terraformer`を使用する方法を採用した。[Datadog公式ページにて推奨](https://docs.datadoghq.com/ja/integrations/faq/how-to-import-datadog-resources-into-terraform/)されていることと、リソースが多い場合一括でimportできるので結果的に一番手間が少ないと判断したためである。

###Datadog環境（example）

- Organizetion1: _Yojo_Dog_
    - resourse: _Dog_moniter_dashboard_
- Organization2: _Yojo_Cat_
    - resourse: _Cat_moniter_dashboard_

今回は、`Yojo_Cat`環境の`Cat_moniter_dashboard`をterraformerを使ってtfファイルとして出力し、`Yojo_Dog`環境にterraformからapplyすることで、2つのdashboardがある状態を作ります！:muscle: :innocent:

#使用ツール
- __Terraform__: コンピュータやネットワークを自動構築するツール。Datadog用のモジュールが用意されているためDatadogの環境をコード化、自動化できる。

- __terraformer__: 構築済みの既存リソース（AWSやDatadogなど）を読み取り、terraformのコードとして出力してくれるインポートツール

- __tfenv__: 複数のTerraformバージョンを切り替えして使用できる管理ツール

#構築作業手順
###ツールのインストール、セットアップ
local環境は:apple:Mac:apple:向けとなります。:desktop:Widows:desktop:をお使いの方は~~Macを買ってください:relaxed:~~
WSLのUbuntuとかでできるんじゃないですかねぇ（投げやり）

####tfenvのインストール

```bash
#Terraformがインストールされている場合エラーになるので一旦消す
$ brew uninstall terraform

#tfenvをインストール
$ brew install tfenv

#tfenvの確認
$ tfenv --version
tfenv 2.2.0
```

####tfenvからterraformをインストール

今回はterraformer用に`v0.13.6`を使用します。

```bash
#tfenvからterraformをインストール
$ tfenv install 0.13.6

Installing Terraform v0.13.6
Downloading release tarball from https://releases.hashicorp.com/terraform/0.13.6/terraform_0.13.6_darwin_amd64.zip
##################################################################################################################### 100.0%
Downloading SHA hash file from https://releases.hashicorp.com/terraform/0.13.6/terraform_0.13.6_SHA256SUMS
No keybase install found, skipping OpenPGP signature verification
Archive:  tfenv_download.TOSAgI/terraform_0.13.6_darwin_amd64.zip
  inflating: /usr/local/Cellar/tfenv/2.2.0/versions/0.13.6/terraform
Installation of terraform v0.13.6 successful. To make this your default version, run 'tfenv use 0.13.6'

#インストールされたか確認
$ tfenv list
* 0.13.6 (set by /usr/local/Cellar/tfenv/2.2.0/version)

#インストールしたterraformバージョンを使用する
$ tfenv use 0.13.6

#terraformコマンドの確認
$ terraform --version
Terraform v0.13.6
```

####terraformerのインストール

```bash
$ brew install terraformer
$ terraformer version
Terraformer v0.8.10
```

###Datadogリソースをインポートする準備
`terraformer`でDatadogリソースをインポートするには`API Key`と`APP Key`が必要になります。
以下手順で確認します。

####API Keyの確認

Datadogのコンソールにログインし、左のメニューから`Integrations → APIs`を選択します。
以下画面に遷移しますので、API Keysを展開しKeyを控えておきます。

![datadogapi.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1112510/7c290e44-5299-5d71-cd07-e6f6d10c6d27.png)

####APP Keyの確認

同様のページから`Application Keys have moved to the Teams page`を選択するとApp Keyの作成、確認ページに遷移します。
作成済みのKeyがあっても作成時に一度しかKeyは確認できないようです。
また、Key作成の際は自身のユーザの権限のみを持ちますので個人ではなく会社でやる際は~~偉い人を脅す~~確認しましょう。

`+New Key`ボタンを押すとKey作成されクリップボードにコピーすることができます。控えておいてください。

![datadogapp.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1112510/43cd5e53-7f52-d448-6fc1-c5a9d08f7d74.png)

これでこのOrganizationからterraformerでtfファイルをインポートする準備ができました！！ヨシっ！！:smiley_cat: :point_right:

同様の手順でもう一つのOrganizationのKeyを控えておきます。

###terraformerの実行

####terraformの初期設定

まずはterraformを実行できるようにサクっと環境を作りましょう。

```bash

#実行ディレクトリの作成（自由に）
terraform
└── datadog
```

####main.tfの作成

[Datadog公式ドキュメント](https://docs.datadoghq.com/ja/integrations/faq/how-to-import-datadog-resources-into-terraform/)通りに書いていきます。
`api_key`　`app_key`は__Datadogリソースをインポートする準備__で取得したリソースを書き出したい方のOrganizationのものを記載します。
この記事ではkeyを直接記載しますので間違えてgithubに公開しないように気をつけてくださいね:frowning2:

`Yojo_Cat`のリソースを`Yojo_Dog`に追加するので以下のようになります。


```terraform/datadog/main.tf
terraform {
  required_providers {
    datadog = {
      source = "Datadog/datadog"
    }
  }
}

provider "datadog" {
  api_key = "Yojo_Dog_api_key"
  app_key = "Yojo_Dog_app_key"
}

```

####initする

Datadog用のprovidersをインストールするため、`init`します。

```bash
$ terraform init

Terraform has been successfully initialized!
```

####1つ目のOrganizationのリソースを`terraformer import`する

今回はdashboardだけですが、その他のリソースがある場合は`--resorces=hoge,fuga,piyo`とカンマ区切りで指定できます。
また、特定のリソースだけを出力する場合は`--filter=dashboard=Dog_moniter_dashboardID`のように指定できます。[^example]

[^example]: `--filter`で使用するIDはリソースのnameではなく個別にDatadogから振られているIDを使います。ブラウザから例えば対象のダッシュボードを開くとブラウザのURLにIDが表示されているのでそれを使用します。

```bash

#Yojo_Dog Organizationのリソースを出力
$ terraformer import datadog --resources=dashboard --api-key "Yojo_Dog_api_key" --app-key "Yojo_Dog_app_key"

2021/03/05 02:58:27 datadog importing... dashboard
2021/03/05 02:58:28 Refreshing state... datadog_dashboard.tfer--dashboard_<Dog_moniter_dashboardID>
2021/03/05 02:58:36 datadog Connecting....
2021/03/05 02:58:36 datadog save dashboard
2021/03/05 02:58:37 datadog save tfstate for dashboard

#generateディレクトリとファイルが出力される
./generated
└── datadog
    └── dashboard
        ├── dashboard.tf
        ├── outputs.tf
        ├── provider.tf
        └── terraform.tfstate
```

####2つ目のOrganizationのリソースを`terraformer import`する

このまま実行すると__なんと全部上書きされてしまいます・・・What's!?:scream:__
なので、別のディレクトリに一旦退避させましょう:sweat:

```bash
terraform
└── datadog
    ├── Yojo_Dog
    │   └── generated
    │       └── datadog
    │           └── dashboard
    │               ├── dashboard.tf
    │               ├── outputs.tf
    │               ├── provider.tf
    │               └── terraform.tfstate
    └── main.tf
#Yojo_Cat Organizationのリソースを出力
$ terraformer import datadog --resources=dashboard --api-key "Yojo_Cat_api_key" --app-key "Yojo_Cat_app_key"

2021/03/05 03:16:40 datadog importing... dashboard
2021/03/05 03:16:44 Refreshing state... datadog_dashboard.tfer--dashboard_<Cat_moniter_dashboardID>
2021/03/05 03:16:55 datadog save dashboard
2021/03/05 03:16:56 datadog save tfstate for dashboard

#generateディレクトリとファイルが出力される
.
├── Yojo_Dog
│   └── generated
│       └── datadog
│           └── dashboard
│               ├── dashboard.tf
│               ├── outputs.tf
│               ├── provider.tf
│               └── terraform.tfstate
├── generated
│   └── datadog
│       └── dashboard
│           ├── dashboard.tf
│           ├── outputs.tf
│           ├── provider.tf
│           └── terraform.tfstate
└── main.tf

#統一感だしていこ？？？笑笑笑
.
├── Yojo_Cat
(略)
├── Yojo_Dog
```

###リソースファイルの修正

出力したファイルはそのままでは使うことができません。これを使用するために2つのことをファイル追加修正します。

1. tfファイルのバージョンアップを行う
2. 競合するIDを削除する

---
####tfファイルのバージョンアップを行う
これは[Datadog公式ドキュメント](https://docs.datadoghq.com/ja/integrations/faq/how-to-import-datadog-resources-into-terraform/)の手順で推奨されているので実行します。
terraformerでimportしたtfファイルのバージョンを`v0.13.x`に調整します。

```bash
#Yojo_Catに実行
$ pwd
/datadog/Yojo_Cat/datadog/dashboard

$ terraform 0.13upgrade .

Would you like to upgrade the module in the current directory?
  Only 'yes' will be accepted to confirm.

  Enter a value: yes #yesを入力してエンター

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

Upgrade complete!

#なんか変わってる。まぁ任せた！！
$ git status
Changes not staged for commit:
	modified:   provider.tf

Untracked files:
  (use "git add <file>..." to include in what will be committed)
	versions.tf

#initする
$ terraform init
Terraform has been successfully initialized

.
├── dashboard.tf
├── outputs.tf
├── provider.tf
├── terraform.tfstate
└── versions.tf

#忘れずにYojo_Dogにも適用する
$ pwd
/terraform/datadog/Yojo_Dog/generated/datadog/dashboard

$ terraform 0.13upgrade .
$ terraform init
```

####競合するIDを削除する
terraformerの弱点の一つとして、リソースの固有IDまでtfファイルにもってきてしまうことです。
このままapplyしようとしても既存リソースと競合してしまいエラーとなります。

ちょっと状況を確認してみましょう。

```bash
$  terraform plan
Error: "widget.10.id": this field cannot be set
  on dashboard.tf line 125, in resource "datadog_dashboard" "tfer--dashboard_<dashboard_ID>":
 125: resource "datadog_dashboard" "tfer--dashboard_<dashboard_ID>" {

Error: "widget.10.id": this field cannot be set
.
.
.
.
echo '天才の僕「ぎゃーーー！！！！」'
```

・・・とご丁寧にどのリソースやウィジットがエラーになってるか書いているのでtfファイルを修正していきましょう:relaxed:
以下は`"爆速なお仕事（笑）"`が特技の筆者の方法です。

```bash
$ ls
dashboard.tf      outputs.tf        provider.tf       terraform.tfstate versions.tf

#伝説の武器、VSCodeを起動！！俺のターーーン！！！！！（エンターキーをターン！する）
$ code dashboard.tf
```

`ID = .*$`で検索してみると。。。
なんかいた！！！！これが入ってるとapplyできません。
全部いらないので置換で消しちゃいましょう。神経質な方は確認してから消しましょう。
筆者はできなきゃ戻せばいいやみたいな悪いエンジニアなのでそのまま全部置換:heart_eyes:

![new_vscode.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1112510/772d68a6-68f2-497c-0f11-bb81d204a802.png)



エラーが出なくなるまで特定して修正します。

```bash
$ terraform plan
Error: api_key and app_key must be set unless validate = false
```
上記は大丈夫なやつです。generated配下でplanしているのでキーがなくて正常です。
エラーが出なくなったらもう一つのOrganizationも修正しましょう。

:innocent: :interrobang:

```bash
$ terraform plan
Error: "widget": required field is not set
6731: resource "datadog_dashboard" "tfer--dashboard_<dashboard_ID>" {
```
見たところ、widgetが無いテストみたいなダッシュボードでした。
からっぽのdashboardはGUIから保存できてしまいますがコード上は許されていないようです。

ただファイルから削除しただけだとtfstateとの整合性が取れなくなりややこしくなります。
その辺りは、今回は触れません。

つまり、、、GUIから物理削除してimportしなおしました！
（exampleだとdashboardが1個になってますが、こういうエラーもあるよということで例外紹介でした）

###リソースを統合する
いよいよ最終章です。2つのOrganizatioのdashboardをおまとめして、Yojo_Dog環境を最強にします。

####ファイルの移動
importしたデータメインディレクトリに移動しましょう。
`dashboard.t` `terraform.tfstate` `versions.tf`が対象です。

```bash
$ pwd
/datadog/Yojo_Cat/datadog/dashboard

$ cp dashboard.tf terraform.tfstate versions.tf ../../../

.
├── Yojo_Cat
│   └── datadog
│       └── dashboard
│           ├── dashboard.tf
│           ├── outputs.tf
│           ├── provider.tf
│           ├── terraform.tfstate
│           └── versions.tf
├── Yojo_Dog
│   └── generated
│       └── datadog
│           └── dashboard
│               ├── dashboard.tf
│               ├── outputs.tf
│               ├── provider.tf
│               ├── terraform.tfstate
│               └── versions.tf
├── dashboard.tf
├── main.tf
├── terraform.tfstate
└── versions.tf

```
###planで内容を確認する。
注意：このログは良く確認してください。適当に実行してしまうと・・・
:star_and_crescent:破壊と創造:star_of_david:を行うシステム障害の神になりえます。
しっっかっかっかっかりと`ログを確認`して念のため退職届を書いておきましょう。

```bash
#ファイルが増えているので一旦initしておく
$ terraform init
$ terraform plan

#変更のあるリソースを確認
datadog_dashboard.tfer--dashboard_<Cat_moniter_dashboard_ID>: Refreshing state... [id=Cat_moniter_dashboard_ID]

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

#Createdのみなはず。deleteがあるなら要確認
An execution plan has been generated and is shown below.
Resource actions are indicated with the following symbols:
  + create

Terraform will perform the following actions:

  # datadog_dashboard.tfer--dashboard_<Cat_moniter_dashboard_ID> will be created
  + resource "datadog_dashboard" "<Cat_moniter_dashboard_ID>" {
      + dashboard_lists_removed = (known after apply)
      + description             = <<~EOT
            ## Overall
            Dashbord for
（略）
# +が追加される部分です。リソース、ウィジットなど変更点を確認しましょう。

#最後に変更点のカウントが出ます。
Plan: 1 to add, 0 to change, 0 to destroy.
------------------------------------------------------------------------

```

###applyする
ここまで問題がなければapplyすることでリソースの統合ができるはずです。
Yojo_DogにYojo_Catの全てをぶっこんであげましょう！！！:golf:

```bash
$ terraform apply
datadog_dashboard.tfer--dashboard_<Cat_moniter_dashboard_ID>: Refreshing state... [id=<Cat_moniter_dashboard_ID>]

An execution plan has been generated and is shown below.
Resource actions are indicated with the following symbols:
  + create
（略）

#実行するか聞かれるのでyesを入力します。
Do you want to perform these actions?
  Terraform will perform the actions described above.
  Only 'yes' will be accepted to approve.

  Enter a value: yes

Apply complete! Resources: 7 added, 0 changed, 0 destroyed.

Outputs:
datadog_dashboard_tfer--dashboard_<Cat_moniter_dashboard_ID>_id = datadog_dashboard.tfer--dashboard_<Cat_moniter_dashboard_ID>.id

```
Apply complete!が出ていれば成功です！お疲れ様でした:muscle::innocent:

###結果をGUIから確認する
ナイスー！！！！！
勝ち申した！！！！
![datadog_apply.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1112510/78528653-52a7-e1fc-60f7-35508fcdd889.png)


---

####Twitterやってます！エンジニアアカウントも音楽アカウントもフォローしてね:two_hearts::two_hearts::two_hearts::two_hearts::two_hearts:

<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">わんわんもにゃんにゃんもかわいいけど、どっちかだったら…😑💭<br><br>わんわんが好きです😚😬😝😘💞💞💞💞💞✨✨✨✨<br><br>🐶🐶🥰🥰🥰🥰🥰💨</p>&mdash; 天才ハッカー幼女こはるちゃん (@koharu_infra) <a href="https://twitter.com/koharu_infra/status/1367540588409626627?ref_src=twsrc%5Etfw">March 4, 2021</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>