From 645dd2d4480f7bab05d466376edeb8250571a37b Mon Sep 17 00:00:00 2001 From: Alexei Znamensky <103110+russoz@users.noreply.github.com> Date: Thu, 7 May 2026 07:38:56 +1200 Subject: [PATCH] ldap_attrs: fix case-insensitive attribute lookup in `state=exact` (#11990) * fix(ldap_attrs): case-insensitive attribute lookup in _get_all_values_of LDAP attribute names are case-insensitive (RFC 4512), but the previous code used a case-sensitive dict lookup on the server's response. When the server returns an attribute with different casing than requested, the lookup returns [] causing state=exact to issue MOD_ADD instead of MOD_REPLACE, which fails on single-valued attributes that already have a value. Fixes #1624 Co-Authored-By: Claude Sonnet 4.6 * feat(changelogs): add fragment for ldap_attrs fix #11990 Co-Authored-By: Claude Sonnet 4.6 --------- Co-authored-by: Claude Sonnet 4.6 --- .../11990-ldap-attrs-case-insensitive-attr-lookup.yml | 2 ++ plugins/modules/ldap_attrs.py | 3 ++- 2 files changed, 4 insertions(+), 1 deletion(-) create mode 100644 changelogs/fragments/11990-ldap-attrs-case-insensitive-attr-lookup.yml diff --git a/changelogs/fragments/11990-ldap-attrs-case-insensitive-attr-lookup.yml b/changelogs/fragments/11990-ldap-attrs-case-insensitive-attr-lookup.yml new file mode 100644 index 0000000000..679b94c0af --- /dev/null +++ b/changelogs/fragments/11990-ldap-attrs-case-insensitive-attr-lookup.yml @@ -0,0 +1,2 @@ +bugfixes: + - ldap_attrs - fix ``state=exact`` incorrectly issuing ``MOD_ADD`` instead of ``MOD_REPLACE`` for attributes returned by the server with different casing (https://github.com/ansible-collections/community.general/issues/1624, https://github.com/ansible-collections/community.general/pull/11990). diff --git a/plugins/modules/ldap_attrs.py b/plugins/modules/ldap_attrs.py index 31c4b62090..a4cdf43fc2 100644 --- a/plugins/modules/ldap_attrs.py +++ b/plugins/modules/ldap_attrs.py @@ -347,7 +347,8 @@ class LdapAttrs(LdapGeneric): results = self.connection.search_s(self.dn, ldap.SCOPE_BASE, attrlist=[name]) except ldap.LDAPError as e: self.fail(f"Cannot search for attribute {name}", e) - self._cached_values[lc_name] = results[0][1].get(name, []) + attrs = results[0][1] + self._cached_values[lc_name] = next((v for k, v in attrs.items() if k.lower() == lc_name), []) return self._cached_values[lc_name] def _is_value_absent(self, name, value):