Alright, who here actually *knows* what's in their shared vaults? Not the vague "some logins and secure notes" answer. I mean specifics: stale entries, items with missing fields, duplicates, overly broad permissions.
Our security team wanted a report. The 1Password admin console is good, but not for deep, custom audits. So I built a script.
It uses the 1Password CLI (`op`) and the Python SDK. The goal: connect, list all vaults accessible to the service account, then iterate through each item and run checks. Outputs a simple markdown report.
Here's the core logic for fetching and checking items:
```python
import subprocess
import json
def get_vaults():
"""Fetch vaults accessible to the service account."""
cmd = ["op", "vault", "list", "--format=json"]
result = subprocess.run(cmd, capture_output=True, text=True, check=True)
return json.loads(result.stdout)
def audit_item(item):
"""Run checks on a single item."""
issues = []
if item.get("fields") is None:
issues.append("No fields defined")
else:
# Check for empty required fields like 'password'
for field in item["fields"]:
if field.get("type") == "PASSWORD" and not field.get("value"):
issues.append("Empty password field")
if "lastEditedBy" not in item:
issues.append("No last edited by metadata")
return issues
# Main loop
for vault in get_vaults():
vault_name = vault["name"]
cmd = ["op", "item", "list", f"--vault={vault_name}", "--format=json"]
items = json.loads(subprocess.run(cmd, capture_output=True, text=True).stdout)
for item in items:
item_details_cmd = ["op", "item", "get", item["id"], "--format=json"]
full_item = json.loads(subprocess.run(item_details_cmd, capture_output=True, text=True).stdout)
problems = audit_item(full_item)
if problems:
print(f"VAULT: {vault_name} | ITEM: {item['title']} | ISSUES: {', '.join(problems)}")
```
Key checks it performs:
* Items with completely empty fields (like a login with no username/password).
* Password fields that are literally empty strings.
* Missing `lastEditedBy` metadata (can indicate old import artifacts).
* Flagging vaults with excessive membership counts (needs a separate `op` call for members).
It's not fancy, but it's reproducible and runs in our CI pipeline weekly. The report gets posted to a secure channel. Found dozens of orphaned items and a few shared vaults with outdated user lists.
Anyone else doing something similar? I'm looking to add cost-optimization checks next, like flagging vaults with >100 items that might be better split for least-privilege access.
- pp
pipelines > meetings
Script's fine for a one-off. But running this on a cron? You're now managing service account tokens, CLI updates, and a whole Python env just to check if passwords are empty. Feels heavy.
Why not just hook the vault events into your existing monitoring? CloudWatch Events, Datadog, whatever. Get alerts when something's weird, not a weekly report.
Also, good luck with that `op` CLI in a container. The auth dance is "fun".
The skeleton is solid for programmatic access, but `subprocess` to the CLI introduces a significant point of failure in a script meant for reliability. The CLI's exit codes and output format can change between versions.
You should use the official `op` Python SDK directly for the item fetching. It handles the session, JSON parsing, and errors more cleanly. Your `audit_item` function is the valuable part; keep that logic.
```python
import op
def get_vaults():
"""Fetch vaults using the SDK."""
# Uses the existing CLI session automatically
return op.Vault.list()
```
This removes the JSON parsing and subprocess management. Also, for cron, consider having the script export its findings as structured JSON to an object store (S3, GCS) and use your existing dashboard tools to visualize the trend of "issues per vault" over time. That's where the real operational insight is.
Latency is the enemy.