diff --git a/README-dnszone.md b/README-dnszone.md
index 607e77bc211f57bdbe3b3d74e489153332fad7bc..775933126d1f4b6a1946b1a44a7ceae011775365 100644
--- a/README-dnszone.md
+++ b/README-dnszone.md
@@ -133,6 +133,22 @@ Example playbook to enable a zone:
       state: enabled
 ```
 
+Example playbook to allow per-zone privilege delegation:
+
+``` yaml
+---
+- name: Playbook to enable per-zone privilege delegation
+  hosts: ipaserver
+  become: true
+
+  tasks:
+  - name: Enable privilege delegation.
+    ipadnszone:
+      ipaadmin_password: SomeADMINpassword
+      name: testzone.local
+      permission: true
+```
+
 
 Example playbook to remove a zone:
 ```yaml
@@ -223,6 +239,7 @@ Variable | Description | Required
 `ttl`| Time to live for records at zone apex | no
 `default_ttl`| Time to live for records without explicit TTL definition | no
 `nsec3param_rec`| NSEC3PARAM record for zone in format: hash_algorithm flags iterations salt | no
+`permission` \| `managedby` | Set per-zone access delegation permission. | no
 `skip_overlap_check`| Force DNS zone creation even if it will overlap with an existing zone | no
 `skip_nameserver_check` | Force DNS zone creation even if nameserver is not resolvable | no
 
@@ -238,4 +255,6 @@ Variable | Description | Returned When
 Authors
 =======
 
-Sergio Oliveira Campos
+- Sergio Oliveira Campos
+- Thomas Woerner
+- Rafael Jeffman
diff --git a/plugins/modules/ipadnszone.py b/plugins/modules/ipadnszone.py
index a44e8cfb83687a0f7adfe6bdfbdac9d7d981eb96..78002b3f4337bd216b72d0dcc42092edc5764602 100644
--- a/plugins/modules/ipadnszone.py
+++ b/plugins/modules/ipadnszone.py
@@ -142,6 +142,11 @@ options:
        salt.
     required: false
     type: str
+  permission:
+    description: Set per-zone access delegation permission.
+    required: false
+    type: bool
+    aliases: ["managedby"]
   skip_overlap_check:
     description: |
       Force DNS zone creation even if it will overlap with an existing zone
@@ -154,6 +159,7 @@ options:
 author:
   - Sergio Oliveira Campos (@seocam)
   - Thomas Woerner (@t-woerner)
+  - Rafael Jeffman (@rjeffman)
 """  # noqa: E501
 
 EXAMPLES = """
@@ -253,6 +259,9 @@ class DNSZoneModule(IPAAnsibleModule):
             "idnsallowdynupdate": "dynamic_update",
             "idnssecinlinesigning": "dnssec",
             "idnsupdatepolicy": "update_policy",
+            # FreeIPA uses 'managedby' for dnszone and dnsforwardzone
+            # to manage 'permissions'.
+            "managedby": "permission",
             # Mapping by method
             "idnsforwarders": self.get_ipa_idnsforwarders,
             "idnsallowtransfer": self.get_ipa_idnsallowtransfer,
@@ -434,7 +443,7 @@ class DNSZoneModule(IPAAnsibleModule):
             is_zone_active = False
         else:
             zone = response["result"]
-            # FreeIPA 4.9.10+ and 4.10 use proper mapping for boolean vaalues.
+            # FreeIPA 4.9.10+ and 4.10 use proper mapping for boolean values.
             # See: https://github.com/freeipa/freeipa/pull/6294
             is_zone_active = (
                 str(zone.get("idnszoneactive")[0]).upper() == "TRUE"
@@ -462,18 +471,24 @@ class DNSZoneModule(IPAAnsibleModule):
             self.fail_json(
                 msg="Either `name` or `name_from_ip` must be provided."
             )
+        # check invalid parameters
+        invalid = []
         if self.ipa_params.state != "present":
-            invalid = ["name_from_ip"]
-
-            self.params_fail_used_invalid(invalid, self.ipa_params.state)
+            invalid .extend(["name_from_ip"])
+        if self.ipa_params.state == "absent":
+            invalid.extend(["permission"])
+        self.params_fail_used_invalid(invalid, self.ipa_params.state)
 
     def define_ipa_commands(self):
         for zone_name in self.get_zone_names():
             # Look for existing zone in IPA
             zone, is_zone_active = self.get_zone(zone_name)
-            args = self.ipa_params.get_ipa_command_args(zone=zone)
 
             if self.ipa_params.state in ["present", "enabled", "disabled"]:
+                args = self.ipa_params.get_ipa_command_args(zone=zone)
+                # We'll handle "managedby" after dnszone add/mod.
+                args.pop("managedby", None)
+
                 if not zone:
                     # Since the zone doesn't exist we just create it
                     #   with given args
@@ -487,6 +502,16 @@ class DNSZoneModule(IPAAnsibleModule):
                     if not compare_args_ipa(self, args, zone):
                         self.commands.append((zone_name, "dnszone_mod", args))
 
+                # Permissions must be set on existing zones.
+                if self.ipa_params.permission is not None:
+                    is_managed = zone.get("managedby")
+                    if self.ipa_params.permission and not is_managed:
+                        self.commands.append(
+                            (zone_name, "dnszone_add_permission", {}))
+                    if not self.ipa_params.permission and is_managed:
+                        self.commands.append(
+                            (zone_name, "dnszone_remove_permission", {}))
+
             if self.ipa_params.state == "enabled" and not is_zone_active:
                 self.commands.append((zone_name, "dnszone_enable", {}))
 
@@ -555,6 +580,8 @@ def get_argument_spec():
         ttl=dict(type="int", required=False, default=None),
         default_ttl=dict(type="int", required=False, default=None),
         nsec3param_rec=dict(type="str", required=False, default=None),
+        permission=dict(type="bool", required=False, default=None,
+                        aliases=["managedby"]),
         skip_nameserver_check=dict(type="bool", required=False, default=None),
         skip_overlap_check=dict(type="bool", required=False, default=None),
     )
diff --git a/tests/dnszone/test_dnszone.yml b/tests/dnszone/test_dnszone.yml
index 0491e263449eb1925dd5d0ea6854888fd642f2d7..3d74d43afd222ede46754814534c8a373fd32c55 100644
--- a/tests/dnszone/test_dnszone.yml
+++ b/tests/dnszone/test_dnszone.yml
@@ -3,6 +3,10 @@
   hosts: "{{ ipa_test_host | default('ipaserver') }}"
   become: true
   gather_facts: true
+  module_defaults:
+    ipadnszone:
+      ipaadmin_password: SomeADMINpassword
+      ipaapi_context: "{{ ipa_context | default(omit) }}"
 
   tasks:
 
@@ -13,8 +17,6 @@
   # Tests
   - name: Check if zone is present, when it shouldn't be.
     ipadnszone:
-      ipaadmin_password: SomeADMINpassword
-      ipaapi_context: "{{ ipa_context | default(omit) }}"
       name: testzone.local
       state: present
     check_mode: yes
@@ -23,8 +25,6 @@
 
   - name: Check if zone is present again, when it shouldn't be.
     ipadnszone:
-      ipaadmin_password: SomeADMINpassword
-      ipaapi_context: "{{ ipa_context | default(omit) }}"
       name: testzone.local
       state: present
     check_mode: yes
@@ -33,8 +33,6 @@
 
   - name: Ensure zone is present.
     ipadnszone:
-      ipaadmin_password: SomeADMINpassword
-      ipaapi_context: "{{ ipa_context | default(omit) }}"
       name: testzone.local
       state: present
     register: result
@@ -42,8 +40,6 @@
 
   - name: Check if zone is present, when it should be.
     ipadnszone:
-      ipaadmin_password: SomeADMINpassword
-      ipaapi_context: "{{ ipa_context | default(omit) }}"
       name: testzone.local
       state: present
     check_mode: yes
@@ -52,8 +48,6 @@
 
   - name: Ensure zone is present, again.
     ipadnszone:
-      ipaadmin_password: SomeADMINpassword
-      ipaapi_context: "{{ ipa_context | default(omit) }}"
       name: testzone.local
       state: present
     register: result
@@ -61,8 +55,6 @@
 
   - name: Ensure zone is disabled.
     ipadnszone:
-      ipaadmin_password: SomeADMINpassword
-      ipaapi_context: "{{ ipa_context | default(omit) }}"
       name: testzone.local
       state: disabled
     register: result
@@ -70,8 +62,6 @@
 
   - name: Ensure zone is disabled, again.
     ipadnszone:
-      ipaadmin_password: SomeADMINpassword
-      ipaapi_context: "{{ ipa_context | default(omit) }}"
       name: testzone.local
       state: disabled
     register: result
@@ -79,8 +69,6 @@
 
   - name: Ensure zone is enabled.
     ipadnszone:
-      ipaadmin_password: SomeADMINpassword
-      ipaapi_context: "{{ ipa_context | default(omit) }}"
       name: testzone.local
       state: enabled
     register: result
@@ -88,8 +76,6 @@
 
   - name: Ensure zone is enabled, again.
     ipadnszone:
-      ipaadmin_password: SomeADMINpassword
-      ipaapi_context: "{{ ipa_context | default(omit) }}"
       name: testzone.local
       state: enabled
     register: result
@@ -97,8 +83,6 @@
 
   - name: Ensure forward_policy is none.
     ipadnszone:
-      ipaadmin_password: SomeADMINpassword
-      ipaapi_context: "{{ ipa_context | default(omit) }}"
       name: testzone.local
       forward_policy: none
     register: result
@@ -106,8 +90,6 @@
 
   - name: Ensure forward_policy is none, again.
     ipadnszone:
-      ipaadmin_password: SomeADMINpassword
-      ipaapi_context: "{{ ipa_context | default(omit) }}"
       name: testzone.local
       forward_policy: none
     register: result
@@ -115,8 +97,6 @@
 
   - name: Ensure forward_policy is first.
     ipadnszone:
-      ipaadmin_password: SomeADMINpassword
-      ipaapi_context: "{{ ipa_context | default(omit) }}"
       name: testzone.local
       forward_policy: first
     register: result
@@ -124,8 +104,6 @@
 
   - name: Ensure forward_policy is first, again.
     ipadnszone:
-      ipaadmin_password: SomeADMINpassword
-      ipaapi_context: "{{ ipa_context | default(omit) }}"
       name: testzone.local
       forward_policy: first
     register: result
@@ -133,8 +111,6 @@
 
   - name: Ensure first forwarder is set.
     ipadnszone:
-      ipaadmin_password: SomeADMINpassword
-      ipaapi_context: "{{ ipa_context | default(omit) }}"
       name: testzone.local
       forwarders:
         - ip_address: 8.8.8.8
@@ -144,8 +120,6 @@
 
   - name: Ensure first and second forwarder are set.
     ipadnszone:
-      ipaadmin_password: SomeADMINpassword
-      ipaapi_context: "{{ ipa_context | default(omit) }}"
       name: testzone.local
       forwarders:
         - ip_address: 8.8.8.8
@@ -156,8 +130,6 @@
 
   - name: Ensure first and second forwarder are set, again.
     ipadnszone:
-      ipaadmin_password: SomeADMINpassword
-      ipaapi_context: "{{ ipa_context | default(omit) }}"
       name: testzone.local
       forwarders:
         - ip_address: 8.8.8.8
@@ -168,8 +140,6 @@
 
   - name: Ensure only second forwarder is set.
     ipadnszone:
-      ipaadmin_password: SomeADMINpassword
-      ipaapi_context: "{{ ipa_context | default(omit) }}"
       name: testzone.local
       forwarders:
         - ip_address: 2001:4860:4860::8888
@@ -178,16 +148,12 @@
 
   - name: Nothing changes.
     ipadnszone:
-      ipaadmin_password: SomeADMINpassword
-      ipaapi_context: "{{ ipa_context | default(omit) }}"
       name: testzone.local
     register: result
     failed_when: result.changed or result.failed
 
   - name: Ensure no forwarders are set.
     ipadnszone:
-      ipaadmin_password: SomeADMINpassword
-      ipaapi_context: "{{ ipa_context | default(omit) }}"
       name: testzone.local
       forwarders: []
     register: result
@@ -195,56 +161,70 @@
 
   - name: Create zones test1
     ipadnszone:
-      ipaadmin_password: SomeADMINpassword
-      ipaapi_context: "{{ ipa_context | default(omit) }}"
       name: test1.testzone.local
     register: result
     failed_when: not result.changed or result.failed
 
   - name: Create zones test1, again
     ipadnszone:
-      ipaadmin_password: SomeADMINpassword
-      ipaapi_context: "{{ ipa_context | default(omit) }}"
       name: test1.testzone.local
     register: result
     failed_when: result.changed or result.failed
 
   - name: Create zones test2
     ipadnszone:
-      ipaadmin_password: SomeADMINpassword
-      ipaapi_context: "{{ ipa_context | default(omit) }}"
       name: test2.testzone.local
     register: result
     failed_when: not result.changed or result.failed
 
   - name: Create zones test2, again
     ipadnszone:
-      ipaadmin_password: SomeADMINpassword
-      ipaapi_context: "{{ ipa_context | default(omit) }}"
       name: test2.testzone.local
     register: result
     failed_when: result.changed or result.failed
 
   - name: Create zones test3
     ipadnszone:
-      ipaadmin_password: SomeADMINpassword
-      ipaapi_context: "{{ ipa_context | default(omit) }}"
       name: test3.testzone.local
     register: result
     failed_when: not result.changed or result.failed
 
   - name: Create zones test3, again
     ipadnszone:
-      ipaadmin_password: SomeADMINpassword
-      ipaapi_context: "{{ ipa_context | default(omit) }}"
       name: test3.testzone.local
     register: result
     failed_when: result.changed or result.failed
 
+  - name: Ensure zone test1.testzone.local has management permissioon
+    ipadnszone:
+      name: test1.testzone.local
+      permission: true
+    register: result
+    failed_when: not result.changed or result.failed
+
+  - name: Ensure zone test1.testzone.local has management permissioon
+    ipadnszone:
+      name: test1.testzone.local
+      permission: true
+    register: result
+    failed_when: result.changed or result.failed
+
+  - name: Ensure zone test1.testzone.local don't have management permissioon
+    ipadnszone:
+      name: test1.testzone.local
+      permission: false
+    register: result
+    failed_when: not result.changed or result.failed
+
+  - name: Ensure zone test1.testzone.local don't have management permissioon
+    ipadnszone:
+      name: test1.testzone.local
+      permission: false
+    register: result
+    failed_when: result.changed or result.failed
+
   - name: Ensure multiple zones are absent
     ipadnszone:
-      ipaadmin_password: SomeADMINpassword
-      ipaapi_context: "{{ ipa_context | default(omit) }}"
       name:
         - test1.testzone.local
         - test2.testzone.local
@@ -255,8 +235,6 @@
 
   - name: Ensure multiple zones are absent, again
     ipadnszone:
-      ipaadmin_password: SomeADMINpassword
-      ipaapi_context: "{{ ipa_context | default(omit) }}"
       name:
         - test1.testzone.local
         - test2.testzone.local