実践Terraformを読んで学びになったことと読了前に早めに知っておきたかったこと

[実践Terraform　AWSにおけるシステム設計とベストプラクティス](https://nextpublishing.jp/book/10983.html)のコードを写経して学びになったことをざっくりまとめてみました。
あと読了する前に知っておきたかったなということがあったのでそれも書いてみました。
参考になれば幸いです。

※TerraformもAWSもGet Startedやチュートリアルに載っていそうな基本も学べたのですがそれらは省いています。

## Terraformについて学びになったこと

### Terraformのインストール方法と各方法に対する所感

方法は3つありました。

* Terraformをそのままインストール→簡単
* tfenvを使ってインストール→複数人で開発するときに適している
  * .terraformversionファイルをリポジトリに含めるとチームメンバーが「tfenvinstall」コマンドを実行するだけで、バージョンを統一できる。
* DockernizedTerraform（terraformがインストールされたコンテナ）を使う→個人で使う分には十分。バージョン切り替えもイメージを変えればいいだけなので。ただ環境変数の設定などでハマりどころはある。（別記事でまとめる予定）

### コマンド実施の基本的な流れ

1. initで初期化
2. planで変更内容を確認
3. applyでコードの内容を適用（この時点でEC2インスタンスの起動やネットワーク設定の反映がされる）
4. （不要になったら）destoryでリソースを削除

注意点

* applyによって既存のリソースを更新できるが、コードの変更内容によってはリソースが一度削除されて再生成されるのでplan実施時に削除されるか確認が必要。
* planで変更内容はでるが、実行してからはじめてわかるエラーもある。
  * たとえばplanでは存在しないamiidを指定しているコードがあってもエラーにならない。apply実施ではじめてエラーとなり、誤っているとわかる。

### Terraformでの設計や実装時の注意

基本的な考え方は他のプログラミング言語とは変わらないと感じました。
コードの可読性や再利用性、モジュールの凝集度や結合度を考えて設計して実装するのは共通して大事！

ディレクトリ構成は[標準](https://www.terraform.io/docs/modules/index.html#standard-module-structure)があるので、基本的にはこれに倣って作るほうが良いようです。ただこれをそのまま使うと開発やステージング、本番環境を一部パラメータを変更して構築したいときにどうするか悩むところがありました。このあたりは検索すると色んな人が考えて実践しているようなので参考にさせていただこうと思います。

* [オレオレterraformディレクトリ構成ベストプラクティス](https://qiita.com/eigo_s/items/02264a5a7ad0ff6c5387)
* [Terraformにおけるディレクトリ構造のベストプラクティス](https://dev.classmethod.jp/devops/directory-layout-bestpractice-in-terraform/)

## AWSについて学びになったこと

### git-secretsとawslabsのリポジトリの存在

便利なツールとしてgit-secretsが紹介されていました。

> git-secretsはアクセスキーやパスワードのような秘匿情報をGitでコミットしようとすると、警告してくれます。

野村 友規. 実践Terraform　AWSにおけるシステム設計とベストプラクティス (Japanese Edition) (Kindle の位置No.249-250). Kindle 版.

これはhttps://github.com/awslabs/git-secretsで公開されていました。作成しているアカウントの[awslabs](https://github.com/awslabs)はAWS関連で有用なものがありそうでした。

### 各種ログはJSON形式で出力させるようにするとCloudWatchLogsInsightsで簡単にログが検索できる

CloudWatchLogsInsightsはCloudWatchLogsで残っているログがJSON形式であれば専用の検索クエリで検索できるようになります。そのため、ECS FargateとかでコンテナのアプリケーションログをJSON形式で出力するようにしておくと、わざわざログ検索の仕組みを作らなくてよくなります。
ただし、CloudWatch Logsはストレージとしては割高なので古いログはS3へ入れるようにした方が良いとのこと。

GCPやAzureなどの他のクラウドサービスも、ログを特定の形式にしていたら検索しやすいようにしているかもしれません。他のクラウドサービス使うときも調べておいたほうが良い項目だなと思いました。

### 実践する前に知っておいたほうが楽だと思ったこと

以下のことは本を読む前に知っておいたらちょっと楽になったかなと思いました。

#### Terraformのファイル名の付け方

* tfファイル名はAWSサービス名か役割のどちらかで統一したほうがいい
* ファイル名はAWSサービス名と役割のどちらを書くにしても短縮形はあまり使わないほうがいい（自分でもわからなくなるため）

気づいたら以下のようにファイルができていて、ファイルを開かないと詳細がわからないという残念な感じになりました。

```zsh
├── imds.tf  <-- imdsは"in memory data store"の略で中身はElastic Cacheの定義。サービス名でも役割でもない。辛い。
├── kms.tf   <-- AWS Key Management Service (KMS)の定義。サービス名がそのままファイル名に。
├── lb.tf    <-- Elastic Load BalancingのApplication Load Balancerを使って構築するLoad Balancer。役割がファイル名に。
├── main.tf
├── network.tf <-- Amazon VPCのみを使って定義したネットワーク設定。役割がファイル名に。
...
```

とくにはじめて触るサービスの場合は略さない方がよかったですね。AWSやAmazon Elasticは省略されていても問題なさそうなので、
サービス名で統一するなら以下のようにしたらよかったかなと。

* AWS Key Management Service（KMS） -> KeyMangagementService.tf
* Amazon Elastic Container Registry (ECR) -> ContainerRegistry.tf
* Amazon Elastic Container Service (Amazon ECS) -> ContainerService.tf

まずリソースを指定して削除できるようにするオプションは中盤辺りから知っておけば楽だっただろうなと思いました。↓です。

```sh
terraform destroy -target=aws_db_instance.example
```

ちょっと試したいことがあってサンプルコードを一部変えてapplyを実行したらなんかよくわからない状態になることがありました。このとき、一度作り直したいためサブコマンドdestroyで全部ぶっ壊して都度applyしていたのですが、そうすると別に壊さなくていいネットワーク設定や独立しているEC2インスタンスまで壊してまた作っていました。これはけっこう時間がかかります。そのため試したい部分だけ変更して作り治せるようにこのオプションを使っていればよかったなと思いました。

あとは、デバッグログの設定方法。

> TF_LOGにはログレベルとして、TRACE・DEBUG・INFO・WARN・ERRORを指定できます。DEBUGレベルのログを出力する場合、次のように実行します。
> $ TF_LOG=debug terraform apply
> また、環境変数「TF_LOG_PATH」を使うと、ログをファイル出力できます。
> $ TF_LOG=debug TF_LOG_PATH=/tmp/terraform.log terraform apply

野村 友規. 実践Terraform　AWSにおけるシステム設計とベストプラクティス (Japanese Edition) (Kindle の位置No.3843-3848). Kindle 版.

これは本の終わりの方の28章「落ち穂拾い」で書かれています。ただ、こちらもよくわからないエラーで失敗したときに原因を辿れるように先に知っておければなと思いました。

## さいごに

上記意外にもやってみたらハマって、そこで学びになった点はあるのですが、それも書き出すとめっちゃ長くなるのでそれは別で書こうかなと思います。
得られたことが多い分、まとめようとするとボリュームも多い・・。
次に本を読むときは章単位でまとめていこうと思いました。時間が経つと忘れてしまうこともあるし、内容をまとめにくくなるので。