So it turns out that building VMs from scratch is a thing of the past. Instead, vagrant is the way to go!

First steps

Get vagrant from the link above - it’s an install which, if you’re on Windows, will require you to reboot. I should note, you’ll need to have virtualbox installed first. I’m pretty sure you can use other VM providers, but this one is pretty standard and well supported.

I like to keep things (somewhat) organised, so I changed the default working directory for vagrant to VAGRANT_HOME=u:\virt\vagrant (under Windows you can set those permanently via Control Panel -> Environment Variables). You can find more about vagrant’s environment variables here.

Once done, add a default box:

vagrant box add precise64 http://files.vagrantup.com/precise64.box

Which will show something like:

C:\bin\Console2>vagrant box add precise64 http://files.vagrantup.com/precise64.box
==> box: Adding box 'precise64' (v0) for provider:
    box: Downloading: http://files.vagrantup.com/precise64.box
    box: Progress: 1% (Rate: 465k/s, Estimated time remaining: 0:17:47))

Go grab a coffee in the meantime.

Once all of that is ready, you’ll need to do a vagrant init to initialise the box. This does nothing more than create a default configuration file (called Vagrantfile) in your current directory (so watch out).

The defaults sound, and the only section I changed is the below:

config.vm.box = "precise64"

and:

config.vm.provider "virtualbox" do |vb|
  # Display the VirtualBox GUI when booting the machine
  vb.gui = false
  # Customize the amount of memory on the VM:
  vb.memory = "512"
end

(We’ll worry about sharing data between the host and the guest later)

Do a vagrant up, and you’re up and running (no pun intented):

U:\virt\vagrant>vagrant up
Bringing machine 'default' up with 'virtualbox' provider...
==> default: Importing base box 'precise64'...
==> default: Matching MAC address for NAT networking...
==> default: Setting the name of the VM: vagrant_default_1430691171637_21594
==> default: Clearing any previously set network interfaces...
==> default: Preparing network interfaces based on configuration...
default: Adapter 1: nat
==> default: Forwarding ports...
default: 22 => 2222 (adapter 1)
==> default: Running 'pre-boot' VM customizations...
i==> default: Booting VM...
==> default: Waiting for machine to boot. This may take a few minutes...

Time to log into the box!

U:\virt\vagrant>vagrant ssh
`ssh` executable not found in any directories in the %PATH% variable. Is an
SSH client installed? Try installing Cygwin, MinGW or Git, all of which
contain an SSH client. Or use your favorite SSH client with the following
authentication information shown below:

Host: 127.0.0.1
Port: 2222
Username: vagrant
Private key: U:/virt/vagrant/.vagrant/machines/default/virtualbox/private_key

Oh wait… Okay - time to fire up PuTTY. But reforehand note that the ‘Private key’ defined above is not directly compatible with PuTTY (see here for more info - you’ll have to use PuTTYgen to convert between formats).

Set up the key as such:

vagrant_putty

And voila - you’re in!

login as: vagrant
Authenticating with public key "imported-openssh-key"
Welcome to Ubuntu 12.04 LTS (GNU/Linux 3.2.0-23-generic x86_64)

 * Documentation:  https://help.ubuntu.com/
 New release '14.04.2 LTS' available.
 Run 'do-release-upgrade' to upgrade to it.

 Welcome to your Vagrant-built virtual machine.
 Last login: Fri Sep 14 06:23:18 2012 from 10.0.2.2
 vagrant@precise64:~$

Building something useful

We can now everything we need to do to turn this fresh VM into a useable machine - like installing packages, setting up dot files etc… and package it up with vagrant package --base my-cool-new-machine. This will essentially turn what we did into a template. We are now free to use the box as we see fit. Let’s give this a try by installing git on the box (via sudo apt-get install git).

To package a box, we only need to run vagrant package --base <box_name>. The trouble I had was that <box_name> wasn’t what I thought it was. It’s actually the name virtualbox gives the VM. So how do we get hold of that? VBoxManage to the rescue:

c:\Program Files\Oracle\VirtualBox>VBoxManage list vms
"vagrant_default_1430691171637_21594" {1a7089f0-fd7e-49c3-9744-443ef261edc0}

(VBoxManage.exe is probably not in your path - just go to the virtualbox install directory and it should be there)

Shut down your VM (or vagrant will do it for you) and run:

U:\virt\vagrant>vagrant package --base vagrant_default_1430691171637_21594
==> vagrant_default_1430691171637_21594: Attempting graceful shutdown of VM...
vagrant_default_1430691171637_21594: Guest communication could not be established! This is usually because
vagrant_default_1430691171637_21594: SSH is not running, the authentication information was changed,
vagrant_default_1430691171637_21594: or some other networking issue. Vagrant will force halt, if
vagrant_default_1430691171637_21594: capable.
==> vagrant_default_1430691171637_21594: Forcing shutdown of VM...
==> vagrant_default_1430691171637_21594: Clearing any previously set forwarded ports...
==> vagrant_default_1430691171637_21594: Exporting VM...
==> vagrant_default_1430691171637_21594: Compressing package to: U:/virt/vagrant/package.box

And you’ll see a package.box file in the local directory.

So now what? We first need to register it with vargant:

U:\virt\vagrant>vagrant box add box_with_git package.box
==> box: Adding box 'box_with_git' (v0) for provider:
    box: Downloading: file://U:/virt/vagrant/package.box
    box: Progress: 0% (Rate: 0/s, Estimated time remaining
    box: Progress: 25% (Rate: 227M/s, Estimated time remaining
    box: Progress: 41% (Rate: 58.1M/s, Estimated time remaining
    box: Progress: 98% (Rate: 130M/s, Estimated time remaining
    box: Progress: 100% (Rate: 118M/s, Estimated time remaining
    box: :--:--)
==> box: Successfully added box 'box_with_git' (v0) for 'virtualbox'!

We can confirm this was successful via:

U:\virt\vagrant>vagrant box list
box_with_git (virtualbox, 0)
precise64    (virtualbox, 0)

Create a directory where vagrant will store all the related information, and do a vagrant init box_with_git.

U:\virt\vagrant>mkdir project_a

U:\virt\vagrant>cd project_a

U:\virt\vagrant\project_a>vagrant init box_with_git
A `Vagrantfile` has been placed in this directory. You are now
ready to `vagrant up` your first virtual environment! Please read
the comments in the Vagrantfile as well as documentation on
`vagrantup.com` for more information on using Vagrant.

It’s worth remembering that the Vagrantfile which has just been created in project_a is pretty bare - just like we had to change the amount of memory etc… above, we need to do the same thing here (I’m sure there must be a better way!).

Bringing it up (note the cwd):

U:\virt\vagrant\project_a>vagrant up
Bringing machine 'default' up with 'virtualbox' provider...
==> default: Clearing any previously set forwarded ports...
==> default: Clearing any previously set network interfaces...
==> default: Preparing network interfaces based on configuration...
    default: Adapter 1: nat
==> default: Forwarding ports...
    default: 22 => 2222 (adapter 1)
==> default: Running 'pre-boot' VM customizations...
==> default: Booting VM...
==> default: Waiting for machine to boot. This may take a few minutes...
    default: SSH address: 127.0.0.1:2222
    default: SSH username: vagrant
    default: SSH auth method: private key
    default: Warning: Connection timeout. Retrying...
    default: Warning: Authentication failure. Retrying...

Woops. What just happened? Press ctrl-c to get out of that. Running vagrant ssh-config gives us a clue:

U:\virt\vagrant\project_a>vagrant ssh-config
Host default
  HostName 127.0.0.1
  User vagrant
  Port 2222
  UserKnownHostsFile /dev/null
  StrictHostKeyChecking no
  PasswordAuthentication no
  IdentityFile U:/virt/vagrant/insecure_private_key
  IdentitiesOnly yes
  LogLevel FATAL

Aha - IdentityFile isn’t right. On the default box this is defined as:

IdentityFile U:/virt/vagrant/.vagrant/machines/default/virtualbox/private_key

Open up Vagrantfile in project_a, and tell vagrant to use this instead:

config.ssh.private_key_path = "U:/virt/vagrant/.vagrant/machines/default/virtualbox/private_key"

Re-run vagrant up and the error is gone (we’ll walk through how to create ‘proper’ pairs later). Note you could also specify a password login, but you’d need to make sure it was set up correctly on the box first.

Automata Extraordinaire

Puppet!