【完全自動化】 Terraform ＋ Ansible で OCI に VM / ADB 環境を構築

## はじめに

Oracle Cloud Infrastructure（OCI）では、アカウントを作成するだけで VM や Autonomous Database（ADB）を無料で使える **Always Free枠** が用意されています。

Webコンソールから手動操作でリソースを作成することもできますが、慣れてくると「繰り返し使う構成をコードで管理したい」「削除や再作成を自動化したい」と思うはずです。

そこで登場するのが、**Infrastructure as Code（IaC）** の考え方です。
IaC とは、**インフラ構成をコードとして定義・管理することで、インフラのプロビジョニングや変更を自動化する手法**です。人手による設定ミスを防ぎ、再現性や保守性を高めるメリットがあります。

本記事では、この IaC を実現するツールである **Terraform** と **Ansible** を組み合わせて、**ネットワーク構成から VM・ADB の作成、そして VM 上から SQL*Plus で ADB に接続するまでの一連の流れを自動化**します。

主な特徴は以下の通りです。
- Terraform：ネットワーク・VM・ADB の定義と作成
- Ansible：VM への Oracle クライアントインストール、キー生成（API/SSH）、接続確認
- Docker：実行環境をコンテナ化し、環境差異を吸収
- GitHub：Terraform / Ansible のコードはすべて **GitHub で公開済み**（詳細は後述）

※画面は2025年5月現在のものになります。

## 作成する構成の概要

本記事では、以下のような構成を OCI 上に自動で構築していきます。

- **仮想マシン（VM）**  
  | 項目             | 値                                               |  
  |-----------------|--------------------------------------------------|  
  | シェイプ（shape） | VM.Standard.E2.1.Micro（x86 / Always Free 対象） |  
  | OS              | Oracle Linux 8                                  |  
  | 接続方式         | パブリックサブネット経由で SSH のみ許可                 |
  <br>

- **Autonomous Database（ADB）**  
  | 項目               | 値                             |  
  |-------------------|--------------------------------|
  | ワークロード        | OLTP（ATP）                   |  
  | CPUコア数          | 1（Always Free 上限）          |  
  | ストレージサイズ     | 1TB（Always Free 上限）        |  
  | バージョン          | 23ai                         |  
  | Free Tier フラグ   | true                         |
  <br>

- **ネットワーク構成**  
  | 項目                    | 内容                                    |  
  |------------------------|-----------------------------------------|  
  | パブリック・サブネット     | VM 接続用に構成                           |  
  | インターネットゲートウェイ | VM に対してインターネット経由の SSH 接続を許可 |  
  | セキュリティリスト        | ポート 22（SSH）のみ許可                 |

  > VM → ADB への接続は、SQL*Plus を使用します（Oracle クライアントは Ansible で自動インストール）。

## 事前準備

Terraform / Ansible を実行する環境を整え、**OCI へのアクセスに必要な設定**を行います。

### 1. OCI アカウントの作成

アカウントをお持ちでない方は、下記を参考に作成してください。
[OCIアカウントの作成手順（別記事）](https://qiita.com/kitamura-db/items/970b714f9ca2f6cb7503)

### 2. Docker のインストール

本記事では、Terraform / Ansible を使った自動構築をローカル環境で実行するために、Docker 上に作成した実行環境（コンテナ）を使用します。  
そのために使用するのが **Docker Desktop**（Windows / Mac向けの Docker 実行環境）です。  
**Docker Desktop** は、コンテナ化されたアプリケーションをローカルで実行するためのツールです。  

以下の公式ドキュメントを参考に、お使いのOSに合わせてインストールしてください。

- **Macの場合**  
  [Docker Desktop for Mac インストール手順（公式ドキュメント）](https://docs.docker.com/desktop/install/mac-install/)   
  
- **Windowsの場合**  
  [Docker Desktop for Windows インストール手順（公式ドキュメント）](https://docs.docker.com/desktop/install/windows-install/)  
  
- **Linuxの場合**  
  **Docker Desktop** ではなく、 **Docker Engine** を直接インストールします。
  OSごとのパッケージマネージャ（apt や dnf など）を使ってインストールします。  
  [Docker Engine for Linux インストール手順（公式ドキュメント）](https://docs.docker.com/engine/install/)


### 3. GitHub からコードを取得

本記事で使用する Terraform / Ansible のコードは下記の GitHub に公開済みです。 

> `git clone` コマンドを使用するため、事前に Git をインストールしてください。
> お使いのOSに応じた手順は、以下の公式サイトをご参照ください。
> [Git公式サイト](https://git-scm.com)

```bash
git clone https://github.com/kitamura-db/oci-vm-adb-autobuild.git
cd oci-vm-adb-autobuild
```
  
本リポジトリには、Terraform / Ansible 実行に必要な環境をすべて含んだ Dockerfile が用意されています。
下記のコマンドで Docker コンテナを起動し、すぐに作業を始められます。

```bash
cd docker
docker-compose up -d --build
docker exec -it iac-cli bash
```

起動後、コンテナ内で `terraform` や `ansible` コマンドが使える状態になります。

### 4. OCI を操作するために必要な情報の取得とキーの生成（API/SSH）

Terraform で OCI を操作するには、以下の情報をあらかじめ取得、生成・登録しておく必要があります。

- テナンシーOCIDとリージョン識別子の取得
- ユーザーOCIDの取得
- API キーの生成と OCI への登録

これらは後ほど `provider.tf` で使用します。

- SSHキーの生成

これは後ほど `vm.tf` で使用します。

#### テナンシーOCIDとリージョン識別子の取得

ブラウザから https://cloud.oracle.com にアクセスします。
テナンシー名＋ユーザー名＋パスワードでログインします。
右上に人の形をしたアイコンがあるのでクリックします。

![ホーム画面](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4071373/cf80f070-81ab-4ad0-9a46-cdad6957f8ba.png)

「**テナンシ**」を選択します。

![プロファイル画面](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4071373/88f1163f-0e84-452d-808c-88de4bde7ddc.png)

テナンシーOCIDは「**コピー**」をクリックして、メモします。
リージョン識別子は、「**ホーム・リージョン**」が「**Tokyo**」であれば「`ap-tokyo-1`」、「**Osaka**」であれば「`ap-osaka-1`」をメモします。
これ以外が表示されている方は、[こちら](https://docs.oracle.com/ja-jp/iaas/Content/General/Concepts/regions.htm)を参考にリージョン識別子をメモしてください。

![テナンシ画面](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4071373/0c7f3f6b-cdc5-4672-a659-9a6706840f62.png)

#### ユーザーOCIDの取得

右上に人の形をしたアイコンがあるのでクリックします。

![ホーム画面](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4071373/0e2d7da1-4f3e-4699-88f0-548fc583a475.png)

「**ユーザー設定**」を選択します。

![プロファイル画面](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4071373/47dcd5ba-e9db-4a94-b723-1b0d2bece081.png)

「**コピー**」をクリックして、メモします。

![ユーザー画面](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4071373/b0ad7c1d-e02e-48f8-aa63-ddbd1a0efe16.png)


#### APIキーの生成とOCIへの登録

#### Ansible でAPI キーを生成

OCI 用の API キー（公開鍵・秘密鍵）を生成します。
今回は「**自動化**」をキーワードにしているのでキーの生成に「**Ansible**」を使用しました。
Docker コンテナ内で下記のコマンドで生成します。

```bash
cd /home/iacuser/ansible
ansible-playbook -i localhost, -c local oci_api_key_generate.yml
```

> Ansible の実行後には、必ず **PLAY RECAP の `failed=0`** を確認してください。
> ![Ansible実行ログ](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4071373/e1c63be5-7b89-416a-8412-1fc6153918b5.png)

生成後のディレクトリ構成です。

```plaintext
/home/iacuser/.config/oci
├── oci_api_key.pem          # ← 秘密鍵
└── oci_api_key_public.pem   # ← 公開鍵（これをOCIに登録します）
```

なお、今回は後続の手順で **SSHキーの生成も行う**ため、`.config` 配下でまとめて管理しやすいように、OCI のデフォルトディレクトリ（`~/.oci`）ではなく、**独自のディレクトリに生成しています**。

また、Docker 環境でキーを生成している都合上、生成された `oci_api_key_public.pem` は、**ホストと共有できるように `ansible` ディレクトリにも自動でコピー**されます。
次の「OCIへの公開鍵登録手順」で参照しやすくするための工夫です。

#### OCIへの公開鍵登録手順

以下の手順で、生成した公開鍵（`oci_api_key_public.pem`）をOCIに登録します。

1. OCIにログイン  
   ブラウザから https://cloud.oracle.com にアクセスします。  
   テナンシー名＋ユーザー名＋パスワードでログインします。  
   <br>

2. 右上のユーザーアイコン → 「ユーザー設定」へ  
   右上に人の形をしたアイコンがあるのでクリックします。  

   ![ホーム画面](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4071373/f08400ac-b43f-434c-9f22-8a4164626d7a.png)

   「**ユーザー設定**」を選択します。  

   ![プロファイル画面](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4071373/b9cf428c-9141-4989-bb1d-602df8f857b9.png)  
   <br>

3. APIキーの追加  
  リソースメニューの「**APIキー**」、「**APIキーの追加**」をクリックします。 

   ![APIキー一覧画面](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4071373/826a4dd9-bf49-4cff-9aaa-97fddd99b01e.png)  
   
   「**Ansible でAPI キーを生成**」で作成した「公開鍵」を登録します。  
   
   ![APIキー追加画面](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4071373/298e780f-d65c-419e-bb15-a4f53ef54d93.png)
  
   「**フィンガー・プリント**」をメモします。  
   Terraform の `provider.tf` に記述する値のうち、`fingerprint` に該当します。

   「**閉じる**」ボタンをクリックします。  

   ![APIキープレビュー画面](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4071373/45c805fa-aace-4bc2-9bbc-583327a9bcf2.png)

#### SSHキーの生成

Terraform で VM を作成する際に、あらかじめ公開鍵を指定しておくことで、SSH ログインが可能になります。
このキーも Ansible を使って自動生成します。
Docker コンテナ内で下記のコマンドで生成します。

```bash
cd /home/iacuser/ansible
ansible-playbook -i localhost, -c local ssh_key_generate.yml
```

生成後のディレクトリ構成です。

```plaintext
/home/iacuser/.config/ssh
├── id_rsa      # ← 秘密鍵
└── id_rsa.pub  # ← 公開鍵（これをVM作成時に登録します）
```

### 5. Terraform 用ファイル（provider.tf / terraform.tfvars）の準備

これまでに生成・メモした情報をもとに、`provider.tf` を構成します。

今回は、`provider.tf` に直接値を記述するのではなく、**変数定義ファイル `terraform.tfvars` に値を記述し、`provider.tf` から参照する形式**にしています。

#### provider.tf の定義例

```hcl
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.tfvars の作成

clone した terraform 直下に  `terraform.tfvars.sample` が含まれています。  
先ほど取得・生成した情報を元に修正し、`terraform.tfvars` という名前で保存してください。

#### OCI 接続情報

```hcl
tenancy_ocid        = "<Tenancy OCID>"
user_ocid           = "<User OCID>"
fingerprint         = "<API key fingerprint>"
region              = "<Region identifier (e.g., ap-tokyo-1)>"
```

#### ADB（Autonomous Database）関連の設定

```hcl
adb_admin_password  = "<Administrator password for ADB>"
adb_wallet_password = "<Wallet password (used for SQL*Plus connection)>"
```

それぞれのパスワードには要件があります。
- **ADB 管理者パスワードの要件**  
  パスワードは**12文字から30文字**とし、**大文字、小文字および数字をそれぞれ1つ以上**含める必要があります。パスワードに**二重引用符(")文字**または**ユーザー名 admin**を含めることはできません。  
  <br>

- **ウォレット（インスタンス・ウォレット）パスワードの要件**  
  パスワードは**8文字から60文字**とし、**英字と数字をそれぞれ1つ以上**含める必要があります。

なお、OCI のチュートリアルでは、両方のパスワードに **Welcome12345#** という値を使っているケースもあります。

## Terraform で VM＋ADB を作成

準備が整いましたので、Terraform でVM＋ADB を作成します。
作業はたったの3ステップ。これが Terraform の大きな魅力です。

まず、Docker コンテナ内で Terraform の作業ディレクトリに移動した上で、以下の3コマンドを順番に実行します。

```bash
cd /home/iacuser/terraform
terraform init
terraform plan
terraform apply
```

それぞれのコマンドで、**Error:** や **Warning:** が出ていないことを確認してください。

- `terraform init` : 初期化成功時は次のように表示されます  

  ![terraform init実行ログ](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4071373/65a92b3b-a387-4209-b204-18c2b2401fe5.png)

- `terraform plan` : 問題なければ次のように表示されます  
  
  ![terraform plan実行ログ](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4071373/4dfad223-2bab-446c-a04b-e6688653b741.png)

- `terraform apply` : 確認メッセージが表示されたら `yes` を入力して進めます  
  
  ![terraform apply確認メッセージ](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4071373/6cedf36c-3e17-42a1-a45a-18f53e42d20b.png)

- 完了後、次のように表示されれば作成成功です  

  ![terraform apply実行ログ](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4071373/3ba81c12-f0ad-424c-9cc8-1a0d905ad71b.png)

### Terraform の出力を保存

Ansible で VM や ADB に接続するため、Terraform で作成されたリソース情報（VM の IP アドレスや ADB 名など）を Ansible から読み取れるように JSON 形式でファイルに保存します。

```bash
terraform output -json > terraform_outputs.json
```

## Ansible を使って VM に SQL*Plus 実行環境を構築

続いて、Ansible を用いて VM に Oracle クライアント（SQL*Plus）をインストールし、ADB への接続確認まで自動化します。

> Ansible の実行後には、必ず **PLAY RECAP の `failed=0`** を確認してください。

### 1. OCI CLI セットアップ（OCI API を Ansible から操作するため）

Ansible の作業ディレクトリに移動した上で、下記のコマンドで OCI CLI をインストールし、接続に必要な設定を自動で行います。

```bash
cd /home/iacuser/ansible
ansible-playbook -i localhost, -c local oci_config_setup.yml
```

> **補足：**
> このプレイブックでは、OCI CLI の設定を .bashrc に追記しています。これは今後 OCI CLI を利用する際に毎回環境変数を再設定しなくて済むようにするためのものであり、本記事内の後続処理には直接関係しません。

### 2. ADB Wallet ダウンロード（ADB 接続に必要な証明書を取得）

OCI CLI を使って、ADB 接続に必要な**インスタンス・ウォレット（証明書）** をダウンロードします。
実行すると、`wallet_<ADB名>.zip` というファイルが、Ansible **プレイブックを実行したディレクトリ**に出力されます。

```bash
ansible-playbook -i localhost, -c local adb_wallet_download.yml
```

### 3. Ansible inventory（hosts.ini）セットアップ（VM の接続先情報を自動生成）

VM の接続先情報 `hosts.ini` を自動生成します。

```bash
ansible-playbook -i localhost, -c local generate_inventory.yml
```

### 4. SQL*Plus セットアップおよび接続テスト（Oracle クライアントを VM にインストールし、ADB 接続確認）

VM 上に Oracle クライアント（SQL*Plus）をインストールし、ADB への接続確認を行います。
接続確認では、下記の SQL を実行して ADB が正常に動作していることを確認します。

```sql
SELECT INSTANCE_NAME || ' | ' || STATUS || ' | ' || DATABASE_STATUS FROM V$INSTANCE;
```

> この SQL は、Ansible の sqlplus_setup.yml に組み込まれています。

実行コマンドは下記のとおりです。

```bash
ansible-playbook -i inventory/hosts.ini --limit demo_vm sqlplus_setup.yml
```

初回接続時には、下記のような SSH ホスト確認プロンプトが表示されることがあります。

```bash
Are you sure you want to continue connecting (yes/no/[fingerprint])? 
```

`yes` を入力して接続を許可してください。

> **`--limit demo_vm`** について
> このオプションは、**`hosts.ini`** に定義されたホストグループ **`demo_vm`** のみを対象に処理を実行します。  
> 複数のホスト定義がある場合でも、**対象の VM のみに限定して処理する**よう指示しています。

**接続に失敗する場合の注意**
SSH 接続時に下記のようなエラーメッセージが表示されることがあります。

```plantext
System is booting up. Unprivileged users are not permitted to log in yet. Please come back later. For technical details, see pam_nologin(8).
```
このメッセージは、**VM がまだ起動中で、一般ユーザーがログインできない状態**であることを意味します。
**数十秒ほど待ってから再度 `ansible-playbook` を実行**してください。

正常に接続できると、下記のように SQL の実行結果が表示されます。最初の `INSTANCE_NAME` は環境によって異なります。

![SQL実行結果](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4071373/c0580b30-6527-4f92-9d01-d226f3c76e7f.png)

ここまでで、VM 上に SQL*Plus の実行環境が整い、ADB への接続確認までが完了しました。

## 不要になったら terraform destroy で一発削除

検証や作業が完了し、構築した環境が不要になった場合は、`terraform` ディレクトリに移動し、下記のコマンドで**作成したリソース（VM、ADBなど）を一括で削除**できます。

```bash
cd /home/iacuser/terraform
terraform destroy
```

この操作は OCI 上のリソースのみを削除し、Docker内に作成した Terraform / Ansible 関連ファイルは残ります。
あわせてDocker内の構成ファイルや一時ファイルも削除したい場合は、下記のコマンドを実行してください。

- **Terraform 関連ファイルの削除**  
  
  ```bash
  cd /home/iacuser/terraform
  rm -rf .terraform .terraform.lock.hcl terraform.tfstate terraform.tfstate.backup terraform.tfvars terraform_outputs.json
  ```  
  <br>

- **Ansible 関連ファイルの削除**

  ```bash
  cd /home/iacuser/ansible
  rm -rf inventory/hosts.ini wallet_demoadb.zip oci_api_key_public.pem
  ```

## Compartment を削除する場合の注意点

Terraform で作成された OCI の compartment（demo-compartment）は、**デフォルトでは `terraform destroy` を実行しても自動削除されません。**

これは、Terraform の `oci_identity_compartment` リソースにおいて、`enable_delete` パラメータが `false`（デフォルト値）であるためです。

もし `terraform destroy` 時に Compartment も削除したい場合は、下記のように `true` を明示的に指定する必要があります（本記事で紹介している GitHub リポジトリのコードでは、`true` に設定されています）。

```hcl
resource "oci_identity_compartment" "demo_compartment" {
  name            = var.compartment_name
  description     = var.compartment_description
  compartment_id  = var.tenancy_ocid

  # Optional
  enable_delete   = true
}
```

なお、**Compartment を削除するには、その中に存在するリソースがすべて削除済みである必要があり**、リソースが残っているとエラーになります。

この挙動や制約については、Terraform の公式ドキュメントでも説明されていますので、必要に応じて下記をご参照ください。
[terraform.io - oci_identity_compartment](https://registry.terraform.io/providers/oracle/oci/latest/docs/resources/identity_compartment)

## Docker 環境の停止・削除（任意）

作業完了後にローカル環境も整理したい場合は、下記のコマンドで Docker コンテナを停止／削除できます。

- **コンテナの停止（中断したい場合）**  
  
  ```bash
  docker-compose down
  ```  
  <br>

- **コンテナの削除（完全に削除したい場合）**  
  
  ```bash
  docker-compose down --volumes --rmi all
  ```

  > `--volumes` はボリューム（永続データ）を削除します。
  > `--rmi all` はビルド済みのイメージを削除します。次回起動時は再ビルドが必要です。

## トラブルシューティング

### SQL*Plus のインストール時にメモリ不足で失敗する場合

VM 上で SQL*Plus 環境を構築する際、メモリ不足に陥ることがあります。
swap を作成して回避した例を紹介します。

```bash
sudo fallocate -l 2G /swapfile
sudo chmod 600 /swapfile
sudo mkswap /swapfile
sudo swapon /swapfile
```

`sqlplus_setup.yml` の `vm_swap_setup` がそれにあたります。

### Terraform で ADB の CPU 数が`0`と認識される場合

`terraform apply` 実行後に、再度 `plan` や `apply` を実行すると、**ADB の CPU コア数が `0` と誤って検出される**ことがあります。

```plantext
Terraform will perform the following actions:

  # oci_database_autonomous_database.k_adb will be updated in-place
  ~ resource "oci_database_autonomous_database" "k_adb" {
      ~ cpu_core_count                               = 0 -> 1
        id                                           = "xxxxxxxxxxxxx"
        # (72 unchanged attributes hidden)

        # (9 unchanged blocks hidden)
    }

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

この挙動を抑制するには、対象リソース（例：adb.tf）に下記の lifecycle ブロックを追加します。

```hcl
  lifecycle {
    ignore_changes = [
      cpu_core_count,
    ]
  }
```

これにより、Terraform が cpu_core_count の差分を無視し、意図しない再適用を回避できます。

## まとめ

本記事では、Terraform と Ansible を活用して、OCI 上に VM と Autonomous Database（ADB）を自動構築し、VM から SQL*Plus で接続する環境を構築しました。

OCI の **Always Free枠**内で構成されているため、コストを気にせず試せるのも魅力です。

今後も自動化＋クラウド活用のノウハウを蓄積していく予定です。

質問や改善点があれば、ぜひコメントでお知らせください！