Puppet 3 Beginner's Guide
Puppet 3 Beginner's Guide
info
Puppet 3 Beginner's Guide
John Arundel
BIRMINGHAM - MUMBAI
www.it-ebooks.info
Puppet 3 Beginner's Guide
All rights reserved. No part of this book may be reproduced, stored in a retrieval system,
or transmitted in any form or by any means, without the prior written permission of the
publisher, except in the case of brief quotations embedded in critical articles or reviews.
Every effort has been made in the preparation of this book to ensure the accuracy of the
information presented. However, the information contained in this book is sold without
warranty, either express or implied. Neither the author, nor Packt Publishing, and its dealers
and distributors will be held liable for any damages caused or alleged to be caused directly
or indirectly by this book.
Packt Publishing has endeavored to provide trademark information about all of the
companies and products mentioned in this book by the appropriate use of capitals.
However, Packt Publishing cannot guarantee the accuracy of this information.
ISBN 978-1-78216-124-0
www.packtpub.com
www.it-ebooks.info
Credits
Reviewers Proofreader
Ugo Bellavance Lawrence A. Herman
Jason Slagle
Johan De Wit Indexer
Monica Ajmera Mehta
Acquisition Editor
Joanne Fitzpatrick Graphics
Ronak Dhruv
www.it-ebooks.info
About the Author
John Arundel is an infrastructure consultant who helps people make their computer
systems more reliable, useful, and cost-effective and has fun doing it. He has what Larry
Wall describes as the three great virtues of a programmer: laziness, impatience, and hubris.
Laziness, because he doesn't like doing work that a computer could do instead. Impatience,
because he wants to get stuff done right away. Hubris, because he likes building systems that
are as good as he can make them.
He was formerly a senior operations engineer at global telco Verizon, designing resilient,
high-performance infrastructures for corporations such as Ford, McDonald's, and Bank of
America. He now works independently, helping to bring enterprise-grade performance and
reliability to clients with slightly smaller pockets but very big ideas.
He likes writing books, especially about Puppet. It seems that at least some people enjoy
reading them, or maybe they just like the pictures. He also occasionally provides training and
coaching on Puppet, which turns out to be far harder than simply doing the work himself.
Off the clock, he can usually be found driving a Land Rover up some mountain or other.
He lives in a small cottage in Cornwall and believes, like Cicero, that if you have a garden
and a library, then you have everything you need.
www.it-ebooks.info
About the Reviewers
Ugo Bellavance has done most of his studies in e-commerce, started using Linux at Red
Hat 5.2, got Linux training from Savoir-Faire-Linux at the age of 20, and got his RHCE on RHEL
6 in 2011. He's been a consultant in the past, but he's now an employee for a provincial
government agency for which he manages the infrastructure (servers, workstations,
network, security, virtualization, SAN/NAS, PBX). He's a big fan of open-source software
and its underlying philosophy. He's worked with Debian, Ubuntu, and SUSE, but what he
knows best is RHEL-based distributions. He's known for his contributions to the MailScanner
project (he has been a technical reviewer for the MailScanner book), but he also gave time to
different open-source projects, such as mondorescue, OTRS, SpamAssassin, pfSense, and a
few others.
I thank my lover, Lysanne, who accepted allowing me some free time slots
for this review even with a 2-year-old and a 6-month-old to take care of.
The presence of these 3 human beings in my life is simply invaluable.
www.it-ebooks.info
Jason Slagle is a 15-year veteran of Systems and Network administration. Having worked
on everything from Linux systems to Cisco networks and SAN Storage, he is always looking
for ways to make his work repeatable and automated. When he is not hacking at a computer
for work or pleasure, he enjoys running, cycling, and occasionally geocaching.
I'd like to thank my wife, Heather, for being patient through the challenges
of being married to a lifelong systems guy, and my new son, Jacob, for
bringing a smile to my face on even the longest days.
Johan De Wit was an early Linux user and he still remembers those days building a 0.9x
Linux kernel on his brand-new 486 computer that took a whole night, and always had
a great love for the UNIX Operating System.
Besides his work with Puppet, he spends a lot of his free time with his two lovely kids
and his two Belgian draft horses, and if time and the weather permit, he likes to drive
his chopper.
www.it-ebooks.info
www.PacktPub.com
Did you know that Packt offers eBook versions of every book published, with PDF and ePub files
available? You can upgrade to the eBook version at www.PacktPub.com and as a print book
customer, you are entitled to a discount on the eBook copy. Get in touch with us at service@
packtpub.com for more details.
At www.PacktPub.com, you can also read a collection of free technical articles, sign up for a
range of free newsletters and receive exclusive discounts and offers on Packt books and eBooks.
http://PacktLib.PacktPub.com
Do you need instant solutions to your IT questions? PacktLib is Packt's online digital book library.
Here, you can access, read and search across Packt's entire library of books.
Why Subscribe?
Fully searchable across every book published by Packt
Copy and paste, print and bookmark content
On demand and accessible via web browser
www.it-ebooks.info
www.it-ebooks.info
Table of Contents
Preface 1
Chapter 1: Introduction to Puppet 7
The problem 8
Configuration management 8
A day in the life of a sysadmin 8
Keeping the configuration synchronized 9
Repeating changes across many servers 10
Self-updating documentation 10
Coping with different platforms 10
Version control and history 11
Solving the problem 11
Reinventing the wheel 11
A waste of effort 12
Transferable skills 12
Configuration management tools 12
Infrastructure as code 13
Dawn of the devop 13
Job satisfaction 14
The Puppet advantage 14
Welcome aboard 15
The Puppet way 15
Growing your network 16
Cloud scaling 16
What is Puppet? 16
The Puppet language 16
Resources and attributes 17
Summary 18
Configuration management 18
What Puppet does 18
www.it-ebooks.info
Table of Contents
[ ii ]
www.it-ebooks.info
Table of Contents
[ iii ]
www.it-ebooks.info
Table of Contents
Access control 75
What is SSH? 75
Managing SSH keys 75
Time for action – adding an SSH authorized key 76
Generating new SSH keys 78
Special-purpose keys 78
Locking user accounts 78
Managing SSH configuration 79
Time for action – deploying an SSH configuration file 79
User privileges 80
sudo 81
Time for action – deploying a sudoers file 81
Summary 83
Security practices 83
User resources 83
Removing or locking accounts 84
Managing SSH keys 84
Configuring SSH 84
Managing privileges with sudo 85
Chapter 6: Tasks and templates 87
Running commands with exec resources 88
Time for action – running an arbitrary command 88
Running commands selectively 89
Triggering commands 90
Chaining commands 90
Command search paths 91
Scheduled tasks 92
Time for action – scheduling a backup 92
More scheduling options 94
Running jobs at regular intervals 94
Running a job as a specified user 94
Exercise 94
Distributing files 95
Time for action – using a recursive file resource 95
Using templates 97
Time for action – templating an Nginx virtual host 97
Inline templates 101
System facts 101
Doing the math 102
Putting it all together 102
[ iv ]
www.it-ebooks.info
Table of Contents
Summary 103
Exec resources 103
Scheduled jobs 104
Recursive file resources 105
Templates 105
Chapter 7: Definitions and Classes 107
Grouping resources into arrays 108
Definitions 109
Passing parameters to definitions 111
Optional parameters 112
Time for action – creating a definition for Nginx websites 112
Multiple instances of definitions 115
Exercise 115
Classes 115
Defining classes 115
Putting classes inside modules 116
Declaring classes 116
What's the difference between a class and a definition? 117
Time for action – creating an NTP class 117
Summary 120
Arrays 120
Definitions 120
Classes 121
Chapter 8: Expressions and Logic 123
Conditionals 123
If statements 124
else and elsif 124
Unless statements 125
Case statements 125
The default case 127
Matching multiple cases 127
Selectors 127
Expressions 128
Comparisons 128
Equality 128
Magnitude 129
Substrings 129
Boolean operators 130
Combining Boolean operators 130
Arithmetic operators 130
[v]
www.it-ebooks.info
Table of Contents
[ vi ]
www.it-ebooks.info
Table of Contents
Errors 157
Compilation errors 158
Diagnosing errors 158
Missing file sources 158
Missing parent directory 159
Mistyped command line options 160
Summary 160
Reporting 160
Debug and dry-run modes 160
Printing messages 161
Monitoring Puppet 161
Common Puppet errors 161
Chapter 10: Moving on Up 163
Puppet style 164
Break out code into modules 164
Refactor common code into definitions 164
Keep node declarations simple 166
Use puppet-lint 167
Make comments superfluous 168
Puppet learning resources 169
Reference 169
Resource types 169
Language and syntax 170
Facts 170
Style 170
Modules and code 171
Puppet Forge 171
The Puppet Cookbook 171
Projects 172
Puppet everywhere 173
User accounts 173
System toolbox 173
Time sync 173
Monitoring server 174
Puppetize your key services 174
Automate backups 175
Set up staging servers 175
Automate everything 175
Last word 176
Index 179
[ vii ]
www.it-ebooks.info
www.it-ebooks.info
Preface
If you work with computer systems, then you know how time-consuming it can be to install
and configure software, to do administration tasks such as backups and user management,
and to keep the machines up to date with security patches and new releases. Maybe you've
already come up with some written procedures, shell scripts, and other ways to document
your work and make it more automated and reliable.
Perhaps you've read about how Puppet can help with this, but aren't sure how to get started.
The online documentation is great for reference, but doesn't really explain the whole thing
from scratch. Many of the books and tutorials available spend a lot of time explaining how to
set up your Puppet server and infrastructure before ever getting to the point where you can
use Puppet to actually do something.
I've taken the same approach in this book. Without going into lots of theory or background
detail, I'll show you how to do useful things with Puppet right away: install packages
and config files, create users, set up scheduled jobs, and so on. Every exercise deals with
something real and practical that you're likely to need in your work, and you'll see the
complete Puppet code to make it happen, along with step-by-step instructions for what to
type and what output you'll see.
After each exercise, I'll explain in detail what each line of code does and how it works, so that
you can adapt it to your own purposes, and feel confident that you understand everything
that's happened. By the end of the book, you will have all the skills you need to do real,
useful, everyday work with Puppet.
www.it-ebooks.info
What this book covers
Chapter 1, Introduction to Puppet, explains the problem of configuration management and
why traditional manual approaches to them don't scale. It shows how Puppet deals with
these problems efficiently, and introduces the basic architecture of Puppet.
Chapter 2, First Steps with Puppet, guides you through installing Puppet for the first time,
creating a simple manifest, and applying it to a machine. You'll see how to use the Puppet
language to describe and modify resources, such as a text file.
Chapter 3, Packages, Files, and Services, shows you how to use these key resource types,
and how they work together. We'll work through a complete and useful example based on
the Nginx web server.
Chapter 4, Managing Puppet with Git, describes a simple and powerful way to connect
machines together using Puppet, and to distribute your manifests and work on them
collaboratively using the version control system Git.
Chapter 5, Managing Users, outlines some good practices for user administration and shows
how to use Puppet to implement them. You'll also see how to control access using SSH and
manage user privileges using sudo.
Chapter 6, Tasks and Templates, covers more key aspects of automation: scheduling tasks,
and building configuration files from dynamic data using Puppet's template mechanism.
Chapter 7, Definitions and Classes, builds on previous chapters by showing you how to
organize Puppet code into reusable modules and objects. We'll see how to create definitions
and classes, and how to pass parameters to them.
Chapter 8, Expressions and Logic, delves into the Puppet language and shows how to control
flow using conditional statements and logical expressions, and how to build arithmetic and
string expressions. It also covers operators, arrays, and hashes.
Chapter 9, Reporting and Troubleshooting, looks at the practical side of working with
Puppet: how to diagnose and solve common problems, debugging Puppet's operations,
and understanding Puppet error messages.
Chapter 10, Moving on Up, shows you how to make your Puppet code more elegant, more
readable, and more maintainable. It offers some links and suggestions for further reading,
and outlines a series of practical projects that will help you deliver measurable business
value using Puppet.
www.it-ebooks.info
What you need for this book
You'll need a computer system (preferably, but not essentially, Ubuntu Linux-based) and
access to the Internet. You won't need to be a UNIX expert or an experienced sysadmin;
I'll assume you can log in, run commands, and edit files, but otherwise I'll explain everything
you need as we go.
Conventions
In this book, you will find several headings appearing frequently.
Instructions often need some extra explanation to make sense, so they are followed with:
You will also find some other learning aids in the book, including:
www.it-ebooks.info
Preface
You will also find a number of styles of text that distinguish between different kinds of
information. Here are some examples of these styles, and an explanation of their meaning.
Code words in text, database table names, folder names, filenames, file extensions,
pathnames, dummy URLs, user input, and Twitter handles are shown as follows: "To have
Puppet read a manifest file and apply it to the server, use the puppet apply command."
When we wish to draw your attention to a particular part of a code block, the relevant lines
or items are set in bold:
file { '/tmp/hello':
content => "Hello, world\n",
}
New terms and important words are shown in bold. Words that you see on the screen, in
menus or dialog boxes for example, appear in the text like this: "On the Select Destination
Location screen, click on Next to accept the default destination."
[4]
www.it-ebooks.info
Preface
Reader feedback
Feedback from our readers is always welcome. Let us know what you think about this
book—what you liked or may have disliked. Reader feedback is important for us to
develop titles that you really get the most out of.
If there is a topic that you have expertise in and you are interested in either writing
or contributing to a book, see our author guide at www.packtpub.com/authors.
Customer support
Now that you are the proud owner of a Packt book, we have a number of things to help
you to get the most from your purchase.
Errata
Although we have taken every care to ensure the accuracy of our content, mistakes do
happen. If you find a mistake in one of our books—maybe a mistake in the text or the
code—we would be grateful if you would report this to us. By doing so, you can save other
readers from frustration and help us improve subsequent versions of this book. If you find
any errata, please report them by visiting http://www.packtpub.com/submit-errata,
selecting your book, clicking on the errata submission form link, and entering the details of
your errata. Once your errata are verified, your submission will be accepted and the errata
will be uploaded to our website, or added to any list of existing errata, under the Errata
section of that title.
[5]
www.it-ebooks.info
Preface
Piracy
Piracy of copyright material on the Internet is an ongoing problem across all media. At Packt,
we take the protection of our copyright and licenses very seriously. If you come across any
illegal copies of our works, in any form, on the Internet, please provide us with the location
address, or website name immediately so that we can pursue a remedy.
We appreciate your help in protecting our authors, and our ability to bring you
valuable content.
Questions
You can contact us at [email protected] if you are having a problem with any
aspect of the book, and we will do our best to address it.
[6]
www.it-ebooks.info
Introduction to Puppet
1
For a list of all the ways technology has failed to improve the quality of life,
please press three.
— Alice Kahn
In this chapter, you'll learn what Puppet is, and what it can help you do. Whether you're
a system administrator, a developer who needs to fix servers from time to time, or just
someone who's annoyed at how long it takes to set up a new laptop, you'll have come
across the kind of problems that Puppet is designed to solve.
A TYPICAL DAY...
WORK, YOU
$# * !
USER NOT
FOUND
LICENSE
INVALID
PASSWORD
WRONG
RETRY
UNEXPECTED
ERROR NOT
INSTALLED
www.it-ebooks.info
Introduction to Puppet
The problem
We have the misfortune to be living in the present. In the future, of course, computers will
be smart enough to just figure out what we want, and do it. Until then, we have to spend a
lot of time telling telling the computer things it should already know.
When you buy a new laptop, you can't just plug it in, get your e-mail, and start work.
You have to tell it your name, your e-mail address, the address of your ISP's e-mail servers,
and so on.
Also, you need to install the programs you use: your preferred web browser, word processor,
and so on. Some of this software may need license keys. Your various logins and accounts
need passwords. You have to set all the preferences up the way you're used to.
This is a tedious process. How long does it take you to get from a box-fresh computer to
being productive? For me, it probably takes about a week to get things just as I want them.
It's all the little details.
Configuration management
This problem is called configuration management, and thankfully we don't have it with
a new laptop too often. But imagine multiplying it by fifty or a hundred computers, and
setting them all up manually.
When I started out as a system administrator, that's pretty much what I did. A large part
of my time was spent configuring server machines and making them ready for use. This is
more or less the same process as setting up a new laptop: installing software, licensing it,
configuring it, setting passwords, and so on.
Assuming the machine has been physically put together, racked, cabled, and powered,
and the operating system is installed, what do we have to do to make it usable as a server
for cat-pictures.com?
[8]
www.it-ebooks.info
Chapter 1
That's a lot of work. It may take a day or two if this is the first time you're setting up the
server. If you're smart, you'll write down everything you do, so next time you can simply
run through the steps and copy and paste all the commands you need. Even so, the next
time you build a cat-pictures server, it'll still take you a couple of hours to do this.
If the live server goes down and you suddenly need to build a replacement, that's a couple
of hours of downtime, and with a pointy-haired boss yelling at you, it's a bad couple
of hours.
Wouldn't it be nice if you could write a specification of how the server should be set up,
and you could apply it to as many machines as you liked?
Your painstaking notes will no longer be up to date with reality. While you were on vacation,
the developers installed a couple of new Ruby gems that the application now depends on—I
guess they forgot to tell you. Even if everybody keeps the build document up to date with
changes, no one actually tests the modified build process, so there's no way to know if it still
works end-to-end.
Also, the latest version of MySQL in the Linux distribution has changed, and now it doesn't
support some of the configuration parameters you used before. So the differences start
to accumulate.
By the time you've got four or five servers, they're all a little different. Which is the
authoritative one? Or are they all slightly wrong? The longer they're around, the
more they will drift apart.
Wouldn't it be nice if the configuration on all your machines could be regularly checked
and synchronized with a central, standard version?
[9]
www.it-ebooks.info
Introduction to Puppet
Humans just aren't good at accurately repeating complex tasks over and over; that's why
we invented robots. It's easy to make mistakes, leave things out, or be interrupted and lose
track of what you've done.
Changes happen all the time, and it becomes increasingly difficult to keep things up to date
and in sync as your infrastructure grows.
Wouldn't it be nice if you only had to make changes in one place, and they rolled out to
your whole network automatically?
Self-updating documentation
A new sysadmin joins your organization, and she needs to know where all the servers are,
and what they do. Even if you keep scrupulous documentation, it can't always be relied on.
In real life, we're too busy to stop every five minutes and document what we just did.
The only accurate documentation, in fact, is the servers themselves. You can look at a
server to see how it's configured, but that only applies while you still have the machine.
If something goes wrong and you can't access the machine, or the data on it, your only
option is to reconstruct the lost configuration from scratch.
The command to create a new user account is slightly different for Red Hat Linux from
the equivalent command for Ubuntu, for example. Solaris is a little different again. Each
command is doing basically the same job, but has differences in syntax, arguments, and
default values.
This means that any attempt to automate user management across your network has to
take account of all these differences, and if you add another platform to the mix, then
that further increases the complexity of the code required to handle it.
[ 10 ]
www.it-ebooks.info
Chapter 1
Wouldn't it be nice if you could just say how things should be, and not worry about the
details of how to make it happen?
When you're making manual, ad hoc changes to systems, you can't roll back to a point in
time. It's hard to undo a whole series of changes. You don't have a way of keeping track of
what you did and how things changed.
This is bad enough if there's just one of you. When you're working in a team, it gets even
worse, with everybody making independent changes and getting in each other's way.
When you have a problem, you need a way to know what changed, and when, and who did
it. Ideally, you could look at your configuration document and say, "Hmm, Carol checked in
a change to the FTP server last night, and today no one can log in. It looks like she made a
typo." You can fix or back out of the change, and have Carol buy the team lunch.
Because everyone has his own proprietary, unique system, the skills associated with it
aren't transferable. When you get a new job, all the time and effort you spent becoming
a wizard on your organization's CM system goes to waste; you have to learn a new one.
[ 11 ]
www.it-ebooks.info
Introduction to Puppet
A waste of effort
Also, there's a whole lot of duplicated effort. The world really doesn't need more template
engines, for example. Once a decent one exists, it would make sense for everybody to use it,
and take advantage of ongoing improvements and updates.
It's not just the CM system itself that represents duplicated, wasted effort. The configuration
scripts and templates you write could also be shared and improved by others, if only they
had access to them. After all, most server software is pretty widely used. A program in
configuration language that sets up Apache could be used by everybody who uses
Apache—if it were a standard language.
Transferable skills
Once you have a CM system with a critical mass of users, you get a lot of benefits. A new
system administrator doesn't have to write his own CM tool, he just grabs one off the shelf.
Once he learns to use it, and to write programs in the standard language, he can take that
skill with him to other jobs.
He can choose from a large library of existing programs in the standard configuration
language, covering most of the popular software in use. These programs are updated and
improved to keep up with changes in the software and operating systems they manage.
This kind of beneficial network effect is why we don't have a million different operating
systems, or programming languages, or processor chips. There's strong pressure for people
to converge on a standard. On the other hand, we don't have just one of each of those things
either. There's never just one solution that pleases everybody.
If you're not happy with an existing CM system, and you have the skills, you can write one
that works the way you prefer. If enough other people feel the same way, they will form a
critical mass of users for the new system. But this won't happen indefinitely; standardization
pressure means the market will tend to converge on a small number of competing systems.
There really isn't much to choose between these different systems. They all solve more or
less the same problems—the ones we saw earlier in this chapter—in more or less the same
way. Some people prefer the Puppet way of doing things; some people are more comfortable
with Chef, and so on.
[ 12 ]
www.it-ebooks.info
Chapter 1
But essentially, these, and many other CM systems, are all great solutions to the CM
problem, and it's not very important which one you choose as long as you choose one.
Infrastructure as code
Once we start writing programs to configure machines, we get some benefits right away.
We can adopt the tools and techniques that regular programmers—who write code in Ruby
or Java, for example—have used for years:
This can make us more agile and flexible as system administrators, able to deal with
fast-changing requirements and deliver things quickly to the business. We can also
produce higher-quality, more reliable work.
That's changing fast. System administrators, facing the challenge of scaling systems to
enormous size for the web, have had to get smart about programming and automation.
Developers, who now often build applications, services, and businesses by themselves,
couldn't do what they do without knowing how to set up and fix servers.
The term "devops" has begun to be used to describe the growing overlap between these
skill sets. It can mean sysadmins who happily turn their hand to writing code when needed,
or developers who don't fear the command line, or it can simply mean the people for whom
the distinction is no longer useful.
Devops write code, herd servers, build apps, scale systems, analyze outages, and fix bugs.
With the advent of CM systems, devs and ops are now all just people who work with code.
[ 13 ]
www.it-ebooks.info
Introduction to Puppet
Job satisfaction
Being a sysadmin, in the traditional sense, is not usually a very exciting job. Instead of
getting to apply your experience and ingenuity to make things better, faster, and more
reliable, you spend a lot of time just fixing problems, and making manual configuration
changes that could really be done by a machine. The following carefully-researched
diagram shows how traditional system administration compares to some other jobs in
both excitement and stress levels:
!
Boring Exciting
SOFA JET-POWERED
Relaxing
TESTER SOFA TESTER
We can see from this that manual sysadmin work is both more stressful and more boring
than we would like. Boring, because you're not really using your brain, and stressful, because
things keep going wrong despite your best efforts.
Automating at least some of the dull manual work can make sysadmin work more exciting,
because it frees you for things that are more important and challenging, such as figuring out
how to make your systems more resilient or more performant.
Having an automated infrastructure means your servers are consistent, up to date, and
well-documented, so it can also make your job a little less stressful. Or, at any rate, it can
give you the freedom to be stressed about more interesting things.
Let's look at an example sysadmin task and see how it's handled the traditional way and then
the Puppet way.
[ 14 ]
www.it-ebooks.info
Chapter 1
Welcome aboard
A new developer has joined the organization. She needs a user account on all the servers.
The traditional approach will be as follows:
1. Log in to server 1.
2. Run the useradd rachel command to create the new user.
3. Create Rachel's home directory.
4. Log in to server 2 and repeat these steps.
5. Log in to server 3 and repeat these steps.
6. Log in to server 4 and repeat these steps.
7. Log in to server 5 and repeat these steps.
8. The first three steps will be repeated for all the servers.
Puppet runs automatically a few minutes later on all your machines and picks up the
change you made. It checks the list of users on the machine, and if Rachel isn't on the list,
Puppet will take action. It detects what kind of operating system is present and knows what
commands need to be run in that environment to add a user. After Puppet has completed its
work, the list of users on the machine will match the ones in your Puppet code.
The key differences from the traditional, manual approach are as follows:
You only had to specify the steps to create a new user once, instead of doing
them every time for each new user
You only had to add the user in one place, instead of on every machine in
your infrastructure
You didn't have to worry about the OS-specific details of how to add users
[ 15 ]
www.it-ebooks.info
Introduction to Puppet
By the time you've got to, say, five servers, the Puppet advantage is obvious. Not counting
the initial investment in setting up Puppet, you're getting things done five times faster. Your
colleague doing things the traditional, hand-crafted way is still only on machine number 2 by
the time you're heading home.
Above ten servers the traditional approach becomes almost unmanageable. You spend most
of your time simply doing repetitive tasks over and over just to keep up with changes. To look
at it in another, more commercial way, your firm needs ten sysadmins to get as much work
done as one person with Puppet.
Cloud scaling
Beyond ten or so servers, there simply isn't a choice. You can't manage an infrastructure
like this by hand. If you're using a cloud computing architecture, where servers are created
and destroyed minute-by-minute in response to changing demand, the artisan approach to
server crafting just won't work.
What is Puppet?
We've seen the problems that Puppet solves, and how it solves them, by letting you express
the way your servers should be configured in code form. Puppet itself is an interpreter that
reads those descriptions (written in the Puppet language) and makes configuration changes
on a machine so that it conforms to your specification.
In English, this code says, "The curl package should be installed". This snippet of code
results in Puppet doing the following:
www.it-ebooks.info
Chapter 1
This is Puppet language for the declaration "The jen user should be present." Again, this
results in Puppet checking for the existence of the jen user on the system, and creating
it if necessary.
So you can see that the Puppet program—the Puppet manifest—for your configuration
is a set of declarations about what things should exist, and how they should be configured.
You don't give commands, such as "Do this, then do that." Rather, you describe how things
should be, and let Puppet take care of making it happen. These are two quite different kinds
of programming. The first (procedural style) is the traditional model used by languages, such
as C, Python, shell, and so on. Puppet's is called declarative style because you declare what
the end result should be, rather than specifying the steps to get there.
This means that you can apply the same Puppet manifest repeatedly to a machine and the
end result will be the same, no matter how many times you run the "program". It's better to
think of Puppet manifests as a kind of executable specification rather than as a program in
the traditional sense.
Puppet lets you describe configuration in terms of resources—what things should exist—and
their attributes. You don't have to get into the details of how resources are created and
configured on different platforms. Puppet just takes care of it.
Here are some of the kinds of resources you can describe in Puppet:
Packages
Files
Services
Users
Groups
YUM repos
Nagios configuration
[ 17 ]
www.it-ebooks.info
Introduction to Puppet
Log messages
/etc/hosts entries
Network interfaces
SSH keys
SELinux settings
Kerberos configuration
ZFS attributes
E-mail aliases
Mailing lists
Mounted filesystems
Scheduled jobs
VLANs
Solaris zones
In fact, since you can define custom resources to manage anything that's not covered by the
built-in resources, there are no limits. Puppet allows you to automate every possible aspect
of system configuration.
Summary
A quick rundown of what we've learned in this chapter.
Configuration management
Manual configuration management is tedious and repetitive, it's error-prone, and it
doesn't scale well. Puppet is a tool for automating this process.
You describe your configuration in terms of resources such as packages and files.
This description is called a manifest.
Puppet supports a wide range of different platforms and operating systems, and it will
automatically run the appropriate commands to apply your manifest in each environment.
[ 18 ]
www.it-ebooks.info
Chapter 1
You can write a manifest once and apply it to many machines, avoiding
duplicated work
You can keep all your servers in sync with each other, and with the manifest
The Puppet manifest also acts as live documentation, which is guaranteed to
be up to date
Puppet copes with differences between operating systems, platforms, command
syntaxes, and so on
Because Puppet manifests are code, you can version and manage them in the
same way as any other source code
Scaling
The problems with manual configuration management become acute when your
infrastructure scales to 5-10 servers. Beyond that, especially when you're operating in
the cloud where servers can be created and destroyed in response to changing demand,
some way of automating your configuration management is essential.
Puppet is a declarative programming language: that is, it describes how things should be,
rather than listing a series of actions to take, as in some other programming languages, such
as Perl or shell. Puppet compares the current state of a server to its manifest, and changes
only those things that don't match. This means you can run Puppet as many times as you
want and the end result will be the same.
[ 19 ]
www.it-ebooks.info
www.it-ebooks.info
First steps with Puppet
2
Beginnings are such delicate times.
In this chapter you'll learn how to install Puppet, how to write your first manifest, and how
to put Puppet to work configuring a server. You'll also understand how Puppet reads and
applies a manifest.
class memcache {
package { 'memcache':
ensure => present,
}
service { 'memcache':
ensure => running,
}
}
ACME
CHAIR
CO
www.it-ebooks.info
First steps with Puppet
Although Puppet runs on a number of different platforms, I'm not going to provide detailed
instructions for all of them. Throughout this book I'll be using the Ubuntu 12.04 LTS "Precise"
distribution of Linux for my examples. I'll point out where specific commands or file locations
are likely to be different for other operating systems.
I'm using an Amazon EC2 cloud instance to demonstrate setting up Puppet, though you
may prefer to use a physical server, a Linux workstation, or a Vagrant virtual machine (with
Internet access). I'll log in as the Ubuntu user and use sudo to run commands that need root
privileges (the default setup on Ubuntu).
1. Set a suitable hostname for your server (ignore any warning from sudo):
ubuntu@domU-12-31-39-09-51-23:~$ sudo hostname demo
ubuntu@domU-12-31-39-09-51-23:~$ sudo su -c 'echo demo >/etc/
hostname'
sudo: unable to resolve host demo
2. Log out and log back in to check that the hostname is now correctly set:
ubuntu@demo:~$
4. Copy the IP address of your server (here it's 10.210.86.209) and add this to
the /etc/hosts file (use your own hostname and domain):
ubuntu@demo:~$ sudo su -c 'echo 10.210.86.209 demo demo.example.
com >>/etc/hosts'
sudo: unable to resolve host demo
[ 22 ]
www.it-ebooks.info
Chapter 2
www.it-ebooks.info
First steps with Puppet
You can find out how to configure your particular system for the Puppet
Labs repos at the following page:
http://docs.puppetlabs.com/guides/puppetlabs_
package_repositories.html
[ 24 ]
www.it-ebooks.info
Chapter 2
used.
Get:1 http://us-east-1.ec2.archive.ubuntu.com/ubuntu/ precise/main
libreadline5 amd64 5.2-11 [128 kB]
...
Setting up puppet (3.0.2-1puppetlabs1) ...
* Starting puppet agent
puppet not configured to start, please edit /etc/default/puppet to
enable
[ OK ]
Processing triggers for libc-bin ...
ldconfig deferred processing now taking place
If you're using Red Hat Enterprise Linux, CentOS, or another Linux distribution
that uses the Yum package system, you should run $ sudo yum install
puppet to install Puppet.
If you're on a Mac, you can download and install suitable DMG images from
Puppet Labs:
https://downloads.puppetlabs.com/mac/
If you're using Windows, you can download the MSI packages from the Puppet
Labs website:
https://downloads.puppetlabs.com/windows/
If the version of Puppet you've installed is not exactly the same, it doesn't matter; you'll get
whatever is the latest version made available by Puppet Labs. If you're installing Puppet from
a different place, or from source files, then as long as your version is newer than 3.0, you'll
have no trouble running the examples in this book.
If you have a version of Puppet that is older (for example, Puppet 2.6 or 2.7) you may find
that some things don't work or work differently from the way you'd expect. Many changes
in syntax that were deprecated in older versions, for example, no longer work at all in
Puppet 3.0. I recommend that you upgrade to Puppet 3.0 or later if at all possible.
[ 25 ]
www.it-ebooks.info
First steps with Puppet
Create the file site.pp anywhere you like, with the following contents:
file { '/tmp/hello':
content => "Hello, world\n",
}
How it works
You can probably guess what this manifest will do, but I'll explain the code in detail first.
file { '/tmp/hello':
The word file begins a resource declaration for a file resource. Recall that a resource is
some bit of configuration that you want Puppet to manage: for example, a file, user account,
or package. A resource declaration looks like this:
RESOURCE { NAME:
ATTRIBUTE => VALUE,
...
}
RESOURCE indicates the type of resource you're declaring; in this case, it's a file.
NAME is a unique identifier that distinguishes this instance of the resource from any other
that Puppet knows about. With file resources, it's usual for this to be the full path to the file,
in this case, /tmp/hello.
There follows a list of attributes that describe how the resource should be configured. The
attributes available depend on the type of resource. For a file, you can set attributes such as
content, owner, group, and mode.
The content attribute sets the contents of a file to a string value you provide. Here, the
contents of the file are declared to be Hello, world followed by a newline character.
Note that content specifies the entire content of the file; the string you provide will replace
anything already in the file, rather than being appended to it.
[ 26 ]
www.it-ebooks.info
Chapter 2
Run the following command in the same directory where you created site.pp:
ubuntu@demo:~$ puppet apply site.pp
Notice: /Stage[main]//Node[demo]/File[/tmp/hello]/ensure: defined
content as '{md5}bc6e6f16b8a077ef5fbc8d59d0b931b9'
Notice: Finished catalog run in 0.05 seconds
Puppet then works through the list, applying each resource in turn:
First, it checks if the resource exists on the server. If not, Puppet creates it.
In the example, we've declared that the file /tmp/hello should exist. The first
time you run puppet apply, this won't be the case, so Puppet will create the file
for you.
Then, for each resource, it checks the value of each attribute in the manifest against
what actually exists on the server.
In our example, there's just one attribute, content. We've specified that the
content of the file should be Hello, world. If the file is empty, or contains
something else, Puppet will overwrite the file with what the manifest says it
should contain.
In this case, the file will be empty the first time you apply the manifest, so Puppet
will write the string Hello, world into it.
[ 27 ]
www.it-ebooks.info
First steps with Puppet
The answer is yes. If any attribute of the file, including its contents, doesn't match the
manifest, Puppet will change it so that it does.
This can lead to some surprising results if you manually edit a file managed by Puppet.
If you make changes to a file without also changing the Puppet manifest to match,
Puppet will overwrite the file the next time it runs, and your changes will be lost.
So it's a good idea to add a comment to files that Puppet is managing; something like:
# This file is managed by Puppet - any manual edits will be lost
Add this to Puppet's copy of the file when you first deploy it, and it will remind you and
others not to make manual changes.
Exercise
Modify your manifest to have Puppet write a message to the system's /etc/motd file.
It should be a cheerful, encouraging message so that users logging on to the system will
feel that Puppet has things under control.
[ 28 ]
www.it-ebooks.info
Chapter 2
Your directory structure should now look as shown in the following diagram:
puppet
manifests
site.pp
This is done with a node declaration ("node" is the Puppet term for an individual machine
that has a Puppet configuration). A node declaration looks like this:
node NODENAME {
RESOURCE
RESOURCE
...
}
[ 29 ]
www.it-ebooks.info
First steps with Puppet
Here NODENAME is the hostname of the relevant machine, and RESOURCE is a resource
declaration.
If resources are not contained inside a node declaration, Puppet will always apply them
(as we saw with the /tmp/hello file). But if they are inside a node declaration, Puppet
will apply them only on a machine whose hostname matches the node name.
You could put all your Puppet manifests in a single file, and it would make no difference to
Puppet. But it's much better and easier to manage if you break them up into several files.
Conventionally, the top-level "master" file that includes everything else is named site.pp.
You should put your node declarations in a file named nodes.pp, and we'll do this in the
next example.
3. Your puppet directory should now look as shown in the following diagram:
puppet
manifests
nodes.pp
site.pp
[ 30 ]
www.it-ebooks.info
Chapter 2
So it will apply everything within the node 'demo' declaration, which in our example has
already been applied, so there's nothing for Puppet to do for now.
Although Puppet doesn't really mind how you organize your manifests within files—you can
have everything within one big site.pp file if you like—it's a good idea to split them up into
logical divisions. A common practice is to keep site.pp fairly small and just use it to load
other manifest files, such as nodes.pp.
Summary
A quick rundown of what we've learned in this chapter.
Installing Puppet
You can install Puppet by downloading and installing the Puppet Labs APT repo package,
then running apt-get install puppet.
Manifests
A manifest consists of a list of resource declarations. A resource declaration specifies
a particular aspect of system configuration that you want Puppet to manage: a file,
for example.
Resource declarations consist of a name and a list of attributes. The resource name is a
unique identifier, which you can use to refer to this specific resource, if you need to. Its
attributes specify various things about the resource that you want to control with Puppet.
Different types of resources have different attributes, but for a file resource, attributes
include content, which specifies the contents of the file as a string.
[ 31 ]
www.it-ebooks.info
First steps with Puppet
Puppet processes a manifest by comparing the specified resources to what currently exists
on the machine. Any missing resources will be created; attributes that do not match will be
changed to match the manifest.
Manual changes to a file managed by Puppet will be lost when Puppet next applies
the manifest.
Nodes
Node declarations identify a specific machine by its hostname, and tell Puppet which
resources should be applied to that node. Any resources that are not part of a node
declaration will be applied to all nodes. Put your node declarations in nodes.pp.
[ 32 ]
www.it-ebooks.info
Packages, Files, and Services
3
It's not denial. I'm just selective about the reality I accept.
The most common types of resources you'll manage with Puppet are packages, files, and
services. They often occur together, with a package providing a service, and the service
requiring a configuration file. In this chapter you'll see how to use Puppet to manage these
resources effectively.
WILL THAT
BE ALL, SIR?
E
COFFE
THANKS,
ROBOT
BUTLER!
BACON
www.it-ebooks.info
Packages, Files, and Services
Packages
Puppet's package resource will install, update, or remove a package for you, using the system
native package management tools (in the case of Ubuntu, that's the Advanced Package Tool
(APT). If you were setting up a server manually, you might run a command such as:
apt-get install nginx
Puppet will take the necessary actions by running apt-get behind the scenes.
[ 34 ]
www.it-ebooks.info
Chapter 3
2. Run Puppet:
ubuntu@demo:~/puppet$ sudo puppet apply manifests/site.pp
Notice: /Stage[main]//Node[demo]/Package[nginx]/ensure: ensure
changed 'purged' to 'present'
Notice: Finished catalog run in 3.10 seconds
Remember that the node keyword introduces a node declaration, a list of resources that are
to be applied only to node demo.
package { 'nginx':
ensure => installed,
}
In this case, there is one resource, of type package. As with the file resource we created
in Chapter 2, First steps with Puppet, the resource declaration consists of the following:
Each resource type has a different list of attributes that you can control. A useful attribute
for package resources is ensure. We use this attribute to install (or sometimes remove)
packages.
ensure => installed,
When we apply this manifest, Puppet checks whether the nginx package is installed.
If this is the first time you've applied the manifest, the package probably won't be present,
so Puppet prints a message telling us that the package is being installed:
Notice: /Stage[main]//Node[demo]/Package[nginx]/ensure: ensure changed
'purged' to 'present'
As we saw with the file resource, once the resource has been created the first time,
subsequent Puppet runs will do nothing because the state of the system already matches
the manifest:
ubuntu@demo:~/puppet$ sudo puppet apply manifests/site.pp
Notice: Finished catalog run in 0.08 seconds
[ 35 ]
www.it-ebooks.info
Packages, Files, and Services
We'd prefer that our servers all be in the same state. To make sure this is the case, you can
specify a version identifier for the package instead of installed:
package { 'nginx':
ensure => '1.1.19-1ubuntu0.1',
}
The exact version string will depend on the Linux distribution and package repository you're
using. To see what version of a package you currently have installed on Ubuntu, you can run
the following command:
ubuntu@demo:~/puppet$ apt-cache policy nginx
nginx:
Installed: 1.1.19-1ubuntu0.1
Candidate: 1.1.19-1ubuntu0.1
Version table:
*** 1.1.19-1ubuntu0.1 0
500 http://us-east-1.ec2.archive.ubuntu.com/ubuntu/ precise-
updates/universe amd64 Packages
100 /var/lib/dpkg/status
1.1.19-1 0
500 http://us-east-1.ec2.archive.ubuntu.com/ubuntu/ precise/
universe amd64 Packages
[ 36 ]
www.it-ebooks.info
Chapter 3
What if package names are different on different operating systems? This does
happen; for example, the package that manages NTP may be called ntp on
some distributions and ntpd on others. If you have to write Puppet code that
takes account of platform differences like this, you can use a Puppet construct
called a selector to choose the appropriate package name. This is explained in
detail later in the book, in Chapter 8, Expressions and Logic.
Removing packages
Occasionally you need to make sure a package is removed entirely from a machine, perhaps
because it could cause conflicts with a package you're installing. If you're using the Nginx
web server, for example, it's a good idea to remove the Apache package that ships with
Ubuntu by default. If Apache is running, Nginx can't start, because Apache will grab the web
server port.
package { 'apache2.2-common':
ensure => absent,
}
Using ensure => absent will remove the package if it's installed.
Updating packages
Another value that ensure can take on a package resource is latest. This will cause
Puppet to check which version of the package is available in the repository (if you're using
Ubuntu, this includes any additional APT sources that you may have configured, such as the
Puppet Labs repo). If it is newer than the installed version, Puppet will upgrade the package
to the latest version.
package { 'puppet':
ensure => latest,
}
Just because you can do this doesn't mean it's necessarily a good idea. Upgrading a
package version can cause unexpected failures or problems, so I tend to avoid doing this on
production systems. I certainly don't want it happening automatically, in the middle of the
night, when I'm not around to respond to any issues.
If you run a staging server on which you can test any updates or changes before applying
them to production (an approach I heartily endorse), this can be a good way to do it. You can
have your staging server ensure => latest for critical packages and thus find out straight
away if a new upstream package release breaks your system.
[ 37 ]
www.it-ebooks.info
Packages, Files, and Services
Also, ensure => latest can be a good way of managing updates if you control the
package repository (for example, if you run your own APT repo. You can find a recipe to
do this in Chapter 5, Working with Files and Packages of The Puppet Cookbook, Packt
Publishing). In this situation, you only release a package to your repository once you have
tested it thoroughly and verified that it doesn't cause any problems. Once it's available in the
repo, all machines will update their versions automatically using ensure => latest.
Modules
To make your Puppet manifests more readable and maintainable, it's a good idea to arrange
them into modules. A Puppet module is a way of grouping related resources. In our example,
we're going to make an nginx module that will contain all Puppet code relating to Nginx.
[ 38 ]
www.it-ebooks.info
Chapter 3
You can see that the nginx resource has been replaced by the line include nginx.
To Puppet, this means, "Look for a class called nginx and include all the resources in
it on this node."
A class in Puppet is simply a named bundle of resources that you want to apply together.
A module might contain many classes, but our example nginx module just contains one
class, also named nginx:
class nginx {
package { 'nginx':
ensure => installed,
}
}
The class keyword declares a group of resources (here, the package resource for Nginx)
identified by the name nginx. We can then use the include keyword elsewhere to include
all the resources in the class at once.
[ 39 ]
www.it-ebooks.info
Packages, Files, and Services
Why do this? Well, for one thing, it means we could include the nginx class on many nodes
without repeating the same resource declarations over and over:
node 'demo' {
include nginx
}
node 'demo2' {
include nginx
}
node 'demo3' {
include nginx
}
But we're getting ahead of ourselves. For now, let's just say that grouping resources into
classes and modules helps us organize our code so it's easy to read and maintain.
Did you notice we used a slightly different form of the puppet apply
command?
puppet apply manifests/site.pp --modulepath=/home/ubuntu/puppet/modules/
We haven't needed to give a modulepath argument before, but now we're
using a module, so we need to tell Puppet where to find it.
[ 40 ]
www.it-ebooks.info
Chapter 3
Services
So we're using a module to manage Nginx on the server. That's great, but so far we've only
installed the nginx package. In order to run the web server, we would need to start and stop
it manually using the command line. Fortunately, we can automate this with Puppet as well.
package { 'nginx':
ensure => installed,
require => Package['apache2.2-common'],
}
service { 'nginx':
ensure => running,
require => Package['nginx'],
}
}
[ 41 ]
www.it-ebooks.info
Packages, Files, and Services
On Ubuntu, the default setup includes the Apache web server, which would conflict with
Nginx if we tried to run it at the same time. So by specifying ensure => absent, we
remove the Apache package.
The require attribute tells Puppet that this resource depends on another resource,
which must be applied first. In this case, we want the removal of Apache to be applied
before the installation of Nginx. We'll see more about the require attribute in the
Requiring resources section.
Is there any implied order to attributes? In other words, does Puppet do the
ensure part before the require part, or doesn't it matter what order you
list them in? Actually, it doesn't matter; Puppet will consider all the attributes
of a resource before making any changes, so you can think of them as all
being applied at the same time. If a resource uses ensure, it's good style to
put that first, but it doesn't make any difference to Puppet.
By now you know that this declares a resource of type service. Service resources manage
daemons, or background processes, on the server. The ensure attribute tells Puppet what
state the service should be in:
ensure => running,
When you ran Puppet, it checked the status of the nginx service and found it stopped,
so Puppet started the service for you:
Notice: /Stage[main]/Nginx/Service[nginx]/ensure: ensure changed
'stopped' to 'running'
If you ran Puppet again, there would be no change because Nginx is already running, so the
server matches the manifest.
[ 42 ]
www.it-ebooks.info
Chapter 3
Requiring resources
What about that require attribute? require specifies a dependency between resources.
For example, we have to have the Nginx package installed before we can run the Nginx
service. That makes sense, and the require attribute expresses this relationship between
the two resources.
require => Package['nginx'],
Any resource can have a require attribute, and the value must be another resource
declared somewhere in your manifest.
Did you notice that Package is capitalized? That tells Puppet you're referring
to a named instance of a package resource, with the name following in square
brackets:
Package['nginx']
You might wonder what happens if your resources require each other in a loop: one
resource requires another, which requires another, which requires the first resource,
similar to this:
file { '/tmp/file1':
require => File['/tmp/file2'],
}
file { '/tmp/file2':
require => File['/tmp/file3'],
}
file { '/tmp/file3':
require => File['/tmp/file1'],
}
[ 43 ]
www.it-ebooks.info
Packages, Files, and Services
Will Puppet just go round and round in circles forever? Let's see:
ubuntu@demo:~/puppet$ papply
Error: Could not apply complete catalog: Found 1 dependency cycle:
(File[/tmp/file1] => File[/tmp/file3] => File[/tmp/file2] => File[/tmp/
file1])
Try the '--graph' option and opening the resulting '.dot' file in
OmniGraffle or GraphViz
Sometimes dependency cycles can be more subtle than this, and harder to figure out.
As the error message suggests, you can get some help by giving the --graph option to
Puppet, which will then produce a diagram of the dependency cycle for you.
Note that Puppet can only figure out explicit dependency cycles, as in this example. More
problematic are cycles caused by side effects; if a file notifies a service, and the service itself
causes the file to change, Puppet will detect that the file has changed and so notify the
service, and this will continue forever. Happily, this situation doesn't arise very often, but it
can be hard to work out what's going on when it does.
service { 'nginx':
ensure => running,
enable => true,
}
Setting enable => true will configure the service to start at boot time (specifically, on
Ubuntu, to start in runlevels 2, 3, 4, and 5, and stop in runlevels 0, 1, and 6). To disable
the automatic service startup (for example, if the service is managed by a high-availability
framework such as Heartbeat), set enable => false.
[ 44 ]
www.it-ebooks.info
Chapter 3
Some also support a status command, which determines whether or not the service is
currently running:
ubuntu@demo:~/puppet$ sudo service nginx status
* nginx is running
When Puppet manages a service, it will try to use the status command to check the
service's status. In some cases this doesn't work, either because the script doesn't support
the status argument or because it returns an incorrect exit code. If you have this problem,
you can use the hasstatus attribute to change this behavior:
service { 'my-service':
ensure => running,
hasstatus => false,
}
If hasstatus is false for a service, Puppet will instead look at the system process list
(such as that produced by the ps command) and see if the service name is listed in it.
If it is, Puppet assumes the service is running. Otherwise, it will attempt to start it.
If the service name itself wouldn't appear in the process list, you can specify a different
pattern for Puppet to search for using the pattern attribute:
service { 'my-service':
ensure => running,
hasstatus => false,
pattern => 'ruby myservice.rb',
}
If the service status can't be detected from the process list, you can give Puppet a command
to run that will return an appropriate exit status (0 for running, any other value for not
running) using the status attribute:
service { 'my-service':
ensure => running,
hasstatus => false,
status => 'grep running /var/lib/myservice/status.txt',
}
[ 45 ]
www.it-ebooks.info
Packages, Files, and Services
However, some services support a restart or reload command, which may be preferable
to stopping and starting the service. For example, some daemons keep a lot of state
information in memory, and if you stopped the service this would be lost.
In this case, you can specify a command that Puppet should use to restart the service using
the restart attribute:
service { 'ssh':
ensure => running,
restart => '/usr/sbin/service ssh reload',
}
If you need to, you can also provide a start or stop attribute, specifying commands to start
or stop the service. This isn't usually necessary, but it's there just in case.
Files
So Nginx is installed and running, but it's not yet serving a website. To do that, we have to
have Puppet install a config file on the server to define an Nginx virtual host. This will tell
Nginx how to respond to requests for the cat-pictures website.
Next, we'll create the virtual host file for Puppet to deploy:
[ 46 ]
www.it-ebooks.info
Chapter 3
Next, we'll add a resource that will deploy this file to the server.
service { 'nginx':
ensure => running,
require => Package['nginx'],
}
file { '/etc/nginx/sites-enabled/default':
source => 'puppet:///modules/nginx/cat-pictures.conf',
notify => Service['nginx'],
}
}
Be careful with the source value in the code above. It starts with
puppet followed by three slashes, not two:
puppet:///modules/nginx...
Not
puppet://modules/nginx...
6. Run Puppet:
ubuntu@demo:~/puppet$ papply
Notice: /Stage[main]/Nginx/File[/etc/nginx/sites-enabled/default]/
ensure: defined content as '{md5}0750fd1b8da76b84f2597de76c1b9bce'
Notice: /Stage[main]/Nginx/Service[nginx]: Triggered 'refresh'
from 1 events
Notice: Finished catalog run in 0.36 seconds
[ 47 ]
www.it-ebooks.info
Packages, Files, and Services
source is a file attribute that we haven't seen before. Previously we used content to
supply the contents of the file as a string. Here, source tells Puppet where to find a copy
of the file:
puppet:///modules/nginx/cat-pictures.conf
This looks a bit like a URL, but it tells Puppet to look in the modules/nginx/files
directory for a file named cat-pictures.conf.
Notice that the source URL doesn't contain the word files. It's just
puppet:///modules/MODULENAME/FILENAME. When Puppet
translates this URL into a disk path, it becomes modules/MODULENAME/
files/FILENAME. If you find this confusing, you're in good company.
One question that might occur to you is, "What about when I'm running Puppet on several
different machines? Where does the file come from in that case? Will each machine have its
own copy of the file, or will it come from some central place?"
The answer depends on how you run Puppet across multiple machines; whether you use a
central server (known as a Puppetmaster) or whether each machine gets its own copy of the
manifest. We'll explore this in detail later, and build a complete working solution, in Chapter
4, Managing Puppet with Git.
[ 48 ]
www.it-ebooks.info
Chapter 3
It means "whenever this file is changed, tell Service['nginx'] to restart". That's what we
saw happen as Puppet deployed the file (which of course counts as a change):
Notice: /Stage[main]/Nginx/File[/etc/nginx/sites-enabled/default]/ensure:
defined content as '{md5}0750fd1b8da76b84f2597de76c1b9bce'
Notice:/Stage[main]/Nginx/Service[nginx]: Triggered 'refresh' from 1
events
service { THE_STUFF:
ensure => running,
require => Package[THE_STUFF],
}
file { '/etc/THE_STUFF.conf':
source => 'puppet:///modules/THE_STUFF/THE_STUFF.conf',
notify => Service[THE_STUFF],
}
}
[ 49 ]
www.it-ebooks.info
Packages, Files, and Services
Exercise
Modify the nginx class to create /var/www/cat-pictures and the index.html file you
previously set up manually.
Summary
A quick rundown of what we've learnt in this chapter.
Packages
The package resource is used to manage packages. To install a package, you set the ensure
attribute to installed.
To install the latest version of the package available in the repo, use ensure => latest.
Modules
To help organize your code, you can put related resources into a module. For example, to
create an nginx module, create the file modules/nginx/manifests/init.pp and put
this in it:
# Manage nginx webserver
class nginx {
...
}
[ 50 ]
www.it-ebooks.info
Chapter 3
Services
To manage services, use the service resource type. The ensure attribute controls whether
or not the service should be running. To specify that the service should be running, use
ensure => running. To specify that it should be stopped, use ensure => stopped.
If a service's control script doesn't support a status command, you can set hasstatus =>
false for the service resource. In this case, Puppet will look in the system process table to
see if the service is running.
If you need Puppet to search the process table for something other than the service's name,
you can specify what to search for using the pattern attribute.
If searching the process table won't work, you can provide a command for Puppet to use to
determine the service's status, using the status attribute.
You can also specify custom service start and stop commands using the start and
stop attributes.
Resource dependencies
You can specify a dependency between resources using the require attribute:
require => Package['nginx'],
If resource B requires resource A, then Puppet will make sure the resources are applied in
the right order.
[ 51 ]
www.it-ebooks.info
Packages, Files, and Services
Files
You can have Puppet deploy a copy of a file using the source attribute:
file { '/etc/nginx/sites-enabled/default':
source => 'puppet:///modules/nginx/cat-pictures.conf',
}
File resources can trigger a service to be restarted using the notify attribute. This is useful
for configuration files, for which changes often don't take effect until the relevant service
is restarted:
notify => Service['nginx'],
[ 52 ]
www.it-ebooks.info
Managing Puppet with Git
4
If you do not change direction, you may end up where you are heading.
– Lao-tzu
In this chapter you'll learn how to use the Git version control system to manage your Puppet
manifests. I'll also show you how to use Git to distribute the manifests to multiple machines,
so that you can start managing your whole network with Puppet.
ING
IT’S GREAT BE
IN CONTROL!
If you're already familiar with Git, you can save some reading by skipping ahead
to the Time for action – importing your manifests into Git section. If not, here's
a gentle introduction.
www.it-ebooks.info
Managing Puppet with Git
Even if you're the only person who works on a piece of source code (for example, Puppet
manifests), it's still useful to be able to see what changes you made, and when. For example,
you might remember that you fixed a bug last week, but not exactly how, and it would be
handy to be able to see exactly what lines in which file were changed.
When you're working on code with others, you need a way to communicate changes to the
rest of the team. A version control tool such as Git not only tracks everyone's changes, but
lets you record a message about what you did and why. For example, a change might be
marked with the following message:
Author: John Arundel <[email protected]>
Date: Wed Aug 8 18:57:25 2012 +0100
It tells you when the change happened, who made it, and (if the commit message is well
written) why it was made. You can also see what file was changed, and which lines were
added, altered, or removed as follows:
modules/proxy/files/sysctl.conf
+net.ipv4.netfilter.ip_conntrack_max = 256000
Imagine you're trying to track down a bug; having a complete history of code changes would
be a big help. It also means you can, if necessary, roll back the state of the code to any point
in history and examine it.
You might think this introduces a lot of extra complications. In fact, it's very simple. Git keeps
out of your way until you need it, and all you have to do is write a commit message when
you decide to record changes to the code.
Another very important role of version control is to allow several people to work
independently on the code, and to merge all their separate changes back together and
resolve any conflicts. Git provides very powerful tools for doing this. If you're working on
Puppet code in a team, it's critical that you use some kind of version control to handle it.
In this chapter we'll add Git version control to the manifests we've been developing, and I'll
show you some of the useful things Git can do.
[ 54 ]
www.it-ebooks.info
Chapter 4
2. Check if Git is correctly installed (the exact version number doesn't matter,
as long as it's reasonably up-to-date):
ubuntu@demo:~$ git --version
git version 1.7.9.5
4. Now set your identification details for Git (use your own name and e-mail):
ubuntu@demo:~/puppet$ git config --global user.name "John Arundel"
ubuntu@demo:~/puppet$ git config --global user.email john@
bitfieldconsulting.com
5. Tell Git to manage all the files and subdirectories in this directory:
ubuntu@demo:~/puppet$ git add .
6. Finally, have Git take a snapshot of the current state of the code:
ubuntu@demo:~/puppet$ git commit -m "importing"
[master (root-commit) 36f88cb] importing
4 files changed, 25 insertions(+)
create mode 100644 manifests/nodes.pp
create mode 100644 manifests/site.pp
create mode 100644 modules/nginx/files/cat-pictures.conf
create mode 100644 modules/nginx/manifests/init.pp
When you create a new repo, it contains no files, so the git add command adds files to the
list that Git should track:
git add .
[ 55 ]
www.it-ebooks.info
Managing Puppet with Git
This command adds everything in this directory. The full stop (.) is UNIX shorthand for the
current directory.
Instead of storing every successive version of a file, Git just keeps the differences.
For example, if you add a line to a file and then commit that change, Git stores only
the new line and the details of which file it modifies.
For this to work, of course, there has to be an initial commit; a snapshot of the starting state
that Git will then track changes from. This first commit is what you created when you ran the
following command:
git commit -m "Importing"
The -m switch lets you attach a message to the commit, so that you or other people can see
your comments in the history.
[ 56 ]
www.it-ebooks.info
Chapter 4
#
no changes added to commit (use "git add" and/or "git commit -a")
4. Use git diff to show you how the code differs from the snapshot taken at
the last commit:
ubuntu@demo:~/puppet$ git diff
diff --git a/modules/nginx/manifests/init.pp b/modules/nginx/
manifests/init.pp
index b152f17..f272a7c 100644
--- a/modules/nginx/manifests/init.pp
+++ b/modules/nginx/manifests/init.pp
@@ -5,6 +5,7 @@ class nginx {
service { 'nginx':
ensure => running,
+ enable => true,
require => Package['nginx'],
}
5. Add the changed file to the set that will be included in the next commit:
ubuntu@demo:~/puppet$ git add modules/nginx/manifests/init.pp
commit 36f88cbf36782bd8e74499bb23a3a8aa5cc44ef9
Author: John Arundel <[email protected]>
Date: Mon Oct 22 16:38:58 2012 +0000
Importing
[ 57 ]
www.it-ebooks.info
Managing Puppet with Git
8. Use git whatchanged to have Git display a diff showing what was changed in
the commit:
ubuntu@demo:~/puppet$ git whatchanged -p -n 1
commit ad719887ef68535dd6b76bab8bcee9b76edb3c98
Author: John Arundel <[email protected]>
Date: Mon Oct 22 17:08:34 2012 +0000
service { 'nginx':
ensure => running,
+ enable => true,
require => Package['nginx'],
}
You have now changed the code so that it differs from that stored in Git's database, and you
can see which files are different using git status:
# modified: modules/nginx/manifests/init.pp
[ 58 ]
www.it-ebooks.info
Chapter 4
The next step was to tell Git to include this change in the next commit, by using the
git add command:
Now you make the actual commit, with a suitable explanatory message:
ubuntu@demo:~/puppet$ git commit -m "have nginx start at boot time"
The change (or more accurately, set of changes; in this case we only made one) is now stored
in Git's database, and we can see it using the git log command:
ubuntu@demo:~/puppet$ git log
commit ad719887ef68535dd6b76bab8bcee9b76edb3c98
Author: John Arundel <[email protected]>
Date: Mon Oct 22 17:08:34 2012 +0000
commit 36f88cbf36782bd8e74499bb23a3a8aa5cc44ef9
Author: John Arundel <[email protected]>
Date: Mon Oct 22 16:38:58 2012 +0000
Importing
The long string of hexadecimal characters following commit is called the commit hash,
and it uniquely identifies the commit in this repo:
commit ad719887ef68535dd6b76bab8bcee9b76edb3c98
Whenever you need to refer to a particular commit, you can use this hash to identify it.
As time goes on, you will still be able to see every change you've committed to the repo right
back to the initial import. The git whatchanged command shows you the effect of each
change, just like git diff does for uncommitted changes:
service { 'nginx':
ensure => running,
+ enable => true,
require => Package['nginx'],
}
[ 59 ]
www.it-ebooks.info
Managing Puppet with Git
You can skip the git add step by using the -a flag to git commit, as
follows:
git commit -a -m "Have nginx start at boot time"
This automatically adds all changed files to the commit. However, it's a good
idea to use git status and git add to see precisely what changes you
are committing. Sometimes you may want to split your changes into two or
more separate commits.
Also, if you have added new files that Git doesn't know about yet, you'll still
need to use git add to tell Git they should be placed under its control.
On the other hand, if you are making a large number of complicated changes and you are
not sure quite when you'll be done, it might be wise to make a few separate commits along
the way, so that if necessary you can roll the code back to a previous state. Commits cost
nothing, so when you feel a commit is needed, go ahead and make it.
Branching
Git has a powerful feature called branching, which lets you create a parallel copy of the code
(a branch) and make changes to it independently. At any time you can choose to merge those
changes back into the master branch. Or, if changes have been made to the master branch in
the meantime, you can merge those into your working branch and carry on.
This is extremely useful when working with Puppet, because it means you can switch a single
machine to your branch while you're testing it and working on it. The changes you make
won't be visible to other machines that aren't on your branch, so there's no danger
of accidentally rolling out changes before you're ready.
Once you're done, you can merge your changes back into that master and have them roll out
to all machines.
Similarly, two or more people can work independently on their own branches, exchanging
individual commits with each other and with the master branch as they choose. This is a very
flexible and useful way of working.
[ 60 ]
www.it-ebooks.info
Chapter 4
There are several ways to do this, and Puppet has a built-in server capability (Puppetmaster),
which lets each client machine request its own compiled manifest via HTTP. However, when I
work with clients to help them build Puppet infrastructures, I usually recommend a different
approach, using Git to distribute the manifests.
This has a number of advantages over the Puppetmaster approach, and is in some
ways simpler.
Reliability
Although your master Git server (or even GitHub) may go down, you will still be able to run
Puppet on all your client machines and push changes to them using Git. Git is inherently
distributed, unlike the Puppetmaster architecture.
Scalability
You can keep on adding machines indefinitely, and each one looks after itself. By contrast, using
a Puppetmaster moves all the workload of compiling manifests from the client machine to a
single server, which places heavy demands on that server as the network grows.
Simplicity
All you need to do is clone a Git repo. By contrast, adding new Puppet nodes using a
Puppetmaster requires you to generate a certificate request on the client, and sign it on the
server before Puppet can run. Automating this process adds complexity, and changing the
Puppetmaster SSL certificate (for example, if the master server is replaced) requires resigning
all the client certificates. You can set up autosigning, but this introduces a potentially quite
serious security hole.
It's only fair to admit that there are different opinions about this, and some people favor the
Puppetmaster approach, and even think it's simpler than using Git. However, what's simple
to you depends on what you already know. Lots of people already know how to use Git; if
not, it's a very useful thing to learn, and you can apply that knowledge to more than
just Puppet.
In the following sections, we'll create a "master" repo, use it to distribute our manifests to a
new server, and then set up an automatic method of pulling changes and applying them to
each machine.
[ 61 ]
www.it-ebooks.info
Managing Puppet with Git
3. Now create a git user that will own the master repo and control access to it:
ubuntu@demo:/var/git$ sudo useradd -d /var/git git
ubuntu@demo:/var/git$ sudo chown -R git:git /var/git
4. Just to verify that these steps have worked, check out a temporary clone of the
master repo:
ubuntu@demo:/tmp$ cd /tmp
ubuntu@demo:/tmp$ git clone /var/git/puppet.git
Cloning into 'puppet'...
done.
ubuntu@demo:/tmp$ ls puppet
manifests modules
ubuntu@demo:/tmp$ rm -r puppet
5. Now create a secure shell (SSH) keypair for the git user so that it can log in
from remote machines to clone and update the Git repo. When prompted for a
passphrase, just hit Enter.
ubuntu@demo:/tmp$ sudo su - git
$ ssh-keygen
Generating public/private rsa key pair.
Enter file in which to save the key (/var/git/.ssh/id_rsa):
Created directory '/var/git/.ssh'.
Enter passphrase (empty for no passphrase):
Enter same passphrase again:
Your identification has been saved in /var/git/.ssh/id_rsa.
Your public key has been saved in /var/git/.ssh/id_rsa.pub.
[ 62 ]
www.it-ebooks.info
Chapter 4
6. Create an authorized_keys file for git containing the public key you just
generated:
git@demo:~$ cd .ssh
git@demo:~/.ssh$ ls
git@demo:~/.ssh$ cp id_rsa.pub authorized_keys
7. You should now be able to log into the git account via SSH using this key:
git@demo:~/.ssh$ ssh git@localhost
Welcome to Ubuntu 12.04.1 LTS (GNU/Linux 3.2.0-29-virtual x86_64)
...
You now have a master Git repo containing your manifests, and an SSH key that you can use
to check out the repo on other machines.
[ 63 ]
www.it-ebooks.info
Managing Puppet with Git
2. Create a .ssh directory and private key file, and set appropriate permissions:
ubuntu@demo2:~$ sudo su - git
git@demo2:~$ mkdir .ssh
git@demo2:~$ chmod 700 .ssh
git@demo2:~$ touch .ssh/id_rsa
git@demo2:~$ chmod 600 .ssh/id_rsa
3. On your first server, display the SSH private key for git and copy it to the clipboard:
ubuntu@demo:~$ sudo cat ~git/.ssh/id_rsa
-----BEGIN RSA PRIVATE KEY-----
MIIEowIBAAKCAQEA1wR9i+bkwsNIcyd1ojhBH13ecuOhGfoJpjdjSjocBjf2fJRa
...
GOTLXyqpcrez/Ijbc9TJsaFNisnb1HqBR31J/N2StjHmwjtOmlwL
-----END RSA PRIVATE KEY-----
5. Press i to enter insert mode and paste the key from the clipboard:
-----BEGIN RSA PRIVATE KEY-----
MIIEowIBAAKCAQEA1wR9i+bkwsNIcyd1ojhBH13ecuOhGfoJpjdjSjocBjf2fJRa
...
GOTLXyqpcrez/Ijbc9TJsaFNisnb1HqBR31J/N2StjHmwjtOmlwL
-----END RSA PRIVATE KEY-----
[ 64 ]
www.it-ebooks.info
Chapter 4
8. You should now be able to clone the repo onto the new machine:
git@demo2:~$ git clone 23.20.119.201:/var/git/puppet.git
Cloning into 'puppet'...
remote: Counting objects: 17, done.
remote: Compressing objects: 100% (10/10), done.
remote: Total 17 (delta 1), reused 0 (delta 0)
Receiving objects: 100% (17/17), 1.27 KiB, done.
Resolving deltas: 100% (1/1), done.
[ 65 ]
www.it-ebooks.info
Managing Puppet with Git
Exercise
If you work as part of a team, have one of your colleagues clone the master repo and make
some changes. She'll need the private SSH key you created for git (or you can add her SSH
public key to the authorized_keys file for the git user).
Have her push the changes to the master repo, and then update the working copy on the
demo2 box and apply it.
[ 66 ]
www.it-ebooks.info
Chapter 4
Now everyone in your team can work independently on the Puppet manifests, making and
pushing changes, and applying them to all the machines controlled by Puppet.
The simplest way to do this is with a cron job, which updates the repo and then runs Puppet
if anything has changed.
5. Add the following line to give git permission to run the papply script as root:
git ALL = (root) NOPASSWD: /usr/local/bin/papply
[ 67 ]
www.it-ebooks.info
Managing Puppet with Git
9. Add a cron job for git to run this script automatically, and save the file:
*/10 * * * * /usr/local/bin/pull-updates
10. Check that the git user's crontab has been updated:
git@demo2:~$ crontab -l |grep update
*/10 * * * * /usr/local/bin/pull-updates
If any changes are pulled, the script will go on to run papply to apply the changes.
So now whenever you push a change to the master Puppet repo, the demo2 machine will
automatically pick it up and apply it.
Summary
A quick rundown of what we've learned in this chapter.
[ 68 ]
www.it-ebooks.info
Chapter 4
As you're working on a set of changes, you can see how the current code differs from Git's
stored version using git diff. The git status command will show you which files Git
thinks may need to be committed.
You can see the complete history of changes to your repo using the git log command. git
whatchanged will show you the differences in each file before and after the commit.
Networking Puppet
The problem of distributing your Puppet manifests securely and efficiently to a number
of machines can be solved in several ways. The traditional way is to use a special extra
server called a Puppetmaster, which authenticates all the other machines and gives
them their manifests. For small infrastructures, this is overkill; for large infrastructures,
it's slow. Consequently, I usually recommend a different approach: using Git as the
distribution mechanism.
Using Git to distribute your Puppet manifests to multiple machines is a simple, reliable,
and scalable alternative to using a Puppetmaster. All you need is a Git repo from which
each machine clones its own working copy and runs Puppet locally via a cron job.
An easy way to make this secure is to use Git over SSH, with a private key you distribute
to each machine that is authorized to pull Puppet manifests.
Since it's a very good idea to use Git anyway, to manage changes to your Puppet code, and
to enable your team members to work on the Puppet manifests in a distributed way, this is
simply a logical extension of that idea.
You don't need an extra server (which would in any case be a single point of failure), and it
also makes it easy for you to test changes and upgrades using Git branches.
You can set up a script to pull updates from Git and run Puppet automatically if there are any
changes. It's a good idea to trigger the script to run at intervals using cron.
[ 69 ]
www.it-ebooks.info
www.it-ebooks.info
Managing users
5
The real problem isn't whether machines think but whether people do.
— B.F. Skinner
In this chapter, you'll learn how to use Puppet to create and manage user accounts,
configure SSH access and keys, and control user privileges via sudo.
SSH
www.it-ebooks.info
Managing users
Users
One of the most common system administration tasks is setting up user accounts.
We'll see how Puppet can help with this in a moment, but first a word about the kind
of user configuration we should be aiming for.
Everyone who needs access to a machine has her own user account with an SSH key
(not a password)
Access to special-purpose accounts, such as those used to deploy and run
applications, or a database, is controlled by authorizing specific SSH keys, rather
than using passwords
Accounts that need certain, specific superuser privileges can get them via the
sudo mechanism
The root account is not accessible via the network (but there is secure, out-of-band
access to the system console)
Third parties, such as contractors and support staff, get temporary access with
limited privileges, which can be revoked once a job is finished
Setting up policies like these, while highly desirable from a security point of view, is
time-consuming to do by hand and difficult to maintain. If a new user arrives, someone
has to add and configure his account on every server. If a user leaves, the accounts have
to be removed or locked everywhere.
It's not surprising that many organizations, under time pressure and needing things to work
right away, don't bother too much about security and access control. In many cases the
simplest thing to do is for everyone to log in as root using the same password, often for all
machines. Even if there are official policies about security, people often don't follow them,
because it's more convenient to do things an insecure way.
[ 72 ]
www.it-ebooks.info
Chapter 5
When this is the case, it's much easier to ensure that security policies are followed, without
hindering people from doing their jobs. When your SSH key works everywhere, you
don't need to share or write down passwords, and when your account has the necessary
privileges, you don't need to use root. So everybody benefits.
Puppet provides a number of ways to help you manage users. The user resource type
controls user accounts, and the ssh_authorized_key resource type controls SSH access to
accounts. You can use Puppet to control user privileges by managing the sudoers file, and
you can also replace the default SSH configuration file with a more secure version managed
by Puppet.
In the rest of this chapter, we'll see how to use these techniques, again using our
cat-pictures.com example site.
[ 73 ]
www.it-ebooks.info
Managing users
The home attribute sets the path to the user's home directory. Puppet will not create this
directory for you unless you also set the managehome attribute:
managehome => true,
So the manifest says that a user named art should exist, whose full name is Art Vandelay,
and that his home directory should be /home/art, and that that directory should exist.
Note that we have not specified a password for the user, and as a result art
will not yet be able to log in. Although Puppet can set passwords for users
(with the password attribute) I recommend you use SSH authentication
instead, which is much more secure than using a password. We'll see how to
do this later in the Access control section.
When you run Puppet, the art account will be removed (though Art's home directory and
any files he owned will remain).
[ 74 ]
www.it-ebooks.info
Chapter 5
Just removing the user resource declaration from your Puppet code won't
actually remove the user's account from your machines. If you think about it,
this makes sense. Otherwise, Puppet would remove all accounts it hasn't been
specifically told about, including root!
So when you want to remove a user, change their ensure attribute from
present to absent, and Puppet will delete the account for you. Once
this change has been applied to all machines, you can remove the user
declaration from your Puppet manifest.
Access control
Having created the user's account, we now need to provide a secure way for him to log in.
We can do this using the SSH protocol.
What is SSH?
SSH is a more secure way of controlling user access than the traditional "username and
password" approach. Instead of using a password, which the user has to keep secret, it uses
two pieces of information: the public key and the private key. Only the private key has to be
secret. You can put your public key on any computer, or publish it to the world if you like.
But no one can log in to an account controlled by your public key unless they also have the
matching private key.
This has the pleasant consequence that you only need one SSH key, and you can use it for
everything. It's a very bad idea to use the same password for multiple accounts, but with
SSH, that's no problem. So long as you keep the private key secret, you can use your public
key everywhere.
[ 75 ]
www.it-ebooks.info
Managing users
2. If you don't have an SSH key, you can generate one for this exercise:
ubuntu@demo:~$ ssh-keygen
Generating public/private rsa key pair.
Enter file in which to save the key (/home/ubuntu/.ssh/id_rsa):
Enter passphrase (empty for no passphrase):
Enter same passphrase again:
Your identification has been saved in /home/ubuntu/.ssh/id_rsa.
Your public key has been saved in /home/ubuntu/.ssh/id_rsa.pub.
4. Edit your manifests/nodes.pp file as follows (using your own key string as
the value for key):
node 'demo' {
user { 'art':
ensure => present,
comment => 'Art Vandelay',
home => '/home/art',
managehome => true,
[ 76 ]
www.it-ebooks.info
Chapter 5
}
ssh_authorized_key { 'art_ssh':
user => 'art',
type => 'rsa',
key => 'AAAAB3NzaC1yc2EAAAABIwAAAIEA3ATqENg+GWAC
a2BzeqTdGnJhNoBer8x6pfWkzNzeM8Zx7/2Tf2pl7kHdbsiTXEUaw
qzXZQtZzt/j3Oya+PZjcRpWNRzprSmd2UxEEPTqDw9LqY5S2B8og/
NyzWaIYPsKoatcgC7VgYHplcTbzEhGu8BsoEVBGYu3IRy5RkAcZik=',
}
}
5. Run Puppet:
ubuntu@demo:~/puppet$ papply
Notice: /Stage[main]//Node[demo]/Ssh_authorized_key[art_ssh]/
ensure: created
Notice: Finished catalog run in 0.05 seconds
6. Now test that you have access to the art account using this key. On a machine that
has your SSH key, run the following command:
$ ssh art@demo
Welcome to Ubuntu 12.04.1 LTS (GNU/Linux 3.2.0-29-virtual x86_64)
The name (art_ssh in this case) can be anything you like, so long as it's unique. It will be
added as a comment at the end of the key in the authorized_keys file.
We need to specify the user account for which this key will grant access:
user => 'art',
We also have to tell Puppet the key type (rsa or dsa; you'll know which it is because the key
file itself contains ssh-rsa or ssh-dsa at the beginning):
type => 'rsa',
And lastly the key string, which in this case should be your own key instead of mine:
key => 'AAAAB3NzaC1yc2EAAAABIwAAAIEA3ATqENg+GWACa2B
zeqTdGnJhNoBer8x6pfWkzNzeM8Zx7/2Tf2pl7kHdbsiTXEUawq
zXZQtZzt/j3Oya+PZjcRpWNRzprSmd2UxEEPTqDw9LqY5S2B8og/
NyzWaIYPsKoatcgC7VgYHplcTbzEhGu8BsoEVBGYu3IRy5RkAcZik=',
[ 77 ]
www.it-ebooks.info
Managing users
Puppet will then add this key to the file /home/art/.ssh/authorized_keys. When you
try to log in to Art's account via SSH, the system will look in this file to see if your private key
matches any of the public keys listed there. Assuming it does, you'll be able to log in.
Give the user the secret key file (fabian) and put the matching public key into Puppet as an
ssh_authorized_key resource for that user.
Special-purpose keys
Sometimes an automatic process on one machine needs access to another machine.
For example, you might have a daily cron job that uploads logs to a central storage server.
So how do you manage this securely?
One simple way is to create a user account on the target machine dedicated to the purpose:
log uploading, for example. This account is secured with SSH, and access is restricted to a
special private key that you create. The private key is distributed with Puppet to only those
machines that need it, and can be removed or changed at any time.
This is exactly the approach we took in an earlier chapter for setting up automatic access
to a Git server, so that machines can pull their Puppet config at regular intervals and apply
changes. You can use this idea to manage access for any automated task.
For even greater security, you can give each machine its own private key, and authorize the
target machine for all the corresponding public keys.
[ 78 ]
www.it-ebooks.info
Chapter 5
The value for key in the example above is an empty single-quoted string (''). This will
disable SSH logins for the user. If you have enabled password authentication (which I don't
recommend, but you might need it in some situations) then this won't stop the user from
logging in using his password. To do this, set a password of a single star (*) in Puppet:
user { 'art':
ensure => present,
comment => 'Art Vandelay',
home => '/home/art',
managehome => true,
password => '*',
}
This will block the user from logging in via password (though SSH will still work unless you
also disable that, as shown above). To unlock the account, remove the password attribute
and re-set the user's password using the passwd command.
file { '/etc/ssh/sshd_config':
source => 'puppet:///modules/ssh/sshd_config',
notify => Service['ssh'],
[ 79 ]
www.it-ebooks.info
Managing users
5. Run Puppet:
ubuntu@demo:~/puppet$ papply
Notice: /Stage[main]/Ssh/File[/etc/ssh/sshd_config]/content:
content changed '{md5}5f15065f987c4d9851ad3448d4aadfa6' to '{md5}6
e96247a35996ba5adc36acbf34faf9b'
Notice: /Stage[main]/Ssh/Service[ssh]: Triggered 'refresh' from 1
events
Notice: Finished catalog run in 0.23 seconds
6. Check that you can still log in from another machine as ubuntu or art:
john@T-Bone:~$ ssh ubuntu@demo
Welcome to Ubuntu 12.04.1 LTS (GNU/Linux 3.2.0-29-virtual x86_64)
User privileges
Linux and other UNIX-like operating systems commonly have two levels of user privilege: the
root user, who can edit system files and perform operations tasks, such as rebooting the
machine, and normal users, who can only edit and read files owned by themselves, and have
no special privileges. This ensures that users don't get access to files or commands that they
shouldn't have. However, sometimes you need to grant special privileges to a user, without
giving her full access to the root account. You can do this using a UNIX command called sudo.
[ 80 ]
www.it-ebooks.info
Chapter 5
sudo
The sudo command allows normal users to run commands with root privileges, if this is
specifically authorized by the system administrator. For example, a developer user might be
given privileges to run service nginx restart as root.
The set of users allowed to assume root privileges, and the specific commands they can
run, is specified in the file /etc/sudoers. We can use Puppet to manage this file, and thus
control user privileges on the machine.
[ 81 ]
www.it-ebooks.info
Managing users
5. If there are any errors, correct them before moving on. If you use Puppet to deploy a
sudoers file that contains syntax errors, no users will be able to sudo anything, and
you will need to log in as root in order to fix the problem. So whenever you make a
change to Puppet's copy of the sudoers file, use the visudo command as above to
check the syntax.
6. Add this to your node definition in manifests/nodes.pp:
include sudoers
7. Run Puppet:
ubuntu@demo:~/puppet$ papply
Notice: /Stage[main]/Sudoers/File[/etc/sudoers]/content: content
changed '{md5}5755c84fcb480985818c6daa9faa386c' to '{md5}
f9d8dbf9b36280c3e860af7eede92fd1'
Notice: Finished catalog run in 0.10 seconds
8. Run the following command as the ubuntu user to verify that the changes have
taken effect:
ubuntu@demo:~/puppet$ sudo whoami
root
9. Run the following command as the art user, to test whether he has the privilege to
run /bin/ls as root:
art@demo:~$ sudo /bin/ls -l /
total 80
drwxr-xr-x 2 root root 4096 Aug 22 05:49 bin
drwxr-xr-x 3 root root 4096 Jan 9 13:54 boot
drwxr-xr-x 12 root root 3840 Jan 9 13:47 dev
drwxr-xr-x 89 root root 4096 Jan 14 15:29 etc
drwxr-xr-x 3 root root 4096 Aug 22 05:48 home
The line root ALL = (ALL) ALL allows user root to sudo any command as root
(this might seem unnecessary, but it's included for consistency, and to make sure any scripts
that use sudo don't suddenly fail if run as root).
[ 82 ]
www.it-ebooks.info
Chapter 5
The line ubuntu ALL = (ALL) NOPASSWD:ALL allows user ubuntu to run any
command, on any system, as any user, without having to enter a password. (You can have
sudo require the user's password, if you use passwords, to make things a little more secure.
Generally though, sudoers entries are used for scripts and automated jobs that can't enter
a password anyway.)
The line art ALL = (ALL) NOPASSWD: /bin/ls is more specific. It allows user art to
run only the command /bin/ls (with any arguments). No other commands will work:
art@demo:~$ sudo /sbin/halt
[sudo] password for art:
Sorry, user art is not allowed to execute '/sbin/halt' as root on
demo.
Summary
A quick rundown of what we've learned in this chapter.
Security practices
If you follow good security practices for your network, each user should have her own
named account with SSH (not password) access. Any special-purpose accounts should be
authorized for the SSH keys of the specific users that need access to them. Login as root
should be disallowed (except on a secure console).
User resources
Puppet can manage users directly using the user resource:
user { 'art':
ensure => present,
...
}
You can specify the user's full name with the comment attribute:
comment => 'Art Vandelay',
[ 83 ]
www.it-ebooks.info
Managing users
Just removing the user resource from Puppet won't remove the user account from the
server, so if you need to delete the account, make sure you use ensure => absent.
To lock an account, for example to temporarily disable access, set the ssh_authorized_
key to an empty string and the password to a * character.
ssh_authorized_key { 'art_ssh':
...
}
Specify the key metadata with the user, type, and key attributes:
user => 'art',
type => 'rsa',
key => 'AAAAB3...',
If you need to generate new SSH keys for users, you can do it with the ssh-keygen command:
ssh-keygen -f fabian
Configuring SSH
Puppet can also manage global SSH configuration by deploying the /etc/ssh/sshd_
config file. You can limit the list of users allowed to log in by specifying the AllowUsers
parameter in this file:
AllowUsers ubuntu art
[ 84 ]
www.it-ebooks.info
Chapter 5
[ 85 ]
www.it-ebooks.info
www.it-ebooks.info
Tasks and templates
6
You can tell whether a man is clever by his answers. You can tell whether a man
is wise by his questions.
— Naguib Mahfouz
In this chapter, you'll learn how to use Puppet's resource types to run commands, schedule
regular tasks, and distribute large trees of files. You'll also find out how to insert values
dynamically into files using templates.
TAS
SCH K
EDULE
TODO.TXT
www.it-ebooks.info
Tasks and templates
But what if you want Puppet to run a certain command directly? You can do this using
an exec resource. This is a very flexible and powerful resource, and you can use it to
implement almost anything in Puppet. In this section we'll see how to get the most from
exec resources.
2. Run Puppet:
ubuntu@demo:~/puppet$ papply
Notice: /Stage[main]//Node[demo]/Exec[Run my arbitrary command]/
returns: executed successfully
Notice: Finished catalog run in 0.14 seconds
3. Check the output produced (you won't see exactly the same date and time shown
here, unless you're a Time Lord):
ubuntu@demo:~/puppet$ cat /tmp/command.output.txt
I ran this command on Mon Dec 17 16:14:04 UTC 2012
[ 88 ]
www.it-ebooks.info
Chapter 6
Note that the UNIX commands, echo and date, are specified with their full path. This is
because Puppet wants to be sure exactly which command you mean.
When Puppet runs, it applies the exec resource by running the command:
/bin/echo I ran this command on `/bin/date` >/tmp/command.output.txt
The creates attribute specifies the full path to a file. Puppet will check to see if this file
already exists. If it does, the exec won't be run. This is a neat way to have a command run
only if it is needed, and not otherwise.
Did you notice we also added the cwd attribute? This tells Puppet the
directory in which to run the command (cwd stands for current working
directory), so that any files created by the command, like john.pub in this
example, will end up in that directory.
You can also use the unless or onlyif attributes to control when an exec is run. unless
or onlyif both specify a command for Puppet to run to test whether the exec needs to
be applied.
The exit status of the test command determines what Puppet should do. For example:
exec { 'add-cloudera-apt-key':
command => '/usr/bin/apt-key add /tmp/cloudera.pub',
unless => '/usr/bin/apt-key list |grep Cloudera',
}
[ 89 ]
www.it-ebooks.info
Tasks and templates
Here, we're using an exec to add an APT repository key to the system keyring. This only
needs to be done once, so the unless command checks whether the key has already been
added. If the grep succeeds, we know the key is already present, so we don't need to do
anything. The exit status will be zero, so Puppet won't apply the exec. On the other hand,
if the grep fails, the exit status will be non-zero so Puppet will apply the exec.
Using onlyif, the opposite logic applies; the exec will be run only if the test command
succeeds (exits with a zero status).
Triggering commands
Another way to control when an exec is run is to use the refreshonly attribute:
exec { 'icinga-config-check':
command => '/usr/sbin/icinga -v /etc/icinga/icinga.cfg && /usr/
sbin/service icinga restart',
refreshonly => true,
subscribe => File['/etc/icinga/icinga.cfg'],
}
When refreshonly is set, Puppet will not apply the exec unless it's triggered by
subscribe or notify from some other resource. In this example, the exec subscribes
to the file /etc/icinga/icinga.cfg. If this file changes, Puppet will run the exec,
but not otherwise.
This is a very useful pattern when you want to take some action if a config file changes,
especially if you want to sanity-check the file's contents (as in the example) before restarting
the service that reads it.
Chaining commands
Often you have a series of commands that need to run in a particular order (for example,
if you're installing software from source, you might need to download a file, unpack it, build
it, and install it). To do this, for short sequences, you can use the shell && construct as shown
in the preceding example:
/usr/sbin/icinga -v /etc/icinga/icinga.cfg && /usr/sbin/service icinga
restart
This will chain the commands together in the order you specify, bailing out if any of the
commands fail.
[ 90 ]
www.it-ebooks.info
Chapter 6
For more complicated sequences, or where you may also need to trigger individual
commands from other resources, you can use the require attribute to specify the
ordering explicitly:
exec { 'command-1':
command => '/bin/echo Step 1',
}
exec { 'command-2':
command => '/bin/echo Step 2',
require => Exec['command-1'],
}
exec { 'command-3':
command => '/bin/echo Step 3',
require => Exec['command-2'],
}
Now when Puppet sees a command name, it will search the directories you specify looking
for the matching commands.
If you want to specify a set of default search paths for all exec resources, you can put this in
your site.pp file:
Exec {
path => ['/bin', '/usr/bin'],
}
[ 91 ]
www.it-ebooks.info
Tasks and templates
Note the capital E for Exec. This means "make this the default for all exec resources."
Then you can use unqualified commands without an explicit path attribute:
exec { 'Run my arbitrary command':
command => 'echo I ran this command on `date` >/tmp/command.output.
txt',
}
Puppet will use the default paths you specified: /bin and /usr/bin.
Scheduled tasks
Typically, when you want a command to be run at a certain time of day, or at regular
intervals, you can use the UNIX cron facility. For example, a backup job might run every
night at 4 a.m., or a queue processing task might run every 5 minutes.
Puppet can manage cron jobs directly using the cron resource type. Here's an example.
2. Run Puppet:
ubuntu@demo:~/puppet$ papply
Notice: /Stage[main]//Node[demo]/Cron[Back up cat-pictures]/
ensure: created
Notice: Finished catalog run in 0.12 seconds
[ 92 ]
www.it-ebooks.info
Chapter 6
The preceding line sets the command to run (in this case, an rsync command to back up
all files and directories under /var/www/cat-pictures to /cat-pictures-backup).
As with exec resources, commands need to be qualified with their full path.
If minute is not specified, it defaults to *; that is, it runs every minute! So always
specify both hour and minute (if there is no hour, the job runs every hour at the
minute you specify).
Note that Puppet adds a helpful header to the crontab file, warning you
not to meddle in the affairs of Puppet. In fact, you can safely add, remove,
and modify any cron jobs not managed by Puppet. Puppet identifies the
cron jobs it's managing by the Puppet Name comment above each job.
So, as the warning suggests, don't remove or edit these comments or
Puppet will think the job is missing and add a new copy of it.
[ 93 ]
www.it-ebooks.info
Tasks and templates
If any of these attributes are not supplied, they default to *; that is, every weekday, every
month, or every day of the month.
You can use the same pattern with the other time attributes, for example, to run a job every
6 hours on the hour:
hour => '*/6',
minute => '00',
Exercise
Use a cron resource to automate the pull-updates job you set up in Chapter 4, Managing
Puppet with Git, which automatically pulls Git changes and applies Puppet on each machine.
Make this part of every machine's base configuration.
[ 94 ]
www.it-ebooks.info
Chapter 6
Distributing files
We've seen in previous chapters how to use Puppet's file resource to deploy a single file to
a server. Sometimes, though, you need to copy a whole directory tree of files, without having
to list each individual file in your Puppet manifest. The recurse attribute allows you to do
this. We'll see how to use it in the next example.
2. Create a directory for the images, and some placeholder image files (for extra credit,
download some real pictures of cats from the Internet):
ubuntu@demo:~/puppet$ mkdir modules/cat-pictures/files/img
ubuntu@demo:~/puppet$ mkdir modules/cat-pictures/files/img/
cat_001.jpg
ubuntu@demo:~/puppet$ mkdir modules/cat-pictures/files/img/
cat_002.jpg
ubuntu@demo:~/puppet$ mkdir modules/cat-pictures/files/img/
cat_003.jpg
file { '/var/www/cat-pictures/img':
source => 'puppet:///modules/cat-pictures/img',
recurse => true,
require => File['/var/www/cat-pictures'],
}
}
[ 95 ]
www.it-ebooks.info
Tasks and templates
4. Run Puppet:
ubuntu@demo:~/puppet$ papply
Notice: /Stage[main]//Node[demo]/File[/var/www/cat-pictures]/
ensure: created
Notice: /Stage[main]//Node[demo]/File[/var/www/cat-pictures/img]/
ensure: created
Notice: /File[/var/www/cat-pictures/img/cat_002.jpg]/ensure:
created
Notice: /File[/var/www/cat-pictures/img/cat_001.jpg]/ensure:
created
Notice: /File[/var/www/cat-pictures/img/cat_003.jpg]/ensure:
created
Notice: Finished catalog run in 0.08 seconds
We haven't seen a file resource before without either a source or a content attribute.
ensure => directory will create a directory, as you might expect. If you said ensure =>
present instead, with no other attributes, Puppet would create an empty file.
The following code is the part that does the heavy lifting:
file { '/var/www/cat-pictures/img':
source => 'puppet:///modules/cat-pictures/img',
recurse => true,
require => File['/var/www/cat-pictures'],
}
The source attribute is as you've used it before, but the recurse => true attribute tells
Puppet to copy all files and directories contained in the source. This includes our handful of
cat pictures, but it could be thousands of files in a tree of directories many levels deep.
In practice Puppet is rather slow to manage large file trees, because it has
to examine every file in the tree on every run to determine if it is up to
date with the source. In this situation, you might be better off using Git, for
example, to manage large trees of files.
[ 96 ]
www.it-ebooks.info
Chapter 6
Using templates
In a previous example we had Puppet deploy an Nginx virtual host file for the
cat-pictures application. In this case we simply used a file resource with
the cat-pictures.conf file distributed from Puppet.
If we wanted to generalize this solution to manage many different websites, it would quickly
become tedious to supply an almost identical virtual host file for each site, altering only the
name and domain of the site.
What we would prefer is to give Puppet a template file into which it could just insert these
variables for each different site. The template function serves just this purpose. Anywhere
you have multiple files that differ only slightly, or files that need to contain dynamic
information, you can use a template.
service { 'nginx':
ensure => running,
enable => true,
require => Package['nginx'],
}
file { '/etc/nginx/sites-enabled/default':
ensure => absent,
}
}
[ 97 ]
www.it-ebooks.info
Tasks and templates
$site_name = 'cat-pictures'
$site_domain = 'cat-pictures.com'
file { '/etc/nginx/sites-enabled/cat-pictures.conf':
content => template('nginx/vhost.conf.erb'),
notify => Service['nginx'],
}
}
5. Run Puppet:
ubuntu@demo:~/puppet$ papply
Notice:/Stage[main]//Node[demo]/File[/etc/nginx/sites-enabled/cat-
pictures.conf]/ensure: defined content as '{md5}0750fd1b8da76b84f2
597de76c1b9bce'
Notice: /Stage[main]/Nginx/File[/etc/nginx/sites-enabled/default]/
ensure: removed
Notice: /Stage[main]/Nginx/Service[nginx]: Triggered 'refresh'
from 1 events
Notice: Finished catalog run in 0.74 seconds
[ 98 ]
www.it-ebooks.info
Chapter 6
file { '/etc/nginx/sites-enabled/default':
ensure => absent,
}
The <%= %> signs mark where parameters will go; we will supply site_name and
site_domain later, when we use the template. Puppet will replace <%= @site_name %>
with the value of the site_name variable.
Then in the nodes.pp file, we include the nginx module on the node:
node 'demo' {
include nginx
Before using the template, we need to set values for the variables site_name and
site_domain:
$site_name = 'cat-pictures'
$site_domain = 'cat-pictures.com'
Note that when we refer to these variables in Puppet code, we use a $ prefix ($site_name),
but in the template it's an @ prefix (@site_name). This is because in templates we're actually
writing Ruby, not Puppet!
When you use a variable name inside a quoted string, it's a good idea to wrap
it in curly brackets as follows:
"The domain is ${site_domain}"
Not:
"The domain is $site_domain"
This helps to distinguish the variable name from the literal string it's used in
(and any other variables you might be using in the same string).
[ 99 ]
www.it-ebooks.info
Tasks and templates
Now we can use the template to generate the Nginx virtual host file:
file { '/etc/nginx/sites-enabled/cat-pictures.conf':
content => template('nginx/vhost.conf.erb'),
notify => Service['nginx'],
}
This looks just like any other file resource, with a content attribute, but we previously
gave the contents of the file as a literal string:
content => "Hello, world\n",
The argument to template tells Puppet where to find the template file. The path
nginx/vhost.conf.erb
Translates to
modules/nginx/templates/vhost.conf.erb
Puppet now evaluates the template, inserting the values of any variables referenced in
<%= %> signs, and generates the final output:
server {
listen 80;
root /var/www/cat-pictures;
server_name cat-pictures.com;
}
You might think this is a lot of trouble to go to just to end up with the same file we had
before. Of course, having gone to the trouble of using a template, we can now easily create
virtual hosts for other sites using the same template file:
node 'demo2' {
include nginx
$site_name = 'dog-pictures'
$site_domain = 'dog-pictures.com'
file { '/etc/nginx/sites-enabled/dog-pictures.conf':
content => template('nginx/vhost.conf.erb'),
notify => Service['nginx'],
}
}
[ 100 ]
www.it-ebooks.info
Chapter 6
Inline templates
You don't need to use a separate template file to take advantage of the power of templates.
The inline_template function lets you put a template string right in your Puppet code:
file { '/tmp/the_answer.txt':
content => inline_template("What do you get if you multiply six by
nine? <%= 6 * 7 %>.\n")
}
System facts
It's often useful to be able to get information about the system, such as its IP address
or operating system version. Puppet's companion tool, Facter, provides this information.
To see the list of facts available about your system, run the command:
ubuntu@demo:~/puppet$ facter
architecture => amd64
...
uptime_hours => 2109
uptime_seconds => 7593471
virtual => xenu
You can reference any of these facts in a template (or in your Puppet code) just like
a variable:
content => inline_template("My address is <%= @ipaddress %>.\n")
There are a lot of facts. The ones you will most likely use in Puppet manifests are:
[ 101 ]
www.it-ebooks.info
Tasks and templates
Having copied this code (of mine) from a production system, I see that it isn't really very
good. It assumes the only units returned will be MB or GB, so it will fail on systems with
memory measured in terabytes (TB), for example. But you get the idea, and your code
will be better.
[ 102 ]
www.it-ebooks.info
Chapter 6
Summary
A quick rundown of what we've learned in this chapter.
Exec resources
Anything you can do on the command line, Puppet can do with an exec resource.
Specify the command to run using the command attribute:
exec { 'Run my arbitrary command':
command => '/bin/echo I ran this command on `/bin/date` >/tmp/
command.output.txt',
}
By default, an exec resource will always be applied, every time you run Puppet. There are
several ways to control whether or when an exec will be applied:
To apply the command only when triggered by some other resource, use the
refreshonly attribute:
exec { 'icinga-config-check':
command => '/usr/sbin/icinga -v /etc/icinga/icinga.cfg && /usr/
sbin/service icinga restart',
refreshonly => true,
subscribe => File['/etc/icinga/icinga.cfg'],
}
This will apply the exec only when the resource it subscribes to (/etc/icinga/icinga.
cfg) is changed. You could have the other resource notify the exec instead, which has the
same effect.
[ 103 ]
www.it-ebooks.info
Tasks and templates
For short sequences of commands, you can chain them in a single exec using
the & shell operator:
/usr/sbin/icinga -v /etc/icinga/icinga.cfg && /usr/sbin/service icinga
restart
For longer sequences using multiple exec resources, you can specify the necessary ordering
using require:
exec { 'command-1':
command => '/bin/echo Step 1',
}
exec { 'command-2':
command => '/bin/echo Step 2',
require => Exec['command-1'],
}
Puppet requires you to specify the full path to each command you run in an exec, unless
you specify a list of paths to search for commands using the path attribute:
exec { 'Run my arbitrary command':
command => 'echo I ran this command on `date` >/tmp/command.output.
txt',
path => ['/bin', '/usr/bin'],
}
You can set a default list of paths for all exec resources in your site.pp file:
Exec {
path => ['/bin', '/usr/bin'],
}
Scheduled jobs
To run commands at a specified time of day, or at regular intervals, you can use a cron
resource:
cron { 'Back up cat-pictures':
command => '/usr/bin/rsync -az /var/www/cat-pictures/ /cat-pictures-
backup/',
hour => '04',
minute => '00',
}
You can use any combination of these attributes to set the scheduled time for the job: hour,
minute, day, weekday, monthday, month.
[ 104 ]
www.it-ebooks.info
Chapter 6
You can run a job at regular intervals (every 5 minutes, for example) with a setting like this:
minute => '*/5',
Cron jobs default to running as root. To make a job execute as a particular user, specify the
user attribute:
Templates
Templates can be used wherever you need to insert information into a file based on Puppet
variables or Facter facts. You can also use Ruby code in templates to do math or string
computations, or read and write files, anything, in fact, that Ruby can do. Just specify a
template file using the template function:
file { '/etc/nginx/sites-enabled/cat-pictures.conf':
content => template('nginx/vhost.conf.erb'),
notify => Service['nginx'],
}
The most common use for templates is simply inserting the value of a variable:
server_name <%= @site_domain %>;
Inline templates don't require a separate template file; you just supply the template to
Puppet as a string in your manifest and call the inline_template function to evaluate it:
file { '/tmp/the_answer.txt':
content => inline_template("What do you get if you multiply six by
nine? <%= 6 * 7 %>.\n")
}
[ 105 ]
www.it-ebooks.info
www.it-ebooks.info
Definitions and Classes
7
There are basically two types of people. People who accomplish things, and
people who claim to have accomplished things. The first group is less crowded.
— Mark Twain
In this chapter, you'll learn how to group resources into reusable clumps that you can refer
to by name, making it easy to create lots of similar resources at once. You can also make your
Puppet manifests shorter, neater, and more readable by eliminating duplicated code.
Employee of
the Month
node 'web' {
This is so include memcache
awesome ! include rails
include nginx
include my_app
}
D
GOL
S R
TA
www.it-ebooks.info
Definitions and Classes
package { 'php5-fpm':
ensure => installed,
}
package { 'php-pear':
ensure => installed,
}
You can make your code shorter and simpler by grouping them into a single resource
declaration with a list of names, as follows:
package { [ 'php5-cli',
'php5-fpm',
'php-pear' ]:
ensure => installed,
}
A comma-separated list in square brackets, shown in the following code line, is called
an array:
[ 'php5-cli', 'php5-fpm', 'php-pear' ]
I've split it over multiple lines to make it more readable, but it's all the same to Puppet.
Arrays are acceptable in many places where otherwise you might use a single value:
require => [ Package['ntp'], File['/etc/ntp.conf'] ],
And they are especially useful when declaring lots of instances of the same resource type,
which only differ in their names:
file { [ '/var/www/myapp',
'/var/www/myapp/releases',
'/var/www/myapp/shared',
'/var/www/myapp/shared/config',
'/var/www/myapp/shared/log',
'/var/www/myapp/shared/pids',
'/var/www/myapp/shared/system' ]:
ensure => directory,
}
[ 108 ]
www.it-ebooks.info
Chapter 7
Any attributes you add (file ownership or mode, for example) will be the same for every file
in the array. This is a great way to set attributes for a large number of resources all at once.
Definitions
Grouping resources into arrays is very helpful, but it only works with instances of a single
resource type. What if you want to group resources of different types? Let's take an example:
creating scheduled jobs that run a script at a particular time. For each job, we need to have
Puppet deploy the script file itself to the server:
file { '/usr/local/bin/backup_database':
source => 'puppet:///modules/scripts/backup_database',
mode => '0755',
}
So far, so good. But when you have ten jobs to run, all this typing gets a little repetitive:
file { '/usr/local/bin/job1':
source => 'puppet:///modules/scripts/job1',
mode => '0755',
}
cron { 'Run job1':
command => '/usr/local/bin/job1',
hour => '00',
minute => '00',
}
file { '/usr/local/bin/job2':
source => 'puppet:///modules/scripts/job2',
mode => '0755',
}
cron { 'Run job2':
command => '/usr/local/bin/job2',
hour => '00',
minute => '00',
}
...
# and so on
[ 109 ]
www.it-ebooks.info
Definitions and Classes
Worse, when you have lots of duplicated code like this, it becomes very difficult to maintain.
If you want to change a parameter for all your jobs (say, to run them all at 1 a.m. instead of
midnight) you have to track down every job in your code and make the same modification.
That's tedious and error-prone.
A better way is to group this pair of resources (the file and the cron job) and give them
a name using the define keyword:
# Manages a script plus the cron job to run it
define script_job() {
file { "/usr/local/bin/${name}":
source => "puppet:///modules/scripts/${name}",
mode => '0755',
}
cron { "Run ${name}":
command => "/usr/local/bin/${name}",
hour => '00',
minute => '00',
}
}
You can see that this is exactly the same as the resources we had before, except that the
name of the job has been replaced with ${name}, and the whole thing is wrapped inside
these lines:
define script_job() {
...
}
The resource that you create using the define keyword is called a definition. A definition
can be used just like a regular resource type:
script_job { 'backup_database':
}
Wherever ${name} occurred in the definition, it's been replaced with backup_database.
[ 110 ]
www.it-ebooks.info
Chapter 7
To do this, add the name of the parameter in round brackets following the name of
the define:
define script_job( $hour ) {
...
}
You can then refer to $hour anywhere inside the definition and get its value:
cron { "Run ${name}":
command => "/usr/local/bin/${name}",
hour => $hour,
minute => '00',
}
Quotes
If you put double quotes around a string, Puppet will process it for variable
references (replacing ${name} with backup_database, for example).
It will also interpret the special escape sequences such as \n for new line.
If you use single quotes, Puppet will leave the string just as it is. So Puppet
Labs official style guidelines say:
All strings that do not contain variables should be enclosed in single quotes.
Double quotes should be used when variable interpolation is required.
When you declare an instance of script_job, you now have to pass in a value for hour just
like any other resource attribute:
script_job { 'backup_database':
hour => '05',
}
You can pass more than one parameter using a comma-separated list:
define script_job( $hour, $minute ) {
file { "/usr/local/bin/${name}":
source => "puppet:///modules/scripts/${name}",
mode => '0755',
}
[ 111 ]
www.it-ebooks.info
Definitions and Classes
And passing multiple parameters to a definition is just like setting multiple attributes on
a regular resource:
script_job { 'backup_database':
hour => '05',
minute => '30',
}
Optional parameters
We don't always care what time a job runs, so it would be nice to have the hour and minute
parameters take some default value (00, say). You can do this by specifying the default value
in the parameter list:
define script_job( $hour = '00', $minute = '00' ) {
...
}
Now if we don't specify an hour or minute for a script_job, they will get the default
values. Here is an instance of script_job declared this way, with no parameters:
script_job { 'backup_database':
}
This results in a job that runs at midnight. However, if you pass in values for hour or minute,
they will override the defaults, and a script_job like this will run every hour:
script_job { 'download_tweets':
hour => "*",
}
[ 112 ]
www.it-ebooks.info
Chapter 7
3. Run Puppet:
ubuntu@demo:~/puppet$ papply
Notice: /Stage[main]//Node[demo]/Nginx::Website[adorable-animals]/
File[/etc/nginx/sites-enabled/adorable-animals.conf]/ensure:
defined content as '{md5}53febc966302b52afc5346803606ced3'
Notice: /Stage[main]/Nginx/Service[nginx]: Triggered 'refresh'
from 1 events
Notice: Finished catalog run in 0.35 seconds
[ 113 ]
www.it-ebooks.info
Definitions and Classes
This pulls in the nginx class, which we set up in earlier chapters to manage the Nginx server.
The next line sets up the $site_name variable that we're going to use in the template:
$site_name = $name
You might remember that $name is a special parameter that Puppet sets implicitly for you.
When you declare a resource, you give it a name in quotes after the resource type:
package { 'nginx':
...
So inside that package definition, $name will have the value nginx. Similarly, we declared
this instance of nginx::website with the name adorable-animals:
nginx::website { 'adorable-animals':
So here, $name will have the value adorable-animals. We assign this value to the variable
$site_name.
Next, we declare a file resource for the Nginx virtual host file:
file { "/etc/nginx/sites-enabled/${site_name}.conf":
We know that $site_name has the value adorable-animals, so the actual file Puppet
creates will be /etc/nginx/sites-enabled/adorable-animals.conf.
[ 114 ]
www.it-ebooks.info
Chapter 7
Puppet will interpolate the values for site_name and site_domain into this template,
as we saw in the previous chapter. So the actual contents of the file will be:
server {
listen 80;
root /var/www/adorable-animals;
server_name adorable-animals.com;
}
Exercise
Extend the nginx::website definition so that it restarts or reloads the nginx service
whenever a virtual host file changes.
Classes
We've seen classes before, when we used the class keyword to group together the Puppet
resources that implement some particular service, such as Nginx:
# Manage nginx webserver
class nginx {
package { 'nginx':
ensure => installed,
}
}
Defining classes
The class keyword introduces a new class definition:
class nginx {
...
}
[ 115 ]
www.it-ebooks.info
Definitions and Classes
You can also specify some parameters that the class accepts:
class appserver($domain,$database) {
...
}
The exception is the class named after the module (for example, nginx). This should be in
the file modules/nginx/manifests/init.pp.
Declaring classes
There are different ways to declare a class (that is, to create an instance of it and apply it to
the current node) once you've defined it. If you don't need to give the class parameters, the
simplest way is to use include, as we did before:
include nginx
Alternatively, you can use require. This behaves just like include, except it specifies that
everything in the required class must be applied immediately, before Puppet moves on to
the next part of the code:
require nginx
If the class does need parameters, declare it like this (a bit like a resource):
class { 'cluster_node':
role => 'master',
}
[ 116 ]
www.it-ebooks.info
Chapter 7
You can include the same class from several different places, and Puppet won't mind. But
you can only use a resource-like declaration once (because resources have to be unique).
Well, there is some overlap between them. Both classes and definitions bundle a group of
different resources into a single named entity that you can create instances of, with some
optional parameters. In older versions of Puppet, classes didn't take parameters, which made
the two types more distinct.
However, there are important differences. Classes are singletons; that is, Puppet only allows
one instance of a class to exist on a node at a time.
This can be very useful when the class has system-wide effects (installing Nginx, for example)
and you want to prevent it from being used multiple times. If you had two Nginx classes,
each specifying a different version of Nginx, that could cause problems.
Definitions, by contrast, can have as many instances as you like. We saw this earlier when we
created multiple websites on the same machine using the nginx::website definition.
Will you need to have multiple instances of this on the same node (for example,
a website)? If so, use a definition.
Could this cause conflicts with other instances of the same thing on this node
(for example, a web server)? If so, use a class.
[ 117 ]
www.it-ebooks.info
Definitions and Classes
file { '/etc/ntp.conf':
content => template('ntp/ntp.conf.erb'),
notify => Service['ntp'],
}
service { 'ntp':
ensure => running,
enable => true,
require => [ Package['ntp'], File['/etc/ntp.conf'] ],
}
}
[ 118 ]
www.it-ebooks.info
Chapter 7
5. Run Puppet:
ubuntu@demo:~/puppet$ papply
Notice: /Stage[main]/Ntp/Package[ntp]/ensure: created
Notice: /Stage[main]/Ntp/File[/etc/ntp.conf]/ensure: defined
content as '{md5}65e3b66fbf63d0c6c667179b5d0c5216'
Notice: /Stage[main]/Ntp/Service[ntp]: Triggered 'refresh' from 1
events
Notice: Finished catalog run in 4.99 seconds
The class takes one parameter, server, with a default value of UNSET (so the parameter is
optional). It's a good idea to set your default values to something like UNSET, which makes it
very obvious that a value hasn't been provided, rather than using an empty string.
This means that if the value of $server is UNSET, everything between the <% if -%> and
<% end -%> tags will be ignored, and the file will contain only the default NTP settings.
[ 119 ]
www.it-ebooks.info
Definitions and Classes
If $server is anything other than UNSET, a line like this will be added to the file:
server us.pool.ntp.org prefer
Note that this depends on both the ntp package (we can hardly start the service until the
software's installed) and the ntp.conf file. As we saw in Chapter 3, Packages, Files, and
Services, require implies notify, so if the ntp.conf file is changed later on, the service
will be restarted to pick up the changes.
Summary
A quick rundown of what we've learned in this chapter.
Arrays
You can refer to or declare a number of identical resources concisely by giving them
as an array:
package { [ 'php5-cli', 'php5-fpm', 'php-pear' ]:
ensure => installed,
}
Definitions
You can group together resources of any type by using the define keyword to
create a definition:
define script_job() {
RESOURCE1
RESOURCE2
...
}
[ 120 ]
www.it-ebooks.info
Chapter 7
You create an instance of a definition by declaring it just as though it were a built-in resource:
script_job { 'backup_database': }
Definitions can take parameters, if you specify them in () after the definition name:
...
You can make these parameters optional by giving default values for them:
define script_job( $hour = '00', $minute = '00' ) {
...
}
To pass parameters to the definition, specify them just like normal resource attributes:
script_job { 'backup_database':
hour => '05',
minute => '30',
}
Classes
Classes are like definitions, and you introduce them with the class keyword:
class nginx::loadbalancer {
If the class takes no parameters, you can use the include or require keywords to create
an instance of the class on a node:
include postfix
require loadbalancer::nginx
If the class takes parameters, you use the class keyword to instantiate it, but in a
resource-like way:
class { 'cluster_node':
role => 'master',
}
You can include or require the same class in many different places without a problem,
but if the class takes parameters this isn't the case. There can only be one instance of a
parameterized class on each node. This makes parameterized classes more suitable for
things that make system-wide changes that could potentially conflict with other instances
of the same class.
[ 121 ]
www.it-ebooks.info
www.it-ebooks.info
Expressions and Logic
8
A young man should read five hours in a day, and so may acquire a great deal
of knowledge.
— Samuel Johnson
In this chapter, you'll learn how to make choices in your Puppet manifests, how to do
arithmetic, logic, and string operations in the Puppet language, and how to use regular
expressions to match patterns in strings. You'll also find out about some useful Puppet
data types: arrays and hashes.
$eggs = 61
.80
$bacon = 1
$eggs
?
+$bacon =
$#!?
Conditionals
It's useful to be able to do different things in a manifest depending on the value of some
variable or expression. Puppet provides several ways to do this. The first is the if statement.
www.it-ebooks.info
Expressions and Logic
If statements
An if statement has the following form:
if EXPRESSION {
OPTIONAL_SOMETHING
}
The part of the manifest represented by OPTIONAL_SOMETHING will only be applied if the
value of EXPRESSION is true. We'll learn more about expressions later in the chapter, but for
now let's take a simple example:
if $eggs == 61 {
notify { 'Glory be, eggs have just gone up to 61¢ a dozen!': }
}
Puppet reads the expression $eggs == 61, and decides whether it evaluates to true or
false. If the variable $eggs does have the value 61, the expression will be true, and if it
doesn't, it will be false.
If the expression is true, Puppet will apply everything inside the braces:
notify { 'Glory be, eggs have just gone up to 61¢ a dozen!': }
If the expression is false, Puppet will simply skip the contents of the braces and proceed to
the next part of the manifest. So if is called a conditional statement; it makes part of the
manifest conditional on some expression being true.
[ 124 ]
www.it-ebooks.info
Chapter 8
The contents of the else branch will only be applied if the condition is not true.
To build up more complex conditional statements, you can use elsif to add more tests.
Puppet will try them in sequence:
if $::processorcount >= 16 {
include cpu_intensive_application
} elsif $::processorcount >= 4 {
include medium_application
} else {
include lightweight_application
}
As you can see, the extended form of the if statement looks like this:
if EXPRESSION {
OPTIONAL_SOMETHING
} elsif ANOTHER_EXPRESSION {
OPTIONAL_SOMETHING_ELSE
} else {
OPTIONAL_OTHER_THING
}
You can have as many elsif branches as you want; Puppet will test each of the conditions in
order, and if none of them matches the else branch (if there is one) will be applied instead.
Unless statements
As you might imagine, unless is like if, but with the opposite sense. The block is not
applied if the expression is true. An unless statement has this form:
unless EXPRESSION {
OPTIONAL_SOMETHING
}
Again, EXPRESSION is a logical expression (one that can evaluate to true or false).
This time, the OPTIONAL_SOMETHING is only applied if EXPRESSION is false.
You can't use elsif or else with unless; Puppet treats this as a syntax error.
Case statements
If you just have one or two choices to make, the if statement is ideal. However, if you need
to choose between several alternatives, it becomes awkward to write:
if $::operatingsystem == 'Ubuntu' {
include os_specific::ubuntu
[ 125 ]
www.it-ebooks.info
Expressions and Logic
case takes an expression and tries to match it against a list of values (the cases). If one
matches, Puppet will apply the corresponding code block. If there is a default case, this
will be applied if none of the other cases match.
Note that EXPRESSION can be any expression; it's not restricted to logical expressions as the
if and unless statements are.
The code blocks BLOCK1, BLOCK2, and BLOCK3 can be any Puppet code, though it's a good
idea to keep the blocks short enough so that you can see the whole case statement at once.
If you need to have a lot of code in the blocks, you can use include to apply classes you've
defined somewhere else.
Puppet will apply only the first case that matches, and ignore any subsequent ones, so if it's
possible for there to be multiple matches you should list them in order of preference. The
default case must always come at the end.
[ 126 ]
www.it-ebooks.info
Chapter 8
This will halt Puppet with the error message you specify.
This makes it clear to anyone reading your code that no action is needed if none of the cases
match. It's a good principle of programming that "explicit is better than implicit."
Selectors
Sometimes you want to choose between a number of different values depending on the
result of some expression. You could do it with a case statement that sets a variable:
case $::operatingsystem {
'Ubuntu': {
$os_type = 'Debianlike'
}
'RedHat': {
$os_type = 'Redhatlike'
}
'Darwin': {
$os_type = 'Mac OS'
}
default: {
$os_type = 'UNKNOWN'
}
}
notify { "You're running a ${os_type} system": }
[ 127 ]
www.it-ebooks.info
Expressions and Logic
But this is a bit tedious and repetitive. For situations like this, Puppet provides the selector,
which is like a case statement, but instead of matching a case and applying a code block, it
matches a case and returns a value.
$os_type = $::operatingsystem ? {
'Ubuntu' => 'Debianlike',
'RedHat' => 'Redhatlike',
'Darwin' => 'Mac OS',
default => 'UNKNOWN',
}
notify { "You're running a ${os_type} system": }
As with a case statement, Puppet goes through the list in order, returning the first match it
finds. If nothing matches, the value for default is returned.
You can use a selector anywhere that expects a value, but it's good style to assign the value
of a selector to a variable, and then use that variable, as in the $os_type example.
Expressions
It's time to look at expressions in a little more detail, and see what kind of expressions
Puppet allows us to construct.
Comparisons
An important kind of expression is the comparison expression. This compares two values,
and the expression is true or false depending on the result of the comparison.
Equality
We've already seen an expression involving a comparison of two values:
$eggs == '61'
And we know the == operator means "is equal to." Its opposite is the != operator
(not equal to):
$username != 'FOTHERINGTON-THOMAS'
Comparison expressions like these are logical expressions; their value is either true or false.
By the way, true and false are reserved words in Puppet that stand for these logical
values. You can use them like any other literal values:
$raining = true
if $raining {
include umbrella
}
[ 128 ]
www.it-ebooks.info
Chapter 8
Magnitude
You can also compare values using the following operators:
However, their operands (the values they work on) can only be numbers. If you try to say
something as follows:
if $eggs > 'TALBOT?' {
...
}
Puppet will not self-destruct like a computer in a bad sci-fi movie, but it will complain:
Error: comparison of Fixnum with String failed
Substrings
Similarly, there is another comparison operator that only works with string operands, in.
if 'eggs' in 'Can you believe the price of eggs?' {
...
}
in tests whether the first operand is a substring of the other. For example, this expression
is true:
'spring' in 'springfield'
[ 129 ]
www.it-ebooks.info
Expressions and Logic
Boolean operators
Boolean, or logical, operators work on logical values (things that evaluate to true or false).
You can use them to build up more complex expressions from simpler components. For
example, the and operator takes two logical expressions as operands:
$eggs > 61 and $eggs < 100
This expression is true if both operands are true. So the expression will be true if $eggs is
both greater than 61 and less than 100.
If one or both of the operands is false, the and expression will also be false.
$eggs > 61 and $eggs < 100
The preceding expression will be false if $eggs is 120. Although $eggs > 61 is true, $eggs
< 100 is false, so the and expression evaluates to false.
The or operator is a little more forgiving. It is true if either (or both) of its operands is true:
$eggs > 61 or $today == 'Thursday'
The ! (not) operator takes only one operand, and flips its value. If $raining is true, then !
$raining is false, and vice versa.
Arithmetic operators
Puppet's arithmetic operators all work with numeric operands, and the value of an
arithmetic expression is always a number (so you can't use it as a test in a conditional
statement, for example). You can use the following familiar operators:
+ (addition)
- (subtraction)
* (multiplication)
/ (division)
[ 130 ]
www.it-ebooks.info
Chapter 8
There are also two bitwise shift operators, << and >>, which multiply and divide integers by
powers of 2.
$x << $y
Regular expressions
We've seen a couple of different ways of testing string values already. You can compare
strings for equality:
if $role == 'webserver' {
...
}
But what if you want to test for patterns of characters? Say, app followed by any characters,
followed by staging. Puppet has a special pattern-matching language you can use for this:
if $::hostname =~ /app.*staging/ {
...
}
This expression will be true if $::hostname is any of the following, and many more:
app_staging
app-1-staging
application_staging
appstaging
my_app_staging_server
[ 131 ]
www.it-ebooks.info
Expressions and Logic
This kind of pattern is called a regular expression, or regex for short, and Puppet uses the
slash character (/) to mark the start and end of regular expressions.
Operators
The operator which tests whether a string matches a regex, as in the previous example,
is the regex match operator, =~:
VALUE =~ /REGEX/
The operator with the opposite sense is the regex non-match operator, !~:
VALUE !~ /REGEX/
Syntax
The simplest regular expression is just the literal string that you want to match:
$animal =~ /cat/
The preceding expression matches cat, cot, cut, crt, and so on. To match from zero to any
number of wildcard characters, use .*:
/c.*t/
The preceding expression matches ct, cat, count, constitutionalist, and so on.
To match digits only, use the sequence \d:
/app\d*/
The preceding expression matches app, app1, app200, app99999, and so on.
[ 132 ]
www.it-ebooks.info
Chapter 8
Conditionals
As we've seen, expressions involving regexes are very useful as the test in a
conditional statement:
if VALUE =~ /REGEX/ {
DO_SOMETHING
}
So you can use regular expressions with if and unless statements, but you can also use
them as cases in case statements:
case $::ec2_placement_availability_zone {
/us-.*/: { notify { 'In United States': } }
/eu-.*/: { notify { 'In Europe': } }
default: { notify { 'Some other region': } }
}
Capture variables
If you need to refer to the actual text that was matched, it will be available in the special
variable called $0. This is called a capture variable. Within the scope of the conditional
statement, you can refer to $0 to get the string that the regex successfully matched, if any:
$uname = generate('/bin/uname','-a')
if $uname =~ /\d+\.\d+\.\d+/ {
notify { "I have kernel version ${0}": }
}
The output of the command uname -a on a Linux server usually looks something like this:
Linux demo 3.2.0-29-virtual #46-Ubuntu SMP Fri Jul 27 17:23:50 UTC 2012
x86_64 x86_64 x86_64 GNU/Linux
[ 133 ]
www.it-ebooks.info
Expressions and Logic
So the code above looks for a string that matches the regular expression:
/\d+\.\d+\.\d+/
This expression matches three numbers separated by periods. In this case the match
text will be:
3.2.0
You can also capture smaller parts of the regular expression, by putting it in parentheses,
like this:
/abc(def)ghi/
The value of anything matched by the characters in parentheses will be available as $1:
$uname = generate('/bin/uname','-a')
if $uname =~ /(\d+)\.\d+\.\d+/ {
notify { "I have kernel version ${0}, major version ${1}": }
}
You can use more than one set of parentheses, and the values for each will be available as
$1, $2, and so on.
These capture variables are only good within the block of the conditional expression, so use
them or lose them. If you need to preserve one of these values for later, you can assign it to
a regular variable:
if $uname =~ /(\d+)\.\d+\.\d+/ {
$major_version = $1
}
Substitutions
Sometimes it's handy to be able to search and replace text within strings. Puppet gives you
this capability with the regsubst function, which matches text with a regular expression
and replaces it with the value you specify:
regsubst(STRING, REGEX, REPLACEMENT)
[ 134 ]
www.it-ebooks.info
Chapter 8
The STRING argument is the input. The REGEX is what you want to match in the input.
The REPLACEMENT is what you want to replace any matched text with. For example:
$output = regsubst('Look at my cat picture', 'cat', 'dog')
notify { $output: }
The regex can simply be a literal string, as in this example, or it can be more complicated:
$output = regsubst('Look at my cat picture','my .* picture','something
more interesting')
notify { $output: }
You can also use capture variables, as in conditional statements. Here, the contents of
successive capture variables are named \1, \2, and so on.
$output = regsubst('Look at my cat picture','my (.*) picture','this
adorable \1')
notify { $output: }
There are a few syntax differences when using regular expressions with
regsubst. Instead of putting the regular expression within slashes (/
REGEX/) you use quotes ('REGEX'). And as we just saw, the capture
variables are named \1, \2, \3 instead of $1, $2, $3. The makers of Puppet
put these little differences in to make sure you're paying attention.
Node definitions
A handy place to use regular expressions is in node definitions. You can apply a node
definition not merely to a hostname:
node 'demo' {
...
}
[ 135 ]
www.it-ebooks.info
Expressions and Logic
Or to a list of hostnames:
node 'demo1', 'demo2', 'demo3' {
...
}
You can also apply a node definition to hostnames matching a regular expression:
node /demo.*/ {
...
}
This is very useful when you have a number of otherwise identical servers whose hostnames
match some pattern:
node /web.*/ {
include webserver
}
node /app.*/ {
include appserver
}
node /db.*/ {
include dbserver
}
Node definitions don't support capture variables, so you can't capture the
matched text and use it inside the node definition as you can in a conditional
statement. If you want to capture some part of the hostname, you can do
this with regsubst and the $::hostname fact.
[ 136 ]
www.it-ebooks.info
Chapter 8
To make an array, all you need to do is put square brackets round it:
['jerry', 'george', 'elaine']
If you use an array in the context where a resource name is expected, this has the effect of
declaring a resource for each member of the array:
$developers = ['jerry', 'george', 'elaine']
notify { $developers: }
This is why the trick of declaring an array of package names works: it declares a package
resource for each member of the array.
However, this doesn't work if the array is interpolated into a string. In that case, the
members of the array are all simply clumped together in the string:
$developers = ['jerry', 'george', 'elaine']
notify { "The developers are: $developers": }
The elements are numbered from 0 upwards, with 0 being the first element, 1 the second,
and so on. If this seems odd to you, you can always refer to element 0 as the zeroth element
instead. Computer scientists and mathematicians will understand you perfectly.
You can also number elements backwards from the end of the array. For example, the last
element of an array is element [-1]:
$developers = ['jerry', 'george', 'elaine']
notify { "The last developer is: ${developers[-1]}": }
[ 137 ]
www.it-ebooks.info
Expressions and Logic
You can also use the in operator to test if some value is a member of an array:
if $crewmember in ['Frank', 'Dave'] {
notify { "I'm sorry, ${crewmember}. I'm afraid I can't do that.": }
}
Hashes
A hash is a set of pairs of elements. The first member of each pair is called the key, and the
second is the value. Here's an example:
$interfaces = {
'lo0' => '127.0.0.1',
'eth0' => '192.168.0.1',
}
You can think of a hash as being like an array, but instead of looking up elements by number,
you look them up by name (the key):
$address = $interfaces['eth0']
notify { "Interface eth0 has address ${address}": }
The key must be a string, but the value can be any data type:
$contrived_example = {
'fish' => 'babel',
'answer' => 42,
'crew' => ['Ford Prefect', 'Arthur Dent'],
'hash' => { 'Warning' => 'Beware of the leopard' }
}
Multilevel hashes
As you can see, the value can be a string, a number, an array, or even another hash. This
means you can construct multilevel hashes, where you use a series of increasingly specific
keys to get what you want. For example:
[ 138 ]
www.it-ebooks.info
Chapter 8
$interfaces = {
'lo0' => {
'address' => '127.0.0.1',
'netmask' => '255.0.0.0',
},
'eth0' => {
'address' => '192.168.0.1',
'netmask' => '255.255.255.0',
}
}
$eth0_netmask = $interfaces['eth0']['netmask']
notify { "eth0 has netmask ${eth0_netmask}": }
Summary
A quick rundown of what we've learned in this chapter.
Conditionals
You can conditionally apply a block of Puppet code using an if statement:
if EXPRESSION {
OPTIONAL_SOMETHING
}
You can add extra elsif clauses and an optional else clause:
if EXPRESSION {
OPTIONAL_SOMETHING
} elsif ANOTHER_EXPRESSION {
[ 139 ]
www.it-ebooks.info
Expressions and Logic
OPTIONAL_SOMETHING_ELSE
} else {
OPTIONAL_OTHER_THING
}
The else clause, if present, will be applied if none of the conditions match.
The case statement lets you conditionally apply code if any of a number of possible cases
are matched:
case EXPRESSION {
CASE1 { BLOCK1 }
CASE2 { BLOCK2 }
CASE3 { BLOCK3 }
...
default : { ... }
}
With a selector, you can test a number of cases and return a value:
$result = EXPRESSION ? {
CASE1 => VALUE1,
CASE2 => VALUE2,
CASE3 => VALUE3,
default => DEFAULT_VALUE,
}
Operators
You can build expressions using different kinds of operators:
Regular expressions
You can use regular expressions to match patterns of characters:
if $::hostname =~ /app.*staging/ {
[ 140 ]
www.it-ebooks.info
Chapter 8
Regular expressions can also be the cases for selectors and case statements:
case $::ec2_placement_availability_zone {
/us-.*/: { notify { 'In United States': } }
...
}
$ec2_family = $::ec2_instance_type ? {
/t1/ => 'micro',
...
}
The text matched by a regular expression, or part of a regular expression grouped with
parentheses, is available in the capture variables $0, $1, $2, and so on.
if $uname =~ /(\d+)\.\d+\.\d+/ {
notify { "I have kernel version ${0}, major version ${1}": }
}
Text substitution
To substitute text in strings, use the regsubst function with a suitable regular expression:
$output = regsubst('Look at my cat picture','my (.*) picture','this
adorable \1')
notify { $output: }
Arrays
Arrays are sets of values surrounded by square brackets:
['jerry', 'george', 'elaine']
[ 141 ]
www.it-ebooks.info
Expressions and Logic
To look up an element in an array by number (starting from zero), use square brackets after
the array name:
$developers[0]
Hashes
A hash is a set of key/value pairs grouped inside curly braces:
$interfaces = {
'lo0' => '127.0.0.1',
'eth0' => '192.168.0.1',
}
You look up hash values with a string key in square brackets after the hash name:
$address = $interfaces['eth0']
Hash keys must be strings, but hash values can be strings, numbers, arrays, or other hashes:
$interfaces = {
'lo0' => {
'address' => '127.0.0.1',
'netmask' => '255.0.0.0',
},
'eth0' => {
'address' => '192.168.0.1',
'netmask' => '255.255.255.0',
}
}
[ 142 ]
www.it-ebooks.info
Reporting and troubleshooting
9
Often, the most important piece of information is that something has
gone wrong.
In this chapter, you'll learn how to get information on what Puppet's doing, when it runs,
the changes it makes, how to monitor Puppet, and what to do about many common errors
you may encounter.
UH-OH...
BASIC SOLDERING
ELECTRONICS
FOR DUMMIES
www.it-ebooks.info
Reporting and troubleshooting
Reporting
Most of the time you'll probably be happy for Puppet to just run and do its job. In some
situations, however, it can be very useful to have Puppet record information about exactly
what it did and when it did it. This facility in Puppet is called reporting.
For example, if something is not working as you expected, you can look at Puppet's reports
and get a very detailed picture of what's going on. Or you might want to monitor what
Puppet is doing across your whole network and record performance information over time.
You can also see if Puppet runs are failing, and diagnose the reason.
Summary reports
You can get a quick overview of what Puppet is doing on a given run by using the
--summarize flag to puppet apply. It will report some overall statistics on timing and
resources changed:
ubuntu@demo:~/puppet$ papply --summarize
Notice: /Stage[main]//Node[demo]/File[/tmp/test]/ensure: defined content
as '{md5}5d41402abc4b2a76b9719d911017c592'
Notice: Finished catalog run in 0.06 seconds
Changes:
Total: 1
Events:
Success: 1
Total: 1
Resources:
Changed: 1
Out of sync: 1
Skipped: 6
Total: 9
Time:
Filebucket: 0.00
File: 0.00
Config retrieval: 0.15
Total: 0.16
Last run: 1360157807
Version:
Config: 1360157805
Puppet: 3.0.2
[ 144 ]
www.it-ebooks.info
Chapter 9
This can be helpful if you want to make sure that Puppet is doing what you think it should.
However, if you need more information, especially about changes to specific resources, you'll
need to enable full reports. We'll see how to do this in the next section.
Enabling reports
Reporting is enabled in the Ubuntu Puppet package by default, but if you're using another
distribution or installing Puppet from another source, this may not be the case. To check
your setting, run the following command:
ubuntu@demo:~/puppet$ sudo puppet config print report
true
If the setting is false, and you want to enable reporting, edit the file /etc/puppet/
puppet.conf and add the following setting:
[main]
report=true
What's in a report?
Puppet produces detailed reports every time it runs, recording the following:
[ 145 ]
www.it-ebooks.info
Reporting and troubleshooting
Let's look at an example. We'll have Puppet make a change to a resource, and then examine
the resulting report.
2. If the result is false, follow the instructions in the Enabling reports section.
3. Edit your manifests/nodes.pp file as follows:
node 'demo' {
file { '/tmp/test':
content => 'Zaphod Beeblebrox, this is a very large drink',
}
}
4. Run Puppet:
ubuntu@demo:~/puppet$ papply
Notice: /Stage[main]//Node[demo]/File[/tmp/test]/content: content
changed '{md5}e705c4d685bf03258eb5ba0dc767905b' to '{md5}
aea5a3708af83f6e53b4b391b469ae44'
Notice: Finished catalog run in 0.11 seconds
5. Find the report file generated by Puppet. First, check where Puppet is configured to
write its reports (the default location on Ubuntu is /var/lib/puppet/reports):
ubuntu@demo:~/puppet$ sudo puppet config print reportdir
/var/lib/puppet/reports
8. You should see a directory with the same name as the hostname of your machine:
root@demo:/var/lib/puppet/reports# ls
demo.compute-1.internal
[ 146 ]
www.it-ebooks.info
Chapter 9
11. The file's name is generated from the date and time of the Puppet run, so the name
of your file will be different. Display the contents of the file:
root@demo:/var/lib/puppet/reports/demo.compute-1.internal# less
201302011343.yaml
--- !ruby/object:Puppet::Transaction::Report
status: changed
kind: apply
host: demo.compute-1.internal
configuration_version: 1359726210
...
12. Look for the section relating to the file resource you created:
File[/tmp/test]: !ruby/object:Puppet::Resource::Status
resource: File[/tmp/test]
file: /home/ubuntu/puppet/manifests/nodes.pp
line: 4
evaluation_time: 0.016333
change_count: 1
out_of_sync_count: 1
tags:
- file
- node
- demo
- class
time: 2013-02-01 13:43:32.529725 +00:00
events:
- !ruby/object:Puppet::Transaction::Event
audited: false
[ 147 ]
www.it-ebooks.info
Reporting and troubleshooting
property: ensure
previous_value: !ruby/sym absent
desired_value: !ruby/sym file
historical_value:
message: "defined content as '{md5}
aea5a3708af83f6e53b4b391b469ae44'"
name: !ruby/sym file_created
status: success
time: 2013-02-01 13:43:32.530130 +00:00
out_of_sync: true
changed: true
resource_type: File
title: /tmp/test
skipped: false
failed: false
There will be lots of information in the report, about all the other resources on the machine,
as well as some of Puppet's internal data. However, the part we're interested in at the
moment is the Puppet::Resource::Status section relating to the /tmp/test file:
File[/tmp/test]: !ruby/object:Puppet::Resource::Status
resource: File[/tmp/test]
file: /home/ubuntu/puppet/manifests/nodes.pp
line: 4
evaluation_time: 0.016333
change_count: 1
out_of_sync_count: 1
[ 148 ]
www.it-ebooks.info
Chapter 9
The number of properties that were found to be out of sync with the manifest:
out_of_sync_count: 1
The value requested by the manifest (although we didn't specify ensure => file,
this is implicit for a file resource):
desired_value: !ruby/sym file
[ 149 ]
www.it-ebooks.info
Reporting and troubleshooting
The status field indicates that the configuration of the machine was changed on this
Puppet run. If the Puppet run was successful, but no resources were changed, the status
would be unchanged. If there was an error, the status would be failed.
Using reports
Although you don't often need to see this level of detail about what Puppet's doing, it can be
useful when something's not working right and you need to figure out why.
For example, if you think Puppet should be making a particular change, and it's not
happening, you can use the report to help troubleshoot the problem. Turn on reporting,
run Puppet, and inspect the report as we did in the previous example. Find the resource in
question and you'll be able to see what Puppet thinks it should be, whether it's in sync with
the manifest, and whether there were any failures.
For larger-scale reporting on a whole network of Puppet-managed machines, you can set
up a report server where Puppet will send reports from each machine. These can then
be aggregated and processed, and you can see graphs and results using a tool like Puppet
Dashboard. This is beyond the scope of this book, but you can find out more at:
https://puppetlabs.com/puppet/related-projects/dashboard/
Debug runs
Running Puppet with the --debug flag will not produce as much detail as a report, but still
gives you much more information than a normal Puppet run. For example:
ubuntu@demo:~/puppet$ papply --debug
Debug: importing '/home/ubuntu/puppet/manifests/nodes.pp' in environment
production
Debug: Failed to load library 'selinux' for feature 'selinux'
Debug: Creating default schedules
Debug: Using settings: adding file resource 'graphdir': 'File[/var/lib/
puppet/state/graphs]
:links=>:follow, :backup=>false, :ensure=>:directory, :loglevel=>:debug,
:path=>"/var/lib/puppet/state/graphs"}'
[ 150 ]
www.it-ebooks.info
Chapter 9
...
Notice: Finished catalog run in 0.08 seconds
Debug: Using settings: adding file resource 'rrddir': 'File[/var/
lib/puppet/rrd]{:links=>:follow, :group=>"puppet", :backup=>false,
:ensure=>:directory, :owner=>"puppet", :mode=>"750", :loglevel=>:debug,
:path=>"/var/lib/puppet/rrd"}'
Debug: Finishing transaction 69968312591020
Debug: Received report to process from demo.compute-1.internal
Debug: Processing report from demo.compute-1.internal with processor
Puppet::Reports::Store
Because the --debug flag tells Puppet to output everything it does, this usually produces a
lot of information that isn't interesting, but it may help you in some situations to figure out
why Puppet is doing something it shouldn't, or not doing something it should.
Noop runs
By its very nature, Puppet can produce big changes on a machine in a single run. Depending
on the manifest, it can change or delete files, restart services, drop databases, or do many
other potentially destructive things. So it would be nice to have Puppet tell us what it's going
to do before it does it.
The --noop flag does exactly this. Noop stands for no-operation; in other words, do
everything except actually make changes to the system. This is also sometimes known as
dry-run mode. Let's see an example:
ubuntu@demo:~/puppet$ papply --noop
Notice: /Stage[main]//Node[demo]/File[/tmp/test]/ensure: current_value
absent, should be file (noop)
Notice: Node[demo]: Would have triggered 'refresh' from 1 events
Notice: Class[Main]: Would have triggered 'refresh' from 1 events
Notice: Stage[main]: Would have triggered 'refresh' from 1 events
Notice: Finished catalog run in 0.06 seconds
This is telling us that Puppet has found one resource out of sync:
Notice: /Stage[main]//Node[demo]/File[/tmp/test]/ensure: current_value
absent, should be file (noop)
[ 151 ]
www.it-ebooks.info
Reporting and troubleshooting
The ensure property for the file /tmp/test should be file, but instead it is absent.
In other words, the manifest says there should be a file /tmp/test, but there isn't.
Puppet will fix this by creating the file, when you run Puppet without the --noop flag.
ubuntu@demo:~/puppet$ papply
Notice: /Stage[main]//Node[demo]/File[/tmp/test]/ensure: defined content
as '{md5}aea5a3708af83f6e53b4b391b469ae44'
Notice: Finished catalog run in 0.06 seconds
So dry-run mode is very useful for making sure that Puppet will only make the changes you
expected to see. If you're not sure what would change, or you want to make sure that your
changes won't affect a running service, for example, you can use dry-run mode to find out.
Be warned: dry-run mode doesn't come with any guarantees. It's quite
possible to do a dry run with no errors, but then encounter a problem
running Puppet for real. For example, if the manifest tries to install a
package that doesn't exist in the repository, this will succeed in dry-run
mode, because there's no way for Puppet to know in advance that it won't
work. Similarly, exec resources won't actually be run, so dry-run mode
can't tell you whether they will succeed or fail. Test your critical changes in a
staging environment, rather than relying solely on dry-run mode.
Syntax checking
If you want to make sure there are no syntax errors in your manifest, you can use Puppet's
parser validate command to check this:
Validation mode only attempts to compile the manifest and validate the syntax. It won't
actually apply anything, so you can safely run this command anywhere.
You could run this check manually or via a Git hook, for example, to validate the manifest
before committing it to your repository.
Debug output
When Puppet isn't doing what you expect, it can be very difficult to work out why.
A time-honored debugging technique used by many programmers is to print out
information at different points to show you what's going on.
[ 152 ]
www.it-ebooks.info
Chapter 9
Notify resources
A handy way to do this is to use a notify resource. We've sneaked these into the book
several times so far without explaining what they are. A notify resource simply prints out
its name to the console when you run Puppet:
notify { 'Got this far!': }
A simple message like this can help you figure out whether Puppet is even loading or
applying a particular bit of code. If you want to find out the value of a variable at a certain
point in the manifest, you can interpolate it into a string, like this:
notify { "I think my hostname is ${::hostname}": }
Note that you need double quotes ("like this") around the string or Puppet won't
process it for variables. You'll see an output like this:
ubuntu@demo:~/puppet$ papply
Notice: I think my hostname is demo
Notice: /Stage[main]//Node[demo]/Notify[I think my hostname is demo]/
message: defined 'message' as 'I think my hostname is demo'
Notice: Finished catalog run in 0.06 seconds
Exec output
If you use an exec resource to run a command, and the command fails, Puppet will give you
an error message including the output from the command. For example, if you have an exec
like this:
exec { 'this-will-fail':
command => '/bin/cat /tmp/doesntexist',
}
[ 153 ]
www.it-ebooks.info
Reporting and troubleshooting
As you can see, Puppet not only reports that the command returned a failed exit status:
Error: /bin/cat /tmp/doesntexist returned 1 instead of one of [0]
Very useful! But sometimes the command can succeed and yet whatever was supposed to
happen doesn't happen. We'd like to be able to see the output of the command even though
it didn't return an error. To do this, set the logoutput attribute of the exec to true:
exec { 'this-will-succeed-but-give-us-output-anyway':
command => '/bin/cat /etc/hostname',
logoutput => true,
}
The default value of logoutput is on_failure, which means "only show the command
output if it fails." Setting it to true will always show the command output. If you set it to
false, you'll never see command output even in the case of a failure.
[ 154 ]
www.it-ebooks.info
Chapter 9
Puppet interprets a non-zero exit status as failure, and raises an error. If you want to run a
command that returns a non-zero exit status, but you're happy for Puppet to ignore this, you
can specify the returns attribute for the exec, to tell Puppet what exit status to expect:
exec { 'this-will-fail-but-that-is-ok':
command => '/bin/cat /tmp/doesntexist',
returns => 1,
}
In this case, Puppet will only raise an error if the exit status is something other than 1.
Monitoring
Devops people like to say, "If it's not monitored, it's not in production." By "monitored,"
what we really mean is that some automated system is checking whatever it is, and alerting
you if there's a problem. If your customers know the system is down before you do, then you
don't have effective monitoring.
Puppet has some built-in support for Nagios in particular, and can automatically generate
monitoring checks for hosts and services that you manage in your Puppet manifest. This
requires PuppetDB, a central database that stores information about your nodes. We haven't
space here to go into the details of PuppetDB and stored configuration, but you can find out
more at:
https://puppetlabs.com/blog/introducing-puppetdb-put-your-data-to-
work/
[ 155 ]
www.it-ebooks.info
Reporting and troubleshooting
What to monitor
However you manage your monitoring infrastructure, there are some basic things you will
want to monitor:
You can also monitor Puppet itself. This is especially useful if you are running Puppet
automatically from cron, perhaps using a similar setup to that described in Chapter 4,
Managing Puppet with Git. We'd like to know at least:
This will write the current date and time to the file /tmp/puppet.lastrun, and you can
check this file with your monitoring system. If you run Puppet every 10 minutes, say, then
the timestamp file should be no more than 10 minutes old. Allowing a little time for the
Puppet run itself, which could take up to a few minutes, you might set your monitoring
system to alert you if the file is, say, 15 minutes old.
Did you notice that we've specified backup => false for the puppet.
lastrun file? Normally, Puppet creates a backup copy of any file it changes,
and stores it on the machine in a place called the clientbucket. This can be handy
if you ever accidentally overwrite an important file, and want to retrieve its
original contents. In this case, however, Puppet will be changing the file every
time it runs, and we don't want to waste space storing lots of useless backup
copies. backup => false tells Puppet never to back up this file.
[ 156 ]
www.it-ebooks.info
Chapter 9
The cron job that runs Puppet didn't fire. Maybe it was disabled by someone making
local changes to the machine, who then forgot to re-enable it.
Git wasn't able to pull changes. Maybe the Git server is down, or inaccessible, or
the SSH authentication got messed up. Maybe someone made local changes to the
Puppet repo but then didn't commit them, causing git pull to complain.
Puppet wasn't able to run. Maybe there's a typo in the manifest, or another error
that means the manifest doesn't compile properly.
Whatever the reason, you'll be able to go in and investigate why Puppet isn't running,
and you'll know which machines are potentially out of sync with the manifest.
Staying in sync
Some people don't like to run Puppet regularly on their machines because they worry that
it might change something unexpectedly. In fact, the best way to avoid this is to run Puppet
all the time. Why? Because if you don't do this, when you eventually do run Puppet on a
machine, there will be lots of changes all happening at once, which makes it difficult to
diagnose any problems you may have.
Also, one of the main benefits of using Puppet is that you know your machines are all in
sync with each other and the manifest. If you make a change to the manifest that could
potentially break something on a machine, it's better for you to find out now so you can fix
it. Running Puppet with --noop can help you make sure that your latest changes haven't
caused problems.
It's a good idea, if your budget allows, to set up some staging servers, and make them as
similar as possible to your production systems. You can then test any changes to Puppet,
package versions, configuration, or software releases on the staging servers and eliminate
any problems before rolling out to production.
If you're very risk-averse, you could run Puppet automatically on the staging servers but only
run it manually on the live servers when you need to push out a change.
Errors
The two main kinds of error you're likely to encounter when running Puppet are compilation
errors—errors in the manifest itself, or in template files—and errors from commands
executed by Puppet when applying the manifest. We'll look at these in turn.
[ 157 ]
www.it-ebooks.info
Reporting and troubleshooting
Compilation errors
If you make a typo in the manifest, or some other kind of error, Puppet will usually alert
you when you run puppet apply (or puppet parser validate). It will tell you:
Diagnosing errors
Let's take an example. If we apply a manifest containing a deliberate typo, like this
(can you spot it?):
file { '/tmp/test':
contents => 'Hello, world'
}
We actually should have said content, not contents, and Puppet is quite helpful about
pointing out exactly where the problem is.
Here are some other common errors you might come across, with some hints on what might
cause them.
Instead of:
puppet:///modules/sudoers/sudoers
That is, to put a double slash (//) instead of a triple slash (///) before modules.
We're all used to typing web URLs, which typically have a double slash.
[ 158 ]
www.it-ebooks.info
Chapter 9
The optional HOSTNAME is usually omitted unless you're using a Puppet file server, so the URI
just looks like this:
puppet:///modules/...
If you miss out the third slash, Puppet will think you're trying to specify a HOSTNAME where it
can find the file, and complain:
Error: /Stage[main]//Node[demo]/File[/tmp/test]: Could not evaluate:
getaddrinfo: Name or service not known
Could not retrieve file metadata for puppet://modules/sudoers/sudoers:
getaddrinfo: Name or service not known
If the source URI is correctly formatted, but the source file just doesn't exist (maybe you
forgot to create it), Puppet will say instead:
Error: /Stage[main]//Node[demo]/File[/tmp/test]: Could not evaluate:
Could not retrieve information from environment production source(s)
puppet:///modules/sudoers/sudoers
Puppet requires that the directory /tmp/testdir exist before it can create the file test in
it. If it doesn't, you'll see an error message similar to:
Error: Could not set 'file' on ensure: No such file or directory - /tmp/
testdir/test.puppettmp_236 at 4:/home/ubuntu/puppet/manifests/nodes.pp
You might expect that Puppet would simply create any missing path components for you.
Alas! there are limits to what even a robot butler can do. You have to create the parent
directory as a separate resource:
file { '/tmp/testdir':
ensure => directory,
}
file { '/tmp/testdir/test':
content => 'Hello, world',
}
Puppet is, however, smart enough to figure out the file /tmp/testdir/test depends
on the directory /tmp/testdir being created first, so you don't have to add an explicit
require for this.
[ 159 ]
www.it-ebooks.info
Reporting and troubleshooting
Summary
A quick rundown of what we've learned in this chapter.
Reporting
You can get a summary report of what Puppet did on its run by using the --summarize flag
with puppet apply. For more detailed reporting, enable reports by setting report=true
in /etc/puppet/puppet.conf.
Puppet will write report files to (by default, but you can change this) /var/lib/puppet/
reports, in a directory named after the machine's hostname. Each report file will be named
according to the date and time of the Puppet run it covers.
Puppet's report files include some summary data about the run itself, and how many
resources were found to be out of sync with the manifest. This is followed by a detailed list
of all the resources on the server and the number of properties that were changed or out of
sync for each resource.
If any resource was changed, the report will include details of each property that was
changed, with its previous value and updated value.
You can check your Puppet manifest for compilation errors using the puppet parser
validate command.
[ 160 ]
www.it-ebooks.info
Chapter 9
Printing messages
To print out debugging messages, or other information, use a notify resource, which simply
prints out its name to the console during Puppet's run:
notify { "I think my hostname is ${::hostname}": }
Commands run via exec will print their output if the command returns a failed (non-zero)
exit status. To see the output even if the command succeeds, set the logoutput attribute
to true:
exec { 'this-will-succeed-but-give-us-output-anyway':
command => '/bin/cat /etc/hostname',
logoutput => true,
}
If a command routinely returns a failed exit status, but you're happy for Puppet to ignore
it and carry on, you can specify the exit status that should be expected using the returns
attribute:
exec { 'this-will-fail-but-that-is-ok':
command => '/bin/cat /tmp/doesntexist',
returns => 1,
}
Monitoring Puppet
If you want to be able to monitor whether Puppet is running successfully on a number of
machines, without having to check each one, you can have Puppet write a timestamp file
every time it runs, and check this file with your monitoring system. If the file is not updated
regularly, there may be a problem running Puppet on the system.
Could not retrieve file metadata for XXX: getaddrinfo: Name or service not known
[ 161 ]
www.it-ebooks.info
Reporting and troubleshooting
Could not evaluate: Could not retrieve information from environment production
source(s) XXX
The source file may not be present or in the right location in the Puppet repo.
Error: Could not set 'file' on ensure: No such file or directory XXX
The file path may specify a parent directory (or directories) that doesn't exist. You can use
separate file resources in Puppet to create these.
Could not parse for environment production: Syntax error at end of file
at line 1
You may have mistyped some command line options (particularly, using a single hyphen
instead of a double hyphen).
[ 162 ]
www.it-ebooks.info
Moving on Up
10
There are only two mistakes one can make on the road to truth: not going all
the way and not starting.
— Buddha
In this chapter, you'll learn some simple principles for writing better Puppet manifests, find
some resources for learning more about Puppet, and get some ideas for practical projects
that will help you start putting your Puppet skills to work.
MANAGING
D
ADVANCE HUMANS
PUPPET
www.it-ebooks.info
Moving on Up
Puppet style
Just like everyone else, I want to be a nonconformist, too. But when it comes to programming,
conformity is a virtue. When your code looks the same as everybody else's, it's easy to read,
easy to understand, and easy to maintain. Here are some simple Puppet style tips you can
adopt now to help those who work on your code in the future, including yourself.
For example, if you're writing code that manages a particular customer-facing service, such
as a website or an API, that could be a module. Similarly, code that manages a specific piece
of software such as Apache, MySQL, or Hadoop should have its own module.
Modules can then be connected together to do useful things; for example, a module to
manage Drupal might use the Apache module, the PHP module, the MySQL module, and
so on. If your modules are well-structured, there should be very little duplication of code.
service { 'foo_worker':
ensure => running,
enable => true,
provider => upstart,
require => File['/etc/init/foo_worker.conf'],
}
file{ '/etc/init/bar_worker.conf':
source => 'puppet:///modules/admin/bar_worker.upstart',
mode => '0755',
}
[ 164 ]
www.it-ebooks.info
Chapter 10
service { 'bar_worker':
ensure => running,
enable => true,
provider => upstart,
require => File['/etc/init/bar_worker.conf'],
}
file{ '/etc/init/baz_worker.conf':
source => 'puppet:///modules/admin/baz_worker.upstart',
mode => '0755',
}
service { 'baz_worker':
ensure => running,
enable => true,
provider => upstart,
require => File['/etc/init/baz_worker.conf'],
}
service { "${name}_worker":
ensure => running,
enable => true,
provider => upstart,
require => File["/etc/init/${name}_worker.conf"],
}
}
In other words, identify what is common to several sections of code, and extract that part
into a definition. This not only makes the code easier to understand, but if you need to
modify it later, you only need to change it in one place. It's also more scalable because it's
easy to add another worker_service (or a hundred).
[ 165 ]
www.it-ebooks.info
Moving on Up
Don't take refactoring too far, though; it can overcomplicate your code. Better to have
slightly repetitive code that's easy to understand and extend, than code that's elegant
but difficult to follow.
You can look at this manifest and say, "Ah! web1 is a web server." All the individual resources,
modules, parameters, and other clutter are pushed down into the web server module so that
the node declaration simply says what the box is for.
Another example:
node 'base-server' {
include admin::basics
include user::sysops
include monitoring::target
}
Here, we have a bunch of stuff that is common to all (or most) servers:
include admin::basics
include user::sysops
include monitoring::target
We've extracted this out into a base-server declaration. There's no actual server named
base-server; however, actual servers can inherit from this node declaration and get
everything in it:
node 'cluster660' inherits 'base-server' {
We know what role this machine has because it includes this class:
class { 'hadoop::node':
[ 166 ]
www.it-ebooks.info
Chapter 10
This node declaration contains only the key information that the node needs to do its job,
and tells us at a glance what that job is.
If your node declarations contain business logic, or individual resources, think about
refactoring these into a class or module that the node can include.
Use puppet-lint
puppet-lint is a useful tool that checks your manifest to make sure it conforms to
the Puppet Labs official style guidelines, and catches a number of common problems.
For example, code like this:
node 'demo' {
file { "/tmp/test":
content => 'Hello, world',
mode => 644,
}
}
The preceding code will produce the following output from puppet-lint:
ubuntu@demo:~/puppet$ puppet-lint manifests/nodes.pp
ERROR: trailing whitespace found on line 2
WARNING: unquoted file mode on line 4
WARNING: double quoted string containing no variables on line 2
WARNING: mode should be represented as a 4 digit octal value or symbolic
mode on line 4
WARNING: indentation of => is not properly aligned on line 4
[ 167 ]
www.it-ebooks.info
Moving on Up
To find out more about puppet-lint and to see what tests it runs on your code, visit the
site https://github.com/rodjek/puppet-lint
If you keep your code lint-clean (which is to say, it passes puppet-lint with no errors or
warnings), you can be reasonably confident that it conforms to style guidelines and doesn't
contain any dangerous or deprecated syntax. This will make it easier and safer to upgrade to
new versions of Puppet as they're released.
It also means your code will be easier for others to understand and work on.
There is a tendency to sprinkle comments liberally throughout code, often because it's not
clear what the code is doing or why it's there. Instead, rewrite the code so that no comment
is needed. You can do this by using a simple, logical structure for your code and choosing
descriptive names for things.
Assume that anyone reading your code is familiar with Puppet (or at least as familiar as you
are), so you don't need to explain how the language works:
# This will run a command
exec { 'do-the-stuff':
...
}
If part of your code works by complicated magic, which you feel needs explanation in
comments, simply remove the magic, and rewrite the code in a simple, obvious way.
Similarly, comments like this are a sign of problems:
# Not sure exactly why this works - DO NOT TOUCH!!
[ 168 ]
www.it-ebooks.info
Chapter 10
There are useful comments, however. A good rule of thumb for comments, as with commit
messages, is "Not what, but why." Why is this piece of code necessary?
# apache2-utils gives us rotatelogs
package { "apache2-utils": ensure => installed }
Reference
It might seem obvious, but one of the best sources of reference documentation for Puppet
is the Puppet Labs site itself. To save you a lot of clicking around, here are the links you'll
probably use the most.
Resource types
One link that I keep bookmarked at all times is the Puppet Type Reference:
http://docs.puppetlabs.com/references/latest/type.html
This lists each of the types of Puppet resources—file, exec, user, and so on—with a
complete description of all the attributes of each resource and what they do. Each resource
also has a breakdown of the features supported by its providers or platforms.
Puppet also has some built-in help on resource types, available via the puppet describe
command. For example:
ubuntu@demo:~/puppet$ puppet describe --list
These are the types known to puppet:
augeas - Apply a change or an array of changes to the ...
computer - Computer object management using DirectorySer ...
cron - Installs and manages cron jobs
exec - Executes external commands
file - Manages files, including their content, owner ...
...
[ 169 ]
www.it-ebooks.info
Moving on Up
Parameters
----------
- **backup**
Whether files should be backed up before
being replaced.
This describes every part of the Puppet language and syntax: variables, classes, data types,
and so on. If you need to check how a particular Puppet construct works, or find out what's
available, this is an excellent place to look.
Facts
For working with Facter, use the Core Facts Reference:
http://docs.puppetlabs.com/facter/latest/core_facts.html
This lists all of the standard facts that you can use to get information about machines,
such as fqdn, memorysize, operatingsystem, and so on.
Style
The official Puppet Labs style guidelines (as implemented by puppet-lint, for example)
are here:
http://docs.puppetlabs.com/guides/style_guide.html
You may not agree with all of the style rules (I'm not crazy about some of them), but there
are advantages to using standard coding style. If your organization's coding style is different,
or you need to break the rules for some other reason, go ahead, but it's always good to know
what rules you're breaking.
[ 170 ]
www.it-ebooks.info
Chapter 10
Puppet Forge
The Puppet Forge is a community repository of Puppet code:
http://forge.puppetlabs.com/
There you can find open source modules for managing things such as Apache, MySQL,
MongoDB, Ganglia, Sphinx, and many others. These can be very useful to look at and get
ideas from. In some cases you may be able to download and use the module directly in your
infrastructure as is; most of the time, you will need to adapt and modify the code a little to
work in your environment.
Be warned that the code on Puppet Forge is of variable quality. Some modules are excellent,
mature, highly portable, well-documented, and up to date. Others aren't. Often it's quickest,
easiest, and best to simply write your own code. This has the additional advantage that you
learn more about Puppet while you're doing it.
A quick way to find out whether there is any Puppet Forge code relevant to what you're
working on is to use the puppet module search command:
ubuntu@demo:~/puppet$ puppet module search memcached
Notice: Searching https://forge.puppetlabs.com ...
NAME DESCRIPTION AUTHOR KEYWORDS
saz-memcached UNKNOWN @saz debian redhat fedora ubuntu memcached
The book is aimed at those who have some familiarity with Puppet (perhaps those who've
worked their way through the Puppet Beginner's Guide) and outlines a number of specific
techniques and recipes for doing things with Puppet:
[ 171 ]
www.it-ebooks.info
Moving on Up
The book contains lots of complete, working code to do all the things above and many more.
As in this book, each piece of code is explained line-by-line so that you can see how it works,
and use the same ideas in your own Puppet code.
It also shows you many of the tips, tricks, ideas, and advanced techniques that I've picked up
over many years of working with Puppet, and that there wasn't room to cover in this book,
such as:
Projects
The best way to learn is by doing, so here are some things you might like to try to do with
Puppet that will improve your skills and your infrastructure at the same time. Most of these
projects are fairly small—a few hours of work, maybe—but each will give you a valuable win
and make your life easier. They provide a series of stepping-stones from your first use of
Puppet to a completely automated environment.
[ 172 ]
www.it-ebooks.info
Chapter 10
Puppet everywhere
Project: First, install Puppet on all the machines you're responsible for. Set up a central Git
repo as described in Chapter 4, Managing Puppet with Git, and have each of the machines
pull from the repo and run Puppet automatically. For now, Puppet won't actually manage
anything, so all your node declarations will look like this:
node 'kermit' {
}
That's fine. Once you've got Puppet everywhere, you can start adding things to it.
Win: It's now easy to add configuration to any machine, simply by putting something in its
node declaration.
User accounts
Project: Create a base node definition that which every machine inherits, as described earlier
in this chapter in the Keep node declarations simple section. To this base node, add your own
user account and SSH key as described in Chapter 5, Managing Users. You probably want to
give yourself full sudo privileges as well. Add any other users who need login access
to machines.
Win: You can now easily log in to every machine using your own named account and key,
and run commands with root privileges using sudo.
System toolbox
Project: Add a set of packages to your base node containing software that you find useful for
system administration: for example, htop, dstat, iptraf, tmux, mosh, vim, and so on. If
you have custom configurations for any of these, add the config files to Puppet.
Win: You now have a well-equipped sysadmin environment on every machine, configured
the way you want it.
Time sync
Project: Use Puppet to add the NTP service to all of your machines and set them to the
UTC time zone. If you have a central NTP server, or your ISP does, configure ntp.conf
to use this.
Win: All server clocks are now in sync and in the same time zone, which prevents a variety
of obscure problems, and makes troubleshooting much easier (you can cross-reference
timestamps in logfiles, for example).
[ 173 ]
www.it-ebooks.info
Moving on Up
Monitoring server
Project: If you don't already have a monitoring server such as Icinga, set one up to monitor
your machines as described in Chapter 9, Reporting and Troubleshooting. You don't have to
automate the install of Icinga for now, but have Puppet manage the list of hosts to monitor
(hosts.cfg for Icinga) and the list of services to check (services.cfg).
Win: You now have automated monitoring and you can see the state of your network at a
glance, including whether any hosts are down. In the future, it'll be easy to add new hosts
and services to your monitoring system.
Your priorities for bringing services under Puppet management should be driven by business
considerations. What service or facility is most critical or earns the most money for your
business? Or, if you're a non-profit, the most business for your business?
Once you've decided on the most important thing to Puppetize, make a list of exactly what
needs to be managed. For example, if it's a website, you might list the following things to be
managed by Puppet:
The list should contain everything you would have to do manually to set up a new server to
serve the website (not including, for example, installing the operating system, since Puppet
requires that, too). You probably also won't include deploying the website itself, unless it
consists of just a few static files. What Puppet needs do to is make the machine ready to
have the site deployed to it, whatever the deployment process is (FTP, Capistrano, shell
scripts, and so on).
Don't attempt to have Puppet manage these things on the existing live server. Instead, set
up a new server and build up the configuration, checking against the live machine as you go
to make sure you have included everything. Then it's easy to know when you're done; when
you can deploy the site to the new machine and it works identically to the live version,
you're done.
You can turn down or repurpose the old machine (keep it around for a little while, though,
just in case there's something you missed).
[ 174 ]
www.it-ebooks.info
Chapter 10
Win: Your key service is under Puppet management, and that service can now be easily and
quickly built on a new server if anything happens to the live one. Also, you have complete
documentation for what's required to run it.
Automate backups
Project: Use Puppet to distribute backup scripts to each machine and run backup jobs
automatically via cron. You should have a local copy of all important data (that is, in a backup
directory on the machine) and an offsite copy of anything that can't easily be reconstructed.
This should be off the machine, off your infrastructure, and out of your ISP's data center
(Amazon S3 is one option).
Monitor the backup jobs with your monitoring server (have the job write a logfile, and you
can monitor that the logfile has been touched, and doesn't contain any error messages).
Use Puppet to build copies of your machines and test restoring the data to them. Write
down the procedure you follow to do this, so that someone else could follow it, and put
the procedure document where it can be easily found. Knowledge of the restore procedure
shouldn't die with you.
Win: Your data (and thus your business) is no longer a hostage to fortune. You needn't just
hope that your hard disks won't fail, or that your ISP won't lose connectivity. In fact, no
sentence that contains the word "hope" is part of a viable operations strategy.
Win: You have a staging environment where you can try out changes (no more committing
and hoping). Also, it's easy to create copies of your live server, for redundancy, load
balancing, or development VMs.
Automate everything
Project: Extend Puppet management to any remaining parts of your infrastructure that still
require manual setup. For any particular machine or service, ask yourself this question:
If I wipe and reinstall this machine, then run Puppet, will it be in production?
If the answer is No, then you still have some work to do. If the answer is Yes, then do the test
to make sure. (It might be wise to use a replacement server rather than wiping the live one
and finding you can't rebuild it.)
[ 175 ]
www.it-ebooks.info
Moving on Up
If there are manual steps that you can't automate or do without (restoring data from a
backup, say), write down a detailed procedure for what has to be done, so that someone
else could follow it. Write down what you need to type, what you'll see, error messages you
might encounter, and so on.
Your written procedures are business-critical software just like your application source code.
Procedures are just software that runs on humans. Write, test, and maintain procedures with
as much care and pride as you do your computer software.
Win: You can spend less time on day-to-day operations matters, such as building and
configuring servers, and you can concentrate on really valuable tasks, such as making your
systems faster, more resilient, and more cost-effective.
You have more time to communicate with your colleagues, instead of computers.
You can make infrastructure changes quickly and safely, making the business more agile.
You have time for training, learning, research, experimentation, and innovation.
You can share your knowledge with others by helping them use Puppet to achieve what
they need to do. In the process, you'll learn expertise from them about their own domains
and specialties.
Last word
System administration can be a rather conservative profession. ("If it ain't broke, don't fix it.")
Worse, some system administrators suffer from an attitude problem. Perhaps they perceive
themselves as undervalued by colleagues, like a kind of digital janitor. Perhaps they're reluctant
to share what they know, for fear of making themselves dispensable. Perhaps they're simply so
overloaded with time-consuming work that their default response is "Go away!"
This can lead to "BOFH": the system administrator as remote, unfriendly, inaccessible,
enforcing unhelpful and bureaucratic policies, rejecting new ideas. The last person, in fact,
you'd want to ask for help with a problem.
Automation tools such as Puppet are a threat to this kind of sysadmin, because she sees
herself as the guardian of the secret technical information about how the systems work.
"Why, if all that information was in Puppet, everyone would be able to see and understand
it, and they could build and manage their own servers! Then I wouldn't be needed!"
[ 176 ]
www.it-ebooks.info
Chapter 10
Obviously, this isn't you, or you wouldn't be reading this book (unless someone bought it for
you and left it on your desk, pointedly highlighting this section). But if you know someone
who fits that description, share this with them:
The more you automate the tedious parts of your job, the more time you have for
the exciting and challenging parts. You know, the ones that need a brain
The more opportunity you have to use your brain, the more you can learn about
and explore new technologies and ideas
The more automated your systems, the more quickly you can deliver new things,
and the more you can be known as the person who solves problems, instead of
creating them
The more you can innovate, improve the status quo, and add value for the business,
the more indispensable you'll be to the business
The more you share your knowledge, teach, and inspire others, the more your
colleagues will value you, and the higher the opinion they'll have of your skills
and expertise. And that won't go unnoticed by whoever signs your paycheck
[ 177 ]
www.it-ebooks.info
www.it-ebooks.info
Index
Symbols C
! $raining 130 capture variable 133, 134
-m switch 56 case statement
--noop flag 151 about 125, 126
! (not) operator 130 default case 127
cat-pictures.com 8
A changes
automatic pulling 67
Advanced Package Tool. See APT pushing, to master repo 65, 66
and expression 130 repeating, across servers 10
APT 34 classes
arbitrary command about 115, 121
running 88 and definition, differentiating 117
architecture 101 declaring 116
arithmetic operators 130, 131 defining 115
arrays putting, into modules 116
about 120, 136, 141 class keyword 39, 115, 117
definitions 120 cloud scaling 16
resources, grouping into 108, 109 code
resources, grouping with 136, 137 breaking, into modules 164
values, getting out 137 refactoring, into definitions 164-166
artisan server crafting 9 command line options 160
attributes 17 commands
automatic pull-and-apply script 67, 68 arbitrary command, running 88, 89
chaining 90
B running selectively 89, 90
running, with exec resources 88
backup
search paths 91
scheduling 92, 93
trigerring 90
base-server declaration 166
comment attribute 74
bitwise shift operators 131
comments 168, 169
BOFH syndrome 176
commit 60
boolean operators
comparison expression 128
combining 130
branching 60
www.it-ebooks.info
comparisons E
equality 128
magnitude 129 else branch 125
substrings 129 elseif statement 124
compilation errors 158 else statement 124
conditionals equality, comparisons 128
about 123, 133, 139 ERB 102
capture variable 133, 134 errors
case statements 125, 126 about 157
If statements 124 command line options, mistyped 160
selectors 127, 128 compilation errors 158
configuration management 8, 18 diagnosing 158
configuration management system (CM system) file sources, missing 158, 159
tools 11, 12, 13 parent directory, missing 159
Core Facts Reference exec output 153, 154
URL 170 exec resources
creates attribute 89 about 103
cron resource 104 commands, running with 88
current working directory. See cwd exit status 155
cwd 89 expression
about 128
D arithmetic operators 130, 131
boolean operators 130
daemons 42
debug output F
about 152
notify resource 153 Facter 101, 170
debug runs 150 facts, puppet manifest
declarative programming language 19 architecture 101
declarative style 17 fqdn 101
default case, case statement 127 hostname 101
definition ipaddress 101
about 110, 117 memorysize 101
common code, refactoring into 164, 165, 166 operatingsystem 101
creating, for Nginx websites 113, 114 files
multiple instances 115 about 46, 52
parameters, passing 111, 112 virtual host, deploying 46-48
directory structure file sources
creating 29 missing 158, 159
DMI images fqdn 101
installing, from Puppet Labs website 25 fully-qualified domain name. See fqdn
documentation
self-updating 10 G
dry-run mode 151, 160
Git
about 68
manifest, importing 55, 56
git whatchanged command 59
[ 180 ]
www.it-ebooks.info
H importing, in Git 55, 56
nodes.pp file, creating 29, 30
hashes organizing 28
about 138, 142 Puppet, applying 26
hash keys, testing 139 reliability 61
multilevel hashes 138 scalability 61
hasstatus 45 simplicity 61
home attribute 74 master Git repo
hostname 101 creating 62, 63
master repo
I changes, pushing to 65, 66
cloning, to new machine 63-65
if statements 124 memorysize 101
infrastructure as code 13 messages
inline_template function 101 printing 161
installing, Puppet modules
prerequisites 22 about 38, 50
steps for 23, 24, 25 code, breaking into 164
ipaddress 101 Nginx module, creating 38, 39
issues monitoring
about 8 about 155
solving 11 managing, with puppet 155
puppet 161
J puppet status 156
jen user 17 MSI images
jobs installing, from Puppet Labs website 25
running, as specified user 94 multilevel hash 138, 142
running, at regular intervals 94
scheduled 104 N
Nginx
K installing 34
key 138 nginx class 116
Nginx module
creating 38, 39
L Nginx service
logical expression 128 adding 41
Nginx websites
M definition, creating for 113, 114
node
magnitude, comparisons 129 adding 65
manifest declarations 166, 167
about 18, 26, 31 node declaration
applying 27 about 29, 32
changing 56-59 creating 30
directory structure, creating 29 node definitions 135, 136
distributing 61
existing files, modifying 28
[ 181 ]
www.it-ebooks.info
nodes.pp file puppet
creating 29, 30 about 7, 16
no-operation 151 advantages 14, 19
noop runs 151 code, breaking into modules 164
notify 49 existing files, modifying 28
notify resource 153 features 72, 73
NTP class installing, prerequisites for 22
creating 117-120 installing, steps for 23-25
issues 157, 161
O language 16-19
learning, resources 169
onlyif attribute 89 manifest file, applying 27
operands 129 manifest file, creating 26
operatingsystem 101 manifests, organizing 28
operators 140 packages 34
OPTIONAL_SOMETHING 124 monitoring, managing 155
or operator 130 resources 17, 18
scaling 19
P style 164
user, creating 73, 74
packages
uses 18
about 34
puppet apply command
Nginx, installing 34
creating 40
removing 37, 50
Puppet Cookbook
specific versions, installing 36
URL 171
updating 37
PuppetDB 155
parameters
Puppet Forge
about 121
URL 171
optional 112
Puppet Labs style guidelines
passing, to definition 111, 112
URL 170
parent directory 159
Puppet Language Reference
private key 75
URL 170
procedural style 17
puppet learning, resources
projects
facts 170
about 172
language and syntax 170
automate everything 175, 176
modules and code 171
backups, automating 175
Puppet Cookbook 171, 172
key services, puppetizing 174
Puppet Forge 171
monitoring server 174
reference 169
Puppet everywhere 173
types 169
staging servers, setting up 175
puppet-lint 168
system toolbox 173
Puppetmaster 48, 69
time sync 173
Puppet status
user accounts 173
monitoring 156
public key 75
Puppet Type Reference
pull-updates script 68
URL 169
[ 182 ]
www.it-ebooks.info
R selectors 127, 128
service pattern 49
recursive file resource services
using 95, 96 about 41, 44, 51
refactoring control commands 51
about 39 Nginx service, adding 41, 42
common code, into definition 164-166 restarting 46
refreshonly attribute 90, 103 starting 46, 51
regex. See regular expression starting, at boot time 44
regsubst function 141 status, options 51
regular expression stopping 46
about 131, 140 with no support status 45
conditionals 133 singletons 117
node definitions 135, 136 SSH
operators 132 about 75
substitutions 134, 135 authorized key, adding 76, 77
syntax 132 configuration file, deploying 79, 80
reporting 144, 160 configuration, managing 79
reports configuring 84
about 144 keys, generating 78
enabling 145 keys, managing 75, 84
generating 146-148 special-purpose keys 78
summary reports 144 SSH authentication 74
using 150 ssh module 79
repository 55 STRING argument 135
require attribute 42, 43 substitutions 134, 135
resource-like way 121 substrings, comparisons 129
resources sudo
about 17, 18, 43, 44 privileges, managing with 85
dependencies 51 sudo command 81
for learning puppet 169, 170 sudoers file
grouping 109, 110 deploying 81
grouping, into arrays 108, 109 syntax 81
grouping, with arrays 136, 137 syntax
root account 72 checking 152
Ruby regular expression syntax sysadmin
URL 132 job satisfaction 14
Run my arbitrary command 88 tasks 8, 9
system administration. See sysadmin
S
scheduling
T
about 92 tasks
backup 92 scheduling 92
options 94 template
security and access control 72 about 105
inline templates 101
[ 183 ]
www.it-ebooks.info
Nginx virtual host 97, 98 removing 84
using 97 resources 83
template function 100 security and access control 72
text substitution 141 user privileges
about 80
U sudo command 81
sudoers file, deploying 81
unless attribute 89 user resource 74
unless command 90
unless statement 125 V
user
about 72 value 138
accounts, locking 78, 84 version control 11, 54, 69
accounts, removing 74 visudo command 82
creating 73, 74
[ 184 ]
www.it-ebooks.info
Thank you for buying
Puppet 3 Beginner’s Guide
www.it-ebooks.info
Puppet 2.7 Cookbook
ISBN: 978-1-84951-538-2 Paperback: 300 pages
www.it-ebooks.info
OpenStack Cloud Computing Cookbook
ISBN: 978-1-84951-732-4 Paperback: 318 pages
www.it-ebooks.info
www.it-ebooks.info