Set up a VM to run the UniFi controller with Ansible

It's been a while since I set up my UniFi controller and it's about time to move to a bigger box. This time I want to set it up using Ansible so I don't have to do all the research again when I want to move it again.

There are third-party docker containers available that have the UniFi controller in them, but I want the UniFi controller updated as soon as possible so I'm installing natively.

I'm using Debian 12 for this recap because Ubuntu is pushing their snaps a bit too hard.

Permalink to “Determine the steps”

There are a few steps to go from a blank system to a UniFi controller:

Permalink to “Update and upgrade”

First we want to update and upgrade the installed system packages, easy since there's a builtin for apt.

- name: Update and upgrade apt packages
  become: true
  apt:
    upgrade: yes
    update_cache: yes
    
Permalink to “Add the UniFi apt repo”

There's official instructions for this at the UniFi help site but there's some caveats to how they do it that I don't like:

  1. apt-key is deprecated
  2. Adding keys to /etc/apt/trusted.gpg.d is insecure.

With all of those in mind, we'll add the keyring to another folder and use the new sources list.

- name: Ensure dependencies for using apt repo over HTTPS are installed
  become: true
  ansible.builtin.apt:
    name: "{{ item }}"
    state: present
    loop:
    - ca-certificates
    - apt-transport-https
- name: Add UniFi apt key
  become: true
  ansible.builtin.get_url:
    url: https://dl.ui.com/unifi/unifi-repo.gpg
    dest: /etc/apt/keyrings/unifi-repo.gpg
    mode: '0644'
- name: Add unifi repository to apt
  become: true
  ansible.builtin.deb822_repository:
    name: 100-ubnt-unifi
    types: deb
    architectures:
      - amd64
      - arm64
    uris: https://www.ui.com/downloads/unifi/debian
    suites: stable
    components: ubiquiti
    signed_by: /etc/apt/keyrings/unifi-repo.gpg

Installing MongoDB & UniFi

The UniFi install docs have us installing the ancient MongoDB 3.6. Eww. UniFi 9.0+ supports the latest (at writing) MongoDB 8.0 so we'll be installing that.
This somewhat mirrors the official MongoDB docs barring some slight changes to put the gpg key next to where we put the UniFi key.
Once we've installed MongoDB we'll install UniFi and make sure both are running and set to start at boot.

- name: Add MongoDB apt key
  become: true
  ansible.builtin.get_url:
    url: https://www.mongodb.org/static/pgp/server-8.0.asc
    dest: /etc/apt/keyrings/mongodb-server-8.0.asc
    mode: '0644'
- name: Add mongodb repository to apt
  become: true
  ansible.builtin.deb822_repository:
    name: mongodb-org-8.0
    types: deb
    uris: http://repo.mongodb.org/apt/debian
    suites: bookworm/mongodb-org/8.0
    components: main
    signed_by: /etc/apt/keyrings/mongodb-server-8.0.asc
# install
- name: Install
  become: true
  ansible.builtin.apt:
    update_cache: true
    name: "{{ item }}"
    state: present
    loop:
    - mongodb-org
    - unifi
- name: Ensure services start on boot
  become: true
  ansible.builtin.service:
    name: "{{ item }}"
    state: started
    enabled: true
    loop:
    - mongod
    - unifi
Permalink to “Installing Certbot & Issuing a certificate”

2 big parts here in a single section.
First up is installing certbot, for which we'll use the OS packages rather than installing from pip.
Second up we'll issue a certificate, for which we'll use DNS validation so we can serve other things on the 80/443 of this server.
We're going to register without an email since they're not sending out expiration notices anymore.
You'll want to produce a template file with your DNS credentials and place it in the templates directory relative to this playbook.

- name: Install certbot
  become: true
  ansible.builtin.apt:
    name: "{{ item }}"
    state: present
  loop:
    - python3-certbot
    - python3-certbot-dns-cloudflare
    - cron
- name: Copy certbot dns credentials
  ansible.builtin.template:
    dest: ~/cfdnscreds.ini
    src: templates/cfdnscreds.ini
    mode: '0600'
- name: Create a certificate
  ansible.builtin.command:
    argv:
      - sudo
      - certbot
      - certonly
      - --noninteractive
      - --agree-tos
      - --register-unsafely-without-email
      - -d
      - <your domain>
      - --dns-cloudflare
      - --dns-cloudflare-credentials
      - ~/cfdnscreds.ini
- name: Add autorenew certbot crontab entry
  ansible.builtin.cron:
    name: "certbot renew"
    minute: 38
    hour: 0,12
    job: sudo certbot renew -q

Put the certificate in the UniFi keystore and keep it up to date

This section relies on a script created by Steve Jenkins, which takes care of loading our cert into the UniFi keystore so it can serve HTTPS directly. Download this script and modify the config options at the top to fit your environment, uncommenting the "Debian/Ubuntu" section.
This section doesn't actually run the script, it just sets it up to be run. You can run it manually if you want to confirm that it's all working before you let it go.

- name: Copy unifi dns certificate helper
  ansible.builtin.template:
    dest: ~/unifi_ssl_import.sh
    src: templates/unifi_ssl_import.sh
    mode: '0700'
- name: Add cron entry for unifi sll import
  become: true
  ansible.builtin.cron:
    name: "unifi ssl import"
    user: root
    minute: 45
    hour: 0,12
    job: /home/<your user>/unifi_ssl_import.sh

Conclusion

This was a fun side task and helped me learn a bit more about Ansible and apt. I welcome your feedback and hope this helps you set up an Ansible playbook that sets up a UniFi controller.