2026-03-29 11:47:53 +00:00
|
|
|
---
|
|
|
|
|
- name: Delete stale Tailscale devices with reserved hostnames
|
|
|
|
|
block:
|
|
|
|
|
- name: Get Tailscale devices from API
|
|
|
|
|
uri:
|
|
|
|
|
url: "https://api.tailscale.com/api/v2/tailnet/{{ tailscale_tailnet }}/devices"
|
|
|
|
|
method: GET
|
|
|
|
|
headers:
|
|
|
|
|
Authorization: "Bearer {{ tailscale_api_key }}"
|
|
|
|
|
return_content: true
|
|
|
|
|
register: ts_devices
|
|
|
|
|
|
|
|
|
|
- name: Find stale devices matching reserved hostnames
|
|
|
|
|
set_fact:
|
|
|
|
|
stale_devices: >-
|
|
|
|
|
{{ ts_devices.json.devices | default([])
|
|
|
|
|
| selectattr('hostname', 'defined')
|
|
|
|
|
| selectattr('hostname', 'in', tailscale_reserved_hostnames)
|
2026-03-29 13:52:23 +00:00
|
|
|
| rejectattr('online', 'defined')
|
|
|
|
|
| list
|
|
|
|
|
+
|
|
|
|
|
ts_devices.json.devices | default([])
|
|
|
|
|
| selectattr('hostname', 'defined')
|
|
|
|
|
| selectattr('hostname', 'in', tailscale_reserved_hostnames)
|
|
|
|
|
| selectattr('online', 'defined')
|
|
|
|
|
| rejectattr('online', 'equalto', true)
|
2026-03-29 11:47:53 +00:00
|
|
|
| list }}
|
|
|
|
|
|
|
|
|
|
- name: Delete stale devices
|
|
|
|
|
uri:
|
|
|
|
|
url: "https://api.tailscale.com/api/v2/device/{{ item.id }}"
|
|
|
|
|
method: DELETE
|
|
|
|
|
headers:
|
|
|
|
|
Authorization: "Bearer {{ tailscale_api_key }}"
|
|
|
|
|
status_code: 200
|
|
|
|
|
loop: "{{ stale_devices }}"
|
|
|
|
|
loop_control:
|
|
|
|
|
label: "{{ item.name }} ({{ item.id }})"
|
|
|
|
|
when: stale_devices | length > 0
|
|
|
|
|
|
|
|
|
|
- name: Report cleaned devices
|
|
|
|
|
debug:
|
|
|
|
|
msg: "Deleted stale Tailscale device: {{ item.name }}"
|
|
|
|
|
loop: "{{ stale_devices }}"
|
|
|
|
|
when: stale_devices | length > 0
|
|
|
|
|
|
|
|
|
|
- name: No stale devices found
|
|
|
|
|
debug:
|
|
|
|
|
msg: "No stale Tailscale devices found."
|
|
|
|
|
when: stale_devices | length == 0
|
|
|
|
|
when:
|
|
|
|
|
- tailscale_api_key is defined
|
|
|
|
|
- tailscale_api_key | length > 0
|