We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN 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.

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `awx_collection/plugins/modules/tower_settings.py`
Content:
```
1 #!/usr/bin/python
2 # coding: utf-8 -*-
3 
4 # (c) 2018, Nikhil Jain <nikjain@redhat.com>
5 # GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
6 
7 from __future__ import absolute_import, division, print_function
8 __metaclass__ = type
9 
10 
11 ANSIBLE_METADATA = {'metadata_version': '1.1',
12                     'status': ['preview'],
13                     'supported_by': 'community'}
14 
15 
16 DOCUMENTATION = '''
17 ---
18 module: tower_settings
19 author: "Nikhil Jain (@jainnikhil30)"
20 short_description: Modify Ansible Tower settings.
21 description:
22     - Modify Ansible Tower settings. See
23       U(https://www.ansible.com/tower) for an overview.
24 options:
25     name:
26       description:
27         - Name of setting to modify
28       type: str
29     value:
30       description:
31         - Value to be modified for given setting.
32         - If given a non-string type, will make best effort to cast it to type API expects.
33         - For better control over types, use the C(settings) param instead.
34       type: str
35     settings:
36       description:
37         - A data structure to be sent into the settings endpoint
38       type: dict
39 requirements:
40   - pyyaml
41 extends_documentation_fragment: awx.awx.auth
42 '''
43 
44 EXAMPLES = '''
45 - name: Set the value of AWX_PROOT_BASE_PATH
46   tower_settings:
47     name: AWX_PROOT_BASE_PATH
48     value: "/tmp"
49   register: testing_settings
50 
51 - name: Set the value of AWX_PROOT_SHOW_PATHS
52   tower_settings:
53     name: "AWX_PROOT_SHOW_PATHS"
54     value: "'/var/lib/awx/projects/', '/tmp'"
55   register: testing_settings
56 
57 - name: Set the LDAP Auth Bind Password
58   tower_settings:
59     name: "AUTH_LDAP_BIND_PASSWORD"
60     value: "Password"
61   no_log: true
62 
63 - name: Set all the LDAP Auth Bind Params
64   tower_settings:
65     settings:
66       AUTH_LDAP_BIND_PASSWORD: "password"
67       AUTH_LDAP_USER_ATTR_MAP:
68         email: "mail"
69         first_name: "givenName"
70         last_name: "surname"
71 '''
72 
73 from ..module_utils.tower_api import TowerAPIModule
74 
75 try:
76     import yaml
77     HAS_YAML = True
78 except ImportError:
79     HAS_YAML = False
80 
81 
82 def coerce_type(module, value):
83     # If our value is already None we can just return directly
84     if value is None:
85         return value
86 
87     yaml_ish = bool((
88         value.startswith('{') and value.endswith('}')
89     ) or (
90         value.startswith('[') and value.endswith(']'))
91     )
92     if yaml_ish:
93         if not HAS_YAML:
94             module.fail_json(msg="yaml is not installed, try 'pip install pyyaml'")
95         return yaml.safe_load(value)
96     elif value.lower in ('true', 'false', 't', 'f'):
97         return {'t': True, 'f': False}[value[0].lower()]
98     try:
99         return int(value)
100     except ValueError:
101         pass
102     return value
103 
104 
105 def main():
106     # Any additional arguments that are not fields of the item can be added here
107     argument_spec = dict(
108         name=dict(),
109         value=dict(),
110         settings=dict(type='dict'),
111     )
112 
113     # Create a module for ourselves
114     module = TowerAPIModule(
115         argument_spec=argument_spec,
116         required_one_of=[['name', 'settings']],
117         mutually_exclusive=[['name', 'settings']],
118         required_if=[['name', 'present', ['value']]]
119     )
120 
121     # Extract our parameters
122     name = module.params.get('name')
123     value = module.params.get('value')
124     new_settings = module.params.get('settings')
125 
126     # If we were given a name/value pair we will just make settings out of that and proceed normally
127     if new_settings is None:
128         new_value = coerce_type(module, value)
129 
130         new_settings = {name: new_value}
131 
132     # Load the existing settings
133     existing_settings = module.get_endpoint('settings/all')['json']
134 
135     # Begin a json response
136     json_response = {'changed': False, 'old_values': {}}
137 
138     # Check any of the settings to see if anything needs to be updated
139     needs_update = False
140     for a_setting in new_settings:
141         if a_setting not in existing_settings or existing_settings[a_setting] != new_settings[a_setting]:
142             # At least one thing is different so we need to patch
143             needs_update = True
144             json_response['old_values'][a_setting] = existing_settings[a_setting]
145 
146     # If nothing needs an update we can simply exit with the response (as not changed)
147     if not needs_update:
148         module.exit_json(**json_response)
149 
150     # Make the call to update the settings
151     response = module.patch_endpoint('settings/all', **{'data': new_settings})
152 
153     if response['status_code'] == 200:
154         # Set the changed response to True
155         json_response['changed'] = True
156 
157         # To deal with the old style values we need to return 'value' in the response
158         new_values = {}
159         for a_setting in new_settings:
160             new_values[a_setting] = response['json'][a_setting]
161 
162         # If we were using a name we will just add a value of a string, otherwise we will return an array in values
163         if name is not None:
164             json_response['value'] = new_values[name]
165         else:
166             json_response['values'] = new_values
167 
168         module.exit_json(**json_response)
169     elif 'json' in response and '__all__' in response['json']:
170         module.fail_json(msg=response['json']['__all__'])
171     else:
172         module.fail_json(**{'msg': "Unable to update settings, see response", 'response': response})
173 
174 
175 if __name__ == '__main__':
176     main()
177
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

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 @@
     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 @@
         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 @@
 
         # 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:

{"golden_diff": "diff --git a/awx_collection/plugins/modules/tower_settings.py b/awx_collection/plugins/modules/tower_settings.py\n--- a/awx_collection/plugins/modules/tower_settings.py\n+++ b/awx_collection/plugins/modules/tower_settings.py\n@@ -133,7 +133,7 @@\n     existing_settings = module.get_endpoint('settings/all')['json']\n \n     # Begin a json response\n-    json_response = {'changed': False, 'old_values': {}}\n+    json_output = {'changed': False, 'old_values': {}, 'new_values': {}}\n \n     # Check any of the settings to see if anything needs to be updated\n     needs_update = False\n@@ -141,18 +141,29 @@\n         if a_setting not in existing_settings or existing_settings[a_setting] != new_settings[a_setting]:\n             # At least one thing is different so we need to patch\n             needs_update = True\n-            json_response['old_values'][a_setting] = existing_settings[a_setting]\n+            json_output['old_values'][a_setting] = existing_settings[a_setting]\n+            json_output['new_values'][a_setting] = new_settings[a_setting]\n+\n+    if module._diff:\n+        json_output['diff'] = {\n+            'before': json_output['old_values'],\n+            'after': json_output['new_values']\n+        }\n \n     # If nothing needs an update we can simply exit with the response (as not changed)\n     if not needs_update:\n-        module.exit_json(**json_response)\n+        module.exit_json(**json_output)\n+\n+    if module.check_mode and module._diff:\n+        json_output['changed'] = True\n+        module.exit_json(**json_output)\n \n     # Make the call to update the settings\n     response = module.patch_endpoint('settings/all', **{'data': new_settings})\n \n     if response['status_code'] == 200:\n         # Set the changed response to True\n-        json_response['changed'] = True\n+        json_output['changed'] = True\n \n         # To deal with the old style values we need to return 'value' in the response\n         new_values = {}\n@@ -161,11 +172,11 @@\n \n         # If we were using a name we will just add a value of a string, otherwise we will return an array in values\n         if name is not None:\n-            json_response['value'] = new_values[name]\n+            json_output['value'] = new_values[name]\n         else:\n-            json_response['values'] = new_values\n+            json_output['values'] = new_values\n \n-        module.exit_json(**json_response)\n+        module.exit_json(**json_output)\n     elif 'json' in response and '__all__' in response['json']:\n         module.fail_json(msg=response['json']['__all__'])\n     else:\n", "issue": "awx.awx.tower_settings does not return expected changes when running in check mode with diff\n<!-- Issues are for **concrete, actionable bugs and feature requests** only - if you're just asking for debugging help or technical support, please use:\r\n\r\n- http://webchat.freenode.net/?channels=ansible-awx\r\n- https://groups.google.com/forum/#!forum/awx-project\r\n\r\nWe have to limit this because of limited volunteer time to respond to issues! -->\r\n\r\n##### ISSUE TYPE\r\n - Bug Report\r\n\r\n##### SUMMARY\r\nRunning 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).\r\nAs 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.\r\n\r\n##### ENVIRONMENT\r\n* AWX version: 15.0.1\r\n* AWX install method: docker on linux\r\n* Ansible version:  2.9.10\r\n* Operating System: Debian 10\r\n* Web Browser: Chrome\r\n\r\n##### STEPS TO REPRODUCE\r\n\r\nTask extract:\r\n```\r\n- name: Set LDAP\r\n  tower_settings:\r\n    settings:\r\n      AUTH_LDAP_SERVER_URI: \"ldap://ldap.example.com:389\"\r\n      AUTH_LDAP_BIND_DN: CN=user,DC=example,DC=com\r\n      AUTH_LDAP_BIND_PASSWORD: password\r\n      AUTH_LDAP_START_TLS: true\r\n```\r\nMake sure that some of these settings have a different value in AWX.\r\nRun the task with and without check&diff mode.\r\n\r\n##### EXPECTED RESULTS\r\nExpected at least some insight why the module reports changed status when running with check&diff mode.\r\n\r\nFor 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:\r\n```\r\nchanged: [localhost] => {\r\n    \"changed\": true,\r\n    \"invocation\": {\r\n        \"module_args\": {\r\n            \"settings\": {\r\n                \"AUTH_LDAP_SERVER_URI\": \"ldap://ldap.example.com:389\",\r\n                \"AUTH_LDAP_BIND_DN\": \"CN=user,DC=example,DC=com\",\r\n                \"AUTH_LDAP_BIND_PASSWORD\": \"password\",\r\n                \"AUTH_LDAP_START_TLS\": true\r\n            },\r\n            <...other module args ...>\r\n        }\r\n    },\r\n    \"old_values\": {\r\n        \"AUTH_LDAP_SERVER_URI\": \"ldap://nonexistent.com:389\"\r\n    },\r\n    \"values\": {\r\n        \"AUTH_LDAP_SERVER_URI\": \"ldap://ldap.example.com:389\"\r\n    }\r\n}\r\n```\r\n\r\n##### ACTUAL RESULTS\r\nRunning the module with check&diff mode produces less usable output regarding the variables to be changed:\r\n```\r\nchanged: [localhost] => {\r\n    \"changed\": true,\r\n    \"invocation\": {\r\n        \"module_args\": {\r\n            \"settings\": {\r\n                \"AUTH_LDAP_SERVER_URI\": \"ldap://ldap.example.com:389\",\r\n                \"AUTH_LDAP_BIND_DN\": \"CN=user,DC=example,DC=com\",\r\n                \"AUTH_LDAP_BIND_PASSWORD\": \"password\",\r\n                \"AUTH_LDAP_START_TLS\": true\r\n            },\r\n            <...other module args ...>\r\n        }\r\n}\r\n```\r\n##### ADDITIONAL INFORMATION\r\n\r\nAs 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.\r\n\r\nEdit:\r\nCompletely forgot about diff mode, but ofc thats where i expect to see the detailed changes.\n", "before_files": [{"content": "#!/usr/bin/python\n# coding: utf-8 -*-\n\n# (c) 2018, Nikhil Jain <nikjain@redhat.com>\n# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)\n\nfrom __future__ import absolute_import, division, print_function\n__metaclass__ = type\n\n\nANSIBLE_METADATA = {'metadata_version': '1.1',\n                    'status': ['preview'],\n                    'supported_by': 'community'}\n\n\nDOCUMENTATION = '''\n---\nmodule: tower_settings\nauthor: \"Nikhil Jain (@jainnikhil30)\"\nshort_description: Modify Ansible Tower settings.\ndescription:\n    - Modify Ansible Tower settings. See\n      U(https://www.ansible.com/tower) for an overview.\noptions:\n    name:\n      description:\n        - Name of setting to modify\n      type: str\n    value:\n      description:\n        - Value to be modified for given setting.\n        - If given a non-string type, will make best effort to cast it to type API expects.\n        - For better control over types, use the C(settings) param instead.\n      type: str\n    settings:\n      description:\n        - A data structure to be sent into the settings endpoint\n      type: dict\nrequirements:\n  - pyyaml\nextends_documentation_fragment: awx.awx.auth\n'''\n\nEXAMPLES = '''\n- name: Set the value of AWX_PROOT_BASE_PATH\n  tower_settings:\n    name: AWX_PROOT_BASE_PATH\n    value: \"/tmp\"\n  register: testing_settings\n\n- name: Set the value of AWX_PROOT_SHOW_PATHS\n  tower_settings:\n    name: \"AWX_PROOT_SHOW_PATHS\"\n    value: \"'/var/lib/awx/projects/', '/tmp'\"\n  register: testing_settings\n\n- name: Set the LDAP Auth Bind Password\n  tower_settings:\n    name: \"AUTH_LDAP_BIND_PASSWORD\"\n    value: \"Password\"\n  no_log: true\n\n- name: Set all the LDAP Auth Bind Params\n  tower_settings:\n    settings:\n      AUTH_LDAP_BIND_PASSWORD: \"password\"\n      AUTH_LDAP_USER_ATTR_MAP:\n        email: \"mail\"\n        first_name: \"givenName\"\n        last_name: \"surname\"\n'''\n\nfrom ..module_utils.tower_api import TowerAPIModule\n\ntry:\n    import yaml\n    HAS_YAML = True\nexcept ImportError:\n    HAS_YAML = False\n\n\ndef coerce_type(module, value):\n    # If our value is already None we can just return directly\n    if value is None:\n        return value\n\n    yaml_ish = bool((\n        value.startswith('{') and value.endswith('}')\n    ) or (\n        value.startswith('[') and value.endswith(']'))\n    )\n    if yaml_ish:\n        if not HAS_YAML:\n            module.fail_json(msg=\"yaml is not installed, try 'pip install pyyaml'\")\n        return yaml.safe_load(value)\n    elif value.lower in ('true', 'false', 't', 'f'):\n        return {'t': True, 'f': False}[value[0].lower()]\n    try:\n        return int(value)\n    except ValueError:\n        pass\n    return value\n\n\ndef main():\n    # Any additional arguments that are not fields of the item can be added here\n    argument_spec = dict(\n        name=dict(),\n        value=dict(),\n        settings=dict(type='dict'),\n    )\n\n    # Create a module for ourselves\n    module = TowerAPIModule(\n        argument_spec=argument_spec,\n        required_one_of=[['name', 'settings']],\n        mutually_exclusive=[['name', 'settings']],\n        required_if=[['name', 'present', ['value']]]\n    )\n\n    # Extract our parameters\n    name = module.params.get('name')\n    value = module.params.get('value')\n    new_settings = module.params.get('settings')\n\n    # If we were given a name/value pair we will just make settings out of that and proceed normally\n    if new_settings is None:\n        new_value = coerce_type(module, value)\n\n        new_settings = {name: new_value}\n\n    # Load the existing settings\n    existing_settings = module.get_endpoint('settings/all')['json']\n\n    # Begin a json response\n    json_response = {'changed': False, 'old_values': {}}\n\n    # Check any of the settings to see if anything needs to be updated\n    needs_update = False\n    for a_setting in new_settings:\n        if a_setting not in existing_settings or existing_settings[a_setting] != new_settings[a_setting]:\n            # At least one thing is different so we need to patch\n            needs_update = True\n            json_response['old_values'][a_setting] = existing_settings[a_setting]\n\n    # If nothing needs an update we can simply exit with the response (as not changed)\n    if not needs_update:\n        module.exit_json(**json_response)\n\n    # Make the call to update the settings\n    response = module.patch_endpoint('settings/all', **{'data': new_settings})\n\n    if response['status_code'] == 200:\n        # Set the changed response to True\n        json_response['changed'] = True\n\n        # To deal with the old style values we need to return 'value' in the response\n        new_values = {}\n        for a_setting in new_settings:\n            new_values[a_setting] = response['json'][a_setting]\n\n        # If we were using a name we will just add a value of a string, otherwise we will return an array in values\n        if name is not None:\n            json_response['value'] = new_values[name]\n        else:\n            json_response['values'] = new_values\n\n        module.exit_json(**json_response)\n    elif 'json' in response and '__all__' in response['json']:\n        module.fail_json(msg=response['json']['__all__'])\n    else:\n        module.fail_json(**{'msg': \"Unable to update settings, see response\", 'response': response})\n\n\nif __name__ == '__main__':\n    main()\n", "path": "awx_collection/plugins/modules/tower_settings.py"}], "after_files": [{"content": "#!/usr/bin/python\n# coding: utf-8 -*-\n\n# (c) 2018, Nikhil Jain <nikjain@redhat.com>\n# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)\n\nfrom __future__ import absolute_import, division, print_function\n__metaclass__ = type\n\n\nANSIBLE_METADATA = {'metadata_version': '1.1',\n                    'status': ['preview'],\n                    'supported_by': 'community'}\n\n\nDOCUMENTATION = '''\n---\nmodule: tower_settings\nauthor: \"Nikhil Jain (@jainnikhil30)\"\nshort_description: Modify Ansible Tower settings.\ndescription:\n    - Modify Ansible Tower settings. See\n      U(https://www.ansible.com/tower) for an overview.\noptions:\n    name:\n      description:\n        - Name of setting to modify\n      type: str\n    value:\n      description:\n        - Value to be modified for given setting.\n        - If given a non-string type, will make best effort to cast it to type API expects.\n        - For better control over types, use the C(settings) param instead.\n      type: str\n    settings:\n      description:\n        - A data structure to be sent into the settings endpoint\n      type: dict\nrequirements:\n  - pyyaml\nextends_documentation_fragment: awx.awx.auth\n'''\n\nEXAMPLES = '''\n- name: Set the value of AWX_PROOT_BASE_PATH\n  tower_settings:\n    name: AWX_PROOT_BASE_PATH\n    value: \"/tmp\"\n  register: testing_settings\n\n- name: Set the value of AWX_PROOT_SHOW_PATHS\n  tower_settings:\n    name: \"AWX_PROOT_SHOW_PATHS\"\n    value: \"'/var/lib/awx/projects/', '/tmp'\"\n  register: testing_settings\n\n- name: Set the LDAP Auth Bind Password\n  tower_settings:\n    name: \"AUTH_LDAP_BIND_PASSWORD\"\n    value: \"Password\"\n  no_log: true\n\n- name: Set all the LDAP Auth Bind Params\n  tower_settings:\n    settings:\n      AUTH_LDAP_BIND_PASSWORD: \"password\"\n      AUTH_LDAP_USER_ATTR_MAP:\n        email: \"mail\"\n        first_name: \"givenName\"\n        last_name: \"surname\"\n'''\n\nfrom ..module_utils.tower_api import TowerAPIModule\n\ntry:\n    import yaml\n    HAS_YAML = True\nexcept ImportError:\n    HAS_YAML = False\n\n\ndef coerce_type(module, value):\n    # If our value is already None we can just return directly\n    if value is None:\n        return value\n\n    yaml_ish = bool((\n        value.startswith('{') and value.endswith('}')\n    ) or (\n        value.startswith('[') and value.endswith(']'))\n    )\n    if yaml_ish:\n        if not HAS_YAML:\n            module.fail_json(msg=\"yaml is not installed, try 'pip install pyyaml'\")\n        return yaml.safe_load(value)\n    elif value.lower in ('true', 'false', 't', 'f'):\n        return {'t': True, 'f': False}[value[0].lower()]\n    try:\n        return int(value)\n    except ValueError:\n        pass\n    return value\n\n\ndef main():\n    # Any additional arguments that are not fields of the item can be added here\n    argument_spec = dict(\n        name=dict(),\n        value=dict(),\n        settings=dict(type='dict'),\n    )\n\n    # Create a module for ourselves\n    module = TowerAPIModule(\n        argument_spec=argument_spec,\n        required_one_of=[['name', 'settings']],\n        mutually_exclusive=[['name', 'settings']],\n        required_if=[['name', 'present', ['value']]]\n    )\n\n    # Extract our parameters\n    name = module.params.get('name')\n    value = module.params.get('value')\n    new_settings = module.params.get('settings')\n\n    # If we were given a name/value pair we will just make settings out of that and proceed normally\n    if new_settings is None:\n        new_value = coerce_type(module, value)\n\n        new_settings = {name: new_value}\n\n    # Load the existing settings\n    existing_settings = module.get_endpoint('settings/all')['json']\n\n    # Begin a json response\n    json_output = {'changed': False, 'old_values': {}, 'new_values': {}}\n\n    # Check any of the settings to see if anything needs to be updated\n    needs_update = False\n    for a_setting in new_settings:\n        if a_setting not in existing_settings or existing_settings[a_setting] != new_settings[a_setting]:\n            # At least one thing is different so we need to patch\n            needs_update = True\n            json_output['old_values'][a_setting] = existing_settings[a_setting]\n            json_output['new_values'][a_setting] = new_settings[a_setting]\n\n    if module._diff:\n        json_output['diff'] = {\n            'before': json_output['old_values'],\n            'after': json_output['new_values']\n        }\n\n    # If nothing needs an update we can simply exit with the response (as not changed)\n    if not needs_update:\n        module.exit_json(**json_output)\n\n    if module.check_mode and module._diff:\n        json_output['changed'] = True\n        module.exit_json(**json_output)\n\n    # Make the call to update the settings\n    response = module.patch_endpoint('settings/all', **{'data': new_settings})\n\n    if response['status_code'] == 200:\n        # Set the changed response to True\n        json_output['changed'] = True\n\n        # To deal with the old style values we need to return 'value' in the response\n        new_values = {}\n        for a_setting in new_settings:\n            new_values[a_setting] = response['json'][a_setting]\n\n        # If we were using a name we will just add a value of a string, otherwise we will return an array in values\n        if name is not None:\n            json_output['value'] = new_values[name]\n        else:\n            json_output['values'] = new_values\n\n        module.exit_json(**json_output)\n    elif 'json' in response and '__all__' in response['json']:\n        module.fail_json(msg=response['json']['__all__'])\n    else:\n        module.fail_json(**{'msg': \"Unable to update settings, see response\", 'response': response})\n\n\nif __name__ == '__main__':\n    main()\n", "path": "awx_collection/plugins/modules/tower_settings.py"}]}