VM Images as Release Artifacts: What Dev Teams Taught Operations
Software developers figured out something operations teams were slow to learn: reproducibility comes from artifacts, not procedures. A compiled binary is reproducible because the build system produces it deterministically from source code. An application deployment is reproducible because the same binary gets applied to each environment. The procedure for deploying is much less important than the artifact being deployed.
In 2013, I started applying this principle explicitly to Azure VM infrastructure, and it changed how I thought about environment management in a way that's still relevant today.
The Procedure vs Artifact Distinction
An operations runbook is a procedure: "Step 1: Attach a data disk. Step 2: Format with 64KB allocation units. Step 3: Set SQL Server max memory to total RAM minus 4GB." Procedures have execution risk. Someone follows step 3 before step 1. Someone uses a different formula for max memory. Someone skips the allocation unit size because they don't understand why it matters. Two environments built from the same procedure will drift.
An artifact is a thing you apply: "Deploy VM from image sql-server-2012-sp2-baseline-v1.3.vhd." The image embodies the procedure's output. It doesn't have execution risk because there's nothing to execute — you just use it. The variance comes from how the image was built, not from how it's applied.
The VM Image as a Release Artifact
The golden image workflow I described earlier becomes much more powerful when you treat image creation as a formal process with the same rigor you'd apply to a software release:
- Version the image. Name images with version numbers:
sql-server-baseline-v1.0,sql-server-baseline-v1.1. When a patch level changes or a configuration standard is updated, create a new version. Don't overwrite the old image — you want to be able to roll back to a known-good baseline. - Document what changed between versions. A changelog for infrastructure images. What patches are included? What configuration changes from v1.0 to v1.1? This sounds like overhead, but when you're troubleshooting a regression and trying to understand what changed between the VM that was working and the VM that isn't, this documentation is exactly what you need.
- Test the image before releasing it. Provision a test VM from the new image. Run your baseline monitoring queries. Confirm performance is within expected ranges. Confirm backup jobs run. Confirm connectivity from the application tier. Pass the checklist before using the image for any production provisioning.
- Store images in Azure Blob Storage with access controls. Image files are large and shouldn't be ephemeral. Treat them like build artifacts: retained for a minimum period, access-controlled, and stored in a storage account that's backed up.
The Version Pinning Benefit
When you version images, you can pin environments to specific versions. Production might be on sql-server-baseline-v1.2 while dev is on v1.3 (the version with the latest Windows patches). If something behaves differently between dev and production, the image version is an explicit variable in your investigation — not a mystery about what was installed when.
This is exactly what application developers get from semantic versioning and package managers. "It works in dev but not in prod" becomes "it works on v1.3 of the image but not v1.2" — a concrete, investigable difference rather than an environmental mystery.
The Handoff Between Roles
The image as artifact model also changed how infrastructure work could be handed off. When infrastructure state was implicit — it's what's installed on the server, and only the person who installed it knows exactly what that is — knowledge transfer required shadowing and tribal knowledge. When infrastructure state is explicit in an image and its changelog, a new person on the team can understand the environment by reading the documentation, not by asking the person who set it up.
Dev teams learned this with containers a few years later, and it stuck. The VHD image was the pre-Docker equivalent of a container image — same concept, different form factor, same payoff. As always, I'm here to help.