Terraform入門メモ：Terraform CLI導入～IBM Cloud VPC に VSI 作成

主に以下を参照して実施。端末はWindows10。
>[Getting started with Terraform on IBM Cloud](https://cloud.ibm.com/docs/ibm-cloud-provider-for-terraform?topic=ibm-cloud-provider-for-terraform-getting-started)
>[Provisioning an IBM Cloud virtual server for VPC](https://cloud.ibm.com/docs/ibm-cloud-provider-for-terraform?topic=ibm-cloud-provider-for-terraform-sample_vpc_config)

#端末にTerraform CLIをインストール
terraform用ディレクトリを作成

```
PS C:\work> mkdir terraform; cd terraform

    ディレクトリ: C:\work

Mode                 LastWriteTime         Length Name
----                 -------------         ------ ----
d-----        2021/06/16     13:17                terraform

PS C:\work\terraform>
```

Terraformをダウンロードして展開する
https://releases.hashicorp.com/terraform/


```
PS C:\work\terraform> dir

    ディレクトリ: C:\work\terraform

Mode                 LastWriteTime         Length Name
----                 -------------         ------ ----
d-----        2021/06/16     13:18                terraform_0.14.11_windows_amd64
-a----        2021/06/16     13:15       34043279 terraform_0.14.11_windows_amd64.zip

PS C:\work\terraform> dir terraform_0.14.11_windows_amd64

    ディレクトリ: C:\work\terraform\terraform_0.14.11_windows_amd64

Mode                 LastWriteTime         Length Name
----                 -------------         ------ ----
-a----        2021/06/16     13:18       84003704 terraform.exe

PS C:\work\terraform>
```

パスを通してコマンド実行確認

```
PS C:\work\terraform> $ENV:Path=$ENV:Path+";C:\work\terraform\terraform_0.14.11_windows_amd64"
PS C:\work\terraform> terraform
Usage: terraform [global options] <subcommand> [args]

The available commands for execution are listed below.
The primary workflow commands are given first, followed by
less common or more advanced commands.

Main commands:
  init          Prepare your working directory for other commands
  validate      Check whether the configuration is valid
  plan          Show changes required by the current configuration
  apply         Create or update infrastructure
  destroy       Destroy previously-created infrastructure

All other commands:
  console       Try Terraform expressions at an interactive command prompt
  fmt           Reformat your configuration in the standard style
  force-unlock  Release a stuck lock on the current workspace
  get           Install or upgrade remote Terraform modules
  graph         Generate a Graphviz graph of the steps in an operation
  import        Associate existing infrastructure with a Terraform resource
  login         Obtain and save credentials for a remote host
  logout        Remove locally-stored credentials for a remote host
  output        Show output values from your root module
  providers     Show the providers required for this configuration
  refresh       Update the state to match remote systems
  show          Show the current state or a saved plan
  state         Advanced state management
  taint         Mark a resource instance as not fully functional
  untaint       Remove the 'tainted' state from a resource instance
  version       Show the current Terraform version
  workspace     Workspace management

Global options (use these before the subcommand, if any):
  -chdir=DIR    Switch to a different working directory before executing the
                given subcommand.
  -help         Show this help output, or the help for a specified subcommand.
  -version      An alias for the "version" subcommand.
PS C:\work\terraform>
```

#VPC環境の用意
今回ここはGUIで実施。詳細は割愛

+ VPC作成
+ サブネット作成
+ SSH鍵アップロード
+ セキュリティグループ作成
    + インバウンドは社内VPN経由（送信元アドレスが下記 x.x.x.0/24）でのSSHとPingを許可
![sg.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/505855/489a3f7a-153c-2bbd-3fe7-1e414f39f0f0.png)


#.tfファイル作成

###versions.tf

※バージョンは以下で確認できる
https://registry.terraform.io/providers/IBM-Cloud/ibm/latest 

```:versions.tf
terraform {
   required_providers {
      ibm = {
         source = "IBM-Cloud/ibm"
         version = "1.26.2"
      }
    }
}
```

###terraform.tfvars

変数の定義

```:terraform.tfvars
ibmcloud_api_key="xxxxxx"
resource_group="c6bcd9b18bb64c288510d19806dd12dc"
```

※APIキーは過去に作成済のものを使用していて詳細は割愛
※リソースグループIDは以下のように確認できる

```
PS C:\work\terraform> ibmcloud resource groups
Retrieving all resource groups under account 1234567890a1234567890b1234567890 as xxxx@jp.ibm.com...
OK
Name                    ID                                 Default Group   State
resourcegroup1   c6bcd9b18bb64c288510d19806dd12dc   false           ACTIVE
```

###provider.tf

```:provider.tf
variable "ibmcloud_api_key" {}
variable "resource_group" {}

provider "ibm" {
  ibmcloud_api_key = var.ibmcloud_api_key
  region           = "jp-tok"
  resource_group   = var.resource_group
}
```

###vsi-test.tf

VSIとFloating IPを作成するための情報を定義

+ `locals`はローカル変数
+ `data`は既存のIBM Cloudリソースの情報を取得
    + VPC等々既存リソースのリソース名を指定
    + `resource`のパラメータで既存リソースのID値を指定する必要があるが、ここで取得しておけばID値を調べることなく data.xxx.id のように指定できる
+ `resource`が作成するリソースの情報（GUIならオーダー画面で指定する項目に相当）

```:vsi-test.tf
locals {
  ZONE     = "jp-tok-2"
  BASENAME = "test"
}

data "ibm_is_vpc" "vpc" {
  name = "vpc-${local.BASENAME}"
}

data "ibm_is_subnet" "sbn" {
  name = "sbn-${local.BASENAME}"
}

data "ibm_is_security_group" "sg" {
  name = "sg-${local.BASENAME}"
}

data "ibm_is_image" "centos" {
  name = "ibm-centos-8-3-minimal-amd64-3"
}

data "ibm_is_ssh_key" "ssh_key" {
  name = "key-test"
}

resource "ibm_is_instance" "vsi" {
  name    = "vsi-test"
  vpc     = data.ibm_is_vpc.vpc.id
  zone    = local.ZONE
  keys    = [data.ibm_is_ssh_key.ssh_key.id]
  image   = data.ibm_is_image.centos.id
  profile = "cx2-2x4"
  primary_network_interface {
    subnet = data.ibm_is_subnet.sbn.id
    security_groups = [data.ibm_is_security_group.sg.id]
  }
  resource_group = var.resource_group
}

resource "ibm_is_floating_ip" "fip" {
  name   = "fip-${local.BASENAME}"
  target = ibm_is_instance.vsi.primary_network_interface[0].id
  resource_group = var.resource_group
}
```



# terraform 実行
###terraform init

```
PS C:\work\terraform> terraform init

Initializing the backend...

Initializing provider plugins...
- Reusing previous version of ibm-cloud/ibm from the dependency lock file
- Using previously-installed ibm-cloud/ibm v1.26.2

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.
PS C:\work\terraform>
```

###terraform plan

```
PS C:\work\terraform> terraform plan

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:

  # ibm_is_floating_ip.fip will be created
  + resource "ibm_is_floating_ip" "fip" {
      + address                 = (known after apply)
      + id                      = (known after apply)
      + name                    = "fip-test"
      + resource_controller_url = (known after apply)
      + resource_crn            = (known after apply)
      + resource_group          = "c6bcd9b18bb64c288510d19806dd12dc"
      + resource_group_name     = (known after apply)
      + resource_name           = (known after apply)
      + resource_status         = (known after apply)
      + status                  = (known after apply)
      + tags                    = (known after apply)
      + target                  = (known after apply)
      + zone                    = (known after apply)
    }

  # ibm_is_instance.vsi will be created
  + resource "ibm_is_instance" "vsi" {
      + disks                   = (known after apply)
      + gpu                     = (known after apply)
      + id                      = (known after apply)
      + image                   = "r022-95e7a9a1-8707-49ea-bdef-693311570ce0"
      + keys                    = [
        ]
      + memory                  = (known after apply)
      + name                    = "vsi-test"
      + profile                 = "cx2-2x4"
      + resource_controller_url = (known after apply)
      + resource_crn            = (known after apply)
      + resource_group          = "c6bcd9b18bb64c288510d19806dd12dc"
      + resource_group_name     = (known after apply)
      + resource_name           = (known after apply)
      + resource_status         = (known after apply)
      + status                  = (known after apply)
      + tags                    = (known after apply)
      + vcpu                    = (known after apply)
      + volume_attachments      = (known after apply)
      + vpc                     = "r022-45600dc1-1f1a-4a09-a0a7-6131cbbdcfb4"
      + wait_before_delete      = true
      + zone                    = "jp-tok-2"

      + boot_volume {
          + encryption = (known after apply)
          + iops       = (known after apply)
          + name       = (known after apply)
          + profile    = (known after apply)
          + size       = (known after apply)
        }

      + primary_network_interface {
          + allow_ip_spoofing    = false
          + id                   = (known after apply)
          + name                 = (known after apply)
          + primary_ipv4_address = (known after apply)
          + security_groups      = [
              + "r022-9101424b-84e2-4081-8484-53baffaeb9b7",
            ]
          + subnet               = "02f7-3d141b46-f9ac-46d6-9645-748471961b84"
        }
    }

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.

PS C:\work\terraform>
```

### terraform apply

```
PS C:\work\terraform> 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:

  # ibm_is_floating_ip.fip will be created
  + resource "ibm_is_floating_ip" "fip" {
      + address                 = (known after apply)
      + id                      = (known after apply)
      + name                    = "fip-test"
      + resource_controller_url = (known after apply)
      + resource_crn            = (known after apply)
      + resource_group          = "c6bcd9b18bb64c288510d19806dd12dc"
      + resource_group_name     = (known after apply)
      + resource_name           = (known after apply)
      + resource_status         = (known after apply)
      + status                  = (known after apply)
      + tags                    = (known after apply)
      + target                  = (known after apply)
      + zone                    = (known after apply)
    }

  # ibm_is_instance.vsi will be created
  + resource "ibm_is_instance" "vsi" {
      + disks                   = (known after apply)
      + gpu                     = (known after apply)
      + id                      = (known after apply)
      + image                   = "r022-95e7a9a1-8707-49ea-bdef-693311570ce0"
      + keys                    = [
          + "r022-fba74a1a-c774-429f-b833-906cd913a64e",
        ]
      + memory                  = (known after apply)
      + name                    = "vsi-test"
      + profile                 = "cx2-2x4"
      + resource_controller_url = (known after apply)
      + resource_crn            = (known after apply)
      + resource_group          = "c6bcd9b18bb64c288510d19806dd12dc"
      + resource_group_name     = (known after apply)
      + resource_name           = (known after apply)
      + resource_status         = (known after apply)
      + status                  = (known after apply)
      + tags                    = (known after apply)
      + vcpu                    = (known after apply)
      + volume_attachments      = (known after apply)
      + vpc                     = "r022-45600dc1-1f1a-4a09-a0a7-6131cbbdcfb4"
      + wait_before_delete      = true
      + zone                    = "jp-tok-2"

      + boot_volume {
          + encryption = (known after apply)
          + iops       = (known after apply)
          + name       = (known after apply)
          + profile    = (known after apply)
          + size       = (known after apply)
        }

      + primary_network_interface {
          + allow_ip_spoofing    = false
          + id                   = (known after apply)
          + name                 = (known after apply)
          + primary_ipv4_address = (known after apply)
          + security_groups      = [
              + "r022-9101424b-84e2-4081-8484-53baffaeb9b7",
            ]
          + subnet               = "02f7-3d141b46-f9ac-46d6-9645-748471961b84"
        }
    }

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

ibm_is_instance.vsi: Creating...
ibm_is_instance.vsi: Still creating... [10s elapsed]
ibm_is_instance.vsi: Still creating... [20s elapsed]
ibm_is_instance.vsi: Still creating... [30s elapsed]
ibm_is_instance.vsi: Creation complete after 30s [id=02f7_d2362719-28e8-4a58-8c60-102892ce46cd]
ibm_is_floating_ip.fip: Creating...
ibm_is_floating_ip.fip: Still creating... [10s elapsed]
ibm_is_floating_ip.fip: Creation complete after 15s [id=r022-512fdb3d-5525-4082-82c0-10c6a1be2456]

Apply complete! Resources: 2 added, 0 changed, 0 destroyed.
PS C:\work\terraform>
```


# 作成されたVSIの確認

VSIが作成され、`running` になった

```
PS C:\work\terraform> ibmcloud is instances
Listing instances for generation 2 compute in resource group resourcegroup1 and region jp-tok under account IBM as user xxxx@jp.ibm.com...
ID                                          Name            Status    Address       Floating IP      Profile   Image                            VPC                Zone       Resource group
02f7_d2362719-28e8-4a58-8c60-102892ce46cd   vsi-test   running   yyy.yyy.yyy.yyy   xxx.xxx.xxx.xxx   cx2-2x4   ibm-centos-8-3-minimal-amd64-3   vpc-test   jp-tok-2   resourcegroup1
PS C:\work\terraform>
```

Floating IP宛にSSH接続OK。
指定したOSで作成されている。

```
PS C:\work\terraform> ssh -i C:\Users\AaaBbb\.ssh\key-test root@xxx.xxx.xxx.xxx
The authenticity of host 'xxx.xxx.xxx.xxx (xxx.xxx.xxx.xxx)' can't be established.
ECDSA key fingerprint is SHA256:xxxxxxxxxxxxxxxxxxxxxxxxxxx.
Are you sure you want to continue connecting (yes/no/[fingerprint])? yes
Warning: Permanently added 'xxx.xxx.xxx.xxx' (ECDSA) to the list of known hosts.
[root@vsi-test ~]# cat /etc/redhat-release
CentOS Linux release 8.3.2011
[root@vsi-test ~]#
```

VPNを切ると接続できなくなる（セキュリティグループがちゃんと適用できている）

```
PS C:\work\terraform> ssh -i C:\Users\AaaBbb\.ssh\key-test root@xxx.xxx.xxx.xxx
ssh: connect to host xxx.xxx.xxx.xxx port 22: Connection timed out
PS C:\work\terraform>
```

#リソースの削除
### terraform destroy
terraformで作成したリソースは、terraform destroy で削除することができる

```
PS C:\work\terraform> terraform destroy

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

Terraform will perform the following actions:

  # ibm_is_floating_ip.fip will be destroyed
  - resource "ibm_is_floating_ip" "fip" {
      - address                 = "xxx.xxx.xxx.xxx" -> null
      - id                      = "r022-512fdb3d-5525-4082-82c0-10c6a1be2456" -> null
      - name                    = "fip-test" -> null
      - resource_controller_url = "https://cloud.ibm.com/vpc-ext/network/floatingIPs" -> null
      - resource_crn            = "crn:v1:bluemix:public:is:jp-tok-2:a/1fc8373f538a408187ffedbe62e5796a::floating-ip:r022-512fdb3d-5525-4082-82c0-10c6a1be2456" -> null
      - resource_group          = "c6bcd9b18bb64c288510d19806dd12dc" -> null
      - resource_group_name     = "resourcegroup1" -> null
      - resource_name           = "fip-test" -> null
      - resource_status         = "available" -> null
      - status                  = "available" -> null
      - tags                    = [] -> null
      - target                  = "02f7-4b102bad-5d14-45b3-bda8-81657fec56a9" -> null
      - zone                    = "jp-tok-2" -> null
    }

  # ibm_is_instance.vsi will be destroyed
  - resource "ibm_is_instance" "vsi" {
      - disks                   = [] -> null
      - gpu                     = [] -> null
      - id                      = "02f7_d2362719-28e8-4a58-8c60-102892ce46cd" -> null
      - image                   = "r022-95e7a9a1-8707-49ea-bdef-693311570ce0" -> null
      - keys                    = [
          - "r022-fba74a1a-c774-429f-b833-906cd913a64e",
        ] -> null
      - memory                  = 4 -> null
      - name                    = "vsi-test" -> null
      - profile                 = "cx2-2x4" -> null
      - resource_controller_url = "https://cloud.ibm.com/vpc-ext/compute/vs" -> null
      - resource_crn            = "crn:v1:bluemix:public:is:jp-tok-2:a/1fc8373f538a408187ffedbe62e5796a::instance:02f7_d2362719-28e8-4a58-8c60-102892ce46cd" -> null
      - resource_group          = "c6bcd9b18bb64c288510d19806dd12dc" -> null
      - resource_group_name     = "resourcegroup1" -> null
      - resource_name           = "vsi-test" -> null
      - resource_status         = "running" -> null
      - status                  = "running" -> null
      - tags                    = [] -> null
      - vcpu                    = [
          - {
              - architecture = "amd64"
              - count        = 2
            },
        ] -> null
      - volume_attachments      = [
          - {
              - id          = "02f7-b0fdfe7e-27ba-41de-a46a-671de4f15bcf"
              - name        = "clubbing-divisive-pointer-letdown"
              - volume_crn  = "crn:v1:bluemix:public:is:jp-tok-2:a/1fc8373f538a408187ffedbe62e5796a::volume:r022-708837e6-4e27-44d6-b0ac-620484213e50"
              - volume_id   = "r022-708837e6-4e27-44d6-b0ac-620484213e50"
              - volume_name = "shawl-grime-detour-presoaked"
            },
        ] -> null
      - vpc                     = "r022-45600dc1-1f1a-4a09-a0a7-6131cbbdcfb4" -> null
      - wait_before_delete      = true -> null
      - zone                    = "jp-tok-2" -> null

      - boot_volume {
          - iops    = 3000 -> null
          - name    = "shawl-grime-detour-presoaked" -> null
          - profile = "general-purpose" -> null
          - size    = 100 -> null
        }

      - primary_network_interface {
          - allow_ip_spoofing    = false -> null
          - id                   = "02f7-4b102bad-5d14-45b3-bda8-81657fec56a9" -> null
          - name                 = "stalemate-theorize-donator-steerable" -> null
          - port_speed           = 0 -> null
          - primary_ipv4_address = "yyy.yyy.yyy.yyy" -> null
          - security_groups      = [
              - "r022-9101424b-84e2-4081-8484-53baffaeb9b7",
            ] -> null
          - subnet               = "02f7-3d141b46-f9ac-46d6-9645-748471961b84" -> null
        }
    }

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

Do you really want to destroy all resources?
  Terraform will destroy all your managed infrastructure, as shown above.
  There is no undo. Only 'yes' will be accepted to confirm.

  Enter a value: yes

ibm_is_floating_ip.fip: Destroying... [id=r022-512fdb3d-5525-4082-82c0-10c6a1be2456]
ibm_is_floating_ip.fip: Still destroying... [id=r022-512fdb3d-5525-4082-82c0-10c6a1be2456, 10s elapsed]
ibm_is_floating_ip.fip: Destruction complete after 14s
ibm_is_instance.vsi: Destroying... [id=02f7_d2362719-28e8-4a58-8c60-102892ce46cd]
ibm_is_instance.vsi: Still destroying... [id=02f7_d2362719-28e8-4a58-8c60-102892ce46cd, 10s elapsed]
ibm_is_instance.vsi: Still destroying... [id=02f7_d2362719-28e8-4a58-8c60-102892ce46cd, 20s elapsed]
ibm_is_instance.vsi: Still destroying... [id=02f7_d2362719-28e8-4a58-8c60-102892ce46cd, 30s elapsed]
ibm_is_instance.vsi: Still destroying... [id=02f7_d2362719-28e8-4a58-8c60-102892ce46cd, 40s elapsed]
ibm_is_instance.vsi: Still destroying... [id=02f7_d2362719-28e8-4a58-8c60-102892ce46cd, 50s elapsed]
ibm_is_instance.vsi: Destruction complete after 52s

Destroy complete! Resources: 2 destroyed.
PS C:\work\terraform>
```

VSIがいなくなったことを確認

```
PS C:\work\terraform> ibmcloud is instances
Listing instances for generation 2 compute in resource group resourcegroup1 and region jp-tok under account IBM as user xxxx@jp.ibm.com...
No instances were found.
PS C:\work\terraform>
```


#エラー対応メモ
VSI作成に至るまでの terraform 実行エラーと`vsi-test.tf`ファイル修正メモ

### terraform plan 実行時のエラー
##### Error: No image found with name xxx

```
Error: No image found with name  centos-8-amd64

  on vsi-test.tf line 36, in data "ibm_is_image" "centos":
  36: data "ibm_is_image" "centos" {
```

利用可能なimageは`ibmcloud is images`で確認できる。
`OS name`ではなく`Name`の値を設定する。

```
PS C:\work> ibmcloud is images
Listing images for generation 2 compute in resource group resourcegroup1 and region jp-tok under account IBM as user xxxx@jp.ibm.com...
ID                                          Name                                               Status       Arch    OS name                              OS version                                File size(GB)   Visibility   Owner type   Encryption   Resource group
r022-f10e4ea0-1f0b-4fb6-8cfd-1c9aad475046   ibm-centos-7-9-minimal-amd64-3                     available    amd64   centos-7-amd64                       7.x - Minimal Install                     1               public       provider     none         -
r022-95e7a9a1-8707-49ea-bdef-693311570ce0   ibm-centos-8-3-minimal-amd64-3                     available    amd64   centos-8-amd64                       8.x - Minimal Install                     1               public       provider     none         -
（略）
```


| 修正前 | 修正後 | 
|:-----------|------------|
| data "ibm_is_image" "centos" { <br> &nbsp; &nbsp; name = "centos-8-amd64" <br> } | data "ibm_is_image" "centos" {<br> &nbsp; &nbsp; name = "ibm-centos-8-3-minimal-amd64-3" <br> } | 


##### Error: Incorrect attribute value type

```
Error: Incorrect attribute value type

  on vsi-test.tf line 53, in resource "ibm_is_instance" "vsi-test":
  53:   keys    = "r022-fba74a1a-c774-429f-b833-906cd913a64e"

Inappropriate value for attribute "keys": set of string required.
```

以下の例にもあるように、SSHキーは配列で指定する、つまり[]で括る必要がある
https://registry.terraform.io/providers/IBM-Cloud/ibm/latest/docs/resources/is_instance

| 修正前 | 修正後 | 
|:-----------|------------|
| resource "ibm_is_instance" "vsi" { <br>  &nbsp; &nbsp; (略) <br> &nbsp; &nbsp; keys = data.ibm_is_ssh_key.ssh_key.id | resource "ibm_is_instance" "vsi" { <br>  &nbsp; &nbsp; (略) <br> &nbsp; &nbsp; keys = [data.ibm_is_ssh_key.ssh_key.id]  | 



##terraform applyでのエラー
##### Error: user does not have permission to use default resource group


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

ibm_is_instance.vsi-test: Creating...

Error: user does not have permission to use default resource group

  on vsi-test.tf line 49, in resource "ibm_is_instance" "vsi-test":
  49: resource "ibm_is_instance" "vsi-test" {
```

リソースグループを指定する必要がある（GUIでも同様なことですが）

| 修正前 | 修正後 | 
|:-----------|------------|
| resource "ibm_is_instance" "vsi" { <br> &nbsp; &nbsp; (略) <br> } <br><br>  | resource "ibm_is_instance" "vsi" { <br> &nbsp; &nbsp; (略) <br>  &nbsp; &nbsp; resource_group = var.resource_group <br> }  |