HCP Terraform の GitHub 連携: VCS-driven workflow の設定

インフラの変更は、アプリのコードと同じように PR でレビューしてから反映したい。
HCP Terraform の workspace を GitHub のリポジトリにつなぐと、run(plan や apply の実行)を GitHub の操作から始められる。
PR を出すと plan が実行され、マージすると apply まで進む。
この記事では、workspace をリポジトリにつないで、次の 3 つを確かめる。
- PR を出したときに始まる run と、PR に表示される結果
- main にマージしたときに始まる run が、承認なしで apply まで進むこと
- つないだあとは、ローカルの
terraform applyが断られること
前提
workspace tfc-study-stg が AWS の S3 バケットを 1 つ管理していて、run はローカルの terraform コマンドから始めている状態(「HCP Terraform の AWS 連携: Dynamic provider credentials の設定」を参照)から始める。
VCS-driven workflow の仕組み
VCS は version control system の略で、この記事では GitHub のこと。
HCP Terraform の run を始める形は 2 つある。
- CLI-driven workflow
- ローカルの
terraform planやterraform applyが run を始める - run に使う
.tfは、コマンドがローカルのディレクトリから送る
- ローカルの
- VCS-driven workflow
- リポジトリへの push や PR が run を始める
- run に使う
.tfは、HCP Terraform がリポジトリから取得する
この記事では、CLI-driven workflow の workspace を VCS-driven workflow に変える。
GitHub の操作と run の対応
workspace は、リポジトリの 1 つのブランチ(この記事では main)につなぐ。つなぐと、GitHub の操作で次の run が始まる。
| GitHub の操作 | 始まる run | apply |
|---|---|---|
| main 宛ての PR を作る、更新する | plan だけの run(speculative plan) | 進めない |
| main に push する、PR をマージする | plan と apply の run | plan のあと、承認を待つか自動で進む |
push や PR があったことは、GitHub が HCP Terraform に通知(webhook)する。
設定するもの
GitHub に 1 つ、HCP Terraform の workspace に 2 つ設定する。
- GitHub App
- HCP Terraform 用のアプリで、名前は Terraform Cloud。GitHub のアカウントにインストールする
- HCP Terraform がリポジトリの中身を読み、push や PR の通知を受け取れるようになる
- VCS connection
- workspace の設定。run に使う
.tfをどのリポジトリのどのブランチから取得するかを決める
- workspace の設定。run に使う
- auto-apply
- workspace の設定。plan が成功した run を、承認を待たずに apply まで進める
run に使う .tf を GitHub のリポジトリに置く
workspace につなぐリポジトリを GitHub に作り、main ブランチに次のファイルを置く。
.tfファイル.terraform.lock.hcl(provider のバージョンを固定するファイル).gitignore(.terraform/をリポジトリに入れないため)
この記事で変更するのは、S3 バケットを定義している main.tf。
resource "random_pet" "name" {
length = 2
}
resource "aws_s3_bucket" "study" {
bucket = "tfc-study-${var.environment}-${random_pet.name.id}"
tags = {
Environment = var.environment
Owner = var.owner
}
}
リポジトリの .tf は、workspace の state と差分がない状態にしておく。つないだ直後の run で、意図しない変更が出ないようにするため。
GitHub の操作で run が始まるように workspace をリポジトリにつなぐ
HCP Terraform の画面で workspace を開き、左のメニューの Settings → Version Control から設定する。
- Connect to version control を押し、Version control workflow を選ぶ
- 接続先に GitHub.com を選ぶ
- リポジトリの一覧から、
.tfを置いたリポジトリを選ぶ - 設定を確認して Update VCS settings を押す
GitHub App にリポジトリを見せる
-
の一覧に出るのは、GitHub App(Terraform Cloud)が見えるリポジトリだけ。一覧が空のときは、GitHub で次の設定をする。
-
GitHub の Settings → Applications で、Terraform Cloud の Configure を開く
-
Repository access で Only select repositories を選び、つなぎたいリポジトリを追加する
アプリに見せるリポジトリは、このように使うものだけに絞れる。
VCS connection の設定
- の画面には次の設定がある。この記事ではすべて既定のままにした。
| 設定 | 既定 | 意味 |
|---|---|---|
| Terraform Working Directory | リポジトリの直下 | terraform を実行するディレクトリ |
| Automatic Run Triggering | Always trigger runs | run を始めるきっかけになるファイル。既定はすべてのファイル |
| VCS Branch | 既定のブランチ | push で run を始めるブランチ |
| Automatic speculative plans | 有効 | PR で plan だけの run を始めるか |
つないだ直後に始まる run
つなぐと、HCP Terraform が main の最新のコミットを取得し、run が 1 つ始まる。

一覧から読み取れることは 3 つ。
- run の名前はコミットメッセージになる
- triggered via GitHub と、ブランチ、コミットのハッシュが表示される。どのコミットの
.tfで実行した run かが分かる - リポジトリの
.tfと state に差分がないので、plan だけで終わっている(Planned and finished)
リポジトリ以外から変更が入らないようにローカルの apply は断られる
リポジトリにつないだ workspace では、ローカルの terraform apply を実行できない。
$ terraform apply
╷
│ Error: Apply not allowed for workspaces with a VCS connection
│
│ A workspace that is connected to a VCS requires the VCS-driven workflow to ensure that the VCS remains the single source of truth.
╵
ローカルの .tf をそのまま apply できると、main にない変更が AWS に入ってしまう。apply の入口をリポジトリだけにすることで、それを防いでいる。
terraform plan は実行できる。plan だけの run で、state を変えないため。PR を作る前に、ローカルで差分を確かめたいときに使える。
マージで apply まで進めるために auto-apply を有効にする
VCS-driven workflow の run は、既定では plan のあとで止まり、画面の Confirm & Apply が押されるのを待つ。
マージしたらそのまま反映したい workspace では、Settings → General の Auto-apply を有効にする。
有効にすると、plan が成功した run は承認を待たずに apply まで進む。
マージで始まる run が plan で止まるか apply まで進むかは、この設定 1 つで決まる。
マージ前に差分を確かめるために PR を出す
バケットにタグを 1 つ追加する変更で PR を作る。main.tf の tags に ManagedBy の行を追加する。
tags = {
Environment = var.environment
Owner = var.owner
ManagedBy = "terraform"
}
ブランチ add-managed-by-tag にコミットして push し、main 宛ての PR を作る。
PR を作ると、起きることは 5 つ。
sequenceDiagram
participant me as 自分
participant gh as GitHub
participant hcp as HCP Terraform
participant env as run の実行環境
me->>gh: 1. PR を作る
gh->>hcp: 2. PR ができたことを通知する
hcp->>gh: 3. PR のブランチの .tf を取得する
hcp->>env: 4. plan だけの run を実行する
hcp->>gh: 5. 結果を PR に返す
- main 宛ての PR を作る
- GitHub が HCP Terraform に通知する
- HCP Terraform が、PR のブランチ(
add-managed-by-tag)の.tfを取得する - その
.tfと workspace の state で、plan だけの run を実行する - plan が成功したかどうかを、PR に返す
PR に表示される結果
PR のページのチェックの欄に、HCP Terraform の結果が 1 行出る。

- 名前は
Terraform Cloud/<organization>/repo-id-<ID>の形 - plan が成功すると Success になる
- PR に出るのは成功か失敗かだけ。plan の中身は、この行のリンクから HCP Terraform の run のページを開いて確かめる
plan だけで終わる run
PR から始まった run のページは次のとおり。

- triggered a pull request run (#1) from GitHub と表示される。PR の番号が入る
- plan は
0 to add, 1 to change, 0 to destroy。バケットのtagsにManagedByが追加される - 状態は Planned and finished。apply の段階はなく、Confirm & Apply も出ない
ページの下には次の文が出る。
This run was started from a pull request and cannot be applied. To apply these changes the pull request must first be merged into the default branch.
(この run は PR から始まったもので、apply できない。変更を apply するには、先に PR を既定のブランチにマージする必要がある)
PR の run は、マージしたら何が起きるかを見せるだけ。state も AWS のバケットも変えない。
マージして apply まで進むことを確かめる
PR をマージすると、main に新しいコミットが入り、run が始まる。

- run の名前はマージコミットのメッセージ。triggered a run from GitHub と表示される
- plan の結果は PR のときと同じ
1 to change - Confirm & Apply で止まらず、そのまま apply に進んで Applied で終わる。auto-apply を有効にしたため
AWS のバケットを確かめると、タグが追加されている。
$ aws s3api get-bucket-tagging --bucket tfc-study-stg-national-starfish
{
"TagSet": [
{
"Key": "Environment",
"Value": "stg"
},
{
"Key": "Owner",
"Value": "<owner>"
},
{
"Key": "ManagedBy",
"Value": "terraform"
}
]
}
HCP Terraform の画面でも、ローカルの terraform コマンドでも操作していない。PR のマージだけで AWS に反映された。
PR のチェックの欄は、plan の結果のまま変わらない。HCP Terraform は apply の結果を PR に返さない。
apply が成功したかは、HCP Terraform の run のページで確かめる。
全体像
CLI-driven workflow と VCS-driven workflow を並べる。
| CLI-driven workflow | VCS-driven workflow | |
|---|---|---|
| run を始めるもの | ローカルの terraform plan、terraform apply |
PR(plan だけ)、main へのマージ(plan と apply) |
run が使う .tf |
ローカルのディレクトリのもの | リポジトリのコミットのもの |
| apply の前の承認 | ローカルで yes、または画面の Confirm & Apply |
auto-apply が有効なら不要。無効なら画面の Confirm & Apply |
ローカルの terraform apply |
実行できる | 断られる |
ローカルの terraform plan |
実行できる | 実行できる |
| 変数、state、AWS の認証 | workspace のもの | workspace のもの |
PR とマージで動いたものは次のとおり。
flowchart LR
me[自分]
subgraph gh[GitHub のリポジトリ]
pr["PR<br>ブランチ add-managed-by-tag"]
main["main ブランチ"]
check["PR のチェックの欄"]
end
subgraph hcp[HCP Terraform]
subgraph ws["workspace: tfc-study-stg"]
state[state]
end
specrun[["plan だけの run"]]
run[["plan と apply の run<br>auto-apply で承認なし"]]
end
subgraph aws[AWS]
bucket[("S3 バケット")]
end
me -- "PR を作る" --> pr
me -- "マージする" --> main
pr -- "通知" --> specrun
specrun -- "plan の結果" --> check
main -- "通知" --> run
run -- "タグを追加する" --> bucket
run -- "書き込む" --> state
図から読み取れることは 3 つ。
- 変更を入れるために操作するのは GitHub だけ
- PR から始まる run は、plan の結果を PR に返すだけで、AWS にも state にも書き込まない
- マージから始まる run が apply まで進むのは、workspace の auto-apply が有効なため
まとめ
- workspace を GitHub のリポジトリのブランチにつなぐと、run は GitHub の操作から始まる。run に使う
.tfは、HCP Terraform がリポジトリから取得する - PR を作ると plan だけの run が始まり、成功か失敗かが PR に表示される。この run は apply できない
- つないだブランチにマージすると、plan と apply の run が始まる。plan のあとで承認を待つか、そのまま apply に進むかは、workspace の auto-apply で決まる
- つないだ workspace では、ローカルの
terraform applyは断られる。terraform planは実行できる
参考 URL
- [official] UI and VCS-driven run workflow in HCP Terraform | Terraform | HashiCorp Developer
- [official] Configure workspace VCS connections in HCP Terraform | Terraform | HashiCorp Developer
- [official] Set up the GitHub.com (GitHub App) VCS provider | Terraform | HashiCorp Developer
- [official] Connect to VCS providers | Terraform | HashiCorp Developer
- [official] Workspaces settings in HCP Terraform | Terraform | HashiCorp Developer
- [official] The CLI-driven remote run workflow for HCP Terraform | Terraform | HashiCorp Developer
- [official] Remote operations in HCP Terraform | Terraform | HashiCorp Developer