From 43327b47f3abd12ac93b92987f40fd67e45562cf Mon Sep 17 00:00:00 2001 From: Bert-Jan Fikse Date: Wed, 14 Jan 2026 10:03:06 +0100 Subject: [PATCH 01/17] chore: add new role boilerplate for authentik --- roles/authentik/README.md | 38 +++++++++++++++++++++++++++++++ roles/authentik/defaults/main.yml | 3 +++ roles/authentik/handlers/main.yml | 3 +++ roles/authentik/meta/main.yml | 35 ++++++++++++++++++++++++++++ roles/authentik/tasks/main.yml | 3 +++ roles/authentik/tests/inventory | 3 +++ roles/authentik/tests/test.yml | 6 +++++ roles/authentik/vars/main.yml | 3 +++ 8 files changed, 94 insertions(+) create mode 100644 roles/authentik/README.md create mode 100644 roles/authentik/defaults/main.yml create mode 100644 roles/authentik/handlers/main.yml create mode 100644 roles/authentik/meta/main.yml create mode 100644 roles/authentik/tasks/main.yml create mode 100644 roles/authentik/tests/inventory create mode 100644 roles/authentik/tests/test.yml create mode 100644 roles/authentik/vars/main.yml diff --git a/roles/authentik/README.md b/roles/authentik/README.md new file mode 100644 index 0000000..225dd44 --- /dev/null +++ b/roles/authentik/README.md @@ -0,0 +1,38 @@ +Role Name +========= + +A brief description of the role goes here. + +Requirements +------------ + +Any pre-requisites that may not be covered by Ansible itself or the role should be mentioned here. For instance, if the role uses the EC2 module, it may be a good idea to mention in this section that the boto package is required. + +Role Variables +-------------- + +A description of the settable variables for this role should go here, including any variables that are in defaults/main.yml, vars/main.yml, and any variables that can/should be set via parameters to the role. Any variables that are read from other roles and/or the global scope (ie. hostvars, group vars, etc.) should be mentioned here as well. + +Dependencies +------------ + +A list of other roles hosted on Galaxy should go here, plus any details in regards to parameters that may need to be set for other roles, or variables that are used from other roles. + +Example Playbook +---------------- + +Including an example of how to use your role (for instance, with variables passed in as parameters) is always nice for users too: + + - hosts: servers + roles: + - { role: username.rolename, x: 42 } + +License +------- + +BSD + +Author Information +------------------ + +An optional section for the role authors to include contact information, or a website (HTML is not allowed). diff --git a/roles/authentik/defaults/main.yml b/roles/authentik/defaults/main.yml new file mode 100644 index 0000000..2c22ebb --- /dev/null +++ b/roles/authentik/defaults/main.yml @@ -0,0 +1,3 @@ +#SPDX-License-Identifier: MIT-0 +--- +# defaults file for authentik diff --git a/roles/authentik/handlers/main.yml b/roles/authentik/handlers/main.yml new file mode 100644 index 0000000..fbc8aa0 --- /dev/null +++ b/roles/authentik/handlers/main.yml @@ -0,0 +1,3 @@ +#SPDX-License-Identifier: MIT-0 +--- +# handlers file for authentik diff --git a/roles/authentik/meta/main.yml b/roles/authentik/meta/main.yml new file mode 100644 index 0000000..6f91fd3 --- /dev/null +++ b/roles/authentik/meta/main.yml @@ -0,0 +1,35 @@ +#SPDX-License-Identifier: MIT-0 +galaxy_info: + author: your name + description: your role description + company: your company (optional) + + # If the issue tracker for your role is not on github, uncomment the + # next line and provide a value + # issue_tracker_url: http://example.com/issue/tracker + + # Choose a valid license ID from https://spdx.org - some suggested licenses: + # - BSD-3-Clause (default) + # - MIT + # - GPL-2.0-or-later + # - GPL-3.0-only + # - Apache-2.0 + # - CC-BY-4.0 + license: license (GPL-2.0-or-later, MIT, etc) + + min_ansible_version: 2.2 + + # If this a Container Enabled role, provide the minimum Ansible Container version. + # min_ansible_container_version: + + galaxy_tags: [] + # List tags for your role here, one per line. A tag is a keyword that describes + # and categorizes the role. Users find roles by searching for tags. Be sure to + # remove the '[]' above, if you add tags to this list. + # + # NOTE: A tag is limited to a single word comprised of alphanumeric characters. + # Maximum 20 tags per role. + +dependencies: [] + # List your role dependencies here, one per line. Be sure to remove the '[]' above, + # if you add dependencies to this list. diff --git a/roles/authentik/tasks/main.yml b/roles/authentik/tasks/main.yml new file mode 100644 index 0000000..28b2aa5 --- /dev/null +++ b/roles/authentik/tasks/main.yml @@ -0,0 +1,3 @@ +#SPDX-License-Identifier: MIT-0 +--- +# tasks file for authentik diff --git a/roles/authentik/tests/inventory b/roles/authentik/tests/inventory new file mode 100644 index 0000000..03ca42f --- /dev/null +++ b/roles/authentik/tests/inventory @@ -0,0 +1,3 @@ +#SPDX-License-Identifier: MIT-0 +localhost + diff --git a/roles/authentik/tests/test.yml b/roles/authentik/tests/test.yml new file mode 100644 index 0000000..862cecd --- /dev/null +++ b/roles/authentik/tests/test.yml @@ -0,0 +1,6 @@ +#SPDX-License-Identifier: MIT-0 +--- +- hosts: localhost + remote_user: root + roles: + - authentik diff --git a/roles/authentik/vars/main.yml b/roles/authentik/vars/main.yml new file mode 100644 index 0000000..498262f --- /dev/null +++ b/roles/authentik/vars/main.yml @@ -0,0 +1,3 @@ +#SPDX-License-Identifier: MIT-0 +--- +# vars file for authentik From f81449604946e19a826beb220dbe0270e9b80c74 Mon Sep 17 00:00:00 2001 From: Bert-Jan Fikse Date: Wed, 14 Jan 2026 12:47:45 +0100 Subject: [PATCH 02/17] chore: add basic docker-compose for authentik --- roles/authentik/defaults/main.yml | 30 +++++++ roles/authentik/tasks/main.yml | 41 ++++++++++ .../authentik/templates/docker-compose.yml.j2 | 79 +++++++++++++++++++ 3 files changed, 150 insertions(+) create mode 100644 roles/authentik/templates/docker-compose.yml.j2 diff --git a/roles/authentik/defaults/main.yml b/roles/authentik/defaults/main.yml index 2c22ebb..4c346e6 100644 --- a/roles/authentik/defaults/main.yml +++ b/roles/authentik/defaults/main.yml @@ -1,3 +1,33 @@ #SPDX-License-Identifier: MIT-0 --- # defaults file for authentik + +# Base directory configuration (inherited from base role or defined here) +docker_compose_base_dir: /etc/docker/compose +docker_volume_base_dir: /srv/data + +# Authentik-specific configuration +authentik_service_name: authentik +authentik_docker_compose_dir: "{{ docker_compose_base_dir }}/{{ authentik_service_name }}" +authentik_docker_volume_dir: "{{ docker_volume_base_dir }}/{{ authentik_service_name }}" + +# Authentik service configuration +authentik_domain: "authentik.local.test" +authentik_image: "ghcr.io/goauthentik/server:2025.12.0" +authentik_port: 9000 +authentik_secret_key: "changeme-generate-a-random-string" + +# PostgreSQL configuration +authentik_postgres_image: "postgres:16-alpine" +authentik_postgres_db: authentik +authentik_postgres_user: authentik +authentik_postgres_password: "changeme" + +# Traefik configuration +authentik_traefik_network: "proxy" +authentik_backend_network: "backend" +authentik_use_ssl: true + +# Authentik environment settings +authentik_log_level: "info" +authentik_error_reporting_enabled: false diff --git a/roles/authentik/tasks/main.yml b/roles/authentik/tasks/main.yml index 28b2aa5..612a224 100644 --- a/roles/authentik/tasks/main.yml +++ b/roles/authentik/tasks/main.yml @@ -1,3 +1,44 @@ #SPDX-License-Identifier: MIT-0 --- # tasks file for authentik + +- name: Create docker compose directory + file: + path: "{{ authentik_docker_compose_dir }}" + state: directory + mode: '0755' + +- name: Create authentik data directory + file: + path: "{{ authentik_docker_volume_dir }}/data" + state: directory + mode: '0755' + +- name: Create authentik certs directory + file: + path: "{{ authentik_docker_volume_dir }}/certs" + state: directory + mode: '0755' + +- name: Create authentik templates directory + file: + path: "{{ authentik_docker_volume_dir }}/templates" + state: directory + mode: '0755' + +- name: Create postgres data directory + file: + path: "{{ authentik_docker_volume_dir }}/postgresql" + state: directory + mode: '0755' + +- name: Create docker-compose file for authentik + template: + src: docker-compose.yml.j2 + dest: "{{ authentik_docker_compose_dir }}/docker-compose.yml" + mode: '0644' + +- name: Start authentik containers + community.docker.docker_compose_v2: + project_src: "{{ authentik_docker_compose_dir }}" + state: present diff --git a/roles/authentik/templates/docker-compose.yml.j2 b/roles/authentik/templates/docker-compose.yml.j2 new file mode 100644 index 0000000..1b962de --- /dev/null +++ b/roles/authentik/templates/docker-compose.yml.j2 @@ -0,0 +1,79 @@ +services: + postgres: + image: {{ authentik_postgres_image }} + restart: unless-stopped + environment: + POSTGRES_DB: {{ authentik_postgres_db }} + POSTGRES_USER: {{ authentik_postgres_user }} + POSTGRES_PASSWORD: {{ authentik_postgres_password }} + volumes: + - {{ authentik_docker_volume_dir }}/postgresql:/var/lib/postgresql/data + networks: + - {{ authentik_backend_network }} + healthcheck: + test: ["CMD-SHELL", "pg_isready -d {{ authentik_postgres_db }} -U {{ authentik_postgres_user }}"] + start_period: 20s + interval: 30s + retries: 5 + timeout: 5s + + server: + image: {{ authentik_image }} + restart: unless-stopped + command: server + environment: + AUTHENTIK_SECRET_KEY: {{ authentik_secret_key }} + AUTHENTIK_POSTGRESQL__HOST: postgres + AUTHENTIK_POSTGRESQL__NAME: {{ authentik_postgres_db }} + AUTHENTIK_POSTGRESQL__USER: {{ authentik_postgres_user }} + AUTHENTIK_POSTGRESQL__PASSWORD: {{ authentik_postgres_password }} + AUTHENTIK_LOG_LEVEL: {{ authentik_log_level }} + AUTHENTIK_ERROR_REPORTING__ENABLED: "{{ authentik_error_reporting_enabled | lower }}" + volumes: + - {{ authentik_docker_volume_dir }}/data:/data + - {{ authentik_docker_volume_dir }}/templates:/templates + depends_on: + postgres: + condition: service_healthy + networks: + - {{ authentik_backend_network }} + - {{ authentik_traefik_network }} + labels: + - traefik.enable=true + - traefik.docker.network={{ authentik_traefik_network }} + - traefik.http.routers.{{ authentik_service_name }}.rule=Host(`{{ authentik_domain }}`) +{% if authentik_use_ssl %} + - traefik.http.routers.{{ authentik_service_name }}.entrypoints=websecure + - traefik.http.routers.{{ authentik_service_name }}.tls=true +{% else %} + - traefik.http.routers.{{ authentik_service_name }}.entrypoints=web +{% endif %} + - traefik.http.services.{{ authentik_service_name }}.loadbalancer.server.port={{ authentik_port }} + + worker: + image: {{ authentik_image }} + restart: unless-stopped + command: worker + user: root + environment: + AUTHENTIK_SECRET_KEY: {{ authentik_secret_key }} + AUTHENTIK_POSTGRESQL__HOST: postgres + AUTHENTIK_POSTGRESQL__NAME: {{ authentik_postgres_db }} + AUTHENTIK_POSTGRESQL__USER: {{ authentik_postgres_user }} + AUTHENTIK_POSTGRESQL__PASSWORD: {{ authentik_postgres_password }} + AUTHENTIK_LOG_LEVEL: {{ authentik_log_level }} + AUTHENTIK_ERROR_REPORTING__ENABLED: "{{ authentik_error_reporting_enabled | lower }}" + volumes: + - {{ authentik_docker_volume_dir }}/data:/data + - {{ authentik_docker_volume_dir }}/certs:/certs + - {{ authentik_docker_volume_dir }}/templates:/templates + depends_on: + postgres: + condition: service_healthy + networks: + - {{ authentik_backend_network }} + +networks: + {{ authentik_backend_network }}: + {{ authentik_traefik_network }}: + external: true From 3f8afa12ef948405b1dc1cde9d08456c8ddc873e Mon Sep 17 00:00:00 2001 From: Bert-Jan Fikse Date: Wed, 14 Jan 2026 13:49:40 +0100 Subject: [PATCH 03/17] feat: add ability to provision using blueprints --- roles/authentik/defaults/main.yml | 20 +++++++++ roles/authentik/tasks/main.yml | 21 +++++++++ .../blueprints/blueprint-oidc-app.yaml.j2 | 43 +++++++++++++++++++ .../authentik/templates/docker-compose.yml.j2 | 12 ++++++ 4 files changed, 96 insertions(+) create mode 100644 roles/authentik/templates/blueprints/blueprint-oidc-app.yaml.j2 diff --git a/roles/authentik/defaults/main.yml b/roles/authentik/defaults/main.yml index 4c346e6..92a17df 100644 --- a/roles/authentik/defaults/main.yml +++ b/roles/authentik/defaults/main.yml @@ -31,3 +31,23 @@ authentik_use_ssl: true # Authentik environment settings authentik_log_level: "info" authentik_error_reporting_enabled: false + +# Blueprints +# OIDC apps to provision + +authentik_oidc_apps: [] +# - slug: grafana +# name: Grafana +# client_id_env: GRAFANA_OIDC_CLIENT_ID +# client_secret_env: GRAFANA_OIDC_CLIENT_SECRET +# redirect_uris: +# - url: "https://grafana.example.com/login/generic_oauth" +# matching_mode: strict +# signing_key_name: "authentik Self-signed Certificate" +# flows: +# authorization_slug: default-provider-authorization-implicit-consent +# invalidation_slug: default-provider-invalidation-flow +# scopes: [openid, email, profile, offline_access] +authentik_blueprint_env: [] +# GRAFANA_OIDC_CLIENT_ID: "grafana" +# GRAFANA_OIDC_CLIENT_SECRET: "{{ vault_grafana_oidc_secret }}" \ No newline at end of file diff --git a/roles/authentik/tasks/main.yml b/roles/authentik/tasks/main.yml index 612a224..134245e 100644 --- a/roles/authentik/tasks/main.yml +++ b/roles/authentik/tasks/main.yml @@ -32,6 +32,20 @@ state: directory mode: '0755' +- name: Create blueprints directory + file: + path: "{{ authentik_docker_volume_dir }}/blueprints" + state: directory + mode: '0755' + +- name: Render OIDC blueprints + ansible.builtin.template: + src: blueprints/blueprint-oidc-app.yaml.j2 + dest: "{{ authentik_docker_volume_dir }}/blueprints/10-oidc-{{ item.slug }}.yaml" + mode: "0644" + loop: "{{ authentik_oidc_apps }}" + register: oidc_templates + - name: Create docker-compose file for authentik template: src: docker-compose.yml.j2 @@ -42,3 +56,10 @@ community.docker.docker_compose_v2: project_src: "{{ authentik_docker_compose_dir }}" state: present + recreate: >- + {{ + ( + (oidc_templates is defined and (oidc_templates.results | selectattr('changed') | list | length > 0)) + or (proxy_templates is defined and (proxy_templates.results | selectattr('changed') | list | length > 0)) + ) | ternary('always','auto') + }} diff --git a/roles/authentik/templates/blueprints/blueprint-oidc-app.yaml.j2 b/roles/authentik/templates/blueprints/blueprint-oidc-app.yaml.j2 new file mode 100644 index 0000000..7270de8 --- /dev/null +++ b/roles/authentik/templates/blueprints/blueprint-oidc-app.yaml.j2 @@ -0,0 +1,43 @@ +# yaml-language-server: $schema=https://goauthentik.io/blueprints/schema.json +version: 1 +metadata: + name: "oidc-{{ item.slug }}" + labels: + blueprints.goauthentik.io/instantiate: "true" + blueprints.goauthentik.io/description: "OIDC provider + application for {{ item.slug }}" + +entries: + - model: authentik_providers_oauth2.oauth2provider + id: oidc-provider-{{ item.slug }} + identifiers: + name: {{ item.slug }} + attrs: + name: {{ item.slug }} + client_type: confidential + client_id: !Env {{ item.client_id_env }} + client_secret: !Env {{ item.client_secret_env }} + + redirect_uris: +{% for ru in item.redirect_uris %} + - url: "{{ ru.url }}" + matching_mode: {{ ru.matching_mode | default('strict') }} +{% endfor %} + + authorization_flow: !Find [authentik_flows.flow, [slug, {{ item.flows.authorization_slug | default('default-provider-authorization-implicit-consent') }}]] + invalidation_flow: !Find [authentik_flows.flow, [slug, {{ item.flows.invalidation_slug | default('default-provider-invalidation-flow') }}]] + + property_mappings: +{% for s in (item.scopes | default(['openid','email','profile','offline_access'])) %} + - !Find [authentik_providers_oauth2.scopemapping, [scope_name, {{ s }}]] +{% endfor %} + + signing_key: !Find [authentik_crypto.certificatekeypair, [name, {{ item.signing_key_name | default('authentik Self-signed Certificate') }}]] + + - model: authentik_core.application + id: app-{{ item.slug }} + identifiers: + slug: {{ item.slug }} + attrs: + name: "{{ item.name | default(item.slug) }}" + slug: {{ item.slug }} + provider: !KeyOf oidc-provider-{{ item.slug }} diff --git a/roles/authentik/templates/docker-compose.yml.j2 b/roles/authentik/templates/docker-compose.yml.j2 index 1b962de..c6b1460 100644 --- a/roles/authentik/templates/docker-compose.yml.j2 +++ b/roles/authentik/templates/docker-compose.yml.j2 @@ -29,7 +29,13 @@ services: AUTHENTIK_POSTGRESQL__PASSWORD: {{ authentik_postgres_password }} AUTHENTIK_LOG_LEVEL: {{ authentik_log_level }} AUTHENTIK_ERROR_REPORTING__ENABLED: "{{ authentik_error_reporting_enabled | lower }}" +{% if authentik_blueprint_env|length > 0 %} +{% for k, v in authentik_blueprint_env.items() %} + {{ k }}: "{{ v }}" +{% endfor %} +{% endif %} volumes: + - {{ authentik_docker_volume_dir }}/blueprints:/blueprints - {{ authentik_docker_volume_dir }}/data:/data - {{ authentik_docker_volume_dir }}/templates:/templates depends_on: @@ -63,10 +69,16 @@ services: AUTHENTIK_POSTGRESQL__PASSWORD: {{ authentik_postgres_password }} AUTHENTIK_LOG_LEVEL: {{ authentik_log_level }} AUTHENTIK_ERROR_REPORTING__ENABLED: "{{ authentik_error_reporting_enabled | lower }}" +{% if authentik_blueprint_env|length > 0 %} +{% for k, v in authentik_blueprint_env.items() %} + {{ k }}: "{{ v }}" +{% endfor %} +{% endif %} volumes: - {{ authentik_docker_volume_dir }}/data:/data - {{ authentik_docker_volume_dir }}/certs:/certs - {{ authentik_docker_volume_dir }}/templates:/templates + - {{ authentik_docker_volume_dir }}/blueprints:/blueprints depends_on: postgres: condition: service_healthy From 438a41356af030b3961e6fcc7239028aa412e3b5 Mon Sep 17 00:00:00 2001 From: Bert-Jan Fikse Date: Wed, 14 Jan 2026 14:05:05 +0100 Subject: [PATCH 04/17] feat: add ability to provision proxy apps using blueprints --- roles/authentik/defaults/main.yml | 13 ++++++- roles/authentik/tasks/main.yml | 8 +++++ .../blueprints/blueprint-proxy-app.yaml.j2 | 36 +++++++++++++++++++ 3 files changed, 56 insertions(+), 1 deletion(-) create mode 100644 roles/authentik/templates/blueprints/blueprint-proxy-app.yaml.j2 diff --git a/roles/authentik/defaults/main.yml b/roles/authentik/defaults/main.yml index 92a17df..d7ca79c 100644 --- a/roles/authentik/defaults/main.yml +++ b/roles/authentik/defaults/main.yml @@ -33,7 +33,17 @@ authentik_log_level: "info" authentik_error_reporting_enabled: false # Blueprints -# OIDC apps to provision +authentik_proxy_apps: [] +# - slug: whoami +# name: whoami +# internal_host: "http://whoami:80" +# external_host: "https://whoami.example.com" +# skip_path_regex: | +# ^/healthz$ +# flows: +# authentication_slug: default-authentication-flow +# authorization_slug: default-provider-authorization-implicit-consent +# invalidation_slug: default-provider-invalidation-flow authentik_oidc_apps: [] # - slug: grafana @@ -48,6 +58,7 @@ authentik_oidc_apps: [] # authorization_slug: default-provider-authorization-implicit-consent # invalidation_slug: default-provider-invalidation-flow # scopes: [openid, email, profile, offline_access] + authentik_blueprint_env: [] # GRAFANA_OIDC_CLIENT_ID: "grafana" # GRAFANA_OIDC_CLIENT_SECRET: "{{ vault_grafana_oidc_secret }}" \ No newline at end of file diff --git a/roles/authentik/tasks/main.yml b/roles/authentik/tasks/main.yml index 134245e..dd9aa2e 100644 --- a/roles/authentik/tasks/main.yml +++ b/roles/authentik/tasks/main.yml @@ -46,6 +46,14 @@ loop: "{{ authentik_oidc_apps }}" register: oidc_templates +- name: Render Proxy blueprints + ansible.builtin.template: + src: blueprints/blueprint-proxy-app.yaml.j2 + dest: "{{ authentik_docker_volume_dir }}/blueprints/20-proxy-{{ item.slug }}.yaml" + mode: "0644" + loop: "{{ authentik_proxy_apps }}" + register: proxy_templates + - name: Create docker-compose file for authentik template: src: docker-compose.yml.j2 diff --git a/roles/authentik/templates/blueprints/blueprint-proxy-app.yaml.j2 b/roles/authentik/templates/blueprints/blueprint-proxy-app.yaml.j2 new file mode 100644 index 0000000..5e29756 --- /dev/null +++ b/roles/authentik/templates/blueprints/blueprint-proxy-app.yaml.j2 @@ -0,0 +1,36 @@ +# yaml-language-server: $schema=https://goauthentik.io/blueprints/schema.json +version: 1 +metadata: + name: "proxy-{{ item.slug }}" + labels: + blueprints.goauthentik.io/instantiate: "true" + blueprints.goauthentik.io/description: "Proxy provider + application for {{ item.slug }}" + +entries: + - model: authentik_providers_proxy.proxyprovider + id: proxy-provider-{{ item.slug }} + identifiers: + name: {{ item.slug }} + attrs: + name: {{ item.slug }} + + authentication_flow: !Find [authentik_flows.flow, [slug, {{ item.flows.authentication_slug | default('default-authentication-flow') }}]] + authorization_flow: !Find [authentik_flows.flow, [slug, {{ item.flows.authorization_slug | default('default-provider-authorization-implicit-consent') }}]] + invalidation_flow: !Find [authentik_flows.flow, [slug, {{ item.flows.invalidation_slug | default('default-provider-invalidation-flow') }}]] + + internal_host: "{{ item.internal_host }}" + external_host: "{{ item.external_host }}" + +{% if item.skip_path_regex is defined and item.skip_path_regex|length > 0 %} + skip_path_regex: | +{{ item.skip_path_regex | indent(8, true) }} +{% endif %} + + - model: authentik_core.application + id: app-{{ item.slug }} + identifiers: + slug: {{ item.slug }} + attrs: + name: "{{ item.name | default(item.slug) }}" + slug: {{ item.slug }} + provider: !KeyOf proxy-provider-{{ item.slug }} From 0106e8801f496c6d7b3f9932618f3d1b7c562853 Mon Sep 17 00:00:00 2001 From: Bert-Jan Fikse Date: Wed, 14 Jan 2026 14:31:22 +0100 Subject: [PATCH 05/17] feat: add ability to provision outposts using blueprints --- roles/authentik/defaults/main.yml | 12 +++++++++ roles/authentik/tasks/main.yml | 9 +++++++ .../blueprints/outpost-proxy.yaml.j2 | 27 +++++++++++++++++++ 3 files changed, 48 insertions(+) create mode 100644 roles/authentik/templates/blueprints/outpost-proxy.yaml.j2 diff --git a/roles/authentik/defaults/main.yml b/roles/authentik/defaults/main.yml index d7ca79c..e809299 100644 --- a/roles/authentik/defaults/main.yml +++ b/roles/authentik/defaults/main.yml @@ -45,6 +45,18 @@ authentik_proxy_apps: [] # authorization_slug: default-provider-authorization-implicit-consent # invalidation_slug: default-provider-invalidation-flow +authentik_proxy_outposts: [] +# - name: "proxy-main" +# type: "proxy" +# service_connection: null +# providers: +# - whoami +# - nextcloud-proxy +# config: +# authentik_host: "https://authentik.local.test/" +# authentik_host_browser: "https://authentik.local.test/" +# log_level: "info" + authentik_oidc_apps: [] # - slug: grafana # name: Grafana diff --git a/roles/authentik/tasks/main.yml b/roles/authentik/tasks/main.yml index dd9aa2e..69fedb7 100644 --- a/roles/authentik/tasks/main.yml +++ b/roles/authentik/tasks/main.yml @@ -54,6 +54,14 @@ loop: "{{ authentik_proxy_apps }}" register: proxy_templates +- name: Render outpost blueprints + template: + src: blueprints/outpost-proxy.yaml.j2 + dest: "{{ authentik_docker_volume_dir }}/blueprints/30-outpost-{{ item.name }}.yaml" + mode: "0644" + loop: "{{ authentik_proxy_outposts }}" + register: outpost_bp + - name: Create docker-compose file for authentik template: src: docker-compose.yml.j2 @@ -69,5 +77,6 @@ ( (oidc_templates is defined and (oidc_templates.results | selectattr('changed') | list | length > 0)) or (proxy_templates is defined and (proxy_templates.results | selectattr('changed') | list | length > 0)) + or (outpost_bp is defined and (outpost_bp.results | selectattr('changed') | list | length > 0)) ) | ternary('always','auto') }} diff --git a/roles/authentik/templates/blueprints/outpost-proxy.yaml.j2 b/roles/authentik/templates/blueprints/outpost-proxy.yaml.j2 new file mode 100644 index 0000000..1aebe6c --- /dev/null +++ b/roles/authentik/templates/blueprints/outpost-proxy.yaml.j2 @@ -0,0 +1,27 @@ +# yaml-language-server: $schema=https://goauthentik.io/blueprints/schema.json +version: 1 +metadata: + name: "outpost-{{ item.name }}" + labels: + blueprints.goauthentik.io/instantiate: "true" + +entries: + - model: authentik_outposts.outpost + identifiers: + name: "{{ item.name }}" + attrs: + name: "{{ item.name }}" + type: {{ item.type | default('proxy') }} + service_connection: {{ item.service_connection | default('null') }} + + providers: +{% for p in item.providers %} + - !Find [authentik_providers_proxy.proxyprovider, [name, {{ p }}]] +{% endfor %} + +{% if item.config is defined %} + config: +{% for k, v in item.config.items() %} + {{ k }}: {{ v | tojson }} +{% endfor %} +{% endif %} From 359622d17a771c854cf48a2df0df7dfeeb79dda7 Mon Sep 17 00:00:00 2001 From: Bert-Jan Fikse Date: Wed, 14 Jan 2026 15:35:18 +0100 Subject: [PATCH 06/17] feat: add ability to provision entra login sources using blueprints --- roles/authentik/defaults/main.yml | 26 +++++++++++- roles/authentik/tasks/main.yml | 17 ++++++++ .../blueprint-login-sources.yaml.j2 | 18 +++++++++ .../blueprints/blueprint-source-entra.yaml.j2 | 40 +++++++++++++++++++ 4 files changed, 100 insertions(+), 1 deletion(-) create mode 100644 roles/authentik/templates/blueprints/blueprint-login-sources.yaml.j2 create mode 100644 roles/authentik/templates/blueprints/blueprint-source-entra.yaml.j2 diff --git a/roles/authentik/defaults/main.yml b/roles/authentik/defaults/main.yml index e809299..8213b4d 100644 --- a/roles/authentik/defaults/main.yml +++ b/roles/authentik/defaults/main.yml @@ -73,4 +73,28 @@ authentik_oidc_apps: [] authentik_blueprint_env: [] # GRAFANA_OIDC_CLIENT_ID: "grafana" -# GRAFANA_OIDC_CLIENT_SECRET: "{{ vault_grafana_oidc_secret }}" \ No newline at end of file +# GRAFANA_OIDC_CLIENT_SECRET: "{{ vault_grafana_oidc_secret }}" +# ENTRA_TENANT_ID: "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" +# ENTRA_CLIENT_ID: "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" +# ENTRA_CLIENT_SECRET: "{{ vault_entra_client_secret }}" + +# Oauth sources +authentik_entra_sources: [] +# - slug: entra-id +# name: "Login with Entra" +# tenant_mode: single # single | common +# tenant_id_env: ENTRA_TENANT_ID +# client_id_env: ENTRA_CLIENT_ID +# client_secret_env: ENTRA_CLIENT_SECRET +# scopes: +# - openid +# - profile +# - email +# # add only if you really need group sync on login: +# # - https://graph.microsoft.com/GroupMember.Read.All + + +# Show Entra on login screen: +authentik_login_source_ids: [] +# - "source-entra-entra-id" +authentik_identification_stage_name: default-authentication-identification diff --git a/roles/authentik/tasks/main.yml b/roles/authentik/tasks/main.yml index 69fedb7..fa5caed 100644 --- a/roles/authentik/tasks/main.yml +++ b/roles/authentik/tasks/main.yml @@ -62,6 +62,21 @@ loop: "{{ authentik_proxy_outposts }}" register: outpost_bp +- name: Render Entra source blueprints + ansible.builtin.template: + src: blueprints/blueprint-source-entra.yaml.j2 + dest: "{{ authentik_docker_volume_dir }}/blueprints/20-source-entra-{{ item.slug }}.yaml" + mode: "0644" + loop: "{{ authentik_entra_sources }}" + register: entra_bp + +- name: Render login stage sources blueprint + ansible.builtin.template: + src: blueprints/blueprint-login-sources.yaml.j2 + dest: "{{ authentik_docker_volume_dir }}/blueprints/21-login-sources.yaml" + mode: "0644" + register: login_bp + - name: Create docker-compose file for authentik template: src: docker-compose.yml.j2 @@ -78,5 +93,7 @@ (oidc_templates is defined and (oidc_templates.results | selectattr('changed') | list | length > 0)) or (proxy_templates is defined and (proxy_templates.results | selectattr('changed') | list | length > 0)) or (outpost_bp is defined and (outpost_bp.results | selectattr('changed') | list | length > 0)) + or (entra_bp is defined and (entra_bp.results | selectattr('changed') | list | length > 0)) + or (login_bp is defined and login_bp.changed) ) | ternary('always','auto') }} diff --git a/roles/authentik/templates/blueprints/blueprint-login-sources.yaml.j2 b/roles/authentik/templates/blueprints/blueprint-login-sources.yaml.j2 new file mode 100644 index 0000000..9a7b76d --- /dev/null +++ b/roles/authentik/templates/blueprints/blueprint-login-sources.yaml.j2 @@ -0,0 +1,18 @@ +# yaml-language-server: $schema=https://goauthentik.io/blueprints/schema.json +version: 1 +metadata: + name: "login-sources" + labels: + blueprints.goauthentik.io/instantiate: "true" + blueprints.goauthentik.io/description: "Set sources on the identification stage" + +entries: + - model: authentik_stages_identification.identificationstage + identifiers: + name: "{{ authentik_identification_stage_name }}" + attrs: + # NOTE: this SETS the sources list (it doesn’t append). + sources: +{% for src_id in authentik_login_source_ids %} + - !KeyOf {{ src_id }} +{% endfor %} diff --git a/roles/authentik/templates/blueprints/blueprint-source-entra.yaml.j2 b/roles/authentik/templates/blueprints/blueprint-source-entra.yaml.j2 new file mode 100644 index 0000000..acab07b --- /dev/null +++ b/roles/authentik/templates/blueprints/blueprint-source-entra.yaml.j2 @@ -0,0 +1,40 @@ +# yaml-language-server: $schema=https://goauthentik.io/blueprints/schema.json +version: 1 +metadata: + name: "source-entra-{{ item.slug }}" + labels: + blueprints.goauthentik.io/instantiate: "true" + blueprints.goauthentik.io/description: "Microsoft Entra ID OAuth source ({{ item.slug }})" + +entries: + - model: authentik_sources_oauth.oauthsource + id: source-entra-{{ item.slug }} + identifiers: + slug: {{ item.slug }} + attrs: + name: "{{ item.name | default('Microsoft Entra ID') }}" + slug: {{ item.slug }} + + # Authentik’s OAuth sources support vendor-specific types. + # Entra guide calls it “Entra ID OAuth Source”. + provider_type: entraid + + consumer_key: !Env {{ item.client_id_env }} + consumer_secret: !Env {{ item.client_secret_env }} + + scopes: +{% for s in (item.scopes | default(['openid','profile','email'])) %} + - {{ s }} +{% endfor %} + +{% if (item.tenant_mode | default('single')) == 'single' %} + authorization_url: !Format ["https://login.microsoftonline.com/%s/oauth2/v2.0/authorize", !Env {{ item.tenant_id_env }}] + access_token_url: !Format ["https://login.microsoftonline.com/%s/oauth2/v2.0/token", !Env {{ item.tenant_id_env }}] + profile_url: "https://graph.microsoft.com/v1.0/me" + oidc_jwks_url: !Format ["https://login.microsoftonline.com/%s/discovery/v2.0/keys", !Env {{ item.tenant_id_env }}] +{% else %} + authorization_url: "https://login.microsoftonline.com/common/oauth2/v2.0/authorize" + access_token_url: "https://login.microsoftonline.com/common/oauth2/v2.0/token" + profile_url: "https://graph.microsoft.com/v1.0/me" + oidc_jwks_url: "https://login.microsoftonline.com/common/discovery/v2.0/keys" +{% endif %} From d800d43c71bdafbf07611fe44561014bf31abeb1 Mon Sep 17 00:00:00 2001 From: Bert-Jan Fikse Date: Wed, 14 Jan 2026 15:43:03 +0100 Subject: [PATCH 07/17] feat: add ability to provision local users using blueprints --- roles/authentik/defaults/main.yml | 13 +++++++ roles/authentik/tasks/main.yml | 9 +++++ .../blueprints/blueprint-local-users.yaml.j2 | 34 +++++++++++++++++++ 3 files changed, 56 insertions(+) create mode 100644 roles/authentik/templates/blueprints/blueprint-local-users.yaml.j2 diff --git a/roles/authentik/defaults/main.yml b/roles/authentik/defaults/main.yml index 8213b4d..8ac0785 100644 --- a/roles/authentik/defaults/main.yml +++ b/roles/authentik/defaults/main.yml @@ -98,3 +98,16 @@ authentik_entra_sources: [] authentik_login_source_ids: [] # - "source-entra-entra-id" authentik_identification_stage_name: default-authentication-identification + +# Local users to provision +authentik_local_users: [] +# - username: admin +# name: "Admin User" +# email: "admin@example.com" +# password_env: AUTHENTIK_ADMIN_PASSWORD # reference env var in authentik_blueprint_env +# is_active: true +# groups: +# - authentik Admins +# attributes: +# settings: +# locale: en diff --git a/roles/authentik/tasks/main.yml b/roles/authentik/tasks/main.yml index fa5caed..49cacb7 100644 --- a/roles/authentik/tasks/main.yml +++ b/roles/authentik/tasks/main.yml @@ -77,6 +77,14 @@ mode: "0644" register: login_bp +- name: Render local users blueprint + ansible.builtin.template: + src: blueprints/blueprint-local-users.yaml.j2 + dest: "{{ authentik_docker_volume_dir }}/blueprints/05-local-users.yaml" + mode: "0644" + when: authentik_local_users | length > 0 + register: local_users_bp + - name: Create docker-compose file for authentik template: src: docker-compose.yml.j2 @@ -95,5 +103,6 @@ or (outpost_bp is defined and (outpost_bp.results | selectattr('changed') | list | length > 0)) or (entra_bp is defined and (entra_bp.results | selectattr('changed') | list | length > 0)) or (login_bp is defined and login_bp.changed) + or (local_users_bp.changed | default(false)) ) | ternary('always','auto') }} diff --git a/roles/authentik/templates/blueprints/blueprint-local-users.yaml.j2 b/roles/authentik/templates/blueprints/blueprint-local-users.yaml.j2 new file mode 100644 index 0000000..d40454b --- /dev/null +++ b/roles/authentik/templates/blueprints/blueprint-local-users.yaml.j2 @@ -0,0 +1,34 @@ +# yaml-language-server: $schema=https://goauthentik.io/blueprints/schema.json +version: 1 +metadata: + name: "local-users" + labels: + blueprints.goauthentik.io/instantiate: "true" + blueprints.goauthentik.io/description: "Local user accounts" + +entries: +{% for user in authentik_local_users %} + - model: authentik_core.user + id: user-{{ user.username }} + identifiers: + username: {{ user.username }} + attrs: + username: {{ user.username }} + name: "{{ user.name | default(user.username) }}" + email: "{{ user.email | default('') }}" + is_active: {{ user.is_active | default(true) | lower }} +{% if user.password_env is defined %} + password: !Env {{ user.password_env }} +{% endif %} +{% if user.groups is defined and user.groups | length > 0 %} + groups: +{% for group in user.groups %} + - !Find [authentik_core.group, [name, {{ group }}]] +{% endfor %} +{% endif %} +{% if user.attributes is defined %} + attributes: +{{ user.attributes | to_nice_yaml(indent=2) | indent(8, first=true) }} +{% endif %} + +{% endfor %} \ No newline at end of file From 8828436adfa4a31970bff1d915ad7db730c1ff8e Mon Sep 17 00:00:00 2001 From: Bert-Jan Fikse Date: Wed, 14 Jan 2026 15:47:42 +0100 Subject: [PATCH 08/17] chore: add minimal readme --- roles/authentik/README.md | 45 +++++++++++---------------------------- 1 file changed, 13 insertions(+), 32 deletions(-) diff --git a/roles/authentik/README.md b/roles/authentik/README.md index 225dd44..2fb18d1 100644 --- a/roles/authentik/README.md +++ b/roles/authentik/README.md @@ -1,38 +1,19 @@ -Role Name -========= +# Authentik -A brief description of the role goes here. +Deploys Authentik identity provider with Docker Compose. -Requirements ------------- +## Variables -Any pre-requisites that may not be covered by Ansible itself or the role should be mentioned here. For instance, if the role uses the EC2 module, it may be a good idea to mention in this section that the boto package is required. +See `defaults/main.yml` for all available variables. -Role Variables --------------- +## Blueprints -A description of the settable variables for this role should go here, including any variables that are in defaults/main.yml, vars/main.yml, and any variables that can/should be set via parameters to the role. Any variables that are read from other roles and/or the global scope (ie. hostvars, group vars, etc.) should be mentioned here as well. +The role renders blueprints for: +- Local users (`authentik_local_users`) +- OIDC applications (`authentik_oidc_apps`) +- Proxy applications (`authentik_proxy_apps`) +- Proxy outposts (`authentik_proxy_outposts`) +- Entra ID sources (`authentik_entra_sources`) +- Login screen sources (`authentik_login_source_ids`) -Dependencies ------------- - -A list of other roles hosted on Galaxy should go here, plus any details in regards to parameters that may need to be set for other roles, or variables that are used from other roles. - -Example Playbook ----------------- - -Including an example of how to use your role (for instance, with variables passed in as parameters) is always nice for users too: - - - hosts: servers - roles: - - { role: username.rolename, x: 42 } - -License -------- - -BSD - -Author Information ------------------- - -An optional section for the role authors to include contact information, or a website (HTML is not allowed). +Secrets are passed via `authentik_blueprint_env` using environment variable references. \ No newline at end of file From f8b9975ae4381c1e4ec4d637a263b3d4348cae14 Mon Sep 17 00:00:00 2001 From: Bert-Jan Fikse Date: Wed, 14 Jan 2026 15:51:19 +0100 Subject: [PATCH 09/17] chore: add removal of stale blueprints --- roles/authentik/tasks/main.yml | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/roles/authentik/tasks/main.yml b/roles/authentik/tasks/main.yml index 49cacb7..5a28a69 100644 --- a/roles/authentik/tasks/main.yml +++ b/roles/authentik/tasks/main.yml @@ -38,6 +38,31 @@ state: directory mode: '0755' +- name: Find existing blueprint files + find: + paths: "{{ authentik_docker_volume_dir }}/blueprints" + patterns: "*.yaml" + register: existing_blueprints + +- name: Build list of expected blueprint files + set_fact: + expected_blueprints: >- + {{ + (authentik_oidc_apps | map(attribute='slug') | map('regex_replace', '^(.*)$', '10-oidc-\1.yaml') | list) + + (authentik_proxy_apps | map(attribute='slug') | map('regex_replace', '^(.*)$', '20-proxy-\1.yaml') | list) + + (authentik_proxy_outposts | map(attribute='name') | map('regex_replace', '^(.*)$', '30-outpost-\1.yaml') | list) + + (authentik_entra_sources | map(attribute='slug') | map('regex_replace', '^(.*)$', '20-source-entra-\1.yaml') | list) + + ['21-login-sources.yaml'] + + ((authentik_local_users | length > 0) | ternary(['05-local-users.yaml'], [])) + }} + +- name: Remove stale blueprint files + file: + path: "{{ item.path }}" + state: absent + loop: "{{ existing_blueprints.files }}" + when: item.path | basename not in expected_blueprints + - name: Render OIDC blueprints ansible.builtin.template: src: blueprints/blueprint-oidc-app.yaml.j2 From 3d3a09025aaf7480fc79ee32e8bd1c7397d751f7 Mon Sep 17 00:00:00 2001 From: Bert-Jan Fikse Date: Wed, 14 Jan 2026 16:03:20 +0100 Subject: [PATCH 10/17] feat: allow deletion of ressources via blueprints --- roles/authentik/README.md | 11 +++++- roles/authentik/defaults/main.yml | 11 ++++++ roles/authentik/tasks/main.yml | 12 +++++- .../blueprints/blueprint-cleanup.yaml.j2 | 38 +++++++++++++++++++ 4 files changed, 70 insertions(+), 2 deletions(-) create mode 100644 roles/authentik/templates/blueprints/blueprint-cleanup.yaml.j2 diff --git a/roles/authentik/README.md b/roles/authentik/README.md index 2fb18d1..8311190 100644 --- a/roles/authentik/README.md +++ b/roles/authentik/README.md @@ -16,4 +16,13 @@ The role renders blueprints for: - Entra ID sources (`authentik_entra_sources`) - Login screen sources (`authentik_login_source_ids`) -Secrets are passed via `authentik_blueprint_env` using environment variable references. \ No newline at end of file +Secrets are passed via `authentik_blueprint_env` using environment variable references. + +## Removing resources + +To remove resources from Authentik, move slugs to the removal lists: +- `authentik_removed_oidc_apps` +- `authentik_removed_proxy_apps` +- `authentik_removed_local_users` + +After confirming deletion, remove the slug from the list. \ No newline at end of file diff --git a/roles/authentik/defaults/main.yml b/roles/authentik/defaults/main.yml index 8ac0785..85e8a15 100644 --- a/roles/authentik/defaults/main.yml +++ b/roles/authentik/defaults/main.yml @@ -111,3 +111,14 @@ authentik_local_users: [] # attributes: # settings: # locale: en + +# Resources to remove from Authentik (cleanup) +# Add slugs/names here when removing from the lists above +authentik_removed_oidc_apps: [] +# - grafana + +authentik_removed_proxy_apps: [] +# - whoami + +authentik_removed_local_users: [] +# - olduser diff --git a/roles/authentik/tasks/main.yml b/roles/authentik/tasks/main.yml index 5a28a69..83805ad 100644 --- a/roles/authentik/tasks/main.yml +++ b/roles/authentik/tasks/main.yml @@ -53,7 +53,8 @@ (authentik_proxy_outposts | map(attribute='name') | map('regex_replace', '^(.*)$', '30-outpost-\1.yaml') | list) + (authentik_entra_sources | map(attribute='slug') | map('regex_replace', '^(.*)$', '20-source-entra-\1.yaml') | list) + ['21-login-sources.yaml'] + - ((authentik_local_users | length > 0) | ternary(['05-local-users.yaml'], [])) + ((authentik_local_users | length > 0) | ternary(['05-local-users.yaml'], [])) + + (((authentik_removed_oidc_apps | length > 0) or (authentik_removed_proxy_apps | length > 0) or (authentik_removed_local_users | length > 0)) | ternary(['00-cleanup.yaml'], [])) }} - name: Remove stale blueprint files @@ -110,6 +111,14 @@ when: authentik_local_users | length > 0 register: local_users_bp +- name: Render cleanup blueprint + ansible.builtin.template: + src: blueprints/blueprint-cleanup.yaml.j2 + dest: "{{ authentik_docker_volume_dir }}/blueprints/00-cleanup.yaml" + mode: "0644" + when: (authentik_removed_oidc_apps | length > 0) or (authentik_removed_proxy_apps | length > 0) or (authentik_removed_local_users | length > 0) + register: cleanup_bp + - name: Create docker-compose file for authentik template: src: docker-compose.yml.j2 @@ -129,5 +138,6 @@ or (entra_bp is defined and (entra_bp.results | selectattr('changed') | list | length > 0)) or (login_bp is defined and login_bp.changed) or (local_users_bp.changed | default(false)) + or (cleanup_bp.changed | default(false)) ) | ternary('always','auto') }} diff --git a/roles/authentik/templates/blueprints/blueprint-cleanup.yaml.j2 b/roles/authentik/templates/blueprints/blueprint-cleanup.yaml.j2 new file mode 100644 index 0000000..27e2461 --- /dev/null +++ b/roles/authentik/templates/blueprints/blueprint-cleanup.yaml.j2 @@ -0,0 +1,38 @@ +# yaml-language-server: $schema=https://goauthentik.io/blueprints/schema.json +version: 1 +metadata: + name: "cleanup" + labels: + blueprints.goauthentik.io/instantiate: "true" + blueprints.goauthentik.io/description: "Remove stale resources" + +entries: +{% for slug in authentik_removed_oidc_apps %} + # Remove OIDC app: {{ slug }} + - model: authentik_core.application + state: absent + identifiers: + slug: {{ slug }} + - model: authentik_providers_oauth2.oauth2provider + state: absent + identifiers: + name: {{ slug }} +{% endfor %} +{% for slug in authentik_removed_proxy_apps %} + # Remove proxy app: {{ slug }} + - model: authentik_core.application + state: absent + identifiers: + slug: {{ slug }} + - model: authentik_providers_proxy.proxyprovider + state: absent + identifiers: + name: {{ slug }} +{% endfor %} +{% for username in authentik_removed_local_users %} + # Remove user: {{ username }} + - model: authentik_core.user + state: absent + identifiers: + username: {{ username }} +{% endfor %} \ No newline at end of file From b00a051b9d0f050888d325bfa14d8da82d962b35 Mon Sep 17 00:00:00 2001 From: Bert-Jan Fikse Date: Wed, 14 Jan 2026 16:23:52 +0100 Subject: [PATCH 11/17] chore: add blueprint stuff to seperate task file --- roles/authentik/tasks/blueprints.yml | 97 ++++++++++++++++++++++++++++ roles/authentik/tasks/main.yml | 95 +-------------------------- 2 files changed, 100 insertions(+), 92 deletions(-) create mode 100644 roles/authentik/tasks/blueprints.yml diff --git a/roles/authentik/tasks/blueprints.yml b/roles/authentik/tasks/blueprints.yml new file mode 100644 index 0000000..58c8b19 --- /dev/null +++ b/roles/authentik/tasks/blueprints.yml @@ -0,0 +1,97 @@ +#SPDX-License-Identifier: MIT-0 +--- +# Blueprint rendering tasks for authentik + +- name: Find existing blueprint files + find: + paths: "{{ authentik_docker_volume_dir }}/blueprints" + patterns: "*.yaml" + register: existing_blueprints + +- name: Build list of expected blueprint files + set_fact: + expected_blueprints: >- + {{ + (authentik_oidc_apps | map(attribute='slug') | map('regex_replace', '^(.*)$', '10-oidc-\1.yaml') | list) + + (authentik_proxy_apps | map(attribute='slug') | map('regex_replace', '^(.*)$', '20-proxy-\1.yaml') | list) + + (authentik_proxy_outposts | map(attribute='name') | map('regex_replace', '^(.*)$', '30-outpost-\1.yaml') | list) + + (authentik_entra_sources | map(attribute='slug') | map('regex_replace', '^(.*)$', '20-source-entra-\1.yaml') | list) + + ['21-login-sources.yaml'] + + ((authentik_local_users | length > 0) | ternary(['05-local-users.yaml'], [])) + + (((authentik_removed_oidc_apps | length > 0) or (authentik_removed_proxy_apps | length > 0) or (authentik_removed_local_users | length > 0)) | ternary(['00-cleanup.yaml'], [])) + }} + +- name: Remove stale blueprint files + file: + path: "{{ item.path }}" + state: absent + loop: "{{ existing_blueprints.files }}" + when: item.path | basename not in expected_blueprints + +- name: Render OIDC blueprints + ansible.builtin.template: + src: blueprints/blueprint-oidc-app.yaml.j2 + dest: "{{ authentik_docker_volume_dir }}/blueprints/10-oidc-{{ item.slug }}.yaml" + mode: "0644" + loop: "{{ authentik_oidc_apps }}" + register: oidc_templates + +- name: Render Proxy blueprints + ansible.builtin.template: + src: blueprints/blueprint-proxy-app.yaml.j2 + dest: "{{ authentik_docker_volume_dir }}/blueprints/20-proxy-{{ item.slug }}.yaml" + mode: "0644" + loop: "{{ authentik_proxy_apps }}" + register: proxy_templates + +- name: Render outpost blueprints + ansible.builtin.template: + src: blueprints/outpost-proxy.yaml.j2 + dest: "{{ authentik_docker_volume_dir }}/blueprints/30-outpost-{{ item.name }}.yaml" + mode: "0644" + loop: "{{ authentik_proxy_outposts }}" + register: outpost_bp + +- name: Render Entra source blueprints + ansible.builtin.template: + src: blueprints/blueprint-source-entra.yaml.j2 + dest: "{{ authentik_docker_volume_dir }}/blueprints/20-source-entra-{{ item.slug }}.yaml" + mode: "0644" + loop: "{{ authentik_entra_sources }}" + register: entra_bp + +- name: Render login stage sources blueprint + ansible.builtin.template: + src: blueprints/blueprint-login-sources.yaml.j2 + dest: "{{ authentik_docker_volume_dir }}/blueprints/21-login-sources.yaml" + mode: "0644" + register: login_bp + +- name: Render local users blueprint + ansible.builtin.template: + src: blueprints/blueprint-local-users.yaml.j2 + dest: "{{ authentik_docker_volume_dir }}/blueprints/05-local-users.yaml" + mode: "0644" + when: authentik_local_users | length > 0 + register: local_users_bp + +- name: Render cleanup blueprint + ansible.builtin.template: + src: blueprints/blueprint-cleanup.yaml.j2 + dest: "{{ authentik_docker_volume_dir }}/blueprints/00-cleanup.yaml" + mode: "0644" + when: (authentik_removed_oidc_apps | length > 0) or (authentik_removed_proxy_apps | length > 0) or (authentik_removed_local_users | length > 0) + register: cleanup_bp + +- name: Set blueprints_changed fact + set_fact: + blueprints_changed: >- + {{ + (oidc_templates is defined and (oidc_templates.results | selectattr('changed') | list | length > 0)) + or (proxy_templates is defined and (proxy_templates.results | selectattr('changed') | list | length > 0)) + or (outpost_bp is defined and (outpost_bp.results | selectattr('changed') | list | length > 0)) + or (entra_bp is defined and (entra_bp.results | selectattr('changed') | list | length > 0)) + or (login_bp is defined and login_bp.changed) + or (local_users_bp.changed | default(false)) + or (cleanup_bp.changed | default(false)) + }} \ No newline at end of file diff --git a/roles/authentik/tasks/main.yml b/roles/authentik/tasks/main.yml index 83805ad..a762969 100644 --- a/roles/authentik/tasks/main.yml +++ b/roles/authentik/tasks/main.yml @@ -38,86 +38,8 @@ state: directory mode: '0755' -- name: Find existing blueprint files - find: - paths: "{{ authentik_docker_volume_dir }}/blueprints" - patterns: "*.yaml" - register: existing_blueprints - -- name: Build list of expected blueprint files - set_fact: - expected_blueprints: >- - {{ - (authentik_oidc_apps | map(attribute='slug') | map('regex_replace', '^(.*)$', '10-oidc-\1.yaml') | list) + - (authentik_proxy_apps | map(attribute='slug') | map('regex_replace', '^(.*)$', '20-proxy-\1.yaml') | list) + - (authentik_proxy_outposts | map(attribute='name') | map('regex_replace', '^(.*)$', '30-outpost-\1.yaml') | list) + - (authentik_entra_sources | map(attribute='slug') | map('regex_replace', '^(.*)$', '20-source-entra-\1.yaml') | list) + - ['21-login-sources.yaml'] + - ((authentik_local_users | length > 0) | ternary(['05-local-users.yaml'], [])) + - (((authentik_removed_oidc_apps | length > 0) or (authentik_removed_proxy_apps | length > 0) or (authentik_removed_local_users | length > 0)) | ternary(['00-cleanup.yaml'], [])) - }} - -- name: Remove stale blueprint files - file: - path: "{{ item.path }}" - state: absent - loop: "{{ existing_blueprints.files }}" - when: item.path | basename not in expected_blueprints - -- name: Render OIDC blueprints - ansible.builtin.template: - src: blueprints/blueprint-oidc-app.yaml.j2 - dest: "{{ authentik_docker_volume_dir }}/blueprints/10-oidc-{{ item.slug }}.yaml" - mode: "0644" - loop: "{{ authentik_oidc_apps }}" - register: oidc_templates - -- name: Render Proxy blueprints - ansible.builtin.template: - src: blueprints/blueprint-proxy-app.yaml.j2 - dest: "{{ authentik_docker_volume_dir }}/blueprints/20-proxy-{{ item.slug }}.yaml" - mode: "0644" - loop: "{{ authentik_proxy_apps }}" - register: proxy_templates - -- name: Render outpost blueprints - template: - src: blueprints/outpost-proxy.yaml.j2 - dest: "{{ authentik_docker_volume_dir }}/blueprints/30-outpost-{{ item.name }}.yaml" - mode: "0644" - loop: "{{ authentik_proxy_outposts }}" - register: outpost_bp - -- name: Render Entra source blueprints - ansible.builtin.template: - src: blueprints/blueprint-source-entra.yaml.j2 - dest: "{{ authentik_docker_volume_dir }}/blueprints/20-source-entra-{{ item.slug }}.yaml" - mode: "0644" - loop: "{{ authentik_entra_sources }}" - register: entra_bp - -- name: Render login stage sources blueprint - ansible.builtin.template: - src: blueprints/blueprint-login-sources.yaml.j2 - dest: "{{ authentik_docker_volume_dir }}/blueprints/21-login-sources.yaml" - mode: "0644" - register: login_bp - -- name: Render local users blueprint - ansible.builtin.template: - src: blueprints/blueprint-local-users.yaml.j2 - dest: "{{ authentik_docker_volume_dir }}/blueprints/05-local-users.yaml" - mode: "0644" - when: authentik_local_users | length > 0 - register: local_users_bp - -- name: Render cleanup blueprint - ansible.builtin.template: - src: blueprints/blueprint-cleanup.yaml.j2 - dest: "{{ authentik_docker_volume_dir }}/blueprints/00-cleanup.yaml" - mode: "0644" - when: (authentik_removed_oidc_apps | length > 0) or (authentik_removed_proxy_apps | length > 0) or (authentik_removed_local_users | length > 0) - register: cleanup_bp +- name: Render blueprints + import_tasks: blueprints.yml - name: Create docker-compose file for authentik template: @@ -129,15 +51,4 @@ community.docker.docker_compose_v2: project_src: "{{ authentik_docker_compose_dir }}" state: present - recreate: >- - {{ - ( - (oidc_templates is defined and (oidc_templates.results | selectattr('changed') | list | length > 0)) - or (proxy_templates is defined and (proxy_templates.results | selectattr('changed') | list | length > 0)) - or (outpost_bp is defined and (outpost_bp.results | selectattr('changed') | list | length > 0)) - or (entra_bp is defined and (entra_bp.results | selectattr('changed') | list | length > 0)) - or (login_bp is defined and login_bp.changed) - or (local_users_bp.changed | default(false)) - or (cleanup_bp.changed | default(false)) - ) | ternary('always','auto') - }} + recreate: "{{ blueprints_changed | ternary('always', 'auto') }}" \ No newline at end of file From 5ed12c64d0e975bdde78fcd39c9689e6b9ebc2d5 Mon Sep 17 00:00:00 2001 From: Bert-Jan Fikse Date: Wed, 14 Jan 2026 16:50:33 +0100 Subject: [PATCH 12/17] chore: add authentik_login_user_fields to allow showing custom fields, or removing them e.g when using social + local logins --- roles/authentik/defaults/main.yml | 6 ++++++ .../templates/blueprints/blueprint-login-sources.yaml.j2 | 9 +++++++-- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/roles/authentik/defaults/main.yml b/roles/authentik/defaults/main.yml index 85e8a15..5f88df1 100644 --- a/roles/authentik/defaults/main.yml +++ b/roles/authentik/defaults/main.yml @@ -99,6 +99,12 @@ authentik_login_source_ids: [] # - "source-entra-entra-id" authentik_identification_stage_name: default-authentication-identification +# Local login fields to show on login screen (username, email, upn) +# Set to empty list to hide local login form entirely +authentik_login_user_fields: + - username + - email + # Local users to provision authentik_local_users: [] # - username: admin diff --git a/roles/authentik/templates/blueprints/blueprint-login-sources.yaml.j2 b/roles/authentik/templates/blueprints/blueprint-login-sources.yaml.j2 index 9a7b76d..610dee8 100644 --- a/roles/authentik/templates/blueprints/blueprint-login-sources.yaml.j2 +++ b/roles/authentik/templates/blueprints/blueprint-login-sources.yaml.j2 @@ -4,14 +4,19 @@ metadata: name: "login-sources" labels: blueprints.goauthentik.io/instantiate: "true" - blueprints.goauthentik.io/description: "Set sources on the identification stage" + blueprints.goauthentik.io/description: "Set sources and user fields on the identification stage" entries: - model: authentik_stages_identification.identificationstage identifiers: name: "{{ authentik_identification_stage_name }}" attrs: - # NOTE: this SETS the sources list (it doesn’t append). + # Local login fields (username, email, upn) + user_fields: +{% for field in authentik_login_user_fields %} + - {{ field }} +{% endfor %} + # OAuth/social login sources sources: {% for src_id in authentik_login_source_ids %} - !KeyOf {{ src_id }} From 7f639e81916c75aa51e4438f80df52590d364ddf Mon Sep 17 00:00:00 2001 From: Bert-Jan Fikse Date: Wed, 14 Jan 2026 17:36:07 +0100 Subject: [PATCH 13/17] fix: add lock_timout to apt cache update should prevent errors with new vagrant machines, where apt is still locked by vagrant --- roles/base/tasks/main.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/roles/base/tasks/main.yml b/roles/base/tasks/main.yml index 6a6eabb..57910d9 100644 --- a/roles/base/tasks/main.yml +++ b/roles/base/tasks/main.yml @@ -6,6 +6,7 @@ ansible.builtin.apt: update_cache: true cache_valid_time: 3600 + lock_timeout: 180 when: ansible_facts["os_family"] == "Debian" - name: Install required packages for Docker @@ -20,7 +21,6 @@ state: present when: ansible_facts["os_family"] == "Debian" - - name: Install convenience packages ansible.builtin.apt: name: From fe31369f8c2b6260158db1e894bc27ed7d657c89 Mon Sep 17 00:00:00 2001 From: Bert-Jan Fikse Date: Wed, 14 Jan 2026 17:36:52 +0100 Subject: [PATCH 14/17] chore: add healthcheck to authentik and wait for it after deployment --- roles/authentik/tasks/main.yml | 4 +++- roles/authentik/templates/docker-compose.yml.j2 | 6 ++++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/roles/authentik/tasks/main.yml b/roles/authentik/tasks/main.yml index a762969..aa14bd3 100644 --- a/roles/authentik/tasks/main.yml +++ b/roles/authentik/tasks/main.yml @@ -51,4 +51,6 @@ community.docker.docker_compose_v2: project_src: "{{ authentik_docker_compose_dir }}" state: present - recreate: "{{ blueprints_changed | ternary('always', 'auto') }}" \ No newline at end of file + recreate: "{{ blueprints_changed | ternary('always', 'auto') }}" + wait: true + wait_timeout: 300 \ No newline at end of file diff --git a/roles/authentik/templates/docker-compose.yml.j2 b/roles/authentik/templates/docker-compose.yml.j2 index c6b1460..90d6819 100644 --- a/roles/authentik/templates/docker-compose.yml.j2 +++ b/roles/authentik/templates/docker-compose.yml.j2 @@ -21,6 +21,12 @@ services: image: {{ authentik_image }} restart: unless-stopped command: server + healthcheck: + test: ["CMD", "ak", "healthcheck"] + start_period: 30s + interval: 10s + retries: 5 + timeout: 5s environment: AUTHENTIK_SECRET_KEY: {{ authentik_secret_key }} AUTHENTIK_POSTGRESQL__HOST: postgres From f1f8406c7ed50d4b7e875c6b52c91f8f79d738fa Mon Sep 17 00:00:00 2001 From: Bert-Jan Fikse Date: Wed, 14 Jan 2026 17:37:28 +0100 Subject: [PATCH 15/17] chore: move blueprints to custom folder otherwise it overwrites default blueprints provided by authentik --- roles/authentik/templates/docker-compose.yml.j2 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/roles/authentik/templates/docker-compose.yml.j2 b/roles/authentik/templates/docker-compose.yml.j2 index 90d6819..6daa2a1 100644 --- a/roles/authentik/templates/docker-compose.yml.j2 +++ b/roles/authentik/templates/docker-compose.yml.j2 @@ -41,7 +41,7 @@ services: {% endfor %} {% endif %} volumes: - - {{ authentik_docker_volume_dir }}/blueprints:/blueprints + - {{ authentik_docker_volume_dir }}/blueprints:/blueprints/custom - {{ authentik_docker_volume_dir }}/data:/data - {{ authentik_docker_volume_dir }}/templates:/templates depends_on: @@ -84,7 +84,7 @@ services: - {{ authentik_docker_volume_dir }}/data:/data - {{ authentik_docker_volume_dir }}/certs:/certs - {{ authentik_docker_volume_dir }}/templates:/templates - - {{ authentik_docker_volume_dir }}/blueprints:/blueprints + - {{ authentik_docker_volume_dir }}/blueprints:/blueprints/custom depends_on: postgres: condition: service_healthy From a63da2a29ebe706ab55221f9d28ee73dd249ca8b Mon Sep 17 00:00:00 2001 From: Bert-Jan Fikse Date: Wed, 14 Jan 2026 17:55:31 +0100 Subject: [PATCH 16/17] chore: reorder blueprints so referenced objects are created before applying --- roles/authentik/tasks/blueprints.yml | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/roles/authentik/tasks/blueprints.yml b/roles/authentik/tasks/blueprints.yml index 58c8b19..e40b774 100644 --- a/roles/authentik/tasks/blueprints.yml +++ b/roles/authentik/tasks/blueprints.yml @@ -12,12 +12,12 @@ set_fact: expected_blueprints: >- {{ - (authentik_oidc_apps | map(attribute='slug') | map('regex_replace', '^(.*)$', '10-oidc-\1.yaml') | list) + - (authentik_proxy_apps | map(attribute='slug') | map('regex_replace', '^(.*)$', '20-proxy-\1.yaml') | list) + - (authentik_proxy_outposts | map(attribute='name') | map('regex_replace', '^(.*)$', '30-outpost-\1.yaml') | list) + - (authentik_entra_sources | map(attribute='slug') | map('regex_replace', '^(.*)$', '20-source-entra-\1.yaml') | list) + - ['21-login-sources.yaml'] + - ((authentik_local_users | length > 0) | ternary(['05-local-users.yaml'], [])) + + (authentik_oidc_apps | map(attribute='slug') | map('regex_replace', '^(.*)$', '50-oidc-\1.yaml') | list) + + (authentik_proxy_apps | map(attribute='slug') | map('regex_replace', '^(.*)$', '60-proxy-\1.yaml') | list) + + (authentik_proxy_outposts | map(attribute='name') | map('regex_replace', '^(.*)$', '70-outpost-\1.yaml') | list) + + (authentik_entra_sources | map(attribute='slug') | map('regex_replace', '^(.*)$', '40-source-entra-\1.yaml') | list) + + ['45-login-sources.yaml'] + + ((authentik_local_users | length > 0) | ternary(['10-local-users.yaml'], [])) + (((authentik_removed_oidc_apps | length > 0) or (authentik_removed_proxy_apps | length > 0) or (authentik_removed_local_users | length > 0)) | ternary(['00-cleanup.yaml'], [])) }} @@ -31,7 +31,7 @@ - name: Render OIDC blueprints ansible.builtin.template: src: blueprints/blueprint-oidc-app.yaml.j2 - dest: "{{ authentik_docker_volume_dir }}/blueprints/10-oidc-{{ item.slug }}.yaml" + dest: "{{ authentik_docker_volume_dir }}/blueprints/50-oidc-{{ item.slug }}.yaml" mode: "0644" loop: "{{ authentik_oidc_apps }}" register: oidc_templates @@ -39,7 +39,7 @@ - name: Render Proxy blueprints ansible.builtin.template: src: blueprints/blueprint-proxy-app.yaml.j2 - dest: "{{ authentik_docker_volume_dir }}/blueprints/20-proxy-{{ item.slug }}.yaml" + dest: "{{ authentik_docker_volume_dir }}/blueprints/60-proxy-{{ item.slug }}.yaml" mode: "0644" loop: "{{ authentik_proxy_apps }}" register: proxy_templates @@ -47,7 +47,7 @@ - name: Render outpost blueprints ansible.builtin.template: src: blueprints/outpost-proxy.yaml.j2 - dest: "{{ authentik_docker_volume_dir }}/blueprints/30-outpost-{{ item.name }}.yaml" + dest: "{{ authentik_docker_volume_dir }}/blueprints/70-outpost-{{ item.name }}.yaml" mode: "0644" loop: "{{ authentik_proxy_outposts }}" register: outpost_bp @@ -55,7 +55,7 @@ - name: Render Entra source blueprints ansible.builtin.template: src: blueprints/blueprint-source-entra.yaml.j2 - dest: "{{ authentik_docker_volume_dir }}/blueprints/20-source-entra-{{ item.slug }}.yaml" + dest: "{{ authentik_docker_volume_dir }}/blueprints/40-source-entra-{{ item.slug }}.yaml" mode: "0644" loop: "{{ authentik_entra_sources }}" register: entra_bp @@ -63,14 +63,14 @@ - name: Render login stage sources blueprint ansible.builtin.template: src: blueprints/blueprint-login-sources.yaml.j2 - dest: "{{ authentik_docker_volume_dir }}/blueprints/21-login-sources.yaml" + dest: "{{ authentik_docker_volume_dir }}/blueprints/45-login-sources.yaml" mode: "0644" register: login_bp - name: Render local users blueprint ansible.builtin.template: src: blueprints/blueprint-local-users.yaml.j2 - dest: "{{ authentik_docker_volume_dir }}/blueprints/05-local-users.yaml" + dest: "{{ authentik_docker_volume_dir }}/blueprints/10-local-users.yaml" mode: "0644" when: authentik_local_users | length > 0 register: local_users_bp From 21d340de055ead222749c9c9640615f2045053b1 Mon Sep 17 00:00:00 2001 From: Bert-Jan Fikse Date: Wed, 14 Jan 2026 18:00:56 +0100 Subject: [PATCH 17/17] fix: correctly reference login_source in blueprint-login-sources --- roles/authentik/defaults/main.yml | 6 +++--- .../templates/blueprints/blueprint-login-sources.yaml.j2 | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/roles/authentik/defaults/main.yml b/roles/authentik/defaults/main.yml index 5f88df1..460ba2d 100644 --- a/roles/authentik/defaults/main.yml +++ b/roles/authentik/defaults/main.yml @@ -94,9 +94,9 @@ authentik_entra_sources: [] # # - https://graph.microsoft.com/GroupMember.Read.All -# Show Entra on login screen: -authentik_login_source_ids: [] -# - "source-entra-entra-id" +# Show OAuth sources on login screen (list of source slugs): +authentik_login_sources: [] +# - slug: entra-id authentik_identification_stage_name: default-authentication-identification # Local login fields to show on login screen (username, email, upn) diff --git a/roles/authentik/templates/blueprints/blueprint-login-sources.yaml.j2 b/roles/authentik/templates/blueprints/blueprint-login-sources.yaml.j2 index 610dee8..acbb635 100644 --- a/roles/authentik/templates/blueprints/blueprint-login-sources.yaml.j2 +++ b/roles/authentik/templates/blueprints/blueprint-login-sources.yaml.j2 @@ -16,8 +16,8 @@ entries: {% for field in authentik_login_user_fields %} - {{ field }} {% endfor %} - # OAuth/social login sources + # OAuth/social login sources (use !Find to reference sources from other blueprints) sources: -{% for src_id in authentik_login_source_ids %} - - !KeyOf {{ src_id }} +{% for src in authentik_login_sources %} + - !Find [authentik_sources_oauth.oauthsource, [slug, {{ src.slug }}]] {% endfor %}