terraformで大石泉すき

この記事は [「大石泉すき」アドベントカレンダー](https://qiita.com/advent-calendar/2019/ohishi-izumi-suki) 14日目の記事となります。

普段は千枝PのU149好きをやっていますが、プログラミング好きな泉ちゃんは気になっていました。そんな中で、ひたすら大石泉すきを叫ぶ方々に感銘を受け、微力ながら紹介できたらいいなと思って参加しました。

## この記事で書くこと

さて、IaCという言葉はご存知でしょうか？

そうです、**Izumi ohishi as Code** の略です。
ウソです、Infrastructure as Codeの略です。
気持ち的には本当です。

この記事では、Infrastructure as Codeを軽く紹介しつつ、大石泉ちゃんをコード管理していきます。ツールにはterraformを利用します。

お品書きは以下の通りです。

* outputを利用してとりあえずカレンダーのレギュレーションを満たす
* pluginを作って大石泉をコード管理する

## Infrastrructure as Code とはなにか

カレンダーを見るとインフラをやってる方があまりいないように感じたのでIaCとは何か軽く説明します。

とはいっても、その名の通りインフラをコード管理するというものです。
オライリーのInfrastructure as Codeの本には、アプリケーション開発で得られたプラクティスをインフラの管理にも適用することとありました。

要は

* 設定やスクリプトなどあらゆるものをバージョン管理する
* CI/CDを実現する

これらのアプリケーション開発で当たり前となっていることをインフラでも行うことです。

昔は手順書やパラメータシートから人力でサーバを構築していました。
今や手順書やパラメータシートに相当するものから自動でインフラが構築できるようになりました。
自動で作る元となるものをコードと呼んでいます。

APIでインフラを構築できるようになったので、こういう概念が広まって来ています。
※もちろん昔から同じようなことを行ってきている人はいると思いますので、クラウドがあるからとか、これを使えばIaC！ということではありません。

## 何のためにコードで管理するのか

あらゆるツールの導入においてWhyがあってHowがあると思います。
Whyをちゃんとしておかないと、ツール入れただけで今までと変わってなくね？となるので注意した方がいいです。

ということで何のためにIaCが必要なのかと言うと...

私の会社ではAWSリソースをコード管理していますが、

* やらかさないために事前レビューを必須にしたい
* やらかさないために同じ手順でインフラの構築と変更を行うようにしたい
* やらかさないために開発本番環境で異なる設定とならないようにしたい

というようなことを目的としてIaCを実施しています。
※ Izumi as Code としては事前に今後の方向性をレビューするためと言ったところでしょうか。

勝手に更新されてサーバごとに設定差異がある、なぜか開いてるポートがある、手順と違うコマンド打つ、レビューなく勝手に更新する、といったことが頻繁ではないにしてもちょいちょいあったものを防ぎたいと思っていました。

コードがあれば事前レビューがやりやすくなります。
理由あってCDはしていませんが、毎回同じコマンドを叩いて更新できます。
つまり同じ手順で更新するようになります。
またパラメータを変更するだけで開発と本番を分けることもできるようになります。

このようなことを実現する手段の一つとしてHashiCorp社が提供している[terraform](https://github.com/hashicorp/terraform/)があります。


## 大石泉の何を管理するか

terraformにはproviderというものがあり、管理する対象ごとに用意されています。

有名なものはもちろんAWSです。
このインスタンスはこの設定で構築します、今こういう状態ですよ、ということを管理します。

他にもazure、gcpといったクラウドはもちろん、postgresqlや、k8sなども管理できたりします。

ざっくり行ってしまえばterraformは状態管理ツールです。
providerさえあれば、一種のDBのように扱えます。

つまりは泉ちゃんの状態を管理することができると置き換えられるのですが、ここではプロフィールを管理したいと思います。

## terraformの基本

```shell
$ vim something-file.tf # コードを書く
$ terraform init        # プラグインや状態ファイルの準備などをする
$ terraform plan        # applyしたらどういう挙動になるか調べる
$ terraform apply       # 実際にインフラを構築する
```

これだけでインフラが構築できるようになります。
説明はコメントに書いた通りです。

コードは分岐や変数なども多少ありますが、パラメータシートのようなものです。
普段行っているプログラミング的にコードを書くというよりは、パラメータシートの穴埋めをするようなものです。jsonのキーバリューを埋めていくような感じです。

## まずはレギュレーションを満たす

※2,3日で何も知らないところからpluginを作る自信が無く、出来なかったときのためにひとまずレギュレーションを満たしておきます。後ろの方でpluginの説明があったらなんとかなったということです。

terraformにはoutputという標準出力に出力する仕組みが有ります。
モジュールや他で作られたリソースから値を参照できるようにするということも出来たりするのですが、今回は出力目的として利用します。

まずは管理対象となる泉ちゃんコードです

```hcl:ohishiizumi.tf
variable "ohishiizumi" {
  type = object({
    name = string
    type = string
    height = number
    weight = number
    body = object({
      bust = number
      waist = number
      hip   = number
    })
    age = number
    birthday = string
  })

  default = {
    name = "大石泉"
    type = "Cool"
    height = 157
    weight = 41
    body  = {
      bust = 83
      waist = 55
      hip = 82
    }
    age = 15
    birthday = "11/11"
  }
}

output "私" {
  value = "${var.ohishiizumi.name}好き"
}

```

早速出力してみます

```shell
$ terraform init
$ terraform apply

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

Outputs:
私 = 大石泉好き

$ for i in $(seq 1 10); do terraform output; done
私 = 大石泉好き
私 = 大石泉好き
私 = 大石泉好き
私 = 大石泉好き
私 = 大石泉好き
私 = 大石泉好き
私 = 大石泉好き
私 = 大石泉好き
私 = 大石泉好き
私 = 大石泉好き
```

はい、というわけで、terraformを使って大石泉好きを出力することが出来ました。
やったー

## プラグインを作ってみる

なんとかなりました。

output出来たことによりレギュレーションは満たせましたが、これではとてもコード管理出来たとは言えません。

書くまでもありませんがあえて言うなら、

* 大石泉の実態が存在しない(リソースが作成されているわけではない)
* コードと実態に差分が出来たとしてもそれを検知できない

ということで管理できているとは言えません。

泉ちゃんの実際のリソースってなんぞやという話なのですが、ここでは便宜的にjsonファイルをリソースとします。

ということで、プロフィールをjsonに出力するような[プラグイン](https://github.com/fullsat/terraform-provider-ohishiizumi)を書きました。

pluginの作り方はこの記事の趣旨ではありませんので、割愛しますが、だいたい ohishiizumi/resource_ohishiizumi_profile.go に詰め込みました。

go言語初心者すぎてどう書いて良いのか分からずかなり冗長的になっている分逆に読みやすいのではないかとも思っています。

pluginの作り方が気になる方は、公式ドキュメントを読んで、templateやarchiveプロバイダーを読み進めると理解できます。go初心者ですが1時間くらいで作り方がなんとなく理解できました。

そうでなくても[こちらのかた](https://febc-yamamoto.hatenablog.jp/entry/terraform-custom-provider-01)の記事が素晴らしくまとまっているのでこれを読めばいいと思います。

## terraform-provider-ohishiizumiで大石泉好き

さて作ったpluginで大石泉ちゃんをCreating!

泉ちゃんのコードです

```hcl:ohishiizumi.tf
resource "ohishiizumi_profile" "ohishiizumi" {
  name = "大石泉"
  type = "Cool"
  height = 157
  weight = 41
  body  = {
    bust = 83
    waist = 55
    hip = 82
  }
  age = 15
  birthday = "11/11"
}

output "私" {
  value = "${ohishiizumi_profile.ohishiizumi.name}好き"
}
```

このテキストファイルを利用して今後のプロデュース方針等をレビューしていくというわけです。

さて実行してみます


```shell
$ terraform apply                                                                                                                    
                                                                                                                                              
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:                                                                                                 

  # ohishiizumi_profile.ohishiizumi will be created
  + resource "ohishiizumi_profile" "ohishiizumi" {
      + age      = 15
      + birthday = "11/11"
      + body     = {
          + "bust"  = 83
          + "hip"   = 82
          + "waist" = 55
        }
      + height   = 157
      + id       = (known after apply)
      + name     = "大石泉"
      + type     = "Cool"
      + weight   = 41
    }

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

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

ohishiizumi_profile.ohishiizumi: Creating...
ohishiizumi_profile.ohishiizumi: Creation complete after 0s [id=1234801546]

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

Outputs:

私 = 大石泉好き
```

Applyできました！

> ohishiizumi_profile.ohishiizumi: Creating...

なんかワクワクしますね。
泉ちゃんの実態はというと

```json
$ cat 1234801546.json 
{
    "name": "大石泉",
    "type": "Cool",
    "weight": 41,
    "height": 157,
    "age": 15,
    "birthday": "11/11",
    "body": {
        "bust": 83,
        "waist": 55,
        "hip": 82
    }
}
```

できました！


## まとめ

リソースのあと片付けにterraform destroyを流して今回はお別れです。
※その名の通りリソースを削除するコマンドです。

```shell
$ terraform destroy                                                                                                          
ohishiizumi_profile.ohishiizumi: Refreshing state... [id=1234801546]                                                                          
                                                                                                                                              
An execution plan has been generated and is shown below.                                                                                      
Resource actions are indicated with the following symbols:                                                                                    
  - destroy

Terraform will perform the following actions:

  # ohishiizumi_profile.ohishiizumi will be destroyed
  - resource "ohishiizumi_profile" "ohishiizumi" {
      - age      = 15 -> null
      - birthday = "11/11" -> null
      - body     = {
          - "bust"  = 83
          - "hip"   = 82
          - "waist" = 54
        } -> null
      - height   = 157 -> null
      - id       = "1234801546" -> null
      - name     = "大石泉" -> null
      - type     = "Cool" -> null
      - weight   = 41 -> null
    }

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

Do you really want to destroy all resources?
  Terraform will destroy all your managed infrastructure, as shown above.
  There is no undo. Only 'yes' will be accepted to confirm.

  Enter a value: yes

ohishiizumi_profile.ohishiizumi: Destroying... [id=1234801546]

Error: 大石泉ちゃんは何があろうが壊せない

```

>Error: 大石泉ちゃんは何があろうが壊せない
>Error: 大石泉ちゃんは何があろうが壊せない
>Error: 大石泉ちゃんは何があろうが壊せない
>Error: 大石泉ちゃんは何があろうが壊せない
>Error: 大石泉ちゃんは何があろうが壊せない

大石泉は何があろうと消えたりしないぜ！

**完**