Categorie: Devops

Building a development environment with Vagrant, Packer, Windows 10 and Server 2016

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.


You can checkout the Packer templates on my Github page here:


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

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


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

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

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`}}",
      "vboxmanage": [
   "provisioners": [
      "type": "powershell",
      "scripts": [
  "post-processors": [
      "type": "vagrant",
      "keep_input_artifact": false,
      "output": "windows_10_{{.Provider}}.box",
      "vagrantfile_template": "vagrantfile-windows_10.template"
  "variables": {
    "iso_url": "",
    "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`}}",

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": [

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: and this Github repo:

Vagrant, Parallels and Windows guests

For all of you running a Mac, this is how to create a Windows guest with Vagrant and Packer.


Vagrant supports Parallels 8 and higher, but please be aware that Vagrant only supports the Pro version of Parallels 11.

First install the plugin:

vagrant plugin install vagrant-parallels

Second, get the Parallels virtualization SDK here

Third, get Packer and unzip it in your Vagrant dir.

Fourth, clone packer-windows from Github.

git clone

Now make sure the packer and the packer-windows executables are in the same folder. This makes the command easier because you don’t need to think hard to make sure the paths to all the files and commands are right.

Fifth, download the Windows server iso upfront. I usually download the Windows Server iso first from here so that I don’t have to wait for the download to finish during the packer build process.

Sixth, get this json config file for Parallels. You see that I put the iso from the fifth step on the desktop and reference it in the file.

Seventh, start building and grab coffee

./packer build -var iso_url=~/Desktop/9600.16384.WINBLUE_RTM.130821-1623_X64FRE_SERVER_EVAL_EN-US-IRM_SSS_X64FREE_EN-US_DV5.ISO windows_2012_r2_parallels.json 

And another coffee..

Screen Shot 2015-10-07 at 07.02.50

Step eight: add the template to vagrant

vagrant box add --name windows_2012_r2

Step nine: create a Vagrant file

Create a folder named lab01 and put a Vagrantfile in it:

Vagrant.require_version ">= 1.6.2"

$root_provision_script = <<'ROOT_PROVISION_SCRIPT'
& $env:windir\system32\tzutil /s "W. Europe Standard Time"


Vagrant.configure("2") do |config|
    config.vm.define "lab01" = "windows_2012_r2"
    config.vm.hostname = "lab01" 
    config.vm.provider :virtualbox do |v, override|
        v.gui = false
        #v.customize ["modifyvm", :id, "--memory", 2048]
        #v.customize ["modifyvm", :id, "--cpus", 2]
    end :forwarded_port, guest: 3389, host: 3391, id: "rdp", auto_correct: true :forwarded_port, guest: 22, host: 2223, id: "ssh", auto_correct: true "private_network", ip: ""
    config.vm.provision "shell", inline: $root_provision_script
    #config.vm.provision "shell", path: "sysprep.ps1"


Then issue a ‘vagrant up’ and we’re done.

Create a lab on Azure with Vagrant and Powershell

If you want to spin up a lab quickly to test things in a Windows environment, you can use an Azure trial account. It is possible to create trial accounts indefinitely so it will cost you nothing. So, let’s go.

For this scenario, I am assuming you are on Windows. By the way, I did the same on a Macbook but instead of Powershell I used the Azure CLI for Mac (runs on Node.js). Check this.

Step 1. Create an Azure trial account

Create a trial account on Azure here.
You will need to supply your credit card info and you should use an mail address that has not been used before for a trial. I am on Google Apps, so I can create mail addresses as much as I like.

Step 2. Install Azure Powershell

You’ll need Azure Powershell to query the available images.
Install the Azure Powershell with the msi (or Web Platform Installer).
I’ve been trying to install the SDK with OneGet, but it seems to be not available.

This gives you a brand new shell.
Not happy with it because it doen’t have a cursor. Let’s fix that:

[Console]::CursorSize = 25

Step 3. Add your Azure credentials



and enter your credentials


Next, get the publishsettings.



Save your publishsettings (e.g. on c:\temp) and import them:

Import-AzurePublishSettingsFile c:\temp\%your trial account%-credentials.publishsettings

Step 4. Generate certificates

I would advise to use Cmder with msysgit integration, if you don’t already. Cmder is my go to terminal emulator. I use it for Powershell, Git Bash and ordinary DOS. So install Cmder with Chocolatey.

  • First create a pem certificate which is conveniently valid for 10 years. This contains a public key and private key.
  • Then create a pfx certicate based on this pem certifcate.
  • From the pfx, generate a cer to upload to Azure.

openssl req -x509 -nodes -days 3650 -newkey rsa:2048 -keyout azurecert.pem -out azurecert.pem

openssl pkcs12 -export -out azurecert.pfx -in azurecert.pem -name "Vagrant Azure Cert"

openssl x509 -inform pem -in azurecert.pem -outform der -out azurecert.cer

Thanks to this article.

Step 5. Upload the cer file to Azure

I can’t figure out how this works with Powershell, so log on to your subscription and add the .cer file:


First go to settings, then to Management Certificates and upload your .cer file.


Step 6. Install the Vagrant Plugin for Azure

vagrant plugin install vagrant-azure
vagrant box add azure

Now take a look at the Vagrant file for this box. It is located here: C:\Users\yourname\.vagrant.d\boxes\azure\0\azure\Vagrantfile.
In this file you can define some constants that will be applied to every Azure box you create. I’ve changed ‘azure.vm.size’ from ‘Small’ to ‘Medium’ and added ‘azure.vm.location’ = West Europe.

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

# Vagrantfile API/syntax version. Don't touch unless you know what you're doing!

Vagrant.configure(VAGRANTFILE_API_VERSION) do |config|
  # All Vagrant configuration is done here. The most common configuration
  # options are documented and commented below. For a complete reference,
  # please see the online documentation at

  config.vm.provider :azure do |azure|
    azure.vm_size = 'Medium'
    azure.vm_location = 'West Europe' # e.g., West US

Step 7. Create a new Vagrant file for your Azure box

Now it’s time to create the Azure Vagrant box. Without much further ado, this is my Vagrantfile:

# --
Vagrant.configure('2') do |config| = 'azure'

    config.vm.provider :azure do |azure, override|
        azure.mgmt_certificate = 'insert path to you pem certifcate'
        azure.mgmt_endpoint = ''
        azure.subscription_id = 'insert your Azure subscription ID'
        azure.vm_image = ''
        azure.vm_name = 'box01' # max 15 characters. contains letters, number and hyphens. can start with letters and can end with letters and numbers

        azure.vm_password = 'Vagrant!' # min 8 characters. should contain a lower case letter, an uppercase letter, a number and a special character

        azure.storage_acct_name = 'azureboxesstorage2015' # optional. A new one will be generated if not provided.
        azure.cloud_service_name = 'azureboxes' # same as vm_name. leave blank to auto-generate
        azure.vm_location = 'West Europe' # e.g., West US

    azure.tcp_endpoints = '3389:53390' # opens the Remote Desktop internal port that listens on public port 53389. Without this, you cannot RDP to a Windows VM.

The Vagrantfile is of based on the Vagrantfile supplied by

You can get a list of available Azure VM images by logging on to your Azure subscription with Powershell and issue the following command:

Get-AzureVMImage | where-object { $_.Label -like "Windows Server 2012 R2 *" }| select imagename,imagefamily

Step 8. Vagrant up

Now it’s time to issue a Vagrant up.

This is will generate some error messages because the vm needs to initialize (I assume).


Just issue an vagrant up again until it says: The machine is already created.

Then you can go ahead and RDP into your new VM:


So there you go, now you are all set to deploy Azure images until the cloud bursts.


Powershell Desired State Configuration simple example

While still in ‘Devop Mood’, let’s quickly figure out Desired State Configuration.

This image gives a nice overview of the DSC architecture:


If you have a Windows 2012 R2 Server with the latest updates and KB2883200 installed, you’re good to go. Check it like so:
wmic qfe | find "KB2883200"

What Powershell Modules are there installed anyway? Go ahead, open a Powershell console and type $env:PSModulePath -split ";"
This displays the locations on your PC where there are Powershell modules installed.

PS C:\Users\vagrant> $env:PSModulePath -split ";"
C:\Program Files\WindowsPowerShell\Modules

Now if you cd into C:\Windows\system32\WindowsPowerShell\v1.0\Modules\ you get to see all the modules. If all is well, one of them is PSDesiredStateConfiguration. This is where the DSC commandlets are hidden.


A resource is an ‘object’ which you can configure with DSC. There are 3 sources for resources.

Out of the box resources
Out of the box with Powershell v4, there are modules for File, Registry, Services etcetera. To see the complete list, do:

PS C:\> Get-DscResource | select name

This outputs:


Community resources
The Powershell community has also written some modules for DSC resources, like DNS, Active Directory and Hyper-V. You can find them here and they are prefixed with a c.

Experimental resources
The Powershell team itself also provides some experimental resources which you can find here. These resources are prefixed with an x.

You can download these resources and add them to the $env.PSModulePath folder. Which is in my case: C:\Windows\system32\WindowsPowerShell\v1.0\Modules\.

Create your first configuration

These steps describe the DSC process:

  • Add the required DSC resources
  • Create a configuration script
  • Execute the script to generate a MOF file
  • Apply the MOF to the target nodes

Now let’s start with adding a folder named “c:\Replica” on every node. That means we could use the File resource, which comes out of the box. Now, how to use this file resource? Luckily, the Powershell folks have made things very easy for us. We can just type get-dscresource File -syntax and lo and behold:

PS C:\> Get-DscResource File -Syntax
File [string] #ResourceName
    DestinationPath = [string]
    [ Attributes = [string[]] { Archive | Hidden | ReadOnly | System }  ]
    [ Checksum = [string] { CreatedDate | ModifiedDate | SHA-1 | SHA-256 | SHA-512 }  ]
    [ Contents = [string] ]
    [ Credential = [PSCredential] ]
    [ DependsOn = [string[]] ]
    [ Ensure = [string] { Absent | Present }  ]
    [ Force = [bool] ]
    [ MatchSource = [bool] ]
    [ Recurse = [bool] ]
    [ SourcePath = [string] ]
    [ Type = [string] { Directory | File }  ]

So this basically explains how to use the File resource.

Fire up Powershell ISE and start type:

Configuration MyFirstConfig {

Node DSC1 {
    File SyncDir {
        DestinationPath = "c:\replica"
        Type = "Directory" 
        Ensure = "Present"   

MyFirstConfig -OutputPath c:\DSC

If you execute this script a MOF file will be created in c:\DSC.

Apply the MOF

Now go ahead and type: Start-DscConfiguration -Path c:\DSC

This will output:

PS C:\Windows\System32\WindowsPowerShell\v1.0\Modules> Start-DscConfiguration -Path c:\DSC

Id     Name            PSJobTypeName   State         HasMoreData     Location             Command
--     ----            -------------   -----         -----------     --------             -------
8      Job8            Configuratio... Running       True            DSC1                 Start-DscConfiguration...

And if all is well there is indeed a folder named ‘replica’ on your C:\ drive.


This was a very simple example of Powershell DSC. I think it is quite nice and I am planning to explore its possibilities in the near future (like tomorrow or so).


Quickly build a Windows lab with VirtualBox, Packer, Vagrant and Chocolatey


I’m preparing a Powershell training for work and I needed an easy lab with virtual machines which I can easily distribute among the students. It should obviously all be Windows based.

So I started out working with Windows 10 and Vagrant to create a virtual lab, as I did on my Mac and Ubuntu box before, and I am very happy and (surprised) to announce that it works very smoothly on Windows. And I used Packer to create a Windows base box for Vagrant.

So I had great fun setting up my training lab and it would be a shame to keep it all for myself. So here goes. The Buttonfactory goes Devops!

Install Windows 10 Technical Preview and Chocolatey

Unfortunately, the preview of Windows 10 is not available anymore. If you look hard enough, I think it is possible to grab an iso somewhere.
By the way, the rest of these directions work also under Windows 7, 8 and 8.1.


Install Chocolatey
After installing Windows 10 I would advise to install Chocolatey. Chocolatey is like Ninite on the command line (or like apt-get for Windows if you will) and it is going te be very important in the future.

Open a command prompt as Administrator and paste this command in your terminal:

@powershell -NoProfile -ExecutionPolicy Bypass -Command "iex ((new-object net.webclient).DownloadString(''))" && SET PATH=%PATH%;%ALLUSERSPROFILE%\chocolatey\bin

(Or just head over to and follow the installation instructions).


Install Cmder
Then the first thing todo is install Cmder, a perfect replacement for the Windows terminal (which sucks a bit).
Fire up the command prompt as an admin and type:

cinst cmder -y

The Cmder binaries are installed in c:\tools. It is added to the path so you can just fire it up (start, run) and type Cmder.
Cmder also incorporates Msysgit which brings you, apart from Git, a light set of GNU tools as well like ls, grep, awk and vim, which I adore.

You can add another tab with a new console and tick ‘Run as administrator’ for convenience.
2015-07-19 14_17_18-ConEmu





And you can designate a tab for Powershell as well:
2015-07-19 14_19_59-Cmder







Install VirtualBox, Vagrant and Packer

This is also possible from Chocolatey.

cinst virtualbox vagrant -y

Add vagrant to the path. My path looks like this: C:\tools\cmder;C:\PROGRA~2\Oracle\VirtualBox;c:\HashiCorp\Vagrant\bin

The next steps to install Packer are:

  • Open Cmder
  • Type: mkdir \Vagrant
  • Type: cd \Vagrant
  • Type: git clone
  • Cd into packer-windows and see what has been downloaded
  • Now download packer and extract all contents to the packer-windows folder. (I think that is convenient)

Build a Windows Server 2012 R2 base box

This paragraph is based on this blog post from Rui Lopes.

We are going to edit the windows_2012_r2.json a bit to adjust it to our needs:

  • On line 38: set headless to false to see what is going on
  • On line 42: change the ssh_wait_timeout from 4h to 8h (to have enough time to install all updates)
  • On line 76: Remove the ./scripts/compact.bat (it takes ages to run) and the ./scripts/chef.bat (we are not gonna need it) lines.
  • You might want to change the answer file to change the Culture, but that can be done in the Vagrantfile later.

Now we are ready to build the box. Make sure you are in the c:\vagrant\packer-windows folder and type:

packer build -only virtualbox-iso windows_2012_r2.json

And now we wait. This takes forever. I’d suggest you go for a walk or perhaps to bed early. But the price is big!

Add the Windows Server 2012 R2 base box to Vagrant

When you return from your other extra curricular activity, you will see a box file is created in the c:\vagrant\packer\packer-windows folder.
You should add this box to Vagrant:

vagrant box add --name windows_2012_r2

This will also take a short time.

It will add the box to C:\Users\\.vagrant.d\boxes\windows_2012_r2\0\virtualbox:

2015-07-19 15_27_06-Cmder

You can basically get rid of the box file in c:\vagrant\packer\packer-windows now, or copy it to an USB drive or so because for now it is not longer needed.

Fire up the Virtual Server already!

  • Go to c:\Vagrant and create a dir for the server (mine is called DSC1)
  • cd into DSC1
  • Type vagrant init
  • Edit the Vagrantfile that this creates and make it to look like so:
    Vagrant.require_version ">= 1.6.2"
    $root_provision_script = <<'ROOT_PROVISION_SCRIPT'
    & $env:windir\system32\tzutil /s "W. Europe Standard Time"
    Vagrant.configure("2") do |config|
        config.vm.define "DSC1" = "windows_2012_r2"
        config.vm.hostname = "DSC1"
        config.vm.provider :virtualbox do |v, override|
            v.gui = true
            v.customize ["modifyvm", :id, "--memory", 2048]
            v.customize ["modifyvm", :id, "--cpus", 2]
    "private_network", ip: ""
        config.vm.provision "shell", inline: $root_provision_script
  • Save it
  • Type: vagrant up

When it is finished, you type vagrant rdp and you can log on to your box.
(Username vagrant, password vagrant).

2015-07-19 15_42_33-Quickly build a Windows lab with VirtualBox, Packer, Vagrant and Chocolatey _ Th

Now to halt the box: you type: vagrant halt.
To destroy the box: you type vagrant destroy.
And you can start again with a clean slate in seconds with vagrant up.

Create more folders for other servers, adjust the Vagrantfile (server name, IP address) and spin them up.

Extra: add Chocolatey to the Vagrant box

You can provision your Vagrant boxes with Chocolatey, and install Cmder and Notepad2 (or whatever packages you might need from the Choco repos). To do so, change the Vagrantfile like so:

Vagrant.require_version ">= 1.6.2"

$root_provision_script = <<'ROOT_PROVISION_SCRIPT'
#adjust the time zone:
& $env:windir\system32\tzutil /s "W. Europe Standard Time"

#install Chocolatey
iex ((new-object net.webclient).DownloadString(''))

#and install Cmder and Notepad2
choco install cmder notepad2 -y

Vagrant.configure("2") do |config|
    config.vm.define "DSC1" = "windows_2012_r2"
	  config.vm.hostname = "DSC1"

	        config.vm.provider :virtualbox do |v, override|
		        v.gui = true
		        v.customize ["modifyvm", :id, "--memory", 2048]
		        v.customize ["modifyvm", :id, "--cpus", 2]
			    end "private_network", ip: ""
      config.vm.provision "shell", inline: $root_provision_script