Terraform を自動実行したいなら Atlantis

この記事は [Terraform Advent Calendar 2021](https://qiita.com/advent-calendar/2021/terraform) の5日目です。 Atlantis の話が書きたいけど書く場所がなくて遅ればせながら枠を探したところ、5日目の枠が空いていることに6日朝に気付いて押さえたため、投稿は遅くなっております。

ということでこのエントリーでは全力で [Atlantis](https://www.runatlantis.io/) を推します。

## Atlantis とは

Terraform の自動実行にはみなさん何を用いているでしょうか。2021年現在だと HashiCorp 提供の [Terraform Cloud](https://www.terraform.io/cloud) でマネージドなパイプラインが簡単に組めますし、同じく HashiCorp が GitHub Actions を使ったワークフローを [Automate Terraform with GitHub Actions | Terraform - HashiCorp Learn](https://learn.hashicorp.com/tutorials/terraform/github-actions) で公開していたりと、自動実行環境はかなり組みやすくなっています。その他様々な CI ツールなどを用いている場合もあるかもしれません。

Atlantis は Terraform の自動実行環境を提供する OSS です。クラウドサービスではなく OSS ですので、自前で Atlantis サーバーをホストしなくてはならないという手間はあるのですが、一度建ててしまえば非常に簡単にワークフローが整備できます。

Qiita で `terraform atlantis` と検索しても15件しか現時点でヒットしないなど、残念ながら国内だとそこまでメジャーではないのかなという印象です。が、個人的には Terraform を自動実行するならまずこれを push したいぐらいにはオススメのツールです。

## Atlantis の基本動作

Atlantis は VCS と連携させることで動作します。　Github, GitLab, Bitbucket, Azure Devops などに対応しているようですが、筆者は GitHub 連携でしか使ったことがないので、以下、 GitHub 連携を前提として書きます。

GitHub と Atlantis を繋ぎ、自動実行のワークフローを作ると、 Atlantis は以下のことをしてくれます。

1. Pull Request 作成時に `terraform plan` を自動実行、結果をコメントに貼付
1. Pull Request 内で `atlantis apply` とコメントすると `terraform apply` を実行、結果をコメントに貼付

基本的なワークフローはザックリ言えばこの2つだけですが、これで必要十分だとも言えます。自前でワークフローを書いたりせずとも、とりあえずこの2つはやってくれるというだけで「なかなかいいんじゃない？」と言えるようにも思いますが、もちろん機能はこれだけではありません。次から、 Atlantis の好きなところを紹介していきます。

## Atlantis のここが好き！

### merge 前に apply ができる

例えば Terraform Cloud では、 merge をトリガーとして `terraform apply` が始まりますが、 Atlantis は merge 前に `apply` するスタイルが基本です。好みが分かれるかもしれませんが、僕はこれが好きなポイントの1つです。

というのも、 `apply` は Fail する可能性が常にあります。従って merge 後 `apply` のワークフローでは、 GitHub Flow が言うところの「デフォルトブランチが常に deploy 可能」な状態を保てないおそれがあります。 merge 後に `apply` が失敗し、デフォルトブランチが「壊れた」状態になれば、下手をすれば、そこで他メンバーの開発にストップをかけなくてはならなくなります。

merge 前 `apply` ですと、デフォルトブランチは常に deploy されたものと同期が取れた状態を保てます。延々と Fail して PR がなかなか merge できない、ということにも成り得ますが、例えばアプリケーション開発において、テストの通っていないブランチを merge するか？と考えれば、それは致し方ないことと言えます。僕としてはこちらの方が安心感があって好きです。

### lock 機能も持っている

Terraform の運用では、複数人が同時に `plan` や `apply` を行うと結果に不整合が出るため、 `state lock` を行うことが推奨されています。 

Atlantis では `state lock` は行いませんが、 PR が作成されて `plan` が開始された時点で、その対象 workspace を Atlantis 上で lock し、実質的に `state lock` と同様の効果が臨めます。 lock されている workspace に対し、別の変更をかけた PR を並行で作ると、どの PR が lock を取っているかをコメントで教えてくれます。

![スクリーンショット 2021-12-06 20.08.50.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/19465/d40d82d7-a280-c88a-0ba1-966491b1ca92.png)

なお、必要であれば `atlantis unlock` コマンドや、 GUI を用いることで lock の解除が可能です。

### 構築と設定が（個人的には）簡単

Atlantis サーバーは自前運用が必要です。イマドキ operation 用のサーバー構築・運用なんてなるべくやりたくないところですが、 Atlantis は構築が簡単になるよう [Helm Chart](https://github.com/runatlantis/helm-charts/tree/main/charts/atlantis) や [Terraform module](https://registry.terraform.io/modules/terraform-aws-modules/atlantis/aws/latest) などの手段が用意されており、ハードルはある程度下げられています。僕が実際に使ったことがあるのは Terraform module だけですが、1時間もかからずに構築を終えられました。構成としては ECS Fargate 上に展開し、 ALB 経由でアクセスする形になります。

また設定もわりと簡単な部類だと思います。構築後、 GitHub との連携は GitHub App を設けることと、 PR の連携のために GitHub webhook を Atlantis の `/events` に送ってあげるという、主に2点で設定します。 Terraform を管理しているレポジトリを Atlantis 管理下に入れるには、レポジトリ内に `atlantis.yaml` を置くだけです。 

```yaml
version: 3
projects:
- dir: project1
  autoplan:
    when_modified: ["*.tf*"]
```

この設定で、 `./project1` 配下の `.tf` ファイルが更新されたら自動で `plan` が走るようになります。

一度建ててさえしまえば、あとは YAML に数行追加していくだけでワークフローが自動化できるので、運用としては非常に楽だと感じています。

### ワークフローをカスタマイズできる

ワークフローにはカスタマイズの余地があります。

例えば `atlantis apply` を実行可能な条件として `approved` と `mergeable` を指定できます。前者は PR が approve されていることが条件となり、後者は GitHub の status check が通って mergeable になっていることが条件になります。無秩序な　apply はこれで防ぐことができます。

他にも、 `terraform plan/apply` 以外の任意のコマンドをワークフローに追加できたり、 [Conftest](https://www.conftest.dev/) による Policy Check を盛り込めたりなど、カスタマイズの対応は多岐に渡ります。これらの設定も基本的には `atlantis.yaml` の編集で行っていきます。

基本的なワークフローはデフォルトで設定されるので、ただ自動化だけが目的ならシンプルに扱えますし、その上で物足りない部分があれば自由にカスタマイズすることもできるという振れ幅は大きな魅力です。

### Atlantis が `required_version` を読んでくれる

Terraform の `required_version` を upgrade したのに、自動化ワークフロー側で使う Terraform Version の更新を忘れて `plan` がコケる、というのは Terraform 自動化あるあるだと思っています。

Atlantis の場合、 `required_version` で `=` により固定的にバージョン指定していれば、そのバージョンを読んで自動的に使ってくれるので、設定の手間がありません。個人的には `=` 指定で特に差し支えがないと思っています。バージョンアップも [renovate](https://docs.renovatebot.com/modules/manager/terraform/) などを使えば自動化可能ですし、最近は Atlantis にあわせて `=` 指定を取ることが多くなりました。

### terraform plan 結果ハイライトに diff を使っている

地味ですが、最初見たときは天才の発想だと思いました。要はこうなります。

![スクリーンショット 2021-12-06 20.47.53.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/19465/abd65a45-4f75-e1d7-92fa-d94637637b66.png)

## Atlantis のここがつらい

つらみポイントも書いておきます。

### merge 前に apply を忘れる

merge 前に `apply` を行うのは手動ですので、つい忘れることがあります。 Atlantis は PR 抜きにして任意のタイミングでの手動実行には対応していないので、こうなるとちょっと面倒です。

GitHub の status check で `apply` の成功を含めることもできます。が、その場合先述の `mergeable` を apply 条件として設定すると、 apply 前は mergeable ではないので apply ができないという鶏卵状態になるという困った問題がありました。

この点、最近改善の動きはあり、 [feat: filter out atlantis/apply from mergeability clause by nishkrishnan · Pull Request #1856 · runatlantis/atlantis](https://github.com/runatlantis/atlantis/pull/1856) で apply 前の mergeability check から apply 自身を外すようになったっぽいです。まだ試せてないので、今後試してみたいです。

### GUI に認証の仕組みがない

Atlantis には GUI も存在しており、現在 lock されている workspace 一覧の表示と、その中から任意の workspace を unlock する操作ができます。 unlock 操作は PR 上にコメントを書くことでも可能なので、実運用の中で GUI を利用する機会はほとんどありませんし、クリティカルな情報が表示されるものでもないのですが、筒抜けの状態で運用するのもあまり良い気分ではありません。しかしながら Atlantis には GUI 認証の仕組みが長らくありませんでした。

回避手段は存在しており、先述の Terraform module では Amazon Cognito を使った認証の仕組みを付与できるようになっていますし、自前で IP 制限したり、 ALB や CloudFront で BASIC 認証を設けることも可能でしょう。また、こちらも最近改善されているところで、 BASIC 認証のサポートが [feat: add BasicAuth Support to Atlantis ServeHTTP by fblgit · Pull Request #1777 · runatlantis/atlantis](https://github.com/runatlantis/atlantis/pull/1777) で Atlantis 本体に組み込まれました。

### Progress が見えない

`plan` や `apply` の　Progress を見る仕組みがありません。ですので、長くかかる `plan` や `apply` を仕掛けると、しばらく Atlantis が何の応答もしなくなるので不安になります。本気で不安になったときは Atlantis サーバーのメトリクスを見に行って、 CPU がゴリゴリ使われているのを見て安堵するときもあります。

実行が遅いと感じるならサーバースペックを積めばある程度解決する話でもあるので、その点はセルフホストする故のメリットとも言えるかもしれません。

### Docs に書いていない仕様もある

これは OSS ではよくあることですし、人によっては特につらみでもないと思います。たまにですが、 Docs に記載がなくて、 Issues を探ったりコードを読んだりして初めて気付く仕様はあったりします。例えば先述した BASIC 認証が実装された際、 LB の Health Check はどうすればいいのだろうと調べているうちに [/healthz を認証対象から除外した](https://github.com/runatlantis/atlantis/pull/1896) という PR に辿り着いたのですが、Health Check 用の endpoint が切られていること自体、このとき初めて知りました（もし Docs 内など見逃していたらすみません）。

まぁ Docs が本当に足りないと思ったら PR すればよい話ですし、 OSS に慣れていればそれほど気にする話ではないとは思います。

## まとめ

実は、この記事を書いている最中に　[tfmigrate + Atlantis でTerraformリファクタリング機能をCI/CDに組み込む - Qiita](https://qiita.com/minamijoyo/items/36d89cab4cd9f26fc098)　という記事が上がって拝見し、「あ、一部被った内容の記事を上げてしまうかもしれない……申し訳ない……」と気付いたのですが、そのまま上げさせてもらいました。 Atlantis 推しの記事はそれほど多くないので、増えれば増えるほど嬉しいと言うのが個人的な思いです。僕の記事は基本的な内容しか書けていませんので、より Atlantis の拡張性などを実感したい場合には是非こちらの記事を読んでいただければと、勝手に勧めさせてもらいます。


Atlantis は「大事なことに注力する」ために使える OSS だな、というのが僕の印象です。ワークフローを一から考えてメンテするのはあまりやりたくなくて、 Terraform Cloud と同様、ツール側でうまいことやってくれるほうが好みです。その上で、自分たちに必要なワークフローを上に積み重ねながら使っていける、必要ならば Issues を開いたり PR を投げながら使っていけるというのはとてもワクワクするところでもあります。

Terraform 自動実行環境の整備に Toil 感を覚えていたりしましたら、是非一度検討してみてはいかがでしょうか。