Auto-update blog content from Obsidian: 2025-07-01 20:14:02
All checks were successful
Blog Deployment / Check-Rebuild (push) Successful in 5s
Blog Deployment / Build (push) Has been skipped
Blog Deployment / Deploy-Staging (push) Successful in 9s
Blog Deployment / Test-Staging (push) Successful in 2s
Blog Deployment / Merge (push) Successful in 6s
Blog Deployment / Deploy-Production (push) Successful in 9s
Blog Deployment / Test-Production (push) Successful in 2s
Blog Deployment / Clean (push) Has been skipped
Blog Deployment / Notify (push) Successful in 3s
All checks were successful
Blog Deployment / Check-Rebuild (push) Successful in 5s
Blog Deployment / Build (push) Has been skipped
Blog Deployment / Deploy-Staging (push) Successful in 9s
Blog Deployment / Test-Staging (push) Successful in 2s
Blog Deployment / Merge (push) Successful in 6s
Blog Deployment / Deploy-Production (push) Successful in 9s
Blog Deployment / Test-Production (push) Successful in 2s
Blog Deployment / Clean (push) Has been skipped
Blog Deployment / Notify (push) Successful in 3s
This commit is contained in:
@@ -11,7 +11,7 @@ categories:
|
||||
|
||||
In one of my [previous article]({{< ref "post/3-terraform-create-vm-proxmox" >}}), I explained how to deploy Virtual Machines on Proxmox using Terraform from scratch.
|
||||
|
||||
Here I want to detail how to transform this piece of code in a reusable Terraform module. I will then show you how to modify your code to make use of it.
|
||||
Here I want to detail how to transform this piece of code in a reusable Terraform module. I will then show you how to modify your code to make use of it in other projects.
|
||||
|
||||
---
|
||||
## What is a Terraform Module?
|
||||
@@ -29,7 +29,218 @@ We will now transform the Terraform code from the [previous project]({{< ref "po
|
||||
|
||||
### Code Structure
|
||||
|
||||
Our module will live next to our project, in another folder:
|
||||
Our module will live next to our projects, in another folder:
|
||||
```plaintext
|
||||
terraform
|
||||
`-- modules
|
||||
`-- pve_vm
|
||||
|-- main.tf
|
||||
|-- provider.tf
|
||||
`-- variables.tf
|
||||
```
|
||||
|
||||
### Module's Code
|
||||
|
||||
Basically, the module files are those from the project we are transforming. I just kept out the parts related to the proxmox cluster, which will stay at the project level.
|
||||
|
||||
The module `pve_vm` will be decomposed in 3 files:
|
||||
- **main**: The core logic
|
||||
- **provider**: The providers needed to function
|
||||
- **variables**: The variables of the module
|
||||
|
||||
#### `main.tf`
|
||||
|
||||
```hcl
|
||||
data "proxmox_virtual_environment_vms" "template" {
|
||||
filter {
|
||||
name = "name"
|
||||
values = ["${var.vm_template}"]
|
||||
}
|
||||
}
|
||||
|
||||
resource "proxmox_virtual_environment_file" "cloud_config" {
|
||||
content_type = "snippets"
|
||||
datastore_id = "local"
|
||||
node_name = var.node_name
|
||||
source_raw {
|
||||
file_name = "${var.vm_name}.cloud-config.yaml"
|
||||
data = <<-EOF
|
||||
#cloud-config
|
||||
hostname: ${var.vm_name}
|
||||
package_update: true
|
||||
package_upgrade: true
|
||||
packages:
|
||||
- qemu-guest-agent
|
||||
users:
|
||||
- default
|
||||
- name: ${var.vm_user}
|
||||
groups: sudo
|
||||
shell: /bin/bash
|
||||
ssh-authorized-keys:
|
||||
- "${var.vm_user_sshkey}"
|
||||
sudo: ALL=(ALL) NOPASSWD:ALL
|
||||
runcmd:
|
||||
- systemctl enable qemu-guest-agent
|
||||
- reboot
|
||||
EOF
|
||||
}
|
||||
}
|
||||
|
||||
resource "proxmox_virtual_environment_vm" "vm" {
|
||||
name = var.vm_name
|
||||
node_name = var.node_name
|
||||
tags = var.vm_tags
|
||||
agent {
|
||||
enabled = true
|
||||
}
|
||||
stop_on_destroy = true
|
||||
clone {
|
||||
vm_id = data.proxmox_virtual_environment_vms.template.vms[0].vm_id
|
||||
node_name = data.proxmox_virtual_environment_vms.template.vms[0].node_name
|
||||
}
|
||||
bios = var.vm_bios
|
||||
machine = var.vm_machine
|
||||
cpu {
|
||||
cores = var.vm_cpu
|
||||
type = "host"
|
||||
}
|
||||
memory {
|
||||
dedicated = var.vm_ram
|
||||
}
|
||||
disk {
|
||||
datastore_id = var.node_datastore
|
||||
interface = "scsi0"
|
||||
size = 4
|
||||
}
|
||||
initialization {
|
||||
user_data_file_id = proxmox_virtual_environment_file.cloud_config.id
|
||||
datastore_id = var.node_datastore
|
||||
interface = "scsi1"
|
||||
ip_config {
|
||||
ipv4 {
|
||||
address = "dhcp"
|
||||
}
|
||||
}
|
||||
}
|
||||
network_device {
|
||||
bridge = "vmbr0"
|
||||
vlan_id = var.vm_vlan
|
||||
}
|
||||
operating_system {
|
||||
type = "l26"
|
||||
}
|
||||
vga {
|
||||
type = "std"
|
||||
}
|
||||
lifecycle {
|
||||
ignore_changes = [
|
||||
initialization
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
output "vm_ip" {
|
||||
value = proxmox_virtual_environment_vm.vm.ipv4_addresses[1][0]
|
||||
description = "VM IP"
|
||||
}
|
||||
```
|
||||
|
||||
#### `provider.tf`
|
||||
|
||||
```hcl
|
||||
terraform {
|
||||
required_providers {
|
||||
proxmox = {
|
||||
source = "bpg/proxmox"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### `variables.tf`
|
||||
|
||||
> ⚠️ The defaults are based on my environment, adapt them to yours.
|
||||
|
||||
```hcl
|
||||
variable "node_name" {
|
||||
description = "Proxmox host for the VM"
|
||||
type = string
|
||||
}
|
||||
|
||||
variable "node_datastore" {
|
||||
description = "Datastore used for VM storage"
|
||||
type = string
|
||||
default = "ceph-workload"
|
||||
}
|
||||
|
||||
variable "vm_template" {
|
||||
description = "Template of the VM"
|
||||
type = string
|
||||
default = "ubuntu-cloud"
|
||||
}
|
||||
|
||||
variable "vm_name" {
|
||||
description = "Hostname of the VM"
|
||||
type = string
|
||||
}
|
||||
|
||||
variable "vm_user" {
|
||||
description = "Admin user of the VM"
|
||||
type = string
|
||||
default = "vez"
|
||||
}
|
||||
|
||||
variable "vm_user_sshkey" {
|
||||
description = "Admin user SSH key of the VM"
|
||||
type = string
|
||||
default = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAID62LmYRu1rDUha3timAIcA39LtcIOny1iAgFLnxoBxm vez@bastion"
|
||||
}
|
||||
|
||||
variable "vm_cpu" {
|
||||
description = "Number of CPU cores of the VM"
|
||||
type = number
|
||||
default = 1
|
||||
}
|
||||
|
||||
variable "vm_ram" {
|
||||
description = "Number of RAM (MB) of the VM"
|
||||
type = number
|
||||
default = 2048
|
||||
}
|
||||
|
||||
variable "vm_bios" {
|
||||
description = "Type of BIOS used for the VM"
|
||||
type = string
|
||||
default = "ovmf"
|
||||
}
|
||||
|
||||
variable "vm_machine" {
|
||||
description = "Type of machine used for the VM"
|
||||
type = string
|
||||
default = "q35"
|
||||
}
|
||||
|
||||
variable "vm_vlan" {
|
||||
description = "VLAN of the VM"
|
||||
type = number
|
||||
default = 66
|
||||
}
|
||||
|
||||
variable "vm_tags" {
|
||||
description = "Tags for the VM"
|
||||
type = list(any)
|
||||
default = ["test"]
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
## Deploy a VM Using our Module
|
||||
|
||||
Now that we've moved all the resources required to deploy our VM into the `pve_vm` module, our project folder only needs to call that module and provide the necessary variables.
|
||||
|
||||
### Code Structure
|
||||
|
||||
For clarity, I've separated the modules and the projects:
|
||||
```plaintext
|
||||
terraform
|
||||
|-- modules
|
||||
@@ -45,10 +256,60 @@ terraform
|
||||
`-- variables.tf
|
||||
```
|
||||
|
||||
### Module
|
||||
### Project's Code
|
||||
|
||||
The module `pve_vm` will be decomposed in 3 files:
|
||||
- **main**: The core logic
|
||||
- **provider**: The providers needed to function
|
||||
- **variables**: The variables of the module
|
||||
In this example, I manually provide the values when calling my module, the others are related to the cluster
|
||||
#### `main.tf`
|
||||
|
||||
```hcl
|
||||
module "pve_vm" {
|
||||
source = "../../modules/pve_vm"
|
||||
node_name = "zenith"
|
||||
vm_name = "zenith-vm"
|
||||
vm_cpu = 2
|
||||
vm_ram = 2048
|
||||
vm_vlan = 66
|
||||
}
|
||||
|
||||
output "vm_ip" {
|
||||
value = module.pve_vm.vm_ip
|
||||
}
|
||||
```
|
||||
|
||||
#### `provider.tf`
|
||||
|
||||
```hcl
|
||||
terraform {
|
||||
required_providers {
|
||||
proxmox = {
|
||||
source = "bpg/proxmox"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
provider "proxmox" {
|
||||
endpoint = var.proxmox_endpoint
|
||||
api_token = var.proxmox_api_token
|
||||
insecure = false
|
||||
ssh {
|
||||
agent = false
|
||||
private_key = file("~/.ssh/id_ed25519")
|
||||
username = "root"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### `variables.tf`
|
||||
|
||||
```hcl
|
||||
variable "proxmox_endpoint" {
|
||||
description = "Proxmox URL endpoint"
|
||||
type = string
|
||||
}
|
||||
|
||||
variable "proxmox_api_token" {
|
||||
description = "Proxmox API token"
|
||||
type = string
|
||||
sensitive = true
|
||||
}
|
||||
```
|
Reference in New Issue
Block a user