GCPプロジェクトのリソースをTerraformで作成する　入門

### 公式のチュートリアル

公式が良い方はこちら。
https://cloud.google.com/community/tutorials/managing-gcp-projects-with-terraform

以降は私の簡略化した整理として、GCP × Terraformのはじめの一歩を記載している。

* * *

### 何が嬉しいか
- 環境をテンプレート化できるので、作ったり削除したりが1コマンドで出来る
- 複数環境（開発環境、検証環境、本番環境）を用意するときに横展開しやすい
- 環境と定義ファイルの差分が見れるので、いつの間にか設定が変わった（変えていた）ことに気付ける、すぐ直せる

* * *

### 前提

- GCPプロジェクトが作れる（GUIで作れる、またはgcloudコマンドが使える）、もしくは既に存在する
- terraformコマンドが使える
    - インストールはMacなら`brew install terraform`

* * *

### GCPプロジェクトを作る

Terraformで管理するGCPプロジェクトを用意しておく。GUIで作っても可。

```shell-session
$ gcloud projects create sandbox-terraform-xxxxx
Create in progress for [https://cloudresourcemanager.googleapis.com/v1/projects/sandbox-terraform-xxxxx].
Waiting for [operations/cp.8749279776492160000] to finish...done. 
```

* * *

### tfファイルを作って作業用フォルダに置く

tfファイルとはインフラリソースをテキストで定義したもの。拡張子は`.tf`。
https://www.terraform.io/docs/configuration/index.html

Terraformでは様々な環境（例えばAWS、Azure、OpenStack）のリソースごとにproviderプラグインが用意されている。
GCPのリソースをtfファイルで記述するには、google providerプラグインを用いる。
https://www.terraform.io/docs/providers/google/index.html

まずは最小限の設定と、試しにGCSのバケット定義のみを記載する。
Terraformとproviderの要求バージョンは最初から指定しておくのが良いと思う。

```main.tf
terraform {
  required_version = "0.11.8" # Terraformの要求バージョン
}

## project ##

provider "google" {
  credentials = "${file("account.json")}"
  project     = "sandbox-terraform-xxxxx"
  region      = "us-central1"
  version     = "1.17.1" # google providerプラグインの要求バージョン
}

### storage ###

resource "google_storage_bucket" "test-bucket" {
  name          = "sandbox-terraform-xxxxx-test" # バケット名
  location      = "us-central1"
  storage_class = "REGIONAL"
}
```

* * *

### Terraformのために初期設定する(terraform init)

tfファイルを含む作業ディレクトリを初期化する。
https://www.terraform.io/docs/commands/init.html
ここではproviderプラグインがインストールされている。

```shell-session
$ terraform init

Initializing provider plugins...
- Checking for available provider plugins on https://releases.hashicorp.com...
- Downloading plugin for provider "google" (1.17.1)...

Terraform has been successfully initialized!

You may now begin working with Terraform. Try running "terraform plan" to see
any changes that are required for your infrastructure. All Terraform commands
should now work.

If you ever set or change modules or backend configuration for Terraform,
rerun this command to reinitialize your working directory. If you forget, other
commands will detect it and remind you to do so if necessary.


$ tree .terraform/
.terraform/
└── plugins
    └── darwin_amd64
        ├── lock.json
        └── terraform-provider-google_v1.17.1_x4

```

* * *

### tfファイルをフォーマットする(terraform fmt)

規定されたフォーマットとスタイルに書き換えられる。
https://www.terraform.io/docs/commands/fmt.html

```shell-session
$ terraform fmt
main.tf
```

* * *

### 認証用JSONファイルの準備

GCPコンソール画面から、IAM→サービスアカウントでサービスアカウントを作成する。
https://console.cloud.google.com/iam-admin/serviceaccounts
権限を付与し、キーを作成する。
![スクリーンショット 2018-10-15 18.37.48.png](https://qiita-image-store.s3.amazonaws.com/0/175280/464cf942-1bae-c1b7-b123-5dbcae91e859.png)
![スクリーンショット 2018-10-15 18.38.21.png](https://qiita-image-store.s3.amazonaws.com/0/175280/7a087393-ce24-d7d8-9ae1-4619c3ade823.png)
![スクリーンショット 2018-10-15 18.38.52.png](https://qiita-image-store.s3.amazonaws.com/0/175280/da47c71c-bd8e-5c1e-b9a9-5433a93eb2fd.png)
![スクリーンショット 2018-10-15 18.38.59.png](https://qiita-image-store.s3.amazonaws.com/0/175280/e8d12a76-5672-b0d2-8fcd-94dc63821458.png)

キーがダウンロードできるので、account.jsonとしてtfファイルと同じフォルダへ配置する。

```
$ ls
account.json	main.tf
```

* * *

### 実行したときのplanを確認する(terraform plan)

このtfファイルで実行した際、何がどう変わるかという、実行プランが表示される。
https://www.terraform.io/docs/commands/plan.html

```shell-session
$ terraform plan
Refreshing Terraform state in-memory prior to plan...
The refreshed state will be used to calculate this plan, but will not be
persisted to local or remote state storage.


------------------------------------------------------------------------

An execution plan has been generated and is shown below.
Resource actions are indicated with the following symbols:
  + create

Terraform will perform the following actions:

  + google_storage_bucket.raw-data
      id:            <computed>
      force_destroy: "false"
      location:      "US-CENTRAL1"
      name:          "sandbox-terraform-xxxxx-test"
      project:       <computed>
      self_link:     <computed>
      storage_class: "REGIONAL"
      url:           <computed>


Plan: 1 to add, 0 to change, 0 to destroy.

------------------------------------------------------------------------

Note: You didn't specify an "-out" parameter to save this plan, so Terraform
can't guarantee that exactly these actions will be performed if
"terraform apply" is subsequently run.
```

* * *

### 確認したplanのとおりに適用する(terraform apply)

このtfファイルで実行した際、何がどう変わるかという、実行プランが表示される。
https://www.terraform.io/docs/commands/plan.html

```shell-session
$ terraform apply

An execution plan has been generated and is shown below.
Resource actions are indicated with the following symbols:
  + create

Terraform will perform the following actions:

  + google_storage_bucket.raw-data
      id:            <computed>
      force_destroy: "false"
      location:      "US-CENTRAL1"
      name:          "sandbox-terraform-xxxxx-test"
      project:       <computed>
      self_link:     <computed>
      storage_class: "REGIONAL"
      url:           <computed>


Plan: 1 to add, 0 to change, 0 to destroy.

Do you want to perform these actions?
  Terraform will perform the actions described above.
  Only 'yes' will be accepted to approve.

  Enter a value: yes

google_storage_bucket.raw-data: Creating...
  force_destroy: "" => "false"
  location:      "" => "US-CENTRAL1"
  name:          "" => "sandbox-terraform-xxxxx-test"
  project:       "" => "<computed>"
  self_link:     "" => "<computed>"
  storage_class: "" => "REGIONAL"
  url:           "" => "<computed>"
google_storage_bucket.raw-data: Creation complete after 1s (ID: sandbox-terraform-xxxxx-test)

Apply complete! Resources: 1 added, 0 changed, 0 destroyed.
```

* * *

### この先考えること

- 環境ごとの差異を表したい
- 基本的に使うリージョンやゾーンは固定なので、定数化したい
- `tfstate`ファイルをどう管理するか
- tfファイルを分割したい