Terraformに入門してみた

## Terraformって何？
[Hashicorp](https://www.hashicorp.com/)社が開発しているオープンソースのインフラの自動構築ツール。
インフラのリソースをコードとして定義することができ、コードに従ってリソースを構築してくれる。

インフラリソースをコードで定義することを**Infrastructure as Code(IaC)**という。
Terraform以外にもIaCツールとして以下のようなものがある。

| ツール名|管理対象 |対応|コード定義|
|:--|:--|:--|:--|
| [AWS Cloud Formation](https://aws.amazon.com/jp/cloudformation/)|マネージドサービス全般の構成|AWS|YAML|
| [Azure Resource Manager](https://docs.microsoft.com/ja-jp/azure/azure-resource-manager/)|マネージドサービス全般の構成|Azure|JSON|
| [Deployment Manager](https://cloud.google.com/deployment-manager/docs/fundamentals?hl=ja)|マネージドサービス全般の構成|GCP|YAML等|
| [Serverless Framework](https://www.serverless.com/framework/docs/providers/aws/guide/intro/) |サーバレスサービス全般の構成| AWS<br>Azure<br>GCP<br>Alibaba Cloud ​|YAML|

## Terraformの実行順序
npmやbundlerに近い。

1. Terraformファイルを書く
2. terraform initで初期化する
3. 実行結果を確認してファイルを修正
4. 実際に実行してインフラを構築
5. 変更したい場合は、ファイルを編集して再度実行

## 管理ファイル
tfという拡張子のファイルで管理する。

```tf:公式サンプルより
terraform {
  required_providers {
    google = {
      source = "hashicorp/google"
      version = "3.5.0"
    }
  }
}

provider "google" {
  credentials = file("<NAME>.json")

  project = "<PROJECT_ID>"
  region  = "us-central1"
  zone    = "us-central1-c"
}

resource "google_compute_network" "vpc_network" {
  name = "terraform-network"
}
```
#### terraform
terraformブロックには、terraform自体の設定を記述する。依存するライブラリのバージョンや、メタ情報を管理するstateファイルの置き場所を指定する。

#### provider
リージョンや認証情報など、AWSやGCPなどのサービスと通信するための設定を記述する。

#### resource
実際に構築するインフラの設定を記述する。
`resource "リソース名" "変数名"`の形で指定でき、変数を使用してresourceの中身を参照できる。

## GCPをTerraformでリソース管理してみる
#### 1. インストール
[公式サイト](https://learn.hashicorp.com/tutorials/terraform/install-cli)から、TerraformのCLIをインストールする。
[ここ](https://www.terraform.io/downloads.html)からバイナリをダウンロードして解凍。

インストールを確認。

```terminal
$ terraform --version
Terraform v1.0.11
```

#### 2. やること
VPCを利用してGCPにプライベートなネットワークを立ち上げ、ネットワーク内にインスタンスを立ち上げる。

#### 3. リソースの作成
[GCPに関するDocument](https://registry.terraform.io/providers/hashicorp/google/latest/docs)を見ながら、tfファイルを作成していく。

```main.tf
terraform {
  required_providers {
    google = {
      source  = "hashicorp/google"
      version = "3.5.0"
    }
  }
}

provider "google" {
  credentials = file(var.credentials_file)

  project = var.project
  region  = var.region
  zone    = var.zone
}

resource "google_compute_network" "vpc_network" {
  name = "terraform-network"
}

resource "google_compute_instance" "vm_instance" {
  name         = "terraform-instance"
  machine_type = "f1-micro"

  boot_disk {
    initialize_params {
      image = "debian-cloud/debian-9"
    }
  }

  network_interface {
    network = google_compute_network.vpc_network.name
    access_config {
    }
  }
}
```
```variable.tf
variable "project" {
  default = "<your project name>"
}

variable "credentials_file" {
  default = "<your path to service account json file>"
}

variable "region" {
  default = "us-central1"
}

variable "zone" {
  default = "us-central1-c"
}
```

#### 初期化
下記コマンドを実行する。

```terminal
$ terraform init
```
ファイルと同じディレクトリに下記ディレクトリ / ファイルが作成される。
.terraform
.terraform.lock.hcl

#### 確認
下記コマンドを実行する。

```terminal
$ terraform plan
```

ファイルの文法やアカウントの権限にミスがあると、ここでチェックしてくれる。
###### エラーの場合

```terminal
$ terraform plan
╷
│ Error: Unsupported attribute
│ 
│   on main.tf line 13, in provider "google":
│   13:   project     = "${lookup(var.project_name.tf_sample, "${terraform.workspace}")}"
│     ├────────────────
│     │ var.project_name is object with 1 attribute "tf-sample"
│ 
│ This object does not have an attribute named "tf_sample".
╵
╷
│ Error: error archiving directory: could not archive missing directory: ./../src
│ 
│   with data.archive_file.function_zip,
│   on main.tf line 42, in data "archive_file" "function_zip":
│   42: data "archive_file" "function_zip" {
│ 
╵
```

###### 成功した場合
```termnal
$ terraform plan
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.vm_instance will be created
  + resource "google_compute_instance" "vm_instance" {
      + can_ip_forward       = false
      + cpu_platform         = (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                 = "terraform-instance"
      + project              = (known after apply)
      + self_link            = (known after apply)
      + tags_fingerprint     = (known after apply)
      + zone                 = (known after apply)

      + 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  = "debian-cloud/debian-9"
              + labels = (known after apply)
              + size   = (known after apply)
              + type   = (known after apply)
            }
        }

      + network_interface {
          + name               = (known after apply)
          + network            = "terraform-network"
          + network_ip         = (known after apply)
          + subnetwork         = (known after apply)
          + subnetwork_project = (known after apply)

          + access_config {
              + nat_ip       = (known after apply)
              + network_tier = (known after apply)
            }
        }

      + scheduling {
          + automatic_restart   = (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)
            }
        }
    }

  # google_compute_network.vpc_network will be created
  + resource "google_compute_network" "vpc_network" {
      + auto_create_subnetworks         = true
      + delete_default_routes_on_create = false
      + gateway_ipv4                    = (known after apply)
      + id                              = (known after apply)
      + ipv4_range                      = (known after apply)
      + name                            = "terraform-network"
      + project                         = (known after apply)
      + routing_mode                    = (known after apply)
      + self_link                       = (known after apply)
    }

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

#### 実行
下記コマンドを実行する。

```terminal
$ terraform apply
```

これで実際にリソースが作成される。
![スクリーンショット 2021-11-16 午後11.21.25.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/493565/1ea8ca2b-632c-fc4a-63ad-13e2ba0d74a5.png)

![vm.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/493565/9393cbf5-1c19-f6e3-3fb8-a362b12f7910.png)
![vm show.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/493565/a304236a-c75f-beae-6038-835a7fb0192c.png)



変更したい場合は、ファイルを変更 -> apply でリソースの変更が可能。

#### 削除
下記コマンドを実行する。

```terminal
$ terraform destroy
```
作成したリソースが削除される。

##注意点
あくまで管理できるのはインフラの構成のみ。
サービスアカウントの設定やAPIの有効化などは管理できない。