certbot cannot issue for internal hostnames (*.nas.local), which made nginx-enabled services with internal names fail on deploy. Add a <service>_nginx_letsencrypt flag (default false) and computed <service>_nginx_ssl_certificate/_key paths: when off the vhost uses the shared self-signed cert (nginx_default_ssl_cert), when on it uses the Let's Encrypt lineage. Gate the certbot include on the flag and apply the pattern to all service roles (deluge, fdroid, gitea, grafana, immich, metabase, ntfy, prowlarr, radarr, seerr, static_web, uptime_kuma).
367 lines
11 KiB
YAML
367 lines
11 KiB
YAML
---
|
|
# Grafana installation and configuration
|
|
|
|
- name: Validate required passwords are set
|
|
ansible.builtin.assert:
|
|
that:
|
|
- grafana_postgres_password is defined
|
|
- grafana_postgres_password | length >= 12
|
|
- grafana_admin_password is defined
|
|
- grafana_admin_password | length >= 12
|
|
- grafana_secret_key is defined
|
|
- grafana_secret_key | length >= 16
|
|
fail_msg: |
|
|
grafana_postgres_password, grafana_admin_password, and grafana_secret_key are required.
|
|
See roles/grafana/defaults/main.yml for configuration instructions.
|
|
success_msg: "Password validation passed"
|
|
|
|
- name: Load OS-specific variables
|
|
ansible.builtin.include_vars: "{{ item }}"
|
|
with_first_found:
|
|
- "{{ ansible_facts['os_family'] }}.yml"
|
|
- "debian.yml"
|
|
|
|
# Debian/Ubuntu installation
|
|
- name: Ensure apt keyrings directory exists (Debian/Ubuntu)
|
|
ansible.builtin.file:
|
|
path: /etc/apt/keyrings
|
|
state: directory
|
|
mode: "0755"
|
|
when: ansible_facts['os_family'] == 'Debian'
|
|
|
|
- name: Install Grafana APT key (Debian/Ubuntu)
|
|
ansible.builtin.get_url:
|
|
url: "{{ grafana_apt_key_url }}"
|
|
dest: /etc/apt/keyrings/grafana.asc
|
|
mode: "0644"
|
|
when: ansible_facts['os_family'] == 'Debian'
|
|
|
|
- name: Add Grafana APT repository (Debian/Ubuntu)
|
|
ansible.builtin.deb822_repository:
|
|
name: grafana
|
|
types: [deb]
|
|
uris: "{{ grafana_apt_repo_uri }}"
|
|
suites: "{{ grafana_apt_repo_suite }}"
|
|
components: [main]
|
|
signed_by: /etc/apt/keyrings/grafana.asc
|
|
state: present
|
|
when: ansible_facts['os_family'] == 'Debian'
|
|
|
|
- name: Install Grafana package (Debian/Ubuntu)
|
|
ansible.builtin.apt:
|
|
name: "{{ grafana_package }}={{ grafana_version }}"
|
|
state: present
|
|
update_cache: true
|
|
when: ansible_facts['os_family'] == 'Debian'
|
|
|
|
# Arch Linux installation
|
|
- name: Install Grafana package (Arch Linux)
|
|
community.general.pacman:
|
|
name: "{{ grafana_package }}"
|
|
state: present
|
|
when: ansible_facts['os_family'] == 'Archlinux'
|
|
|
|
# Database setup
|
|
- name: Create PostgreSQL database
|
|
community.postgresql.postgresql_db:
|
|
name: "{{ grafana_postgres_db_name }}"
|
|
state: present
|
|
become: true
|
|
become_user: "{{ postgres_admin_user }}"
|
|
|
|
- name: Create PostgreSQL user
|
|
community.postgresql.postgresql_user:
|
|
name: "{{ grafana_postgres_user }}"
|
|
password: "{{ grafana_postgres_password }}"
|
|
state: present
|
|
become: true
|
|
become_user: "{{ postgres_admin_user }}"
|
|
|
|
- name: Grant database privileges
|
|
community.postgresql.postgresql_privs:
|
|
login_db: "{{ grafana_postgres_db_name }}"
|
|
roles: "{{ grafana_postgres_user }}"
|
|
type: database
|
|
privs: ALL
|
|
state: present
|
|
become: true
|
|
become_user: "{{ postgres_admin_user }}"
|
|
|
|
- name: Grant schema privileges
|
|
community.postgresql.postgresql_privs:
|
|
login_db: "{{ grafana_postgres_db_name }}"
|
|
roles: "{{ grafana_postgres_user }}"
|
|
type: schema
|
|
objs: public
|
|
privs: CREATE,USAGE
|
|
state: present
|
|
become: true
|
|
become_user: "{{ postgres_admin_user }}"
|
|
|
|
- name: Ensure minimal privileges
|
|
community.postgresql.postgresql_user:
|
|
name: "{{ grafana_postgres_user }}"
|
|
role_attr_flags: NOSUPERUSER,NOCREATEDB,NOCREATEROLE
|
|
state: present
|
|
become: true
|
|
become_user: "{{ postgres_admin_user }}"
|
|
|
|
# Grafana configuration
|
|
- name: Configure Grafana
|
|
community.general.ini_file:
|
|
path: "{{ grafana_config_path }}"
|
|
section: "{{ item.section }}"
|
|
option: "{{ item.option }}"
|
|
value: "{{ item.value }}"
|
|
mode: "0640"
|
|
owner: root
|
|
group: "{{ grafana_group }}"
|
|
loop:
|
|
- section: "server"
|
|
option: "http_port"
|
|
value: "{{ grafana_http_port }}"
|
|
- section: "server"
|
|
option: "domain"
|
|
value: "{{ grafana_domain }}"
|
|
- section: "server"
|
|
option: "root_url"
|
|
value: "{{ grafana_root_url }}"
|
|
- section: "database"
|
|
option: "type"
|
|
value: "{{ grafana_postgres_type }}"
|
|
- section: "database"
|
|
option: "host"
|
|
value: "{{ grafana_postgres_host }}:{{ grafana_postgres_port }}"
|
|
- section: "database"
|
|
option: "name"
|
|
value: "{{ grafana_postgres_db_name }}"
|
|
- section: "database"
|
|
option: "user"
|
|
value: "{{ grafana_postgres_user }}"
|
|
- section: "database"
|
|
option: "password"
|
|
value: "{{ grafana_postgres_password }}"
|
|
- section: "database"
|
|
option: "ssl_mode"
|
|
value: "{{ grafana_postgres_ssl_mode }}"
|
|
- section: "security"
|
|
option: "admin_user"
|
|
value: "{{ grafana_admin_user }}"
|
|
- section: "security"
|
|
option: "admin_password"
|
|
value: "{{ grafana_admin_password }}"
|
|
- section: "security"
|
|
option: "secret_key"
|
|
value: "{{ grafana_secret_key }}"
|
|
- section: "analytics"
|
|
option: "reporting_enabled"
|
|
value: "{{ grafana_reporting_enabled }}"
|
|
- section: "analytics"
|
|
option: "check_for_updates"
|
|
value: "{{ grafana_check_for_updates }}"
|
|
notify: Restart grafana
|
|
|
|
# Provisioning (datasources + dashboards as code)
|
|
- name: Ensure Grafana provisioning directories exist
|
|
ansible.builtin.file:
|
|
path: "{{ item }}"
|
|
state: directory
|
|
owner: "{{ grafana_user }}"
|
|
group: "{{ grafana_group }}"
|
|
mode: "0755"
|
|
loop:
|
|
- "{{ grafana_provisioning_path }}/datasources"
|
|
- "{{ grafana_provisioning_path }}/dashboards"
|
|
|
|
- name: Deploy Prometheus datasource provisioning
|
|
ansible.builtin.copy:
|
|
src: prometheus-datasource.yaml
|
|
dest: "{{ grafana_provisioning_path }}/datasources/prometheus.yaml"
|
|
owner: "{{ grafana_user }}"
|
|
group: "{{ grafana_group }}"
|
|
mode: "0644"
|
|
notify: Restart grafana
|
|
|
|
- name: Deploy dashboard provider provisioning
|
|
ansible.builtin.template:
|
|
src: dashboards-provider.yaml.j2
|
|
dest: "{{ grafana_provisioning_path }}/dashboards/dashboards.yaml"
|
|
owner: "{{ grafana_user }}"
|
|
group: "{{ grafana_group }}"
|
|
mode: "0644"
|
|
notify: Restart grafana
|
|
|
|
- name: Deploy node_exporter dashboard
|
|
ansible.builtin.copy:
|
|
src: node-exporter-dashboard.json
|
|
dest: "{{ grafana_provisioning_path }}/dashboards/node-exporter.json"
|
|
owner: "{{ grafana_user }}"
|
|
group: "{{ grafana_group }}"
|
|
mode: "0644"
|
|
notify: Restart grafana
|
|
|
|
- name: Deploy NUT dashboard
|
|
ansible.builtin.copy:
|
|
src: nut-status-dashboard.json
|
|
dest: "{{ grafana_provisioning_path }}/dashboards/nut-status.json"
|
|
owner: "{{ grafana_user }}"
|
|
group: "{{ grafana_group }}"
|
|
mode: "0644"
|
|
notify: Restart grafana
|
|
|
|
- name: Deploy S.M.A.R.T. dashboard
|
|
ansible.builtin.copy:
|
|
src: smartctl-dashboard.json
|
|
dest: "{{ grafana_provisioning_path }}/dashboards/smartctl.json"
|
|
owner: "{{ grafana_user }}"
|
|
group: "{{ grafana_group }}"
|
|
mode: "0644"
|
|
notify: Restart grafana
|
|
|
|
- name: Deploy fail2ban dashboard
|
|
ansible.builtin.copy:
|
|
src: fail2ban-dashboard.json
|
|
dest: "{{ grafana_provisioning_path }}/dashboards/fail2ban.json"
|
|
owner: "{{ grafana_user }}"
|
|
group: "{{ grafana_group }}"
|
|
mode: "0644"
|
|
notify: Restart grafana
|
|
|
|
- name: Remove legacy combined services dashboard
|
|
ansible.builtin.file:
|
|
path: "{{ grafana_provisioning_path }}/dashboards/services.json"
|
|
state: absent
|
|
notify: Restart grafana
|
|
|
|
- name: Deploy services dashboards (nginx, postgres, valkey, unbound)
|
|
ansible.builtin.copy:
|
|
src: "{{ item }}"
|
|
dest: "{{ grafana_provisioning_path }}/dashboards/{{ item }}"
|
|
owner: "{{ grafana_user }}"
|
|
group: "{{ grafana_group }}"
|
|
mode: "0644"
|
|
loop:
|
|
- nginx-dashboard.json
|
|
- postgres-dashboard.json
|
|
- valkey-dashboard.json
|
|
- unbound-dashboard.json
|
|
- wireguard-dashboard.json
|
|
notify: Restart grafana
|
|
|
|
- name: Deploy backup and systemd dashboards
|
|
ansible.builtin.copy:
|
|
src: "{{ item }}"
|
|
dest: "{{ grafana_provisioning_path }}/dashboards/{{ item }}"
|
|
owner: "{{ grafana_user }}"
|
|
group: "{{ grafana_group }}"
|
|
mode: "0644"
|
|
loop:
|
|
- backup-dashboard.json
|
|
- systemd-dashboard.json
|
|
notify: Restart grafana
|
|
|
|
- name: Deploy ZFS dashboard
|
|
ansible.builtin.copy:
|
|
src: zfs-dashboard.json
|
|
dest: "{{ grafana_provisioning_path }}/dashboards/zfs.json"
|
|
owner: "{{ grafana_user }}"
|
|
group: "{{ grafana_group }}"
|
|
mode: "0644"
|
|
notify: Restart grafana
|
|
|
|
# Alerting provisioning (Unified Alerting -> ntfy webhook)
|
|
- name: Validate ntfy alerting settings are set
|
|
ansible.builtin.assert:
|
|
that:
|
|
- grafana_ntfy_webhook_url is defined
|
|
- grafana_ntfy_webhook_url | length > 0
|
|
- grafana_ntfy_token is defined
|
|
- grafana_ntfy_token | length > 0
|
|
fail_msg: |
|
|
grafana_alerting_enabled is true but grafana_ntfy_webhook_url / grafana_ntfy_token are not set.
|
|
See roles/grafana/defaults/main.yml for configuration instructions.
|
|
success_msg: "ntfy alerting settings validated"
|
|
when: grafana_alerting_enabled
|
|
|
|
- name: Ensure Grafana alerting provisioning directory exists
|
|
ansible.builtin.file:
|
|
path: "{{ grafana_provisioning_path }}/alerting"
|
|
state: directory
|
|
owner: "{{ grafana_user }}"
|
|
group: "{{ grafana_group }}"
|
|
mode: "0755"
|
|
when: grafana_alerting_enabled
|
|
|
|
- name: Deploy Grafana alerting contact points
|
|
ansible.builtin.template:
|
|
src: alerting-contact-points.yaml.j2
|
|
dest: "{{ grafana_provisioning_path }}/alerting/contact-points.yaml"
|
|
owner: "{{ grafana_user }}"
|
|
group: "{{ grafana_group }}"
|
|
mode: "0600"
|
|
when: grafana_alerting_enabled
|
|
notify: Restart grafana
|
|
|
|
- name: Deploy Grafana alerting notification policies
|
|
ansible.builtin.copy:
|
|
src: alerting-policies.yaml
|
|
dest: "{{ grafana_provisioning_path }}/alerting/policies.yaml"
|
|
owner: "{{ grafana_user }}"
|
|
group: "{{ grafana_group }}"
|
|
mode: "0644"
|
|
when: grafana_alerting_enabled
|
|
notify: Restart grafana
|
|
|
|
- name: Deploy Grafana disk alert rules
|
|
ansible.builtin.copy:
|
|
src: alerting-rules.yaml
|
|
dest: "{{ grafana_provisioning_path }}/alerting/rules.yaml"
|
|
owner: "{{ grafana_user }}"
|
|
group: "{{ grafana_group }}"
|
|
mode: "0644"
|
|
when: grafana_alerting_enabled
|
|
notify: Restart grafana
|
|
|
|
- name: Remove Grafana alerting provisioning when disabled
|
|
ansible.builtin.file:
|
|
path: "{{ grafana_provisioning_path }}/alerting/{{ item }}"
|
|
state: absent
|
|
loop:
|
|
- contact-points.yaml
|
|
- policies.yaml
|
|
- rules.yaml
|
|
when: not grafana_alerting_enabled
|
|
notify: Restart grafana
|
|
|
|
# Service management
|
|
- name: Enable and start Grafana service
|
|
ansible.builtin.systemd:
|
|
name: "{{ grafana_service }}"
|
|
enabled: true
|
|
state: started
|
|
daemon_reload: true
|
|
|
|
# Nginx integration
|
|
- name: Provision TLS certificate for Grafana
|
|
ansible.builtin.include_tasks: "{{ role_path }}/../nginx/tasks/certbot.yml"
|
|
vars:
|
|
certbot_hostname: "{{ grafana_nginx_hostname }}"
|
|
when: grafana_nginx_enabled and grafana_nginx_letsencrypt
|
|
|
|
- name: Deploy nginx vhost
|
|
ansible.builtin.template:
|
|
src: nginx-vhost.conf.j2
|
|
dest: "{{ nginx_conf_dir | default('/etc/nginx/conf.d') }}/grafana.conf"
|
|
owner: root
|
|
group: root
|
|
mode: "0644"
|
|
when: grafana_nginx_enabled
|
|
notify: Reload nginx
|
|
|
|
- name: Remove nginx vhost when disabled
|
|
ansible.builtin.file:
|
|
path: "{{ nginx_conf_dir | default('/etc/nginx/conf.d') }}/grafana.conf"
|
|
state: absent
|
|
when: not grafana_nginx_enabled
|
|
notify: Reload nginx
|