--- - name: Service Inventory and Documentation Generator hosts: all gather_facts: yes vars: inventory_timestamp: "{{ ansible_date_time.iso8601 }}" inventory_dir: "/tmp/service_inventory" documentation_dir: "/tmp/service_docs" tasks: - name: Create inventory directories file: path: "{{ item }}" state: directory mode: '0755' loop: - "{{ inventory_dir }}" - "{{ documentation_dir }}" delegate_to: localhost run_once: true - name: Check if Docker is available shell: command -v docker >/dev/null 2>&1 register: docker_available changed_when: false ignore_errors: yes - name: Skip Docker tasks if not available set_fact: skip_docker: "{{ docker_available.rc != 0 }}" - name: Discover running services shell: | echo "=== SERVICE DISCOVERY ===" # System services (systemd) if command -v systemctl >/dev/null 2>&1; then echo "SYSTEMD_SERVICES:" systemctl list-units --type=service --state=active --no-legend | head -20 | while read service rest; do port_info="" # Try to extract port information from service files if systemctl show "$service" --property=ExecStart 2>/dev/null | grep -qE ":[0-9]+"; then port_info=$(systemctl show "$service" --property=ExecStart 2>/dev/null | grep -oE ":[0-9]+" | head -1) fi echo "$service$port_info" done echo "" fi # Synology services (if available) if command -v synoservice >/dev/null 2>&1; then echo "SYNOLOGY_SERVICES:" synoservice --list 2>/dev/null | grep -E "^\[.*\].*running" | head -20 echo "" fi # Network services (listening ports) echo "NETWORK_SERVICES:" if command -v netstat >/dev/null 2>&1; then netstat -tuln 2>/dev/null | grep LISTEN | head -20 elif command -v ss >/dev/null 2>&1; then ss -tuln 2>/dev/null | grep LISTEN | head -20 fi echo "" register: system_services changed_when: false - name: Discover Docker services shell: | if ! command -v docker >/dev/null 2>&1; then echo "Docker not available" exit 0 fi echo "=== DOCKER SERVICE DISCOVERY ===" # Get detailed container information docker ps --format "table {{.Names}}\t{{.Image}}\t{{.Status}}\t{{.Ports}}" 2>/dev/null | while IFS=$'\t' read name image status ports; do if [ "$name" != "NAMES" ]; then echo "CONTAINER: $name" echo " Image: $image" echo " Status: $status" echo " Ports: $ports" # Try to get more details labels=$(docker inspect "$name" --format '{{range $key, $value := .Config.Labels}}{{$key}}={{$value}}{{"\n"}}{{end}}' 2>/dev/null | head -5) if [ -n "$labels" ]; then echo " Labels:" echo "$labels" | sed 's/^/ /' fi # Check for health status health=$(docker inspect "$name" --format '{{.State.Health.Status}}' 2>/dev/null) if [ "$health" != "" ] && [ -n "$health" ]; then echo " Health: $health" fi echo "" fi done register: docker_services changed_when: false when: not skip_docker - name: Analyze service configurations shell: | echo "=== CONFIGURATION ANALYSIS ===" # Find common configuration directories config_dirs="/etc /opt /home/*/config /volume1/docker" echo "Configuration directories found:" for dir in $config_dirs; do if [ -d "$dir" ]; then # Look for common config files find "$dir" -maxdepth 3 -name "*.conf" -o -name "*.yaml" -o -name "*.yml" -o -name "*.json" -o -name "*.env" 2>/dev/null | head -10 | while read config_file; do if [ -r "$config_file" ]; then echo " $config_file" fi done fi done echo "" # Docker Compose files echo "Docker Compose files:" find /opt /home -name "docker-compose*.yml" -o -name "compose*.yml" 2>/dev/null | head -10 | while read compose_file; do echo " $compose_file" # Extract service names services=$(grep -E "^ [a-zA-Z0-9_-]+:" "$compose_file" 2>/dev/null | sed 's/://g' | sed 's/^ //' | head -5) if [ -n "$services" ]; then echo " Services: $(echo $services | tr '\n' ' ')" fi done register: config_analysis changed_when: false - name: Detect web interfaces and APIs shell: | echo "=== WEB INTERFACE DETECTION ===" # Common web interface ports web_ports="80 443 8080 8443 3000 5000 8000 9000 9090 3001 8081 8082 8083 8084 8085" for port in $web_ports; do # Check if port is listening if netstat -tuln 2>/dev/null | grep -q ":$port " || ss -tuln 2>/dev/null | grep -q ":$port "; then echo "Port $port is active" # Try to detect service type if curl -s -m 3 -I "http://localhost:$port" 2>/dev/null | head -1 | grep -q "200\|301\|302"; then server_header=$(curl -s -m 3 -I "http://localhost:$port" 2>/dev/null | grep -i "server:" | head -1) title=$(curl -s -m 3 "http://localhost:$port" 2>/dev/null | grep -i "" | head -1 | sed 's/<[^>]*>//g' | xargs) echo " HTTP Response: OK" if [ -n "$server_header" ]; then echo " $server_header" fi if [ -n "$title" ]; then echo " Title: $title" fi # Check for common API endpoints for endpoint in /api /health /status /metrics /version; do if curl -s -m 2 "http://localhost:$port$endpoint" >/dev/null 2>&1; then echo " API endpoint: http://localhost:$port$endpoint" break fi done fi echo "" fi done register: web_interfaces changed_when: false ignore_errors: yes - name: Generate service catalog set_fact: service_catalog: timestamp: "{{ inventory_timestamp }}" hostname: "{{ inventory_hostname }}" system_info: os: "{{ ansible_distribution }} {{ ansible_distribution_version }}" kernel: "{{ ansible_kernel }}" architecture: "{{ ansible_architecture }}" services: system: "{{ system_services.stdout }}" docker: "{{ docker_services.stdout if not skip_docker else 'Docker not available' }}" configurations: "{{ config_analysis.stdout }}" web_interfaces: "{{ web_interfaces.stdout }}" - name: Display service inventory debug: msg: | ========================================== 📋 SERVICE INVENTORY - {{ inventory_hostname }} ========================================== 🖥️ SYSTEM INFO: - OS: {{ service_catalog.system_info.os }} - Kernel: {{ service_catalog.system_info.kernel }} - Architecture: {{ service_catalog.system_info.architecture }} 🔧 SYSTEM SERVICES: {{ service_catalog.services.system }} 🐳 DOCKER SERVICES: {{ service_catalog.services.docker }} ⚙️ CONFIGURATIONS: {{ service_catalog.services.configurations }} 🌐 WEB INTERFACES: {{ service_catalog.services.web_interfaces }} ========================================== - name: Generate JSON service inventory copy: content: | { "timestamp": "{{ service_catalog.timestamp }}", "hostname": "{{ service_catalog.hostname }}", "system_info": { "os": "{{ service_catalog.system_info.os }}", "kernel": "{{ service_catalog.system_info.kernel }}", "architecture": "{{ service_catalog.system_info.architecture }}" }, "services": { "system": {{ service_catalog.services.system | to_json }}, "docker": {{ service_catalog.services.docker | to_json }}, "configurations": {{ service_catalog.services.configurations | to_json }}, "web_interfaces": {{ service_catalog.services.web_interfaces | to_json }} } } dest: "{{ inventory_dir }}/{{ inventory_hostname }}_inventory_{{ ansible_date_time.epoch }}.json" delegate_to: localhost - name: Generate Markdown documentation copy: content: | # Service Documentation - {{ inventory_hostname }} **Generated:** {{ inventory_timestamp }} **System:** {{ service_catalog.system_info.os }} ({{ service_catalog.system_info.architecture }}) ## 🔧 System Services ``` {{ service_catalog.services.system }} ``` ## 🐳 Docker Services ``` {{ service_catalog.services.docker }} ``` ## ⚙️ Configuration Files ``` {{ service_catalog.services.configurations }} ``` ## 🌐 Web Interfaces & APIs ``` {{ service_catalog.services.web_interfaces }} ``` ## 📊 Quick Stats - **Hostname:** {{ inventory_hostname }} - **OS:** {{ service_catalog.system_info.os }} - **Kernel:** {{ service_catalog.system_info.kernel }} - **Architecture:** {{ service_catalog.system_info.architecture }} - **Docker Available:** {{ 'Yes' if not skip_docker else 'No' }} --- *Auto-generated by Ansible service_inventory.yml playbook* dest: "{{ documentation_dir }}/{{ inventory_hostname }}_services.md" delegate_to: localhost - name: Generate consolidated inventory (run once) shell: | cd "{{ inventory_dir }}" echo "# Homelab Service Inventory" > consolidated_inventory.md echo "" >> consolidated_inventory.md echo "**Generated:** {{ inventory_timestamp }}" >> consolidated_inventory.md echo "" >> consolidated_inventory.md # Process all JSON files for json_file in *_inventory_*.json; do if [ -f "$json_file" ]; then hostname=$(basename "$json_file" | cut -d'_' -f1) echo "## 🖥️ $hostname" >> consolidated_inventory.md echo "" >> consolidated_inventory.md # Extract key information using basic tools if command -v jq >/dev/null 2>&1; then os=$(jq -r '.system_info.os' "$json_file" 2>/dev/null || echo "Unknown") echo "- **OS:** $os" >> consolidated_inventory.md echo "- **File:** [$json_file](./$json_file)" >> consolidated_inventory.md echo "- **Documentation:** [${hostname}_services.md](../service_docs/${hostname}_services.md)" >> consolidated_inventory.md else echo "- **File:** [$json_file](./$json_file)" >> consolidated_inventory.md fi echo "" >> consolidated_inventory.md fi done echo "---" >> consolidated_inventory.md echo "*Auto-generated by Ansible service_inventory.yml playbook*" >> consolidated_inventory.md delegate_to: localhost run_once: true - name: Summary message debug: msg: | 📋 Service inventory complete for {{ inventory_hostname }} 📄 JSON Report: {{ inventory_dir }}/{{ inventory_hostname }}_inventory_{{ ansible_date_time.epoch }}.json 📖 Markdown Doc: {{ documentation_dir }}/{{ inventory_hostname }}_services.md 📚 Consolidated: {{ inventory_dir }}/consolidated_inventory.md 💡 Use this playbook regularly to maintain up-to-date service documentation 💡 JSON files can be consumed by monitoring systems or dashboards