You can use Bolt to apply blocks of Puppet code, called manifest blocks, to remote targets.
You can create manifest blocks that use existing content from the Forge, or use a plan to mix procedural orchestration and action with declarative resource configuration from a block. Most features of the Puppet language are available in a manifest block: classes, custom resource types, and functions. For information about what language features aren't supported, see Manifest block limitations.
🔩 Tip: If you installed Bolt as a Ruby Gem, make sure you have installed the core modules required to use the
puppet apply
command. These modules are listed in the Bolt GitHub repository and you can install them using the module management workflow.
📖 Related information
Similar to the puppet apply
command, which applies a
standalone Puppet manifest to a local system, you can use Bolt to apply
Puppet manifests to remote targets.
Manifest blocks require facts to compile. When Bolt applies Puppet manifests, it
automatically installs the packages necessary to run the apply command and
gathers facts using facter, making the
facts available to the manifest block. Bolt also identifies targets that do not
have Puppet agents and runs the puppet_agent::install
task to install the agent.
Note: Bolt installs the Puppet agent package to enable the use of Puppet code. It does not require setting up a server-agent architecture between the remote systems and the local system running Bolt.
Bolt can apply a manifest from a file by passing an absolute path to the manifest:
-
*nix shell command
bolt apply manifests/server.pp --targets servers
-
PowerShell cmdlet
Invoke-BoltApply -Manifest manifests/server.pp -Targets servers
Bolt can apply manifest code directly by passing the code to the execute
command-line option:
-
*nix shell command
bolt apply --execute "file { '/etc/puppetlabs': ensure => present }" --targets servers
-
PowerShell cmdlet
Invoke-BoltApply -Execute "file { '/etc/puppetlabs': ensure => present }" -Targets servers
When applying manifest blocks, it's important to understand the distinction between definitions and declarations.
Definitions, which include class definitions and defined resource types, are blocks of Puppet code that can be used when called by name. Defining a class or defined type makes the code available to a catalog, but does not add it to the catalog. A class or defined type that is not called by name, or declared, is not added to the catalog, and thus none of the code in those definitions will be applied to the targets.
When you apply a manifest containing only definitions, Bolt will issue a helpful warning to let you know that the apply will do nothing:
$ bolt apply --execute "define bolt { file { 'etc/puppetlabs': ensure => present } }" --targets servers
Manifest only contains definitions and will result in no changes on the targets.
. . .
📖 Related information
Bolt compiles the code in your manifest block into a catalog. Bolt compiles code in the following order:
- Facts gathered from the targets or set in your inventory.
vars
set in your inventory.
Like the code compiled with the puppet apply
command, all of the variables are
generated. As a result, you can reuse code between Bolt and Puppet. Bolt then
copies custom module content from the modulepath to the targets and applies the
catalog using Puppet.
After the catalog compiles and is executed successfully on all targets, bolt apply
displays the reports generated by applying the catalog on each target.
When you run the command, Bolt will first install required packages and gather facts, apply the catalog compiled from the manifest blocks, and then report a summary of any changes made on the remote systems:
Starting: install puppet and gather facts on target1, target2
Finished: install puppet and gather facts with 0 failures in 3.81 sec
Starting: apply catalog on target1, target2
Started on target1...
Started on target2...
Finished on target1:
changed: 1, failed: 0, unchanged: 0, skipped: 0, noop: 0
Finished on target2:
changed: 1, failed: 0, unchanged: 0, skipped: 0, noop: 0
Finished: apply catalog with 0 failures in 4.52 sec
Successful on 2 targets: target1,target2
Ran on 2 targets in 8.42 sec
You can apply Puppet code from a module downloaded from the Puppet Forge in a
YAML plan. To apply Puppet code in a YAML plan, use the YAML plan resources
step.
To learn more about applying Puppet code in YAML plans, see Writing YAML plans.
You can apply manifest blocks to remote systems during execution of a Puppet
plan using the apply
function.
Manifest blocks require facts to compile. If your plan includes a manifest
block, use the apply_prep
function in your plan before your manifest block.
The apply_prep
function installs the packages necessary to run the apply
command and gathers facts by running
facter, making the facts available to
the manifest block. The apply_prep
function also identifies the targets that
do not have Puppet agents and runs the puppet_agent::install
task to install the agent.
Note: Bolt installs the Puppet agent package to enable the use of Puppet code. It does not require setting up a server-agent architecture between the remote systems and the local system running Bolt.
The apply
function supports the following options:
-
_catch_errors => true
returns aResultSet
including failed results, rather than failing the plan. -
_description => <DESCRIPTION>
adds a description to the apply block, allowing you to distinguish apply blocks. -
_noop => true
applies the manifest block in Puppet no-operation mode, returning a report of the changes it would make, but takes no action. -
_run_as => <USER>
applies the manifest block as the specified user. (This option is for transports that allow a user to run commands under a different username.)
# Preview installing docker as root on $targets.
apply($targets, _catch_errors => true, _noop => true, _run_as => root) {
include 'docker'
}
Bolt compiles the code in your manifest block into a catalog. Bolt compiles code in the following order:
- Facts gathered from the targets or set in your inventory.
- Local variables from the plan.
vars
set in your inventory.
Like the code compiled with the puppet apply
command, all the variables are
generated. As a result, you can reuse code between Bolt and Puppet. Bolt then
copies custom module content from the Bolt modulepath to the targets and
applies the catalog using Puppet.
After the catalog compiles and is executed successfully on all targets, apply
returns the reports generated by applying the catalog on each target.
The apply
function returns a ResultSet
object
that contains an ApplyResult
object
for each target.
$results = apply($targets) { ... }
$results.each |$result| {
notice($result.report)
}
The ApplyResult
object includes a
report
method that returns a hash representation of the
Puppet::Transaction::Report
object. Each property on the object corresponds to a key with the same name in
the report hash. However, not every property is presented in the hash for every
result. Only properties that have a value from the apply are present in the
hash.
For example, to access and print the logs from a report in your plan, you can
access the logs
key in the report hash:
plan example (
TargetSpec $targets
) {
# Install the puppet-agent package and gather facts
$targets.apply_prep
# Apply Puppet code
$apply_results = apply($targets) {
file { '/etc/puppetlabs':
ensure => present
}
}
# Print log messages from the report
$apply_results.each |$result| {
$result.report['logs'].each |$log| {
out::message("${log['level'].upcase}: ${log['message']}")
}
}
}
Running the above plan prints output similar to this:
$ bolt plan run example --targets server
Starting: plan example
Starting: install puppet and gather facts on server
Finished: install puppet and gather facts with 0 failures in 4.01 sec
Starting: apply catalog on server
Finished: apply catalog with 0 failures in 4.59 sec
INFO: Applying configuration version '1612480754'
INFO: Creating state file /tmp/d20210204-3818-1az3s99/var/state/state.yaml
NOTICE: Applied catalog in 0.01 seconds
Finished: plan example in 8.64 sec
Each target requires a separate catalog be compiled with its unique facts
and
vars
. The apply action compiles and applies catalogs in parallel on the Bolt
host. Concurrency of catalog compilation is controlled by the
compile-concurrency
config option. This option is limited to twice the number
of threads your CPU can run concurrently. Catalog application, on the other
hand, uses the Bolt default thread pool controlled by the concurrency
option.
Use Hiera to separate configuration from context-specific data, where context might be fact-based or the name of a target.
Note: Only Hiera version 5 is supported in Bolt.
Hiera is a key-value configuration data look up system, used for separating data
from Puppet code. You use Hiera data to implicitly override default class
parameters. You can also explicitly look up data from Hiera via the lookup
function:
plan do_thing() {
apply('localhost') {
notice("Some data in Hiera: ${lookup('mydata')}")
}
}
Manifest block compilation can access Hiera data that you add to your Bolt
configuration. The default location for Hiera config is <PROJECT DIRECTORY>/hiera.yaml
.
You can change this with the hiera-config
key in a Bolt config file or the
--hiera-config
CLI option.
Following the Hiera 5 convention, the default data directory is relative to
hiera.yaml
at <PROJECT DIRECTORY>/data
. For configuration file examples, see Configuring
Hiera. Bolt
will not automatically load any Hiera data from this directory. If you want to load data from a
data file you must set the relevant paths in your Hiera config.
Note: The target name is made available to Hiera as
trusted.certname
. You can learn more about interpolating variables into Hiera config here.
If a custom data provider is used, such as hiera-eyaml
, which allows you to
encrypt your data, the gem dependencies must be available to Bolt. See Install
gems with Bolt packages.
Hiera interpolations are not supported in Bolt outside of apply blocks, because Hiera data is looked up per-target and plans do not run in a per-target context. If you want to use Hiera to look up data outside of an apply block, you can use a plan hierarchy.
You can use Puppet log functions in Bolt plans, but Bolt log levels do not map
directly to Puppet log levels. For example, a notice
function in a plan logs
at the info
level in Bolt. Log levels map as follows:
Puppet log level | Bolt log level |
---|---|
debug |
trace |
info |
debug |
notice |
info |
warning |
warn |
err |
error |
alert |
error |
emerg |
fatal |
crit |
fatal |
📖 Related information
In addition to the standard Puppet functions available to a catalog, such as
lookup()
, you can use the following Bolt functions in a manifest block:
Exported resources are not supported in manifest blocks. You must pass exported
resources directly instead of exporting and collecting them from PuppetDB. If
you need to interact with resources managed during a normal run, use the
puppetdb_query
function.
In addition, the following top-level variables, which exist in normal catalog compilation, are not included during manifest block compilation:
$server_facts
- Master variables like
$servername
$environment
You can optionally set these from a target's vars
, but they don't have
defaults in Bolt.
Create a manifest that sets up a web server with nginx, and run it as a plan.
-
Go to the
site-modules
directory in the default Bolt project directory:~/.puppetlabs/bolt/site-modules
-
Create a module named profiles.
- If you use the Puppet Development Kit:
pdk new module profiles
- Otherwise create
~/.puppetlabs/bolt/site-modules/profiles
- If you use the Puppet Development Kit:
-
Add a
plans
directory to the profiles module. -
In the plans directory, create a manifest file called
nginx_install.pp
and add the following code:plan profiles::nginx_install( TargetSpec $targets, String $site_content = 'hello!', ) { # Install the puppet-agent package if Puppet is not detected. # Copy over custom facts from the Bolt modulepath. # Run the `facter` command line tool to gather target information. $targets.apply_prep # Compile the manifest block into a catalog apply($targets) { if($facts['os']['family'] == 'redhat') { package { 'epel-release': ensure => present, before => Package['nginx'], } $html_dir = '/usr/share/nginx/html' } else { $html_dir = '/var/www/html' } package {'nginx': ensure => present, } file {"${html_dir}/index.html": content => $site_content, ensure => file, } service {'nginx': ensure => 'running', enable => 'true', require => Package['nginx'] } } }
-
Run the plan on a target:
bolt plan run profiles::nginx_install --targets mytarget.mydomain
-
In a web browser, open
mytarget.mydomain
. The page displays the texthello!
🔩 Tip: For complex web server deployments, consider adding the puppet-nginx module.
📖 Related information
Create a manifest that sets up a web server with IIS and run it as a plan.
-
Create a directory to use as your Bolt project or use an existing directory.
-
Convert the directory into a Bolt project named 'profiles' by running
bolt project init profiles
, orNew-BoltProject -Name profiles
if you're using PowerShell. -
Add the
puppetlabs-iis
module to yourbolt-project.yaml
under themodules
key:modules: - name: puppetlabs-iis version_requirement: 4.3.2
-
Run
bolt module install
orInstall-BoltModule
if you're using PowerShell. -
Create a new Puppet language Bolt plan inside your project by running
bolt plan new profiles
orNew-BoltPlan -Name profiles -Pp
on PowerShell. This creates aplans/
directory in your Bolt project, and a file namedinit.pp
inside that directory with some basic commands. -
Replace the contents of
<PROJECT DIRECTORY>/plans/init.pp
with this Bolt plan:plan profiles( TargetSpec $targets, String $site_content = 'hello!', ) { # Install the puppet-agent package if Puppet is not detected. # Copy over custom facts from the Bolt modulepath. # Run the `facter` command line tool to gather target information. $targets.apply_prep # Compile the manifest block into a catalog return apply($targets, '_catch_errors' => true) { $iis_features = ['Web-WebServer','Web-Scripting-Tools'] iis_feature { $iis_features: ensure => 'present', } # Delete the default website to prevent a port binding conflict. iis_site {'Default Web Site': ensure => absent, require => Iis_feature['Web-WebServer'], } iis_site { 'minimal': ensure => 'started', physicalpath => 'c:\\inetpub\\minimal', applicationpool => 'DefaultAppPool', require => [ File['minimal'], Iis_site['Default Web Site'] ], } file { 'minimal': ensure => 'directory', path => 'c:\\inetpub\\minimal', } file { 'content': ensure => 'file', path => 'c:\\inetpub\\minimal\\index.html', content => $site_content, } } }
This plan uses the
apply
function to apply a manifest to targets. The manifest uses the puppetlabs-iis module to set up an IIS server on the targets. -
Run the plan on a target: *nix shell command
bolt plan run profiles --targets winrm://mytarget.mydomain
PowerShell cmdlet
Invoke-BoltPlan -Name profiles -Targets winrm://mytarget.mydomain
-
In a web browser, open
mytarget.mydomain
. The page displays the texthello!
📖 Related information
- IIS
- Specify targets
- Bolt projects
- Puppet language Bolt plans
- For information on how Bolt loads the modulepath, see Modules overview.
- To learn more about using Puppet device modules, see Running Bolt on network devices