Templates belong in the content library and is versioned and published to other vCenters. Packer can do the last piece of pushing into Content library as well.
Packer makes a new dated template each month in the library. Terraform’s data.tf decides which version new VMs get, with one line per name, and old versions are kept for rollback
I wiped my existing templates to start clean and created a local content library in vCenter called DC1-Library-Template on Datastore01…
The modified version is available in https://github.com/vmanalyst/privatecloud/tree/main/packer-vsphere/ubuntu

Part 1: Packer
Two small edits in ubuntu.pkr.hcl (and the same in rhel.pkr.hcl) and rest whatever we did in previous blog is same.
convert_to_template = true
Replace the convert line to our content library DC1-Library-Template
content_library_destination {
library = "DC1-Library-Template"
name = "${var.vm_name}-${formatdate("YYYY-MM-DD", timestamp())}"
destroy = true
}This “${var.vm_name}-${formatdate(“YYYY-MM-DD”, timestamp())}” matters. If the build VM has the same name as an existing template, packer build -force deletes that template to make room for the build.

Automating Redhat builds with Packer
That’s all that’s needed in packer so let’s just create a template so let navigate to our rhel folder
Validate the config using packer validate
packer validate -var-file=../vsphere.pkrvars.hcl -var-file=rocky9.pkrvars.hcl .
Build the VM template using packer build command
packer build -force -on-error=ask -var-file=../vsphere.pkrvars.hcl -var-file=rocky9.pkrvars.hcl .


VM template are checked in

Automating Ubuntu builds with Packer
Lets do the same to our ubuntu image so browse to ubuntu folder

alidate the config using packer validate
packer validate -var-file=../vsphere.pkrvars.hcl -var-file=ubuntu2204.pkrvars.hcl .
packer validate -var-file=../vsphere.pkrvars.hcl -var-file=ubuntu2604.pkrvars.hcl .
Build the VM template using packer build command for both Ubuntu 2024 and Ubuntu 2026
packer build -force -on-error=ask -var-file=../vsphere.pkrvars.hcl -var-file=ubuntu2204.pkrvars.hcl .packer build -force -on-error=ask -var-file=../vsphere.pkrvars.hcl -var-file=ubuntu2604.pkrvars.hcl .
All templates now checked in

Part 2: Terraform
Now the fun part. We can’t use the new template straight away; Terraform has to be told about it first.
This is my current terraform.tfvars.

All these VMs are on Ubuntu 24.04, because they don’t set os, so they use the default, ubuntu:

Tell Terraform about the new template
To build a new VM with the latest image on Ubuntu24/26 we need few tweaks to be done to Terraform
Go to data.tf. and give the exact template name we see on the vCenter here.


That’s all Terraform needs to know about the new template.
Terraform: protect the existing VMs first
Before pointing Terraform at the new templates, add one block to main.tf. Without it, changing a template name would make Terraform destroy and rebuild every VM that used the old one.
cd /etc/ansible/terraform/dev && pwd
Build a VM from it
Now let’s run terraform plan and see what happens. So, it says 7VM to be added and nothing to destroy

Run terraform plan -out=tfplan to add the plan to our save so this shows +7 VM to be added and no changes are going to be done on exisiting VM.

If it says anything else, stop and find out why before applying. Finally, apply the saved plan using terraform apply tfplan

That’s how you add a new OS version to Packer and then Terraform and build VMs from it safely.

Monthly updates and rollback
Next month’s build adds a new item next to this one, for example tpl-rocky9-2026-12-01. To move new VMs onto it, change the date on one line:
rocky = "tpl-rocky9-2026-12-01" # was 2026-11-01Then terraform plan, which should say No changes: existing VMs are untouched, and only VMs created from now on use December’s build.
To build a VM from an older version on purpose, give that version its own name in data.tf:
rocky = "tpl-rocky9-2026-12-01" # current
rocky_2611 = "tpl-rocky9-2026-11-01" # November build, kept on purposeThen choose it per VM in tfvars:
"app-new-01" = { ip = "172.16.11.184", os = "rocky" } # December build
"app-legacy-01" = { ip = "172.16.11.185", os = "rocky_2611" } # November buildA rollback works the same way: point rocky back at the previous date.
That’s it to versioning in Packer and building VM’s out via Terraform. Finally, Git it.

