chroju.dev/blog

the world as code

Terraform Cloud の terraform バージョンアップを GitHub Actions で自動化する

Terraform Cloud がサービス開始して約1年、 state file の保存のみならず、 planapply の実行環境としてもバリバリ使わせてもらっている。ちょっと面倒なのが、 plan 等を実行する workspace の Terraform バージョン設定である。常に latest を使うというアグレッシブな設定もできるが、そうしない限りは手動でバージョンアップが必要になっている。

Image from Gyazo

まぁガンガン latest にしていくのも考え方によってはありかもしれないが、メジャーバージョンアップ( Terraform の場合 0.12 → 0.13 をメジャーバージョンアップと呼びたい)までされてしまうのは抵抗がある人も多いのではないだろうか。そもそも Terraform には remote config の中にバージョン指定を出来る箇所があり、自分の場合はこんな感じで 0.12 台のみを許可している。

terraform {
  backend "remote" {
    hostname = "app.terraform.io"
    organization = "sample"

    workspaces {
      name = "sample"
    }
  }
  required_version = "~> 0.12.0"
}

望むらくは、この required version をもとにしていい感じにバージョン設定してほしい。なのでそういう GitHub Actions を作った。

terraform-cloud-updater

Terraform Cloud Workspace Auto Update - GitHub Marketplace
Automates the terraform version update of your Terraform cloud workspace
Terraform Cloud Workspace Auto Update - GitHub Marketplace favicon https://github.com/marketplace/actions/terraform-cloud-workspace-auto-update
Terraform Cloud Workspace Auto Update - GitHub Marketplace

input として最低限、対象の Terraform ファイルが保存された working_dir を指定し、環境変数 TFE_TOKEN で Terraform Cloud の API トークンを渡してあげれば動作する。

on:
  push:
    branches:
      - master

jobs:
  tf_cloud_update:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v2
      - name: check update
        id: check
        uses: chroju/terraform-cloud-updater@v1
        with:
          working_dir: ./terraform
        env:
          TFE_TOKEN: ${{ secrets.TFE_TOKEN }}
      - name: result
        run: echo "${{ steps.check.outputs.output }}"
        if: "${{ steps.check.outputs.is_available_update == 'true' }}"

デフォルトの設定だと workspace のバージョンアップまでは自動で行わず、新しいバージョンが見つかったら outputs.is_available_update の値が true になり、バージョン情報が outputs.output にセットされるだけだ。あとは is_available_update で条件分岐して、例えば slack などで通知する方式を想定している。 comment_pr という input を true にすれば、 Pull request をトリガーとして使った際にコメントで通知してくれる機能も作ってはあるが、バージョンチェックであれば cron 形式で定期実行するほうがいいだろう。

input の auto_updatetrue にすることで、自動的に更新まで行ってくれる。その際、 required version との整合性を確認し、整合しない場合には更新は行わず、通知だけが行われる。以下は comment_prtrue にした場合のサンプルだが、 outputs.output からも同様のメッセージが取得できる。

Image from Gyazo

実装

go-tfe

GitHub - hashicorp/go-tfe: Terraform Cloud/Enterprise API Client/SDK in Golang
Terraform Cloud/Enterprise API Client/SDK in Golang - hashicorp/go-tfe
GitHub - hashicorp/go-tfe: Terraform Cloud/Enterprise API Client/SDK in Golang favicon https://github.com/hashicorp/go-tfe
GitHub - hashicorp/go-tfe: Terraform Cloud/Enterprise API Client/SDK in Golang

Terraform Cloud の API ライブラリは hashicorp が公開している。名前が tfe になっているが、これは Terraform Enterprise と Cloud の共通 API ライブラリだからであって、 Cloud で特に問題なく使えた。

ちなみに Go で CLI ツールとして書いているので、この Action のもとになっているレポジトリを build すれば、ローカルで実行する CLI としても使うことができる。

$ terraform-cloud-updater -h
Usage: terraform-cloud-updater [--version] [--help] <command> [<args>]

Available commands are:
    check     Check if new Terraform version is available
    update    Update Terraform cloud workspace terraform version

required version の処理

一番面倒くさかったのが required version だった。セマンティックバージョニングはいわゆる小数ではなく、ドットを2つ挟んだ数値形式なので、単なる数値比較を行なうことができず、逐一ロジックを組んでいった。また required version を示すオペランドの中には、 ~> 0.12 (0.12.0 以上かつ 0.13.0 未満の意)といった複数の意味をはらんだものもあり、これも自前で判定ロジックを組んだ。

と、いろいろ書いた末に「セマンティックバージョニングをよろしくやってくれるライブラリあるんでは?」と思って探してみたら当の hashicorp から公開されていたわけなのだが。勉強になったのでいいか、と思いつつ、こちらも比較で見てみようと思っている。

GitHub - hashicorp/go-version: A Go (golang) library for parsing and verifying versions and version constraints.
A Go (golang) library for parsing and verifying versions and version constraints. - hashicorp/go-version
GitHub - hashicorp/go-version: A Go (golang) library for parsing and verifying versions and version constraints. favicon https://github.com/hashicorp/go-version
GitHub - hashicorp/go-version: A Go (golang) library for parsing and verifying versions and version constraints.

初めての GitHub Actions

GitHub Actions 自体はよく使っているものの、自分で Action を作るのはこれが初めてだった。ここでは詳細な作り方などには触れないが、 Action を実行する Dockerfile と entrypoint.sh を書き、あとはメタ情報を定義した action.yml を設置するだけでよいので、非常にお手軽に感じた。ちなみに Action は JS か Docker で作れるのだが、後者を選んだのは単に JS が書けないから。書けるのであれば実行速度などを鑑みても JS のほうがいいと思う。

なお、 GitHub Actions は GitHub Marketplace で公開ができる。そのための操作も非常にシンプルで、レポジトリのルートに action.yml を置くと勝手に判定されて、 Marketplace で公開するためのボタンが表示されるようになっていた。

Image from Gyazo

Terraform のバージョン管理という根深い問題

Terraform のバージョン管理問題はなかなか根深い。というのも Terraform の仕様上、一度使用したバージョンより古いバージョンに戻ることはできないようになっているからだ。そのため複数の Terraform レポジトリを扱っている場合、バージョンは required version に従って一括で揃えられるようにしたいという欲求が大きい。

複数の Terraform バージョンを切り替えるためのコマンドラインツール tfenv においても、 tfenv doesn't read the required_version from conf.tf · Issue #124 · tfutils/tfenv ということで、 required version に則って自動的に切り替えてほしいという issue が挙がっており、このあたりは悩んでいる人が多いのではないかと推察している。