diff --git a/roles/ipaclient/defaults/main.yml b/roles/ipaclient/defaults/main.yml
index 068c2167e3c7601e25aa91823ddbd72bd01a00bf..ad73831b7b217f6660cefeb57ebe603c5c2a8712 100644
--- a/roles/ipaclient/defaults/main.yml
+++ b/roles/ipaclient/defaults/main.yml
@@ -13,7 +13,7 @@ ipaclient_ssh_trust_dns: no
 ipaclient_no_ssh: no
 ipaclient_no_sshd: no
 ipaclient_no_sudo: no
-#ipaclient_no_dns_sshfp: no
+ipaclient_no_dns_sshfp: no
 ipaclient_force: no
 ipaclient_force_ntpd: no
 ipaclient_no_nisdomain: no
@@ -24,6 +24,7 @@ ipassd_permit: no
 ipassd_enable_dns_updates: no
 ipassd_no_krb5_offline_passwords: no
 ipassd_preserve_sssd: no
+ipaclient_request_cert: no
 
 ### packages ###
 ipaclient_install_packages: yes
diff --git a/roles/ipaclient/library/ipaclient_setup_nss.py b/roles/ipaclient/library/ipaclient_setup_nss.py
index 2778ce9ee4933d18dcd669f73fa89292927cc9c1..1d8de99c48bf9f3db3cb125bdf62866ec7872216 100644
--- a/roles/ipaclient/library/ipaclient_setup_nss.py
+++ b/roles/ipaclient/library/ipaclient_setup_nss.py
@@ -72,6 +72,67 @@ options:
     required: false
     type: bool
     default: no
+
+  enable_dns_updates:
+    description: Configures the machine to attempt dns updates when the ip address changes.
+    required: false
+    type: bool
+    default: no
+  all_ip_addresses:
+    description: All routable IP addresses configured on any interface will be added to DNS
+    required: false
+    type: bool
+    default: no
+  ip_addresses:
+    description: Specify IP addresses that should be added to DNS.
+    required: false
+    type: list
+    default: None
+  request_cert:
+    description: request certificate for the machine
+    required: false
+    type: bool
+    default: no
+  preserve_sssd:
+    description: Preserve old SSSD configuration if possible
+    required: false
+    type: bool
+    default: no
+  no_ssh:
+    description: Do not configure OpenSSH client
+    required: false
+    type: bool
+    default: no
+  no_sshd:
+    description: Do not configure OpenSSH server
+    required: false
+    type: bool
+    default: no
+  no_sudo:
+    description: Do not configure SSSD as data source for sudo
+    required: false
+    type: bool
+    default: no
+  fixed_primary:
+    description: Configure sssd to use fixed server as primary IPA server
+    required: false
+    type: bool
+    default: no
+  permit:
+    description: Disable access rules by default, permit all access.
+    required: false
+    type: bool
+    default: no
+  no_krb5_offline_passwords:
+    description: Configure SSSD not to store user password when the server is offline
+    required: false
+    type: bool
+    default: no
+  no_dns_sshfp:
+    description: Do not automatically create DNS SSHFP records
+    required: false
+    type: bool
+    default: no
 author:
     - Thomas Woerner
 '''
@@ -111,201 +172,328 @@ def main():
             ca_enabled=dict(required=True, type='bool'),
             mkhomedir=dict(required=False, type='bool'),
             on_master=dict(required=False, type='bool'),
+
+            enable_dns_updates=dict(required=False, type='bool'),
+            all_ip_addresses=dict(required=False, type='bool', default=False),
+            ip_addresses=dict(required=False, type='list', default=None),
+            request_cert=dict(required=False, type='bool', default=False),
+            preserve_sssd=dict(required=False, type='bool'),
+            no_ssh=dict(required=False, type='bool'),
+            no_sshd=dict(required=False, type='bool'),
+            no_sudo=dict(required=False, type='bool'),
+            fixed_primary=dict(required=False, type='bool'),
+            permit=dict(required=False, type='bool'),
+            no_krb5_offline_passwords=dict(required=False, type='bool'),
+            no_dns_sshfp=dict(required=False, type='bool', default=False),
         ),
         supports_check_mode = True,
     )
 
     module._ansible_debug = True
-    servers = module.params.get('servers')
-    realm = module.params.get('realm')
+    cli_server = module.params.get('servers')
+    cli_realm = module.params.get('realm')
     hostname = module.params.get('hostname')
-    basedn = module.params.get('basedn')
-    domain = module.params.get('domain')
-    principal = module.params.get('principal')
+    cli_basedn = module.params.get('basedn')
+    cli_domain = module.params.get('domain')
+    options.principal = module.params.get('principal')
     subject_base = module.params.get('subject_base')
     ca_enabled = module.params.get('ca_enabled')
-    mkhomedir = module.params.get('mkhomedir')
-    on_master = module.params.get('on_master')
+    options.mkhomedir = module.params.get('mkhomedir')
+    options.on_master = module.params.get('on_master')
 
     fstore = sysrestore.FileStore(paths.IPA_CLIENT_SYSRESTORE)
     statestore = sysrestore.StateFile(paths.IPA_CLIENT_SYSRESTORE)
 
     os.environ['KRB5CCNAME'] = paths.IPA_DNS_CCACHE
-    
-    options.dns_updates = False
-    options.all_ip_addresses = False
-    options.ip_addresses = None
-    options.request_cert = False
+
+    options.dns_updates = module.params.get('enable_dns_updates')
+    options.all_ip_addresses = module.params.get('all_ip_addresses')
+    options.ip_addresses = ansible_module_get_parsed_ip_addresses(module)
+    options.request_cert = module.params.get('request_cert')
     options.hostname = hostname
-    options.preserve_sssd = False
-    options.on_master = False
-    options.conf_ssh = True
-    options.conf_sshd = True
-    options.conf_sudo = True
-    options.primary = False
-    options.permit = False
-    options.krb5_offline_passwords = False
-    options.create_sshfp = True
+    options.host_name = hostname
+    options.preserve_sssd = module.params.get('preserve_sssd')
+    options.no_ssh = module.params.get('no_ssh')
+    options.conf_ssh = not options.no_ssh
+    options.no_sshd = module.params.get('no_sshd')
+    options.conf_sshd = not options.no_sshd
+    options.no_sudo = module.params.get('no_sudo')
+    options.conf_sudo = not options.no_sudo
+    options.primary = module.params.get('fixed_primary')
+    options.permit = module.params.get('permit')
+    options.no_krb5_offline_passwords = module.params.get(
+        'no_krb5_offline_passwords')
+    options.krb5_offline_passwords = not options.no_krb5_offline_passwords
+    options.no_dns_sshfp = module.params.get('no_dns_sshfp')
+    options.create_sshfp = not options.no_dns_sshfp
+    options.no_sssd = False
+    options.sssd = not options.no_sssd
+    options.no_ac = False
+
+    CCACHE_FILE = paths.IPA_DNS_CCACHE
+
+    api.bootstrap(context='cli_installer',
+                  confdir=paths.ETC_IPA,
+                  debug=False,
+                  delegate=False)
+    api.finalize()
+
+    api.Backend.rpcclient.connect()
+    try:
+        api.Backend.rpcclient.forward('ping')
+    except errors.KerberosError as e:
+        # Cannot connect to the server due to Kerberos error, trying with
+        # delegate=True
+        api.Backend.rpcclient.disconnect()
+        api.Backend.rpcclient.connect(delegate=True)
+        api.Backend.rpcclient.forward('ping')
 
     ##########################################################################
 
-    # Create IPA NSS database
     try:
-        create_ipa_nssdb()
-    except ipautil.CalledProcessError as e:
-        module.fail_json(msg="Failed to create IPA NSS database: %s" % e)
 
-    # Get CA certificates from the certificate store
-    try:
-        ca_certs = get_certs_from_ldap(servers[0], basedn, realm,
-                                       ca_enabled)
-    except errors.NoCertificateError:
-        if ca_enabled:
-            ca_subject = DN(('CN', 'Certificate Authority'), subject_base)
-        else:
-            ca_subject = None
-        ca_certs = certstore.make_compat_ca_certs(ca_certs, realm,
-                                                  ca_subject)
-    ca_certs_trust = [(c, n, certstore.key_policy_to_trust_flags(t, True, u))
-                      for (c, n, t, u) in ca_certs]
-
-    if hasattr(paths, "KDC_CA_BUNDLE_PEM"):
-        x509.write_certificate_list(
-            [c for c, n, t, u in ca_certs if t is not False],
-            paths.KDC_CA_BUNDLE_PEM)
-    if hasattr(paths, "CA_BUNDLE_PEM"):
-        x509.write_certificate_list(
-            [c for c, n, t, u in ca_certs if t is not False],
-            paths.CA_BUNDLE_PEM)
-
-    # Add the CA certificates to the IPA NSS database
-    module.debug("Adding CA certificates to the IPA NSS database.")
-    ipa_db = certdb.NSSDatabase(paths.IPA_NSSDB_DIR)
-    for cert, nickname, trust_flags in ca_certs_trust:
+        # Create IPA NSS database
         try:
-            ipa_db.add_cert(cert, nickname, trust_flags)
-        except CalledProcessError as e:
-            module.fail_json(msg="Failed to add %s to the IPA NSS database." % nickname)
-
-    # Add the CA certificates to the platform-dependant systemwide CA store
-    tasks.insert_ca_certs_into_systemwide_ca_store(ca_certs)
-
-    if not on_master:
-        client_dns(servers[0], hostname, options)
-        configure_certmonger(fstore, subject_base, realm, hostname,
-                             options, ca_enabled)
-
-    if hasattr(paths, "SSH_CONFIG_DIR"):
-        ssh_config_dir = paths.SSH_CONFIG_DIR
-    else:
-        ssh_config_dir = services.knownservices.sshd.get_config_dir()
-    update_ssh_keys(hostname, ssh_config_dir, options.create_sshfp)
-
-    try:
-        os.remove(paths.IPA_DNS_CCACHE)
-    except Exception:
-        pass
-
-    ##########################################################################
-
-    # Name Server Caching Daemon. Disable for SSSD, use otherwise
-    # (if installed)
-    nscd = services.knownservices.nscd
-    if nscd.is_installed():
-        if NUM_VERSION < 40500:
-            save_state(nscd)
-        else:
-            save_state(nscd, statestore)
+            create_ipa_nssdb()
+        except ipautil.CalledProcessError as e:
+            raise ScriptError(
+                "Failed to create IPA NSS database: %s" % e,
+                rval=CLIENT_INSTALL_ERROR)
 
+        # Get CA certificates from the certificate store
         try:
-            nscd_service_action = 'stop'
-            nscd.stop()
-        except Exception:
-            module.warn("Failed to %s the %s daemon" %
-                        (nscd_service_action, nscd.service_name))
+            ca_certs = get_certs_from_ldap(cli_server[0], cli_basedn, cli_realm,
+                                           ca_enabled)
+        except errors.NoCertificateError:
+            if ca_enabled:
+                ca_subject = DN(('CN', 'Certificate Authority'), subject_base)
+            else:
+                ca_subject = None
+            ca_certs = certstore.make_compat_ca_certs(ca_certs, cli_realm,
+                                                      ca_subject)
+        ca_certs_trust = [(c, n, certstore.key_policy_to_trust_flags(t, True, u))
+                          for (c, n, t, u) in ca_certs]
+
+        if hasattr(paths, "KDC_CA_BUNDLE_PEM"):
+            x509.write_certificate_list(
+                [c for c, n, t, u in ca_certs if t is not False],
+                paths.KDC_CA_BUNDLE_PEM,
+                # mode=0o644
+            )
+        if hasattr(paths, "CA_BUNDLE_PEM"):
+            x509.write_certificate_list(
+                [c for c, n, t, u in ca_certs if t is not False],
+                paths.CA_BUNDLE_PEM,
+                # mode=0o644
+            )
+
+        # Add the CA certificates to the IPA NSS database
+        logger.debug("Adding CA certificates to the IPA NSS database.")
+        ipa_db = certdb.NSSDatabase(paths.IPA_NSSDB_DIR)
+        for cert, nickname, trust_flags in ca_certs_trust:
+            try:
+                ipa_db.add_cert(cert, nickname, trust_flags)
+            except CalledProcessError as e:
+                raise ScriptError(
+                    "Failed to add %s to the IPA NSS database." % nickname,
+                    rval=CLIENT_INSTALL_ERROR)
+
+        # Add the CA certificates to the platform-dependant systemwide CA store
+        tasks.insert_ca_certs_into_systemwide_ca_store(ca_certs)
+
+        if not options.on_master:
+            client_dns(cli_server[0], hostname, options)
+            configure_certmonger(fstore, subject_base, cli_realm, hostname,
+                                 options, ca_enabled)
+
+        if hasattr(paths, "SSH_CONFIG_DIR"):
+            ssh_config_dir = paths.SSH_CONFIG_DIR
+        else:
+            ssh_config_dir = services.knownservices.sshd.get_config_dir()
+        update_ssh_keys(hostname, ssh_config_dir, options.create_sshfp)
 
         try:
-            nscd.disable()
+            os.remove(CCACHE_FILE)
         except Exception:
-            module.warn("Failed to disable %s daemon. Disable it manually." %
-                        nscd.service_name)
-
-    nslcd = services.knownservices.nslcd
-    if nslcd.is_installed():
-        if NUM_VERSION < 40500:
-            save_state(nslcd)
-        else:
-            save_state(nslcd, statestore)
-
-    ##########################################################################
+            pass
 
-    # Modify nsswitch/pam stack
-    tasks.modify_nsswitch_pam_stack(sssd=True,
-                                    mkhomedir=mkhomedir,
-                                    statestore=statestore)
+        argspec_save_state = inspect.getargspec(save_state)
 
-    module.log("SSSD enabled")
-
-    argspec = inspect.getargspec(services.service)
-    if len(argspec.args) > 1:
-        sssd = services.service('sssd', api)
-    else:
-        sssd = services.service('sssd')
-    try:
-        sssd.restart()
-    except CalledProcessError:
-        module.warn("SSSD service restart was unsuccessful.")
-
-    try:
-        sssd.enable()
-    except CalledProcessError as e:
-        module.warn(
-            "Failed to enable automatic startup of the SSSD daemon: "
-            "%s", e)
-
-    if configure_openldap_conf(fstore, basedn, servers):
-        module.log("Configured /etc/openldap/ldap.conf")
-    else:
-        module.log("Failed to configure /etc/openldap/ldap.conf")
-
-    # Check that nss is working properly
-    if not on_master:
-        user = principal
-        if user is None or user == "":
-            user = "admin@%s" % domain
-            module.log("Principal is not set when enrolling with OTP"
-                       "; using principal '%s' for 'getent passwd'" % user)
-        elif '@' not in user:
-            user = "%s@%s" % (user, domain)
-        n = 0
-        found = False
-        # Loop for up to 10 seconds to see if nss is working properly.
-        # It can sometimes take a few seconds to connect to the remote
-        # provider.
-        # Particulary, SSSD might take longer than 6-8 seconds.
-        while n < 10 and not found:
-            try:
-                ipautil.run([paths.GETENT if hasattr(paths, "GETENT") else "getent", "passwd", user])
-                found = True
-            except Exception as e:
-                time.sleep(1)
-                n = n + 1
-
-        if not found:
-            module.fail_json(msg="Unable to find '%s' user with 'getent "
-                             "passwd %s'!" % (user.split("@")[0], user))
-            if conf:
-                module.log("Recognized configuration: %s" % conf)
+        # Name Server Caching Daemon. Disable for SSSD, use otherwise
+        # (if installed)
+        nscd = services.knownservices.nscd
+        if nscd.is_installed():
+            if "statestore" in argspec_save_state.args:
+                save_state(nscd, statestore)
             else:
-                module.fail_json(msg=
-                                 "Unable to reliably detect "
-                                 "configuration. Check NSS setup manually.")
+                save_state(nscd)
+            nscd_service_action = None
+            try:
+                if options.sssd:
+                    nscd_service_action = 'stop'
+                    nscd.stop()
+                else:
+                    nscd_service_action = 'restart'
+                    nscd.restart()
+            except Exception:
+                logger.warning(
+                    "Failed to %s the %s daemon",
+                    nscd_service_action, nscd.service_name)
+                if not options.sssd:
+                    logger.warning(
+                        "Caching of users/groups will not be available")
 
             try:
-                hardcode_ldap_server(servers)
-            except Exception as e:
-                module.fail_json(msg="Adding hardcoded server name to "
-                                 "/etc/ldap.conf failed: %s" % str(e))
+                if options.sssd:
+                    nscd.disable()
+                else:
+                    nscd.enable()
+            except Exception:
+                if not options.sssd:
+                    logger.warning(
+                        "Failed to configure automatic startup of the %s daemon",
+                        nscd.service_name)
+                    logger.info(
+                        "Caching of users/groups will not be "
+                        "available after reboot")
+                else:
+                    logger.warning(
+                        "Failed to disable %s daemon. Disable it manually.",
+                        nscd.service_name)
+
+        else:
+            # this is optional service, just log
+            if not options.sssd:
+                logger.info(
+                    "%s daemon is not installed, skip configuration",
+                    nscd.service_name)
+
+        nslcd = services.knownservices.nslcd
+        if nslcd.is_installed():
+            if "statestore" in argspec_save_state.args:
+                save_state(nslcd, statestore)
+            else:
+                save_state(nslcd)
+
+        retcode, conf = (0, None)
+
+        if not options.no_ac:
+            # Modify nsswitch/pam stack
+            argspec = inspect.getargspec(tasks.modify_nsswitch_pam_stack)
+            if "sudo" in argspec.args:
+                tasks.modify_nsswitch_pam_stack(
+                    sssd=options.sssd,
+                    mkhomedir=options.mkhomedir,
+                    statestore=statestore,
+                    sudo=options.conf_sudo
+                )
+            else:
+                tasks.modify_nsswitch_pam_stack(
+                    sssd=options.sssd,
+                    mkhomedir=options.mkhomedir,
+                    statestore=statestore
+                )
+
+            if hasattr(paths, "AUTHSELECT") and paths.AUTHSELECT is not None:
+                # authselect is used
+                # if mkhomedir, make sure oddjobd is enabled and started
+                if options.mkhomedir:
+                    oddjobd = services.service('oddjobd', api)
+                    running = oddjobd.is_running()
+                    enabled = oddjobd.is_enabled()
+                    statestore.backup_state('oddjobd', 'running', running)
+                    statestore.backup_state('oddjobd', 'enabled', enabled)
+                    try:
+                        if not enabled:
+                            oddjobd.enable()
+                        if not running:
+                            oddjobd.start()
+                    except Exception as e:
+                        logger.critical("Unable to start oddjobd: %s", str(e))
+
+            logger.info("%s enabled", "SSSD" if options.sssd else "LDAP")
+
+            if options.sssd:
+                sssd = services.service('sssd', api)
+                try:
+                    sssd.restart()
+                except CalledProcessError:
+                    logger.warning("SSSD service restart was unsuccessful.")
+
+                try:
+                    sssd.enable()
+                except CalledProcessError as e:
+                    logger.warning(
+                        "Failed to enable automatic startup of the SSSD daemon: "
+                        "%s", e)
+
+            if not options.sssd:
+                tasks.modify_pam_to_use_krb5(statestore)
+                logger.info("Kerberos 5 enabled")
+
+            # Update non-SSSD LDAP configuration after authconfig calls as it would
+            # change its configuration otherways
+            if not options.sssd:
+                for configurer in [configure_ldap_conf, configure_nslcd_conf]:
+                    (retcode, conf, filenames) = configurer(
+                        fstore, cli_basedn, cli_realm,
+                        cli_domain, cli_server, dnsok,
+                        options, nosssd_files[configurer.__name__])
+                    if retcode:
+                        raise ScriptError(rval=CLIENT_INSTALL_ERROR)
+                    if conf:
+                        logger.info(
+                            "%s configured using configuration file(s) %s",
+                            conf, filenames)
+
+            if configure_openldap_conf(fstore, cli_basedn, cli_server):
+                logger.info("Configured /etc/openldap/ldap.conf")
+            else:
+                logger.info("Failed to configure /etc/openldap/ldap.conf")
+
+            # Check that nss is working properly
+            if not options.on_master:
+                user = options.principal
+                if user is None:
+                    user = "admin@%s" % cli_domain
+                    logger.info("Principal is not set when enrolling with OTP"
+                                "; using principal '%s' for 'getent passwd'",
+                                user)
+                elif '@' not in user:
+                    user = "%s@%s" % (user, cli_domain)
+                n = 0
+                found = False
+                # Loop for up to 10 seconds to see if nss is working properly.
+                # It can sometimes take a few seconds to connect to the remote
+                # provider.
+                # Particulary, SSSD might take longer than 6-8 seconds.
+                while n < 10 and not found:
+                    try:
+                        ipautil.run([paths.GETENT, "passwd", user])
+                        found = True
+                    except Exception as e:
+                        time.sleep(1)
+                        n = n + 1
+
+                if not found:
+                    logger.error("Unable to find '%s' user with 'getent "
+                                 "passwd %s'!", user.split("@")[0], user)
+                    if conf:
+                        logger.info("Recognized configuration: %s", conf)
+                    else:
+                        logger.error(
+                            "Unable to reliably detect "
+                            "configuration. Check NSS setup manually.")
+
+                    try:
+                        hardcode_ldap_server(cli_server)
+                    except Exception as e:
+                        logger.error(
+                            "Adding hardcoded server name to "
+                            "/etc/ldap.conf failed: %s", str(e))
+
+    except ScriptError as e:
+        module.fail_json(msg=str(e))
 
     ##########################################################################
 
diff --git a/roles/ipaclient/module_utils/ansible_ipa_client.py b/roles/ipaclient/module_utils/ansible_ipa_client.py
index 345ad1d723099577ec693e0537aee370b6b43c6d..0008fc31133ea1d734a114426c11a1b28b929803 100644
--- a/roles/ipaclient/module_utils/ansible_ipa_client.py
+++ b/roles/ipaclient/module_utils/ansible_ipa_client.py
@@ -235,3 +235,19 @@ else:
 
     raise Exception("freeipa version '%s' is too old" % VERSION)
 
+
+def ansible_module_get_parsed_ip_addresses(ansible_module,
+                                           param='ip_addresses'):
+    ip_addresses = ansible_module.params.get(param)
+    if ip_addresses is None:
+        return None
+
+    ip_addrs = [ ]
+    for ip in ip_addresses:
+        try:
+            ip_parsed = ipautil.CheckedIPAddress(ip)
+        except Exception as e:
+            ansible_module.fail_json(msg="Invalid IP Address %s: %s" % (ip, e))
+        ip_addrs.append(ip_parsed)
+    return ip_addrs
+
diff --git a/roles/ipaclient/tasks/install.yml b/roles/ipaclient/tasks/install.yml
index 57c636ee3202c5d8c938fb95d36c6d4b70d1b5bf..3d6b63fcced44f9a33acbc5cc374d4df13ac2777 100644
--- a/roles/ipaclient/tasks/install.yml
+++ b/roles/ipaclient/tasks/install.yml
@@ -267,6 +267,18 @@
         mkhomedir: "{{ ipaclient_mkhomedir }}"
         ca_enabled: "{{ result_ipaclient_api.ca_enabled }}"
         on_master: "{{ ipaclient_on_master }}"
+        enable_dns_updates: "{{ ipassd_enable_dns_updates }}"
+        all_ip_addresses: "{{ ipaclient_all_ip_addresses }}"
+        ip_addresses: "{{ ipaclient_ip_addresses | default(omit) }}"
+        request_cert: "{{ ipaclient_request_cert }}"
+        preserve_sssd: "{{ ipassd_preserve_sssd }}"
+        no_ssh: "{{ ipaclient_no_ssh }}"
+        no_sshd: "{{ ipaclient_no_sshd }}"
+        no_sudo: "{{ ipaclient_no_sudo }}"
+        fixed_primary: "{{ ipassd_fixed_primary }}"
+        permit: "{{ ipassd_permit }}"
+        no_krb5_offline_passwords: "{{ ipassd_no_krb5_offline_passwords }}"
+        no_dns_sshfp: "{{ ipaclient_no_dns_sshfp }}"
 
     - name: Install - Configure SSH and SSHD
       ipaclient_setup_ssh: