Managing secrets via Python SDK
What is the Python SDK
The Python SDK is a library for programmatic interaction with Passwork. It's designed for advanced automation where CLI capabilities aren't enough.
| Feature | Description |
|---|---|
| Reading secrets | Retrieve passwords, fields, and attachments by ID or search |
| Modifying secrets | Update passwords, fields, tags, and descriptions |
| Creating secrets | Programmatically add records to specific folders |
| Managing structure | Work with folders and vaults, move records around |
When to use SDK vs CLI
| Scenario | Recommendation |
|---|---|
| CI/CD pipeline, deploy script | passwork-cli — simpler, no code needed |
| Password rotation with custom logic | Python SDK — more flexibility, better error handling |
| Migrating from another system | Python SDK — transform data as needed |
| Checking secret integrity | Python SDK — implement complex validation |
| Grabbing a single secret in bash | passwork-cli get — just one command |
Installation
Install with pip from GitHub:
# PyPI
pip install passwork-python
# Or from GitHub via SSH
pip install git+ssh://[email protected]:passwork-me/passwork-python.git
# Or from GitHub via HTTPS
pip install git+https://github.com/passwork-me/passwork-python.git
See also: Python connector.
Initializing the client
from passwork import Client
client = Client(
url="https://passwork.example.com",
token="your_access_token",
)
tip
Avoid hardcoding tokens in your code. Use environment variables instead:
import os
from passwork import Client
client = Client(
url=os.environ["PASSWORK_HOST"],
token=os.environ["PASSWORK_TOKEN"],
)
Reading secrets
By record ID
item = client.get_password(item_id="<item-id>")
# Standard fields
print(item.login)
print(item.password)
# Custom fields
db_host = item.fields["MYSQL_HOST"]
stripe_key = item.fields["STRIPE_SECRET"]
By folder
# Fetch all records from a folder
items = client.list_folder_items(folder_id="<folder-id>")
for item in items:
print(f"{item.title}: {item.login}")
By search string
# Find records matching a query
items = client.search("mysql production")
for item in items:
print(f"{item.id}: {item.title}")
Modifying secrets
Updating an existing record
# Load the record
item = client.get_password(item_id="<item-id>")
# Change fields
item.password = "Jk8$mNp#2xLq!9Wz"
item.fields["STRIPE_SECRET"] = "sk_live_abc123xyz"
# Persist changes
client.update_password(item)
Creating a new record
client.create_password(
folder_id="<folder-id>",
title="MySQL Primary",
login="backend_svc",
password="Tm4#kL9$pQ2!xZ7",
fields={
"MYSQL_HOST": "mysql.prod.internal",
"MYSQL_PORT": "3306",
},
tags=["production", "db"],
)
Bulk operations
The SDK enables automation such as:
- moving records between folders;
- adding or removing tags in bulk;
- updating fields based on a template;
- migrating the
secrets/*hierarchy.
# Add a tag to all records in a folder
items = client.list_folder_items(folder_id="<folder-id>")
for item in items:
if "legacy" not in item.tags:
item.tags.append("legacy")
client.update_password(item)
Practical examples
Rotating a database password
Full example with error handling for PostgreSQL password rotation:
import os
import secrets
import psycopg2
from passwork import Client
def rotate_db_password(item_id: str, db_user: str):
"""Rotate the password for a PostgreSQL user."""
# Set up the client
client = Client(
url=os.environ["PASSWORK_HOST"],
token=os.environ["PASSWORK_TOKEN"],
)
# Create a fresh password
new_password = secrets.token_urlsafe(32)
# Retrieve admin credentials for the DB connection
admin_item = client.get_password(item_id=os.environ["PG_ADMIN_ITEM_ID"])
try:
# Apply the new password in PostgreSQL
conn = psycopg2.connect(
host=admin_item.fields["PG_HOST"],
dbname="postgres",
user=admin_item.login,
password=admin_item.password,
)
conn.autocommit = True
with conn.cursor() as cur:
cur.execute(
"ALTER ROLE %s WITH PASSWORD %s",
(db_user, new_password)
)
conn.close()
# Store the updated password in Passwork
item = client.get_password(item_id=item_id)
item.password = new_password
client.update_password(item)
print(f"Password rotated for {db_user}")
return True
except Exception as e:
print(f"Rotation failed: {e}")
return False
# Usage
rotate_db_password(
item_id="<item-id>",
db_user="order_service"
)
Checking secret validity
Script that periodically verifies stored credentials still work:
import os
import psycopg2
from passwork import Client
def check_db_credentials(folder_id: str):
"""Verify that database credentials in Passwork are valid."""
client = Client(
url=os.environ["PASSWORK_HOST"],
token=os.environ["PASSWORK_TOKEN"],
)
items = client.list_folder_items(folder_id=folder_id)
broken = []
for item in items:
if "db" not in item.tags:
continue
try:
conn = psycopg2.connect(
host=item.fields.get("PG_HOST"),
dbname=item.fields.get("PG_DATABASE", "postgres"),
user=item.login,
password=item.password,
connect_timeout=5,
)
conn.close()
print(f"[OK] {item.title}")
except Exception as e:
print(f"[FAIL] {item.title}: {e}")
broken.append(item)
# Flag the record for review
if "stale" not in item.tags:
item.tags.append("stale")
client.update_password(item)
return broken
# Usage
broken = check_db_credentials(folder_id="<infrastructure/production/databases>")
if broken:
print(f"\n{len(broken)} secrets need review")
Importing secrets from files
Migrate secrets from .env files into Passwork:
import os
from pathlib import Path
from passwork import Client
def migrate_env_file(env_path: str, folder_id: str, tags: list):
"""Import secrets from a .env file into Passwork."""
client = Client(
url=os.environ["PASSWORK_HOST"],
token=os.environ["PASSWORK_TOKEN"],
)
env_file = Path(env_path)
secrets_dict = {}
# Parse the .env file
for line in env_file.read_text().splitlines():
line = line.strip()
if not line or line.startswith("#"):
continue
if "=" in line:
key, value = line.split("=", 1)
secrets_dict[key.strip()] = value.strip().strip('"\'')
# Store in Passwork
client.create_password(
folder_id=folder_id,
title=env_file.stem,
fields=secrets_dict,
tags=tags,
)
print(f"Imported {len(secrets_dict)} secrets from {env_path}")
# Usage
migrate_env_file(
env_path="./legacy/.env.production",
folder_id="<infrastructure/production/backend>",
tags=["production", "imported"],
)