Young Leaves

Terraform でAzure 仮想マシンのカスタムデータを使い、Web サーバーを導入した状態の仮想マシンを作成する

Terraform でAzure 仮想マシンを作成する時、事前にパッケージを導入したり特定のファイルを作成するなど行いたい時があります。今回はTerraform を使用し、Web サーバーのApache を事前に導入したAzure 仮想マシンを作成し、ブラウザから接続する一連の流れについて説明します。

実行環境

Terraform

1.6.6

前提条件

  • ローカルPC にTerraform がインストール済みであること
  • Terraform からAzure への接続用サービスプリンシパルが作成済みであること
  • SSH 公開鍵を事前に作成していること。作成していない場合は「ssh-keygen」コマンドで作成しておくこと

Terraform のインストール、Azure への接続用サービスプリンシパルが未作成の方は以下記事を参考に準備をしてください。

Mac OS にTerraform をインストールしAzure 用の設定をする

Azure 仮想マシンに必要なリソース

Azure 仮想マシンを作成し、パブリックの接続をできるようにするには以下のリソースが必要となります。

  • リソースグループ
  • 仮想ネットワーク
  • 仮想ネットワーク内のサブネット
  • HTTP 通信を許可するネットワークセキュリティグループ
  • パブリックIP アドレス
  • 仮想ネットワークインターフェース

動作などを考えなければネットワークセキュリティグループやパブリックIP アドレスも不要となりますが、今回は仮想マシン内にあるWeb サーバへ接続を行うためこれらのリソースを作成しています。

Terraform でカスタムデータを入力する方法について

Terraform でAzure 仮想マシンのカスタムデータを入力するには「azurerm_linux_virtual_machine」内の「custom_data」属性を利用します。単一の行であればそのまま入力できますが、複数行を入力する場合、ローカル変数などにカスタムデータで実行するコマンドを定義した後、「base64encode」で読み込む必要があります。

<省略>
locals {
  custom_data = <<EOF
<ここに実行するコマンドを設定>
EOF
}

resource "azurerm_linux_virtual_machine" "vm" {
  <省略>

  custom_data    = base64encode(local.custom_data)

  <省略>
  }
}

今回の構成

今回は仮想マシンのみを構成し接続するところまでとしているため、以下のような構成を作成します。

仮想マシンの構成は以下とします。

OS

Ubuntu 22.04

VM サイズ

Standard_B1s

OS ディスク

Standard_LRS

Terraform ファイルの作成

初めにリソースグループ、仮想マシンに必要なリソースを作成するTerraform ファイルを作成します。今回は仮想マシンを作成する一連のリソースをまとめていますが、実務などで作成する場合はリソースの種類ごとにTerraform ファイルを分割したり変数用のファイルを作成するなど行ってください。

terraform {
  required_providers {
    azurerm = {
      source  = "hashicorp/azurerm"
      version = "~> 3.0.2"
    }
  }

  required_version = ">= 1.6.0"
}

provider "azurerm" {
  features {}
}

# カスタムデータ用のローカル変数を定義
locals {
  custom_data = <<EOF
#!/bin/bash
apt update
apt install -y apache2
EOF
}

# リソースグループを作成
resource "azurerm_resource_group" "rg" {
  name     = "createvmdemo-rg"
  location = "japaneast"
}

# 仮想ネットワークを作成
resource "azurerm_virtual_network" "vnet" {
  name                = "createvmdemo-vnet"
  address_space       = ["10.0.0.0/16"]
  location            = azurerm_resource_group.rg.location
  resource_group_name = azurerm_resource_group.rg.name
}

# 仮想ネットワーク内にサブネットを作成
resource "azurerm_subnet" "snet" {
  name                 = "createvmdemo-snet"
  resource_group_name  = azurerm_resource_group.rg.name
  virtual_network_name = azurerm_virtual_network.vnet.name
  address_prefixes     = ["10.0.1.0/24"]
}

# ネットワークセキュリティグループを作成
resource "azurerm_network_security_group" "nsg" {
  name                = "createvmdemo-nsg"
  location            = azurerm_resource_group.rg.location
  resource_group_name = azurerm_resource_group.rg.name

  # 80番ポートを許可するセキュリティルールを作成
  security_rule {
    name                       = "allow-http-nsgsr"
    priority                   = 100
    direction                  = "Inbound"
    access                     = "Allow"
    protocol                   = "Tcp"
    source_port_range          = "*"
    destination_port_range     = "80"
    source_address_prefix      = "*"
    destination_address_prefix = "*"
  }

  # 22番ポートを許可するセキュリティルールを作成
  # 今回はデモ用のため22番ポートは全てのIPアドレスを許可している
  security_rule {
    name                       = "allow-ssh-nsgsr"
    priority                   = 200
    direction                  = "Inbound"
    access                     = "Allow"
    protocol                   = "Tcp"
    source_port_range          = "*"
    destination_port_range     = "22"
    source_address_prefix      = "*"
    destination_address_prefix = "*"
  }
}

# ネットワークセキュリティグループをサブネットに関連付け
resource "azurerm_subnet_network_security_group_association" "nsgassociate" {
  subnet_id                 = azurerm_subnet.snet.id
  network_security_group_id = azurerm_network_security_group.nsg.id
}

# パブリックIPアドレスを作成
resource "azurerm_public_ip" "pip" {
  name                    = "createvmdemo-pip"
  location                = azurerm_resource_group.rg.location
  resource_group_name     = azurerm_resource_group.rg.name
  allocation_method       = "Static"
  idle_timeout_in_minutes = 30
  sku                     = "Standard"
}

# 仮想ネットワークインターフェースを作成
resource "azurerm_network_interface" "nic" {
  name                = "createvmdemo-nic"
  location            = azurerm_resource_group.rg.location
  resource_group_name = azurerm_resource_group.rg.name

  # 仮想ネットワークインターフェースにパブリックIPアドレスを関連付ける
  ip_configuration {
    name                          = "global"
    subnet_id                     = azurerm_subnet.snet.id
    public_ip_address_id          = azurerm_public_ip.pip.id
    private_ip_address_allocation = "Dynamic"
  }
}

# 仮想マシンを作成
# SSH用の公開鍵が存在しないとエラーとなるため事前に作成すること
resource "azurerm_linux_virtual_machine" "vm" {
  name                = "createvmdemo-vm"
  resource_group_name = azurerm_resource_group.rg.name
  location            = azurerm_resource_group.rg.location
  size                = "Standard_B1s"
  admin_username      = "createvmdemouser"
  network_interface_ids = [
    azurerm_network_interface.nic.id,
  ]

  source_image_reference {
    publisher = "Canonical"
    offer     = "0001-com-ubuntu-server-jammy"
    sku       = "22_04-lts"
    version   = "latest"
  }

  admin_ssh_key {
    username   = "createvmdemouser"
    public_key = file("~/.ssh/id_rsa.pub")
  }

  os_disk {
    name                 = "createvmdemo-osdisk" 
    disk_size_gb         = 128
    caching              = "ReadWrite"
    storage_account_type = "Standard_LRS"
  }
  
  custom_data    = base64encode(local.custom_data)
}

構文チェック、リソースの承認

Terraform ファイルの準備が完了後、内容の確認を行います。

# Terraformファイルの構文チェックをする
# 「Success! The configuration is valid.」と表示されればOK
terraform validate

# Terraformで作成されるAzureリソースを確認する
terraform plan

# リソースを承認する
terraform apply

リソース承認後、Azure Portal よりリソースが作成されたことを確認します。リソース確認後、動作確認用にパブリックIP アドレスが必要となるため、作成した仮想マシンからパブリックIP アドレスをコピーしておきます。

動作確認

Azure リソース作成後、ブラウザから「http://<仮想マシンのパブリックIP アドレス>」を入力し、Ubuntu のApache 初期画面が表示されることを確認します。

リソースのクリーンアップ

動作確認後、Terraform で作成したAzure リソースを削除します。

# Terraformで作成したAzureリソースを削除する
terraform destroy

まとめ

  • Azure 仮想マシン でカスタムデータを利用する場合は「custom_data」を使う。
  • カスタムデータで複数行の入力を行いたい場合、ローカル変数に実行内容を定義したのち、「base64encode」で複数行読み込む。

参考資料