diff --git a/library/sssd.py b/library/sssd.py
new file mode 100644
index 0000000000000000000000000000000000000000..3c9c9dce63f6a7b51499b1d94cb460d579173207
--- /dev/null
+++ b/library/sssd.py
@@ -0,0 +1,268 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+
+# Authors:
+#   Thomas Woerner <twoerner@redhat.com>
+#
+# Based on ipa-client-install code
+#
+# Copyright (C) 2017  Red Hat
+# see file 'COPYING' for use and warranty information
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+ANSIBLE_METADATA = {
+    'metadata_version': '1.0',
+    'supported_by': 'community',
+    'status': ['preview'],
+}
+
+DOCUMENTATION = '''
+---
+module: sssd_conf
+short description: Configure sssd
+description:
+Configure sssd
+options:
+  servers:
+    description: The FQDN of the IPA servers to connect to.
+    required: false
+  domain:
+    description: The primary DNS domain of an existing IPA deployment.
+    required: false
+  realm:
+    description: The Kerberos realm of an existing IPA deployment.
+    required: true
+  hostname:
+    description: The hostname of the machine to join (FQDN).
+    required: true
+  services:
+    description: The services that should be enabled in the ssd configuration.
+    required: true
+  krb5_offline_passwords:
+    description: Whether user passwords are stored when the server is offline.
+    required: false
+  on_master:
+    description: Whether the configuration is done on the maseter or not.
+    required: false
+  primary:
+    description: Whether to use fixed server as primary IPA server.
+    required: false
+  preserve_sssd:
+    description: Preserve old SSSD configuration if possible.
+    required: false
+  permit:
+    description: Disable access rules by default, permit all access.
+    required: false
+  dns_updates:
+    description: Configures the machine to attempt dns updates when the ip address changes.
+    required: false
+  all_ip_addresses:
+    description: All routable IP addresses configured on any interface will be added to DNS.
+    required: false
+author:
+    - Thomas Woerner
+'''
+
+EXAMPLES = '''
+- name: Configure SSSD
+  sssd:
+    servers: ["server1.example.com","server2.example.com"]
+    domain: example.com
+    realm: EXAMPLE.COM
+    hostname: client1.example.com
+    services: ["ssh", "sudo"]
+    cache_credentials: yes
+    krb5_offline_passwords: yes
+'''
+
+RETURN = '''
+'''
+
+import os
+import SSSDConfig
+from ansible.module_utils.basic import AnsibleModule
+from ipalib.install import sysrestore
+from ipaplatform.paths import paths
+from ipapython.ipautil import file_exists
+from ipaclient.install.client import get_server_connection_interface, \
+    configure_nsswitch_database
+
+
+def sssd_enable_service(module, sssdconfig, service):
+    try:
+        sssdconfig.new_service(service)
+    except SSSDConfig.ServiceAlreadyExists:
+        pass
+    except SSSDConfig.ServiceNotRecognizedError:
+        module.fail_json(
+            msg="Unable to activate the %s service in SSSD config." % service)
+    sssdconfig.activate_service(service)
+
+def main():
+    module = AnsibleModule(
+        argument_spec = dict(
+            servers=dict(required=True, type='list'),
+            domain=dict(required=True),
+            realm=dict(required=True),
+            hostname=dict(required=True),
+            services=dict(required=True, type='list'),
+            krb5_offline_passwords=dict(required=False, type='bool'),
+            on_master=dict(required=False, type='bool'),
+            primary=dict(required=False, type='bool'),
+            preserve_sssd=dict(required=False, type='bool'),
+            permit=dict(required=False, type='bool'),
+            dns_updates=dict(required=False, type='bool'),
+            all_ip_addresses=dict(required=False, type='bool'),
+        ),
+        # required_one_of = ( [ '', '' ] ),
+        supports_check_mode = True,
+    )
+
+    module._ansible_debug = True
+    cli_servers = module.params.get('servers')
+    cli_domain = module.params.get('domain')
+    cli_realm = module.params.get('realm')
+    client_hostname = module.params.get('hostname')
+    services = module.params.get('services')
+    krb5_offline_passwords = module.params.get('krb5_offline_passwords')
+    on_master = module.params.get('on_master')
+    primary = module.params.get('primary')
+    preserve_sssd = module.params.get('preserve_sssd')
+    permit = module.params.get('permit')
+    dns_updates = module.params.get('dns_updates')
+    all_ip_addresses = module.params.get('all_ip_addresses')
+
+    fstore = sysrestore.FileStore(paths.IPA_CLIENT_SYSRESTORE)
+    client_domain = client_hostname[client_hostname.find(".")+1:]
+
+    try:
+        sssdconfig = SSSDConfig.SSSDConfig()
+        sssdconfig.import_config()
+    except Exception as e:
+        if os.path.exists(paths.SSSD_CONF) and preserve_sssd:
+            # SSSD config is in place but we are unable to read it
+            # In addition, we are instructed to preserve it
+            # This all means we can't use it and have to bail out
+            module.fail_json(
+                msg="SSSD config exists but cannot be parsed: %s" % str(e))
+
+        # SSSD configuration does not exist or we are not asked to preserve it,
+        # create new one
+        # We do make new SSSDConfig instance because IPAChangeConf-derived
+        # classes have no means to reset their state and ParseError exception
+        # could come due to parsing error from older version which cannot be
+        # upgraded anymore, leaving sssdconfig instance practically unusable
+        # Note that we already backed up sssd.conf before going into this
+        # routine
+        if isinstance(e, IOError):
+            pass
+        else:
+            # It was not IOError so it must have been parsing error
+            module.fail_json(msg="Unable to parse existing SSSD config.")
+
+        module.log("New SSSD config will be created")
+        sssdconfig = SSSDConfig.SSSDConfig()
+        sssdconfig.new_config()
+
+    try:
+        domain = sssdconfig.new_domain(cli_domain)
+    except SSSDConfig.DomainAlreadyExistsError:
+        module.log("Domain %s is already configured in existing SSSD "
+                   "config, creating a new one." % cli_domain)
+        sssdconfig = SSSDConfig.SSSDConfig()
+        sssdconfig.new_config()
+        domain = sssdconfig.new_domain(cli_domain)
+
+    if on_master:
+        sssd_enable_service(module, sssdconfig, 'ifp')
+
+    if (("ssh" in services and file_exists(paths.SSH_CONFIG)) or
+        ("sshd" in services and file_exists(paths.SSHD_CONFIG))):
+        sssd_enable_service(module, sssdconfig, 'ssh')
+
+    if "sudo" in services:
+        sssd_enable_service(module, sssdconfig, 'sudo')
+        configure_nsswitch_database(fstore, 'sudoers', ['sss'],
+                                    default_value=['files'])
+
+    domain.add_provider('ipa', 'id')
+
+    # add discovery domain if client domain different from server domain
+    # do not set this config in server mode (#3947)
+    if not on_master and cli_domain != client_domain:
+        domain.set_option('dns_discovery_domain', cli_domain)
+
+    if not on_master:
+        if primary:
+            domain.set_option('ipa_server', ', '.join(cli_servers))
+        else:
+            domain.set_option('ipa_server',
+                              '_srv_, %s' % ', '.join(cli_servers))
+    else:
+        domain.set_option('ipa_server_mode', 'True')
+        # the master should only use itself for Kerberos
+        domain.set_option('ipa_server', cli_servers[0])
+
+        # increase memcache timeout to 10 minutes when in server mode
+        try:
+            nss_service = sssdconfig.get_service('nss')
+        except SSSDConfig.NoServiceError:
+            nss_service = sssdconfig.new_service('nss')
+
+        nss_service.set_option('memcache_timeout', 600)
+        sssdconfig.save_service(nss_service)
+
+    domain.set_option('ipa_domain', cli_domain)
+    domain.set_option('ipa_hostname', client_hostname)
+    if cli_domain.lower() != cli_realm.lower():
+        domain.set_option('krb5_realm', cli_realm)
+
+    # Might need this if /bin/hostname doesn't return a FQDN
+    # domain.set_option('ipa_hostname', 'client.example.com')
+
+    domain.add_provider('ipa', 'auth')
+    domain.add_provider('ipa', 'chpass')
+    if not permit:
+        domain.add_provider('ipa', 'access')
+    else:
+        domain.add_provider('permit', 'access')
+
+    domain.set_option('cache_credentials', True)
+
+    # SSSD will need TLS for checking if ipaMigrationEnabled attribute is set
+    # Note that SSSD will force StartTLS because the channel is later used for
+    # authentication as well if password migration is enabled. Thus set
+    # the option unconditionally.
+    domain.set_option('ldap_tls_cacert', paths.IPA_CA_CRT)
+
+    if dns_updates:
+        domain.set_option('dyndns_update', True)
+        if all_ip_addresses:
+            domain.set_option('dyndns_iface', '*')
+        else:
+            iface = get_server_connection_interface(cli_servers[0])
+            domain.set_option('dyndns_iface', iface)
+    if krb5_offline_passwords:
+        domain.set_option('krb5_store_password_if_offline', True)
+
+    domain.set_active(True)
+
+    sssdconfig.save_domain(domain)
+    sssdconfig.write(paths.SSSD_CONF)
+
+    module.exit_json(changed=True)
+
+if __name__ == '__main__':
+    main()