TL; DR
If you’ve ever glued together 12 curl calls, piped them through jq, and told yourself “This is fine”… This PEClient Ruby library might be just what you need.
PEClient.rb is a Ruby client library for Puppet Enterprise / Puppet Core HTTP APIs that lets you call endpoints like Ruby code, not like a shell script that is one whitespace away from chaos.
Use the PEClient library when pe-client-tools doesn’t go far enough, or when you need Puppet API access inside your Ruby automation (Rake tasks, CI jobs, internal tooling). This post will help you better understand when to reach for PEClient, how it works, and quick examples to start.
Who Is This For?
PEClient will be a great fit for you if:
- You operate Puppet at scale and need repeatable automation, not “ClickOps.”
- You’re integrating Puppet with other tooling (Terraform, CI/CD, internal platforms).
- You need API access beyond what the Console or
pe-client-toolsconveniently exposes. - You would rather write Ruby than parse CLI output that changes on update day.
PEClient is not an official, supported, Perforce Puppet library and is maintained on a best-effort basis. That means it is not a fit for teams that can only use libraries that have official vendor support and SLAs on response. However, if you love bringing in new open-source tools into your stack, this will be a great fit!
The Problem
Puppet exposes a lot of API endpoints, which is great, until you’re stuck assembling authentication, headers, request bodies, and response parsing in every script.
Sure, you can curl everything… but you’ll eventually end up maintaining your own mini client anyway. Nobody wakes up excited to debug their own ad‑hoc HTTP wrapper at 2 A.M.
If you’re hitting APIs with ad‑hoc scripts, you end up researching endpoints, wiring auth and headers, and managing request/response parsing. Then you must maintain it. Wasting time on the scripts which is time stolen from real automation.
What About pe-client-tools?
Puppet Enterprise Client Tools are great for humans at a terminal providing “secure and convenient” access from a workstation.
When you embed these tools inside automation, you inherit a few risks:
- Parsing CLI output is brittle (format changes happen).
- You end up writing a wrapper anyway (input validation, retries, error mapping).
- Your "library" becomes a collection of shell-outs (harder to test, harder to maintain).
There is a Better Way!
With Puppet already using Ruby, your team is most likely already familiar with the language.
You probably have custom tooling you use to automate things like provisioning your infrastructure, handling ad-hoc requests from management, and more.
Why not leave the finer details of how to talk to APIs to a library that can take that off your hands? Built using Faraday, the PEClient library requests and responses use Plain Old Ruby Objects (PORO), no special Structs, and no custom objects to force your data into. PEClient.rb allows you to write API calls in the most natural way we know how... with Ruby!
The PEClient library has as few dependencies as possible with faraday being the only one. And if you are wondering about Ruby support, we’ve got that covered too. PEClient supports all currently supported Ruby versions (3.2 to 4.0), with each version being tested to confirm it works as expected.
In practice, this means less glue code, fewer brittle wrappers, and API calls that read like Ruby instead of raw HTTP.
What you get with PEClient:
- Ruby methods that map to Puppet APIs (no DIY URL stitching)
- Plain hashes in/out (PORO-friendly)
- Token or certificate authentication supported
- Consistent error classes for HTTP failures
So, what's the catch? Good news! There isn’t one. PEClient is designed to be easy to implement, so you can focus automating your infrastructure.
Architecture Overview
Ease of use was a primary goal for this library. It should be easy to install, use, and customize to suit your needs.
To facilitate this, the library is designed using a client for handling HTTP interactions and Resources which provide entry points for the API endpoints:
- Resources are lazy loaded only being required when you need it.
- The client owns connection/authorization concerns.
- Resources group the API endpoints in a way that mirrors the docs, so you can discover APIs via code completion.
- Lazy loading keeps startup time low, handy for short-lived jobs like CI steps or one-off automation runs.
Quickstart
Install the gem via Bundler to pull the repository directly from GitHub.
# Gemfile
gem "pe_client", git: "https://github.com/puppetlabs/pe_client.rb"
# Install
bundle install
The following is an example with minimal configuration, configuring the client using environment variables. Once the client has been initialised an API request can be made, such as calling the RBAC V1 API endpoint to return the current user.
base_url is the URL of your puppet server, for example https://puppet.example.com.
api_key is your RBAC API key generated through puppet-access (part of pe-client-tools), the PE Console or with PEClient by calling client.rbac_v1.tokens.generate.
ca_file is your certificate authority public key to secure the connection between PEClient and your Puppet Server.
# Initializes the client, authenticates, and performs a simple call
client = PEClient.new(
base_url: ENV.fetch("PE_BASE_URL"),
api_key: ENV.fetch("PE_API_TOKEN"),
ca_file: ENV.fetch("PE_CA_FILE", "/etc/puppetlabs/puppet/ssl/certs/ca.pem"),
)
# Get the current user (who the API Key belongs to)
client.rbac_v1.users.current
# =>
# {"email" => "",
# "is_revoked" => false,
# "last_login" => "2025-12-16T01:56:58.87Z",
# "is_remote" => false,
# "login" => "admin",
# "is_superuser" => true,
# "id" => "42bf351c-f9ec-40af-84ad-e976fec7f4bd",
# "role_ids" => [1],
# "display_name" => "Administrator",
# "is_group" => false}
Authentication
With Puppet Enterprise you have two methods available for authentication: RBAC and Certificate.
If you need to interact with majority of the API endpoints provided, you usually want to use RBAC for authentication. RBAC is used to grant individual users the permission to perform specific actions without needing to give them the keys to the kingdom.
You can generate an API Token either through the PE Console, or straight from within PEClient.
The following example shows multiple ways to initialize the client:
- Providing an API Token that you have already generated.
- Generating a token using PEClient.
# Bring your own token
client = PEClient.new(
base_url: ENV.fetch("PE_BASE_URL"),
api_key: ENV.fetch("PE_API_TOKEN"),
ca_file: ENV.fetch("PE_CA_FILE", "/etc/puppetlabs/puppet/ssl/certs/ca.pem"),
)
# Generate a token
client = PEClient.new(
base_url: ENV.fetch("PE_BASE_URL"),
ca_file: ENV.fetch("PE_CA_FILE", "/etc/puppetlabs/puppet/ssl/certs/ca.pem"),
)
## Creates a token that lasts 5 minutes
response = client.rbac_v1.tokens.generate(
login: "<USERNAME>",
password: "<PASSWORD>",
lifetime: "5m"
)
## => {"token" => "generated_secret_api_key"}
client = PEClient.new(
base_url: ENV.fetch("PE_BASE_URL"),
api_key: response["token"],
ca_file: ENV.fetch("PE_CA_FILE", "/etc/puppetlabs/puppet/ssl/certs/ca.pem"),
)
What about those API endpoints that don't support RBAC but require a certificate? PEClient.rb can handle that too! This example shows how to initialize the client with a client certificate and key.
# Client certificate authentication
client = PEClient.new(
base_url: ENV.fetch("PE_BASE_URL"),
ca_file: ENV.fetch("PE_CA_FILE", "/etc/puppetlabs/puppet/ssl/certs/ca.pem"),
) do |conn|
conn.ssl[:client_cert] = '/path/to/client_cert.pem'
conn.ssl[:client_key] = '/path/to/client_key.pem'
end
Errors
All errors raised by PEClient.rb come under the PEClient::Error base class, which can be used to capture any generic error raised by PEClient.rb.
For HTTP Errors a PEClient::HTTPError base class is used.
# HTTP 400 errors
class BadRequestError < HTTPError; end
# HTTP 401 errors
class UnauthorizedError < HTTPError; end
# HTTP 403 errors
class ForbiddenError < HTTPError; end
# HTTP 404 errors
class NotFoundError < HTTPError; end
# HTTP 409 errors
class ConflictError < HTTPError; end
# HTTP 500-599 errors
class ServerError < HTTPError; end
For example:
begin
users = client.rbac_v1.users.list("invalid SID")
rescue PEClient::HTTPError => e
warn "HTTP #{e.response.code}: #{e.response.body}"
rescue PEClient::Error => e
warn "PEClient error: #{e.message}"
end
Compatibility and Versioning
These are the current language and API compatibility notes at the time of publishing. Please check the GitHub repo for the latest notes on compatibility.
- Ruby: 3.2–4.0 (CI tested)
- Puppet API versions: Targeting PE 2025.6 / Puppet Core 8.16+
-
Deprecations: Marked endpoints emit warnings via
@deprecateddocs
Where Can I Find PEClient?
PEClient can be found on GitHub. Please note that this library is not an official, supported Perforce product and is maintained on a best-effort basis.
If you encounter any bugs or want to request a missing feature, please raise an issue. Pull Requests are always welcome!



Top comments (0)