- 2025.11.08:再実行時の変更について追記
- 2025.11.24:
proxmox_cloud_init_diskリソースを使ったCloud-init設定と、bpg/proxmoxプロバイダについて追記
クラウドイメージのテンプレートとcloud-initを使ったProxmoxでの設定済みVMのプロビジョニングや、それをAnsibleで自動化するというのは以前にやってたけど、それをより宣言型になるようTerraform対応ってのはやるやる詐欺のまま放置したまま…
ただ最近仕事でこのパターンの組み合わせをやりそうな流れになってきたので、手元の環境で試してみた。
前提として、クラウドイメージを使ったVMのテンプレートがすでにある状態。
本エントリでは、Ubuntu 24.04をクローン可能な ubuntu2404-template を用意してある。
接続設定
認証と権限
あるべき状態としては「Terraform実行用に適切な権限を持たせたユーザーのトークンを使用」が理想だが、ひとまずコードが動くことの確認をしたかったので、rootのパスワード認証で実施。
パスワード認証の場合は以下の環境変数をセットしておけばOK
- PM_USER=root@pam
- PM_PASS=rootのパスワード
トークンの場合は以下
Docs overview | Telmate/proxmox | Terraform | Terraform Registry
※ 同様の理由でバックエンド設定も省略
required_providers/provider設定
installation | Guides | Telmate/proxmox | Terraform | Terraform Registry
2025.09.14時点で最新が3.0.2-rc04なのでそれを指定。
pm_api_urlにPVEのURLを https://<pveのアドレス>:8006/api2/json の形式で記述する。
※ URLは環境変数 PM_API_URL でも指定可能
terraform { required_providers { proxmox = { source = "telmate/proxmox" version = "3.0.2-rc04" } } } provider "proxmox" { pm_api_url = "https://<pveのアドレス>:8006/api2/json" pm_tls_insecure = true }
いったんここまででinitを実行してプロバイダをインストール。
$ terraform init Initializing the backend... Initializing provider plugins... - Finding telmate/proxmox versions matching "3.0.2-rc04"... - Installing telmate/proxmox v3.0.2-rc04... - Installed telmate/proxmox v3.0.2-rc04 (self-signed, key ID A9EBBE091B35AFCE) Partner and community providers are signed by their developers. If you'd like to know more about provider signing, you can read about it here: https://developer.hashicorp.com/terraform/cli/plugins/signing Terraform has created a lock file .terraform.lock.hcl to record the provider selections it made above. Include this file in your version control repository so that Terraform can guarantee to make the same selections by default when you run "terraform init" in the future. Terraform has been successfully initialized! You may now begin working with Terraform. Try running "terraform plan" to see any changes that are required for your infrastructure. All Terraform commands should now work. If you ever set or change modules or backend configuration for Terraform, rerun this command to reinitialize your working directory. If you forget, other commands will detect it and remind you to do so if necessary.
VMの作成 (テンプレートからのクローンとcloud-init設定)
前述のterraformとproviderに加えてこんな感じ。
TerraformのPVEにおけるVM作成はシンプルで proxmox_vm_qemu リソースのみで実現する。
また、Ansibleの場合(クローンするタスクと、設定更新のタスクと複数必要)と異なり一発で行ける。
proxmox_vm_qemu | Resources | Telmate/proxmox | Terraform | Terraform Registry
resource "proxmox_vm_qemu" "pve-vm-example" { name = "tf-vm-example" target_node = "pve01" vmid = 2002 agent = 1 scsihw = "virtio-scsi-pci" tags = "dev" clone = "ubuntu2404-template" cpu { cores = 2 type = "x86-64-v2-AES" } memory = 8192 network { id = 0 bridge = "vmbr0" model = "e1000" } network { id = 1 bridge = "vmbr1" model = "e1000" } disk { slot = "ide0" type = "cloudinit" storage = "local-lvm" } disk { slot = "scsi0" type = "disk" storage = "local-lvm" size = "15G" format = "raw" } # serial serial { id = "0" type = "socket" } # cloud-init os_type = "cloud-init" ciuser = "zaki" cipassword = "curry_tabetai" sshkeys = "ssh-ed25519 AAA...[snip]" nameserver = "192.168.0.4" ipconfig0 = "ip=192.168.0.78/24,gw=192.168.0.1" ipconfig1 = "ip=172.16.0.78/23" }
VMのテンプレートはクラウドイメージを使って事前に作成してあるものを使用する。
上記この場合、テンプレート名は"ubuntu2404-template"を使用。
※ 2025.11.08更新
当初は以下のように「scsi0/disk → ide0/cloudinit」の順に記述していが、これだと再実行時にno changesとならずに「cloudinit → disk」の順に変更が走るような動作となったので、入れ替えている。
入れ替えればno changesになる。
(telmate/proxmox v3.0.2-rc04で確認)
disk { slot = "scsi0" type = "disk" storage = "local-lvm" size = "15G" } disk { slot = "ide0" type = "cloudinit" storage = "local-lvm" }
VM設定
主なパラメタと設定例は以下の通り
| パラメタ | 内容 |
|---|---|
| name | VM名 |
| target_node | クラスタ内のVMを作成するPVEノード |
| vmid | VM ID |
| os_type | cloud-initを設定(後述) |
| agent | QEMU Guest Agentの有効化 |
| memory | メモリサイズ(MB) |
| clone | クローン元テンプレート名 |
| scsihw | virtio-scsi-pci |
scsihwの設定はドキュメントには特に言及はないが、手元の環境では未設定の場合はVMが動作しなかった。
正常に動作している既存VMの設定を参考にvirtio-scsi-pciを指定すれば動作した。
※ 2025.11.08追記:tagsでタグを指定できるが、未設定あるいは空文字("")やnull指定だと、再実行時にno changesにならない。実害はないが…仕様??
# proxmox_vm_qemu.pve-vm-example will be updated in-place
~ resource "proxmox_vm_qemu" "fpve-vm-example" {
id = "pve01/qemu/2002"
name = "pve-vm-example"
- tags = " " -> null
# (52 unchanged attributes hidden)
CPU設定 / cpu { ... }
| パラメタ | 内容 |
|---|---|
| cores | CPUコア |
| type | CPU種別 |
ネットデバイス設定 / network { ... }
idはUIで確認できるnet0などの数字部分で、0から順に使っていけば良い。
複数のNICを使う場合はidの値を増やして複数記述する。
| パラメタ | 内容 |
|---|---|
| id | デバイスID |
| bridge | ネットワークブリッジ(vmbr0など) |
| model | NICのモデル |
ストレージ / disk { ... }
ネットワークと同様、使用する分だけ複数記述する。
| パラメタ | 内容 |
|---|---|
| slot | scsi0やide0などの種別と番号 |
| type | diskやcloudinitなどストレージ種別 |
| storage | PVEにおけるストレージ名 |
| size | type=diskの場合のディスクサイズ |
テンプレートからクローンする際に重要なのは、ディスクサイズを元のテンプレートのディスクサイズより小さい値を指定すると「Boot failed: not a bootable disk」「No bootable device.」と出力され起動に失敗するので注意。
シリアルポート / serial { ... }
テンプレートからクローンする際はこれを指定しないと「コンソール」で接続に失敗するため設定しておく。
| パラメタ | 内容 |
|---|---|
| id | 0 |
| type | socket |
cloud-init関連
cloud-initで与えられるパラメタを設定する。
Ansibleと同じくciがprefixにつくものが多いためわかりやすいはず。
| パラメタ | 内容 |
|---|---|
| ciuser | ユーザー名 |
| cipassword | パスワード |
| sshkeys | 公開鍵 |
| nameserver | ネームサーバー設定 |
| ipconfig0 | nic0(network.id=0)の設定 |
| ipconfig1 | nic1(network.id=1)の設定 |
NICの設定は必要なだけセットする。
なお、前述のos_typeにはcloud-initを設定しておくのが無難だと思うが、手元の環境では未設定でも影響しなかった。ドキュメントの記載は以下の通りなので、ip=の設定があれば未設定でも良さそうな気はする。
When
os_typeiscloud-initnot settingip=is equivalent toskip_ipv4==trueandip6=toskip_ipv6==true.
proxmox_cloud_init_disk
※ 2025.11.24追記
Proxmoxが標準で用意しているCloud-initの項目以外のカスタマイズについては、proxmox_cloud_init_diskリソースがあり、これを使うことでパッケージインストール他Cloud-initの機能をフルに(たぶん。未確認)活用できる。
が、この機能は、以前動作確認したPVEのスニペット領域に配置するカスタムCloud-initファイルを使用した構成と異なり、上記のciuserなどのPVE提供の機能と併用が(少なくとも試した限りでは)不可のため、「設定済みVMをプロビジョニング」する場合はユーザーやネットワークその他もろもろ完全マニュアルで指定する必要があるので注意。
このリソースを使用する場合は、ざっと以下の通りで、proxmox_cloud_init_diskでCloud-init定義を記述し、proxmox_vm_qemuのVM定義でCloud-init定義を参照する。
前述の通りこのCloud-init定義にすべてを記述する必要があるため、下記の内容のみだとユーザー定義は書かれた通り「ユーザーが作成されるだけ」になり、デフォルトシェルやsudoers設定が無かったりで、率直なところ使い勝手はあまりよくない。
resource "proxmox_cloud_init_disk" "ci" { name = local.vm_name pve_node = local.pve_node storage = "fileserver" meta_data = yamlencode({ instance_id = sha1(local.vm_name) local-hostname = local.vm_name }) user_data = <<-EOT #cloud-config users: - ${var.username} chpasswd: expire: false users: - name: ${var.username} password: ${var.password}$ type: text ssh_authorized_keys: - ${var.ssh_pub_key} packages: - podman - qemu-guest-agent runcmd: - systemctl start qemu-guest-agent EOT network_config = <<-EOT version: 1 config: - type: physical name: ens18 subnets: - type: dhcp EOT } resource "proxmox_vm_qemu" "tf-vm-ci-sample" { name = local.vm_name target_node = local.pve_node vmid = 2003 agent = 1 scsihw = "virtio-scsi-pci" tags = "dev" os_type = "cloud-init" cpu { cores = 2 type = "x86-64-v2-AES" } memory = 4096 disks { scsi { scsi0 { disk { size = "15G" storage = "local-lvm" format = "raw" } } scsi1 { cdrom { iso = proxmox_cloud_init_disk.ci.id } } } } network { id = 0 bridge = "vmbr0" model = "e1000" } serial { id = 0 type = "socket" } # [...] 以下省略
これについては、telmate/proxmoxでなくbpg/proxmoxプロバイダを使うといい感じに定義できるのを確認。以下参照。
関連
雑感
Ansibleの場合は(明示的にブートしなければ)VMを作るところまでだが、Terraformだとbootしてcloud-initによる初期設定まで全部行われるため(※ 2025.11.08追記:これは vm_state オプションによる動作。デフォルトがrunningのため起動する)、terraform applyを実行すれば手元の環境だと約60秒後にはpingの応答があるくらいのスピード感でVMを生成できて非常によかった。
あとAWSでしかTerraform使ったことなかったため、ローカルネットワーク上にあるPVEにterraform planすると秒で結果が出力されるのはビビった。笑