This is a template repository, not a real project. It is the
starting skeleton for infrastructure-automation projects: a small role-pipeline pattern in plain Python (dry-run first,
per-host error isolation, explicit registry), OpenBao-first secrets
handling, gitignored inventories with checked-in examples, tests that
run offline, and runbook/architecture doc skeletons. To use it: click
Use this template on GitHub (or clone and re-init), then work
through TEMPLATE_USAGE.md before writing any real code.
One-sentence description of what this project provisions/deploys and on what infrastructure (host class, FIPS mode or not, live or planning stage).
Building or operating this project? See
DEPLOYMENT_RUNBOOK.md (dependency-ordered
build sequence, real gotchas from first live deployment) and
OPERATIONS_RUNBOOK.md (health checks,
maintenance, troubleshooting).
Longer description: what problem this solves, why it exists, what it deliberately does not do, and how it relates to other projects in the lab if relevant (additive to an existing system? replacing something? a dependency of something else?).
- Python 3.11+,
git - Anything this specific project needs: build tooling, sibling repos it checks out next to itself, credential files, infrastructure that must already exist and be reachable.
- See
docs/INFRASTRUCTURE_PREREQUISITES.mdfor the full detail once this project has any.
pip install -r requirements.txt # pyyaml, rich, paramiko, hvac, pytest, ruff, yamllint
make check # ruff + yamllint + 11 tests, all should pass on a fresh checkoutmake check is the one gate. CI runs the same command on every merge
request (.gitlab-ci.yml, .github/workflows/ci.yml, or
.gitea/workflows/ci.yml, whichever the host reads). make format
fixes what ruff can fix.
CONTRIBUTING.md has the full set. Short version: branch type/topic
from a protected main, Conventional Commits with a body that says why,
every change by merge request with the checklist filled in, linear
history, semver tags, and a line in CHANGELOG.md. Nothing secret,
rendered, or large-binary in Git.
roles/<role_name>/role.py- one stage of the provisioning pipeline per role, each a plain Python module with arun(target, apply, console, **kwargs)function. Explicit registry inroles/__init__.py(ROLES = {"name": module.run, ...}) - not filesystem auto-discovery, so an unknown role name fails loudly instead of silently skipping.modules/- shared helper code used by 2+ roles in this project (inventory loading, SSH helpers, report writing). Sibling projects don't import each other'smodules/orroles/- each project keeps its own copy of anything it needs, even if near-identical to another project's version. Deliberate convention: every project is independent and self-contained, so one project can be refactored, archived, or handed off without breaking any other.modules/secrets.py- OpenBao access (AppRole login + KV-v2 read viahvac), fully self-contained. OpenBao is the primary secrets mechanism: seed new credentials there first, and have roles read them withget_secret("<project>/<name>")instead of from local files. Configuration is env-var driven (OPENBAO_ADDR,OPENBAO_CACERT,OPENBAO_SECRET_ID, plusopenbao_role_idin the gitignoredinventory/openbao_approle.yml) - see the module docstring.inventory/*.example.yml- checked-in templates for the realinventory/*.ymlfiles (gitignored, contain real IPs/hostnames/ credentials-by-reference). Copy the.example.yml, fill it in, never commit the real one.scripts/provision.py- the CLI entry point:--role <name> --host <vm_name>|--all [--apply]. Dry-run by default.tests/- onetest_<role_name>.pyper role, plustest_roles_registry.pyasserting every registered role'srun()has the right signature.reports/- generated output (provisioning run reports, downloaded artifacts). Gitignored except.gitkeep; never commit real output.Makefile,pyproject.toml,.yamllint,.editorconfig- the checks and the settings behind them.CONTRIBUTING.md,.gitmessage,CHANGELOG.md, and the merge request template under.gitlab/,.github/, and.gitea/carry the Git conventions.