Packing Windows on Azure, with Ansible provisioning

https://docs.microsoft.com/en-us/azure/virtual-machines/windows/build-image-with-packer

I would like to provision a Windows 2016 VM in Azure or GCP with Ansible as provisioner.
Ansible can provision Windows using Vagrant like shown in this repo https://github.com/jborean93/packer-windoze But Azure is different and it is unclear how to enable WinRM for Ansible on that platform. I could not find a working example of Windows provisioning with Ansible on GitHub.

I’m trying to make sense of this options presented on this pages by Hashicorp, maybe it makes enough sense to you to advise me?

[https://www.packer.io/docs/provisioners/ansible.html]

Background:
[https://docs.ansible.com/ansible/latest/scenario_guides/guide_azure.html]
[https://medium.com/slalom-engineering/azure-packer-592c4dc0e23a]

With the kind help of Microsoft I found a solution with this packer.json

{
  "builders": [
    {
      "client_id": "{{user `arm_client_id`}}",
      "client_secret": "{{user `arm_client_secret`}}",
      "communicator": "winrm",
      "image_offer": "{{user `image_offer`}}",
      "image_publisher": "{{user `image_publisher`}}",
      "image_sku": "{{user `image_sku`}}",
      "image_version": "latest",
      "location": "{{user `arm_location`}}",
      "managed_image_name": "{{user `managed_image_name`}}",
      "managed_image_resource_group_name": "{{user `arm_resource_group`}}",
      "os_type": "Windows",
      "subscription_id": "{{user `arm_subscription_id`}}",
      "tenant_id": "{{user `arm_tenant_id`}}",
      "type": "azure-arm",
      "vm_size": "Standard_D8_v3",
      "winrm_insecure": true,
      "winrm_timeout": "1h",
      "winrm_use_ssl": true,
      "winrm_username": "packer"
    }
  ],
  "provisioners": [
    {
      "type": "powershell",
      "inline": "Invoke-RestMethod -Headers @{\"Metadata\"=\"true\"} -URI 'http://169.254.169.254/metadata/instance/network/interface/0/ipv4/ipAddress/0/publicIpAddress?api-version=2017-08-01&format=text' | Out-File -Append -Encoding utf8 C:/Windows/Temp/ip-address"
    },
    {
      "type": "file",
      "direction": "download",
      "source": "C:/Windows/Temp/ip-address",
      "destination": "./ansible/hosts"
    },
    {
      "type": "shell-local",
      "inline": [
        "IP=`cut -b 4- ansible/hosts`",
        "echo \"[default]\\n${IP}\" > ansible/hosts"
      ]
    },
    {
      "type": "shell-local",
      "environment_vars": "WINRMPASS={{.WinRMPassword}}",
      "command": "ansible-playbook -vv -i ./ansible/hosts packer.yml"
    },
    {
    "type": "powershell",
    "inline": [
      "(gcim win32_service | ? { $_.name -match 'WindowsAzureGuestAgent' }).PathName",
      "get-service WindowsAzureGuestAgent | ft -autosize"
      ]
    },
    {
      "type": "powershell",
      "inline": [
        " # NOTE: the following *3* lines are only needed if the you have installed the Guest Agent.",
        "  while ((Get-Service RdAgent).Status -ne 'Running') { Start-Sleep -s 5 }",
        "  while ((Get-Service WindowsAzureTelemetryService).Status -ne 'Running') { Start-Sleep -s 5 }",
        "  while ((Get-Service WindowsAzureGuestAgent).Status -ne 'Running') { Start-Sleep -s 5 }",
        "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",
        "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 } }"
      ]
    }
  ],
  "variables": {
    "arm_client_id": "{{env `ARM_CLIENT_ID`}}",
    "arm_client_secret": "{{env `ARM_CLIENT_SECRET`}}",
    "arm_location": "{{env `ARM_LOCATION`}}",
    "arm_resource_group": "{{env `ARM_RESOURCE_GROUP`}}images",
    "arm_subscription_id": "{{env `ARM_SUBSCRIPTION_ID`}}",
    "arm_tenant_id": "{{env `ARM_TENNANT_ID`}}",
    "image_offer": "{{env `ARM_IMAGE_OFFER`}}",
    "image_publisher": "{{env `ARM_IMAGE_PUBLISHER`}}",
    "image_sku": "{{env `ARM_IMAGE_SKU`}}",
    "managed_image_name": "{{env `ARM_MANAGED_IMAGE_NAME`}}"
  }
}
1 Like

Did you ever get this working?

Yes, I did, and I’m sharing it https://github.com/dockpack/azure-windows-ci-vm-image

Thanks, checking it out now.

I see you don’t user the ansible provisioner. This seems to be where I am getting hung up. We use administrator and a password we provide for our AMI’s, does your script allow for that, or does it depend on the generated password.

I do not embed a pasword in the azure image. When ansible creates a VM with the module azure_rm_virtualmachine, then I pass administrator credentials.

Ansible is run by packer with shell-local for the temporary winrm password in the environment.

Sorry, I realized I am looking for AWS examples.

AMI is an image format specifically for AWS. You can include ‘AMI’ in you search for Amazon Web Services.