[Terraform] Terafform入門 on GCP その１

[Infrastructure as Codeを少し楽にするterraformerが出た - Qiita](https://qiita.com/shogomuranushi/items/98cf2c9800187f51e982)
今更ですが、この記事を拝見し、そろそろやるか、と思い始めてみました。


# 環境

- Terraform v0.12.6
- provider.google v2.12.0

# 環境準備

## 1. install terraform

```shell
$ brew install terraform
...
$ terraform -v
Terraform v0.12.6
```

## 2. create service account

- **gcloudコマンドはインストール済み**
- terraform実行用のサービスアカウトを作成します。
- 権限は「Project > 編集者」をつけました
- 鍵をjson形式でダウンロード
  - `credentials.json` として置いておきます
  - 名前は適当です
- service accounntをactivateします

  ```shell
  gcloud auth activate-service-account --key-file=credentials.json
  ```
- projectをセットしておきます

  ```shell
  $ gcloud config set project xxxxx
  ```
  
## 3. create GCS bucket

`xxx.tfstate` ファイルを保存する為のバケットをGCSに作成しておきます。
※管理対象のインフラの状態を保存するファイル

```shell
$ gsutil mb -c multi_regional -l Asia gs://xxxx-tf-state
```

# Providerの設定を書く

```terraform:provider.tf
provider "google" {
  credentials = "${file("credentials.json")}"
  project     = "${var.project}"
  region      = "${var.region}"
}
```

```terraform:variables.tf
variable "project" {
  default = "xxx"
}

variable "region" {
  default = "asia-northeast1"
}
```

- project,regionはvariables.tfに外だししました
- credentials.jsonはさっきつくったservice accountの鍵ファイルです


# backend設定を書く

tfstateファイルをGCS上で管理するための設定

```terraform:backend.tf
terraform {
  backend "gcs" {
    bucket      = "xxxx-tf-state"
    path        = "practice.tfstate"
    credentials = "credentials.json"
  }
}
```

# terraform initする

```shell
$ terraform init

Initializing the backend...

Initializing provider plugins...

The following providers do not have any version constraints in configuration,
so the latest version was installed.

To prevent automatic upgrades to new major versions that may contain breaking
changes, it is recommended to add version = "..." constraints to the
corresponding provider blocks in configuration, with the constraint strings
suggested below.

* provider.google: version = "~> 2.12"

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

ん、なにかWarningが出ました。

providerにバージョン指定しておいたほうがいいよ、って事ですかね。
入れてみます

```diff
diff --git a/provider.tf b/provider.tf
index 1e8066b..ec409f4 100644
--- a/provider.tf
+++ b/provider.tf
@@ -2,4 +2,5 @@ provider "google" {
   credentials = "${file("credentials.json")}"
   project     = "${var.project}"
   region      = "${var.region}"
+  version     = "~> 2.12"
 }
```

```shell
$ terraform init

Initializing the backend...

Initializing provider plugins...

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

warningでなくなりましたね。

```shell
$ gsutil ls gs://xxxx-tf-state
gs://xxxx-tf-state/practice.tfstate
```

ちゃんとtfstateファイルも出来てます。

# VPCを作ってみる

試しに何か作ってみたいのでVPCを作ってみることにします

## リソース定義

```terraform:network.tf
resource "google_compute_network" "vpc" {
  name = "terraform-practice-network"
}

resource "google_compute_subnetwork" "vpc_subnet1" {
  name          = "terraform-practice-network-subnet1"
  ip_cidr_range = "${var.subnet_cidr_range}"
  network       = "${google_compute_network.vpc.name}"
  description   = "example.subnet1"
  region        = "${var.region}"
}
```

```terraform:variables.tf
variable "project" {
  default = "xxxx"
}

variable "region" {
  default = "asia-northeast1"
}

variable "subnet_cidr_range" {
  default = "192.168.10.0/24"
}
```

## plan

```shell
$ terraform plan
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_compute_network.vpc will be created
  + resource "google_compute_network" "vpc" {
      + auto_create_subnetworks         = true
      + delete_default_routes_on_create = false
      + gateway_ipv4                    = (known after apply)
      + id                              = (known after apply)
      + name                            = "terraform-practice-network"
      + project                         = (known after apply)
      + routing_mode                    = (known after apply)
      + self_link                       = (known after apply)
    }

  # google_compute_subnetwork.vpc_subnet1 will be created
  + resource "google_compute_subnetwork" "vpc_subnet1" {
      + creation_timestamp = (known after apply)
      + description        = "example.subnet1"
      + fingerprint        = (known after apply)
      + gateway_address    = (known after apply)
      + id                 = (known after apply)
      + ip_cidr_range      = "192.168.10.0/24"
      + name               = "terraform-practice-network-subnet1"
      + network            = "terraform-practice-network"
      + project            = (known after apply)
      + region             = "asia-northeast1"
      + secondary_ip_range = (known after apply)
      + self_link          = (known after apply)
    }

Plan: 2 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.
```

## apply

とりあえず行けそうなので、applyしてみます

```shell
$ 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_compute_network.vpc will be created
  + resource "google_compute_network" "vpc" {
      + auto_create_subnetworks         = true
      + delete_default_routes_on_create = false
      + gateway_ipv4                    = (known after apply)
      + id                              = (known after apply)
      + name                            = "terraform-practice-network"
      + project                         = (known after apply)
      + routing_mode                    = (known after apply)
      + self_link                       = (known after apply)
    }

  # google_compute_subnetwork.vpc_subnet1 will be created
  + resource "google_compute_subnetwork" "vpc_subnet1" {
      + creation_timestamp = (known after apply)
      + description        = "example.subnet1"
      + fingerprint        = (known after apply)
      + gateway_address    = (known after apply)
      + id                 = (known after apply)
      + ip_cidr_range      = "192.168.10.0/24"
      + name               = "terraform-practice-network-subnet1"
      + network            = "terraform-practice-network"
      + project            = (known after apply)
      + region             = "asia-northeast1"
      + secondary_ip_range = (known after apply)
      + self_link          = (known after apply)
    }

Plan: 2 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_compute_network.vpc: Creating...
google_compute_network.vpc: Still creating... [10s elapsed]
google_compute_network.vpc: Still creating... [20s elapsed]
google_compute_network.vpc: Still creating... [30s elapsed]
google_compute_network.vpc: Still creating... [40s elapsed]
google_compute_network.vpc: Creation complete after 49s [id=terraform-practice-network]
google_compute_subnetwork.vpc_subnet1: Creating...
google_compute_subnetwork.vpc_subnet1: Still creating... [10s elapsed]
google_compute_subnetwork.vpc_subnet1: Still creating... [20s elapsed]
google_compute_subnetwork.vpc_subnet1: Still creating... [30s elapsed]
google_compute_subnetwork.vpc_subnet1: Still creating... [40s elapsed]
google_compute_subnetwork.vpc_subnet1: Still creating... [50s elapsed]
google_compute_subnetwork.vpc_subnet1: Creation complete after 59s [id=asia-northeast1/terraform-practice-network-subnet1]

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

結構時間かかりましたが、ちゃんと出来たようです。

```shell
$ gcloud compute networks list
NAME                        SUBNET_MODE  BGP_ROUTING_MODE  IPV4_RANGE  GATEWAY_IPV4
default                     AUTO         REGIONAL
terraform-practice-network  AUTO         REGIONAL
$ gcloud compute networks subnets list | grep subnet1
terraform-practice-network-subnet1  asia-northeast1          terraform-practice-network  192.168.10.0/24
```

## nameをvariablesにしてみる

```terraform:network.tf
resource "google_compute_network" "vpc" {
  name = "${var.vpc_name}"
}

resource "google_compute_subnetwork" "vpc_subnet1" {
  name          = "${var.subnetwork_name}"
  ip_cidr_range = "${var.subnet_cidr_range}"
  network       = "${google_compute_network.vpc.name}"
  description   = "example.subnet1"
  region        = "${var.region}"
}
```

```terraform:variables.tf
variable "project" {
  default = "xxxx"
}

variable "region" {
  default = "asia-northeast1"
}

variable "subnet_cidr_range" {
  default = "192.168.10.0/24"
}

variable "vpc_name" {
  default = "terraform-practice-network"
}

variable "subnetwork_name" {
  default = "terraform-practice-network-subnet1"
}
```

```shell
$ 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.

google_compute_network.vpc: Refreshing state... [id=terraform-practice-network]
google_compute_subnetwork.vpc_subnet1: Refreshing state... [id=asia-northeast1/terraform-practice-network-subnet1]

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

No changes. Infrastructure is up-to-date.

This means that Terraform did not detect any differences between your
configuration and real physical resources that exist. As a result, no
actions need to be performed.
```

変化ないですね。

# 環境毎の構成を考える

production,staging など環境毎の構成をどう実現するか考えてみます。
今回は
[Terraform 運用ベストプラクティス 2019 ~workspace をやめてみた等諸々~ - 長生村本郷Engineers'Blog](http://kenzo0107.hatenablog.com/entry/2019/04/17/103558)
こちらの記事がとても納得感があったので、参考にさせて頂きました :bow:

今はproductionしかないですが、こんな感じにしてみました。

```
$ tree
.
├── credentials.json
└── environments
    └── production
        ├── backend.tf
        ├── network.tf
        ├── provider.tf
        └── variables.tf
```

# module化してみる

せっかくvariablesに変数を定義しているので、各リソース定義をstagingでも使えるようにして共通化したいですね。
networkの設定をmoduleに分離してみたいと思います。

## ディレクトリ構成

```
$ tree
.
├── credentials.json
├── environments
│   └── production
│       ├── backend.tf
│       ├── main.tf
│       ├── provider.tf
│       └── variables.tf
└── modules
    └── network
        └── main.tf
```

## リソース定義を書き直す

```terraform:modules/network/main.tf
ariable "vpc_name" {}
variable "subnetwork_name" {}
variable "subnet_cidr_range" {}
variable "region" {}

resource "google_compute_network" "vpc" {
  name = "${var.vpc_name}"
  auto_create_subnetworks = false
}

resource "google_compute_subnetwork" "vpc_subnet1" {
  name          = "${var.subnetwork_name}"
  ip_cidr_range = "${var.subnet_cidr_range}"
  network       = "${google_compute_network.vpc.name}"
  description   = "example.subnet1"
  region        = "${var.region}"
}
```

```terraform:environments/production/main.tf
module "network" {
  source = "../../modules/network"

  vpc_name          = "${var.vpc_name}"
  subnetwork_name   = "${var.subnetwork_name}"
  subnet_cidr_range = "${var.subnet_cidr_range}"
  region            = "${var.region}"
}
```

## plan

planで確認してみます。

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

google_compute_network.vpc: Refreshing state... [id=terraform-practice-network]
google_compute_subnetwork.vpc_subnet1: Refreshing state... [id=asia-northeast1/terraform-practice-network-subnet1]

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

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

Terraform will perform the following actions:

  # google_compute_network.vpc will be destroyed
  - resource "google_compute_network" "vpc" {
      - auto_create_subnetworks         = false -> null
      - delete_default_routes_on_create = false -> null
      - id                              = "terraform-practice-network" -> null
      - name                            = "terraform-practice-network" -> null
      - project                         = "xxxx" -> null
      - routing_mode                    = "REGIONAL" -> null
      - self_link                       = "https://www.googleapis.com/compute/v1/projects/xxxx/global/networks/terraform-practice-network" -> null
    }

  # google_compute_subnetwork.vpc_subnet1 will be destroyed
  - resource "google_compute_subnetwork" "vpc_subnet1" {
      - creation_timestamp       = "2019-08-05T08:17:21.744-07:00" -> null
      - description              = "example.subnet1" -> null
      - enable_flow_logs         = false -> null
      - fingerprint              = "9zINd1OQU44=" -> null
      - gateway_address          = "192.168.10.1" -> null
      - id                       = "asia-northeast1/terraform-practice-network-subnet1" -> null
      - ip_cidr_range            = "192.168.10.0/24" -> null
      - name                     = "terraform-practice-network-subnet1" -> null
      - network                  = "https://www.googleapis.com/compute/v1/projects/xxxx/global/networks/terraform-practice-network" -> null
      - private_ip_google_access = false -> null
      - project                  = "xxxx" -> null
      - region                   = "asia-northeast1" -> null
      - secondary_ip_range       = [] -> null
      - self_link                = "https://www.googleapis.com/compute/v1/projects/xxxx/regions/asia-northeast1/subnetworks/terraform-practice-network-subnet1" -> null
    }

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

  # module.network.google_compute_subnetwork.vpc_subnet1 will be created
  + resource "google_compute_subnetwork" "vpc_subnet1" {
      + creation_timestamp = (known after apply)
      + description        = "example.subnet1"
      + fingerprint        = (known after apply)
      + gateway_address    = (known after apply)
      + id                 = (known after apply)
      + ip_cidr_range      = "192.168.10.0/24"
      + name               = "terraform-practice-network-subnet1"
      + network            = "terraform-practice-network"
      + project            = (known after apply)
      + region             = "asia-northeast1"
      + secondary_ip_range = (known after apply)
      + self_link          = (known after apply)
    }

Plan: 2 to add, 0 to change, 2 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.
```

destroy -> create になってしまいました。

## terraform state mv

[Terraform の state mv を使ってべた書きしたリソースをモジュールに移す tips | Cry for the Moon](https://mozami.me/2018/05/12/terraform_state_mv.html)

ベタ書きしていたリソースをモジュール化する時は `terraform state mv` で移動させるらしい。
やってみます。

```shell
$ terraform state mv google_compute_network.vpc module.network.google_compute_network.vpc

Error: Invalid target address

Cannot move to module.network.google_compute_network.vpc: module.network does
not exist in the current state.
```

エラーになってしまいました。
`module.network` なんて無いよ、と。

[Unable to move resource to new module · Issue #21346 · hashicorp/terraform](https://github.com/hashicorp/terraform/issues/21346)

こんなIssueを発見。

[allow moving resource to new modules not in state by jbardin · Pull Request #22299 · hashicorp/terraform](https://github.com/hashicorp/terraform/pull/22299)

これで直ったのかな？
2019/08/02にマージされているようです。
が、まだリリースされていないようでした。※2019/08/06 22:32時点

https://github.com/hashicorp/terraform/issues/21346#issuecomment-498701941

## `import` and `state rm`

workaroundとして、importしてから古いの消せば行けるよ、と書いてあったのでやってみます

```shell
$ terraform import module.network.google_compute_network.vpc terraform-practice-network
module.network.google_compute_network.vpc: Importing from ID "terraform-practice-network"...
module.network.google_compute_network.vpc: Import prepared!
  Prepared google_compute_network for import
module.network.google_compute_network.vpc: Refreshing state... [id=terraform-practice-network]

Import successful!

The resources that were imported are shown above. These resources are now in
your Terraform state and will henceforth be managed by Terraform.

$ terraform state rm google_compute_network.vpc
Removed google_compute_network.vpc
Successfully removed 1 resource instance(s).
$ 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.

module.network.google_compute_network.vpc: Refreshing state... [id=terraform-practice-network]
google_compute_subnetwork.vpc_subnet1: Refreshing state... [id=asia-northeast1/terraform-practice-network-subnet1]

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

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

Terraform will perform the following actions:

  # google_compute_subnetwork.vpc_subnet1 will be destroyed
  - resource "google_compute_subnetwork" "vpc_subnet1" {
      - creation_timestamp       = "2019-08-05T08:17:21.744-07:00" -> null
      - description              = "example.subnet1" -> null
      - enable_flow_logs         = false -> null
      - fingerprint              = "9zINd1OQU44=" -> null
      - gateway_address          = "192.168.10.1" -> null
      - id                       = "asia-northeast1/terraform-practice-network-subnet1" -> null
      - ip_cidr_range            = "192.168.10.0/24" -> null
      - name                     = "terraform-practice-network-subnet1" -> null
      - network                  = "https://www.googleapis.com/compute/v1/projects/xxxx/global/networks/terraform-practice-network" -> null
      - private_ip_google_access = false -> null
      - project                  = "xxxx" -> null
      - region                   = "asia-northeast1" -> null
      - secondary_ip_range       = [] -> null
      - self_link                = "https://www.googleapis.com/compute/v1/projects/xxxx/regions/asia-northeast1/subnetworks/terraform-practice-network-subnet1" -> null
    }

  # module.network.google_compute_subnetwork.vpc_subnet1 will be created
  + resource "google_compute_subnetwork" "vpc_subnet1" {
      + creation_timestamp = (known after apply)
      + description        = "example.subnet1"
      + fingerprint        = (known after apply)
      + gateway_address    = (known after apply)
      + id                 = (known after apply)
      + ip_cidr_range      = "192.168.10.0/24"
      + name               = "terraform-practice-network-subnet1"
      + network            = "terraform-practice-network"
      + project            = (known after apply)
      + region             = "asia-northeast1"
      + secondary_ip_range = (known after apply)
      + self_link          = (known after apply)
    }

Plan: 1 to add, 0 to change, 1 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.
```

VPCのdestroyとcreateが消えました！
なるほど。
一旦これでsubnetworkも対応してしまいます。

```shell
$ terraform import module.network.google_compute_subnetwork.vpc_subnet1 terraform-practice-network-subnet1
module.network.google_compute_subnetwork.vpc_subnet1: Importing from ID "terraform-practice-network-subnet1"...
module.network.google_compute_subnetwork.vpc_subnet1: Import prepared!
  Prepared google_compute_subnetwork for import
module.network.google_compute_subnetwork.vpc_subnet1: Refreshing state... [id=asia-northeast1/terraform-practice-network-subnet1]

Import successful!

The resources that were imported are shown above. These resources are now in
your Terraform state and will henceforth be managed by Terraform.

$ terraform state rm google_compute_subnetwork.vpc_subnet1
Removed google_compute_subnetwork.vpc_subnet1
Successfully removed 1 resource instance(s).
$ 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.

module.network.google_compute_network.vpc: Refreshing state... [id=terraform-practice-network]
module.network.google_compute_subnetwork.vpc_subnet1: Refreshing state... [id=asia-northeast1/terraform-practice-network-subnet1]

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

No changes. Infrastructure is up-to-date.

This means that Terraform did not detect any differences between your
configuration and real physical resources that exist. As a result, no
actions need to be performed.
```

行けました。

# GCP上にある既存リソースをterraformに入れる

すでにstate mvが出来なかったところでつかってしまいましたが、 `import` を使うと既存のリソースをtfstateに入れることが出来るようです。
既存のリソース管理をTerraformに移行する時には必要になりそうなのでやってみます。

## GCEインスタンスを立てる

まずは、GCEインスタンスを手動で立てておきます。
VPCはさっき作ったやつにします。

- マシンタイプ
  - f1-micro（vCPU x 1、メモリ 0.6 GB）
- ゾーン
  - asia-northeast1-a
- network
  - terraform-practice-network
- subnetwork
  - terraform-practice-network-subnet1
- 内部IP
  - 192.168.10.2
- boot disk image
  - ubuntu-1804-bionic-v20190722a

## リソース定義を書く

[Google: google_compute_instance - Terraform by HashiCorp](https://www.terraform.io/docs/providers/google/d/datasource_compute_instance.html)

せっかくなのでmodulesに書いていきます。（productionとstagingで同じmoduleを使う想定）
今回は踏み台サーバを想定したインスタンスにしたいと思いますので、名前は `bastion` にします。

```terraform:modules/bastion/main.tf
variable "name" {}
variable "subnetwork_name" {}
variable "machine_type" {}
variable "region" {}
variable "zone" {}
variable "boot_disk_image" {}
variable "private_ip" {}
variable "service_account" {}

resource "google_compute_address" "bastion" {
  name         = "${var.name}"
  region       = "${var.region}"
}

resource "google_compute_instance" "bastion" {
  name         = "${var.name}"
  machine_type = "${var.machine_type}"
  zone         = "${var.zone}"
  tags         = ["server", "bastion"]

  boot_disk {
    initialize_params {
      image = "https://www.googleapis.com/compute/v1/projects/ubuntu-os-cloud/global/images/${var.boot_disk_image}"
    }
  }

  network_interface {
    network_ip = "${var.private_ip}"
    subnetwork = "${var.subnetwork_name}"
    access_config {
      # static external ip
      nat_ip   = "${google_compute_address.bastion.address}"
    }
  }

  service_account {
    email  = "${var.service_account}"
    scopes = [
      "https://www.googleapis.com/auth/devstorage.read_only",
      "https://www.googleapis.com/auth/logging.write",
      "https://www.googleapis.com/auth/monitoring.write",
      "https://www.googleapis.com/auth/service.management.readonly",
      "https://www.googleapis.com/auth/servicecontrol",
      "https://www.googleapis.com/auth/trace.append",
    ]
  }
}
```

```terraform:main.tf
module "network" {
  source = "../../modules/network"

  vpc_name          = "${var.vpc_name}"
  subnetwork_name   = "${var.subnetwork_name}"
  subnet_cidr_range = "${var.subnet_cidr_range}"
  region            = "${var.region}"
}

module "bastion" {
  source = "../../modules/bastion"

  name            = "${var.bastion_name}"
  subnetwork_name = "${var.subnetwork_name}"
  machine_type    = "f1-micro"
  region          = "${var.region}"
  zone            = "${var.region_zone}"
  boot_disk_image = "ubuntu-1804-bionic-v20190722a"
  private_ip      = "192.168.10.2"
  service_account = "xxx-compute"
}
```

```terraform:variables.tf
variable "project" {
  default = "xxxx"
}

variable "region" {
  default = "asia-northeast1"
}

variable "region_zone" {
  default = "asia-northeast1-a"
}

variable "subnet_cidr_range" {
  default = "192.168.10.0/24"
}

variable "vpc_name" {
  default = "terraform-practice-network"
}

variable "subnetwork_name" {
  default = "terraform-practice-network-subnet1"
}

variable "bastion_name" {
  default = "terraform-practice-instance-1"
}
```

variablesに完全に外だしすべきなのか、main.tfに直接書いてしまうのかが微妙なラインですが、一旦他でも使うかもしれないような値はvariablesに出すくらいにしてます。

## import

```shell
$ terraform import module.bastion.google_compute_instance.bastion xxxx/asia-northeast1-a/terraform-practice-instance-1
module.bastion.google_compute_instance.bastion: Importing from ID "xxxx/asia-northeast1-a/terraform-practice-instance-1"...
module.bastion.google_compute_instance.bastion: Import prepared!
  Prepared google_compute_instance for import
module.bastion.google_compute_instance.bastion: Refreshing state... [id=terraform-practice-instance-1]

Import successful!

The resources that were imported are shown above. These resources are now in
your Terraform state and will henceforth be managed by Terraform.
```

インスタンスを作成すると自動的にGCEインスタンスのデフォルトサービスアカウントが作成されているので、サービスアカウントもimportしてresourceに定義しようと思ったんですが、
自動で作成されるサービスアカウントはaccount名が数値から始まっていて、
Terraformのgoogle_service_accountのaccount_idは `[a-z]` ではじまる必要があるようで一旦諦めました。
([Google: google_service_account - Terraform by HashiCorp](https://www.terraform.io/docs/providers/google/r/google_service_account.html#account_id))

※ importする場合は `Identity and Access Management (IAM) API` を有効にする必要があります。Developer Consoleから有効化しました

## plan

```shell
$ 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.

module.network.google_compute_network.vpc: Refreshing state... [id=terraform-practice-network]
module.bastion.google_compute_instance.bastion: Refreshing state... [id=terraform-practice-instance-1]
module.network.google_compute_subnetwork.vpc_subnet1: Refreshing state... [id=asia-northeast1/terraform-practice-network-subnet1]

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

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

Terraform will perform the following actions:

  # module.bastion.google_compute_address.bastion will be created
  + resource "google_compute_address" "bastion" {
      + address            = (known after apply)
      + address_type       = "EXTERNAL"
      + creation_timestamp = (known after apply)
      + id                 = (known after apply)
      + name               = "terraform-practice-instance-1"
      + network_tier       = (known after apply)
      + project            = (known after apply)
      + region             = "asia-northeast1"
      + self_link          = (known after apply)
      + subnetwork         = (known after apply)
      + users              = (known after apply)
    }

  # module.bastion.google_compute_instance.bastion will be updated in-place
  ~ resource "google_compute_instance" "bastion" {
        can_ip_forward       = false
        cpu_platform         = "Intel Broadwell"
        deletion_protection  = false
        guest_accelerator    = []
        id                   = "terraform-practice-instance-1"
        instance_id          = "xxx"
        label_fingerprint    = "xxx"
        labels               = {}
        machine_type         = "f1-micro"
        metadata             = {}
        metadata_fingerprint = "xxx"
        name                 = "terraform-practice-instance-1"
        project              = "xxxx"
        self_link            = "https://www.googleapis.com/compute/v1/projects/xxxx/zones/asia-northeast1-a/instances/terraform-practice-instance-1"
      ~ tags                 = [
          + "bastion",
          + "server",
        ]
        tags_fingerprint     = "xxx"
        zone                 = "asia-northeast1-a"

        boot_disk {
            auto_delete = true
            device_name = "terraform-practice-instance-1"
            source      = "https://www.googleapis.com/compute/v1/projects/xxxx/zones/asia-northeast1-a/disks/terraform-practice-instance-1"

            initialize_params {
                image  = "https://www.googleapis.com/compute/v1/projects/ubuntu-os-cloud/global/images/ubuntu-1804-bionic-v20190722a"
                labels = {}
                size   = 10
                type   = "pd-standard"
            }
        }

      ~ network_interface {
            name               = "nic0"
            network            = "https://www.googleapis.com/compute/v1/projects/xxxx/global/networks/terraform-practice-network"
            network_ip         = "192.168.10.2"
            subnetwork         = "https://www.googleapis.com/compute/v1/projects/xxxx/regions/asia-northeast1/subnetworks/terraform-practice-network-subnet1"
            subnetwork_project = "xxxx"

          ~ access_config {
              ~ nat_ip       = "xxx.xxx.xxx.xxx" -> (known after apply)
                network_tier = "PREMIUM"
            }
        }

        scheduling {
            automatic_restart   = true
            on_host_maintenance = "MIGRATE"
            preemptible         = false
        }

        service_account {
            email  = "xxxx-compute@developer.gserviceaccount.com"
            scopes = [
                "https://www.googleapis.com/auth/devstorage.read_only",
                "https://www.googleapis.com/auth/logging.write",
                "https://www.googleapis.com/auth/monitoring.write",
                "https://www.googleapis.com/auth/service.management.readonly",
                "https://www.googleapis.com/auth/servicecontrol",
                "https://www.googleapis.com/auth/trace.append",
            ]
        }

        timeouts {}
    }

Plan: 1 to add, 1 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.
```

なんか行けそうですね。

## apply

```shell
$ terraform apply
...
module.bastion.google_compute_address.bastion: Creating...
module.bastion.google_compute_address.bastion: Creation complete after 4s [id=xxxx/asia-northeast1/terraform-practice-instance-1]
module.bastion.google_compute_instance.bastion: Modifying... [id=terraform-practice-instance-1]
module.bastion.google_compute_instance.bastion: Still modifying... [id=terraform-practice-instance-1, 10s elapsed]
module.bastion.google_compute_instance.bastion: Still modifying... [id=terraform-practice-instance-1, 20s elapsed]
module.bastion.google_compute_instance.bastion: Still modifying... [id=terraform-practice-instance-1, 30s elapsed]
module.bastion.google_compute_instance.bastion: Modifications complete after 34s [id=terraform-practice-instance-1]

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

# 次回

- 既存の手動で作ったリソースをすべて手で書き起こしていくのはしんどいので、terraformerをつかってみたい
    - [GoogleCloudPlatform/terraformer: CLI tool to generate terraform files from existing infrastructure (reverse Terraform). Infrastructure to Code](https://github.com/GoogleCloudPlatform/terraformer)