クイックスタート:5分でTerraform + Proxmoxを動かす
私はProxmox VEで12台のVMとコンテナを管理するhomelabを運用しています。本番環境に上げる前にあらゆるものをテストするためのplaygroundです。6ヶ月間Terraformを使ってインフラ全体を管理してきた結果、手動でVMをクリックして作成したいとは思わなくなりました。すべてがコードになり、レビュー、ロールバック、数分での再構築が可能です。
詳細に入る前に、まず試してみて体感してください:
ステップ1:Terraformをインストール
# Ubuntu/Debian
wget -O- https://apt.releases.hashicorp.com/gpg | sudo gpg --dearmor -o /usr/share/keyrings/hashicorp-archive-keyring.gpg
echo "deb [signed-by=/usr/share/keyrings/hashicorp-archive-keyring.gpg] https://apt.releases.hashicorp.com $(lsb_release -cs) main" | sudo tee /etc/apt/sources.list.d/hashicorp.list
sudo apt update && sudo apt install terraform
terraform version
ステップ2:ProxmoxでAPIトークンを作成
Proxmox Web UIにアクセスし、Datacenter → Permissions → API Tokens → Addの順に進みます。user root@pamのトークンを作成し、「Privilege Separation」のチェックを外してトークンに十分な権限を付与します。トークン文字列をコピーしてください — 表示されるのは1回だけです。
ステップ3:最小限の設定ファイル
terraform {
required_providers {
proxmox = {
source = "bpg/proxmox"
version = "~> 0.60"
}
}
}
provider "proxmox" {
endpoint = "https://192.168.1.100:8006/"
api_token = "root@pam!terraform=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
insecure = true # 有効なSSL証明書がある場合は削除
}
resource "proxmox_virtual_environment_vm" "ubuntu_vm" {
name = "ubuntu-test"
node_name = "pve"
vm_id = 200
cpu {
cores = 2
}
memory {
dedicated = 2048
}
disk {
datastore_id = "local-lvm"
file_id = "local:iso/ubuntu-22.04-live-server-amd64.iso"
interface = "virtio0"
size = 20
}
network_device {
bridge = "vmbr0"
}
}
terraform init
terraform plan
terraform apply
実行が完了すると、VM ubuntu-testがProxmoxのダッシュボードにすぐ表示されます — クリックは一切不要です。
詳細解説:bpg/proxmoxプロバイダーの仕組み
現在、Proxmox向けのプロバイダーとして主に2つが使われています:Telmate/proxmox(古く、メンテナンスが少ない)とbpg/proxmox(より新しく、アクティブで、より多くのリソースをサポート)。両方試した結果、Ubuntu 22.04でTelmateがCloud-Initを正しく処理しなかったため、bpgに移行しました。
主要なリソースタイプ
proxmox_virtual_environment_vm— VMの作成と管理proxmox_virtual_environment_container— LXCコンテナproxmox_virtual_environment_download_file— ISOまたはクラウドイメージのダウンロードproxmox_virtual_environment_pool— リソースプールの作成proxmox_virtual_environment_user— Proxmoxユーザーの管理
Cloud-Initを使った実際のワークフロー
Cloud-Initが設定されたテンプレートからクローンする方法を毎日使っています。VMが起動したらすぐにSSH接続できます — 手動設定は不要です。
resource "proxmox_virtual_environment_vm" "web_server" {
name = "web-prod-01"
node_name = "pve"
vm_id = 201
clone {
vm_id = 9000 # Ubuntu Cloud-InitテンプレートのID
full = true # フルクローン、テンプレートに依存しない
}
cpu {
cores = 4
type = "x86-64-v2-AES"
}
memory {
dedicated = 4096
}
network_device {
bridge = "vmbr0"
model = "virtio"
}
initialization {
ip_config {
ipv4 {
address = "192.168.1.201/24"
gateway = "192.168.1.1"
}
}
user_account {
username = "ubuntu"
keys = [file("~/.ssh/id_ed25519.pub")]
}
dns {
servers = ["1.1.1.1", "8.8.8.8"]
}
}
lifecycle {
ignore_changes = [
initialization, # planの度に再適用されるのを防ぐ
]
}
}
応用:for_eachとモジュールで複数VMを管理
VMの数が増えると — 私の場合は12台 — リソースブロックのコピーペーストではメンテナンスが困難になります。for_eachとlocalsを組み合わせることでスッキリ解決できます:
locals {
vms = {
"web-01" = { cores = 2, memory = 2048, ip = "192.168.1.201", vm_id = 201 }
"web-02" = { cores = 2, memory = 2048, ip = "192.168.1.202", vm_id = 202 }
"db-01" = { cores = 4, memory = 8192, ip = "192.168.1.210", vm_id = 210 }
"cache" = { cores = 2, memory = 4096, ip = "192.168.1.220", vm_id = 220 }
}
}
resource "proxmox_virtual_environment_vm" "vms" {
for_each = local.vms
name = each.key
node_name = "pve"
vm_id = each.value.vm_id
clone {
vm_id = 9000
full = true
}
cpu {
cores = each.value.cores
}
memory {
dedicated = each.value.memory
}
initialization {
ip_config {
ipv4 {
address = "${each.value.ip}/24"
gateway = "192.168.1.1"
}
}
user_account {
username = "ubuntu"
keys = [file("~/.ssh/id_ed25519.pub")]
}
}
}
新しいVMを追加するには、locals.vmsに1行追加するだけです。削除したい場合は、その行を削除してterraform applyを実行します。
AnsibleとのOutput連携
TerraformがVMを作成した後、Ansibleが内部のプロビジョニングを担当します。この2つのツールを組み合わせるのが、私が本番環境で使っているワークフローです:
output "vm_ips" {
value = {
for k, v in proxmox_virtual_environment_vm.vms :
k => v.initialization[0].ip_config[0].ipv4[0].address
}
}
# AnsibleのダイナミックインベントリのIPを取得
terraform output -json vm_ips | jq -r 'to_entries[] | "\(.key) ansible_host=\(.value | split("/")[0])"'
6ヶ月の本番運用で得た実践的なTips
1. Cloud-Initテンプレートを正しく作成する — 最重要ステップ
テンプレートは手動インストールのISOからではなく、クラウドイメージから作成する必要があります。これが最初に最も時間を費やした部分です:
# Proxmoxホスト上で実行
wget https://cloud-images.ubuntu.com/jammy/current/jammy-server-cloudimg-amd64.img
qm create 9000 --name "ubuntu-22.04-cloud" --memory 2048 --cores 2 --net0 virtio,bridge=vmbr0
qm importdisk 9000 jammy-server-cloudimg-amd64.img local-lvm
qm set 9000 --scsihw virtio-scsi-pci --scsi0 local-lvm:vm-9000-disk-0
qm set 9000 --boot c --bootdisk scsi0
qm set 9000 --ide2 local-lvm:cloudinit
qm set 9000 --serial0 socket --vga serial0
qm set 9000 --agent enabled=1
qm template 9000
2. リモートバックエンドでstateファイルを安全に保存
stateファイル(terraform.tfstate)は、TerraformがどのVMが存在するかを把握するために使用されます。このファイルを失うと、すべてのトラッキングが失われます。セルフホストのMinIOをS3バックエンドとして使用するのは、homelabに適した解決策です:
terraform {
backend "s3" {
bucket = "terraform-state"
key = "proxmox/homelab.tfstate"
region = "main"
endpoint = "http://192.168.1.50:9000"
skip_credentials_validation = true
skip_metadata_api_check = true
skip_region_validation = true
force_path_style = true
}
}
3. applyの前に必ずplanを確認する
# planをファイルに保存し、十分に確認してからapply
terraform plan -out=tfplan
terraform show tfplan
terraform apply tfplan
# 特定のVMを削除(全削除しない)
terraform destroy -target='proxmox_virtual_environment_vm.vms["web-01"]'
よくあるエラーと対処法
- Cloud-Initが適用されない:テンプレートにcloudinit IDEドライブがあるか確認 —
qm set 9000 --ide2 local-lvm:cloudinitのステップが不足している - applyでPermission denied:APIトークンに
VM.Allocate、VM.Clone、Datastore.AllocateSpace、VM.Config.Networkの権限が必要 - Terraformが”waiting for VM to start”で止まる:テンプレートにQEMU Guest Agentがインストールされていない —
qm set 9000 --agent enabled=1のステップが不足している - destroyが成功しない:VMにスナップショットがある場合、Terraformは自動削除できない — 先にProxmoxで手動でスナップショットを削除する必要があります。このエラーのデバッグに2時間かかったことがあります
Terraformでhomelab全体のインフラをセットアップし終えると、必要に応じて10分もかからず最初から再構築できます。それがProxmoxとIaCに初期設定の時間を費やす価値がある理由です。

