From a1ce0e6491b449b34412429545c2dc5b8b775fbb Mon Sep 17 00:00:00 2001 From: Kevin Veen-Birkenbach Date: Fri, 23 Oct 2026 16:16:51 +0200 Subject: [PATCH] fix(web-app-keycloak): create the dictionary's client scopes before converging the client The client update converges defaultClientScopes and optionalClientScopes and fails with "Desired client scope does not exist in the realm" for a scope the realm lacks. A realm imported from the current dictionary has them all, but a realm an older release created does not, and the RBAC scope was only created later, in 04_rbac_client_scope.yml, after the convergence had already failed. The Keycloak stage on echoserver stopped there on its legacy realm. 01_client.yml now reads the realm's client scopes and creates every scope of KEYCLOAK_DICTIONARY_REALM.clientScopes that is missing, before the convergence; existing scopes are left alone, and on an imported realm the step creates nothing. The RBAC-only create in 04 is gone with it. Co-Authored-By: Claude Opus 5.5 (1M context) --- .../tasks/update/01_client.yml | 19 +++++++++++++++++++ .../tasks/update/04_rbac_client_scope.yml | 16 ---------------- 2 files changed, 19 insertions(+), 16 deletions(-) diff --git a/roles/web-app-keycloak/tasks/update/01_client.yml b/roles/web-app-keycloak/tasks/update/01_client.yml index 19b01e1e40..b34db16d58 100644 --- a/roles/web-app-keycloak/tasks/update/01_client.yml +++ b/roles/web-app-keycloak/tasks/update/01_client.yml @@ -1,4 +1,23 @@ --- +- name: ⬇️ Read the client scopes of the realm + shell: "{{ KEYCLOAK_EXEC_KCADM }} get client-scopes -r {{ KEYCLOAK_REALM }} --format json" + register: kc_realm_client_scopes + changed_when: false + +- name: ✅ Create the client scopes the realm dictionary declares and the realm lacks + shell: | + cat <<'JSON' | {{ KEYCLOAK_EXEC_KCADM }} create client-scopes -r {{ KEYCLOAK_REALM }} -f - + {{ kc_client_scope | to_json }} + JSON + loop: >- + {{ KEYCLOAK_DICTIONARY_REALM.clientScopes + | rejectattr('name', 'in', kc_realm_client_scopes.stdout | kcadm_json | map(attribute='name') | list) + | list }} + loop_control: + loop_var: kc_client_scope + label: "{{ kc_client_scope.name }}" + changed_when: true + - name: "🔄 Update Client settings" vars: kc_object_kind: "client" diff --git a/roles/web-app-keycloak/tasks/update/04_rbac_client_scope.yml b/roles/web-app-keycloak/tasks/update/04_rbac_client_scope.yml index 9d6ee571b9..f263347a46 100644 --- a/roles/web-app-keycloak/tasks/update/04_rbac_client_scope.yml +++ b/roles/web-app-keycloak/tasks/update/04_rbac_client_scope.yml @@ -1,20 +1,4 @@ --- -- name: ✅ Ensure RBAC client scope exists - shell: | - cat <<'JSON' | {{ KEYCLOAK_EXEC_KCADM }} create client-scopes -r {{ KEYCLOAK_REALM }} -f - - {{ - ( - KEYCLOAK_DICTIONARY_REALM.clientScopes - | selectattr('name','equalto', KEYCLOAK_RBAC_GROUP_CLAIM) - | list | first - ) | to_json - }} - JSON - register: create_rbac_scope - changed_when: create_rbac_scope.rc == 0 - failed_when: create_rbac_scope.rc != 0 and ('already exists' not in (create_rbac_scope.stderr | lower)) - no_log: "{{ MASK_CREDENTIALS_IN_LOGS | bool }}" - - name: ⬇️ Get all client scopes shell: "{{ KEYCLOAK_EXEC_KCADM }} get client-scopes -r {{ KEYCLOAK_REALM }} --format json" register: all_scopes