# Ansible + Terraform homelab monorepo — Makefile # All operations go through these targets. Never run ansible-playbook or terraform directly. VENV := .venv PYTHON := $(VENV)/bin/python PIP := $(VENV)/bin/pip ANSIBLE := $(VENV)/bin/ansible PLAYBOOK := $(VENV)/bin/ansible-playbook GALAXY := $(VENV)/bin/ansible-galaxy LINT := $(VENV)/bin/ansible-lint MOLECULE := $(VENV)/bin/molecule # Vault password is resolved via ansible.cfg (vault_password_file); no flag needed. VAULT_ARGS := INVENTORY := -i inventories/production/hosts.yml TF := terraform TF_ENV ?= staging MOLECULE_IMAGE := forgejo.nyumbani.baobab.band/sjat/molecule-debian13:latest MOLECULE_DOCKERFILE := .docker/molecule-debian13/Dockerfile .DEFAULT_GOAL := help .PHONY: help setup collections lint test test-all check deploy encrypt decrypt new-role \ tf-init tf-plan tf-apply tf-output tf-inventory \ molecule-image molecule-image-push help: @echo "" @echo "Ansible homelab — available targets:" @echo "" @echo " make setup Create venv and install Python deps" @echo " make collections Install Ansible collections" @echo " make lint Run yamllint + ansible-lint" @echo " make test ROLE= Run Molecule tests for a role" @echo " make test-all Run Molecule tests for all roles" @echo " make check PLAYBOOK= Dry-run a playbook (check mode)" @echo " make deploy PLAYBOOK= Run a playbook against production" @echo " make encrypt FILE= Encrypt a vault file" @echo " make decrypt FILE= Decrypt a vault file" @echo " make new-role NAME= Scaffold a new role" @echo "" @echo " make tf-init [TF_ENV=staging] Initialise Terraform providers" @echo " make tf-plan [TF_ENV=staging] Show Terraform plan" @echo " make tf-apply [TF_ENV=staging] Apply Terraform changes" @echo " make tf-output [TF_ENV=staging] Print Terraform outputs as JSON" @echo " make tf-inventory [TF_ENV=staging] Regenerate Ansible inventory from Terraform outputs" @echo "" @echo " TF_ENV defaults to 'staging'. Use TF_ENV=production for production." @echo "" @echo " make molecule-image Build the Molecule test image locally" @echo " make molecule-image-push Push the test image to the Forgejo registry" @echo "" # ── Environment setup ───────────────────────────────────────────────────────── setup: python3 -m venv $(VENV) $(PIP) install --upgrade pip $(PIP) install -r requirements.txt @echo "Venv ready. Activate with: source $(VENV)/bin/activate" collections: $(GALAXY) collection install -r requirements.yml --upgrade # ── Linting ─────────────────────────────────────────────────────────────────── lint: $(VENV)/bin/yamllint . $(LINT) $(PYTHON) scripts/check-tags.py # ── Testing ─────────────────────────────────────────────────────────────────── test: ifndef ROLE $(error ROLE is required: make test ROLE=) endif cd roles/$(ROLE) && PATH="$(CURDIR)/$(VENV)/bin:$$PATH" molecule test test-all: @for role in roles/*/; do \ echo "── Testing $$role ──"; \ cd $$role && PATH="$(CURDIR)/$(VENV)/bin:$$PATH" molecule test; cd ../..; \ done # ── Playbook execution ──────────────────────────────────────────────────────── check: ifndef PLAYBOOK $(error PLAYBOOK is required: make check PLAYBOOK=) endif $(PLAYBOOK) $(INVENTORY) $(VAULT_ARGS) --check --diff playbooks/$(PLAYBOOK).yml deploy: ifndef PLAYBOOK $(error PLAYBOOK is required: make deploy PLAYBOOK=) endif $(PLAYBOOK) $(INVENTORY) $(VAULT_ARGS) playbooks/$(PLAYBOOK).yml # ── Vault ───────────────────────────────────────────────────────────────────── encrypt: ifndef FILE $(error FILE is required: make encrypt FILE=) endif $(ANSIBLE)-vault encrypt $(FILE) decrypt: ifndef FILE $(error FILE is required: make decrypt FILE=) endif $(ANSIBLE)-vault decrypt $(FILE) # ── Molecule test image ─────────────────────────────────────────────────────── molecule-image: docker build -t $(MOLECULE_IMAGE) -f $(MOLECULE_DOCKERFILE) . molecule-image-push: molecule-image docker push $(MOLECULE_IMAGE) # ── Terraform ───────────────────────────────────────────────────────────────── tf-init: $(TF) -chdir=terraform/environments/$(TF_ENV) init tf-plan: $(TF) -chdir=terraform/environments/$(TF_ENV) plan tf-apply: $(TF) -chdir=terraform/environments/$(TF_ENV) apply tf-output: $(TF) -chdir=terraform/environments/$(TF_ENV) output -json tf-inventory: ifndef TF_ENV $(error TF_ENV is required: make tf-inventory TF_ENV=) endif $(TF) -chdir=terraform/environments/$(TF_ENV) output -json \ | $(PYTHON) scripts/tf_to_inventory.py > inventories/$(TF_ENV)/hosts.yml @echo "Inventory written to inventories/$(TF_ENV)/hosts.yml" # ── Role scaffolding ────────────────────────────────────────────────────────── new-role: ifndef NAME $(error NAME is required: make new-role NAME=) endif mkdir -p roles/$(NAME)/tasks roles/$(NAME)/handlers roles/$(NAME)/defaults \ roles/$(NAME)/templates roles/$(NAME)/files roles/$(NAME)/meta \ roles/$(NAME)/molecule/default echo "---" > roles/$(NAME)/tasks/main.yml echo "---" > roles/$(NAME)/handlers/main.yml echo "---" > roles/$(NAME)/defaults/main.yml echo "---" > roles/$(NAME)/meta/main.yml printf '# %s\n\nRole description here.\n' "$(NAME)" > roles/$(NAME)/README.md cp .scaffold/molecule.yml roles/$(NAME)/molecule/default/molecule.yml sed 's/ROLE_NAME_PLACEHOLDER/$(NAME)/g' .scaffold/converge.yml > roles/$(NAME)/molecule/default/converge.yml cp .scaffold/verify.yml roles/$(NAME)/molecule/default/verify.yml @echo "Role $(NAME) scaffolded at roles/$(NAME)/" @echo "Next: fill in meta/main.yml, defaults/main.yml, tasks/main.yml, README.md"