Conditional statements and expressions
Conditional statements let your Puppet code behave differently in different situations. They are most helpful when combined with facts or with data retrieved from an external source. Puppet supports if and unless statements, case statements, and selectors.
Examples
if
statement evaluates the given
condition and, if the condition resolves to true
, executes the given code.
This example includes an elsif
condition, and gives a warning if you try to
include the ntp
class on a virtual machine or on machine running macOS:if $facts['is_virtual'] {
warning('Tried to include class ntp on virtual machine; this node might be misclassified.')
} elsif $facts['os']['family'] == 'Darwin' {
warning('This NTP module does not yet work on our Mac laptops.')
} else {
include ntp
}
unless
statement takes a Boolean condition and an arbitrary block of Puppet code, evaluates the condition, and if the condition is
false, execute the code block. This statement sets $maxclient
to 500 unless
the system memory is above the specified
parameter.unless $facts['memory']['system']['totalbytes'] > 1073741824 {
$maxclient = 500
}
case $facts['os']['name'] {
'RedHat', 'CentOS': {
include role::redhat
}
/^(Debian|Ubuntu)$/: {
include role::debian
}
default: {
include role::generic
}
}
$rootgroup = $facts['os']['family'] ? {
'RedHat' => 'wheel',
/(Debian|Ubuntu)/ => 'wheel',
default => 'root',
}
file { '/etc/passwd':
ensure => file,
owner => 'root',
group => $rootgroup,
}
if
statements
An "if" statement
takes a Boolean condition and an arbitrary block of Puppet
code, and executes the code block only if the condition is true. Optionally, an if
statement can include elsif
and else
clauses.
Behavior
Puppet's if
statements behave much
like those in any other language. The if
condition
is evaluated first and, if it is true, the if
code
block is executed. If it is false, each elsif
condition (if present) is tested in order, and if all conditions fail, the else
code block (if present) is executed. If none of the
conditions in the statement match and there is no else
block, Puppet does nothing and
moves on. If statements executes a maximum of one code block.
In addition to executing the code in a block, an if
statement also produces a value, so the if
statement
can be used wherever a value is allowed.The value of an if
expression is the value of the last expression in the executed
block, or undef
if no block was executed.
Syntax
if
statement consists of: -
The
if
keyword. -
A condition (any expression resolving to a Boolean value).
-
A pair of curly braces containing any Puppet code.
-
Optionally: any number of
elsif
clauses, which are processed in order. -
Optionally: the
else
keyword and a pair of curly braces containing Puppet code.
elsif
clause consists of: -
The
elsif
keyword. -
A condition.
-
A pair of curly braces containing any Puppet code.
if $facts['is_virtual'] {
# Our NTP module is not supported on virtual machines:
warning('Tried to include class ntp on virtual machine; this node might be misclassified.')
} elsif $facts['os']['name'] == 'Darwin' {
warning('This NTP module does not yet work on our Mac laptops.')
} else {
# Normal node, include the class.
include ntp
}
Conditions
The condition of an if
statement can be any expression that resolves
to a Boolean value. This includes:
-
Expressions, including arbitrarily nested
and
andor
expressions -
Functions that return values
Expressions that resolve to non-Boolean values are automatically converted to Booleans. For more information, see the Booleans documentation.
Regex capture variables
$1
, $2
), and the entire match is available as $0
. This example captures any digits from a hostname
such as www01
and www02
, and stores them in the $1
variable: if $trusted['certname'] =~ /^www(\d+)\./ {
notice("Welcome to web server number $1.")
}
Regex capture variables are different from other variables in a couple of ways:
-
The values of the numbered variables do not persist outside the code block associated with the pattern that set them.
-
In nested conditionals, each conditional has its own set of values for the set of numbered variables. At the end of an interior statement, the numbered variables are reset to their previous values for the remainder of the outside statement. This causes conditional statements to act like local scopes, but only with regard to the numbered variables.
unless
statements
"Unless" statements work
like reversed if
statements. They take a
Boolean condition and an arbitrary block of Puppet code, evaluate
the condition, and if it is false, execute the code block. They cannot include elsif
clauses.
Behavior
The condition is evaluated first and, if it is false, the code block is executed. If the condition is true, Puppet does nothing and moves on.
In addition to executing the code in a block, an unless
statement is also an
expression that produces a value, and it can be used wherever a value is allowed. The value
of an unless
expression is
the value of the last expression in the executed block. If no block was executed, the value
is undef
.
Syntax
unless
statement is:-
The
unless
keyword. -
A condition (any expression resolving to a Boolean value).
-
A pair of curly braces containing any Puppet code.
-
Optionally: the
else
keyword and a pair of curly braces containing Puppet code.
elsif
clause in an
unless
statement. If you do, compilation fails with a syntax
error.unless $facts['memory']['system']['totalbytes'] > 1073741824 {
$maxclient = 500
}
The condition of an unless
statement can be any expression that resolves to a Boolean value. This
includes:
-
Expressions, including arbitrarily nested
and
andor
expressions. -
Functions that return values.
Expressions that resolve to non-Boolean values are automatically converted to Booleans. For more information, see the Booleans documentation.
Regex capture variables
Although unless
statements receive regex capture variables like if
statements, you wouldn't usually use one, because the code in
the statement is executed only if the condition doesn't match anything. It generally makes
more sense to use an if
statement.
case
statements
Like if
statements,
case statements
choose one of several blocks of arbitrary Puppet code to
execute. They take a control expression and a list of cases and code blocks, and execute the
first block whose case value matches the control expression.
Puppet compares the control expression to each of the cases, in the order they are listed (except for the top-most level default case, which always goes last). It executes the block of code associated with the first matching case, and ignores the remainder of the statement.Case statements execute a maximum of one code block. If none of the cases match, Puppet does nothing and moves on.
In addition
to executing the code in a block, a case
statement
is also an expression that produces a value, and can be used wherever a value is
allowed. The value of a case
expression is the value
of the last expression in the executed block. If no block was executed, the value is
undef
.
The control expression of a case statement can be any expression that resolves to a value. This includes:
-
Functions that return values.
Syntax
The general form of a case statement is:
The
case
keyword.A control expression, which is any expression resolving to a value.
An opening curly brace.
-
Any number of possible matches, which consist of:
A case or comma-separated list of cases.
A colon.
A pair of curly braces containing any arbitrary Puppet code.
A closing curly brace case.
case $facts['os']['name'] {
'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
}
Case matching
A case can be any expression that resolves to a value, for example, literal
values, variables and function calls. You can use a comma-separated list of cases to
associate multiple cases with the same block of code. To use values from a variable as
cases, use the *
splat
operator to convert an array of values into a comma-separated list of values.
Depending on the data type of a case's value, Puppet uses one of following behaviors to test whether the case matches:
Most data types, for example, strings and Booleans, are compared to the control value with the
==
equality operator, which is case-insensitive when comparing strings.Regular expressions are compared to the control value with the
=~
matching operator, which is case-sensitive. Regex cases only match strings.Data types, such as
Integer
, are compared to the control value with the=~
matching operator. This tests whether the control value is an instance of that data type.Arrays are recursively compared to the control value. First, Puppet checks whether the control and array are the same length, then each corresponding element is compared using these same case matching rules.
Hashes compare each key-value pair. To match, the control value and the case must have the same keys, and each corresponding value is compared using these same case matching rules.
The special value
default
matches anything, and unless nested inside an array or hash, is always tested last regardless of its position in the list.
Regex capture variables
$1
,
$2
), and the entire match
is available as $0
:
case $trusted['hostname'] {
/www(\d+)/: { notice("Welcome to web server number ${1}"); include role::web }
default: { include role::generic }
}
This example captures any digits from a hostname such as www01
and www02
and store them in the $1
variable.
Regex capture variables are different from other variables in a couple of ways:
The values of the numbered variables do not persist outside the code block associated with the pattern that set them.
In nested conditionals, each conditional has its own set of values for the set of numbered variables. At the end of an interior statement, the numbered variables are reset to their previous values for the remainder of the outside statement. This causes conditional statements to act like local scopes, but only with regard to the numbered variables.
Best practices
Case statements must have a default case:
If the rest of your cases are meant to be comprehensive, putting a
fail('message')
call in the default case makes your code more robust by protecting against mysterious failures due to behavior changes elsewhere in your manifests.If your cases aren't comprehensive and you want nodes that match none to do nothing, write a default case with an empty code block (
default: {}
). This makes your intention obvious to the next person who maintains your code.
Selector expressions
Selector expressions are similar to case statements, but instead of executing code, they return a value.
Behavior
The entire selector expression is treated as a single value.Puppet compares the control expression to each of the cases, in
the order they are listed (except for the default
case,
which always goes last). When it finds a matching case, it treats that value as the value of
the expression and ignore the remainder of the expression. If none of the cases match, Puppet fails compilation with an error, unless a default
case is also provided.
-
Functions that return values
-
Variable assignments
-
Resource attributes
-
Function arguments
-
Resource titles
-
A value in another selector
-
Expressions
Syntax
Selectors resemble a cross between a case statement and the ternary operator found in other languages. The general form of a selector is:
-
A control expression, which is any expression resolving to a value.
-
The
?
(question mark) keyword. -
An opening curly brace.
-
Any number of possible matches, each of which consists of:
-
A case.
-
The
=>
(hash rocket) keyword. -
A value, which can be any expression resolving to a value.
-
A trailing comma.
-
- A closing curly brace.
$rootgroup
is determined using the
value of $facts['os']['family']
:
$rootgroup = $facts['os']['family'] ? {
'Redhat' => 'wheel',
/(Debian|Ubuntu)/ => 'wheel',
default => 'root',
}
file { '/etc/passwd':
ensure => file,
owner => 'root',
group => $rootgroup,
}
Case matching
In selector statements, you cannot use lists of cases. If the control expression is a string and you need more than one case associated with a single value, use a regular expression. Otherwise, use a case statement instead of a selector, because case statements do allow lists of cases. For more information, see Case statements.
Regex capture variables
If you use regular expression cases, any captures
from parentheses in the pattern are available inside the associated value as numbered
variables ($1
, $2
), and the entire match is
available as $0
:
puppet
$system = $facts['os']['name'] ? {
/(RedHat|Debian)/ => "our system is ${1}",
default => "our system is unknown",
}
Regex capture variables are different from other variables in a couple of ways:
-
The values of the numbered variables do not persist outside the value associated with the pattern that set them.
-
In nested conditionals, each conditional has its own set of values for the set of numbered variables. At the end of an interior statement, the numbered variables are reset to their previous values for the remainder of the outside statement. This causes conditional statements to act like local scopes, but only with regard to the numbered variables.