Versioned VM Templates with Packer and the vSphere Content Library

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-01

Then 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 purpose

Then 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 build

A 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.

(Visited 5 times, 1 visits today)

By Ash Thomas

Ash Thomas is a seasoned IT professional with extensive experience as a technical expert, complemented by a keen interest in blockchain technology.