Creating Reusable Puppet Profiles
Bram Vogelaar
@attachmentgenie
$whoami
• Used to be a Molecular Biologist
• Then became a Dev
• Now an Ops
• Currently Cloud Engineer @ The Factory
Webserver.pp circapuppet 0.24
includeapache
apache::vhost{ 'vhost.example.com':
port => '80',
docroot => '/var/www/vhost',
}
include::php
class { '::mysql::server':
root_password=> 'secret',
}
class { '::mysql::server::account_security':}
class { '::mysql::client': }
mysql::db{ 'mydb':
user => 'myuser',
password=> 'topsecret',
host => 'localhost',
grant => ['SELECT', 'UPDATE'],
}
include::firewall
firewall{'http':
dport => '80',
proto => 'tcp',
action => 'accept',
}
Webserver.pp circapuppet 0.24
class customer_x inherits webserver {
includecustomer_x
}
class customer_y inherits webserver {
includecustomer_y
}
A design pattern emerged
• Component modules — Modules that manage one particular technology.
• Profiles — Wrapper classes that use (multiple) component modules to configure a layered technology stack.
• Roles — Wrapper classes that use multiple profiles to build a complete system configuration.
https://www.craigdunn.org/2012/05/239/
Roles & Profiles
class roles::webserver {
include::profiles::apache
}
class roles::database {
include::profiles::mysql
}
class roles::allinone {
include::profiles::apache
include::profiles::mysql
}
• profiles::website::apache::vhosts:
'icinga.alerting.vagrant':
port:80
docroot:'/usr/share/icingaweb2/public'
directories:
- 'icingaweb':
path:'/usr/share/icingaweb2/public'
directoryindex:'index.php'
options:
- 'SymLinksIfOwnerMatch'
setenv:
- 'ICINGAWEB_CONFIGDIR"/etc/icingaweb2"'
rewrites:
- 'icingaweb':
comment: 'Icingaweb2'
rewrite_base:'/icingaweb2'
rewrite_cond:
- '%%{}{REQUEST_FILENAME} -s[OR]'
- '%%{}{REQUEST_FILENAME} -l [OR]'
- '%%{}{REQUEST_FILENAME} -d‘
…
YAML Programming
Roles & Profiles
• First approach: Granular roles
• Second approach: Conditional logic
• Third approach: Nested roles
• Fourth approach: Multiple roles per node
• Fifth approach: Super profiles
• Sixth approach: Building roles in the node classifier
https://puppet.com/docs/pe/2021.3/osp/designing_convenient_roles.html
modules ❯ pdk new module [--template-url=<value>]
modules ❯ pdk convert [--template-url=<value>]
modules ❯ pdk new class profiles::technology
modules ❯ pdk new defined_type profiles::technology::thing
modules ❯ bundle exec rake pdksync
Standardize your layout (and toolset)
#Installs,configures,andmanagesnomad
#
#@exampleTosetup asinglenomadserver,withseveralagentsattached,ontheserver.
# class { 'nomad':
# config_hash=>{
# 'region' =>'us-west',
# 'datacenter'=>'ptk',
# 'bind_addr' =>'0.0.0.0',
# 'data_dir' =>'/opt/nomad',
# 'server' =>{
# 'enabled' => true,
# 'bootstrap_expect'=>3,
# }
# }
# }
Document yourcode…no really!
modules ❯ pdk validate –parallel
modules ❯ pdk test unit
Validate your code… no really
• class roles::webserver {
includeprofiles::bootstrap
include profiles::database
includeprofiles::webserver
}
Roles & Profiles
class nginx (
$temp_path = ‘/tmp’,
$confd_only = false,
$daemon = undef,
$severity = 'error‘,
…
Using types
class profiles::website::nginx (
Stdlib::Absolutepath $temp_path,
Boolean $confd_only = false,
Optional[Enum['on', 'off']] $daemon = undef,
Nginx::Severity $severity = 'error',
…
Using types
type Nginx::Severity = Enum[
'debug',
'info',
'notice',
'warn',
'error',
'crit',
'alert',
'emerg' # lint:ignore:trailing_comma
]
…
Using types
modules ❯ grep -r "apache::vhost" . | wc –l
modules ❯ 75
(D)on’t (R)epeat (Y)ourself
• define profile_static_app::vhost(
String$public_name,
String$internal_name,
String$package_name,
) {
::profile_static_app::instance{$public_name:
package_name=>$package_name,
ensure => $package_ensure,
}
::profile_apache::vhost{$public_name:
docroot =>$docroot,
internal_name => $internal_name,
internal_aliases=>$internal_aliases,
public_name =>$public_name,
public_aliases =>$public_aliases,
force_ssl =>$force_ssl,
letsencrypt =>$letsencrypt,
rproxy_type =>$rproxy_type,
rproxy_basicauth=> $rproxy_basicauth,
custom_fragment =>$custom_fragment,
}
}
Defined Types (vhost)
• php::fpm::pool{ $name:
access_log => $acces_log,
listen => $fpm_listen,
pm =>$process_manager,
pm_status_path=>"/${name}_status",
}
if $create_cachetool_config{
::profile_php::cachetool::config{ $name:
fpm_listen=>$fpm_listen,
location =>$cachetool_config_dir,
}
}
if $enable_ship_logs{
::profile_base::rsyslog::file{ "${name}-pool":
location=>$acces_log,
}
}
if $enable_ship_metrics{
notify{ 'nometricstoseehere':}
}
}
Defined Types (app instance)
create_resources( 'apache::vhost', $vhosts, $instances)
VS
create_resources( '::profile_symfony::vhost', $instances)
create_resources( '::profile_drupal::vhost', $instances)
Profiles andDefined Types
Feature flag it all
class profiles::website (
Boolean $apache = false,
Boolean $nginx = false,
Boolean $traefik = false,
){
if $apache{
class {'::profiles::website::apache': }
}
if $nginx{
class {'::profiles::website::nginx': }
}
if $traefik{
class {'::profiles::website::traefik': }
}
}
---
classes:
- roles::prometheus
profiles::alerting::alertmanager:true
profiles::bootstrap::keepalive:true
…
Feature flag it all
Contact
bram@attachmentgenie.com
@attachmentgenie
https://www.slideshare.net/attachmentgenie
https://github.com/attachmentgenie/attachmentgenie-profiles
The Floor is yours…
Questions ?

Creating Reusable Puppet Profiles