{"id":3316,"date":"2023-01-13T18:23:06","date_gmt":"2023-01-14T00:23:06","guid":{"rendered":"https:\/\/microsoftgeek.com\/?p=3316"},"modified":"2023-01-13T18:23:06","modified_gmt":"2023-01-14T00:23:06","slug":"using-packer-to-build-windows-images-on-azure","status":"publish","type":"post","link":"https:\/\/microsoftgeek.com\/?p=3316","title":{"rendered":"Using Packer to build Windows Images (on Azure)"},"content":{"rendered":"\n<h2 class=\"wp-block-heading\">What is Packer?<\/h2>\n\n\n\n<p><a href=\"https:\/\/www.packer.io\/\" target=\"_blank\" rel=\"noreferrer noopener\">Packer\u00a0<\/a>is a tool developed by Hashicorp (the company behind Terraform) that makes it easy to automate the creation of custom images (or AMIs in AWS). The tool itself will automate the creation of VMs, will run whatever scripts you want it to run, and will then instruct your cloud provider to take that image. Packer itself is a client-side tool which will connect to your cloud provider to do the configuration.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Setting up Packer on your machine<\/h2>\n\n\n\n<p>I mentioned that Packer is a client-side tool. It\u2019s a simple binary; installing it is as simple as downloading the binary and adding it your path. I decided to copy it to \/usr\/local\/bin \u2013 but you can store it anywhere on your path.<\/p>\n\n\n\n<p><em>btw. If you\u2019re planning to run this in the Azure cloud shell, Packer comes pre-installed.<\/em><\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>mkdir packer\ncd packer\nwget https:\/\/releases.hashicorp.com\/packer\/1.4.3\/packer_1.4.3_linux_amd64.zip\nunzip packer_1.4.3_linux_amd64.zip\nsudo mv packer \/usr\/local\/bin\ncd ..\nrm -rf packer<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">Creating a first simple Windows Image on Azure<\/h2>\n\n\n\n<p>Let\u2019s start with creating a simple Windows image. To get packer to run, it needs access to a service principal and a RBAC rights to your subscription. When Packer builds the image, it will create a new resource group, deploy the machine, create the image and then destroy that resource group, hence it requires RBAC. The following script will do that:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>rgName=\"image-rg\"\nlocation=\"westus2\"\naz group create -n $rgName -l $location\naz ad sp create-for-rbac --name nf-packer-windows\n#note down appid and password\nappid=yourappid\nsppassword=yourpassword<\/code><\/pre>\n\n\n\n<p>Ok, with that done, we can go ahead and create our first Packer image. The json definition below will install IIS on a Windows machine and sysprep it.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>{\n  \"builders\": &#91;{\n    \"type\": \"azure-arm\",\n\n    \"client_id\": \"xxx\",\n    \"client_secret\": \"xxx\",\n    \"tenant_id\": \"you can get this via az account show\",\n    \"subscription_id\": \"you can get this via az account list\",\n\n    \"managed_image_resource_group_name\": \"image-rg\",\n    \"managed_image_name\": \"packer-windows\",\n\n    \"os_type\": \"Windows\",\n    \"image_publisher\": \"MicrosoftWindowsServer\",\n    \"image_offer\": \"WindowsServer\",\n    \"image_sku\": \"2016-Datacenter\",\n\n    \"communicator\": \"winrm\",\n    \"winrm_use_ssl\": true,\n    \"winrm_insecure\": true,\n    \"winrm_timeout\": \"5m\",\n    \"winrm_username\": \"packer\",\n\n\n    \"location\": \"westus2\",\n    \"vm_size\": \"Standard_DS2_v2\"\n  }],\n  \"provisioners\": &#91;{\n    \"type\": \"powershell\",\n    \"inline\": &#91;\n      \"Add-WindowsFeature Web-Server\",\n      \"&amp; $env:SystemRoot\\\\System32\\\\Sysprep\\\\Sysprep.exe \/oobe \/generalize \/quiet \/quit\",\n      \"while($true) { $imageState = Get-ItemProperty HKLM:\\\\SOFTWARE\\\\Microsoft\\\\Windows\\\\CurrentVersion\\\\Setup\\\\State | Select ImageState; if($imageState.ImageState -ne 'IMAGE_STATE_GENERALIZE_RESEAL_TO_OOBE') { Write-Output $imageState.ImageState; Start-Sleep -s 10  } else { break } }\"\n    ]\n  }]\n}<\/code><\/pre>\n\n\n\n<p>To build this image, you use the following command in your shell:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>packer build simpleimage.json<\/code><\/pre>\n\n\n\n<p>This will take about 15 minutes to complete, and afterwards you should see your image pop up:<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img decoding=\"async\" src=\"https:\/\/blog.nillsf.com\/wp-content\/uploads\/2019\/08\/2019-08-20-13_49_06-image-rg-Microsoft-Azure-and-6-more-pages-Microsoft-Edge.jpg\" alt=\"\" class=\"wp-image-301\"\/><figcaption class=\"wp-element-caption\">The image packer built will show up in your images list.<\/figcaption><\/figure>\n\n\n\n<p>If you are curious like I am, I also had a peek in the resource group Packer created. There\u2019s 1 strange thing to see there, which an Azure Key Vault. I had a quick look in the\u00a0<a href=\"https:\/\/github.com\/hashicorp\/packer\/blob\/ce71932f956116cd1f6ffc274a9edf09a508f3b3\/builder\/azure\/common\/template\/template_builder.go\" target=\"_blank\" rel=\"noreferrer noopener\">Packer source code<\/a>\u00a0to see what that was used, and it appears this Key Vault is used to store the certificate used by WinRM.<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img decoding=\"async\" src=\"https:\/\/blog.nillsf.com\/wp-content\/uploads\/2019\/08\/2019-08-20-12_10_12-packer-Resource-Group-75d8ebch4w-Microsoft-Azure-and-1-more-page-Microsoft-E.jpg\" alt=\"\" class=\"wp-image-302\"\/><\/figure>\n\n\n\n<h2 class=\"wp-block-heading\">Adding an external script to your template \u2013 making image names unique<\/h2>\n\n\n\n<p>In our previous example, we did a relatively simple deployment \u2013 where we used an inline script to do our configuration. In a more complex setup, you\u2019ll probably want to use an external script that is developed independently from your Packer template.<\/p>\n\n\n\n<p>This is where the file operator comes in handy. You\u2019ll find a quick example below of copying a PowerShell script from your local machine to Packer which Packer will execute on the target machine.<\/p>\n\n\n\n<p>Another thing I changed in the file is I added a timestamp to our image name. This comes in handy when you\u2019re building multiple versions and you need to maintain unique image names.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>{\n  \"builders\": &#91;{\n    \"type\": \"azure-arm\",\n\n    \"client_id\": \"xxx\",\n    \"client_secret\": \"xxx\",\n    \"tenant_id\": \"72f988bf-86f1-41af-91ab-2d7cd011db47\",\n    \"subscription_id\": \"d19dddf3-9520-4226-a313-ae8ee08675e5\",\n\n    \"managed_image_resource_group_name\": \"image-rg\",\n    \"managed_image_name\": \"worker-{{timestamp}}\",\n\n    \"os_type\": \"Windows\",\n    \"image_publisher\": \"MicrosoftWindowsServer\",\n    \"image_offer\": \"WindowsServer\",\n    \"image_sku\": \"2016-Datacenter\",\n\n    \"communicator\": \"winrm\",\n    \"winrm_use_ssl\": true,\n    \"winrm_insecure\": true,\n    \"winrm_timeout\": \"5m\",\n    \"winrm_username\": \"packer\",\n\n\n    \"location\": \"westus2\",\n    \"vm_size\": \"Standard_DS2_v2\"\n  }],\n  \"provisioners\": &#91;\n    {\n      \"type\": \"file\",\n      \"source\": \"worker.ps1\",\n      \"destination\": \"c:\\\\worker.ps1\",\n      \"direction\": \"upload\"\n     },\n    {\n    \"type\": \"powershell\",\n    \"inline\": \"c:\\\\worker.ps1\"\n  }]\n}<\/code><\/pre>\n\n\n\n<p>This template will copy a file called worker.ps1 on our local system to the C-drive on our temporary system and execute that script. Please note that my target script also contains the sysprep logic that was previously executed in-line.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Adding variables to your template<\/h2>\n\n\n\n<p>Finally, you\u2019ll probably also want to add variables to your template. This will allow you to re-use the same template and pass in updates or create different versions using variables.<\/p>\n\n\n\n<p>Let\u2019s update our template to also allow for variables. We\u2019ll add in 3 variables and reference those in the template.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>{\n  \"variables\": {\n    \"file1-download-url\": \"\",\n    \"file2-download-url\": \"\",\n    \"functionality\": \"\"\n  },\n\n  \"builders\": &#91;{\n    \"type\": \"azure-arm\",\n\n    \"client_id\": \"xxx\",\n    \"client_secret\": \"xxx\",\n    \"tenant_id\": \"72f988bf-86f1-41af-91ab-2d7cd011db47\",\n    \"subscription_id\": \"d19dddf3-9520-4226-a313-ae8ee08675e5\",\n\n    \"managed_image_resource_group_name\": \"image-rg\",\n    \"managed_image_name\": \"{{user `functionality`}}-{{timestamp}}\",\n\n    \"os_type\": \"Windows\",\n    \"image_publisher\": \"MicrosoftWindowsServer\",\n    \"image_offer\": \"WindowsServer\",\n    \"image_sku\": \"2016-Datacenter\",\n\n    \"communicator\": \"winrm\",\n    \"winrm_use_ssl\": true,\n    \"winrm_insecure\": true,\n    \"winrm_timeout\": \"5m\",\n    \"winrm_username\": \"packer\",\n\n\n    \"location\": \"westus2\",\n    \"vm_size\": \"Standard_DS4_v2\"\n  }],\n  \"provisioners\": &#91;\n    {\n      \"type\": \"file\",\n      \"source\": \"setup.ps1\",\n      \"destination\": \"c:\\\\setup.ps1\",\n      \"direction\": \"upload\"\n     },\n    {\n    \"type\": \"powershell\",\n    \"inline\": \"c:\\\\setup.ps1 -file1 '{{user `file1-download-url`}}' -file2 '{{user `file2-download-url`}}'  -functionality '{{user `functionality`}}' \"\n  }]\n}<\/code><\/pre>\n\n\n\n<p>And next to that, we can create a variables file:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>{\n    \"file1-download-url\": \"https:\/\/nfnstacc1.blob.core.windows.net\/demo\/blob.txt\",\n    \"file2-download-url\": \"https:\/\/nfnstacc1.blob.core.windows.net\/demo\/blob2.txt\",\n    \"functionality\": \"web-server\"\n}<\/code><\/pre>\n\n\n\n<p>In order to deploy this using Packer, the following command will do the job:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>packer build -var-file packer.vars.json packer.json<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">Conclusion<\/h2>\n\n\n\n<p>I had a blast playing around with Packer. It\u2019s been on my \u2018I want to play around with this\u2019-list for a couple of months, and I never got around to it. Now that I\u2019ve used it once, it showed me how easy it is to create custom images. I\u2019ve spent to many hours logging into Windows boxes, and doing a mistake in the Sysprep configuration. Packer takes that burden away.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>What is Packer? Packer\u00a0is a tool developed by Hashicorp (the company behind Terraform) that makes it easy to automate the creation of custom images (or AMIs in AWS). The tool itself will automate the creation of VMs, will run whatever scripts you want it to run, and will then instruct your cloud provider to take [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[69,35],"tags":[],"class_list":["post-3316","post","type-post","status-publish","format-standard","hentry","category-azure","category-cloud-computing"],"_links":{"self":[{"href":"https:\/\/microsoftgeek.com\/index.php?rest_route=\/wp\/v2\/posts\/3316","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/microsoftgeek.com\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/microsoftgeek.com\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/microsoftgeek.com\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/microsoftgeek.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=3316"}],"version-history":[{"count":1,"href":"https:\/\/microsoftgeek.com\/index.php?rest_route=\/wp\/v2\/posts\/3316\/revisions"}],"predecessor-version":[{"id":3317,"href":"https:\/\/microsoftgeek.com\/index.php?rest_route=\/wp\/v2\/posts\/3316\/revisions\/3317"}],"wp:attachment":[{"href":"https:\/\/microsoftgeek.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=3316"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/microsoftgeek.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=3316"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/microsoftgeek.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=3316"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}