How to clean up stuff from roles that aren't used any more on a server?

Suppose I have a host that is, among other things, a web server, where the related Ansible role installs nginx, performs some essential configuration in /etc/nginx, and opens up ports 80 and 443 in the firewall.

At some point I want that particular host to not be a web server any more, because for some reason I moved that service elsewhere. Just removing the server from [webservers] in the inventory would leave garbage in the server. Ideally, I'd like to uninstall nginx, remove the /etc/nginx directory (and some other directories), and close up ports 80 and 443 in the firewall.

In Puppet I can do this. A host that is a web server will have something like this in its configuration:

class { 'nginx':
  ensure => present,
}

and all I have to do is to replace "present" with "absent". If the nginx class is well-written, it will undo the changes it has made. (Typically an administrator will replace "present" with "absent", and later, when he's certain that all affected hosts have undone the configuration, he will remove the item from the manifest.)

What's more, I think that the Puppet firewall module automatically removes firewall rules that can't be found in the manifest any more; so I think that, for the firewall, you don't even need to do that "absent" thing above, the firewall will automatically close up anyway.

How can I achieve these things with Ansible?


Solution 1:

With Ansible, you wouldn't really do this any differently from how you would do it with Puppet.

In your example where you would set

class { 'nginx':
  ensure => absent,
}

you are relying on the author of that puppet module having written the necessary code to deal with removing everything. Not every puppet module has this.

Similarly, with Ansible, you might have roles that have both the necessary steps to install it as well as to remove it. The difference is only in how to invoke the two.

One approach could be one where the role in question exposes a variable to toggle the behaviour. For example, that nginx role might take a variable nginx_state which takes the values installed and absent.

In the role's tasks/main.yml, the role author might have something along the lines of..

- include: install.yml
  when: nginx_state|default('present') == "present"

- include: uninstall.yml
  when: nginx_state|default('present') == "absent"

..with the respective install/uninstall logic being split up between those two conditionally-included files.

Ansible roles can also be nested. As another way to do the same, a role author might for example provide a role nginx with another role inside of it, called uninstalled. You could then do:

- name: Uninstall nginx
  hosts: some_group
  roles:
    - nginx/uninstalled

Ansible, when compared to Puppet, arguably has fewer rules and guidelines about how things should be done so practices vary a bit more out in the wild, but the same concepts apply.

Solution 2:

Since you have your configuration/provisioning in Ansible, you can simply blow away the whole server, reinstall/provision a fresh one and have a nice clean known state to work it.

If you actually want to "reconfigure" it for other purposes then you'd need to create a new playbook that performs the necessary clean up tasks.

As far as I'm aware, all Ansible packaging modules support state=absent to ensure that a given package is not installed on your server. Additionally, apt module has purge=yes parameter which will clean up any remaining customized config files.

You could also create tasks to confirm that port 80 is firewalled. However, since you won't have any processes running on that port, firewalling it won't make any difference to the security of your server.