diff --git a/changelogs/fragments/11502-keycloak-config-mapper.yaml b/changelogs/fragments/11502-keycloak-config-mapper.yaml new file mode 100644 index 0000000000..b7c1c1d186 --- /dev/null +++ b/changelogs/fragments/11502-keycloak-config-mapper.yaml @@ -0,0 +1,2 @@ +bugfixes: + - "keycloak_user_federation - mapper config item can be an array (https://github.com/ansible-collections/community.general/issues/11502, https://github.com/ansible-collections/community.general/pull/11515)." diff --git a/plugins/modules/keycloak_user_federation.py b/plugins/modules/keycloak_user_federation.py index c448d0247f..479d4ec015 100644 --- a/plugins/modules/keycloak_user_federation.py +++ b/plugins/modules/keycloak_user_federation.py @@ -744,15 +744,30 @@ def normalize_kc_comp(comp): def sanitize(comp): + def sanitize_value(v): + """Convert list values: single-element lists to strings, multi-element lists sorted alphabetically, others as-is.""" + if isinstance(v, list): + if len(v) == 0: + return None + elif len(v) == 1: + return v[0] + else: + return sorted(v) + else: + return v + compcopy = deepcopy(comp) if "config" in compcopy: - compcopy["config"] = {k: v[0] for k, v in compcopy["config"].items()} + compcopy["config"] = {k: sanitize_value(v) for k, v in compcopy["config"].items()} + # Remove None values (empty lists converted) + compcopy["config"] = {k: v for k, v in compcopy["config"].items() if v is not None} if "bindCredential" in compcopy["config"]: compcopy["config"]["bindCredential"] = "**********" if "mappers" in compcopy: for mapper in compcopy["mappers"]: if "config" in mapper: - mapper["config"] = {k: v[0] for k, v in mapper["config"].items()} + mapper["config"] = {k: sanitize_value(v) for k, v in mapper["config"].items()} + mapper["config"] = {k: v for k, v in mapper["config"].items() if v is not None} return compcopy @@ -886,11 +901,15 @@ def main(): if mappers is not None: for mapper in mappers: if mapper.get("config") is not None: - mapper["config"] = { - k: [str(v).lower() if not isinstance(v, str) else v] - for k, v in mapper["config"].items() - if mapper["config"][k] is not None - } + new_config = {} + for k, v in mapper["config"].items(): + if v is None: + continue + if isinstance(v, list): + new_config[k] = [str(item).lower() if not isinstance(item, str) else item for item in v] + else: + new_config[k] = [str(v).lower() if not isinstance(v, str) else v] + mapper["config"] = new_config # Filter and map the parameters names that apply comp_params = [