自作Terraform Provider用のGitHub Actionsを自作して公開してみた

## はじめに

[Terraform用のGitHub Actions](https://www.terraform.io/docs/github-actions/getting-started/)は2018/10くらいからあるようなのですが、考えてみると今回自作した [Terraform provider for Enterprise Cloud](https://github.com/nttcom/terraform-provider-ecl) に使えるのだろうか・・・とちょっと検証してみて、比較的すぐだめだとわかりました。

理由は [Community Provider](https://www.terraform.io/docs/providers/type/community-index.html) の場合、そのバイナリを挿入する方法がないこと。

基本GitHub ActionsはDockerイメージの上で動くわけですが、そこから呼ばれている `entrypoint.sh` にはそのバイナリをプラグインパスに対して送り込む方法がないんですよね・・・。
[このへん](https://github.com/hashicorp/terraform-github-actions/tree/master/plan)とか見るとそういう事がわかります。

しょうがないんで勉強も兼ねて自作することにしました。

## やってみたこと

今回は以下の思想で自作Provider用のGitHub Actionsを準備することにしました。

- GitHub Actionsから呼び出されるイメージは、基本hashicorp/terraformベースのものにする
- イメージビルド時は、Dockerfile内で、コンパイル済みの自作プロバイダのバイナリを `/` においておくように処理をしておく
    - GitHub Actions経由で上記イメージがrunされる際、どうやら `/github` が `-v` 経由でマウントされている。かつ、ここが `$HOME` 相当になってしまうため、イメージビルドの段階で `~/.terraform.d/` 配下にプロバイダのバイナリをおいておく意味がないように見える(実際の挙動はコードレベルでちゃんと確認しておきたいが・・・)
- GitHub Actionsの各サブコマンド(`plan` , `validate` ...)毎にentrypoint.shを作っておき、その中で、事前に `/` においておいたバイナリをプラグインの所定のディレクトリである、 `~/.terraform.d/plugins/linux_amd64` に `コピーする` という処理を毎回入れる
- その後に `terraform init` や `terraform apply` 等が動くようにすることで、プロバイダが見つからない問題を回避する

改善点は多々ありますが、一旦これで実現してみようかと思います。

## ベースのイメージを作るためのDockerfileを用意

まず [ここ](https://github.com/keiichi-hikita/terraform-provider-ecl-github-actions-baseimage) にベースのイメージ用のDockerfileとentrypoint.shを作ります。

(`FROM:`) は `hashicorp/terraform:0.12.3`にしてます。

実際にGitHub Actions側に仕込むイメージは、さらにこれを継承して作るイメージですね。

それ用のイメージにLABELを付与することでworkflowのアイコンがそれっぽくなるので。

ということで、繰り返しになりますがこれはベース用です。

## Docker Hubと連携

このリポジトリを Docker Hubと連携しておき、ビルドは自動化します。

まずGitHub AccountとConnectして、その後リポジトリを作ります。
今回は、 `keiichihikita/terraform-ecl` という形にしました。

次にこのリポジトリに対してビルドの設定を追加します。

<img width="1106" alt="スクリーンショット 2019-06-28 10.26.59.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/317673/7d4cbcea-ccda-af6b-54f3-df949dcef04e.png">

とりあえず↑のような感じにしました。

で `Save` して、手動で `Trigger` してみます。
どうやら上手く作れたようです。 `SUCCESS` となりました。

<img width="1106" alt="スクリーンショット 2019-06-28 10.27.44.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/317673/e1ec7cd2-399d-1083-a651-e60ea3737f7f.png">


## GitHub Actionsのリポジトリを用意

GitHub Actionsの各アクションはつまるところリポジトリです。

[こんな](https://github.com/keiichi-hikita/terraform-ecl-github-actions) 感じで作ってみました。
ほとんど本家のパクリですが。

変えたのは大きく２点です

- ベースのイメージを変えてます。例えば [こんな](https://github.com/keiichi-hikita/terraform-ecl-github-actions/blob/master/plan/Dockerfile#L1) 感じです。
- 最初に書いた、プロバイダのバイナリを `$HOME/.terraform.d/plugins/linux_amd64` 配下に持ってくる処理を追記しています。[こんな](https://github.com/keiichi-hikita/terraform-ecl-github-actions/blob/master/plan/entrypoint.sh#L37-L41) 感じです。

## このアクションを使ってみる

特にwebhookとかが存在していないリポジトリを別に作って、そこにPRを投げて動作確認をします。
(これはプライベートリポジトリで準備してますので、構成はなんとなく以下から推測していただければ・・・)

### main.workflowの準備

GitHub Actionsを動かすためには、 `.github/main.workflow` をおいておく必要があるようで。
これはブラウザからGUIでも作れますし、普通にファイルを `HCL` で作るのでもどちらでもOKです。
(以外だったのはHCLで書かれている点でしたね）

基本的な流れは、[こちら](https://www.terraform.io/docs/github-actions/getting-started/)にかかれているのですが、ちょっと考えるべき点だけを引用しながら以下に書いておきます。

> Find the latest version from https://github.com/hashicorp/terraform-github-actions/releases and replace all instances of @v<latest version>. For example: uses = "hashicorp/terraform-github-actions/plan@v3.0".

自分の場合は、[自分の用意したGitHub Actions](https://github.com/keiichi-hikita/terraform-ecl-github-actions) のmasterを見るように変更しています

> Directories — If your Terraform configuration is not in the root of your repo, replace all instances of

自分の場合は、`tf` ファイル群をおいている（要はGitHub Actionsを適用しているリポジトリが）トップレベルに `tf` ファイルがある構成になっているので特に考慮は不要です。
実際、以下のような形です。

```
% tree .                                                                                                                                                               .
├── main.tf
├── provider.tf
└── terraform.tf
```

> Workspaces — If your Terraform runs in a different Terraform workspace than default, change the TF_ACTION_WORKSPACE environment variable in the terraform-plan action.

`Terraform Cloud Free Tier` の場合、workspaceは使えませんのでこれもdefaultのままにしてあります。

ということで、最終的に `main.workflow` はこうなりました。

```go
workflow "Terraform" {
  resolves = "terraform-plan"
  on = "pull_request"
}

action "filter-to-pr-open-synced" {
  uses = "actions/bin/filter@master"
  args = "action 'opened|synchronize'"
}

action "terraform-fmt" {
  uses = "keiichi-hikita/terraform-ecl-github-actions/fmt@master"
  needs = "filter-to-pr-open-synced"
  secrets = ["GITHUB_TOKEN"]
  env = {
    TF_ACTION_WORKING_DIR = "."
  }
}

action "terraform-init" {
  uses = "keiichi-hikita/terraform-ecl-github-actions/init@master"
  needs = "terraform-fmt"
  secrets = ["GITHUB_TOKEN", "TF_ACTION_TFE_TOKEN"]
  env = {
    TF_ACTION_WORKING_DIR = "."
  }
}

action "terraform-validate" {
  uses = "keiichi-hikita/terraform-ecl-github-actions/validate@master"
  needs = "terraform-init"
  secrets = ["GITHUB_TOKEN", "TF_ACTION_TFE_TOKEN"]
  env = {
    TF_ACTION_WORKING_DIR = "."
  }
}

action "terraform-plan" {
  uses = "keiichi-hikita/terraform-ecl-github-actions/plan@master"
  needs = "terraform-validate"
  secrets = ["GITHUB_TOKEN", "TF_ACTION_TFE_TOKEN"]
  env = {
    TF_ACTION_WORKING_DIR = "."
    # If you're using Terraform workspaces, set this to the workspace name.
    TF_ACTION_WORKSPACE = "default"
  }
}
```
図にするとこんな形になっています。（途中までしか見えない）
アイコンを青くしています（本家のは紫）が、これは各アクションのDockerfileのLABELで変えてます。

<img width="1659" alt="スクリーンショット 2019-06-28 13.46.16.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/317673/4f1928cc-87bf-284a-a1a1-75a99138f4ca.png">

## 実際にPull Requestを投げてみる

`main.tf` に以下のような変更を加えてPRします。

```go
resource "ecl_compute_keypair_v2" "kp1" {
  name = "kp1"
}
```

うまくいくと、以下のような感じで処理が順次流れていきます、。

<img width="811" alt="スクリーンショット 2019-06-28 13.41.31.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/317673/93e474c5-e0a6-bcd7-d3a9-d41a31a3b8b0.png">

で、最終的にコメント欄がこうなります。

<img width="1052" alt="スクリーンショット 2019-06-28 13.41.25.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/317673/10576a79-b431-7991-4462-971e9e6bfdbe.png">

はい、無事Planまでたどり着くことができました :tada:


## 感想

GitHub Actionsのdeveloper向けドキュメントをもっと読みたい。

個人的には純正アクションにプロバイダのバイナリを放り込む入り口があればいいなぁと思いますね。
ただ `entorypoint.sh` で毎回wgetとかしちゃうといちいち遅いので、本体のイメージには組み込めないだろうし難しいところだなぁと思います。
どういう方法がベストなのだろうか・・・。
多分Officialではないプロバイダをお持ちの方は同じ悩みを持っているのではないでしょうか。

この感じだと、[前回書いた](https://qiita.com/keiichi-hikita/items/1b8fc103496371971fce) のですが、Atlantisのほうが環境は準備しやすかったし、使い勝手も良さそう。

まぁ、そのうち `Terraform Cloud Free Tier` から出来るようになるはずなので、そうなれば多分それが最強なんじゃないかなと思ったり。

ということで、かなりレアだとは思いますが、

- Enterprise CloudをTerraformで構築したい
- そのCIをGitHubだけで完結させたい

という方は是非お使いください。

・・・考えてみるとstateがTerraform Cloudな状態でしか試験してないけど・・・。
多分大丈夫！