Infrastructure-as-Code project that automates the creation and configuration of virtual machines on a Proxmox VE hypervisor using Terraform, with a remote MinIO (S3-compatible) backend for state storage and a GitLab CI/CD pipeline for validation, planning, and deployment.
This repository was built as a home-lab project to practice real-world IaC workflows: remote state management, CI/CD automation, secrets handling, and VM bootstrapping with cloud-init.
Running this project will:
- Connect to a Proxmox VE node via its API.
- Clone a pre-existing VM template (defined by
template_vm_id). - Provision one or more VMs with custom CPU, memory, disk, and networking settings.
- Bootstrap each VM on first boot using cloud-init, which:
- Sets the hostname and FQDN
- Creates a default
ubuntuuser with sudo access - Enables SSH password authentication
- Installs packages required for Active Directory / domain integration (
realmd,sssd,adcli,krb5-user, etc.) and the QEMU guest agent
- Store the Terraform state remotely in a self-hosted MinIO bucket (S3-compatible), instead of locally, so state is shared and safe.
- Output the resulting VM IP addresses and VM IDs.
All of this is wired into a GitLab CI/CD pipeline with validate → plan → apply stages, where apply requires a manual trigger.
GitLab CI/CD ──▶ Terraform ──▶ Proxmox VE API ──▶ New VM(s)
│ │
▼ ▼
MinIO (S3 backend) cloud-init bootstrap
stores terraform.tfstate (hostname, user, packages)
| File | Purpose |
|---|---|
main.tf |
Defines the cloud-init user-data snippet and the Proxmox VM resource, iterating over every VM declared in vms |
variables.tf |
Declares all input variables (Proxmox node, template ID, VM map, sensitive credentials) |
terraform.tfvars |
Defines the actual VMs to create (cores, memory, disk, IP, gateway, DNS) |
provider.tf |
Configures the bpg/proxmox provider and the S3/MinIO remote backend |
outputs.tf |
Prints the resulting VM IP addresses and VM IDs after apply |
.gitlab-ci.yml |
CI/CD pipeline: validate → plan → apply |
- A running Proxmox VE server with an API token generated for Terraform
- A VM template already created in Proxmox (its ID goes into
template_vm_id) - A self-hosted or cloud MinIO instance (or any S3-compatible storage) to hold the Terraform state
- Terraform
>= 1.xinstalled locally, or GitLab CI/CD runners for automated execution - SSH access from the Terraform host to the Proxmox node (used by the
sshblock of the provider for actions the API doesn't cover)
Edit terraform.tfvars and describe each VM you want created:
vms = {
"web01" = {
cores = 2
memory = 2048
disk_gb = 40
ip_cidr = "192.170.70.31/24"
gateway = "192.170.70.254"
dns = "192.170.70.3"
}
}Add as many entries as needed — every key becomes both the VM name and its hostname.
The following variables are sensitive and must never be committed to the repository. Provide them via environment variables, a terraform.tfvars file excluded from Git, or (recommended) CI/CD secret variables:
| Variable | Description |
|---|---|
minio_password |
Secret key used to authenticate against the MinIO S3 backend |
proxapitoken |
Proxmox API token used to authenticate the provider |
proxsshpass |
SSH password for the Proxmox host (used by the provider's SSH helper) |
In provider.tf, update:
- The MinIO endpoint (
endpoints.s3) and bucket name to match your storage server - The Proxmox
endpointURL and SSHusernameto match your environment
export TF_VAR_minio_password="your-minio-password"
export TF_VAR_proxapitoken="your-proxmox-api-token"
export TF_VAR_proxsshpass="your-proxmox-ssh-password"
terraform init
terraform validate
terraform plan
terraform applyThe pipeline (.gitlab-ci.yml) runs automatically on every push:
- validate – runs
terraform validateto catch syntax/config errors - plan – runs
terraform planto preview changes - apply – runs
terraform apply -auto-approve, but requires a manual trigger in the GitLab UI (safe for a lab environment; a production setup would add an approval gate before this stage)
Set the following as masked/protected CI/CD variables in your GitLab project settings (Settings → CI/CD → Variables):
MINIO_PASSWORDPROXAPITOKENPROXSSHPASS
The pipeline maps them automatically to the Terraform variables via TF_VAR_*.
After a successful apply, Terraform prints:
vm_ips– a map of VM name → assigned IPv4 addressvm_ids– a map of VM name → Proxmox VM ID
- All credentials (Proxmox API token, SSH password, MinIO secret key) are passed as sensitive Terraform variables and injected via CI/CD secret variables — never hard-coded.
- The default cloud-init configuration sets a simple password for the
ubuntuuser for convenience in a lab setting. Replace this with SSH-key-only authentication before using this in any real environment. - TLS verification is disabled (
insecure = true) for the Proxmox provider, which is acceptable for a self-signed home-lab certificate but should be enabled with a valid certificate in production.
- Replace password-based cloud-init auth with SSH key injection
- Add an
approvestage beforeapplyfor production use - Parameterize the Proxmox/MinIO endpoints instead of hard-coding them
- Add per-environment
tfvarsfiles (dev/staging/prod) and matching state keys
Terraform · Proxmox VE (via the bpg/proxmox provider) · MinIO (S3-compatible remote state) · cloud-init · GitLab CI/CD
Built as a personal home-lab / portfolio project to demonstrate Infrastructure as Code, remote state management, secrets handling, and CI/CD pipeline design.