terraform コマンドをプロジェクトごとにポータブルにする

terraform コマンドのバージョンをプロジェクトごとに明記しておいても、それぞれのプロジェクトごとに都度指定のバージョンのバイナリを落としてくるのはちょっと面倒くさいので、 それを docker をつかって解決できないか考えてみました。以下のものを使います。

  • Docker
  • hashicorp/terraform dockerイメージ
  • direnv

まず、Docker Hub には hashicorp が terraform の 公式 docker image を公開しているのでこれを使って以下のようなシェルスクリプトを書きます。

bin/terraform:

#!/bin/bash
ROOT_DIR=$(cd $(dirname $0); pwd -P | xargs dirname)

exec docker run \
  -it \
  --rm \
  --workdir=$(pwd -P) \
  -v "${ROOT_DIR}:${ROOT_DIR}" \
  -e "TF_VAR_AWS_ACCESS_KEY_ID=${AWS_ACCESS_KEY_ID}" \
  -e "TF_VAR_AWS_SECRET_ACCESS_KEY=${AWS_SECRET_ACCESS_KEY}" \
  hashicorp/terraform:0.12.25 $*

キモの部分は -v "${ROOT_DIR}:${ROOT_DIR}"--workdir=$(pwd -P) の部分で、 ROOT_DIR/Users/username/foo-project というパスだった場合に、コンテナに /Users/username/foo-project というパスが生成され、ホストの /Users/username/foo-project がマウントされます。そしてさらにコンテナでコマンドを実行するときのワーキングディレクトリを --workdir=$(pwd -P) で指定するとホストとコンテナでパス構成とカレントディレクトリが一致するので、ホスト上の実行ファイルのような感覚で使用することができます。

また、--rm オプションをつけているので実行するたびにゴミが増えていくということもありません。

terraform から環境変数に触りたい場合は -e オプションで任意の環境変数を渡しておきます。

さらに direnv を使うとプロジェクトごとに有効な環境変数PATHを設定できて便利です。

.envrc:

if [ -f .envrc.local ]; then
  source .envrc.local
fi

PATH_add bin

.envrc.local:

export AWS_ACCESS_KEY_ID=xxxxx
export AWS_SECRET_ACCESS_KEY=xxxxx

ここまですると、以下のようにホスト所にインストールされた terraform コマンドと同様に使うことが出来ます。

terraform plan

これらをまとめると以下のようなディレクトリ構成になります。

ディレクトリ構成:

/Users/username/foo-project # project-root
├── .envrc               # direnv の設定ファイル
├── .envrc.local         # バージョン管理しない
├── .envrc.local.sample  # .envrc.localの雛形
├── bin
│   └── terraform       # ポータブルなterraformコマンド
└── terraform            # terraformのファイル郡
    ├── enviromnents
    │   └── production
    │       └── main.tf
    └── modules
        └── main
            ├── ec2.tf
            ...