commit 41943f117490b1d2c89a530d831b270f73854d94 Author: Eric Meehan Date: Fri Nov 6 14:00:23 2026 -0500 Initial Grafana role, split out of ansible-role-eom Moves the Grafana Helm deployment and LDAP template out of ericomeehan.eom into its own role, and: - maps cn=DevOps Members,ou=Teams to Editor in the DevOps org (org 2), after DevOps Owners so owners keep Admin - pins chart 11.4.2 from Bitnami's OCI registry (the HTTP index no longer carries it) so redeploys don't upgrade to Grafana 12 - pins the bitnamilegacy/grafana image the cluster is already running Co-Authored-By: Claude Opus 5.5 diff --git a/README.md b/README.md new file mode 100644 index 0000000..c774fab --- /dev/null +++ b/README.md @@ -0,0 +1,59 @@ +ansible-role-grafana +==================== + +Deploys Grafana to the eom.dev Kubernetes cluster with the Bitnami Helm chart, +with LDAP sign-in mapped to Grafana organizations. + +Requirements +------------ + +- `kubernetes.core` collection, Helm on the target host +- OpenLDAP at `openldap.openldap.svc.cluster.local` +- Prometheus and Alertmanager in the `prometheus` namespace + +Role Variables +-------------- + +Required (from `secrets.yaml`): + +- `grafana_admin_password` +- `openldap_readonly_password` + +Defaults (`defaults/main.yml`): + +| Variable | Default | +|---|---| +| `grafana_chart_ref` | `oci://registry-1.docker.io/bitnamicharts/grafana` | +| `grafana_chart_version` | `11.4.2` | +| `grafana_image_repository` | `bitnamilegacy/grafana` | +| `grafana_image_tag` | `11.4.0-debian-12-r0` | +| `grafana_domain` | `grafana.eom.dev` | + +LDAP group mappings +------------------- + +Defined in `templates/ldap.toml.j2`. Grafana uses the first mapping that +matches per organization, so owner groups must come before member groups. + +| LDAP group | Org | Role | +|---|---|---| +| Grafana Administrators | 1 (Main) | Admin + server admin | +| Grafana Users | 1 (Main) | Viewer | +| DevOps Owners | 2 (DevOps) | Admin | +| DevOps Members | 2 (DevOps) | Editor | +| Finance Owners | 3 (Finance) | Admin | + +Example Playbook +---------------- + + - hosts: alpha-control-plane + become: true + vars_files: + - ../secrets.yaml + roles: + - role: ericomeehan.grafana + +License +------- + +MIT-0 diff --git a/defaults/main.yml b/defaults/main.yml new file mode 100644 index 0000000..3da70c3 --- /dev/null +++ b/defaults/main.yml @@ -0,0 +1,15 @@ +#SPDX-License-Identifier: MIT-0 +--- +# defaults file for ansible-role-grafana + +# Bitnami removed old chart versions from charts.bitnami.com; pull the pinned +# version from the OCI registry so redeploys don't silently upgrade Grafana. +grafana_chart_ref: oci://registry-1.docker.io/bitnamicharts/grafana +grafana_chart_version: 11.4.2 + +# Bitnami moved its free images to bitnamilegacy; the old bitnami/grafana tag +# referenced by the chart no longer pulls. +grafana_image_repository: bitnamilegacy/grafana +grafana_image_tag: 11.4.0-debian-12-r0 + +grafana_domain: grafana.eom.dev diff --git a/handlers/main.yml b/handlers/main.yml new file mode 100644 index 0000000..7ae01f9 --- /dev/null +++ b/handlers/main.yml @@ -0,0 +1,3 @@ +#SPDX-License-Identifier: MIT-0 +--- +# handlers file for ansible-role-grafana diff --git a/meta/main.yml b/meta/main.yml new file mode 100644 index 0000000..47fa1dc --- /dev/null +++ b/meta/main.yml @@ -0,0 +1,13 @@ +#SPDX-License-Identifier: MIT-0 +galaxy_info: + author: Eric Meehan + description: Deploy Grafana on the eom.dev Kubernetes cluster with LDAP sign-in + company: eom.dev + license: MIT-0 + min_ansible_version: "2.15" + galaxy_tags: + - grafana + - monitoring + - kubernetes + +dependencies: [] diff --git a/tasks/main.yml b/tasks/main.yml new file mode 100644 index 0000000..5231d3e --- /dev/null +++ b/tasks/main.yml @@ -0,0 +1,66 @@ +#SPDX-License-Identifier: MIT-0 +--- +# tasks file for ansible-role-grafana +- name: Deploy Grafana + kubernetes.core.helm: + name: grafana + chart_ref: "{{ grafana_chart_ref }}" + chart_version: "{{ grafana_chart_version }}" + release_namespace: grafana + create_namespace: true + values: + image: + repository: "{{ grafana_image_repository }}" + tag: "{{ grafana_image_tag }}" + metrics: + enabled: true + admin: + user: grafana_admin + password: "{{ grafana_admin_password }}" + persistence: + size: 64Gi + grafana: + extraEnvVars: + - name: GF_SERVER_DOMAIN + value: "{{ grafana_domain }}" + - name: GF_SERVER_ROOT_URL + value: "https://{{ grafana_domain }}/" + smtp: + enabled: true + user: grafana + password: "{{ grafana_admin_password }}" + host: mail.eom.dev:587 + fromAddress: grafana@eom.dev + fromName: Grafana + ldap: + enabled: true + allowSignUp: true + configuration: "{{ lookup('template', 'ldap.toml.j2') }}" + ingress: + enabled: true + pathType: Prefix + hostname: "{{ grafana_domain }}" + annotations: + cert-manager.io/cluster-issuer: ca-issuer + ingressClassName: nginx + tls: true + datasources: + secretDefinition: + apiVersion: 1 + datasources: + - name: Prometheus + type: prometheus + access: proxy + orgId: 1 + url: http://prometheus-server.prometheus.svc.cluster.local + version: 1 + editable: true + isDefault: true + - name: Alertmanager + uid: alertmanager + type: alertmanager + access: proxy + orgId: 1 + url: http://prometheus-alertmanager.prometheus.svc.cluster.local + version: 1 + editable: true diff --git a/templates/ldap.toml.j2 b/templates/ldap.toml.j2 new file mode 100644 index 0000000..c86dbb2 --- /dev/null +++ b/templates/ldap.toml.j2 @@ -0,0 +1,75 @@ +[[servers]] +# Ldap server host (specify multiple hosts space separated) +host = "openldap.openldap.svc.cluster.local" +# Default port is 389 or 636 if use_ssl = true +port = 389 +# Set to true if LDAP server should use an encrypted TLS connection (either with STARTTLS or LDAPS) +use_ssl = false +# If set to true, use LDAP with STARTTLS instead of LDAPS +start_tls = false +# The value of an accepted TLS cipher. By default, this value is empty. Example value: ["TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384"]) +# For a complete list of supported ciphers and TLS versions, refer to: https://go.dev/src/crypto/tls/cipher_suites.go +# Starting with Grafana v11.0 only ciphers with ECDHE support are accepted for TLS 1.2 connections. +tls_ciphers = [] +# This is the minimum TLS version allowed. By default, this value is empty. Accepted values are: TLS1.1 (only for Grafana v10.4 or earlier), TLS1.2, TLS1.3. +min_tls_version = "" +# set to true if you want to skip SSL cert validation +ssl_skip_verify = false +# set to the path to your root CA certificate or leave unset to use system defaults +# root_ca_cert = "/path/to/certificate.crt" +# Authentication against LDAP servers requiring client certificates +# client_cert = "/path/to/client.crt" +# client_key = "/path/to/client.key" + +# Search user bind dn +bind_dn = "cn=readonly,dc=eom,dc=dev" +# Search user bind password +# If the password contains # or ; you have to wrap it with triple quotes. Ex """#password;""" +bind_password = "{{ openldap_readonly_password }}" +# We recommend using variable expansion for the bind_password, for more info https://grafana.com/docs/grafana/latest/setup-grafana/configure-grafana/#variable-expansion +# bind_password = '$__env{LDAP_BIND_PASSWORD}' + +# Timeout in seconds. Applies to each host specified in the 'host' entry (space separated). +timeout = 30 + +# User search filter, for example "(cn=%s)" or "(sAMAccountName=%s)" or "(uid=%s)" +# Allow login from email or username, example "(|(sAMAccountName=%s)(userPrincipalName=%s))" +search_filter = "(&(objectClass=posixAccount)(uid=%s)(memberOf=cn=Grafana Users,ou=Grafana,ou=Services,dc=eom,dc=dev))" + +# An array of base dns to search through +search_base_dns = ["dc=eom,dc=dev"] + +# Specify names of the LDAP attributes your LDAP uses +[servers.attributes] +username = "uid" +email = "mail" +name = "givenName" +surname = "sn" +member_of = "memberOf" + +[[servers.group_mappings]] +group_dn = "cn=Grafana Administrators,ou=Grafana,ou=Services,dc=eom,dc=dev" +org_id = 1 +org_role = "Admin" +grafana_admin = true + +[[servers.group_mappings]] +group_dn = "cn=Grafana Users,ou=Grafana,ou=Services,dc=eom,dc=dev" +org_id = 1 +org_role = "Viewer" + +[[servers.group_mappings]] +group_dn = "cn=DevOps Owners,ou=Teams,dc=eom,dc=dev" +org_id = 2 +org_role = "Admin" + +# Must stay after DevOps Owners: Grafana applies the first matching mapping per org. +[[servers.group_mappings]] +group_dn = "cn=DevOps Members,ou=Teams,dc=eom,dc=dev" +org_id = 2 +org_role = "Editor" + +[[servers.group_mappings]] +group_dn = "cn=Finance Owners,ou=Teams,dc=eom,dc=dev" +org_id = 3 +org_role = "Admin" diff --git a/tests/inventory b/tests/inventory new file mode 100644 index 0000000..03ca42f --- /dev/null +++ b/tests/inventory @@ -0,0 +1,3 @@ +#SPDX-License-Identifier: MIT-0 +localhost + diff --git a/tests/test.yml b/tests/test.yml new file mode 100644 index 0000000..54ed728 --- /dev/null +++ b/tests/test.yml @@ -0,0 +1,6 @@ +#SPDX-License-Identifier: MIT-0 +--- +- hosts: localhost + remote_user: root + roles: + - ansible-role-grafana diff --git a/vars/main.yml b/vars/main.yml new file mode 100644 index 0000000..ef67b5c --- /dev/null +++ b/vars/main.yml @@ -0,0 +1,3 @@ +#SPDX-License-Identifier: MIT-0 +--- +# vars file for ansible-role-grafana