mirror of
				https://github.com/kevinveenbirkenbach/computer-playbook.git
				synced 2025-10-31 10:19:09 +00:00 
			
		
		
		
	keycloak(role): add realm support to generic updater
- Allow kc_object_kind='realm' - Map endpoint to 'realms' and default lookup_field to 'id' - Use realm-specific kcadm GET/UPDATE (no -r flag) - Preserve immutables: id, realm - Guard query-based ID resolution to non-realm objects Context: fixing failure in 'Update REALM mail settings' task. See: https://chatgpt.com/share/68affdb8-3d28-800f-8480-aa6a74000bf8
This commit is contained in:
		| @@ -1,20 +1,20 @@ | ||||
| # Generic updater for Keycloak client/component via kcadm. | ||||
| # Generic updater for Keycloak client/component/realm via kcadm. | ||||
| # Flow: resolve ID → read current object → merge with desired → preserve immutable fields → update via stdin. | ||||
| # | ||||
| # Required vars (pass via include): | ||||
| # - kc_object_kind:   "client" | "component" | ||||
| # - kc_lookup_value:  e.g., KEYCLOAK_CLIENT_ID or KEYCLOAK_LDAP_CMP_NAME | ||||
| # - kc_object_kind:   "client" | "component" | "client-scope" | "realm" | ||||
| # - kc_lookup_value:  e.g., KEYCLOAK_CLIENT_ID or KEYCLOAK_LDAP_CMP_NAME or KEYCLOAK_REALM | ||||
| # - kc_desired:       dict, e.g., KEYCLOAK_DICTIONARY_CLIENT or KEYCLOAK_DICTIONARY_LDAP | ||||
| # | ||||
| # Optional: | ||||
| # - kc_lookup_field:  override lookup field (defaults: clientId for client, name for component) | ||||
| # - kc_lookup_field:  override lookup field (defaults: clientId for client, name for component, id for realm) | ||||
| # - kc_merge_path:    if set (e.g. "config"), only that subkey is merged | ||||
| # - kc_force_attrs:   dict to force on the final payload (merged last) | ||||
|  | ||||
| - name: Assert required vars | ||||
|   assert: | ||||
|     that: | ||||
|       - kc_object_kind in ['client','component','client-scope'] | ||||
|       - kc_object_kind in ['client','component','client-scope','realm'] | ||||
|       - kc_lookup_value is defined | ||||
|       - kc_desired is defined | ||||
|     fail_msg: "kc_object_kind, kc_lookup_value, kc_desired are required." | ||||
| @@ -26,11 +26,13 @@ | ||||
|       {{ 'clients' if kc_object_kind == 'client' | ||||
|          else 'components' if kc_object_kind == 'component' | ||||
|          else 'client-scopes' if kc_object_kind == 'client-scope' | ||||
|          else 'realms' if kc_object_kind == 'realm' | ||||
|          else '' }} | ||||
|     kc_lookup_field_eff: >- | ||||
|       {{ 'clientId' if kc_object_kind == 'client' | ||||
|          else (kc_lookup_field | default('name')) if kc_object_kind == 'component' | ||||
|          else 'name' if kc_object_kind == 'client-scope' | ||||
|          else 'id' if kc_object_kind == 'realm' | ||||
|          else '' }} | ||||
|  | ||||
| - name: Resolve object id (direct when lookup_field is id) | ||||
| @@ -39,7 +41,7 @@ | ||||
|     kc_obj_id: "{{ kc_lookup_value | string }}" | ||||
|  | ||||
| - name: Resolve object id via query | ||||
|   when: kc_lookup_field_eff != 'id' | ||||
|   when: kc_lookup_field_eff != 'id' and kc_object_kind != 'realm' | ||||
|   shell: > | ||||
|     {% if kc_object_kind == 'client-scope' -%} | ||||
|     {{ KEYCLOAK_EXEC_KCADM }} get client-scopes -r {{ KEYCLOAK_REALM }} --format json | ||||
| @@ -72,8 +74,11 @@ | ||||
|  | ||||
| - name: Read current object | ||||
|   shell: > | ||||
|     {{ KEYCLOAK_EXEC_KCADM }} get {{ kc_api }}/{{ kc_obj_id }} | ||||
|     -r {{ KEYCLOAK_REALM }} --format json | ||||
|     {% if kc_object_kind == 'realm' -%} | ||||
|     {{ KEYCLOAK_EXEC_KCADM }} get {{ kc_api }}/{{ kc_obj_id }} --format json | ||||
|     {%- else -%} | ||||
|     {{ KEYCLOAK_EXEC_KCADM }} get {{ kc_api }}/{{ kc_obj_id }} -r {{ KEYCLOAK_REALM }} --format json | ||||
|     {%- endif %} | ||||
|   register: kc_cur | ||||
|   changed_when: false | ||||
|   no_log: "{{ MASK_CREDENTIALS_IN_LOGS | bool }}" | ||||
| @@ -141,12 +146,23 @@ | ||||
|       }} | ||||
|   no_log: "{{ MASK_CREDENTIALS_IN_LOGS | bool }}" | ||||
|  | ||||
| # Preserve immutables for client-scope | ||||
| - name: Preserve immutable fields for client-scope | ||||
|   when: kc_object_kind == 'client-scope' | ||||
|   set_fact: | ||||
|     desired_obj: "{{ desired_obj | combine({'id': cur_obj.id, 'name': cur_obj.name}, recursive=True) }}" | ||||
|  | ||||
| - name: Preserve immutable fields for realm | ||||
|   when: kc_object_kind == 'realm' | ||||
|   set_fact: | ||||
|     desired_obj: >- | ||||
|       {{ | ||||
|         desired_obj | ||||
|         | combine({ | ||||
|             'id': cur_obj.id, | ||||
|             'realm': cur_obj.realm | ||||
|           }, recursive=True) | ||||
|       }} | ||||
|   no_log: "{{ MASK_CREDENTIALS_IN_LOGS | bool }}" | ||||
|  | ||||
| # Optional forced attributes (e.g., frontchannelLogout) | ||||
| - name: Apply forced attributes (optional) | ||||
| @@ -156,8 +172,14 @@ | ||||
|  | ||||
| - name: Update object via stdin | ||||
|   shell: | | ||||
|     {% if kc_object_kind == 'realm' -%} | ||||
|     cat <<'JSON' | {{ KEYCLOAK_EXEC_KCADM }} update {{ kc_api }}/{{ kc_obj_id }} -f - | ||||
|     {{ desired_obj | to_json }} | ||||
|     JSON | ||||
|     {%- else -%} | ||||
|     cat <<'JSON' | {{ KEYCLOAK_EXEC_KCADM }} update {{ kc_api }}/{{ kc_obj_id }} -r {{ KEYCLOAK_REALM }} -f - | ||||
|     {{ desired_obj | to_json }} | ||||
|     JSON | ||||
|     {%- endif %} | ||||
|   async: "{{ ASYNC_TIME if ASYNC_ENABLED | bool else omit }}" | ||||
|   poll:  "{{ ASYNC_POLL if ASYNC_ENABLED | bool else omit }}" | ||||
|   | ||||
		Reference in New Issue
	
	Block a user