--- # Fédération LDAP via kcadm — idempotent. Crée le realm applicatif + le provider de # stockage LDAP (OpenLDAP, READ_ONLY). Éprouvé avant codification (testmail se connecte). - name: Attendre que Keycloak réponde ansible.builtin.wait_for: host: 127.0.0.1 port: 8080 timeout: 120 when: not ansible_check_mode - name: Configurer le realm et la fédération LDAP (kcadm, idempotent) ansible.builtin.shell: executable: /bin/bash cmd: | set -euo pipefail KC={{ serveur_keycloak_home }}/bin/kcadm.sh CHANGED=0 "$KC" config credentials --server http://localhost:8080 --realm master \ --user {{ serveur_keycloak_admin_user }} --password "$KC_ADMIN_PW" >/dev/null if ! "$KC" get realms/{{ serveur_keycloak_realm }} >/dev/null 2>&1; then "$KC" create realms -s realm={{ serveur_keycloak_realm }} -s enabled=true >/dev/null CHANGED=1 fi RID=$("$KC" get realms/{{ serveur_keycloak_realm }} --fields id 2>/dev/null \ | grep -oiE '[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}' | head -1) # Le provider est cree S'IL MANQUE, et son URL de connexion est RECONCILIEE # s'il existe. Creer sans jamais corriger laissait une valeur perimee vivre # indefiniment : le 2026-08-07, le provider pointait encore # `ldaps://id-ldap-01...` — le nom d'hote errone corrige le matin meme dans # `resoudre_annuaire`. La federation etait muette, et la synchronisation des # groupes echouait sur `UnknownHost`. # # C'est de l'INFRASTRUCTURE : elle se reconcilie. Seules les APPARTENANCES # ne le sont pas (D-67), et elles ne vivent pas ici. FEDID=$("$KC" get components -r {{ serveur_keycloak_realm }} \ -q type=org.keycloak.storage.UserStorageProvider \ --fields id,name --format csv --noquotes 2>/dev/null \ | awk -F, '$2 == "{{ serveur_keycloak_ldap_nom }}" {print $1}' | head -1) if [ -n "$FEDID" ]; then URL_ACTUELLE=$("$KC" get components/"$FEDID" -r {{ serveur_keycloak_realm }} 2>/dev/null \ | grep -o '"connectionUrl" : \[ "[^"]*"' | grep -o 'ldaps\?://[^"]*' | head -1) # `|| true` en fin de chaine : avec `pipefail`, un grep sans correspondance # tuerait le script entier. Et l'extraction ne s'ancre pas sur `$` — la ligne # se termine par un guillemet, pas par la valeur. # Le mot de passe de LIAISON ne se compare pas : Keycloak le masque (`****`). # On memorise donc son empreinte, comme pour les comptes de secours. Sans # cela, faire tourner `vault_openldap_admin` couperait Keycloak de # l'annuaire — la federation garderait l'ancienne valeur, et plus personne # ne pourrait se connecter. EMPREINTE_VOULUE="{{ serveur_keycloak_ldap_bind_password | hash('sha256') }}" EMPREINTE_POSEE=$(cat {{ serveur_keycloak_marqueur_bind }} 2>/dev/null || true) if [ "$EMPREINTE_POSEE" != "$EMPREINTE_VOULUE" ]; then "$KC" update components/"$FEDID" -r {{ serveur_keycloak_realm }} \ -s "config.bindCredential=[\"$LDAP_BIND_PW\"]" >/dev/null printf '%s\n' "$EMPREINTE_VOULUE" > {{ serveur_keycloak_marqueur_bind }} chmod 600 {{ serveur_keycloak_marqueur_bind }} CHANGED=1 fi MODE_ACTUEL=$("$KC" get components/"$FEDID" -r {{ serveur_keycloak_realm }} 2>/dev/null \ | sed -n 's/.*"editMode" : \[ "\([A-Z_]*\)".*/\1/p' | head -1 || true) if [ "$URL_ACTUELLE" != "{{ serveur_keycloak_ldap_url }}" ] \ || [ "$MODE_ACTUEL" != "{{ serveur_keycloak_ldap_edit_mode }}" ]; then "$KC" update components/"$FEDID" -r {{ serveur_keycloak_realm }} \ -s 'config.connectionUrl=["{{ serveur_keycloak_ldap_url }}"]' \ -s 'config.usersDn=["{{ serveur_keycloak_ldap_users_dn }}"]' \ -s 'config.bindDn=["{{ serveur_keycloak_ldap_bind_dn }}"]' \ -s 'config.editMode=["{{ serveur_keycloak_ldap_edit_mode }}"]' >/dev/null CHANGED=1 fi fi if [ -z "$FEDID" ]; then "$KC" create components -r {{ serveur_keycloak_realm }} \ -s name={{ serveur_keycloak_ldap_nom }} -s providerId=ldap \ -s providerType=org.keycloak.storage.UserStorageProvider -s parentId="$RID" \ -s 'config.enabled=["true"]' \ -s 'config.vendor=["other"]' \ -s 'config.editMode=["{{ serveur_keycloak_ldap_edit_mode }}"]' \ -s 'config.importEnabled=["true"]' \ -s 'config.connectionUrl=["{{ serveur_keycloak_ldap_url }}"]' \ -s 'config.usersDn=["{{ serveur_keycloak_ldap_users_dn }}"]' \ -s 'config.bindDn=["{{ serveur_keycloak_ldap_bind_dn }}"]' \ -s "config.bindCredential=[\"$LDAP_BIND_PW\"]" \ -s 'config.authType=["simple"]' \ -s 'config.searchScope=["2"]' \ -s 'config.usernameLDAPAttribute=["{{ serveur_keycloak_ldap_username_attr }}"]' \ -s 'config.rdnLDAPAttribute=["{{ serveur_keycloak_ldap_username_attr }}"]' \ -s 'config.uuidLDAPAttribute=["{{ serveur_keycloak_ldap_uuid_attr }}"]' \ -s 'config.userObjectClasses=["{{ serveur_keycloak_ldap_object_classes }}"]' \ -s 'config.useTruststoreSpi=["always"]' \ -s 'config.connectionPooling=["true"]' >/dev/null CHANGED=1 fi [ "$CHANGED" = 1 ] && echo SETOPS_CHANGED || echo SETOPS_OK environment: KC_ADMIN_PW: "{{ serveur_keycloak_admin_password }}" LDAP_BIND_PW: "{{ serveur_keycloak_ldap_bind_password }}" register: serveur_keycloak_fed changed_when: "'SETOPS_CHANGED' in serveur_keycloak_fed.stdout" no_log: true retries: 5 delay: 6 until: serveur_keycloak_fed.rc == 0 when: not ansible_check_mode