Terraformを単品で使うのではなく、GitLabと一緒に使う方法

# すべてGitLabで完結してくれるので楽

Terraformを使う時、エディタで設定ファイルを用意し、適当なマシンにTerraformをインストールして実行する、というのが一般的ですが、GitLabを使うと、一歩(というか、数段)上の運用が可能になるので、ぜひおすすめしたいです。

# なにが嬉しいか

1.tfファイルのバージョン管理をお任せできる
2.stateファイルの補完もお任せできる
3.secret detectionも付いてくる
4.イシューをつかって、コラボしながら、修正・変更管理が可能

など、など、なにはともあれ、使ってみましょう。

# 具体的な手順

## 仕組みについて

まず、さらっと仕組みを説明します。

GitLabには2つの種類があります。GitLab SaaS版と、GitLab Self-Managed版です。
SaaS版は、まさにアカウント登録すると、すぐ使えます。Self-Managed版は、当たり前ですが、自分でどこかクラウドとかオンプレマシンを用意してインストールします。

SaaS版を使えば、文字通り、すべての環境がGitLab社におまかせできるので、かなり心理的に楽になります。Self-Managed版は、自分でインスタンスの面倒を見る必要があります。

次に、GitLabでTerraformを動かす、ってどういうことかを説明します。

Hashicorp社とか、Terraformの書籍を見ると分かりますが、まず適当なマシン(たとえば、自分のMacとか)にHomebrewとか、あるいはHashicorp社のサイトからインストーラーをダウンロードして、インストールします。そして、まず、きちんとインストールできたかどうかを確認して、次にmain.tfとか、ファイルを作って、お作法にしたがって、定義を書いていきます。AWSやらGCPやらAzureなどへのサービスアカウントのIDとかを埋め込んで、動かす、というやつですね。

GitLabで動かす、というのはどういうことかというと、このTerraformのインストール作業がまず要らなくなります。正確にいうと、GitLabのエージェント、とも言える、「Runner」というところで動かします。というか、動かしてくれます。そして、もう少し踏み込んで説明すると、このRunner上で、「TerraformがインストールされたDockerイメージを動作させて、その中で、自分がつくったTerraformの定義ファイル(main.tfなど)を実行する方法」と、「Runnerも所詮、Linux等のマシンなので、ここにTerraformのインストーラーで、Terraformをインストールして、動くことを確認して、main.tfなどを動かす」の２択があります。このご時世、後者はダサすぎるので、本記事では前者のDockerイメージで動かす方法を説明します。ダサいけど、後者しか選択できない方は、後述するinclude文をつかった手法が使えないので、自力で頑張ることになります。面倒なので、本記事では説明しません。

Runnerは、Linuxであったり、MacやらWindowsやらいろいろありますが、GitLabのSaaS版を使うと、普通になにも考えなければ、Linux版のRunnerで動かすことになります。これも、GitLabのSaaS上で用意してくれているので、私達としては、なにも考える必要はないです。そもそも、Terraformの定義を実行するマシンのOSなんて、なんでもよく、main.tf等の内容どおり、ミスらずに設定してくれればいいだけです。

逆に、Self-Managed版のGitLabを使うときは、自分でRunner用のマシンを別途用意し、GitLabサーバーとRunnerの連携設定も済ませておく必要があります。ここは先程説明した部分ですね。Dockerにするなら、Yum等でDockerが動くようにしておけばよいです。

## 設定開始 - GitLab SaaS版を使う場合

本記事では、GitLab SaaS版で説明します。

### Step 1. アカウント取得
Free版でもトライアル版でも、Premium/Ultimate版でもなんでもいいので、GitLab.comでアカウントを取得します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3325737/d9a045e4-2e37-55f5-b25b-9f2a08ea7f53.png)

### Step 2. グループ作成
アカウント作成時にグループを作成させられるので、それを使います。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3325737/119fea45-5244-1034-d61f-645c30b9fdd5.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3325737/ac422be6-be3c-6b8b-85b0-7ef15c9dc23b.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3325737/482506f9-1a0d-9a67-0020-c10255126cc0.png)

### Step 3. レポジトリに移動
もろもろ設定ファイルを仕込むので、レポジトリに移動します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3325737/b8c77b95-5c1c-9b10-386a-8c302cfe5409.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3325737/ce888da2-1b54-859b-8d75-03aed97a8b5a.png)

### Step 4. .gitlab-ci.yml ファイルを作成
GitLabは所詮バッチプログラムを実行しまくるエンジンみたいなもので、その親玉ファイルである「.gitlab-ci.yml」ファイルを作成します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3325737/25e89b8a-d9ae-47e6-8a1e-dd840ffa3793.png)
中身はこちらです。スペースとか気をつけてください。
```
include:
  - template: Terraform.latest.gitlab-ci.yml

variables:
 TF_STATE_NAME: default
 TF_CACHE_KEY: default
 TF_ROOT: terraform
```
ちなみに、ここでTF_ROOTで「terraform」って指定してますが、これは、このレポジトリ上で直下に(私は)main.tfとかを作るのが好きじゃなく、「terraform」というフォルダを作って管理したいから、こうしているだけです。なくてもよいです。
基本的に、このあたりの部分は、こちら( https://docs.gitlab.com/ee/user/infrastructure/iac/ )で説明しているので、がんばって読んでください。
ここで、
```
include:
  - template: Terraform.latest.gitlab-ci.yml
```
とありますよね。この指定があるので、このGitLabはDockerをつかって、Terraformの設定を実行する、という動作になります。

とりあえず、なにも頭を使わずに「Commit Change」して保存しましょう。

### Step 5. terraform フォルダを作成
次に、「terraform」フォルダを作ります。名前はなんでもいいですが、先程のTF_ROOTと同じ名前にしてくださいね。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3325737/f86c90ee-5ace-4103-4bb5-759c5cae6fca.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3325737/b1a8df58-336f-1559-a593-7f2e513688af.png)

### Step 6. main.tfファイルを作成
Terraformのお作法的に、昔はmain.tfだけでよかったのですが、いまはオシャレにファイルを分割するのが流行ってるみたいですね。ここでは、理解することだけにフォーカスしたいので、main.tfファイルに全部設定します。(矢印部分、マスキングしてます)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3325737/b4b28c6e-64d1-8309-ff77-612ec9ec17fb.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3325737/3de57579-393e-6c98-73cb-b9d82b9fb025.png)

このmain.tfファイルについては、ここでしっかり解説が必要なので説明します。
まず、
```
terraform {
  backend "http" {
  }
  required_providers {
    gitlab = {
      source  = "gitlabhq/gitlab"
      version = "16.0.3"
    }
  }
}
```
の部分ですが、ここの元ネタは、「https://registry.terraform.io/providers/gitlabhq/gitlab/latest」のここから来てます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3325737/8dc9066f-5774-d4f6-f138-d220eac6a853.png)
別に、GitLabのマニュアルとかみると、必ずしもこのバージョンではなく、もうすこし古いものだと思います。このサイトを参考にする理由は、基本的にはここは新しいものがいいので、新しいものを指定する(探す)方法を、ここでお伝えしています。

よくみると、カスタマイズを入れているのですが、まずこちらですね。
```
  backend "http" {
  }
```
これはなにか、というと、Terraformの世界の話になります。Terraformでは、設定に沿って実行させると、環境を構築しますが、その時の失敗やら成功やら、いまの設定状態やらなんでもかんでもstateファイルというファイルにjson形式で"再現時に利用するために記録"します。このファイルは、普通Terraformで設定をスタートさせた端末上に作成されるのですが、ここでこのようにbackend指定しておくと(httpは単なるプロトコルですが、まぁ、ここは深く考えなくてもよいです。)、そのstateファイルを作業用マシン(GitLabの世界でいうと、Runnerですね)ではなく、なんと、GitLab SaaS側で補完してくれます。
ちなみに、どこに保管してくれるかというと、ここです。![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3325737/a7c1d2e3-a9df-1d59-107d-d78c947dd543.png)
一通り設定して、実行して、成功したら、ここにアクセスしてみてください。stateファイルのダウンロードもできますから、それをもらって、どこかでお好きにどうぞ、な感じの運用も可能です。

次に
```
variable "gitlab_access_token" {
  type = string
}

provider "gitlab" {
  # Configuration options
  token = var.gitlab_access_token
}
```
の部分ですが、これは、そのstateファイルをGitLab SaaS側にアップロードするために、(Runnerから見て、外から、このGitLab SaaSにアクセスすことになるので)認証用としてトークンが必要になります。
ちなみにこのトークンは、GitHubやGitLabを使ったことがある人はわかると思いますが、プロファイル設定等からえられる、Access Tokenのことです。![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3325737/ad2cb814-3352-9f5b-2abb-467125ecb5d7.png)

次にマスキングしていた部分ですが
```
data "gitlab_project" "example_project" {
  id = xxxxxxxx
}
```
このxxxxxxxxも同じく、stateファイルの保存のために指定が必要で、いまつかっているプロジェクトのIDを指定します。このIDは、ここで取得できます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3325737/028fe776-0806-b297-e8b3-f34f99e35872.png)

そして最後に
```
resource "gitlab_project_variable" "sample_project_variable" {
  project = data.gitlab_project.example_project.id
  key     = "example_variable"
  value   = "Greeting Master!"
}
```
です。ここでは、単に適当な変数に値を設定している「だけ」の、まったく意味無しのアクションをTerraformにさせているだけです。
本来であれば、ここの部分に、めいいっぱい、例えば、インスタンスのサイズはどうの、CPU何個、メモリいくつ、などなど、好き放題、巨大な設定を書き連ねればよいです。

最後に「Commit Change」してコミット、保存します。

### Step 7. Access Tokenの設定
先程、Access Tokenの話をしましたが、あのmain.tfファイルの中で直書きしてもかまいません。しかし、昨今、Terraformの設定ファイルにシークレットを書くのはダメ(セキュリティ観点で)というのがあるので、外出しをしています。
その外出しですが、じゃぁ、どこで設定するか、というと、こちらです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3325737/d775f0a6-f88c-ebc4-38c7-65714a7b2cde.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3325737/8b2b322d-496a-f840-7cd6-f386e2240dcf.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3325737/574d022e-c0b5-7f84-ad43-b04c95743126.png)
ここで、
```
TF_VAR_gitlab_access_token
```
って、ちょっと違和感ありますよね。「TF_VAR_」を頭にPrefixとして付け加えることで、先程のmain.tf内では、いい感じのこのAccess_Tokenが代入されます。
こういうルールみたいなのは、「.gitlab-ci.yml」の中で指定した、includeファイルの中の奥で定義されています。

### Step 8. main.tfの実行
実は、ここに来るまでに、何度かGitLabは実行を試みています。これはGitLabの仕組みで変更があると常に実行する、みたいな動作をするのがデフォルトなのですが、もちろん、制御は可能です。
このあたりの話は、GitLabを詳しく学ばないといけないので、ここでは説明を省きます。

で、実際に実行してみましょう。
左のメニューから「Build-Pipeline」を選択してみます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3325737/57ab2bcd-28e9-db76-bfc6-84dcee3cdc89.png)
failedだらけだと思いますが、これが先程、変更加えた(レポジトリに変更加える毎に実行)際に、都度パイプライン(正確には、.gitlab-ci.ymlファイルの中身通りに動作した)を実行した様子です。どれも中途半端だったので、全部失敗しています。

この時点で、実は完璧な設定を完了し、最後にAccess Tokenという環境変数を定義したので、その後のパイプラインはまだ実行されていません。
ここで右上のRun Pipelineを実行します。このボタンは、GitLab、もっというと、Gitのmainブランチ(デフォルトブランチ)の中身と、.gitlab-ci.ymlを組み合わせて、処理を実行する、という意味になります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3325737/487a5170-72b7-788f-554f-4145e2213dab.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3325737/3a144a09-00ac-33f0-2ad4-17b258447a84.png)
ちなみに、「kics-iac-sast」は、IaCでいうところのSAST(セキュリティスキャン)をしています。シークレットとかをmain.tfに書いておくと、いろいろと検知してくれますよ。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3325737/d910ea0a-97da-86a3-ba6c-22a2f40d0c1f.png)
ここまで来れば完了です。いま、Deployだけ緑になっていませんね。
これは、mainブランチの場合、Deploy(最終的な実行)は手動キックにする、という、冒頭で指定した、Includeの定義ファイルの中にそれが指定されているのです。
なにも考えずにDeployさせるということもできますが、それはまた今度の機会に説明します。

ここでは、このDeployの部分の→部分をクリックします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3325737/2c7dabc1-2118-06e4-329f-963d0150a95d.png)
普通成功しますが、もしかすると、次のように失敗する場合があります(何度かやって、中途半端にTerraformのstateファイルができちゃった場合)。この場合は、stateファイルを削除します。![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3325737/2dd769ce-dafc-4d8d-1978-7ecfced7a7db.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3325737/8c16188d-4f63-ae1f-ee39-be603ad3f161.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3325737/23bb1eb2-f2d1-1cdf-f6fc-258eb36f50b4.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3325737/8143f511-a6d2-b335-2503-16294e8f948d.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3325737/74366995-fc1a-5bac-bcea-695ad58b3c2a.png)
そして、次のように、もう一度リトライします。![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3325737/b521e247-577c-6a75-0120-129c6c5aa69b.png)
最終的にこうなれば、OKです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3325737/fe8258f2-c4cd-29e5-2829-28d98e7bed68.png)

すばらしい。main.tfで「resource」なんたら、と書いた部分に、好き放題やっていただくのもよし、「terraform」フォルダ以下に、各種terraformの書籍等を参考に好き放題ファイルを分割をするのもよし、あとは好きにやってください。

# 準備はできましたね

どうですか、強いていえば、Terraformのmain.tfとかを書いて、テスト環境(VPCとか)で好き放題めちゃくちゃやって、仕上げて、完成したら、本番としたい、という感じですよね。
この場合、.gitlab-ci.ymlファイル内で、「ブランチ名がmainだったらproduction用のVPCを指定」「ブランチ名がそれ以外、とかxxxで始まる名前だったら、staging用のVPCを指定」みたいなことが可能です。

それらは、GitLabのymlファイルの文法のお勉強が必要なので、それはまた別の機会に。

本記事で、とにかく、GitLabでTerraformを動かすときに、必要なステップや流れを確認する際にご利用ください。

GitLabバンザイ。