Terraform インストールして GCP 向け設定と GCE でインスタンス構築

## はじめに
いままで IaC は Ansible しかやってなかった人が Terraform って何？ って状態からスタートして調べてやったことを記載する。
Terraform を動かす環境は、端末とサーバで対応できるように、Mac と CentOS 両方で実施した。
実際に Terraform で GCP 上に VM 1台を構築して挙動を確認した。

## 記載構成

下記の構成で記載している

- Terraform とは？
    - 個人的に Terraform とは？を調べた内容をまとめたもの
- 実施構成
    - 今回実施する構成 (GCP, stateファイル保存場所など)
- Terraform インストール
    - 端末(MacOS)、サーバ(CentOS) の２種類のインストール方法を記載
- GCP 向け環境設定
    - GCP 向けの設定、ステートファイル保存場所の作成
- GCP での VM インスタンス構築試験
    - VM インスタンスを1台構築、変更、削除、ライフサイクルを実際に試した内容

## Terraform とは？
### 概要
 - Infrastructure as Code (IaC) Tool
 - インフラストラクチャ定義ツール
    - [分類参照: [Infrastructure as Code クラウドにおけるサーバ管理の原則とプラクティス O'Reilly Japan](https://www.oreilly.co.jp/books/9784873117966/)]
 - 宣言的 (Declarative) 記載モデル
 - 記述言語は HCL (HashiCorp Language. 独自言語)
 - リソースをコード(.tf)で定義してを自動化
 - ステータスはステートファイル(.tfstate)を Terraform が生成・維持して構成管理
 - 開発は HashiCorp 社で、OSS として公開
 - Enterprise 版がある
    - Hashicorp による商用サポート
    - 機能の追加
        - GUI, Workspace などチーム機能, CI/CD 機能, などなど
    - Sentinel (有償版のみ) があり、Policy as Code 機能の提供
    - Terraform Cloud という SaaS 版もある (5名までは無料)
 - 様々なインフラに対応している


### Terraform の特徴・構成
[hashicorp japan の GitHub](https://github.com/hashicorp-japan/terraform-workshop-jp) でリンク公開しているスライドがわかりやすかったです
https://docs.google.com/presentation/d/1Ovdee0FIrJ_h66B5DToQNYKWJ9XRbudS0RCk4d_x1Eg/edit?usp=sharing

### Ansible との違い
Ansible は命令実行モデルとなり、サーバでのOSより上の構成管理を得意としているらしい([参考](https://www.lac.co.jp/lacwatch/service/20201216_002380.html))
分類としては、サーバー定義ツール [分類参照: [Infrastructure as Code クラウドにおけるサーバ管理の原則とプラクティス O'Reilly Japan](https://www.oreilly.co.jp/books/9784873117966/)] にあたるらしく、Terraform と領域を分ける

得意なレイヤを分けて使われているようです
[参照: [Infrastructure as Code クラウドにおけるサーバ管理の原則とプラクティス O'Reilly Japan](https://www.oreilly.co.jp/books/9784873117966/)]

- インフラストラクチャ定義ツール: Terraform
    - クラウドなどインフラ全体
    - 高い水準でインフラストラクチャをプロビジョニング、構成/設定する
- サーバー定義ツール： Ansible 
    - サーバー自体の細部を扱う (OSより上の構成管理)
    - ソフトウェアパッケージ、ユーザーアカウントなど、さまざまなタイプの構成/設定が含まれる

#### コンテナ環境で例えると
イメージとしてはコンテナ環境だと、ざっくり下記のような感じなのかなと個人的に思っています。
(右側はオートヒーリング等はなかったり(クラウド等インフラ側機能)、イメージ作成はPackerがあったり違いは色々あります)

 - Kubernetes ≒ Terraform
    - 宣言モデル. 全体の定義をする
 - Dockerfile ≒ Ansible 
    - 命令実行モデル. 単体の構成管理をする


### その他,インフラストラクチャ定義ツール
各クラウドベンダが下記のプロビジョニングツールを出している

| クラウドベンダ | プロビジョニングツール |
|:-:|:-:|
| AWS | [AWS CloudFormation](https://aws.amazon.com/jp/cloudformation/) |
| GCP | [Cloud Deployment Manager](https://cloud.google.com/deployment-manager?hl=ja) |
| Azure | [Azure Resource Manager](https://docs.microsoft.com/ja-jp/azure/azure-resource-manager/management/overview) |

上記と Terraform どちらを使うかが選択肢になりそうです。
Terraform で対応してない機能を使いたい場合は、各クラウドベンダ提供のツール、でなければ Terraform が良さげです。

下記画像の通り、情報量の多さとしては、terraform が多そうです。(2021.02.13時点)
※ Resource Manager は別の意図の検索も含まれているトレンド(Azure つけると下がりすぎたのでこれで比較した)

![スクリーンショット 2021-02-14 10.32.56.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/326183/70d47624-0246-af96-628d-77d1dfc556eb.png)


### ざっくり結論 (個人的な感想含む)
 - クラウドプロビジョニングには Terraform (インフラストラクチャ定義ツール)が便利そう
 - サーバ単体のミドルウェア以上は Ansible などで棲み分けで利用が良さそう
 - Terraform 競合は各クラウドベンダが出しているツールで、特別な事情がなければ Terraform で良さそう

## 実施構成

今回は下記のような構成で実施している

![terraform (1).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/326183/b64ca7c8-ae32-9d71-da15-1e2ed64c3056.png)

- Terraform インストール環境
    - 下記2種類
        - Local-PC(Mac)
        - Remote-Server (CentOS)
    - 今回、CI-Server での構築はしない (実際 CI/CD 管理では、CIサーバで GitOps などを実施されるようです)
- .tfファイル
    - 外部リポジトリで管理する予定(記載範囲外)
- ステートファイル(tfstate)
    - GCS (GoogleCloud Storage) に専用バケットを構築して保存・参照先にする
- provider
    - 各プロビジョニング先の provider が必要
    - 今回は hashicorp/google を使用する (init時に自動でインストールされる)


## Terraform インストール
terraform と必要になるツールのインストールを実施する

### 実施環境
MacBook Air (M1, 2020) : 手元端末で terraform を実施する想定
CentOS 8 : サーバ利用想定

### Mac でのインストール
Terraform を実行するためのインストール内容を記載します
Homebrew, Google Cloud SDK がインストール済みなら、「2. tfenv インストール」のみで対応可能です

1. Homebrew インストール
2. tfenv インストール
3. Google Cloud SDK インストール

#### 1. Homebrew インストール
[Homebrew のページ](https://brew.sh/index_ja)のインストールコマンドにしたがってインストール


```
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
```
※パスワード要求が一度されます

Install 完了後下記画面が出るので sh の profile に登録する

```sh:usernameは環境による
echo 'eval $(/opt/homebrew/bin/brew shellenv)' >> /Users/[username]/.zprofile
eval $(/opt/homebrew/bin/brew shellenv)
```

最後にバージョン確認しておく

```
% brew --version
Homebrew 3.0.0
Homebrew/homebrew-core (git revision fdab0; last commit 2021-02-11)
```

#### 2. tfenv install
複数の terraform を管理してくれる**tfenv**があるのでインストールする

```
brew install tfenv
```

現在 tfenv での terraform サポートバージョン確認する

```
tfenv list-remote
```

下記のように表示される (2021.02.11時点)

```
% tfenv list-remote
0.15.0-alpha20210210
0.15.0-alpha20210127
0.15.0-alpha20210107
0.14.6
0.14.5
0.14.4
0.14.3
0.14.2
0.14.1
0.14.0
0.14.0-rc1
0.14.0-beta2
0.14.0-beta1
0.14.0-alpha20201007
0.14.0-alpha20200923
0.14.0-alpha20200910
0.13.6
...
```

上記で alpha,beta,rc を外して安定板をインストールするので今回は**0.14.6**で実施する

```
tfenv install 0.14.6
```

下記のような画面が出るので、案内に従い use でインストールしたバージョンを指定する

![スクリーンショット 2021-02-11 11.50.57.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/326183/0b72da86-1862-a279-5f64-b9cb71e93f3d.png)

```
tfenv use 0.14.6
```

terraform が指定したバージョンがインストールされ利用できることが確認できる

```
% terraform version
Terraform v0.14.6
```

##### VScode 対応
HashiCorp が terraform 用の Extension を出しているので利用可能

![スクリーンショット 2021-02-11 11.58.57.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/326183/e261d2e9-0fb2-dc84-0290-d0e65a40d8f6.png)

#### 3. Google Cloud SDK インストール
今回、対象を Google Cloud にするので Google Cloud SDK をインストールする

インストール方法は公式サイトを参照し実行する
https://cloud.google.com/sdk/docs/downloads-interactive?hl=ja

zsh で実施したかったため、実際には下記でイントールした

```sh:zshでインストールする場合の例
cd ~
curl -O https://dl.google.com/dl/cloudsdk/channels/rapid/downloads/google-cloud-sdk-324.0.0-darwin-x86_64.tar.gz
tar zxvf google-cloud-sdk-324.0.0-darwin-x86_64.tar.gz
rm google-cloud-sdk-324.0.0-darwin-x86_64.tar.gz
./google-cloud-sdk/install.sh --rc-path ~/.zprofile -q
sed -i -e 's/bash.inc/zsh.inc/g' ~/.zprofile
source ~/.zprofile
```

初期設定を実施する

```
gcloud init
```

### CentOS でのインストール
Terraform を実行するためのインストール内容を記載します
Ansible でのインストール Role 例を記載します

1. Terraform インストール
1. Google Cloud SDK インストール
1. (2021.12.18追記) tfenv インストール

#### 1. Terraform インストール (CentOS)
terraform 公式サイトを参照してインストールする
https://learn.hashicorp.com/tutorials/terraform/install-cli?in=terraform/gcp-get-started#install-terraform

ansible role 例は下記の通り。(CentOS8で実証済み)

```yml:terraform_role
---
- name: install dnf repository centos8
  shell: "dnf config-manager --add-repo https://rpm.releases.hashicorp.com/RHEL/hashicorp.repo"
  become: true
  when:
    - ansible_distribution == "CentOS"
    - ansible_distribution_major_version == "8"

- name: install terraform centos8
  dnf:
    name: terraform
    state: latest
    enablerepo: "hashicorp"
  become: true
  when:
    - ansible_distribution == "CentOS"
    - ansible_distribution_major_version == "8"
```

terraform がインストールできたことを確認する (Versionは2021.02.12時点)

```sh
% terraform --version
Terraform v0.14.6
```

#### 2. Google Cloud SDK インストール

```yml:google_cloud_sdk_install_role
---
- name: install yum repository centos8
  yum_repository:
    name: google-cloud-sdk
    description: "Google Cloud SDK repo"
    baseurl: https://packages.cloud.google.com/yum/repos/cloud-sdk-el8-x86_64
    enabled: no
    gpgcheck: yes
    repo_gpgcheck: yes
    gpgkey:
      - https://packages.cloud.google.com/yum/doc/yum-key.gpg
      - https://packages.cloud.google.com/yum/doc/rpm-package-key.gpg
  become: true
  when:
    - ansible_distribution == "CentOS"
    - ansible_distribution_major_version == "8"

- name: install google cloud sdk centos8
  dnf:
    name: google-cloud-sdk
    state: latest
    enablerepo: "google-cloud-sdk"
  become: true
  when:
    - ansible_distribution == "CentOS"
    - ansible_distribution_major_version == "8"
```

gcloud がインストールできたことを確認する (Versionは2021.02.12時点)

```sh
% gcloud --version
Google Cloud SDK 327.0.0
alpha 2021.02.05
beta 2021.02.05
bq 2.0.64
core 2021.02.05
gsutil 4.58
```

### 3. (2021.12.18追記) tfenv インストール

下記、tfenv install ansible 例 (ansible ユーザのディレクトリ配下にインストールする)

```yaml:tasks/main.yml
- name: tfenv / clone tfenv for user
  git:
    repo: 'https://github.com/tfutils/tfenv.git'
    dest: "{{ ansible_user_dir }}/.tfenv"
    version: v2.2.2

- name: tfenv / bash_profile setting for user
  lineinfile:
    dest: "{{ ansible_user_dir }}/.bash_profile"
    line: 'export PATH=$HOME/.tfenv/bin:$PATH'
    backup: yes
  when:
    - '"bash" in ansible_env.SHELL'

- name: tfenv / zprofile setting for user
  lineinfile:
    dest: "{{ ansible_user_dir }}/.zprofile"
    line: 'export PATH=$HOME/.tfenv/bin:$PATH'
    backup: yes
  when:
    - '"zsh" in ansible_env.SHELL'

```

## GCP 向け環境設定
terraform で GCP を操作するための設定や準備をする

### 実行環境
下記に載せている GCP 環境
https://qiita.com/suzuyui/items/947867f52897417ee31b

プロジェクトおよび VPC は作成済み
今回は、サービスプロジェクトへ GCE で 1台 VM を構築する
※今回実施内容的には、subenetの記載方法とroleの追加以外は特に単独プロジェクトと差異はない

### 実施項目
下記の順番で実施する

1. terraform 向け Service Account 作成
2. tfstate ファイル保存 GCS バケット作成・設定
3. terraform init

### 1. terraform 向け Service Account 作成
terraform が GCP の操作をするためのサービスアカウントを作成して、terraformへ設定する

作成内容は Terraform 側のチュートリアル内容に則す ([Setting up GCP](https://learn.hashicorp.com/tutorials/terraform/google-cloud-platform-build#setting-up-gcp) Webコンソール利用想定での記載になっている)

#### 1.1. 実施するプロジェクトを選択 / Select the project you created in the previous step.

gcloud で VM インスタンス構築するプロジェクトとその IAM 権限をもつユーザアカウントを設定する

```sh:gcloud_config
gcloud config set account [PROJECT_USER_ACCOUNT]
gcloud config set project [PROJECT_ID]
```
※ログインしてない場合は、`gcloud auth login`を最初に実施する

#### 1.2. サービスアカウント新規作成 / Under "Service account", select "New service account". Give it any name you like.

Terraform 向けのサービスアカウントを作成する

```sh:service_account作成
gcloud iam service-accounts create terraform --display-name "Terraform"
```

#### 1.3. Editor ロールの付与 / For the Role, choose "Project -> Editor".

作成したサービスアカウントに`Editor`ロールを付与する (これは参照先のままで、環境ごとに適切なロールを付与すること)

```sh:Editorロールの付与(適切なロールがある場合は、要変更)
gcloud projects add-iam-policy-binding [PROJECT_ID] \
  --member serviceAccount:terraform@[PROJECT_ID].iam.gserviceaccount.com \
  --role roles/editor
```

#### 1.4. Json キーの作成とダウンロード / Leave the "Key Type" as JSON. Click "Create" to create the key and save the key file to your system.

作成したサービスアカウントのキーを生成する ([参照](https://cloud.google.com/sdk/gcloud/reference/iam/service-accounts/keys/create?hl=ja-jp))

```sh:サービスアカウントの認証キーをJSONで作成(jsonファイル名はここでのてきとうな名前で変更可)
gcloud iam service-accounts keys create terraform_serviceacoount_credential.json \
  --iam-account terraform@[PROJECT_ID].iam.gserviceaccount.com
```

実施したフォルダに、`terraform_serviceacoount_credential.json`ってファイルが生成されます

#### (共有 VPC を利用している場合) 共有 VPC 側でも Subnet 利用ロールを付与
```sh:shared_vpc_管理者とホストプロジェクトへ変更
gcloud config set account [HOST_PROJECT_USER_ACCOUNT]
gcloud config set project [HOST_PROJECT_ID]
```

```sh:ネットワーク利用者ロール付与
gcloud projects add-iam-policy-binding [HOST_PROJECT_ID] \
  --member serviceAccount:terraform@[PROJECT_ID].iam.gserviceaccount.com \
  --role roles/compute.networkUser
```

#### 1.5. main.tf の作成
GCP の認証情報 (作成したサービスアカウントのキー)と、操作するプロジェクトを指定する

```tf:main.tf
provider "google" {
  credentials = file("terraform_serviceacoount_credential.json")

  project     = "[PROJECT_ID]"
}
```

以上で、terraform 実際対象のプロジェクトの指定と、認証の設定まで完了

### 2. tfstate ファイル保存 GCS バケット作成・設定
ステートファイル(tfstate)を共有利用できるように、GCSに保存用のバケットを作成して、terrafromへ設定する
https://www.terraform.io/docs/language/settings/backends/gcs.html

上記 URL に**highly recommended**として、`enable Object Versioning on the GCS bucket` とあるので、
versioning と lifecycle の設定を実施する
パラメータは、無料枠に収まるようにストレージクラスを`STANDARD`、リージョンを`us-west1`にした。(月5Gまで対象.[参照](https://cloud.google.com/storage/pricing?hl=ja#storage-pricing))

- [PROJECT-ID] : 対象プロジェクトのID
- [BUCKET_NAME] : 保存するバケット名。GCP全体でユニークな必要がある。

```sh:gsutil_bucket作成
gsutil mb -p [PROJECT-ID] -c STANDARD -l us-west1 -b on gs://[BUCKET_NAME]/
gsutil versioning set on gs://[BUCKET_NAME]/
gsutil lifecycle set terraform-state-bucket_lifecycle.json gs://[BUCKET_NAME]/
```

```json:terraform-state-bucket_lifecycle.json
{"rule": [{"action": {"type": "Delete"}, "condition": {"numNewerVersions": 5}}]}
```

上記作成したバケットをバックエンドとして指定する。

```tf:backend設定(main.tfに追記)
terraform {
  backend "gcs" {
    bucket  = "[BUCKET_NAME]"
    prefix  = "terraform/state"
  }
}
```

以上で、trraform のバックエンド設定完了

### 3. terraform init

Terraform の初期化を行う
必要なプラグインのイントールなども実施する

```
terraform init
```

実行結果は下記の通り
GCPに関するProvider Plugin がインストールされてたことが確認できる

![スクリーンショット 2021-02-11 13.30.36.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/326183/8030e4f5-28af-9721-6056-8f5e160aa63a.png)

以上で、terraform から GCP を操作する設定完了

## GCP での VM インスタンス構築試験
実際に GCP で VMインスタンスを 1台構築して、動作を確認する

### 実施項目
下記の順番で実施する

1. VMインスタンス定義
2. terraform plan
3. terraform apply
4. terraform destroy
5. lifecycle

### 1. VMインスタンス定義

VM 一台を構築するリソース定義ファイルを作成する
([参考](https://learn.hashicorp.com/tutorials/terraform/google-cloud-platform-change?in=terraform/gcp-get-started#adding-resources))

とりあえず今回の試験は、ファイルを分けずに main.tf に追記して実施している

```tf:VM一台をserviceprojectに構築する例(main.tfに追記.HostProjectNameなどは環境による)
resource "google_compute_instance" "vm_instance" {
  name         = "terraform-create-sv01"
  machine_type = "f1-micro"
  zone         = "us-west1-b"

  boot_disk {
    initialize_params {
      image = "centos-cloud/centos-7"
    }
  }
  network_interface {
    subnetwork = "projects/[HostProjectName]/regions/us-west1/subnetworks/private-subnet01"
    access_config {
    }
  }
}

```

記載方法の詳細は [Terraform 公式サイト参照](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/compute_instance)

- 記載内容
    - name: VMインスタンス名
    - machine_type： マシンタイプ
        - https://cloud.google.com/compute/docs/machine-types
    - zone: VM作成ゾーン
    - boot_disk/initialize_params/image: イメージ名
        - https://cloud.google.com/compute/docs/images#gcloud
        - `gcloud compute images list`で一覧取得可能で、該当するイメージ名を指定する
    - network_interface/subnetwork: VMで設定するサブネット
        - ここでは共有VPCサブネットを指定するためprojectsから指定している
        - 共有 VPC で利用許可された `privete-subnet01` というサブネットを利用して作成する例となっている


### 2. terraform plan

実際にどのような変更をするかを事前に確認できる
plan では変更は行われない (dry run)

```
terraform plan
```

実行結果は下記の通り
VM が `+` で追加されることが見える

![スクリーンショット 2021-02-14 14.17.12.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/326183/e4b78ca6-3da1-e0e0-7cfd-e8841032ea06.png)



#### terraform plan で Error が出る場合
下記のようなエラーが出る場合は`gcloud auth application-default login`を実施する

```
Error: Attempted to load application default credentials since neither `credentials` nor `access_token` was set in the provider block.  No credentials loaded. To use your gcloud credentials, run 'gcloud auth application-default login'.  Original error: google: could not find default credentials. See https://developers.google.com/accounts/docs/application-default-credentials for more information.
```

```sh:auth_application_default_login_error対応
gcloud auth application-default login
```

### 3. terraform apply
変更の適用を実施する
実際に VM が作成される

```
terraform apply
```

実行結果が下記となる

![スクリーンショット 2021-02-14 14.20.16.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/326183/e581b490-7044-7fa8-4eec-85c3fc8856a3.png)


**Enter a value:** と出るので`yes`と入力する

![スクリーンショット 2021-02-14 14.22.02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/326183/4ac82f82-a2e0-3bd9-e2e0-27c25cd48667.png)


VM構築が成功して、Webコンソールでも確認できる
![スクリーンショット 2021-02-11 13.55.58.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/326183/e1b2eec6-62bf-f9ab-509b-10c0766bda42.png)

#### terraform apply 実施中のステートファイルロックの様子

`terraform apply` 中にバックエンドにしているバケットを見ると、ロックファイルが作成されていることがわかる

```sh:apply実行中にはtflockファイルが作成される
% gsutil ls gs://[BUCKET_NAME]/terraform/state/
gs://[BUCKET_NAME]/terraform/state/default.tflock
gs://[BUCKET_NAME]/terraform/state/default.tfstate
```

```sh:apply終了後にはtflockファイルがなくなっている
% gsutil ls gs://[BUCKET_NAME]/terraform/state/
gs://[BUCKET_NAME]/terraform/state/default.tfstate
```

#### 変更
リソースを変更して動作を確認する ([参考](https://learn.hashicorp.com/tutorials/terraform/google-cloud-platform-change?in=terraform/gcp-get-started#changing-resources)

下記のように先程のファイルに`tags         = ["web", "dev"]`行を追記する

```tf:tag追加
resource "google_compute_instance" "vm_instance" {
  name         = "terraform-create-sv01"
  machine_type = "f1-micro"
  zone         = "us-west1-b"

+ tags         = ["web", "dev"]
  #...
```

変更したら`terrafrom apply` をして動作を確認する

```
terraform apply
```

出力例が下記の通り。

![スクリーンショット 2021-02-14 14.32.58.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/326183/d651fc6b-e8bd-50b5-2fa2-a9ab5defff38.png)

追加した tags のところが、`+` となっていることを確認できる。
確認したら**Enter a value:** で`yes`を入力して適用を実施する

### 4. terraform destroy
作成したリソースの削除を実施する
実際に VM が削除される

```
terraform destroy
```

実行結果は下記となる
![スクリーンショット 2021-02-14 14.39.31.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/326183/943d3f24-6826-ec56-fa64-6941964d0fc9.png)


apply時同様、**Enter a value:** と出るので`yes`と入力する
![スクリーンショット 2021-02-14 14.42.55.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/326183/21633e8c-8753-7942-0f37-6a54c11d92d1.png)


以上で、削除も完了となる

### 5. lifecycle
#### 5.1 デフォルト動作
再構築が必要な動作の確認として、VMインスタンスのリネームを実施する
VM 名をリネーム (.tfファイルのname:を変更) すると、現状の VM が削除され新しい名前の VM が追加される
([参考](https://learn.hashicorp.com/tutorials/terraform/google-cloud-platform-change?in=terraform/gcp-get-started#destructive-changes))

前段階で削除しているので、
`terraform apply`を実施して VM インスタンスが構築された状況にしてから下記を実施する。

```tf:nameのみ01から02に変更
resource "google_compute_instance" "vm_instance" {
-  name         = "terraform-create-sv01"
+  name         = "terraform-create-sv02"
  #...
```

上記変更後に`terraform apply`した結果が下記であり、
**-/+ destroy and then create replacement** とある通り、削除後に作成して置き換える動作になる

![スクリーンショット 2021-02-14 14.50.55.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/326183/8beae099-2ec4-5400-46b5-cb4376d506e3.png)


下記`yes`入力後に、**terraform-create-sv01**がDestroyされ、
その後に、**terraform-create-sv02** がCreateが実施される様子が見える

![スクリーンショット 2021-02-14 14.55.25.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/326183/2c7e7f4a-52f4-6e23-aee6-5f6f0c5df43f.png)


上記の動作を変えたい場合は、**lifecycle**の記載を実施する
[lifecycle](https://www.terraform.io/docs/language/meta-arguments/lifecycle.html) は下記の 3種類があり、それぞれ試してみる

- lifecycle
    - create_before_destroy
    - prevent_destroy
    - ignore_changes

#### 5.2 create_before_destroy
新しい交換用のオブジェクトを作成後に、古いオブジェクトを削除します
一時的に２重で存在することになるため、衝突や重複などでの問題がないかリソース理解が必要になります
※参照(https://www.terraform.io/docs/language/meta-arguments/lifecycle.html#create_before_destroy)

本件での記載例は下記の通り
再度リネームして、`lifecycle` を追記してます (一度`google_compute_instance`箇所全体を再掲)

```tf:lifecycle_create_before_destroy
resource "google_compute_instance" "vm_instance" {
  name         = "terraform-create-sv01"
  machine_type = "f1-micro"
  zone         = "us-west1-b"

  tags         = ["web", "dev"]

  boot_disk {
    initialize_params {
      image = "centos-cloud/centos-7"
    }
  }
  network_interface {
    subnetwork = "projects/[HostProjectName]/regions/us-west1/subnetworks/private-subnet01"
    access_config {
    }
  }

  lifecycle {
    create_before_destroy = true
  }
}
```

上記を apply した結果が下記となり、
**+/- create replacement and then destroy** とデフォルト時と出力が変わっていることが確認できる

![スクリーンショット 2021-02-14 15.01.31.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/326183/acddc006-2034-38f2-5c7b-84d9a713af9a.png)


実行動作も、下記の通り、**Creating...**から始まり、完了後に**Destroyng...**が始まることが見える

![スクリーンショット 2021-02-14 15.07.52.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/326183/10e8c534-6b31-8b51-8df2-911bd565504c.png)


#### 5.3 prevent_destroy
このメタ引数を**true**に設定すると、引数が構成に存在する限り、
Terraform はリソースに関連付けられたインフラストラクチャオブジェクトを破壊する計画をエラーで拒否する。
これはデータベースインスタンスなどに有効。ただし、resource ブロック自体が構成から削除された場合は、リソース破棄を防ぐことはできない。
 ([参照](https://www.terraform.io/docs/language/meta-arguments/lifecycle.html#prevent_destroy))

下記の通り、`name`と`lifecycle`の箇所を変更して挙動を確認します。

```tf:lifecycle_prevent_destroy
resource "google_compute_instance" "vm_instance" {
-  name         = "terraform-create-sv01"
+  name         = "terraform-create-sv02"
  #...
  lifecycle {
-   create_before_destroy = true
+   prevent_destroy = true
  }
}
```

plan または、 apply を実施すると下記のような出力となり、
**Error: Instance cannot be destroyed**となる

![スクリーンショット 2021-02-14 15.15.23.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/326183/32c2a37f-8736-e341-9e1b-5a2313aff139.png)


#### 5.4 ignore_changes
属性名のリストを指定して、指定した属性への変更は無視するようにする。
将来変更される予定があるデータについて影響を与えないようにしておくことができる。
リストの代わりに、special キーワード`all`を使用して、すべての属性を無視するようにすることもできる。
([参照](https://www.terraform.io/docs/language/meta-arguments/lifecycle.html#ignore_changes))

下記記載例。
`tags`で試す。
実施前は`tags         = ["web", "dev"]`のみ箇所を、
"ignoretest"を追加して`tags         = ["web", "dev", "ignoretest"]`にしている
`lifecycle` で `ignore_changes =` に`tags`を追加して、tagsの変化を無視するようにしている。


```tf:lifecycle_ignore_changes
resource "google_compute_instance" "vm_instance" {
  name         = "terraform-create-sv01"
  machine_type = "f1-micro"
  zone         = "us-west1-b"

  tags         = ["web", "dev", "ignoretest"]

  boot_disk {
    initialize_params {
      image = "centos-cloud/centos-7"
    }
  }
  network_interface {
    subnetwork = "projects/[HostProjectName]/regions/us-west1/subnetworks/private-subnet01"
    access_config {
    }
  }

  lifecycle {
    ignore_changes = [
      # Ignore changes to tags, e.g. because a management agent
      # updates these based on some ruleset managed elsewhere.
      tags,
    ]
  }
}
```

下記が上記変更後に、`terraform apply` を実施した例。
**No changes. Infrastructure is up-to-date.**となり、tagsが変更されているが無視される。

![スクリーンショット 2021-02-14 21.43.42.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/326183/cef7de9c-773b-4ac0-9915-76d740a7061f.png)


以上で、VMインスタンスの構築から削除、再構築や削除防止などのライフサイクル管理などを試験し動作確認できた。

## さいごに
Terraform を調べて環境構築、VMインスタンス構築・管理まで実施してみた

既存から Terraform ファイルを作成する GCP が作成している [Terraformer](https://github.com/GoogleCloudPlatform/terraformer) なるものがあるらしく、今後試してみたい

また、組織・プロジェクト、VPC などの基礎的な箇所にお IaC 化や、GKE 構築なども実施する予定

## 参考資料
GitHub: hashicorp-japan/terraform-workshop-jp
https://github.com/hashicorp-japan/terraform-workshop-jp
https://docs.google.com/presentation/d/1Ovdee0FIrJ_h66B5DToQNYKWJ9XRbudS0RCk4d_x1Eg/edit?usp=sharing

Hashicorp Learn / Get Started - Google Cloud
https://learn.hashicorp.com/tutorials/terraform/google-cloud-platform-build#setting-up-gcp

Infrastructure as Code クラウドにおけるサーバ管理の原則とプラクティス O'Reilly Japan (2017) 著：Kief Morris　監訳：宮下 剛輔 訳：長尾 高弘
https://www.oreilly.co.jp/books/9784873117966/

Infrastructure as Codeを実現するTerraformとAnsible～それぞれの得意領域と使い分け～
https://www.lac.co.jp/lacwatch/service/20201216_002380.html

Google Cloud Platformで学ぶTerraform 〜基礎編〜 著：[カエルと空](https://techbookfest.org/organization/5652017242439680)
https://techbookfest.org/product/6331235183886336

Terraform Language - The lifecycle Meta-Argument
https://www.terraform.io/docs/language/meta-arguments/lifecycle.html