#!/usr/bin/env python3 """ Read `terraform output -json` from stdin, emit an Ansible hosts.yml to stdout. Usage: terraform -chdir=terraform/environments/ output -json \\ | python3 scripts/tf_to_inventory.py > inventories//hosts.yml Expected Terraform output shape: { "vms": { "value": { "hostname": { "ip": "192.168.1.10", "group": "docker_hosts" } } } } Valid groups: control, docker_hosts, proxmox_hosts, offsite_hosts (control and offsite_hosts hold manually-provisioned hosts not in Terraform; they are valid so their sections appear in the generated inventory — see ADR-009.) """ import json import sys VALID_GROUPS = {"control", "docker_hosts", "proxmox_hosts", "offsite_hosts"} def main() -> None: try: data = json.load(sys.stdin) except json.JSONDecodeError as exc: print(f"error: could not parse Terraform output JSON: {exc}", file=sys.stderr) sys.exit(1) vms = data.get("vms", {}).get("value", {}) if not vms: print("warning: no VMs in Terraform output — writing empty inventory", file=sys.stderr) groups: dict[str, dict[str, str]] = {} for hostname, info in vms.items(): group = info.get("group", "") if group not in VALID_GROUPS: print( f"error: unknown group '{group}' for host '{hostname}' " f"(valid: {', '.join(sorted(VALID_GROUPS))})", file=sys.stderr, ) sys.exit(1) groups.setdefault(group, {})[hostname] = info["ip"] lines = [ "---", "# Generated by scripts/tf_to_inventory.py — do not edit manually.", "# Regenerate with: make tf-inventory TF_ENV=", "# This OVERWRITES the file, including any manually-added control node (ubongo) —", "# re-add it afterwards (the one hand-edit exception; docs/runbooks/new-host.md Part E).", "", "all:", " children:", ] for group in sorted(VALID_GROUPS): lines.append(f" {group}:") hosts = groups.get(group, {}) if hosts: lines.append(" hosts:") for hostname in sorted(hosts): lines.append(f" {hostname}:") lines.append(f" ansible_host: {hosts[hostname]}") else: lines.append(" hosts: {}") print("\n".join(lines)) if __name__ == "__main__": main()