Skip to content

Secrets & Keychain

Neil Martin edited this page Sep 11, 2026 · 15 revisions

Secrets & Keychain

Secret storage

Add a profile at the prompts and jamf-cli stores the secrets in your system keychain. It prompts for credentials with hidden input, which keeps them out of shell history and process listings.

jamf-cli config add-profile prod \
  --url https://jamf.company.com \
  --auth-method oauth2
# You'll be prompted: Client ID: abc123
#                     Client Secret: ********

The config file holds a reference:

profiles:
  prod:
    url: https://jamf.company.com
    auth-method: oauth2
    client-id: keychain:jamf-cli/prod/client-id
    client-secret: keychain:jamf-cli/prod/client-secret

The values live in macOS Keychain (or Linux secret-service).

Credential Input Policy

A credential's accepted source depends on who holds it. The rule keeps secrets out of shell history, ps output and CI logs.

Credential Accepted from
Human: a Jamf Pro account username and password (pro setup --credentials create) Interactive prompts only, password read with hidden input. No flag, no environment variable, no stdin. Never stored.
Machine: bearer token, client ID, client secret, Jamf School API key, Jamf Security Cloud application secrets JAMF_* / JAMFPROTECT_* / JAMFSCHOOL_* / JAMFSECURITY_* environment variables for CI/CD; interactive prompts for manual use; keychain: / env: / file: profile references for persistent storage; --token-file for a file-based bearer token in CI.

Consequences:

  • No command has a --password, --token, --client-secret, --token-stdin or --client-secret-stdin flag, and none will be added. A credential in argv shows up in ps and lands in shell history and CI logs.

  • Setup commands prompt. pro setup, protect setup, school setup, security setup, platform setup and config add-profile collect credentials with hidden input, and each refuses --no-input. For unattended use, set the environment variables and skip profiles, or write the profile with env: / file: references (below).

  • A flag may name a credential's source; it may never carry the value. pro setup --credentials existing|create is the shape that makes the distinction concrete: existing takes an API client you already created in Jamf Pro, create has jamf-cli make one, and both branches read every secret from an interactive prompt. Neither accepts one from a flag, an environment variable or stdin, and both refuse --no-input.

  • A credential is proved before it is written. pro setup --credentials existing and config add-profile each do one client-credentials token exchange, and a failure writes nothing. config add-profile --no-verify skips it for offline pre-seeding; the check is skipped anyway for token auth (a bearer token is only testable by spending it) and for an env: or file: reference, often written on a machine that cannot reach the server it names.

    Verification says the pair is valid and nothing about whether its privileges reach any command. A 403 at the point of use answers that, in wording setup cannot produce.

  • Some commands take a secret as payload data (pro computer-inventory set-auto-admin-password, for instance). Those read it from a file (--password-file) or prompt for it.

Using Secrets in CI/CD and Containers

On headless systems with no keychain, take one of two approaches.

Environment variables, the shortest path for CI/CD, with no config file:

# Jamf Pro instance (oauth2)
export JAMF_URL="https://jamf.company.com"
export JAMF_CLIENT_ID="your-client-id"
export JAMF_CLIENT_SECRET="your-client-secret"

jamf-cli pro computers list --no-input

For the platform gateway, the same three variables plus the scope level the integration was created at. The gateway host is enough to select platform auth, which makes an organization-scoped credential (one naming no scope ID) usable this way:

export JAMF_URL="https://us.api.jamfcloud.com"
export JAMF_CLIENT_ID="your-client-id"
export JAMF_CLIENT_SECRET="your-client-secret"
export JAMF_ENVIRONMENT_ID="your-environment-id"   # or JAMF_TENANT_ID, and never both

jamf-cli pro blueprints list --no-input

Neither scope ID is a secret: they are identifiers, and they are the part of a platform profile you can commit. Setting both is refused with a usage error, since an integration is created at one level and its credential works with that level alone. See Configuration & Profiles#Scope levels for what each level reaches, and Platform API GA Migration if you are coming from the public beta, whose credentials were revoked at GA.

Config file with secret references, for persistent profiles on headless systems. Write the config file with env: or file: prefixed values:

# CI/CD: reference environment variables
profiles:
  ci:
    url: https://jamf.company.com
    auth-method: oauth2
    client-id: env:JAMF_CLIENT_ID
    client-secret: env:JAMF_CLIENT_SECRET

# Docker/Kubernetes: reference mounted secret files
  container:
    url: https://jamf.company.com
    auth-method: oauth2
    client-id: file:/run/secrets/jamf-client-id
    client-secret: file:/run/secrets/jamf-client-secret

With a prefix, jamf-cli stores the value in config as-is and the keychain stays out of it.

Secret Format Reference

Prefix Syntax Resolved at
keychain: keychain:jamf-cli/prod/client-secret Runtime: reads from system keychain
env: env:JAMF_CLIENT_SECRET Runtime: reads from environment variable
file: file:/run/secrets/jamf-token Runtime: reads and trims file contents

jamf-cli resolves secrets at runtime, so the environment variables and files need to exist when you run a command.

Keychain reference format

The full format is keychain:service/profile/field. Omit the service when it is jamf-cli (the default):

keychain:jamf-cli/prod/client-secret   # explicit
keychain:prod/client-secret               # equivalent (default service)

Profile Lifecycle

Adding a profile: bare secret values go to the keychain. Prefixed values (env:, file:) go into config as-is.

Removing a profile: jamf-cli finds any keychain: references and deletes the matching keychain items:

$ jamf-cli config remove-profile prod
Removed keychain item: jamf-cli/prod/client-secret
Profile "prod" removed.

Setup wizard: each setup command stores its secrets in the keychain, under <profile>/<field>:

Command Keychain fields written
pro setup client-id, client-secret, on both --credentials branches. existing stores the pair you typed; create stores the pair it generated.
platform setup client-id, client-secret
protect setup client-id, client-secret
school setup network-id, api-key, plus client-id / client-secret when platform access was configured
security setup risk-client-id, risk-client-secret, lifecycle-client-id, lifecycle-client-secret, sse-client-id, sse-client-secret, for the pairs entered

platform setup and security setup merge into a profile of the same name, so one profile holds both the gateway credentials and the Radar pairs. Running one after the other in either order keeps both.

On headless systems, use config add-profile with env: or file: instead.

Validating: run config validate to check that every secret resolves before you deploy.

Keychain write failures

The system credential store can refuse a write: a locked login keychain on macOS surfaces as an opaque code like exit status 154. Setup and config add-profile wrap these failures with OS-specific guidance:

  • macOS: unlock the login keychain with security unlock-keychain ~/Library/Keychains/login.keychain-db, then retry.
  • Linux: start and unlock a Secret Service provider (e.g. GNOME Keyring / gnome-keyring-daemon).
  • Anywhere: fall back to a config profile with env: or file: secret references (see above), which leave the keychain untouched.

One-Off Commands Without a Profile

For a one-off command with no saved profile, use environment variables or --token-file. See CI/CD & Scripting#Authentication in CI for the full env var reference and CI pipeline examples.

# Quick one-off with env vars
export JAMF_URL=https://jamf.company.com
export JAMF_CLIENT_ID=abc123
export JAMF_CLIENT_SECRET=s3cret
jamf-cli pro computers list

# Token from a file
jamf-cli --url https://jamf.company.com \
  --token-file /run/secrets/jamf-token \
  pro computers list

Security Notes

  • No plaintext secrets in config: the CLI rejects a bare value unless it can auto-store it to the keychain
  • Keychain failure is fatal: the CLI reports it and stores nothing in plaintext
  • Config file permissions: created 0600 (owner read/write only) in a 0700 directory
  • Safe to share config output: config show prints references such as keychain:jamf-cli/prod/client-secret, with no secret values
  • No credential flags or stdin: the CLI accepts no credential as a flag or through a stdin pipe, which keeps them out of process listings and shell history. Use environment variables, config profiles, --token-file, or interactive prompts

jamf-cli Wiki


Products

  • Jamf Pro: jamf-cli pro
  • Jamf Platform API: jamf-cli pro (blueprints, benchmarks, DDM reports)
  • Jamf Platform: jamf-cli platform (AI Governance, Jamf Account, audit)
  • Jamf Protect: jamf-cli protect
  • Jamf School: jamf-cli school
  • Jamf Security Cloud: jamf-cli security

Clone this wiki locally