Skip to content

Instantly share code, notes, and snippets.


rsrchboy/ Secret

Last active Aug 29, 2015
What would you like to do?
tutorial -- vagrant + chef

Manage Chef without giving up your box

In our previous tutorial Installing Chef on the CenturyLink Cloud, we showed how to set up three hosts: one as a Chef server, one as management workstation (client), and one as a Chef-managed node.

However, sometimes it's useful to have the Chef client on your local workstation. Given that the Chef client install feels somewhat... invasive... for a machine used as a primary workstation -- you know, the machine you absolutely, without hesitation depend on in order to access and manage everything else -- this seems like an excellent opportunity to examine using Vagrant in conjunction with a virtualization provider to provide for an easy, repeatable way to create contained VM's usable as Chef management interfaces.


Easy, reproducible build of an Ubuntu 14.04LTS (trusty) VirtualBox VM with the Chef management tools installed.


Installed and configured on your system:

Note that if you're using Ubuntu Trusty (14.04LTS) the latest level of vagrant available through the Ubuntu apt repositories is 1.4.3, as of this writing.

Using a different virtualization provider is left as an exercise for a willing reader.

The files (and some others) described in this article can be obtained by cloning this repository:

git clone

Ok, ready? Go!

Building our VM

Picking a base box

A Vagrant base box is basically a template box; it's used as the foundation of the box you'll be creating. If you're familiar with how Docker images are built then this concept is very similar to the "FROM" line in a Dockerfile.

Thanks to Vagrant Cloud, there are a large number of publicly accessible base boxes available, and the Ubuntu team has an official 64-bit trusty base box they maintain.

Creating our initial Vagrantfile

Create a minimal initial Vagrantfile with:

$ vagrant init --minimal ubuntu/trusty64
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
`` for more information on using Vagrant.

...this yields:

[gist a328b4d542d8c731b067 file="Vagrantfile.minimal" /]

If you want to explore a bit, leaving off the --minimal creates a substantially larger Vagrantfile.

Provision our VM

Now comes the fun part. Basically, we take all the steps outlined in the previous tutorial and have Vagrant execute them inside the virtual machine as part of the provisioning process.

The initial Vagrantfile created above contains only the barebones minimum required to launch a new virtual machine based off of the ubuntu/trusty64 image. (And a lot of comments!)

Let's stick all of our needed provisioning into one script:

[gist a328b4d542d8c731b067 file="" /]

...and configure Vagrant:

[gist a328b4d542d8c731b067 file="Vagrantfile" /]

Fire it up!

Now, all we should need to do is "vagrant up" in order to launch and provision our VM:

[gist a328b4d542d8c731b067 file="vagrant-up.log" /]

There's a lot of noise generated by Chef's installation script, so I've omitted it from the log, above. To check the status, you can run "vagrant status":

[gist a328b4d542d8c731b067 file="vagrant-status.log" /]

Configuring the Chef Client

To do this you can use the '' script. Note that the directory containing the Vagrantfile is exported into the VM at '/vagrant', so we can log in to our newly-created VM and configure the client as such:

[gist a328b4d542d8c731b067 file="vagrant-client-configure.log" /]

...for brevity, the above output has been trimmed; it should mirror the description you see from the previous tutorial.


Now, whenever you need to use your Chef management tools, you can simply "vagrant ssh" into this VM.


# Configure our Chef tools.
# Run as:
# ./ server.hostname.or.ip
# Based on:
# there's no place like $HOME...
# clone the chef-repo repository if we don't have one already
[ -d chef-repo ] || git clone
cd chef-repo
mkdir -p ~/chef-repo/.chef
echo '.chef' >> .git/info/exclude
cd ~/chef-repo/.chef
# fetch and use our server's keys...
scp root@$_svr:/etc/chef-server/admin.pem .
scp root@$_svr:/etc/chef-server/chef-validator.pem .
knife configure --initial
# sanity check
knife client list
$ git clone
# Provision our VM with Chef tools.
# Chris Weyl <> 2014
apt-get install -y git
curl -L | bash
chef-client -v
# ensure our path is configured
echo 'export PATH="/opt/chef/embedded/bin:$PATH"' > /etc/profile.d/
$ vagrant ssh
Welcome to Ubuntu 14.04 LTS (GNU/Linux 3.13.0-30-generic x86_64)
* Documentation:
System information as of Sun Sep 21 23:15:09 UTC 2014
System load: 0.0 Processes: 72
Usage of /: 3.1% of 39.34GB Users logged in: 0
Memory usage: 24% IP address for eth0:
Swap usage: 0%
Graph this data and manage this system at:
Get cloud support with Ubuntu Advantage Cloud Guest:
Last login: Sun Sep 21 23:15:10 2014 from
vagrant@chef-mgmt:~$ ls /vagrant Vagrantfile Vagrantfile.minimal Vagrantfile.with-comments vagrant-status.log vagrant-up.log
vagrant@chef-mgmt:~$ sh /vagrant/
Cloning into 'chef-repo'...
remote: Counting objects: 225, done.
remote: Total 225 (delta 0), reused 0 (delta 0)
Receiving objects: 100% (225/225), 46.12 KiB | 0 bytes/s, done.
Resolving deltas: 100% (58/58), done.
Checking connectivity... done.
# ... etc
$ vagrant init --minimal ubuntu/trusty64
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
`` for more information on using Vagrant.
$ vagrant status
Current machine states:
ctl/chef-mgmt running (virtualbox)
The VM is running. To stop this VM, you can run `vagrant halt` to
shut it down forcefully, or you can run `vagrant suspend` to simply
suspend the virtual machine. In either case, to restart it again,
simply run `vagrant up`.
$ vagrant up
Bringing machine 'ctl/chef-mgmt' up with 'virtualbox' provider...
==> ctl/chef-mgmt: Importing base box 'ubuntu/trusty64'...
==> ctl/chef-mgmt: Matching MAC address for NAT networking...
==> ctl/chef-mgmt: Checking if box 'ubuntu/trusty64' is up to date...
==> ctl/chef-mgmt: Setting the name of the VM: 001-vagrant-encapsulating-chef_ctlchef-mgmt_1411340598472_76936
==> ctl/chef-mgmt: Clearing any previously set forwarded ports...
==> ctl/chef-mgmt: Clearing any previously set network interfaces...
==> ctl/chef-mgmt: Preparing network interfaces based on configuration...
ctl/chef-mgmt: Adapter 1: nat
==> ctl/chef-mgmt: Forwarding ports...
ctl/chef-mgmt: 22 => 2222 (adapter 1)
==> ctl/chef-mgmt: Booting VM...
==> ctl/chef-mgmt: Waiting for machine to boot. This may take a few minutes...
ctl/chef-mgmt: SSH address:
ctl/chef-mgmt: SSH username: vagrant
ctl/chef-mgmt: SSH auth method: private key
ctl/chef-mgmt: Warning: Connection timeout. Retrying...
ctl/chef-mgmt: Warning: Remote connection disconnect. Retrying...
==> ctl/chef-mgmt: Machine booted and ready!
==> ctl/chef-mgmt: Checking for guest additions in VM...
==> ctl/chef-mgmt: Setting hostname...
==> ctl/chef-mgmt: Mounting shared folders...
ctl/chef-mgmt: /vagrant => /home/rsrchboy/work/CL/tutorials/001-vagrant-encapsulating-chef
==> ctl/chef-mgmt: Running provisioner: shell...
ctl/chef-mgmt: Running: /tmp/user/1000/
# ... lots of provisioning text snipped ...
# -*- mode: ruby -*-
# vi: set ft=ruby ts=2 :
# Vagrantfile API/syntax version. Don't touch unless you know what you're doing!
Vagrant.configure(VAGRANTFILE_API_VERSION) do |config|
# give our box a useful name
config.vm.define 'ctl/chef-mgmt' = 'ubuntu/trusty64'
config.vm.hostname = 'chef-mgmt'
config.ssh.forward_agent = true
config.vm.provision :shell, path: ''
# -*- mode: ruby -*-
# vi: set ft=ruby ts=2 :
# Vagrantfile API/syntax version. Don't touch unless you know what you're doing!
Vagrant.configure(VAGRANTFILE_API_VERSION) do |config| = "ubuntu/trusty64"
# -*- 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
# Every Vagrant virtual environment requires a box to build off of. = "ubuntu/trusty64"
# Disable automatic box update checking. If you disable this, then
# boxes will only be checked for updates when the user runs
# `vagrant box outdated`. This is not recommended.
# config.vm.box_check_update = false
# Create a forwarded port mapping which allows access to a specific port
# within the machine from a port on the host machine. In the example below,
# accessing "localhost:8080" will access port 80 on the guest machine.
# "forwarded_port", guest: 80, host: 8080
# Create a private network, which allows host-only access to the machine
# using a specific IP.
# "private_network", ip: ""
# Create a public network, which generally matched to bridged network.
# Bridged networks make the machine appear as another physical device on
# your network.
# "public_network"
# If true, then any SSH connections made will enable agent forwarding.
# Default value: false
# config.ssh.forward_agent = true
# Share an additional folder to the guest VM. The first argument is
# the path on the host to the actual folder. The second argument is
# the path on the guest to mount the folder. And the optional third
# argument is a set of non-required options.
# config.vm.synced_folder "../data", "/vagrant_data"
# Provider-specific configuration so you can fine-tune various
# backing providers for Vagrant. These expose provider-specific options.
# Example for VirtualBox:
# config.vm.provider "virtualbox" do |vb|
# # Don't boot with headless mode
# vb.gui = true
# # Use VBoxManage to customize the VM. For example to change memory:
# vb.customize ["modifyvm", :id, "--memory", "1024"]
# end
# View the documentation for the provider you're using for more
# information on available options.
# Enable provisioning with CFEngine. CFEngine Community packages are
# automatically installed. For example, configure the host as a
# policy server and optionally a policy file to run:
# config.vm.provision "cfengine" do |cf|
# cf.am_policy_hub = true
# # cf.run_file = ""
# end
# You can also configure and bootstrap a client to an existing
# policy server:
# config.vm.provision "cfengine" do |cf|
# cf.policy_server_address = ""
# end
# Enable provisioning with Puppet stand alone. Puppet manifests
# are contained in a directory path relative to this Vagrantfile.
# You will need to create the manifests directory and a manifest in
# the file default.pp in the manifests_path directory.
# config.vm.provision "puppet" do |puppet|
# puppet.manifests_path = "manifests"
# puppet.manifest_file = "site.pp"
# end
# Enable provisioning with chef solo, specifying a cookbooks path, roles
# path, and data_bags path (all relative to this Vagrantfile), and adding
# some recipes and/or roles.
# config.vm.provision "chef_solo" do |chef|
# chef.cookbooks_path = "../my-recipes/cookbooks"
# chef.roles_path = "../my-recipes/roles"
# chef.data_bags_path = "../my-recipes/data_bags"
# chef.add_recipe "mysql"
# chef.add_role "web"
# # You may also specify custom JSON attributes:
# chef.json = { mysql_password: "foo" }
# end
# Enable provisioning with chef server, specifying the chef server URL,
# and the path to the validation key (relative to this Vagrantfile).
# The Opscode Platform uses HTTPS. Substitute your organization for
# ORGNAME in the URL and validation key.
# If you have your own Chef Server, use the appropriate URL, which may be
# HTTP instead of HTTPS depending on your configuration. Also change the
# validation key to validation.pem.
# config.vm.provision "chef_client" do |chef|
# chef.chef_server_url = ""
# chef.validation_key_path = "ORGNAME-validator.pem"
# end
# If you're using the Opscode platform, your validator client is
# ORGNAME-validator, replacing ORGNAME with your organization name.
# If you have your own Chef Server, the default validation client name is
# chef-validator, unless you changed the configuration.
# chef.validation_client_name = "ORGNAME-validator"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment