This commit is contained in:
Mike McFetridge
2026-07-20 09:23:17 -04:00
parent c1315882da
commit 72272e4006
3179 changed files with 562960 additions and 14 deletions
@@ -0,0 +1,93 @@
---
- name: Docker orphan detection
hosts: all
become: true
gather_facts: false
vars:
report_root: "/opt/docker-reports"
report_timestamp: "{{ lookup('pipe', 'date +%Y%m%d-%H%M%S') }}"
report_file_name: "docker_orphan_detection_<< inventory_hostname >>_<< report_timestamp >>.txt"
tasks:
- name: Ensure report root exists
ansible.builtin.file:
path: "<< report_root >>"
state: directory
mode: "0750"
- name: List exited containers
ansible.builtin.command: docker ps -a --filter status=exited --format "{{.Names}}"
register: orphan_exited_containers
changed_when: false
- name: List dead containers
ansible.builtin.command: docker ps -a --filter status=dead --format "{{.Names}}"
register: orphan_dead_containers
changed_when: false
- name: List dangling images
ansible.builtin.command: docker images -f dangling=true -q
register: orphan_dangling_images
changed_when: false
- name: List dangling volumes
ansible.builtin.command: docker volume ls -qf dangling=true
register: orphan_dangling_volumes
changed_when: false
- name: List dangling networks
ansible.builtin.command: docker network ls -qf dangling=true
register: orphan_dangling_networks
changed_when: false
- name: Build orphan report body
ansible.builtin.set_fact:
docker_orphan_report_body: |
Host: << inventory_hostname >>
Timestamp: {{ lookup('pipe', 'date -u +%Y-%m-%dT%H:%M:%SZ') }}
Exited containers (<< orphan_exited_containers.stdout_lines | length >>):
<< (orphan_exited_containers.stdout_lines | default([])) | join('\n') if (orphan_exited_containers.stdout_lines | length > 0) else 'none' >>
Dead containers (<< orphan_dead_containers.stdout_lines | length >>):
<< (orphan_dead_containers.stdout_lines | default([])) | join('\n') if (orphan_dead_containers.stdout_lines | length > 0) else 'none' >>
Dangling images (<< orphan_dangling_images.stdout_lines | length >>):
<< (orphan_dangling_images.stdout_lines | default([])) | join('\n') if (orphan_dangling_images.stdout_lines | length > 0) else 'none' >>
Dangling volumes (<< orphan_dangling_volumes.stdout_lines | length >>):
<< (orphan_dangling_volumes.stdout_lines | default([])) | join('\n') if (orphan_dangling_volumes.stdout_lines | length > 0) else 'none' >>
Dangling networks (<< orphan_dangling_networks.stdout_lines | length >>):
<< (orphan_dangling_networks.stdout_lines | default([])) | join('\n') if (orphan_dangling_networks.stdout_lines | length > 0) else 'none' >>
- name: Save local orphan detection report
ansible.builtin.copy:
dest: "<< report_root >>/<< report_file_name >>"
content: "<< docker_orphan_report_body >>"
mode: "0640"
- name: Build Discord report content
ansible.builtin.set_fact:
discord_orphan_report_content: |
<< discord_message_prefix | default('Docker Orphan Detection Report') >>
```text
<< docker_orphan_report_body | truncate(1700, true, '...') >>
```
when:
- discord_enabled | default(false)
- (discord_webhook_url | default('')) | length > 0
- name: Send orphan report to Discord webhook
ansible.builtin.uri:
url: "<< discord_webhook_url | default('') >>"
method: POST
body_format: json
body:
username: "<< discord_username | default('Docker Reporter') >>"
content: "<< discord_orphan_report_content >>"
status_code: 204
when:
- discord_enabled | default(false)
- (discord_webhook_url | default('')) | length > 0
@@ -0,0 +1,155 @@
{
"slug": "docker-orphan-detection",
"kind": "ansible",
"metadata": {
"name": "Docker Orphan Detection",
"description": "Detects orphaned Docker resources (dead/exited containers, dangling images, volumes, and networks) and optionally sends a report to Discord via webhook.",
"tags": [
"docker",
"audit",
"orphan",
"report"
],
"icon": {
"provider": "mdi",
"id": "magnify",
"color": "fuchsia"
},
"draft": true,
"version": {
"name": "1",
"source_dep_name": "manual/docker-orphan-detection"
}
},
"variables": [
{
"title": "Discord Webhook",
"name": "discord",
"items": [
{
"name": "discord_enabled",
"type": "bool",
"title": "Discord",
"required": false,
"default": false,
"description": "Send orphan detection summary to Discord webhook"
},
{
"name": "discord_webhook_url",
"type": "str",
"title": "Discord Webhook URL",
"required": false,
"default": ""
},
{
"name": "discord_username",
"type": "str",
"title": "Discord Username",
"required": false,
"default": "Docker Reporter",
"description": "Sender name for webhook message",
"config": {
"placeholder": "Docker Reporter"
}
},
{
"name": "discord_message_prefix",
"type": "str",
"title": "Discord Message Prefix",
"required": false,
"default": "Docker Orphan Detection Report",
"description": "Prefix line sent before report content",
"config": {
"placeholder": "Docker Orphan Detection Report"
}
}
]
},
{
"title": "Internal",
"name": "internal",
"items": [
{
"name": "report_root",
"type": "str",
"title": "Report Root",
"required": false,
"default": "/reports",
"config": {
"placeholder": "/reports"
}
},
{
"name": "orphan_dead_containers",
"type": "str",
"title": "Orphan Dead Containers",
"required": false,
"default": ""
},
{
"name": "orphan_dangling_images",
"type": "str",
"title": "Orphan Dangling Images",
"required": false,
"default": ""
},
{
"name": "orphan_dangling_volumes",
"type": "str",
"title": "Orphan Dangling Volumes",
"required": false,
"default": ""
},
{
"name": "docker_orphan_report_body",
"type": "str",
"title": "Orphan Report Body",
"required": false,
"default": ""
},
{
"name": "report_timestamp",
"type": "str",
"title": "Report Timestamp",
"required": false,
"default": ""
},
{
"name": "orphan_exited_containers",
"type": "str",
"title": "Orphan Exited Containers",
"required": false,
"default": ""
},
{
"name": "orphan_dangling_networks",
"type": "str",
"title": "Orphan Dangling Networks",
"required": false,
"default": ""
},
{
"name": "report_file_name",
"type": "str",
"title": "Report File Name",
"required": false,
"default": ""
},
{
"name": "discord_orphan_report_content",
"type": "str",
"title": "Orphan Report",
"required": false,
"default": ""
},
{
"name": "inventory_hostname",
"type": "str",
"title": "Inventory Hostname",
"required": false,
"default": ""
}
]
}
]
}