Puppet is a configuration management solution. Users describe the desired state of a server or software and configuration management achieves this state. This brings following advantages:
Puppet uses Ruby to describe the desired state of a server, called a node. It does so with the use of primitives called resource types. By default, every 30 minutes, the puppet agent authenticates itself against the puppet server. It then sends a list of properties of itself called facts. The server looks at the facts and the configuration files called manifests and compiles the desired state for the node. It then sends that configuration back to the node, where the agent applies it.
To give an idea of how powerful this can be, here are a couple examples of increasing complexity showcasing what puppet can do for you.
Example: User
This example creates the user username on node myserver and adds it to the group wheel.
node 'myserver' {
user { 'username':
ensure => 'present',
groups => ['wheel'],
}
}
This file that would be stored on the puppet server is the manifest. The resource type in this example is user. Every resource type has optional and required properties. In this example, ensure is required and groups is optional. This specific configuration would only be applied to myserver. You can apply configurations to all nodes by placing it outside of a node definition.
It is possible to take a couple of resource definitions and store them as modules. A module is similar to a library. These modules can be shared online, and you usually find one for every major software package. The official way to share modules is through the puppet forge: https://forge.puppet.com/
Example: Postgres
This example installs a postgres server on node myserver, and creates a database db, owned by username, identified by password. It does so using the postgresql module.
node 'myserver' {
class { 'postgresql::server': }
postgresql::server::db { 'db':
user => 'username',
password => 'password',
}
}
In this case postgresql is a module. The module itself takes care of identifying the operating system, downloading and installing the program, and then configuring it according to the manifest. This is a basic example but the module allows a great deal of customization.
Note that it is not necessary to know SQL or how to actually install a postgres server to do so. Official modules are well maintained and provide a sane and secure base configuration.
It is also possible to use facts in manifests. Facts act like variables.
Example: Conditions using facts
This example uses the rsyslog module to configure rsyslog on all non-windows machines.
if $osfamily != 'windows' {
class { 'rsyslog::client': }
}
$osfamily is a fact. These facts are gathered every time the puppet agent runs. Note that because this definition is outside of a node definition, it gets applied to all nodes. However, rsyslog::client will only be executed on nodes that do not run windows.
Since puppet uses ruby, programmatic elements like control flows and variables can be used in manifests.
With the addition of PuppetDB you can share information between multiple nodes. This allows one node to influence configuration on a different node. Classic examples include load balancers or monitoring solutions.
Example: Registering a host with monitoring using exported resources
This example creates an exported resource on a node, and then imports that resource on the monitoring server, adding the host to the monitoring. It is using the Icinga2 puppet module.
@@icinga2::object::host { $::fqdn:
display_name => $::fqdn,
ipv4_address => $::ipaddress_eth0,
}
node 'icinga2' {
Icinga2::Object::Host <<| |>> { }
}
@@icinga2::object::host creates a host definition object. This gets created by every node that executes this code. The @@ marks it as an exported resource. Usually, nodes do not share information in puppet. Exported resources allow to do that.
Note that all the property values in the host definition are facts. This means they will be different for every node which executes it.
Finally, the exported resource gets imported by the icinga2 node. The Icinga2 module is responsible for making sure that the correct configuration files are created and reloaded.