Skip to content

Instantly share code, notes, and snippets.

What would you like to do?
Chef Intro Workshop

Chef Workshop

This workshop will take you through the very basics of creating and running Chef recipes with chef-solo.

This workshop assumes you're running on a Mac or Linux. If you're running Windows best to set up a Linux VM to run this workshop.

Getting Started

You'll need an up-to-date version of Chef installed. The easiest way is to use the pre-made Chef install packages from the Chef site. These packages come bundled with an embedded Ruby installation, so it's all ready to go out of the box.

If the install works you should be able to run chef-solo --help from the command line.

The Simplest Recipe

Let's make the simplest recipe possible. Open an editor and create a file called hello_world.rb:

directory "/tmp/hello"

file "/tmp/hello/hello_world.txt" do
  content "Hello Chef world"

This code defines two "resources":

  1. A directory that should be created
  2. A file with the contents Hello Chef world

Now from the command line run:

$ chef-apply hello_world.rb

You'll see some output indicating that the resources specified in the recipe are being created. Now run the chef-apply command a second time. This time you'll notice that Chef says that the resources are "up to date". This is idempotency in action. Chef does not execute your resources again if they are already in the desired state.

Let's set the file permission of the hello_world.txt file.

directory "/tmp/hello"

file "/tmp/hello/hello_world.txt" do
  content "Hello Chef world"
  mode 0600

Now re-run the chef-apply command. You'll see Chef updating the file permissions.

There are lots of other resources and each resource has many attributes that you can use to configure them. They're all documented in the Chef Resources reference, which you'll end up spending a lot of time in as you write more complex recipes.

So there you have it, the simplest possible Chef recipe. That wasn't so hard, was it?

Making a Cookbook

Chef-apply is fine for playing around, but in reality you'll want to use chef-solo to run real cookbooks. Let's turn our simple recipe into a real Chef cookbook named "hello".

Cookbooks must have a particular directory structure. You can read about this on the Chef website, but all we need is a top level cookbooks directory, a subdirectory of that with the name of our cookbook ("hello") and a subdirectory of that to hold the recipes:

$ mkdir -p cookbooks/hello/recipes

Now copy the hello_world.rb recipe that you created earlier into the recipes directory.

To run it with chef-solo you need to create a config file to tell it where to find your cookbook. Create a file called "solo.rb" in the top level project directory with the following contents:

cookbook_path File.expand_path(File.dirname(__FILE__)) + '/cookbooks'

Now run chef-solo:

$ chef-solo -c solo.rb -o 'hello::hello_world'

This runs chef-solo pointing at the config file you just created and tells it to run the "hello-world" recipe from the "hello" cookbook.

If all goes well you should see the same output as before.

Congratulations you just made a cookbook. Easy right?

Using Templates

So our recipe would be much more useful and reusable if we could customize the contents of the hello_world.txt file. Let's do that and introduce Chef templates.

First lets make the contents of the file a template. Chef has a rather complex way of working out which template to use for a particular environment, but I recommend ignoring that and always using just the "default" template location. To create the template simply create a file cookbooks/hello/templates/default/hello_world.txt.erb with the following contents:

Hello and welcome to Chef, <%= @name %>.

The <%= @name => is template syntax and will get substituted with whatever value we pass for "name" from the recipe when we invoke the template.

Now edit your recipe to make the file load its contents from this template:

directory "/tmp/hello"

template "/tmp/hello/hello_world.txt" do
  source "hello_world.txt.erb"
  variables({name: "Bilbo Baggins"})
  mode 0600

Run your recipe and you should see the contents of the file change as expected.

Parameterizing Your Recipe With Attributes

It's not very useful always greeting Bilbo Baggins - let's make this a configurable part of our recipe using Chef attributes.

Whenever you run a Chef recipe, Chef creates a global variable called node that is a big hash of configuration "attributes" for the current Chef run. Chef has a complex way of resolving these attributes, but it's best to keep things simple.

Let's first change our recipe to use an attribute:

directory "/tmp/hello"

template "/tmp/hello/hello_world.txt" do
  source "hello_world.txt.erb"
  variables({name: node['hello']['name']})
  mode 0600

Now create a file cookbooks/hello/attributes/default.rb. This is where you define the default values of all your attributes.

default['hello']['name'] = 'Frodo Baggins'

Rerun chef-solo and check the results. As a final step, let's override the default value for our attribute by passing in override attributes to chef-solo. Create a file node.json in the top level project directory.

  "hello": {
    "name": "Gandalf the Grey"

This file holds the attributes that will be passed into our recipe. They will override any default values specified in the cookbook. In a real scenario, this is where you would define the various attributes that vary between the different environments you deploy to (you'd likely have one node.json per environment).

Re-run chef-solo with this command line:

$ chef-solo -c solo.rb -o 'hello::hello_world' -j node.json

And there you have it. A fully parameterised hello world cookbook ready to be deployed across the thousands of servers under your command.

Where to Next?

Now you've had a taste of writing Chef recipes, here's a few useful resources to help you learn more:

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.