diff --git a/plugins/module_utils/ansible_freeipa_module.py b/plugins/module_utils/ansible_freeipa_module.py
index e8d62e2800d5a57a973c16560ae5195b36be991e..b5716277cc8e553596af411694115dd0d2ad8d26 100644
--- a/plugins/module_utils/ansible_freeipa_module.py
+++ b/plugins/module_utils/ansible_freeipa_module.py
@@ -44,7 +44,7 @@ else:
     import netaddr
     import gssapi
     from datetime import datetime
-    from pprint import pformat
+    from contextlib import contextmanager
 
     # ansible-freeipa requires locale to be C, IPA requires utf-8.
     os.environ["LANGUAGE"] = "C"
@@ -109,22 +109,6 @@ else:
     if six.PY3:
         unicode = str
 
-    # AnsibleModule argument specs for all modules
-    ipamodule_base_spec = dict(
-        ipaadmin_principal=dict(type="str", default="admin"),
-        ipaadmin_password=dict(type="str", required=False, no_log=True),
-    )
-
-    # Get ipamodule common vars as nonlocal
-    def get_ipamodule_base_vars(module):
-        ipaadmin_principal = module_params_get(module, "ipaadmin_principal")
-        ipaadmin_password = module_params_get(module, "ipaadmin_password")
-
-        return dict(
-            ipaadmin_principal=ipaadmin_principal,
-            ipaadmin_password=ipaadmin_password,
-        )
-
     def valid_creds(module, principal):  # noqa
         """Get valid credentials matching the princial, try GSSAPI first."""
         if "KRB5CCNAME" in os.environ:
@@ -254,29 +238,6 @@ else:
         return operation(version.parse(VERSION),
                          version.parse(requested_version))
 
-    def execute_api_command(module, principal, password, command, name, args):
-        """
-        Execute an API command.
-
-        Get KRB ticket if not already there, initialize api, connect,
-        execute command and destroy ticket again if it has been created also.
-        """
-        ccache_dir = None
-        ccache_name = None
-        try:
-            if not valid_creds(module, principal):
-                ccache_dir, ccache_name = temp_kinit(principal, password)
-            api_connect()
-
-            return api_command(module, command, name, args)
-        except Exception as e:
-            module.fail_json(msg=str(e))
-
-        finally:
-            temp_kdestroy(ccache_dir, ccache_name)
-        # fix pylint inconsistent return
-        return None
-
     def date_format(value):
         accepted_date_formats = [
             LDAP_GENERALIZED_TIME_FORMAT,  # generalized time
@@ -568,7 +529,260 @@ else:
         def __getattr__(self, name):
             return self.get(name)
 
-    class FreeIPABaseModule(AnsibleModule):
+    class IPAAnsibleModule(AnsibleModule):
+        """
+        IPA Ansible Module.
+
+        This class is an extended version of the Ansible Module that provides
+        IPA specific methods to simplify module generation.
+
+        Simple example:
+
+        from ansible.module_utils.ansible_freeipa_module import \
+            IPAAnsibleModule
+
+        def main():
+            ansible_module = IPAAnsibleModule(
+                argument_spec=dict(
+                      name=dict(type="str", aliases=["cn"], default=None),
+                      state=dict(type="str", default="present",
+                                 choices=["present", "absent"]),
+                ),
+            )
+
+            # Get parameters
+            name = ansible_module.params_get("name")
+            state = ansible_module.params_get("state")
+
+            # Connect to IPA API
+            with ansible_module.ipa_connect():
+
+                # Execute command
+                if state == "present":
+                    ansible_module.ipa_command(["command_add", name, {}])
+                else:
+                    ansible_module.ipa_command(["command_del", name, {}])
+
+            # Done
+
+            ansible_module.exit_json(changed=True)
+
+        if __name__ == "__main__":
+            main()
+
+        """
+
+        # IPAAnsibleModule argument specs used for all modules
+        ipa_module_base_spec = dict(
+            ipaadmin_principal=dict(type="str", default="admin"),
+            ipaadmin_password=dict(type="str", required=False, no_log=True),
+        )
+
+        def __init__(self, *args, **kwargs):
+            # Extend argument_spec with ipa_module_base_spec
+            if "argument_spec" in kwargs:
+                _spec = kwargs["argument_spec"]
+                _spec.update(self.ipa_module_base_spec)
+                kwargs["argument_spec"] = _spec
+
+            # pylint: disable=super-with-arguments
+            super(IPAAnsibleModule, self).__init__(*args, **kwargs)
+
+        @contextmanager
+        def ipa_connect(self, context=None):
+            """
+            Create a context with a connection to IPA API.
+
+            Parameters
+            ----------
+            context: string
+                An optional parameter defining which context API
+                commands will be executed.
+
+            """
+            # ipaadmin vars
+            ipaadmin_principal = self.params_get("ipaadmin_principal")
+            ipaadmin_password = self.params_get("ipaadmin_password")
+
+            ccache_dir = None
+            ccache_name = None
+            try:
+                if not valid_creds(self, ipaadmin_principal):
+                    ccache_dir, ccache_name = temp_kinit(
+                        ipaadmin_principal, ipaadmin_password)
+                api_connect(context)
+            except Exception as e:
+                self.fail_json(msg=str(e))
+            else:
+                try:
+                    yield ccache_name
+                except Exception as e:
+                    self.fail_json(msg=str(e))
+                finally:
+                    temp_kdestroy(ccache_dir, ccache_name)
+
+        def params_get(self, name):
+            """
+            Retrieve value set for module parameter.
+
+            Parameters
+            ----------
+            name: string
+                The name of the parameter to retrieve.
+
+            """
+            return module_params_get(self, name)
+
+        def ipa_command(self, command, name, args):
+            """
+            Execute an IPA API command with a required `name` argument.
+
+            Parameters
+            ----------
+            command: string
+                The IPA API command to execute.
+            name: string
+                The name parameter to pass to the command.
+            args: dict
+                The parameters to pass to the command.
+
+            """
+            return api_command(self, command, name, args)
+
+        def ipa_command_no_name(self, command, args):
+            """
+            Execute an IPA API command requiring no `name` argument.
+
+            Parameters
+            ----------
+            command: string
+                The IPA API command to execute.
+            args: dict
+                The parameters to pass to the command.
+
+            """
+            return api_command_no_name(self, command, args)
+
+        @staticmethod
+        def ipa_get_domain():
+            """Retrieve IPA API domain."""
+            return api_get_domain()
+
+        @staticmethod
+        def ipa_get_realm():
+            """Retrieve IPA API realm."""
+            return api_get_realm()
+
+        @staticmethod
+        def ipa_command_exists(command):
+            """
+            Check if IPA command is supported.
+
+            Parameters
+            ----------
+            command: string
+                The IPA API command to verify.
+
+            """
+            return api_check_command(command)
+
+        @staticmethod
+        def ipa_command_param_exists(command, name):
+            """
+            Check if IPA command support a specific parameter.
+
+            Parameters
+            ----------
+            command: string
+                The IPA API command to test.
+            name: string
+                The parameter name to verify.
+
+            """
+            return api_check_param(command, name)
+
+        @staticmethod
+        def ipa_check_version(oper, requested_version):
+            """
+            Compare available IPA version.
+
+            Parameters
+            ----------
+            oper: string
+                The relational operator to use.
+            requested_version: string
+                The version to compare to.
+
+            """
+            return api_check_ipa_version(oper, requested_version)
+
+        def execute_ipa_commands(self, commands, handle_result=None,
+                                 **handle_result_user_args):
+            """
+            Execute IPA API commands from command list.
+
+            Parameters
+            ----------
+            commands: list of string tuple
+                The list of commands in the form (name, command and args)
+                For commands that do not require a 'name', None needs be
+                used.
+            handle_result: function
+                The user function to handle results of the single commands
+            handle_result_user_args: dict (user args mapping)
+                The user args to pass to handle_result function
+
+            Example (ipauser module):
+
+            def handle_result(module, result, command, name, args, exit_args):
+                if "random" in args and command in ["user_add", "user_mod"] \
+                   and "randompassword" in result["result"]:
+                    exit_args.setdefault(name, {})["randompassword"] = \
+                        result["result"]["randompassword"]
+
+            exit_args = {}
+            changed = module.execute_ipa_commands(commands, handle_result,
+                                                  exit_args=exit_args)
+
+            if len(names) == 1:
+                ansible_module.exit_json(changed=changed,
+                                         user=exit_args[names[0]])
+            else:
+                ansible_module.exit_json(changed=changed, user=exit_args)
+
+            """
+            # No commands, report no changes
+            if commands is None:
+                return False
+
+            # In check_mode return if there are commands to do
+            if self.check_mode:
+                return len(commands) > 0
+
+            changed = False
+            for name, command, args in commands:
+                try:
+                    if name is None:
+                        result = self.ipa_command_no_name(command, args)
+                    else:
+                        result = self.ipa_command(command, name, args)
+
+                    if "completed" in result:
+                        if result["completed"] > 0:
+                            changed = True
+                    else:
+                        changed = True
+
+                    if handle_result is not None:
+                        handle_result(self, result, command, name, args,
+                                      **handle_result_user_args)
+
+                except Exception as e:
+                    self.fail_json(msg="%s: %s: %s" % (command, name, str(e)))
+
+            return changed
+
+    class FreeIPABaseModule(IPAAnsibleModule):
         """
         Base class for FreeIPA Ansible modules.
 
@@ -641,10 +855,6 @@ else:
             # pylint: disable=super-with-arguments
             super(FreeIPABaseModule, self).__init__(*args, **kwargs)
 
-            # Attributes to store kerberos credentials (if needed)
-            self.ccache_dir = None
-            self.ccache_name = None
-
             # Status of an execution. Will be changed to True
             #   if something is actually peformed.
             self.changed = False
@@ -723,64 +933,6 @@ else:
             """Define commands that will be run in IPA server."""
             raise NotImplementedError
 
-        def api_command(self, command, name=None, args=None):
-            """Execute a single command in IPA server."""
-            if args is None:
-                args = {}
-
-            if name is None:
-                return api_command_no_name(self, command, args)
-
-            return api_command(self, command, name, args)
-
-        def __enter__(self):
-            """
-            Connect to IPA server.
-
-            Check the there are working Kerberos credentials to connect to
-            IPA server. If there are not we perform a temporary kinit
-            that will be terminated when exiting the context.
-
-            If the connection fails ``ipa_connected`` attribute will be set
-            to False.
-            """
-            principal = self.ipa_params.ipaadmin_principal
-            password = self.ipa_params.ipaadmin_password
-
-            try:
-                if not valid_creds(self, principal):
-                    self.ccache_dir, self.ccache_name = temp_kinit(
-                        principal, password,
-                    )
-
-                api_connect()
-
-            except Exception as excpt:
-                self.fail_json(msg=str(excpt))
-            else:
-                self.ipa_connected = True
-
-            return self
-
-        def __exit__(self, exc_type, exc_val, exc_tb):
-            """
-            Terminate a connection with the IPA server.
-
-            Deal with exceptions, destroy temporary kinit credentials and
-            exit the module with proper arguments.
-
-            """
-            # TODO: shouldn't we also disconnect from api backend?
-            temp_kdestroy(self.ccache_dir, self.ccache_name)
-
-            if exc_type == SystemExit:
-                raise
-
-            if exc_val:
-                self.fail_json(msg=str(exc_val))
-
-            self.exit_json(changed=self.changed, **self.exit_args)
-
         def get_command_errors(self, command, result):
             """Look for erros into command results."""
             # Get all errors
@@ -814,7 +966,7 @@ else:
 
             for name, command, args in self.ipa_commands:
                 try:
-                    result = self.api_command(command, name, args)
+                    result = self.ipa_command(command, name, args)
                 except Exception as excpt:
                     self.fail_json(msg="%s: %s: %s" % (command, name,
                                                        str(excpt)))
@@ -846,16 +998,10 @@ else:
             equal = compare_args_ipa(self, command_args, ipa_attrs)
             return not equal
 
-        def pdebug(self, value):
-            """Debug with pretty formatting."""
-            self.debug(pformat(value))
-
         def ipa_run(self):
             """Execute module actions."""
-            with self:
-                if not self.ipa_connected:
-                    return
-
+            with self.ipa_connect():
                 self.check_ipa_params()
                 self.define_ipa_commands()
                 self._run_ipa_commands()
+            self.exit_json(changed=self.changed, **self.exit_args)
diff --git a/plugins/modules/ipaautomember.py b/plugins/modules/ipaautomember.py
index aeaf7173ead46e859ea281fac00f0654fbef48a0..49c8d9a28170db8360296645740c1bef4056eca6 100644
--- a/plugins/modules/ipaautomember.py
+++ b/plugins/modules/ipaautomember.py
@@ -22,14 +22,6 @@
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
 
-from ansible.module_utils._text import to_text
-from ansible.module_utils.ansible_freeipa_module import (
-    api_command, api_command_no_name, api_connect, compare_args_ipa,
-    gen_add_del_lists, temp_kdestroy, temp_kinit, valid_creds,
-    ipalib_errors
-)
-from ansible.module_utils.basic import AnsibleModule
-
 ANSIBLE_METADATA = {
     "metadata_version": "1.0",
     "supported_by": "community",
@@ -42,13 +34,9 @@ DOCUMENTATION = """
 module: ipaautomember
 short description: Add and delete FreeIPA Auto Membership Rules.
 description: Add, modify and delete an IPA Auto Membership Rules.
+extends_documentation_fragment:
+  - ipamodule_base_docs
 options:
-  ipaadmin_principal:
-    description: The admin principal
-    default: admin
-  ipaadmin_password:
-    description: The admin password
-    required: false
   name:
     description: The automember rule
     required: true
@@ -138,14 +126,19 @@ RETURN = """
 """
 
 
+from ansible.module_utils.ansible_freeipa_module import (
+    IPAAnsibleModule, compare_args_ipa, gen_add_del_lists, ipalib_errors
+)
+
+
 def find_automember(module, name, grouping):
     _args = {
         "all": True,
-        "type": to_text(grouping)
+        "type": grouping
     }
 
     try:
-        _result = api_command(module, "automember_show", to_text(name), _args)
+        _result = module.ipa_command("automember_show", name, _args)
     except ipalib_errors.NotFound:
         return None
     return _result["result"]
@@ -157,13 +150,13 @@ def gen_condition_args(grouping,
                        exclusiveregex=None):
     _args = {}
     if grouping is not None:
-        _args['type'] = to_text(grouping)
+        _args['type'] = grouping
     if key is not None:
-        _args['key'] = to_text(key)
+        _args['key'] = key
     if inclusiveregex is not None:
-        _args['automemberinclusiveregex'] = to_text(inclusiveregex)
+        _args['automemberinclusiveregex'] = inclusiveregex
     if exclusiveregex is not None:
-        _args['automemberexclusiveregex'] = to_text(exclusiveregex)
+        _args['automemberexclusiveregex'] = exclusiveregex
 
     return _args
 
@@ -171,9 +164,9 @@ def gen_condition_args(grouping,
 def gen_args(description, grouping):
     _args = {}
     if description is not None:
-        _args["description"] = to_text(description)
+        _args["description"] = description
     if grouping is not None:
-        _args['type'] = to_text(grouping)
+        _args['type'] = grouping
 
     return _args
 
@@ -195,12 +188,9 @@ def check_condition_keys(ansible_module, conditions, aciattrs):
 
 
 def main():
-    ansible_module = AnsibleModule(
+    ansible_module = IPAAnsibleModule(
         argument_spec=dict(
             # general
-            ipaadmin_principal=dict(type="str", default="admin"),
-            ipaadmin_password=dict(type="str", required=False, no_log=True),
-
             inclusive=dict(type="list",
                            aliases=["automemberinclusiveregex"], default=None,
                            options=dict(
@@ -235,27 +225,25 @@ def main():
     # Get parameters
 
     # general
-    ipaadmin_principal = ansible_module.params.get("ipaadmin_principal")
-    ipaadmin_password = ansible_module.params.get("ipaadmin_password")
-    names = ansible_module.params.get("name")
+    names = ansible_module.params_get("name")
 
     # present
-    description = ansible_module.params.get("description")
+    description = ansible_module.params_get("description")
 
     # conditions
-    inclusive = ansible_module.params.get("inclusive")
-    exclusive = ansible_module.params.get("exclusive")
+    inclusive = ansible_module.params_get("inclusive")
+    exclusive = ansible_module.params_get("exclusive")
 
     # action
-    action = ansible_module.params.get("action")
+    action = ansible_module.params_get("action")
     # state
-    state = ansible_module.params.get("state")
+    state = ansible_module.params_get("state")
 
     # grouping/type
-    automember_type = ansible_module.params.get("automember_type")
+    automember_type = ansible_module.params_get("automember_type")
 
-    rebuild_users = ansible_module.params.get("users")
-    rebuild_hosts = ansible_module.params.get("hosts")
+    rebuild_users = ansible_module.params_get("users")
+    rebuild_hosts = ansible_module.params_get("hosts")
 
     if (rebuild_hosts or rebuild_users) and state != "rebuild":
         ansible_module.fail_json(
@@ -267,15 +255,9 @@ def main():
     # Init
     changed = False
     exit_args = {}
-    ccache_dir = None
-    ccache_name = None
     res_find = None
 
-    try:
-        if not valid_creds(ansible_module, ipaadmin_principal):
-            ccache_dir, ccache_name = temp_kinit(ipaadmin_principal,
-                                                 ipaadmin_password)
-        api_connect()
+    with ansible_module.ipa_connect():
 
         commands = []
 
@@ -287,16 +269,16 @@ def main():
             if inclusive is not None or exclusive is not None:
                 # automember_type is either "group" or "hostgorup"
                 if automember_type == "group":
-                    _type = "user"
+                    _type = u"user"
                 elif automember_type == "hostgroup":
-                    _type = "host"
+                    _type = u"host"
                 else:
                     ansible_module.fail_json(
                         msg="Bad automember type '%s'" % automember_type)
 
                 try:
-                    aciattrs = api_command(
-                        ansible_module, "json_metadata", to_text(_type), {}
+                    aciattrs = ansible_module.ipa_command(
+                        "json_metadata", _type, {}
                     )['objects'][_type]['aciattrs']
                 except Exception as ex:
                     ansible_module.fail_json(
@@ -372,7 +354,7 @@ def main():
                 if action == "automember":
                     if res_find is not None:
                         commands.append([name, 'automember_del',
-                                         {'type': to_text(automember_type)}])
+                                         {'type': automember_type}])
 
                 elif action == "member":
                     if res_find is None:
@@ -400,17 +382,13 @@ def main():
             elif state == "rebuild":
                 if automember_type:
                     commands.append([None, 'automember_rebuild',
-                                     {"type": to_text(automember_type)}])
+                                     {"type": automember_type}])
                 if rebuild_users:
                     commands.append([None, 'automember_rebuild',
-                                    {"users": [
-                                        to_text(_u)
-                                        for _u in rebuild_users]}])
+                                    {"users": rebuild_users}])
                 if rebuild_hosts:
                     commands.append([None, 'automember_rebuild',
-                                    {"hosts": [
-                                        to_text(_h)
-                                        for _h in rebuild_hosts]}])
+                                    {"hosts": rebuild_hosts}])
 
         # Check mode exit
         if ansible_module.check_mode:
@@ -419,10 +397,9 @@ def main():
         for name, command, args in commands:
             try:
                 if name is None:
-                    result = api_command_no_name(ansible_module, command, args)
+                    result = ansible_module.ipa_command_no_name(command, args)
                 else:
-                    result = api_command(ansible_module, command,
-                                         to_text(name), args)
+                    result = ansible_module.ipa_command(command, name, args)
 
                 if "completed" in result:
                     if result["completed"] > 0:
@@ -440,12 +417,6 @@ def main():
             # as exceptions. Therefore the error section is not here as
             # in other modules.
 
-    except Exception as e:
-        ansible_module.fail_json(msg=str(e))
-
-    finally:
-        temp_kdestroy(ccache_dir, ccache_name)
-
     # Done
     ansible_module.exit_json(changed=changed, **exit_args)
 
diff --git a/plugins/modules/ipaautomountlocation.py b/plugins/modules/ipaautomountlocation.py
index 44748c0ed5ae1051fb2ebd5aff3e24df33b522c4..f1925ddc82c9d197e20ab1767b5139c0d998e4ba 100644
--- a/plugins/modules/ipaautomountlocation.py
+++ b/plugins/modules/ipaautomountlocation.py
@@ -33,13 +33,9 @@ author: chris procter
 short_description: Manage FreeIPA autommount locations
 description:
 - Add and delete an IPA automount location
+extends_documentation_fragment:
+  - ipamodule_base_docs
 options:
-  ipaadmin_principal:
-    description: The admin principal
-    default: admin
-  ipaadmin_password:
-    description: The admin password
-    required: false
   name:
     description: The automount location to be managed
     required: true
@@ -79,9 +75,9 @@ class AutomountLocation(FreeIPABaseModule):
 
     def get_location(self, location):
         try:
-            response = self.api_command("automountlocation_show",
-                                        location,
-                                        {})
+            response = self.ipa_command(
+                "automountlocation_show", location, {}
+            )
         except ipalib_errors.NotFound:
             return None
         else:
diff --git a/plugins/modules/ipaconfig.py b/plugins/modules/ipaconfig.py
index dad9bd9df45d665aea6b6766417ec03573aa16fd..dc27b3411e85f009c0ab4f5b7b7479975d726c61 100644
--- a/plugins/modules/ipaconfig.py
+++ b/plugins/modules/ipaconfig.py
@@ -34,13 +34,9 @@ author: chris procter
 short_description: Modify IPA global config options
 description:
 - Modify IPA global config options
+extends_documentation_fragment:
+  - ipamodule_base_docs
 options:
-    ipaadmin_principal:
-        description: The admin principal
-        default: admin
-    ipaadmin_password:
-        description: The admin password
-        required: false
     maxusername:
         description: Set the maximum username length between 1-255
         required: false
@@ -159,7 +155,7 @@ EXAMPLES = '''
   tasks:
     - name: return current values of the global configuration options
       ipaconfig:
-        ipaadmin_password: password
+        ipaadmin_password: SomeADMINpassword
       register: result
     - name: display default login shell
       debug:
@@ -167,7 +163,7 @@ EXAMPLES = '''
 
     - name: set defaultshell and maxusername
       ipaconfig:
-        ipaadmin_password: password
+        ipaadmin_password: SomeADMINpassword
         defaultshell: /bin/bash
         maxusername: 64
 '''
@@ -251,14 +247,12 @@ config:
 '''
 
 
-from ansible.module_utils.basic import AnsibleModule
-from ansible.module_utils.ansible_freeipa_module import temp_kinit, \
-    temp_kdestroy, valid_creds, api_connect, api_command_no_name, \
-    compare_args_ipa, module_params_get, ipalib_errors
+from ansible.module_utils.ansible_freeipa_module import \
+    IPAAnsibleModule, compare_args_ipa, ipalib_errors
 
 
 def config_show(module):
-    _result = api_command_no_name(module, "config_show", {})
+    _result = module.ipa_command_no_name("config_show", {})
 
     return _result["result"]
 
@@ -270,11 +264,9 @@ def gen_args(params):
 
 
 def main():
-    ansible_module = AnsibleModule(
+    ansible_module = IPAAnsibleModule(
         argument_spec=dict(
             # general
-            ipaadmin_principal=dict(type="str", default="admin"),
-            ipaadmin_password=dict(type="str", required=False, no_log=True),
             maxusername=dict(type="int", required=False,
                              aliases=['ipamaxusernamelength']),
             maxhostname=dict(type="int", required=False,
@@ -334,11 +326,6 @@ def main():
     # Get parameters
 
     # general
-    ipaadmin_principal = module_params_get(ansible_module,
-                                           "ipaadmin_principal")
-    ipaadmin_password = module_params_get(ansible_module,
-                                          "ipaadmin_password")
-
     field_map = {
         "maxusername": "ipamaxusernamelength",
         "maxhostname": "ipamaxhostnamelength",
@@ -366,7 +353,7 @@ def main():
 
     params = {}
     for x in field_map:
-        val = module_params_get(ansible_module, x)
+        val = ansible_module.params_get(x)
 
         if val is not None:
             params[field_map.get(x, x)] = val
@@ -407,14 +394,11 @@ def main():
 
     changed = False
     exit_args = {}
-    ccache_dir = None
-    ccache_name = None
     res_show = None
-    try:
-        if not valid_creds(ansible_module, ipaadmin_principal):
-            ccache_dir, ccache_name = temp_kinit(ipaadmin_principal,
-                                                 ipaadmin_password)
-        api_connect()
+
+    # Connect to IPA API
+    with ansible_module.ipa_connect():
+
         if params:
             res_show = config_show(ansible_module)
             params = {
@@ -425,10 +409,13 @@ def main():
                and not compare_args_ipa(ansible_module, params, res_show):
                 changed = True
                 if not ansible_module.check_mode:
-                    api_command_no_name(ansible_module, "config_mod", params)
-
+                    try:
+                        ansible_module.ipa_command_no_name("config_mod",
+                                                           params)
+                    except ipalib_errors.EmptyModlist:
+                        changed = False
         else:
-            rawresult = api_command_no_name(ansible_module, "config_show", {})
+            rawresult = ansible_module.ipa_command_no_name("config_show", {})
             result = rawresult['result']
             del result['dn']
             for key, value in result.items():
@@ -460,13 +447,6 @@ def main():
                         exit_args[k] = (value[0] == "TRUE")
                     else:
                         exit_args[k] = value
-    except ipalib_errors.EmptyModlist:
-        changed = False
-    except Exception as e:
-        ansible_module.fail_json(msg="%s %s" % (params, str(e)))
-
-    finally:
-        temp_kdestroy(ccache_dir, ccache_name)
 
     # Done
     ansible_module.exit_json(changed=changed, config=exit_args)
diff --git a/plugins/modules/ipadelegation.py b/plugins/modules/ipadelegation.py
index a5f5469940e32f0f50a4ccf433c923757b02f3bf..846e1277ecc92742c888e3379fe4085f7f38bfbf 100644
--- a/plugins/modules/ipadelegation.py
+++ b/plugins/modules/ipadelegation.py
@@ -31,13 +31,9 @@ DOCUMENTATION = """
 module: ipadelegation
 short description: Manage FreeIPA delegations
 description: Manage FreeIPA delegations and delegation attributes
+extends_documentation_fragment:
+  - ipamodule_base_docs
 options:
-  ipaadmin_principal:
-    description: The admin principal.
-    default: admin
-  ipaadmin_password:
-    description: The admin password.
-    required: false
   name:
     description: The list of delegation name strings.
     required: true
@@ -112,21 +108,14 @@ RETURN = """
 """
 
 
-from ansible.module_utils.basic import AnsibleModule
 from ansible.module_utils.ansible_freeipa_module import \
-    temp_kinit, temp_kdestroy, valid_creds, api_connect, api_command, \
-    compare_args_ipa, module_params_get
-import six
-
-
-if six.PY3:
-    unicode = str
+    IPAAnsibleModule, compare_args_ipa
 
 
 def find_delegation(module, name):
     """Find if a delegation with the given name already exist."""
     try:
-        _result = api_command(module, "delegation_show", name, {"all": True})
+        _result = module.ipa_command("delegation_show", name, {"all": True})
     except Exception:  # pylint: disable=broad-except
         # An exception is raised if delegation name is not found.
         return None
@@ -148,12 +137,9 @@ def gen_args(permission, attribute, membergroup, group):
 
 
 def main():
-    ansible_module = AnsibleModule(
+    ansible_module = IPAAnsibleModule(
         argument_spec=dict(
             # general
-            ipaadmin_principal=dict(type="str", default="admin"),
-            ipaadmin_password=dict(type="str", required=False, no_log=True),
-
             name=dict(type="list", aliases=["aciname"], default=None,
                       required=True),
             # present
@@ -177,19 +163,16 @@ def main():
     # Get parameters
 
     # general
-    ipaadmin_principal = module_params_get(ansible_module,
-                                           "ipaadmin_principal")
-    ipaadmin_password = module_params_get(ansible_module, "ipaadmin_password")
-    names = module_params_get(ansible_module, "name")
+    names = ansible_module.params_get("name")
 
     # present
-    permission = module_params_get(ansible_module, "permission")
-    attribute = module_params_get(ansible_module, "attribute")
-    membergroup = module_params_get(ansible_module, "membergroup")
-    group = module_params_get(ansible_module, "group")
-    action = module_params_get(ansible_module, "action")
+    permission = ansible_module.params_get("permission")
+    attribute = ansible_module.params_get("attribute")
+    membergroup = ansible_module.params_get("membergroup")
+    group = ansible_module.params_get("group")
+    action = ansible_module.params_get("action")
     # state
-    state = module_params_get(ansible_module, "state")
+    state = ansible_module.params_get("state")
 
     # Check parameters
 
@@ -236,13 +219,9 @@ def main():
 
     changed = False
     exit_args = {}
-    ccache_dir = None
-    ccache_name = None
-    try:
-        if not valid_creds(ansible_module, ipaadmin_principal):
-            ccache_dir, ccache_name = temp_kinit(ipaadmin_principal,
-                                                 ipaadmin_password)
-        api_connect()
+
+    # Connect to IPA API
+    with ansible_module.ipa_connect():
 
         commands = []
         for name in names:
@@ -318,8 +297,7 @@ def main():
 
         for name, command, args in commands:
             try:
-                result = api_command(ansible_module, command, name,
-                                     args)
+                result = ansible_module.ipa_command(command, name, args)
                 if "completed" in result:
                     if result["completed"] > 0:
                         changed = True
@@ -329,12 +307,6 @@ def main():
                 ansible_module.fail_json(msg="%s: %s: %s" % (command, name,
                                                              str(e)))
 
-    except Exception as e:
-        ansible_module.fail_json(msg=str(e))
-
-    finally:
-        temp_kdestroy(ccache_dir, ccache_name)
-
     # Done
 
     ansible_module.exit_json(changed=changed, **exit_args)
diff --git a/plugins/modules/ipadnsconfig.py b/plugins/modules/ipadnsconfig.py
index 8d705807bc0d8ca057c7cede0153ee40e4da1bc7..843237f02cb24bece960d6d61405a8c9489b7c24 100644
--- a/plugins/modules/ipadnsconfig.py
+++ b/plugins/modules/ipadnsconfig.py
@@ -32,14 +32,9 @@ DOCUMENTATION = """
 module: ipadnsconfig
 short description: Manage FreeIPA dnsconfig
 description: Manage FreeIPA dnsconfig
+extends_documentation_fragment:
+  - ipamodule_base_docs
 options:
-  ipaadmin_principal:
-    description: The admin principal
-    default: admin
-  ipaadmin_password:
-    description: The admin password
-    required: false
-
   forwarders:
     description: The list of global DNS forwarders.
     required: false
@@ -70,6 +65,7 @@ options:
 EXAMPLES = """
 # Ensure global DNS forward configuration, allowing PTR record synchronization.
 - ipadnsconfig:
+    ipaadmin_password: SomeADMINpassword
     forwarders:
       - ip_address: 8.8.4.4
       - ip_address: 2001:4860:4860::8888
@@ -79,6 +75,7 @@ EXAMPLES = """
 
 # Ensure forwarder is absent.
 - ipadnsconfig:
+    ipaadmin_password: SomeADMINpassword
     forwarders:
       - ip_address: 2001:4860:4860::8888
         port: 53
@@ -86,21 +83,20 @@ EXAMPLES = """
 
 # Disable PTR record synchronization.
 - ipadnsconfig:
+    ipaadmin_password: SomeADMINpassword
     allow_sync_ptr: no
 
 # Disable global forwarders.
 - ipadnsconfig:
+    ipaadmin_password: SomeADMINpassword
     forward_policy: none
 """
 
 RETURN = """
 """
 
-from ansible.module_utils.basic import AnsibleModule
-from ansible.module_utils.ansible_freeipa_module import temp_kinit, \
-    temp_kdestroy, valid_creds, api_connect, \
-    api_command_no_name, compare_args_ipa, module_params_get, \
-    is_ipv4_addr, is_ipv6_addr
+from ansible.module_utils.ansible_freeipa_module import \
+    IPAAnsibleModule, compare_args_ipa, is_ipv4_addr, is_ipv6_addr
 
 
 def find_dnsconfig(module):
@@ -108,7 +104,7 @@ def find_dnsconfig(module):
         "all": True,
     }
 
-    _result = api_command_no_name(module, "dnsconfig_show", _args)
+    _result = module.ipa_command_no_name("dnsconfig_show", _args)
 
     if "result" in _result:
         if _result["result"].get('idnsforwarders', None) is None:
@@ -170,12 +166,8 @@ def main():
        port=dict(type=int, required=False, default=None)
     )
 
-    ansible_module = AnsibleModule(
+    ansible_module = IPAAnsibleModule(
        argument_spec=dict(
-           # general
-           ipaadmin_principal=dict(type='str', default='admin'),
-           ipaadmin_password=dict(type='str', no_log=True),
-
            # dnsconfig
            forwarders=dict(type='list', default=None, required=False,
                            options=dict(**forwarder_spec)),
@@ -192,17 +184,12 @@ def main():
 
     ansible_module._ansible_debug = True
 
-    # general
-    ipaadmin_principal = module_params_get(ansible_module,
-                                           "ipaadmin_principal")
-    ipaadmin_password = module_params_get(ansible_module,
-                                          "ipaadmin_password")
+    # dnsconfig
+    forwarders = ansible_module.params_get('forwarders') or []
+    forward_policy = ansible_module.params_get('forward_policy')
+    allow_sync_ptr = ansible_module.params_get('allow_sync_ptr')
 
-    forwarders = module_params_get(ansible_module, 'forwarders') or []
-    forward_policy = module_params_get(ansible_module, 'forward_policy')
-    allow_sync_ptr = module_params_get(ansible_module, 'allow_sync_ptr')
-
-    state = module_params_get(ansible_module, 'state')
+    state = ansible_module.params_get('state')
 
     # Check parameters.
     invalid = []
@@ -218,13 +205,9 @@ def main():
     # Init
 
     changed = False
-    ccache_dir = None
-    ccache_name = None
-    try:
-        if not valid_creds(ansible_module, ipaadmin_principal):
-            ccache_dir, ccache_name = temp_kinit(ipaadmin_principal,
-                                                 ipaadmin_password)
-        api_connect()
+
+    # Connect to IPA API
+    with ansible_module.ipa_connect():
 
         res_find = find_dnsconfig(ansible_module)
         args = gen_args(ansible_module, state, res_find, forwarders,
@@ -234,7 +217,7 @@ def main():
         if not compare_args_ipa(ansible_module, args, res_find):
             try:
                 if not ansible_module.check_mode:
-                    api_command_no_name(ansible_module, 'dnsconfig_mod', args)
+                    ansible_module.ipa_command_no_name('dnsconfig_mod', args)
                 # If command did not fail, something changed.
                 changed = True
 
@@ -242,12 +225,6 @@ def main():
                 msg = str(e)
                 ansible_module.fail_json(msg="dnsconfig_mod: %s" % msg)
 
-    except Exception as e:
-        ansible_module.fail_json(msg=str(e))
-
-    finally:
-        temp_kdestroy(ccache_dir, ccache_name)
-
     # Done
 
     ansible_module.exit_json(changed=changed)
diff --git a/plugins/modules/ipadnsforwardzone.py b/plugins/modules/ipadnsforwardzone.py
index 1c2a22e53d84e9a0f4e4b677222a1af95e2039cb..f6d4a24d635e8da4bbded7c320105447563f19ee 100644
--- a/plugins/modules/ipadnsforwardzone.py
+++ b/plugins/modules/ipadnsforwardzone.py
@@ -34,13 +34,9 @@ author: chris procter
 short_description: Manage FreeIPA DNS Forwarder Zones
 description:
 - Add and delete an IPA DNS Forwarder Zones using IPA API
+extends_documentation_fragment:
+  - ipamodule_base_docs
 options:
-  ipaadmin_principal:
-    description: The admin principal
-    default: admin
-  ipaadmin_password:
-    description: The admin password
-    required: false
   name:
     description:
     - The DNS zone name which needs to be managed.
@@ -85,7 +81,7 @@ options:
 EXAMPLES = '''
 # Ensure dns zone is present
 - ipadnsforwardzone:
-    ipaadmin_password: MyPassword123
+    ipaadmin_password: SomeADMINpassword
     state: present
     name: example.com
     forwarders:
@@ -96,7 +92,7 @@ EXAMPLES = '''
 
 # Ensure dns zone is present, with forwarder on non-default port
 - ipadnsforwardzone:
-    ipaadmin_password: MyPassword123
+    ipaadmin_password: SomeADMINpassword
     state: present
     name: example.com
     forwarders:
@@ -107,7 +103,7 @@ EXAMPLES = '''
 
 # Ensure that dns zone is removed
 - ipadnsforwardzone:
-    ipaadmin_password: MyPassword123
+    ipaadmin_password: SomeADMINpassword
     name: example.com
     state: absent
 '''
@@ -116,11 +112,9 @@ RETURN = '''
 '''
 
 
-from ansible.module_utils.basic import AnsibleModule
 from ansible.module_utils._text import to_text
-from ansible.module_utils.ansible_freeipa_module import temp_kinit, \
-    temp_kdestroy, valid_creds, api_connect, api_command, compare_args_ipa, \
-    module_params_get
+from ansible.module_utils.ansible_freeipa_module import \
+    IPAAnsibleModule, compare_args_ipa
 
 
 def find_dnsforwardzone(module, name):
@@ -128,7 +122,7 @@ def find_dnsforwardzone(module, name):
         "all": True,
         "idnsname": name
     }
-    _result = api_command(module, "dnsforwardzone_find", name, _args)
+    _result = module.ipa_command("dnsforwardzone_find", name, _args)
 
     if len(_result["result"]) > 1:
         module.fail_json(
@@ -167,11 +161,9 @@ def forwarder_list(forwarders):
 
 
 def main():
-    ansible_module = AnsibleModule(
+    ansible_module = IPAAnsibleModule(
         argument_spec=dict(
             # general
-            ipaadmin_principal=dict(type="str", default="admin"),
-            ipaadmin_password=dict(type="str", required=False, no_log=True),
             name=dict(type="list", aliases=["cn"], default=None,
                       required=True),
             forwarders=dict(type="list", default=None, required=False,
@@ -199,19 +191,14 @@ def main():
     ansible_module._ansible_debug = True
 
     # Get parameters
-    ipaadmin_principal = module_params_get(ansible_module,
-                                           "ipaadmin_principal")
-    ipaadmin_password = module_params_get(ansible_module,
-                                          "ipaadmin_password")
-    names = module_params_get(ansible_module, "name")
-    action = module_params_get(ansible_module, "action")
+    names = ansible_module.params_get("name")
+    action = ansible_module.params_get("action")
     forwarders = forwarder_list(
-        module_params_get(ansible_module, "forwarders"))
-    forwardpolicy = module_params_get(ansible_module, "forwardpolicy")
-    skip_overlap_check = module_params_get(ansible_module,
-                                           "skip_overlap_check")
-    permission = module_params_get(ansible_module, "permission")
-    state = module_params_get(ansible_module, "state")
+        ansible_module.params_get("forwarders"))
+    forwardpolicy = ansible_module.params_get("forwardpolicy")
+    skip_overlap_check = ansible_module.params_get("skip_overlap_check")
+    permission = ansible_module.params_get("permission")
+    state = ansible_module.params_get("state")
 
     if state == 'present' and len(names) != 1:
         ansible_module.fail_json(
@@ -257,21 +244,17 @@ def main():
     changed = False
     exit_args = {}
     args = {}
-    ccache_dir = None
-    ccache_name = None
     is_enabled = "IGNORE"
-    try:
+
+    # Connect to IPA API
+    with ansible_module.ipa_connect():
+
         # we need to determine 3 variables
         # args = the values we want to change/set
         # command = the ipa api command to call del, add, or mod
         # is_enabled = is the current resource enabled (True)
         #             disabled (False) and do we care (IGNORE)
 
-        if not valid_creds(ansible_module, ipaadmin_principal):
-            ccache_dir, ccache_name = temp_kinit(ipaadmin_principal,
-                                                 ipaadmin_password)
-        api_connect()
-
         for name in names:
             commands = []
             command = None
@@ -387,15 +370,9 @@ def main():
 
             # Execute commands
             for _name, command, args in commands:
-                api_command(ansible_module, command, _name, args)
+                ansible_module.ipa_command(command, _name, args)
                 changed = True
 
-    except Exception as e:
-        ansible_module.fail_json(msg=str(e))
-
-    finally:
-        temp_kdestroy(ccache_dir, ccache_name)
-
     # Done
     ansible_module.exit_json(changed=changed, dnsforwardzone=exit_args)
 
diff --git a/plugins/modules/ipadnsrecord.py b/plugins/modules/ipadnsrecord.py
index 1741a7a74a2051d0aa439a87521ed48d3fecee88..3950b31395fe00328e2a99f9472953824b283654 100644
--- a/plugins/modules/ipadnsrecord.py
+++ b/plugins/modules/ipadnsrecord.py
@@ -33,13 +33,9 @@ DOCUMENTATION = """
 module: ipadnsrecord
 short description: Manage FreeIPA DNS records
 description: Manage FreeIPA DNS records
+extends_documentation_fragment:
+  - ipamodule_base_docs
 options:
-  ipaadmin_principal:
-    description: The admin principal
-    default: admin
-  ipaadmin_password:
-    description: The admin password
-    required: false
   records:
     description: The list of user dns records dicts
     required: false
@@ -864,11 +860,9 @@ RETURN = """
 """
 
 
-from ansible.module_utils.basic import AnsibleModule
 from ansible.module_utils._text import to_text
-from ansible.module_utils.ansible_freeipa_module import temp_kinit, \
-    temp_kdestroy, valid_creds, api_connect, api_command, module_params_get, \
-    is_ipv4_addr, is_ipv6_addr, ipalib_errors
+from ansible.module_utils.ansible_freeipa_module import \
+    IPAAnsibleModule, is_ipv4_addr, is_ipv6_addr, ipalib_errors
 import dns.reversename
 import dns.resolver
 
@@ -1106,12 +1100,9 @@ def configure_module():
         uri_weight=dict(type='int', required=False),
     )
 
-    ansible_module = AnsibleModule(
+    ansible_module = IPAAnsibleModule(
         argument_spec=dict(
             # general
-            ipaadmin_principal=dict(type="str", default="admin"),
-            ipaadmin_password=dict(type="str", no_log=True),
-
             name=dict(type="list", aliases=["record_name"], default=None,
                       required=False),
 
@@ -1148,8 +1139,8 @@ def find_dnsrecord(module, dnszone, name):
     }
 
     try:
-        _result = api_command(
-            module, "dnsrecord_show", to_text(dnszone), _args)
+        _result = module.ipa_command(
+            "dnsrecord_show", to_text(dnszone), _args)
     except ipalib_errors.NotFound:
         return None
 
@@ -1217,21 +1208,6 @@ def check_parameters(module, state, zone_name, record):
                     (x, state))
 
 
-def connect_to_api(module):
-    """Connect to the IPA API."""
-    ipaadmin_principal = module_params_get(module, "ipaadmin_principal")
-    ipaadmin_password = module_params_get(module, "ipaadmin_password")
-
-    ccache_dir = None
-    ccache_name = None
-    if not valid_creds(module, ipaadmin_principal):
-        ccache_dir, ccache_name = temp_kinit(ipaadmin_principal,
-                                             ipaadmin_password)
-    api_connect()
-
-    return ccache_dir, ccache_name
-
-
 def get_entry_from_module(module, name):
     """Create an entry dict from attributes in module."""
     attrs = [
@@ -1243,9 +1219,9 @@ def get_entry_from_module(module, name):
 
     for key_set in [_RECORD_FIELDS, _PART_MAP, attrs]:
         entry.update({
-            key: module_params_get(module, key)
+            key: module.params_get(key)
             for key in key_set
-            if module_params_get(module, key) is not None
+            if module.params_get(key) is not None
         })
 
     return entry
@@ -1436,10 +1412,10 @@ def main():
     """Execute DNS record playbook."""
     ansible_module = configure_module()
 
-    global_zone_name = module_params_get(ansible_module, "zone_name")
-    names = module_params_get(ansible_module, "name")
-    records = module_params_get(ansible_module, "records")
-    state = module_params_get(ansible_module, "state")
+    global_zone_name = ansible_module.params_get("zone_name")
+    names = ansible_module.params_get("name")
+    records = ansible_module.params_get("records")
+    state = ansible_module.params_get("state")
 
     # Check parameters
 
@@ -1459,11 +1435,9 @@ def main():
 
     changed = False
     exit_args = {}
-    ccache_dir = None
-    ccache_name = None
 
-    try:
-        ccache_dir, ccache_name = connect_to_api(ansible_module)
+    # Connect to IPA API
+    with ansible_module.ipa_connect():
 
         commands = []
 
@@ -1501,8 +1475,8 @@ def main():
         # Execute commands
         for name, command, args in commands:
             try:
-                result = api_command(
-                    ansible_module, command, to_text(name), args)
+                result = ansible_module.ipa_command(
+                    command, to_text(name), args)
                 if "completed" in result:
                     if result["completed"] > 0:
                         changed = True
@@ -1519,12 +1493,6 @@ def main():
                 ansible_module.fail_json(
                     msg="%s: %s: %s" % (command, name, error_message))
 
-    except Exception as e:
-        ansible_module.fail_json(msg=str(e))
-
-    finally:
-        temp_kdestroy(ccache_dir, ccache_name)
-
     # Done
     ansible_module.exit_json(changed=changed, host=exit_args)
 
diff --git a/plugins/modules/ipadnszone.py b/plugins/modules/ipadnszone.py
index ae8ed93ad5da2172123df2f194b4347db9d2c520..266a0723fd3587d65797891ebd925294f6130b90 100644
--- a/plugins/modules/ipadnszone.py
+++ b/plugins/modules/ipadnszone.py
@@ -31,13 +31,9 @@ DOCUMENTATION = """
 module: ipadnszone
 short description: Manage FreeIPA dnszone
 description: Manage FreeIPA dnszone
+extends_documentation_fragment:
+  - ipamodule_base_docs
 options:
-  ipaadmin_principal:
-    description: The admin principal
-    default: admin
-  ipaadmin_password:
-    description: The admin password
-    required: false
   name:
     description: The zone name string.
     required: true
@@ -408,7 +404,9 @@ class DNSZoneModule(FreeIPABaseModule):
         get_zone_args = {"idnsname": zone_name, "all": True}
 
         try:
-            response = self.api_command("dnszone_show", args=get_zone_args)
+            response = self.ipa_command_no_name(
+                "dnszone_show", args=get_zone_args
+            )
         except ipalib_errors.NotFound:
             zone = None
             is_zone_active = False
diff --git a/plugins/modules/ipagroup.py b/plugins/modules/ipagroup.py
index fa0d89423a09ba4e326b5a44b0f969da46b7f821..c19644c2b2df4cce147187cfb6ade5edbad06fdd 100644
--- a/plugins/modules/ipagroup.py
+++ b/plugins/modules/ipagroup.py
@@ -31,13 +31,9 @@ DOCUMENTATION = """
 module: ipagroup
 short description: Manage FreeIPA groups
 description: Manage FreeIPA groups
+extends_documentation_fragment:
+  - ipamodule_base_docs
 options:
-  ipaadmin_principal:
-    description: The admin principal
-    default: admin
-  ipaadmin_password:
-    description: The admin password
-    required: false
   name:
     description: The group name
     required: false
@@ -182,10 +178,8 @@ EXAMPLES = """
 RETURN = """
 """
 
-from ansible.module_utils.basic import AnsibleModule
-from ansible.module_utils.ansible_freeipa_module import temp_kinit, \
-    temp_kdestroy, valid_creds, api_connect, api_command, compare_args_ipa, \
-    api_check_param, module_params_get, gen_add_del_lists, api_check_command, \
+from ansible.module_utils.ansible_freeipa_module import \
+    IPAAnsibleModule, compare_args_ipa, gen_add_del_lists, \
     gen_add_list, gen_intersection_list
 
 
@@ -195,7 +189,7 @@ def find_group(module, name):
         "cn": name,
     }
 
-    _result = api_command(module, "group_find", name, _args)
+    _result = module.ipa_command("group_find", name, _args)
 
     if len(_result["result"]) > 1:
         module.fail_json(
@@ -278,12 +272,9 @@ def should_modify_group(module, res_find, args, nonposix, posix, external):
 
 
 def main():
-    ansible_module = AnsibleModule(
+    ansible_module = IPAAnsibleModule(
         argument_spec=dict(
             # general
-            ipaadmin_principal=dict(type="str", default="admin"),
-            ipaadmin_password=dict(type="str", required=False, no_log=True),
-
             name=dict(type="list", aliases=["cn"], default=None,
                       required=True),
             # present
@@ -319,31 +310,24 @@ def main():
     # Get parameters
 
     # general
-    ipaadmin_principal = module_params_get(
-        ansible_module,
-        "ipaadmin_principal",
-    )
-    ipaadmin_password = module_params_get(ansible_module, "ipaadmin_password")
-    names = module_params_get(ansible_module, "name")
+    names = ansible_module.params_get("name")
 
     # present
-    description = module_params_get(ansible_module, "description")
-    gid = module_params_get(ansible_module, "gid")
-    nonposix = module_params_get(ansible_module, "nonposix")
-    external = module_params_get(ansible_module, "external")
-    posix = module_params_get(ansible_module, "posix")
-    nomembers = module_params_get(ansible_module, "nomembers")
-    user = module_params_get(ansible_module, "user")
-    group = module_params_get(ansible_module, "group")
-    service = module_params_get(ansible_module, "service")
-    membermanager_user = module_params_get(ansible_module,
-                                           "membermanager_user")
-    membermanager_group = module_params_get(ansible_module,
-                                            "membermanager_group")
-    externalmember = module_params_get(ansible_module, "externalmember")
-    action = module_params_get(ansible_module, "action")
+    description = ansible_module.params_get("description")
+    gid = ansible_module.params_get("gid")
+    nonposix = ansible_module.params_get("nonposix")
+    external = ansible_module.params_get("external")
+    posix = ansible_module.params_get("posix")
+    nomembers = ansible_module.params_get("nomembers")
+    user = ansible_module.params_get("user")
+    group = ansible_module.params_get("group")
+    service = ansible_module.params_get("service")
+    membermanager_user = ansible_module.params_get("membermanager_user")
+    membermanager_group = ansible_module.params_get("membermanager_group")
+    externalmember = ansible_module.params_get("externalmember")
+    action = ansible_module.params_get("action")
     # state
-    state = module_params_get(ansible_module, "state")
+    state = ansible_module.params_get("state")
 
     # Check parameters
 
@@ -378,21 +362,19 @@ def main():
 
     changed = False
     exit_args = {}
-    ccache_dir = None
-    ccache_name = None
-    try:
-        if not valid_creds(ansible_module, ipaadmin_principal):
-            ccache_dir, ccache_name = temp_kinit(ipaadmin_principal,
-                                                 ipaadmin_password)
-        api_connect()
-
-        has_add_member_service = api_check_param("group_add_member", "service")
+
+    # Connect to IPA API
+    with ansible_module.ipa_connect():
+
+        has_add_member_service = ansible_module.ipa_command_param_exists(
+            "group_add_member", "service")
         if service is not None and not has_add_member_service:
             ansible_module.fail_json(
                 msg="Managing a service as part of a group is not supported "
                 "by your IPA version")
 
-        has_add_membermanager = api_check_command("group_add_member_manager")
+        has_add_membermanager = ansible_module.ipa_command_exists(
+            "group_add_member_manager")
         if ((membermanager_user is not None or
              membermanager_group is not None) and not has_add_membermanager):
             ansible_module.fail_json(
@@ -684,8 +666,7 @@ def main():
 
         for name, command, args in commands:
             try:
-                result = api_command(ansible_module, command, name,
-                                     args)
+                result = ansible_module.ipa_command(command, name, args)
                 if "completed" in result:
                     if result["completed"] > 0:
                         changed = True
@@ -706,12 +687,6 @@ def main():
             if len(errors) > 0:
                 ansible_module.fail_json(msg=", ".join(errors))
 
-    except Exception as e:
-        ansible_module.fail_json(msg=str(e))
-
-    finally:
-        temp_kdestroy(ccache_dir, ccache_name)
-
     # Done
 
     ansible_module.exit_json(changed=changed, **exit_args)
diff --git a/plugins/modules/ipahbacrule.py b/plugins/modules/ipahbacrule.py
index d81112f67ecd8b8d71c33215796c714222a20883..ea8fd73aca656484ee6479d541da16f005e9e510 100644
--- a/plugins/modules/ipahbacrule.py
+++ b/plugins/modules/ipahbacrule.py
@@ -31,13 +31,9 @@ DOCUMENTATION = """
 module: ipahbacrule
 short description: Manage FreeIPA HBAC rules
 description: Manage FreeIPA HBAC rules
+extends_documentation_fragment:
+  - ipamodule_base_docs
 options:
-  ipaadmin_principal:
-    description: The admin principal
-    default: admin
-  ipaadmin_password:
-    description: The admin password
-    required: false
   name:
     description: The hbacrule name
     required: true
@@ -156,11 +152,9 @@ EXAMPLES = """
 RETURN = """
 """
 
-from ansible.module_utils.basic import AnsibleModule
-from ansible.module_utils.ansible_freeipa_module import temp_kinit, \
-    temp_kdestroy, valid_creds, api_connect, api_command, compare_args_ipa, \
-    module_params_get, gen_add_del_lists, gen_add_list, \
-    gen_intersection_list, api_get_domain, ensure_fqdn
+from ansible.module_utils.ansible_freeipa_module import \
+    IPAAnsibleModule, compare_args_ipa, gen_add_del_lists, gen_add_list, \
+    gen_intersection_list, ensure_fqdn
 
 
 def find_hbacrule(module, name):
@@ -169,7 +163,7 @@ def find_hbacrule(module, name):
         "cn": name,
     }
 
-    _result = api_command(module, "hbacrule_find", name, _args)
+    _result = module.ipa_command("hbacrule_find", name, _args)
 
     if len(_result["result"]) > 1:
         module.fail_json(
@@ -198,12 +192,9 @@ def gen_args(description, usercategory, hostcategory, servicecategory,
 
 
 def main():
-    ansible_module = AnsibleModule(
+    ansible_module = IPAAnsibleModule(
         argument_spec=dict(
             # general
-            ipaadmin_principal=dict(type="str", default="admin"),
-            ipaadmin_password=dict(type="str", required=False, no_log=True),
-
             name=dict(type="list", aliases=["cn"], default=None,
                       required=True),
             # present
@@ -236,26 +227,23 @@ def main():
     # Get parameters
 
     # general
-    ipaadmin_principal = module_params_get(ansible_module,
-                                           "ipaadmin_principal")
-    ipaadmin_password = module_params_get(ansible_module, "ipaadmin_password")
-    names = module_params_get(ansible_module, "name")
+    names = ansible_module.params_get("name")
 
     # present
-    description = module_params_get(ansible_module, "description")
-    usercategory = module_params_get(ansible_module, "usercategory")
-    hostcategory = module_params_get(ansible_module, "hostcategory")
-    servicecategory = module_params_get(ansible_module, "servicecategory")
-    nomembers = module_params_get(ansible_module, "nomembers")
-    host = module_params_get(ansible_module, "host")
-    hostgroup = module_params_get(ansible_module, "hostgroup")
-    hbacsvc = module_params_get(ansible_module, "hbacsvc")
-    hbacsvcgroup = module_params_get(ansible_module, "hbacsvcgroup")
-    user = module_params_get(ansible_module, "user")
-    group = module_params_get(ansible_module, "group")
-    action = module_params_get(ansible_module, "action")
+    description = ansible_module.params_get("description")
+    usercategory = ansible_module.params_get("usercategory")
+    hostcategory = ansible_module.params_get("hostcategory")
+    servicecategory = ansible_module.params_get("servicecategory")
+    nomembers = ansible_module.params_get("nomembers")
+    host = ansible_module.params_get("host")
+    hostgroup = ansible_module.params_get("hostgroup")
+    hbacsvc = ansible_module.params_get("hbacsvc")
+    hbacsvcgroup = ansible_module.params_get("hbacsvcgroup")
+    user = ansible_module.params_get("user")
+    group = ansible_module.params_get("group")
+    action = ansible_module.params_get("action")
     # state
-    state = module_params_get(ansible_module, "state")
+    state = ansible_module.params_get("state")
 
     # Check parameters
 
@@ -318,16 +306,12 @@ def main():
 
     changed = False
     exit_args = {}
-    ccache_dir = None
-    ccache_name = None
-    try:
-        if not valid_creds(ansible_module, ipaadmin_principal):
-            ccache_dir, ccache_name = temp_kinit(ipaadmin_principal,
-                                                 ipaadmin_password)
-        api_connect()
+
+    # Connect to IPA API
+    with ansible_module.ipa_connect():
 
         # Get default domain
-        default_domain = api_get_domain()
+        default_domain = ansible_module.ipa_get_domain()
 
         # Ensure fqdn host names, use default domain for simple names
         if host is not None:
@@ -620,8 +604,7 @@ def main():
         errors = []
         for name, command, args in commands:
             try:
-                result = api_command(ansible_module, command, name,
-                                     args)
+                result = ansible_module.ipa_command(command, name, args)
                 if "completed" in result:
                     if result["completed"] > 0:
                         changed = True
@@ -642,12 +625,6 @@ def main():
         if len(errors) > 0:
             ansible_module.fail_json(msg=", ".join(errors))
 
-    except Exception as e:
-        ansible_module.fail_json(msg=str(e))
-
-    finally:
-        temp_kdestroy(ccache_dir, ccache_name)
-
     # Done
 
     ansible_module.exit_json(changed=changed, **exit_args)
diff --git a/plugins/modules/ipahbacsvc.py b/plugins/modules/ipahbacsvc.py
index 26e6a95752668f84cc2c1692f62fdd39d31c6187..c6e31d47b7c99192e85833f9a136426e816825f8 100644
--- a/plugins/modules/ipahbacsvc.py
+++ b/plugins/modules/ipahbacsvc.py
@@ -31,13 +31,9 @@ DOCUMENTATION = """
 module: ipahbacsvc
 short description: Manage FreeIPA HBAC Services
 description: Manage FreeIPA HBAC Services
+extends_documentation_fragment:
+  - ipamodule_base_docs
 options:
-  ipaadmin_principal:
-    description: The admin principal
-    default: admin
-  ipaadmin_password:
-    description: The admin password
-    required: false
   name:
     description: The group name
     required: false
@@ -70,19 +66,17 @@ EXAMPLES = """
 RETURN = """
 """
 
-from ansible.module_utils.basic import AnsibleModule
-from ansible.module_utils._text import to_text
-from ansible.module_utils.ansible_freeipa_module import temp_kinit, \
-    temp_kdestroy, valid_creds, api_connect, api_command, compare_args_ipa
+from ansible.module_utils.ansible_freeipa_module import \
+    IPAAnsibleModule, compare_args_ipa
 
 
 def find_hbacsvc(module, name):
     _args = {
         "all": True,
-        "cn": to_text(name),
+        "cn": name,
     }
 
-    _result = api_command(module, "hbacsvc_find", to_text(name), _args)
+    _result = module.ipa_command("hbacsvc_find", name, _args)
 
     if len(_result["result"]) > 1:
         module.fail_json(
@@ -96,18 +90,15 @@ def find_hbacsvc(module, name):
 def gen_args(description):
     _args = {}
     if description is not None:
-        _args["description"] = to_text(description)
+        _args["description"] = description
 
     return _args
 
 
 def main():
-    ansible_module = AnsibleModule(
+    ansible_module = IPAAnsibleModule(
         argument_spec=dict(
             # general
-            ipaadmin_principal=dict(type="str", default="admin"),
-            ipaadmin_password=dict(type="str", required=False, no_log=True),
-
             name=dict(type="list", aliases=["cn", "service"], default=None,
                       required=True),
             # present
@@ -126,15 +117,13 @@ def main():
     # Get parameters
 
     # general
-    ipaadmin_principal = ansible_module.params.get("ipaadmin_principal")
-    ipaadmin_password = ansible_module.params.get("ipaadmin_password")
-    names = ansible_module.params.get("name")
+    names = ansible_module.params_get("name")
 
     # present
-    description = ansible_module.params.get("description")
+    description = ansible_module.params_get("description")
 
     # state
-    state = ansible_module.params.get("state")
+    state = ansible_module.params_get("state")
 
     # Check parameters
 
@@ -158,13 +147,9 @@ def main():
 
     changed = False
     exit_args = {}
-    ccache_dir = None
-    ccache_name = None
-    try:
-        if not valid_creds(ansible_module, ipaadmin_principal):
-            ccache_dir, ccache_name = temp_kinit(ipaadmin_principal,
-                                                 ipaadmin_password)
-        api_connect()
+
+    # Connect to IPA API
+    with ansible_module.ipa_connect():
 
         commands = []
 
@@ -203,18 +188,12 @@ def main():
 
         for name, command, args in commands:
             try:
-                api_command(ansible_module, command, to_text(name), args)
+                ansible_module.ipa_command(command, name, args)
                 changed = True
             except Exception as e:
                 ansible_module.fail_json(msg="%s: %s: %s" % (command, name,
                                                              str(e)))
 
-    except Exception as e:
-        ansible_module.fail_json(msg=str(e))
-
-    finally:
-        temp_kdestroy(ccache_dir, ccache_name)
-
     # Done
 
     ansible_module.exit_json(changed=changed, **exit_args)
diff --git a/plugins/modules/ipahbacsvcgroup.py b/plugins/modules/ipahbacsvcgroup.py
index 141fa5391448d93dede6dd74a5a5e817447574f9..5e301d6a318b6edcaa8a930623874c971154bf21 100644
--- a/plugins/modules/ipahbacsvcgroup.py
+++ b/plugins/modules/ipahbacsvcgroup.py
@@ -32,13 +32,9 @@ DOCUMENTATION = """
 module: ipahbacsvcgroup
 short description: Manage FreeIPA hbacsvcgroups
 description: Manage FreeIPA hbacsvcgroups
+extends_documentation_fragment:
+  - ipamodule_base_docs
 options:
-  ipaadmin_principal:
-    description: The admin principal
-    default: admin
-  ipaadmin_password:
-    description: The admin password
-    required: false
   name:
     description: The hbacsvcgroup name
     required: false
@@ -101,20 +97,17 @@ EXAMPLES = """
 RETURN = """
 """
 
-from ansible.module_utils.basic import AnsibleModule
-from ansible.module_utils._text import to_text
-from ansible.module_utils.ansible_freeipa_module import temp_kinit, \
-    temp_kdestroy, valid_creds, api_connect, api_command, compare_args_ipa, \
-    gen_add_del_lists
+from ansible.module_utils.ansible_freeipa_module import \
+    IPAAnsibleModule, compare_args_ipa, gen_add_del_lists
 
 
 def find_hbacsvcgroup(module, name):
     _args = {
         "all": True,
-        "cn": to_text(name),
+        "cn": name,
     }
 
-    _result = api_command(module, "hbacsvcgroup_find", to_text(name), _args)
+    _result = module.ipa_command("hbacsvcgroup_find", name, _args)
 
     if len(_result["result"]) > 1:
         module.fail_json(
@@ -128,7 +121,7 @@ def find_hbacsvcgroup(module, name):
 def gen_args(description, nomembers):
     _args = {}
     if description is not None:
-        _args["description"] = to_text(description)
+        _args["description"] = description
     if nomembers is not None:
         _args["nomembers"] = nomembers
 
@@ -138,18 +131,15 @@ def gen_args(description, nomembers):
 def gen_member_args(hbacsvc):
     _args = {}
     if hbacsvc is not None:
-        _args["member_hbacsvc"] = [to_text(svc) for svc in hbacsvc]
+        _args["member_hbacsvc"] = hbacsvc
 
     return _args
 
 
 def main():
-    ansible_module = AnsibleModule(
+    ansible_module = IPAAnsibleModule(
         argument_spec=dict(
             # general
-            ipaadmin_principal=dict(type="str", default="admin"),
-            ipaadmin_password=dict(type="str", required=False, no_log=True),
-
             name=dict(type="list", aliases=["cn"], default=None,
                       required=True),
             # present
@@ -170,17 +160,15 @@ def main():
     # Get parameters
 
     # general
-    ipaadmin_principal = ansible_module.params.get("ipaadmin_principal")
-    ipaadmin_password = ansible_module.params.get("ipaadmin_password")
-    names = ansible_module.params.get("name")
+    names = ansible_module.params_get("name")
 
     # present
-    description = ansible_module.params.get("description")
-    nomembers = ansible_module.params.get("nomembers")
-    hbacsvc = ansible_module.params.get("hbacsvc")
-    action = ansible_module.params.get("action")
+    description = ansible_module.params_get("description")
+    nomembers = ansible_module.params_get("nomembers")
+    hbacsvc = ansible_module.params_get("hbacsvc")
+    action = ansible_module.params_get("action")
     # state
-    state = ansible_module.params.get("state")
+    state = ansible_module.params_get("state")
 
     # Check parameters
 
@@ -213,13 +201,9 @@ def main():
 
     changed = False
     exit_args = {}
-    ccache_dir = None
-    ccache_name = None
-    try:
-        if not valid_creds(ansible_module, ipaadmin_principal):
-            ccache_dir, ccache_name = temp_kinit(ipaadmin_principal,
-                                                 ipaadmin_password)
-        api_connect()
+
+    # Connect to IPA API
+    with ansible_module.ipa_connect():
 
         commands = []
 
@@ -257,18 +241,14 @@ def main():
                         if len(hbacsvc_add) > 0:
                             commands.append([name, "hbacsvcgroup_add_member",
                                              {
-                                                 "hbacsvc":
-                                                 [to_text(svc)
-                                                  for svc in hbacsvc_add],
+                                                 "hbacsvc": hbacsvc_add
                                              }])
                         # Remove members
                         if len(hbacsvc_del) > 0:
                             commands.append([name,
                                              "hbacsvcgroup_remove_member",
                                              {
-                                                 "hbacsvc":
-                                                 [to_text(svc)
-                                                  for svc in hbacsvc_del],
+                                                 "hbacsvc": hbacsvc_del
                                              }])
                 elif action == "member":
                     if res_find is None:
@@ -278,8 +258,7 @@ def main():
                     # Ensure members are present
                     commands.append([name, "hbacsvcgroup_add_member",
                                      {
-                                         "hbacsvc": [to_text(svc)
-                                                     for svc in hbacsvc],
+                                         "hbacsvc": hbacsvc
                                      }])
             elif state == "absent":
                 if action == "hbacsvcgroup":
@@ -294,8 +273,7 @@ def main():
                     # Ensure members are absent
                     commands.append([name, "hbacsvcgroup_remove_member",
                                      {
-                                         "hbacsvc": [to_text(svc)
-                                                     for svc in hbacsvc],
+                                         "hbacsvc": hbacsvc
                                      }])
             else:
                 ansible_module.fail_json(msg="Unkown state '%s'" % state)
@@ -308,8 +286,7 @@ def main():
         errors = []
         for name, command, args in commands:
             try:
-                result = api_command(ansible_module, command, to_text(name),
-                                     args)
+                result = ansible_module.ipa_command(command, name, args)
                 if "completed" in result:
                     if result["completed"] > 0:
                         changed = True
@@ -332,12 +309,6 @@ def main():
         if len(errors) > 0:
             ansible_module.fail_json(msg=", ".join(errors))
 
-    except Exception as e:
-        ansible_module.fail_json(msg=str(e))
-
-    finally:
-        temp_kdestroy(ccache_dir, ccache_name)
-
     # Done
 
     ansible_module.exit_json(changed=changed, **exit_args)
diff --git a/plugins/modules/ipahost.py b/plugins/modules/ipahost.py
index 0bd5b1b426b74b7a638b9f27abbc07a5587b5ef5..86453a775abd6203c581f8f9f16e7873a191def4 100644
--- a/plugins/modules/ipahost.py
+++ b/plugins/modules/ipahost.py
@@ -31,13 +31,9 @@ DOCUMENTATION = """
 module: ipahost
 short description: Manage FreeIPA hosts
 description: Manage FreeIPA hosts
+extends_documentation_fragment:
+  - ipamodule_base_docs
 options:
-  ipaadmin_principal:
-    description: The admin principal
-    default: admin
-  ipaadmin_password:
-    description: The admin password
-    required: false
   name:
     description: The full qualified domain name.
     aliases: ["fqdn"]
@@ -380,7 +376,7 @@ EXAMPLES = """
 
 # Ensure host is absent
 - ipahost:
-    ipaadmin_password: password1
+    ipaadmin_password: SomeADMINpassword
     name: host01.example.com
     state: absent
 """
@@ -404,15 +400,10 @@ host:
           returned: always
 """
 
-from ansible.module_utils.basic import AnsibleModule
-from ansible.module_utils._text import to_text
-from ansible.module_utils.ansible_freeipa_module import temp_kinit, \
-    temp_kdestroy, valid_creds, api_connect, api_command, compare_args_ipa, \
-    module_params_get, gen_add_del_lists, encode_certificate, api_get_realm, \
-    is_ipv4_addr, is_ipv6_addr, ipalib_errors
+from ansible.module_utils.ansible_freeipa_module import \
+    IPAAnsibleModule, compare_args_ipa, gen_add_del_lists, \
+    encode_certificate, is_ipv4_addr, is_ipv6_addr, ipalib_errors
 import six
-
-
 if six.PY3:
     unicode = str
 
@@ -423,7 +414,7 @@ def find_host(module, name):
     }
 
     try:
-        _result = api_command(module, "host_show", to_text(name), _args)
+        _result = module.ipa_command("host_show", name, _args)
     except ipalib_errors.NotFound as e:
         msg = str(e)
         if "host not found" in msg:
@@ -450,17 +441,16 @@ def find_dnsrecord(module, name):
 
     _args = {
         "all": True,
-        "idnsname": to_text(host_name)
+        "idnsname": host_name
     }
 
-    _result = api_command(module, "dnsrecord_show", to_text(domain_name),
-                          _args)
+    _result = module.ipa_command("dnsrecord_show", domain_name, _args)
 
     return _result["result"]
 
 
 def show_host(module, name):
-    _result = api_command(module, "host_show", to_text(name), {})
+    _result = module.ipa_command("host_show", name, {})
     return _result["result"]
 
 
@@ -663,12 +653,9 @@ def main():
         # krbprincipalname
     )
 
-    ansible_module = AnsibleModule(
+    ansible_module = IPAAnsibleModule(
         argument_spec=dict(
             # general
-            ipaadmin_principal=dict(type="str", default="admin"),
-            ipaadmin_password=dict(type="str", no_log=True),
-
             name=dict(type="list", aliases=["fqdn"], default=None,
                       required=False),
 
@@ -705,56 +692,52 @@ def main():
     # Get parameters
 
     # general
-    ipaadmin_principal = module_params_get(ansible_module,
-                                           "ipaadmin_principal")
-    ipaadmin_password = module_params_get(ansible_module,
-                                          "ipaadmin_password")
-    names = module_params_get(ansible_module, "name")
-    hosts = module_params_get(ansible_module, "hosts")
+    names = ansible_module.params_get("name")
+    hosts = ansible_module.params_get("hosts")
 
     # present
-    description = module_params_get(ansible_module, "description")
-    locality = module_params_get(ansible_module, "locality")
-    location = module_params_get(ansible_module, "location")
-    platform = module_params_get(ansible_module, "platform")
-    os = module_params_get(ansible_module, "os")
-    password = module_params_get(ansible_module, "password")
-    random = module_params_get(ansible_module, "random")
-    certificate = module_params_get(ansible_module, "certificate")
-    managedby_host = module_params_get(ansible_module, "managedby_host")
-    principal = module_params_get(ansible_module, "principal")
-    allow_create_keytab_user = module_params_get(
-        ansible_module, "allow_create_keytab_user")
-    allow_create_keytab_group = module_params_get(
-        ansible_module, "allow_create_keytab_group")
-    allow_create_keytab_host = module_params_get(
-        ansible_module, "allow_create_keytab_host")
-    allow_create_keytab_hostgroup = module_params_get(
-        ansible_module, "allow_create_keytab_hostgroup")
-    allow_retrieve_keytab_user = module_params_get(
-        ansible_module, "allow_retrieve_keytab_user")
-    allow_retrieve_keytab_group = module_params_get(
-        ansible_module, "allow_retrieve_keytab_group")
-    allow_retrieve_keytab_host = module_params_get(
-        ansible_module, "allow_retrieve_keytab_host")
-    allow_retrieve_keytab_hostgroup = module_params_get(
-        ansible_module, "allow_retrieve_keytab_hostgroup")
-    mac_address = module_params_get(ansible_module, "mac_address")
-    sshpubkey = module_params_get(ansible_module, "sshpubkey")
-    userclass = module_params_get(ansible_module, "userclass")
-    auth_ind = module_params_get(ansible_module, "auth_ind")
-    requires_pre_auth = module_params_get(ansible_module, "requires_pre_auth")
-    ok_as_delegate = module_params_get(ansible_module, "ok_as_delegate")
-    ok_to_auth_as_delegate = module_params_get(ansible_module,
-                                               "ok_to_auth_as_delegate")
-    force = module_params_get(ansible_module, "force")
-    reverse = module_params_get(ansible_module, "reverse")
-    ip_address = module_params_get(ansible_module, "ip_address")
-    update_dns = module_params_get(ansible_module, "update_dns")
-    update_password = module_params_get(ansible_module, "update_password")
+    description = ansible_module.params_get("description")
+    locality = ansible_module.params_get("locality")
+    location = ansible_module.params_get("location")
+    platform = ansible_module.params_get("platform")
+    os = ansible_module.params_get("os")
+    password = ansible_module.params_get("password")
+    random = ansible_module.params_get("random")
+    certificate = ansible_module.params_get("certificate")
+    managedby_host = ansible_module.params_get("managedby_host")
+    principal = ansible_module.params_get("principal")
+    allow_create_keytab_user = ansible_module.params_get(
+        "allow_create_keytab_user")
+    allow_create_keytab_group = ansible_module.params_get(
+        "allow_create_keytab_group")
+    allow_create_keytab_host = ansible_module.params_get(
+        "allow_create_keytab_host")
+    allow_create_keytab_hostgroup = ansible_module.params_get(
+        "allow_create_keytab_hostgroup")
+    allow_retrieve_keytab_user = ansible_module.params_get(
+        "allow_retrieve_keytab_user")
+    allow_retrieve_keytab_group = ansible_module.params_get(
+        "allow_retrieve_keytab_group")
+    allow_retrieve_keytab_host = ansible_module.params_get(
+        "allow_retrieve_keytab_host")
+    allow_retrieve_keytab_hostgroup = ansible_module.params_get(
+        "allow_retrieve_keytab_hostgroup")
+    mac_address = ansible_module.params_get("mac_address")
+    sshpubkey = ansible_module.params_get("sshpubkey")
+    userclass = ansible_module.params_get("userclass")
+    auth_ind = ansible_module.params_get("auth_ind")
+    requires_pre_auth = ansible_module.params_get("requires_pre_auth")
+    ok_as_delegate = ansible_module.params_get("ok_as_delegate")
+    ok_to_auth_as_delegate = ansible_module.params_get(
+        "ok_to_auth_as_delegate")
+    force = ansible_module.params_get("force")
+    reverse = ansible_module.params_get("reverse")
+    ip_address = ansible_module.params_get("ip_address")
+    update_dns = ansible_module.params_get("update_dns")
+    update_password = ansible_module.params_get("update_password")
     # general
-    action = module_params_get(ansible_module, "action")
-    state = module_params_get(ansible_module, "state")
+    action = ansible_module.params_get("action")
+    state = ansible_module.params_get("state")
 
     # Check parameters
 
@@ -786,17 +769,13 @@ def main():
 
     changed = False
     exit_args = {}
-    ccache_dir = None
-    ccache_name = None
-    try:
-        if not valid_creds(ansible_module, ipaadmin_principal):
-            ccache_dir, ccache_name = temp_kinit(ipaadmin_principal,
-                                                 ipaadmin_password)
-        api_connect()
+
+    # Connect to IPA API
+    with ansible_module.ipa_connect():
 
         # Check version specific settings
 
-        server_realm = api_get_realm()
+        server_realm = ansible_module.ipa_get_realm()
 
         commands = []
         host_set = set()
@@ -973,7 +952,7 @@ def main():
                         # Principals are not returned as utf8 for IPA using
                         # python2 using host_show, therefore we need to
                         # convert the principals that we should remove.
-                        principal_del = [to_text(x) for x in principal_del]
+                        principal_del = [unicode(x) for x in principal_del]
 
                         (allow_create_keytab_user_add,
                          allow_create_keytab_user_del) = \
@@ -1373,8 +1352,7 @@ def main():
         errors = []
         for name, command, args in commands:
             try:
-                result = api_command(ansible_module, command, to_text(name),
-                                     args)
+                result = ansible_module.ipa_command(command, name, args)
                 if "completed" in result:
                     if result["completed"] > 0:
                         changed = True
@@ -1428,12 +1406,6 @@ def main():
         if len(errors) > 0:
             ansible_module.fail_json(msg=", ".join(errors))
 
-    except Exception as e:
-        ansible_module.fail_json(msg=str(e))
-
-    finally:
-        temp_kdestroy(ccache_dir, ccache_name)
-
     # Done
 
     ansible_module.exit_json(changed=changed, host=exit_args)
diff --git a/plugins/modules/ipahostgroup.py b/plugins/modules/ipahostgroup.py
index b64f53e56ff0d74d35f26297f144a481119252ff..b0f7857b171a871f7d56e4347ea1760a88628e90 100644
--- a/plugins/modules/ipahostgroup.py
+++ b/plugins/modules/ipahostgroup.py
@@ -32,13 +32,9 @@ DOCUMENTATION = """
 module: ipahostgroup
 short description: Manage FreeIPA hostgroups
 description: Manage FreeIPA hostgroups
+extends_documentation_fragment:
+  - ipamodule_base_docs
 options:
-  ipaadmin_principal:
-    description: The admin principal
-    default: admin
-  ipaadmin_password:
-    description: The admin password
-    required: false
   name:
     description: The hostgroup name
     required: false
@@ -138,11 +134,9 @@ EXAMPLES = """
 RETURN = """
 """
 
-from ansible.module_utils.basic import AnsibleModule
-from ansible.module_utils.ansible_freeipa_module import temp_kinit, \
-    temp_kdestroy, valid_creds, api_connect, api_command, compare_args_ipa, \
-    module_params_get, gen_add_del_lists, api_check_command, api_check_param, \
-    gen_add_list, gen_intersection_list
+from ansible.module_utils.ansible_freeipa_module import \
+    IPAAnsibleModule, compare_args_ipa, gen_add_del_lists, gen_add_list, \
+    gen_intersection_list
 
 
 def find_hostgroup(module, name):
@@ -151,7 +145,7 @@ def find_hostgroup(module, name):
         "cn": name,
     }
 
-    _result = api_command(module, "hostgroup_find", name, _args)
+    _result = module.ipa_command("hostgroup_find", name, _args)
 
     if len(_result["result"]) > 1:
         module.fail_json(
@@ -185,12 +179,9 @@ def gen_member_args(host, hostgroup):
 
 
 def main():
-    ansible_module = AnsibleModule(
+    ansible_module = IPAAnsibleModule(
         argument_spec=dict(
             # general
-            ipaadmin_principal=dict(type="str", default="admin"),
-            ipaadmin_password=dict(type="str", required=False, no_log=True),
-
             name=dict(type="list", aliases=["cn"], default=None,
                       required=True),
             # present
@@ -217,25 +208,19 @@ def main():
     # Get parameters
 
     # general
-    ipaadmin_principal = module_params_get(ansible_module,
-                                           "ipaadmin_principal")
-    ipaadmin_password = module_params_get(ansible_module,
-                                          "ipaadmin_password")
-    names = module_params_get(ansible_module, "name")
+    names = ansible_module.params_get("name")
 
     # present
-    description = module_params_get(ansible_module, "description")
-    nomembers = module_params_get(ansible_module, "nomembers")
-    host = module_params_get(ansible_module, "host")
-    hostgroup = module_params_get(ansible_module, "hostgroup")
-    membermanager_user = module_params_get(ansible_module,
-                                           "membermanager_user")
-    membermanager_group = module_params_get(ansible_module,
-                                            "membermanager_group")
-    rename = module_params_get(ansible_module, "rename")
-    action = module_params_get(ansible_module, "action")
+    description = ansible_module.params_get("description")
+    nomembers = ansible_module.params_get("nomembers")
+    host = ansible_module.params_get("host")
+    hostgroup = ansible_module.params_get("hostgroup")
+    membermanager_user = ansible_module.params_get("membermanager_user")
+    membermanager_group = ansible_module.params_get("membermanager_group")
+    rename = ansible_module.params_get("rename")
+    action = ansible_module.params_get("action")
     # state
-    state = module_params_get(ansible_module, "state")
+    state = ansible_module.params_get("state")
 
     # Check parameters
 
@@ -287,15 +272,11 @@ def main():
 
     changed = False
     exit_args = {}
-    ccache_dir = None
-    ccache_name = None
-    try:
-        if not valid_creds(ansible_module, ipaadmin_principal):
-            ccache_dir, ccache_name = temp_kinit(ipaadmin_principal,
-                                                 ipaadmin_password)
-        api_connect()
-
-        has_add_membermanager = api_check_command(
+
+    # Connect to IPA API
+    with ansible_module.ipa_connect():
+
+        has_add_membermanager = ansible_module.ipa_command_exists(
             "hostgroup_add_member_manager")
         if ((membermanager_user is not None or
              membermanager_group is not None) and not has_add_membermanager):
@@ -303,7 +284,8 @@ def main():
                 msg="Managing a membermanager user or group is not supported "
                 "by your IPA version"
             )
-        has_mod_rename = api_check_param("hostgroup_mod", "rename")
+        has_mod_rename = ansible_module.ipa_command_param_exists(
+            "hostgroup_mod", "rename")
         if not has_mod_rename and rename is not None:
             ansible_module.fail_json(
                 msg="Renaming hostgroups is not supported by your IPA version")
@@ -515,7 +497,7 @@ def main():
         # Execute commands
         for name, command, args in commands:
             try:
-                result = api_command(ansible_module, command, name, args)
+                result = ansible_module.ipa_command(command, name, args)
                 if "completed" in result:
                     if result["completed"] > 0:
                         changed = True
@@ -537,12 +519,6 @@ def main():
             if len(errors) > 0:
                 ansible_module.fail_json(msg=", ".join(errors))
 
-    except Exception as e:
-        ansible_module.fail_json(msg=str(e))
-
-    finally:
-        temp_kdestroy(ccache_dir, ccache_name)
-
     # Done
 
     ansible_module.exit_json(changed=changed, **exit_args)
diff --git a/plugins/modules/ipalocation.py b/plugins/modules/ipalocation.py
index 6c7579449d60c6d8b0b7f9d6b9c8000fb5bc8303..48d5b4876f5e110cbfccae05e9c11f558165a727 100644
--- a/plugins/modules/ipalocation.py
+++ b/plugins/modules/ipalocation.py
@@ -66,21 +66,14 @@ RETURN = """
 """
 
 
-from ansible.module_utils.basic import AnsibleModule
 from ansible.module_utils.ansible_freeipa_module import \
-    temp_kinit, temp_kdestroy, valid_creds, api_connect, api_command, \
-    compare_args_ipa, module_params_get, ipamodule_base_spec, \
-    get_ipamodule_base_vars
-import six
-
-if six.PY3:
-    unicode = str
+    IPAAnsibleModule, compare_args_ipa
 
 
 def find_location(module, name):
     """Find if a location with the given name already exist."""
     try:
-        _result = api_command(module, "location_show", name, {"all": True})
+        _result = module.ipa_command("location_show", name, {"all": True})
     except Exception:  # pylint: disable=broad-except
         # An exception is raised if location name is not found.
         return None
@@ -96,20 +89,16 @@ def gen_args(description):
 
 
 def main():
-    # Arguments
-    argument_spec = dict(
-        name=dict(type="list", aliases=["idnsname"],
-                  default=None, required=True),
-        # present
-        description=dict(required=False, type='str', default=None),
-        # state
-        state=dict(type="str", default="present",
-                   choices=["present", "absent"]),
-    )
-    argument_spec.update(ipamodule_base_spec)
-
-    ansible_module = AnsibleModule(
-        argument_spec=argument_spec,
+    ansible_module = IPAAnsibleModule(
+        argument_spec=dict(
+            name=dict(type="list", aliases=["idnsname"],
+                      default=None, required=True),
+            # present
+            description=dict(required=False, type='str', default=None),
+            # state
+            state=dict(type="str", default="present",
+                       choices=["present", "absent"]),
+        ),
         supports_check_mode=True,
     )
 
@@ -118,14 +107,13 @@ def main():
     # Get parameters
 
     # general
-    base_vars = get_ipamodule_base_vars(ansible_module)
-    names = module_params_get(ansible_module, "name")
+    names = ansible_module.params_get("name")
 
     # present
-    description = module_params_get(ansible_module, "description")
+    description = ansible_module.params_get("description")
 
     # state
-    state = module_params_get(ansible_module, "state")
+    state = ansible_module.params_get("state")
 
     # Check parameters
 
@@ -148,15 +136,9 @@ def main():
 
     changed = False
     exit_args = {}
-    ccache_dir = None
-    ccache_name = None
-    try:
-        if not valid_creds(ansible_module, base_vars["ipaadmin_principal"]):
-            ccache_dir, ccache_name = temp_kinit(
-                base_vars["ipaadmin_principal"],
-                base_vars["ipaadmin_password"])
-        api_connect()
 
+    # Connect to IPA API
+    with ansible_module.ipa_connect():
         commands = []
         for name in names:
             # Make sure location exists
@@ -194,8 +176,7 @@ def main():
 
         for name, command, args in commands:
             try:
-                result = api_command(ansible_module, command, name,
-                                     args)
+                result = ansible_module.ipa_command(command, name, args)
                 if "completed" in result:
                     if result["completed"] > 0:
                         changed = True
@@ -205,12 +186,6 @@ def main():
                 ansible_module.fail_json(msg="%s: %s: %s" % (command, name,
                                                              str(e)))
 
-    except Exception as e:
-        ansible_module.fail_json(msg=str(e))
-
-    finally:
-        temp_kdestroy(ccache_dir, ccache_name)
-
     # Done
 
     ansible_module.exit_json(changed=changed, **exit_args)
diff --git a/plugins/modules/ipapermission.py b/plugins/modules/ipapermission.py
index 3b2635c897be29434e314433043fb988d63bcb20..5921675f53757fbc18134fc04164ea141a1c1a00 100644
--- a/plugins/modules/ipapermission.py
+++ b/plugins/modules/ipapermission.py
@@ -31,13 +31,9 @@ DOCUMENTATION = """
 module: ipapermission
 short description: Manage FreeIPA permission
 description: Manage FreeIPA permission and permission members
+extends_documentation_fragment:
+  - ipamodule_base_docs
 options:
-  ipaadmin_principal:
-    description: The admin principal.
-    default: admin
-  ipaadmin_password:
-    description: The admin password.
-    required: false
   name:
     description: The permission name string.
     required: true
@@ -133,20 +129,14 @@ RETURN = """
 """
 
 
-from ansible.module_utils.basic import AnsibleModule
 from ansible.module_utils.ansible_freeipa_module import \
-    temp_kinit, temp_kdestroy, valid_creds, api_connect, api_command, \
-    compare_args_ipa, module_params_get, api_check_ipa_version
-import six
-
-if six.PY3:
-    unicode = str
+    IPAAnsibleModule, compare_args_ipa
 
 
 def find_permission(module, name):
     """Find if a permission with the given name already exist."""
     try:
-        _result = api_command(module, "permission_show", name, {"all": True})
+        _result = module.ipa_command("permission_show", name, {"all": True})
     except Exception:  # pylint: disable=broad-except
         # An exception is raised if permission name is not found.
         return None
@@ -191,12 +181,9 @@ def gen_args(right, attrs, bindtype, subtree,
 
 
 def main():
-    ansible_module = AnsibleModule(
+    ansible_module = IPAAnsibleModule(
         argument_spec=dict(
             # general
-            ipaadmin_principal=dict(type="str", default="admin"),
-            ipaadmin_password=dict(type="str", required=False, no_log=True),
-
             name=dict(type="list", aliases=["cn"],
                       default=None, required=True),
             # present
@@ -243,31 +230,27 @@ def main():
     # Get parameters
 
     # general
-    ipaadmin_principal = module_params_get(ansible_module,
-                                           "ipaadmin_principal")
-    ipaadmin_password = module_params_get(ansible_module, "ipaadmin_password")
-    names = module_params_get(ansible_module, "name")
+    names = ansible_module.params_get("name")
 
     # present
-    right = module_params_get(ansible_module, "right")
-    attrs = module_params_get(ansible_module, "attrs")
-    bindtype = module_params_get(ansible_module, "bindtype")
-    subtree = module_params_get(ansible_module, "subtree")
-    extra_target_filter = module_params_get(ansible_module,
-                                            "extra_target_filter")
-    rawfilter = module_params_get(ansible_module, "rawfilter")
-    target = module_params_get(ansible_module, "target")
-    targetto = module_params_get(ansible_module, "targetto")
-    targetfrom = module_params_get(ansible_module, "targetfrom")
-    memberof = module_params_get(ansible_module, "memberof")
-    targetgroup = module_params_get(ansible_module, "targetgroup")
-    object_type = module_params_get(ansible_module, "object_type")
-    no_members = module_params_get(ansible_module, "no_members")
-    rename = module_params_get(ansible_module, "rename")
-    action = module_params_get(ansible_module, "action")
+    right = ansible_module.params_get("right")
+    attrs = ansible_module.params_get("attrs")
+    bindtype = ansible_module.params_get("bindtype")
+    subtree = ansible_module.params_get("subtree")
+    extra_target_filter = ansible_module.params_get("extra_target_filter")
+    rawfilter = ansible_module.params_get("rawfilter")
+    target = ansible_module.params_get("target")
+    targetto = ansible_module.params_get("targetto")
+    targetfrom = ansible_module.params_get("targetfrom")
+    memberof = ansible_module.params_get("memberof")
+    targetgroup = ansible_module.params_get("targetgroup")
+    object_type = ansible_module.params_get("object_type")
+    no_members = ansible_module.params_get("no_members")
+    rename = ansible_module.params_get("rename")
+    action = ansible_module.params_get("action")
 
     # state
-    state = module_params_get(ansible_module, "state")
+    state = ansible_module.params_get("state")
 
     # Check parameters
 
@@ -311,7 +294,7 @@ def main():
                 msg="Argument '%s' can not be used with action "
                 "'%s' and state '%s'" % (x, action, state))
 
-    if bindtype == "self" and api_check_ipa_version("<", "4.8.7"):
+    if bindtype == "self" and ansible_module.ipa_check_version("<", "4.8.7"):
         ansible_module.fail_json(
             msg="Bindtype 'self' is not supported by your IPA version.")
 
@@ -324,13 +307,9 @@ def main():
 
     changed = False
     exit_args = {}
-    ccache_dir = None
-    ccache_name = None
-    try:
-        if not valid_creds(ansible_module, ipaadmin_principal):
-            ccache_dir, ccache_name = temp_kinit(ipaadmin_principal,
-                                                 ipaadmin_password)
-        api_connect()
+
+    # Connect to IPA API
+    with ansible_module.ipa_connect():
 
         commands = []
         for name in names:
@@ -454,8 +433,7 @@ def main():
 
         for name, command, args in commands:
             try:
-                result = api_command(ansible_module, command, name,
-                                     args)
+                result = ansible_module.ipa_command(command, name, args)
                 if "completed" in result:
                     if result["completed"] > 0:
                         changed = True
@@ -480,12 +458,6 @@ def main():
             if len(errors) > 0:
                 ansible_module.fail_json(msg=", ".join(errors))
 
-    except Exception as e:
-        ansible_module.fail_json(msg=str(e))
-
-    finally:
-        temp_kdestroy(ccache_dir, ccache_name)
-
     # Done
 
     ansible_module.exit_json(changed=changed, **exit_args)
diff --git a/plugins/modules/ipaprivilege.py b/plugins/modules/ipaprivilege.py
index 66af01e5a07a1f4850a18c9b49a6bbc84e4203d1..3256eba97f3633a399cfb77c555b57b7d40df0fa 100644
--- a/plugins/modules/ipaprivilege.py
+++ b/plugins/modules/ipaprivilege.py
@@ -34,13 +34,9 @@ DOCUMENTATION = """
 module: ipaprivilege
 short description: Manage FreeIPA privilege
 description: Manage FreeIPA privilege and privilege members
+extends_documentation_fragment:
+  - ipamodule_base_docs
 options:
-  ipaadmin_principal:
-    description: The admin principal.
-    default: admin
-  ipaadmin_password:
-    description: The admin password.
-    required: false
   name:
     description: The list of privilege name strings.
     required: true
@@ -111,10 +107,8 @@ RETURN = """
 """
 
 
-from ansible.module_utils.basic import AnsibleModule
 from ansible.module_utils.ansible_freeipa_module import \
-    temp_kinit, temp_kdestroy, valid_creds, api_connect, api_command, \
-    compare_args_ipa, module_params_get, gen_add_del_lists
+    IPAAnsibleModule, compare_args_ipa, gen_add_del_lists
 import six
 
 if six.PY3:
@@ -124,7 +118,7 @@ if six.PY3:
 def find_privilege(module, name):
     """Find if a privilege with the given name already exist."""
     try:
-        _result = api_command(module, "privilege_show", name, {"all": True})
+        _result = module.ipa_command("privilege_show", name, {"all": True})
     except Exception:  # pylint: disable=broad-except
         # An exception is raised if privilege name is not found.
         return None
@@ -133,12 +127,9 @@ def find_privilege(module, name):
 
 
 def main():
-    ansible_module = AnsibleModule(
+    ansible_module = IPAAnsibleModule(
         argument_spec=dict(
             # general
-            ipaadmin_principal=dict(type="str", default="admin"),
-            ipaadmin_password=dict(type="str", required=False, no_log=True),
-
             name=dict(type="list", aliases=["cn"],
                       default=None, required=True),
             # present
@@ -160,19 +151,16 @@ def main():
     # Get parameters
 
     # general
-    ipaadmin_principal = module_params_get(ansible_module,
-                                           "ipaadmin_principal")
-    ipaadmin_password = module_params_get(ansible_module, "ipaadmin_password")
-    names = module_params_get(ansible_module, "name")
+    names = ansible_module.params_get("name")
 
     # present
-    description = module_params_get(ansible_module, "description")
-    permission = module_params_get(ansible_module, "permission")
-    rename = module_params_get(ansible_module, "rename")
-    action = module_params_get(ansible_module, "action")
+    description = ansible_module.params_get("description")
+    permission = ansible_module.params_get("permission")
+    rename = ansible_module.params_get("rename")
+    action = ansible_module.params_get("action")
 
     # state
-    state = module_params_get(ansible_module, "state")
+    state = ansible_module.params_get("state")
 
     # Check parameters
     invalid = []
@@ -211,13 +199,9 @@ def main():
 
     changed = False
     exit_args = {}
-    ccache_dir = None
-    ccache_name = None
-    try:
-        if not valid_creds(ansible_module, ipaadmin_principal):
-            ccache_dir, ccache_name = temp_kinit(ipaadmin_principal,
-                                                 ipaadmin_password)
-        api_connect()
+
+    # Connect to IPA API
+    with ansible_module.ipa_connect():
 
         commands = []
         for name in names:
@@ -328,8 +312,7 @@ def main():
 
         for name, command, args in commands:
             try:
-                result = api_command(ansible_module, command, name,
-                                     args)
+                result = ansible_module.ipa_command(command, name, args)
                 if "completed" in result:
                     if result["completed"] > 0:
                         changed = True
@@ -354,12 +337,6 @@ def main():
             if len(errors) > 0:
                 ansible_module.fail_json(msg=", ".join(errors))
 
-    except Exception as e:
-        ansible_module.fail_json(msg=str(e))
-
-    finally:
-        temp_kdestroy(ccache_dir, ccache_name)
-
     # Done
 
     ansible_module.exit_json(changed=changed, **exit_args)
diff --git a/plugins/modules/ipapwpolicy.py b/plugins/modules/ipapwpolicy.py
index c9509f4ddf4f16d5863d90427771376e6c9be7cd..758ee6dbe8654702e48a7e357da594bfb0643d34 100644
--- a/plugins/modules/ipapwpolicy.py
+++ b/plugins/modules/ipapwpolicy.py
@@ -111,19 +111,17 @@ EXAMPLES = """
 RETURN = """
 """
 
-from ansible.module_utils.basic import AnsibleModule
-from ansible.module_utils._text import to_text
-from ansible.module_utils.ansible_freeipa_module import temp_kinit, \
-    temp_kdestroy, valid_creds, api_connect, api_command, compare_args_ipa
+from ansible.module_utils.ansible_freeipa_module import \
+    IPAAnsibleModule, compare_args_ipa
 
 
 def find_pwpolicy(module, name):
     _args = {
         "all": True,
-        "cn": to_text(name),
+        "cn": name,
     }
 
-    _result = api_command(module, "pwpolicy_find", to_text(name), _args)
+    _result = module.ipa_command("pwpolicy_find", name, _args)
 
     if len(_result["result"]) > 1:
         module.fail_json(
@@ -160,12 +158,9 @@ def gen_args(maxlife, minlife, history, minclasses, minlength, priority,
 
 
 def main():
-    ansible_module = AnsibleModule(
+    ansible_module = IPAAnsibleModule(
         argument_spec=dict(
             # general
-            ipaadmin_principal=dict(type="str", default="admin"),
-            ipaadmin_password=dict(type="str", required=False, no_log=True),
-
             name=dict(type="list", aliases=["cn"], default=None,
                       required=False),
             # present
@@ -198,28 +193,26 @@ def main():
     # Get parameters
 
     # general
-    ipaadmin_principal = ansible_module.params.get("ipaadmin_principal")
-    ipaadmin_password = ansible_module.params.get("ipaadmin_password")
-    names = ansible_module.params.get("name")
+    names = ansible_module.params_get("name")
 
     # present
-    maxlife = ansible_module.params.get("maxlife")
-    minlife = ansible_module.params.get("minlife")
-    history = ansible_module.params.get("history")
-    minclasses = ansible_module.params.get("minclasses")
-    minlength = ansible_module.params.get("minlength")
-    priority = ansible_module.params.get("priority")
-    maxfail = ansible_module.params.get("maxfail")
-    failinterval = ansible_module.params.get("failinterval")
-    lockouttime = ansible_module.params.get("lockouttime")
+    maxlife = ansible_module.params_get("maxlife")
+    minlife = ansible_module.params_get("minlife")
+    history = ansible_module.params_get("history")
+    minclasses = ansible_module.params_get("minclasses")
+    minlength = ansible_module.params_get("minlength")
+    priority = ansible_module.params_get("priority")
+    maxfail = ansible_module.params_get("maxfail")
+    failinterval = ansible_module.params_get("failinterval")
+    lockouttime = ansible_module.params_get("lockouttime")
 
     # state
-    state = ansible_module.params.get("state")
+    state = ansible_module.params_get("state")
 
     # Check parameters
 
     if names is None:
-        names = ["global_policy"]
+        names = [u"global_policy"]
 
     if state == "present":
         if len(names) != 1:
@@ -245,13 +238,8 @@ def main():
 
     changed = False
     exit_args = {}
-    ccache_dir = None
-    ccache_name = None
-    try:
-        if not valid_creds(ansible_module, ipaadmin_principal):
-            ccache_dir, ccache_name = temp_kinit(ipaadmin_principal,
-                                                 ipaadmin_password)
-        api_connect()
+
+    with ansible_module.ipa_connect():
 
         commands = []
 
@@ -292,18 +280,12 @@ def main():
 
         for name, command, args in commands:
             try:
-                api_command(ansible_module, command, to_text(name), args)
+                ansible_module.ipa_command(command, name, args)
                 changed = True
             except Exception as e:
                 ansible_module.fail_json(msg="%s: %s: %s" % (command, name,
                                                              str(e)))
 
-    except Exception as e:
-        ansible_module.fail_json(msg=str(e))
-
-    finally:
-        temp_kdestroy(ccache_dir, ccache_name)
-
     # Done
 
     ansible_module.exit_json(changed=changed, **exit_args)
diff --git a/plugins/modules/iparole.py b/plugins/modules/iparole.py
index 31fa2f85e3aea9f3ceddd08eb60da8ce18a5ce22..4a073e6da9bdda77a75b294afe0edd2ea7502394 100644
--- a/plugins/modules/iparole.py
+++ b/plugins/modules/iparole.py
@@ -33,13 +33,9 @@ DOCUMENTATION = """
 module: iparole
 short description: Manage FreeIPA role
 description: Manage FreeIPA role
+extends_documentation_fragment:
+  - ipamodule_base_docs
 options:
-  ipaadmin_principal:
-    description: The admin principal.
-    default: admin
-  ipaadmin_password:
-    description: The admin password.
-    required: false
   role:
     description: The list of role name strings.
     required: true
@@ -102,11 +98,9 @@ EXAMPLES = """
 # pylint: disable=wrong-import-position
 # pylint: disable=import-error
 # pylint: disable=no-name-in-module
-from ansible.module_utils.basic import AnsibleModule
 from ansible.module_utils._text import to_text
 from ansible.module_utils.ansible_freeipa_module import \
-    temp_kinit, temp_kdestroy, valid_creds, api_connect, api_command, \
-    gen_add_del_lists, compare_args_ipa, module_params_get, api_get_realm
+    IPAAnsibleModule, gen_add_del_lists, compare_args_ipa
 import six
 
 
@@ -117,7 +111,7 @@ if six.PY3:
 def find_role(module, name):
     """Find if a role with the given name already exist."""
     try:
-        _result = api_command(module, "role_show", name, {"all": True})
+        _result = module.ipa_command("role_show", name, {"all": True})
     except Exception:  # pylint: disable=broad-except
         # An exception is raised if role name is not found.
         return None
@@ -134,7 +128,7 @@ def gen_args(module):
     args = {}
 
     for param, arg in arg_map.items():
-        value = module_params_get(module, param)
+        value = module.params_get(param)
         if value is not None:
             args[arg] = value
 
@@ -143,8 +137,8 @@ def gen_args(module):
 
 def check_parameters(module):
     """Check if parameters passed for module processing are valid."""
-    action = module_params_get(module, "action")
-    state = module_params_get(module, "state")
+    action = module.params_get("action")
+    state = module.params_get("state")
 
     invalid = []
 
@@ -158,30 +152,15 @@ def check_parameters(module):
             invalid.extend(['privilege'])
 
     for arg in invalid:
-        if module_params_get(module, arg) is not None:
+        if module.params_get(arg) is not None:
             module.fail_json(
                 msg="Argument '%s' can not be used with action '%s'" %
                 (arg, state))
 
 
-def verify_credentials(module):
-    """Ensure there are valid Kerberos credentials."""
-    ccache_dir = None
-    ccache_name = None
-
-    ipaadmin_principal = module_params_get(module, "ipaadmin_principal")
-    ipaadmin_password = module_params_get(module, "ipaadmin_password")
-
-    if not valid_creds(module, ipaadmin_principal):
-        ccache_dir, ccache_name = temp_kinit(ipaadmin_principal,
-                                             ipaadmin_password)
-
-    return (ccache_dir, ccache_name)
-
-
 def member_intersect(module, attr, memberof, res_find):
     """Filter member arguments from role found by intersection."""
-    params = module_params_get(module, attr)
+    params = module.params_get(attr)
     if not res_find:
         return params
     filtered = []
@@ -193,7 +172,7 @@ def member_intersect(module, attr, memberof, res_find):
 
 def member_difference(module, attr, memberof, res_find):
     """Filter member arguments from role found by difference."""
-    params = module_params_get(module, attr)
+    params = module.params_get(attr)
     if not res_find:
         return params
     filtered = []
@@ -248,7 +227,7 @@ def filter_service(module, res_find, predicate):
     modified service to be compared to.
     """
     _services = []
-    service = module_params_get(module, 'service')
+    service = module.params_get('service')
     if service:
         existing = [to_text(x) for x in res_find.get('member_service', [])]
         for svc in service:
@@ -262,7 +241,7 @@ def ensure_role_with_members_is_present(module, name, res_find, action):
     """Define commands to ensure member are present for action `role`."""
     commands = []
     privilege_add, privilege_del = gen_add_del_lists(
-        module_params_get(module, "privilege"),
+        module.params_get("privilege"),
         res_find.get('memberof_privilege', []))
 
     if privilege_add:
@@ -277,7 +256,7 @@ def ensure_role_with_members_is_present(module, name, res_find, action):
 
     for key in ["user", "group", "host", "hostgroup"]:
         add_list, del_list = gen_add_del_lists(
-            module_params_get(module, key),
+            module.params_get(key),
             res_find.get('member_%s' % key, [])
         )
         if add_list:
@@ -286,8 +265,10 @@ def ensure_role_with_members_is_present(module, name, res_find, action):
             del_members[key] = [to_text(item) for item in del_list]
 
     service = [
-        to_text(svc) if '@' in svc else ('%s@%s' % (svc, api_get_realm()))
-        for svc in (module_params_get(module, 'service') or [])
+        to_text(svc)
+        if '@' in svc
+        else ('%s@%s' % (svc, module.ipa_get_realm()))
+        for svc in (module.params_get('service') or [])
     ]
     existing = [str(svc) for svc in res_find.get('member_service', [])]
     add_list, del_list = gen_add_del_lists(service, existing)
@@ -364,7 +345,7 @@ def process_commands(module, commands):
 
     for name, command, args in commands:
         try:
-            result = api_command(module, command, name, args)
+            result = module.ipa_command(command, name, args)
             if "completed" in result:
                 if result["completed"] > 0:
                     changed = True
@@ -386,7 +367,7 @@ def role_commands_for_name(module, state, action, name):
     """Define commands for the Role module."""
     commands = []
 
-    rename = module_params_get(module, "rename")
+    rename = module.params_get("rename")
 
     res_find = find_role(module, name)
 
@@ -421,12 +402,9 @@ def role_commands_for_name(module, state, action, name):
 
 def create_module():
     """Create module description."""
-    ansible_module = AnsibleModule(
+    ansible_module = IPAAnsibleModule(
         argument_spec=dict(
             # generalgroups
-            ipaadmin_principal=dict(type="str", default="admin"),
-            ipaadmin_password=dict(type="str", required=False, no_log=True),
-
             name=dict(type="list", aliases=["cn"], default=None,
                       required=True),
             # present
@@ -463,15 +441,13 @@ def main():
     check_parameters(ansible_module)
 
     # Init
-    ccache_dir = None
-    ccache_name = None
-    try:
-        ccache_dir, ccache_name = verify_credentials(ansible_module)
-        api_connect()
 
-        state = module_params_get(ansible_module, "state")
-        action = module_params_get(ansible_module, "action")
-        names = module_params_get(ansible_module, "name")
+    # Connect to IPA API
+    with ansible_module.ipa_connect():
+
+        state = ansible_module.params_get("state")
+        action = ansible_module.params_get("action")
+        names = ansible_module.params_get("name")
         commands = []
 
         for name in names:
@@ -480,12 +456,6 @@ def main():
 
         changed, exit_args = process_commands(ansible_module, commands)
 
-    except Exception as exception:  # pylint: disable=broad-except
-        ansible_module.fail_json(msg=str(exception))
-
-    finally:
-        temp_kdestroy(ccache_dir, ccache_name)
-
     # Done
     ansible_module.exit_json(changed=changed, **exit_args)
 
diff --git a/plugins/modules/ipaselfservice.py b/plugins/modules/ipaselfservice.py
index 801a8f54fd26fbc8808fb96cfd5f2d5fb6a071ab..81b4461ce87ef6f627fc13defa42eef8280c68c3 100644
--- a/plugins/modules/ipaselfservice.py
+++ b/plugins/modules/ipaselfservice.py
@@ -31,13 +31,9 @@ DOCUMENTATION = """
 module: ipaselfservice
 short description: Manage FreeIPA selfservices
 description: Manage FreeIPA selfservices and selfservice attributes
+extends_documentation_fragment:
+  - ipamodule_base_docs
 options:
-  ipaadmin_principal:
-    description: The admin principal.
-    default: admin
-  ipaadmin_password:
-    description: The admin password.
-    required: false
   name:
     description: The list of selfservice name strings.
     required: true
@@ -103,21 +99,14 @@ RETURN = """
 """
 
 
-from ansible.module_utils.basic import AnsibleModule
 from ansible.module_utils.ansible_freeipa_module import \
-    temp_kinit, temp_kdestroy, valid_creds, api_connect, api_command, \
-    compare_args_ipa, module_params_get
-import six
-
-
-if six.PY3:
-    unicode = str
+    IPAAnsibleModule, compare_args_ipa
 
 
 def find_selfservice(module, name):
     """Find if a selfservice with the given name already exist."""
     try:
-        _result = api_command(module, "selfservice_show", name, {"all": True})
+        _result = module.ipa_command("selfservice_show", name, {"all": True})
     except Exception:  # pylint: disable=broad-except
         # An exception is raised if selfservice name is not found.
         return None
@@ -135,12 +124,9 @@ def gen_args(permission, attribute):
 
 
 def main():
-    ansible_module = AnsibleModule(
+    ansible_module = IPAAnsibleModule(
         argument_spec=dict(
             # general
-            ipaadmin_principal=dict(type="str", default="admin"),
-            ipaadmin_password=dict(type="str", required=False, no_log=True),
-
             name=dict(type="list", aliases=["aciname"], default=None,
                       required=True),
             # present
@@ -162,17 +148,14 @@ def main():
     # Get parameters
 
     # general
-    ipaadmin_principal = module_params_get(ansible_module,
-                                           "ipaadmin_principal")
-    ipaadmin_password = module_params_get(ansible_module, "ipaadmin_password")
-    names = module_params_get(ansible_module, "name")
+    names = ansible_module.params_get("name")
 
     # present
-    permission = module_params_get(ansible_module, "permission")
-    attribute = module_params_get(ansible_module, "attribute")
-    action = module_params_get(ansible_module, "action")
+    permission = ansible_module.params_get("permission")
+    attribute = ansible_module.params_get("attribute")
+    action = ansible_module.params_get("action")
     # state
-    state = module_params_get(ansible_module, "state")
+    state = ansible_module.params_get("state")
 
     # Check parameters
 
@@ -219,13 +202,9 @@ def main():
 
     changed = False
     exit_args = {}
-    ccache_dir = None
-    ccache_name = None
-    try:
-        if not valid_creds(ansible_module, ipaadmin_principal):
-            ccache_dir, ccache_name = temp_kinit(ipaadmin_principal,
-                                                 ipaadmin_password)
-        api_connect()
+
+    # Connect to IPA API
+    with ansible_module.ipa_connect():
 
         commands = []
         for name in names:
@@ -301,8 +280,7 @@ def main():
 
         for name, command, args in commands:
             try:
-                result = api_command(ansible_module, command, name,
-                                     args)
+                result = ansible_module.ipa_command(command, name, args)
                 if "completed" in result:
                     if result["completed"] > 0:
                         changed = True
@@ -312,12 +290,6 @@ def main():
                 ansible_module.fail_json(msg="%s: %s: %s" % (command, name,
                                                              str(e)))
 
-    except Exception as e:
-        ansible_module.fail_json(msg=str(e))
-
-    finally:
-        temp_kdestroy(ccache_dir, ccache_name)
-
     # Done
 
     ansible_module.exit_json(changed=changed, **exit_args)
diff --git a/plugins/modules/ipaserver.py b/plugins/modules/ipaserver.py
index 167394e372bd1f6de15bc82009e8cf9fc43f418a..c1bfe119af8dcbe382aa9b27678c3db9edf6d525 100644
--- a/plugins/modules/ipaserver.py
+++ b/plugins/modules/ipaserver.py
@@ -31,13 +31,9 @@ DOCUMENTATION = """
 module: ipaserver
 short description: Manage FreeIPA server
 description: Manage FreeIPA server
+extends_documentation_fragment:
+  - ipamodule_base_docs
 options:
-  ipaadmin_principal:
-    description: The admin principal.
-    default: admin
-  ipaadmin_password:
-    description: The admin password.
-    required: false
   name:
     description: The list of server name strings.
     required: true
@@ -184,20 +180,14 @@ RETURN = """
 """
 
 
-from ansible.module_utils.basic import AnsibleModule
 from ansible.module_utils.ansible_freeipa_module import \
-    temp_kinit, temp_kdestroy, valid_creds, api_connect, api_command, \
-    api_command_no_name, compare_args_ipa, module_params_get, DNSName
-import six
-
-if six.PY3:
-    unicode = str
+    IPAAnsibleModule, compare_args_ipa, DNSName
 
 
 def find_server(module, name):
     """Find if a server with the given name already exist."""
     try:
-        _result = api_command(module, "server_show", name, {"all": True})
+        _result = module.ipa_command("server_show", name, {"all": True})
     except Exception:  # pylint: disable=broad-except
         # An exception is raised if server name is not found.
         return None
@@ -208,12 +198,12 @@ def find_server(module, name):
 def server_role_status(module, name):
     """Get server role of a hidden server with the given name."""
     try:
-        _result = api_command_no_name(module, "server_role_find",
-                                      {"server_server": name,
-                                       "role_servrole": 'IPA master',
-                                       "include_master": True,
-                                       "raw": True,
-                                       "all": True})
+        _result = module.ipa_command_no_name("server_role_find",
+                                             {"server_server": name,
+                                              "role_servrole": 'IPA master',
+                                              "include_master": True,
+                                              "raw": True,
+                                              "all": True})
     except Exception:  # pylint: disable=broad-except
         # An exception is raised if server name is not found.
         return None
@@ -246,12 +236,9 @@ def gen_args(location, service_weight, no_members, delete_continue,
 
 
 def main():
-    ansible_module = AnsibleModule(
+    ansible_module = IPAAnsibleModule(
         argument_spec=dict(
             # general
-            ipaadmin_principal=dict(type="str", default="admin"),
-            ipaadmin_password=dict(type="str", required=False, no_log=True),
-
             name=dict(type="list", aliases=["cn"],
                       default=None, required=True),
             # present
@@ -282,14 +269,11 @@ def main():
     # Get parameters
 
     # general
-    ipaadmin_principal = module_params_get(ansible_module,
-                                           "ipaadmin_principal")
-    ipaadmin_password = module_params_get(ansible_module, "ipaadmin_password")
-    names = module_params_get(ansible_module, "name")
+    names = ansible_module.params_get("name")
 
     # present
-    location = module_params_get(ansible_module, "location")
-    service_weight = module_params_get(ansible_module, "service_weight")
+    location = ansible_module.params_get("location")
+    service_weight = ansible_module.params_get("service_weight")
     # Service weight smaller than 0 leads to resetting service weight
     if service_weight is not None and \
        (service_weight < -1 or service_weight > 65535):
@@ -298,19 +282,18 @@ def main():
             service_weight)
     if service_weight == -1:
         service_weight = ""
-    hidden = module_params_get(ansible_module, "hidden")
-    no_members = module_params_get(ansible_module, "no_members")
+    hidden = ansible_module.params_get("hidden")
+    no_members = ansible_module.params_get("no_members")
 
     # absent
-    delete_continue = module_params_get(ansible_module, "delete_continue")
-    ignore_topology_disconnect = module_params_get(
-        ansible_module, "ignore_topology_disconnect")
-    ignore_last_of_role = module_params_get(ansible_module,
-                                            "ignore_last_of_role")
-    force = module_params_get(ansible_module, "force")
+    delete_continue = ansible_module.params_get("delete_continue")
+    ignore_topology_disconnect = ansible_module.params_get(
+        "ignore_topology_disconnect")
+    ignore_last_of_role = ansible_module.params_get("ignore_last_of_role")
+    force = ansible_module.params_get("force")
 
     # state
-    state = module_params_get(ansible_module, "state")
+    state = ansible_module.params_get("state")
 
     # Check parameters
 
@@ -338,13 +321,9 @@ def main():
 
     changed = False
     exit_args = {}
-    ccache_dir = None
-    ccache_name = None
-    try:
-        if not valid_creds(ansible_module, ipaadmin_principal):
-            ccache_dir, ccache_name = temp_kinit(ipaadmin_principal,
-                                                 ipaadmin_password)
-        api_connect()
+
+    # Connect to IPA API
+    with ansible_module.ipa_connect():
 
         commands = []
         for name in names:
@@ -414,8 +393,7 @@ def main():
 
         for name, command, args in commands:
             try:
-                result = api_command(ansible_module, command, name,
-                                     args)
+                result = ansible_module.ipa_command(command, name, args)
                 if "completed" in result:
                     if result["completed"] > 0:
                         changed = True
@@ -425,12 +403,6 @@ def main():
                 ansible_module.fail_json(msg="%s: %s: %s" % (command, name,
                                                              str(e)))
 
-    except Exception as e:
-        ansible_module.fail_json(msg=str(e))
-
-    finally:
-        temp_kdestroy(ccache_dir, ccache_name)
-
     # Done
 
     ansible_module.exit_json(changed=changed, **exit_args)
diff --git a/plugins/modules/ipaservice.py b/plugins/modules/ipaservice.py
index 50081be5340492e24e6e157d9920d642b33085e2..b72e09150d36f226121002d83ab7aed36f2e8185 100644
--- a/plugins/modules/ipaservice.py
+++ b/plugins/modules/ipaservice.py
@@ -32,13 +32,9 @@ DOCUMENTATION = """
 module: ipaservice
 short description: Manage FreeIPA service
 description: Manage FreeIPA service
+extends_documentation_fragment:
+  - ipamodule_base_docs
 options:
-  ipaadmin_principal:
-    description: The admin principal
-    default: admin
-  ipaadmin_password:
-    description: The admin password
-    required: false
   name:
     description: The service to manage
     required: true
@@ -226,11 +222,9 @@ EXAMPLES = """
 RETURN = """
 """
 
-from ansible.module_utils.basic import AnsibleModule
-from ansible.module_utils.ansible_freeipa_module import temp_kinit, \
-    temp_kdestroy, valid_creds, api_connect, api_command, compare_args_ipa, \
-    encode_certificate, gen_add_del_lists, module_params_get, to_text, \
-    api_check_param, ipalib_errors
+from ansible.module_utils.ansible_freeipa_module import \
+    IPAAnsibleModule, compare_args_ipa, encode_certificate, \
+    gen_add_del_lists, ipalib_errors
 
 
 def find_service(module, name):
@@ -239,7 +233,7 @@ def find_service(module, name):
     }
 
     try:
-        _result = api_command(module, "service_show", to_text(name), _args)
+        _result = module.ipa_command("service_show", name, _args)
     except ipalib_errors.NotFound:
         return None
 
@@ -349,12 +343,9 @@ def check_parameters(module, state, action, names, parameters):
 
 
 def init_ansible_module():
-    ansible_module = AnsibleModule(
+    ansible_module = IPAAnsibleModule(
         argument_spec=dict(
             # general
-            ipaadmin_principal=dict(type="str", default="admin"),
-            ipaadmin_password=dict(type="str", required=False, no_log=True),
-
             name=dict(type="list", aliases=["service"], default=None,
                       required=True),
             # service attributesstr
@@ -424,51 +415,48 @@ def main():
     # Get parameters
 
     # general
-    ipaadmin_principal = module_params_get(ansible_module,
-                                           "ipaadmin_principal")
-    ipaadmin_password = module_params_get(ansible_module, "ipaadmin_password")
-    names = module_params_get(ansible_module, "name")
+    names = ansible_module.params_get("name")
 
     # service attributes
-    principal = module_params_get(ansible_module, "principal")
-    certificate = module_params_get(ansible_module, "certificate")
-    pac_type = module_params_get(ansible_module, "pac_type")
-    auth_ind = module_params_get(ansible_module, "auth_ind")
-    skip_host_check = module_params_get(ansible_module, "skip_host_check")
-    force = module_params_get(ansible_module, "force")
-    requires_pre_auth = module_params_get(ansible_module, "requires_pre_auth")
-    ok_as_delegate = module_params_get(ansible_module, "ok_as_delegate")
-    ok_to_auth_as_delegate = module_params_get(ansible_module,
-                                               "ok_to_auth_as_delegate")
-
-    smb = module_params_get(ansible_module, "smb")
-    netbiosname = module_params_get(ansible_module, "netbiosname")
-
-    host = module_params_get(ansible_module, "host")
-
-    allow_create_keytab_user = module_params_get(
-        ansible_module, "allow_create_keytab_user")
-    allow_create_keytab_group = module_params_get(
-        ansible_module, "allow_create_keytab_group")
-    allow_create_keytab_host = module_params_get(
-        ansible_module, "allow_create_keytab_host")
-    allow_create_keytab_hostgroup = module_params_get(
-        ansible_module, "allow_create_keytab_hostgroup")
-
-    allow_retrieve_keytab_user = module_params_get(
-        ansible_module, "allow_retrieve_keytab_user")
-    allow_retrieve_keytab_group = module_params_get(
-        ansible_module, "allow_retrieve_keytab_group")
-    allow_retrieve_keytab_host = module_params_get(
-        ansible_module, "allow_retrieve_keytab_host")
-    allow_retrieve_keytab_hostgroup = module_params_get(
-        ansible_module, "allow_retrieve_keytab_hostgroup")
-    delete_continue = module_params_get(ansible_module, "delete_continue")
+    principal = ansible_module.params_get("principal")
+    certificate = ansible_module.params_get("certificate")
+    pac_type = ansible_module.params_get("pac_type")
+    auth_ind = ansible_module.params_get("auth_ind")
+    skip_host_check = ansible_module.params_get("skip_host_check")
+    force = ansible_module.params_get("force")
+    requires_pre_auth = ansible_module.params_get("requires_pre_auth")
+    ok_as_delegate = ansible_module.params_get("ok_as_delegate")
+    ok_to_auth_as_delegate = ansible_module.params_get(
+        "ok_to_auth_as_delegate")
+
+    smb = ansible_module.params_get("smb")
+    netbiosname = ansible_module.params_get("netbiosname")
+
+    host = ansible_module.params_get("host")
+
+    allow_create_keytab_user = ansible_module.params_get(
+        "allow_create_keytab_user")
+    allow_create_keytab_group = ansible_module.params_get(
+        "allow_create_keytab_group")
+    allow_create_keytab_host = ansible_module.params_get(
+        "allow_create_keytab_host")
+    allow_create_keytab_hostgroup = ansible_module.params_get(
+        "allow_create_keytab_hostgroup")
+
+    allow_retrieve_keytab_user = ansible_module.params_get(
+        "allow_retrieve_keytab_user")
+    allow_retrieve_keytab_group = ansible_module.params_get(
+        "allow_retrieve_keytab_group")
+    allow_retrieve_keytab_host = ansible_module.params_get(
+        "allow_retrieve_keytab_host")
+    allow_retrieve_keytab_hostgroup = ansible_module.params_get(
+        "allow_retrieve_keytab_hostgroup")
+    delete_continue = ansible_module.params_get("delete_continue")
 
     # action
-    action = module_params_get(ansible_module, "action")
+    action = ansible_module.params_get("action")
     # state
-    state = module_params_get(ansible_module, "state")
+    state = ansible_module.params_get("state")
 
     # check parameters
     check_parameters(ansible_module, state, action, names, vars())
@@ -477,15 +465,11 @@ def main():
 
     changed = False
     exit_args = {}
-    ccache_dir = None
-    ccache_name = None
-    try:
-        if not valid_creds(ansible_module, ipaadmin_principal):
-            ccache_dir, ccache_name = temp_kinit(ipaadmin_principal,
-                                                 ipaadmin_password)
-        api_connect()
 
-        has_skip_host_check = api_check_param(
+    # Connect to IPA API
+    with ansible_module.ipa_connect():
+
+        has_skip_host_check = ansible_module.ipa_command_param_exists(
             "service_add", "skip_host_check")
         if skip_host_check and not has_skip_host_check:
             ansible_module.fail_json(
@@ -850,7 +834,7 @@ def main():
         errors = []
         for name, command, args in commands:
             try:
-                result = api_command(ansible_module, command, name, args)
+                result = ansible_module.ipa_command(command, name, args)
 
                 if "completed" in result:
                     if result["completed"] > 0:
@@ -876,12 +860,6 @@ def main():
         if len(errors) > 0:
             ansible_module.fail_json(msg=", ".join(errors))
 
-    except Exception as ex:
-        ansible_module.fail_json(msg=str(ex))
-
-    finally:
-        temp_kdestroy(ccache_dir, ccache_name)
-
     # Done
     ansible_module.exit_json(changed=changed, **exit_args)
 
diff --git a/plugins/modules/ipasudocmd.py b/plugins/modules/ipasudocmd.py
index 32a25ab0fc08cf294fabe04fde70f0aace3b58d0..1785e78e891a9916e408ac3e03b45ce98dc9aa73 100644
--- a/plugins/modules/ipasudocmd.py
+++ b/plugins/modules/ipasudocmd.py
@@ -32,13 +32,9 @@ DOCUMENTATION = """
 module: ipasudocmd
 short description: Manage FreeIPA sudo command
 description: Manage FreeIPA sudo command
+extends_documentation_fragment:
+  - ipamodule_base_docs
 options:
-  ipaadmin_principal:
-    description: The admin principal
-    default: admin
-  ipaadmin_password:
-    description: The admin password
-    required: false
   name:
     description: The sudo command
     required: true
@@ -71,19 +67,17 @@ EXAMPLES = """
 RETURN = """
 """
 
-from ansible.module_utils.basic import AnsibleModule
-from ansible.module_utils._text import to_text
-from ansible.module_utils.ansible_freeipa_module import temp_kinit, \
-    temp_kdestroy, valid_creds, api_connect, api_command, compare_args_ipa
+from ansible.module_utils.ansible_freeipa_module import \
+    IPAAnsibleModule, compare_args_ipa
 
 
 def find_sudocmd(module, name):
     _args = {
         "all": True,
-        "sudocmd": to_text(name),
+        "sudocmd": name,
     }
 
-    _result = api_command(module, "sudocmd_find", to_text(name), _args)
+    _result = module.ipa_command("sudocmd_find", name, _args)
 
     if len(_result["result"]) > 1:
         module.fail_json(
@@ -97,18 +91,15 @@ def find_sudocmd(module, name):
 def gen_args(description):
     _args = {}
     if description is not None:
-        _args["description"] = to_text(description)
+        _args["description"] = description
 
     return _args
 
 
 def main():
-    ansible_module = AnsibleModule(
+    ansible_module = IPAAnsibleModule(
         argument_spec=dict(
             # general
-            ipaadmin_principal=dict(type="str", default="admin"),
-            ipaadmin_password=dict(type="str", required=False, no_log=True),
-
             name=dict(type="list", aliases=["sudocmd"], default=None,
                       required=True),
             # present
@@ -125,14 +116,12 @@ def main():
     # Get parameters
 
     # general
-    ipaadmin_principal = ansible_module.params.get("ipaadmin_principal")
-    ipaadmin_password = ansible_module.params.get("ipaadmin_password")
-    names = ansible_module.params.get("name")
+    names = ansible_module.params_get("name")
 
     # present
-    description = ansible_module.params.get("description")
+    description = ansible_module.params_get("description")
     # state
-    state = ansible_module.params.get("state")
+    state = ansible_module.params_get("state")
 
     # Check parameters
     if state == "absent":
@@ -147,13 +136,9 @@ def main():
 
     changed = False
     exit_args = {}
-    ccache_dir = None
-    ccache_name = None
-    try:
-        if not valid_creds(ansible_module, ipaadmin_principal):
-            ccache_dir, ccache_name = temp_kinit(ipaadmin_principal,
-                                                 ipaadmin_password)
-        api_connect()
+
+    # Connect to IPA API
+    with ansible_module.ipa_connect():
 
         commands = []
 
@@ -189,8 +174,7 @@ def main():
         # Execute commands
         for name, command, args in commands:
             try:
-                result = api_command(ansible_module, command, to_text(name),
-                                     args)
+                result = ansible_module.ipa_command(command, name, args)
                 # Check if any changes were made by any command
                 if command == 'sudocmd_del':
                     changed |= "Deleted" in result['summary']
@@ -200,12 +184,6 @@ def main():
                 ansible_module.fail_json(msg="%s: %s: %s" % (command, name,
                                                              str(e)))
 
-    except Exception as e:
-        ansible_module.fail_json(msg=str(e))
-
-    finally:
-        temp_kdestroy(ccache_dir, ccache_name)
-
     # Done
 
     ansible_module.exit_json(changed=changed, **exit_args)
diff --git a/plugins/modules/ipasudocmdgroup.py b/plugins/modules/ipasudocmdgroup.py
index 3d9e738b91920ccc03f202572f7ff37df69818c0..8a77596eeb953c8141e803bf097cbbb0f92d87a4 100644
--- a/plugins/modules/ipasudocmdgroup.py
+++ b/plugins/modules/ipasudocmdgroup.py
@@ -32,13 +32,9 @@ DOCUMENTATION = """
 module: ipasudocmdgroup
 short description: Manage FreeIPA sudocmd groups
 description: Manage FreeIPA sudocmd groups
+extends_documentation_fragment:
+  - ipamodule_base_docs
 options:
-  ipaadmin_principal:
-    description: The admin principal
-    default: admin
-  ipaadmin_password:
-    description: The admin password
-    required: false
   name:
     description: The sudocmodgroup name
     required: false
@@ -103,18 +99,15 @@ EXAMPLES = """
 RETURN = """
 """
 
-from ansible.module_utils.basic import AnsibleModule
-from ansible.module_utils._text import to_text
-from ansible.module_utils.ansible_freeipa_module import temp_kinit, \
-    temp_kdestroy, valid_creds, api_connect, api_command, compare_args_ipa, \
-    gen_add_del_lists, ipalib_errors
+from ansible.module_utils.ansible_freeipa_module import \
+    IPAAnsibleModule, compare_args_ipa, gen_add_del_lists, ipalib_errors
 
 
 def find_sudocmdgroup(module, name):
     args = {"all": True}
 
     try:
-        _result = api_command(module, "sudocmdgroup_show", to_text(name), args)
+        _result = module.ipa_command("sudocmdgroup_show", name, args)
     except ipalib_errors.NotFound:
         return None
     else:
@@ -124,7 +117,7 @@ def find_sudocmdgroup(module, name):
 def gen_args(description, nomembers):
     _args = {}
     if description is not None:
-        _args["description"] = to_text(description)
+        _args["description"] = description
     if nomembers is not None:
         _args["nomembers"] = nomembers
 
@@ -140,12 +133,9 @@ def gen_member_args(sudocmd):
 
 
 def main():
-    ansible_module = AnsibleModule(
+    ansible_module = IPAAnsibleModule(
         argument_spec=dict(
             # general
-            ipaadmin_principal=dict(type="str", default="admin"),
-            ipaadmin_password=dict(type="str", required=False, no_log=True),
-
             name=dict(type="list", aliases=["cn"], default=None,
                       required=True),
             # present
@@ -166,17 +156,15 @@ def main():
     # Get parameters
 
     # general
-    ipaadmin_principal = ansible_module.params.get("ipaadmin_principal")
-    ipaadmin_password = ansible_module.params.get("ipaadmin_password")
-    names = ansible_module.params.get("name")
+    names = ansible_module.params_get("name")
 
     # present
-    description = ansible_module.params.get("description")
-    nomembers = ansible_module.params.get("nomembers")
-    sudocmd = ansible_module.params.get("sudocmd")
-    action = ansible_module.params.get("action")
+    description = ansible_module.params_get("description")
+    nomembers = ansible_module.params_get("nomembers")
+    sudocmd = ansible_module.params_get("sudocmd")
+    action = ansible_module.params_get("action")
     # state
-    state = ansible_module.params.get("state")
+    state = ansible_module.params_get("state")
 
     # Check parameters
 
@@ -209,13 +197,9 @@ def main():
 
     changed = False
     exit_args = {}
-    ccache_dir = None
-    ccache_name = None
-    try:
-        if not valid_creds(ansible_module, ipaadmin_principal):
-            ccache_dir, ccache_name = temp_kinit(ipaadmin_principal,
-                                                 ipaadmin_password)
-        api_connect()
+
+    # Connect to IPA API
+    with ansible_module.ipa_connect():
 
         commands = []
 
@@ -255,9 +239,7 @@ def main():
                         if len(sudocmd_add) > 0:
                             commands.append([name, "sudocmdgroup_add_member",
                                              {
-                                                 "sudocmd": [to_text(c)
-                                                             for c in
-                                                             sudocmd_add]
+                                                 "sudocmd": sudocmd_add
                                              }
                                              ])
                         # Remove members
@@ -265,9 +247,7 @@ def main():
                             commands.append([name,
                                              "sudocmdgroup_remove_member",
                                              {
-                                                 "sudocmd": [to_text(c)
-                                                             for c in
-                                                             sudocmd_del]
+                                                 "sudocmd": sudocmd_del
                                              }
                                              ])
                 elif action == "member":
@@ -277,7 +257,7 @@ def main():
 
                     # Ensure members are present
                     commands.append([name, "sudocmdgroup_add_member",
-                                     {"sudocmd": [to_text(c) for c in sudocmd]}
+                                     {"sudocmd": sudocmd}
                                      ])
             elif state == "absent":
                 if action == "sudocmdgroup":
@@ -291,7 +271,7 @@ def main():
 
                     # Ensure members are absent
                     commands.append([name, "sudocmdgroup_remove_member",
-                                     {"sudocmd": [to_text(c) for c in sudocmd]}
+                                     {"sudocmd": sudocmd}
                                      ])
             else:
                 ansible_module.fail_json(msg="Unkown state '%s'" % state)
@@ -303,8 +283,7 @@ def main():
         # Execute commands
         for name, command, args in commands:
             try:
-                result = api_command(ansible_module, command, to_text(name),
-                                     args)
+                result = ansible_module.ipa_command(command, name, args)
                 if action == "member":
                     if "completed" in result and result["completed"] > 0:
                         changed = True
@@ -331,12 +310,6 @@ def main():
             if len(errors) > 0:
                 ansible_module.fail_json(msg=", ".join(errors))
 
-    except Exception as e:
-        ansible_module.fail_json(msg=str(e))
-
-    finally:
-        temp_kdestroy(ccache_dir, ccache_name)
-
     # Done
 
     ansible_module.exit_json(changed=changed, **exit_args)
diff --git a/plugins/modules/ipasudorule.py b/plugins/modules/ipasudorule.py
index 89f0f519fd04f9e94406b1698318142986ae3f53..ca60d090aa9d670e9e58f6618e74e38ff56d80c8 100644
--- a/plugins/modules/ipasudorule.py
+++ b/plugins/modules/ipasudorule.py
@@ -31,13 +31,9 @@ DOCUMENTATION = """
 module: ipasudorule
 short description: Manage FreeIPA sudo rules
 description: Manage FreeIPA sudo rules
+extends_documentation_fragment:
+  - ipamodule_base_docs
 options:
-  ipaadmin_principal:
-    description: The admin principal
-    default: admin
-  ipaadmin_password:
-    description: The admin password
-    required: false
   name:
     description: The sudorule name
     required: true
@@ -187,10 +183,9 @@ EXAMPLES = """
 RETURN = """
 """
 
-from ansible.module_utils.basic import AnsibleModule
-from ansible.module_utils.ansible_freeipa_module import temp_kinit, \
-    temp_kdestroy, valid_creds, api_connect, api_command, compare_args_ipa, \
-    module_params_get, gen_add_del_lists, gen_add_list, gen_intersection_list
+from ansible.module_utils.ansible_freeipa_module import \
+    IPAAnsibleModule, compare_args_ipa, gen_add_del_lists, gen_add_list, \
+    gen_intersection_list
 
 
 def find_sudorule(module, name):
@@ -199,7 +194,7 @@ def find_sudorule(module, name):
         "cn": name,
     }
 
-    _result = api_command(module, "sudorule_find", name, _args)
+    _result = module.ipa_command("sudorule_find", name, _args)
 
     if len(_result["result"]) > 1:
         module.fail_json(
@@ -235,12 +230,9 @@ def gen_args(description, usercat, hostcat, cmdcat, runasusercat,
 
 
 def main():
-    ansible_module = AnsibleModule(
+    ansible_module = IPAAnsibleModule(
         argument_spec=dict(
             # general
-            ipaadmin_principal=dict(type="str", default="admin"),
-            ipaadmin_password=dict(type="str", required=False, no_log=True),
-
             name=dict(type="list", aliases=["cn"], default=None,
                       required=True),
             # present
@@ -286,42 +278,37 @@ def main():
     # Get parameters
 
     # general
-    ipaadmin_principal = module_params_get(ansible_module,
-                                           "ipaadmin_principal")
-    ipaadmin_password = module_params_get(ansible_module, "ipaadmin_password")
-    names = module_params_get(ansible_module, "name")
+    names = ansible_module.params_get("name")
 
     # present
     # The 'noqa' variables are not used here, but required for vars().
     # The use of 'noqa' ensures flake8 does not complain about them.
-    description = module_params_get(ansible_module, "description")  # noqa
-    cmdcategory = module_params_get(ansible_module, 'cmdcategory')  # noqa
-    usercategory = module_params_get(ansible_module, "usercategory")  # noqa
-    hostcategory = module_params_get(ansible_module, "hostcategory")  # noqa
-    runasusercategory = module_params_get(ansible_module,           # noqa
+    description = ansible_module.params_get("description")  # noqa
+    cmdcategory = ansible_module.params_get('cmdcategory')  # noqa
+    usercategory = ansible_module.params_get("usercategory")  # noqa
+    hostcategory = ansible_module.params_get("hostcategory")  # noqa
+    runasusercategory = ansible_module.params_get(          # noqa
                                           "runasusercategory")
-    runasgroupcategory = module_params_get(ansible_module,          # noqa
+    runasgroupcategory = ansible_module.params_get(         # noqa
                                            "runasgroupcategory")
-    hostcategory = module_params_get(ansible_module, "hostcategory")  # noqa
-    nomembers = module_params_get(ansible_module, "nomembers")  # noqa
-    host = module_params_get(ansible_module, "host")
-    hostgroup = module_params_get(ansible_module, "hostgroup")
-    user = module_params_get(ansible_module, "user")
-    group = module_params_get(ansible_module, "group")
-    allow_sudocmd = module_params_get(ansible_module, 'allow_sudocmd')
-    allow_sudocmdgroup = module_params_get(ansible_module,
-                                           'allow_sudocmdgroup')
-    deny_sudocmd = module_params_get(ansible_module, 'deny_sudocmd')
-    deny_sudocmdgroup = module_params_get(ansible_module,
-                                          'deny_sudocmdgroup')
-    sudooption = module_params_get(ansible_module, "sudooption")
-    order = module_params_get(ansible_module, "order")
-    runasuser = module_params_get(ansible_module, "runasuser")
-    runasgroup = module_params_get(ansible_module, "runasgroup")
-    action = module_params_get(ansible_module, "action")
+    hostcategory = ansible_module.params_get("hostcategory")  # noqa
+    nomembers = ansible_module.params_get("nomembers")  # noqa
+    host = ansible_module.params_get("host")
+    hostgroup = ansible_module.params_get("hostgroup")
+    user = ansible_module.params_get("user")
+    group = ansible_module.params_get("group")
+    allow_sudocmd = ansible_module.params_get('allow_sudocmd')
+    allow_sudocmdgroup = ansible_module.params_get('allow_sudocmdgroup')
+    deny_sudocmd = ansible_module.params_get('deny_sudocmd')
+    deny_sudocmdgroup = ansible_module.params_get('deny_sudocmdgroup')
+    sudooption = ansible_module.params_get("sudooption")
+    order = ansible_module.params_get("order")
+    runasuser = ansible_module.params_get("runasuser")
+    runasgroup = ansible_module.params_get("runasgroup")
+    action = ansible_module.params_get("action")
 
     # state
-    state = module_params_get(ansible_module, "state")
+    state = ansible_module.params_get("state")
 
     # Check parameters
 
@@ -393,13 +380,9 @@ def main():
 
     changed = False
     exit_args = {}
-    ccache_dir = None
-    ccache_name = None
-    try:
-        if not valid_creds(ansible_module, ipaadmin_principal):
-            ccache_dir, ccache_name = temp_kinit(ipaadmin_principal,
-                                                 ipaadmin_password)
-        api_connect()
+
+    # Connect to IPA API
+    with ansible_module.ipa_connect():
 
         commands = []
 
@@ -850,8 +833,7 @@ def main():
         errors = []
         for name, command, args in commands:
             try:
-                result = api_command(ansible_module, command, name,
-                                     args)
+                result = ansible_module.ipa_command(command, name, args)
 
                 if "completed" in result:
                     if result["completed"] > 0:
@@ -873,12 +855,6 @@ def main():
         if len(errors) > 0:
             ansible_module.fail_json(msg=", ".join(errors))
 
-    except Exception as ex:
-        ansible_module.fail_json(msg=str(ex))
-
-    finally:
-        temp_kdestroy(ccache_dir, ccache_name)
-
     # Done
 
     ansible_module.exit_json(changed=changed, **exit_args)
diff --git a/plugins/modules/ipatopologysegment.py b/plugins/modules/ipatopologysegment.py
index d29090251daddcfe5e095cef3e8b32fbca8c4eb9..e8ef2b0a9d19af8e8acaf7b96b4c68007afe8efa 100644
--- a/plugins/modules/ipatopologysegment.py
+++ b/plugins/modules/ipatopologysegment.py
@@ -31,13 +31,9 @@ DOCUMENTATION = """
 module: ipatopologysegment
 short description: Manage FreeIPA topology segments
 description: Manage FreeIPA topology segments
+extends_documentation_fragment:
+  - ipamodule_base_docs
 options:
-  ipaadmin_principal:
-    description: The admin principal
-    default: admin
-  ipaadmin_password:
-    description: The admin password
-    required: false
   suffix:
     description: Topology suffix
     required: true
@@ -67,35 +63,41 @@ author:
 
 EXAMPLES = """
 - ipatopologysegment:
+    ipaadmin_password: SomeADMINpassword
     suffix: domain
     left: ipaserver.test.local
     right: ipareplica1.test.local
     state: present
 
 - ipatopologysegment:
+    ipaadmin_password: SomeADMINpassword
     suffix: domain
     name: ipaserver.test.local-to-replica1.test.local
     state: absent
 
 - ipatopologysegment:
+    ipaadmin_password: SomeADMINpassword
     suffix: domain
     left: ipaserver.test.local
     right: ipareplica1.test.local
     state: absent
 
 - ipatopologysegment:
+    ipaadmin_password: SomeADMINpassword
     suffix: ca
     name: ipaserver.test.local-to-replica1.test.local
     direction: left-to-right
     state: reinitialized
 
 - ipatopologysegment:
+    ipaadmin_password: SomeADMINpassword
     suffix: domain+ca
     left: ipaserver.test.local
     right: ipareplica1.test.local
     state: absent
 
 - ipatopologysegment:
+    ipaadmin_password: SomeADMINpassword
     suffix: domain+ca
     left: ipaserver.test.local
     right: ipareplica1.test.local
@@ -113,19 +115,16 @@ not-found:
   type: list
 """
 
-from ansible.module_utils.basic import AnsibleModule
-from ansible.module_utils._text import to_text
-from ansible.module_utils.ansible_freeipa_module import temp_kinit, \
-    temp_kdestroy, valid_creds, api_connect, api_command
+from ansible.module_utils.ansible_freeipa_module import IPAAnsibleModule
 
 
 def find_left_right(module, suffix, left, right):
     _args = {
-        "iparepltoposegmentleftnode": to_text(left),
-        "iparepltoposegmentrightnode": to_text(right),
+        "iparepltoposegmentleftnode": left,
+        "iparepltoposegmentrightnode": right,
     }
-    _result = api_command(module, "topologysegment_find",
-                          to_text(suffix), _args)
+    _result = module.ipa_command("topologysegment_find",
+                                 suffix, _args)
     if len(_result["result"]) > 1:
         module.fail_json(
             msg="Combination of left node '%s' and right node '%s' is "
@@ -138,10 +137,10 @@ def find_left_right(module, suffix, left, right):
 
 def find_cn(module, suffix, name):
     _args = {
-        "cn": to_text(name),
+        "cn": name,
     }
-    _result = api_command(module, "topologysegment_find",
-                          to_text(suffix), _args)
+    _result = module.ipa_command("topologysegment_find",
+                                 suffix, _args)
     if len(_result["result"]) > 1:
         module.fail_json(
             msg="CN '%s' is not unique for suffix '%s'" % (name, suffix))
@@ -156,7 +155,7 @@ def find_left_right_cn(module, suffix, left, right, name):
         left_right = find_left_right(module, suffix, left, right)
         if left_right is not None:
             if name is not None and \
-               left_right["cn"][0] != to_text(name):
+               left_right["cn"][0] != name:
                 module.fail_json(
                     msg="Left and right nodes do not match "
                     "given name name (cn) '%s'" % name)
@@ -174,10 +173,8 @@ def find_left_right_cn(module, suffix, left, right, name):
 
 
 def main():
-    ansible_module = AnsibleModule(
+    ansible_module = IPAAnsibleModule(
         argument_spec=dict(
-            ipaadmin_principal=dict(type="str", default="admin"),
-            ipaadmin_password=dict(type="str", required=False, no_log=True),
             suffix=dict(choices=["domain", "ca", "domain+ca"], required=True),
             name=dict(type="str", aliases=["cn"], default=None),
             left=dict(type="str", aliases=["leftnode"], default=None),
@@ -195,14 +192,12 @@ def main():
 
     # Get parameters
 
-    ipaadmin_principal = ansible_module.params.get("ipaadmin_principal")
-    ipaadmin_password = ansible_module.params.get("ipaadmin_password")
-    suffixes = ansible_module.params.get("suffix")
-    name = ansible_module.params.get("name")
-    left = ansible_module.params.get("left")
-    right = ansible_module.params.get("right")
-    direction = ansible_module.params.get("direction")
-    state = ansible_module.params.get("state")
+    suffixes = ansible_module.params_get("suffix")
+    name = ansible_module.params_get("name")
+    left = ansible_module.params_get("left")
+    right = ansible_module.params_get("right")
+    direction = ansible_module.params_get("direction")
+    state = ansible_module.params_get("state")
 
     # Check parameters
 
@@ -214,14 +209,8 @@ def main():
 
     changed = False
     exit_args = {}
-    ccache_dir = None
-    ccache_name = None
-    try:
-        if not valid_creds(ansible_module, ipaadmin_principal):
-            ccache_dir, ccache_name = temp_kinit(ipaadmin_principal,
-                                                 ipaadmin_password)
-        api_connect()
 
+    with ansible_module.ipa_connect():
         commands = []
 
         for suffix in suffixes.split("+"):
@@ -233,17 +222,17 @@ def main():
                     ansible_module.fail_json(
                         msg="Left and right need to be set.")
                 args = {
-                    "iparepltoposegmentleftnode": to_text(left),
-                    "iparepltoposegmentrightnode": to_text(right),
+                    "iparepltoposegmentleftnode": left,
+                    "iparepltoposegmentrightnode": right,
                 }
                 if name is not None:
-                    args["cn"] = to_text(name)
+                    args["cn"] = name
 
                 res_left_right = find_left_right(ansible_module, suffix,
                                                  left, right)
                 if res_left_right is not None:
                     if name is not None and \
-                       res_left_right["cn"][0] != to_text(name):
+                       res_left_right["cn"][0] != name:
                         ansible_module.fail_json(
                             msg="Left and right nodes already used with "
                             "different name (cn) '%s'" % res_left_right["cn"])
@@ -260,7 +249,7 @@ def main():
                     # else: Nothing to change
                 else:
                     if name is None:
-                        args["cn"] = to_text("%s-to-%s" % (left, right))
+                        args["cn"] = "%s-to-%s" % (left, right)
                     commands.append(["topologysegment_add", args, suffix])
 
             elif state in ["absent", "disabled"]:
@@ -333,15 +322,9 @@ def main():
         # Execute command
 
         for command, args, _suffix in commands:
-            api_command(ansible_module, command, to_text(_suffix), args)
+            ansible_module.ipa_command(command, _suffix, args)
             changed = True
 
-    except Exception as e:
-        ansible_module.fail_json(msg=str(e))
-
-    finally:
-        temp_kdestroy(ccache_dir, ccache_name)
-
     # Done
 
     ansible_module.exit_json(changed=changed, **exit_args)
diff --git a/plugins/modules/ipatopologysuffix.py b/plugins/modules/ipatopologysuffix.py
index ab1e4135f3356b92c05f6c9681df0fb9b79f2296..4711926410824319f75e563ff81dd5732cbefa44 100644
--- a/plugins/modules/ipatopologysuffix.py
+++ b/plugins/modules/ipatopologysuffix.py
@@ -31,13 +31,9 @@ DOCUMENTATION = """
 module: ipatopologysuffix
 short description: Verify FreeIPA topology suffix
 description: Verify FreeIPA topology suffix
+extends_documentation_fragment:
+  - ipamodule_base_docs
 options:
-  ipaadmin_principal:
-    description: The admin principal
-    default: admin
-  ipaadmin_password:
-    description: The admin password
-    required: false
   suffix:
     description: Topology suffix
     required: true
@@ -52,6 +48,7 @@ author:
 
 EXAMPLES = """
 - ipatopologysuffix:
+    ipaadmin_password: SomeADMINpassword
     suffix: domain
     state: verified
 """
@@ -59,16 +56,12 @@ EXAMPLES = """
 RETURN = """
 """
 
-from ansible.module_utils.basic import AnsibleModule
-from ansible.module_utils._text import to_text
-from ansible.module_utils.ansible_freeipa_module import execute_api_command
+from ansible.module_utils.ansible_freeipa_module import IPAAnsibleModule
 
 
 def main():
-    ansible_module = AnsibleModule(
+    ansible_module = IPAAnsibleModule(
         argument_spec=dict(
-            ipaadmin_principal=dict(type="str", default="admin"),
-            ipaadmin_password=dict(type="str", required=False, no_log=True),
             suffix=dict(choices=["domain", "ca"], required=True),
             state=dict(type="str", default="verified",
                        choices=["verified"]),
@@ -80,10 +73,8 @@ def main():
 
     # Get parameters
 
-    ipaadmin_principal = ansible_module.params.get("ipaadmin_principal")
-    ipaadmin_password = ansible_module.params.get("ipaadmin_password")
-    suffix = ansible_module.params.get("suffix")
-    state = ansible_module.params.get("state")
+    suffix = ansible_module.params_get("suffix")
+    state = ansible_module.params_get("state")
 
     # Check parameters
 
@@ -91,16 +82,15 @@ def main():
 
     # Create command
 
-    if state in ["verified"]:
-        command = "topologysuffix_verify"
-        args = {}
-    else:
+    if state not in ["verified"]:
         ansible_module.fail_json(msg="Unkown state '%s'" % state)
 
     # Execute command
 
-    execute_api_command(ansible_module, ipaadmin_principal, ipaadmin_password,
-                        command, to_text(suffix), args)
+    with ansible_module.ipa_connect():
+        # Execute command
+        ansible_module.ipa_command(["topologysuffix_verify", suffix,
+                                    {}])
 
     # Done
 
diff --git a/plugins/modules/ipatrust.py b/plugins/modules/ipatrust.py
index 3c14077b3390b39f8b5b3a7d65d7d52fd7a909f4..0d9036ebfb60216f54c57aea0f911d8437e29ac6 100644
--- a/plugins/modules/ipatrust.py
+++ b/plugins/modules/ipatrust.py
@@ -20,9 +20,6 @@
 # You should have received a copy of the GNU General Public License
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
-from ansible.module_utils.ansible_freeipa_module import temp_kinit, \
-    temp_kdestroy, valid_creds, api_connect, api_command, module_params_get
-from ansible.module_utils.basic import AnsibleModule
 ANSIBLE_METADATA = {'metadata_version': '1.1',
                     'supported_by': 'community',
                     'status': ['preview'],
@@ -33,6 +30,8 @@ DOCUMENTATION = """
 module: ipatrust
 short_description: Manage FreeIPA Domain Trusts.
 description: Manage FreeIPA Domain Trusts.
+extends_documentation_fragment:
+  - ipamodule_base_docs
 options:
   realm:
     description:
@@ -97,6 +96,7 @@ author:
 EXAMPLES = """
 # add ad-trust
 - ipatrust:
+    ipaadmin_password: SomeADMINpassword
     realm: ad.example.test
     trust_type: ad
     admin: Administrator
@@ -105,6 +105,7 @@ EXAMPLES = """
 
 # delete ad-trust
 - ipatrust:
+    ipaadmin_password: SomeADMINpassword
     realm: ad.example.test
     state: absent
 """
@@ -113,13 +114,17 @@ RETURN = """
 """
 
 
+from ansible.module_utils.ansible_freeipa_module import \
+    IPAAnsibleModule
+
+
 def find_trust(module, realm):
     _args = {
         "all": True,
         "cn": realm,
     }
 
-    _result = api_command(module, "trust_find", realm, _args)
+    _result = module.ipa_command("trust_find", realm, _args)
 
     if len(_result["result"]) > 1:
         module.fail_json(msg="There is more than one realm '%s'" % (realm))
@@ -132,7 +137,7 @@ def find_trust(module, realm):
 def del_trust(module, realm):
     _args = {}
 
-    _result = api_command(module, "trust_del", realm, _args)
+    _result = module.ipa_command("trust_del", realm, _args)
     if len(_result["result"]["failed"]) > 0:
         module.fail_json(
             msg="Trust deletion has failed for '%s'" % (realm))
@@ -141,7 +146,7 @@ def del_trust(module, realm):
 def add_trust(module, realm, args):
     _args = args
 
-    _result = api_command(module, "trust_add", realm, _args)
+    _result = module.ipa_command("trust_add", realm, _args)
 
     if "cn" not in _result["result"]:
         module.fail_json(
@@ -174,11 +179,9 @@ def gen_args(trust_type, admin, password, server, trust_secret, base_id,
 
 
 def main():
-    ansible_module = AnsibleModule(
+    ansible_module = IPAAnsibleModule(
         argument_spec=dict(
             # general
-            ipaadmin_principal=dict(type="str", default="admin"),
-            ipaadmin_password=dict(type="str", required=False, no_log=True),
             realm=dict(type="str", default=None, required=True),
             # state
             state=dict(type="str", default="present",
@@ -207,35 +210,29 @@ def main():
     ansible_module._ansible_debug = True
 
     # general
-    ipaadmin_principal = module_params_get(
-        ansible_module, "ipaadmin_principal")
-    ipaadmin_password = module_params_get(ansible_module, "ipaadmin_password")
-    realm = module_params_get(ansible_module, "realm")
+    realm = ansible_module.params_get("realm")
 
     # state
-    state = module_params_get(ansible_module, "state")
+    state = ansible_module.params_get("state")
 
     # trust
-    trust_type = module_params_get(ansible_module, "trust_type")
-    admin = module_params_get(ansible_module, "admin")
-    password = module_params_get(ansible_module, "password")
-    server = module_params_get(ansible_module, "server")
-    trust_secret = module_params_get(ansible_module, "trust_secret")
-    base_id = module_params_get(ansible_module, "base_id")
-    range_size = module_params_get(ansible_module, "range_size")
-    range_type = module_params_get(ansible_module, "range_type")
-    two_way = module_params_get(ansible_module, "two_way")
-    external = module_params_get(ansible_module, "external")
+    trust_type = ansible_module.params_get("trust_type")
+    admin = ansible_module.params_get("admin")
+    password = ansible_module.params_get("password")
+    server = ansible_module.params_get("server")
+    trust_secret = ansible_module.params_get("trust_secret")
+    base_id = ansible_module.params_get("base_id")
+    range_size = ansible_module.params_get("range_size")
+    range_type = ansible_module.params_get("range_type")
+    two_way = ansible_module.params_get("two_way")
+    external = ansible_module.params_get("external")
 
     changed = False
     exit_args = {}
-    ccache_dir = None
-    ccache_name = None
-    try:
-        if not valid_creds(ansible_module, ipaadmin_principal):
-            ccache_dir, ccache_name = temp_kinit(
-                ipaadmin_principal, ipaadmin_password)
-        api_connect()
+
+    # Connect to IPA API
+    with ansible_module.ipa_connect():
+
         res_find = find_trust(ansible_module, realm)
 
         if state == "absent":
@@ -257,12 +254,6 @@ def main():
                     add_trust(ansible_module, realm, args)
                 changed = True
 
-    except Exception as e:
-        ansible_module.fail_json(msg=str(e))
-
-    finally:
-        temp_kdestroy(ccache_dir, ccache_name)
-
     # Done
 
     ansible_module.exit_json(changed=changed, **exit_args)
diff --git a/plugins/modules/ipauser.py b/plugins/modules/ipauser.py
index 32aa9454632be2d2293c5bebf875fbea7ad6fcec..7e44a3f3c90f2e8452d454efd48b23f80987ff38 100644
--- a/plugins/modules/ipauser.py
+++ b/plugins/modules/ipauser.py
@@ -31,13 +31,9 @@ DOCUMENTATION = """
 module: ipauser
 short description: Manage FreeIPA users
 description: Manage FreeIPA users
+extends_documentation_fragment:
+  - ipamodule_base_docs
 options:
-  ipaadmin_principal:
-    description: The admin principal
-    default: admin
-  ipaadmin_password:
-    description: The admin password
-    required: false
   name:
     description: The list of users (internally uid).
     required: false
@@ -472,16 +468,11 @@ user:
           returned: always
 """
 
-from ansible.module_utils.basic import AnsibleModule
-from ansible.module_utils._text import to_text
-from ansible.module_utils.ansible_freeipa_module import temp_kinit, \
-    temp_kdestroy, valid_creds, api_connect, api_command, date_format, \
-    compare_args_ipa, module_params_get, api_check_param, api_get_realm, \
-    api_command_no_name, gen_add_del_lists, encode_certificate, \
-    load_cert_from_str, DN_x500_text, api_check_command
-import six
-
 
+from ansible.module_utils.ansible_freeipa_module import \
+    IPAAnsibleModule, compare_args_ipa, gen_add_del_lists, date_format, \
+    encode_certificate, load_cert_from_str, DN_x500_text, to_text
+import six
 if six.PY3:
     unicode = str
 
@@ -494,7 +485,7 @@ def find_user(module, name, preserved=False):
     if preserved:
         _args["preserved"] = preserved
 
-    _result = api_command(module, "user_find", name, _args)
+    _result = module.ipa_command("user_find", name, _args)
 
     if len(_result["result"]) > 1:
         module.fail_json(
@@ -792,12 +783,9 @@ def main():
         nomembers=dict(type='bool', default=None),
     )
 
-    ansible_module = AnsibleModule(
+    ansible_module = IPAAnsibleModule(
         argument_spec=dict(
             # general
-            ipaadmin_principal=dict(type="str", default="admin"),
-            ipaadmin_password=dict(type="str", required=False, no_log=True),
-
             name=dict(type="list", aliases=["login"], default=None,
                       required=False),
             users=dict(type="list", aliases=["login"], default=None,
@@ -836,69 +824,65 @@ def main():
     # Get parameters
 
     # general
-    ipaadmin_principal = module_params_get(ansible_module,
-                                           "ipaadmin_principal")
-    ipaadmin_password = module_params_get(ansible_module, "ipaadmin_password")
-    names = module_params_get(ansible_module, "name")
-    users = module_params_get(ansible_module, "users")
+    names = ansible_module.params_get("name")
+    users = ansible_module.params_get("users")
 
     # present
-    first = module_params_get(ansible_module, "first")
-    last = module_params_get(ansible_module, "last")
-    fullname = module_params_get(ansible_module, "fullname")
-    displayname = module_params_get(ansible_module, "displayname")
-    initials = module_params_get(ansible_module, "initials")
-    homedir = module_params_get(ansible_module, "homedir")
-    shell = module_params_get(ansible_module, "shell")
-    email = module_params_get(ansible_module, "email")
-    principal = module_params_get(ansible_module, "principal")
-    principalexpiration = module_params_get(ansible_module,
-                                            "principalexpiration")
+    first = ansible_module.params_get("first")
+    last = ansible_module.params_get("last")
+    fullname = ansible_module.params_get("fullname")
+    displayname = ansible_module.params_get("displayname")
+    initials = ansible_module.params_get("initials")
+    homedir = ansible_module.params_get("homedir")
+    shell = ansible_module.params_get("shell")
+    email = ansible_module.params_get("email")
+    principal = ansible_module.params_get("principal")
+    principalexpiration = ansible_module.params_get(
+        "principalexpiration")
     if principalexpiration is not None:
         if principalexpiration[:-1] != "Z":
             principalexpiration = principalexpiration + "Z"
         principalexpiration = date_format(principalexpiration)
-    passwordexpiration = module_params_get(ansible_module,
-                                           "passwordexpiration")
+    passwordexpiration = ansible_module.params_get("passwordexpiration")
     if passwordexpiration is not None:
         if passwordexpiration[:-1] != "Z":
             passwordexpiration = passwordexpiration + "Z"
         passwordexpiration = date_format(passwordexpiration)
-    password = module_params_get(ansible_module, "password")
-    random = module_params_get(ansible_module, "random")
-    uid = module_params_get(ansible_module, "uid")
-    gid = module_params_get(ansible_module, "gid")
-    city = module_params_get(ansible_module, "city")
-    userstate = module_params_get(ansible_module, "userstate")
-    postalcode = module_params_get(ansible_module, "postalcode")
-    phone = module_params_get(ansible_module, "phone")
-    mobile = module_params_get(ansible_module, "mobile")
-    pager = module_params_get(ansible_module, "pager")
-    fax = module_params_get(ansible_module, "fax")
-    orgunit = module_params_get(ansible_module, "orgunit")
-    title = module_params_get(ansible_module, "title")
-    manager = module_params_get(ansible_module, "manager")
-    carlicense = module_params_get(ansible_module, "carlicense")
-    sshpubkey = module_params_get(ansible_module, "sshpubkey")
-    userauthtype = module_params_get(ansible_module, "userauthtype")
-    userclass = module_params_get(ansible_module, "userclass")
-    radius = module_params_get(ansible_module, "radius")
-    radiususer = module_params_get(ansible_module, "radiususer")
-    departmentnumber = module_params_get(ansible_module, "departmentnumber")
-    employeenumber = module_params_get(ansible_module, "employeenumber")
-    employeetype = module_params_get(ansible_module, "employeetype")
-    preferredlanguage = module_params_get(ansible_module, "preferredlanguage")
-    certificate = module_params_get(ansible_module, "certificate")
-    certmapdata = module_params_get(ansible_module, "certmapdata")
-    noprivate = module_params_get(ansible_module, "noprivate")
-    nomembers = module_params_get(ansible_module, "nomembers")
+    password = ansible_module.params_get("password")
+    random = ansible_module.params_get("random")
+    uid = ansible_module.params_get("uid")
+    gid = ansible_module.params_get("gid")
+    city = ansible_module.params_get("city")
+    userstate = ansible_module.params_get("userstate")
+    postalcode = ansible_module.params_get("postalcode")
+    phone = ansible_module.params_get("phone")
+    mobile = ansible_module.params_get("mobile")
+    pager = ansible_module.params_get("pager")
+    fax = ansible_module.params_get("fax")
+    orgunit = ansible_module.params_get("orgunit")
+    title = ansible_module.params_get("title")
+    manager = ansible_module.params_get("manager")
+    carlicense = ansible_module.params_get("carlicense")
+    sshpubkey = ansible_module.params_get("sshpubkey")
+    userauthtype = ansible_module.params_get("userauthtype")
+    userclass = ansible_module.params_get("userclass")
+    radius = ansible_module.params_get("radius")
+    radiususer = ansible_module.params_get("radiususer")
+    departmentnumber = ansible_module.params_get("departmentnumber")
+    employeenumber = ansible_module.params_get("employeenumber")
+    employeetype = ansible_module.params_get("employeetype")
+    preferredlanguage = ansible_module.params_get("preferredlanguage")
+    certificate = ansible_module.params_get("certificate")
+    certmapdata = ansible_module.params_get("certmapdata")
+    noprivate = ansible_module.params_get("noprivate")
+    nomembers = ansible_module.params_get("nomembers")
     # deleted
-    preserve = module_params_get(ansible_module, "preserve")
+    preserve = ansible_module.params_get("preserve")
     # mod
-    update_password = module_params_get(ansible_module, "update_password")
+    update_password = ansible_module.params_get("update_password")
     # general
-    action = module_params_get(ansible_module, "action")
-    state = module_params_get(ansible_module, "state")
+    action = ansible_module.params_get("action")
+    state = ansible_module.params_get("state")
 
     # Check parameters
 
@@ -930,21 +914,17 @@ def main():
 
     changed = False
     exit_args = {}
-    ccache_dir = None
-    ccache_name = None
-    try:
-        if not valid_creds(ansible_module, ipaadmin_principal):
-            ccache_dir, ccache_name = temp_kinit(ipaadmin_principal,
-                                                 ipaadmin_password)
-        api_connect()
+
+    # Connect to IPA API
+    with ansible_module.ipa_connect():
 
         # Check version specific settings
 
-        server_realm = api_get_realm()
+        server_realm = ansible_module.ipa_get_realm()
 
         # Default email domain
 
-        result = api_command_no_name(ansible_module, "config_show", {})
+        result = ansible_module.ipa_command_no_name("config_show", {})
         default_email_domain = result["result"]["ipadefaultemaildomain"][0]
 
         # Extend email addresses
@@ -1048,7 +1028,8 @@ def main():
             # be part of check_parameters as this is used also before the
             # connection to the API has been established.
             if passwordexpiration is not None and \
-               not api_check_param("user_add", "krbpasswordexpiration"):
+               not ansible_module.ipa_command_param_exists(
+                   "user_add", "krbpasswordexpiration"):
                 ansible_module.fail_json(
                     msg="The use of passwordexpiration is not supported by "
                     "your IPA version")
@@ -1058,7 +1039,7 @@ def main():
             # be part of check_parameters as this is used also before the
             # connection to the API has been established.
             if certmapdata is not None and \
-               not api_check_command("user_add_certmapdata"):
+               not ansible_module.ipa_command_exists("user_add_certmapdata"):
                 ansible_module.fail_json(
                     msg="The use of certmapdata is not supported by "
                     "your IPA version")
@@ -1387,8 +1368,7 @@ def main():
         errors = []
         for name, command, args in commands:
             try:
-                result = api_command(ansible_module, command, name,
-                                     args)
+                result = ansible_module.ipa_command(command, name, args)
                 if "completed" in result:
                     if result["completed"] > 0:
                         changed = True
@@ -1432,12 +1412,6 @@ def main():
         if len(errors) > 0:
             ansible_module.fail_json(msg=", ".join(errors))
 
-    except Exception as e:
-        ansible_module.fail_json(msg=str(e))
-
-    finally:
-        temp_kdestroy(ccache_dir, ccache_name)
-
     # Done
     ansible_module.exit_json(changed=changed, user=exit_args)
 
diff --git a/plugins/modules/ipavault.py b/plugins/modules/ipavault.py
index 5b8a5484da6a7aeb7035c33ea6e6727205900008..7af6c353e7c2bb29b8ce96a2d0619820e9a53e4e 100644
--- a/plugins/modules/ipavault.py
+++ b/plugins/modules/ipavault.py
@@ -31,13 +31,9 @@ DOCUMENTATION = """
 module: ipavault
 short description: Manage vaults and secret vaults.
 description: Manage vaults and secret vaults. KRA service must be enabled.
+extends_documentation_fragment:
+  - ipamodule_base_docs
 options:
-  ipaadmin_principal:
-    description: The admin principal
-    default: admin
-  ipaadmin_password:
-    description: The admin password
-    required: false
   name:
     description: The vault name
     required: true
@@ -317,12 +313,9 @@ vault:
 
 import os
 from base64 import b64decode
-from ansible.module_utils.basic import AnsibleModule
 from ansible.module_utils._text import to_text
-from ansible.module_utils.ansible_freeipa_module import temp_kinit, \
-    temp_kdestroy, valid_creds, api_connect, api_command, \
-    gen_add_del_lists, compare_args_ipa, module_params_get, exit_raw_json, \
-    ipalib_errors
+from ansible.module_utils.ansible_freeipa_module import IPAAnsibleModule, \
+    gen_add_del_lists, compare_args_ipa, exit_raw_json, ipalib_errors
 
 
 def find_vault(module, name, username, service, shared):
@@ -338,7 +331,7 @@ def find_vault(module, name, username, service, shared):
     else:
         _args['shared'] = shared
 
-    _result = api_command(module, "vault_find", name, _args)
+    _result = module.ipa_command("vault_find", name, _args)
 
     if len(_result["result"]) > 1:
         module.fail_json(
@@ -579,7 +572,7 @@ def get_stored_data(module, res_find, args):
 
     # retrieve vault stored data
     try:
-        result = api_command(module, 'vault_retrieve', name, pwdargs)
+        result = module.ipa_command('vault_retrieve', name, pwdargs)
     except ipalib_errors.NotFound:
         return None
 
@@ -587,12 +580,9 @@ def get_stored_data(module, res_find, args):
 
 
 def main():
-    ansible_module = AnsibleModule(
+    ansible_module = IPAAnsibleModule(
         argument_spec=dict(
             # generalgroups
-            ipaadmin_principal=dict(type="str", default="admin"),
-            ipaadmin_password=dict(type="str", required=False, no_log=True),
-
             name=dict(type="list", aliases=["cn"], default=None,
                       required=True),
 
@@ -663,45 +653,40 @@ def main():
     ansible_module._ansible_debug = True
 
     # general
-    ipaadmin_principal = module_params_get(ansible_module,
-                                           "ipaadmin_principal")
-    ipaadmin_password = module_params_get(ansible_module, "ipaadmin_password")
-    names = module_params_get(ansible_module, "name")
+    names = ansible_module.params_get("name")
 
     # present
-    description = module_params_get(ansible_module, "description")
-
-    username = module_params_get(ansible_module, "username")
-    service = module_params_get(ansible_module, "service")
-    shared = module_params_get(ansible_module, "shared")
-
-    users = module_params_get(ansible_module, "users")
-    groups = module_params_get(ansible_module, "groups")
-    services = module_params_get(ansible_module, "services")
-    owners = module_params_get(ansible_module, "owners")
-    ownergroups = module_params_get(ansible_module, "ownergroups")
-    ownerservices = module_params_get(ansible_module, "ownerservices")
-
-    vault_type = module_params_get(ansible_module, "vault_type")
-    salt = module_params_get(ansible_module, "vault_salt")
-    password = module_params_get(ansible_module, "vault_password")
-    password_file = module_params_get(ansible_module, "vault_password_file")
-    new_password = module_params_get(ansible_module, "new_password")
-    new_password_file = module_params_get(ansible_module, "new_password_file")
-    public_key = module_params_get(ansible_module, "vault_public_key")
-    public_key_file = module_params_get(ansible_module,
-                                        "vault_public_key_file")
-    private_key = module_params_get(ansible_module, "vault_private_key")
-    private_key_file = module_params_get(ansible_module,
-                                         "vault_private_key_file")
-
-    vault_data = module_params_get(ansible_module, "vault_data")
-
-    datafile_in = module_params_get(ansible_module, "datafile_in")
-    datafile_out = module_params_get(ansible_module, "datafile_out")
-
-    action = module_params_get(ansible_module, "action")
-    state = module_params_get(ansible_module, "state")
+    description = ansible_module.params_get("description")
+
+    username = ansible_module.params_get("username")
+    service = ansible_module.params_get("service")
+    shared = ansible_module.params_get("shared")
+
+    users = ansible_module.params_get("users")
+    groups = ansible_module.params_get("groups")
+    services = ansible_module.params_get("services")
+    owners = ansible_module.params_get("owners")
+    ownergroups = ansible_module.params_get("ownergroups")
+    ownerservices = ansible_module.params_get("ownerservices")
+
+    vault_type = ansible_module.params_get("vault_type")
+    salt = ansible_module.params_get("vault_salt")
+    password = ansible_module.params_get("vault_password")
+    password_file = ansible_module.params_get("vault_password_file")
+    new_password = ansible_module.params_get("new_password")
+    new_password_file = ansible_module.params_get("new_password_file")
+    public_key = ansible_module.params_get("vault_public_key")
+    public_key_file = ansible_module.params_get("vault_public_key_file")
+    private_key = ansible_module.params_get("vault_private_key")
+    private_key_file = ansible_module.params_get("vault_private_key_file")
+
+    vault_data = ansible_module.params_get("vault_data")
+
+    datafile_in = ansible_module.params_get("datafile_in")
+    datafile_out = ansible_module.params_get("datafile_out")
+
+    action = ansible_module.params_get("action")
+    state = ansible_module.params_get("state")
 
     # Check parameters
 
@@ -732,17 +717,10 @@ def main():
 
     changed = False
     exit_args = {}
-    ccache_dir = None
-    ccache_name = None
-    try:
-        if not valid_creds(ansible_module, ipaadmin_principal):
-            ccache_dir, ccache_name = temp_kinit(ipaadmin_principal,
-                                                 ipaadmin_password)
-            # Need to set krb5 ccache name, due to context='ansible-freeipa'
-            if ccache_name is not None:
-                os.environ["KRB5CCNAME"] = ccache_name
 
-        api_connect(context='ansible-freeipa')
+    with ansible_module.ipa_connect(context='ansible-freeipa') as ccache_name:
+        if ccache_name is not None:
+            os.environ["KRB5CCNAME"] = ccache_name
 
         commands = []
 
@@ -970,7 +948,7 @@ def main():
         errors = []
         for name, command, args in commands:
             try:
-                result = api_command(ansible_module, command, name, args)
+                result = ansible_module.ipa_command(command, name, args)
 
                 if command == 'vault_archive':
                     changed = 'Archived data into' in result['summary']
@@ -1012,12 +990,6 @@ def main():
         if len(errors) > 0:
             ansible_module.fail_json(msg=", ".join(errors))
 
-    except Exception as exception:
-        ansible_module.fail_json(msg=str(exception))
-
-    finally:
-        temp_kdestroy(ccache_dir, ccache_name)
-
     # Done
 
     # exit_raw_json is a replacement for ansible_module.exit_json that
diff --git a/utils/templates/ipamodule+member.py.in b/utils/templates/ipamodule+member.py.in
index bd63739897c59c63df7ba4e46042b884274e2395..c3e37727b5a189ee6d30b78e3d38f8a86f09c16d 100644
--- a/utils/templates/ipamodule+member.py.in
+++ b/utils/templates/ipamodule+member.py.in
@@ -31,13 +31,9 @@ DOCUMENTATION = """
 module: ipa$name
 short description: Manage FreeIPA $name
 description: Manage FreeIPA $name and $name members
+extends_documentation_fragment:
+  - ipamodule_base_docs
 options:
-  ipaadmin_principal:
-    description: The admin principal.
-    default: admin
-  ipaadmin_password:
-    description: The admin password.
-    required: false
   name:
     description: The list of $name name strings.
     required: true
@@ -65,17 +61,20 @@ options:
 EXAMPLES = """
 # Ensure $name NAME is present
 - ipa$name:
+    ipaadmin_password: SomeADMINpassword
     name: NAME
     PARAMETERS
 
 # Ensure $name "NAME" member PARAMETER2 VALUE is present
 - ipa$name:
+    ipaadmin_password: SomeADMINpassword
     name: NAME
     PARAMETER2: VALUE
     action: member
 
 # Ensure $name "NAME" member PARAMETER2 VALUE is absent
 - ipa$name:
+    ipaadmin_password: SomeADMINpassword
     name: NAME
     PARAMETER2: VALUE
     action: member
@@ -83,11 +82,13 @@ EXAMPLES = """
 
 # Ensure $name NAME is absent
 - ipa$name:
+    ipaadmin_password: SomeADMINpassword
     name: NAME
     state: absent
 
 # Ensure $name NAME ...
 - ipa$name:
+    ipaadmin_password: SomeADMINpassword
     name: NAME
     CHANGE PARAMETERS
 """
@@ -96,10 +97,10 @@ RETURN = """
 """
 
 
-from ansible.module_utils.basic import AnsibleModule
 from ansible.module_utils.ansible_freeipa_module import \
-    temp_kinit, temp_kdestroy, valid_creds, api_connect, api_command, \
-    compare_args_ipa, module_params_get, gen_add_del_lists
+    IPAAnsibleModule, compare_args_ipa, gen_add_del_lists, gen_add_list, \
+    gen_intersection_list
+
 import six
 
 if six.PY3:
@@ -109,7 +110,7 @@ if six.PY3:
 def find_$name(module, name):
     """Find if a $name with the given name already exist."""
     try:
-        _result = api_command(module, "$name_show", name, {"all": True})
+        _result = module.ipa_command("$name_show", name, {"all": True})
     except Exception:  # pylint: disable=broad-except
         # An exception is raised if $name name is not found.
         return None
@@ -132,12 +133,9 @@ def gen_member_args(PARAMETER2):
 
 
 def main():
-    ansible_module = AnsibleModule(
+    ansible_module = IPAAnsibleModule(
         argument_spec=dict(
             # general
-            ipaadmin_principal=dict(type="str", default="admin"),
-            ipaadmin_password=dict(type="str", required=False, no_log=True),
-
             name=dict(type="list", aliases=["API_PARAMETER_NAME"],
                       default=None, required=True),
             # present
@@ -159,18 +157,15 @@ def main():
     # Get parameters
 
     # general
-    ipaadmin_principal = module_params_get(ansible_module,
-                                           "ipaadmin_principal")
-    ipaadmin_password = module_params_get(ansible_module, "ipaadmin_password")
-    names = module_params_get(ansible_module, "name")
+    names = ansible_module.params_get("name")
 
     # present
-    PARAMETER1 = module_params_get(ansible_module, "PARAMETER1")
-    PARAMETER2 = module_params_get(ansible_module, "PARAMETER2")
-    action = module_params_get(ansible_module, "action")
+    PARAMETER1 = ansible_module.params_get("PARAMETER1")
+    PARAMETER2 = ansible_module.params_get("PARAMETER2")
+    action = ansible_module.params_get("action")
 
     # state
-    state = module_params_get(ansible_module, "state")
+    state = ansible_module.params_get("state")
 
     # Check parameters
 
@@ -200,13 +195,9 @@ def main():
 
     changed = False
     exit_args = {}
-    ccache_dir = None
-    ccache_name = None
-    try:
-        if not valid_creds(ansible_module, ipaadmin_principal):
-            ccache_dir, ccache_name = temp_kinit(ipaadmin_principal,
-                                                 ipaadmin_password)
-        api_connect()
+
+    # Connect to IPA API
+    with ansible_module.ipa_connect():
 
         commands = []
         for name in names:
@@ -257,13 +248,18 @@ def main():
                         ansible_module.fail_json(
                             msg="No $name '%s'" % name)
 
-                    if PARAMETER2 is None:
-                        ansible_module.fail_json(msg="No PARAMETER2 given")
+                    # Reduce add lists for PARAMETER2
+                    # to new entries only that are not in res_find.
+                    if PARAMETER2 is not None and \
+                       "API_PARAMETER2_NAME" in res_find:
+                        PARAMETER2 = gen_add_list(
+                            PARAMETER2, res_find["API_PARAMETER2_NAME"])
 
-                    commands.append([name, "$name_add_member",
-                                     {
-                                         "PARAMETER2": PARAMETER2,
-                                     }])
+                    if PARAMETER2 is not None:
+                        commands.append([name, "$name_add_member",
+                                         {
+                                             "PARAMETER2": PARAMETER2,
+                                         }])
 
             elif state == "absent":
                 if action == "$name":
@@ -275,13 +271,17 @@ def main():
                         ansible_module.fail_json(
                             msg="No $name '%s'" % name)
 
-                    if PARAMETER2 is None:
-                        ansible_module.fail_json(msg="No PARAMETER2 given")
+                    # Reduce del lists of member_host and member_hostgroup,
+                    # to the entries only that are in res_find.
+                    if PARAMETER2 is not None:
+                        PARAMETER2 = gen_intersection_list(
+                            PARAMETER2, res_find.get("API_PARAMETER2_NAME"))
 
-                    commands.append([name, "$name_remove_member",
-                                     {
-                                         "PARAMETER2": PARAMETER2,
-                                     }])
+                    if PARAMETER2 is not None:
+                        commands.append([name, "$name_remove_member",
+                                         {
+                                             "PARAMETER2": PARAMETER2,
+                                         }])
 
             else:
                 ansible_module.fail_json(msg="Unkown state '%s'" % state)
@@ -294,8 +294,7 @@ def main():
 
         for name, command, args in commands:
             try:
-                result = api_command(ansible_module, command, name,
-                                     args)
+                result = ansible_module.ipa_command(command, name, args)
                 if "completed" in result:
                     if result["completed"] > 0:
                         changed = True
@@ -320,12 +319,6 @@ def main():
             if len(errors) > 0:
                 ansible_module.fail_json(msg=", ".join(errors))
 
-    except Exception as e:
-        ansible_module.fail_json(msg=str(e))
-
-    finally:
-        temp_kdestroy(ccache_dir, ccache_name)
-
     # Done
 
     ansible_module.exit_json(changed=changed, **exit_args)
diff --git a/utils/templates/ipamodule.py.in b/utils/templates/ipamodule.py.in
index 233c5985ca93198c60828f1a9d34e18c8885b47b..f7d1e538190d0ba3c46f80c80102a864a2020c97 100644
--- a/utils/templates/ipamodule.py.in
+++ b/utils/templates/ipamodule.py.in
@@ -31,13 +31,9 @@ DOCUMENTATION = """
 module: ipa$name
 short description: Manage FreeIPA $name
 description: Manage FreeIPA $name
+extends_documentation_fragment:
+  - ipamodule_base_docs
 options:
-  ipaadmin_principal:
-    description: The admin principal.
-    default: admin
-  ipaadmin_password:
-    description: The admin password.
-    required: false
   name:
     description: The list of $name name strings.
     required: true
@@ -60,16 +56,19 @@ options:
 EXAMPLES = """
 # Ensure $name NAME is present
 - ipa$name:
+    ipaadmin_password: SomeADMINpassword
     name: NAME
     PARAMETERS
 
 # Ensure $name NAME is absent
 - ipa$name:
+    ipaadmin_password: SomeADMINpassword
     name: NAME
     state: absent
 
 # Ensure $name NAME ...
 - ipa$name:
+    ipaadmin_password: SomeADMINpassword
     name: NAME
     CHANGE PARAMETERS
 """
@@ -78,10 +77,8 @@ RETURN = """
 """
 
 
-from ansible.module_utils.basic import AnsibleModule
 from ansible.module_utils.ansible_freeipa_module import \
-    temp_kinit, temp_kdestroy, valid_creds, api_connect, api_command, \
-    compare_args_ipa, module_params_get
+    IPAAnsibleModule, compare_args_ipa
 import six
 
 if six.PY3:
@@ -91,7 +88,7 @@ if six.PY3:
 def find_$name(module, name):
     """Find if a $name with the given name already exist."""
     try:
-        _result = api_command(module, "$name_show", name, {"all": True})
+        _result = module.ipa_command("$name_show", name, {"all": True})
     except Exception:  # pylint: disable=broad-except
         # An exception is raised if $name name is not found.
         return None
@@ -109,12 +106,9 @@ def gen_args(PARAMETER1, PARAMETER2):
 
 
 def main():
-    ansible_module = AnsibleModule(
+    ansible_module = IPAAnsibleModule(
         argument_spec=dict(
             # general
-            ipaadmin_principal=dict(type="str", default="admin"),
-            ipaadmin_password=dict(type="str", required=False, no_log=True),
-
             name=dict(type="list", aliases=["API_PARAMETER_NAME"],
                       default=None, required=True),
             # present
@@ -134,17 +128,14 @@ def main():
     # Get parameters
 
     # general
-    ipaadmin_principal = module_params_get(ansible_module,
-                                           "ipaadmin_principal")
-    ipaadmin_password = module_params_get(ansible_module, "ipaadmin_password")
-    names = module_params_get(ansible_module, "name")
+    names = ansible_module.params_get("name")
 
     # present
-    PARAMETER1 = module_params_get(ansible_module, "PARAMETER1")
-    PARAMETER2 = module_params_get(ansible_module, "PARAMETER2")
+    PARAMETER1 = ansible_module.params_get("PARAMETER1")
+    PARAMETER2 = ansible_module.params_get("PARAMETER2")
 
     # state
-    state = module_params_get(ansible_module, "state")
+    state = ansible_module.params_get("state")
 
     # Check parameters
 
@@ -170,13 +161,9 @@ def main():
 
     changed = False
     exit_args = {}
-    ccache_dir = None
-    ccache_name = None
-    try:
-        if not valid_creds(ansible_module, ipaadmin_principal):
-            ccache_dir, ccache_name = temp_kinit(ipaadmin_principal,
-                                                 ipaadmin_password)
-        api_connect()
+
+    # Connect to IPA API
+    with ansible_module.ipa_connect():
 
         commands = []
         for name in names:
@@ -215,8 +202,7 @@ def main():
 
         for name, command, args in commands:
             try:
-                result = api_command(ansible_module, command, name,
-                                     args)
+                result = ansible_module.ipa_command(command, name, args)
                 if "completed" in result:
                     if result["completed"] > 0:
                         changed = True
@@ -226,12 +212,6 @@ def main():
                 ansible_module.fail_json(msg="%s: %s: %s" % (command, name,
                                                              str(e)))
 
-    except Exception as e:
-        ansible_module.fail_json(msg=str(e))
-
-    finally:
-        temp_kdestroy(ccache_dir, ccache_name)
-
     # Done
 
     ansible_module.exit_json(changed=changed, **exit_args)