はじめに
本記事では、OCI Resource Manager で管理している既存の Terraform Stack に対して、Terraform 管理外の OCI リソースを取り込む方法についてまとめていきます。
OCI を運用していると、以下のように Terraform 管理外のリソースが存在するケースがあります。
- OCI コンソールから手動で作成したリソース
- OCI CLI から作成したリソース
- Terraform 導入以前から存在するリソース
- 一時的に手動作成したものの、後から Terraform 管理へ移行したいリソース
まず、本記事で頻出する Terraform の用語を簡単に整理します。
| 用語 | 概要 |
|---|---|
| Terraform Configuration | Terraform がインフラをどのような状態にするかを記述した .tf ファイル群 |
| Resource Block | Terraform Configuration の中で、個々のリソースを定義する部分 |
| Resource Address | Terraform 上で個々のリソースを識別する名前 |
| Terraform State | Terraform が管理しているリソースと、実際のクラウドリソースとの対応関係などを保持する状態情報 |
例えば、以下の Terraform Configuration があるとします。
.
├── main.tf
├── variables.tf
├── outputs.tf
└── network.tf
このうち、以下のような記述が Resource Block です。
resource "oci_core_vcn" "example" {
compartment_id = var.compartment_id
cidr_block = "10.0.0.0/16"
display_name = "example-vcn"
}
この Resource Block の Resource Address は以下になります。
oci_core_vcn.example
Terraform State では、この Resource Address と実際の OCI リソースが紐付けられています。
Terraform Configuration
↓
oci_core_vcn.example
Terraform State
↓
oci_core_vcn.example
↓
ocid1.vcn.oc1.ap-tokyo-1.xxxxx
OCI
↓
実際に存在するVCN
この関係を踏まえると、Terraform 管理外の既存 OCI リソースを Resource Manager の管理下へ取り込むには、下記の通り、両方を既存リソースに対応させる必要があります。
Terraform Configuration
+
Terraform State
Terraform Configuration には対象リソースの Resource Block を追加し、Terraform State にはその Resource Address と実際の OCI リソースを紐付けます。
Terraform 1.5 以降では、import block を利用することで、この既存リソースの紐付けを通常の Plan / Apply フローに組み込むことができます。
Resource Block
+
import block
↓
Plan
↓
Apply
↓
既存OCIリソースをTerraform Stateへ登録
OCI Resource Manager では Terraform 1.5.x がサポートされているため、この仕組みを Resource Manager の Plan / Apply Job に組み込むことができます。
今回の記事では、さらに既存 OCI リソースの Resource Block を一から記述する作業もできるだけ減らすため、Resource Discovery を利用します。
全体の流れは以下の通りです。
既存OCIリソース
↓
Resource Discovery
↓
Terraform Configuration生成
↓
既存Stack向けにResource Blockを調整
↓
import blockを追加
↓
Resource ManagerでPlan
↓
Resource ManagerでApply
↓
既存リソースをTerraform StateへImport
↓
再度Planして差分確認
なお、Resource Discovery の利用は必須ではありません。
既存リソースの Terraform Configuration は、例えば以下の方法でも用意できます。
- Resource Block を手動で記述する
- Cloud Shell 上で
importblock +terraform plan -generate-config-outを利用する - OCI Resource Discovery を利用する
本記事では、既存リソースの HCL 化をできるだけオフロードし、本番運用時にも同じ手順を利用しやすくするため、Resource Discovery を採用します。
本記事では以下についてまとめていきます。
- Terraform の
importblock について -
terraform importCLI との違い - Resource Manager に既存リソースを取り込む考え方
- Resource Discovery を利用した Terraform Configuration の生成
- 既存 Resource Manager Stack への Resource Block の追加
-
importblock の追加 - Resource Manager の Plan / Apply による Import
- Import 後の差分確認
Terraform の import について
Terraform では、既存リソースを Terraform 管理下へ取り込む方法として Import が用意されています。
Terraform 1.5 以降では、Terraform Configuration に import block を記述できます。
例えば、既存の OCI VCN を取り込む場合は以下のように記述します。
import {
to = oci_core_vcn.example
id = "ocid1.vcn.oc1.ap-tokyo-1.xxxxx"
}
Import 先となる Resource Block も用意します。
resource "oci_core_vcn" "example" {
compartment_id = var.compartment_id
cidr_block = "10.0.0.0/16"
display_name = "example-vcn"
}
この状態で Plan を実行すると、Terraform は新規作成ではなく、既存リソースを Import する処理として認識します。
Plan: 1 to import, 0 to add, 0 to change, 0 to destroy.
その後 Apply を実行することで、既存リソースが Terraform State に登録されます。
Resource Block
+
import block
↓
terraform plan
↓
terraform apply
↓
Terraform StateへImport
Terraform の公式ドキュメントでも、以下の流れが案内されています。
- Resource Block を定義
-
importblock を追加 - Plan で Import 内容を確認
- Apply で State へ Import
terraform import CLI との違い
従来から利用できる terraform import CLI でも、既存リソースを Terraform State に取り込めます。
例えば以下の通りです。
terraform import \
oci_core_vcn.example \
ocid1.vcn.oc1.ap-tokyo-1.xxxxx
terraform import を実行すると、Terraform CLI が Terraform State を更新し、
oci_core_vcn.example
↓
ocid1.vcn.oc1.ap-tokyo-1.xxxxx
という対応関係を State に登録します。
そのため、Terraform State ファイルを手作業で編集する必要はありません。
通常の Terraform 環境であれば、
terraform import
↓
Terraform CLIがStateを更新
という処理になります。
一方、OCI Resource Manager で管理している Stack に対して terraform import CLI を利用する場合は、少し手順が増えます。
Resource Manager が管理している Terraform State は Resource Manager 側に保持されているため、一度 State を取得し、その State に対して terraform import を実行したうえで、更新された State を Resource Manager へ戻す必要があります。
Resource Manager
↓
Terraform Stateを取得
↓
terraform.tfstate
↓
terraform import
↓
Terraform CLIがStateを更新
↓
更新済みterraform.tfstate
↓
Resource Managerへ戻す
つまり、terraform import CLI を利用する場合のポイントは、
Stateを手作業で編集する
ことではなく、
Resource Managerが保持しているStateを
一度外部へ取得して更新し、
再度Resource Managerへ戻す必要がある
という点です。
一方、Terraform 1.5 以降の import block を利用する場合は、Import 操作を Terraform Configuration として記述できます。
import {
to = oci_core_vcn.example
id = "ocid1.vcn.oc1.ap-tokyo-1.xxxxx"
}
これを Resource Block と一緒に Resource Manager Stack へ追加し、
Resource Manager Plan
↓
Resource Manager Apply
↓
Terraform StateへImport
とすることで、Resource Manager の通常の Plan / Apply フローの中で既存リソースを State へ登録できます。
そのため、import block を利用する場合は、
Stateを取得
↓
terraform import
↓
更新したStateをResource Managerへ戻す
という State ファイルの持ち出し・再Importの工程が不要になります。
この点が、Resource Manager で import block を利用する大きなメリットです。
generate-config-out について
Terraform では、Resource Block を手動で作成せず、既存リソースから Terraform Configuration を生成することもできます。
まず import block を作成します。
import {
to = oci_core_vcn.example
id = "ocid1.vcn.oc1.ap-tokyo-1.xxxxx"
}
そのうえで以下を実行します。
terraform plan -generate-config-out=generated.tf
Terraform は Provider から既存リソースの情報を取得し、Resource Block を生成します。
ただし、生成された Configuration はそのまま本番コードとして利用できるとは限りません。
不要な属性の削除や Variable 化、既存 Resource への参照変更など、既存 Stack に合わせた調整が必要です。
OCIでTerraform Configurationを生成する方法
OCI で既存リソースの Terraform Configuration を用意する方法はいくつかあります。
手動で Resource Block を作成
OCI Provider のドキュメントを確認しながら Resource Block を作成する方法です。
リソース数や属性が少ない場合にはシンプルですが、複雑なリソースでは記述量が多くなります。
Cloud Shellで generate-config-out を利用
Cloud Shell 上で Terraform CLI を実行し、
terraform plan -generate-config-out=generated.tf
を利用する方法です。
Code Editor を利用して Terraform ファイルを編集することもできますが、Code Editor と Cloud Shell は別の機能です。
- Code Editor:ファイルを編集する環境
- Cloud Shell:CLI コマンドを実行するシェル環境
Code Editor から Cloud Shell のターミナルを起動することもできます。
Resource Discoveryを利用
OCI Resource Manager には、すでにデプロイされている OCI リソースから Terraform Configuration を生成する Resource Discovery が用意されています。
Resource Manager の公式ドキュメントでも、Terraform Configuration を用意する方法として Resource Discovery が案内されています。
今回 Resource Discovery を利用する理由
今回の検証では Resource Discovery を利用します。
Resource Discovery は必須ではありませんが、手動で作成した既存 OCI リソースを Terraform 管理へ移行する場合、Resource Block を一から記述する作業を減らせます。
今回 Resource Discovery を採用する理由は以下です。
- 既存 OCI リソースから Terraform Configuration を生成できる
- Resource Block を一から記述する必要がない
- 実際の OCI リソースの設定をベースに HCL を作成できる
- 既存リソースの HCL 化を OCI 側へオフロードできる
- 同様の Import 作業が発生した場合にも手順を再利用しやすい
なお、Resource Discovery は既存リソースをそのまま完全な Terraform コードへ変換する移行ツールではありません。
生成された Terraform Configuration は、既存 Stack に合わせて調整するためのベースとして利用します。
使用方法(デモ)
検証構成
検証構成図は以下の通りです。
なお、以下リソースは作成済みで進めていきます。
- OCI Resource Manager Stack
- Terraform 管理外で作成した OCI リソース
今回の検証では、Terraform 管理外となっているOCI リソースを、既存の Resource Manager Stack へ追加します。
実施する流れは以下です。
① Resource DiscoveryでTerraform Configurationを生成
↓
② Resource Blockを既存Stack向けに調整
↓
③ Resource Blockを既存Stackへ追加
↓
④ import blockを追加
↓
⑤ Resource ManagerでPlan
↓
⑥ Resource ManagerでApply
↓
⑦ 再度Planして差分確認
- 環境コードは以下 GitHub にあげてますので、よかったら覗いてみてください
Resource Discovery のセットアップ
まず、Terraform 管理下へ取り込みたい OCI リソースの Terraform Configuration を生成します。
今回の検証では、Mac のローカル環境から Resource Discovery を実行します。
Resource Discovery は OCI Terraform Provider の機能として提供されているため、Resource Discovery 実行用として OCI Terraform Provider をローカル環境へ配置します。
また、Resource Discovery は内部で Terraform CLI を利用するため、Terraform CLI が実行できることも前提となります。
早速、OCI Terraform Provider をセットアップしましょう。
MAC の CPU アーキテクチャは以下の通りです。
$ uname -m
arm64
そのため、Apple Silicon 向けの darwin_arm64 を利用します。
利用する OCI Terraform Provider の情報を変数に設定します。
OCI_PROVIDER_VERSION="9.3.0"
OCI_PROVIDER_OS="darwin"
OCI_PROVIDER_ARCH="arm64"
OCI_PROVIDER_TMP_DIR="/tmp/oci-provider"
ダウンロードする ZIP ファイルのパスを設定します。
OCI_PROVIDER_ZIP="${OCI_PROVIDER_TMP_DIR}/terraform-provider-oci_${OCI_PROVIDER_VERSION}_${OCI_PROVIDER_OS}_${OCI_PROVIDER_ARCH}.zip"
一時ディレクトリを作成します。
mkdir -p "${OCI_PROVIDER_TMP_DIR}"
OCI Terraform Provider をダウンロードします。
curl -L \
-o "${OCI_PROVIDER_ZIP}" \
"https://releases.hashicorp.com/terraform-provider-oci/${OCI_PROVIDER_VERSION}/terraform-provider-oci_${OCI_PROVIDER_VERSION}_${OCI_PROVIDER_OS}_${OCI_PROVIDER_ARCH}.zip"
ダウンロードした ZIP ファイルを展開します。
展開に成功した場合は、不要になった ZIP ファイルを削除します。
unzip \
"${OCI_PROVIDER_ZIP}" \
-d "${OCI_PROVIDER_TMP_DIR}" \
&& rm -f "${OCI_PROVIDER_ZIP}"
展開結果を確認します。
ls -l "${OCI_PROVIDER_TMP_DIR}"
展開した OCI Terraform Provider を /usr/local/bin 配下へ配置します。
sudo mv \
"${OCI_PROVIDER_TMP_DIR}"/terraform-provider-oci_* \
/usr/local/bin/
配置された OCI Terraform Provider のパスを変数に設定します。
OCI_PROVIDER_BIN=$(ls /usr/local/bin/terraform-provider-oci*)
取得結果を確認します。
echo "${OCI_PROVIDER_BIN}"
例えば、以下のようなパスが表示されます。
/usr/local/bin/terraform-provider-oci_v9.3.0
Resource Discovery の実行を簡略化するため、OCI Terraform Provider に対して tf-oci のシンボリックリンクを作成します。
sudo ln -sfn \
"${OCI_PROVIDER_BIN}" \
/usr/local/bin/tf-oci
作成したシンボリックリンクを確認します。
ls -l /usr/local/bin/tf-oci
必要に応じて OCI Terraform Provider に実行権限を付与します。
sudo chmod +x "${OCI_PROVIDER_BIN}"
Resource Discovery が利用できることを確認します。
Resource Discovery に対応している OCI サービスの一覧を表示します。
tf-oci -command=list_export_services
Resource Discovery に対応している Terraform Resource の一覧を表示します。
tf-oci -command=list_export_resources
list_export_services および list_export_resources は、OCI Provider が対応している Resource Discovery のサービス・リソース一覧を表示するため、この時点では OCI への認証設定は不要です。
実際に OCI 上のリソースを取得する -command=export を実行する際には、別途 OCI の認証設定が必要になります。
Terraform Configuration 生成
Resource Discovery のセットアップが完了したら、実際に OCI 上の既存リソースを検出します。
今回の検証では、OCI CLI のセッション認証を利用します。詳細は以下記事を参考にしてみてください。
今回は、Subnetをインポートします。
OCIDを変数に格納しておきます。
SUBNET_OCID="<subnet-ocid>"
OCI Terraform Provider では、Subnet は以下の Resource Type で管理します。
oci_core_subnet
Subnet は OCID を Import ID として利用できます。
Resource Discovery では複数の Terraform Configuration が生成されます。
そのため、別途出力先ディレクトリを作成しておくと安全です。
DISCOVERY_OUTPUT_DIR="$(pwd)/resource-discovery"
既存の出力が残っている場合に備えて、一度削除してから作成します。
rm -rf "${DISCOVERY_OUTPUT_DIR}" \
&& mkdir -p "${DISCOVERY_OUTPUT_DIR}"
続いて、Resource Discovery がセッション認証を利用できるように環境変数を設定します。
export OCI_AUTH="SecurityToken"
export OCI_CONFIG_FILE_PROFILE="ADMIN"
export TF_VAR_region="ap-tokyo-1"
プロファイル名及びリージョンは適宜変更してください
Resource Discovery では、-ids オプションを利用することで対象リソースを個別に指定できます。
-ids は以下の形式で指定します。
Terraform Resource Type:Resource ID
今回は Subnet のため、以下となります。
oci_core_subnet:<SUBNET_OCID>
準備が整ったので、以下コマンドを実行して Terraform Configuration を生成します。
tf-oci \
-command=export \
-ids="oci_core_subnet:${SUBNET_OCID}" \
-output_path="${DISCOVERY_OUTPUT_DIR}"
生成された Terraform Configuration の確認
Resource Discovery の実行が完了したら、生成された Terraform Configuration を確認します。
今回指定した出力先は以下です。
DISCOVERY_OUTPUT_DIR="$(pwd)/resource-discovery"
生成されたファイルを確認します。
find "${DISCOVERY_OUTPUT_DIR}" -type f
今回 Import 対象として指定した Subnet の Resource Block は、生成された resources.tf から確認できます。
cat "${DISCOVERY_OUTPUT_DIR}/resources.tf"
例えば、以下のような Resource Block が生成されます。
resource "oci_core_subnet" "export_subnet" {
cidr_block = "10.0.1.0/24"
compartment_id = "ocid1.compartment.oc1..xxxxx"
display_name = "private-subnet"
prohibit_public_ip_on_vnic = true
vcn_id = "ocid1.vcn.oc1.ap-tokyo-1.xxxxx"
}
生成される属性は、実際の Subnet の設定内容によって異なります。
Resource Block を既存 Stack 向けに調整
Resource Discovery で生成された Resource Block は、そのまま既存 Stack に追加するのではなく、既存 Stack の Terraform Configuration に合わせて調整します。
例えば、Resource Discovery では以下のように OCID が直接記述される場合があります。
resource "oci_core_subnet" "export_subnet" {
compartment_id = "ocid1.compartment.oc1..xxxxx"
vcn_id = "ocid1.vcn.oc1.ap-tokyo-1.xxxxx"
# ...
}
既存 Stack で Compartment や VCN を Variable や Resource Reference で管理している場合は、それに合わせて変更します。
resource "oci_core_subnet" "private" {
cidr_block = "10.0.1.0/24"
compartment_id = var.compartment_id
display_name = "private-subnet"
prohibit_public_ip_on_vnic = true
vcn_id = oci_core_vcn.main.id
}
このように、Resource Discovery で生成された Terraform Configuration は、完成したコードとしてそのまま利用するのではなく、既存 Stack に取り込むためのベースとして利用します。
主に以下を確認します。
- Resource Name
- Compartment の参照方法
- VCN の参照方法
- Route Table の参照方法
- Security List の参照方法
- Variable
- Local Values
- Tag
- Public / Private Subnet の設定
既存 Stack へ Resource Block を追加
調整した Subnet の Resource Block を、既存 Resource Manager Stack の Terraform Configuration に追加します。
例えば、以下のように main.tf へ追加します。
resource "oci_core_subnet" "private" {
cidr_block = "10.0.1.0/24"
compartment_id = var.compartment_id
display_name = "private-subnet"
prohibit_public_ip_on_vnic = true
vcn_id = oci_core_vcn.main.id
}
Resource Discovery で生成された Resource Block を既存 Stack へ転記したら、resource-discovery 配下の生成ファイルは不要になるため削除します。
rm -rf "${DISCOVERY_OUTPUT_DIR}"
- 今回は Terraform 実行ディレクトリ配下に
resource-discoveryディレクトリを作成しています - Resource Discovery の生成物をそのまま Terraform Configuration として利用しない場合は、既存 Stack へ必要な定義を転記した後に削除しておきます
この時点では、Terraform Configuration に Subnet の定義を追加しただけであり、Terraform State には既存 Subnet は登録されていません。
そのため、このまま Plan を実行すると、新規作成対象として認識される可能性があります。
Plan: 1 to add, 0 to change, 0 to destroy.
既存 Subnet を新規作成せずに Terraform 管理下へ取り込むため、次に import block を追加します。
import block を追加
既存 Subnet と、先ほど追加した Resource Block を紐付けます。
import {
to = oci_core_subnet.private
id = "ocid1.subnet.oc1.ap-tokyo-1.xxxxx"
}
to には、Import 先となる Resource Address を指定します。
oci_core_subnet.private
id には、既存 Subnet の OCID を指定します。
Subnet は OCID を Import ID として利用できます。
Resource Block と import block を合わせると、以下のようになります。
import {
to = oci_core_subnet.private
id = "ocid1.subnet.oc1.ap-tokyo-1.xxxxx"
}
resource "oci_core_subnet" "private" {
cidr_block = "10.0.1.0/24"
compartment_id = var.compartment_id
display_name = "private-subnet"
prohibit_public_ip_on_vnic = true
vcn_id = oci_core_vcn.main.id
}
Resource Manager Stack を更新
Resource Block と import block の追加が完了したら、Terraform Configuration を既存 Resource Manager Stack へ反映します。
Stack の更新後、通常通り Plan Job を実行します。
Plan では、対象の Subnet が新規作成ではなく Import 対象として認識されていることを確認します。
Plan: 1 to import, 0 to add, 0 to change, 0 to destroy.
意図しない変更や削除、再作成が発生していないことを確認したうえで、Apply Job を実行します。
Apply が完了すると、既存 Subnet が Resource Manager の Terraform State に登録されます。
oci_core_subnet.private
↓
ocid1.subnet.oc1.ap-tokyo-1.xxxxx
最後に再度 Plan を実行し、差分がないことを確認します。
Plan: 0 to add, 0 to change, 0 to destroy.
これで、Terraform 管理外で作成した既存 Subnet を、OCI Resource Manager の管理下へ取り込むことができました。
おわりに
今回は、OCI Resource Manager で管理している既存 Stack に対して、Terraform 管理外の OCI リソースを取り込む方法について整理しました。
今回のポイントは、Terraform 1.5 以降の import block を利用することで、既存リソースの Import を Resource Manager の通常の Plan / Apply フローに組み込めること です。
従来の terraform import CLI でも既存リソースを State へ登録できます。
この場合、State ファイルそのものを手作業で編集する必要はなく、terraform import が State を更新します。
ただし、Resource Manager で管理している Stack に対して利用する場合は、
Resource ManagerのStateを取得
↓
terraform import
↓
Terraform CLIがStateを更新
↓
更新したStateをResource Managerへ戻す
という State の持ち出し・再Importが必要になります。
一方、import block を利用すれば、
Resource Block
+
import block
↓
Resource Manager Plan
↓
Resource Manager Apply
という通常の Terraform ワークフローで既存リソースを管理下へ取り込めます。
さらに今回は Resource Discovery を組み合わせることで、
既存OCIリソース
↓
Resource Discovery
↓
Terraform Configuration生成
↓
既存Stack向けに調整
↓
import block
↓
Resource Manager Plan / Apply
という形にしました。
Resource Discovery は必須ではありませんが、手動作成された既存 OCI リソースを Terraform 化する際の HCL 記述を省力化できます。
既存環境を Terraform 管理へ移行する場合は、Import そのものだけでなく、Import 後に Terraform Configuration・Terraform State・実リソースの状態を一致させることが重要です。
Apply 前後の Plan を確認し、意図しない変更・削除・再作成が発生しないことを確認しながら進めるのがよいと思います。
🌟この記事が誰かの役に立てば幸いです!
また、ご質問やフィードバックもお待ちしています。
番外編
import block と terraform import の違い
| 項目 | import block | terraform import |
|---|---|---|
| Import の定義場所 | Terraform Configuration | CLI |
| Stateの更新 | Apply 時に Terraform が更新 |
terraform import 実行時に Terraform CLI が更新 |
| Stateの手動編集 | 不要 | 不要 |
| Plan で Import 内容を事前確認 | 可能 | Import 自体は CLI から直接実行 |
| Import 定義をGit管理 | 可能 | 不可 |
| Resource Managerでの利用 | 通常の Plan / Apply フローに載せられる | Resource Manager の State を取得・更新・再Importする必要がある |
Terraform Configurationを用意する方法の違い
| 方法 | 特徴 |
|---|---|
| 手動でResource Blockを作成 | シンプルだが、複雑なリソースでは記述負荷が高い |
Cloud Shell + -generate-config-out
|
Terraform標準機能で既存リソースからHCLを生成できる |
| Resource Discovery | OCIの既存リソースからTerraform Configurationを生成できる |
本記事では Resource Discovery を利用しています。
Code EditorとCloud Shellの違い
| 項目 | Code Editor | Cloud Shell |
|---|---|---|
| 主な用途 | コード編集 | CLI実行 |
| Terraformファイル編集 | ○ | ○ |
terraform コマンド実行 |
ターミナルとしてはCloud Shellを利用 | ○ |
oci コマンド実行 |
ターミナルとしてはCloud Shellを利用 | ○ |

