Terraformに入門したメモ

AWSにもある程度慣れてきてコンソールでポチポチリソースを作成するのがまだるっこしくなってきたので、ちまたで評判のいいPragmatic Terraform on AWSをゲットしてTerraformに入門。
AWSをある程度知っている人がTerraform入門するにはうってつけの本かと。これは同人誌とはすごい。

適宜Terraform公式も参照しつつ数日いじってみてTerraform完全に理解した。

少し調べれば割と素直にやりたいことができるので楽しい。
だいぶネットで知見も貯まっているしね。

CloudFormationにしなかったのはなんとなく?
パスカルケースやキャメルケースよりスネークケースの方が好きとかそんなん。

Terraformのインストール

Homebrewでterraformを直接入れることもできるがtfenvを使ってみる。rbenvのように環境ごとにterraformのバージョンを変えることができる。

brew install tfenv
mkdir pragmatic-terraform-on-aws
cd pragmatic-terraform-on-aws
echo 0.12.2 > .terraform-version
tfenv install

Terraformのコードを書く準備

VSCodeのTerraform拡張

Terraform

便利。

ただ2019/6月現在、0.12に対応してないみたいで新しい記法などは文法エラーが出てしまう。

無視するのも気持ち悪いが、Terraformの方では問題なく動くのでそのままにしとくか。エディタのエラー消すために古い記法で書くとかなんだか違う気がするしな・・・

.gitignore

https://github.com/github/gitignore/blob/master/Terraform.gitignore

実行ユーザーの準備

Terraform実行ユーザーをIAMで作成する

AdministratorAccess持ちのユーザーを作成してアクセスキーとシークレットキーを保存。ここはさすがに手動。

作成するリソースが決まっていれば権限を絞ったユーザーを作成するのがいいのかも。とりあえずお試し段階ではAdministratorAccessで。

Profile

~/.aws/credencialsにProfileを作成。terraformという名前で。

mail.tf内で以下のようにプロファイルを指定できる。

provider "aws" {
  profile = "terraform"
  region = "ap-northeast-1"
}

運用ノウハウ

リソースの状態を見る

terraform show
terraform state list
terraform state show 

とかとか。

terraform import

既存のAWSリソースをtfstateに書き込んでTerraform管理下に置くことができる。

terraform import aws_s3_bucket.example <リソースID>

tfファイルに現状が反映されるわけではないので、import直後に不用意にapplyするとリソースが作り直されたりして死ぬ。危険。
tfstateを直接見るか、以下のコマンドでStateに反映された内容を見てtfファイルにコードとして落とし込む。

terraform state show aws_s3_bucket.example

間違えてimportしたものを管理下から外したいときは次のコマンドで。

terraform state rm aws_s3_bucket.example

なおtfファイルまで作成してくれるツールもあるようだ。

dtan4/terraforming

ファイル分割とモジュール化、コンポーネント分割

単純にmain.tfを複数のtfファイルに分割できる。
同じディレクトリに置いておけばTerraformは実行時にtfファイルを全て自動的に読み込む。
依存関係なども問題ない。多分パース時に連結してから処理しているんだろう。

単なるファイル分割では見通しが悪くなってきた場合はディレクトリを分けてモジュール化を検討する。

またコンポーネント分割でtfstateファイルを必要に応じて分けると、何か失敗したときの影響範囲を小さくしたり実行時間を短くすることができる。

Remote State

Terraformを実行すると作られるtfstateファイルはTerraformの適用状態を管理するファイルで、中身を見るとJSONでリソースの状態が保持されている。

コードを書く前に準備した.gitignoreに含まれているが、ソロ開発だと普通にGitにコミットしてGitHubで管理してもいいかも。
ただソロでもpush忘れやpull忘れがあることを考慮するとRemote Stateを使っておいた方が手堅い。

Remote StateはS3のバケットなどにtfstateを置いてStateを中央管理するもの。
複数人が触る状況だとRemote Stateが必須となる。各自のローカルのtfstateと実際のインフラの状況が一致しなくなるので。

またローカルのtfstateを誤って削除してしまい死んだりもするしやはり基本的にRemote Stateが良い。
てか死んだ。importしていけば元に戻せるけど・・・これはめんどくさい。
S3上のファイルを削除してしまう可能性もゼロではないけど、ローカルのファイルよりそれは起こりにくいだろうし。

環境ごとの対応

本番環境やステージング環境、開発環境など複数の環境において同様のインフラを構築したければ、それぞれの環境で別個のtfstateファイルを使えば実現できる。
ディレクトリ分割方式とWordspacesを使う方法があるようだ。

ディレクトリ分割は慣例的に発生したやり方?で、productionやstagingなど環境の数だけディレクトリを掘ってそれぞれのディレクトリに同様のファイルを置く。
パラメータの違うだけのコードがコピーされると言ううげーっとなるやり方だが、こっちが主流らしい。
実際試して見るとモジュール化と合わせればかなりDRYにできるし悪くはなさそう。

Workspacesを使う方法は公式のやり方でコードがDRYになるが、terraform.workspace変数の内容を見つつリソース調整を行うコードを書くのはややダルい。
実行の時に-var-fileで環境ごとのtf-varsファイルを指定するのも普通にファイル名間違えて惨事になりそうだなと。

既存の環境を実際にTerraformに落とし込んでみると本番だけに作りたい、ステージングだけに作りたいというリソースがすぐに出てきてWorkspacesで対応しようとすると大変だった。というか無理だった。
Workspacesで分岐できるようにif文が書けたらいいのにと思ったが、それはそれで地獄の始まりかもしれない。

Workspacesの方が公式の機能だし、ディレクトリ分割で環境ごとにファイルがたくさんできるのめんどくさいよねとWorkspaces推しで取り組んだものの割と早々に挫折した。
実はもっとうまいやり方があるのかもだが、今のところはディレクトリ分割の方が良いと感じている。

参考

ベストプラクティス

  • リモートバックエンドを利用する
  • Terraformバージョンを固定する
  • プロバイダバージョンを固定する
  • リモートステートを使いこなす
  • データソースを使いこなす
  • 削除操作を抑止する
  • コードフォーマットをかける
  • バリデーションをかける

by Pragmatic Terraform on AWS

AnsibleやChefとの併用

https://www.terraform.io/intro/vs/chef-puppet.html