#!/usr/bin/env bash # Write Ansible vault secrets into .env (for local scripts / reference). # Does not print secret values. Does not overwrite non-empty .env keys. set -euo pipefail REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" ENV_FILE="${1:-${REPO_ROOT}/.env}" VAULT_FILE="${REPO_ROOT}/inventories/production/group_vars/all/vault.yml" VAULT_PASS="${HOME}/.ansible-vault-pass" ANSIBLE_VAULT="${REPO_ROOT}/.venv/bin/ansible-vault" [[ -f "${VAULT_PASS}" ]] || { echo "Missing ${VAULT_PASS}" >&2; exit 1; } "${REPO_ROOT}/.venv/bin/python3" - "${ENV_FILE}" "${VAULT_FILE}" "${VAULT_PASS}" "${ANSIBLE_VAULT}" <<'PY' import subprocess, sys, yaml from pathlib import Path env_file, vault_file, vault_pass, ansible_vault = sys.argv[1:5] # vault key -> .env key MAP = { "vault_mailcow_api_key": "MAILCOW_API_KEY", "vault_alerts_mailbox_password": "ALERTS_PASSWORD", "vault_uptime_kuma_password": "KUMA_PASSWORD", "vault_uptime_kuma_user": "KUMA_USER", "vault_uptime_kuma_url": "KUMA_URL", "vault_umami_admin_password": "UMAMI_ADMIN_PASSWORD", "vault_umami_db_password": "UMAMI_DB_PASS", "vault_umami_app_secret": "UMAMI_APP_SECRET", "vault_kuma_smtp_host": "SMTP_HOST", "vault_kuma_smtp_port": "SMTP_PORT", "vault_kuma_smtp_user": "SMTP_USER", "vault_kuma_smtp_password": "SMTP_PASS", "vault_kuma_smtp_to": "SMTP_TO", "vault_mattermost_url": "MATTERMOST_URL", "vault_mattermost_token": "MATTERMOST_TOKEN", "vault_mattermost_allowed_users": "MATTERMOST_ALLOWED_USERS", } def parse_env(text): d = {} for line in text.splitlines(): line = line.strip() if not line or line.startswith("#") or "=" not in line: continue k, _, v = line.partition("=") d[k.strip()] = v.strip().strip("'").strip('"') return d text = subprocess.check_output( [ansible_vault, "view", vault_file, "--vault-password-file", vault_pass], text=True, ) data = yaml.safe_load(text) or {} existing = parse_env(Path(env_file).read_text()) if Path(env_file).exists() else {} merged = dict(existing) for vk, ek in MAP.items(): val = data.get(vk) if val is None or val == "": continue if merged.get(ek): continue merged[ek] = str(val) pw = data.get("vault_mailcow_mailbox_passwords") or {} if pw.get("alerts") and not merged.get("ALERTS_PASSWORD"): merged["ALERTS_PASSWORD"] = str(pw["alerts"]) header = """# Merged from Ansible vault (make vault-export-env). Fill gaps manually. # vault → .env: make vault-export-env # .env → vault: make vault-import-env # hosts → .env → vault: make vault-pull-infra-secrets """ body = "\n".join(f"{k}={v}" for k, v in sorted(merged.items())) + "\n" Path(env_file).write_text(header + body) print(f"Wrote {len(merged)} keys to {env_file} (existing non-empty keys kept)") PY chmod 600 "${ENV_FILE}" 2>/dev/null || true