Repository: ansible-collections/community.general

Issue:
ldap_search sets changed to true, and shouldn't
##### SUMMARY
ldap_search always sets changed to true.  But searching never changes anything so changed should always be false.

This is true in HEAD as of this writing, although I did not report that in the ansible version below.  I read the code.
##### ISSUE TYPE
- Bug Report

##### COMPONENT NAME
ldap_search

##### ANSIBLE VERSION
<!--- Paste verbatim output from "ansible --version" between quotes -->
```paste below
ansible 2.10.1
  config file = /home/ubuntu/ansible/ansible.cfg
  configured module search path = ['/home/ubuntu/.ansible/plugins/modules', '/usr/share/ansible/plugins/modules']
  ansible python module location = /home/ubuntu/ansible/venv/lib/python3.6/site-packages/ansible
  executable location = venv/bin/ansible
  python version = 3.6.9 (default, Jul 17 2020, 12:50:27) [GCC 8.4.0]
```

##### CONFIGURATION
<!--- Paste verbatim output from "ansible-config dump --only-changed" between quotes -->
```paste below
COLLECTIONS_PATHS(/home/ubuntu/ansible/ansible.cfg) = ['/home/ubuntu/ansible/col
DEFAULT_BECOME(/home/ubuntu/ansible/ansible.cfg) = True
INTERPRETER_PYTHON(/home/ubuntu/ansible/ansible.cfg) = /usr/bin/python3
```

##### OS / ENVIRONMENT
<!--- Provide all relevant information below, e.g. target OS versions, network device firmware, etc. -->
Linux READACTED 5.4.0-1025-aws #25~18.04.1-Ubuntu SMP Fri Sep 11 12:03:04 UTC 2020 x86_64 x86_64 x86_64 GNU/Linux

##### STEPS TO REPRODUCE
<!--- Describe exactly how to reproduce the problem, using a minimal test-case -->

<!--- Paste example playbooks or commands between quotes below -->
```yaml
- hosts: localhost
  gather_facts:	false
  tasks:
  - community.general.ldap_search:
      attrs: dn
      dn: "cn=schema,cn=config"
      scope: onelevel
      filter: "(cn=*}nis)"

```

<!--- HINT: You can paste gist.github.com links for larger files -->

##### EXPECTED RESULTS
<!--- Describe what you expected to happen when running the steps above -->
The task should set changed to false.  Also when: filter: "(cn=nonexistant)"

##### ACTUAL RESULTS
<!--- Describe what actually happened. If possible run with extra verbosity (-vvvv) -->
The task sets changed to true.

<!--- Paste verbatim command output between quotes -->
```paste below
PLAY [redacted] ******************************************************

TASK [community.general.ldap_search] *******************************************
changed: [redacted]

PLAY RECAP *********************************************************************
redacted    : ok=1    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   

```



Discussion:
Files identified in the description:
* [`plugins/modules/net_tools/ldap/ldap_search.py`](https://github.com/ansible-collections/community.general/blob/master/plugins/modules/net_tools/ldap/ldap_search.py)

If these files are inaccurate, please update the `component name` section of the description or use the `!component` bot command.

[click here for bot help](https://github.com/ansible/ansibullbot/blob/master/ISSUE_HELP.md)
<!--- boilerplate: components_banner --->
cc @eryx12o45 @jtyr @nerzhul
[click here for bot help](https://github.com/ansible/ansibullbot/blob/master/ISSUE_HELP.md)
<!--- boilerplate: notify --->

Resolving patch:
diff --git a/plugins/modules/net_tools/ldap/ldap_search.py b/plugins/modules/net_tools/ldap/ldap_search.py
--- a/plugins/modules/net_tools/ldap/ldap_search.py
+++ b/plugins/modules/net_tools/ldap/ldap_search.py
@@ -112,7 +112,7 @@ def main():
         except Exception as exception:
             module.fail_json(msg="Attribute action failed.", details=to_native(exception))
 
-    module.exit_json(changed=True)
+    module.exit_json(changed=False)
 
 
 def _extract_entry(dn, attrs):
@@ -144,24 +144,20 @@ def _load_schema(self):
             self.attrsonly = 0
 
     def _load_scope(self):
-        scope = self.module.params['scope']
-        if scope == 'base':
-            self.scope = ldap.SCOPE_BASE
-        elif scope == 'onelevel':
-            self.scope = ldap.SCOPE_ONELEVEL
-        elif scope == 'subordinate':
-            self.scope = ldap.SCOPE_SUBORDINATE
-        elif scope == 'children':
-            self.scope = ldap.SCOPE_SUBTREE
-        else:
-            raise AssertionError('Implementation error')
+        spec = dict(
+            base=ldap.SCOPE_BASE,
+            onelevel=ldap.SCOPE_ONELEVEL,
+            subordinate=ldap.SCOPE_SUBORDINATE,
+            children=ldap.SCOPE_SUBTREE,
+        )
+        self.scope = spec[self.module.params['scope']]
 
     def _load_attrs(self):
         self.attrlist = self.module.params['attrs'] or None
 
     def main(self):
         results = self.perform_search()
-        self.module.exit_json(changed=True, results=results)
+        self.module.exit_json(changed=False, results=results)
 
     def perform_search(self):
         try:


Test patch: