TerraformでOracle Cloud(OCI)の基本的なリソースを一通り作ってみた

2019/02/11 新規作成
***

Oracle Cloud Infrastructure(OCI)のリソースを作る方法は、Web管理コンソール経由、oci cli、Ansible、chefといろいろありますが、今回はTerraformで作ってみました（tfから手作り）。
※今回の記事で作成したtfファイルを一部[Githubで公開](https://github.com/feifeifo/terraformocitest)


その他の方法で作成した際の記事
 - [「Oracle Cloud(OCI)のcliで基本的なOCIリソースを一通り作ってみた」](https://qiita.com/feifo/items/275d240ba672f6260775)

# はじめに
## やりたいこと
以下の環境をTerraformで作る（[oci cliで作った時](https://qiita.com/feifo/items/275d240ba672f6260775)と同じ構成です）
![image.png](https://qiita-image-store.s3.amazonaws.com/0/109270/5d6ae0d2-175d-f211-2af4-4e8c6e0b5fcf.png)

- ネットワーク
    - VCN (192.168.100.0/24)
    - Internet Gateway
    - Security List
    - Route table
    - subnet (192.168.100.0/27,  192.168.100.32/27)
- インスタンス
    - Webサーバー想定のComputeインスタンス
    - DBサーバー想定のDbaaSインスタンス


## 前提
- Terraformはインストール済み
- Windows 10 環境からterraformを実行
- terraformのバージョンは`Terraform v0.11.11`
- oci pluginのバージョンは`provider.oci: version = "~> 3.14"`
- OCIのリソースについて説明の説明はしません


## 参考
- TerraformのDocument
    - [Oracle Cloud Infrastructure Provider](https://www.terraform.io/docs/providers/oci/index.html)
- GitHubにあるOCIのサンプル
    - [terraform-provider-oci/docs/examples](https://github.com/terraform-providers/terraform-provider-oci/tree/master/docs/examples)
- Qiita記事
    - [Terraform Best Practices in 2017](https://qiita.com/shogomuranushi/items/e2f3ff3cfdcacdd17f99)


# 1.準備

## 1-1.準備しておくこと
- Terraformを使えるようにしておく（インストール+環境変数設定）
    - サイト : [Terraform Download](https://www.terraform.io/downloads.html)
- OCIの基本的な準備
    - (ユーザー作成)
    - (コンパートメント作成)
    - APIキーの登録
- OCIのprovider使うための情報をメモ
    - tenancyのocid
    - userのocid
    - userのfingerprint
    - api用の秘密鍵のパス
    - 使用するregion
- OCIリソースを作成・利用するために必要な情報をメモ
    - compartment_id（コンパートメントのOCID）
- 作りたいリソースごとに必要な情報をメモ
    - インスタンスで利用するイメージのOCIDなど

## 1-2.流れ
- ディレクトリ構成、tfファイルの分割単位などの検討（今回はパス）
- tfファイルを書く
    - provier(oci)の情報
    - network（VCN、GW、routing tableなど）の設定情報
    - computeの設定情報
    - dbaasの設定情報
- terraformを実行する
    - `> terraform init`　：初期化の処理。Plug-inのインストール処理など
    - `> terraform plan`　：構文チェック、プランの作成など
    - `> terraform apply`　：プランの実行


# 2.tfファイルを書く
tfファイルを書いていきます。今回は1から作っていますが、オラクル社が公開しているサンプルを編集していく形の方が楽だと思います（[sample](https://github.com/terraform-providers/terraform-provider-oci/tree/master/docs/examples)）。

terraformは指定した**ディレクトリ内（指定しない場合はカレントディレクトリ）の全ての`*.tf`もしくは`*.tf.json`ファイルを読み込み**ます。そのため、必要に応じてtfファイルを分割して、記載することができます（1つに全てを書いてもOK）。

今回はこんな感じにディレクトリにずらーっと並べました。

```
PS C:\Terraform\oci> dir


    ディレクトリ: C:\Terraform\oci


Mode                LastWriteTime         Length Name
----                -------------         ------ ----
d-----       2019/02/10     22:42                .terraform
d-----       2019/02/10     21:44                key
-a----       2019/02/11      2:11            793 common.tf
-a----       2019/02/11      2:11            827 compute.tf
-a----       2019/02/11      2:45            613 DbaaS-var.tf
-a----       2019/02/11      3:11            356 DbaaS.auto.tfvars
-a----       2019/02/11      2:45           1142 dbaas.tf
-a----       2019/02/10     22:07            668 instance-var.tf
-a----       2019/02/11      2:36            158 instance.auto.tfvars
-a----       2019/02/10     23:24           1342 network-var.tf
-a----       2019/02/10     23:25           1463 network.auto.tfvars
-a----       2019/02/11      2:53           4881 network.tf
-a----       2019/02/11      1:08            701 output.tf
-a----       2019/02/10     22:09            483 provider-var.tf
-a----       2019/02/10     23:07            202 provider.tf
```

※複数のリソースを組み合わせて複雑な構成のシステムを作成する場合は、モジュールなどを使った方が便利ですが、今回はシンプルに1つのディレクトリ配下に適当に分けたtfファイルをずらずらと並べます。

- **tfファイルを書く際の注意**

1.Terraformのパーサーは**UTF-8 byte-order mark(BOM)をサポートしていません**。terraformコマンド実行時に以下のエラーが出た場合は、tfファイルがBOMとなっていないか確認すると良いと思います。
`Error: Error parsing <path>\xxxxxx.tf: At 1:1: illegal char`

2."\"はエスケープする必要があります("\\\")。こちらは以下のエラーがでるのでわかりやすいです。
`Error: Error parsing <path>\xxxxxx.tf: At 11:35: illegal char escape`

## 2-1.provier用のtf

### プロバイダ情報の指定
プロバイダごとに必要な情報が決まっているので、確認します。
OCIの場合は以下↓（oci providerのバージョンを指定することも可能）

```json:provider.tf
provider "oci" {
  tenancy_ocid = "${var.tenancy_ocid}"
  user_ocid = "${var.user_ocid}"
  fingerprint = "${var.fingerprint}"
  private_key_path = "${var.private_key_path}"
  region = "${var.region}"
}
```
↑[TerraformのDoc](https://www.terraform.io/docs/providers/oci/index.html)より

※変数（${ }の部分）を使わず、直接値を記載することも当然可能ですが、変数にしておいて、変数だけまとめたtfを別途用意しておけば、それ以外のtfファイルは広く共有が可能になります。上のファイルはterraformでociリソースを作成する人なら誰もがそのまま利用可能となっています（逆に言えば、そういった形でtfファイルが至る所に公開されているので、あとは自分の環境に合わせた変数用のファイルを作成するだけでOK）。

### 変数の宣言

**変数は宣言して上で、値を代入する必要があります**。型などを定義する宣言部（variable block）ではデフォルト値を指定することもできます（下の例では型は省略し、デフォルト値のみ指定しています）。
provider用のtfについては、全ての変数にデフォルト値を定義し、個別に値の代入はしていません。後にでてくる各リソースについては、宣言部と代入する部分でファイルを分けています。

```json:provider-var.tf
variable "tenancy_ocid" {
  default = "xxxxx"
}
variable "user_ocid" {
  default = "xxxxx"
}
variable "fingerprint" {
  default = "xxxxx"
}
variable "private_key_path" {
  default = "xxxxx"
}
variable "region" {
  default = "xxxxx"
}
```

### 変数の代入
デフォルト値を指定していない場合や、デフォルト値を使わない場合は、変数へ値を代入する必要があります。代入する方法は、いくつかあります。

- コマンド実行時に変数への代入が書かれたファイルを指定(`-var "ファイル名"`)
- 変数へ値を代入するためのデフォルトファイル（`terraform.tfvars`, `*.auto.tfvars`）を使用
- 環境変数（`TF_VAR_<変数名>`）をセットしておく
- コマンド実行時に指定（上記のいずれでも変数に値を代入しなかった場合に、対話的に設定）

providerの設定については、デフォルトに設定したものをそのまま使うため、代入のためのファイルは作成しません。


## 2-2.リソース共通用のtf
リソースに共通で必要な変数やデータソースは、まとめて宣言・代入しておいた方が楽です。

```json:common.tf
# リソースを作成する際は必ずコンパートメントを指定する必要があるので、宣言しておきます。
variable "compartment_ocid" {
  default = "xxxxx"
}

# 多くのリソースでADを指定することがあるので、テナンシーのAD名をリストで取得しておきます
data "oci_identity_availability_domains" "ADs" {
  provider = "oci.us-phoenix-1"
  compartment_id = "${var.tenancy_ocid}"
}

# タグでリソースを管理するために、宣言をしてきます
variable "defined_tag" {
  default = "<tagnamespace>.<tag>"
}

variable "defined_tag_value" {
  default = "xxxxx"
}
```
参考
：[TerraformのData sourceの説明](https://www.terraform.io/docs/configuration/data-sources.html)
：[TerraformのOCI Providerのavailability domainデータソース リファレンス](https://www.terraform.io/docs/providers/oci/d/identity_availability_domains.html)

## 2-3.Network作成用のtf
ネットワークといっても、VCN、subnet、Internet Gateway、Route table、Security ruleなどいろいろなコンポーネントがあり、tfファイルを分けた方がわかりやすいかもしれませんが、今回は1つのtfファイルとします。

リソースを作成する場合の構文は以下から確認。必須の項目と、指定したい項目のみを引っ張ってくる感じ。
https://www.terraform.io/docs/providers/oci/index.html
※例えばvcnなら「oci_core_vcn」の部分を見る

### Network関連リソースの作成
init時に以下のエラーがでたので、security_list_ids をリストとして"[]"で囲いました。
`Error: oci_core_subnet.test_db_subnet: security_list_ids: should be a list`
`Error: oci_core_subnet.test_web_subnet: security_list_ids: should be a list`


```json:network.tf
# VCN
resource "oci_core_vcn" "test_vcn" {
    cidr_block = "${var.vcn_cidr_block}"          # 必須
    compartment_id = "${var.compartment_ocid}"      # 必須
    display_name = "${var.vcn_display_name}"
    dns_label = "${var.vcn_dns_label}"
    defined_tags = "${map("${var.defined_tag}",  "${var.defined_tag_value}")}"
}

# Internet Gateway
resource "oci_core_internet_gateway" "test_internet_gateway" {
    compartment_id = "${var.compartment_ocid}"    # 必須
    vcn_id = "${oci_core_vcn.test_vcn.id}"        # 必須(上で作成したVCNのidを利用)
    display_name = "${var.internet_gateway_display_name}"
    enabled = true
    defined_tags = "${map("${var.defined_tag}",  "${var.defined_tag_value}")}"
}

# route table
resource "oci_core_route_table" "test_route_table" {
    compartment_id = "${var.compartment_ocid}"      # 必須
    route_rules {
        network_entity_id = "${oci_core_internet_gateway.test_internet_gateway.id}" # 必須(上で作成したIGのidを利用)
        destination = "0.0.0.0/0"
    }
    vcn_id = "${oci_core_vcn.test_vcn.id}"
    display_name = "${var.route_table_display_name}"
    defined_tags = "${map("${var.defined_tag}",  "${var.defined_tag_value}")}"
}


# Security List(Web-Subnet用)
resource "oci_core_security_list" "test_security_list_web" {
    compartment_id = "${var.compartment_ocid}"     # 必須
    egress_security_rules {
        destination = "${var.sl_egress_destination_web}" # 必須
        protocol = "${var.sl_egress_protocol_web}"       # 必須
        stateless = false
        }
    ingress_security_rules {
        source = "${var.sl_ingress_source_web}"          # 必須
        protocol = "${var.sl_ingress_protocol_web}"      # 必須
        stateless = false
        tcp_options {
            max = "${var.sl_ingress_tcp_dest_port_max_web}"
            min = "${var.sl_ingress_tcp_dest_port_min_web}"
        }
    }
    vcn_id = "${oci_core_vcn.test_vcn.id}"
    display_name = "${var.sl_display_name_web}"
    defined_tags = "${map("${var.defined_tag}",  "${var.defined_tag_value}")}"
}


# Security List(DB-Subnet用)
resource "oci_core_security_list" "test_security_list_db" {
    compartment_id = "${var.compartment_ocid}"     # 必須
    egress_security_rules {
        destination = "${var.sl_egress_destination_db}" # 必須
        protocol = "${var.sl_egress_protocol_db}"       # 必須
        stateless = false
        }
    ingress_security_rules {
        source = "${var.sl_ingress_source_db}"          # 必須
        protocol = "${var.sl_ingress_protocol_db}"      # 必須
        stateless = false
        tcp_options {
            max = "${var.sl_ingress_tcp_dest_port_max_db1}"
            min = "${var.sl_ingress_tcp_dest_port_min_db1}"
        }
    }
    ingress_security_rules {
        source = "${var.sl_ingress_source_db}"          # 必須
        protocol = "${var.sl_ingress_protocol_db}"      # 必須
        stateless = false
        tcp_options {
            max = "${var.sl_ingress_tcp_dest_port_max_db2}"
            min = "${var.sl_ingress_tcp_dest_port_min_db2}"
        }
    }
    vcn_id = "${oci_core_vcn.test_vcn.id}"
    display_name = "${var.sl_display_name_db}"
    defined_tags = "${map("${var.defined_tag}",  "${var.defined_tag_value}")}"
}


# Subent(Web)
resource "oci_core_subnet" "test_web_subnet" {
    availability_domain = "${lookup(data.oci_identity_availability_domains.ADs.availability_domains[0],"name")}" # 必須
    cidr_block = "${var.web_subnet_cidr_block}"                 # 必須
    compartment_id = "${var.compartment_ocid}"                  # 必須
    security_list_ids = ["${oci_core_security_list.test_security_list_web.id}"]       # 必須
    vcn_id = "${oci_core_vcn.test_vcn.id}"                      # 必須

    display_name = "${var.web_subnet_display_name}"
    dns_label = "${var.web_subnet_dns_label}"
    prohibit_public_ip_on_vnic = "${var.web_subnet_prohibit_public_ip_on_vnic}"
    route_table_id = "${oci_core_route_table.test_route_table.id}"
    defined_tags = "${map("${var.defined_tag}",  "${var.defined_tag_value}")}"
}


# Subent(db)
resource "oci_core_subnet" "test_db_subnet" {
    availability_domain = "${lookup(data.oci_identity_availability_domains.ADs.availability_domains[0],"name")}" # 必須
    cidr_block = "${var.db_subnet_cidr_block}"                  # 必須
    compartment_id = "${var.compartment_ocid}"                  # 必須
    security_list_ids = ["${oci_core_security_list.test_security_list_db.id}"]       # 必須
    vcn_id = "${oci_core_vcn.test_vcn.id}"                      # 必須

    display_name = "${var.db_subnet_display_name}"
    dns_label = "${var.db_subnet_dns_label}"
    prohibit_public_ip_on_vnic = "${var.db_subnet_prohibit_public_ip_on_vnic}"
    defined_tags = "${map("${var.defined_tag}",  "${var.defined_tag_value}")}"
}
```

### 変数の宣言
今回、ネットワーク用の変数についてはデフォルト値を設定せず、ただ宣言のみを行います。

```json:network-var.tf
# VCN
variable "vcn_cidr_block" {}
variable "vcn_display_name" {}
variable "vcn_dns_label" {}

# internet gateway
variable "internet_gateway_display_name"  {}

# route table
variable "route_table_display_name" {}

# security list web
variable "sl_egress_destination_web" {}
variable "sl_egress_protocol_web" {}
variable "sl_ingress_source_web" {}
variable "sl_ingress_protocol_web" {}
variable "sl_ingress_tcp_dest_port_max_web" {}
variable "sl_ingress_tcp_dest_port_min_web" {}
variable "sl_display_name_web" {}

# security list db
variable "sl_egress_destination_db" {}
variable "sl_egress_protocol_db" {}
variable "sl_ingress_source_db" {}
variable "sl_ingress_protocol_db" {}
variable "sl_ingress_tcp_dest_port_max_db1" {}
variable "sl_ingress_tcp_dest_port_min_db1" {}
variable "sl_ingress_tcp_dest_port_max_db2" {}
variable "sl_ingress_tcp_dest_port_min_db2" {}
variable "sl_display_name_db" {}

# subnet web
variable "web_subnet_cidr_block" {}
variable "web_subnet_display_name" {}
variable "web_subnet_dns_label" {}
variable "web_subnet_prohibit_public_ip_on_vnic" {}

# subnet db
variable "db_subnet_cidr_block" {}
variable "db_subnet_display_name" {}
variable "db_subnet_dns_label" {}
variable "db_subnet_prohibit_public_ip_on_vnic" {}
```

### 変数の代入
上で宣言した変数に対して、値を代入するファイルを作ります。

```shell:network.auto.tfvars
# VCN
vcn_cidr_block   = "192.168.100.0/24"
vcn_display_name = "Qiita-VCN"
vcn_dns_label    = "QiitaVCN"

# internet gateway
internet_gateway_display_name = "QiitaIGW"

# route table
route_table_display_name = "Qiita_RT_Web"

# security list web
sl_egress_destination_web  = "192.168.100.0/24"
sl_egress_protocol_web     = "6"
sl_ingress_source_web      = "x.x.x.x/32"
sl_ingress_protocol_web    = "6"
sl_ingress_tcp_dest_port_max_web = "22"
sl_ingress_tcp_dest_port_min_web = "22" 
sl_display_name_web   = "Qiita_SL_Web"

# security list db
sl_egress_destination_db   = "192.168.100.0/24"
sl_egress_protocol_db      = "6"
sl_ingress_source_db       = "192.168.100.0/24"
sl_ingress_protocol_db     = "6"
sl_ingress_tcp_dest_port_max_db1   = "22"
sl_ingress_tcp_dest_port_min_db1   = "22"
sl_ingress_tcp_dest_port_max_db2   = "1521"
sl_ingress_tcp_dest_port_min_db2   = "1521"
sl_display_name_db     = "Qiita_SL_DB"

# subnet web
web_subnet_cidr_block     = "192.168.100.32/27"
web_subnet_display_name   = "Qiita_Subnet_Web"
web_subnet_dns_label      = "qiitasubnetweb"
web_subnet_prohibit_public_ip_on_vnic   = "false"

# subnet db
db_subnet_cidr_block      = "192.168.100.0/27"
db_subnet_display_name    = "Qiita_Subnet_DB"
db_subnet_dns_label       = "qiitasubnetdb"
db_subnet_prohibit_public_ip_on_vnic   = "true"
```


## 2-4.Compute作成用のtf
リソースを作成する場合の構文は以下から確認
https://www.terraform.io/docs/providers/oci/index.html

### Computeリソース
availability domainとsubnetについて上で定義したものを指定。

```json:compute.tf
resource "oci_core_instance" "test_instance" {
    count = "${var.NumInstances}"
    availability_domain = "${oci_core_subnet.test_web_subnet.availability_domain}"    # 必須
    compartment_id = "${var.compartment_ocid}"                     # 必須
    shape = "${var.instance_shape}"                                # 必須
    display_name = "${var.instance_display_name}"
    create_vnic_details {
        subnet_id = "${oci_core_subnet.test_web_subnet.id}"
    }
    source_details {
        source_id = "${var.instance_image_ocid[var.region]}"
        source_type = "image"
    }
    metadata {
        ssh_authorized_keys = "${var.ssh_public_key}"
    }
    defined_tags = "${map("${var.defined_tag}",  "${var.defined_tag_value}")}"
}

```

### 変数の宣言
OS imageのデフォルトとしてOracle Linux 7.6を指定。ただし、リージョンによってOCIDが異なるため、map型で定義。各OCIDについてはマニュアルから確認。
https://docs.cloud.oracle.com/iaas/images/image/7d31cb1d-f31f-450c-95c4-0539776c3dcf/


```json:instance-var.tf
variable "NumInstances"{
  default = "1"
}
variable "instance_shape" {
  default = "VM.Standard2.1"
}
variable "instance_display_name" {}
variable "instance_image_ocid" {
  type = "map"
  default = {
    us-phoenix-1 = "ocid1.image.oc1.phx.aaaaaaaadjnj3da72bztpxinmqpih62c2woscbp6l3wjn36by2cvmdhjub6a"
    us-ashburn-1   = "ocid1.image.oc1.iad.aaaaaaaawufnve5jxze4xf7orejupw5iq3pms6cuadzjc7klojix6vmk42va"
    eu-frankfurt-1 = "ocid1.image.oc1.eu-frankfurt-1.aaaaaaaagbrvhganmn7awcr7plaaf5vhabmzhx763z5afiitswjwmzh7upna"
    uk-london-1    = "ocid1.image.oc1.uk-london-1.aaaaaaaajwtut4l7fo3cvyraate6erdkyf2wdk5vpk6fp6ycng3dv2y3ymvq"
  }
}
variable "ssh_public_key" {}
```

### 変数の代入

```shell:instance.auto.tfvars
NumInstances   = "1"
instance_shape = "VM.Standard2.1"
instance_display_name = "WebInstance"
ssh_public_key = "XXXXXX"  # ファイルではなく値(省略するとterraform実行時に対話的に入力)
```

## 2-5.DbaaS作成用のtf
リソースを作成する場合の構文は以下から確認
https://www.terraform.io/docs/providers/oci/index.html

### DBaaSリソース
availability domainとsubnetについて上で定義したものを指定。
`Error: oci_database_db_system.test_db_system: ssh_public_keys: should be a list`が出たので、ssh_public_keys のみリストとして"[]"で囲いました。

```json:dbaas.tf
resource "oci_database_db_system" "test_db_system" {
    availability_domain = "${oci_core_subnet.test_db_subnet.availability_domain}"  # 必須
    compartment_id = "${var.compartment_ocid}"                         # 必須
    database_edition = "${var.db_system_database_edition}"             # 必須
    db_home {           # 必須
        database {
            admin_password = "${var.db_system_db_home_database_admin_password}"
            db_name = "${var.db_system_db_home_database_db_name}"
        }
        db_version = "${var.db_system_db_home_db_version}"
        display_name = "${var.db_system_db_home_display_name}"
    }
    hostname = "${var.db_system_hostname}"
    shape = "${var.db_system_shape}"
    ssh_public_keys = ["${var.db_system_ssh_public_keys}"]
    subnet_id = "${oci_core_subnet.test_db_subnet.id}"
    cpu_core_count = "${var.db_system_cpu_core_count}"
    display_name = "${var.db_system_display_name}"
    node_count = "${var.db_system_node_count}"
}
```

### 変数の宣言
```json:DbaaS-var.tf
variable "db_system_database_edition" {
  default = "ENTERPRISE_EDITION"
}
variable "db_system_db_home_database_admin_password" {}
variable "db_system_db_home_database_db_name" {}
variable "db_system_db_home_db_version" {}
variable "db_system_db_home_display_name" {}
variable "db_system_display_name" {}
variable "db_system_hostname" {}
variable "db_system_shape" {
  default = "VM.Standard2.1"
}
variable "db_system_ssh_public_keys" {}
variable "db_system_cpu_core_count" {
  default = "2"
}
variable "db_system_node_count" {
  default = "1"
}
```

### 変数の代入

```shell:DbaaS.auto.tfvars
db_system_db_home_database_admin_password   = "ZjTF2TN_48#SpP6"
db_system_db_home_database_db_name = "ORCL"
db_system_db_home_db_version = "12.1.0.2"
db_system_db_home_display_name = "Qiitadbhome"
db_system_display_name =  "Qiitadbsystem"
db_system_hostname = "test-db-instance"
db_system_ssh_public_keys = "xxxxxxx"  # ファイルではなく値(省略するとterraform実行時に対話的に入力)
```

## 2-6.Output用のtf
terraform applyを行った際に、変数の値を出力させることができます。例えばcomputeインスタンス作成時にprivate ipを指定しなかった場合、自動的に割り振られるので、実際に割り振られたアドレスを確認するためにoutputブロックで出力させる内容を定義します。


```json:output.tf
output "InstancePrivateIP" {
  value = ["${oci_core_instance.test_instance.*.private_ip}"]
}

output "InstancePublicIP" {
  value = ["${oci_core_instance.test_instance.*.public_ip}"]
}
```



# 3.Terraform実行

## 3-1. initの実行

`> terraform init`
init時にconfigファイルのパースが行われ、不備がある場合はエラーとなります。
問題がなければ、ociのpluginがダウンロード・インストールされます。


```shell
PS C:\Terraform\oci> terraform init

Initializing provider plugins...
- Checking for available provider plugins on https://releases.hashicorp.com...
- Downloading plugin for provider "oci" (3.14.1)...

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

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

* provider.oci: version = "~> 3.14"

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

## 3-2. planの実行

`> terraform plan -out=<plan名>`

<details><summary>実行結果（長いので折りたたんでます）</summary><div>

```shell
PS C:\Terraform\oci> terraform plan -out=testplan
Refreshing Terraform state in-memory prior to plan...
The refreshed state will be used to calculate this plan, but will not be
persisted to local or remote state storage.

data.oci_identity_availability_domains.ADs: Refreshing state...

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

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

Terraform will perform the following actions:

  + oci_core_instance.test_instance
      id:                                                                  <computed>
      availability_domain:                                                 "xxxx:PHX-AD-1"
      boot_volume_id:                                                      <computed>
      compartment_id:                                                      "ocid1.compartment.oc1..xxxxxxxxxxx"
      create_vnic_details.#:                                               "1"
      create_vnic_details.0.assign_public_ip:                              "true"
      create_vnic_details.0.display_name:                                  <computed>
      create_vnic_details.0.freeform_tags.%:                               <computed>
      create_vnic_details.0.private_ip:                                    <computed>
      create_vnic_details.0.skip_source_dest_check:                        <computed>
      create_vnic_details.0.subnet_id:                                     "${oci_core_subnet.test_web_subnet.id}"
      defined_tags.${var.defined_tag}:                                     <computed>
      defined_tags.%:                                                      "1"
      display_name:                                                        "WebInstance"
      fault_domain:                                                        <computed>
      freeform_tags.%:                                                     <computed>
      image:                                                               <computed>
      ipxe_script:                                                         <computed>
      is_pv_encryption_in_transit_enabled:                                 <computed>
      launch_mode:                                                         <computed>
      launch_options.#:                                                    <computed>
      metadata.%:                                                          "1"
      metadata.ssh_authorized_keys:                                        "xxxxxxx"
      private_ip:                                                          <computed>
      public_ip:                                                           <computed>
      region:                                                              <computed>
      shape:                                                               "VM.Standard2.1"
      source_details.#:                                                    "1"
      source_details.0.boot_volume_size_in_gbs:                            <computed>
      source_details.0.kms_key_id:                                         <computed>
      source_details.0.source_id:                                          "ocid1.image.oc1.phx.xxxxxxxxxxx"
      source_details.0.source_type:                                        "image"
      state:                                                               <computed>
      subnet_id:                                                           <computed>
      time_created:                                                        <computed>
      time_maintenance_reboot_due:                                         <computed>

  + oci_core_internet_gateway.test_internet_gateway
      id:                                                                  <computed>
      compartment_id:                                                      "ocid1.compartment.oc1..xxxxxxxxxxx"
      defined_tags.${var.defined_tag}:                                     <computed>
      defined_tags.%:                                                      "1"
      display_name:                                                        "QiitaIGW"
      enabled:                                                             "true"
      freeform_tags.%:                                                     <computed>
      state:                                                               <computed>
      time_created:                                                        <computed>
      time_modified:                                                       <computed>
      vcn_id:                                                              "${oci_core_vcn.test_vcn.id}"

  + oci_core_route_table.test_route_table
      id:                                                                  <computed>
      compartment_id:                                                      "ocid1.compartment.oc1..xxxxxxxxxxx"
      defined_tags.${var.defined_tag}:                                     <computed>
      defined_tags.%:                                                      "1"
      display_name:                                                        "Qiita_RT_Web"
      freeform_tags.%:                                                     <computed>
      route_rules.#:                                                       "1"
      route_rules.~173963605.cidr_block:                                   <computed>
      route_rules.~173963605.destination:                                  "0.0.0.0/0"
      route_rules.~173963605.destination_type:                             <computed>
      route_rules.~173963605.network_entity_id:                            "${oci_core_internet_gateway.test_internet_gateway.id}"
      state:                                                               <computed>
      time_created:                                                        <computed>
      time_modified:                                                       <computed>
      vcn_id:                                                              "${oci_core_vcn.test_vcn.id}"

  + oci_core_security_list.test_security_list_db
      id:                                                                  <computed>
      compartment_id:                                                      "ocid1.compartment.oc1..xxxxxxxxxxx"
      defined_tags.${var.defined_tag}:                                     <computed>
      defined_tags.%:                                                      "1"
      display_name:                                                        "Qiita_SL_DB"
      egress_security_rules.#:                                             "1"
      egress_security_rules.4286408791.destination:                        "192.168.100.0/24"
      egress_security_rules.4286408791.destination_type:                   <computed>
      egress_security_rules.4286408791.icmp_options.#:                     "0"
      egress_security_rules.4286408791.protocol:                           "6"
      egress_security_rules.4286408791.stateless:                          "false"
      egress_security_rules.4286408791.tcp_options.#:                      "0"
      egress_security_rules.4286408791.udp_options.#:                      "0"
      freeform_tags.%:                                                     <computed>
      ingress_security_rules.#:                                            "2"
      ingress_security_rules.2488673465.icmp_options.#:                    "0"
      ingress_security_rules.2488673465.protocol:                          "6"
      ingress_security_rules.2488673465.source:                            "192.168.100.0/24"
      ingress_security_rules.2488673465.source_type:                       <computed>
      ingress_security_rules.2488673465.stateless:                         "false"
      ingress_security_rules.2488673465.tcp_options.#:                     "1"
      ingress_security_rules.2488673465.tcp_options.0.max:                 "1521"
      ingress_security_rules.2488673465.tcp_options.0.min:                 "1521"
      ingress_security_rules.2488673465.tcp_options.0.source_port_range.#: "0"
      ingress_security_rules.2488673465.udp_options.#:                     "0"
      ingress_security_rules.3060896391.icmp_options.#:                    "0"
      ingress_security_rules.3060896391.protocol:                          "6"
      ingress_security_rules.3060896391.source:                            "192.168.100.0/24"
      ingress_security_rules.3060896391.source_type:                       <computed>
      ingress_security_rules.3060896391.stateless:                         "false"
      ingress_security_rules.3060896391.tcp_options.#:                     "1"
      ingress_security_rules.3060896391.tcp_options.0.max:                 "22"
      ingress_security_rules.3060896391.tcp_options.0.min:                 "22"
      ingress_security_rules.3060896391.tcp_options.0.source_port_range.#: "0"
      ingress_security_rules.3060896391.udp_options.#:                     "0"
      state:                                                               <computed>
      time_created:                                                        <computed>
      vcn_id:                                                              
"${oci_core_vcn.test_vcn.id}"

  + oci_core_security_list.test_security_list_web
      id:                                                                  <computed>
      compartment_id:                                                      "ocid1.compartment.oc1..xxxxxxxxxxx"
      defined_tags.${var.defined_tag}:                                     <computed>
      defined_tags.%:                                                      "1"
      display_name:                                                        "Qiita_SL_Web"
      egress_security_rules.#:                                             "1"
      egress_security_rules.4286408791.destination:                        "192.168.100.0/24"
      egress_security_rules.4286408791.destination_type:                   <computed>
      egress_security_rules.4286408791.icmp_options.#:                     "0"
      egress_security_rules.4286408791.protocol:                           "6"
      egress_security_rules.4286408791.stateless:                          "false"
      egress_security_rules.4286408791.tcp_options.#:                      "0"
      egress_security_rules.4286408791.udp_options.#:                      "0"
      freeform_tags.%:                                                     <computed>
      ingress_security_rules.#:                                            "1"
      ingress_security_rules.47193274.icmp_options.#:                      "0"
      ingress_security_rules.47193274.protocol:                            "6"
      ingress_security_rules.47193274.source:                              "x.x.x.x/x"
      ingress_security_rules.47193274.source_type:                         <computed>
      ingress_security_rules.47193274.stateless:                           "false"
      ingress_security_rules.47193274.tcp_options.#:                       "1"
      ingress_security_rules.47193274.tcp_options.0.max:                   "22"
      ingress_security_rules.47193274.tcp_options.0.min:                   "22"
      ingress_security_rules.47193274.tcp_options.0.source_port_range.#:   "0"
      ingress_security_rules.47193274.udp_options.#:                       "0"
      state:                                                               <computed>
      time_created:                                                        <computed>
      vcn_id:                                                              "${oci_core_vcn.test_vcn.id}"

  + oci_core_subnet.test_db_subnet
      id:                                                                  <computed>
      availability_domain:                                                 "xxxx:PHX-AD-1"
      cidr_block:                                                          "192.168.100.0/27"
      compartment_id:                                                      "ocid1.compartment.oc1..xxxxxxxxxxx"
      defined_tags.${var.defined_tag}:                                     <computed>
      defined_tags.%:                                                      "1"
      dhcp_options_id:                                                     <computed>
      display_name:                                                        "Qiita_Subnet_DB"
      dns_label:                                                           "qiitasubnetdb"
      freeform_tags.%:                                                     <computed>
      prohibit_public_ip_on_vnic:                                          "true"
      route_table_id:                                                      <computed>
      security_list_ids.#:                                                 <computed>
      state:                                                               <computed>
      subnet_domain_name:                                                  <computed>
      time_created:                                                        <computed>
      vcn_id:                                                              "${oci_core_vcn.test_vcn.id}"
      virtual_router_ip:                                                   <computed>
      virtual_router_mac:                                                  <computed>

  + oci_core_subnet.test_web_subnet
      id:                                                                  <computed>
      availability_domain:                                                 "xxxx:PHX-AD-1"
      cidr_block:                                                          "192.168.100.32/27"
      compartment_id:                                                      "ocid1.compartment.oc1..xxxxxxxxxxx"
      defined_tags.${var.defined_tag}:                                     <computed>
      defined_tags.%:                                                      "1"
      dhcp_options_id:                                                     <computed>
      display_name:                                                        "Qiita_Subnet_Web"
      dns_label:                                                           "qiitasubnetweb"
      freeform_tags.%:                                                     <computed>
      prohibit_public_ip_on_vnic:                                          "false"
      route_table_id:                                                      "${oci_core_route_table.test_route_table.id}"
      security_list_ids.#:                                                 <computed>
      state:                                                               <computed>
      subnet_domain_name:                                                  <computed>
      time_created:                                                        <computed>
      vcn_id:                                                              "${oci_core_vcn.test_vcn.id}"
      virtual_router_ip:                                                   <computed>
      virtual_router_mac:                                                  <computed>

  + oci_core_vcn.test_vcn
      id:                                                                  <computed>
      cidr_block:                                                          "192.168.100.0/24"
      compartment_id:                                                      "ocid1.compartment.oc1..xxxxxxxxxxx"
      default_dhcp_options_id:                                             <computed>
      default_route_table_id:                                              <computed>
      default_security_list_id:                                            <computed>
      defined_tags.${var.defined_tag}:                                     <computed>
      defined_tags.%:                                                      "1"
      display_name:                                                        "Qiita-VCN"
      dns_label:                                                           "QiitaVCN"
      freeform_tags.%:                                                     <computed>
      state:                                                               <computed>
      time_created:                                                        <computed>
      vcn_domain_name:                                                     <computed>

  + oci_database_db_system.test_db_system
      id:                                                                  <computed>
      availability_domain:                                                 "xxxx:PHX-AD-1"
      backup_subnet_id:                                                    <computed>
      cluster_name:                                                        <computed>
      compartment_id:                                                      "ocid1.compartment.oc1..xxxxxxxxxxx"
      cpu_core_count:                                                      "2"
      data_storage_percentage:                                             <computed>
      data_storage_size_in_gb:                                             <computed>
      database_edition:                                                    "ENTERPRISE_EDITION"
      db_home.#:                                                           "1"
      db_home.0.database.#:                                                "1"
      db_home.0.database.0.admin_password:                                 <sensitive>
      db_home.0.database.0.backup_id:                                      <computed>
      db_home.0.database.0.backup_tde_password:                            <computed>
      db_home.0.database.0.character_set:                                  <computed>
      db_home.0.database.0.db_backup_config.#:                             <computed>
      db_home.0.database.0.db_name:                                        "ORCL"
      db_home.0.database.0.db_workload:                                    <computed>
      db_home.0.database.0.ncharacter_set:                                 <computed>
      db_home.0.database.0.pdb_name:                                       <computed>
      db_home.0.db_version:                                                "12.1.0.2"
      db_home.0.display_name:                                              "Qiitadbhome"
      disk_redundancy:                                                     <computed>
      display_name:                                                        "Qiitadbsystem"
      domain:                                                              <computed>
      freeform_tags.%:                                                     <computed>
      hostname:                                                            "test-db-instance"
      last_patch_history_entry_id:                                         <computed>
      license_model:                                                       <computed>
      lifecycle_details:                                                   <computed>
      listener_port:                                                       <computed>
      node_count:                                                          "1"
      reco_storage_size_in_gb:                                             <computed>
      scan_dns_record_id:                                                  <computed>
      scan_ip_ids.#:                                                       <computed>
      shape:                                                               "VM.Standard2.1"
      sparse_diskgroup:                                                    <computed>
      ssh_public_keys.#:                                                   "1"
      ssh_public_keys.0:                                                   "xxxxxxxx"
      state:                                                               <computed>
      subnet_id:                                                           "${oci_core_subnet.test_db_subnet.id}"
      time_created:                                                        <computed>
      version:                                                             <computed>
      vip_ids.#:                                                           <computed>


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

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

This plan was saved to: testplan

To perform exactly these actions, run the following command to apply:
    terraform apply "testplan"
```
</div></details>


## 3-3. applyの実行

`> terraform apply "<plan名>"`

planに従ってリソースを作成します。

```shell
PS C:\Terraform\oci> terraform apply "testplan"
# NW系のリソースが先に作られます（勝手に順番は制御してくれる）
oci_core_vcn.test_vcn: Creating...
  cidr_block:               "" => "192.168.100.0/24"

(中略)

# computeとdbaasインスタンスが同時に作成されてます
oci_database_db_system.test_db_system: Still creating... (10s elapsed)
oci_core_instance.test_instance: Still creating... (10s elapsed)
oci_database_db_system.test_db_system: Still creating... (20s elapsed)
oci_core_instance.test_instance: Still creating... (20s elapsed)
oci_database_db_system.test_db_system: Still creating... (30s elapsed)
oci_core_instance.test_instance: Still creating... (30s elapsed)

(中略)

oci_database_db_system.test_db_system: Still creating... (1m41s elapsed)
oci_core_instance.test_instance: Still creating... (1m40s elapsed)
oci_core_instance.test_instance: Creation complete after 1m41s (ID: ocid1.instance.oc1.phx.xxxxxxxxx)
oci_database_db_system.test_db_system: Still creating... (1m51s elapsed)
oci_database_db_system.test_db_system: Still creating... (2m1s elapsed)

(中略)

oci_database_db_system.test_db_system: Still creating... (1h13m44s elapsed)
oci_database_db_system.test_db_system: Still creating... (1h13m54s elapsed)
oci_database_db_system.test_db_system: Creation complete after 1h13m57s (ID: ocid1.dbsystem.oc1.phx.xxxxxxxx)

Apply complete! Resources: 9 added, 0 changed, 0 destroyed.

# output.tfで定義した部分
Outputs:

InstancePrivateIP = [
    192.168.100.34
]
InstancePublicIP = [
    xxx.xxx.xxx.xxx
]
```

`> terraform state list`
リストして作成したリソースを確認。

```
PS C:\Terraform\oci> terraform state list
oci_core_instance.test_instance
oci_core_internet_gateway.test_internet_gateway
oci_core_route_table.test_route_table
oci_core_security_list.test_security_list_db
oci_core_security_list.test_security_list_web
oci_core_subnet.test_db_subnet
oci_core_subnet.test_web_subnet
oci_core_vcn.test_vcn
oci_database_db_system.test_db_system
oci_identity_availability_domains.ADs
oci_identity_tag_namespaces.test_tag_namespaces
```

## 3-4. destroyの実行

`> terraform destroy`
これがかなり便利。terraformで作ったものは、いっぺんに消せる。

```shell
PS C:\Terraform\oci> terraform destroy
oci_core_vcn.test_vcn: Refreshing state... (ID: ocid1.vcn.oc1.phx.xxxxxxxxx)
data.oci_identity_availability_domains.ADs: Refreshing state...
data.oci_identity_tag_namespaces.test_tag_namespaces: Refreshing state...
oci_core_security_list.test_security_list_web: Refreshing state... (ID: ocid1.securitylist.oc1.phx.xxxxxxxxx)
oci_core_internet_gateway.test_internet_gateway: Refreshing state... (ID: ocid1.internetgateway.oc1.phx.xxxxxxxxx)
oci_core_security_list.test_security_list_db: Refreshing state... (ID: ocid1.securitylist.oc1.phx.xxxxxxxxx)
oci_core_subnet.test_db_subnet: Refreshing state... (ID: ocid1.subnet.oc1.phx.xxxxxxxxx)
oci_core_route_table.test_route_table: Refreshing state... (ID: ocid1.routetable.oc1.phx.xxxxxxxxx)
oci_database_db_system.test_db_system: Refreshing state... (ID: ocid1.dbsystem.oc1.phx.xxxxxxxxx)
oci_core_subnet.test_web_subnet: Refreshing state... (ID: ocid1.subnet.oc1.phx.xxxxxxxxx)
oci_core_instance.test_instance: Refreshing state... (ID: ocid1.instance.oc1.phx.xxxxxxxxx)

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

# 削除されるリソース一覧が表示される
Terraform will perform the following actions:

  - oci_core_instance.test_instance

  - oci_core_internet_gateway.test_internet_gateway

  - oci_core_route_table.test_route_table

  - oci_core_security_list.test_security_list_db

  - oci_core_security_list.test_security_list_web

  - oci_core_subnet.test_db_subnet

  - oci_core_subnet.test_web_subnet

  - oci_core_vcn.test_vcn

  - oci_database_db_system.test_db_system


Plan: 0 to add, 0 to change, 9 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 # <--自分で入力

oci_core_instance.test_instance: Destroying... (ID: ocid1.instance.oc1.phx.xxxxxxxxx)
oci_database_db_system.test_db_system: Destroying... (ID: ocid1.dbsystem.oc1.phx.xxxxxxxxx)
oci_core_instance.test_instance: Still destroying... (ID: ocid1.instance.oc1.phx.xxxxxxxxx, 10s elapsed)
oci_database_db_system.test_db_system: Still destroying... (ID: ocid1.dbsystem.oc1.phx.xxxxxxxxx, 10s elapsed)
oci_core_instance.test_instance: Still destroying... (ID: ocid1.instance.oc1.phx.xxxxxxxxx, 20s elapsed)

（中略）

oci_database_db_system.test_db_system: Destruction complete after 10m4s
oci_core_subnet.test_db_subnet: Destroying... (ID: ocid1.subnet.oc1.phx.xxxxxxxxx)
oci_core_subnet.test_db_subnet: Destruction complete after 2s
oci_core_security_list.test_security_list_db: Destroying... (ID: ocid1.securitylist.oc1.phx.xxxxxxxxx)
oci_core_security_list.test_security_list_db: Destruction complete after 0s
oci_core_vcn.test_vcn: Destroying... (ID: ocid1.vcn.oc1.phx.xxxxxxxxx)
oci_core_vcn.test_vcn: Destruction complete after 2s

Destroy complete! Resources: 9 destroyed.
```

以上です。


tfファイルは一部マスクしつつ以下で公開。
https://github.com/feifeifo/terraformocitest


メモ
\- outputにdbaasのip出力させ忘れ
\- security listのポート指定をリストにしたいけど、出来ないっぽい