TerraformでOpenStackリソースを配備

# はじめに

今までOpenStack上にインスタンス等を配備するときはHeatを使っていたが、試しにTerraformを使ってみることにした。

# 環境

* Ubuntu 20.04.3
* Terraform 1.0.6
* OpenStack Queens

# 作業ログ

## Terraformのインストール

以下の`Ubuntu/Debian`の手順を参考にインストールを進める。
https://learn.hashicorp.com/tutorials/terraform/install-cli?in=terraform/aws-get-started#install-terraform

AWS向けの手順のようだが、特にAWS固有の手順には見えないので気にせず進める。

本環境の制約上、hashicorpのリポジトリにアクセスできないため、`Manual installation`でインストールする。

以下のページから、zipファイルをダウンロードし、解凍してバイナリを得る
https://www.terraform.io/downloads.html

```
$ wget https://releases.hashicorp.com/terraform/1.0.6/terraform_1.0.6_linux_amd64.zip
--2021-09-07 01:21:20--  https://releases.hashicorp.com/terraform/1.0.6/terraform_1.0.6_linux_amd64.zip
Connecting to 172.16.70.1:63128... connected.
Proxy request sent, awaiting response... 200 OK
Length: 32677516 (31M) [application/zip]
Saving to: ‘terraform_1.0.6_linux_amd64.zip’

terraform_1.0.6_linux_amd64.zip         100%[============================================================================>]  31.16M  8.44MB/s    in 3.9s    

2021-09-07 01:21:24 (7.89 MB/s) - ‘terraform_1.0.6_linux_amd64.zip’ saved [32677516/32677516]
$ unzip terraform_1.0.6_linux_amd64.zip 
Archive:  terraform_1.0.6_linux_amd64.zip
  inflating: terraform
$ ll terraform
-rwxr-xr-x 1 ubuntu ubuntu 79350901 Sep  3 14:36 terraform*
```

バイナリをパスが通ったディレクトリに移動

```
$ sudo mv terraform /usr/local/bin/
$ ll /usr/local/bin/terraform 
-rwxr-xr-x 1 ubuntu ubuntu 79350901 Sep  3 14:36 /usr/local/bin/terraform*
```

コマンドが使えるか確認

```
$ terraform -v
Terraform v1.0.6
on linux_amd64
```

コマンドの補完の設定

```
$ terraform -install-autocomplete
$ cat ~/.bashrc

... snip ...

complete -C /usr/local/bin/terraform terraform
$ source ~/.bashrc
```

## TerraformでOpenStackリソースを配備

`terraform`と`openstack`のキーワードでググると、以下のページが先頭に出てきた。
https://registry.terraform.io/providers/terraform-provider-openstack/openstack/latest/docs

Providerの定義は以下の通り。

> Terraform relies on plugins called "providers" to interact with cloud providers, SaaS providers, and other APIs.
>
Terraform configurations must declare which providers they require so that Terraform can install and use them. Additionally, some providers require configuration (like endpoint URLs or cloud regions) before they can be used.

https://www.terraform.io/docs/language/providers/index.html

Terraformのプラグインで、インフラ（OpenStackやAWSなどのクラウド）と連携するためのモジュールである。

まずは、例に倣って定義ファイル（tfファイル）を作成する。

```
$ mkdir openstack
$ cd openstack/
$ vim main.tf
```

作成した定義ファイルは以下の通り。

```tf
# Define required providers
terraform {
required_version = ">= 0.14.0"
  required_providers {
    openstack = {
      source  = "terraform-provider-openstack/openstack"
      version = "~> 1.35.0"
    }
  }
}

# Configure the OpenStack Provider
provider "openstack" {
  user_name   = "user01"
  tenant_name = "prj01"
  password    = "p@$$w0rd"
  auth_url    = "https://172.16.71.80:5000/"
  region      = "RegionOne"
  cacert_file = "/etc/ssl/certs/openstack-ca-certificates.crt"
}

# Create a web server
resource "openstack_compute_instance_v2" "test-server" {
  name      = "my_instance"
  image_id  = "4128a986-1f77-4201-9bbf-90de4037dee1"
  flavor_id = "2ce31fef-68b6-4953-81bc-b1c79d22f1b0 "
  key_pair  = "user01key"

  network {
    uuid = "b1bec6fb-a3af-4280-befc-decb376ac5ef"
  }
}
```

### `terraform init`

まずはワークスペースを初期化する。

```
$ terraform init

Initializing the backend...

Initializing provider plugins...
- Finding terraform-provider-openstack/openstack versions matching "~> 1.35.0"...
- Installing terraform-provider-openstack/openstack v1.35.0...
- Installed terraform-provider-openstack/openstack v1.35.0 (self-signed, key ID 4F80527A391BEFD2)

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://www.terraform.io/docs/cli/plugins/signing.html

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!

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

`terraform init`の実行時に、使用するプラグインのダウンロード等が実施される。

### `terraform plan`

terraformの実行で変更される内容を確認する。

```
$ 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:

  # openstack_compute_instance_v2.test-server will be created
  + resource "openstack_compute_instance_v2" "test-server" {
      + access_ip_v4        = (known after apply)
      + access_ip_v6        = (known after apply)
      + all_metadata        = (known after apply)
      + all_tags            = (known after apply)
      + availability_zone   = (known after apply)
      + flavor_id           = "2ce31fef-68b6-4953-81bc-b1c79d22f1b0 "
      + flavor_name         = (known after apply)
      + force_delete        = false
      + id                  = (known after apply)
      + image_id            = "4128a986-1f77-4201-9bbf-90de4037dee1"
      + image_name          = (known after apply)
      + key_pair            = "user01key"
      + name                = "my_instance"
      + power_state         = "active"
      + region              = (known after apply)
      + security_groups     = (known after apply)
      + stop_before_destroy = false

      + network {
          + access_network = false
          + fixed_ip_v4    = (known after apply)
          + fixed_ip_v6    = (known after apply)
          + floating_ip    = (known after apply)
          + mac            = (known after apply)
          + name           = (known after apply)
          + port           = (known after apply)
          + uuid           = "b1bec6fb-a3af-4280-befc-decb376ac5ef"
        }
    }

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

────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────

Note: You didn't use the -out option to save this plan, so Terraform can't guarantee to take exactly these actions if you run "terraform apply" now.
```

### `terraform apply`

tfファイルに定義したリソースが作成される。
`terraform plan`で確認した内容に問題なければ実行する。

```
ubuntu@terraform:~/terraform$ terraform apply 

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:

  # openstack_compute_instance_v2.test-server will be created
  + resource "openstack_compute_instance_v2" "test-server" {
      + access_ip_v4        = (known after apply)
      + access_ip_v6        = (known after apply)
      + all_metadata        = (known after apply)
      + all_tags            = (known after apply)
      + availability_zone   = (known after apply)
      + flavor_id           = "2ce31fef-68b6-4953-81bc-b1c79d22f1b0 "
      + flavor_name         = (known after apply)
      + force_delete        = false
      + id                  = (known after apply)
      + image_id            = "4128a986-1f77-4201-9bbf-90de4037dee1"
      + image_name          = (known after apply)
      + key_pair            = "user01key"
      + name                = "my_instance"
      + power_state         = "active"
      + region              = (known after apply)
      + security_groups     = (known after apply)
      + stop_before_destroy = false

      + network {
          + access_network = false
          + fixed_ip_v4    = (known after apply)
          + fixed_ip_v6    = (known after apply)
          + floating_ip    = (known after apply)
          + mac            = (known after apply)
          + name           = (known after apply)
          + port           = (known after apply)
          + uuid           = "b1bec6fb-a3af-4280-befc-decb376ac5ef"
        }
    }

Plan: 1 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

openstack_compute_instance_v2.test-server: Creating...
openstack_compute_instance_v2.test-server: Still creating... [10s elapsed]
openstack_compute_instance_v2.test-server: Still creating... [20s elapsed]
openstack_compute_instance_v2.test-server: Creation complete after 25s [id=8d1715e9-e276-4480-bf03-7b0776ea7bfa]

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

リソースが作成されていることを確認する。

```
$ openstack server show 8d1715e9-e276-4480-bf03-7b0776ea7bfa
+-------------------------------------+----------------------------------------------------------+
| Field                               | Value                                                    |
+-------------------------------------+----------------------------------------------------------+
| OS-DCF:diskConfig                   | MANUAL                                                   |
| OS-EXT-AZ:availability_zone         | nova                                                     |
| OS-EXT-SRV-ATTR:host                | cmp001                                                   |
| OS-EXT-SRV-ATTR:hypervisor_hostname | cmp001.mcp-smmr18.fujitsu.local                          |
| OS-EXT-SRV-ATTR:instance_name       | instance-0000334a                                        |
| OS-EXT-STS:power_state              | Running                                                  |
| OS-EXT-STS:task_state               | None                                                     |
| OS-EXT-STS:vm_state                 | active                                                   |
| OS-SRV-USG:launched_at              | 2021-09-21T02:58:41.000000                               |
| OS-SRV-USG:terminated_at            | None                                                     |
| accessIPv4                          |                                                          |
| accessIPv6                          |                                                          |
| addresses                           | maintenance-net=10.0.0.16                                |
| config_drive                        | True                                                     |
| created                             | 2021-09-21T02:58:22Z                                     |
| flavor                              | m1.medium (2ce31fef-68b6-4953-81bc-b1c79d22f1b0)         |
| hostId                              | ee90af4ebb1eb84986019854bb9618db381c570845f36a65ecc891cd |
| id                                  | 8d1715e9-e276-4480-bf03-7b0776ea7bfa                     |
| image                               | ubuntu_20.04.3 (4128a986-1f77-4201-9bbf-90de4037dee1)    |
| key_name                            | user01key                                                |
| name                                | my_instance                                              |
| progress                            | 0                                                        |
| project_id                          | cfeb198dbfa2414e8b32534ac0511d52                         |
| properties                          |                                                          |
| security_groups                     | name='default'                                           |
| status                              | ACTIVE                                                   |
| updated                             | 2021-09-21T02:58:41Z                                     |
| user_id                             | a430f45e0f6c4290863b869593cdae6e                         |
| volumes_attached                    |                                                          |
+-------------------------------------+----------------------------------------------------------+
```

tfファイルに定義したインスタンスが作成されていることを確認できた。

## リソースの削除

### `terraform plan -destroy`

削除するリソースを確認

```
$ terraform plan -destroy
openstack_compute_instance_v2.test-server: Refreshing state... [id=8d1715e9-e276-4480-bf03-7b0776ea7bfa]

Note: Objects have changed outside of Terraform

Terraform detected the following changes made outside of Terraform since the last "terraform apply":

  # openstack_compute_instance_v2.test-server has been changed
  ~ resource "openstack_compute_instance_v2" "test-server" {
        id                  = "8d1715e9-e276-4480-bf03-7b0776ea7bfa"
        name                = "my_instance"
      + tags                = []
        # (14 unchanged attributes hidden)

        # (1 unchanged block hidden)
    }

Unless you have made equivalent changes to your configuration, or ignored the relevant attributes using ignore_changes, the following plan may include
actions to undo or respond to these changes.

────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────

Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols:
  - destroy

Terraform will perform the following actions:

  # openstack_compute_instance_v2.test-server will be destroyed
  - resource "openstack_compute_instance_v2" "test-server" {
      - access_ip_v4        = "10.0.0.16" -> null
      - all_metadata        = {} -> null
      - all_tags            = [] -> null
      - availability_zone   = "nova" -> null
      - flavor_id           = "2ce31fef-68b6-4953-81bc-b1c79d22f1b0" -> null
      - flavor_name         = "m1.medium" -> null
      - force_delete        = false -> null
      - id                  = "8d1715e9-e276-4480-bf03-7b0776ea7bfa" -> null
      - image_id            = "4128a986-1f77-4201-9bbf-90de4037dee1" -> null
      - image_name          = "ubuntu_20.04.3" -> null
      - key_pair            = "user01key" -> null
      - name                = "my_instance" -> null
      - power_state         = "active" -> null
      - region              = "RegionOne" -> null
      - security_groups     = [
          - "default",
        ] -> null
      - stop_before_destroy = false -> null
      - tags                = [] -> null

      - network {
          - access_network = false -> null
          - fixed_ip_v4    = "10.0.0.16" -> null
          - mac            = "fa:16:3e:c2:41:5f" -> null
          - name           = "maintenance-net" -> null
          - uuid           = "b1bec6fb-a3af-4280-befc-decb376ac5ef" -> null
        }
    }

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

────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────

Note: You didn't use the -out option to save this plan, so Terraform can't guarantee to take exactly these actions if you run "terraform apply" now.
```

### `terraform destroy`

`terraform plan -destroy`で確認した削除対象リソースに問題なければ、削除を実施する。

```
$ terraform destroy
openstack_compute_instance_v2.test-server: Refreshing state... [id=8d1715e9-e276-4480-bf03-7b0776ea7bfa]

Note: Objects have changed outside of Terraform

Terraform detected the following changes made outside of Terraform since the last "terraform apply":

  # openstack_compute_instance_v2.test-server has been changed
  ~ resource "openstack_compute_instance_v2" "test-server" {
        id                  = "8d1715e9-e276-4480-bf03-7b0776ea7bfa"
        name                = "my_instance"
      + tags                = []
        # (14 unchanged attributes hidden)

        # (1 unchanged block hidden)
    }

Unless you have made equivalent changes to your configuration, or ignored the relevant attributes using ignore_changes, the following plan may include
actions to undo or respond to these changes.

────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────

Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols:
  - destroy

Terraform will perform the following actions:

  # openstack_compute_instance_v2.test-server will be destroyed
  - resource "openstack_compute_instance_v2" "test-server" {
      - access_ip_v4        = "10.0.0.16" -> null
      - all_metadata        = {} -> null
      - all_tags            = [] -> null
      - availability_zone   = "nova" -> null
      - flavor_id           = "2ce31fef-68b6-4953-81bc-b1c79d22f1b0" -> null
      - flavor_name         = "m1.medium" -> null
      - force_delete        = false -> null
      - id                  = "8d1715e9-e276-4480-bf03-7b0776ea7bfa" -> null
      - image_id            = "4128a986-1f77-4201-9bbf-90de4037dee1" -> null
      - image_name          = "ubuntu_20.04.3" -> null
      - key_pair            = "user01key" -> null
      - name                = "my_instance" -> null
      - power_state         = "active" -> null
      - region              = "RegionOne" -> null
      - security_groups     = [
          - "default",
        ] -> null
      - stop_before_destroy = false -> null
      - tags                = [] -> null

      - network {
          - access_network = false -> null
          - fixed_ip_v4    = "10.0.0.16" -> null
          - mac            = "fa:16:3e:c2:41:5f" -> null
          - name           = "maintenance-net" -> null
          - uuid           = "b1bec6fb-a3af-4280-befc-decb376ac5ef" -> null
        }
    }

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

openstack_compute_instance_v2.test-server: Destroying... [id=8d1715e9-e276-4480-bf03-7b0776ea7bfa]
openstack_compute_instance_v2.test-server: Still destroying... [id=8d1715e9-e276-4480-bf03-7b0776ea7bfa, 10s elapsed]
openstack_compute_instance_v2.test-server: Destruction complete after 10s

Destroy complete! Resources: 1 destroyed.
```

OpenStack上から削除されてことを確認する。

```
$ openstack server show 8d1715e9-e276-4480-bf03-7b0776ea7bfa
No server with a name or ID of '8d1715e9-e276-4480-bf03-7b0776ea7bfa' exists.
```

削除されたことを確認できた。

本記事の内容はここまでです。