To run external scripts (like shell, Python, or PowerShell scripts) using Puppet, you primarily leverage Puppet Bolt tasks. These tasks provide a robust and controlled way to execute arbitrary code on remote systems or locally, going beyond standard Puppet resource management.
How to Run Scripts with Puppet (Using Bolt Tasks)
Puppet Bolt tasks are the go-to method for executing custom scripts through Puppet. They allow you to define and run executable files as part of your automation workflows, making complex operational tasks repeatable and scalable.
Understanding Puppet Bolt Tasks
A Puppet Bolt task is essentially any executable file (e.g., a Bash script, Python script, PowerShell script) that Bolt can run on target nodes. Tasks are designed to perform specific actions that might not be easily managed through declarative Puppet manifests alone, such as restarting services, deploying applications, or gathering dynamic information.
Where to Store Your Scripts (Task Files)
For Bolt to discover and execute your scripts effectively, they should be placed in designated directories within your Puppet modules or Bolt project.
The scripts/
Directory (Preferred for Tasks)
The scripts/
directory within your Puppet module is the preferred location for task executables. When a script is placed here, Bolt automatically recognizes it as a task.
- Example Structure:
my_module/scripts/my_script.sh
- Referencing for Execution: When running from the command line, you reference these as
my_module::my_script
. - Referencing for Loading/Plans: If you need to explicitly reference the script file's path within a Bolt plan or task metadata, you would use
<my_module/scripts/my_script.sh>
.
The files/
Directory (Alternative for Supporting Scripts)
You can also store scripts and other supporting files in the files/
directory of a module. Scripts in this directory are not automatically recognized as Bolt tasks but can be used as supporting files for tasks or plans, often uploaded to targets before execution.
- Example Structure:
my_module/files/utility_script.py
- Referencing for Loading/Plans: To load or reference a file from this directory within Bolt operations (e.g., to upload it to a target), you use the format
<my_module/files/utility_script.py>
.
Path Formats for Referencing Scripts
When working with Bolt, you can specify script locations using different path types:
- Module-Relative Paths: The most common way to refer to scripts within a module, such as
my_module/scripts/my_script.sh
ormy_module/files/another_script.py
. Bolt interprets these paths relative to the root of your Bolt project or module. - Absolute File Paths: You can use an absolute path to a script file on your Bolt controller, like
/path/to/my_script.sh
. While flexible for ad-hoc use, this is less portable for reusable tasks.
How to Execute a Script with Puppet Bolt
There are several ways to run your scripts using Bolt, depending on your needs.
1. Running Scripts as Bolt Tasks from the Command Line
This is the most direct way to execute a predefined task.
Command | Description |
---|---|
bolt task run <TASK_NAME> -t <TARGETS> |
Runs a task defined in a module's scripts/ directory on specified target nodes. Parameters are passed as key=value . |
bolt command run '<COMMAND>' -t <TARGETS> |
Executes an arbitrary command string on the specified targets. This can include executing a script that has already been uploaded. |
Example: Running a Task from the scripts/
directory
Let's say you have a script my_module/scripts/hello.sh
:
#!/bin/bash
# my_module/scripts/hello.sh
echo "Hello, $PT_name!"
To run this task, passing a parameter:
bolt task run my_module::hello name=World -t localhost
2. Executing Arbitrary Scripts using Bolt Plans
For scripts that are not formal Bolt tasks (e.g., in files/
), or for more complex orchestration, you can use Bolt plans. A plan allows you to combine multiple steps, including uploading files and running commands.
Example: Running a script from the files/
directory via a Plan
Suppose you have my_module/files/my_utility.sh
that you want to run:
#!/bin/bash
# my_module/files/my_utility.sh
echo "Running a utility script from files/ directory."
hostname
You could create a Bolt plan my_module/plans/run_utility.pp
:
# my_module/plans/run_utility.pp
plan my_module::run_utility(TargetSpec $targets) {
$script_path_on_target = '/tmp/my_utility.sh'
# Upload the script from the module's files/ directory
# Note the syntax: <module/files/script.sh>
upload(get_targets($targets), '<my_module/files/my_utility.sh>', $script_path_on_target)
# Make the script executable on the target
run_command("chmod +x ${script_path_on_target}", $targets)
# Execute the script on the target
run_command($script_path_on_target, $targets)
}
Then, run this plan from your command line:
bolt plan run my_module::run_utility -t webserver
3. Running Tasks from Puppet Manifests or Plans
You can integrate Bolt tasks directly into your Puppet code using the run_task
function within Puppet plans or even in Puppet manifests (though plans are generally preferred for task orchestration).
Example: Calling a Task from a Puppet Plan
In a plan like my_module/plans/deploy_app.pp
, you can trigger another task:
# my_module/plans/deploy_app.pp
plan my_module::deploy_app(TargetSpec $targets) {
# ... other deployment steps ...
# Run a task to restart a service after deployment
run_task('my_module::restart_service', $targets, service_name => 'nginx')
# Assuming 'my_module::restart_service' is a task defined in my_module/scripts/restart_service.sh
}
Then, execute the plan:
bolt plan run my_module::deploy_app -t production_servers
Essential Script Considerations for Bolt Tasks
When writing scripts to be used with Puppet Bolt, keep the following in mind for optimal performance and reliability:
- Shebang Line: Always include a shebang line at the top of your script (e.g.,
#!/bin/bash
,#!/usr/bin/env python
). This tells the operating system which interpreter to use. - Executable Permissions: Ensure your script has executable permissions (
chmod +x myscript.sh
). Bolt typically handles this automatically for tasks in thescripts/
directory, but you might need to manage it manually if uploading withbolt file upload
and then executing withbolt command run
. - Parameters: For Bolt tasks, parameters passed via
key=value
are made available to your script as environment variables prefixed withPT_
. For instance, a parametername=World
becomes$PT_name
in a Bash script. - Exit Codes: Scripts should return an exit code of
0
for success and a non-zero exit code (e.g.,1
) for failure. Bolt uses these codes to determine the success or failure status of the task. - Error Handling: Implement robust error handling and logging within your scripts to make debugging easier.
Best Practices for Scripting with Puppet Bolt
To maximize the benefits of using scripts with Puppet Bolt, follow these best practices:
- Modularize: Break down complex operations into smaller, focused tasks. This enhances reusability and maintainability.
- Idempotency: Strive to make your scripts idempotent. This means running a script multiple times should have the same effect as running it once, preventing unintended changes or errors.
- Version Control: Store all your tasks, plans, and module code in a version control system like Git for tracking changes, collaboration, and easy rollback.
- Testing: Thoroughly test your scripts and plans in isolated development or staging environments before deploying them to production.
What if it's a Puppet Manifest (.pp
) file?
If by "Puppet script" you are referring to a Puppet manifest (a .pp
file containing Puppet code to declare resources), you would apply it rather than run it as an executable script. This is typically done using the Puppet agent or bolt apply
.
- Applying a manifest with Bolt:
bolt apply my_module/manifests/site.pp -t webserver
- Applying a manifest with Puppet Agent:
sudo puppet agent -t
(This requires a configured Puppet agent and master.)