Your First Recipe

Developed with
Start course
2h 12m

In this lesson, we will move from teaching you about Chef to hands-on practice in it.

We will start out with a simple recipe to install a web server, and edit the index.html to tease users that something awesome will be coming soon.

We will create a chef-repo and cookbook to house our recipes. We will open a terminal and decide which directory we want the Chef repository to reside. You will learn how to use the command chef generate repo to create the directory.

We will create a cookbook using two common commands: chef generate cookbook, and knife cookbook create. While they both seem to do the same thing, we will explain when to use each for their designated purpose. We will use the chef generate command for this exercise.

From here it is all hands-on: installing the web server, changing directories, naming the cookbook, and then starting the code. You will continue to test your code as we go through the exercise using Kitchen.

After the web server has been set up with the updated index.html file, we will recap all that you have learned via a quick test.


Welcome back! This is going to be a lot of fun. This is where you go from hearing about how Chef works conceptually, to seeing it in practice.

I’m going to start out this lesson with a simple recipe to install a web server, and edit the index.html so that anyone that views the homepage knows that something awesome will be coming soon. And over the next couple lessons I’ll slowly refactor it to install and configure a LAMP based application. In this case LAMP stands for Linux, Apache, MySQL and Python.

Also I won’t be deploying this to a Chef Server until later in the course, when the Cookbook to deploy the entire LAMP application is complete. That way I can focus on the code, and not working with Chef Server.

So, Chef code lives in a recipe, and recipes require a cookbook, and if you recall, cookbooks reside in the chef-repo. So before I start writing code, I need to create a chef-repo and a cookbook.
The chef-repo is just a directory, and it expects a subdirectory named cookbooks. Chef provides commands to generate the directory structure for you, and so that’s what I’ll use.

To start, I’ll open a terminal and change directories into the directory that I want my Chef repository to reside.

From here, I’ll issue the command “chef generate repo” followed by the name of the repo, which in this case is going to be “ca_repo”. And if I view the directory structure you can see it automatically created the directories that I need. And that’s all there is to creating the chef-repo.

Now I need to create a cookbook, which is just a directory with some specific files such as the metadata.rb file, and directories such as the recipes directory. And again, that means that you could create those manually. However, there are also ways to generate the cookbook for you automatically just like the chef-repo.

The two most common methods are to use the “chef generate cookbook command”, and the “knife cookbook create” command. You might be wondering why there are two commands for the same thing. The answer is that if you use the knife command to create a cookbook it will create a cookbook directory with subdirectories for all of the possible components.
Whereas the “chef generate” command will create a minimalist directory structure, that also includes sub-directories for test code.
Both of these commands allow you to customize the directory structure that gets created. And which one you use will be up to you eventually. For now, I’m going to use the “chef generate” command so it doesn’t create a bunch of directories for components that aren’t needed just yet.

So I’ll change directories into the ca_repo/cookbooks directory, and then use the “chef” command with the “generate cookbook” subcommand, followed by the name of the cookbook. I’ll name it learn_chef_cookbook. I’m including the word cookbook so that it makes it easy for you to recognize the different components. Now, with this having completed, you can see that I can view all of these directories and files from my text editor. Now I have everything I need to start writing code. So it’s time to start.

I said at the beginning of the lesson that the plan is to install a web server, and add some content to the index.html file. So I’m going to start by installing the Apache web server.

In my text editor I’ll keep expanding the directories until I get to the recipes directory in the “learn_chef_cookbook.” And I’ll add my code to this file here named default.rb. This default.rb file was automatically created when I generated the cookbook.

So, I’m going to start out by using the apt_package resource, and I’ll provide it the name of the package I want to install, which in this case is “apache2.”

And then I’ll open up the block with the keyword “do” and you may have noticed that my text editor automatically added the keyword “end” which closes this block. Inside the block I’ll set the action to the symbol :install and then save this file.

When starting out I recommend that you test your code frequently. And that way if something is wrong with the code, you’ll know early and it will help you identify where the problem is. So, let’s execute this recipe. To do that I’ll use Kitchen, which is the testing tool I mentioned previously in the course. Kitchen requires just a bit of setup in order to get it working the way I want. However, once setup, it’s going to save time and effort, because it can automatically run code against multiple VMs. This allows you to test for different operating systems, or OS versions, etc.

So I’m going to edit the .kitchen.yml file that’s in the learn_chef_cookbook directory. Here’s a quick rundown of the kitchen configuration file. I’m not going to explain all of this file in this course. I’m going to explain just the enough to hopefully make it clear how it’s working. So I’ll skip the sections related to running tests. I’ll cover that in a future course on test driven development with Chef.

So, the file starts out by specifying a driver. And Kitchen offers a lot of options for this setting, and I’ll be using the Vagrant driver, which is why I had to install Vagrant earlier in the course. The driver will basically determine how the VM gets created. In this case it’ll be on my Mac, with Vagrant.

Next, is the provisioner, and it’s set to Chef Zero, which is a version of the Chef Server that’s used for development. Chef Zero allows for testing things out in development without needing a Chef Server setup.

I’ll skip the verifier, since that’s not something you need to know for this course. The important part is here in the platforms section. This is where you can specify the VMs that’ll be created. I’m going to use Ubuntu 14.04, and I’ll use the box image from the user bento. This value here for box allows Vagrant to know which VM image to download from the Vagrant image repository.
And then this network section tells Vagrant to map port 80 on the VM to 8081 on my Mac. And that’ll allow me to use my browser to test out the web page. So once everything is working correctly, I’ll be able to browse to localhost:8081 on my Mac and I should see a web page that’s running from the VM.

And the last interesting bit is at the bottom here, and it’s this runlist property. This is where I can tell Kitchen which recipes to run, and from which cookbooks. This is an important concept, and one that I’ll cover in a later. For now, a run list is basically what it sounds like: a list of things that you want Chef to run.

You might be wondering why I’m specifying a cookbook here, and not a recipe, and that’s a great question. Since I’m specifying a cookbook and not specifically a recipe Chef uses the default recipe. Which is that default.rb file where I put my code.

If I had put my code into a file under the recipes directory named lamp.rb and I didn’t want Chef to run the code in the default recipe, I could use this syntax here, where I’d specify the cookbook name, followed by two colons, and then the name of the recipe, which in this example is lamp.

So, that’s a very slightly modified version of the default kitchen file that Chef created for me when I ran the cookbook generator.

Now, to run Kitchen issue the “kitchen converge” command. This is going to do quite a lot of work, especially the first time you run it, because it needs to download the VM image and then run the cookbook against it.

I’ll fast forward to when it’s complete. Scrolling back through, you can see that Kitchen output some useful information as it went along. You can see here under this Recipe section that it executed the default recipe for the learn_chef_cookbook. And then it installed version 2.4.7 of the Apache2 package.

I want pause for just a second to reflect on how cool this is. You may be thinking: “Big deal, I could have had this installed so much faster manually!”
And you’d probably be right. However, once your Chef server is all setup, and all the nodes are registered, you could run this on 1 or even 50,000 nodes at once. So what makes this so cool, at least to me, is that this will scale very well!

Okay, I have the Apache web server installed according to Chef. I want verify that by browsing to it the URL. And...there’s the default Apache landing page.

Now that Apache is installed it’s time to change the index.html to say “coming soon.” So I’ll head back to the default.rb file and add a file resource. The name portion of the file resource is going to be the full path and filename of the file that I want to edit on the node. In this case it’ll be /var/www/html/index.html because that’s where Apache stores the index.html for the default installation on an Ubuntu system.

And I’ll open up the block and add a property for content. And I’ll just throw in some very crude markup with the text “Coming Soon!” Chef allows you to pull a file from the files directory, however if the content isn’t very complex, you can also use the content property. And I’ll save this, and then head back to the command line to re-run the same command that I ran to test this previously.

This time it’s going to show a lot more output because it’s showing the change of the index.html file. And now that it’s complete if I reload the browser, you can see that it’s showing the new content.

With just a few lines of code I’ve installed the Apache web server, and with a few more I’ve set the content in the index.html file.

I want wrap up this lesson here. Even though I only wrote a little bit of code, there was a lot that happened in this lesson. So let’s see how much you remember!

What command is used to run the recipe?
While there are multiple ways to execute a recipe, the method I showed you was to use Kitchen via the “kitchen converge” command, and have it automatically create a VM and run the recipes specified in the runlist.

The next question is: Inside of the runlist if you only specify the name of the cookbook, and not a recipe what does Chef do?
The answer is that, if you specify a cookbook in the runlist without including a recipe name, Chef will run the default recipe. And that default recipe consists of whatever code is in the default.rb file. So if that file is empty, then nothing will happen.

The final question is: Which resource did I use to install Apache and what potential problems do you think it might cause?
In the recipe demo I used the apt_package resource. Now the second part of this question is open ended. However a potential problem that I see is that this cookbook will only work on Debian based Linux distributions, because it requires the use of the apt package manager.
So this recipe isn’t very cross distribution compatible. Though, I’ll fix that up in the next lesson

Alright, if you’re ready to start refactoring this recipe to be a bit more cross-distro compatible, then let’s get started in the next lesson!

About the Author
Learning Paths

Ben Lambert is a software engineer and was previously the lead author for DevOps and Microsoft Azure training content at Cloud Academy. His courses and learning paths covered Cloud Ecosystem technologies such as DC/OS, configuration management tools, and containers. As a software engineer, Ben’s experience includes building highly available web and mobile apps. When he’s not building software, he’s hiking, camping, or creating video games.

Covered Topics