identity: Autodetect FreeIPA server with DNS
diff --git a/lib/ansible/module_utils/ipa.py b/lib/ansible/module_utils/ipa.py
index 381afac7d12583..0d3eb1d3b45ead 100644
--- a/lib/ansible/module_utils/ipa.py
+++ b/lib/ansible/module_utils/ipa.py
@@ -28,13 +28,28 @@
 # USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
 import json
+import socket
 
 import re
 from ansible.module_utils._text import to_bytes, to_native, to_text
 from ansible.module_utils.six import PY3
 from ansible.module_utils.six.moves.urllib.parse import quote
 from ansible.module_utils.urls import fetch_url
-from ansible.module_utils.basic import env_fallback
+from ansible.module_utils.basic import env_fallback, AnsibleFallbackNotFound
+
+
+def _env_then_dns_fallback(*args, **kwargs):
+    ''' Load value from environment or DNS in that order'''
+    try:
+        return env_fallback(*args, **kwargs)
+    except AnsibleFallbackNotFound:
+        # If no host was given, we try to guess it from IPA.
+        # The ipa-ca entry is a standard entry that IPA will have set for
+        # the CA.
+        try:
+            return socket.gethostbyaddr(socket.gethostbyname('ipa-ca'))[0]
+        except Exception:
+            raise AnsibleFallbackNotFound
 
 
 class IPAClient(object):
@@ -181,7 +196,7 @@ def modify_if_diff(self, name, ipa_list, module_list, add_method, remove_method,
 def ipa_argument_spec():
     return dict(
         ipa_prot=dict(type='str', default='https', choices=['http', 'https'], fallback=(env_fallback, ['IPA_PROT'])),
-        ipa_host=dict(type='str', default='ipa.example.com', fallback=(env_fallback, ['IPA_HOST'])),
+        ipa_host=dict(type='str', default='ipa.example.com', fallback=(_env_then_dns_fallback, ['IPA_HOST'])),
         ipa_port=dict(type='int', default=443, fallback=(env_fallback, ['IPA_PORT'])),
         ipa_user=dict(type='str', default='admin', fallback=(env_fallback, ['IPA_USER'])),
         ipa_pass=dict(type='str', required=True, no_log=True, fallback=(env_fallback, ['IPA_PASS'])),
diff --git a/lib/ansible/plugins/doc_fragments/ipa.py b/lib/ansible/plugins/doc_fragments/ipa.py
index d2017b7e4eb70e..20f4c81a17d7c3 100644
--- a/lib/ansible/plugins/doc_fragments/ipa.py
+++ b/lib/ansible/plugins/doc_fragments/ipa.py
@@ -18,7 +18,9 @@ class ModuleDocFragment(object):
     description:
     - IP or hostname of IPA server.
     - If the value is not specified in the task, the value of environment variable C(IPA_HOST) will be used instead.
-    - If both the environment variable C(IPA_HOST) and the value are not specified in the task, then default value is set.
+    - If both the environment variable C(IPA_HOST) and the value are not specified in the task, then DNS will be used to try to discover the FreeIPA server.
+    - The relevant entry needed in FreeIPA is the 'ipa-ca' entry.
+    - If neither the DNS entry, nor the environment C(IPA_HOST), nor the value are available in the task, then the default value will be used.
     - 'Environment variable fallback mechanism is added in version 2.5.'
     default: ipa.example.com
   ipa_user:

##### SUMMARY
This adds the ability for the freeIPA related modules to be able to
auto-detect the IPA server through DNS.

This takes advantage of the fact that a lot of FreeIPA deployments
configure their hosts to use IPA as the nameserver.

This check is only used if we didn't set neither the ipa_host parameter,
nor the environment variable IPA_HOST.

##### ISSUE TYPE
- Feature Pull Request

##### COMPONENT NAME
identity

##### ADDITIONAL INFORMATION
This makes it easier to use the IPA-related modules in the sense that one no longer needs to include the hostname of the IPA server if the nodes are enrolled to it and use IPA as the nameserver. This way, the module can be shared between deployments in a more general fashion.

So, instead of always adding the host to create a service, like this:
```
- hosts: localhost 
  tasks:
  - ipa_service:
      name: beer/some-host.mydomain.example.com
      state: present
      force: true
      ipa_host: ipa.mydomain.example.com
      ipa_user: admin
      ipa_pass: my-super-secret-password
```
you can now merely omit it as follows:
```
- hosts: localhost 
  tasks:
  - ipa_service:
      name: beer/some-host.mydomain.example.com
      state: present
      force: true
      ipa_user: admin
      ipa_pass: my-super-secret-password
```
Assuming that the host that runs this resolves the appropriate DNS entries.