diff --git a/README-trust.md b/README-trust.md
new file mode 100644
index 0000000000000000000000000000000000000000..bc16bd3060aabbc271b4a69315dbd20c8d71a9c0
--- /dev/null
+++ b/README-trust.md
@@ -0,0 +1,119 @@
+Trust module
+============
+
+Description
+-----------
+
+The trust module allows to ensure presence and absence of a domain trust.
+
+Features
+--------
+
+* Trust management
+
+Supported FreeIPA Versions
+--------------------------
+
+FreeIPA versions 4.4.0 and up are supported by the ipatrust module.
+
+Requirements
+------------
+
+**Controller**
+
+* Ansible version: 2.8+
+
+**Node**
+
+* Supported FreeIPA version (see above)
+* samba-4
+* ipa-server-trust-ad
+
+Usage
+=====
+
+Example inventory file
+
+```ini
+[ipaserver]
+ipaserver.test.local
+```
+
+Example playbook to ensure a one-way trust is present:
+Omitting the two_way option implies the default of one-way
+
+```yaml
+---
+- name: Playbook to ensure a one-way trust is present
+  hosts: ipaserver
+  become: true
+
+  tasks:
+  - name: ensure the one-way trust present
+    ipatrust:
+      realm: ad.example.test
+      admin: Administrator
+      password: secret_password
+      state: present
+```
+
+Example playbook to ensure a two-way trust is present using a shared-secret:
+
+```yaml
+---
+- name: Playbook to ensure a two-way trust is present
+  hosts: ipaserver
+  become: true
+
+  tasks:
+  - name: ensure the two-way trust is present
+    ipatrust:
+      realm: ad.example.test
+      trust_secret: my_share_Secret
+      two_way: True
+      state: present
+```
+
+Example playbook to ensure a trust is absent:
+
+```yaml
+---
+- name: Playbook to ensure a trust is absent
+  hosts: ipaserver
+  become: true
+
+  tasks:
+  - name: ensure the trust is absent
+    ipatrust:
+      realm: ad.example.test
+      state: absent
+```
+
+This will only delete the ipa-side of the trust and it does NOT delete the id-range that matches the trust,
+
+Variables
+=========
+
+ipatrust
+-------
+
+Variable | Description | Required
+-------- | ----------- | --------
+`ipaadmin_principal` | The admin principal is a string and defaults to `admin` | no
+`ipaadmin_password` | The admin password is a string and is required if there is no admin ticket available on the node | no
+`realm` | The realm name string. | yes
+`admin` | Active Directory domain administrator string. | no
+`password` | Active Directory domain administrator's password string. | no
+`server` | Domain controller for the Active Directory domain string. | no
+`trust_secret` | Shared secret for the trust string. | no
+`base_id` | First posix id for the trusted domain integer. | no
+`range_size` | Size of the ID range reserved for the trusted domain integer. | no
+`range_type` | Type of trusted domain ID range, It can be one of `ipa-ad-trust` or `ipa-ad-trust-posix`and defaults to `ipa-ad-trust`. | no
+`two_way` | Establish bi-directional trust. By default trust is inbound one-way only. (bool) | no
+`external` | Establish external trust to a domain in another forest. The trust is not transitive beyond the domain. (bool) | no
+`state` | The state to ensure. It can be one of `present` or `absent`, default: `present`. | yes
+
+Authors
+=======
+
+Rob Verduijn
diff --git a/README.md b/README.md
index 246a8b4c01a4132a57d3814f08f2802c5c73ea5a..073f2249315b5418362b694bb3e1a99a84d3f37e 100644
--- a/README.md
+++ b/README.md
@@ -26,6 +26,7 @@ Features
 * Modules for sudocmdgroup management
 * Modules for sudorule management
 * Modules for topology management
+* Modules fot trust management
 * Modules for user management
 * Modules for vault management
 
@@ -427,6 +428,7 @@ Modules in plugin/modules
 * [ipasudorule](README-sudorule.md)
 * [ipatopologysegment](README-topology.md)
 * [ipatopologysuffix](README-topology.md)
+* [ipatrust](README-trust.md)
 * [ipauser](README-user.md)
 * [ipavault](README-vault.md)
 
diff --git a/playbooks/trust/add-trust.yml b/playbooks/trust/add-trust.yml
new file mode 100644
index 0000000000000000000000000000000000000000..4ceedf5dc112fe75e600d5dda7ddf67708462a08
--- /dev/null
+++ b/playbooks/trust/add-trust.yml
@@ -0,0 +1,12 @@
+---
+- name: Playbook to create a trust
+  hosts: ipaserver
+  become: true
+
+  tasks:
+    - name: ensure the trust is present
+      ipatrust:
+        realm: windows.local
+        admin: Administrator
+        password: secret_password
+        state: present
diff --git a/playbooks/trust/del-trust.yml b/playbooks/trust/del-trust.yml
new file mode 100644
index 0000000000000000000000000000000000000000..9a8514d0abe78bd23d1c0c80fc4b54cd61a0ad76
--- /dev/null
+++ b/playbooks/trust/del-trust.yml
@@ -0,0 +1,10 @@
+---
+- name: Playbook to delete trust
+  hosts: ipaserver
+  become: true
+
+  tasks:
+    - name: ensure the trust is absent
+      ipatrust:
+        realm: windows.local
+        state: absent
diff --git a/plugins/modules/ipatrust.py b/plugins/modules/ipatrust.py
new file mode 100644
index 0000000000000000000000000000000000000000..4dc144fd8f43f91c623cc80e21a7f101d5463c99
--- /dev/null
+++ b/plugins/modules/ipatrust.py
@@ -0,0 +1,274 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+
+# Authors:
+#   Rob Verduijn <rob.verduijn@gmail.com>
+#
+# Copyright (C) 2019 By Rob Verduijn
+# see file 'COPYING' for use and warranty information
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+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'],
+                    }
+
+DOCUMENTATION = """
+---
+module: ipatrust
+short_description: Manage FreeIPA Domain Trusts.
+description: Manage FreeIPA Domain Trusts.
+options:
+  realm:
+    description:
+    - Realm name
+    required: true
+  trust_type:
+    description:
+    - Trust type (ad for Active Directory, default)
+    default: ad
+    required: true
+  admin:
+    description:
+    - Active Directory domain administrator
+    required: false
+  password:
+    description:
+    - Active Directory domain administrator's password
+    required: false
+  server:
+    description:
+    - Domain controller for the Active Directory domain (optional)
+    required: false
+  trust_secret:
+    description:
+    - Shared secret for the trust
+    required: false
+  base_id:
+    description:
+    - First Posix ID of the range reserved for the trusted domain
+    required: false
+  range_size:
+    description:
+    - Size of the ID range reserved for the trusted domain
+  range_type:
+    description:
+    - Type of trusted domain ID range, one of ipa-ad-trust, ipa-ad-trust-posix
+    default: ipa-ad-trust
+    required: false
+  two_way:
+    description:
+    - Establish bi-directional trust. By default trust is inbound one-way only.
+    default: false
+    required: false
+    choices: ["true", "false"]
+  external:
+    description:
+    - Establish external trust to a domain in another forest.
+    - The trust is not transitive beyond the domain.
+    default: false
+    required: false
+    choices: ["true", "false"]
+  state:
+    description: State to ensure
+    default: present
+    required: true
+    choices: ["present", "absent"]
+
+author:
+    - Rob Verduijn
+"""
+
+EXAMPLES = """
+# add ad-trust
+- ipatrust:
+    realm: ad.example.test
+    trust_type: ad
+    admin: Administrator
+    password: Welcome2020!
+    state: present
+
+# delete ad-trust
+- ipatrust:
+    realm: ad.example.test
+    state: absent
+"""
+
+RETURN = """
+"""
+
+
+def find_trust(module, realm):
+    _args = {
+        "all": True,
+        "cn": realm,
+    }
+
+    _result = api_command(module, "trust_find", realm, _args)
+
+    if len(_result["result"]) > 1:
+        module.fail_json(msg="There is more than one realm '%s'" % (realm))
+    elif len(_result["result"]) == 1:
+        return _result["result"][0]
+    else:
+        return None
+
+
+def del_trust(module, realm):
+    _args = {}
+
+    _result = api_command(module, "trust_del", realm, _args)
+    if len(_result["result"]["failed"]) > 0:
+        module.fail_json(
+            msg="Trust deletion has failed for '%s'" % (realm))
+    else:
+        return None
+
+
+def add_trust(module, realm, args):
+    _args = args
+
+    _result = api_command(module, "trust_add", realm, _args)
+
+    if "cn" not in _result["result"]:
+        module.fail_json(
+            msg="Trust add has failed for '%s'" % (realm))
+    else:
+        return None
+
+
+def gen_args(trust_type, admin, password, server, trust_secret, base_id,
+             range_size, range_type, two_way, external):
+    _args = {}
+    if trust_type is not None:
+        _args["trust_type"] = trust_type
+    if admin is not None:
+        _args["realm_admin"] = admin
+    if password is not None:
+        _args["realm_passwd"] = password
+    if server is not None:
+        _args["realm_server"] = server
+    if trust_secret is not None:
+        _args["trust_secret"] = trust_secret
+    if base_id is not None:
+        _args["base_id"] = base_id
+    if range_size is not None:
+        _args["range_size"] = range_size
+    if two_way is not None:
+        _args["bidirectional"] = two_way
+    if external is not None:
+        _args["external"] = external
+
+    return _args
+
+
+def main():
+    ansible_module = AnsibleModule(
+        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",
+                       choices=["present", "absent"]),
+            # present
+            trust_type=dict(type="str", default="ad", required=False),
+            admin=dict(type="str", default=None, required=False),
+            password=dict(type="str", default=None,
+                          required=False, no_log=True),
+            server=dict(type="str", default=None, required=False),
+            trust_secret=dict(type="str", default=None,
+                              required=False, no_log=True),
+            base_id=dict(type="int", default=None, required=False),
+            range_size=dict(type="int", default=200000, required=False),
+            range_type=dict(type="str", default="ipa-ad-trust",
+                            required=False, choices=["ipa-ad-trust-posix",
+                                                     "ipa-ad-trust"]),
+            two_way=dict(type="bool", default=False, required=False),
+            external=dict(type="bool", default=False, required=False),
+        ),
+        mutually_exclusive=[["trust_secret", "admin"]],
+        required_together=[["admin", "password"]],
+        supports_check_mode=True
+    )
+
+    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")
+
+    # state
+    state = module_params_get(ansible_module, "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")
+
+    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()
+        res_find = find_trust(ansible_module, realm)
+
+        if state == "absent":
+            if res_find is not None:
+                del_trust(ansible_module, realm)
+                changed = True
+        elif res_find is None:
+            if admin is None and trust_secret is None:
+                ansible_module.fail_json(
+                    msg="one of admin or trust_secret is required when state "
+                        "is present")
+            else:
+                args = gen_args(trust_type, admin, password, server,
+                                trust_secret, base_id, range_size, range_type,
+                                two_way, external)
+
+                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)
+
+
+if __name__ == "__main__":
+    main()
diff --git a/tests/trust/test_trust.yml b/tests/trust/test_trust.yml
new file mode 100644
index 0000000000000000000000000000000000000000..dd93fdfe1c15552df2f5530c3e70f4b3dd190f9e
--- /dev/null
+++ b/tests/trust/test_trust.yml
@@ -0,0 +1,51 @@
+---
+- name: find trust
+  hosts: ipaserver
+  become: true
+  gather_facts: false
+
+  tasks:
+
+  - block:
+
+    - name: delete trust
+      ipatrust:
+        realm: windows.local
+        state: absent
+      register: del_trust
+
+    - name: check for trust
+      shell: |
+        echo 'SomeADMINpassword' | kinit admin
+        ipa trust-find windows.local
+      register: check_find_trust
+      failed_when: "'0 trusts matched' not in check_find_trust.stdout"
+
+    - name: delete id range
+      shell: |
+        echo 'SomeADMINpassword' | kinit admin
+        ipa idrange-del WINDOWS.LOCAL_id_range
+      when: del_trust['changed'] | bool
+
+    - name: check for range
+      shell: |
+        echo 'SomeADMINpassword' | kinit admin
+        ipa idrange-find WINDOWS.LOCAL_id_range
+      register: check_del_idrange
+      failed_when: "'0 ranges matched' not in check_del_idrange.stdout"
+
+    - name: add trust
+      ipatrust:
+        realm: windows.local
+        admin: Administrator
+        password: secret_ad_pw
+        state: present
+
+    - name: check for trust
+      shell: |
+        echo 'SomeADMINpassword' | kinit admin
+        ipa trust-find windows.local
+      register: check_add_trust
+      failed_when: "'1 trust matched' not in check_add_trust.stdout"
+
+    when: trust_test_is_supported | default(false)
\ No newline at end of file