Packer で VM イメージビルド中に Azure ファイル共有をマウントする

Packer で Windows Server 2019 Datacenter の VM イメージを作成しているが、アプリケーションをインストールために、WinRM でインストーラーをアップロードするのは遅すぎてまったく実用的ではなかった。

インストーラーみたいな大きいファイルは、あらかじめ Azure ファイル共有に置いておき、VM イメージをビルド中にマウントしコピーするしかなさそう。

Azure ファイル共有をマウントするスクリプトは、Azure ポータルで下記のようなものを入手できる。

$connectTestResult = Test-NetConnection -ComputerName ストレージアカウント名.file.core.windows.net -Port 445
if ($connectTestResult.TcpTestSucceeded) {
    # 再起動時にドライブが維持されるように、パスワードを保存する
    cmd.exe /C "cmdkey /add:`"ストレージアカウント名.file.core.windows.net`" /user:`"Azure\ストレージアカウント`" /pass:`"パスワード`""
    # ドライブをマウントする
    New-PSDrive -Name Z -PSProvider FileSystem -Root "\\ストレージアカウント名.file.core.windows.net\ファイル共有名" -Persist
} else {
    Write-Error -Message "Unable to reach the Azure storage account via port 445. Check to make sure your organization or ISP is not blocking port 445, or use Azure P2S VPN, Azure S2S VPN, or Express Route to tunnel SMB traffic over a different port."
}

このスクリプトpowershell provisioner で実行する。

variable "client_id" {
  type = string
}
variable "client_secret" {
  type = string
}
variable "tenant_id" {
  type = string
}
variable "subscription_id" {
  type = string
}
variable "managed_image_resource_group_name" {
  type = string
  default = "rg-example"
}
variable "managed_image_name" {
  type = string
  default = "vmi-example"
}
variable "build_resource_group_name" {
  type = string
  default = "rg-example"
}
variable "vm_size" {
  type = string
  default = "Standard_D2_v2"
}

source "azure-arm" "windowsserver2019" {
  client_id = var.client_id
  client_secret = var.client_secret
  tenant_id = var.tenant_id
  subscription_id = var.subscription_id

  managed_image_resource_group_name = var.managed_image_resource_group_name
  managed_image_name = var.managed_image_name

  os_type = "Windows"
  image_publisher = "MicrosoftWindowsServer"
  image_offer = "WindowsServer"
  image_sku = "2019-Datacenter"

  communicator = "winrm"
  winrm_use_ssl = true
  winrm_insecure = true
  winrm_timeout = "10m"
  winrm_username = "packer"
  winrm_password = "P@ssw0rd12345"

  azure_tags = {
    dept = "Engineering"
    task = "Image deployment"
  }

  build_resource_group_name = var.build_resource_group_name
  vm_size = var.vm_size
}

build {
  sources = [
    "sources.azure-arm.windowsserver2019"
  ]

  provisioner "powershell" {
    inline = [
      "while ((Get-Service RdAgent).Status -ne 'Running') { Start-Sleep -s 5 }",
      "while ((Get-Service WindowsAzureGuestAgent).Status -ne 'Running') { Start-Sleep -s 5 }",
    ]
  }

  # Azure ファイル共有をマウントする PowerShell スクリプトを実行
  provisioner "powershell" {
    script = ".\mount-azure-file-share.ps1"
    elevated_user = "packer"
    elevated_password = "P@ssw0rd12345"
  }

  provisioner "powershell" {
    inline = [
      "& $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 } }",
    ]
  }
}

elevated_user と elevated_password を指定し、管理者権限に昇格して実行する必要があるので注意。

指定しないで実行すると、「ネットワークパスが見つかりません」のようなエラーでマウントに失敗した。「権限が無い」というエラーならまだ気付きやすかったのに…。このせいで1日以上潰れた。