Here is another post about creating Windows demo environments with Vagrant. This time we create our own Windows 10 LTSB and Windows 2016 CTP 5 core boxes from scratch with Packer.

packer

You can checkout the Packer templates on my Github page here: https://github.com/jacqinthebox/packer-templates.

TL;DR

For the impatient: I published my boxes at Hashicorp here. You can use them right away with Vagrant and Virtualbox. I'm currently writing Parallels providers as well.

Here is how.
Install Virtualbox and Vagrant for your OS.
Create a folderstructure on your machine for the Vagrant boxes. Then issue a 'vagrant init':

#Create a folder structure to host the Vagrant boxes 
#Can be named anything and placed anywhere you like
New-Item \Boxes\Win10 -Type Directory -Force; cd C:\Boxes\Win10

vagrant init jacqinthebox/windows10LTSB
vagrant up --provider Virtualbox

And now we wait!
When the box has finished downloading you can provision the box to your liking and sysprep it (there is an autounnattend.xml already in c:\logs).
I currently use this Vagrantfile:

# -*- mode: ruby -*-
# vi: set ft=ruby :

$sysprep = <<'SYSPREP'
& $env:windir\system32\sysprep\sysprep.exe /generalize /oobe /unattend:C:\logs\unattend.xml /quiet /shutdown
SYSPREP

Vagrant.configure(2) do |config|
  config.vm.define "client01" 
  config.vm.box = "jacqinthebox/windows10LTSB"
  config.vm.hostname = "client01" 
  config.vm.network "private_network", ip: "192.168.56.10"
  config.vm.network :forwarded_port, guest: 3389, host: 33989, id: "rdp", auto_correct: true
config.vm.provision :shell, inline: $sysprep

end

The box comes with Chocolatey and Package Management preinstalled, so you can install Chrome with a simple:

Install-Package -Providername chocolatey googlechrome -ForceBootstrap -Force

OK, onto how the box was built and why.

Why build a Windows demo environment?

Indeed, why on earth would I want to build a demo environment? Well, I am a Powershell trainer. And I do most of my trainings at the customer premises. So there's no training equipment and I always ask my students to bring their own device. Now how would I make sure we are working on the same machines and the demo's work? Enter Vagrant.

Why not just create sysprepped golden images and clone them all the time?

I love Vagrant. I love automation! Here are some of my other Vagrant posts:

I did learn a lot in the meantime, so I will basically repeat everything in this post. And now Packer supports WinRM, so installing SSH on Windows is no longer necessary.

How to build the box with Packer

Install Vagrant, Virtualbox and Packer. I'm on Windows 10 and using OneGet, indeed, from the commandline! Hurray!

Install-Package -ProviderName Chocolatey - ForceBootstrap -Force vagrant,virtualbox,packer

Then clone the packer-templates Git repo:

git clone https://github.com/jacqinthebox/packer-templates.git

Now build the box like this and add it to Vagrant:

packer build -only virtualbox-iso windows_2016.json
vagrant box add --name windows_2016 windows_2016_virtualbox.box

How does Packer work?

Basically Packer builds the VM with parameters it reads from a json file.
This is the json file for the Windows 10 LTSB machine:

{
  "builders": [
    {
      "type": "virtualbox-iso",
      "iso_url": "{{user `iso_url`}}",
      "iso_checksum_type": "{{user `iso_checksum_type`}}",
      "iso_checksum": "{{user `iso_checksum`}}",
      "headless": false,
      "guest_additions_mode": "attach",
      "boot_wait": "2m",
      "communicator": "winrm",
      "winrm_username": "vagrant",
      "winrm_password": "vagrant",
      "winrm_timeout": "5h",
      "shutdown_command": "shutdown /s /t 10 /f /d p:4:1 /c \"Packer Shutdown\"",
      "shutdown_timeout": "15m",
      "guest_os_type": "Windows81_64",
      "disk_size": 61440,
      "floppy_files": [
        "{{user `autounattend`}}",
        "./answer_files/10/unattend.xml",
        "./scripts/bootstrap.ps1",
        "./scripts/sdelete.exe",
        "./scripts/oracle-cert.cer"
      ],
      "vboxmanage": [
        [
          "modifyvm",
          "{{.Name}}",
          "--memory",
          "2048"
        ],
        [
          "modifyvm",
          "{{.Name}}",
          "--cpus",
          "2"
        ]
      ]
    }
  ],
   "provisioners": [
    {
      "type": "powershell",
      "scripts": [
        "./scripts/provision.ps1"
      ]
    }
  ],
  "post-processors": [
    {
      "type": "vagrant",
      "keep_input_artifact": false,
      "output": "windows_10_{{.Provider}}.box",
      "vagrantfile_template": "vagrantfile-windows_10.template"
    }
  ],
  "variables": {
  
    "iso_url": "http://care.dlservice.microsoft.com/dl/download/6/2/4/624ECF83-38A6-4D64-8758-FABC099503DC/10240.16384.150709-1700.TH1_CLIENTENTERPRISE_S_EVAL_X64FRE_EN-US.ISO",
    "iso_checksum_type": "md5",
    "iso_checksum": "c22bc85b93eb7cc59193f12f30538f78",
     "autounattend": "./answer_files/10/Autounattend.xml"
   }
}

So, the whole creation process of a Vagrant box goes something like this:

  • Packer creates and configures the VM
  • Packer attaches the floppy files to the VM
  • It starts installing the OS, the Windows Installer grabs the answer file from the floppy
  • In the answer file the Vagrant user is created
  • In the answer file we will call a powershell script (bootstrap.ps1) which enables WinRM
  • As soon as WinRM is available, Packer will start provisioning scripts the box by executing provision.ps1
  • By running provision.ps1 the guest additions are installed and the box is compacted with sdelete.exe

Let's dissect parts of this the json file.

 "type": "virtualbox-iso",
 "iso_url": "{{user `iso_url`}}",
 "iso_checksum_type": "{{user `iso_checksum_type`}}",
 "iso_checksum": "{{user `iso_checksum`}}",

Here we declare that we are creating a Virtualbox vm.
The iso_url , iso_checksum_type and iso_checksum are variables, which are set in the variables block in the bottom of the file.

"headless": false,
"guest_additions_mode": "attach",
"boot_wait": "2m",
"communicator": "winrm",
"winrm_username": "vagrant",
"winrm_password": "vagrant",
"winrm_timeout": "5h"

We set headless to false to see what is going on. The guest_additions_mode is attach. Valid options are "upload", "attach", or "disable", but we choose attach because uploading the tools to the VM is not working via WinRM (it does work with SSH, but we are not using SSH). So we attach the guest additions iso and install the tools from there in the provision stage (more about that later). We set the winrm_timeout to 5h. This is the time Packer waits until WinRM becomes available.

"floppy_files": [
     "{{user `autounattend`}}",
     "./answer_files/10/unattend.xml",
     "./scripts/bootstrap.ps1",
     "./scripts/sdelete.exe",
     "./scripts/oracle-cert.cer"
      ],

This is the part where we define the scripts that are placed on the floppy disk that gets attached to the box.

  "provisioners": [
    {
      "type": "powershell",
      "scripts": [
        "./scripts/provision.ps1"
      ]
    }
  ],

This part is executed after WinRM is available. In the provison.ps1 script we enable RDP, install chocolatey, install the guest additions and last but not least the disk gets compacted with sdelete.exe.

So there it is! Clone the repo, check out the answer files and the scripts and build your boxes already!

Special thanks goes to..

This article: http://www.hurryupandwait.io/blog/creating-a-hyper-v-vagrant-box-from-a-virtualbox-vmdk-or-vdi-image and this Github repo: https://github.com/joefitzgerald/packer-windows.