GCPのVM環境をterraformで構築する

# terraformとは

[terraform](https://www.terraform.io/)とはHashiCorp社が提供するGo言語で書かれたInfrastructure as Code（IaC）ツールのオープンソースです。GCPやAWSのクラウドサービス上のインフラリソースなどをコードで定義し、定義したコードを用いてインフラリソースを自動で構築することが出来ます。
terraformを用いてインフラリソースをIaCにすることで、インフラリソースの人為的なミスを減らしインフラリソースの構築・管理のコストを削減することが出来ます。

# terraformを使って、GCE上にVM環境を構築する
## terraformのインストール

:::note warn
※Google Cloud SDKのバージョン357.0.0、Terraformのバージョン1.0.7を使用しています。（この記事記載時点の最新バージョンです）
:::

OS Xでは、homebrewを使用してterraformをインストールすることが出来ます。

```
brew install terraform
```

ターミナル画面から「terraform version」と入力して、バージョン情報が表示されたら正しくインストールされていると思います。

```
$ terraform version
Terraform v1.0.7
on darwin_amd64
```

## GCPの設定

PC端末にCloud SDKがまだインストールされてない方は、[こちら](https://cloud.google.com/sdk/docs/quickstart#mac)からCloud SDKをインストールしてください。

Cloud SDKをインストール時に「gcloud init」でSDKのセットアップを行った際に、認証したgoogleユーザアカウントとは別のアカウントで開発を行う場合は、以下コマンドから今回開発で使用したいgoogleアカウントの認証とプロジェクトIDの設定を行ってください。

```
# GCPにログイン
gcloud auth login
```

```
# ワーキングプロジェクト設定。各自設定しているプロジェクトリスト内の [PROJECT_ID]を設定
gcloud config set project [PROJECT_ID]
```

次に、Cloud SDKでログインしているgoogleユーザアカウントのプロジェクトで使用できるサービスを有効にします。プロジェクト内で有効化できるサービス一覧は以下コマンドで取得することが出来ます。

```
# 有効化できるサービス一覧の取得
gcloud services list --available
```

また、既にプロジェクト内で有効化になっているサービス一覧を取得する場合は以下コマンドで確認できます。

```
# 有効化されているサービスの取得
gcloud services list --enabled
```

今回は、terraformでGCのVM環境を構築するので以下サービスアカウントを有効にしておきます。

```
# プロジェクトのリソースに関するサービス
gcloud services enable cloudresourcemanager.googleapis.com

# リソースに対してロール(アクセス権限)を設定するサービス
gcloud services enable iam.googleapis.com

# プロジェクトで使用できるサービスの設定を行うサービス
gcloud services enable serviceusage.googleapis.com

# GCEの操作に関するサービス
gcloud services enable compute.googleapis.com

# コンテナベースのアプリケーションを構築および管理をこなうサービス
gcloud services enable container.googleapis.com

# ストレージ操作に関するサービス
gcloud services enable storage-component.googleapis.com
```

## GCPアクセス用のCredentialファイル設定

TerraformはGCPにアクセスするため、terraform専用のサービスアカウントを作成しCredentialファイル設定を行います。
まず、terraform用のサービスアカウントを作成します。

```
# サービスアカウント名「terraform-serviceaccount」を作成
gcloud iam service-accounts create terraform-serviceaccount --display-name "Account for Terraform"
```

※サービスアカウント「terraform-serviceaccount」が作成されているかどうか確認するにはGCPのweb管理画面の「IAMと管理」→「IAM」→「権限」の一覧から確認することが出来ます。

![terraform.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154154/19cbab20-bcfa-b660-36c2-79f80929a9aa.png)


次に作成したterraform用アカウントに対して、「Primitive roles（基本の役割）」の権限を付与します。広い範囲の権限を付与することとなるので、複数のアカウントユーザーで開発している場合は「Predefined roles（事前定義された役割）」や「Custom roles（カスタムの役割）」をすることをお勧めします。

```
# サービスアカウント「terraform」に「Primitive roles（基本の役割）」を追加
gcloud projects add-iam-policy-binding [PROJECT_ID] \
  --member serviceAccount:terraform-serviceaccount@[PROJECT_ID].iam.gserviceaccount.com \
  --role roles/editor
```

次に、作成したサービスアカウント「terraform-serviceaccount」に対してCredentialファイルを発行します。
以下コマンドを実行すると、json形式のCredentialファイルが「~/.ssh/terraform.json」に保存されますが、必要に応じて保存先のファイルパス・Credentialファイル名は変更してください。

```
# アカウントのCredentialファイルを発行
# Credentialファイル名は「terraform.json」とする
gcloud iam service-accounts keys create ~/.ssh/terraform.json \
  --iam-account terraform-serviceaccount@[PROJECT_ID].iam.gserviceaccount.com
```

こちらも任意になりますが、必要に応じて先ほど発行したCredentialファイルに対して環境変数を登録しておくと、今後の開発が楽になると思われます。

```
# 環境変数にCredentialファイルを設定する
export GOOGLE_CLOUD_KEYFILE_JSON=~/.ssh/terraform.json
```

## terraformを使ってGCEのVM環境の構築

まずは、terraformでインフラリソースを定義するためのコードを保存するディレクトリを作成します。
※これ以降は作成したディレクトリ「test_terraform」内での作業を想定として記載します。

```
mkdir test_terraform
```

次に、terraformでインフラリソースを構築するにあたってどのプロパイダーを使用するかを定義します。今回は分かりやすくするために、「provider.tf」というファイル名で作成しますが、「main.tf」などのファイル名として定義しても構いません。(作成するファイル「provider.tf」は先ほど作成したディレクトリ「test_terraform」内に保存してください。)

```provider.tf
# プロバイダーはgoogleを使用
# credentials　: 先ほど作成・保存したcredentialsファイル「terraform.json」のファイルパスを記載
#               ※環境変数にCredentialファイルを定義した場合はcredentialsの内容の記載不要
# project    : GCPで使用するプロジェクトIDを指定
# region     : リソースを管理するためのデフォルトのリージョンを指定。今回は「asia-northeast1」とする。

provider "google" {
  credentials = "${file("~/.ssh/terraform.json")}"
  project     = "[PROJECT_ID]"
  region      = "asia-northeast1"
}
```

terrraformこ公式ドキュメントに[Googleプロバイダー構成のリファレンス](https://registry.terraform.io/providers/hashicorp/google/latest/docs/guides/provider_reference)が記載されているので、詳細はそちらをご覧ください。

「provider.tf」が作成出来たら、ターミナル画面上で先ほど作成したフォルダ「test_terraform」に「cd」コマンドで移動してください。ファイル「provider.tf」があるディレクトリで以下コマンドを実行すると、terraformのワークスペースが初期化され、フォルダ「.terraform」やファイル「.terraform.lock.hcl」が作成されるのを確認できると思います。

```
terraform init
```

次に、今回作成するGCEのVM環境をterraformを用いてコードで定義します。今回構築するVM環境のコードはファイル名「compute_instance.tf」とします。

```compute_instance.tf
# GCE上で作成するVMの環境を定義します。
# name     : 構築するVM環境の名前を定義。今回は「test」とします。
# machine_type : マシンタイプを設定。今回は「f1-micro」とする。
# zone     : 構築するVMのゾーンを設定。今回は「asia-northeast1-b」とする。
# auto_delete : インスタンスが削除されたときにディスクが自動削除されるかどうか。デフォルトはtrue。
# image    : このディスクを初期化する元のイメージの設定。(OSイメージの定義)
# size     : ディスクのイメージサイズを設定(GB)。今回は10GBとする。
# type     : GCEのディスクタイプ(HHDかSSDなど)を設定。
# network  : 構築するVM環境が接続するネットワーク名(特にGCP上のネットワークを編集してなければdefaultでok)

resource "google_compute_instance" "default" {
  name          = "test"
  machine_type  = "f1-micro"
  zone          = "asia-northeast1-b"
  boot_disk {
    auto_delete = true
    initialize_params {
      image = "ubuntu-os-cloud/ubuntu-1804-lts"
      size = 10
      type = "pd-standard"
    }
  }
  network_interface {
    network = "default"
  }
}
```

上記で記載したterraformで定義したGCEのVM環境「compute_instance.tf」内に、コメントアウトで内容に関する説明を記載しましたが、そのほかの詳細は[こちらのリファレンス](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/compute_instance)を参照してください。

GCEのVM環境の設定を記載した「compute_instance.tf」を作成したフォルダ「test_terraform」に保存したら、以下コマンドでterraformを用いて定義内容のチェックを行ってください。

```
terraform plan
```

```.txt:コマンド実行結果
Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols:
  + create

Terraform will perform the following actions:

  # google_compute_instance.default will be created
  + resource "google_compute_instance" "default" {
      + can_ip_forward       = false
      + cpu_platform         = (known after apply)
      + current_status       = (known after apply)
      + deletion_protection  = false
      + guest_accelerator    = (known after apply)
      + id                   = (known after apply)
      + instance_id          = (known after apply)
      + label_fingerprint    = (known after apply)
      + machine_type         = "f1-micro"
      + metadata_fingerprint = (known after apply)
      + min_cpu_platform     = (known after apply)
      + name                 = "test"
      + project              = (known after apply)
      + self_link            = (known after apply)
      + tags_fingerprint     = (known after apply)
      + zone                 = "asia-northeast1-b"

      + boot_disk {
          + auto_delete                = true
          + device_name                = (known after apply)
          + disk_encryption_key_sha256 = (known after apply)
          + kms_key_self_link          = (known after apply)
          + mode                       = "READ_WRITE"
          + source                     = (known after apply)

          + initialize_params {
              + image  = "ubuntu-os-cloud/ubuntu-1804-lts"
              + labels = (known after apply)
              + size   = 10
              + type   = "pd-standard"
            }
        }

      + confidential_instance_config {
          + enable_confidential_compute = (known after apply)
        }

      + network_interface {
          + name               = (known after apply)
          + network            = "default"
          + network_ip         = (known after apply)
          + subnetwork         = (known after apply)
          + subnetwork_project = (known after apply)
        }

      + reservation_affinity {
          + type = (known after apply)

          + specific_reservation {
              + key    = (known after apply)
              + values = (known after apply)
            }
        }

      + scheduling {
          + automatic_restart   = (known after apply)
          + min_node_cpus       = (known after apply)
          + on_host_maintenance = (known after apply)
          + preemptible         = (known after apply)

          + node_affinities {
              + key      = (known after apply)
              + operator = (known after apply)
              + values   = (known after apply)
            }
        }
    }

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

　コマンド「terraform plan」では、「.tf」 ファイルに記載された情報を元に、どのようなリソースが 作成/修正/削除 されるかを参照することが出来ます。
　次にコマンド「terraform apply」を実行すると、GCP上に「.tf」ファイルに記載された情報を元にリソースが作成されます。

```
terraform apply
```

　コマンド実行中に以下のように「このアクションを実行しますか？(インフラリソースを構築しますか？)」とターミナル上で聞かれるので、「yes」を入力してください。

```
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
```

以下のような結果が表示されれば、terafformを使ってGCE上でVM環境が構築されると思います。

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

実際に、ブラウザ上のGCP管理画面のVM一覧から、今回構築したVM環境「test」が構築されることが確認できると思いますので、是非確認してみてください。

![スクリーンショット 2021-09-30 21.34.21.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/154154/d3ece046-f302-e72c-f50a-5fb3d6bd525c.png)

# まとめ

今回はterraformを使って、GCP上でVM環境を構築する方法を紹介しました。terraformを使えば他にも、ネットワークの設定やファイヤウォール・ストレージやデータベースなど、様々なリソースをコードで定義することが出来るので、今後も趣味・仕事で使ってるインフラリソースを徐々にコードで明記していきたいと思ってます。