ansible role cloudflared - remove unlisted services
Posted in automation on December 21, 2020 by Adrian Wyssmann ‐ 3 min read
One of the enhancements I want to do for my ansible role “cloudflared” is this:
The role should compare running
cloudflare@xxx
services against the ones listed intunnels
and if not listed anymore to the following:
- stop and remove systemd service
- remove unused config file
- remove log file
The role does use systemd-unit-template cloudflared@{{ tunnel }}.service
and start an instance for each service in the list of tunnels
. So to implement the feature I have to figure out a way, of which related services are running. Well, one could use the shell
-module and start with something like this:
systemctl list-units --type service | grep -oEi 'cloudflared@.+' | cut -d '@' -f 2
This single liner would already give me the information I am looking for. But executing shell commands is usually not the best, so I was looking for the ansible way. It appears service_facts_module is the thing I am looking for:
Return service state information as fact data for various service management utilities
I easily can get all services like this
- hosts: servers
tasks:
- name: Populate service facts
service_facts:
- name: Setting host facts
set_fact:
tunnels_available: "{{ ansible_facts.services | list }}"
- debug: msg={{ tunnels_available }}
This returns something like this, including all services:
ok: [node001] => {
"msg": [
"apparmor",
"cgroupfs-mount",
"console-setup.sh",
"cpufrequtils",
...
"[email protected]",
"[email protected]",
...
A good starting point, however, I am only interested in cloudflared
services, thus the set_fact
has to be enhanced accordingly, using filters. In addition, as the role requires only the names of the tunnels (e.g. ssh
, k8s
), I have to strip away unnecessary information:
...
- name: Setting host facts
set_fact:
tunnels_available: "{{ ansible_facts.services | select('match', 'cloudflared@(.+).service') | map('regex_replace', 'cloudflared@', '') | map('regex_replace', '.service' '') | list }}
...
The result of this, is what I am looking for, I have a list of all available cloudflared
services:
ok: [node001] => (item=k8s) => {
"msg": "k8s"
}
ok: [node001] => (item=ssh) => {
"msg": "ssh"
}
As a last thing, for the removal of services, I only need the ones installed i.e. in the list tunnels_available
and not in the list of tunnels
. Thus I need to get the difference of 2 lists (items in 1 that don’t exist in 2):
...
- name: Setting tunnels to remove
set_fact:
tunnels_to_remove: "{{ tunnels_available | difference(tunnels) }}"
...
So assuming my list tunnels
contains ssh
and http
and I have the tunnels ssh
and k8s
installed - see at the beginning of the post, I should get as a result of k8s
to be removed
ok: [node001] => (item=k8s) => {
"msg": "k8s"
}
It worked, so now I have a variable tunnels_to_remove
which contains a list of all tunnels installed but to be removed. So that’s the staring point for the implementation of the feature. Have a look at the issue and the related code changes for details. Finally here you have a complete playbook which shows the essential steps as a proof-of-concept:
- hosts: servers
vars:
- tunnels:
- ssh
- http
tasks:
- debug: msg={{ item }}
with_items: "{{ tunnels }}"
- name: Populate service facts
service_facts:
- name: Setting tunnels_available
set_fact:
tunnels_available: "{{ ansible_facts.services | select('match', 'cloudflared@(.+).service') | map('regex_replace', 'cloudflared@', '') | map('regex_replace', '.service' '') | list }}"
- debug: msg={{ item }}
with_items: "{{ tunnels_available }}"
- name: Setting tunnels to remove
set_fact:
tunnels_to_remove: "{{ tunnels_available | difference(tunnels) }}"
- debug: msg={{ item }}
with_items: "{{ tunnels_to_remove }}"