Repository: ansible/awx

Issue:
awx.awx.tower_settings does not return expected changes when running in check mode with diff
<!-- Issues are for **concrete, actionable bugs and feature requests** only - if you're just asking for debugging help or technical support, please use:

- http://webchat.freenode.net/?channels=ansible-awx
- https://groups.google.com/forum/#!forum/awx-project

We have to limit this because of limited volunteer time to respond to issues! -->

##### ISSUE TYPE
 - Bug Report

##### SUMMARY
Running the module with check and diff mode simultaneously does not return the list of expected changes. It only returns that the module will change some unknown thing (by setting the `changed` variable true).
As check and diff mode is mainly used for, erm.. checking, this makes it hard to actually check the correct invocation of the module, especially when multiple settings are set.

##### ENVIRONMENT
* AWX version: 15.0.1
* AWX install method: docker on linux
* Ansible version:  2.9.10
* Operating System: Debian 10
* Web Browser: Chrome

##### STEPS TO REPRODUCE

Task extract:
```
- name: Set LDAP
  tower_settings:
    settings:
      AUTH_LDAP_SERVER_URI: "ldap://ldap.example.com:389"
      AUTH_LDAP_BIND_DN: CN=user,DC=example,DC=com
      AUTH_LDAP_BIND_PASSWORD: password
      AUTH_LDAP_START_TLS: true
```
Make sure that some of these settings have a different value in AWX.
Run the task with and without check&diff mode.

##### EXPECTED RESULTS
Expected at least some insight why the module reports changed status when running with check&diff mode.

For eg if AWX server's AUTH_LDAP_SERVER_URI is `ldap://nonexistent.com:389`, then running check&diff mode could produce similar output when running in normal mode:
```
changed: [localhost] => {
    "changed": true,
    "invocation": {
        "module_args": {
            "settings": {
                "AUTH_LDAP_SERVER_URI": "ldap://ldap.example.com:389",
                "AUTH_LDAP_BIND_DN": "CN=user,DC=example,DC=com",
                "AUTH_LDAP_BIND_PASSWORD": "password",
                "AUTH_LDAP_START_TLS": true
            },
            <...other module args ...>
        }
    },
    "old_values": {
        "AUTH_LDAP_SERVER_URI": "ldap://nonexistent.com:389"
    },
    "values": {
        "AUTH_LDAP_SERVER_URI": "ldap://ldap.example.com:389"
    }
}
```

##### ACTUAL RESULTS
Running the module with check&diff mode produces less usable output regarding the variables to be changed:
```
changed: [localhost] => {
    "changed": true,
    "invocation": {
        "module_args": {
            "settings": {
                "AUTH_LDAP_SERVER_URI": "ldap://ldap.example.com:389",
                "AUTH_LDAP_BIND_DN": "CN=user,DC=example,DC=com",
                "AUTH_LDAP_BIND_PASSWORD": "password",
                "AUTH_LDAP_START_TLS": true
            },
            <...other module args ...>
        }
}
```
##### ADDITIONAL INFORMATION

As i see the main reason behind such behaviour is because a [new json_response dict is created](https://github.com/ansible/awx/blob/29926ba5d99b760b32de35e07c211fd3e7691c8d/awx_collection/plugins/modules/tower_settings.py#L136) in the module's code, but the [check_mode exit point](https://github.com/ansible/awx/blob/29926ba5d99b760b32de35e07c211fd3e7691c8d/awx_collection/plugins/module_utils/tower_api.py#L85) uses its own `json_output` dict.

Edit:
Completely forgot about diff mode, but ofc thats where i expect to see the detailed changes.


Discussion:

Resolving patch:
diff --git a/awx_collection/plugins/modules/tower_settings.py b/awx_collection/plugins/modules/tower_settings.py
--- a/awx_collection/plugins/modules/tower_settings.py
+++ b/awx_collection/plugins/modules/tower_settings.py
@@ -133,7 +133,7 @@ def main():
     existing_settings = module.get_endpoint('settings/all')['json']
 
     # Begin a json response
-    json_response = {'changed': False, 'old_values': {}}
+    json_output = {'changed': False, 'old_values': {}, 'new_values': {}}
 
     # Check any of the settings to see if anything needs to be updated
     needs_update = False
@@ -141,18 +141,29 @@ def main():
         if a_setting not in existing_settings or existing_settings[a_setting] != new_settings[a_setting]:
             # At least one thing is different so we need to patch
             needs_update = True
-            json_response['old_values'][a_setting] = existing_settings[a_setting]
+            json_output['old_values'][a_setting] = existing_settings[a_setting]
+            json_output['new_values'][a_setting] = new_settings[a_setting]
+
+    if module._diff:
+        json_output['diff'] = {
+            'before': json_output['old_values'],
+            'after': json_output['new_values']
+        }
 
     # If nothing needs an update we can simply exit with the response (as not changed)
     if not needs_update:
-        module.exit_json(**json_response)
+        module.exit_json(**json_output)
+
+    if module.check_mode and module._diff:
+        json_output['changed'] = True
+        module.exit_json(**json_output)
 
     # Make the call to update the settings
     response = module.patch_endpoint('settings/all', **{'data': new_settings})
 
     if response['status_code'] == 200:
         # Set the changed response to True
-        json_response['changed'] = True
+        json_output['changed'] = True
 
         # To deal with the old style values we need to return 'value' in the response
         new_values = {}
@@ -161,11 +172,11 @@ def main():
 
         # If we were using a name we will just add a value of a string, otherwise we will return an array in values
         if name is not None:
-            json_response['value'] = new_values[name]
+            json_output['value'] = new_values[name]
         else:
-            json_response['values'] = new_values
+            json_output['values'] = new_values
 
-        module.exit_json(**json_response)
+        module.exit_json(**json_output)
     elif 'json' in response and '__all__' in response['json']:
         module.fail_json(msg=response['json']['__all__'])
     else:


Test patch: