Puppet | PS
Overview
The BeyondTrust Password Safe Puppet module (beyondtrust-passwordsafe) integrates BeyondTrust Password Safe with Puppet, enabling Puppet-managed nodes to securely retrieve secrets and managed account credentials at catalog application time, directly on the agent.
The module exposes two Deferred Puppet functions backed by the btps_client Ruby gem:
| Function | Purpose |
|---|---|
passwordsafe::get_secret | Retrieve a Secrets Safe secret (Credential, Text, or File type) |
passwordsafe::get_managed_account | Retrieve a managed account credential using the checkout → retrieve → checkin workflow |
Both functions return a Sensitive[String] value. Puppet automatically redacts Sensitive values from logs, reports, and catalog diffs, ensuring secrets are never exposed in plain text.
Because the functions run as Deferred, secret resolution happens on the agent node during catalog application. Secrets are never transmitted to or stored on the Puppet server.
Prerequisites
| Requirement | Details |
|---|---|
| Puppet | 7.24 or later (< 9.0.0) |
| Ruby gem | btps_client installed in Puppet's gem environment on every agent node |
| BeyondTrust Password Safe | A reachable Password Safe instance with a valid API OAuth2 client configured |
| Network access | Agent nodes must be able to reach the Password Safe API endpoint over HTTPS |
| OAuth2 credentials | A valid client_id and client_secret with appropriate permissions in Password Safe |
The
btps_clientgem must be installed in Puppet's own gem environment (that is, usingpuppet_gemprovider), not in the system Ruby.
Supported Platforms
| Operating System | Versions |
|---|---|
| Red Hat Enterprise Linux | 7, 8, 9 |
| CentOS | 7, 8, 9 |
| Rocky Linux | 8 |
| AlmaLinux | 8 |
| Oracle Linux | 7 |
| Scientific Linux | 7 |
| Debian | 10, 11, 12 |
| Ubuntu | 18.04, 20.04, 22.04 |
| Solaris | 11 |
| Windows Server | 2019, 2022 |
| Windows | 10, 11 |
Key Features
- Agent-side secret retrieval :
Deferredfunctions resolve secrets on the agent node, never on the Puppet server. Sensitive values never traverse the Puppet infrastructure. - Automatic secret redaction : All return values are typed as
Sensitive[String]. Puppet automatically redacts these values from logs, reports, and file diffs. - Secrets Safe support : Retrieve Credential, Text, and File-type secrets stored in BeyondTrust Secrets Safe using a simple slash-delimited path identifier.
- Managed Account support : Retrieve managed account credentials using the full Password Safe checkout → retrieve → checkin lifecycle. Checkin is guaranteed even on failure.
- Flexible credential configuration : Connection parameters can be supplied inline in the Puppet manifest, via
Sensitive[String]wrappers, or viaBTPS_*environment variables on the agent. - TLS verification : SSL certificate verification is enabled by default, enforcing secure communication with the Password Safe API.
- Broad OS support : Tested on major Linux distributions, Solaris, and Windows platforms.
- Puppet 7 and 8 compatible : Supports current Puppet enterprise and open-source releases.
API Reference
passwordsafe::get_secret
passwordsafe::get_secretRetrieves a secret from BeyondTrust Secrets Safe using a slash-delimited path identifier. The last path segment is the secret title; everything before it is the folder path.
'folder/path/title'
└──────────┘ └───┘
folder path title
Signature
passwordsafe::get_secret(
String $identifier,
Optional[Struct[{
Optional['host'] => String,
Optional['base_path'] => String,
Optional['client_id'] => String,
Optional['client_secret'] => Variant[String, Sensitive[String]],
Optional['verify_ssl'] => Boolean,
}]] $options = {}
) >> Sensitive[String]Input Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
identifier | String | Yes | Slash-delimited path to the secret. The last segment is the secret title; everything before the last slash is the folder path. Example: 'production/myapp/db-password' |
options[host] | String | Yes** | Hostname and port of the Password Safe instance. Example: 'bt.example.com:443' |
options[base_path] | String | No | API base path. Defaults to BeyondTrust/api/public/v3 |
options[client_id] | String | Yes** | OAuth2 client ID for API authentication |
options[client_secret] | String or Sensitive[String] | Yes** | OAuth2 client secret. Sensitive[String] is recommended to prevent logging |
options[verify_ssl] | Boolean | No | Whether to verify TLS certificates. Defaults to true. Set false only in development/testing environments |
**Required unless the corresponding BTPS_ environment variable is set on the agent.
Output
| Return value | Type | Description |
|---|---|---|
| Secret value | Sensitive[String] | The secret value (password, text content, or file content). Automatically redacted from Puppet logs and reports. |
The function raises Puppet::Error if:
host,client_id, orclient_secretcannot be resolved from either the options hash or environment variables.- The Password Safe API returns an error response.
passwordsafe::get_managed_account
passwordsafe::get_managed_accountRetrieves a managed account credential from BeyondTrust Password Safe using the checkout → retrieve → checkin workflow. The identifier must follow the 'system_name/account_name' format with exactly one slash. Nested paths are not supported.
'system-name/account-name'
└──────────┘ └───────────┘
system name account name
The checkout is always checked in, even when retrieval fails.
Signature
passwordsafe::get_managed_account(
String $identifier,
Optional[Struct[{
Optional['host'] => String,
Optional['base_path'] => String,
Optional['client_id'] => String,
Optional['client_secret'] => Variant[String, Sensitive[String]],
Optional['duration_minutes'] => Integer,
Optional['verify_ssl'] => Boolean,
}]] $options = {}
) >> Sensitive[String]Input Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
identifier | String | Yes | 'system_name/account_name' — exactly one slash; nested paths are rejected. Example: 'prod-server/svc_account' |
options[host] | String | Yes** | Hostname and port of the Password Safe instance. Example: 'bt.example.com:443' |
options[base_path] | String | No | API base path. Defaults to BeyondTrust/api/public/v3 |
options[client_id] | String | Yes** | OAuth2 client ID for API authentication |
options[client_secret] | String or Sensitive[String] | Yes** | OAuth2 client secret. Sensitive[String] is recommended to prevent logging |
options[duration_minutes] | Integer | No | Checkout duration in minutes. Defaults to 5. Must be greater than 0 |
options[verify_ssl] | Boolean | No | Whether to verify TLS certificates. Defaults to true. Set false only in development/testing environments |
** Required unless the corresponding BTPS_* environment variable is set on the agent.
Output
| Return value | Type | Description |
|---|---|---|
| Credential value | Sensitive[String] | The managed account credential (typically a password). Automatically redacted from Puppet logs and reports. |
The function raises Puppet::Error if:
host,client_id, orclient_secretcannot be resolved from either the options hash or environment variables.- The identifier contains more or fewer than exactly one slash.
- The Password Safe API returns an error response during checkout or retrieval.
Connection Parameters
Each connection parameter in the options hash is optional. When a key is absent or empty, the function falls back to the corresponding BTPS_* environment variable set on the agent node.
| Option key | Environment variable | Default | Notes |
|---|---|---|---|
host | BTPS_HOST | (no default, required) | Include port number, e.g. bt.example.com:443 |
base_path | BTPS_BASE_PATH | BeyondTrust/api/public/v3 | Only change if using a non-standard API path |
client_id | BTPS_CLIENT_ID | (no default, required) | OAuth2 client ID from the Password Safe API configuration |
client_secret | BTPS_CLIENT_SECRET | (no default, required) | OAuth2 client secret; use Sensitive[String] in manifests |
duration_minutes (managed account only) | BTPS_DURATION_MIN | 5 | Checkout duration; must be > 0 |
verify_ssl | BTPS_VERIFY_SSL | true | Set to false only for development/testing; do not disable in production |
host, client_id, and client_secret are always required. The function raises Puppet::Error if any of the three remains unresolved after checking both the options hash and the environment variable.
client_secret accepts either a plain String or a Sensitive[String]; the value is unwrapped automatically before being passed to the API.
Setup Instructions
Step 1: Install the Module
Install the module from the Puppet Forge on your Puppet server:
puppet module install beyondtrust-passwordsafeOr declare it in your Puppetfile for use with r10k or Code Manager:
mod 'beyondtrust-passwordsafe', '0.1.0'Step 2: Install the Ruby Gem on Agents
The btps_client gem must be installed in Puppet's gem environment on every agent node that will use the module functions. Add the following resource to a base profile or role that applies to all relevant nodes:
package { 'btps_client':
ensure => present,
provider => 'puppet_gem',
}Important: Using
provider => 'puppet_gem'installs the gem into Puppet's own gem environment. Do not use thegemprovider (system Ruby), as Puppet'sDeferredfunctions use Puppet's Ruby runtime.
Step 3: Configure Connection Parameters
Choose one of the two supported approaches for supplying connection parameters:
Option A — Inline in the Puppet Manifest (Recommended for clarity)
Pass connection parameters directly in the options hash. Use Sensitive[String] for the client secret:
$db_password = Deferred('passwordsafe::get_secret', [
'production/myapp/db-password',
{
host => 'bt.example.com:443',
base_path => 'BeyondTrust/api/public/v3',
client_id => 'my-oauth-client-id',
client_secret => Sensitive('my-oauth-client-secret'),
verify_ssl => true,
}
])Option B — Environment Variables on the Agent
Set the following environment variables in the Puppet agent service environment (e.g., /etc/sysconfig/puppet, /etc/default/puppet, or the service unit file):
BTPS_HOST=bt.example.com:443
BTPS_BASE_PATH=BeyondTrust/api/public/v3
BTPS_CLIENT_ID=my-oauth-client-id
BTPS_CLIENT_SECRET=my-oauth-client-secret
BTPS_VERIFY_SSL=true
BTPS_DURATION_MIN=5Once set, call the functions with only the identifier:
$db_password = Deferred('passwordsafe::get_secret', ['production/myapp/db-password'])Usage Examples
Retrieve a Secret Safe Secret
# Retrieve a database password from Secrets Safe
$db_password = Deferred('passwordsafe::get_secret', [
'production/postgres/password',
{
host => 'bt.example.com:443',
client_id => 'my-client-id',
client_secret => Sensitive('my-client-secret'),
}
])
# Write the secret to a configuration file
file { '/etc/myapp/db.conf':
content => Deferred('passwordsafe::get_secret', ['production/postgres/db-credential']),
show_diff => false,
mode => '0600',
owner => 'myapp',
group => 'myapp',
}Retrieve a Managed Account Credential
# Retrieve a service account password using managed account checkout
$svc_password = Deferred('passwordsafe::get_managed_account', [
'prod-server/svc_account',
{
host => 'bt.example.com:443',
client_id => 'my-client-id',
client_secret => Sensitive('my-client-secret'),
duration_minutes => 10,
}
])
# Write it to a configuration file
file { '/etc/myapp/svc.conf':
content => Deferred('passwordsafe::get_managed_account', ['prod-server/deploy']),
show_diff => false,
mode => '0600',
}Using Deferred (Recommended)
Deferred causes Puppet to evaluate the function on the agent during catalog application. Secrets are resolved locally and never sent to or stored on the Puppet server:
class myapp (
Sensitive[String] $db_password = Deferred('passwordsafe::get_secret',
['production/myapp/db-password']),
) {
file { '/etc/myapp/database.conf':
content => epp('myapp/database.conf.epp', { password => $db_password }),
show_diff => false,
}
}Using Environment Variables
When all connection parameters are configured as BTPS_* environment variables on the agent, you can call both functions with only the identifier:
$db_password = Deferred('passwordsafe::get_secret', ['production/postgres/password'])
$svc_password = Deferred('passwordsafe::get_managed_account', ['prod-server/svc_account'])Security Considerations
| Topic | Guidance |
|---|---|
| Agent-side resolution | Always use Deferred to resolve secrets on the agent. Never call the functions outside of Deferred, as this would resolve the value on the Puppet server. |
| Sensitive type | Both functions return Sensitive[String]. Pass values using the Sensitive wrapper; do not convert to plain strings unless required by the consuming resource. Use show_diff => false on file resources that include secret content. |
| client_secret | Always supply client_secret as Sensitive('...') in manifests to prevent the value from appearing in catalog data or logs. |
| TLS verification | Do not disable verify_ssl in production environments. Configure a trusted CA bundle on agent nodes if using a private certificate authority. |
| Checkout duration | Set duration_minutes to the minimum value required for your use case. Shorter durations reduce the window of exposure if a credential is compromised during checkout. |
| API permissions | Create a dedicated OAuth2 API client in Password Safe with the minimum permissions required. Scope access to only the secrets and accounts that Puppet agents need. |
Troubleshooting
Troubleshooting
| Symptom | Likely cause | Resolution |
|---|---|---|
Puppet::Error: host is required | BTPS_HOST is not set and host was not supplied in the options hash | Set the BTPS_HOST environment variable on the agent or add host to the options hash |
Puppet::Error: client_id is required | BTPS_CLIENT_ID is missing | Set BTPS_CLIENT_ID on the agent or pass client_id in options |
Puppet::Error: client_secret is required | BTPS_CLIENT_SECRET is missing | Set BTPS_CLIENT_SECRET on the agent or pass client_secret in options |
LoadError: cannot load such file -- btps_client | The btps_client gem is not installed in Puppet's gem environment | Run puppet resource package btps_client ensure=present provider=puppet_gem on the agent |
SSL certificate verification failed | Agent does not trust the Password Safe TLS certificate | Add the CA certificate to the agent's trusted store, or (development only) set verify_ssl => false |
identifier must have exactly one slash | get_managed_account received a nested path | Ensure the identifier is 'system_name/account_name' with exactly one slash |
| Secret is visible in Puppet logs | The consuming resource does not handle Sensitive values | Ensure show_diff => false is set on file resources; do not unwrap Sensitive values unnecessarily |
Limitations
- Tested on Puppet 7.x and 8.x. Puppet versions earlier than 7.24 and 9.x or later are not supported.
- The
btps_clientgem must be installed in Puppet's gem environment on each agent node using thepuppet_gemprovider — not the system Ruby. Deferredfunctions require Puppet 6.0 or later on the agent; Puppet 7.24+ is recommended.get_managed_accountidentifiers must use exactly one slash (system_name/account_name). Nested account paths are not supported.- SSL verification is enabled by default. Do not disable certificate validation in production environments.
Updated about 2 hours ago
