Puppet Language Essentials
The course is part of this learning path
Puppet is an IT automation system. If you need to install, configure, and update servers, then Puppet can help you tremendously. Instead of doing all of these tasks manually, you can tell Puppet to configure your servers for you.
Not only will Puppet free you from the drudgery of repetitive tasks, but you will also gain major benefits, such as consistency, reliability, speed of deployment, ease of recovery, and scalability.
Do you often have slightly different configurations on servers that are supposed to be identical? With Puppet, you’ll no longer have to figure out why something works on one server but doesn’t work on another one because Puppet will configure them in the same way. You’ll also have less downtime because there is less that can go wrong when everything is configured the way it’s supposed to be.
Do you always seem to be adding more servers? Provisioning servers is a breeze when Puppet already knows how to configure them.
This course will get you started on bringing these benefits to your network. It’s a hands-on course with exercises every step of the way to give you experience using Puppet. First, I will show you how to install Puppet on a virtual machine on your own desktop. Then you will use it as a test environment to learn how to write Puppet code to automate server configuration.
- Install Puppet server
- Use pre-built Puppet modules
- Use manifests, classes, resources, facts, nodes, and templates
- Create your own Puppet modules
We've already been using Puppet's configuration language a little bit, but now we're now going to go into it more more deeply.
Like most programming languages, the Puppet language includes support for variables, data types, conditional statements, parameters and so on. To see how these work in Puppet, we're going to build our own module for configuring the Etsy MOTD file rather than using the one provided by Puppet labs.
Suppose that you have a mixture of Red Hat and Ubuntu servers in your network, and you always forget which Linux distribution calls the Apache service Apache 2, and which one calls it httpd. To make things easier, you decide to put a reminder in the message of the day file on each server. So on a Red Hat server, the MOTD file would say that the Apache service is called httpd. And on an Ubuntu server it would say Apache 2.
Okay, I admit that this probably isn't how you would use Puppet in the real world. But this exercise does have the advantage of using a simple configuration file that you can write from scratch, while still having enough to it to demonstrate many aspects of the Puppet language. Once you've learned how to do this exercise, and the template exercises in the following lessons, then you'll be able to apply this knowledge to doing things like configuring Apache itself.
Okay, back to the task at hand. First, we need to build a directory tree for our module. So go into the modules directory, and use Puppet module generate. Don't forget the skip interview parameter. Let's call the module MOTD Apache. Remember that we need to include the author's name and a dash before the module name. We'll just use test as the author again.
The first thing we're going to do is write some code that figures out which operating system is running on a server so we know which message to put in the MOTD file. Luckily, Puppet makes this very easy because it generates a list of facts about each node before running your code. The program that does this is called Facter, note that it is spelled with an er rather than an or. You can see the facts that Facter generates just by running it at the command line. We'll pipe it to more because it generates a very long list of facts.
You can see that it lists things like the disks that are on the node. We're not going to go through these here because it's much easier to see what facts are available by looking at Puppet's online documentation.
Although there are a lot of facts on this page, most of them are actually legacy facts that you probably won't need to use. Just look at the modern facts for now. As of this moment, there are only 37 facts, and you probably won't even use most of these. The disrelatively small list is deceptive because each one of these facts contains numerous elements. The fact we need right now is OS. If you click on that, you'll see that it has a ton of elements within it. The obvious one to use to identify a nodes operating system is the name element. However, it would be better to use the family element just in case our code runs on a node that isn't on either Red Hat or Ubuntu. For example, if a node is running CentOS, then it will follow the same conventions as Red Hat, or if a node is running Debian, it will use the same conventions as Ubuntu.
Now that we know which fact to use, let's edit the init.pp manifest.
First, we're going to set a variable to the OS family of the node it's running on. Creating a variable in Puppet is very simple, you just put a dollar sign in front of a name. Then you put an equal sign and what you want to set it to. We'll call our variable OS family and set it to the family element of the OS fact.
There are two ways to refer to a fact. You can either refer to it with the regular variable notation, so in this case it would be $os and then family in square brackets because family is an element of the OS variable, and we have to use a ray notation for that. Or you can refer to it with the dollar facts variable name, so in this case it would be $facts and then os in square brackets and then family in square brackets. Although the first way is simpler, I recommend that you use the second way because that will make it very clear that you're referencing a built in fact rather than a variable that was set somewhere else.
Oh, and one more thing. Puppet variables are actually constants because you can't reassign them after you've set their value. But people call them variables anyway.
Alright, so we have our first line of code.
Next we're going to use an if statement so we can run different code depending on which OS family is on the node. Puppet's if statement syntax is similar to how it is in most other languages, but especially Ruby. After the if, you put the conditional expression. We'll start with the OS family variable and then we need to use a comparison operator. Here are all of the comparison operators in Puppet.
The one we need is the equality operator which is two equal signs. After the equality operator, we need to put the name of the OS family in quotes. In the case of Ubuntu, it's Debian. Then we put in the opening curly brace.
Now we'll set a variable to the string we want to put in the MOTD file for a Debian based OS. Let's call the variable MOTD_content.
Finally, we put in the closing curly brace. Now we need to put in an else statement. If we want to say that all OS families other than Debian or Red Hat, then a simple else will do. But we shouldn't do that in case this code runs on a node with a different OS family. So let's use an else if. In Puppet, like in Ruby, the else if command is spelled elsif. Then we put the conditional expression to test for a Red Hat family and finish with an opening curly brace.
Now we set the MOTD_content variable for the Red Hat family.
Now we put in a closing brace and then we need a catch all for all other OS families, so we'll use else and an opening brace.
We'll set the MOTD_content for unknown OS's, and finish with a final closing brace.
Now that the MOTD_content variable is set, we need to put it in the MOTD file. We've already covered how to do this so I'll just add the code.
Let's try it out. Now we'll see what's in the MOTD file. Considering that we're running on an Ubuntu system, it is correct in saying that the Apache service name is Apache 2.
Although this code works, frankly it's a little bit messy and hard to read. Let's see what other conditionals are available in Puppet.
An unless statement is sort of like the reverse of an if statement, so that won't work. A case statement would be a good choice for this, but a selector would be even better. A selector is similar to a case statement, but it returns a value instead of executing code.
Let's edit init.pp again. I'll just put the converted code in so you can see what it looks like.
That looks much nicer doesn't it? The selector is a very handy tool in Puppet because there are many times when you want to set a variable to one of a set of potential values based on a particular fact.
Also notice that since I just set the service variable instead of the entire message that will go in the MOTD file, I had to change how the content attribute is set. This is an example of how you can put a variable inside a string. As long as you put double quotes around the string, then you can put a variable inside the string and it's value will be put in its place. Note that I used curly braces around the variable name. In this case it would work even if I didn't put the braces in because the backslash N after service is clearly not part of the variable name. But it's a good idea to get in the habit of putting the braces in because they are often necessary.
When we do another Puppet run with the new code, Puppet doesn't change anything because the new code does exactly the same thing as the old code did.
And that's it for this lesson.
About the Author
Guy launched his first training website in 1995 and he's been helping people learn IT technologies ever since. He has been a sysadmin, instructor, sales engineer, IT manager, and entrepreneur. In his most recent venture, he founded and led a cloud-based training infrastructure company that provided virtual labs for some of the largest software vendors in the world. Guy’s passion is making complex technology easy to understand. His activities outside of work have included riding an elephant and skydiving (although not at the same time).