Configuring rc Flags on OpenBSD via Ansible

A recent project of mine involved configuring an OpenBSD system via Ansible. On OpenBSD, some software must be configured using command line flags set in rc.conf.local. Every time I ran the Ansible playbook, the flags would be reset to their defaults (more precisely, erased entirely from rc.conf.local). What gives?

Watch Out for the Service Module

After a very frustrating couple of hours, I realized what the problem is. When I used the service module to enable a daemon, all other entries in rc.conf.local related to that service were removed, including the flags entry I had just created via my previous task. I suspect this may be fixed by the use of ‘rcctl’ in newer versions of Ansible than what’s currently in Homebrew.

Regardless, it’s really easy to work around once you know what’s happening. Simply use the service module to enable the package before you configure its flags.

Configuring rc Flags Using Ansible

Given that the current service module is useless for configuring these flags, I thought I’d share my strategy for configuring them. My first thought was to use ‘rcctl’ via the shell module. However, this has a drawback: Ansible isn’t able to know whether the command had any effect, and therefore any notification you might specify will always be run.

To work around this, I made use of the lineinfile module, like so:

- name: configure service's rc flags
  become: true
  lineinfile:
    dest: /etc/rc.conf.local
    regexp: "^service_flags="
    line: service__flags="--some-option"
    create: yes
  notify: restart service

Unfortunately, because the service module clobbers these flags, the handler will always fire because the task always results in changes. Avoiding this case becomes complicated, because enabling some services is done by adding to a list of pkg_scripts that will likely include other services.

This, too, can be worked around: the lineinfile module includes an option called backreferences. When set to true, you’ll be able to reference regexp groups in your line. Done right, you can preserve the other packages while ensuring the service you’re enabling is present, and you won’t needlessly notify your handlers.

Conclusion

It’s really unfortunate that there isn’t a more elegant or proper way to handle this with Ansible. That being said, this workaround isn’t awful. Since I didn’t find much useful information about this problem when searching the web, I hope this post can save someone out there a couple hours in the future.