--- - 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) | 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) | 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