Table of contents
1.
Introduction
2.
Writing plans
3.
Naming Plans
4.
Defining plan permissions
4.1.
Use parameter types to fine-tune access.
4.2.
Specifying plan parameters
5.
Using Hiera data in plans
6.
Target Objects
6.1.
TargetSpec
6.2.
Set variables and facts on targets
7.
Collect facts from the target
7.1.
Collect facts from PuppetDB
8.
Returning results from Plans
9.
Frequently Asked Question
9.1.
What is Puppet PDK?
9.2.
What is a forge puppet?
9.3.
What are modules in Puppet?
10.
Conclusion
Last Updated: Mar 27, 2024
Medium

Writing Plans in Puppet Enterprise

Author Muskan Sharma
0 upvote
Career growth poll
Do you think IIT Guwahati certified course can help you in your career?

Introduction

Hey Readers!!

Have you ever heard the term puppet?

It's not the Puppet we played with in our childhood.

Puppet is a software configuration management and deployment tool that is open source. It is most commonly used on Linux and Windows to manage multiple application servers simultaneously.

In this article, you will learn about WritingPlans in Puppet Enterprise.

Let's begin!! 

Writing Plans in Puppet Enterprise

Writing plans

Plans enable you to execute several tasks with a single command, compute input values for tasks. By wrapping activities and other operations within plans, they also give you more flexibility when establishing unique RBAC limits.

Writing Puppet plans provides better error handling and more sophisticated control than YAML plans. Plans written in the Puppet language also enable you to apply Puppet code blocks to remote targets.

Naming Plans

Place plan files with the following file extensions in your module's./plans directory:

  • Puppet plans extension- .pp 
  • YAML plans extension- .yaml, not .yml  

Plan names are made up of two or more name segments that indicate:

  • The name of the module in which the plan is located.
  • The plan file's name, without the extension.
  • The path within the module if plan is in a subdirectory of ./plans.

Each name segment of a plan:

  • It must start with a lowercase letter.
  • Lowercase letters, digits, or underscores are acceptable.
  • It cannot be a reserved word.
  • The name must not be the same as any Puppet data type.
  • Namespace segments must correspond to the regular expression \A[a-z][a-z0-9_]*\Z

Defining plan permissions

RBAC for plans is not the same as RBAC for individual tasks. This distinction means that the user can be barred from running a specific task while still being able to run a plan that includes that task.

Defining plan permissions

The RBAC structure for plans enables you to create plans that provide more robust, custom control over task permissions.

For example, if you're configuring permissions for a new user to run plan infra::upgrade git, you can permit them to run the package task but only the git package.

plan infra::upgrade_git (
  TargetSpec $targets,
  Integer $version,
) {
  run_task(‘package’, $targets, name => ’git’, action => ‘upgrade’, version => $version)
}

Use parameter types to fine-tune access.

Parameter types add a layer of control over user permissions. The plan in the upgrade_git example above only provides access to the git package, but users can select any version of git they want. If some versions of the git package have known vulnerabilities, you can use parameter types like Enum to limit the version parameter to safe deployment versions.

The Enum, for example, limits the $version parameter to versions 1:2.17.1-1ubuntu0.13 and 1:2.17.0-1ubuntu1.

plan infra::upgrade_git (
  TargetSpec $targets,
  Enum['1:2.17.1-1ubuntu0.13', '1:2.17.0-1ubuntu1'] $version,
) {
  run_task(‘package’, $targets, name => ‘git’, action => ‘upgrade’, version => $version)
}

PuppetDB queries can also be used to select parameter types.

For example, if you want to limit the targets on which infra::upgrade_git can run.

plan infra::upgrade_git (
  TargetSpec $targets,
  Enum['1:2.17.1-1ubuntu0.13', '1:2.17.0-1ubuntu1'] $version,
) {
  run_task(‘package’, $targets, name => ‘git’, action => ‘upgrade’, version => $version)
}


plan infra::upgrade_git (
   Enum[''1:2.17.1-1ubuntu0.13', '1:2.17.0-1ubuntu1''] $version,
) {
  # Use the puppetdb to find nodes from the other team's web cluster
  $query = [from, nodes, ['=', [fact, cluster], "other_team"]]
  $selected_nodes = puppetdb_query($query).map() |$target| {
    $target[certname]
  }
  run_task(‘package’, $selected_nodes, name => ‘git’, action => ‘upgrade’, version => $version)
}

Specifying plan parameters

Plan parameters can determine which targets to run different parts of your plan. A parameter can be passed as a single target name, a comma-separated list of target names, a Target data type, or an array.

  • The target parameters $load balancers and $webservers are specified as data type TargetSpec in the example plan below.
  • The plan then invokes the run_task function to specify which targets should be used to execute the tasks.
  • By iterating over the list of the Target objects returned by get_targets, the Target names are collected and saved in $webserver names.
  • The task parameters are serialized to JSON format, so extracting the names into an array of strings ensures that the webservers parameter is in a JSON-convertible format.
plan mymodule::my_plan(
  TargetSpec $load_balancer,
  TargetSpec  $webservers,
) {

  # Extract the name of the targets from $webservers
  $webserver_names = get_targets($webservers).map |$n| { $n.name }
 
  # process webservers
  run_task('mymodule::lb_remove', $load_balancer, webservers => $webserver_names)
  run_task('mymodule::update_frontend_app', $webservers, version => '1.2.3')
  run_task('mymodule::lb_add', $load_balancer, webservers => $webserver_names)
 }

Pass the parameters as parameter=value to execute this plan from the command line. The Targetspec accepts an array of JSON target names or a comma-separated string of target names.

puppet plan run mymodule::myplan
load_balancer=lb.myorg.com
webservers='["webserver1.myorg.com","webserver2.myorg.com"]'


Passed parameters to the run_* plan functions are serialized to JSON. In the plan below, the default value of $example_nul is undef. The plan uses the example_nul parameter to call the test::demo undef bash.

plan test::parameter_passing (
  TargetSpec $targets,
  Optional[String[1]] $example_nul = undef,
) {
  return run_task('test::demo_undef_bash', $targets, example_nul => $example_nul)
     }

Implementation of demo_undef_bash.sh

#!/bin/bash
example_env=$PT_ex1_nul
echo "Coding Ninjas: $PT_ex1_nul"
echo "Ninjas:"
     cat -

Using Hiera data in plans

To look up Hiera data, use the lookup() function in plans. You can look up data from inside and outside of apply blocks or use the plan_hierarchy key to look up data inside and outside of apply blocks within the same plan.

You can look up static Hiera data outside of apply blocks by adding a plan hierarchy key at the same level as the hierarchy key to your Hiera configuration.

Static Hiera data can also store user-specific information you want the plan to look up.

Consider the Hiera configuration below, which can be found at <ENV DIR>/hiera.yaml.

version: 5
hierarchy:
  - name: "Target specific data"
    path: "targets/%{trusted.certname}.yaml"
  - name: "Per-OS defaults"
    path: "os/%{facts.os.family}.yaml"
  - name: Common
    path: hierarchy.yaml
 
plan_hierarchy:
  - name: Common
    path: plan_hierarchy.yaml

You can use Hiera to look up a per-target filepath inside an application to block and set a user-specific API key in the plan_hierarchy.yaml data file by using the following data:

Use the data found at <ENV DIR>/data/plan_hierarchy.yaml:

api_key: 1357

As a result, in the first lookup() call, the plan looks up the API key, and in the apply block, it looks up the target-specific data:

plan plan_lookup(
  TargetSpec $targets
) {
  $outside_apply = lookup('api_key')
  run_task("make_request", $targets, 'api_key' => $outside_apply)
  $in_apply = apply($targets) {
    file { ${confpath}:
      ensure  => file,
      content => "setting: false"
    }
  }
}

Target Objects

A target and its specific connection options are represented by the Target object.

For the duration of a plan, the state of a target is stored in the code, allowing you to collect facts or set variables for a target and retrieve them later. Target objects must refer to a PE inventory target.

TargetSpec

The TargetSpec type is a target definition wrapper that allows you to pass single or multiple targets into a plan.

plan loop(TargetSpec $targets) {
  get_targets($targets).each |$target| {
    run_task('my_task', $target)
  }
}

Set variables and facts on targets

To set transport configuration values, variables, and facts from a plan, use the $target.facts() and $target.vars() functions.

Example:

plan variables(String $host) {
  $target = get_targets($host)[0]
  $target.set_var('newly_provisioned', true)
  $targetvars = $target.vars
  run_command("echo 'Variables for ${host}: ${$targetvars}'", $host)
}

Alternatively, use the vars key at the group level to set variables in the inventory file.

groups:
  - name: my_targets
    targets:
      - localhost
    vars:
      operatingsystem: windows
    config:
      transport: ssh

Collect facts from the target

The facts plan links to targets, discovers facts, and stores them on the targets.

To gather information, the plan employs the following methods:

  • It executes a Bash script on ssh targets.
  • It executes a PowerShell script on winrm targets.
  • Facter is run on pcp or targets where the Puppet agent is present.

For example:

plan run_with_facts(TargetSpec $targets) {
  # This collects facts on targets, updates the inventory
  run_plan(facts, targets => $targets)

  $centos_targets = get_targets($targets).filter |$n| { $n.facts['os']['name'] == 'CentOS' }
  $ubuntu_targets = get_targets($targets).filter |$n| { $n.facts['os']['name'] == 'Ubuntu' }
  run_task(centos_task, $centos_targets)
  run_task(ubuntu_task, $ubuntu_targets)
}

Collect facts from PuppetDB

When targets are running the Puppet agent and sending facts to PuppetDB, you can use the puppetdb fact plan to collect the facts for them.

For example:

plan run_with_facts(TargetSpec $targets) {
  # This collects facts on updates and targets the inventory
  run_plan(puppetdb_fact, targets => $targets)

  $centos_targets = get_targets($targets).filter |$n| { $n.facts['os']['name'] == 'CentOS' }
  $ubuntu_targets = get_targets($targets).filter |$n| { $n.facts['os']['name'] == 'Ubuntu' }
  run_task(centos_task, $centos_targets)
  run_task(ubuntu_task, $ubuntu_targets)
}

Returning results from Plans

Return results can be used in other plans or saved for later use by using the return function.

Any plan that does not use the return function yields undef.

Example:

plan return_result(
  $targets
) {
  return run_task('mytask', $targets)
}

Frequently Asked Question

What is Puppet PDK?

The Puppet Development Kit (PDK) contains significant Puppet code development and testing tools for Linux, Windows, and OS X workstations.

What is a forge puppet?

Puppet Forge is a repository of modules created by Puppet, our partners, and the community to assist IT operations practitioners in supercharging and simplifying their automation processes.

What are modules in Puppet?

Modules are known as the collection of manifests, templates, and files.

Conclusion

In this article, we extensively discussed Writing Plans in Puppet Enterprise. Defining plan permission. We also discussed Using Hiera data plans. In the end, we saw the Collected facts from the target.

For more content, Refer to our guided paths on Coding Ninjas Studio to upskill yourself. If you want to explore more, feel free to see our coursesinterview experiences, problems to solvetest serieslibraries, and resources

Thank You

Do upvote our blogs if you find them helpful!

Live masterclass