diff --git a/README-dnsrecord.md b/README-dnsrecord.md
new file mode 100644
index 0000000000000000000000000000000000000000..6f88f4322cebfbc893b75dde71b31c8c959e1c71
--- /dev/null
+++ b/README-dnsrecord.md
@@ -0,0 +1,357 @@
+DNSRecord module
+================
+
+Description
+-----------
+
+The dnsrecord module allows management of DNS records and is as compatible as possible with the Ansible upstream `ipa_dnsrecord` module, but provide some other features like multiple record management in one execution and support for more DNS record types.
+
+
+Features
+--------
+* DNS record management.
+
+
+Supported FreeIPA Versions
+--------------------------
+
+FreeIPA versions 4.4.0 and up are supported by the ipadnsrecord module.
+
+
+Requirements
+------------
+
+**Controller**
+* Ansible version: 2.8+
+
+**Node**
+* Supported FreeIPA version (see above)
+
+
+Usage
+=====
+
+Example inventory file
+
+```ini
+[ipaserver]
+ipaserver.example.com
+```
+
+Example playbook to ensure an AAAA record is present:
+
+```yaml
+---
+- ipadnsrecord:
+    ipaadmin_password: SomeADMINpassword
+    name: host01
+    zone_name: example.com
+    record_type: 'AAAA'
+    record_value: '::1'
+```
+
+Example playbook to ensure an AAAA record is present, with a TTL of 300:
+
+```yaml
+---
+- ipadnsrecord:
+    ipaadmin_password: SomeADMINpassword
+    name: host01
+    zone_name: example.com
+    record_type: 'AAAA'
+    record_value: '::1'
+    record_ttl: 300
+```
+
+Example playbook to ensure an AAAA record is present, with a reverse PTR record:
+```yaml
+---
+- ipadnsrecord:
+    ipaadmin_password: SomeADMINpassword
+    name: host02
+    zone_name: example.com
+    record_type: 'AAAA'
+    record_value: 'fd00::0002'
+    create_reverse: yes
+```
+
+Example playbook to ensure a LOC record is present, given its individual attributes:
+```yaml
+---
+- ipadnsrecord:
+    ipaadmin_password: SomeADMINpassword
+    zone_name: example.com
+    name: host03
+    loc_lat_deg: 52
+    loc_lat_min: 22
+    loc_lat_sec: 23.000
+    loc_lat_dir: N
+    loc_lon_deg: 4
+    loc_lon_min: 53
+    loc_lon_sec: 32.00
+    loc_lon_dir: E
+    loc_altitude: -2.00
+    loc_size: 1.00
+    loc_h_precision: 10000
+    loc_v_precision: 10
+```
+
+Example playbook to ensure multiple DNS records are present:
+
+```yaml
+---
+ipadnsrecord:
+  ipaadmin_password: SomeADMINpassword
+  records:
+    - name: host02
+      zone_name: example.com
+      record_type: A
+      record_value:
+        - "{{ ipv4_prefix }}.112"
+        - "{{ ipv4_prefix }}.122"
+    - name: host02
+      zone_name: example.com
+      record_type: AAAA
+      record_value: ::1
+```
+
+Example playbook to ensure multiple CNAME records are present:
+
+```yaml
+---
+- name: Ensure that 'host03' and 'host04' have CNAME records.
+  ipadnsrecord:
+    ipaadmin_password: SomeADMINpassword
+    zone_name: example.com
+    records:
+    - name: host03
+      cname_hostname: host03.example.com
+    - name: host04
+      cname_hostname: host04.example.com
+```
+
+Example playbook to ensure NS record is absent:
+
+```yaml
+---
+- ipadnsrecord:
+    ipaadmin_password: SomeADMINpassword
+    zone_name: example.com
+    name: host04
+    ns_hostname: host04
+    state: absent
+```
+
+Example playbook to ensure LOC record is present, with fields:
+
+```yaml
+---
+- ipadnsrecord:
+    ipaadmin_password: SomeADMINpassword
+    zone_name: example.com
+    name: host04
+    loc_lat_deg: 52
+    loc_lat_min: 22
+    loc_lat_sec: 23.000
+    loc_lat_dir: N
+    loc_lon_deg: 4
+    loc_lon_min: 53
+    loc_lon_sec: 32.000
+    loc_lon_dir: E
+    loc_altitude: -2.00
+    loc_size: 0.00
+    loc_h_precision: 10000
+    loc_v_precision: 10
+```
+
+Change value of an existing LOC record:
+
+```yaml
+---
+- ipadnsrecord:
+  ipaadmin_password: SomeADMINpassword
+  zone_name: example.com
+  name: host04
+  loc_size: 1.00
+  loc_rec: 52 22 23 N 4 53 32 E -2 0 10000 10
+```
+
+Example playbook to ensure multiple A records are present:
+
+```yaml
+- ipadnsrecord:
+    ipaadmin_password: SomeADMINpassword
+    zone_name: example.com
+    name: host04
+    a_rec:
+      - 192.168.122.221
+      - 192.168.122.222
+      - 192.168.122.223
+      - 192.168.122.224
+```
+
+Example playbook to ensure A and AAAA records are present, with reverse records (PTR):
+```yaml
+- ipadnsrecord:
+    ipaadmin_password: SomeADMINpassword
+    zone_name: example.com
+    name: host01
+    a_rec:
+      - 192.168.122.221
+      - 192.168.122.222
+    aaaa_rec:
+      - fd00:;0001
+      - fd00::0002
+    create_reverse: yes
+```
+
+Example playbook to ensure multiple A and AAAA records are present, but only A records have reverse records:
+```yaml
+- ipadnsrecord:
+    ipaadmin_password: SomeADMINpassword
+    zone_name: example.com
+    name: host01
+    a_ip_address: 192.168.122.221
+    aaaa_ip_address: fd00::0001
+    a_create_reverse: yes
+```
+
+Example playbook to ensure multiple DNS records are absent:
+
+```yaml
+---
+- ipadnsrecord:
+    ipaadmin_password: SomeADMINpassword
+    zone_name: example.com
+    records:
+    - name: host01
+      del_all: yes
+    - name: host02
+      del_all: yes
+    - name: host03
+      del_all: yes
+    - name: host04
+      del_all: yes
+    - name: _ftp._tcp
+      del_all: yes
+    - name: _sip._udp
+      del_all: yes
+    state: absent
+```
+
+Variables
+=========
+
+ipadnsrecord
+------------
+
+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
+`zone_name` \| `dnszone` | The DNS zone name to which DNS record needs to be managed. You can use one global zone name for multiple records. | no
+  required: true
+`records` | The list of dns records dicts. Each `records` dict entry can contain **record variables**. | no
+  | **Record variables** | no
+**Record variables** | Used when defining a single record. | no
+`state` | The state to ensure. It can be one of `present` or `absent`, and defaults to `present`. | yes
+
+
+**Record Variables:**
+
+Variable | Description | Required
+-------- | ----------- | --------
+`zone_name` \| `dnszone` | The DNS zone name to which DNS record needs to be managed. You can use one global zone name for multiple records. When used on a `records` dict, overrides the global `zone_name`. | yes
+`name` \| `record_name` | The DNS record name to manage. | yes
+`record_type` | The type of DNS record. Supported values are  `A`, `AAAA`, `A6`, `AFSDB`, `CERT`, `CNAME`, `DLV`, `DNAME`, `DS`, `KX`, `LOC`, `MX`, `NAPTR`, `NS`, `PTR`, `SRV`, `SSHFP`, `TLSA`, `TXT`, `URI`, and defaults to `A`. | no
+`record_value` | Manage DNS record name with this values. | no
+`record_ttl` | Set the TTL for the record. (int) | no
+`del_all` | Delete all associated records. (bool) | no
+`a_rec` \| `a_record` | Raw A record. | no
+`aaaa_rec` \| `aaaa_record` |  Raw AAAA record. | no
+`a6_rec` \| `a6_record` | Raw A6 record data. | no
+`afsdb_rec` \| `afsdb_record` | Raw AFSDB record. | no
+`cert_rec` \| `cert_record` | Raw CERT record. | no
+`cname_rec` \| `cname_record` | Raw CNAME record. | no
+`dlv_rec` \| `dlv_record` |  Raw DLV record. | no
+`dname_rec` \| `dname_record` | Raw DNAM record. | no
+`ds_rec` \| `ds_record` | Raw DS record. | no
+`kx_rec` \| `kx_record` |  Raw KX record. | no
+`loc_rec` \| `loc_record` | Raw LOC record. | no
+`mx_rec` \| `mx_record` | Raw MX record. | no
+`naptr_rec` \| `naptr_record` | Raw NAPTR record. | no
+`ns_rec` \| `ns_record` | Raw NS record. | no
+`ptr_rec` \| `ptr_record` | Raw PTR record. | no
+`srv_rec` \| `srv_record` | Raw SRV record. | no
+`sshfp_rec` \| `sshfp_record` | Raw SSHFP record. | no
+`tlsa_rec` \| `tlsa_record` | Raw TLSA record. | no
+`txt_rec` \| `txt_record` | Raw TXT record. | no
+`uri_rec` \| `uri_record` | Raw URI record. | no
+`ip_address` | IP adress for A or AAAA records. Set `record_type` to `A` or `AAAA`. | no
+`create_reverse` \| `reverse` | Create reverse records for `A` and `AAAA` record types. There is no equivalent to remove reverse records. (bool) | no
+`a_ip_address` | IP adress for A records. Set `record_type` to `A`. | no
+`a_create_reverse` | Create reverse records only for `A` records. There is no equivalent to remove reverse records. (bool) | no
+`aaaa_ip_address` | IP adress for AAAA records. Set `record_type` `AAAA`. | no
+`aaaa_create_reverse` | Create reverse records only for `AAAA` record types. There is no equivalent to remove reverse records. (bool) | no
+`a6_data` | A6 record. Set `record_type` to `A6`. | no
+`afsdb_subtype` | AFSDB Subtype. Set `record_type` to `AFSDB`. (int) | no
+`afsdb_hostname` | AFSDB Hostname. Set `record_type` to `AFSDB`. | no
+`cert_type` | CERT Certificate Type. Set `record_type` to `CERT`. (int) | no
+`cert_key_tag` | CERT Key Tag. Set `record_type` to `CERT`. (int) | no
+`cert_algorithm` | CERT Algorithm. Set `record_type` to `CERT`. (int) | no
+`cert_certificate_or_crl` | CERT Certificate or  Certificate Revocation List (CRL). Set `record_type` to `CERT`. | no
+`cname_hostname` | A hostname which this alias hostname points to. Set `record_type` to `CNAME`. | no
+`dlv_key_tag` | DS Key Tag. Set `record_type` to `DLV`. (int) | no
+`dlv_algorithm` | DLV Algorithm. Set `record_type` to `DLV`. (int) | no
+`dlv_digest_type` | DLV Digest Type. Set `record_type` to `DLV`. (int) | no
+`dlv_digest` | DLV Digest. Set `record_type` to `DLV`. | no
+`dname_target` | DNAME Target. Set `record_type` to `DNAME`. | no
+`ds_key_tag` | DS Key Tag. Set `record_type` to `DS`. (int) | no
+`ds_algorithm` | DS Algorithm. Set `record_type` to `DS`. (int) | no
+`ds_digest_type` | DS Digest Type. Set `record_type` to `DS`. (int) | no
+`ds_digest` | DS Digest. Set `record_type` to `DS`. | no
+`kx_preference` | Preference given to this exchanger. Lower values are more preferred. Set `record_type` to `KX`. (int) | no
+`kx_exchanger` | A host willing to act as a key exchanger.  Set `record_type` to `KX`. | no
+`loc_lat_deg` | LOC Degrees Latitude. Set `record_type` to `LOC`. (int) | no
+`loc_lat_min` | LOC Minutes Latitude. Set `record_type` to `LOC`. (int) | no
+`loc_lat_sec` | LOC Seconds Latitude. Set `record_type` to `LOC`. (float) | no
+`loc_lat_dir` | LOC Direction Latitude. Valid values are `N` or `S`. Set `record_type` to `LOC`. (int) | no
+`loc_lon_deg` | LOC Degrees Longitude. Set `record_type` to `LOC`. (int) | no
+`loc_lon_min` | LOC Minutes Longitude. Set `record_type` to `LOC`. (int) | no
+`loc_lon_sec` | LOC Seconds Longitude. Set `record_type` to `LOC`. (float) | no
+`loc_lon_dir` | LOC Direction Longitude. Valid values are `E` or `W`. Set `record_type` to `LOC`. (int) | no
+`loc_altitude` | LOC Altitude. Set `record_type` to `LOC`. (float) | no
+`loc_size` | LOC Size. Set `record_type` to `LOC`. (float) | no
+`loc_h_precision` | LOC Horizontal Precision. Set `record_type` to `LOC`. (float) | no
+`loc_v_precision` | LOC Vertical Precision. Set `record_type` to `LOC`. (float) | no
+`mx_preference` | Preference given to this exchanger. Lower values are more preferred. Set `record_type` to `MX`. (int) | no
+`mx_exchanger` | A host willing to act as a mail exchanger.  Set `record_type` to `LOC`. | no
+`naptr_order` | NAPTR Order. Set `record_type` to `NAPTR`. (int) | no
+`naptr_preference` | NAPTR Preference. Set `record_type` to `NAPTR`. (int) | no
+`naptr_flags` | NAPTR Flags. Set `record_type` to `NAPTR`. | no
+`naptr_service` | NAPTR Service. Set `record_type` to `NAPTR`. | no
+`naptr_regexp` | NAPTR Regular Expression. Set `record_type` to `NAPTR`. | no
+`naptr_replacement` | NAPTR Replacement. Set `record_type` to `NAPTR`. | no
+`ns_hostname` | NS Hostname. Set `record_type` to `NS`. | no
+`ptr_hostname` | The hostname this reverse record points to. . Set `record_type` to `PTR`. | no
+`srv_priority` | Lower number means higher priority. Clients will attempt to contact the server with the lowest-numbered priority they can reach. Set `record_type` to `SRV`. (int) | no
+`srv_weight` | Relative weight for entries with the same priority. Set `record_type` to `SRV`. (int) | no
+`srv_port` | SRV Port. Set `record_type` to `SRV`. (int) | no
+`srv_target` | The domain name of the target host or '.' if the service is decidedly not available at this domain. Set `record_type` to `SRV`. | no
+`sshfp_algorithm` | SSHFP Algorithm. Set `record_type` to `SSHFP`. (int) | no
+`sshfp_fp_type` | SSHFP Fingerprint Type. Set `record_type` to `SSHFP`. (int) | no
+`sshfp_fingerprint`| SSHFP Fingerprint. Set `record_type` to `SSHFP`. (int) | no
+`txt_data` | TXT Text Data. Set `record_type` to `TXT`. | no
+`tlsa_cert_usage` | TLSA Certificate Usage. Set `record_type` to `TLSA`. (int) | no
+`tlsa_selector` | TLSA Selector. Set `record_type` to `TLSA`. (int) | no
+`tlsa_matching_type` | TLSA Matching Type. Set `record_type` to `TLSA`. (int) | no
+`tlsa_cert_association_data` | TLSA Certificate Association Data. Set `record_type` to `TLSA`. | no
+`uri_target` | Target Uniform Resource Identifier according to RFC 3986. Set `record_type` to `URI`. | no
+`uri_priority` | Lower number means higher priority. Clients will attempt to contact the URI with the lowest-numbered priority they can reach. Set `record_type` to `URI`. (int) | no
+`uri_weight` | Relative weight for entries with the same priority. Set `record_type` to `URI`. (int) | no
+
+
+Authors
+=======
+
+Rafael Guterres Jeffman
diff --git a/README.md b/README.md
index c6583fd4190fcd1e273aa2e263ac3b9250c80a9f..246a8b4c01a4132a57d3814f08f2802c5c73ea5a 100644
--- a/README.md
+++ b/README.md
@@ -12,6 +12,7 @@ Features
 * One-time-password (OTP) support for client installation
 * Repair mode for clients
 * Modules for dns forwarder management
+* Modules for dns record management
 * Modules for dns zone management
 * Modules for group management
 * Modules for hbacrule management
@@ -411,6 +412,7 @@ Modules in plugin/modules
 
 * [ipadnsconfig](README-dnsconfig.md)
 * [ipadnsforwardzone](README-dnsforwardzone.md)
+* [ipadnsrecord](README-dnsrecord.md)
 * [ipadnszone](README-dnszone.md)
 * [ipagroup](README-group.md)
 * [ipahbacrule](README-hbacrule.md)
diff --git a/playbooks/dnsrecord/ensure-A-and-AAAA-records-are-absent.yml b/playbooks/dnsrecord/ensure-A-and-AAAA-records-are-absent.yml
new file mode 100644
index 0000000000000000000000000000000000000000..f4dffc66db52d12829279970fe608e4484712bbb
--- /dev/null
+++ b/playbooks/dnsrecord/ensure-A-and-AAAA-records-are-absent.yml
@@ -0,0 +1,18 @@
+---
+- name: Test PTR Record is present.
+  hosts: ipaserver
+  become: true
+  gather_facts: false
+
+  tasks:
+  # Ensure a PTR record is present
+  - name: Ensure that 'host04' has A and AAAA records.
+    ipadnsrecord:
+      ipaadmin_password: SomeADMINpassword
+      zone_name: ipatest.local
+      records:
+      - name: host04
+        a_ip_address: 192.168.122.104
+      - name: host04
+        aaaa_ip_address: ::1
+      state: absent
diff --git a/playbooks/dnsrecord/ensure-A-and-AAAA-records-are-present.yml b/playbooks/dnsrecord/ensure-A-and-AAAA-records-are-present.yml
new file mode 100644
index 0000000000000000000000000000000000000000..b59acfe0fc2cb456b67088c09911774b8befe632
--- /dev/null
+++ b/playbooks/dnsrecord/ensure-A-and-AAAA-records-are-present.yml
@@ -0,0 +1,17 @@
+---
+- name: Test PTR Record is present.
+  hosts: ipaserver
+  become: true
+  gather_facts: false
+
+  tasks:
+  # Ensure a PTR record is present
+  - name: Ensure that 'host04' has A and AAAA records.
+    ipadnsrecord:
+      ipaadmin_password: SomeADMINpassword
+      zone_name: ipatest.local
+      records:
+      - name: host04
+        a_ip_address: 192.168.122.104
+      - name: host04
+        aaaa_ip_address: ::1
diff --git a/playbooks/dnsrecord/ensure-CNAME-record-is-absent.yml b/playbooks/dnsrecord/ensure-CNAME-record-is-absent.yml
new file mode 100644
index 0000000000000000000000000000000000000000..9b02b14728ab7bdd6d6f2b89d6dac16294d2f586
--- /dev/null
+++ b/playbooks/dnsrecord/ensure-CNAME-record-is-absent.yml
@@ -0,0 +1,13 @@
+---
+- name: Test CNAME Record is present.
+  hosts: ipaserver
+  become: true
+  gather_facts: false
+
+  tasks:
+  # Ensure that 'host04' has CNAME, with cname_hostname
+  - ipadnsrecord:
+      zone_name: example.com
+      name: host04
+      cname_hostname: host04.example.com
+      state: absent
diff --git a/playbooks/dnsrecord/ensure-CNAME-record-is-present.yml b/playbooks/dnsrecord/ensure-CNAME-record-is-present.yml
new file mode 100644
index 0000000000000000000000000000000000000000..e6e918c35a072261bc97074d281b70386e40808d
--- /dev/null
+++ b/playbooks/dnsrecord/ensure-CNAME-record-is-present.yml
@@ -0,0 +1,12 @@
+---
+- name: Test CNAME Record is present.
+  hosts: ipaserver
+  become: true
+  gather_facts: false
+
+  tasks:
+  # Ensure that 'host04' has CNAME, with cname_hostname
+  - ipadnsrecord:
+      zone_name: example.com
+      name: host04
+      cname_hostname: host04.example.com
diff --git a/playbooks/dnsrecord/ensure-MX-record-is-present.yml b/playbooks/dnsrecord/ensure-MX-record-is-present.yml
new file mode 100644
index 0000000000000000000000000000000000000000..139c168180aa2d2aa7db61d1b0f280b49d4789d4
--- /dev/null
+++ b/playbooks/dnsrecord/ensure-MX-record-is-present.yml
@@ -0,0 +1,15 @@
+---
+- name: Ensure MX Record is present.
+  hosts: ipaserver
+  become: true
+  gather_facts: false
+
+  tasks:
+  # Ensure an MX record is absent
+  - ipadnsrecord:
+      ipaadmin_password: SomeADMINpassword
+      name: '@'
+      record_type: 'MX'
+      record_value: '1 mailserver.example.com'
+      zone_name: example.com
+      state: present
diff --git a/playbooks/dnsrecord/ensure-PTR-record-is-present.yml b/playbooks/dnsrecord/ensure-PTR-record-is-present.yml
new file mode 100644
index 0000000000000000000000000000000000000000..0a59c0d29e80116571b279f839c371635ac4aaeb
--- /dev/null
+++ b/playbooks/dnsrecord/ensure-PTR-record-is-present.yml
@@ -0,0 +1,15 @@
+---
+- name: Test PTR Record is present.
+  hosts: ipaserver
+  become: true
+  gather_facts: false
+
+  tasks:
+  # Ensure a PTR record is present
+  - ipadnsrecord:
+      ipaadmin_password: SomeADMINpassword
+      name: 5
+      record_type: 'PTR'
+      record_value: 'internal.ipa.example.com'
+      zone_name: 2.168.192.in-addr.arpa
+      state: present
diff --git a/playbooks/dnsrecord/ensure-SRV-record-is-present.yml b/playbooks/dnsrecord/ensure-SRV-record-is-present.yml
new file mode 100644
index 0000000000000000000000000000000000000000..3c18ff34f909a485217b137d35995f97cb9d68cc
--- /dev/null
+++ b/playbooks/dnsrecord/ensure-SRV-record-is-present.yml
@@ -0,0 +1,15 @@
+---
+- name: Test SRV Record is present.
+  hosts: ipaserver
+  become: true
+  gather_facts: false
+
+  tasks:
+  # Ensure a SRV record is present
+  - ipadnsrecord:
+      ipaadmin_password: SomeADMINpassword
+      name: _kerberos._udp.example.com
+      record_type: 'SRV'
+      record_value: '10 50 88 ipa.example.com'
+      zone_name: example.com
+      state: present
diff --git a/playbooks/dnsrecord/ensure-SSHFP-record-is-present.yml b/playbooks/dnsrecord/ensure-SSHFP-record-is-present.yml
new file mode 100644
index 0000000000000000000000000000000000000000..99ec554251a75448fe8cceb311ca118d640395c1
--- /dev/null
+++ b/playbooks/dnsrecord/ensure-SSHFP-record-is-present.yml
@@ -0,0 +1,16 @@
+---
+- name: Test SSHFP Record is present.
+  hosts: ipaserver
+  become: true
+  gather_facts: false
+
+  tasks:
+  # Ensure a SSHFP record is present
+  # SSHFP fingerprint generated with `ssh-keygen -r host04.testzone.local`
+  - ipadnsrecord:
+      ipaadmin_password: SomeADMINpassword
+      zone_name: example.com
+      name: host04
+      sshfp_algorithm: 1
+      sshfp_fp_type: 1
+      sshfp_fingerprint: d21802c61733e055b8d16296cbce300efb8a167a
diff --git a/playbooks/dnsrecord/ensure-TLSA-record-is-present.yml b/playbooks/dnsrecord/ensure-TLSA-record-is-present.yml
new file mode 100644
index 0000000000000000000000000000000000000000..65e947929e35c9dd3ff4e1caa73fe7105dc30b60
--- /dev/null
+++ b/playbooks/dnsrecord/ensure-TLSA-record-is-present.yml
@@ -0,0 +1,16 @@
+---
+- name: Test SSHFP Record is present.
+  hosts: ipaserver
+  become: true
+  gather_facts: false
+
+  tasks:
+  # Ensure a SSHFP record is present
+  - ipadnsrecord:
+      ipaadmin_password: SomeADMINpassword
+      zone_name: example.com
+      name: host04
+      tlsa_cert_usage: 3
+      tlsa_selector: 1
+      tlsa_matching_type: 1
+      tlsa_cert_association_data: 9c0ad776dbeae8d9d55b0ad42899d30235c114d5f918fd69746e4279e47bdaa2
diff --git a/playbooks/dnsrecord/ensure-TXT-record-is-present.yml b/playbooks/dnsrecord/ensure-TXT-record-is-present.yml
new file mode 100644
index 0000000000000000000000000000000000000000..35be86e164d80f36bc85dcab5b0e0c729cdf6ef3
--- /dev/null
+++ b/playbooks/dnsrecord/ensure-TXT-record-is-present.yml
@@ -0,0 +1,15 @@
+---
+- name: Test TXT Record is present.
+  hosts: ipaserver
+  become: true
+  gather_facts: false
+
+  tasks:
+  # Ensure a TXT record is absent
+  - ipadnsrecord:
+      ipaadmin_password: SomeADMINpassword
+      name: _kerberos
+      record_type: 'TXT'
+      record_value: 'EXAMPLE.COM'
+      zone_name: example.com
+      state: present
diff --git a/playbooks/dnsrecord/ensure-URI-record-is-present.yml b/playbooks/dnsrecord/ensure-URI-record-is-present.yml
new file mode 100644
index 0000000000000000000000000000000000000000..be1b25df4d0e609ba3dfc72a2631ba4c2e3160fe
--- /dev/null
+++ b/playbooks/dnsrecord/ensure-URI-record-is-present.yml
@@ -0,0 +1,17 @@
+---
+- name: Test URI Record is present.
+  hosts: ipaserver
+  become: true
+  gather_facts: false
+
+  tasks:
+  # Ensure a URI record is absent
+  - ipadnsrecord:
+      ipaadmin_password: SomeADMINpassword
+      name: _ftp._tcp
+      record_type: 'URI'
+      uri_priority: 10
+      uri_weight: 1
+      uri_target: ftp://ftp.example.com/public
+      zone_name: example.com
+      state: present
diff --git a/playbooks/dnsrecord/ensure-dnsrecord-is-absent.yml b/playbooks/dnsrecord/ensure-dnsrecord-is-absent.yml
new file mode 100644
index 0000000000000000000000000000000000000000..3cfed05c966c0720fe69c8ae84ca0229bdb5b524
--- /dev/null
+++ b/playbooks/dnsrecord/ensure-dnsrecord-is-absent.yml
@@ -0,0 +1,15 @@
+---
+- name: Test DNS Record is absent.
+  hosts: ipaserver
+  become: true
+  gather_facts: false
+
+  tasks:
+  # Ensure that dns record is absent
+  - ipadnsrecord:
+      ipaadmin_password: SomeADMINpassword
+      name: host01
+      zone_name: example.com
+      record_type: 'AAAA'
+      record_value: '::1'
+      state: absent
diff --git a/playbooks/dnsrecord/ensure-dnsrecord-is-present.yml b/playbooks/dnsrecord/ensure-dnsrecord-is-present.yml
new file mode 100644
index 0000000000000000000000000000000000000000..b1ae113dca4241d025e7dad1c732cadc32e11290
--- /dev/null
+++ b/playbooks/dnsrecord/ensure-dnsrecord-is-present.yml
@@ -0,0 +1,15 @@
+---
+- name: Test DNS Record is present.
+  hosts: ipaserver
+  become: true
+  gather_facts: false
+
+  tasks:
+  # Ensure that dns record is present
+  - ipadnsrecord:
+      ipaadmin_password: SomeADMINpassword
+      name: host01
+      zone_name: example.com
+      record_type: 'AAAA'
+      record_value: '::1'
+      state: present
diff --git a/playbooks/dnsrecord/ensure-dnsrecord-with-reverse-is-present.yml b/playbooks/dnsrecord/ensure-dnsrecord-with-reverse-is-present.yml
new file mode 100644
index 0000000000000000000000000000000000000000..bef7d33aafceb7c01dbfb2c9e241407d83e0c2ff
--- /dev/null
+++ b/playbooks/dnsrecord/ensure-dnsrecord-with-reverse-is-present.yml
@@ -0,0 +1,15 @@
+---
+- name: Test DNS Record is present.
+  hosts: ipaserver
+  become: true
+  gather_facts: false
+
+  tasks:
+  # Ensure that dns record is present
+  - ipadnsrecord:
+      ipaadmin_password: SomeADMINpassword
+      name: host01
+      zone_name: example.com
+      ip_address: 192.160.123.45
+      create_reverse: yes
+      state: present
diff --git a/playbooks/dnsrecord/ensure-multiple-A-records-are-present.yml b/playbooks/dnsrecord/ensure-multiple-A-records-are-present.yml
new file mode 100644
index 0000000000000000000000000000000000000000..eb7be24cec92c1736d31fb1deaef9e8bdc186d95
--- /dev/null
+++ b/playbooks/dnsrecord/ensure-multiple-A-records-are-present.yml
@@ -0,0 +1,17 @@
+---
+- name: Playbook to manage DNS records.
+  hosts: ipaserver
+  become: yes
+  gather_facts: no
+
+  tasks:
+  - name: Ensure that 'host04' has multiple A records.
+    ipadnsrecord:
+      ipaadmin_password: SomeADMINpassword
+      zone_name: ipatest.local
+      name: host01
+      a_rec:
+        - 192.168.122.221
+        - 192.168.122.222
+        - 192.168.122.223
+        - 192.168.122.224
diff --git a/playbooks/dnsrecord/ensure-presence-multiple-records.yml b/playbooks/dnsrecord/ensure-presence-multiple-records.yml
new file mode 100644
index 0000000000000000000000000000000000000000..94e01aea7680bb691edc7e2f37b6c2dfcc7e4820
--- /dev/null
+++ b/playbooks/dnsrecord/ensure-presence-multiple-records.yml
@@ -0,0 +1,21 @@
+---
+- name: Test multiple DNS Records are present.
+  hosts: ipaserver
+  become: true
+  gather_facts: false
+
+  tasks:
+  # Ensure that multiple dns records are present
+  - ipadnsrecord:
+      ipaadmin_password: SomeADMINpassword
+      records:
+        - name: host01
+          zone_name: example.com
+          record_type: A
+          record_value:
+            - 192.168.122.112
+            - 192.168.122.122
+        - name: host01
+          zone_name: testzone.local
+          record_type: AAAA
+          record_value: ::1
diff --git a/plugins/modules/ipadnsrecord.py b/plugins/modules/ipadnsrecord.py
new file mode 100644
index 0000000000000000000000000000000000000000..89528e5f7e26a3ca80dbf4066a0fdbc9d7e457dc
--- /dev/null
+++ b/plugins/modules/ipadnsrecord.py
@@ -0,0 +1,1509 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+
+# Authors:
+#   Rafael Guterres Jeffman <rjeffman@redhat.com>
+#
+# Copyright (C) 2020 Red Hat
+# see file 'COPYING' for use and warranty information
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+"""DNS Record ansible-freeipa module."""
+
+ANSIBLE_METADATA = {
+    "metadata_version": "1.0",
+    "supported_by": "community",
+    "status": ["preview"],
+}
+
+DOCUMENTATION = """
+---
+module: ipadnsrecord
+short description: Manage FreeIPA DNS records
+description: Manage FreeIPA DNS records
+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
+    options:
+      name:
+        description: The DNS record name to manage.
+        aliases: ["record_name"]
+        required: true
+      zone_name:
+        description: The DNS zone name to which DNS record needs to be managed.
+        aliases: ["dnszone"]
+        required: true (if not provided globally)
+      record_type:
+        description: The type of DNS record.
+        choices: ["A", "AAAA", "A6", "AFSDB", "CERT", "CNAME", "DLV", "DNAME",
+                  "DS", "KX", "LOC", "MX", "NAPTR", "NS", "PTR", "SRV",
+                  "SSHFP", "TLSA", "TXT", "URI"]
+        default: "A"
+      record_value:
+        description: Manage DNS record name with these values.
+        required: false
+        type: list
+      record_ttl:
+        description: Set the TTL for the record.
+        required: false
+        type: int
+      del_all:
+        description: Delete all associated records.
+        required: false
+        type: bool
+      a_rec:
+        description: Raw A record.
+        required: false
+        aliases: ["a_record"]
+      aaaa_rec:
+        description: Raw AAAA record.
+        required: false
+        aliases: ["aaaa_record"]
+      a6_rec:
+        description: Raw A6 record.
+        required: false
+        aliases: ["a6_record"]
+      afsdb_rec:
+        description: Raw AFSDB record.
+        required: false
+        aliases: ["afsdb_record"]
+      cert_rec:
+        description: Raw CERT record.
+        required: false
+        aliases: ["cert_record"]
+      cname_rec:
+        description: Raw CNAME record.
+        required: false
+        aliases: ["cname_record"]
+      dlv_rec:
+        description: Raw DLV record.
+        required: false
+        aliases: ["dlv_record"]
+      dname_rec:
+        description: Raw DNAM record.
+        required: false
+        aliases: ["dname_record"]
+      ds_rec:
+        description: Raw DS record.
+        required: false
+        aliases: ["ds_record"]
+      kx_rec:
+        description: Raw KX record.
+        required: false
+        aliases: ["kx_record"]
+      loc_rec:
+        description: Raw LOC record.
+        required: false
+        aliases: ["loc_record"]
+      mx_rec:
+        description: Raw MX record.
+        required: false
+        aliases: ["mx_record"]
+      naptr_rec:
+        description: Raw NAPTR record.
+        required: false
+        aliases: ["naptr_record"]
+      ns_rec:
+        description: Raw NS record.
+        required: false
+        aliases: ["ns_record"]
+      ptr_rec:
+        description: Raw PTR record.
+        required: false
+        aliases: ["ptr_record"]
+      srv_rec:
+        description: Raw SRV record.
+        required: false
+        aliases: ["srv_record"]
+      sshfp_rec:
+        description: Raw SSHFP record.
+        required: false
+        aliases: ["sshfp_record"]
+      tlsa_rec:
+        description: Raw TLSA record.
+        required: false
+        aliases: ["tlsa_record"]
+      txt_rec:
+        description: Raw TXT record.
+        required: false
+        aliases: ["txt_record"]
+      uri_rec:
+        description: Raw URI record.
+        required: false
+        aliases: ["uri_record"]
+      ip_address:
+        description: IP adresses for A or AAAA records.
+        required: false
+        type: string
+      a_ip_address:
+        description: IP adresses for A records.
+        required: false
+        type: string
+      a_create_reverse:
+        description:
+          Create reverse record for A records.
+          There is no equivalent to remove reverse records.
+        type: bool
+        required: false
+      aaaa_ip_address:
+        description: IP adresses for AAAA records.
+        required: false
+        type: string
+      aaaa_create_reverse:
+        description:
+          Create reverse record for AAAA records.
+          There is no equivalent to remove reverse records.
+        type: bool
+        required: false
+      create_reverse:
+        description:
+          Create reverse record for A or AAAA record types.
+          There is no equivalent to remove reverse records.
+        type: bool
+        required: false
+        aliases: ["reverse"]
+      a6_data:
+        description: A6 record data.
+        required: false
+      afsdb_subtype:
+        description: AFSDB Subtype
+        required: false
+        type: int
+      afsdb_hostname:
+        discription: AFSDB Hostname
+        required: false
+        type: string
+      cert_type:
+        descriptioon: CERT Certificate Type
+        required: false
+        type: int
+      cert_key_tag:
+        description: CERT Key Tag
+        required: false
+        type: int
+      cert_algorithm:
+        description: CERT Algorithm
+        required: false
+        type: int
+      cert_certificate_or_crl:
+        description: CERT Certificate or Certificate Revocation List (CRL).
+        required: false
+        type: string
+      cname_hostname:
+        description: A hostname which this alias hostname points to.
+        required: false
+        type: string
+      dlv_key_tag:
+        description: DS Key Tag
+        required: false
+        type: int
+      dlv_algorithm:
+        description: DLV Algorithm
+        required: false
+        type: int
+      dlv_digest_type:
+        description: DLV Digest Type
+        required: false
+        type: int
+      dlv_digest:
+        descriptinion: DLV Digest
+        required: false
+        type: string
+      dname_target:
+        description: DNAME Target
+        required: false
+        type: string
+      ds_key_tag:
+        description: DS Key Tag
+        required: false
+        type: int
+      ds_algorithm:
+        description: DS Algorithm
+        required: false
+        type: int
+      ds_digest_type:
+        description: DS Digest Type
+        required: false
+        type: int
+      ds_digest:
+        descriptinion: DS Digest
+        required: false
+        type: string
+      kx_preference:
+        description:
+          Preference given to this exchanger. Lower values are more preferred.
+        required: false
+        type: int
+      kx_exchanger:
+        description: A host willing to act as a key exchanger.
+        required: false
+        type: string
+      loc_lat_deg:
+        description: LOC Degrees Latitude
+        required: false
+        type: int
+      loc_lat_min:
+        description: LOC Minutes Latitude
+        required: false
+        type: int
+      loc_lat_sec:
+        description: LOC Seconds Latitude
+        required: false
+        type: float
+      loc_lat_dir:
+        description: LOC Direction Latitude
+        required: false
+        choices: ["N", "S"]
+      loc_lon_deg:
+        description: LOC Degrees Longitude
+        required: false
+        type: int
+      loc_lon_min:
+        description: LOC Minutes Longitude
+        required: false
+        type: int
+      loc_lon_sec:
+        description: LOC Seconds Longitude
+        required: false
+        type: float
+      loc_lon_dir:
+        description: LOC Direction Longitude
+        required: false
+        choices: ["E", "W"]
+      loc_altitude:
+        description: LOC Altitude
+        required: false
+        type: float
+      loc_size:
+        description: LOC Size
+        required: false
+        type: float
+      loc_h_precision:
+        description: LOC Horizontal Precision
+        required: false
+        type: float
+      loc_v_precision:
+        description: LOC Vertical Precision
+        required: false
+        type: float
+      mx_preference:
+        description:
+          Preference given to this exchanger. Lower values are more preferred.
+        required: false
+        type: int
+      mx_exchanger:
+        description: A host willing to act as a mail exchanger.
+        required: false
+        type: string
+      naptr_order:
+        description: NAPTR Order
+        required: false
+        type: int
+      naptr_preference:
+        description: NAPTR Preference
+        required: false
+        type: int
+      naptr_flags:
+        description: NAPTR Flags
+        required: false
+        type: string
+      naptr_service:
+        description: NAPTR Service
+        required: false
+        type: string
+      naptr_regexp:
+        description: NAPTR Regular Expression
+        required: false
+        type: string
+      naptr_replacement:
+        description: NAPTR Replacement
+        required: false
+        type: string
+      ns_hostname:
+        description: NS Hostname
+        required: false
+        type: string
+      ptr_hostname:
+        description: The hostname this reverse record points to.
+        required: false
+        type: string
+      srv_priority:
+        description:
+          Lower number means higher priority. Clients will attempt to contact
+          the server with the lowest-numbered priority they can reach.
+        required: false
+        type: int
+      srv_weight:
+        description: Relative weight for entries with the same priority.
+        required: false
+        type: int
+      srv_port:
+        description: SRV Port
+        required: false
+        type: int
+      srv_target:
+        description:
+          The domain name of the target host or '.' if the service is decidedly
+          not available at this domain.
+        required: false
+        type: string
+      sshfp_algorithm:
+        description: SSHFP Algorithm
+      sshfp_fp_type:
+        description: SSHFP Fingerprint Type
+        required: False
+        type: int
+      sshfp_fingerprint:
+        description: SSHFP Fingerprint
+        required: False
+        type: string
+      txt_data:
+        description: TXT Text Data
+        required: false
+        type: string
+      tlsa_cert_usage:
+        description: TLSA Certificate Usage
+        required: false
+        type: int
+      tlsa_selector:
+        descrpition: TLSA Selector
+        required: false
+        type: int
+      tlsa_matching_type:
+        descrpition: TLSA Matching Type
+        required: false
+        type: int
+      tlsa_cert_association_data:
+        descrpition: TLSA Certificate Association Data
+        required: false
+        type: string
+      uri_target:
+        description: Target Uniform Resource Identifier according to RFC 3986.
+        required: false
+        type: string
+      uri_priority:
+        description:
+          Lower number means higher priority. Clients will attempt to contact
+          the URI with the lowest-numbered priority they can reach.
+        required: false
+        type: int
+      uri_weight:
+        description: Relative weight for entries with the same priority.
+        required: false
+        type: int
+  zone_name:
+    description: The DNS zone name to which DNS record needs to be managed.
+    aliases: ["dnszone"]
+    required: true (if not provided on each record)
+  name:
+    description: The DNS record name to manage.
+    aliases: ["record_name"]
+    required: true
+  record_type:
+    description: The type of DNS record.
+    required: false
+    choices: ["A", "AAAA", "A6", "AFSDB", "CERT", "CNAME", "DLV", "DNAME",
+              "DS", "KX", "LOC", "MX", "NAPTR", "NS", "PTR", "SRV", "SSHFP",
+              "TLSA", "TXT", "URI"]
+    default: "A"
+  record_value:
+    description: Manage DNS record name with this values.
+    required: false
+    type: list
+  record_ttl:
+    description: Set the TTL for the record.
+    required: false
+    type: int
+  del_all:
+    description: Delete all associated records.
+    required: false
+    type: bool
+  a_rec:
+    description: Raw A record.
+    required: false
+    aliases: ["a_record"]
+  aaaa_rec:
+    description: Raw AAAA record.
+    required: false
+    aliases: ["aaaa_record"]
+  a6_rec:
+    description: Raw A6 record.
+    required: false
+    aliases: ["a6_record"]
+  afsdb_rec:
+    description: Raw AFSDB record.
+    required: false
+    aliases: ["afsdb_record"]
+  cert_rec:
+    description: Raw CERT record.
+    required: false
+    aliases: ["cert_record"]
+  cname_rec:
+    description: Raw CNAME record.
+    required: false
+    aliases: ["cname_record"]
+  dlv_rec:
+    description: Raw DLV record.
+    required: false
+    aliases: ["dlv_record"]
+  dname_rec:
+    description: Raw DNAM record.
+    required: false
+    aliases: ["dname_record"]
+  ds_rec:
+    description: Raw DS record.
+    required: false
+    aliases: ["ds_record"]
+  kx_rec:
+    description: Raw KX record.
+    required: false
+    aliases: ["kx_record"]
+  loc_rec:
+    description: Raw LOC record.
+    required: false
+    aliases: ["loc_record"]
+  mx_rec:
+    description: Raw MX record.
+    required: false
+    aliases: ["mx_record"]
+  naptr_rec:
+    description: Raw NAPTR record.
+    required: false
+    aliases: ["naptr_record"]
+  ns_rec:
+    description: Raw NS record.
+    required: false
+    aliases: ["ns_record"]
+  ptr_rec:
+    description: Raw PTR record.
+    required: false
+    aliases: ["ptr_record"]
+  srv_rec:
+    description: Raw SRV record.
+    required: false
+    aliases: ["srv_record"]
+  sshfp_rec:
+    description: Raw SSHFP record.
+    required: false
+    aliases: ["sshfp_record"]
+  tlsa_rec:
+    description: Raw TLSA record.
+    required: false
+    aliases: ["tlsa_record"]
+  txt_rec:
+    description: Raw TXT record.
+    required: false
+    aliases: ["txt_record"]
+  uri_rec:
+    description: Raw URI record.
+    required: false
+    aliases: ["uri_record"]
+  ip_address:
+    description: IP adresses for A ar AAAA.
+    aliases: ["a_ip_address", "aaaa_ip_address"]
+    required: false
+    type: string
+  create_reverse:
+    description:
+      Create reverse record for A or AAAA record types.
+      There is no equivalent to remove reverse records.
+    type: bool
+    required: false
+    aliases: ["reverse"]
+  a_ip_address:
+    description: IP adresses for A records.
+    required: false
+    type: string
+  a_create_reverse:
+    description:
+      Create reverse record for A records.
+      There is no equivalent to remove reverse records.
+    type: bool
+    required: false
+  aaaa_ip_address:
+    description: IP adresses for AAAA records.
+    required: false
+    type: string
+  aaaa_create_reverse:
+    description:
+      Create reverse record for AAAA records.
+      There is no equivalent to remove reverse records.
+    type: bool
+    required: false
+  afsdb_subtype:
+    description: AFSDB Subtype
+    required: false
+    type: int
+  afsdb_hostname:
+    discription: AFSDB Hostname
+    required: false
+    type: string
+  cert_type:
+    descriptioon: CERT Certificate Type
+    required: false
+    type: int
+  cert_key_tag:
+    description: CERT Key Tag
+    required: false
+    type: int
+  cert_algorithm:
+    description: CERT Algorithm
+    required: false
+    type: int
+  cert_certificate_or_crl:
+    description: CERT Certificate/CRL
+    required: false
+    type: string
+  cname_hostname:
+    description: A hostname which this alias hostname points to.
+    required: false
+    type: string
+  dlv_key_tag:
+    description: DS Key Tag
+    required: false
+    type: int
+  dlv_algorithm:
+    description: DLV Algorithm
+    required: false
+    type: int
+  dlv_digest_type:
+    description: DLV Digest Type
+    required: false
+    type: int
+  dlv_digest:
+    descriptinion: DLV Digest
+    required: false
+    type: string
+  dname_target:
+    description: DNAME Target
+    required: false
+    type: string
+  ds_key_tag:
+    description: DS Key Tag
+    required: false
+    type: int
+  ds_algorithm:
+    description: DS Algorithm
+    required: false
+    type: int
+  ds_digest_type:
+    description: DS Digest Type
+    required: false
+    type: int
+  ds_digest:
+    descriptinion: DS Digest
+    required: false
+    type: string
+  kx_preference:
+    description:
+      Preference given to this exchanger. Lower values are more preferred.
+    required: false
+    type: int
+  kx_exchanger:
+    description: A host willing to act as a key exchanger.
+    required: false
+    type: string
+  loc_lat_deg:
+    description: LOC Degrees Latitude
+    required: false
+    type: int
+  loc_lat_min:
+    description: LOC Minutes Latitude
+    required: false
+    type: int
+  loc_lat_sec:
+    description: LOC Seconds Latitude
+    required: false
+    type: float
+  loc_lat_dir:
+    description: LOC Direction Latitude
+    required: false
+    choices: ["N", "S"]
+  loc_lon_deg:
+    description: LOC Degrees Longitude
+    required: false
+    type: int
+  loc_lon_min:
+    description: LOC Minutes Longitude
+    required: false
+    type: int
+  loc_lon_sec:
+    description: LOC Seconds Longitude
+    required: false
+    type: float
+  loc_lon_dir:
+    description: LOC Direction Longitude
+    required: false
+    choices: ["E", "W"]
+  loc_altitude:
+    description: LOC Altitude
+    required: false
+    type: float
+  loc_size:
+    description: LOC Size
+    required: false
+    type: float
+  loc_h_precision:
+    description: LOC Horizontal Precision
+    required: false
+    type: float
+  loc_v_precision:
+    description: LOC Vertical Precision
+    required: false
+    type: float
+  mx_preference:
+    description:
+      Preference given to this exchanger. Lower values are more preferred.
+    required: false
+    type: int
+  mx_exchanger:
+    description: A host willing to act as a mail exchanger.
+    required: false
+    type: string
+  naptr_order:
+    description: NAPTR Order
+    required: false
+    type: int
+  naptr_preference:
+    description: NAPTR Preference
+    required: false
+    type: int
+  naptr_flags:
+    description: NAPTR Flags
+    required: false
+    type: string
+  naptr_service:
+    description: NAPTR Service
+    required: false
+    type: string
+  naptr_regexp:
+    description: NAPTR Regular Expression
+    required: false
+    type: string
+  naptr_replacement:
+    description: NAPTR Replacement
+    required: false
+    type: string
+  ns_hostname:
+    description: NS Hostname
+    required: false
+    type: string
+  ptr_hostname:
+    description: The hostname this reverse record points to.
+    required: false
+    type: string
+  srv_priority:
+    description:
+      Lower number means higher priority. Clients will attempt to contact the
+      server with the lowest-numbered priority they can reach.
+    required: false
+    type: int
+  srv_weight:
+    description: Relative weight for entries with the same priority.
+    required: false
+    type: int
+  srv_port:
+    description: SRV Port
+    required: false
+    type: int
+  srv_target:
+    description:
+      The domain name of the target host or '.' if the service is decidedly not
+      available at this domain.
+    required: false
+    type: string
+  sshfp_algorithm:
+    description: SSHFP Algorithm
+  sshfp_fp_type:
+    description: SSHFP Fingerprint Type
+    required: False
+    type: int
+  sshfp_fingerprint:
+    description: SSHFP Fingerprint
+    required: False
+    type: string
+  txt_data:
+    description: TXT Text Data
+    required: false
+    type: string
+  tlsa_cert_usage:
+    description: TLSA Certificate Usage
+    required: false
+    type: int
+  tlsa_selector:
+    descrpition: TLSA Selector
+    required: false
+    type: int
+  tlsa_matching_type:
+    descrpition: TLSA Matching Type
+    required: false
+    type: int
+  tlsa_cert_association_data:
+    descrpition: TLSA Certificate Association Data
+    required: false
+    type: string
+  uri_target:
+    description: Target Uniform Resource Identifier according to RFC 3986.
+    required: false
+    type: string
+  uri_priority:
+    description:
+      Lower number means higher priority. Clients will attempt to contact the
+      URI with the lowest-numbered priority they can reach.
+    required: false
+    type: int
+  uri_weight:
+    description: Relative weight for entries with the same priority.
+    required: false
+    type: int
+  state:
+    description: State to ensure
+    default: present
+    choices: ["present", "absent"]
+
+author:
+    - Rafael Guterres Jeffman
+"""
+
+EXAMPLES = """
+# Ensure dns record is present
+- ipadnsrecord:
+    ipaadmin_password: SomeADMINpassword
+    name: vm-001
+    zone_name: example.com
+    record_type: 'AAAA'
+    record_value: '::1'
+
+# Ensure that dns record exists with a TTL
+- ipadnsrecord:
+    ipaadmin_password: SomeADMINpassword
+    name: host01
+    zone_name: example.com
+    record_type: 'AAAA'
+    record_value: '::1'
+    record_ttl: 300
+
+# Ensure that dns record exists with a reverse record
+- ipadnsrecord:
+    ipaadmin_password: SomeADMINpassword
+    name: host02
+    zone_name: example.com
+    record_type: 'AAAA'
+    record_value: 'fd00::0002'
+    create_reverse: yes
+
+# Ensure a PTR record is present
+- ipadnsrecord:
+    ipaadmin_password: SomeADMINpassword
+    name: 5
+    zone_name: 2.168.192.in-addr.arpa
+    record_type: 'PTR'
+    record_value: 'internal.ipa.example.com'
+
+# Ensure a TXT record is present
+- ipadnsrecord:
+    ipaadmin_password: SomeADMINpassword
+    name: _kerberos
+    zone_name: example.com
+    record_type: 'TXT'
+    record_value: 'EXAMPLE.COM'
+
+# Ensure a SRV record is present
+- ipadnsrecord:
+    ipaadmin_password: SomeADMINpassword
+    name: _kerberos._udp.example.com
+    zone_name: example.com
+    record_type: 'SRV'
+    record_value: '10 50 88 ipa.example.com'
+
+# Ensure an MX record is present
+- ipadnsrecord:
+    ipaadmin_password: SomeADMINpassword
+    name: '@'
+    zone_name: example.com
+    record_type: 'MX'
+    record_value: '1 mailserver.example.com'
+
+# Ensure that dns record is absent
+- ipadnsrecord:
+    ipaadmin_password: SomeADMINpassword
+    name: host01
+    zone_name: example.com
+    record_type: 'AAAA'
+    record_value: '::1'
+    state: absent
+"""
+
+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
+import dns.reversename
+import dns.resolver
+import ipalib.errors
+import six
+
+
+if six.PY3:
+    unicode = str
+
+_SUPPORTED_RECORD_TYPES = [
+    "A", "AAAA", "A6", "AFSDB", "CERT", "CNAME", "DLV", "DNAME", "DS", "KX",
+    "LOC", "MX", "NAPTR", "NS", "PTR", "SRV", "SSHFP", "TLSA", "TXT", "URI"]
+
+_RECORD_FIELDS = [
+    "a_rec", "aaaa_rec", "a6_rec", "afsdb_rec", "cert_rec",
+    "cname_rec", "dlv_rec", "dname_rec", "ds_rec", "kx_rec", "loc_rec",
+    "mx_rec", "naptr_rec", "ns_rec", "ptr_rec", "srv_rec", "sshfp_rec",
+    "tlsa_rec", "txt_rec", "uri_rec"
+]
+
+_PART_MAP = {
+    'a_ip_address': 'a_part_ip_address',
+    'a_create_reverse': 'a_extra_create_reverse',
+    'aaaa_ip_address': 'aaaa_part_ip_address',
+    'aaaa_create_reverse': 'aaaa_extra_create_reverse',
+    'a6_data': 'a6_part_data',
+    'afsdb_subtype': 'afsdb_part_subtype',
+    'afsdb_hostname': 'afsdb_part_hostname',
+    'cert_type': 'cert_part_type',
+    'cert_key_tag': 'cert_part_key_tag',
+    'cert_algorithm': 'cert_part_algorithm',
+    'cert_certificate_or_crl': 'cert_part_certificate_or_crl',
+    'cname_hostname': 'cname_part_hostname',
+    'dlv_algorithm': 'dlv_part_algorithm',
+    'dlv_digest': 'dlv_part_digest',
+    'dlv_digest_type': 'dlv_part_digest_type',
+    'dlv_key_tag': 'dlv_part_key_tag',
+    'dname_target': 'dname_part_target',
+    'ds_algorithm': 'ds_part_algorithm',
+    'ds_digest': 'ds_part_digest',
+    'ds_digest_type': 'ds_part_digest_type',
+    'ds_key_tag': 'ds_part_key_tag',
+    'kx_preference': 'kx_part_preference',
+    'kx_exchanger': 'kx_part_exchanger',
+    "loc_lat_deg": "loc_part_lat_deg",
+    "loc_lat_min": "loc_part_lat_min",
+    "loc_lat_sec": "loc_part_lat_sec",
+    "loc_lat_dir": "loc_part_lat_dir",
+    "loc_lon_deg": "loc_part_lon_deg",
+    "loc_lon_min": "loc_part_lon_min",
+    "loc_lon_sec": "loc_part_lon_sec",
+    "loc_lon_dir": "loc_part_lon_dir",
+    "loc_altitude": "loc_part_altitude",
+    "loc_size": "loc_part_size",
+    "loc_h_precision": "loc_part_h_precision",
+    "loc_v_precision": "loc_part_v_precision",
+    "mx_preference": "mx_part_preference",
+    "mx_exchanger": 'mx_part_exchanger',
+    "naptr_order": "naptr_part_order",
+    "naptr_preference": "naptr_part_preference",
+    "naptr_flags": "naptr_part_flags",
+    "naptr_service": "naptr_part_service",
+    "naptr_regexp": "naptr_part_regexp",
+    "naptr_replacement": "naptr_part_replacement",
+    'ns_hostname': 'ns_part_hostname',
+    'ptr_hostname': 'ptr_part_hostname',
+    "srv_priority": "srv_part_priority",
+    "srv_weight": "srv_part_weight",
+    "srv_port": "srv_part_port",
+    "srv_target": "srv_part_target",
+    'sshfp_algorithm': 'sshfp_part_algorithm',
+    'sshfp_fingerprint': 'sshfp_part_fingerprint',
+    'sshfp_fp_type': 'sshfp_part_fp_type',
+    "tlsa_cert_usage": "tlsa_part_cert_usage",
+    "tlsa_cert_association_data": "tlsa_part_cert_association_data",
+    "tlsa_matching_type": "tlsa_part_matching_type",
+    "tlsa_selector": "tlsa_part_selector",
+    'txt_data': 'txt_part_data',
+    "uri_priority": "uri_part_priority",
+    "uri_target": "uri_part_target",
+    "uri_weight": "uri_part_weight"
+}
+
+_RECORD_PARTS = {
+    "arecord": ["a_part_ip_address", "a_extra_create_reverse"],
+    "aaaarecord": [
+        "aaaa_part_ip_address", "aaaa_extra_create_reverse"
+    ],
+    "a6record": ["a6_part_data"],
+    "afsdbrecord": ['afsdb_part_subtype', 'afsdb_part_hostname'],
+    "cert_rec": [
+        'cert_part_type', 'cert_part_key_tag', 'cert_part_algorithm',
+        'cert_part_certificate_or_crl'
+    ],
+    "cnamerecord": ["cname_part_hostname"],
+    "dlvrecord": [
+        'dlv_part_key_tag', 'dlv_part_algorithm', 'dlv_part_digest_type',
+        'dlv_part_digest'
+    ],
+    "dnamerecord": ["dname_part_target"],
+    "dsrecord": ['ds_part_key_tag', 'ds_part_algorithm',
+                 'ds_part_digest_type', 'ds_part_digest'],
+    "kxrecord": ['kx_part_preference', 'kx_part_exchanger'],
+    "locrecord": [
+        "loc_part_lat_deg", "loc_part_lat_min", "loc_part_lat_sec",
+        "loc_part_lat_dir", "loc_part_lon_deg", "loc_part_lon_min",
+        "loc_part_lon_sec", "loc_part_lon_dir", "loc_part_altitude",
+        "loc_part_size", "loc_part_h_precision", "loc_part_v_precision"
+    ],
+    "mxrecord": ['mx_part_preference', 'mx_part_exchanger'],
+    "naptrrecord": [
+        "naptr_part_order", "naptr_part_preference", "naptr_part_flags",
+        "naptr_part_service", "naptr_part_regexp", "naptr_part_replacement"
+    ],
+    "nsrecord": ["ns_part_hostname"],
+    "ptrrecord": ["ptr_part_hostname"],
+    "srvrecord": [
+        "srv_part_priority", "srv_part_weight", "srv_part_port",
+        "srv_part_target",
+    ],
+    "sshfprecord": [
+        'sshfp_part_algorithm', 'sshfp_part_fingerprint',
+        'sshfp_part_fp_type'
+    ],
+    "tlsarecord": [
+        "tlsa_part_cert_usage", "tlsa_part_cert_association_data",
+        "tlsa_part_matching_type", "tlsa_part_selector"
+    ],
+    "txtrecord": ["txt_part_data"],
+    "urirecord": ["uri_part_priority", "uri_part_target", "uri_part_weight"],
+}
+
+
+def configure_module():
+    """Configure ipadnsrecord ansible module variables."""
+    record_spec = dict(
+        zone_name=dict(type='str', required=False, aliases=['dnszone']),
+        record_type=dict(type='str', default="A",
+                         choices=["A", "AAAA", "A6", "AFSDB", "CERT", "CNAME",
+                                  "DLV", "DNAME", "DS", "KX", "LOC", "MX",
+                                  "NAPTR", "NS", "PTR", "SRV", "SSHFP", "TLSA",
+                                  "TXT", "URI"]),
+        record_value=dict(type='list', required=False),
+        record_ttl=dict(type='int', required=False),
+        del_all=dict(type='bool', required=False),
+        a_rec=dict(type='list', required=False, aliases=['a_record']),
+        aaaa_rec=dict(type='list', required=False, aliases=['aaaa_record']),
+        a6_rec=dict(type='list', required=False, aliases=['a6_record']),
+        afsdb_rec=dict(type='list', required=False, aliases=['afsdb_record']),
+        cert_rec=dict(type='list', required=False, aliases=['cert_record']),
+        cname_rec=dict(type='list', required=False, aliases=['cname_record']),
+        dlv_rec=dict(type='list', required=False, aliases=['dlv_record']),
+        dname_rec=dict(type='list', required=False, aliases=['dname_record']),
+        ds_rec=dict(type='list', required=False, aliases=['ds_record']),
+        kx_rec=dict(type='list', required=False, aliases=['kx_record']),
+        loc_rec=dict(type='list', required=False, aliases=['loc_record']),
+        mx_rec=dict(type='list', required=False, aliases=['mx_record']),
+        naptr_rec=dict(type='list', required=False, aliases=['naptr_record']),
+        ns_rec=dict(type='list', required=False, aliases=['ns_record']),
+        ptr_rec=dict(type='list', required=False, aliases=['ptr_record']),
+        srv_rec=dict(type='list', required=False, aliases=['srv_record']),
+        sshfp_rec=dict(type='list', required=False, aliases=['sshfp_record']),
+        tlsa_rec=dict(type='list', required=False, aliases=['tlsa_record']),
+        txt_rec=dict(type='list', required=False, aliases=['txt_record']),
+        uri_rec=dict(type='list', required=False, aliases=['uri_record']),
+        ip_address=dict(type='str', required=False),
+        create_reverse=dict(type='bool', required=False, aliases=['reverse']),
+        a_ip_address=dict(type='str', required=False),
+        a_create_reverse=dict(type='bool', required=False),
+        aaaa_ip_address=dict(type='str', required=False),
+        aaaa_create_reverse=dict(type='bool', required=False),
+        a6_data=dict(type='str', required=False),
+        afsdb_subtype=dict(type='int', required=False),
+        afsdb_hostname=dict(type='str', required=False),
+        cert_type=dict(type='int', required=False),
+        cert_key_tag=dict(type='int', required=False),
+        cert_algorithm=dict(type='int', required=False),
+        cert_certificate_or_crl=dict(type='str', required=False),
+        cname_hostname=dict(type='str', required=False),
+        dlv_key_tag=dict(type='int', required=False),
+        dlv_algorithm=dict(type='int', required=False),
+        dlv_digest_type=dict(type='int', required=False),
+        dlv_digest=dict(type='str', required=False),
+        dname_target=dict(type='str', required=False),
+        ds_key_tag=dict(type='int', required=False),
+        ds_algorithm=dict(type='int', required=False),
+        ds_digest_type=dict(type='int', required=False),
+        ds_digest=dict(type='str', required=False),
+        kx_preference=dict(type='int', required=False),
+        kx_exchanger=dict(type='str', required=False),
+        loc_lat_deg=dict(type='int', required=False),
+        loc_lat_min=dict(type='int', required=False),
+        loc_lat_sec=dict(type='float', required=False),
+        loc_lat_dir=dict(type='str', required=False),
+        loc_lon_deg=dict(type='int', required=False),
+        loc_lon_min=dict(type='int', required=False),
+        loc_lon_sec=dict(type='float', required=False),
+        loc_lon_dir=dict(type='str', required=False),
+        loc_altitude=dict(type='float', required=False),
+        loc_size=dict(type='float', required=False),
+        loc_h_precision=dict(type='float', required=False),
+        loc_v_precision=dict(type='float', required=False),
+        mx_preference=dict(type='int', required=False),
+        mx_exchanger=dict(type='str', required=False),
+        naptr_order=dict(type='int', required=False),
+        naptr_preference=dict(type='int', required=False),
+        naptr_flags=dict(type='str', required=False),
+        naptr_service=dict(type='str', required=False),
+        naptr_regexp=dict(type='str', required=False),
+        naptr_replacement=dict(type='str', required=False),
+        ns_hostname=dict(type='str', required=False),
+        ptr_hostname=dict(type='str', required=False),
+        srv_priority=dict(type='int', required=False),
+        srv_weight=dict(type='int', required=False),
+        srv_port=dict(type='int', required=False),
+        srv_target=dict(type='str', required=False),
+        sshfp_algorithm=dict(type='int', required=False),
+        sshfp_fingerprint=dict(type='str', required=False),
+        sshfp_fp_type=dict(type='int', required=False),
+        tlsa_cert_usage=dict(type='int', required=False),
+        tlsa_cert_association_data=dict(type='str', required=False),
+        tlsa_matching_type=dict(type='int', required=False),
+        tlsa_selector=dict(type='int', required=False),
+        txt_data=dict(type='str', required=False),
+        uri_priority=dict(type='int', required=False),
+        uri_target=dict(type='str', required=False),
+        uri_weight=dict(type='int', required=False),
+    )
+
+    ansible_module = AnsibleModule(
+        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),
+
+            records=dict(type="list", default=None,
+                         options=dict(
+                            # Here name is a simple string
+                            name=dict(type='str', required=True,
+                                      aliases=['record_name']),
+                            **record_spec),
+                         ),
+
+            # general
+            state=dict(type="str", default="present",
+                       choices=["present", "absent", "disabled"]),
+
+            # Add record specific parameters for simple use case
+            **record_spec
+        ),
+        mutually_exclusive=[["name", "records"], ['record_value', 'del_all']],
+        required_one_of=[["name", "records"]],
+        supports_check_mode=True,
+    )
+
+    ansible_module._ansible_debug = True
+
+    return ansible_module
+
+
+def find_dnsrecord(module, dnszone, name, **records):
+    """Find a DNS record based on its name (idnsname)."""
+    _args = {record: value for record, value in records.items()}
+    _args["all"] = True
+    if name != '@':
+        _args['idnsname'] = to_text(name)
+
+    try:
+        _result = api_command(
+            module, "dnsrecord_find", to_text(dnszone), _args)
+    except ipalib.errors.NotFound:
+        return None
+
+    if len(_result["result"]) > 1 and name != '@':
+        module.fail_json(
+            msg="There is more than one dnsrecord for '%s',"
+                " zone '%s'" % (name, dnszone))
+    else:
+        if len(_result["result"]) == 1:
+            return _result["result"][0]
+        else:
+            for _res in _result["result"]:
+                if 'idnsname' in _res:
+                    for x in _res['idnsname']:
+                        if '@' == to_text(x):
+                            return _res
+    return None
+
+
+def check_parameters(module, state, zone_name, record):
+    """Check if parameters are correct."""
+    if zone_name is None:
+        module.fail_json(msg="Msssing required argument: zone_name")
+
+    record_type = record.get('record_type', None)
+    record_value = record.get('record_value', None)
+    if record_type is not None:
+        if record_type not in _SUPPORTED_RECORD_TYPES:
+            module.fail_json(
+                msg="Record Type '%s' is not supported." % record_type)
+
+    has_record = any(record.get(rec, None) for rec in _RECORD_FIELDS)
+
+    has_part_record = any(record.get(rec, None) for rec in _PART_MAP)
+
+    special_list = ['ip_address']
+    has_special = any(record.get(rec, None) for rec in special_list)
+
+    invalid = []
+
+    if state == 'present':
+        if has_record or has_part_record or has_special:
+            if record_value:
+                module.fail_json(
+                    msg="Cannot use record data with `record_value`.")
+        elif not record_value:
+            module.fail_json(msg="No record data provided.")
+
+        invalid = ['del_all']
+
+    if state == 'absent':
+        del_all = record.get('del_all', None)
+        if record_value:
+            if has_record or has_part_record or del_all:
+                module.fail_json(
+                    msg="Cannot use record data with `record_value`.")
+        elif not (has_record or has_part_record or del_all):
+            module.fail_json(
+                msg="Either a record description or `del_all` is required.")
+        invalid = list(_PART_MAP.keys())
+        invalid.extend(['create_reverse', 'dns_ttl'])
+
+    for x in invalid:
+        if x in record:
+            module.fail_json(
+                msg="Variable `%s` cannot be used in state `%s`" %
+                    (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 = [
+        'del_all', 'zone_name', 'record_type', 'record_value', 'record_ttl',
+        "ip_address", "create_reverse"
+    ]
+
+    entry = {'name': name}
+
+    for key_set in [_RECORD_FIELDS, _PART_MAP, attrs]:
+        entry.update({
+            key: module_params_get(module, key)
+            for key in key_set
+            if module_params_get(module, key) is not None
+        })
+
+    return entry
+
+
+def create_reverse_ip_record(module, zone_name, name, ips):
+    """Create a reverse record for an IP (PTR record)."""
+    _cmds = []
+    for address in ips:
+        reverse_ip = dns.reversename.from_address(address)
+        reverse_zone = dns.resolver.zone_for_name(reverse_ip)
+        reverse_host = to_text(reverse_ip).replace(".%s" % reverse_zone, '')
+
+        rev_find = find_dnsrecord(module, reverse_zone, reverse_host)
+        if rev_find is None:
+            rev_args = {
+                'idnsname': to_text(reverse_host),
+                "ptrrecord": "%s.%s" % (name, zone_name)
+            }
+            _cmds.append([reverse_zone, 'dnsrecord_add', rev_args])
+
+    return _cmds
+
+
+def ensure_data_is_list(data):
+    """Ensure data is represented as a list."""
+    return data if isinstance(data, list) else [data]
+
+
+def gen_args(entry):
+    """Generate IPA API arguments for a given `entry`."""
+    args = {'idnsname': to_text(entry['name'])}
+
+    if 'del_all' in entry:
+        args['del_all'] = entry['del_all']
+
+    record_value = entry.get('record_value', None)
+
+    if record_value is not None:
+        record_type = entry['record_type']
+        rec = "{}record".format(record_type.lower())
+        args[rec] = ensure_data_is_list(record_value)
+
+    else:
+        for field in _RECORD_FIELDS:
+            record_value = entry.get(field, None)
+            if record_value is not None:
+                record_type = field.split('_')[0]
+                rec = "{}record".format(record_type.lower())
+                args[rec] = ensure_data_is_list(record_value)
+
+        records = {
+            key: rec for key, rec in _PART_MAP.items() if key in entry
+        }
+        for key, rec in records.items():
+            args[rec] = entry[key]
+
+    if 'ip_address' in entry:
+        ip_address = entry['ip_address']
+        if is_ipv4_addr(ip_address):
+            args['a_part_ip_address'] = ip_address
+        if is_ipv6_addr(ip_address):
+            args['aaaa_part_ip_address'] = ip_address
+
+    if entry.get('create_reverse', False):
+        if 'a_part_ip_address' in args or 'arecord' in args:
+            args['a_extra_create_reverse'] = True
+        if 'aaaa_part_ip_address' in args or 'aaaarecord' in args:
+            args['aaaa_extra_create_reverse'] = True
+
+    if 'record_ttl' in entry:
+        args['dnsttl'] = entry['record_ttl']
+
+    return args
+
+
+def define_commands_for_present_state(module, zone_name, entry, res_find):
+    """Define commnads for `state: present`."""
+    _commands = []
+
+    name = to_text(entry['name'])
+    args = gen_args(entry)
+
+    if res_find is None:
+        _commands.append([zone_name, 'dnsrecord_add', args])
+    else:
+        # Create reverse records for existing records
+        for ipv in ['a', 'aaaa']:
+            record = ('%srecord' % ipv)
+            if record in args and ('%s_extra_create_reverse' % ipv) in args:
+                cmds = create_reverse_ip_record(
+                    module, zone_name, name, args[record])
+                _commands.extend(cmds)
+                del args['%s_extra_create_reverse' % ipv]
+                if '%s_ip_address' not in args:
+                    del args[record]
+        for record, fields in _RECORD_PARTS.items():
+            part_fields = [f for f in fields if f in args]
+            if part_fields:
+                if record in args:
+                    # user wants to update record.
+                    if len(args[record]) > 1:
+                        module.fail_json(msg="Cannot modify multiple records "
+                                             "of the same type at once.")
+
+                    existing = find_dnsrecord(module, zone_name, name,
+                                              **{record: args[record][0]})
+                    if existing is None:
+                        module.fail_json(msg="``%s` not found." % record)
+                    else:
+                        # update DNS record
+                        _args = {k: args[k] for k in part_fields if k in args}
+                        _args["idnsname"] = to_text(args["idnsname"])
+                        _args[record] = res_find[record]
+                        if 'dns_ttl' in args:
+                            _args['dns_ttl'] = args['dns_ttl']
+                        _commands.append([zone_name, 'dnsrecord_mod', _args])
+                    # remove record from args, as it will not be used again.
+                    del args[record]
+                else:
+                    for f in part_fields:
+                        _args = {k: args[k] for k in part_fields}
+                        _args['idnsname'] = name
+                        _commands.append([zone_name, 'dnsrecord_add', _args])
+                # clean used fields from args
+                for f in part_fields:
+                    if f in args:
+                        del args[f]
+            else:
+                if record in args:
+                    add_list = []
+                    for value in args[record]:
+                        existing = find_dnsrecord(module, zone_name, name,
+                                                  **{record: value})
+                        if existing is None:
+                            add_list.append(value)
+                    if add_list:
+                        args[record] = add_list
+                        _commands.append([zone_name, 'dnsrecord_add', args])
+
+    return _commands
+
+
+def define_commands_for_absent_state(module, zone_name, entry, res_find):
+    """Define commands for `state: absent`."""
+    _commands = []
+    if res_find is None:
+        return []
+
+    name = entry['name']
+    args = gen_args(entry)
+
+    del_all = args.get('del_all', False)
+
+    records_to_delete = {k: v for k, v in args.items() if k.endswith('record')}
+
+    if del_all and records_to_delete:
+        module.fail_json(msg="Cannot use del_all and record together.")
+
+    if not del_all:
+        delete_records = False
+        for record, values in records_to_delete.items():
+            del_list = []
+            for value in values:
+                existing = find_dnsrecord(
+                    module, zone_name, name, **{record: value})
+                if existing:
+                    del_list.append(value)
+            if del_list:
+                args[record] = del_list
+                delete_records = True
+        if delete_records:
+            _commands.append([zone_name, 'dnsrecord_del', args])
+    else:
+        _commands.append([zone_name, 'dnsrecord_del', args])
+
+    return _commands
+
+
+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")
+
+    # Check parameters
+
+    if (names is None or len(names) < 1) and \
+       (records is None or len(records) < 1):
+        ansible_module.fail_json(msg="One of name and records is required")
+
+    if state == "present":
+        if names is not None and len(names) != 1:
+            ansible_module.fail_json(
+                msg="Only one record can be added at a time.")
+
+    if records is not None:
+        names = records
+
+    # Init
+
+    changed = False
+    exit_args = {}
+    ccache_dir = None
+    ccache_name = None
+
+    try:
+        ccache_dir, ccache_name = connect_to_api(ansible_module)
+
+        commands = []
+
+        for record in names:
+            if isinstance(record, dict):
+                # ensure name is a string
+                zone_name = record.get("zone_name", global_zone_name)
+                name = record['name'] = str(record['name'])
+                entry = record
+            else:
+                zone_name = global_zone_name
+                name = record
+                entry = get_entry_from_module(ansible_module, name)
+
+            check_parameters(ansible_module, state, zone_name, entry)
+
+            res_find = find_dnsrecord(ansible_module, zone_name, name)
+
+            if state == 'present':
+                cmds = define_commands_for_present_state(
+                    ansible_module, zone_name, entry, res_find)
+            elif state == 'absent':
+                cmds = define_commands_for_absent_state(
+                    ansible_module, zone_name, entry, res_find)
+            else:
+                ansible_module.fail_json(msg="Unkown state '%s'" % state)
+
+            if cmds:
+                commands.extend(cmds)
+
+        # Execute commands
+        for name, command, args in commands:
+            try:
+                result = api_command(
+                    ansible_module, command, to_text(name), args)
+                if "completed" in result:
+                    if result["completed"] > 0:
+                        changed = True
+                else:
+                    changed = True
+
+            except ipalib.errors.EmptyModlist:
+                continue
+            except ipalib.errors.DuplicateEntry:
+                continue
+            except Exception as e:
+                error_message = str(e)
+
+                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)
+
+
+if __name__ == "__main__":
+    main()
diff --git a/tests/dnsrecord/env_cleanup.yml b/tests/dnsrecord/env_cleanup.yml
new file mode 100644
index 0000000000000000000000000000000000000000..5b9b7343ff80d9603e91c6c79799054fcb425132
--- /dev/null
+++ b/tests/dnsrecord/env_cleanup.yml
@@ -0,0 +1,135 @@
+---
+  # Cleanup tasks.
+   - name: Ensure that dns records are absent
+     ipadnsrecord:
+       ipaadmin_password: SomeADMINpassword
+       zone_name: "{{ testzone }}"
+       del_all: yes
+       name:
+       - host01
+       - host02
+       - host03
+       - host04
+       - _ftp._tcp
+       - _sip._udp
+       state: absent
+
+   - name: Ensure that dns reverse ipv6 records are absent
+     ipadnsrecord:
+       ipaadmin_password: SomeADMINpassword
+       zone_name: ip6.arpa.
+       del_all: yes
+       name:
+       - 1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.d.f
+       - 1.1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.d.f
+       - 1.2.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.d.f
+       - 4.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.d.f
+       - 4.1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.d.f
+       - 4.2.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.d.f
+       state: absent
+
+   - name: Ensure that dns reverse ipv6 records are absent (workaround)
+     ipadnsrecord:
+       ipaadmin_password: SomeADMINpassword
+       zone_name: "{{ zone_ipv6_reverse_workaround }}"
+       del_all: yes
+       name:
+       - 1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0
+       - 1.1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0
+       - 1.2.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0
+       - 4.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0
+       - 4.1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0
+       - 4.2.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0
+       state: absent
+
+   - name: Ensure that dns reverse records are absent
+     ipadnsrecord:
+       ipaadmin_password: SomeADMINpassword
+       zone_name: "{{ zone_prefix_reverse_24 }}"
+       name:
+       - "101"
+       - "102"
+       - "103"
+       - "104"
+       - "111"
+       - "112"
+       - "113"
+       - "114"
+       - "121"
+       - "122"
+       - "123"
+       - "124"
+       del_all: yes
+       state: absent
+
+   - name: Ensure that dns reverse records are absent (workaround 1)
+     ipadnsrecord:
+       ipaadmin_password: SomeADMINpassword
+       zone_name: "{{ zone_prefix_reverse_16 }}"
+       name:
+       - "101.122"
+       - "102.122"
+       - "103.122"
+       - "104.122"
+       - "111.122"
+       - "112.122"
+       - "113.122"
+       - "114.122"
+       - "121.122"
+       - "122.122"
+       - "123.122"
+       - "124.122"
+       del_all: yes
+       state: absent
+
+   - name: Ensure that dns reverse records are absent (workaround 2)
+     ipadnsrecord:
+       ipaadmin_password: SomeADMINpassword
+       zone_name: "{{ zone_prefix_reverse_8 }}"
+       name:
+       - "168.101.122"
+       - "168.102.122"
+       - "168.103.122"
+       - "168.104.122"
+       - "168.111.122"
+       - "168.112.122"
+       - "168.113.122"
+       - "168.114.122"
+       - "168.121.122"
+       - "168.122.122"
+       - "168.123.122"
+       - "168.124.122"
+       del_all: yes
+       state: absent
+
+   - name: Ensure that "{{ safezone }}" dns records are absent
+     ipadnsrecord:
+       ipaadmin_password: SomeADMINpassword
+       zone_name: "{{ safezone }}"
+       records:
+       - name: iron01
+         del_all: yes
+       state: absent
+
+   - name: Ensure that NS record for "{{ safezone }}" is absent
+     ipadnsrecord:
+       ipaadmin_password: SomeADMINpassword
+       name: iron01
+       zone_name: "{{ safezone }}"
+       ns_rec: iron01
+       state: absent
+
+   - name: Ensure DNS testing zones are absent.
+     ipadnszone:
+       ipaadmin_password: SomeADMINpassword
+       name: "{{ item }}"
+       state: absent
+     with_items:
+       - "{{ zone_prefix_reverse }}"
+       - "{{ zone_prefix_reverse_24 }}"
+       - "{{ zone_prefix_reverse_16 }}"
+       - "{{ zone_prefix_reverse_8 }}"
+       - "{{ testzone }}"
+       - ip6.arpa.
+       - d.f.ip6.arpa.
+       - "{{ safezone }}"
diff --git a/tests/dnsrecord/env_setup.yml b/tests/dnsrecord/env_setup.yml
new file mode 100644
index 0000000000000000000000000000000000000000..d9a8546b991b99ed51c635e88851558245e64b5a
--- /dev/null
+++ b/tests/dnsrecord/env_setup.yml
@@ -0,0 +1,31 @@
+---
+  - name: Setup variables and facts.
+    include_tasks: env_vars.yml
+
+  # Cleanup before setup.
+  - name: Cleanup test environment.
+    include_tasks: env_cleanup.yml
+
+  # Common setup tasks.
+  - name: Ensure DNS testing zones are present.
+    ipadnszone:
+      ipaadmin_password: SomeADMINpassword
+      name: "{{ item }}"
+      skip_nameserver_check: yes
+      skip_overlap_check: yes
+    with_items:
+    - "{{ zone_prefix_reverse }}"
+    - "{{ zone_prefix_reverse_24 }}"
+    - "{{ zone_prefix_reverse_16 }}"
+    - "{{ zone_prefix_reverse_8 }}"
+    - "{{ testzone }}"
+    - ip6.arpa.
+
+  - name: Ensure DNSSEC zone '"{{ safezone }}"' is present.
+    ipadnszone:
+      ipaadmin_password: SomeADMINpassword
+      name: "{{ safezone }}"
+      dnssec: yes
+      skip_nameserver_check: yes
+      skip_overlap_check: yes
+    ignore_errors: yes
diff --git a/tests/dnsrecord/env_vars.yml b/tests/dnsrecord/env_vars.yml
new file mode 100644
index 0000000000000000000000000000000000000000..bb540a0a9447153f5db7b4fd6126ba19f548f845
--- /dev/null
+++ b/tests/dnsrecord/env_vars.yml
@@ -0,0 +1,17 @@
+---
+# Set common vars and facts for test.
+- name: Set IPv4 address prefix.
+  set_fact:
+    ipv4_prefix: '192.168.122'
+    ipv4_reverse_sufix: '122.168.192'
+
+- name: Set zone prefixes.
+  set_fact:
+    testzone: 'testzone.test'
+    safezone: 'safezone.test'
+    zone_ipv6_reverse: "ip6.arpa."
+    zone_ipv6_reverse_workaround: "d.f.ip6.arpa."
+    zone_prefix_reverse: "in-addr.arpa"
+    zone_prefix_reverse_24: "{{ ipv4_prefix.split('.')[::-1] | join ('.') }}.in-addr.arpa"
+    zone_prefix_reverse_16: "{{ ipv4_prefix.split('.')[1::-1] | join ('.') }}.in-addr.arpa"
+    zone_prefix_reverse_8: "{{ ipv4_prefix.split('.')[2::-1] | join ('.') }}.in-addr.arpa"
diff --git a/tests/dnsrecord/test_compatibility_with_ansible_module.yml b/tests/dnsrecord/test_compatibility_with_ansible_module.yml
new file mode 100644
index 0000000000000000000000000000000000000000..e1631135bfff6dde2d9fb43fa9f624fc696e7545
--- /dev/null
+++ b/tests/dnsrecord/test_compatibility_with_ansible_module.yml
@@ -0,0 +1,234 @@
+---
+- name: Test compatibility with Ansible ipa_dnsrecord module.
+  hosts: ipaserver
+  become: true
+  gather_facts: false
+
+  tasks:
+
+  # setup
+  - name: Ensure DNS zones to be used are absent.
+    ipadnszone:
+      ipaadmin_password: SomeADMINpassword
+      name: "{{ item }}"
+      state: absent
+    with_items:
+      - testzone.local
+      - 2.168.192.in-addr.arpa
+
+  - name: Ensure DNS zones to be used are present.
+    ipadnszone:
+      ipaadmin_password: SomeADMINpassword
+      name: "{{ item }}"
+    with_items:
+      - testzone.local
+      - 2.168.192.in-addr.arpa
+
+  - name: Ensure that dns record 'host01' is absent
+    ipadnsrecord:
+      ipaadmin_password: SomeADMINpassword
+      name: host01
+      zone_name: testzone.local
+      record_type: 'AAAA'
+      record_value: '::1'
+      state: absent
+
+  - name: Ensure that dns record 'vm-001' is absent
+    ipadnsrecord:
+      ipaadmin_password: SomeADMINpassword
+      name: vm-001
+      zone_name: testzone.local
+      record_type: 'AAAA'
+      record_value: '::1'
+      state: absent
+
+  - name: Ensure a PTR record is absent
+    ipadnsrecord:
+      ipaadmin_password: SomeADMINpassword
+      name: 5
+      record_type: 'PTR'
+      record_value: 'internal.ipa.testzone.local'
+      zone_name: 2.168.192.in-addr.arpa
+      state: absent
+
+  - name: Ensure a TXT record is absent
+    ipadnsrecord:
+      ipaadmin_password: SomeADMINpassword
+      name: _kerberos
+      record_type: 'TXT'
+      record_value: 'TESTZONE.LOCAL'
+      zone_name: testzone.local
+      state: absent
+
+  - name: Ensure a SRV record is absent
+    ipadnsrecord:
+      ipaadmin_password: SomeADMINpassword
+      name: _kerberos._udp.testzone.local
+      record_type: 'SRV'
+      record_value: '10 50 88 ipa.testzone.local'
+      zone_name: testzone.local
+      state: absent
+
+  - name: Ensure an MX record is absent
+    ipadnsrecord:
+      ipaadmin_password: SomeADMINpassword
+      name: '@'
+      record_type: 'MX'
+      record_value: '1 mailserver.testzone.local'
+      zone_name: testzone.local
+      state: absent
+
+  # tests
+  - name: Ensure dns record is present
+    ipadnsrecord:
+      ipaadmin_password: SomeADMINpassword
+      name: vm-001
+      record_type: 'AAAA'
+      record_value: '::1'
+      zone_name: testzone.local
+      state: present
+    register: result
+    failed_when: not result.changed
+
+  - name: Ensure that dns record exists with a TTL
+    ipadnsrecord:
+      ipaadmin_password: SomeADMINpassword
+      name: host01
+      record_type: 'AAAA'
+      record_value: '::1'
+      record_ttl: 300
+      zone_name: testzone.local
+      state: present
+    register: result
+    failed_when: not result.changed
+
+  - name: Ensure a PTR record is present
+    ipadnsrecord:
+      ipaadmin_password: SomeADMINpassword
+      name: 5
+      record_type: 'PTR'
+      record_value: 'internal.ipa.testzone.local'
+      zone_name: 2.168.192.in-addr.arpa
+      state: present
+    register: result
+    failed_when: not result.changed
+
+  - name: Ensure a TXT record is present
+    ipadnsrecord:
+      ipaadmin_password: SomeADMINpassword
+      name: _kerberos
+      record_type: 'TXT'
+      record_value: 'TESTZONE.LOCAL'
+      zone_name: testzone.local
+      state: present
+    register: result
+    failed_when: not result.changed
+
+  - name: Ensure a SRV record is present
+    ipadnsrecord:
+      ipaadmin_password: SomeADMINpassword
+      name: _kerberos._udp.testzone.local
+      record_type: 'SRV'
+      record_value: '10 50 88 ipa.testzone.local'
+      zone_name: testzone.local
+      state: present
+    register: result
+    failed_when: not result.changed
+
+  - name: Ensure an MX record is present
+    ipadnsrecord:
+      ipaadmin_password: SomeADMINpassword
+      name: '@'
+      record_type: 'MX'
+      record_value: '1 mailserver.testzone.local'
+      zone_name: testzone.local
+      state: present
+    register: result
+    failed_when: not result.changed
+
+  - name: Ensure that dns record is removed
+    ipadnsrecord:
+      ipaadmin_password: SomeADMINpassword
+      name: host01
+      zone_name: testzone.local
+      record_type: 'AAAA'
+      record_value: '::1'
+      state: absent
+    register: result
+    failed_when: not result.changed
+
+   # cleanup
+  - name: Ensure that dns record 'host01' is absent
+    ipadnsrecord:
+      ipaadmin_password: SomeADMINpassword
+      name: host01
+      zone_name: testzone.local
+      record_type: 'AAAA'
+      record_value: '::1'
+      state: absent
+    register: result
+    failed_when: result.changed
+
+  - name: Ensure that dns record 'vm-001' is absent
+    ipadnsrecord:
+      ipaadmin_password: SomeADMINpassword
+      name: vm-001
+      zone_name: testzone.local
+      record_type: 'AAAA'
+      record_value: '::1'
+      state: absent
+    register: result
+    failed_when: not result.changed
+
+  - name: Ensure a PTR record is absent
+    ipadnsrecord:
+      ipaadmin_password: SomeADMINpassword
+      name: 5
+      record_type: 'PTR'
+      record_value: 'internal.ipa.testzone.local'
+      zone_name: 2.168.192.in-addr.arpa
+      state: absent
+    register: result
+    failed_when: not result.changed
+
+  - name: Ensure a TXT record is absent
+    ipadnsrecord:
+      ipaadmin_password: SomeADMINpassword
+      name: _kerberos
+      record_type: 'TXT'
+      record_value: 'TESTZONE.LOCAL'
+      zone_name: testzone.local
+      state: absent
+    register: result
+    failed_when: not result.changed
+
+  - name: Ensure a SRV record is absent
+    ipadnsrecord:
+      ipaadmin_password: SomeADMINpassword
+      name: _kerberos._udp.testzone.local
+      record_type: 'SRV'
+      record_value: '10 50 88 ipa.testzone.local'
+      zone_name: testzone.local
+      state: absent
+    register: result
+    failed_when: not result.changed
+
+  - name: Ensure an MX record is absent
+    ipadnsrecord:
+      ipaadmin_password: SomeADMINpassword
+      name: '@'
+      record_type: 'MX'
+      record_value: '1 mailserver.testzone.local'
+      zone_name: testzone.local
+      state: absent
+    register: result
+    failed_when: not result.changed
+
+  - name: Ensure DNS zones to be used are absent.
+    ipadnszone:
+      ipaadmin_password: SomeADMINpassword
+      name: "{{ item }}"
+      state: absent
+    with_items:
+      - testzone.local
+      - 2.168.192.in-addr.arpa
diff --git a/tests/dnsrecord/test_dnsrecord.yml b/tests/dnsrecord/test_dnsrecord.yml
new file mode 100644
index 0000000000000000000000000000000000000000..6847e80ea9ca27d5288a4bc2cd29abefc61aab36
--- /dev/null
+++ b/tests/dnsrecord/test_dnsrecord.yml
@@ -0,0 +1,1348 @@
+---
+- name: Test dnsrecord
+  hosts: ipaserver
+  become: yes
+  gather_facts: yes
+
+  tasks:
+
+  - name: Setup testing environment.
+    include_tasks: env_setup.yml
+
+  # tests
+  - name: Ensure that dns record 'host01' is present
+    ipadnsrecord:
+      ipaadmin_password: SomeADMINpassword
+      name: host01
+      zone_name: "{{ testzone }}"
+      record_type: AAAA
+      record_value: ::1
+    register: result
+    failed_when: not result.changed
+
+  - name: Ensure that dns record 'host01' is present, again
+    ipadnsrecord:
+      ipaadmin_password: SomeADMINpassword
+      name: host01
+      zone_name: "{{ testzone }}"
+      record_type: AAAA
+      record_value: ::1
+    register: result
+    failed_when: result.changed
+
+  - name: Ensure that dns record 'host02' is present
+    ipadnsrecord:
+      ipaadmin_password: SomeADMINpassword
+      name: host02
+      zone_name: "{{ testzone }}"
+      record_type: A
+      record_value: "{{ ipv4_prefix }}.102"
+    register: result
+    failed_when: not result.changed
+
+  - name: Ensure that dns record 'host02' is present, again
+    ipadnsrecord:
+      ipaadmin_password: SomeADMINpassword
+      name: host02
+      zone_name: "{{ testzone }}"
+      record_type: A
+      record_value: "{{ ipv4_prefix }}.102"
+    register: result
+    failed_when: result.changed
+
+  - name: Modify record 'host02' with multiple A and AAAA record.
+    ipadnsrecord:
+      ipaadmin_password: SomeADMINpassword
+      records:
+        - name: host02
+          zone_name: "{{ testzone }}"
+          record_type: A
+          record_value:
+            - "{{ ipv4_prefix }}.112"
+            - "{{ ipv4_prefix }}.122"
+        - name: host02
+          zone_name: "{{ testzone }}"
+          record_type: AAAA
+          record_value: ::1
+    register: result
+    failed_when: not result.changed
+
+  - name: Modify record 'host02' with multiple A and AAAA record, again.
+    ipadnsrecord:
+      ipaadmin_password: SomeADMINpassword
+      records:
+        - name: host02
+          zone_name: "{{ testzone }}"
+          record_type: A
+          record_value:
+            - "{{ ipv4_prefix }}.112"
+            - "{{ ipv4_prefix }}.122"
+        - name: host02
+          zone_name: "{{ testzone }}"
+          record_type: AAAA
+          record_value: ::1
+    register: result
+    failed_when: result.changed
+
+  - name: Ensure 'host02' A6 record is present.
+    ipadnsrecord:
+      ipaadmin_password: SomeADMINpassword
+      zone_name: "{{ testzone }}"
+      name: host02
+      a6_data: ::1
+    register: result
+    failed_when: not result.changed
+
+  - name: Ensure 'host02' A6 record is present, again.
+    ipadnsrecord:
+      ipaadmin_password: SomeADMINpassword
+      zone_name: "{{ testzone }}"
+      name: host02
+      a6_rec: ::1
+    register: result
+    failed_when: result.changed
+
+  - name: Ensure 'host02' A6 record is absent.
+    ipadnsrecord:
+      ipaadmin_password: SomeADMINpassword
+      zone_name: "{{ testzone }}"
+      name: host02
+      a6_rec: ::1
+      state: absent
+    register: result
+    failed_when: not result.changed
+
+  - name: Ensure 'host02' A6 record is absent, again.
+    ipadnsrecord:
+      ipaadmin_password: SomeADMINpassword
+      zone_name: "{{ testzone }}"
+      name: host02
+      a6_rec: ::1
+      state: absent
+    register: result
+    failed_when: result.changed
+
+  - name: Ensure that dns record 'host03' is present, with reverse record.
+    ipadnsrecord:
+      ipaadmin_password: SomeADMINpassword
+      name: host03
+      zone_name: "{{ testzone }}"
+      a_ip_address: "{{ ipv4_prefix }}.103"
+      a_create_reverse: yes
+    register: result
+    failed_when: not result.changed
+
+  - name: Ensure that dns record 'host03' is present, with reverse record, again
+    ipadnsrecord:
+      ipaadmin_password: SomeADMINpassword
+      name: host03
+      zone_name: "{{ testzone }}"
+      record_type: A
+      record_value: "{{ ipv4_prefix }}.103"
+      create_reverse: yes
+    register: result
+    failed_when: result.changed
+
+  - name: Delete all entries associated with host03
+    ipadnsrecord:
+      ipaadmin_password: SomeADMINpassword
+      zone_name: "{{ testzone }}"
+      name: host03
+      del_all: yes
+      state: absent
+    register: result
+    failed_when: not result.changed
+
+  - name: Delete all entries associated with host03, again
+    ipadnsrecord:
+      ipaadmin_password: SomeADMINpassword
+      zone_name: "{{ testzone }}"
+      name: host03
+      del_all: yes
+      state: absent
+    register: result
+    failed_when: result.changed
+
+  - name: Ensure that 'host04' has CNAME
+    ipadnsrecord:
+      ipaadmin_password: SomeADMINpassword
+      zone_name: "{{ testzone }}"
+      name: host04
+      record_type: CNAME
+      record_value: "host04.{{ testzone }}"
+    register: result
+    failed_when: not result.changed
+
+  - name: Ensure that 'host04' has CNAME, again
+    ipadnsrecord:
+      ipaadmin_password: SomeADMINpassword
+      zone_name: "{{ testzone }}"
+      name: host04
+      cname_hostname: "host04.{{ testzone }}"
+    register: result
+    failed_when: result.changed
+
+  - name: Ensure that 'host04' CNAME is absent
+    ipadnsrecord:
+      ipaadmin_password: SomeADMINpassword
+      zone_name: "{{ testzone }}"
+      name: host04
+      cname_rec: "host04.{{ testzone }}"
+      state: absent
+    register: result
+    failed_when: not result.changed
+
+  - name: Ensure that 'host04' CNAME is absent, again
+    ipadnsrecord:
+      ipaadmin_password: SomeADMINpassword
+      zone_name: "{{ testzone }}"
+      name: host04
+      record_type: CNAME
+      record_value: "host04.{{ testzone }}"
+      state: absent
+    register: result
+    failed_when: result.changed
+
+  - name: Ensure that 'host04' and 'host03' have CNAME, with cname_hostname
+    ipadnsrecord:
+      ipaadmin_password: SomeADMINpassword
+      zone_name: "{{ testzone }}"
+      records:
+        - name: host04
+          cname_hostname: "host04.{{ testzone }}"
+        - name: host03
+          cname_hostname: "host03.{{ testzone }}"
+    register: result
+    failed_when: not result.changed
+
+  - name: Ensure that 'host04' has CNAME, with cname_hostname, again
+    ipadnsrecord:
+      ipaadmin_password: SomeADMINpassword
+      zone_name: "{{ testzone }}"
+      name: host04
+      cname_hostname: "host04.{{ testzone }}"
+    register: result
+    failed_when: result.changed
+
+  - name: Ensure that 'host04' CNAME is absent.
+    ipadnsrecord:
+      ipaadmin_password: SomeADMINpassword
+      zone_name: "{{ testzone }}"
+      name: host04
+      cname_rec: "host04.{{ testzone }}"
+      state: absent
+    register: result
+    failed_when: not result.changed
+
+  - name: Ensure that 'host04' has A record.
+    ipadnsrecord:
+      ipaadmin_password: SomeADMINpassword
+      zone_name: "{{ testzone }}"
+      name: host04
+      ip_address: "{{ ipv4_prefix }}.104"
+    register: result
+    failed_when: not result.changed
+
+  - name: Ensure that 'host04' has A record, again.
+    ipadnsrecord:
+      ipaadmin_password: SomeADMINpassword
+      zone_name: "{{ testzone }}"
+      name: host04
+      ip_address: "{{ ipv4_prefix }}.104"
+    register: result
+    failed_when: result.changed
+
+  - name: Ensure that 'host04' has the same A record with reverse.
+    ipadnsrecord:
+      ipaadmin_password: SomeADMINpassword
+      zone_name: "{{ testzone }}"
+      name: host04
+      a_rec: "{{ ipv4_prefix }}.104"
+      reverse: yes
+    register: result
+    failed_when: not result.changed
+
+  - name: Ensure that 'host04' has the same A record with reverse, again.
+    ipadnsrecord:
+      ipaadmin_password: SomeADMINpassword
+      zone_name: "{{ testzone }}"
+      name: host04
+      a_rec: "{{ ipv4_prefix }}.104"
+      reverse: yes
+    register: result
+    failed_when: result.changed
+
+  - name: Ensure that 'host04' has an A record with reverse, for NS record.
+    ipadnsrecord:
+      ipaadmin_password: SomeADMINpassword
+      zone_name: "{{ testzone }}"
+      name: host04
+      ip_address: "{{ ipv4_prefix }}.114"
+      reverse: yes
+
+  - name: Ensure that 'host04' has an A record with reverse, again.
+    ipadnsrecord:
+      ipaadmin_password: SomeADMINpassword
+      zone_name: "{{ testzone }}"
+      name: host04
+      ip_address: "{{ ipv4_prefix }}.114"
+      reverse: yes
+    register: result
+    failed_when: result.changed
+
+  - name: Ensure that 'host04' has AAAA record.
+    ipadnsrecord:
+      ipaadmin_password: SomeADMINpassword
+      zone_name: "{{ testzone }}"
+      name: host04
+      aaaa_ip_address: fd00::0004
+      aaaa_create_reverse: yes
+    register: result
+    failed_when: not result.changed
+
+  - name: Ensure that 'host04' has AAAA record, again.
+    ipadnsrecord:
+      ipaadmin_password: SomeADMINpassword
+      zone_name: "{{ testzone }}"
+      name: host04
+      ip_address: fd00::0004
+      reverse: yes
+    register: result
+    failed_when: result.changed
+
+  - name: Ensure that 'host04' has AAAA record, without reverse.
+    ipadnsrecord:
+      ipaadmin_password: SomeADMINpassword
+      zone_name: "{{ testzone }}"
+      name: host04
+      ip_address: fd00::0014
+    register: result
+    failed_when: not result.changed
+
+  - name: Ensure that 'host04' previous AAAA record, now has a reverse record.
+    ipadnsrecord:
+      ipaadmin_password: SomeADMINpassword
+      zone_name: "{{ testzone }}"
+      name: host04
+      aaaa_rec: fd00::0014
+      reverse: yes
+    register: result
+    failed_when: not result.changed
+
+  - name: Ensure that 'host04' previous AAAA record, now has a reverse record, again.
+    ipadnsrecord:
+      ipaadmin_password: SomeADMINpassword
+      zone_name: "{{ testzone }}"
+      name: host04
+      aaaa_rec: fd00::0014
+      reverse: yes
+    register: result
+    failed_when: result.changed
+
+  - name: Ensure that 'host04' has PTR record.
+    ipadnsrecord:
+      ipaadmin_password: SomeADMINpassword
+      zone_name: "{{ zone_prefix_reverse_24 }}"
+      name: "124"
+      ptr_hostname: "host04.{{ testzone }}"
+    register: result
+    failed_when: not result.changed
+
+  - name: Ensure that 'host04' has PTR record, again.
+    ipadnsrecord:
+      ipaadmin_password: SomeADMINpassword
+      zone_name: "{{ zone_prefix_reverse_24 }}"
+      name: "124"
+      ptr_hostname: "host04.{{ testzone }}"
+    register: result
+    failed_when: result.changed
+
+  - name: Ensure that 'host04' has PTR record is absent.
+    ipadnsrecord:
+      ipaadmin_password: SomeADMINpassword
+      zone_name: "{{ zone_prefix_reverse_24 }}"
+      name: "124"
+      ptr_rec: "host04.{{ testzone }}"
+      state: absent
+    register: result
+    failed_when: not result.changed
+
+  - name: Ensure that 'host04' has PTR record is absent, again.
+    ipadnsrecord:
+      ipaadmin_password: SomeADMINpassword
+      zone_name: "{{ zone_prefix_reverse_24 }}"
+      name: "124"
+      ptr_rec: "host04.{{ testzone }}"
+      state: absent
+    register: result
+    failed_when: result.changed
+
+  - name: Ensure that 'host04' has DNAME record.
+    ipadnsrecord:
+      ipaadmin_password: SomeADMINpassword
+      zone_name: "{{ testzone }}"
+      name: host04
+      dname_target: "ipa.{{ testzone }}"
+    register: result
+    failed_when: not result.changed
+
+  - name: Ensure that 'host04' has DNAME record, again.
+    ipadnsrecord:
+      ipaadmin_password: SomeADMINpassword
+      zone_name: "{{ testzone }}"
+      name: host04
+      dname_target: "ipa.{{ testzone }}"
+    register: result
+    failed_when: result.changed
+
+  - name: Ensure that 'host04' DNAME record is absent.
+    ipadnsrecord:
+      ipaadmin_password: SomeADMINpassword
+      zone_name: "{{ testzone }}"
+      name: host04
+      dname_rec: "ipa.{{ testzone }}"
+      state: absent
+    register: result
+    failed_when: not result.changed
+
+  - name: Ensure that 'host04' DNAME record is absent, again.
+    ipadnsrecord:
+      ipaadmin_password: SomeADMINpassword
+      zone_name: "{{ testzone }}"
+      name: host04
+      dname_rec: "ipa.{{ testzone }}"
+      state: absent
+    register: result
+    failed_when: result.changed
+
+  - name: Ensure that 'host04' has a A record with reverse, for NS record.
+    ipadnsrecord:
+      ipaadmin_password: SomeADMINpassword
+      zone_name: "{{ testzone }}"
+      name: host04
+      ip_address: "{{ ipv4_prefix }}.114"
+      reverse: yes
+
+  - name: Ensure that 'host04' has NS record.
+    ipadnsrecord:
+      ipaadmin_password: SomeADMINpassword
+      zone_name: "{{ testzone }}"
+      name: host04
+      ns_hostname: host04
+    register: result
+    failed_when: not result.changed
+
+  - name: Ensure that 'host04' has NS record, again.
+    ipadnsrecord:
+      ipaadmin_password: SomeADMINpassword
+      zone_name: "{{ testzone }}"
+      name: host04
+      ns_hostname: host04
+    register: result
+    failed_when: result.changed
+
+  - name: Ensure that 'host04' NS record is absent.
+    ipadnsrecord:
+      ipaadmin_password: SomeADMINpassword
+      zone_name: "{{ testzone }}"
+      name: host04
+      ns_rec: host04
+      state: absent
+    register: result
+    failed_when: not result.changed
+
+  - name: Ensure that 'host04' NS record is absent, again.
+    ipadnsrecord:
+      ipaadmin_password: SomeADMINpassword
+      zone_name: "{{ testzone }}"
+      name: host04
+      ns_rec: host04
+      state: absent
+    register: result
+    failed_when: result.changed
+
+  - name: Ensure that 'host04' DLV record is present.
+    ipadnsrecord:
+      ipaadmin_password: SomeADMINpassword
+      zone_name: "{{ testzone }}"
+      name: host04
+      dlv_key_tag: 12345
+      dlv_algorithm: 3
+      dlv_digest_type: 1
+      # digest is sha1sum of 'host04."{{ testzone }}"'
+      dlv_digest: 08ff468cb25ccd21642989294cc33570da5eb2ba
+    register: result
+    failed_when: not result.changed
+
+  - name: Ensure that 'host04' DLV record is present, again.
+    ipadnsrecord:
+      ipaadmin_password: SomeADMINpassword
+      zone_name: "{{ testzone }}"
+      name: host04
+      dlv_key_tag: 12345
+      dlv_algorithm: 3
+      dlv_digest_type: 1
+      dlv_digest: 08ff468cb25ccd21642989294cc33570da5eb2ba
+    register: result
+    failed_when: result.changed
+
+  - name: Ensure that 'host04' DLV record is present, with a different key tag.
+    ipadnsrecord:
+      ipaadmin_password: SomeADMINpassword
+      zone_name: "{{ testzone }}"
+      name: host04
+      dlv_key_tag: 54321
+      dlv_record: 12345 3 1 08ff468cb25ccd21642989294cc33570da5eb2ba
+    register: result
+    failed_when: not result.changed
+
+  - name: Ensure that 'host04' DLV record is present, with a different key tag, again.
+    ipadnsrecord:
+      ipaadmin_password: SomeADMINpassword
+      zone_name: "{{ testzone }}"
+      name: host04
+      dlv_key_tag: 54321
+      dlv_record: 12345 3 1 08ff468cb25ccd21642989294cc33570da5eb2ba
+    register: result
+    failed_when: result.changed
+
+  - name: Ensure that 'host04' DLV record is absent.
+    ipadnsrecord:
+      ipaadmin_password: SomeADMINpassword
+      zone_name: "{{ testzone }}"
+      name: host04
+      dlv_record: 54321 3 1 08ff468cb25ccd21642989294cc33570da5eb2ba
+      state: absent
+    register: result
+    failed_when: not result.changed
+
+  - name: Ensure that 'host04' DLV record is absent, again.
+    ipadnsrecord:
+      ipaadmin_password: SomeADMINpassword
+      zone_name: "{{ testzone }}"
+      name: host04
+      dlv_record: 54321 3 1 08ff468cb25ccd21642989294cc33570da5eb2ba
+      state: absent
+    register: result
+    failed_when: result.changed
+
+  - name: Ensure that dns record 'iron01' is present
+    ipadnsrecord:
+      ipaadmin_password: SomeADMINpassword
+      name: iron01
+      zone_name: "{{ safezone }}"
+      ip_address: "{{ ansible_default_ipv4.address }}"
+    register: result
+    failed_when: not result.changed
+
+  - name: Ensure that NS record for "{{ safezone }}" is present
+    ipadnsrecord:
+      ipaadmin_password: SomeADMINpassword
+      name: iron01
+      zone_name: "{{ safezone }}"
+      ns_hostname: iron01
+    register: result
+    failed_when: not result.changed
+
+  - name: Ensure that 'iron01' DS record is present.
+    ipadnsrecord:
+      ipaadmin_password: SomeADMINpassword
+      zone_name: "{{ safezone }}"
+      name: iron01
+      ds_key_tag: 12345
+      ds_algorithm: 3
+      ds_digest_type: 1
+      # digest is sha1sum of 'iron01."{{ safezone }}"'
+      ds_digest: 84763786e4213cca9a6938dba5dacd64f87ec216
+    register: result
+    failed_when: not result.changed
+
+  - name: Ensure that 'iron01' DS record is present, again.
+    ipadnsrecord:
+      ipaadmin_password: SomeADMINpassword
+      zone_name: "{{ safezone }}"
+      name: iron01
+      ds_key_tag: 12345
+      ds_algorithm: 3
+      ds_digest_type: 1
+      ds_digest: 84763786e4213cca9a6938dba5dacd64f87ec216
+    register: result
+    failed_when: result.changed
+
+  - name: Ensure that 'iron01' DS record is present, with a different key tag.
+    ipadnsrecord:
+      ipaadmin_password: SomeADMINpassword
+      zone_name: "{{ safezone }}"
+      name: iron01
+      ds_key_tag: 54321
+      ds_rec: 12345 3 1 84763786e4213cca9a6938dba5dacd64f87ec216
+    register: result
+    failed_when: not result.changed
+
+  - name: Ensure that 'iron01' DS record is present, with a different key tag, again.
+    ipadnsrecord:
+      ipaadmin_password: SomeADMINpassword
+      zone_name: "{{ safezone }}"
+      name: iron01
+      ds_key_tag: 54321
+      ds_rec: 12345 3 1 84763786e4213cca9a6938dba5dacd64f87ec216
+    register: result
+    failed_when: result.changed
+
+  - name: Ensure that 'iron01' DS record is absent.
+    ipadnsrecord:
+      ipaadmin_password: SomeADMINpassword
+      zone_name: "{{ safezone }}"
+      name: iron01
+      ds_rec: 54321 3 1 84763786e4213cca9a6938dba5dacd64f87ec216
+      state: absent
+    register: result
+    failed_when: not result.changed
+
+  - name: Ensure that 'iron01' DS record is absent, again.
+    ipadnsrecord:
+      ipaadmin_password: SomeADMINpassword
+      zone_name: "{{ safezone }}"
+      name: iron01
+      ds_rec: 54321 3 1 84763786e4213cca9a6938dba5dacd64f87ec216
+      state: absent
+    register: result
+    failed_when: result.changed
+
+  - name: Ensure that 'host04' AFSDB record is present.
+    ipadnsrecord:
+      ipaadmin_password: SomeADMINpassword
+      zone_name: "{{ testzone }}"
+      name: host04
+      afsdb_subtype: 1
+      afsdb_hostname: host04."{{ testzone }}"
+    register: result
+    failed_when: not result.changed
+
+  - name: Ensure that 'host04' AFSDB record is present, again.
+    ipadnsrecord:
+      ipaadmin_password: SomeADMINpassword
+      zone_name: "{{ testzone }}"
+      name: host04
+      afsdb_subtype: 1
+      afsdb_hostname: host04."{{ testzone }}"
+    register: result
+    failed_when: result.changed
+
+  - name: Ensure that 'host04' AFSDB record subtype is 2.
+    ipadnsrecord:
+      ipaadmin_password: SomeADMINpassword
+      zone_name: "{{ testzone }}"
+      name: host04
+      afsdb_subtype: 2
+      afsdb_rec: 1 host04."{{ testzone }}"
+    register: result
+    failed_when: not result.changed
+
+  - name: Ensure that 'host04' AFSDB record subtype is 2, again.
+    ipadnsrecord:
+      ipaadmin_password: SomeADMINpassword
+      zone_name: "{{ testzone }}"
+      name: host04
+      afsdb_subtype: 2
+      afsdb_rec: 1 host04."{{ testzone }}"
+    register: result
+    failed_when: result.changed
+
+  - name: Ensure that 'host04' AFSDB record is absent.
+    ipadnsrecord:
+      ipaadmin_password: SomeADMINpassword
+      zone_name: "{{ testzone }}"
+      name: host04
+      afsdb_rec: 2 host04."{{ testzone }}"
+      state: absent
+    register: result
+    failed_when: not result.changed
+
+  - name: Ensure that 'host04' AFSDB record is absent, again.
+    ipadnsrecord:
+      ipaadmin_password: SomeADMINpassword
+      zone_name: "{{ testzone }}"
+      name: host04
+      afsdb_rec: 2 host04."{{ testzone }}"
+      state: absent
+    register: result
+    failed_when: result.changed
+
+  # Certificate created with:
+  # - openssl req -x509 -newkey rsa:512 -days 3650 -nodes -keyout private1.key -out cert1.pem -subj '/CN=test'
+  # - openssl x509 -outform der -in cert1.pem -out cert1.der
+  # - base64 cert1.der -w5000
+  - name: Ensure that 'host04' CERT record is present.
+    ipadnsrecord:
+      ipaadmin_password: SomeADMINpassword
+      zone_name: "{{ testzone }}"
+      name: host04
+      cert_type: 1
+      cert_key_tag: 1234
+      cert_algorithm: 3
+      cert_certificate_or_crl: MIIBdTCCAR+gAwIBAgIUb14+Oug2nPy1fOFF5US+uiJ1LfIwDQYJKoZIhvcNAQELBQAwDzENMAsGA1UEAwwEdGVzdDAeFw0yMDAzMjMxODMzNDNaFw0zMDAzMjExODMzNDNaMA8xDTALBgNVBAMMBHRlc3QwXDANBgkqhkiG9w0BAQEFAANLADBIAkEAv/yGOgQbtUZbiQMjVly7bWuUX1oBGZAkCvumYpvsep3o1eJJ6HlREbLUlJmgibuNsjqE0FyrXueMjsD8D4juWQIDAQABo1MwUTAdBgNVHQ4EFgQUNtEmJqasXgN7Sh/huB5tx0ONblYwHwYDVR0jBBgwFoAUNtEmJqasXgN7Sh/huB5tx0ONblYwDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAANBAKhPWPK5+pkT9NLLSZm3ASQJcDkU9asrSoc7MsiHIqSUju/YQgjdHgX0ljS8hnlo1scCITW09UXcNRUYFxwEuoQ=
+    register: result
+    failed_when: not result.changed
+
+  - name: Ensure that 'host04' CERT record is present, again.
+    ipadnsrecord:
+      ipaadmin_password: SomeADMINpassword
+      zone_name: "{{ testzone }}"
+      name: host04
+      cert_type: 1
+      cert_key_tag: 1234
+      cert_algorithm: 3
+      cert_certificate_or_crl: MIIBdTCCAR+gAwIBAgIUb14+Oug2nPy1fOFF5US+uiJ1LfIwDQYJKoZIhvcNAQELBQAwDzENMAsGA1UEAwwEdGVzdDAeFw0yMDAzMjMxODMzNDNaFw0zMDAzMjExODMzNDNaMA8xDTALBgNVBAMMBHRlc3QwXDANBgkqhkiG9w0BAQEFAANLADBIAkEAv/yGOgQbtUZbiQMjVly7bWuUX1oBGZAkCvumYpvsep3o1eJJ6HlREbLUlJmgibuNsjqE0FyrXueMjsD8D4juWQIDAQABo1MwUTAdBgNVHQ4EFgQUNtEmJqasXgN7Sh/huB5tx0ONblYwHwYDVR0jBBgwFoAUNtEmJqasXgN7Sh/huB5tx0ONblYwDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAANBAKhPWPK5+pkT9NLLSZm3ASQJcDkU9asrSoc7MsiHIqSUju/YQgjdHgX0ljS8hnlo1scCITW09UXcNRUYFxwEuoQ=
+    register: result
+    failed_when: result.changed
+
+  - name: Ensure that 'host04' CERT record is absent.
+    ipadnsrecord:
+      ipaadmin_password: SomeADMINpassword
+      zone_name: "{{ testzone }}"
+      name: host04
+      cert_rec: 1 1234 3 MIIBdTCCAR+gAwIBAgIUb14+Oug2nPy1fOFF5US+uiJ1LfIwDQYJKoZIhvcNAQELBQAwDzENMAsGA1UEAwwEdGVzdDAeFw0yMDAzMjMxODMzNDNaFw0zMDAzMjExODMzNDNaMA8xDTALBgNVBAMMBHRlc3QwXDANBgkqhkiG9w0BAQEFAANLADBIAkEAv/yGOgQbtUZbiQMjVly7bWuUX1oBGZAkCvumYpvsep3o1eJJ6HlREbLUlJmgibuNsjqE0FyrXueMjsD8D4juWQIDAQABo1MwUTAdBgNVHQ4EFgQUNtEmJqasXgN7Sh/huB5tx0ONblYwHwYDVR0jBBgwFoAUNtEmJqasXgN7Sh/huB5tx0ONblYwDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAANBAKhPWPK5+pkT9NLLSZm3ASQJcDkU9asrSoc7MsiHIqSUju/YQgjdHgX0ljS8hnlo1scCITW09UXcNRUYFxwEuoQ=
+      state: absent
+    register: result
+    failed_when: not result.changed
+
+  - name: Ensure that 'host04' CERT record is absent, again.
+    ipadnsrecord:
+      ipaadmin_password: SomeADMINpassword
+      zone_name: "{{ testzone }}"
+      name: host04
+      cert_rec: 1 1234 3 MIIBdTCCAR+gAwIBAgIUb14+Oug2nPy1fOFF5US+uiJ1LfIwDQYJKoZIhvcNAQELBQAwDzENMAsGA1UEAwwEdGVzdDAeFw0yMDAzMjMxODMzNDNaFw0zMDAzMjExODMzNDNaMA8xDTALBgNVBAMMBHRlc3QwXDANBgkqhkiG9w0BAQEFAANLADBIAkEAv/yGOgQbtUZbiQMjVly7bWuUX1oBGZAkCvumYpvsep3o1eJJ6HlREbLUlJmgibuNsjqE0FyrXueMjsD8D4juWQIDAQABo1MwUTAdBgNVHQ4EFgQUNtEmJqasXgN7Sh/huB5tx0ONblYwHwYDVR0jBBgwFoAUNtEmJqasXgN7Sh/huB5tx0ONblYwDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAANBAKhPWPK5+pkT9NLLSZm3ASQJcDkU9asrSoc7MsiHIqSUju/YQgjdHgX0ljS8hnlo1scCITW09UXcNRUYFxwEuoQ=
+      state: absent
+    register: result
+    failed_when: result.changed
+
+  - name: Ensure that 'host04' KX record is present.
+    ipadnsrecord:
+      ipaadmin_password: SomeADMINpassword
+      zone_name: "{{ testzone }}"
+      name: host04
+      kx_preference: 10
+      kx_exchanger: keyex."{{ testzone }}"
+    register: result
+    failed_when: not result.changed
+
+  - name: Ensure that 'host04' KX record is present, again.
+    ipadnsrecord:
+      ipaadmin_password: SomeADMINpassword
+      zone_name: "{{ testzone }}"
+      name: host04
+      kx_preference: 10
+      kx_exchanger: keyex."{{ testzone }}"
+    register: result
+    failed_when: result.changed
+
+  - name: Ensure that 'host04' KX record is present with preference set to 20.
+    ipadnsrecord:
+      ipaadmin_password: SomeADMINpassword
+      zone_name: "{{ testzone }}"
+      name: host04
+      kx_preference: 20
+      kx_rec: 10 keyex."{{ testzone }}"
+    register: result
+    failed_when: not result.changed
+
+  - name: Ensure that 'host04' KX record is present with preference set to 20, again.
+    ipadnsrecord:
+      ipaadmin_password: SomeADMINpassword
+      zone_name: "{{ testzone }}"
+      name: host04
+      kx_preference: 20
+      kx_rec: 10 keyex."{{ testzone }}"
+    register: result
+    failed_when: result.changed
+
+  - name: Ensure that 'host04' KX record is present with preference set to 20, one more time.
+    ipadnsrecord:
+      ipaadmin_password: SomeADMINpassword
+      zone_name: "{{ testzone }}"
+      name: host04
+      kx_preference: 20
+      kx_rec: 20 keyex."{{ testzone }}"
+    register: result
+    failed_when: result.changed
+
+  - name: Ensure that 'host04' KX record is absent.
+    ipadnsrecord:
+      ipaadmin_password: SomeADMINpassword
+      zone_name: "{{ testzone }}"
+      name: host04
+      kx_rec: 20 keyex."{{ testzone }}"
+      state: absent
+    register: result
+    failed_when: not result.changed
+
+  - name: Ensure that 'host04' KX record is absent, again.
+    ipadnsrecord:
+      ipaadmin_password: SomeADMINpassword
+      zone_name: "{{ testzone }}"
+      name: host04
+      kx_rec: 20 keyex."{{ testzone }}"
+      state: absent
+    register: result
+    failed_when: result.changed
+
+  - name: Ensure that 'host04' MX record is present.
+    ipadnsrecord:
+      ipaadmin_password: SomeADMINpassword
+      zone_name: "{{ testzone }}"
+      name: host04
+      mx_preference: 10
+      mx_exchanger: mail."{{ testzone }}"
+    register: result
+    failed_when: not result.changed
+
+  - name: Ensure that 'host04' MX record is present, again.
+    ipadnsrecord:
+      ipaadmin_password: SomeADMINpassword
+      zone_name: "{{ testzone }}"
+      name: host04
+      mx_preference: 10
+      mx_exchanger: mail."{{ testzone }}"
+    register: result
+    failed_when: result.changed
+
+  - name: Ensure that 'host04' MX record is present with preference set to 20.
+    ipadnsrecord:
+      ipaadmin_password: SomeADMINpassword
+      zone_name: "{{ testzone }}"
+      name: host04
+      mx_preference: 20
+      mx_rec: 10 mail."{{ testzone }}"
+    register: result
+    failed_when: not result.changed
+
+  - name: Ensure that 'host04' MX record is absent.
+    ipadnsrecord:
+      ipaadmin_password: SomeADMINpassword
+      zone_name: "{{ testzone }}"
+      name: host04
+      mx_rec: 20 mail."{{ testzone }}"
+      state: absent
+    register: result
+    failed_when: not result.changed
+
+  - name: Ensure that 'host04' MX record is absent, again.
+    ipadnsrecord:
+      ipaadmin_password: SomeADMINpassword
+      zone_name: "{{ testzone }}"
+      name: host04
+      mx_rec: 20 mail."{{ testzone }}"
+      state: absent
+    register: result
+    failed_when: result.changed
+
+  - name: Ensure that '_sip._udp' service has NAPTR record is absent, again.
+    ipadnsrecord:
+      ipaadmin_password: SomeADMINpassword
+      zone_name: "{{ testzone }}"
+      name: _sip._udp
+      record_type: NAPTR
+      record_value: '100 10 U SIP+D2U !^.*$!sip:customer-service@example.com! .'
+      state: absent
+    register: result
+    failed_when: result.changed
+
+  - name: Ensure that 'host04' LOC record is present.
+    ipadnsrecord:
+      ipaadmin_password: SomeADMINpassword
+      zone_name: "{{ testzone }}"
+      name: host04
+      loc_lat_deg: 52
+      loc_lat_min: 22
+      loc_lat_sec: 23.000
+      loc_lat_dir: N
+      loc_lon_deg: 4
+      loc_lon_min: 53
+      loc_lon_sec: 32.00
+      loc_lon_dir: E
+      loc_altitude: -2.00
+      loc_size: 0.00
+      loc_h_precision: 10000
+      loc_v_precision: 10
+    register: result
+    failed_when: not result.changed
+
+  - name: Ensure that 'host04' LOC record is present, again.
+    ipadnsrecord:
+      ipaadmin_password: SomeADMINpassword
+      zone_name: "{{ testzone }}"
+      name: host04
+      loc_lat_deg: 52
+      loc_lat_min: 22
+      loc_lat_sec: 23.000
+      loc_lat_dir: N
+      loc_lon_deg: 4
+      loc_lon_min: 53
+      loc_lon_sec: 32.000
+      loc_lon_dir: E
+      loc_altitude: -2.00
+      loc_size: 0.00
+      loc_h_precision: 10000
+      loc_v_precision: 10
+    register: result
+    failed_when: result.changed
+
+  - name: Ensure that 'host04' LOC record is present, with loc_size 1.00.
+    ipadnsrecord:
+      ipaadmin_password: SomeADMINpassword
+      zone_name: "{{ testzone }}"
+      name: host04
+      loc_size: 1.00
+      loc_rec: 52 22 23 N 4 53 32 E -2 0 10000 10
+    register: result
+    failed_when: not result.changed
+
+  - name: Ensure that 'host04' LOC record is absent.
+    ipadnsrecord:
+      ipaadmin_password: SomeADMINpassword
+      zone_name: "{{ testzone }}"
+      name: host04
+      loc_rec: 52 22 23.000 N 4 53 32.000 E -2.00 1.00 10000 10
+      state: absent
+    register: result
+    failed_when: not result.changed
+
+  - name: Ensure that 'host04' LOC record is absent, again.
+    ipadnsrecord:
+      ipaadmin_password: SomeADMINpassword
+      zone_name: "{{ testzone }}"
+      name: host04
+      loc_rec: 52 22 23.000 N 4 53 32.000 E -2.00 1.00 10000 10
+      state: absent
+    register: result
+    failed_when: result.changed
+
+  - name: Ensure that '_sip._udp' service has NAPTR record.
+    ipadnsrecord:
+      ipaadmin_password: SomeADMINpassword
+      zone_name: "{{ testzone }}"
+      name: _sip._udp
+      naptr_order: 100
+      naptr_preference: 10
+      naptr_flags: "U"
+      naptr_service: "SIP+D2U"
+      naptr_regexp: "!^.*$!sip:customer-service@example.com!"
+      naptr_replacement: "."
+    register: result
+    failed_when: not result.changed
+
+  - name: Ensure that '_sip._udp' service has NAPTR record, again.
+    ipadnsrecord:
+      ipaadmin_password: SomeADMINpassword
+      zone_name: "{{ testzone }}"
+      name: _sip._udp
+      naptr_order: 100
+      naptr_preference: 10
+      naptr_flags: "U"
+      naptr_service: "SIP+D2U"
+      naptr_regexp: "!^.*$!sip:customer-service@example.com!"
+      naptr_replacement: "."
+    register: result
+    failed_when: result.changed
+
+  - name: Change '_sip._udp' service NAPTR record `preference` to 20.
+    ipadnsrecord:
+      ipaadmin_password: SomeADMINpassword
+      zone_name: "{{ testzone }}"
+      name: _sip._udp
+      naptr_preference: 20
+      naptr_rec: '100 10 U SIP+D2U !^.*$!sip:customer-service@example.com! .'
+    register: result
+    failed_when: not result.changed
+
+  - name: Ensure that '_sip._udp' service has NAPTR record is absent.
+    ipadnsrecord:
+      ipaadmin_password: SomeADMINpassword
+      zone_name: "{{ testzone }}"
+      name: _sip._udp
+      record_type: NAPTR
+      record_value: '100 20 U SIP+D2U !^.*$!sip:customer-service@example.com! .'
+      state: absent
+    register: result
+    failed_when: not result.changed
+
+  - name: Ensure that '_sip._udp' service has NAPTR record is absent, again.
+    ipadnsrecord:
+      ipaadmin_password: SomeADMINpassword
+      zone_name: "{{ testzone }}"
+      name: _sip._udp
+      record_type: NAPTR
+      record_value: '100 20 U SIP+D2U !^.*$!sip:customer-service@example.com! .'
+      state: absent
+    register: result
+    failed_when: result.changed
+
+  - name: Ensure that '_sip._udp' service has SRV record.
+    ipadnsrecord:
+      ipaadmin_password: SomeADMINpassword
+      zone_name: "{{ testzone }}"
+      name: _sip._udp
+      srv_priority: 10
+      srv_weight: 10
+      srv_port: 5060
+      srv_target: sip-server."{{ testzone }}"
+    register: result
+    failed_when: not result.changed
+
+  - name: Ensure that '_sip._udp' service has SRV record, again.
+    ipadnsrecord:
+      ipaadmin_password: SomeADMINpassword
+      zone_name: "{{ testzone }}"
+      name: _sip._udp
+      srv_priority: 10
+      srv_weight: 10
+      srv_port: 5060
+      srv_target: sip-server."{{ testzone }}"
+    register: result
+    failed_when: result.changed
+
+  - name: Ensure '_sip._udp' SRV record has priority equals to 4.
+    ipadnsrecord:
+      ipaadmin_password: SomeADMINpassword
+      zone_name: "{{ testzone }}"
+      name: _sip._udp
+      srv_priority: 4
+      srv_weight: 10
+      srv_port: 5060
+      srv_target: sip-server."{{ testzone }}"
+      srv_rec: 10 10 5060 sip-server."{{ testzone }}"
+    register: result
+    failed_when: not result.changed
+
+  - name: Ensure '_sip._udp' SRV record has priority equals to 4, again.
+    ipadnsrecord:
+      ipaadmin_password: SomeADMINpassword
+      zone_name: "{{ testzone }}"
+      name: _sip._udp
+      srv_priority: 4
+      srv_weight: 10
+      srv_port: 5060
+      srv_target: sip-server."{{ testzone }}"
+      srv_rec: 10 10 5060 sip-server."{{ testzone }}"
+    register: result
+    failed_when: result.changed
+
+  - name: Ensurer '_sip._udp' SRV record has priority 2, weight 20
+    ipadnsrecord:
+      ipaadmin_password: SomeADMINpassword
+      zone_name: "{{ testzone }}"
+      name: _sip._udp
+      srv_priority: 2
+      srv_weight: 20
+      srv_port: 5060
+      srv_target: sip-server."{{ testzone }}"
+    register: result
+    failed_when: not result.changed
+
+  - name: Ensurer '_sip._udp' SRV record has priority 2, weight 20, again.
+    ipadnsrecord:
+      ipaadmin_password: SomeADMINpassword
+      zone_name: "{{ testzone }}"
+      name: _sip._udp
+      srv_priority: 2
+      srv_weight: 20
+      srv_port: 5060
+      srv_target: sip-server."{{ testzone }}"
+    register: result
+    failed_when: result.changed
+
+  - name: Ensure that '_sip._udp' SRV record is absent.
+    ipadnsrecord:
+      ipaadmin_password: SomeADMINpassword
+      zone_name: "{{ testzone }}"
+      name: _sip._udp
+      srv_record: 2 20 5060 sip-server."{{ testzone }}"
+      state: absent
+    register: result
+    failed_when: not result.changed
+
+  - name: Ensure that '_sip._udp' SRV record is absent, again.
+    ipadnsrecord:
+      ipaadmin_password: SomeADMINpassword
+      zone_name: "{{ testzone }}"
+      name: _sip._udp
+      srv_record: 2 20 5060 sip-server."{{ testzone }}"
+      state: absent
+    register: result
+    failed_when: result.changed
+
+  # SSHFP fingerprint generated with `ssh-keygen -r host04."{{ testzone }}"`
+  - name: Ensure that 'host04' has SSHFP record.
+    ipadnsrecord:
+      ipaadmin_password: SomeADMINpassword
+      zone_name: "{{ testzone }}"
+      name: host04
+      sshfp_algorithm: 1
+      sshfp_fp_type: 1
+      sshfp_fingerprint: d21802c61733e055b8d16296cbce300efb8a167a
+    register: result
+    failed_when: not result.changed
+
+  - name: Ensure that 'host04' has SSHFP record, again.
+    ipadnsrecord:
+      ipaadmin_password: SomeADMINpassword
+      zone_name: "{{ testzone }}"
+      name: host04
+      sshfp_algorithm: 1
+      sshfp_fp_type: 1
+      sshfp_fingerprint: d21802c61733e055b8d16296cbce300efb8a167a
+    register: result
+    failed_when: result.changed
+
+  - name: Ensure that 'host04' SSHFP record is absent.
+    ipadnsrecord:
+      ipaadmin_password: SomeADMINpassword
+      zone_name: "{{ testzone }}"
+      name: host04
+      sshfp_rec: 1 1 d21802c61733e055b8d16296cbce300efb8a167a
+      state: absent
+    register: result
+    failed_when: not result.changed
+
+  - name: Ensure that 'host04' SSHFP record is absent, again.
+    ipadnsrecord:
+      ipaadmin_password: SomeADMINpassword
+      zone_name: "{{ testzone }}"
+      name: host04
+      sshfp_rec: 1 1 d21802c61733e055b8d16296cbce300efb8a167a
+      state: absent
+    register: result
+    failed_when: result.changed
+
+  # Data is sha356sum of 'Some Text to Test', it should be created from
+  # a real certificate.
+  - name: Ensure that 'host04' has TLSA record present.
+    ipadnsrecord:
+      ipaadmin_password: SomeADMINpassword
+      zone_name: "{{ testzone }}"
+      name: host04
+      tlsa_cert_usage: 3
+      tlsa_selector: 1
+      tlsa_matching_type: 1
+      tlsa_cert_association_data: 9c0ad776dbeae8d9d55b0ad42899d30235c114d5f918fd69746e4279e47bdaa2
+    register: result
+    failed_when: not result.changed
+
+  - name: Ensure that 'host04' has TLSA record present, again.
+    ipadnsrecord:
+      ipaadmin_password: SomeADMINpassword
+      zone_name: "{{ testzone }}"
+      name: host04
+      tlsa_cert_usage: 3
+      tlsa_selector: 1
+      tlsa_matching_type: 1
+      tlsa_cert_association_data: 9c0ad776dbeae8d9d55b0ad42899d30235c114d5f918fd69746e4279e47bdaa2
+    register: result
+    failed_when: result.changed
+
+  - name: Modify 'host04' has TLSA record.
+    ipadnsrecord:
+      ipaadmin_password: SomeADMINpassword
+      zone_name: "{{ testzone }}"
+      name: host04
+      tlsa_matching_type: 0
+      tlsa_rec: 3 1 1 9c0ad776dbeae8d9d55b0ad42899d30235c114d5f918fd69746e4279e47bdaa2
+    register: result
+    failed_when: not result.changed
+
+  - name: Modify 'host04' has TLSA record, again.
+    ipadnsrecord:
+      ipaadmin_password: SomeADMINpassword
+      zone_name: "{{ testzone }}"
+      name: host04
+      tlsa_matching_type: 0
+      tlsa_rec: 3 1 1 9c0ad776dbeae8d9d55b0ad42899d30235c114d5f918fd69746e4279e47bdaa2
+    register: result
+    failed_when: result.changed
+
+  - name: Ensure that 'host04' TLSA record is absent.
+    ipadnsrecord:
+      ipaadmin_password: SomeADMINpassword
+      zone_name: "{{ testzone }}"
+      name: host04
+      tlsa_rec: 3 1 0 9c0ad776dbeae8d9d55b0ad42899d30235c114d5f918fd69746e4279e47bdaa2
+      state: absent
+    register: result
+    failed_when: not result.changed
+
+  - name: Ensure that 'host04' TLSA record is absent, again.
+    ipadnsrecord:
+      ipaadmin_password: SomeADMINpassword
+      zone_name: "{{ testzone }}"
+      name: host04
+      tlsa_rec: 3 1 0 9c0ad776dbeae8d9d55b0ad42899d30235c114d5f918fd69746e4279e47bdaa2
+      state: absent
+    register: result
+    failed_when: result.changed
+
+  - name: Ensure that 'host04' has TXT record present.
+    ipadnsrecord:
+      ipaadmin_password: SomeADMINpassword
+      zone_name: "{{ testzone }}"
+      name: host04
+      txt_data: Some Text
+    register: result
+    failed_when: not result.changed
+
+  # - name: Ensure that 'host04' has TXT record present, again.
+  #   ipadnsrecord:
+  #     ipaadmin_password: SomeADMINpassword
+  #     zone_name: "{{ testzone }}"
+  #     name: host04
+  #     txt_data: Some Text
+  #   register: result
+  #   failed_when: result.changed
+
+  - name: Change value of  'host04' TXT record.
+    ipadnsrecord:
+      ipaadmin_password: SomeADMINpassword
+      zone_name: "{{ testzone }}"
+      name: host04
+      txt_data: Some new Text
+      txt_rec: Some Text
+    register: result
+    failed_when: not result.changed
+
+  - name: Add a second TXT record to 'host04'.
+    ipadnsrecord:
+      ipaadmin_password: SomeADMINpassword
+      zone_name: "{{ testzone }}"
+      name: host04
+      txt_rec: Some Other Text
+    register: result
+    failed_when: not result.changed
+
+  - name: Add a second TXT record to 'host04', again.
+    ipadnsrecord:
+      ipaadmin_password: SomeADMINpassword
+      zone_name: "{{ testzone }}"
+      name: host04
+      txt_rec: Some Other Text
+    register: result
+    failed_when: result.changed
+
+  - name: Ensure that one of 'host04' TXT record is absent.
+    ipadnsrecord:
+      ipaadmin_password: SomeADMINpassword
+      zone_name: "{{ testzone }}"
+      name: host04
+      txt_rec: Some new Text
+      state: absent
+    register: result
+    failed_when: not result.changed
+
+  - name: Ensure that one of 'host04' TXT record is absent, again.
+    ipadnsrecord:
+      ipaadmin_password: SomeADMINpassword
+      zone_name: "{{ testzone }}"
+      name: host04
+      txt_rec: Some new Text
+      state: absent
+    register: result
+    failed_when: result.changed
+
+  - name: Ensure that 'host04' TXT record are all absent.
+    ipadnsrecord:
+      ipaadmin_password: SomeADMINpassword
+      zone_name: "{{ testzone }}"
+      name: host04
+      txt_rec:
+      - Some new Text
+      - Some Other Text
+      state: absent
+    register: result
+    failed_when: not result.changed
+
+  - name: Ensure that 'host04' TXT record are all absent, again.
+    ipadnsrecord:
+      ipaadmin_password: SomeADMINpassword
+      zone_name: "{{ testzone }}"
+      name: host04
+      txt_rec:
+      - Some new Text
+      - Some Other Text
+      state: absent
+    register: result
+    failed_when: result.changed
+
+  - name: Ensure that '_ftp._tcp' has URI record.
+    ipadnsrecord:
+      ipaadmin_password: SomeADMINpassword
+      zone_name: "{{ testzone }}"
+      name: _ftp._tcp
+      uri_priority: 10
+      uri_weight: 1
+      uri_target: ftp://ftp.host04."{{ testzone }}"/public
+    register: result
+    failed_when: not result.changed
+
+  - name: Ensure that '_ftp._tcp' has URI record, again
+    ipadnsrecord:
+      ipaadmin_password: SomeADMINpassword
+      zone_name: "{{ testzone }}"
+      name: _ftp._tcp
+      uri_priority: 10
+      uri_weight: 1
+      uri_target: ftp://ftp.host04."{{ testzone }}"/public
+    register: result
+    failed_when: result.changed
+
+  - name: Change '_ftp._tcp' URI record weight to 3 and priority to 5.
+    ipadnsrecord:
+      ipaadmin_password: SomeADMINpassword
+      zone_name: "{{ testzone }}"
+      name: _ftp._tcp
+      uri_priority: 5
+      uri_weight: 3
+      uri_rec: 10 1 ftp://ftp.host04."{{ testzone }}"/public
+    register: result
+    failed_when: not result.changed
+
+  - name: Verify if modification worked.
+    ipadnsrecord:
+      uri_rec: 10 1 ftp://ftp.host04."{{ testzone }}"/public
+      state: absent
+    register: result
+    failed_when: result.changed
+
+
+  - name: Change '_ftp._tcp' URI record weight to 3 and priority to 5, again.
+    ipadnsrecord:
+      ipaadmin_password: SomeADMINpassword
+      zone_name: "{{ testzone }}"
+      name: _ftp._tcp
+      uri_priority: 5
+      uri_weight: 3
+      uri_rec: 5 3 ftp://ftp.host04."{{ testzone }}"/public
+    register: result
+    failed_when: result.changed
+
+  - name: Ensure that '_ftp._tcp' URI record is absent.
+    ipadnsrecord:
+      ipaadmin_password: SomeADMINpassword
+      zone_name: "{{ testzone }}"
+      name: _ftp._tcp
+      uri_rec: 5 3 ftp://ftp.host04."{{ testzone }}"/public
+      state: absent
+    register: result
+    failed_when: not result.changed
+
+  - name: Ensure that '_ftp._tcp' URI record is absent, again.
+    ipadnsrecord:
+      ipaadmin_password: SomeADMINpassword
+      zone_name: "{{ testzone }}"
+      name: _ftp._tcp
+      uri_rec: 5 3 ftp://ftp.host04."{{ testzone }}"/public
+      state: absent
+    register: result
+    failed_when: result.changed
+
+  # cleanup
+  - name: Cleanup test environment.
+    include_tasks: env_cleanup.yml
diff --git a/tests/dnsrecord/test_dnsrecord_full_records.yml b/tests/dnsrecord/test_dnsrecord_full_records.yml
new file mode 100644
index 0000000000000000000000000000000000000000..86e124c882b4415fb93cd217687912e645e0c6d1
--- /dev/null
+++ b/tests/dnsrecord/test_dnsrecord_full_records.yml
@@ -0,0 +1,150 @@
+---
+- name: Test dnsrecord with full records (*-rec variables).
+  hosts: ipaserver
+  become: yes
+  gather_facts: yes
+
+  tasks:
+
+  - name: Setup test environment
+    include_tasks: env_setup.yml
+
+  # tests
+
+  - name: Ensure that dns A record for 'host01' is present
+    ipadnsrecord:
+      ipaadmin_password: SomeADMINpassword
+      name: host01
+      zone_name: "{{ testzone }}"
+      a_rec: 192.168.122.101
+    register: result
+    failed_when: not result.changed
+
+  - name: Ensure that dns A record for 'host01' is present, again
+    ipadnsrecord:
+      ipaadmin_password: SomeADMINpassword
+      name: host01
+      zone_name: "{{ testzone }}"
+      a_rec: 192.168.122.101
+    register: result
+    failed_when: result.changed
+
+  - name: Ensure that dns A records for 'host01' are present
+    ipadnsrecord:
+      ipaadmin_password: SomeADMINpassword
+      name: host01
+      zone_name: "{{ testzone }}"
+      a_rec:
+      - 192.168.122.101
+      - 192.168.122.102
+      - 192.168.122.103
+    register: result
+    failed_when: not result.changed
+
+  - name: Ensure that dns A records for 'host01' are present, again
+    ipadnsrecord:
+      ipaadmin_password: SomeADMINpassword
+      name: host01
+      zone_name: "{{ testzone }}"
+      a_rec:
+      - 192.168.122.101
+      - 192.168.122.102
+      - 192.168.122.103
+    register: result
+    failed_when: result.changed
+
+  - name: Ensure that dns A records for 'host01' are absent
+    ipadnsrecord:
+      ipaadmin_password: SomeADMINpassword
+      name: host01
+      zone_name: "{{ testzone }}"
+      a_rec:
+      - 192.168.122.101
+      - 192.168.122.102
+      state: absent
+    register: result
+    failed_when: not result.changed
+
+  - name: Ensure that dns A records for 'host01' are absent, again
+    ipadnsrecord:
+      ipaadmin_password: SomeADMINpassword
+      name: host01
+      zone_name: "{{ testzone }}"
+      a_rec:
+      - 192.168.122.101
+      - 192.168.122.102
+      state: absent
+    register: result
+    failed_when: result.changed
+
+  ####
+
+  - name: Ensure that dns AAAA record for 'host01' is present
+    ipadnsrecord:
+      ipaadmin_password: SomeADMINpassword
+      name: host01
+      zone_name: "{{ testzone }}"
+      aaaa_rec: fd00::0001
+    register: result
+    failed_when: not result.changed
+
+  - name: Ensure that dns AAAA record for 'host01' is present, again
+    ipadnsrecord:
+      ipaadmin_password: SomeADMINpassword
+      name: host01
+      zone_name: "{{ testzone }}"
+      aaaa_rec: fd00::0001
+    register: result
+    failed_when: result.changed
+
+  - name: Ensure that dns AAAA records for 'host01' are present
+    ipadnsrecord:
+      ipaadmin_password: SomeADMINpassword
+      name: host01
+      zone_name: "{{ testzone }}"
+      aaaa_rec:
+      - fd00::0001
+      - fd00::0011
+      - fd00::0021
+    register: result
+    failed_when: not result.changed
+
+  - name: Ensure that dns AAAAA records for 'host01' are present, again
+    ipadnsrecord:
+      ipaadmin_password: SomeADMINpassword
+      name: host01
+      zone_name: "{{ testzone }}"
+      aaaa_rec:
+      - fd00::0001
+      - fd00::0011
+      - fd00::0021
+    register: result
+    failed_when: result.changed
+
+  - name: Ensure that dns AAAAA records for 'host01' are absent
+    ipadnsrecord:
+      ipaadmin_password: SomeADMINpassword
+      name: host01
+      zone_name: "{{ testzone }}"
+      aaaa_rec:
+      - fd00::0001
+      - fd00::0011
+      state: absent
+    register: result
+    failed_when: not result.changed
+
+  - name: Ensure that dns AAAAA records for 'host01' are absent, again
+    ipadnsrecord:
+      ipaadmin_password: SomeADMINpassword
+      name: host01
+      zone_name: "{{ testzone }}"
+      aaaa_rec:
+      - fd00::0001
+      - fd00::0011
+      state: absent
+    register: result
+    failed_when: result.changed
+
+  # Cleanup
+  - name: Cleanup test environment.
+    include_tasks: env_cleanup.yml