ansible/Makefile
ilia 579f0709ce Update Makefile and inventory configurations for improved task execution and organization
- Refactor Makefile to enhance command structure, including clearer descriptions and usage examples for targets related to development, inventory, and monitoring tasks.
- Update inventory files to ensure correct host configurations and user settings, including adjustments to ansible_user for specific hosts.
- Modify group_vars to streamline Tailscale configuration and ensure proper handling of authentication keys.

These changes improve the clarity and usability of the Makefile and inventory setup, facilitating smoother operations across the infrastructure.
2025-10-09 21:24:45 -04:00

548 lines
21 KiB
Makefile

.PHONY: help bootstrap lint test check dev datascience inventory inventory-all local clean status tailscale tailscale-check tailscale-dev tailscale-status create-vault create-vm monitoring
.DEFAULT_GOAL := help
## Colors for output
BOLD := \033[1m
RED := \033[31m
GREEN := \033[32m
YELLOW := \033[33m
BLUE := \033[34m
RESET := \033[0m
# Playbook paths
PLAYBOOK_SITE := playbooks/site.yml
PLAYBOOK_DEV := playbooks/development.yml
PLAYBOOK_LOCAL := playbooks/local.yml
PLAYBOOK_MAINTENANCE := playbooks/maintenance.yml
PLAYBOOK_TAILSCALE := playbooks/tailscale.yml
PLAYBOOK_PROXMOX := playbooks/infrastructure/proxmox-vm.yml
# Collection and requirement paths
COLLECTIONS_REQ := collections/requirements.yml
PYTHON_REQ := requirements.txt
# Inventory paths
INVENTORY := inventories/production
INVENTORY_HOSTS := $(INVENTORY)/hosts
# Common ansible-playbook command with options
ANSIBLE_PLAYBOOK := ansible-playbook -i $(INVENTORY)
ANSIBLE_ARGS := --vault-password-file ~/.ansible-vault-pass
# Note: sudo passwords are in vault files as ansible_become_password
## Auto-detect current host to exclude from remote operations
CURRENT_IP := $(shell hostname -I | awk '{print $$1}')
CURRENT_HOST := $(shell ansible-inventory --list | jq -r '._meta.hostvars | to_entries[] | select(.value.ansible_host == "$(CURRENT_IP)") | .key' 2>/dev/null | head -1)
EXCLUDE_CURRENT := $(if $(CURRENT_HOST),--limit '!$(CURRENT_HOST)',)
help: ## Show this help message
@echo "$(BOLD)Ansible Development Environment$(RESET)"
@echo ""
@echo "$(BOLD)Available commands:$(RESET)"
@grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf " $(BLUE)%-15s$(RESET) %s\n", $$1, $$2}'
@echo ""
@echo "$(BOLD)Examples:$(RESET)"
@echo " make bootstrap # Set up dependencies"
@echo " make check # Dry run all hosts"
@echo " make apply # Run on all dev hosts"
@echo " make dev HOST=dev01 # Run on specific host"
@echo " make local # Run local playbook"
@echo " make maintenance # Run maintenance on all hosts"
@echo " make maintenance GROUP=dev # Run maintenance on dev group"
@echo " make maintenance HOST=dev01 # Run maintenance on specific host"
@echo " make maintenance CHECK=true # Dry-run maintenance on all hosts"
@echo " make maintenance VERBOSE=true # Run with verbose output"
@echo " make maintenance-verbose GROUP=dev # Verbose maintenance on dev group"
@echo ""
bootstrap: ## Install all project dependencies from requirements files
@echo "$(BOLD)Installing Project Dependencies$(RESET)"
@echo ""
@echo "$(YELLOW)Python Requirements ($(PYTHON_REQ)):$(RESET)"
@if [ -f "$(PYTHON_REQ)" ]; then \
if command -v pipx >/dev/null 2>&1; then \
printf " %-30s " "Installing with pipx"; \
if pipx install -r $(PYTHON_REQ) >/dev/null 2>&1; then \
echo "$(GREEN)✓ Installed$(RESET)"; \
else \
echo "$(YELLOW)⚠ Some packages may have failed$(RESET)"; \
fi; \
elif command -v pip3 >/dev/null 2>&1; then \
printf " %-30s " "Installing with pip3 --user"; \
if pip3 install --user -r $(PYTHON_REQ) >/dev/null 2>&1; then \
echo "$(GREEN)✓ Installed$(RESET)"; \
else \
printf " %-30s " "Trying with --break-system-packages"; \
if pip3 install --break-system-packages -r $(PYTHON_REQ) >/dev/null 2>&1; then \
echo "$(GREEN)✓ Installed$(RESET)"; \
else \
echo "$(RED)✗ Failed$(RESET)"; \
fi; \
fi; \
else \
printf " %-30s " "Python packages"; \
echo "$(YELLOW)⚠ Skipped (pip3/pipx not found)$(RESET)"; \
fi; \
else \
printf " %-30s " "$(PYTHON_REQ)"; \
echo "$(RED)✗ File not found$(RESET)"; \
fi
@echo ""
@echo "$(YELLOW)Node.js Dependencies (package.json):$(RESET)"
@if [ -f "package.json" ] && command -v npm >/dev/null 2>&1; then \
printf " %-30s " "Installing npm packages"; \
if npm install >/dev/null 2>&1; then \
echo "$(GREEN)✓ Installed$(RESET)"; \
else \
echo "$(RED)✗ Failed$(RESET)"; \
fi; \
else \
printf " %-30s " "npm packages"; \
echo "$(YELLOW)⚠ Skipped (package.json or npm not found)$(RESET)"; \
fi
@echo ""
@echo "$(YELLOW)Ansible Collections ($(COLLECTIONS_REQ)):$(RESET)"
@if [ -f "$(COLLECTIONS_REQ)" ]; then \
ansible-galaxy collection install -r $(COLLECTIONS_REQ) 2>&1 | grep -E "(Installing|Skipping|ERROR)" | while read line; do \
if echo "$$line" | grep -q "Installing"; then \
collection=$$(echo "$$line" | awk '{print $$2}' | sed 's/:.*//'); \
printf " $(GREEN)✓ %-30s$(RESET) Installed\n" "$$collection"; \
elif echo "$$line" | grep -q "Skipping"; then \
collection=$$(echo "$$line" | awk '{print $$2}' | sed 's/,.*//'); \
printf " $(BLUE)- %-30s$(RESET) Already installed\n" "$$collection"; \
elif echo "$$line" | grep -q "ERROR"; then \
printf " $(RED)✗ Error: $$line$(RESET)\n"; \
fi; \
done || ansible-galaxy collection install -r $(COLLECTIONS_REQ); \
else \
printf " %-30s " "$(COLLECTIONS_REQ)"; \
echo "$(RED)✗ File not found$(RESET)"; \
fi
@echo ""
lint: ## Run ansible-lint on all playbooks and roles
@echo "$(YELLOW)Running ansible-lint...$(RESET)"
ansible-lint
@echo "$(GREEN)✓ Linting completed$(RESET)"
test-syntax: ## Run comprehensive syntax and validation checks
@echo "$(BOLD)Comprehensive Testing$(RESET)"
@echo ""
@echo "$(YELLOW)Playbook Syntax:$(RESET)"
@for playbook in $(PLAYBOOK_DEV) $(PLAYBOOK_LOCAL) $(PLAYBOOK_MAINTENANCE) $(PLAYBOOK_TAILSCALE); do \
if [ -f "$$playbook" ]; then \
printf " %-25s " "$$playbook"; \
if ansible-playbook "$$playbook" --syntax-check >/dev/null 2>&1; then \
echo "$(GREEN)✓ OK$(RESET)"; \
else \
echo "$(RED)✗ FAIL$(RESET)"; \
fi; \
fi; \
done
@echo ""
@echo "$(YELLOW)Infrastructure Playbooks:$(RESET)"
@for playbook in playbooks/infrastructure/*.yml; do \
if [ -f "$$playbook" ]; then \
printf " %-25s " "$$playbook"; \
if ansible-playbook "$$playbook" --syntax-check >/dev/null 2>&1; then \
echo "$(GREEN)✓ OK$(RESET)"; \
else \
echo "$(RED)✗ FAIL$(RESET)"; \
fi; \
fi; \
done
@echo ""
@echo "$(YELLOW)Role Test Playbooks:$(RESET)"
@for test_playbook in roles/*/tests/test.yml; do \
if [ -f "$$test_playbook" ]; then \
role_name=$$(echo "$$test_playbook" | cut -d'/' -f2); \
printf " %-25s " "roles/$$role_name/test"; \
if ansible-playbook "$$test_playbook" --syntax-check >/dev/null 2>&1; then \
echo "$(GREEN)✓ OK$(RESET)"; \
else \
echo "$(RED)✗ FAIL$(RESET)"; \
fi; \
fi; \
done
@echo ""
@echo "$(YELLOW)Markdown Validation:$(RESET)"
@if [ -f "package.json" ] && command -v npm >/dev/null 2>&1; then \
printf " %-25s " "Markdown syntax"; \
if npm run test:markdown >/dev/null 2>&1; then \
echo "$(GREEN)✓ OK$(RESET)"; \
else \
echo "$(YELLOW)⚠ Issues$(RESET)"; \
fi; \
else \
printf " %-25s " "Markdown syntax"; \
echo "$(YELLOW)⚠ npm/package.json not available$(RESET)"; \
fi
@echo ""
@echo "$(YELLOW)Documentation Links:$(RESET)"
@if [ -f "package.json" ] && command -v npm >/dev/null 2>&1; then \
printf " %-25s " "Link validation"; \
if npm run test:links >/dev/null 2>&1; then \
echo "$(GREEN)✓ OK$(RESET)"; \
else \
echo "$(YELLOW)⚠ Issues$(RESET)"; \
fi; \
else \
printf " %-25s " "Link validation"; \
echo "$(YELLOW)⚠ npm/package.json not available$(RESET)"; \
fi
@echo ""
@echo "$(YELLOW)Configuration Validation:$(RESET)"
@for yaml_file in inventories/production/group_vars/all/main.yml; do \
if [ -f "$$yaml_file" ]; then \
printf " %-25s " "$$yaml_file (YAML)"; \
if python3 -c "import yaml; yaml.safe_load(open('$$yaml_file'))" >/dev/null 2>&1; then \
echo "$(GREEN)✓ OK$(RESET)"; \
else \
echo "$(RED)✗ FAIL$(RESET)"; \
fi; \
fi; \
done
@printf " %-25s " "ansible.cfg (INI)"; \
if python3 -c "import configparser; c=configparser.ConfigParser(); c.read('ansible.cfg')" >/dev/null 2>&1; then \
echo "$(GREEN)✓ OK$(RESET)"; \
else \
echo "$(RED)✗ FAIL$(RESET)"; \
fi
@echo ""
test: ## Run all tests (lint + syntax check if available)
@if command -v ansible-lint >/dev/null 2>&1; then \
echo "$(YELLOW)Running ansible-lint...$(RESET)"; \
ansible-lint 2>/dev/null && echo "$(GREEN)✓ Linting completed$(RESET)" || echo "$(YELLOW)⚠ Linting had issues$(RESET)"; \
echo ""; \
fi
@$(MAKE) test-syntax
check: auto-fallback ## Dry-run the development playbook (--check mode)
@echo "$(YELLOW)Running dry-run on development hosts...$(RESET)"
$(ANSIBLE_PLAYBOOK) $(PLAYBOOK_DEV) --check --diff
check-local: ## Dry-run the local playbook
@echo "$(YELLOW)Running dry-run on localhost...$(RESET)"
$(ANSIBLE_PLAYBOOK) $(PLAYBOOK_LOCAL) --check --diff -K
site: ## Run the complete site playbook
@echo "$(YELLOW)Running complete site deployment...$(RESET)"
$(ANSIBLE_PLAYBOOK) $(PLAYBOOK_SITE)
local: ## Run the local playbook on localhost
@echo "$(YELLOW)Applying local playbook...$(RESET)"
$(ANSIBLE_PLAYBOOK) $(PLAYBOOK_LOCAL) -K
# Host-specific targets
dev: ## Run on specific host (usage: make dev HOST=dev01)
ifndef HOST
@echo "$(RED)Error: HOST parameter required$(RESET)"
@echo "Usage: make dev HOST=dev01"
@exit 1
endif
@echo "$(YELLOW)Running on host: $(HOST)$(RESET)"
$(ANSIBLE_PLAYBOOK) $(PLAYBOOK_DEV) --limit $(HOST)
# Data science role
datascience: ## Install data science stack (usage: make datascience HOST=server01)
ifndef HOST
@echo "$(RED)Error: HOST parameter required$(RESET)"
@echo "Usage: make datascience HOST=server01"
@exit 1
endif
@echo "$(YELLOW)Installing data science stack on: $(HOST)$(RESET)"
$(ANSIBLE_PLAYBOOK) $(PLAYBOOK_DEV) --limit $(HOST) --tags datascience
# Inventory system
inventory: ## Show installed software on host (usage: make inventory HOST=dev01)
ifndef HOST
@echo "$(RED)Error: HOST parameter required$(RESET)"
@echo "Usage: make inventory HOST=dev01"
@exit 1
endif
@echo "$(BOLD)Software Inventory for: $(HOST)$(RESET)"
@echo ""
@ip=$$(ansible-inventory --list | jq -r "._meta.hostvars.$(HOST).ansible_host // empty" 2>/dev/null); \
user=$$(ansible-inventory --list | jq -r "._meta.hostvars.$(HOST).ansible_user // empty" 2>/dev/null); \
if [ -n "$$ip" ] && [ "$$ip" != "null" ] && [ -n "$$user" ] && [ "$$user" != "null" ]; then \
scp -q scripts/inventory.sh $$user@$$ip:/tmp/inventory.sh 2>/dev/null && \
ssh $$user@$$ip 'bash /tmp/inventory.sh; rm /tmp/inventory.sh' 2>/dev/null; \
else \
echo "$(RED)Could not determine IP or user for $(HOST)$(RESET)"; \
fi
inventory-all: ## Show installed software on all hosts
@echo "$(BOLD)Software Inventory - All Hosts$(RESET)\n"
@for host in $$(ansible all --list-hosts 2>/dev/null | grep -v "hosts" | tr -d ' '); do \
echo "$(YELLOW)=== $$host ===$(RESET)"; \
ansible $$host -m script -a "scripts/inventory.sh" 2>/dev/null | sed -n '/CHANGED/,$$p' | tail -n +3 || echo "$(RED)Failed to connect$(RESET)"; \
echo ""; \
done
# Tag-based execution
security: ## Run only security-related roles
@echo "$(YELLOW)Running security roles...$(RESET)"
$(ANSIBLE_PLAYBOOK) $(PLAYBOOK_DEV) --tags security
# Unified maintenance target with intelligent parameter detection
maintenance: ## Run maintenance (usage: make maintenance [GROUP=dev] [HOST=dev01] [SERIAL=1] [CHECK=true] [VERBOSE=true])
@$(MAKE) _maintenance-run
_maintenance-run:
@# Determine target and build command
@TARGET="all"; \
ANSIBLE_CMD="$(ANSIBLE_PLAYBOOK) $(PLAYBOOK_MAINTENANCE)"; \
DESCRIPTION="all hosts"; \
NEED_SUDO=""; \
\
if [ -n "$(HOST)" ]; then \
TARGET="host $(HOST)"; \
ANSIBLE_CMD="$$ANSIBLE_CMD --limit $(HOST)"; \
DESCRIPTION="host $(HOST)"; \
if [ "$(HOST)" = "localhost" ]; then \
NEED_SUDO="-K"; \
fi; \
elif [ -n "$(GROUP)" ]; then \
TARGET="$(GROUP) group"; \
ANSIBLE_CMD="$$ANSIBLE_CMD -e target_group=$(GROUP)"; \
DESCRIPTION="$(GROUP) group"; \
if [ "$(GROUP)" = "local" ]; then \
NEED_SUDO="-K"; \
fi; \
else \
NEED_SUDO="-K"; \
fi; \
\
if [ -n "$(SERIAL)" ]; then \
ANSIBLE_CMD="$$ANSIBLE_CMD -e maintenance_serial=$(SERIAL)"; \
DESCRIPTION="$$DESCRIPTION (serial=$(SERIAL))"; \
fi; \
\
if [ "$(CHECK)" = "true" ]; then \
ANSIBLE_CMD="$$ANSIBLE_CMD --check --diff"; \
echo "$(YELLOW)Dry-run maintenance on $$DESCRIPTION...$(RESET)"; \
else \
echo "$(YELLOW)Running maintenance on $$DESCRIPTION...$(RESET)"; \
fi; \
\
if [ "$(VERBOSE)" = "true" ]; then \
ANSIBLE_CMD="$$ANSIBLE_CMD -v"; \
echo "$(BLUE)Running with verbose output...$(RESET)"; \
fi; \
\
if [ -n "$(GROUP)" ] && [ "$(GROUP)" != "dev" ] && [ "$(GROUP)" != "local" ]; then \
echo "$(BLUE)Available groups: dev, gitea, portainer, homepage, ansible, local$(RESET)"; \
fi; \
\
$$ANSIBLE_CMD $$NEED_SUDO
# Legacy/convenience aliases
maintenance-dev: ## Run maintenance on dev group (legacy alias)
@$(MAKE) maintenance GROUP=dev
maintenance-all: ## Run maintenance on all hosts (legacy alias)
@$(MAKE) maintenance
maintenance-check: ## Dry-run maintenance (legacy alias, usage: make maintenance-check [GROUP=dev])
@$(MAKE) maintenance CHECK=true GROUP=$(GROUP)
maintenance-verbose: ## Run maintenance with verbose output (usage: make maintenance-verbose [GROUP=dev])
@$(MAKE) maintenance VERBOSE=true GROUP=$(GROUP)
docker: ## Install/configure Docker only
@echo "$(YELLOW)Running Docker setup...$(RESET)"
$(ANSIBLE_PLAYBOOK) $(PLAYBOOK_DEV) --tags docker
shell: ## Configure shell only
@echo "$(YELLOW)Running shell configuration...$(RESET)"
$(ANSIBLE_PLAYBOOK) $(PLAYBOOK_DEV) --tags shell
shell-all: ## Configure shell on all shell_hosts (usage: make shell-all)
@echo "$(YELLOW)Running shell configuration on all shell hosts...$(RESET)"
$(ANSIBLE_PLAYBOOK) playbooks/shell.yml $(ANSIBLE_ARGS)
apps: ## Install applications only
@echo "$(YELLOW)Installing applications...$(RESET)"
$(ANSIBLE_PLAYBOOK) $(PLAYBOOK_DEV) --tags apps
# Connectivity targets
ping: auto-fallback ## Ping hosts with colored output (usage: make ping [GROUP=dev] [HOST=dev01])
ifdef HOST
@echo "$(YELLOW)Pinging host: $(HOST)$(RESET)"
@ansible $(HOST) -m ping --one-line | while read line; do \
if echo "$$line" | grep -q "SUCCESS"; then \
echo "$(GREEN)$$line$(RESET)"; \
elif echo "$$line" | grep -q "UNREACHABLE"; then \
echo "$(RED)$$line$(RESET)"; \
else \
echo "$(YELLOW)? $$line$(RESET)"; \
fi; \
done
else ifdef GROUP
@echo "$(YELLOW)Pinging $(GROUP) group...$(RESET)"
@ansible $(GROUP) -m ping --one-line | while read line; do \
if echo "$$line" | grep -q "SUCCESS"; then \
echo "$(GREEN)✓ $$line$(RESET)"; \
elif echo "$$line" | grep -q "UNREACHABLE"; then \
echo "$(RED)✗ $$line$(RESET)"; \
else \
echo "$(YELLOW)? $$line$(RESET)"; \
fi; \
done
else
@echo "$(YELLOW)Pinging all hosts...$(RESET)"
@if [ -n "$(CURRENT_HOST)" ]; then \
echo "$(BLUE)Auto-excluding current host: $(CURRENT_HOST) ($(CURRENT_IP))$(RESET)"; \
fi
@echo ""
@ansible all -m ping --one-line $(EXCLUDE_CURRENT) 2>/dev/null | grep -E "(SUCCESS|UNREACHABLE)" > /tmp/ping_results.tmp; \
success_count=$$(grep -c "SUCCESS" /tmp/ping_results.tmp 2>/dev/null || echo 0); \
fail_count=$$(grep -c "UNREACHABLE" /tmp/ping_results.tmp 2>/dev/null || echo 0); \
while read line; do \
host=$$(echo "$$line" | cut -d' ' -f1); \
if echo "$$line" | grep -q "SUCCESS"; then \
printf "$(GREEN) ✓ %-20s$(RESET) Connected\n" "$$host"; \
elif echo "$$line" | grep -q "UNREACHABLE"; then \
reason=$$(echo "$$line" | grep -o "Permission denied.*" | head -1 || echo "Connection failed"); \
printf "$(RED) ✗ %-20s$(RESET) $$reason\n" "$$host"; \
fi; \
done < /tmp/ping_results.tmp; \
rm -f /tmp/ping_results.tmp; \
echo ""; \
printf "$(BOLD)Summary:$(RESET) $(GREEN)$$success_count connected$(RESET), $(RED)$$fail_count failed$(RESET)\n"
endif
facts: ## Gather facts from all hosts
@echo "$(YELLOW)Gathering facts...$(RESET)"
ansible all -m setup --tree /tmp/facts $(EXCLUDE_CURRENT)
show-current: ## Show current host that will be auto-excluded
@echo "$(BOLD)Current Host Detection:$(RESET)"
@echo " Current IP: $(BLUE)$(CURRENT_IP)$(RESET)"
@echo " Current Host: $(BLUE)$(CURRENT_HOST)$(RESET)"
@echo " Exclude Flag: $(BLUE)$(EXCLUDE_CURRENT)$(RESET)"
clean: ## Clean up ansible artifacts
@echo "$(YELLOW)Cleaning up artifacts...$(RESET)"
rm -rf .ansible/facts/
find . -name "*.retry" -delete
@echo "$(GREEN)✓ Cleanup completed$(RESET)"
# Debug targets
debug: ## Run with debug output enabled
@echo "$(YELLOW)Running with debug output...$(RESET)"
$(ANSIBLE_PLAYBOOK) $(PLAYBOOK_DEV) -e "ansible_debug_output=true"
verbose: ## Run with verbose output
@echo "$(YELLOW)Running with verbose output...$(RESET)"
$(ANSIBLE_PLAYBOOK) $(PLAYBOOK_DEV) -vv
# Quick development workflow
quick: test check ## Quick test and check before applying
@echo "$(GREEN)✓ Ready to apply changes$(RESET)"
# Tailscale management
tailscale: ## Install Tailscale on all machines
@echo "$(YELLOW)Installing Tailscale on all machines...$(RESET)"
$(ANSIBLE_PLAYBOOK) $(PLAYBOOK_TAILSCALE)
@echo "$(GREEN)✓ Tailscale installation complete$(RESET)"
tailscale-check: ## Check Tailscale installation (dry-run)
@echo "$(YELLOW)Checking Tailscale installation...$(RESET)"
$(ANSIBLE_PLAYBOOK) $(PLAYBOOK_TAILSCALE) --check --diff
@echo "$(GREEN)✓ Tailscale check complete$(RESET)"
tailscale-dev: ## Install Tailscale on dev machines only
@echo "$(YELLOW)Installing Tailscale on dev machines...$(RESET)"
$(ANSIBLE_PLAYBOOK) $(PLAYBOOK_TAILSCALE) --limit dev
@echo "$(GREEN)✓ Tailscale installation on dev machines complete$(RESET)"
tailscale-status: ## Check Tailscale status on all machines
@echo "$(BOLD)Tailscale Status$(RESET)"
@if [ -n "$(CURRENT_HOST)" ]; then \
echo "$(BLUE)Auto-excluding current host: $(CURRENT_HOST) ($(CURRENT_IP))$(RESET)"; \
fi
@echo ""
@ansible all -m shell -a "tailscale status --json | jq -r '.Self.DNSName + \" (\" + .Self.TailscaleIPs[0] + \") - \" + .BackendState'" --become $(EXCLUDE_CURRENT) 2>/dev/null | while read line; do \
host=$$(echo "$$line" | cut -d' ' -f1); \
status=$$(echo "$$line" | grep -o "Running\|Stopped\|NeedsLogin" || echo "Unknown"); \
ip=$$(echo "$$line" | grep -o "100\.[0-9.]*" || echo "No IP"); \
if echo "$$line" | grep -q "SUCCESS"; then \
result=$$(echo "$$line" | cut -d'>' -f2 | tr -d ' "'); \
printf " $(GREEN)✓ %-20s$(RESET) %s\n" "$$host" "$$result"; \
elif echo "$$line" | grep -q "FAILED\|UNREACHABLE"; then \
printf " $(RED)✗ %-20s$(RESET) Not available\n" "$$host"; \
fi; \
done || ansible all -m shell -a "tailscale status" --become $(EXCLUDE_CURRENT) 2>/dev/null | grep -E "(SUCCESS|FAILED)" | while read line; do \
host=$$(echo "$$line" | cut -d' ' -f1); \
if echo "$$line" | grep -q "SUCCESS"; then \
printf " $(GREEN)✓ %-20s$(RESET) Connected\n" "$$host"; \
else \
printf " $(RED)✗ %-20s$(RESET) Failed\n" "$$host"; \
fi; \
done
# Vault management
edit-vault: ## Edit encrypted host vars (usage: make edit-vault HOST=dev01)
ifndef HOST
@echo "$(RED)Error: HOST parameter required$(RESET)"
@echo "Usage: make edit-vault HOST=dev01"
@exit 1
endif
ansible-vault edit host_vars/$(HOST).yml
edit-group-vault: ## Edit encrypted group vars (usage: make edit-group-vault)
ansible-vault edit inventories/production/group_vars/all/vault.yml
copy-ssh-key: ## Copy SSH key to specific host (usage: make copy-ssh-key HOST=giteaVM)
ifndef HOST
@echo "$(RED)Error: HOST parameter required$(RESET)"
@echo "Usage: make copy-ssh-key HOST=giteaVM"
@exit 1
endif
@echo "$(YELLOW)Copying SSH key to $(HOST)...$(RESET)"
@ip=$$(ansible-inventory --list | jq -r "._meta.hostvars.$(HOST).ansible_host // empty" 2>/dev/null); \
user=$$(ansible-inventory --list | jq -r "._meta.hostvars.$(HOST).ansible_user // empty" 2>/dev/null); \
if [ -n "$$ip" ] && [ "$$ip" != "null" ] && [ -n "$$user" ] && [ "$$user" != "null" ]; then \
echo "Target: $$user@$$ip"; \
ssh-copy-id $$user@$$ip; \
else \
echo "$(RED)Could not determine IP or user for $(HOST)$(RESET)"; \
echo "Check your inventory and host_vars"; \
fi
create-vault: ## Create encrypted vault file for secrets (passwords, auth keys, etc.)
@echo "$(YELLOW)Creating vault file for storing secrets...$(RESET)"
ansible-vault create group_vars/all/vault.yml
@echo "$(GREEN)✓ Vault file created. Add your secrets here (e.g. vault_tailscale_auth_key)$(RESET)"
create-vm: ## Create Ansible controller VM on Proxmox
@echo "$(YELLOW)Creating Ansible controller VM on Proxmox...$(RESET)"
$(ANSIBLE_PLAYBOOK) $(PLAYBOOK_PROXMOX) --ask-vault-pass
@echo "$(GREEN)✓ VM creation complete$(RESET)"
monitoring: ## Install monitoring tools on all machines
@echo "$(YELLOW)Installing monitoring tools...$(RESET)"
$(ANSIBLE_PLAYBOOK) $(PLAYBOOK_DEV) --tags monitoring
@echo "$(GREEN)✓ Monitoring installation complete$(RESET)"
test-connectivity: ## Test host connectivity with detailed diagnostics and recommendations
@echo "$(YELLOW)Testing host connectivity...$(RESET)"
@if [ -f "test_connectivity.py" ]; then \
python3 test_connectivity.py --hosts-file $(INVENTORY_HOSTS); \
else \
echo "$(RED)Error: test_connectivity.py not found$(RESET)"; \
exit 1; \
fi
auto-fallback: ## Automatically switch to fallback IPs when primary IPs fail
@echo "$(YELLOW)Auto-fallback: Testing and switching to working IPs...$(RESET)"
@if [ -f "auto-fallback.sh" ]; then \
chmod +x auto-fallback.sh && ./auto-fallback.sh; \
else \
echo "$(RED)Error: auto-fallback.sh not found$(RESET)"; \
exit 1; \
fi