Applying Puppet code

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

Applying manifest blocks from the command line

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.

Applying manifest files

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

Applying manifest code

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

Definitions and declarations

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

How manifest blocks are applied

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.

Return value

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

Applying Puppet code from Puppet Forge modules in a YAML plan

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.

Applying manifest blocks from a Puppet plan

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.

Options

The apply function supports the following options:

  • _catch_errors => true returns a ResultSet 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'
}

How manifest blocks are applied

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.

Return value

The apply function returns a ResultSet object that contains an ApplyResult object for each target.

$results = apply($targets) { ... }
$results.each |$result| {
   notice($result.report)
}

Report keys

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

Configuring concurrency

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.

Using Hiera data in a manifest block

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.

Puppet log functions in Bolt

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

Available plan functions

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:

Manifest block limitations

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.

Examples

Create a sample manifest for nginx on Linux

Create a manifest that sets up a web server with nginx, and run it as a plan.

  1. Go to the site-modules directory in the default Bolt project directory: ~/.puppetlabs/bolt/site-modules

  2. Create a module named profiles.

    • If you use the Puppet Development Kit: pdk new module profiles

    • Otherwise create ~/.puppetlabs/bolt/site-modules/profiles

  3. Add a plans directory to the profiles module.

  4. 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']
           }
         }
       }
  5. Run the plan on a target:

    bolt plan run profiles::nginx_install --targets mytarget.mydomain
  6. In a web browser, open mytarget.mydomain. The page displays the text hello!

🔩 Tip: For complex web server deployments, consider adding the puppet-nginx module.

📖 Related information

Create a Bolt plan for IIS on Windows

Create a manifest that sets up a web server with IIS and run it as a plan.

  1. Create a directory to use as your Bolt project or use an existing directory.

  2. Convert the directory into a Bolt project named 'profiles' by running bolt project init profiles, or New-BoltProject -Name profiles if you're using PowerShell.

  3. Add the puppetlabs-iis module to your bolt-project.yaml under the modules key:

    modules:
      - name: puppetlabs-iis
        version_requirement: 4.3.2
  4. Run bolt module install or Install-BoltModule if you're using PowerShell.

  5. Create a new Puppet language Bolt plan inside your project by running bolt plan new profiles or New-BoltPlan -Name profiles -Pp on PowerShell. This creates a plans/ directory in your Bolt project, and a file named init.pp inside that directory with some basic commands.

  6. 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.

  7. 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
  8. In a web browser, open mytarget.mydomain. The page displays the text hello!

📖 Related information