Notes on Puppet DSL

Every time I start writing some puppet code, I have to re-read the documentation to remind myself of the syntax. I have some notes of what I usually refer to so that someone else who has a similar problem like me can refer to it.

Structure of a resource
user {'dave':
	ensure => present,
	uid => '507',
	gid => 'admin',
	home => '/home/admin',
	managehome => true,
}
# details about current resource instance
$ puppet resource user root
# reading up documentation about a resource type
$ puppet describe -s user

# resource declaration and application to test snippet
#/root/training-manifests/1.file.pp
file {'testfile':
	path => '/tmp/testfile',
	ensure => present,
	mode => 0640,
	content > 'i am a test file',
}
#and call apply
$puppet apply 1.file.pp

# notify user
notify {"this is a notice message"}

# Controlling order of execution
file {'/tmp/test1':
	ensrue => present,
	content => 'hi',
}
notify {'/tmp/test1 ahs already been synced':
	require => File['/tmp/test1']
}

#but along from require there are:
#- before
#- require
#- notify
#- subscribe

# Chaining:
file {"/tmp/test1":
	ensure => present,
	content => 'h1',
}
notify {'after':
	message => '/tmp/tesst1'
}
File['/tmp/test1'] -> Notify['after']

ssh example:
# /root/learning-manifests/break_ssh.pp, again
file { '/etc/ssh/sshd_config':
	ensure => file,
	mode => 600,
	source => '/root/learning-manifests/sshd_config',
}
service { 'sshd':
	ensure => running,
	enable => true,
	hasrestart => true,
	hasstatus => true,
	# FYI, those last two attributes default to false, since
	# bad init scripts are more or less endemic.
	subscribe => File['/etc/ssh/sshd_config'],
}

# /root/learning-manifests/break_ssh.pp
package { 'openssh-server':
	ensure => present,
	before => File['/etc/ssh/sshd_config'],
}
file { '/etc/ssh/sshd_config':
	ensure => file,
	mode => 600,
	source => '/root/learning-manifests/sshd_config',
}
service { 'sshd':
	ensure => running,
	enable => true,
	hasrestart => true,
	hasstatus => true,
	subscribe => File['/etc/ssh/sshd_config'],
}

Variables:
$variables always start with a dollar sign
can hold strings, numbers, value like undef etc
fully qualified variables look like $scope::variable top level variables are the same, but their scope is nameless $::top_scope_variable
you can only assign a variable once in a given scope

if condition {
    block of code
} elsif condition {
    block of code
} else {
    block of code
}

if $is_virtual {
    service {'ntpd':
    ensure => stopped,
    enable => false,
  }
} else {
    service { 'ntpd':
    name => 'ntpd',
    ensure => running,
    enable => true,
    hasrestart => true,
    require => Package['ntp'],
  }
}

case $operatingsystem {
    centos: { $apache = "httpd" }
    # Note that these matches are case-insensitive.
        redhat: { $apache = "httpd" }
        debian: { $apache = "apache2" }
        ubuntu: { $apache = "apache2" }
        default: { fail("Unrecognized operating system for webserver")
    }
    # "fail" is a function. We'll get to those later.
}
package {'apache':
    name => $apache,
    ensure => latest,
}

Modules and Classes
class {'security_base': }
class {'webserver_base': }
class {'appserver': }

Classes are singleton collections of resources that puppet can apply as a unie. you can think of them as blocks of code that can be turned on or off. 

class ntp {
	package {'ntp':
		ensure => installed,
	}
}

But to use the class you need to define the class then declare it
class {'ntp': }

# puppet apply --verbose ntp-class.pp
there's there way to declare classes, but it behaves a little bit differently

include ntp
include ntp
include ntp

The include function will declare a classs if it hasn't already been declared, and will do nothing if it has. This means that we can use it safely multiple times, whereas the resource syntax can be used only once. The drawback is that include can't currently be used with parameterized classes, on which more later.

Module Structure:
{module}/
	files/
	lib/
	manifests/
		init.pp
		{class}.pp
		{defined type}.pp
		{namespace}/
			{class}.pp
			{class}.pp
		{templates}/
		{tests}/

- the main directory should be named after the module
- all of the manifests go in the manifests directory
- init.pp that holds the modules main class.
- modules are very useful as they allow for namespacing and grouping of classes

foo/
	manifests/
		init.pp
		bar.pp
		bar/
			baz.pp
init.pp would contain class foo {}
bar.pp would contain class foo::bar {}
baz.pp should contain class foo:bar::baz {}

puppet:///module/testing/test.txt
	will go in {puppet}/modules/{$module}/files

~/.bashrc file
mkmod() {
	mkdir "$1"
	mkdir "$1/files" "$1/lib" "$1/manifests" "$1/templates" "$1/tests"
}

rendering from a template:
file {'/etc/foo.conf':
	ensure => file,
	require => Package['foo'],
	content => template('foo/foo.conf.erb'),
}

if you pass more than one template then it will evaluate both of them and concat the output

template('foo/one.erb', 'foo/two.erb');

Parameterized classes:

class mysql($user, $port) {}
class paramclassexample($value, $value2 = 'default value') {
	notify {'value 1 is ${value1: }
}

More examples/details available here http://docs.puppetlabs.com/guides/language_guide.html

About rp

Architect for large, highly scalable LAMP applications and Technical Manager with special focus on metrics based continuous improvement of teams and products. Rajat has close to a decade of experience of a very wide range of skills related to infrastructure, middleware, app servers all the way to front-end technologies and software development methodologies including agile, iterative waterfall, waterfall as well as ah-hoc startup using the right approach in the right context to reduce time to market.