2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Terraformマスターへの道#3 OCIのWebサーバ環境構築&ドリフト検出

2
Last updated at Posted at 2026-05-31

はじめに

前回まではObject StorageやNotificationsなど、単体サービスをTerraformで作成してみました。
今回は複数サービスを作成する必要があるWebサーバ環境をTerraformでOCI上に構築します。

作成するリソースはVCN、Internet Gateway、Route Table、Public Subnet、Network Security Group、Compute Instanceです。
さらに、OCIコンソールから手動で設定を変更し、OCI Resource Managerのドリフト検出でTerraform管理状態との差分を確認します。

今回作るもの

構成は以下です。

VCN
├─ Internet Gateway
├─ Route Table
├─ Public Subnet
├─ Security List
│  └─ outbound許可
├─ Network Security Group
│  └─ TCP/80 inbound許可
└─ Compute Instance
   └─ cloud-initでnginxを起動

なぜObject Storageより少し難しいのか

Object Storageバケットの作成は、基本的には1つのリソースを作るだけでした。
一方でWebサーバ環境では、Compute Instanceだけを作ってもブラウザからアクセスできません。
以下のような複数の要素が必要になります。

  • VCN
  • Public Subnet
  • Internet Gateway
  • Route Table
  • Security List
  • Network Security Group
  • Compute Instance
  • OS上のWebサーバ設定

今回の例ではSSH接続は行わず、cloud-initでnginxを自動起動して、ブラウザからHTTPアクセスできることを確認します。

ファイル構成

以下のファイルを作成します。

oci-webserver-rm-drift/
├── provider.tf
├── variables.tf
├── main.tf
├── outputs.tf
└── cloud-init.yaml

provider.tfを作成する

Resource ManagerでOCI Providerを使う場合、基本的には region を指定します。

terraform {
  required_version = ">= 1.5.0"

  required_providers {
    oci = {
      source  = "oracle/oci"
      version = ">= 6.0.0, < 9.0.0"
    }
  }
}

provider "oci" {
  region = var.region
}

variables.tfを作成する

variable "region" {
  description = "OCI region"
  type        = string
}

variable "compartment_ocid" {
  description = "Compartment OCID"
  type        = string
}

variable "prefix" {
  description = "Resource name prefix"
  type        = string
  default     = "tf-web"
}

variable "vcn_cidr" {
  description = "VCN CIDR block"
  type        = string
  default     = "10.0.0.0/16"
}

variable "public_subnet_cidr" {
  description = "Public subnet CIDR block"
  type        = string
  default     = "10.0.1.0/24"
}

variable "instance_shape" {
  description = "Compute instance shape"
  type        = string
  default     = "VM.Standard.E2.1.Micro"
}

main.tfを作成する

NSGのルールでは、TCPの80番ポートをインターネットから許可しています。OCI ProviderのNSGセキュリティルールでは、TCPの宛先ポート範囲を指定できます。

data "oci_identity_availability_domains" "ads" {
  compartment_id = var.compartment_ocid
}

data "oci_core_images" "oracle_linux" {
  compartment_id           = var.compartment_ocid
  operating_system         = "Oracle Linux"
  operating_system_version = "8"
  shape                    = var.instance_shape
  sort_by                  = "TIMECREATED"
  sort_order               = "DESC"
}

resource "oci_core_vcn" "web_vcn" {
  compartment_id = var.compartment_ocid
  cidr_block     = var.vcn_cidr
  display_name   = "${var.prefix}-vcn"
  dns_label      = "tfwebvcn"
}

resource "oci_core_internet_gateway" "web_igw" {
  compartment_id = var.compartment_ocid
  vcn_id         = oci_core_vcn.web_vcn.id
  display_name   = "${var.prefix}-igw"
  enabled        = true
}

resource "oci_core_route_table" "public_rt" {
  compartment_id = var.compartment_ocid
  vcn_id         = oci_core_vcn.web_vcn.id
  display_name   = "${var.prefix}-public-rt"

  route_rules {
    destination       = "0.0.0.0/0"
    destination_type  = "CIDR_BLOCK"
    network_entity_id = oci_core_internet_gateway.web_igw.id
  }
}

resource "oci_core_security_list" "public_sl" {
  compartment_id = var.compartment_ocid
  vcn_id         = oci_core_vcn.web_vcn.id
  display_name   = "${var.prefix}-public-sl"

  egress_security_rules {
    protocol    = "all"
    destination = "0.0.0.0/0"
  }
}

resource "oci_core_subnet" "public_subnet" {
  compartment_id             = var.compartment_ocid
  vcn_id                     = oci_core_vcn.web_vcn.id
  cidr_block                 = var.public_subnet_cidr
  display_name               = "${var.prefix}-public-subnet"
  dns_label                  = "public"
  route_table_id             = oci_core_route_table.public_rt.id
  security_list_ids          = [oci_core_security_list.public_sl.id]
  prohibit_public_ip_on_vnic = false
}

resource "oci_core_network_security_group" "web_nsg" {
  compartment_id = var.compartment_ocid
  vcn_id         = oci_core_vcn.web_vcn.id
  display_name   = "${var.prefix}-web-nsg"
}

resource "oci_core_network_security_group_security_rule" "allow_http" {
  network_security_group_id = oci_core_network_security_group.web_nsg.id
  direction                 = "INGRESS"
  protocol                  = "6"
  source                    = "0.0.0.0/0"
  source_type               = "CIDR_BLOCK"
  stateless                 = false
  description               = "Allow HTTP from Internet"

  tcp_options {
    destination_port_range {
      min = 80
      max = 80
    }
  }
}

resource "oci_core_instance" "web_instance" {
  compartment_id      = var.compartment_ocid
  availability_domain = data.oci_identity_availability_domains.ads.availability_domains[0].name
  display_name        = "${var.prefix}-instance"
  shape               = var.instance_shape

  create_vnic_details {
    subnet_id        = oci_core_subnet.public_subnet.id
    assign_public_ip = true
    display_name     = "${var.prefix}-vnic"
    nsg_ids          = [oci_core_network_security_group.web_nsg.id]
  }

  source_details {
    source_type = "image"
    source_id   = data.oci_core_images.oracle_linux.images[0].id
  }

  metadata = {
    user_data = base64encode(file("${path.module}/cloud-init.yaml"))
  }
}

cloud-init.yamlを作成する

Compute Instance起動時にnginxをインストールして起動します。
OCIのCompute Instanceでは、metadataの user_data にbase64エンコードしたcloud-initデータを渡せます。(registry.terraform.io)


#cloud-config
package_update: true
packages:
  - nginx

runcmd:
  - systemctl enable --now nginx
  - firewall-cmd --permanent --add-service=http
  - firewall-cmd --reload
  - echo '<html><body><h1>Hello from Terraform on OCI</h1><p>This web server was created by OCI Resource Manager.</p></body></html>' > /usr/share/nginx/html/index.html

outputs.tfを作成する

Apply後にブラウザでアクセスしやすいよう、パブリックIPとURLを出力します。

output "instance_public_ip" {
  value = oci_core_instance.web_instance.public_ip
}

output "web_url" {
  value = "http://${oci_core_instance.web_instance.public_ip}"
}

output "nsg_id" {
  value = oci_core_network_security_group.web_nsg.id
}

Terraform構成をZIP化する

Resource Managerにアップロードするため、ファイルをZIP化します。
Resource ManagerでローカルのTerraform構成を使う場合、Terraformファイル一式をZIP化してStack作成時にアップロードできます。
terraform-oci-webserver.zipというファイルを作成しました。

Resource ManagerでStackを作成する

OCIコンソールで以下に移動します。
Terraform構成ファイルとして、先ほど作成した terraform-oci-webserver.zip をアップロードします。
image.png

VCNやComputeを作成するので、それにあたって値の確認が必要です。
必要でなければデフォルト値のまま進めます。
image.png

確認して作成します。
image.png

Planを実行する

Stack作成後、まずPlanを実行します。
今回の例では、以下のようなリソースが作成予定になります。

  • VCN
  • Internet Gateway
  • Route Table
  • Security List
  • Public Subnet
  • Network Security Group
  • NSG Security Rule
  • Compute Instance

ログを確認すると各コンポーネントが作成予定となっています。
8コンポーネントが作成予定になっていることがログの最後から確認できました。
image.png

Applyを実行する

Planの内容を確認したらApplyを実行します。
Applyが成功すると、Outputsに web_url が表示されます。

image.png
image.png

ブラウザでWebページを確認する

Outputsに表示されたURLへアクセスします。

http://<instance_public_ip>

以下のような画面が表示されたので成功です!

Hello from Terraform on OCI
This web server was created by OCI Resource Manager.

OCIコンソールから、作成されたリソースも確認することができました。
image.png

NSGルールを手動変更してドリフトを作る

ここから、あえてTerraform管理外の手動変更を行います。
今回はOCIコンソールから、Terraformで作成したNSGのHTTP許可ルールを削除します。
image.png

image.png

Terraformで作成した以下のルールを確認します。
image.png

このルールをOCIコンソールから手動で削除します。
この時点で、Terraformの状態ファイル上は「TCP/80のNSGルールが存在する」ことになっています。
しかし、実際のOCI上では手動で削除されています。
この差分がドリフトです。
image.png

Resource Managerでドリフト検出を実行する

Resource ManagerのStack詳細画面に戻り、ドリフト検出を実行します。
Resource Managerのドリフト検出は、実際のインフラ状態とStackの最後に実行された構成との差分を確認できます。
image.png

今回はすべてのリソースを選択して実行します。
image.png

ドリフト検出結果を確認する

ドリフト検出が完了したら、結果を確認します。
image.png

手動で削除したNSG Security Ruleが、Terraformの状態と実際のOCI上の状態で一致していないことが分かります。
この結果から、Terraform管理しているリソースをOCIコンソールで手動変更すると、管理状態との差分が発生することが確認できました。

補足: 手動削除後のWebアクセス

NSGのHTTPルールを削除したため、ブラウザからWebサーバへアクセスできなくなります。
image.png
これは想定通りです。
TerraformとしてはHTTPルールがあるつもりですが、実際のOCI上では削除されているためです。

Destroyで削除する

検証が終わったら、Resource ManagerからDestroyを実行します。
Destroyが成功すると、Terraformで作成したリソースも削除されます。

まとめ

今回は、単体リソースではなく、ネットワークとComputeを組み合わせてWebサーバ環境を作成しました。
Terraformで複数リソースをつなげて作ることで、依存関係のイメージが少しつかめました。

作成後にNSGルールを手動変更するとTerraformの状態と実際のOCIリソースに差分が出て、そう程度通りに動かなくなるところも確認することができました。
ドリフト検出を活用することでもともとの構成を維持することもできることがわかり、クラウドでサーバー構築する大きなメリットを感じることができました。

2
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?