Terraformをサクッと理解したい！

Terraformについて勉強しました。
Terraformに興味を持ち始めた方、他の記事は詳しすぎて全体像が掴めない方に向けてザックリと説明し、
その後Terraformを使ってproxmox上に仮想マシンを立てる演習をします。

## Terraformってなに？
IaC(infrastructure as code)ツールの一種。
名の通り、GUIでポチポチ設定しなくてもコードさえ書けば「terraform apply」というコマンド一つでインフラ環境が構築できます。
その中でもTerraformはクラウドプロバイダ(AWS、Azure, GCP等)に対応しているため人気。

## Terraformを使うメリットって？
先述した「コマンド一つでインフラ環境が構築できる」ってところが一番大きいと思ってます。
つまり、誰が「terraform apply」コマンドを実行しても**同じ環境が構築できます**。(これすごい)
あと、コードで書かれているものは全てTerraformさんが管理してくれるので、
100台のサーバの設定をコードで書いていれば100台**一気に構築して、一気に削除** ができます。(これもすごい)

## どのサービスで使えるの？
Terraformプロバイダが存在しているものであれば使えます。
(プロバイダは自作できるがハードル高い)

下記、公開されているプロバイダです。

https://registry.terraform.io/browse/providers

## さっそく使ってみよう
雰囲気を知っていただくことを目的とするため、バージョン管理ツールやディレクトリ構成については端折らせていただきます。

※Terraformで使う構成言語(HCL)を端的に解説するので、演習でproxmoxを使うため環境の用意がなく一緒に演習できない方も眺めていただければ幸いです。

## 使用環境
proxmox VE 8.3.0
Rocky Linux 9.4(proxmox上に立ててる)

## Terraformをインストール
``` bash
$ sudo dnf -y update
$ sudo dnf install -y yum-utils
$ sudo dnf config-manager --add-repo https://rpm.releases.hashicorp.com/RHEL/hashicorp.repo
$ sudo dnf -y install terraform
```
Terraformのバージョンが表示されていればインストール完了です。
``` bash
$ terraform --version
```
``` bash:実行結果
Terraform v1.11.4
on linux_amd64
```
参考URL

https://developer.hashicorp.com/terraform/tutorials/aws-get-started/install-cli

## 作業ディレクトリ作成
``` bash
$ mkdir terraform-test #好きな名前のディレクトリでOKです

```

## tfファイル作成
Terraformでは「.tf」拡張子のファイルで定義された内容を実行します。
本来であればごとにtfファイルを分けたりしますが、今回は一つのtfファイルにまとめます。
``` bash
$ touch main.tf #好きなtfファイル名でOKです
```

## proxmoxプロバイダ情報を書く
まず、Terraformでサービスを扱うためには、
プロバイダと呼ばれるTerraformとサービスを橋渡しする役割のものが必要となります。

[proxmoxプロバイダ](https://registry.terraform.io/providers/Telmate/proxmox/latest/docs)のサイトを開き
[USE PROVIDER]をクリックすると書くべき内容が表示されるのでコピペでOKです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2517078/c48ba724-e07f-4a1f-aed3-42db33f2c589.png)

``` vim:main.tf 
terraform {
  required_providers {
    proxmox = {
      source = "Telmate/proxmox"
      version = "3.0.1-rc8"
    }
  }
}
```
これだけでプロバイダの指定とバージョン指定完了です。

## 設定したプロバイダをダウンロードしよう
プロバイダの設定を書けたので、プロバイダを使うためにダウンロードします。

下記コマンドはプロバイダの追加・変更をした際に実行するイメージです。
```
$ terraform init
```


``` markdown:実行結果
Initializing the backend...
Initializing provider plugins...
- Finding telmate/proxmox versions matching "3.0.1-rc8"...
- Installing telmate/proxmox v3.0.1-rc8...
- Installed telmate/proxmox v3.0.1-rc8 (self-signed, key ID A9EBBE091B35AFCE)
Partner and community providers are signed by their developers.
If you'd like to know more about provider signing, you can read about it here:
https://developer.hashicorp.com/terraform/cli/plugins/signing
Terraform has created a lock file .terraform.lock.hcl to record the provider
selections it made above. Include this file in your version control repository
so that Terraform can guarantee to make the same selections by default when
you run "terraform init" in the future.

Terraform has been successfully initialized!
```
<font color="blue">**Terraform has been successfully initialized!**</font>と表示されていればダウンロード完了です。

## なんかファイル/ディレクトリが増えた
ls -laでディレクトリ内部を確認すると
.terraform
.terraform.lock.hcl
というものができてます。

| ファイル／ディレクトリ         | 役割・説明                                                                 |
|------------------------------|---------------------------------------------------------------------------|
| `.terraform/`                | Terraformが内部的に使うキャッシュディレクトリ。プロバイダやモジュールの実体が入る。     | 
| `.terraform.lock.hcl`        | 使用中プロバイダの正確なバージョンとチェックサムを記録。同じバージョンが使われるように再現性を保証するロックファイル。 |

環境によっては複数のTerraformのバージョンを使い分けたりするので必要となります。
特にこのファイル/ディレクトリを触ることはないです。

## proxmoxの認証情報を書こう
プロバイダをダウンロードしてproxmoxサーバにリクエストを送ればすぐ構築できます！ってわけではなくて、しっかりとサーバに対して認証をする必要があります。
(認証しなくてよかったら誰でも私のproxmoxにサーバ立てられますもんね、GUIでログインするのと同じです。)

まずproxmox側でAPIトークンを発行します。
TokenID:好きな名前でOK
Privilage Separation:チェックを外します

:::note warn
Privilage Separationのチェックを外すとAPIトークンを発行するユーザと同じ権限を継承するためトークンの管理に注意してください。
(この場合トークンがroot権限持ってしまってます。)
今回は演習のためチェックを外してます。
:::
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2517078/d7ed83ca-a1bd-49bb-a886-e4c4d7d09f70.png)

Addを押すとシークレットが表示されます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2517078/cd3493ae-d2d2-40d2-99cf-84c9ad8f723d.png)

https://registry.terraform.io/providers/Telmate/proxmox/latest/docs

↑を参考にTokenIDとSecret情報をmain.tfに追記します。
```vim:main.tf
provider "proxmox" {
  # Configuration options
  pm_api_url = "https://192.168.100.100:8006/api2/json"
  pm_api_token_id = "root@pam!terraform-test"
  pm_api_token_secret = "xxxxxxxxxxxxxxxxxxxxxxxxx"
  pm_tls_insecure  = true
}
```
参考サイトにpm_api_url等の引数がどのような役割をしているか説明が載っています。
基本的にはどのサービスでも参考サイトとtfファイルを行き来しながら試行錯誤していく感じになります。(覚えるのはほぼ不可能なので。)

:::note warn
tfファイルに直接TokenIDとSecretをコーディングしてますが、環境変数を使った方法が安全です。
今回は演習のためハードコーディングしてます。
:::
これで認証が行われproxmoxサーバにリクエストを投げることができる状態になります。
※ちなみにプロバイダのことだけど認証情報追加のときは「terraform init」する必要ありません。
## Terraformで仮想マシン立てよう(必要最低限記述編)
例に漏れず参考サイトを使います

https://registry.terraform.io/providers/Telmate/proxmox/latest/docs/resources/vm_qemu

Resources欄が仮想マシンの設定方法が記述してある箇所です。(他のサービスでも同様です。)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2517078/41264e28-856d-4be7-a74c-41fe8d74da31.png)

仮想マシンの設定内容をmain.tfに追記します。
``` vim:main.py
resource "proxmox_vm_qemu" "machine1" {
  name        = "linux-for-TerraformTest"
  target_node = "pve"

  disks {
    ide {
      ide2 {
        cdrom {
          iso = "local:iso/Rocky-9.4-x86_64-minimal.iso"
        }
      }
    }
  }
}
```
これだけです...。
説明の一番上にあったコードをコメントを消したり、値を変更しただけです...。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2517078/73f7c52e-2080-4fbc-ad9c-9e379c26bd26.png)


``` vim:main.py(説明)
resource "proxmox_vm_qemu" "machine1" {　　　←"machine1"はTerraform内部で使う名前(好きな名前でOK) 
  name        = "linux-for-TerraformTest"　　←仮想マシンの名前(好きな名前でOK)
  target_node = "pve"　　　　　　　　　　　　　←どこに仮想マシンを立てるか

  disks {　　　　　　　　　　　　　　　　　　　 ←どのisoファイルを使うか指定
    ide {
      ide2 {
        cdrom {
          iso = "local:iso/Rocky-9.4-x86_64-minimal.iso"
        }
      }
    }
  }
}
```

target_nodeはこの部分の名前です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2517078/ab70b1e7-73ec-43de-9109-6692f0f7039b.png)

サイトによるとisoファイルの指定方法は[ストレージ名]:iso/〇〇〇〇だそうなので、
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2517078/39fa1554-e21a-49bd-9146-e7cb31d344d6.png)

local:iso/Rocky-9.4-x86_64-minimal.isoとなります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2517078/28f20202-6f07-4dfe-9d2f-10efcb12d01f.png)


## 仮想マシン構築する前に設定内容の確認をしよう
terraform planを実行するとコードで定義した内容が表示されます。

``` bash
$ terraform init
```
``` vim:実行結果
  # proxmox_vm_qemu.machine1 will be created
  + resource "proxmox_vm_qemu" "machine1" {
      + additional_wait        = 5
      + agent                  = 0
      + agent_timeout          = 90
      + automatic_reboot       = true
      + balloon                = 0
      + bios                   = "seabios"
      + boot                   = (known after apply)
      + bootdisk               = (known after apply)
      + ciupgrade              = false
      + clone_wait             = 10
      + cores                  = 1
      + cpu_type               = "host"
      + current_node           = (known after apply)
      + default_ipv4_address   = (known after apply)
      + default_ipv6_address   = (known after apply)
      + define_connection_info = true
      + desc                   = "Managed by Terraform."
      + force_create           = false
      + full_clone             = true
      + hotplug                = "network,disk,usb"
      + id                     = (known after apply)
      + kvm                    = true
      + linked_vmid            = (known after apply)
      + memory                 = 512
      + name                   = "linux-for-TerraformTest"
      + onboot                 = false
      + protection             = false
      + reboot_required        = (known after apply)
      + scsihw                 = "lsi"
      + skip_ipv4              = false
      + skip_ipv6              = false
      + sockets                = 1
      + ssh_host               = (known after apply)
      + ssh_port               = (known after apply)
      + tablet                 = true
      + tags                   = (known after apply)
      + target_node            = "pve"
      + unused_disk            = (known after apply)
      + vcpus                  = 0
      + vm_state               = "running"
      + vmid                   = (known after apply)

      + disks {
          + ide {
              + ide2 {
                  + cdrom {
                      + iso = "local:iso/Rocky-9.4-x86_64-minimal.iso"
                    }
                }
            }
        }

      + smbios (known after apply)
    }

Plan: 1 to add, 0 to change, 0 to destroy.
```
仮想マシン名、使用するisoファイルしか定義していないので、
ところどころ(known after apply)と書いてますが、構築してからのお楽しみみたいなイメージです。
定義していない項目でもデフォルト値が用意されてるものもあるようで、
BIOSがSeabiosで
Memoryは512など既に決まっているようですね。

## 内容確認できたので仮想マシンを立てよう
terraform applyを実行することで実際に定義通りの仮想マシンの構築が始まります。
``` bash
$ terraform apply
```

``` vim:実行結果
Do you want to perform these actions?
  Terraform will perform the actions described above.
  Only 'yes' will be accepted to approve.
```
terraform planで表示された内容の後に実行するか確認されたのでyesと入力すると構築が始まり、
``` vim:実行結果
proxmox_vm_qemu.machine1: Creating...
proxmox_vm_qemu.machine1: Still creating... [10s elapsed]
proxmox_vm_qemu.machine1: Creation complete after 11s [id=pve/qemu/103]

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

<font color="blue">**Apply complete!**</font>と表示されたら構築完了です。

## 仮想マシンの内容を見てみると...
構築できました！！
が、しかし、ストレージの割り当てもされてないしメモリも少なすぎて動かないです。
(でもTerraformのすごさの片鱗は見えたんじゃないでしょうか？)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2517078/58c24bd3-67e6-49ea-b2a7-96f6c4b250f2.png)

## 自分が望む設定に修正しましょう
ここからが少し大変でterraform planで表示された設定内容を思い通りに修正しなければいけません。
試行錯誤の始まりです。

目標は
・CPUは2コアにしたい
・メモリは2GBほしい(2048MB)
・インターネットに接続したい
・ストレージ容量50GBにしたい
・起動順序をストレージを最優先にしたい(デフォルトだとISOを読み込んでしまうから)
・SCSIコントローラはVMware PVSCSIにする

やってみましょう。

最終的なmain.tfはこうなります。(サイトで対象の引数を探して試行錯誤してます。)
``` vim:main.tf
  required_providers {
    proxmox = {
      source  = "Telmate/proxmox"
      version = "3.0.1-rc8"
    }
  }
}

provider "proxmox" {
  # Configuration options
  pm_api_url          = "https://192.168.100.100:8006/api2/json"
  pm_api_token_id     = "root@pam!terraform-test"
  pm_api_token_secret = "d518da40-e0b5-4149-8f7c-b125665c50ec"
  pm_tls_insecure     = true
}

resource "proxmox_vm_qemu" "machine1" {
  name        = "linux-for-TerraformTest"
  target_node = "pve"

  disks {
    ide {
      ide2 {
        cdrom {
          iso = "local:iso/Rocky-9.4-x86_64-minimal.iso"
        }
      }
    }
    scsi {
      scsi0 {
        disk {
          storage = "local-lvm"
          size    = "50"
        }
      }
    }
  }
  cores  = "2"
  memory = "2048"
  boot   = "order=scsi0;ide2"
  scsihw   = "pvscsi"
  network {
    id        = 0
    bridge    = "vmbr0"
    firewall  = false
    link_down = false
    model     = "vmxnet3"
  }

}
```

terraform planを実行し、エラーが出ないことを確認しておきます。

``` vim:実行結果
proxmox_vm_qemu.machine1: Refreshing state... [id=pve/qemu/103]

Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the
following symbols:
  ~ update in-place

Terraform will perform the following actions:

  # proxmox_vm_qemu.machine1 will be updated in-place
  ~ resource "proxmox_vm_qemu" "machine1" {
      ~ boot                   = "order=ide2" -> "order=scsi0;ide2"
      ~ cores                  = 1 -> 2
        id                     = "pve/qemu/103"
      ~ memory                 = 512 -> 2048
        name                   = "linux-for-TerraformTest"
      ~ scsihw                 = "lsi" -> "pvscsi"
        tags                   = null
        # (44 unchanged attributes hidden)

      ~ disks {
          + scsi {
              + scsi0 {
                  + disk {
                      + backup               = true
                      + format               = "raw"
                      + iops_r_burst         = 0
                      + iops_r_burst_length  = 0
                      + iops_r_concurrent    = 0
                      + iops_wr_burst        = 0
                      + iops_wr_burst_length = 0
                      + iops_wr_concurrent   = 0
                      + mbps_r_burst         = 0
                      + mbps_r_concurrent    = 0
                      + mbps_wr_burst        = 0
                      + mbps_wr_concurrent   = 0
                      + size                 = "50"
                      + storage              = "local-lvm"
                    }
                }
            }

            # (1 unchanged block hidden)
        }

      + network {
          + bridge    = "vmbr0"
          + firewall  = false
          + id        = 0
          + link_down = false
          + model     = "vmxnet3"
        }

        # (1 unchanged block hidden)
    }

Plan: 0 to add, 1 to change, 0 to destroy.
```
前回apply時との差分が表示されました
エラーなくしっかり反映されているのでterraform applyします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2517078/4efdeb07-314b-4ed9-a917-e336c3cee3fa.png)
起動状態のまま変更を行ったため、仮想マシンを停止すると...

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2517078/7f7b1a27-b8f7-41e4-bb39-f10064f947d5.png)
無事望み通りの設定に変更されました。

## まとめ
Terraformという名前を知っている方を対象に記事を書かせていただきました。
機会があれば、
・なぜ前回apply時の情報が分かるのか
・同じ設定のリソースを複数個作成する方法
・terraform apply時に任意の設定を入力できる方法
などを書きたいとおもい