TerraformによるIBM Cloudリソース管理

# はじめに
TerraformはIaC(Infrastructure as Code)を実現するためのツールの1つで、OSSとして提供されています。類似のツールとしてはAnsibleがありますが、両者は得意な領域が異なります。
Terraformはインフラレベルの構成管理(クラウド上のネットワークや仮想サーバーの構成など)、Ansibleは各仮想サーバー上の構成管理(OS、ミドルウェア、アプリケーションなど)で使われることが多いように思います。

当記事では、IBM CloudリソースをTerraformによって管理する基本的な流れを見ていきたいと思います。単純な例で一通り動かすところまでやってみた時のログを記載しますのでご参考まで。

# Terraform基本知識

簡単にTerraformを使うために最低限抑えておいた方がよい点について補足しておきます。詳細は専門のドキュメントなどをご参照ください。

参考: [Terraform Documentation](https://developer.hashicorp.com/terraform?product_intent=terraform)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/132331/74692c36-fc67-8a6a-e3b1-6f15093fd333.png)

基本的にはTerraformをインストールしたサーバーを用意しておき、構成を記述したコードを元に各クラウド上の構成を管理するということになります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/132331/da803b8f-e81b-800a-e754-6f324c8652aa.png)


## Provider
操作対象のクラウド環境に応じたProviderと呼ばれるTerraform用のモジュールが提供されており、それを使用してクラウド上のリソースを管理することになります。
操作対象ごとのProviderは [Terraform registory](https://registry.terraform.io/browse/providers) のサイトで公開されています。

## 構成ファイル
参考: [Terraform Language Documentation](https://developer.hashicorp.com/terraform/language)

構成情報は拡張子「.tf」のついた複数のファイルで保持され、Terraform独自の言語で記述することになります。
Terraformで管理したいリソースの情報を.tfファイルに記述していくことになりますが、ここでは構成作業を示すのではなく宣言的な記述を行うというのが特徴的です。つまりフローや手順を記述するのではなくあるべき姿を定義しておき、それをターゲットの環境に適用するというイメージになります。

tfファイルでは管理対象のリソースごとに以下のようなブロックで定義していきます(定義するリソースの種類によって少し書き方に違いがありますが、おおむね以下の形式で記述していきます)。

```tf:
<BLOCK TYPE> "<BLOCK LABEL>" "<BLOCK LABEL>" {
  # Block body
  <IDENTIFIER> = <EXPRESSION> # Argument
}
```

主に以下のような定義をしていくことになります。

- **provider**: 管理対象のクラウドに応じたプロバイダーの定義(IBM Cloudのリソースを管理する場合はIBM Cloud用のプロバイダーを定義する)
- **resource**: Terraformで管理したリソースの定義(VPC、サブネット、仮想サーバー、ストレージなど...)
- **data**(data source): Terraformの管理外で作成されたリソースを参照するための定義(既存のリソースを参照したい場合など)
- **variable**(変数): 上の各種定義を行う際に設定値を管理しやすくするために使用する変数の定義

## Terraformによる管理の流れ

基本的には以下のような流れでTerraformを利用することになります。

### (1) Terraformインストール
管理ノードを用意し、Terraformをインストールします。

### (2)構成ファイル作成/変更
構成ファイル(.tf)を作成し、管理対象リソースの定義を行います。

### (3) Terraformオペレーション
主に以下のようなterraformコマンドで各種操作を行います。

初期化: terraform init
最初に初期化処理を行います。作業用ディレクトリの初期化や必要なProviderのインストールなどが行われます。

事前確認: terraform plan
構成ファイルの定義内容/変更内容の確認を行います。

適用: terraform apply
構成ファイルの内容をターゲットの環境に反映します。

状況確認: terraform state
管理対象リソースの状況を確認します。

破棄: terraform destroy
管理対象リソースを破棄します。

# IBM Cloud用プロバイダー

IBM CloudをTerraformから操作するためのプロバイダーがTerraform Registryに提供されています。
[IBM Cloud Provider](https://registry.terraform.io/providers/IBM-Cloud/ibm/latest)

このプロバイダーを使用する際の利用ガイドやtfファイルの設定パラメーターなどは以下のドキュメントに記載があります。
[IBM Cloud Documentation](https://registry.terraform.io/providers/IBM-Cloud/ibm/latest/docs)

また、IBM CloudのドキュメントにもTerraformを利用した管理についてのガイドがあります。
[Terraform on IBM Cloud](https://cloud.ibm.com/docs/ibm-cloud-provider-for-terraform?topic=ibm-cloud-provider-for-terraform-getting-started)

# Terraform使用例

ここでは実際にTerraformを使用して単純なケースでIBM Cloudリソースがどのように管理できるかやってみたいと思います。

## 環境準備

### 全体像

ここでは以下のようなイメージの環境で試してみます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/132331/641a7684-4166-d2ae-66de-f0d30af4a1dd.png)

### IBM Cloud
IBM Cloudにアカウントを用意しそのアカウント上に各種リソースを作成していきます。そのため、リソースを作成する権限のあるユーザーを作成し、API Keyを生成しておきます。

参考: [APIキーの作成](https://cloud.ibm.com/docs/account?topic=account-userapikey&interface=ui#create_user_key)

### Terraformサーバー
ここではWindows11上のWSL(Ubuntu)をTerraformサーバーとして利用します。Terraformのバージョンは環境構築時の最新のものを使用します。

【使用するバージョン】
Ubuntu 20.04.6 LTS (Focal Fossa)
Terraform V1.7.5

#### Terraformインストーラーのダウンロード
ブラウザから以下のサイトにアクセスします。

AMD64のリンクからインストーラーをダウンロード
[Install Terraform - Linux](https://developer.hashicorp.com/terraform/install#linux)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/132331/de4322bc-2d6c-1aa5-f0e0-f6869607621b.png)

ダウンロードされたファイル: `terraform_1.7.5_linux_amd64.zip` (約26MB)

#### Terraformインストール

上でダウンロードしたzipファイルを解凍するとterraformという実行モジュールが一つ展開されます。このモジュールの配置場所をPATHに通せばOKです。

バージョン確認: 
```
user01@IBM-PF3ALW3Q:~$ terraform --version
Terraform v1.7.5
on linux_amd64
```

非常にシンプル！分かりやすい！簡単！

#### その他の構成

構成ファイルを作成する際は、VS Code + Terraform Extensionがあると非常に便利です！
WSLのExtensionと合わせて構成することをおすすめします。
参考: [Terraform Extension for Visual Studio Code](https://marketplace.visualstudio.com/items?itemName=HashiCorp.terraform)

また、構成ファイルについては他の管理者との共有やバージョン管理の観点からGit管理できるようにしておくと便利です。

利用イメージ:
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/132331/e137ab47-dbdb-9aaf-f3c8-7acd1e328990.png)


## サンプル・シナリオ(1)
まず、いくつかのリソースをIBM Cloud上に作成するための簡単な構成ファイルを作成し、Terraform経由で自動構成させてみます。

### 構成ファイル作成

Terraformで管理したいIBM Cloudのリソース定義をするために構成ファイルを作成していきます。
構成ファイルは1つのファイルに作成してもよいですが、複数のファイルに分割して管理することが一般的のようです。任意の作業ディレクトリに任意の名前の拡張子「.tf」のファイルを作成していきます。
ここでは役割毎にいくつかのファイルに分けて定義していきたいと思います。
基本的には特定の作業フォルダ以下に配置しておけばよいです。

#### プロバイダー関連 / provider.tf

```tf:provider.tf
terraform {
  required_version = ">= 1.7"
  required_providers {
    ibm = {
      source = "IBM-Cloud/ibm"
      version = "~> 1.64.0"
    }
  }
}

# Configure the IBM Provider
provider "ibm" {
  ibmcloud_api_key = var.ibmcloud_api_key
  region = var.region
}
```

上の例では、まず`terraform`というブロックでTerraform全般的な設定を行っています。[required_version](https://developer.hashicorp.com/terraform/tutorials/configuration-language/versions)を指定することで、この構成ファイルが前提としているTerraformのバージョンを指定しています。また[required_providers](https://developer.hashicorp.com/terraform/language/providers/requirements#requiring-providers)を指定することで、この構成ファイルが前提としているプロバイダーの条件、具体的にはプロバイダーの種類とバージョンを指定しています。
バージョン指定の仕方についてはちょっと表記が分かりにくいですが、以下に指定方法の詳細が記載されています。
[Version Constraints](https://developer.hashicorp.com/terraform/language/expressions/version-constraints)
`version = "~> 1.64.0"`という指定の場合、一番右側の数字が指定以上のものを許容するということになります。(1.65.2や1.65.5はOKだが、1.66.xx や 1.67.xx はNG)

`provider "ibm"`というブロックで"ibm"というIBM Cloud操作用のプロバイダーの定義を行っています。ここでは以下のパラメータを指定しています。

- `ibmcloud_api_key`: Terraform経由でIBM Cloudの操作を行うためのAPI Keyを指定
- `region`: デフォルトで使用されるリージョン

※いずれも後述の変数を設定値に使用しています。

参考: [IBM Cloud Provider](https://registry.terraform.io/providers/IBM-Cloud/ibm/latest/docs)

#### リソース関連 / resources.tf
ここで管理したいIBM Cloud上のリソースの定義を行っていきます。言ってみればここが本丸です。
Terraformの動作を理解するための単純な例として、既存のVPC上にセキュリティー・グループを2つ作成し、かつ1つのセキュリティー・グループにルールを追加するための定義を行ってみます。(定義内容にあまり意味はありません)

```tf:resources.tf
# Existing VPC
data "ibm_is_vpc" "target_vpc" {
  name = "vpc01"
} 

# security group1
resource "ibm_is_security_group" "my_security_group01" {
  name = "tagtest01"
  vpc = data.ibm_is_vpc.target_vpc.id
  resource_group = var.target_resource_group_id
} 

# security group2
resource "ibm_is_security_group" "my_security_group02" {
  name = "tagtest02"
  vpc = "r022-a9e1fd5d-3911-4cd4-bfb0-032298831d34"
  resource_group = var.target_resource_group_id
}

# Configure Security Group Rule to open SSH
resource "ibm_is_security_group_rule" "security_group_rule_ssh" {
    group = ibm_is_security_group.my_security_group01.id
    direction = "inbound"
    depends_on = [ibm_is_security_group.my_security_group01]    
    remote = var.my_global_ipaddress
    tcp {
      port_min = 22
      port_max = 22
    }
 }
```

各定義内容について補足します。

【VPC】
参考: [ibm_is_vpc](https://registry.terraform.io/providers/IBM-Cloud/ibm/latest/docs/resources/is_vpc)
`data "ibm_is_vpc"`のブロックではdata sourceとしての定義、つまり、既存のVPCを参照するための定義を行っています。2つめのラベル`target_vpc`はTerraform上でこのリソースを参照するための識別子です。
"vpc01"という名前のVPCリソースが既に存在している前提で、他のリソースから参照する場合このように既存リソースに対する定義を行います。
これは参照目的で定義しているリソースなので、Terraform経由で作成/変更/破棄の対象になる訳ではありません。

【Security Group】
参考: [ibm_is_security_group](https://registry.terraform.io/providers/IBM-Cloud/ibm/latest/docs/resources/is_security_group)
`resource "ibm_is_security_group"`のブロックではtagtest01, tagtest02という名前で2つのセキュリティ・グループを作成するための定義をしています。いずれも既存のVPCに紐づけて作成していますが、VPCの指定方法が異なります。
1つ目の方は`vpc = data.ibm_is_vpc.target_vpc.id`という指定をしています。これは上で定義したVPCの定義を参照しており、そのVPCの"id"属性を取得しています。
2つ目の方は`vpc = "r022-a9e1fd5d-3911-4cd4-bfb0-032298831d34"`というようにVPCのIDを直接リテラルで指定しています。これは例えばIBM Cloudの管理コンソールからVPCの詳細情報を参照すると確認できます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/132331/a0f17696-1e07-40bb-2614-0ee452d9a1b2.png)
このように直接IDを指定する場合は先のVPCの定義は不要となります。

※ここでは関連するリソースの情報の参照方法のパターンを理解するために、あえて異なる2つのやり方を試しています。

また、これらのセキュリティ・グループはいずれも同じリソース・グループIDに紐づけて定義しています。ここではリソース・グループのIDを`resource_group = var.target_resource_group_id`というように変数で指定するようにしています。変数の設定方法については後述しますが、変数を参照する際は`var.<変数名>`という形で指定します。


【Security Group Rule】
参考: [ibm_is_security_group_rule](https://registry.terraform.io/providers/IBM-Cloud/ibm/latest/docs/resources/is_security_group_rule)
`resource "ibm_is_security_group_rule"`のブロックで、セキュリティ・グループに付与するルールを指定しています。
上の例では、特定のリモートIPアドレス(my_global_ipaddress)からのインバウンド要求としてポート22を許可する設定をセキュリティ・グループに付与しています。
ここで`depends_on`というパラメーターについて補足します。これはTerraformが提供しているresource間の依存関係を明示的に定義するための仕組みです。このSecurity Group RuleはSecurity Groupに対して設定するものであるため、先にSecurity Groupが作成されている必要があります。Terraformの構成ファイルは宣言的な定義を行うものであってフローや順序性は基本的に意識しません。しかし、このようにお互いに依存関係のあるリソースがある場合は作成の依存関係を制御する必要があります。
Security Group と Security Group Ruleは定義上依存関係が明確に分かるため、暗黙的にその依存関係はTerraformが認識しSecurity Groupを先に作成するといった制御を行ってくれるはずです。ですので本来はdepends_onの指定は不要なのですが、`depndes_on`で明示的に依存関係を示す方法の例として指定しています。

参考: 
[Manage implicit dependencies](https://developer.hashicorp.com/terraform/tutorials/configuration-language/dependencies#manage-implicit-dependencies)
[Manage explicit dependencies](https://developer.hashicorp.com/terraform/tutorials/configuration-language/dependencies#manage-explicit-dependencies)

#### 環境変数定義 / variables.tf

上で定義したプロバイダー関連、リソース関連の設定の中で、一部変数化しているものがあるので、その変数のための定義を行います。

```tf:variables.tf
# API Key
variable "ibmcloud_api_key" {
  type        = string
  description = "Enter your IBM Cloud API Key, you can get your IBM Cloud API key using: https://cloud.ibm.com/iam#/apikeys"
}

# Target location for image
variable "region" {
  type        = string
  default     = "ca-tor"
  description = "Region to run the VSI doing the conversion. Custom image will be stored here"
}

variable "target_resource_group_id" {
  type        = string
  default     = "Default"
  description = "Resource group id for target resources."
}

variable "my_global_ipaddress" {
    type        = string
    default     = "0.0.0.0/0"
    description = "Allowed IP address for Security Group Inboud Rule"
}
```

`variable "xxx"`というブロックで変数の定義を行います。変数定義では変数の方やデフォルト値、description(説明)などを指定できます。

#### 環境変数への値設定 / my-settings.auto.tfvars

先のように一部のパラメータを変数で与えるようにしておくと、管理コマンド実行時に動的に変数の値を指定できるので管理が柔軟に行えます。コマンドの引数で渡すこともできますが、変数の数が多くなると煩雑になるので、実行時に使用される変数の値をファイルで指定しておくことができます。
拡張子「.auto.tfvars」というファイルを作成し変数への値をセットしておくと、そのファイルが自動的に読み込まれて使用されることになります。

```properties:my-settings.auto.tfvars
ibmcloud_api_key = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
target_resource_group_id = "07c38a522f014e74aa065dfe2ab7f9af"
region = "jp-tok"
my_global_ipaddress = "192.168.10.10"
```

### 管理コマンド実行

上で定義した内容をTerraform経由でIBM Cloud上に反映させてみます。構成ファイルが保持されている作業ディレクトリでterraformの操作を行います。
作業ディレクトリはこのような状況です。

```xxx:作業ディレクトリ確認
user01@IBM-PF3ALW3Q:~/Ansible/VSCode_workspace/my_terraform_test00$ ls -la
total 24
drwxr-xr-x  2 user01 user01 4096 Apr  7 22:01 .
drwxr-xr-x 10 user01 user01 4096 Apr  7 20:15 ..
-rwxr-xr-x  1 user01 user01  186 Apr  8 18:59 my-settings.auto.tfvars
-rwxr-xr-x  1 user01 user01  231 Apr  7 21:38 provider.tf
-rwxr-xr-x  1 user01 user01  804 Apr  8 18:25 resources.tf
-rwxr-xr-x  1 user01 user01  692 Apr  7 21:37 variables.tf
```

#### 初期化 / terraform init
"terraform init"コマンドにより作業ディレクトリの初期化を行います。

```xxx:init
user01@IBM-PF3ALW3Q:~/Ansible/VSCode_workspace/my_terraform_test00$ terraform init

Initializing the backend...

Initializing provider plugins...
- Finding ibm-cloud/ibm versions matching "~> 1.64.0"...
- Installing ibm-cloud/ibm v1.64.0...
- Installed ibm-cloud/ibm v1.64.0 (self-signed, key ID AAD3B791C49CC253)

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

この時、構成ファイルの定義に基づいてIBM Cloud用のプロバイダーがインストールされます。

```xxx:作業ディレクトリ確認
user01@IBM-PF3ALW3Q:~/Ansible/VSCode_workspace/my_terraform_test00$ ls -la
total 32
drwxr-xr-x  3 user01 user01 4096 Apr  8 19:06 .
drwxr-xr-x 10 user01 user01 4096 Apr  7 20:15 ..
drwxr-xr-x  3 user01 user01 4096 Apr  8 19:06 .terraform
-rw-r--r--  1 user01 user01  734 Apr  8 19:06 .terraform.lock.hcl
-rwxr-xr-x  1 user01 user01  186 Apr  8 18:59 my-settings.auto.tfvars
-rwxr-xr-x  1 user01 user01  231 Apr  7 21:38 provider.tf
-rwxr-xr-x  1 user01 user01  804 Apr  8 18:25 resources.tf
-rwxr-xr-x  1 user01 user01  692 Apr  7 21:37 variables.tf

user01@IBM-PF3ALW3Q:~/Ansible/VSCode_workspace/my_terraform_test00$ ls -la .terraform/providers/registry.terraform.io/ibm-cloud/ibm/1.64.0/linux_amd64/
total 119244
drwxr-xr-x 2 user01 user01      4096 Apr  8 19:06 .
drwxr-xr-x 3 user01 user01      4096 Apr  8 19:06 ..
-rw-r--r-- 1 user01 user01    299841 Apr  8 19:06 CHANGELOG.md
-rw-r--r-- 1 user01 user01     15977 Apr  8 19:06 LICENSE
-rw-r--r-- 1 user01 user01      1945 Apr  8 19:06 NOTICE
-rw-r--r-- 1 user01 user01      7355 Apr  8 19:06 README.md
-rw-r--r-- 1 user01 user01   6787011 Apr  8 19:06 provider_metadata.json
-rwxr-xr-x 1 user01 user01 114974720 Apr  8 19:06 terraform-provider-ibm_v1.64.0
```

#### 事前確認 / terraform plan

"terraform plan"コマンドにより構成ファイルをチェックし、Terraformによってどのような構成変更が行われるかを事前確認します。

```sh:plan
user01@IBM-PF3ALW3Q:~/Ansible/VSCode_workspace/my_terraform_test00$ terraform plan
data.ibm_is_vpc.target_vpc: Reading...
data.ibm_is_vpc.target_vpc: Read complete after 9s [id=r022-a9e1fd5d-3911-4cd4-bfb0-032298831d34]

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:

  # ibm_is_security_group.my_security_group01 will be created
  + resource "ibm_is_security_group" "my_security_group01" {
      + access_tags             = (known after apply)
      + crn                     = (known after apply)
      + id                      = (known after apply)
      + name                    = "tagtest01"
      + resource_controller_url = (known after apply)
      + resource_crn            = (known after apply)
      + resource_group          = "07c38a522f014e74aa065dfe2ab7f9af"
      + resource_group_name     = (known after apply)
      + resource_name           = (known after apply)
      + rules                   = (known after apply)
      + tags                    = (known after apply)
      + vpc                     = "r022-a9e1fd5d-3911-4cd4-bfb0-032298831d34"
    }

  # ibm_is_security_group.my_security_group02 will be created
  + resource "ibm_is_security_group" "my_security_group02" {
      + access_tags             = (known after apply)
      + crn                     = (known after apply)
      + id                      = (known after apply)
      + name                    = "tagtest02"
      + resource_controller_url = (known after apply)
      + resource_crn            = (known after apply)
      + resource_group          = "07c38a522f014e74aa065dfe2ab7f9af"
      + resource_group_name     = (known after apply)
      + resource_name           = (known after apply)
      + rules                   = (known after apply)
      + tags                    = (known after apply)
      + vpc                     = "r022-a9e1fd5d-3911-4cd4-bfb0-032298831d34"
    }

  # ibm_is_security_group_rule.security_group_rule_ssh will be created
  + resource "ibm_is_security_group_rule" "security_group_rule_ssh" {
      + direction   = "inbound"
      + group       = (known after apply)
      + id          = (known after apply)
      + ip_version  = "ipv4"
      + local       = (known after apply)
      + protocol    = (known after apply)
      + related_crn = (known after apply)
      + remote      = "192.168.10.10"
      + rule_id     = (known after apply)

      + tcp {
          + port_max = 22
          + port_min = 22
        }
    }

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

セキュリティ・グループ2つ、ルール1つ、合計3つのリソースが追加される予定であることが確認できました。

#### 適用 / terraform apply

"terraform apply"コマンドにより実際に定義をIBM Cloud上に反映させてみます。
apply実行時には、planで実行したのと同じ確認がまず行われ、実際にアクションを行ってよいかを確認するためのプロンプトが表示されます。そこで"yes"を入力することで実際の適用が行われます。

```sh:apply
user01@IBM-PF3ALW3Q:~/Ansible/VSCode_workspace/my_terraform_test00$ terraform apply
data.ibm_is_vpc.target_vpc: Reading...
data.ibm_is_vpc.target_vpc: Still reading... [10s elapsed]
data.ibm_is_vpc.target_vpc: Read complete after 13s [id=r022-a9e1fd5d-3911-4cd4-bfb0-032298831d34]

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:

  # ibm_is_security_group.my_security_group01 will be created
  + resource "ibm_is_security_group" "my_security_group01" {
      + access_tags             = (known after apply)
      + crn                     = (known after apply)
      + id                      = (known after apply)
      + name                    = "tagtest01"
      + resource_controller_url = (known after apply)
      + resource_crn            = (known after apply)
      + resource_group          = "07c38a522f014e74aa065dfe2ab7f9af"
      + resource_group_name     = (known after apply)
      + resource_name           = (known after apply)
      + rules                   = (known after apply)
      + tags                    = (known after apply)
      + vpc                     = "r022-a9e1fd5d-3911-4cd4-bfb0-032298831d34"
    }

  # ibm_is_security_group.my_security_group02 will be created
  + resource "ibm_is_security_group" "my_security_group02" {
      + access_tags             = (known after apply)
      + crn                     = (known after apply)
      + id                      = (known after apply)
      + name                    = "tagtest02"
      + resource_controller_url = (known after apply)
      + resource_crn            = (known after apply)
      + resource_group          = "07c38a522f014e74aa065dfe2ab7f9af"
      + resource_group_name     = (known after apply)
      + resource_name           = (known after apply)
      + rules                   = (known after apply)
      + tags                    = (known after apply)
      + vpc                     = "r022-a9e1fd5d-3911-4cd4-bfb0-032298831d34"
    }

  # ibm_is_security_group_rule.security_group_rule_ssh will be created
  + resource "ibm_is_security_group_rule" "security_group_rule_ssh" {
      + direction   = "inbound"
      + group       = (known after apply)
      + id          = (known after apply)
      + ip_version  = "ipv4"
      + local       = (known after apply)
      + protocol    = (known after apply)
      + related_crn = (known after apply)
      + remote      = "192.168.10.10"
      + rule_id     = (known after apply)

      + tcp {
          + port_max = 22
          + port_min = 22
        }
    }

Plan: 3 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_security_group.my_security_group02: Creating...
ibm_is_security_group.my_security_group01: Creating...
ibm_is_security_group.my_security_group01: Creation complete after 7s [id=r022-f396d02f-8898-4b0a-a73a-8fe75ad9bf38]
ibm_is_security_group.my_security_group02: Creation complete after 7s [id=r022-07a470ff-e948-4e5a-947c-1bc63e6e02ef]
ibm_is_security_group_rule.security_group_rule_ssh: Creating...
ibm_is_security_group_rule.security_group_rule_ssh: Creation complete after 2s [id=r022-f396d02f-8898-4b0a-a73a-8fe75ad9bf38.r022-16103105-7d32-4204-8208-ec49fb2535d1]

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

これでTerraform経由でIBM Cloud上にリソースが作成されました。

IBM Cloudの管理コンソールを見てみると、以下のように2つのセキュリティ・グループが作成されているのが確認できます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/132331/bf829876-b74a-36f6-4112-35cd10b71beb.png)
また、一方のセキュリティ・グループにはインバウンドのルールが1つ追加されていることが分かります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/132331/794710ed-407d-c613-4573-8cdfb89e3d39.png)

#### 状況確認 / terraform state

"terraform state list"コマンドで、管理対象のリソース一覧を表示できます。

```sh:リスト表示
user01@IBM-PF3ALW3Q:~/Ansible/VSCode_workspace/my_terraform_test00$ terraform state list
data.ibm_is_vpc.target_vpc
ibm_is_security_group.my_security_group01
ibm_is_security_group.my_security_group02
ibm_is_security_group_rule.security_group_rule_ssh
```

"terraform state show xxx"コマンドで、管理対象リソースの詳細を表示できます。

```sh:リソース詳細表示
user01@IBM-PF3ALW3Q:~/Ansible/VSCode_workspace/my_terraform_test00$ terraform state show ibm_is_security_group.my_security_group01
# ibm_is_security_group.my_security_group01:
resource "ibm_is_security_group" "my_security_group01" {
    access_tags             = []
    crn                     = "crn:v1:bluemix:public:is:jp-tok:a/1fc8373f538a408187ffedbe62e5796a::security-group:r022-f396d02f-8898-4b0a-a73a-8fe75ad9bf38"
    id                      = "r022-f396d02f-8898-4b0a-a73a-8fe75ad9bf38"
    name                    = "tagtest01"
    resource_controller_url = "https://cloud.ibm.com/vpc-ext/network/securityGroups"
    resource_crn            = "crn:v1:bluemix:public:is:jp-tok:a/1fc8373f538a408187ffedbe62e5796a::security-group:r022-f396d02f-8898-4b0a-a73a-8fe75ad9bf38"
    resource_group          = "07c38a522f014e74aa065dfe2ab7f9af"
    resource_group_name     = "ISEI20230707-1626-nervous"
    resource_name           = "tagtest01"
    rules                   = []
    tags                    = []
    vpc                     = "r022-a9e1fd5d-3911-4cd4-bfb0-032298831d34"
}
```

#### 破棄 / terraform destroy

管理対象のリソースを全て破棄することができます。

"terraform plan -destory"コマンドで事前に破棄する場合の動作を確認しておきます。

```sh:plan
user01@IBM-PF3ALW3Q:~/Ansible/VSCode_workspace/my_terraform_test00$ terraform plan -destroy
data.ibm_is_vpc.target_vpc: Reading...
ibm_is_security_group.my_security_group02: Refreshing state... [id=r022-07a470ff-e948-4e5a-947c-1bc63e6e02ef]
data.ibm_is_vpc.target_vpc: Still reading... [10s elapsed]
data.ibm_is_vpc.target_vpc: Read complete after 11s [id=r022-a9e1fd5d-3911-4cd4-bfb0-032298831d34]
ibm_is_security_group.my_security_group01: Refreshing state... [id=r022-f396d02f-8898-4b0a-a73a-8fe75ad9bf38]
ibm_is_security_group_rule.security_group_rule_ssh: Refreshing state... [id=r022-f396d02f-8898-4b0a-a73a-8fe75ad9bf38.r022-16103105-7d32-4204-8208-ec49fb2535d1]

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:

  # ibm_is_security_group.my_security_group01 will be destroyed
  - resource "ibm_is_security_group" "my_security_group01" {
      - access_tags             = [] -> null
      - crn                     = "crn:v1:bluemix:public:is:jp-tok:a/1fc8373f538a408187ffedbe62e5796a::security-group:r022-f396d02f-8898-4b0a-a73a-8fe75ad9bf38" -> null
      - id                      = "r022-f396d02f-8898-4b0a-a73a-8fe75ad9bf38" -> null
      - name                    = "tagtest01" -> null
      - resource_controller_url = "https://cloud.ibm.com/vpc-ext/network/securityGroups" -> null
      - resource_crn            = "crn:v1:bluemix:public:is:jp-tok:a/1fc8373f538a408187ffedbe62e5796a::security-group:r022-f396d02f-8898-4b0a-a73a-8fe75ad9bf38" -> null
      - resource_group          = "07c38a522f014e74aa065dfe2ab7f9af" -> null
      - resource_group_name     = "ISEI20230707-1626-nervous" -> null
      - resource_name           = "tagtest01" -> null
      - rules                   = [
          - {
              - code       = 0
              - direction  = "inbound"
              - ip_version = "ipv4"
              - local      = "0.0.0.0/0"
              - port_max   = 22
              - port_min   = 22
              - protocol   = "tcp"
              - remote     = "192.168.10.10"
              - type       = 0
            },
        ] -> null
      - tags                    = [] -> null
      - vpc                     = "r022-a9e1fd5d-3911-4cd4-bfb0-032298831d34" -> null
    }

  # ibm_is_security_group.my_security_group02 will be destroyed
  - resource "ibm_is_security_group" "my_security_group02" {
      - access_tags             = [] -> null
      - crn                     = "crn:v1:bluemix:public:is:jp-tok:a/1fc8373f538a408187ffedbe62e5796a::security-group:r022-07a470ff-e948-4e5a-947c-1bc63e6e02ef" -> null
      - id                      = "r022-07a470ff-e948-4e5a-947c-1bc63e6e02ef" -> null
      - name                    = "tagtest02" -> null
      - resource_controller_url = "https://cloud.ibm.com/vpc-ext/network/securityGroups" -> null
      - resource_crn            = "crn:v1:bluemix:public:is:jp-tok:a/1fc8373f538a408187ffedbe62e5796a::security-group:r022-07a470ff-e948-4e5a-947c-1bc63e6e02ef" -> null
      - resource_group          = "07c38a522f014e74aa065dfe2ab7f9af" -> null
      - resource_group_name     = "ISEI20230707-1626-nervous" -> null
      - resource_name           = "tagtest02" -> null
      - rules                   = [] -> null
      - tags                    = [] -> null
      - vpc                     = "r022-a9e1fd5d-3911-4cd4-bfb0-032298831d34" -> null
    }

  # ibm_is_security_group_rule.security_group_rule_ssh will be destroyed
  - resource "ibm_is_security_group_rule" "security_group_rule_ssh" {
      - direction   = "inbound" -> null
      - group       = "r022-f396d02f-8898-4b0a-a73a-8fe75ad9bf38" -> null
      - id          = "r022-f396d02f-8898-4b0a-a73a-8fe75ad9bf38.r022-16103105-7d32-4204-8208-ec49fb2535d1" -> null
      - ip_version  = "ipv4" -> null
      - local       = "0.0.0.0/0" -> null
      - protocol    = "tcp" -> null
      - related_crn = "crn:v1:bluemix:public:is:jp-tok:a/1fc8373f538a408187ffedbe62e5796a::security-group:r022-f396d02f-8898-4b0a-a73a-8fe75ad9bf38" -> null
      - remote      = "192.168.10.10" -> null
      - rule_id     = "r022-16103105-7d32-4204-8208-ec49fb2535d1" -> null

      - tcp {
          - port_max = 22 -> null
          - port_min = 22 -> null
        }
    }

Plan: 0 to add, 0 to change, 3 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"コマンドで、管理対象のリソースを全て破棄します。
apply実行時と同様、planで実行したのと同じ確認がまず行われ、実際にアクションを行ってよいかを確認するためのプロンプトが表示されます。そこで"yes"を入力することで実際の破棄が行われます。

```sh:destroy
user01@IBM-PF3ALW3Q:~/Ansible/VSCode_workspace/my_terraform_test00$ terraform destroy
data.ibm_is_vpc.target_vpc: Reading...
ibm_is_security_group.my_security_group02: Refreshing state... [id=r022-07a470ff-e948-4e5a-947c-1bc63e6e02ef]
data.ibm_is_vpc.target_vpc: Read complete after 9s [id=r022-a9e1fd5d-3911-4cd4-bfb0-032298831d34]
ibm_is_security_group.my_security_group01: Refreshing state... [id=r022-f396d02f-8898-4b0a-a73a-8fe75ad9bf38]
ibm_is_security_group_rule.security_group_rule_ssh: Refreshing state... [id=r022-f396d02f-8898-4b0a-a73a-8fe75ad9bf38.r022-16103105-7d32-4204-8208-ec49fb2535d1]

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:

  # ibm_is_security_group.my_security_group01 will be destroyed
  - resource "ibm_is_security_group" "my_security_group01" {
      - access_tags             = [] -> null
      - crn                     = "crn:v1:bluemix:public:is:jp-tok:a/1fc8373f538a408187ffedbe62e5796a::security-group:r022-f396d02f-8898-4b0a-a73a-8fe75ad9bf38" -> null
      - id                      = "r022-f396d02f-8898-4b0a-a73a-8fe75ad9bf38" -> null
      - name                    = "tagtest01" -> null
      - resource_controller_url = "https://cloud.ibm.com/vpc-ext/network/securityGroups" -> null
      - resource_crn            = "crn:v1:bluemix:public:is:jp-tok:a/1fc8373f538a408187ffedbe62e5796a::security-group:r022-f396d02f-8898-4b0a-a73a-8fe75ad9bf38" -> null
      - resource_group          = "07c38a522f014e74aa065dfe2ab7f9af" -> null
      - resource_group_name     = "ISEI20230707-1626-nervous" -> null
      - resource_name           = "tagtest01" -> null
      - rules                   = [
          - {
              - code       = 0
              - direction  = "inbound"
              - ip_version = "ipv4"
              - local      = "0.0.0.0/0"
              - port_max   = 22
              - port_min   = 22
              - protocol   = "tcp"
              - remote     = "192.168.10.10"
              - type       = 0
            },
        ] -> null
      - tags                    = [] -> null
      - vpc                     = "r022-a9e1fd5d-3911-4cd4-bfb0-032298831d34" -> null
    }

  # ibm_is_security_group.my_security_group02 will be destroyed
  - resource "ibm_is_security_group" "my_security_group02" {
      - access_tags             = [] -> null
      - crn                     = "crn:v1:bluemix:public:is:jp-tok:a/1fc8373f538a408187ffedbe62e5796a::security-group:r022-07a470ff-e948-4e5a-947c-1bc63e6e02ef" -> null
      - id                      = "r022-07a470ff-e948-4e5a-947c-1bc63e6e02ef" -> null
      - name                    = "tagtest02" -> null
      - resource_controller_url = "https://cloud.ibm.com/vpc-ext/network/securityGroups" -> null
      - resource_crn            = "crn:v1:bluemix:public:is:jp-tok:a/1fc8373f538a408187ffedbe62e5796a::security-group:r022-07a470ff-e948-4e5a-947c-1bc63e6e02ef" -> null
      - resource_group          = "07c38a522f014e74aa065dfe2ab7f9af" -> null
      - resource_group_name     = "ISEI20230707-1626-nervous" -> null
      - resource_name           = "tagtest02" -> null
      - rules                   = [] -> null
      - tags                    = [] -> null
      - vpc                     = "r022-a9e1fd5d-3911-4cd4-bfb0-032298831d34" -> null
    }

  # ibm_is_security_group_rule.security_group_rule_ssh will be destroyed
  - resource "ibm_is_security_group_rule" "security_group_rule_ssh" {
      - direction   = "inbound" -> null
      - group       = "r022-f396d02f-8898-4b0a-a73a-8fe75ad9bf38" -> null
      - id          = "r022-f396d02f-8898-4b0a-a73a-8fe75ad9bf38.r022-16103105-7d32-4204-8208-ec49fb2535d1" -> null
      - ip_version  = "ipv4" -> null
      - local       = "0.0.0.0/0" -> null
      - protocol    = "tcp" -> null
      - related_crn = "crn:v1:bluemix:public:is:jp-tok:a/1fc8373f538a408187ffedbe62e5796a::security-group:r022-f396d02f-8898-4b0a-a73a-8fe75ad9bf38" -> null
      - remote      = "192.168.10.10" -> null
      - rule_id     = "r022-16103105-7d32-4204-8208-ec49fb2535d1" -> null

      - tcp {
          - port_max = 22 -> null
          - port_min = 22 -> null
        }
    }

Plan: 0 to add, 0 to change, 3 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_security_group_rule.security_group_rule_ssh: Destroying... [id=r022-f396d02f-8898-4b0a-a73a-8fe75ad9bf38.r022-16103105-7d32-4204-8208-ec49fb2535d1]
ibm_is_security_group.my_security_group02: Destroying... [id=r022-07a470ff-e948-4e5a-947c-1bc63e6e02ef]
ibm_is_security_group_rule.security_group_rule_ssh: Destruction complete after 2s
ibm_is_security_group.my_security_group01: Destroying... [id=r022-f396d02f-8898-4b0a-a73a-8fe75ad9bf38]
ibm_is_security_group.my_security_group02: Destruction complete after 8s
ibm_is_security_group.my_security_group01: Destruction complete after 9s

Destroy complete! Resources: 3 destroyed.
```

これでapply時に作成された3つのリソースが破棄されました。


## サンプル・シナリオ(2)

続いて、一度applyされた状態のリソースに対して、その後構成ファイルを変更した場合の動作について見てみます。
一旦、上で示したサンプルの構成ファイルをそのまま使ってapplyした状態であることを前提とします。

### 現状確認

```sh:管理対象リソース確認
user01@IBM-PF3ALW3Q:~/Ansible/VSCode_workspace/my_terraform_test00$ terraform state list
data.ibm_is_vpc.target_vpc
ibm_is_security_group.my_security_group01
ibm_is_security_group.my_security_group02
ibm_is_security_group_rule.security_group_rule_ssh

user01@IBM-PF3ALW3Q:~/Ansible/VSCode_workspace/my_terraform_test00$ terraform state show ibm_is_security_group.my_security_group01
# ibm_is_security_group.my_security_group01:
resource "ibm_is_security_group" "my_security_group01" {
    access_tags             = []
    crn                     = "crn:v1:bluemix:public:is:jp-tok:a/1fc8373f538a408187ffedbe62e5796a::security-group:r022-e7e7719c-b572-4299-9a2a-ea655ce81793"
    id                      = "r022-e7e7719c-b572-4299-9a2a-ea655ce81793"
    name                    = "tagtest01"
    resource_controller_url = "https://cloud.ibm.com/vpc-ext/network/securityGroups"
    resource_crn            = "crn:v1:bluemix:public:is:jp-tok:a/1fc8373f538a408187ffedbe62e5796a::security-group:r022-e7e7719c-b572-4299-9a2a-ea655ce81793"
    resource_group          = "07c38a522f014e74aa065dfe2ab7f9af"
    resource_group_name     = "ISEI20230707-1626-nervous"
    resource_name           = "tagtest01"
    rules                   = []
    tags                    = []
    vpc                     = "r022-a9e1fd5d-3911-4cd4-bfb0-032298831d34"
}

user01@IBM-PF3ALW3Q:~/Ansible/VSCode_workspace/my_terraform_test00$ terraform state show ibm_is_security_group.my_security_group02
# ibm_is_security_group.my_security_group02:
resource "ibm_is_security_group" "my_security_group02" {
    access_tags             = []
    crn                     = "crn:v1:bluemix:public:is:jp-tok:a/1fc8373f538a408187ffedbe62e5796a::security-group:r022-a897b841-f74c-40e1-ae8a-c26a718f5e0c"
    id                      = "r022-a897b841-f74c-40e1-ae8a-c26a718f5e0c"
    name                    = "tagtest02"
    resource_controller_url = "https://cloud.ibm.com/vpc-ext/network/securityGroups"
    resource_crn            = "crn:v1:bluemix:public:is:jp-tok:a/1fc8373f538a408187ffedbe62e5796a::security-group:r022-a897b841-f74c-40e1-ae8a-c26a718f5e0c"
    resource_group          = "07c38a522f014e74aa065dfe2ab7f9af"
    resource_group_name     = "ISEI20230707-1626-nervous"
    resource_name           = "tagtest02"
    rules                   = []
    tags                    = []
    vpc                     = "r022-a9e1fd5d-3911-4cd4-bfb0-032298831d34"
}

user01@IBM-PF3ALW3Q:~/Ansible/VSCode_workspace/my_terraform_test00$ terraform state show ibm_is_security_group_rule.security_group_rule_ssh
# ibm_is_security_group_rule.security_group_rule_ssh:
resource "ibm_is_security_group_rule" "security_group_rule_ssh" {
    direction   = "inbound"
    group       = "r022-e7e7719c-b572-4299-9a2a-ea655ce81793"
    id          = "r022-e7e7719c-b572-4299-9a2a-ea655ce81793.r022-58548357-3017-4343-858a-f7d3756e6687"
    ip_version  = "ipv4"
    local       = "0.0.0.0/0"
    protocol    = "tcp"
    related_crn = "crn:v1:bluemix:public:is:jp-tok:a/1fc8373f538a408187ffedbe62e5796a::security-group:r022-e7e7719c-b572-4299-9a2a-ea655ce81793"
    remote      = "192.168.10.10"
    rule_id     = "r022-58548357-3017-4343-858a-f7d3756e6687"

    tcp {
        port_max = 22
        port_min = 22
    }
}
```

セキュリティー・グループ2つと、ルールが追加されている状態です。

### 構成ファイル変更

resources関連定義のファイルを以下のように変更してみます。

```tf.resources.tf
# Existing VPC
data "ibm_is_vpc" "target_vpc" {
  name = "vpc01"
} 

# Create a VPC
resource "ibm_is_vpc" "my_vpc" {
  name = "example-vpc"
  resource_group = var.target_resource_group_id

} 

# security group1
resource "ibm_is_security_group" "my_security_group01" {
  name = "tagtest01-modified"
  vpc = data.ibm_is_vpc.target_vpc.id
  resource_group = var.target_resource_group_id
} 

# security group2
resource "ibm_is_security_group" "my_security_group02" {
  name = "tagtest02"
  vpc = ibm_is_vpc.my_vpc.id
  resource_group = var.target_resource_group_id
}

# Configure Security Group Rule to open SSH
resource "ibm_is_security_group_rule" "security_group_rule_ssh" {
    group = ibm_is_security_group.my_security_group01.id
    direction = "inbound"
    depends_on = [ibm_is_security_group.my_security_group01]    
    remote = var.my_global_ipaddress
    tcp {
      port_min = 23
      port_max = 23
    }
 }
 ```

変更した箇所を分かりやすくするために変更前と比較したイメージも添付しておきます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/132331/b4c4e493-13d3-9fed-7598-d053282e4ae5.png)

変更点は以下の通りです。

- 新規VPCを追加
- my_security_group01のリソース名を変更
- my_security_group02のVPCを新規作成のものに変更
- my_security_group_rule_sshのポート番号を変更


### 変更内容確認 / terraform plan

```sh:plan
user01@IBM-PF3ALW3Q:~/Ansible/VSCode_workspace/my_terraform_test00$ terraform plan
data.ibm_is_vpc.target_vpc: Reading...
ibm_is_security_group.my_security_group02: Refreshing state... [id=r022-a897b841-f74c-40e1-ae8a-c26a718f5e0c]
data.ibm_is_vpc.target_vpc: Still reading... [10s elapsed]
data.ibm_is_vpc.target_vpc: Read complete after 11s [id=r022-a9e1fd5d-3911-4cd4-bfb0-032298831d34]
ibm_is_security_group.my_security_group01: Refreshing state... [id=r022-e7e7719c-b572-4299-9a2a-ea655ce81793]
ibm_is_security_group_rule.security_group_rule_ssh: Refreshing state... [id=r022-e7e7719c-b572-4299-9a2a-ea655ce81793.r022-58548357-3017-4343-858a-f7d3756e6687]

Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols:
  + create
  ~ update in-place
-/+ destroy and then create replacement

Terraform will perform the following actions:

  # ibm_is_security_group.my_security_group01 will be updated in-place
  ~ resource "ibm_is_security_group" "my_security_group01" {
        id                      = "r022-e7e7719c-b572-4299-9a2a-ea655ce81793"
      ~ name                    = "tagtest01" -> "tagtest01-modified"
        tags                    = []
        # (9 unchanged attributes hidden)
    }

  # ibm_is_security_group.my_security_group02 must be replaced
-/+ resource "ibm_is_security_group" "my_security_group02" {
      ~ access_tags             = [] -> (known after apply)
      ~ crn                     = "crn:v1:bluemix:public:is:jp-tok:a/1fc8373f538a408187ffedbe62e5796a::security-group:r022-a897b841-f74c-40e1-ae8a-c26a718f5e0c" -> (known after apply)
      ~ id                      = "r022-a897b841-f74c-40e1-ae8a-c26a718f5e0c" -> (known after apply)
        name                    = "tagtest02"
      ~ resource_controller_url = "https://cloud.ibm.com/vpc-ext/network/securityGroups" -> (known after apply)
      ~ resource_crn            = "crn:v1:bluemix:public:is:jp-tok:a/1fc8373f538a408187ffedbe62e5796a::security-group:r022-a897b841-f74c-40e1-ae8a-c26a718f5e0c" -> (known after apply)
      ~ resource_group_name     = "ISEI20230707-1626-nervous" -> (known after apply)
      ~ resource_name           = "tagtest02" -> (known after apply)
      ~ rules                   = [] -> (known after apply)
      ~ tags                    = [] -> (known after apply)
      ~ vpc                     = "r022-a9e1fd5d-3911-4cd4-bfb0-032298831d34" # forces replacement -> (known after apply) # forces replacement
        # (1 unchanged attribute hidden)
    }

  # ibm_is_security_group_rule.security_group_rule_ssh will be updated in-place
  ~ resource "ibm_is_security_group_rule" "security_group_rule_ssh" {
        id          = "r022-e7e7719c-b572-4299-9a2a-ea655ce81793.r022-58548357-3017-4343-858a-f7d3756e6687"
        # (8 unchanged attributes hidden)

      ~ tcp {
          ~ port_max = 22 -> 23
          ~ port_min = 22 -> 23
        }
    }

  # ibm_is_vpc.my_vpc will be created
  + resource "ibm_is_vpc" "my_vpc" {
      + access_tags                 = (known after apply)
      + address_prefix_management   = "auto"
      + classic_access              = false
      + crn                         = (known after apply)
      + cse_source_addresses        = (known after apply)
      + default_network_acl         = (known after apply)
      + default_network_acl_crn     = (known after apply)
      + default_network_acl_name    = (known after apply)
      + default_routing_table       = (known after apply)
      + default_routing_table_name  = (known after apply)
      + default_security_group      = (known after apply)
      + default_security_group_crn  = (known after apply)
      + default_security_group_name = (known after apply)
      + health_reasons              = (known after apply)
      + health_state                = (known after apply)
      + id                          = (known after apply)
      + name                        = "example-vpc"
      + no_sg_acl_rules             = false
      + resource_controller_url     = (known after apply)
      + resource_crn                = (known after apply)
      + resource_group              = "07c38a522f014e74aa065dfe2ab7f9af"
      + resource_group_name         = (known after apply)
      + resource_name               = (known after apply)
      + resource_status             = (known after apply)
      + security_group              = (known after apply)
      + status                      = (known after apply)
      + subnets                     = (known after apply)
      + tags                        = (known after apply)
    }

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

結果を見ると、2つのリソースが追加(add)、2つが変更(change)、1つが破棄(destroy)となっています。
my_vpcは新規追加なのでそのままです。my_security_group01とmy_security_group_rule_sshはパラメーターの変更なのでこちらも"変更"ということでよいですね。
my_security_group02については、紐づくVPCが変わるということで単にパラメータ変更では済まないので、リソースを削除して新たに作成しなおすということになるようです。
このように変更する内容によってはパラメーター変更ではなくリソースごと作り直しになることがありますので注意が必要です。

### 変更の適用 / terraform apply

変更した構成ファイルの内容を実環境に適用します。

```sh:apply
user01@IBM-PF3ALW3Q:~/Ansible/VSCode_workspace/my_terraform_test00$ terraform apply
data.ibm_is_vpc.target_vpc: Reading...
ibm_is_security_group.my_security_group02: Refreshing state... [id=r022-a897b841-f74c-40e1-ae8a-c26a718f5e0c]
data.ibm_is_vpc.target_vpc: Read complete after 7s [id=r022-a9e1fd5d-3911-4cd4-bfb0-032298831d34]
ibm_is_security_group.my_security_group01: Refreshing state... [id=r022-e7e7719c-b572-4299-9a2a-ea655ce81793]
ibm_is_security_group_rule.security_group_rule_ssh: Refreshing state... [id=r022-e7e7719c-b572-4299-9a2a-ea655ce81793.r022-58548357-3017-4343-858a-f7d3756e6687]

Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols:
  + create
  ~ update in-place
-/+ destroy and then create replacement

Terraform will perform the following actions:

  # ibm_is_security_group.my_security_group01 will be updated in-place
  ~ resource "ibm_is_security_group" "my_security_group01" {
        id                      = "r022-e7e7719c-b572-4299-9a2a-ea655ce81793"
      ~ name                    = "tagtest01" -> "tagtest01-modified"
        tags                    = []
        # (9 unchanged attributes hidden)
    }

  # ibm_is_security_group.my_security_group02 must be replaced
-/+ resource "ibm_is_security_group" "my_security_group02" {
      ~ access_tags             = [] -> (known after apply)
      ~ crn                     = "crn:v1:bluemix:public:is:jp-tok:a/1fc8373f538a408187ffedbe62e5796a::security-group:r022-a897b841-f74c-40e1-ae8a-c26a718f5e0c" -> (known after apply)
      ~ id                      = "r022-a897b841-f74c-40e1-ae8a-c26a718f5e0c" -> (known after apply)
        name                    = "tagtest02"
      ~ resource_controller_url = "https://cloud.ibm.com/vpc-ext/network/securityGroups" -> (known after apply)
      ~ resource_crn            = "crn:v1:bluemix:public:is:jp-tok:a/1fc8373f538a408187ffedbe62e5796a::security-group:r022-a897b841-f74c-40e1-ae8a-c26a718f5e0c" -> (known after apply)
      ~ resource_group_name     = "ISEI20230707-1626-nervous" -> (known after apply)
      ~ resource_name           = "tagtest02" -> (known after apply)
      ~ rules                   = [] -> (known after apply)
      ~ tags                    = [] -> (known after apply)
      ~ vpc                     = "r022-a9e1fd5d-3911-4cd4-bfb0-032298831d34" # forces replacement -> (known after apply) # forces replacement
        # (1 unchanged attribute hidden)
    }

  # ibm_is_security_group_rule.security_group_rule_ssh will be updated in-place
  ~ resource "ibm_is_security_group_rule" "security_group_rule_ssh" {
        id          = "r022-e7e7719c-b572-4299-9a2a-ea655ce81793.r022-58548357-3017-4343-858a-f7d3756e6687"
        # (8 unchanged attributes hidden)

      ~ tcp {
          ~ port_max = 22 -> 23
          ~ port_min = 22 -> 23
        }
    }

  # ibm_is_vpc.my_vpc will be created
  + resource "ibm_is_vpc" "my_vpc" {
      + access_tags                 = (known after apply)
      + address_prefix_management   = "auto"
      + classic_access              = false
      + crn                         = (known after apply)
      + cse_source_addresses        = (known after apply)
      + default_network_acl         = (known after apply)
      + default_network_acl_crn     = (known after apply)
      + default_network_acl_name    = (known after apply)
      + default_routing_table       = (known after apply)
      + default_routing_table_name  = (known after apply)
      + default_security_group      = (known after apply)
      + default_security_group_crn  = (known after apply)
      + default_security_group_name = (known after apply)
      + health_reasons              = (known after apply)
      + health_state                = (known after apply)
      + id                          = (known after apply)
      + name                        = "example-vpc"
      + no_sg_acl_rules             = false
      + resource_controller_url     = (known after apply)
      + resource_crn                = (known after apply)
      + resource_group              = "07c38a522f014e74aa065dfe2ab7f9af"
      + resource_group_name         = (known after apply)
      + resource_name               = (known after apply)
      + resource_status             = (known after apply)
      + security_group              = (known after apply)
      + status                      = (known after apply)
      + subnets                     = (known after apply)
      + tags                        = (known after apply)
    }

Plan: 2 to add, 2 to change, 1 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_security_group.my_security_group02: Destroying... [id=r022-a897b841-f74c-40e1-ae8a-c26a718f5e0c]
ibm_is_security_group.my_security_group01: Modifying... [id=r022-e7e7719c-b572-4299-9a2a-ea655ce81793]
ibm_is_security_group.my_security_group01: Modifications complete after 7s [id=r022-e7e7719c-b572-4299-9a2a-ea655ce81793]
ibm_is_security_group_rule.security_group_rule_ssh: Modifying... [id=r022-e7e7719c-b572-4299-9a2a-ea655ce81793.r022-58548357-3017-4343-858a-f7d3756e6687]
ibm_is_security_group_rule.security_group_rule_ssh: Modifications complete after 2s [id=r022-e7e7719c-b572-4299-9a2a-ea655ce81793.r022-58548357-3017-4343-858a-f7d3756e6687]
ibm_is_security_group.my_security_group02: Destruction complete after 10s
ibm_is_vpc.my_vpc: Creating...
ibm_is_vpc.my_vpc: Still creating... [10s elapsed]
ibm_is_vpc.my_vpc: Creation complete after 20s [id=r022-8c227158-e88a-489b-a858-8dbb1482f74a]
ibm_is_security_group.my_security_group02: Creating...
ibm_is_security_group.my_security_group02: Creation complete after 2s [id=r022-bbba461c-4f45-4040-be79-a92d131fb802]

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

### 状況確認 / terraform state

変更結果を確認します。

```sh:state
user01@IBM-PF3ALW3Q:~/Ansible/VSCode_workspace/my_terraform_test00$ terraform state list
data.ibm_is_vpc.target_vpc
ibm_is_security_group.my_security_group01
ibm_is_security_group.my_security_group02
ibm_is_security_group_rule.security_group_rule_ssh
ibm_is_vpc.my_vpc

user01@IBM-PF3ALW3Q:~/Ansible/VSCode_workspace/my_terraform_test00$ terraform state show ibm_is_security_group.my_security_group01
# ibm_is_security_group.my_security_group01:
resource "ibm_is_security_group" "my_security_group01" {
    access_tags             = []
    crn                     = "crn:v1:bluemix:public:is:jp-tok:a/1fc8373f538a408187ffedbe62e5796a::security-group:r022-e7e7719c-b572-4299-9a2a-ea655ce81793"
    id                      = "r022-e7e7719c-b572-4299-9a2a-ea655ce81793"
    name                    = "tagtest01-modified"
    resource_controller_url = "https://cloud.ibm.com/vpc-ext/network/securityGroups"
    resource_crn            = "crn:v1:bluemix:public:is:jp-tok:a/1fc8373f538a408187ffedbe62e5796a::security-group:r022-e7e7719c-b572-4299-9a2a-ea655ce81793"
    resource_group          = "07c38a522f014e74aa065dfe2ab7f9af"
    resource_group_name     = "ISEI20230707-1626-nervous"
    resource_name           = "tagtest01-modified"
    rules                   = [
        {
            code       = 0
            direction  = "inbound"
            ip_version = "ipv4"
            local      = "0.0.0.0/0"
            port_max   = 22
            port_min   = 22
            protocol   = "tcp"
            remote     = "192.168.10.10"
            type       = 0
        },
    ]
    tags                    = []
    vpc                     = "r022-a9e1fd5d-3911-4cd4-bfb0-032298831d34"
}

user01@IBM-PF3ALW3Q:~/Ansible/VSCode_workspace/my_terraform_test00$ terraform state show ibm_is_security_group.my_security_group02
# ibm_is_security_group.my_security_group02:
resource "ibm_is_security_group" "my_security_group02" {
    access_tags             = []
    crn                     = "crn:v1:bluemix:public:is:jp-tok:a/1fc8373f538a408187ffedbe62e5796a::security-group:r022-bbba461c-4f45-4040-be79-a92d131fb802"
    id                      = "r022-bbba461c-4f45-4040-be79-a92d131fb802"
    name                    = "tagtest02"
    resource_controller_url = "https://cloud.ibm.com/vpc-ext/network/securityGroups"
    resource_crn            = "crn:v1:bluemix:public:is:jp-tok:a/1fc8373f538a408187ffedbe62e5796a::security-group:r022-bbba461c-4f45-4040-be79-a92d131fb802"
    resource_group          = "07c38a522f014e74aa065dfe2ab7f9af"
    resource_group_name     = "ISEI20230707-1626-nervous"
    resource_name           = "tagtest02"
    rules                   = []
    tags                    = []
    vpc                     = "r022-8c227158-e88a-489b-a858-8dbb1482f74a"
}

user01@IBM-PF3ALW3Q:~/Ansible/VSCode_workspace/my_terraform_test00$ terraform state show ibm_is_security_group_rule.security_group_rule_ssh
# ibm_is_security_group_rule.security_group_rule_ssh:
resource "ibm_is_security_group_rule" "security_group_rule_ssh" {
    direction   = "inbound"
    group       = "r022-e7e7719c-b572-4299-9a2a-ea655ce81793"
    id          = "r022-e7e7719c-b572-4299-9a2a-ea655ce81793.r022-58548357-3017-4343-858a-f7d3756e6687"
    ip_version  = "ipv4"
    local       = "0.0.0.0/0"
    protocol    = "tcp"
    related_crn = "crn:v1:bluemix:public:is:jp-tok:a/1fc8373f538a408187ffedbe62e5796a::security-group:r022-e7e7719c-b572-4299-9a2a-ea655ce81793"
    remote      = "192.168.10.10"
    rule_id     = "r022-58548357-3017-4343-858a-f7d3756e6687"

    tcp {
        port_max = 23
        port_min = 23
    }
}

user01@IBM-PF3ALW3Q:~/Ansible/VSCode_workspace/my_terraform_test00$ terraform state show ibm_is_vpc.my_vpc
# ibm_is_vpc.my_vpc:
resource "ibm_is_vpc" "my_vpc" {
    access_tags                 = []
    address_prefix_management   = "auto"
    classic_access              = false
    crn                         = "crn:v1:bluemix:public:is:jp-tok:a/1fc8373f538a408187ffedbe62e5796a::vpc:r022-8c227158-e88a-489b-a858-8dbb1482f74a"
    cse_source_addresses        = [
        {
            address   = "10.223.196.59"
            zone_name = "jp-tok-1"
        },
        {
            address   = "10.223.211.132"
            zone_name = "jp-tok-2"
        },
        {
            address   = "10.223.213.208"
            zone_name = "jp-tok-3"
        },
    ]
    default_network_acl         = "r022-9385ad76-cb6b-4f5b-8349-3d6f85f8c223"
    default_network_acl_crn     = "crn:v1:bluemix:public:is:jp-tok:a/1fc8373f538a408187ffedbe62e5796a::network-acl:r022-9385ad76-cb6b-4f5b-8349-3d6f85f8c223"
    default_network_acl_name    = "grower-politely-hedging-refocus"
    default_routing_table       = "r022-9fa1780c-98a4-4057-978c-37bb543e04f7"
    default_routing_table_name  = "reexamine-nag-module-unlivable"
    default_security_group      = "r022-6642f207-3eda-4d7a-8140-538bb092cf3f"
    default_security_group_crn  = "crn:v1:bluemix:public:is:jp-tok:a/1fc8373f538a408187ffedbe62e5796a::security-group:r022-6642f207-3eda-4d7a-8140-538bb092cf3f"
    default_security_group_name = "ferry-grinch-dimly-freedom"
    health_reasons              = []
    health_state                = "ok"
    id                          = "r022-8c227158-e88a-489b-a858-8dbb1482f74a"
    name                        = "example-vpc"
    no_sg_acl_rules             = false
    resource_controller_url     = "https://cloud.ibm.com/vpc-ext/network/vpcs"
    resource_crn                = "crn:v1:bluemix:public:is:jp-tok:a/1fc8373f538a408187ffedbe62e5796a::vpc:r022-8c227158-e88a-489b-a858-8dbb1482f74a"
    resource_group              = "07c38a522f014e74aa065dfe2ab7f9af"
    resource_group_name         = "ISEI20230707-1626-nervous"
    resource_name               = "example-vpc"
    resource_status             = "available"
    security_group              = [
        {
            group_id   = "r022-6642f207-3eda-4d7a-8140-538bb092cf3f"
            group_name = "ferry-grinch-dimly-freedom"
            rules      = [
                {
                    code       = 0
                    direction  = "outbound"
                    ip_version = "ipv4"
                    port_max   = 0
                    port_min   = 0
                    protocol   = "all"
                    remote     = "0.0.0.0/0"
                    rule_id    = "r022-16fef6a1-2ef3-4888-a8c1-cd71c918d15b"
                    type       = 0
                },
                {
                    code       = 0
                    direction  = "inbound"
                    ip_version = "ipv4"
                    port_max   = 0
                    port_min   = 0
                    protocol   = "all"
                    remote     = "r022-6642f207-3eda-4d7a-8140-538bb092cf3f"
                    rule_id    = "r022-9f0aaeae-daa3-4858-bee2-388a67034e7c"
                    type       = 0
                },
            ]
        },
    ]
    status                      = "available"
    subnets                     = []
    tags                        = []

    dns {
        enable_hub               = false
        resolution_binding_count = 0

        resolver {
            configuration = "default"
            servers       = [
                {
                    address       = "161.26.0.10"
                    zone_affinity = ""
                },
                {
                    address       = "161.26.0.11"
                    zone_affinity = ""
                },
            ]
            type          = "system"
        }
    }
}
```

my_security_group01("tagtest01-modified")はnameは変更されましたが、idは変わっていないのが確認できます(リソースの属性値が変わっただけ)。
一方my_security_group02("tagtest02")はnameは同じですがidは変わっています(リソースとしては前のものが破棄され、新規に作成された)。

## サンプル・シナリオ(3)

同じような定義のリソースを一部のパラメーターだけ変えて複数追加したり削除したりすることを想定した場合の、よりスマートな定義の方法を試してみます。
具体的にはTerraformで提供されるfor_eachという記述方式を使うことで1ブロックの記述で複数リソースを定義することができます。また、for_eachに使用する変数としてmapsというTypeの変数を利用できます。この辺りの記述方式を試してみます。

参考: 
[The for_each Meta-Argument](https://developer.hashicorp.com/terraform/language/meta-arguments/for_each)
[Maps/Objects](https://developer.hashicorp.com/terraform/language/expressions/types#maps-objects)

### 構成ファイル作成

for_each, map型変数を使って複数リソースを効率よく記述する方法を試してみます。ここでは、複数のセキュリティー・グループをfor_eachを使って定義してみます。
プロバイダー関連(provider.tf)、環境変数定義(variables.tf)、環境変数の設定(my-settings.auto.tfvars)はサンプル・シナリオ(1)と同じものを使用することとします。

※構成ファイルの書き方を検証する目的で作っているものなので、指定している内容にあまり意味はありません。

#### Local Value / locals.tf

```tf:locals.tf
locals{
  sec_group_profiles = {
    tagtest01 = {
      vpc = data.ibm_is_vpc.target_vpc.id
      rule_inbound_remote = "192.168.10.10"
      rule_inbound_port_min = 22
      rule_inbound_port_max = 22
    },
    tagtest02 = {
      vpc = ibm_is_vpc.my_vpc.id
      rule_inbound_remote = "192.168.10.11"
      rule_inbound_port_min = 23
      rule_inbound_port_max = 23    
    }
  }

}
```

作成したいセキュリティー・グループに関するパラメーター(名前やルール)はLocal Valueとして定義することにします。Local Valueは内部的に使用される変数(引数での上書きは不可)です。
参考: [Local Values](https://developer.hashicorp.com/terraform/language/values/locals)

ここでは、`sec_group_profiles`というmap型の変数として定義しています。この中身としてはネストされたmap型の定義をしており、それぞれのリソース名ごとに設定したいパラメーターを定義しています。ここでは、tagtest01, tagtest02という2つのセキュリティー・グループを作成する想定で、それぞれの定義で変更したいものについてのパラメーターを設定しています。

※リソースを増やしたい場合はsec_group_profilesの指定を追加すればよい、という管理方法にするのが目的です。

#### リソース関連 / resources.tf

```tf:resources.tf
# Existing VPC
data "ibm_is_vpc" "target_vpc" {
  name = "vpc01"
} 

# Create a VPC
resource "ibm_is_vpc" "my_vpc" {
  name = "example-vpc"
  resource_group = var.target_resource_group_id

} 

# Security Groups
resource "ibm_is_security_group" "my_security_groups" {
  for_each = local.sec_group_profiles

  name = each.key
  vpc = each.value.vpc
  resource_group = var.target_resource_group_id
} 


# Configure Security Group Rule
resource "ibm_is_security_group_rule" "security_group_rule_ssh" {
    for_each = ibm_is_security_group.my_security_groups

    group = each.value.id
    direction = "inbound" 

    remote = lookup(local.sec_group_profiles, each.value.name).rule_inbound_remote
    tcp {
      port_min = lookup(local.sec_group_profiles, each.value.name).rule_inbound_port_min
      port_max = lookup(local.sec_group_profiles, each.value.name).rule_inbound_port_max
    }
 }
```

【Security Group】
`for_each = local.sec_group_profiles` を指定することで、Local Valueとして定義したsec_group_profilesに設定されたオブジェクトのリスト(ここでは`tagtest01`, `tagtest02`)ごとにSecrity Groupが作成されます。

`each.key`、`each.value`で、for_eachで指定したmapオブジェクトのKeyとValueをそれぞれ参照できます。
`name = each.key` を指定していますので、作成するリソースの名前として`local.sec_group_profiles`のKey値(`tagtest01`, `tagtest02`)をそのまま使用します。
`vpc = each.value.vpc` と指定することで、セキュリティー・グループを作成するVPCとしては`local.sec_group_profiles`の各Key値ごとに設定されたVPC値が使われます。すなわち、tagtest01の場合は`data.ibm_is_vpc.target_vpc.id`(既存のVPC)、tagtest02の場合は`ibm_is_vpc.my_vpc.id`(新規に作成したVPC)が使われることになります。

【Security Group Rule】
ここでは、Security Group Ruleは
`for_each = ibm_is_security_group.my_security_groups` と指定することで、上で作成されたセキュリティー・グループごとにルールも作成するようにしています(for_each指定で作成されたリソース`my_security_groups`もmapオブジェクトとしてハンドリングできるようです)。

`group = each.value.id`と指定することで、ルールを紐づけるセキュリティー・グループを指定しています。
各セキュリティー・グループに設定する他の値はlocal.sec_group_profilesに指定していますが、特定のKey値のValueを参照するためにlookupという関数が使用できます。
参考: [lookup Function](https://developer.hashicorp.com/terraform/language/functions/lookup)
`lookup(local.sec_group_profiles, each.value.name)`このように指定することで、追加しようとしているセキュリティー・グループ名に関連する設定値を取得できますので、そこから設定するリモート・アドレス(`rule_inbound_remote`)やポート番号(`rule_inbound_port_min`,`rule_inbound_port_max`)を参照することができます。

### 管理コマンド実行
それでは上の定義を実際に反映させてみます。

#### 事前確認 / terraform plan

```sh:plan
user01@IBM-PF3ALW3Q:~/Ansible/VSCode_workspace/my_terraform_test002$ terraform plan
data.ibm_is_vpc.target_vpc: Reading...
data.ibm_is_vpc.target_vpc: Still reading... [10s elapsed]
data.ibm_is_vpc.target_vpc: Read complete after 11s [id=r022-a9e1fd5d-3911-4cd4-bfb0-032298831d34]

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:

  # ibm_is_security_group.my_security_groups["tagtest01"] will be created
  + resource "ibm_is_security_group" "my_security_groups" {
      + access_tags             = (known after apply)
      + crn                     = (known after apply)
      + id                      = (known after apply)
      + name                    = "tagtest01"
      + resource_controller_url = (known after apply)
      + resource_crn            = (known after apply)
      + resource_group          = "07c38a522f014e74aa065dfe2ab7f9af"
      + resource_group_name     = (known after apply)
      + resource_name           = (known after apply)
      + rules                   = (known after apply)
      + tags                    = (known after apply)
      + vpc                     = "r022-a9e1fd5d-3911-4cd4-bfb0-032298831d34"
    }

  # ibm_is_security_group.my_security_groups["tagtest02"] will be created
  + resource "ibm_is_security_group" "my_security_groups" {
      + access_tags             = (known after apply)
      + crn                     = (known after apply)
      + id                      = (known after apply)
      + name                    = "tagtest02"
      + resource_controller_url = (known after apply)
      + resource_crn            = (known after apply)
      + resource_group          = "07c38a522f014e74aa065dfe2ab7f9af"
      + resource_group_name     = (known after apply)
      + resource_name           = (known after apply)
      + rules                   = (known after apply)
      + tags                    = (known after apply)
      + vpc                     = (known after apply)
    }

  # ibm_is_security_group_rule.security_group_rule_ssh["tagtest01"] will be created
  + resource "ibm_is_security_group_rule" "security_group_rule_ssh" {
      + direction   = "inbound"
      + group       = (known after apply)
      + id          = (known after apply)
      + ip_version  = "ipv4"
      + local       = (known after apply)
      + protocol    = (known after apply)
      + related_crn = (known after apply)
      + remote      = "192.168.10.10"
      + rule_id     = (known after apply)

      + tcp {
          + port_max = 22
          + port_min = 22
        }
    }

  # ibm_is_security_group_rule.security_group_rule_ssh["tagtest02"] will be created
  + resource "ibm_is_security_group_rule" "security_group_rule_ssh" {
      + direction   = "inbound"
      + group       = (known after apply)
      + id          = (known after apply)
      + ip_version  = "ipv4"
      + local       = (known after apply)
      + protocol    = (known after apply)
      + related_crn = (known after apply)
      + remote      = "192.168.10.11"
      + rule_id     = (known after apply)

      + tcp {
          + port_max = 23
          + port_min = 23
        }
    }

  # ibm_is_vpc.my_vpc will be created
  + resource "ibm_is_vpc" "my_vpc" {
      + access_tags                 = (known after apply)
      + address_prefix_management   = "auto"
      + classic_access              = false
      + crn                         = (known after apply)
      + cse_source_addresses        = (known after apply)
      + default_network_acl         = (known after apply)
      + default_network_acl_crn     = (known after apply)
      + default_network_acl_name    = (known after apply)
      + default_routing_table       = (known after apply)
      + default_routing_table_name  = (known after apply)
      + default_security_group      = (known after apply)
      + default_security_group_crn  = (known after apply)
      + default_security_group_name = (known after apply)
      + health_reasons              = (known after apply)
      + health_state                = (known after apply)
      + id                          = (known after apply)
      + name                        = "example-vpc"
      + no_sg_acl_rules             = false
      + resource_controller_url     = (known after apply)
      + resource_crn                = (known after apply)
      + resource_group              = "07c38a522f014e74aa065dfe2ab7f9af"
      + resource_group_name         = (known after apply)
      + resource_name               = (known after apply)
      + resource_status             = (known after apply)
      + security_group              = (known after apply)
      + status                      = (known after apply)
      + subnets                     = (known after apply)
      + tags                        = (known after apply)
    }

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

```sh:apply
user01@IBM-PF3ALW3Q:~/Ansible/VSCode_workspace/my_terraform_test002$ terraform apply
data.ibm_is_vpc.target_vpc: Reading...
data.ibm_is_vpc.target_vpc: Still reading... [10s elapsed]
data.ibm_is_vpc.target_vpc: Read complete after 11s [id=r022-a9e1fd5d-3911-4cd4-bfb0-032298831d34]

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:

  # ibm_is_security_group.my_security_groups["tagtest01"] will be created
  + resource "ibm_is_security_group" "my_security_groups" {
      + access_tags             = (known after apply)
      + crn                     = (known after apply)
      + id                      = (known after apply)
      + name                    = "tagtest01"
      + resource_controller_url = (known after apply)
      + resource_crn            = (known after apply)
      + resource_group          = "07c38a522f014e74aa065dfe2ab7f9af"
      + resource_group_name     = (known after apply)
      + resource_name           = (known after apply)
      + rules                   = (known after apply)
      + tags                    = (known after apply)
      + vpc                     = "r022-a9e1fd5d-3911-4cd4-bfb0-032298831d34"
    }

  # ibm_is_security_group.my_security_groups["tagtest02"] will be created
  + resource "ibm_is_security_group" "my_security_groups" {
      + access_tags             = (known after apply)
      + crn                     = (known after apply)
      + id                      = (known after apply)
      + name                    = "tagtest02"
      + resource_controller_url = (known after apply)
      + resource_crn            = (known after apply)
      + resource_group          = "07c38a522f014e74aa065dfe2ab7f9af"
      + resource_group_name     = (known after apply)
      + resource_name           = (known after apply)
      + rules                   = (known after apply)
      + tags                    = (known after apply)
      + vpc                     = (known after apply)
    }

  # ibm_is_security_group_rule.security_group_rule_ssh["tagtest01"] will be created
  + resource "ibm_is_security_group_rule" "security_group_rule_ssh" {
      + direction   = "inbound"
      + group       = (known after apply)
      + id          = (known after apply)
      + ip_version  = "ipv4"
      + local       = (known after apply)
      + protocol    = (known after apply)
      + related_crn = (known after apply)
      + remote      = "192.168.10.10"
      + rule_id     = (known after apply)

      + tcp {
          + port_max = 22
          + port_min = 22
        }
    }

  # ibm_is_security_group_rule.security_group_rule_ssh["tagtest02"] will be created
  + resource "ibm_is_security_group_rule" "security_group_rule_ssh" {
      + direction   = "inbound"
      + group       = (known after apply)
      + id          = (known after apply)
      + ip_version  = "ipv4"
      + local       = (known after apply)
      + protocol    = (known after apply)
      + related_crn = (known after apply)
      + remote      = "192.168.10.11"
      + rule_id     = (known after apply)

      + tcp {
          + port_max = 23
          + port_min = 23
        }
    }

  # ibm_is_vpc.my_vpc will be created
  + resource "ibm_is_vpc" "my_vpc" {
      + access_tags                 = (known after apply)
      + address_prefix_management   = "auto"
      + classic_access              = false
      + crn                         = (known after apply)
      + cse_source_addresses        = (known after apply)
      + default_network_acl         = (known after apply)
      + default_network_acl_crn     = (known after apply)
      + default_network_acl_name    = (known after apply)
      + default_routing_table       = (known after apply)
      + default_routing_table_name  = (known after apply)
      + default_security_group      = (known after apply)
      + default_security_group_crn  = (known after apply)
      + default_security_group_name = (known after apply)
      + health_reasons              = (known after apply)
      + health_state                = (known after apply)
      + id                          = (known after apply)
      + name                        = "example-vpc"
      + no_sg_acl_rules             = false
      + resource_controller_url     = (known after apply)
      + resource_crn                = (known after apply)
      + resource_group              = "07c38a522f014e74aa065dfe2ab7f9af"
      + resource_group_name         = (known after apply)
      + resource_name               = (known after apply)
      + resource_status             = (known after apply)
      + security_group              = (known after apply)
      + status                      = (known after apply)
      + subnets                     = (known after apply)
      + tags                        = (known after apply)
    }

Plan: 5 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_vpc.my_vpc: Creating...
ibm_is_vpc.my_vpc: Still creating... [10s elapsed]
ibm_is_vpc.my_vpc: Still creating... [20s elapsed]
ibm_is_vpc.my_vpc: Creation complete after 26s [id=r022-bfbf98bf-e461-4d85-bc57-658452e2807a]
ibm_is_security_group.my_security_groups["tagtest02"]: Creating...
ibm_is_security_group.my_security_groups["tagtest01"]: Creating...
ibm_is_security_group.my_security_groups["tagtest02"]: Creation complete after 3s [id=r022-718f8b76-7c10-4435-b425-ea08fce195d1]
ibm_is_security_group.my_security_groups["tagtest01"]: Creation complete after 4s [id=r022-762534bf-b9e3-4376-9b94-0e1ae4599fd7]
ibm_is_security_group_rule.security_group_rule_ssh["tagtest01"]: Creating...
ibm_is_security_group_rule.security_group_rule_ssh["tagtest02"]: Creating...
ibm_is_security_group_rule.security_group_rule_ssh["tagtest01"]: Creation complete after 1s [id=r022-762534bf-b9e3-4376-9b94-0e1ae4599fd7.r022-43623095-f5a7-4122-9923-24f0fa0e6a14]
ibm_is_security_group_rule.security_group_rule_ssh["tagtest02"]: Creation complete after 1s [id=r022-718f8b76-7c10-4435-b425-ea08fce195d1.r022-7374a943-eb67-41d3-94ea-3446d86ae4a8]

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

#### 状況確認 / terraform state

```sh:state
user01@IBM-PF3ALW3Q:~/Ansible/VSCode_workspace/my_terraform_test002$ terraform state list
data.ibm_is_vpc.target_vpc
ibm_is_security_group.my_security_groups["tagtest01"]
ibm_is_security_group.my_security_groups["tagtest02"]
ibm_is_security_group_rule.security_group_rule_ssh["tagtest01"]
ibm_is_security_group_rule.security_group_rule_ssh["tagtest02"]
ibm_is_vpc.my_vpc

user01@IBM-PF3ALW3Q:~/Ansible/VSCode_workspace/my_terraform_test002$ terraform state show 'ibm_is_security_group.my_security_groups["tagtest01"]'
# ibm_is_security_group.my_security_groups["tagtest01"]:
resource "ibm_is_security_group" "my_security_groups" {
    access_tags             = []
    crn                     = "crn:v1:bluemix:public:is:jp-tok:a/1fc8373f538a408187ffedbe62e5796a::security-group:r022-762534bf-b9e3-4376-9b94-0e1ae4599fd7"
    id                      = "r022-762534bf-b9e3-4376-9b94-0e1ae4599fd7"
    name                    = "tagtest01"
    resource_controller_url = "https://cloud.ibm.com/vpc-ext/network/securityGroups"
    resource_crn            = "crn:v1:bluemix:public:is:jp-tok:a/1fc8373f538a408187ffedbe62e5796a::security-group:r022-762534bf-b9e3-4376-9b94-0e1ae4599fd7"
    resource_group          = "07c38a522f014e74aa065dfe2ab7f9af"
    resource_group_name     = "ISEI20230707-1626-nervous"
    resource_name           = "tagtest01"
    rules                   = []
    tags                    = []
    vpc                     = "r022-a9e1fd5d-3911-4cd4-bfb0-032298831d34"
}

user01@IBM-PF3ALW3Q:~/Ansible/VSCode_workspace/my_terraform_test002$ terraform state show 'ibm_is_security_group.my_security_groups["tagtest02"]'
# ibm_is_security_group.my_security_groups["tagtest02"]:
resource "ibm_is_security_group" "my_security_groups" {
    access_tags             = []
    crn                     = "crn:v1:bluemix:public:is:jp-tok:a/1fc8373f538a408187ffedbe62e5796a::security-group:r022-718f8b76-7c10-4435-b425-ea08fce195d1"
    id                      = "r022-718f8b76-7c10-4435-b425-ea08fce195d1"
    name                    = "tagtest02"
    resource_controller_url = "https://cloud.ibm.com/vpc-ext/network/securityGroups"
    resource_crn            = "crn:v1:bluemix:public:is:jp-tok:a/1fc8373f538a408187ffedbe62e5796a::security-group:r022-718f8b76-7c10-4435-b425-ea08fce195d1"
    resource_group          = "07c38a522f014e74aa065dfe2ab7f9af"
    resource_group_name     = "ISEI20230707-1626-nervous"
    resource_name           = "tagtest02"
    rules                   = []
    tags                    = []
    vpc                     = "r022-bfbf98bf-e461-4d85-bc57-658452e2807a"
}

user01@IBM-PF3ALW3Q:~/Ansible/VSCode_workspace/my_terraform_test002$ terraform state show 'ibm_is_security_group_rule.security_group_rule_ssh["tagtest01"]'
# ibm_is_security_group_rule.security_group_rule_ssh["tagtest01"]:
resource "ibm_is_security_group_rule" "security_group_rule_ssh" {
    direction   = "inbound"
    group       = "r022-762534bf-b9e3-4376-9b94-0e1ae4599fd7"
    id          = "r022-762534bf-b9e3-4376-9b94-0e1ae4599fd7.r022-43623095-f5a7-4122-9923-24f0fa0e6a14"
    ip_version  = "ipv4"
    local       = "0.0.0.0/0"
    protocol    = "tcp"
    related_crn = "crn:v1:bluemix:public:is:jp-tok:a/1fc8373f538a408187ffedbe62e5796a::security-group:r022-762534bf-b9e3-4376-9b94-0e1ae4599fd7"
    remote      = "192.168.10.10"
    rule_id     = "r022-43623095-f5a7-4122-9923-24f0fa0e6a14"

    tcp {
        port_max = 22
        port_min = 22
    }
}

user01@IBM-PF3ALW3Q:~/Ansible/VSCode_workspace/my_terraform_test002$ terraform state show 'ibm_is_security_group_rule.security_group_rule_ssh["tagtest02"]'
# ibm_is_security_group_rule.security_group_rule_ssh["tagtest02"]:
resource "ibm_is_security_group_rule" "security_group_rule_ssh" {
    direction   = "inbound"
    group       = "r022-718f8b76-7c10-4435-b425-ea08fce195d1"
    id          = "r022-718f8b76-7c10-4435-b425-ea08fce195d1.r022-7374a943-eb67-41d3-94ea-3446d86ae4a8"
    ip_version  = "ipv4"
    local       = "0.0.0.0/0"
    protocol    = "tcp"
    related_crn = "crn:v1:bluemix:public:is:jp-tok:a/1fc8373f538a408187ffedbe62e5796a::security-group:r022-718f8b76-7c10-4435-b425-ea08fce195d1"
    remote      = "192.168.10.11"
    rule_id     = "r022-7374a943-eb67-41d3-94ea-3446d86ae4a8"

    tcp {
        port_max = 23
        port_min = 23
    }
}
```

#### IBM Cloud管理コンソールでの確認

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/132331/f473d1a3-186e-b29c-2e8e-b0c024280369.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/132331/5890da1a-3082-7865-6a32-6da7b1efe8b1.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/132331/a82fa590-7f80-7fdb-1f8e-902e05882a63.png)

意図した通り、locals.tfで指定したプロファイルに基づき2つのセキュリティー・グループが作成されました！

# おわりに

Terraformを利用してIBM Cloudリソース(VPC関連リソース)を管理する基本的な手順が確認できました。これを応用すればVSI(仮想サーバー)など他のリソースの管理も同様に行えると思います。

もう少し具体的な例として、z/OSの仮想開発/テスト環境であるWazi as a ServiceをTerraformで管理する例を以下の記事に記載していますのでそちらもご参照ください。
[Wazi aaS: クラウド上でのメインフレーム開発環境構築 - (9) TerraformによるWazi aaS仮想サーバーの管理](https://qiita.com/tomotagwork/items/883652c1534d12f2b042)