13 Puppet
13 Puppet
Puppet
1. What is Puppet?
Puppet follows client-server Model, where one machine acts as server known as puppet master and
the other acts as client known as slave or agent machine.
The Puppet Master is a Machine where all manifests will be developed and ready to be
implemented on the agents.
The agent implements Puppet manifests, or files containing Puppet configuration language that
declare the desired state of the node.
in Puppet, one can safely run the same set of configuration multiple times on the same machine. In
this flow, Puppet checks for the status of the target machine and will only make changes when there
is any specific change in the configuration.
Puppet Master
Puppet Master is the key mechanism which handles all the configuration related stuff. It
applies the configuration to nodes using the Puppet agent.
Catalog
All the configuration changes which are written in Puppet are first converted to a
compiled format called catalog and later those catalogs are applied on the target machine.
The first thing that Puppet master does is to collect the details of
The Puppet agent. Using the factor which is present on all Puppet agents it gets all the machine
level configuration details. All these details are
gathered and sent back to the Puppet master.
Then the puppet master compares the gathered configuration with defined
configuration details, and with the defined configuration it creates a catalog and
sends it to the targeted Puppet nodes.
The Puppet node applies those configurations to get the system into the desired
state.
After puppet agent changing to desired state, that node sends a report back to
the Puppet master. This helps the Puppet master in understanding where the
current state of the system is, as defined in the catalog.
2. INSTALLING PUPPET
Prerequisites
Before we get started with installing Puppet, ensure that you have the following prerequisites:
• Private Network DNS: Forward and reverse DNS must be configured, and every server
must have a unique hostname. If you do not have DNS configured, you must use your hosts
file for name resolution.
• Firewall Open Ports: The Puppet master must be reachable on port 8140.
• Install NTP:Because it acts as a certificate authority for agent nodes, the puppet master
server must maintain accurate system time to avoid potential problems when it issues agent
certificates--certificates can appear to be expired if there are time discrepancies. We will use
Network Time Protocol (NTP) for this purpose.
After installing Puppet server, we can check the file structure as seen below.
Memory Allocation
By default, Puppet Server will be configured to use 2GB of RAM. However, if you want to
experiment with Puppet Server on a VM, you can safely allocate as little as 512MB of memory. To
change the Puppet Server memory allocation:
Open /etc/sysconfig/puppetserver and modify these settings:
# Modify this if you'd like to change the memory allocation, enable JMX, etc
JAVA_ARGS="-Xms2g -Xmx2g"
Replace 2g with the amount of memory you want to allocate to Puppet Server. For example, to
allocate 1GB of memory, use JAVA_ARGS="-Xms1g -Xmx1g"; for 512MB, use
JAVA_ARGS="-Xms512m -Xmx512m".
For more information about the recommended settings for the JVM, please see Oracle’s docs on
JVM tuning.
Restart the puppetserver service after making any changes to this file.
Main: This is known as the global section which is used by all the commands and
services in Puppet. One defines the default values in the main section which can
be overridden by any section present in puppet.conf file.
Master: This section is referred by Puppet master service and Puppet cert command.
User: It is mostly used by Puppet apply command as well as many of the less common commands.
Following are the sample examples for config sections on both puppet master and agents.
[master]
dns_alt_names = puppetmaster01,puppetmaster01.example.com,puppet,puppet.example.com
reports = puppetdb
storeconfigs_backend = puppetdb
storeconfigs = true
environment_timeout = unlimited
[main]
certname = puppetmaster
vi /etc/puppet/puppet.conf
[agent]
server = puppetmaster
The above commands update the puppet master and agent info in the configuration
When the Puppet agent software runs for the first time on any Puppet node, it generates
a certificate and sends the certificate signing request to the Puppet master.
The below command from agent will request for the certificate from the master.
Before the Puppet server is able to communicate and control the agent nodes, it must sign that
particular agent node’s certificate. In the following sections, we will describe how to sign
and check for the signing request.
On the Puppet master, run the following command to see all unsigned certificate requests.
As we have just set up a new agent node, we will see one request for approval. Following
will be the output.
"agent01.example.com" (SHA259)
15:90:C2:FB:ED:69:A4:F7:B1:87:0B:BF:F7:ll:B5:1C:33:F5:76:67:F3:F6:45:AE:07:4B:F
It does not contain any + (sign) in the beginning, which indicates that the certificate is
still not signed.
Sign a Request
In order to sign the new certificate request which was generated when the Puppet agent
run took place on the new node, the Puppet cert sign command would be used, with the
host name of the certificate, which was generated by the newly configured node that needs
to be signed.
Once the above is done, we have our infrastructure ready in which the Puppet master is
now capable of managing newly added nodes.
5. Creating environments
In the IT industry, we can find different teams like development team, testing team, DB
admin team etc. To manage infrastructure for each team separately we can create environments in
puppet.
Edit puppet.conf
To enable directory environments, set environmentpath = $confdir/environments in the Puppet
master’s puppet.conf (in the [main] or [master] section).
Optionally, you can also:
Use the basemodulepath setting to specify global modules that should be available in all
environments. Most people are fine with the default value.
You must have a directory environment for every environment that any nodes are assigned to. At
minimum, you should have a production environment. Nodes assigned to non existent environments
cannot fetch their catalogs.
To create your first environment, create a directory named production in your environmentpath. (If
a production directory doesn’t exist, the Puppet master will try to create one when it starts up.)
Once it is created, you can add modules, a main manifest, and an environment.conf file to it.
Creating an environment
mkdir -p /etc/puppet/environments/production/{modules,manifests}
[master]
manifest= $confdir/environments/Production/manifests/site.pp
modulepath= $confdir/environments/Production/modules
6. Site.pp
/etc/puppet/environments/Production/manifests/site.pp
Visualpath Training & Consulting.
Flat no: 205, Nilgiri Block,Aditya Enclave, Ameerpet, Hyderabad, Phone No: - +91-970 445 5959, 961 824 5689 E-
Mail ID : [email protected], Website : www.visualpath.in.
It is the file where we define the nodes and what changes to be made to get the node to a desired
state in the production environment.
Syntax
# /etc/puppet/environments/production/manifests/site.pp
node 'www1.example.com' {
include common
include apache
include squid
}
node 'db1.example.com' {
include common
include mysql
}
In the example above, only www1.example.com would receive the apache and squid classes, and
only db1.example.com would receive the mysql class.
Node definitions look like class definitions. The general form of a node definition is:
The node keyword
The name(s) of the node(s), separated by commas (with an optional final trailing comma)
An opening curly brace
Any mixture of class declarations, variables, resource declarations, collectors, conditional
statements, chaining relationships, and functions
A closing curly brace
Note: The function “include” can call puppet code from the manifests files. It is explained in detail
in the next chapters
7. Modules
/etc/puppet/environments/Production/modules
Modules are how Puppet finds the classes and defined types it can use — it automatically loads any
class or defined type stored in its modules.
It is a directory where we find all the manifest files, templates and files that are required to prepare
the catalog for the nodes.
Creating Modules
By running the command below, we can create the file structure for the module.
Example
This example module, named “my_module,” shows the standard module layout in more detail:
15. my_module — This outermost directory’s name matches the name of the module.
1. manifests/ — Contains all of the manifests in the module.
1. init.pp — Contains a class definition. This class’s name must match the
module’s name.
2. other_class.pp — Contains a class named my_module::other_class.
3. my_defined_type.pp — Contains a defined type named
my_module::my_defined_type.
4. implementation/ — This directory’s name affects the class names beneath it.
1. foo.pp— Contains a class named my_module::implementation::foo.
2. bar.pp — Contains a class named
my_module::implementation::bar.
2. files/ — Contains static files, which managed nodes can download.
1. service.conf — This file’s source => URL would be
puppet:///modules/my_module/service.conf. Its contents can also be
accessed with the file function, like content =>
file('my_module/service.conf').
3. lib/ — Contains plugins, like custom facts and custom resource types. These will be
used by both the Puppet master server and the Puppet agent service, and they’ll be
synced to all agent nodes whenever they request their configurations. See “Using
Plugins” for more details.
4. facts.d/ — Contains external facts, which are an alternative to Ruby-based custom
facts. These will be synced to all agent nodes, so they can submit values for those
facts to the Puppet master. (Requires Facter 2.0.1 or later.)
5. templates/ — Contains templates, which the module’s manifests can use. See
“Templates” for more details.
1. component.erb — A manifest can render this template with
template('my_module/component.erb').
2. component.epp — A manifest can render this template with
epp('my_module/component.epp'). (The epp function is only available with
the future parser enabled.)
6. tests/ — Contains examples showing how to declare the module’s classes and
defined types.
1. init.pp
2. other_class.pp — Each class or defined type should have an example in the
tests directory.
7. spec/ — Contains spec tests for any plugins in the lib directory.
9. Classes
Classes are named blocks of Puppet code, which are stored in modules for later use and are not
applied until they are invoked by name. They can be added to a node’s catalog by declaring them in
your manifests.
Defining a class makes it available by name, but doesn't automatically evaluate the code inside it.
Before we can use a class, we must define it, which is done with the class keyword, a name, curly
braces, and a block of code:
Declaring a class evaluates the code in the class, and applies all of its resources.
This one actually does something.
Include Function:
It is used for declaration of one or more classes, which results in evaluating all the resources present
inside those classes and finally add them to a catalog. The way it works is, include function accepts
a class name, list of classes or a comma separated list of class names.
Resources are the fundamental unit for modelling system configurations. Each resource describes
some aspect of a system, like a service that must be running or a package that must be installed. The
block of Puppet code that describes a resource is called a resource declaration.
Simplified syntax
Resource declarations have a lot of features, but beginners can accomplish a lot with just a subset of
these. For more advanced syntax (including expressions that declare multiple resources at once), see
Resources (Advanced).
# A resource declaration:
file { '/etc/passwd':
ensure => file,
owner => 'root',
group => 'root',
mode => '0600',
}
After
The form of a resource declaration is:
The resource type, which is a word with no quotes.
An opening curly brace ({).
The title, which is a string.
A colon (:).
Optionally, any number of attribute and value pairs, each of which consists of:
An attribute name, which is a lowercase word with no quotes.
A => (called an arrow, “fat comma,” or “hash rocket”).
A value, which can have any data type.
A trailing comma.
A closing curly brace (}).
DESCRIPTION
This action validates Puppet DSL syntax without compiling a catalog or syncing any resources. If
no manifest files are provided, it will validate the default site manifest.
EXAMPLES
To validate the default site manifest at
/etc/puppet/environments/production/modules/sample_module/manifests/init.pp:
$ puppet parser validate init.pp
After validating the puppet code, we need the apply that module on the node. To achieve that we
have to run the command
The puppet agent is configured to run at a specific interval. The default is 30 minutes. You can
change how often agent pulls the catalog by modifying the “runinterval” setting.
Example
The Puppet agent service defaults to doing a configuration run every 30 minutes. You can configure
this with the runinterval setting in puppet.conf:
# /etc/puppet/puppet.conf
[agent]
runinterval = 2h
Regardless of how you’re running Puppet agent, you can prevent it from doing any Puppet runs by
running
$sudo puppet agent --disable
Variables
Syntax
Assignment
$content = "some content\n"
Variable names are prefixed with a $ (dollar sign). Values are assigned to them with the = (equal
sign) assignment operator.
Resolution
file {'/tmp/testing':
ensure => file,
content => $content,
}
In the above example, A file will be created with the information(of any data type) that is stored in
the variable “$content”
Array
If we want to pass multiple parameters in puppet code, Puppet allows the use of arrays in multiple
areas.
Syntax
Arrays are written as comma-separated lists of values surrounded by square brackets. An optional
trailing comma is allowed between the final value and the closing square bracket.
Example
14. Conditionals
Conditions are situations when the user wishes to execute a set of statement or code when
the defined condition or the required condition is satisfied. Puppet supports two types of
conditions.
Puppet supports “if” and “unless” statements, case statements, and selectors.
“If” Statements
“If” statements take a Boolean condition and an arbitrary block of Puppet code, and will only
execute the block if the condition is true. They can optionally include elsif and else clauses.
Syntax
if $osfamily == 'redhat' {
package {'httpd':
Package {'apache2':
else {
Syntax
case $operatingsystem {
'Solaris': { include role::solaris } # apply the solaris class
'RedHat', 'CentOS': { include role::redhat } # apply the redhat class
/^(Debian|Ubuntu)$/:{ include role::debian } # apply the debian class
default: { include role::generic } # apply the generic class
}
16. Selectors
Selector statements are similar to case statements, but return a value instead of executing a code
block. Selectors are useful when the user wishes to specify a resource attribute and variables which
are different from the default values based on the facts or other variables.
Syntax
Selectors resemble a cross between a case statement and the ternary operator found in other
languages.
$rootgroup = $osfamily ? {
'Solaris' => 'wheel',
/(Darwin|FreeBSD)/ => 'wheel',
default => 'root',
}
file { '/etc/passwd':
ensure => file,
owner => 'root',
group => $rootgroup,
}
In the example above, the value of $rootgroup is determined using the value of $osfamily.
The general form of a selector is:
By default, Puppet applies resources in the order they’re declared in their manifest. However, if a
group of resources must always be managed in a specific order, you should explicitly declare such
relationships with relationship metaparameters.
Syntax: Relationship metaparameters
package { 'openssh-server':
ensure => present,
before => File['/etc/ssh/sshd_config'],
}
Puppet uses four metaparameters to establish relationships, and you can set each of them as an
attribute in any resource. The value of any relationship metaparameter should be a resource
reference pointing to one or more target resources.
If two resources need to happen in order, you can either put a before attribute in the prior one or a
require attribute in the subsequent one; either approach creates the same relationship. The same is
true of notify and subscribe.
The two examples below create the same ordering relationship:
package { 'openssh-server':
ensure => present,
before => File['/etc/ssh/sshd_config'],
}
file { '/etc/ssh/sshd_config':
ensure => file,
mode => '0600',
source => 'puppet:///modules/sshd/sshd_config',
file { '/etc/ssh/sshd_config':
ensure => file,
mode => '0600',
source => 'puppet:///modules/sshd/sshd_config',
notify => Service['sshd'],
}
service { 'sshd':
ensure => running,
enable => true,
subscribe => File['/etc/ssh/sshd_config'],
}
Since an array of resource references can contain resources of differing types, these two examples
also create the same ordering relationship:
service { 'sshd':
ensure => running,
require => [
Package['openssh-server'],
File['/etc/ssh/sshd_config'],
],
}
package { 'openssh-server':
ensure => present,
before => Service['sshd'],
}
file { '/etc/ssh/sshd_config':
ensure => file,
mode => '0600',
source => 'puppet:///modules/sshd/sshd_config',
before => Service['sshd'],
}
_____________________________________________________________________________
First, let’s adjust the file declaration from the previous section. We’ll remove the source
attribute and replace it with a content attribute.
The template() function takes a single argument: the URI of the ERB template. The format of that
URI is always puppet:///modulename/filename. The file should be placed in the templates directory
of the module. ERB templates should end with the .erb extension to indicate that the file contains
tags for the ERB template processor.
$ vi templates/puppet.conf.erb
[main]
[agent]
[user]
Each instance of <%=@variable %> is replaced with the value of the Puppet variable named after
the @sign. The variables named with the @ sign must exist in the same scope (within the
module class) as the template declaration.
There are many other things you can do within an ERB template. You can lookup variables from
another class using the scope.lookupvar() function, or use scope[] as if it was a Hash.
You can call Puppet functions using scope.function_puppet_function(). For example, you could call
the Hiera function to lookup Hiera values within templates (although this practice is strongly
discouraged). This would be done by using scope.function_hiera() to call the same heira() function
we used when introducing Hiera.
Best Practice: Avoid placing direct Hiera calls within the template, as it divides the source of data
for the template between the manifest file and hiera, ensuring confusion. Instead, source the Hiera
variables within the manifest so that all variables are within scope.
As ERB templates were intended for inline Ruby development, you can put any Ruby statement
within <% ... %> tags without the equals sign. Here’s an example that would limit duplicate
assignment of loglevels which don’t differ.
[user]
<% if @apply_loglevel != @loglevel -%>
log_level = <%= @apply_loglevel %>
<% end -%>
By wrapping this line of the template within the Ruby block, it will skip outputting
the configuration line if the loglevel matches the main loglevel, thus simplifying the
configuration file.
Go ahead and test this change right now with puppet apply. You will see the contents
of the puppet configuration file get updated.
Here’s an example where we use the Ruby each () function to iterate through an array of tags which
should be used to limit which resources are applied to the node, as we discussed in Part I. This
example uses the dash creatively to suppress linefeeds and output multiple Puppet servers on a
single line:
[agent]
You’ll note that we don’t put an @ sign before the variable name. That is because we are not
referencing a variable in the Puppet module class, but instead from the locall loop shown in this
example.
_____________________________________________________________________________
1. Company-wide defaults
2. Operating system specific changes
3. Site-specific information
Hiera has two built-in data file backends, YAML and JSON, and then the Puppet data provider.
Each backend support four data types:
1. Arrays
2. Strings
3. true/false (Boolean)
4. Hashes
The most common way to provide data to Hiera is using the YAML file format. The file has a .yaml
file extension.
Files in YAML format always start with three dashes on the first line. The YAML format utilizes
white spaces indentation to indicate the relationships between data. YAML should always be
written using spaces, never tabs, for indentation.
Here are some examples of strings, Boolean, arrays, and hashes in YAML.
# strings
agent_running: 'running'
# boolean expression
agent_atboot: true
# arrays
puppet_components:
- facter
- puppet
# hash of values
puppet:
ensure: 'present'
version: '4.0.4'
# variable loopup
hostname: %{facts::hostname}
We can organize all the data within a single hash That could look as simple as this:
puppet:
We can see, YAML provides a clean, easy to read way to provide data without too much
complicated syntax.
Configuring Hiera
Puppet looks for a Hiera configuration file at the location specified by the hiera_config
configuration variable. By default this is $codedir/hiera.yaml, or /etc/puppet/code/hiera.yaml in
Puppet.
Backends
The configuration key :backends should provide an array which lists the backend data providers that
Hiera should use. There are three built-in backends, the two data types we discussed previously,
YAML and JSON, plus Hiera can utilize data from Puppet.
If you wish to utilize both built-in file types, you could configure it as follows.
:backends:
- yaml
- json
Backend Conguration
For each backend data provider, you name in the backends array, you should create a
top-level entry with the name of the provider. For each backend provide a hash of configuration
data.
For the two built-in file-based backends the only configuration key necessary is :datadir, which
identifies the directory in which the data files reside.
:yaml:
:datadir: /etc/puppetlabs/code/environments/%{::environment}/hieradata
:json:
:datadir: /etc/puppetlabs/code/environments/%{::environment}/hieradata
As the files read by each backend must be named differently, you can use the same data directory
for both data sources as shown above.
You’ll note that we’re using the top-level environment variable (defined by puppet master or client)
to allow different environment data in each environment. Let’s go ahead and create the hieradata
directory now in the environment directories we created in the last chapter.
mkdir /etc/puppetlabs/code/environments/test/hieradata
mkdir /etc/puppetlabs/code/environments/production/hieradata
With this configuration variables from a module named heiradata would be accessed for Hiera
lookups. As mentioned previously this is only useful when the module provides dynamic lookup of
data.
Hierarchy
The final mandatory parameter is :hierarchy. The hierarchy defines the priority order for lookup of
configuration data. For single values Hiera will proceed through the hierarchy until it finds a value
and then stop. For arrays and hashes Hiera will merge data from each level of the hierarchy,
selecting the winner of conflicts based on the :merge_behavior configuration setting.
There are two types of data sources: static and dynamic. Static data sources are files explicitly
named in the hierarchy which contain data. Dynamic data sources are files which are named using
interpolation of local configuration data, such as the host‐ name or operating system of the node.
In a larger enterprise, the data lookup hierarchy could be quite complex, however I recommend the
following for a good starting point.
You would implement this hierarchy using the following configuration syntax. As you can see, we
are interpolating data provided by Facter to choose which files will be read.
:hierarchy:
- defaults
- "%{::hostname}"
- "%{::osfamily}"
- global
Naturally you can extend this hierarchy to use information like the domain name of the node or any
other facter-provided node value.
If you have multiple backends configured, then Hiera will evaluate the entire hierarchy for the first
configured backend, then evaluate the entire hierarchy in order forthe 2nd configured backend, etc.
Complete Example
Following is a complete example of a Hiera configuration file. This example is what we will use for
the remainder of this book. It enables YAML data input from etc/puppetlabs/code/hieradata, with a
hierarchy that uses host-specific information in preference to operating system family information,
finally defaulting to values global to every host.
---
:backends:
______________________________________________________________________________
Description
Installs and manages cron jobs. Every cron resource created by Puppet requires a command and at
least one periodic attribute (hour, minute, month, monthday, weekday, or special). While the name
of the cron job is not part of the actual job, the name is stored in a comment beginning with #
Puppet Name: . These comments are used to match crontab entries created by Puppet with cron
resources.
If an existing crontab entry happens to match the scheduling and command of a cron resource that
has never been synched, Puppet will defer to the existing crontab entry and will not create a new
entry tagged with the # Puppet Name: comment.
Example:
cron { 'logrotate':
command => '/usr/sbin/logrotate',
user => 'root',
hour => 2,
minute => 0,
}
Command
(Property: This attribute represents concrete state on the target system.)
The command to execute in the cron job. The environment provided to the command varies by local
system rules, and it is best to always provide a fully qualified command. The user’s profile is not
sourced when the command is run, so if the user’s environment is desired it should be sourced
manually.
All cron parameters support absent as a value; this will remove any existing values for that field.
hour
(Property: This attribute represents concrete state on the target system.)
minute
(Property: This attribute represents concrete state on the target system.)
The minute at which to run the cron job. Optional; if specified, must be between 0 and 59, inclusive.
user
(Property: This attribute represents concrete state on the target system.)
The user who owns the cron job. This user must be allowed to run cron jobs, which is not currently
checked by Puppet.
This property defaults to the user running Puppet or root.
The default crontab provider executes the system crontab using the user account specified by this
property.
Description
Manages files, including their content, ownership, and permissions.
The file type can manage normal files, directories, and symlinks; the type should be specified in the
ensure attribute.
File contents can be managed directly with the content attribute, or downloaded from a remote
source using the source attribute; the latter can also be used to recursively serve directories (when
the recurse attribute is set to true or local). On Windows, note that file contents are managed in
binary mode; Puppet never automatically translates line endings.
Autorequires: If Puppet is managing the user or group that owns a file, the file resource will
autorequire them. If Puppet is managing any parent directories of a file, the file resource will
autorequire them.
Example:
file { '/etc/inetd.conf':
ensure => link,
target => '/etc/inet/inetd.conf',
}
ensure
(Property: This attribute represents concrete state on the target system.)
Whether the file should exist, and if so what kind of file it should be. Possible values are present,
absent, file, directory, and link.
34. present accepts any form of file existence, and creates a normal file if the file is
missing. (The file will have no content unless the content or source attribute is
used.)
35. absent ensures the file doesn’t exist, and deletes it if necessary.
36. file ensures it’s a normal file, and enables use of the content or source attribute.
37. directory ensures it’s a directory, and enables use of the source, recurse,
recurselimit, ignore, and purge attributes.
38. link ensures the file is a symlink, and requires that you also set the target attribute.
Symlinks are supported on all Posix systems and on Windows Vista / 2008 and
higher. On Windows, managing symlinks requires Puppet agent’s user account to
have the “Create Symbolic Links” privilege; this can be configured in the “User
Description
Executes external commands.
Any command in an exec resource must be able to run multiple times without causing harm — that
is, it must be idempotent. There are three main ways for an exec to be idempotent:
Example
cwd
The directory from which to run the command. If this directory does not exist, the command will
fail
path
The search path used for command execution. Commands must be fully qualified if no path is
specified. Paths can be specified as an array or as a ‘:’ separated list.
creates
A file to look for before running the command. The command will only run if the file doesn’t exist.
This parameter doesn’t cause Puppet to create a file; it is only useful if the command itself creates
a file.
Description
Manage packages. There is a basic dichotomy in package support right now: Some package types
(e.g., yum and apt) can retrieve their own package files, while others (e.g., rpm and sun) cannot. For
Example
package { 'ntp':
ensure => 'installed',
}
ensure
(Property: This attribute represents concrete state on the target system.)
What state the package should be in. On packaging systems that can retrieve new packages on their
own, you can choose which package to retrieve by specifying a version number or latest as the
ensure value. On packaging systems that manage configuration files separately from “normal”
system files, you can uninstall config files by specifying purged as the ensure value. This defaults to
installed.
Description
Sends an arbitrary message to the agent run-time log.
package { 'apache2':
provider=>'apt',
ensure=>'installed'
}
notify { 'Apache2 is installed.':
}
service { 'apache2':
ensure=>'running'
}
notify { 'Apache2 is running.':
}
name
(Namevar: If omitted, this attribute’s value defaults to the resource’s title.)
An arbitrary tag for your own reference; the name of the message.
message
(Property: This attribute represents concrete state on the target system.)
The message to be sent to the log.
(↑ Back to notify attributes)
withpath
Whether to show the full object path. Defaults to false.
Valid values are true, false.
service { ntpd:
ensure => 'running',
enable => true,
}
ensure
(Property: This attribute represents concrete state on the target system.)
Whether a service should be running.
Valid values are stopped (also called false), running (also called true).
enable
(Property: This attribute represents concrete state on the target system.)
Whether a service should be enabled to start at boot. This property behaves quite differently
depending on the platform; wherever possible, it relies on local tools to enable or disable a given
service.
Valid values are true, false, manual, mask.
Description
Manage users. This type is mostly built to manage system users, so it is lacking some features
useful for managing normal users.
This resource type uses the prescribed native tools for creating groups and generally uses POSIX
APIs for retrieving information about them. It does not directly modify /etc/passwd or anything.
user { 'agent1':
ensure => 'present',
ensure
(Property: This attribute represents concrete state on the target system.)
The basic state that the object should be in.
Valid values are present, absent, role.
home
(Property: This attribute represents concrete state on the target system.)
The home directory of the user. The directory must be created separately and is not currently
checked for existence.
uid
(Property: This attribute represents concrete state on the target system.)
The user ID; must be specified numerically. If no user ID is specified when creating a new user,
then one will be chosen automatically. This will likely result in the same user having different UIDs
on different systems, which is not recommended. This is especially noteworthy when managing the
same user on both Darwin and other platforms, since Puppet does UID generation on Darwin, but
the underlying tools do so on other platforms.
shell
(Property: This attribute represents concrete state on the target system.)
The user’s login shell. The shell must exist and be executable.
This attribute cannot be managed on Windows systems.
Requires features manages_shell.
Linux users have their passwords stored as hash in /etc/shadow file. Puppet passes the password
supplied in the user type definition in the /etc/shadow file.
Generate your hash password using openssl command:
#openssl passwd -1
#Enter your password here
Password:
Verifying - Password:
$1$HTQUGYUGYUGwsxQxCp3F/nGc4DCYM
user { 'test_user':
ensure => present,
password =>'$1$HTQUGYUGYUGwsxQxCp3F/nGc4DCYM/',
}
Manage groups. On most platforms this can only create groups. Group membership must be
managed on individual users.
group { 'sysadmin':
ensure => present,
gid => '5000',
}
ensure
(Property: This attribute represents concrete state on the target system.)Create or remove the
group.
Valid values are present, absent.
gid
(Property: This attribute represents concrete state on the target system.)
The group ID. Must be specified numerically. If no group ID is specified when creating a new
group, then one will be chosen automatically according to local system standards. This will likely
result in the same group having different GIDs on different systems, which is not recommended.
Now we are going to work on an exercise which would give us a detailed understanding of Puppet
workflow and its influence on implementing “Infrastructure as Code”
1. Puppet Setup.
Creating an environment with the the name “Production” and its file structure
#mkdir -p /etc/puppet/environments/production/{modules,manifests}
Open puppet.conf and define the puppet server under [main] section and define the
environmentpath and modulepath
[main]
certname = puppetmaster
[master]
environmentpath = $confdir/environments
basemodulepath = $confdir/modules:/opt/puppet/share/modules
After making all the configurations changes, we need to start the puppet server to apply those
changes.
# service puppetmaster start
Setting up wiki
Installing puppet package manager:
#yum install -y http://yum.puppetlabs.com/puppetlabs-release-el-6.noarch.rpm
Install puppet
#yum install -y puppet
Update the puppetmaster info in puppet.conf so that the agent identify the puppet server.
# vi /etc/puppet/puppet.conf
[main]
server = puppetmaster
Once we done with changing the puppet configuration file , the agent is needed to request for a ssl
certificate from the puppet server.
Run the following puppet command to request cert
# puppet agent --verbose --no-daemonize –onetime
Open the agent configuration file and update the info of puppet master
# vi /etc/puppet/puppet.conf
[agent]
server = puppetmaster
To make puppet ready from the next system bootings run the following command
# puppet agent –enable
The puppet master needs to accept the requests from agents through the command,
# puppet cert sign wiki
# puppet cert sign wikitest
Login to puppetserver
We can define the nodes and their desired state in nodes.pp file in the main manifests file. In the
following example we are defining the nodes for the production environment .
# vi /etc/puppet/environments/production/manifests/nodes.pp
node 'wiki'{
file { '/info.txt':
ensure => 'present',
content => inline_template("This file was created by puppet at <%= Time.now %>\n"),
}
}
node 'wikitest' {
file { '/info.txt':
We can observe the presence of a new file ‘/info.txt’ on the nodes when we run this manifest file
from the agents.
The command will pull all the configuration changes specified in the manifest files in the puppet
master.
2. Manifests.
In this section, we are going to discuss some of the most renowned resources like file, packages
and service.
For managing the files in puppet, we have a resource type ‘file’. We use different attributes for
efficient use of ‘file’ resource.
# /etc/puppet/environments/production/manifests/nodes.pp
node 'wiki' {
file { '/info.txt':
ensure => 'present',
content => inline_template("Created by Puppet at <%= Time.now %>\n"),
}
}
node 'wikitest' {
file { '/info.txt':
ensure => 'present',
content => inline_template("Created by Puppet at <%= Time.now %>\n"),
}
}
Go to both the nodes and execute to apply changes
# puppet agent --verbose --no-daemonize --onetime
Package ‘ resource type is to manage the software installations through the attributes like ensure
and name.
}
}
node 'wikitest' {
file { '/info.txt':
ensure => 'present',
content => inline_template("Created by Puppet at <%= Time.now %>\n"),
}
package { 'ntp':
ensure => 'installed',
}
service { ntp:
ensure => 'running',
enable => true,
}
}
2.3 Selectors
For this exercise, we can use selectors to choose a variable based on the facts or other
variables. The following will define different package names based on OS type which can be known
from the facts.
$ntpservice = $osfamily ? {
'redhat' => 'ntpd',
'debian' => 'ntp',
default => 'ntp',
}
node 'wiki' {
file { '/info.txt':
ensure => 'present',
content => inline_template("Created by Puppet at <%= Time.now %>\n"),
package { 'ntp':
ensure => 'installed',
service { $ntpservice:
ensure => 'running',
enable => true,
}
}
node 'wikitest' {
file { '/info.txt':
ensure => 'present',
content => inline_template("Created by Puppet at <%= Time.now %>\n"),
}
package { 'ntp':
ensure => 'installed',
}
service { $ntpservice:
ensure => 'running',
enable => true,
}
}
node 'wiki' {
class { 'linux': }
}
node 'wikitest' {
class { 'linux': }
}
class linux {
$ntpservice = $osfamily ? {
'redhat' => 'ntpd',
'debian' => 'ntp',
default => 'ntp',
}
file { '/info.txt':
ensure => 'present',
content => inline_template("Created by Puppet at <%= Time.now %>\n"),
}
package { 'ntp':
ensure => 'installed',
}
2.5 Variables
This section deals with usage of variables and calling them from the resources. We are using a
variable ‘$admintools’ to pass the package names to the package resource in the manifest file.
node 'wiki' {
class { 'linux': }
}
node 'wikitest' {
class { 'linux': }
}
class linux {
package { $admintools:
ensure => 'installed',
}
$ntpservice = $osfamily ? {
'redhat' => 'ntpd',
'debian' => 'ntp',
default => 'ntp',
}
file { '/info.txt':
ensure => 'present',
content => inline_template("Created by Puppet at <%= Time.now %>\n"),
}
package { 'ntp':
ensure => 'installed',
}
service { $ntpservice:
ensure => 'running',
enable => true,
}
}
3. Modules
Login to puppetmaster
# cd /etc/puppet/environments/production/modules/mediawiki/manifests
# vi init.pp
class mediawiki {
$phpmysql = $osfamily ? {
'redhat' => 'php-mysql',
'debian' => 'php5-mysql',
default => 'php-mysql',
}
package { $phpmysql:
ensure => 'present',
}
}
# vi nodes.pp
node 'wiki'{
class {'linux': }
class {'mediawiki':}
node 'wikitest' {
class {'linux': }
class {'mediawiki':}
}
3.3 Conditionals
The conditionals can be used to choose the variable based on the facts or other variables that enable
the puppet master to prepare a catalog with the required information.
Here, we are choosing a package name for different Os family. There are two kinds of conditional
statements in this example selectors and if conditionals.
Login to puppetmaster
# cd /etc/puppet/environments/production/modules/mediawiki/manifests
# vi init.pp
class mediawiki {
$phpmysql = $osfamily ? {
'redhat' => 'php-mysql',
'debian' => 'php5-mysql',
default => 'php-mysql',
}
package { $phpmysql:
ensure => 'present',
}
if $osfamily == 'redhat' {
package {'php-xml':
ensure => 'present',
}
}
}
Run puppet agent from both the nodes to verify
# puppet agent --verbose --no-daemonize –onetime
https://forge.puppet.com/
Login to puppetmaster
We are going to download a puppet module from the puppet forge named ‘puppetlabs-apache’ .
This module can be used across puppet nodes of different OS types.
# cd /etc/puppet/environments/production/modules
The command to downloading the module is below
# ls -ltr
# cd mediawiki/manifests/
# vi init.pp
class mediawiki {
$phpmysql = $osfamily ? {
'redhat' => 'php-mysql',
'debian' => 'php5-mysql',
default => 'php-mysql',
}
package { $phpmysql:
ensure => 'present',
}
if $osfamily == 'redhat' {
package {'php-xml':
ensure => 'present',
}
}
class { '::apache':
docroot => '/var/www/html',
mpm_module => 'prefork',
subscribe => Package[$phpmysql],
}
class {'::apache::mod::php': }
}
# cd /etc/puppet/environments/production/modules
To download the module, run the following command.
# puppet module install puppetlabs-vcsrepo --modulepath
/etc/puppet/environments/production/modules/
The command will download the module to
/etc/puppet/environments/production/modules/
# ls -ltr
# cd mediawiki/manifests/
# vi init.pp
class { '::apache':
docroot => '/var/www/html',
mpm_module => 'prefork',
subscribe => Package[$phpmysql],
}
class {'::apache::mod::php': }
vcsrepo { '/var/www/html':
ensure => 'present',
provider => 'git',
source => "https://github.com/wikimedia/mediawiki.git"
revision => 'REL1_23'
}
}
subscribe — Applies a resource after the target resource. The subscribing resource refreshes if
the target resource changes.
# vi init.pp
class mediawiki {
$phpmysql = $osfamily ? {
'redhat' => 'php-mysql',
'debian' => 'php5-mysql',
default => 'php-mysql',
}
package { $phpmysql:
ensure => 'present',
}
if $osfamily == 'redhat' {
package {'php-xml':
file {'/var/www/html/index.html':
ensure => 'absent',
}
}
This puppet run is gonna remove the index.html file after trying vcsrepo so ultimately it will fail.
For this will use resource ordering.
Login to puppetmaster
# cd /etc/puppet/environments/production/modules//mediawiki/manifests
# vi init.pp
class mediawiki {
$phpmysql = $osfamily ? {
'redhat' => 'php-mysql',
'debian' => 'php5-mysql',
default => 'php-mysql',
}
package { $phpmysql:
ensure => 'present',
}
if $osfamily == 'redhat' {
package {'php-xml':
ensure => 'present',
}
}
class { '::apache':
docroot => '/var/www/html',
mpm_module => 'prefork',
subscribe => Package[$phpmysql],
}
class {'::apache::mod::php': }
# touch /var/www/html/index.html
# puppet agent --verbose --no-daemonize –onetime
Login to puppetmaster
# cd /etc/puppet/environments/production/modules
# puppet module install puppetlabs-mysql --version 3.9.0 --modulepath
/etc/puppet/environments/production/modules/
# ls -ltr
# cd mediawiki/manifests/
# vi init.pp
class mediawiki {
$phpmysql = $osfamily ? {
'redhat' => 'php-mysql',
'debian' => 'php5-mysql',
default => 'php-mysql',
}
package { $phpmysql:
ensure => 'present',
}
if $osfamily == 'redhat' {
package {'php-xml':
ensure => 'present',
}
}
class { '::apache':
docroot => '/var/www/html',
mpm_module => 'prefork',
subscribe => Package[$phpmysql],
}
class {'::apache::mod::php': }
vcsrepo { '/var/www/html':
ensure => 'present',
provider => 'git',
source => "https://github.com/wikimedia/mediawiki.git",
Login to puppetmaster
# cd /etc/puppet/environments/production/modules
# puppet module install puppetlabs-firewall --modulepath
/etc/puppet/environments/production/modules/
# ls -ltr
# cd mediawiki/manifests/
# vi init.pp
class mediawiki {
$phpmysql = $osfamily ? {
'redhat' => 'php-mysql',
'debian' => 'php5-mysql',
default => 'php-mysql',
}
package { $phpmysql:
ensure => 'present',
}
if $osfamily == 'redhat' {
package {'php-xml':
ensure => 'present',
}
}
class { '::apache':
docroot => '/var/www/html',
mpm_module => 'prefork',
subscribe => Package[$phpmysql],
}
class {'::apache::mod::php': }
vcsrepo { '/var/www/html':
ensure => 'present',
provider => 'git',
source => "https://github.com/wikimedia/mediawiki.git",
revision => 'REL1_23',
}
file {'/var/www/html/index.html':
ensure => 'absent',
}
Now since our mediawiki server is deployed to wiki and wikitest, lets go ahead check that out.
Open up a browser and enter wiki server IP, you should see the mediawiki webpage as shown
below.
class { 'firewall': }
firewall { '000 allow http access':
port => '80',
Proto => 'tcp',
action => 'accept',
}
file {'LocalSettings.php':
path => '/var/www/html/LocalSettings.php',
ensure => 'file',
content => template('mediawiki/LocalSettings.erb'),
}
- Define variables used in template file.
# vi /etc/puppet/environments/production/manifests/nodes.pp
node 'wiki'{
$wikisitename = 'wiki'
$wikimetanamespace = 'Wiki'
$wikiserver = "http://192.168.8.12"
$wikidbserver = 'localhost'
$wikidbname = 'wiki'
$wikidbuser = 'root'
$wikidbpassword = 'training'
$wikiugradekey = 'puppet'
class {'linux':}
class {'mediawiki':}
}
7. Hiera
Hiera facilitates to place all the node specific variables in a file other than manifests
files. This makes easier to work on the variables and the manifest files can be used for other
projects without any modification in them.
Here we are removing all the variables from the manifests file and placing those
node specific variables in differently located files.
# vim /etc/puppet/environments/production/modules/mediawiki/manifests/init.pp
class mediawiki {
$wikimetanamespace = hiera('mediawiki::wikimetanamespace')
$wikisitename = hiera('mediawiki::wikisitename')
$wikiserver = hiera('mediawiki::wikiserver')
$wikidbserver = hiera('mediawiki::wikidbserver')
$wikidbname = hiera('mediawiki::wikidbname')
$wikidbuser = hiera('mediawiki::wikidbuser')
$wikidbpassword = hiera('mediawiki::wikidbpassword')
$wikiupgradekey = hiera('mediawiki::wikiupgradekey')
$phpmysql = $osfamily ? {
'redhat' => 'php-mysql',
'debian' => 'php5-mysql',
default => 'php-mysql',
}
}
# vi /etc/puppet/hiera.yaml
:backends:
- yaml
:yaml:
:datadir:
:hierarchy:
- "%{clientcert}"
- wikidefault
# vi /var/lib/hiera/wiki.yaml
---
mediawiki::wikisitename: wiki
mediawiki::wikisitenamespace: Wiki
mediawiki::wikiserver: http://192.168.1.11
mediawiki::wikidbname: wiki