Provision Azure Boxes with Vagrant

I was playing with Azure the other day, and after spending an hour fighting the web client to provision a bunch of boxes, I realized I could have avoided all that pain by using Vagrant. If you want to learn how, follow along.

(If you’d rather not read all this text, and want to check out the example directly, head to unindented/provision-azure-boxes-with-vagrant.)


The first thing we’ll need to do is download and install VirtualBox and Vagrant. I’ve got the following versions:

$ VBoxManage --version
$ vagrant --version
Vagrant 1.7.2

Then we’ll install the awesome Vagrant Azure Provider by running:

$ vagrant plugin install vagrant-azure
Installing the 'vagrant-azure' plugin. This can take a few minutes...
Installed the plugin 'vagrant-azure (1.1.1)'!

Azure CLI#

We’re also going to install the Azure CLI, as it will help us retrieve certain information we’ll need to create our machine. If we have a recent version of Node.js installed, then it’s just a matter of running:

$ npm install -g azure-cli

Let’s check which version we are on:

$ azure --version
0.9.2 (node: 0.10.38)

Now, in order to interact with Azure machines, we’ll need to download our Publish Settings file, which contains secure credentials and additional information about our Azure subscriptions:

$ azure account download
info:    Executing command account download
info:    Launching browser to
help:    Save the downloaded file, then execute the command
help:      account import <file>
info:    account download command OK

If the command doesn’t launch your browser, just browse to that URL manually.

Once we’ve logged in, the browser will download a file with the extension .publishsettings. Let’s import it:

$ azure account import ~/Downloads/*.publishsettings
info:    Executing command account import
info:    account import command OK

That .publishsettings file contains sensitive information, so be sure to remove it once it has been imported:

$ rm ~/Downloads/*.publishsettings

If we list our accounts, we’ll see the one we’ve just imported:

$ azure account list
info:    Executing command account list
data:    Name                                         Id                                    Tenandt Id  Current
data:    -------------------------------------------  ------------------------------------  ----------  -------
data:    Windows Azure MSDN - Visual Studio Ultimate  XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX  undefined   true
info:    account list command OK

This sensitive information will be stored in plain text at ~/.azure/azureProfile.json. We still need it, but if we wanted to remove all info related to our accounts at a later time, we could run:

$ azure account clear

Anyways, let’s see the list of images using the latest Ubuntu that are available on Azure:

$ azure vm image list | grep -i ubuntu-14_04_2
data:    b39f27a8b8c64d52b05eac6a62ebad85__Ubuntu-14_04_2-LTS-amd64-server-20150506-en-us-30GB                                             Public    Linux
data:    b39f27a8b8c64d52b05eac6a62ebad85__Ubuntu-14_04_2_LTS-amd64-server-20150309-en-us-30GB                                             Public    Linux

Now let’s see where we can provision VMs:

$ azure vm location list
info:    Executing command vm location list
+ Getting locations
data:    Name
data:    ----------------
data:    Central US
data:    South Central US
data:    West US
data:    East US
data:    North Central US
data:    East US 2
data:    North Europe
data:    West Europe
data:    Southeast Asia
data:    East Asia
data:    Japan West
data:    Japan East
data:    Brazil South
info:    vm location list command OK

Awesome. I think we have all the info we need.


The Azure provider seems to only like PEM files with both the public and private keys, and X509 certificates, so let’s get us some of that:

$ openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout ~/.ssh/azurevagrant.key -out ~/.ssh/azurevagrant.key
$ chmod 600 ~/.ssh/azurevagrant.key
$ openssl x509 -inform pem -in ~/.ssh/azurevagrant.key -outform der -out ~/.ssh/azurevagrant.cer

The .cer file contains our public key, but the .pem file contains both our public and private keys, so we’ll need to secure it appropriately! Ok, now we can upload that .cer file as a management certificate in Azure:

Management certificates in Azure


Now the interesting part. Let’s create a Vagrantfile with all this information, so that we can provision a box:


Vagrant.configure(VAGRANTFILE_API_VERSION) do |config|     = 'azure'
  config.vm.box_url = ''

  config.ssh.username         = 'vagrant'
  config.ssh.private_key_path = File.expand_path('~/.ssh/azurevagrant.pem')

  config.vm.provider :azure do |azure|
    azure.mgmt_certificate = File.expand_path('~/.ssh/azurevagrant.pem')
    azure.mgmt_endpoint    = ''
    azure.subscription_id  = 'XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX'

    azure.cloud_service_name = 'azurevagrant'
    azure.storage_acct_name  = 'azurevagrantstorage'
    azure.deployment_name    = 'azurevagrantdeployment'

    azure.vm_name     = 'azurevagrantsmall'
    azure.vm_image    = 'b39f27a8b8c64d52b05eac6a62ebad85__Ubuntu-14_04_2-LTS-amd64-server-20150506-en-us-30GB'
    azure.vm_size     = 'Small'
    azure.vm_location = 'North Europe'

    azure.ssh_port             = '22'
    azure.ssh_private_key_file = File.expand_path('~/.ssh/azurevagrant.pem')
    azure.ssh_certificate_file = File.expand_path('~/.ssh/azurevagrant.cer')

    azure.tcp_endpoints = '8000'

  config.vm.provision 'shell', inline: 'echo OHAI'

Ready to go#

Now it’s just a matter of running vagrant up:

$ time vagrant up
==> ...
==> default: Looking for local port 22
==> default: Found port mapping 22 --> 22
==> default: Rsyncing folder: ~/vagrant-azure/ => /vagrant
==> default: Running provisioner: shell...
==> default: VM OS: Linux
==> default: Provisioning using SSH
==> default: Looking for local port 22
==> default: Found port mapping 22 --> 22
    default: Running: inline script
==> default: stdin: is not a tty
==> default: OHAI

real    4m6.736s
user    0m5.475s
sys     0m0.469s

Ok, it took a while, but the box got provisioned! Now we can ssh into it and do whatever we want:

$ vagrant ssh
==> ...
==> default: Looking for local port 22
==> default: Found port mapping 22 --> 22
Welcome to Ubuntu 14.04.2 LTS (GNU/Linux 3.16.0-37-generic x86_64)

 * Documentation:

 System information disabled due to load higher than 1.0

  Get cloud support with Ubuntu Advantage Cloud Guest:

0 packages can be updated.
0 updates are security updates.


Once we are done with the box, we can destroy it by running:

$ vagrant destroy

Have fun!

Posted on by Daniel Perez Alvarez. Got any comments or suggestions? Send me a tweet or an email.