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:

FunctionPurpose
passwordsafe::get_secretRetrieve a Secrets Safe secret (Credential, Text, or File type)
passwordsafe::get_managed_accountRetrieve 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

RequirementDetails
Puppet7.24 or later (< 9.0.0)
Ruby gembtps_client installed in Puppet's gem environment on every agent node
BeyondTrust Password SafeA reachable Password Safe instance with a valid API OAuth2 client configured
Network accessAgent nodes must be able to reach the Password Safe API endpoint over HTTPS
OAuth2 credentialsA valid client_id and client_secret with appropriate permissions in Password Safe
ℹ️

The btps_client gem must be installed in Puppet's own gem environment (that is, using puppet_gem provider), not in the system Ruby.

Supported Platforms

Operating SystemVersions
Red Hat Enterprise Linux7, 8, 9
CentOS7, 8, 9
Rocky Linux8
AlmaLinux8
Oracle Linux7
Scientific Linux7
Debian10, 11, 12
Ubuntu18.04, 20.04, 22.04
Solaris11
Windows Server2019, 2022
Windows10, 11

Key Features

  • Agent-side secret retrieval : Deferred functions 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 via BTPS_* 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

Retrieves 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

ParameterTypeRequiredDescription
identifierStringYesSlash-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]StringYes**Hostname and port of the Password Safe instance. Example: 'bt.example.com:443'
options[base_path]StringNoAPI base path. Defaults to BeyondTrust/api/public/v3
options[client_id]StringYes**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]BooleanNoWhether 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 valueTypeDescription
Secret valueSensitive[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, or client_secret cannot be resolved from either the options hash or environment variables.
  • The Password Safe API returns an error response.

passwordsafe::get_managed_account

Retrieves 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

ParameterTypeRequiredDescription
identifierStringYes'system_name/account_name' — exactly one slash; nested paths are rejected. Example: 'prod-server/svc_account'
options[host]StringYes**Hostname and port of the Password Safe instance. Example: 'bt.example.com:443'
options[base_path]StringNoAPI base path. Defaults to BeyondTrust/api/public/v3
options[client_id]StringYes**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]IntegerNoCheckout duration in minutes. Defaults to 5. Must be greater than 0
options[verify_ssl]BooleanNoWhether 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 valueTypeDescription
Credential valueSensitive[String]The managed account credential (typically a password). Automatically redacted from Puppet logs and reports.

The function raises Puppet::Error if:

  • host, client_id, or client_secret cannot 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 keyEnvironment variableDefaultNotes
hostBTPS_HOST(no default, required)Include port number, e.g. bt.example.com:443
base_pathBTPS_BASE_PATHBeyondTrust/api/public/v3Only change if using a non-standard API path
client_idBTPS_CLIENT_ID(no default, required)OAuth2 client ID from the Password Safe API configuration
client_secretBTPS_CLIENT_SECRET(no default, required)OAuth2 client secret; use Sensitive[String] in manifests
duration_minutes (managed account only)BTPS_DURATION_MIN5Checkout duration; must be > 0
verify_sslBTPS_VERIFY_SSLtrueSet 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-passwordsafe

Or 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 the gem provider (system Ruby), as Puppet's Deferred functions 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=5

Once 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
TopicGuidance
Agent-side resolutionAlways 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 typeBoth 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_secretAlways supply client_secret as Sensitive('...') in manifests to prevent the value from appearing in catalog data or logs.
TLS verificationDo not disable verify_ssl in production environments. Configure a trusted CA bundle on agent nodes if using a private certificate authority.
Checkout durationSet 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 permissionsCreate 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
SymptomLikely causeResolution
Puppet::Error: host is requiredBTPS_HOST is not set and host was not supplied in the options hashSet the BTPS_HOST environment variable on the agent or add host to the options hash
Puppet::Error: client_id is requiredBTPS_CLIENT_ID is missingSet BTPS_CLIENT_ID on the agent or pass client_id in options
Puppet::Error: client_secret is requiredBTPS_CLIENT_SECRET is missingSet BTPS_CLIENT_SECRET on the agent or pass client_secret in options
LoadError: cannot load such file -- btps_clientThe btps_client gem is not installed in Puppet's gem environmentRun puppet resource package btps_client ensure=present provider=puppet_gem on the agent
SSL certificate verification failedAgent does not trust the Password Safe TLS certificateAdd the CA certificate to the agent's trusted store, or (development only) set verify_ssl => false
identifier must have exactly one slashget_managed_account received a nested pathEnsure the identifier is 'system_name/account_name' with exactly one slash
Secret is visible in Puppet logsThe consuming resource does not handle Sensitive valuesEnsure 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_client gem must be installed in Puppet's gem environment on each agent node using the puppet_gem provider — not the system Ruby.
  • Deferred functions require Puppet 6.0 or later on the agent; Puppet 7.24+ is recommended.
  • get_managed_account identifiers 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.

©2003-2026 BeyondTrust Corporation. All Rights Reserved. Other trademarks identified on this page are owned by their respective owners. BeyondTrust is not a chartered bank or trust company, or depository institution. It is not authorized to accept deposits or trust accounts and is not licensed or regulated by any state or federal banking authority.