Deploying a VM in Azure is all well and good, but what about the base configuration of the VM itself? That’s where Packer comes in, enabling a custom image to be created and configured with server roles, applications, packages, etc. as required. That custom image can then be used to build your virtual machines. Creating it using Packer enables the benefits of infrastructure as code, which can be source control managed and tracked.

In this article, I’ll show how to create a Packer image that simply has the AD DS and DNS server roles added to a base Windows 2019 datacenter image from the Azure Marketplace. This image is then used to deploy a VM using Terraform.

Creating the Packer Image

Create a new .json file called ad.json.

Add a variables block as follows to the top of the file. This defines which variables we will be passing in to build the Packer image. You can leave them blank as shown below, as the values for the variables will be stored in the Azure DevOps pipeline variables section.

{
  "variables": {
  "client_id": "",
  "client_secret": "",
  "tenant_id": "",
  "subscription_id": "",
  "managed_image_name": "",
  "managed_image_resource_group_name": "",
  "WorkingDirectory": "{{env `System_DefaultWorkingDirectory`}}",
  "publisher": "",
  "offer": "",
  "sku": "",
  "location": "",
  "vm_size": ""
},

Next add a builders section to the template.

"builders": [{
  "type": "azure-arm",
  "client_id": "{{user `client_id`}}",
  "client_secret": "{{user `client_secret`}}",
  "subscription_id": "{{user `subscription_id`}}",
  "tenant_id": "{{user `tenant_id`}}",
  "managed_image_resource_group_name": "{{user    `managed_image_resource_group_name`}}",
  "managed_image_name": "{{user `managed_image_name`}}",
  "os_type": "Windows",
  "image_publisher": "{{user `publisher`}}",
  "image_offer": "{{user `offer`}}",
  "image_sku": "{{user `sku`}}",
  "communicator": "winrm",
  "winrm_use_ssl": "true",
  "winrm_insecure": "true",
  "winrm_timeout": "3m",
  "winrm_username": "packer",
  "location": "{{user `location`}}",
  "vm_size": "{{user `vm_size`}}",
  "async_resourcegroup_delete": true
}],

You can define the values for these variables directly in the builders section, or pull them as required from the Azure DevOps pipeline variables. Anything with ”{{user x}}” will expect the variable to be defined in the variable block.

The “async_resourcegroup_delete”: true option allows the pipeline to progress more quickly. Packer deletes the temporary resource group it creates whilst creating the image when it is finished. This can take a long time, so adding this option means the pipeline is not held up waiting for this to happen.

Now add the provisioners block to the config file. This will define the steps to take on top of the base image. I am installing the windows features, restarting the machine, and then generalizing the image ready for use.

"provisioners": [
 {
   "type": "powershell",
   "inline": [
   "Install-WindowsFeature -Name AD-Domain-Services -IncludeManagementTools",
   "Install-WindowsFeature -Name DNS -IncludeManagementTools"
   ]
 },
 {
   "type": "windows-restart",
   "restart_check_command": "powershell -command \"& {Write-Output 'Machine restarted.'}\""
 },
 {
   "type": "powershell",
   "inline": [
   "if( Test-Path $Env:SystemRoot\\windows\\system32\\Sysprep\\unattend.xml ){ rm $Env:SystemRoot\\windows\\system32\\Sysprep\\unattend.xml -Force}",
   "& $env:SystemRoot\\System32\\Sysprep\\Sysprep.exe /oobe /generalize /quiet /quit /mode:vm",
   "while($true) { $imageState = Get-ItemProperty HKLM:\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Setup\\State | Select ImageState; Write-Output $imageState.ImageState; if($imageState.ImageState -ne 'IMAGE_STATE_GENERALIZE_RESEAL_TO_OOBE') { Start-Sleep -s 10 } else { break } }"
   ]
 }
 ]
}

Upload the complete file to your Azure DevOps repo ready for use!

#azure #terraform #azure-devops

Building VM images in Azure using Packer and Deploying withTerraform,Azure DevOps pipeline
1.65 GEEK