[{"data":1,"prerenderedAt":991},["ShallowReactive",2],{"authors":3,"\u002Fblog\u002Fgolden-images-with-packer":26,"\u002Fblog\u002Fgolden-images-with-packer-surround":974,"\u002Fblog\u002Fgolden-images-with-packer-related":981},[4,16],{"id":5,"avatar":6,"bio":7,"extension":8,"github":9,"meta":10,"name":11,"role":12,"stem":13,"website":14,"__hash__":15},"authors\u002Fauthors\u002Fsagar-kapoor.yml","https:\u002F\u002Favatars.githubusercontent.com\u002Fu\u002F69609200?v=4",null,"yml","sagar-kap",{},"Sagar Kapoor","Chief Executive Officer (CEO)","authors\u002Fsagar-kapoor","https:\u002F\u002Fsagarkapoor.eu","NsLNClfs_S-SNHFEnyR6Vq_7zEc1Xxl7QLoKrk_DB6s",{"id":17,"avatar":18,"bio":7,"extension":8,"github":19,"meta":20,"name":21,"role":22,"stem":23,"website":24,"__hash__":25},"authors\u002Fauthors\u002Fsomraj-saha.yml","https:\u002F\u002Favatars.githubusercontent.com\u002Fu\u002F31373860?v=4","jarmos-san",{},"Somraj Saha","Chief Technology Officer (CTO)","authors\u002Fsomraj-saha","https:\u002F\u002Fjarmos.dev","mDAq8GgPZv9H8en0XHAraq3aYi_v8t2ULWGqp4cp3zY",{"id":27,"title":28,"author":29,"body":30,"category":964,"cover":965,"date":966,"description":967,"extension":968,"meta":969,"navigation":179,"path":970,"seo":971,"stem":972,"__hash__":973},"blog\u002Fblog\u002Fgolden-images-with-packer.md","Golden Images at Scale: Building Secure, Multi-Cloud VMs with Packer","somraj-saha",{"type":31,"value":32,"toc":957},"minimark",[33,38,47,70,85,93,96,99,108,111,115,122,125,134,137,791,794,797,800,803,810,814,817,820,823,826,829,832,843,846,850,853,860,874,877,890,893,896,900,903,906,931,934,947,950,953],[34,35,37],"h2",{"id":36},"the-multi-cloud-image-dilemma","The Multi-Cloud Image Dilemma",[39,40,41,42,46],"p",{},"When organizations start scaling across multiple cloud providers, they\ninevitably run in to a hidden bottleneck that management rarely anticipates:\n\"",[43,44,45],"strong",{},"the image pipeline","\".",[39,48,49,50,57,58,63,64,69],{},"At ",[51,52,56],"a",{"href":53,"rel":54},"https:\u002F\u002Fweburz.com",[55],"nofollow","Weburz",", we operate across\n",[51,59,62],{"href":60,"rel":61},"https:\u002F\u002Fazure.microsoft.com",[55],"Microsoft Azure"," and\n",[51,65,68],{"href":66,"rel":67},"https:\u002F\u002Fwww.vultr.com",[55],"Vultr"," for maximum redundancy and flexibility, in\ntheory. On the flip side, in practice, it often means our engineering teams are\ntrapped maintaining entirely separate, siloed processes for every single target\nenvironment. For the longest time, our own deployment velocity was crippled by\nthis exact fragmentation. We weren't slowed down by our own code nor were we\nslowed down by our CI\u002FCD pipelines. Instead what was slowing us down the most,\nwas the sheer weight of manual image configurations.",[39,71,72,73,78,79,84],{},"Early on, building a \"golden image\" meant logging into a base OS instance,\nmanually installing security patches, tweaking local firewall settings,\ninstalling monitoring agents and running a final capture command. This capture\ncommand, it's logic, definition and use cases differ by the vendor's ecosystem,\nfor e.g., Vultr has\n",[51,74,77],{"href":75,"rel":76},"https:\u002F\u002Fdocs.vultr.com\u002Fproducts\u002Fstorage\u002Fsnapshots",[55],"Snapshots"," and Azure has\n",[51,80,83],{"href":81,"rel":82},"https:\u002F\u002Flearn.microsoft.com\u002Fen-us\u002Fazure\u002Fvirtual-machines\u002Fazure-compute-gallery",[55],"Compute Gallery",".\nEach providing a unique feature set and an approach for creating\u002Fmanaging th\ngolden images.",[39,86,87,88,92],{},"Whenever it was time to update a vulnerability or perform a security audit, our\nteam had to scramble, replicating the same manual hardening steps across\ndistinct environments but the process is erroneous. Over time these minor\ninconsistencies compounded into a much larger threat:\n",[51,89,91],{"href":90},"\u002Fblog\u002Fmanaging-infrastructure-drift","infrastructure drift",".",[39,94,95],{},"In a multi-cloud image context drift meant, our staging images in Vultr didn't\nquite match the security hardening profile of our production images on Azure.\nEvery deployment turned into an anxious exercise of wondering if a specific\npackage version discrepancy or missing configuration tweak would break the build\nin region X, even though it passed in region Y.",[39,97,98],{},"Instead of shipping features, our engineers spent precious development hours\ntroubleshooting environment-specific quirks, patching drift manually or\nverifying compliance across fragmented artifact registries. Even though our\napplication delivery was automated, our infrastructure foundation was still\nstuck in The Stone Age.",[39,100,101,102,107],{},"To break free from this drag-on, we realised we had to stop treating multi-cloud\nimage creation as an ad-hoc chore and start treating it as a first-class node.\nThis realisation led us to start using\n",[51,103,106],{"href":104,"rel":105},"https:\u002F\u002Fdeveloper.hashicorp.com\u002Fpacker",[55],"HashiCorp Packer"," which allows us to\ndefine our image inputs once, eliminate drift at the source and finally match\nour cloud ambitions with true deployment velocity.",[39,109,110],{},"The rest of the article provides a brief overview of how we ended up using\nPacker to build\u002Fmanage our golden images for the cloud.",[34,112,114],{"id":113},"standardisation-by-design","Standardisation by Design",[39,116,117,118,121],{},"Once we recognized our manual configurations and drift were throttling our\ndeployment speed, the path forward became clear: we've to remove humans from the\ndirect build loop. The antidote to ad-hoc image creation is \"",[43,119,120],{},"Standardisation\nby Design","\", achieved by treating virtual machine images with the exact same\nrigor, version control, and automated pipelines. These practices are already\npracticed by software engineering teams for their application code and the same\nknowledge is applied for building our \"golden images\" as well.",[39,123,124],{},"Elsewhere, application developers often enjoyed the luxury of repeatable builds.\nTheir code was checked into Git (or some other version-control system), tested\nautomatically via CI\u002FCD, and pushed through pipelines with zero manual\nintervention. Meanwhile, our infrastructure teams were still treating server\nimages like special, hand-crafted artifacts.",[39,126,127,128,133],{},"The shift from manual configuration to Infrastructure-as-Code (IaC) changed that\ndynamic entirely for us. Instead of clicking through cloud consoles or running\nunversioned shell scripts on live instances, your golden image definition became\ndeclarative code in the form of\n",[51,129,132],{"href":130,"rel":131},"https:\u002F\u002Fgithub.com\u002Fhashicorp\u002Fhcl",[55],"HashiCorp Configuration Language (HCL)",".\nEvery package installation, security patch, system user, and configuration file\nis explicitly written out, reviewed in pull requests, and stored in a\nversion-controlled repository.",[39,135,136],{},"For reference, here's a sample of the Packer we use internally;",[138,139,144],"pre",{"className":140,"code":141,"language":142,"meta":143,"style":143},"language-hcl shiki shiki-themes github-dark","packer {\n  required_version = \"~> 1.16.0\"\n\n  required_plugins {\n    azure = {\n      version = \">= 1.4.0\"\n      source  = \"github.com\u002Fhashicorp\u002Fazure\"\n    }\n    vultr = {\n      version = \">= 2.3.2\"\n      source  = \"github.com\u002Fvultr\u002Fvultr\"\n    }\n  }\n}\n\nvariable \"image_version\" {\n  type    = string\n  default = \"1.0.0\"\n}\n\n# 1. Azure Builder Target (Debian 13 \u002F Trixie)\nsource \"azure-arm\" \"debian_azure\" {\n  subscription_id                   = \"your-azure-subscription-id\"\n  managed_image_resource_group_name = \"my-golden-images-rg\"\n  managed_image_name                = \"golden-debian-13-${var.image_version}\"\n\n  os_type         = \"Linux\"\n  image_publisher = \"Debian\"\n  image_offer     = \"debian-13\"\n  image_sku       = \"13-gen2\"\n\n  location        = \"East US\"\n  vm_size         = \"Standard_B2s\"\n  communicator    = \"ssh\"\n  ssh_username    = \"adminuser\"\n}\n\n# 2. Vultr Builder Target (Debian 13)\nsource \"vultr\" \"debian_vultr\" {\n  api_key       = \"your-vultr-api-key\"\n  region        = \"ewr\"        # New Jersey \u002F East Coast\n  plan_id       = \"vc2-1c-2gb\" # Instance size\n  os_id         = 2625         # Debian 13 x64 (trixie)\n  snapshot_name = \"golden-debian-13-${var.image_version}\"\n  ssh_username  = \"root\"\n}\n\n# Unified Build and Provisioning Pipeline\nbuild {\n  sources = [\n    \"source.azure-arm.debian_azure\",\n    \"source.vultr.debian_vultr\"\n  ]\n\n  # Common Security Hardening Provisioner\n  provisioner \"shell\" {\n    inline = [\n      \"echo 'Updating Debian 13 system packages...'\",\n      \"sudo apt-get update && sudo apt-get upgrade -y\",\n\n      \"echo 'Applying basic security hardening...'\",\n      \"sudo sed -i 's\u002F^PermitRootLogin.*\u002FPermitRootLogin no\u002F' \u002Fetc\u002Fssh\u002Fsshd_config\",\n      \"sudo systemctl restart ssh\",\n\n      \"echo 'Installing compliance and monitoring agents...'\",\n      \"sudo apt-get install -y auditd ufw\",\n      \"sudo ufw enable\"\n    ]\n  }\n}\n","hcl","",[145,146,147,160,174,181,189,199,211,222,228,238,248,258,263,269,275,280,292,304,315,320,325,332,346,358,369,398,403,415,426,438,450,455,467,478,489,500,505,510,516,529,540,554,568,582,604,615,620,625,631,639,650,659,665,671,676,682,693,703,711,719,724,732,740,748,753,761,769,775,781,786],"code",{"__ignoreMap":143},[148,149,152,156],"span",{"class":150,"line":151},"line",1,[148,153,155],{"class":154},"svObZ","packer",[148,157,159],{"class":158},"s95oV"," {\n",[148,161,163,166,170],{"class":150,"line":162},2,[148,164,165],{"class":158},"  required_version",[148,167,169],{"class":168},"snl16"," =",[148,171,173],{"class":172},"sU2Wk"," \"~> 1.16.0\"\n",[148,175,177],{"class":150,"line":176},3,[148,178,180],{"emptyLinePlaceholder":179},true,"\n",[148,182,184,187],{"class":150,"line":183},4,[148,185,186],{"class":154},"  required_plugins",[148,188,159],{"class":158},[148,190,192,195,197],{"class":150,"line":191},5,[148,193,194],{"class":158},"    azure",[148,196,169],{"class":168},[148,198,159],{"class":158},[148,200,202,205,208],{"class":150,"line":201},6,[148,203,204],{"class":158},"      version ",[148,206,207],{"class":168},"=",[148,209,210],{"class":172}," \">= 1.4.0\"\n",[148,212,214,217,219],{"class":150,"line":213},7,[148,215,216],{"class":158},"      source  ",[148,218,207],{"class":168},[148,220,221],{"class":172}," \"github.com\u002Fhashicorp\u002Fazure\"\n",[148,223,225],{"class":150,"line":224},8,[148,226,227],{"class":158},"    }\n",[148,229,231,234,236],{"class":150,"line":230},9,[148,232,233],{"class":158},"    vultr",[148,235,169],{"class":168},[148,237,159],{"class":158},[148,239,241,243,245],{"class":150,"line":240},10,[148,242,204],{"class":158},[148,244,207],{"class":168},[148,246,247],{"class":172}," \">= 2.3.2\"\n",[148,249,251,253,255],{"class":150,"line":250},11,[148,252,216],{"class":158},[148,254,207],{"class":168},[148,256,257],{"class":172}," \"github.com\u002Fvultr\u002Fvultr\"\n",[148,259,261],{"class":150,"line":260},12,[148,262,227],{"class":158},[148,264,266],{"class":150,"line":265},13,[148,267,268],{"class":158},"  }\n",[148,270,272],{"class":150,"line":271},14,[148,273,274],{"class":158},"}\n",[148,276,278],{"class":150,"line":277},15,[148,279,180],{"emptyLinePlaceholder":179},[148,281,283,286,290],{"class":150,"line":282},16,[148,284,285],{"class":154},"variable",[148,287,289],{"class":288},"sDLfK"," \"image_version\"",[148,291,159],{"class":158},[148,293,295,298,301],{"class":150,"line":294},17,[148,296,297],{"class":158},"  type",[148,299,300],{"class":168},"    =",[148,302,303],{"class":168}," string\n",[148,305,307,310,312],{"class":150,"line":306},18,[148,308,309],{"class":158},"  default",[148,311,169],{"class":168},[148,313,314],{"class":172}," \"1.0.0\"\n",[148,316,318],{"class":150,"line":317},19,[148,319,274],{"class":158},[148,321,323],{"class":150,"line":322},20,[148,324,180],{"emptyLinePlaceholder":179},[148,326,328],{"class":150,"line":327},21,[148,329,331],{"class":330},"sAwPA","# 1. Azure Builder Target (Debian 13 \u002F Trixie)\n",[148,333,335,338,341,344],{"class":150,"line":334},22,[148,336,337],{"class":154},"source",[148,339,340],{"class":288}," \"azure-arm\"",[148,342,343],{"class":288}," \"debian_azure\"",[148,345,159],{"class":158},[148,347,349,352,355],{"class":150,"line":348},23,[148,350,351],{"class":158},"  subscription_id",[148,353,354],{"class":168},"                   =",[148,356,357],{"class":172}," \"your-azure-subscription-id\"\n",[148,359,361,364,366],{"class":150,"line":360},24,[148,362,363],{"class":158},"  managed_image_resource_group_name",[148,365,169],{"class":168},[148,367,368],{"class":172}," \"my-golden-images-rg\"\n",[148,370,372,375,378,381,384,387,389,392,395],{"class":150,"line":371},25,[148,373,374],{"class":158},"  managed_image_name",[148,376,377],{"class":168},"                =",[148,379,380],{"class":172}," \"golden-debian-13-",[148,382,383],{"class":168},"${",[148,385,386],{"class":158},"var",[148,388,92],{"class":168},[148,390,391],{"class":158},"image_version",[148,393,394],{"class":168},"}",[148,396,397],{"class":172},"\"\n",[148,399,401],{"class":150,"line":400},26,[148,402,180],{"emptyLinePlaceholder":179},[148,404,406,409,412],{"class":150,"line":405},27,[148,407,408],{"class":158},"  os_type",[148,410,411],{"class":168},"         =",[148,413,414],{"class":172}," \"Linux\"\n",[148,416,418,421,423],{"class":150,"line":417},28,[148,419,420],{"class":158},"  image_publisher",[148,422,169],{"class":168},[148,424,425],{"class":172}," \"Debian\"\n",[148,427,429,432,435],{"class":150,"line":428},29,[148,430,431],{"class":158},"  image_offer",[148,433,434],{"class":168},"     =",[148,436,437],{"class":172}," \"debian-13\"\n",[148,439,441,444,447],{"class":150,"line":440},30,[148,442,443],{"class":158},"  image_sku",[148,445,446],{"class":168},"       =",[148,448,449],{"class":172}," \"13-gen2\"\n",[148,451,453],{"class":150,"line":452},31,[148,454,180],{"emptyLinePlaceholder":179},[148,456,458,461,464],{"class":150,"line":457},32,[148,459,460],{"class":158},"  location",[148,462,463],{"class":168},"        =",[148,465,466],{"class":172}," \"East US\"\n",[148,468,470,473,475],{"class":150,"line":469},33,[148,471,472],{"class":158},"  vm_size",[148,474,411],{"class":168},[148,476,477],{"class":172}," \"Standard_B2s\"\n",[148,479,481,484,486],{"class":150,"line":480},34,[148,482,483],{"class":158},"  communicator",[148,485,300],{"class":168},[148,487,488],{"class":172}," \"ssh\"\n",[148,490,492,495,497],{"class":150,"line":491},35,[148,493,494],{"class":158},"  ssh_username",[148,496,300],{"class":168},[148,498,499],{"class":172}," \"adminuser\"\n",[148,501,503],{"class":150,"line":502},36,[148,504,274],{"class":158},[148,506,508],{"class":150,"line":507},37,[148,509,180],{"emptyLinePlaceholder":179},[148,511,513],{"class":150,"line":512},38,[148,514,515],{"class":330},"# 2. Vultr Builder Target (Debian 13)\n",[148,517,519,521,524,527],{"class":150,"line":518},39,[148,520,337],{"class":154},[148,522,523],{"class":288}," \"vultr\"",[148,525,526],{"class":288}," \"debian_vultr\"",[148,528,159],{"class":158},[148,530,532,535,537],{"class":150,"line":531},40,[148,533,534],{"class":158},"  api_key",[148,536,446],{"class":168},[148,538,539],{"class":172}," \"your-vultr-api-key\"\n",[148,541,543,546,548,551],{"class":150,"line":542},41,[148,544,545],{"class":158},"  region",[148,547,463],{"class":168},[148,549,550],{"class":172}," \"ewr\"",[148,552,553],{"class":330},"        # New Jersey \u002F East Coast\n",[148,555,557,560,562,565],{"class":150,"line":556},42,[148,558,559],{"class":158},"  plan_id",[148,561,446],{"class":168},[148,563,564],{"class":172}," \"vc2-1c-2gb\"",[148,566,567],{"class":330}," # Instance size\n",[148,569,571,574,576,579],{"class":150,"line":570},43,[148,572,573],{"class":158},"  os_id",[148,575,411],{"class":168},[148,577,578],{"class":288}," 2625",[148,580,581],{"class":330},"         # Debian 13 x64 (trixie)\n",[148,583,585,588,590,592,594,596,598,600,602],{"class":150,"line":584},44,[148,586,587],{"class":158},"  snapshot_name",[148,589,169],{"class":168},[148,591,380],{"class":172},[148,593,383],{"class":168},[148,595,386],{"class":158},[148,597,92],{"class":168},[148,599,391],{"class":158},[148,601,394],{"class":168},[148,603,397],{"class":172},[148,605,607,609,612],{"class":150,"line":606},45,[148,608,494],{"class":158},[148,610,611],{"class":168},"  =",[148,613,614],{"class":172}," \"root\"\n",[148,616,618],{"class":150,"line":617},46,[148,619,274],{"class":158},[148,621,623],{"class":150,"line":622},47,[148,624,180],{"emptyLinePlaceholder":179},[148,626,628],{"class":150,"line":627},48,[148,629,630],{"class":330},"# Unified Build and Provisioning Pipeline\n",[148,632,634,637],{"class":150,"line":633},49,[148,635,636],{"class":154},"build",[148,638,159],{"class":158},[148,640,642,645,647],{"class":150,"line":641},50,[148,643,644],{"class":158},"  sources",[148,646,169],{"class":168},[148,648,649],{"class":158}," [\n",[148,651,653,656],{"class":150,"line":652},51,[148,654,655],{"class":172},"    \"source.azure-arm.debian_azure\"",[148,657,658],{"class":158},",\n",[148,660,662],{"class":150,"line":661},52,[148,663,664],{"class":172},"    \"source.vultr.debian_vultr\"\n",[148,666,668],{"class":150,"line":667},53,[148,669,670],{"class":158},"  ]\n",[148,672,674],{"class":150,"line":673},54,[148,675,180],{"emptyLinePlaceholder":179},[148,677,679],{"class":150,"line":678},55,[148,680,681],{"class":330},"  # Common Security Hardening Provisioner\n",[148,683,685,688,691],{"class":150,"line":684},56,[148,686,687],{"class":154},"  provisioner",[148,689,690],{"class":288}," \"shell\"",[148,692,159],{"class":158},[148,694,696,699,701],{"class":150,"line":695},57,[148,697,698],{"class":158},"    inline",[148,700,169],{"class":168},[148,702,649],{"class":158},[148,704,706,709],{"class":150,"line":705},58,[148,707,708],{"class":172},"      \"echo 'Updating Debian 13 system packages...'\"",[148,710,658],{"class":158},[148,712,714,717],{"class":150,"line":713},59,[148,715,716],{"class":172},"      \"sudo apt-get update && sudo apt-get upgrade -y\"",[148,718,658],{"class":158},[148,720,722],{"class":150,"line":721},60,[148,723,180],{"emptyLinePlaceholder":179},[148,725,727,730],{"class":150,"line":726},61,[148,728,729],{"class":172},"      \"echo 'Applying basic security hardening...'\"",[148,731,658],{"class":158},[148,733,735,738],{"class":150,"line":734},62,[148,736,737],{"class":172},"      \"sudo sed -i 's\u002F^PermitRootLogin.*\u002FPermitRootLogin no\u002F' \u002Fetc\u002Fssh\u002Fsshd_config\"",[148,739,658],{"class":158},[148,741,743,746],{"class":150,"line":742},63,[148,744,745],{"class":172},"      \"sudo systemctl restart ssh\"",[148,747,658],{"class":158},[148,749,751],{"class":150,"line":750},64,[148,752,180],{"emptyLinePlaceholder":179},[148,754,756,759],{"class":150,"line":755},65,[148,757,758],{"class":172},"      \"echo 'Installing compliance and monitoring agents...'\"",[148,760,658],{"class":158},[148,762,764,767],{"class":150,"line":763},66,[148,765,766],{"class":172},"      \"sudo apt-get install -y auditd ufw\"",[148,768,658],{"class":158},[148,770,772],{"class":150,"line":771},67,[148,773,774],{"class":172},"      \"sudo ufw enable\"\n",[148,776,778],{"class":150,"line":777},68,[148,779,780],{"class":158},"    ]\n",[148,782,784],{"class":150,"line":783},69,[148,785,268],{"class":158},[148,787,789],{"class":150,"line":788},70,[148,790,274],{"class":158},[39,792,793],{},"When VM images are defined as code, the concept of \"patching a running server\"\nbegins to disappear in favor of immutable infrastructure.",[39,795,796],{},"If a package needs an update, we no longer need to SSH into production to fix\nit. Instead, we update our Packer template, trigger a pipeline build, and roll\nout a brand-new, pristine image version (while optionally replacing\u002Fpruning the\nolder versions).",[39,798,799],{},"The operational benefits we observed from this were tremendous! Just like\napplication binaries, our golden images now carry semantic versions and commit\nhashes. If a newly deployed image introduces an unexpected behavior, rolling\nback is as simple as reverting to the previous image tag in our deployment\nmanifest.",[39,801,802],{},"This code-first mentality is where a tool like Packer shines. By abstracting\naway the platform-specific APIs of Azure, and Vultr into a single configuration\nblock, Packer allowed us to codify our organization's compliance baselines and\nsecurity hardening rules once.",[39,804,805,806,809],{},"When a security compliance rule changes, we update a set of HCL files (and some\nscripts). A single ",[145,807,808],{},"packer build"," command then cascades those updates across\nevery cloud environment simultaneously. By standardizing our image pipeline, we\nturned infrastructure from an unpredictable operational hurdle into a\npredictable, automated extension of our software delivery lifecycle.",[34,811,813],{"id":812},"baking-in-security-early","Baking in Security Early:",[39,815,816],{},"Historically, security was treated as a gate at the end of the deployment\nlifecycle. It was treated as a final audit or manual review conducted just\nbefore an application went live. In a fast-paced multi-cloud environment, this\nlate-stage approach fails. If security vulnerabilities or compliance gaps are\nonly discovered after an image is deployed to production, remediation becomes a\npainful, high-friction scramble.",[39,818,819],{},"To achieve true velocity without compromising safety, we embraced a \"shift-left\"\nsecurity model, baking hardening, scanning, and compliance checks directly into\nthe image build pipeline from day one.",[39,821,822],{},"Relying on manual application of Centre for Internet Security (CIS) benchmarks\nor internal hardening guides is a recipe for inconsistency. Human error almost\nguarantees that a critical setting, such as disabling root logins, tightening\nSSH configurations, or configuring audit logging, will be missed on at least one\ncloud target.",[39,824,825],{},"Our approach to deal with such shortcomings is by embedding hardening\nprovisioning steps (using tools like Ansible, or custom shell scripts) directly\ninto our Packer workflow to make our security compliance deterministic. Thereby,\nevery time an image builds, it undergoes the exact same automated hardening\nsequence. If a compliance check fails during the build script, the pipeline\nhalts immediately, preventing insecure artifacts from ever reaching an image\nregistry.",[39,827,828],{},"For teams or companies whose IT security compliance enforces further strict\nmeasures, building a hardened base image is only half the battle. Since software\npackages age quickly, and zero-day vulnerabilities emerge daily rather than\nwaiting for a runtime scanner to flag a vulnerable production server, security\nteams can integrate vulnerability scanning tools directly into the CI\u002FCD\npipeline. All this is only possible thanks to how Packer integrates with the\nentire cloud vendor's ecosystem.",[39,830,831],{},"Using tools that inspect container images, AMIs, or VM disks before they are\npublished, you can automatically screen packages for known Common\nVulnerabilities and Exposures (CVEs). If a package exceeds an acceptable risk\nthreshold (e.g., a critical or high-severity CVE), the pipeline can\nautomatically fail the build, notifying developers and platform engineers to\nupdate their base packages before promotion.",[39,833,834,837,838,842],{},[43,835,836],{},"DISCLAIMER",": Although we've setup such strict compliance enforcements for our\nrequirements (yet) we're mentioning the practice for our readers' reference.\nSome time in the near future we ",[839,840,841],"em",{},"may"," implement such strict compliance\nenforcements in our pipelines. When or if we do so, we will document the process\nand requirements of it as well.",[39,844,845],{},"So, the keynote from this section is; when security is baked into the automated\nbuild pipeline, compliance shifts from an anxious, manual audit preparation\nchore into a continuous, repeatable process. This allows every image to be\ntraceable back to a version-controlled IaC configuration and passes automated\nsecurity gates prior to publication, proving compliance becomes effortless.",[34,847,849],{"id":848},"scaling-the-pipeline","Scaling the Pipeline:",[39,851,852],{},"With our standardized image definitions and embedded security guardrails, the\nfinal operational hurdle is scale. If our engineering teams has to trigger\nbuilds for Azure, and Vultr sequentially, or worse, maintain separate scripts\nfor each provider, our delivery pipeline remains bottlenecked by sheer\nlogistics. On the contrary, true multi-cloud scalability requires orchestrating\nbuilds across disparate cloud ecosystems simultaneously, all driven by a single\nsource of truth.",[39,854,855,856,859],{},"The core philosophy of HashiCorp Packer is \"",[839,857,858],{},"Write Once, Build Everywhere","\". So,\ninstead of writing separate provisioning logic for each cloud provider, you\ndefine your base operating system, packages, security hardening scripts, and\nconfigurations in a single template file.",[39,861,862,863,866,867,870,871,873],{},"Packer's multi-builder architecture allows you to declare multiple targets, such\nas an ",[145,864,865],{},"azure-arm"," builder, a ",[145,868,869],{},"vultr"," builder all within the same set of\nconfigurations. Thereafter, whenever we execute a single ",[145,872,808],{}," command,\nPacker simultaneously provisions, hardens, and captures images across all three\nclouds in parallel.",[39,875,876],{},"By unifying your image pipeline under a single, centralized configuration:",[878,879,880,884,887],"ul",{},[881,882,883],"li",{},"Our provisioning logic is written once and shared universally across all\ntarget environments thereby adhering to the Don't Repeat Yourself (DRY)\nprinciples.",[881,885,886],{},"We eliminate the risk of forgetting to update one cloud provider's script\nwhile updating another.",[881,888,889],{},"Updates to software versions or security patches are managed in one place,\ninstantly propagating to every cloud platform during the next scheduled\npipeline run.",[39,891,892],{},"By hooking our Packer multi-builder configuration into our CI\u002FCD platform\n(usually GitHub Actions), image creation became a complete automated event\ntriggered by code commits, dependency updates, or scheduled cron jobs.",[39,894,895],{},"When a security patch is released, our CI\u002FCD system automatically kicks off a\nparallel build across Azure, and Vultr, runs compliance validations, and\npublishes the resulting golden images to their respective cloud registries\nbefore the engineering team even starts their workday. By orchestrating our\nmulti-cloud pipeline this way, we transformed image management from a heavy\noperational burden into a seamless, high-speed automated engine.",[34,897,899],{"id":898},"lessons-learned-and-whats-next","Lessons Learned and What's Next:",[39,901,902],{},"Transitioning from ad-hoc, manual image building to an automated, multi-cloud\npipeline powered by HashiCorp Packer was a cultural shift as much as a technical\none. Looking back on our journey from the initial multi-cloud dilemma to a fully\nstandardized image factory, a few hard-won lessons stand out-along with some\nmassive performance wins.",[39,904,905],{},"Some of the pitfalls we successfully avoided are:",[907,908,909,916,925,928],"ol",{},[881,910,911,912,915],{},"The \"",[839,913,914],{},"Just SSH and Fix It","\" Trap during our initial journey where it was\ntempting to log into running instances to troubleshoot or patch\nconfigurations directly. We quickly learned that treating instances like\ncattle meant breaking this habit entirely. If something was wrong, the fix\nhad to happen in the code, forcing us to maintain discipline across our IaC\nrepositories.",[881,917,918,919,924],{},"In the beginning, our provisioning scripts tried to do too much, resulting in\nbloated, fragile builds. We learned to keep our base images lean, leveraging\nPacker primarily for foundational hardening and security agents, while\nleaving application-specific layers to runtime deployment tools (such as\n",[51,920,923],{"href":921,"rel":922},"https:\u002F\u002Fwww.docker.com",[55],"Docker",").",[881,926,927],{},"As we added more hardening steps, our build times crept up. We had to learn\nhow to optimize our pipeline caching and leverage parallel multi-builder\nexecution so that security checks didn't bottleneck developer velocity.",[881,929,930],{},"Because our automated pipeline churned out fresh, secure images on a regular\nschedule, we quickly accumulated hundreds of older VM Images and snapshots\nacross our cloud accounts. Left unchecked, this invisible buildup of obsolete\nartifacts began inflating our monthly cloud storage bills, forcing us to\nquickly implement automated retention and cleanup policies.",[39,932,933],{},"Regardless of the pitfalls, we did notice some performance wins in our\napplication deployment pipelines as well thanks to the Packer integrations, such\nas:",[907,935,936,939,944],{},[881,937,938],{},"What used to take hours of manual coordination across multiple cloud\nconsoles now happens in a matter of minutes, entirely hands-off via our\nCI\u002FCD triggers.",[881,940,941,942,92],{},"By stamping out manual configurations and centralizing our templates, we\neliminated the environment-specific anomalies that used to plague our\nstaging-to-production promotions due to\n",[51,943,91],{"href":90},[881,945,946],{},"Compliance reviews that once triggered weeks of anxious preparation are now\nstreamlined. We can point directly to version-controlled Packer manifests\nand automated scan logs to prove our security posture.",[39,948,949],{},"Having conquered the multi-cloud image dilemma, our sights are now set on the\nnext frontier. We are currently exploring automated image lifecycle\npolicies-automatically deprecating and aging out older VM Images and snapshots\nto ensure teams are always pulling the absolute freshest, most secure baselines.\nWe are also looking into tighter integration with service meshes and ephemeral\ntesting frameworks to validate image integrity instantly upon build completion.",[39,951,952],{},"By treating our VM images as code, we didn't just speed up our deployments; we\nbuilt a resilient foundation for the future of our engineering organization. We\nalso hope by sharing the knowledge we gathered from our venture might help you,\nyour team or your company out as well.",[954,955,956],"style",{},"html pre.shiki code .svObZ, html code.shiki .svObZ{--shiki-default:#B392F0}html pre.shiki code .s95oV, html code.shiki .s95oV{--shiki-default:#E1E4E8}html pre.shiki code .snl16, html code.shiki .snl16{--shiki-default:#F97583}html pre.shiki code .sU2Wk, html code.shiki .sU2Wk{--shiki-default:#9ECBFF}html pre.shiki code .sDLfK, html code.shiki .sDLfK{--shiki-default:#79B8FF}html pre.shiki code .sAwPA, html code.shiki .sAwPA{--shiki-default:#6A737D}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}",{"title":143,"searchDepth":162,"depth":162,"links":958},[959,960,961,962,963],{"id":36,"depth":162,"text":37},{"id":113,"depth":162,"text":114},{"id":812,"depth":162,"text":813},{"id":848,"depth":162,"text":849},{"id":898,"depth":162,"text":899},"Infrastructure","\u002Fblog\u002Fgolden-images-with-packer.webp","2026-09-11","Learn how to build secure, immutable Debian 13 golden images for Microsoft Azure and Vultr simultaneously using HashiCorp Packer, Infrastructure-as-Code, and CI\u002FCD automation.","md",{},"\u002Fblog\u002Fgolden-images-with-packer",{"title":28,"description":967},"blog\u002Fgolden-images-with-packer","ew95K3Oc_zPnupWbuRibT1m2-OXwzOXvHFGEbDtErTk",[7,975],{"title":976,"path":977,"stem":978,"description":979,"date":980,"children":-1},"Hello, world","\u002Fblog\u002Fhello-world","blog\u002Fhello-world","Kicking off the Weburz engineering blog — what to expect, and why we're publishing in the open.","2026-05-22",[982,985,989],{"path":90,"title":983,"date":984,"category":964},"Managing Infrastructure Drift: How Packer, Terraform, and Ansible Keep Enterprise Environments Compliant","2026-09-03",{"path":986,"title":987,"date":988,"category":964},"\u002Fblog\u002Fsecurely-self-hosting-postgresql","Securely Self-Hosting PostgreSQL: Configuration, Backups, and Best Practices","2026-08-10",{"path":977,"title":976,"date":980,"category":990},"People",1789725857503]