diff --git a/action_plugins/ipahost.py b/action_plugins/ipahost.py
index 3854dcec805fb6d2dfd927a27d01666eb581fb98..d4bd4b020cc984fc0c473bb06b9124a08367c283 100644
--- a/action_plugins/ipahost.py
+++ b/action_plugins/ipahost.py
@@ -17,61 +17,226 @@
 # You should have received a copy of the GNU General Public License
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
+import gssapi
 import os
+import shutil
+import subprocess
+import tempfile
+from jinja2 import Template
 
 from ansible.errors import AnsibleError
 from ansible.module_utils._text import to_native
 from ansible.plugins.action import ActionBase
 
+try:
+    from __main__ import display
+except ImportError:
+    from ansible.utils.display import Display
+    display = Display()
+
+def run_cmd(args, stdin=None):
+    """
+    Execute an external command.
+    """
+    p_in = None
+    p_out = subprocess.PIPE
+    p_err = subprocess.PIPE
+
+    if stdin:
+        p_in = subprocess.PIPE
+
+    p = subprocess.Popen(args, stdin=p_in, stdout=p_out, stderr=p_err,
+                         close_fds=True)
+    stdout, stderr = p.communicate(stdin)
+
+    return p.returncode
+
+
+def kinit_password(principal, password, ccache_name, config):
+    """
+    Perform kinit using principal/password, with the specified config file
+    and store the TGT in ccache_name.
+    """
+    args = [ "/usr/bin/kinit", principal, '-c', ccache_name]
+    old_config = os.environ.get('KRB5_CONFIG')
+    os.environ['KRB5_CONFIG'] = config
+
+    try:
+        result = run_cmd(args, stdin=password)
+        return result
+    finally:
+        if old_config is not None:
+            os.environ['KRB5_CONFIG'] = old_config
+        else:
+            os.environ.pop('KRB5_CONFIG', None)
+
+
+def kinit_keytab(principal, keytab, ccache_name, config):
+    """
+    Perform kinit using principal/keytab, with the specified config file
+    and store the TGT in ccache_name.
+    """
+    old_config = os.environ.get('KRB5_CONFIG')
+    os.environ['KRB5_CONFIG'] = config
+    try:
+        name = gssapi.Name(principal, gssapi.NameType.kerberos_principal)
+        store = {'ccache': ccache_name,
+                 'client_keytab': keytab}
+        cred = gssapi.Credentials(name=name, store=store, usage='initiate')
+        return cred
+    finally:
+        if old_config is not None:
+            os.environ['KRB5_CONFIG'] = old_config
+        else:
+            os.environ.pop('KRB5_CONFIG', None)
+
+
+KRB5CONF_TEMPLATE = """
+[logging]
+ default = FILE:/var/log/krb5libs.log
+ kdc = FILE:/var/log/krb5kdc.log
+ admin_server = FILE:/var/log/kadmind.log
+
+[libdefaults]
+ default_realm = {{ ipa_realm }}
+ dns_lookup_realm = false
+ dns_lookup_kdc = true
+ rdns = false
+ ticket_lifetime = {{ ipa_lifetime }}
+ forwardable = true
+ udp_preference_limit = 0
+ default_ccache_name = KEYRING:persistent:%{uid}
+
+[realms]
+ {{ ipa_realm }} = {
+  kdc = {{ ipa_server }}:88
+  master_kdc = {{ ipa_server }}:88
+  admin_server = {{ ipa_server }}:749
+  default_domain = {{ ipa_domain }}
+}
+
+[domain_realm]
+ .{{ ipa_domain }} = {{ ipa_realm }}
+ {{ ipa_domain }} = {{ ipa_realm}}
+"""
+
 class ActionModule(ActionBase):
+
     def run(self, tmp=None, task_vars=None):
         """
-        handler for file transfer operations
+        handler for credential cache transfer
 
         ipa* commands can either provide a password or a keytab file
         in order to authenticate on the managed node with Kerberos.
-        When a keytab is provided, it needs to be copied from the control
-        node to the managed node.
-        This Action Module performs the copy when needed.
+        The module is using these credentials to obtain a TGT locally on the
+        control node:
+        - need to create a krb5.conf Kerberos client configuration that is
+        using IPA server
+        - set the environment variable KRB5_CONFIG to point to this conf file
+        - set the environment variable KRB5CCNAME to use a specific cache
+        - perform kinit on the control node
+        This command creates the credential cache file
+        - copy the credential cache file on the managed node
+
+        Then the IPA commands can use this credential cache file.
         """
 
         if task_vars is None:
             task_vars = dict()
 
         result = super(ActionModule, self).run(tmp, task_vars)
+        principal = self._task.args.get('principal', None)
         keytab = self._task.args.get('keytab', None)
         password = self._task.args.get('password', None)
+        lifetime = self._task.args.get('lifetime', '1h')
 
-        if (keytab is None and password is None):
+        if (not keytab and not password):
             result['failed'] = True
             result['msg'] = "keytab or password is required"
             return result
 
-        # If password is supplied, just need to execute the module
-        if password:
-            result.update(self._execute_module(task_vars=task_vars))
+        if not principal:
+            result['failed'] = True
+            result['msg'] = "principal is required"
             return result
 
-        # Password not supplied, need to transfer the keytab file
-        # Check if the source keytab exists
+        data = self._execute_module(module_name='ipa_facts', module_args=dict(),
+                                    task_vars=None)
         try:
-            keytab = self._find_needle('files', keytab)
-        except AnsibleError as e:
+            domain = data['ansible_facts']['ipa']['domain']
+            realm = data['ansible_facts']['ipa']['realm']
+        except KeyError:
             result['failed'] = True
-            result['msg'] = to_native(e)
+            result['msg'] = "The host is not an IPA server"
             return result
 
-        # Create the remote tmp dir
-        tmp = self._make_tmp_path()
-        tmp_keytab = self._connection._shell.join_path(
-            tmp, os.path.basename(keytab))
-        self._transfer_file(keytab, tmp_keytab)
-        self._fixup_perms2((tmp, tmp_keytab))
+        items = principal.split('@')
+        if len(items) < 2:
+            principal = str('%s@%s' % (principal, realm))
 
-        new_module_args = self._task.args.copy()
-        new_module_args.update(dict(keytab=tmp_keytab))
+        # Locally create a temp directory to store krb5.conf and ccache
+        local_temp_dir = tempfile.mkdtemp()
+        krb5conf_name = os.path.join(local_temp_dir, 'krb5.conf')
+        ccache_name = os.path.join(local_temp_dir, 'ccache')
 
-        # Execute module
-        result.update(self._execute_module(module_args=new_module_args, task_vars=task_vars))
-        self._remove_tmp_path(tmp)
-        return result
+        # Create the krb5.conf from the template
+        template = Template(KRB5CONF_TEMPLATE)
+        content = template.render(dict(
+            ipa_server=task_vars['ansible_host'],
+            ipa_domain=domain,
+            ipa_realm=realm,
+            ipa_lifetime=lifetime))
+
+        with open(krb5conf_name, 'w') as f:
+            f.write(content)
+
+        if password:
+            # perform kinit -c ccache_name -l 1h principal
+            res = kinit_password(principal, password, ccache_name,
+                                 krb5conf_name)
+            if res:
+                result['failed'] = True
+                result['msg'] = 'kinit %s with password failed' % principal
+                return result
+
+        else:
+            # Password not supplied, need to use the keytab file
+            # Check if the source keytab exists
+            try:
+                keytab = self._find_needle('files', keytab)
+            except AnsibleError as e:
+                result['failed'] = True
+                result['msg'] = to_native(e)
+                return result
+            # perform kinit -kt keytab
+            try:
+                kinit_keytab(principal, keytab, ccache_name, krb5conf_name)
+            except Exception as e:
+                result['failed'] = True
+                result['msg'] = 'kinit %s with keytab %s failed' % (principal, keytab)
+                return result
+
+        try:
+            # Create the remote tmp dir
+            tmp = self._make_tmp_path()
+            tmp_ccache = self._connection._shell.join_path(
+                tmp, os.path.basename(ccache_name))
+
+            # Copy the ccache to the remote tmp dir
+            self._transfer_file(ccache_name, tmp_ccache)
+            self._fixup_perms2((tmp, tmp_ccache))
+
+            new_module_args = self._task.args.copy()
+            new_module_args.pop('password', None)
+            new_module_args.pop('keytab', None)
+            new_module_args.pop('lifetime', None)
+            new_module_args.update(ccache=tmp_ccache)
+
+            # Execute module
+            result.update(self._execute_module(module_args=new_module_args,
+                                               task_vars=task_vars))
+            return result
+        finally:
+            # delete the local temp directory
+            shutil.rmtree(local_temp_dir, ignore_errors=True)
+            run_cmd(['/usr/bin/kdestroy', '-c', tmp_ccache])
diff --git a/action_plugins/ipahost.pyc b/action_plugins/ipahost.pyc
deleted file mode 100644
index 5ef86f653d89033f779da0dc8329b5e900761cf5..0000000000000000000000000000000000000000
Binary files a/action_plugins/ipahost.pyc and /dev/null differ
diff --git a/inventory/hosts b/inventory/hosts
index a3a3ccf4a0645b122f27f5663999f2f5858207ac..552db14baa85aca610651407fc42fde601e6cdff 100644
--- a/inventory/hosts
+++ b/inventory/hosts
@@ -9,7 +9,13 @@ ipaclient_domain=ipadomain.com
 ipaclient_realm=IPADOMAIN.COM
 ipaclient_server=ipaserver.ipadomain.com
 ipaclient_extraargs=[ '--kinit-attempts=3', '--mkhomedir']
+# if neither ipaclient_password nor ipaclient_keytab is defined,
+# the enrollement will create a OneTime Password and enroll with this OTP
+# In this case ipaserver_password or ipaserver_keytab is required
+#ipaclient_principal=admin
+#ipaclient_password=SecretPassword123
+#ipaclient_keytab=/tmp/krb5.keytab
+ipaserver_principal=admin
+#ipaserver_password=SecretPassword123
+ipaserver_keytab=files/admin.keytab
 
-[ipaservers:vars]
-ipa_admin=admin
-ipa_password=MySecretPassword123
diff --git a/library/ipa_facts.py b/library/ipa_facts.py
new file mode 100644
index 0000000000000000000000000000000000000000..625387fdf30807170a3a50c37e534ba92809eb45
--- /dev/null
+++ b/library/ipa_facts.py
@@ -0,0 +1,175 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+
+import os
+import re
+import six
+from six.moves.configparser import RawConfigParser
+
+from ansible.module_utils.basic import AnsibleModule
+
+try:
+    from ipalib import api
+except ImportError:
+    HAS_IPALIB = False
+else:
+    HAS_IPALIB = True
+    from ipaplatform.paths import paths
+    try:
+        # FreeIPA >= 4.5
+        from ipalib.install import sysrestore
+    except ImportError:
+        # FreeIPA 4.4 and older
+        from ipapython import sysrestore
+
+try:
+    import ipaserver
+except ImportError:
+    HAS_IPASERVER = False
+else:
+    HAS_IPASERVER = True
+
+SERVER_SYSRESTORE_STATE = "/var/lib/ipa/sysrestore/sysrestore.state"
+NAMED_CONF = "/etc/named.conf"
+VAR_LIB_PKI_TOMCAT = "/var/lib/pki/pki-tomcat"
+
+
+def is_ntpd_configured():
+    # ntpd is configured when sysrestore.state contains the line
+    # [ntpd]
+    ntpd_conf_section = re.compile('^\s*\[ntpd\]\s*$')
+
+    try:
+        with open(SERVER_SYSRESTORE_STATE) as f:
+            for line in f.readlines():
+                if ntpd_conf_section.match(line):
+                    return True
+        return False
+    except IOError:
+        return False
+
+def is_dns_configured():
+    # dns is configured when /etc/named.conf contains the line
+    # dyndb "ipa" "/usr/lib64/bind/ldap.so" {
+    bind_conf_section = re.compile('^\s*dyndb\s+"ipa"\s+"[^"]+"\s+{$')
+
+    try:
+        with open(NAMED_CONF) as f:
+            for line in f.readlines():
+                if bind_conf_section.match(line):
+                    return True
+        return False
+    except IOError:
+        return False
+
+def is_dogtag_configured(subsystem):
+    # ca / kra is configured when the directory /var/lib/pki/pki-tomcat/[ca|kra]
+    # exists
+    available_subsystems = { 'ca', 'kra' }
+    assert subsystem in available_subsystems
+
+    return os.path.isdir(os.path.join(VAR_LIB_PKI_TOMCAT, subsystem))
+
+def is_ca_configured():
+    return is_dogtag_configured('ca')
+
+def is_kra_configured():
+    return is_dogtag_configured('kra')
+
+def is_client_configured():
+    # IPA Client is configured when /etc/ipa/default.conf exists
+    # and /var/lib/ipa-client/sysrestore/sysrestore.state exists
+
+    fstore = sysrestore.FileStore(paths.IPA_CLIENT_SYSRESTORE)
+    return (os.path.isfile(paths.IPA_DEFAULT_CONF) and fstore.has_files())
+
+def is_server_configured():
+    # IPA server is configured when /etc/ipa/default.conf exists
+    # and /var/lib/ipa/sysrestore/sysrestore.state exists
+    return (os.path.isfile(paths.IPA_DEFAULT_CONF) and
+            os.path.isfile(SERVER_SYSRESTORE_STATE))
+
+def get_ipa_conf():
+    # Extract basedn, realm and domain from /etc/ipa/default.conf
+    parser = RawConfigParser()
+    parser.read(paths.IPA_DEFAULT_CONF)
+    basedn = parser.get('global', 'basedn')
+    realm = parser.get('global', 'realm')
+    domain = parser.get('global', 'domain')
+    return dict(
+        basedn=basedn,
+        realm=realm,
+        domain=domain
+        )
+
+def get_ipa_version():
+    try:
+        from ipapython import version
+    except ImportError:
+        return None
+    else:
+        version_info = []
+        for part in version.VERSION.split('.'):
+            # DEV versions look like:
+            # 4.4.90.201610191151GITd852c00
+            # 4.4.90.dev201701071308+git2e43db1
+            if part.startswith('dev') or 'GIT' in part:
+                version_info.append(part)
+            else:
+                version_info.append(int(part))
+
+        return dict(
+            api_version=version.API_VERSION,
+            num_version=version.NUM_VERSION,
+            vendor_version=version.VENDOR_VERSION,
+            version=version.VERSION,
+            version_info=version_info
+            )
+        
+def main():
+    module = AnsibleModule(
+        argument_spec = dict(),
+        supports_check_mode=True
+    )
+
+    # The module does not change anything, meaning that
+    # check mode is supported
+
+    ipa_facts = dict(
+        packages= dict(
+            ipalib=HAS_IPALIB,
+            ipaserver=HAS_IPASERVER,
+        ),
+        configured=dict(
+            client=False,
+            server=False,
+            dns=False,
+            ca=False,
+            kra=False,
+            ntpd=False
+        )
+    )
+
+    if HAS_IPALIB:
+        if is_client_configured():
+            ipa_facts['configured']['client'] = True
+
+            ipa_facts['version'] = get_ipa_version()
+            for key,value in six.iteritems(get_ipa_conf()):
+                ipa_facts[key] = value
+
+    if HAS_IPASERVER:
+        if is_server_configured():
+            ipa_facts['configured']['server'] = True
+            ipa_facts['configured']['dns'] = is_dns_configured()
+            ipa_facts['configured']['ca'] = is_ca_configured()
+            ipa_facts['configured']['kra'] = is_kra_configured()
+            ipa_facts['configured']['ntpd'] = is_ntpd_configured()
+
+    module.exit_json(
+        changed=False,
+        ansible_facts=dict(ipa=ipa_facts)
+        )
+
+if __name__ == '__main__':
+    main()
diff --git a/library/ipaclient.py b/library/ipaclient.py
index 37bd9c5b77832104331dd2daddff27dcd1a26e3b..a184066975db834f8f0ff20e9a3d27f056c2a661 100644
--- a/library/ipaclient.py
+++ b/library/ipaclient.py
@@ -151,7 +151,10 @@ def get_ipa_conf():
     parser.read(paths.IPA_DEFAULT_CONF)
     result = dict()
     for item in ['basedn', 'realm', 'domain', 'server', 'host', 'xmlrpc_uri']:
-        value = parser.get('global', item)
+        if parser.has_option('global', item):
+	    value = parser.get('global', item)
+        else:
+            value = None
         if value:
             result[item] = value
 
@@ -251,6 +254,7 @@ def ensure_ipa_client(module):
     if keytab:
         cmd.append("--keytab")
         cmd.append(keytab)
+        cmd.append("-d")
     if otp:
         cmd.append("--password")
         cmd.append(otp)
diff --git a/library/ipahost.py b/library/ipahost.py
index c4914d610a746497860570383367c09ab7f2d62b..08305fc19449e5bd324d29aa7f333fd073be408a 100644
--- a/library/ipahost.py
+++ b/library/ipahost.py
@@ -36,7 +36,7 @@ description:
 options:
   principal:
     description: Kerberos principal used to manage the host
-    required: false
+    required: true
     default: admin
   password:
     description: Password for the kerberos principal
@@ -44,6 +44,10 @@ options:
   keytab:
     description: Keytab file containing the Kerberos principal and encrypted key
     required: false
+  lifetime:
+    description: Sets the default lifetime for initial ticket requests
+    required: false
+    default: 1h
   fqdn:
     description: the fully-qualified hostname of the host to add/modify/remove
     required: true
@@ -251,9 +255,10 @@ def main():
     """
     module = AnsibleModule(
         argument_spec=dict(
-            keytab = dict(required=False, type='path'),
+            #keytab = dict(required=False, type='path'),
             principal = dict(default='admin'),
-            password = dict(required=False, no_log=True),
+            #password = dict(required=False, no_log=True),
+            ccache = dict(required=False, type='path'),
             fqdn = dict(required=True),
             certificates = dict(required=False, type='list'),
             sshpubkey= dict(required=False),
@@ -261,27 +266,21 @@ def main():
             random = dict(default=False, type='bool'),
             state = dict(default='present', choices=[ 'present', 'absent' ]),
         ),
-        required_one_of=[ [ 'password', 'keytab'], ],
-        mutually_exclusive=[ [ 'password', 'keytab' ], ],
+        #mutually_exclusive=[['password','keytab']],
+        #required_one_of=[['[password','keytab']],
         supports_check_mode=True,
     )
 
     principal = module.params.get('principal', 'admin')
     password = module.params.get('password')
     keytab = module.params.get('keytab')
+    ccache = module.params.get('ccache')
     fqdn = unicode(module.params.get('fqdn'))
     state = module.params.get('state')
 
     try:
-        ccache_dir = tempfile.mkdtemp(prefix='krbcc')
-        ccache_name = os.path.join(ccache_dir, 'ccache')
-
-        if keytab:
-            kinit_keytab(principal, keytab, ccache_name)
-        elif password:
-            kinit_password(principal, password, ccache_name)
+        os.environ['KRB5CCNAME']=ccache
 
-        os.environ['KRB5CCNAME'] = ccache_name
         cfg = dict(
             context='ansible_module',
             confdir=paths.ETC_IPA,
diff --git a/roles/ipaclient/tasks/install.yml b/roles/ipaclient/tasks/install.yml
index cc6ce0b000ead420de8fe9bcc9a645e05e3a2ace..a05aee3b3d56004abf29be82bfcea14c8da19c7c 100644
--- a/roles/ipaclient/tasks/install.yml
+++ b/roles/ipaclient/tasks/install.yml
@@ -1,6 +1,32 @@
 ---
 # tasks file for ipaclient
 
+# The following block is executed when using OTP to enroll IPA client
+# ie when neither ipaclient_password not ipaclient_keytab is set
+# It connects to ipaserver and add the host with --random option in order
+# to create a OneTime Password
+- block:
+  - name: Install - Get a One-Time Password for client enrollment
+    ipahost:
+      state: present
+      principal: "{{ ipaserver_principal | default('admin') }}"
+      password: "{{ ipaserver_password | default(omit) }}"
+      keytab: "{{ ipaserver_keytab | default(omit) }}"
+      fqdn: "{{ ansible_fqdn }}"
+      lifetime: "{{ ipaserver_lifetime | default(omit) }}"
+      random: True
+    register: ipahost_output
+    # If the host is already enrolled, this command will exit on error
+    # The error can be ignored
+    failed_when: ipahost_output|failed and "Password cannot be set on enrolled host" not in ipahost_output.msg
+    delegate_to: "{{ groups.ipaservers[0] }}"
+
+  - name: Install - Store the previously obtained OTP
+    set_fact:
+      ipaclient_otp: "{{ipahost_output.host.randompassword if ipahost_output.host is defined else 'dummyotp' }}"
+
+  when: ipaclient_password is not defined and ipaclient_keytab is not defined
+
 - name: Install - Install IPA client package
   package:
     name: "{{ ipaclient_package }}"
@@ -9,11 +35,11 @@
 - name: Install - Configure IPA client
   ipaclient:
     state: present
-    domain: "{{ ipaclient_domain }}"
-    realm: "{{ ipaclient_realm }}"
-    server: "{{ ipaclient_server }}"
-    principal: "{{ ipaclient_principal }}"
-    password: "{{ ipaclient_password }}"
-    keytab: "{{ ipaclient_keytab }}"
-    otp: "{{ ipaclient_otp }}"
-    extra_args: "{{ ipaclient_extraargs }}"
+    domain: "{{ ipaclient_domain | default(omit) }}"
+    realm: "{{ ipaclient_realm | default(omit) }}"
+    server: "{{ ipaclient_server | default(omit) }}"
+    principal: "{{ ipaclient_principal | default(omit) }}"
+    password: "{{ ipaclient_password | default(omit) }}"
+    keytab: "{{ ipaclient_keytab | default(omit) }}"
+    otp: "{{ ipaclient_otp | default(omit) }}"
+    extra_args: "{{ ipaclient_extraargs | default(omit) }}"
diff --git a/roles/ipaclient/vars/default.yml b/roles/ipaclient/vars/default.yml
index a0e63eaf291203dcfae8404d0e863c94679bb936..eb675d4bf87f32eb30fc56a444447e49a8fbb016 100644
--- a/roles/ipaclient/vars/default.yml
+++ b/roles/ipaclient/vars/default.yml
@@ -1,3 +1,3 @@
 # defaults file for ipaclient
-# defaults/fedora.yml
+# vars/default.yml
 ipaclient_package: freeipa-client
diff --git a/roles/ipaclient/vars/rhel.yml b/roles/ipaclient/vars/rhel.yml
index c36d57dd11817a361bea721505fd779278629013..76c7a343fbd3632906c57f2ff7ed07e4e265050f 100644
--- a/roles/ipaclient/vars/rhel.yml
+++ b/roles/ipaclient/vars/rhel.yml
@@ -1,4 +1,4 @@
 # defaults file for ipaclient
-# defaults/rhel.yml
+# vars/rhel.yml
 ipaclient_package: ipa-client
 
diff --git a/site.yml b/site.yml
index 43d5bcc4356ed3401df12dda1a10fc263fcd4136..18a4255a1a4971aaab79a5c7ddae355146e49fd4 100644
--- a/site.yml
+++ b/site.yml
@@ -3,17 +3,6 @@
   hosts: ipaclients
   become: true
 
-  pre_tasks:
-
-  - name: For OTP client registration, add client and get OTP
-    ipahost:
-      keytab: files/admin.keytab
-      fqdn: "{{ ansible_fqdn }}"
-      random: True
-    register: ipahost
-    delegate_to: "{{ groups.ipaservers[0] }}"
-
   roles:
   - role: ipaclient
     state: present
-    ipaclient_otp: "{{ ipahost.host.randompassword }}"