diff --git a/requirements.txt b/requirements.txt
index 1f16c01..cbe0a9d 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -1,2 +1,3 @@
 six>=1.10.0 # MIT
 PyYAML>=3.12 # MIT
+netaddr>=0.7.18 # BSD
diff --git a/tox.ini b/tox.ini
index 920b311..db9b764 100644
--- a/tox.ini
+++ b/tox.ini
@@ -23,6 +23,7 @@ setenv =
   ANSIBLE_LIBRARY={toxinidir}/tripleo_ipa/roles.galaxy/config_template/library:{toxinidir}/tripleo_ipa/ansible_plugins/modules
   ANSIBLE_ROLES_PATH={toxinidir}/tripleo_ipa/roles.galaxy:{toxinidir}/tripleo_ipa/roles
 deps =
+   -r {toxinidir}/requirements.txt
    -r {toxinidir}/molecule-requirements.txt
 changedir = {toxinidir}/tripleo_ipa
 commands = molecule test --all
diff --git a/tripleo_ipa/molecule/default/converge.yml b/tripleo_ipa/molecule/default/converge.yml
index 0ae0d4a..fd1fa21 100644
--- a/tripleo_ipa/molecule/default/converge.yml
+++ b/tripleo_ipa/molecule/default/converge.yml
@@ -141,3 +141,63 @@
     IPA_USER: admin
     IPA_HOST: ipa.example.test
     IPA_PASS: password123
+
+
+- name: Converge - add dns entries
+  hosts: all
+  vars:
+    cloud_domain: ooo.test
+    hosts_entry: '2001:0db8:85a3:0000:0000:8a2e:0370:7334 foo.ooo.test
+
+  2001:0db8:85a3:0000:0000:8a2e:0370:7333 foo.ooo.test
+
+  2001:0db8:85a3:0000:0000:8a2e:0370:7333 bar.ooo.test
+
+  192.168.24.111 bar.ooo.test
+
+  192.168.24.1 undercloud.ctlplane.ooo.test undercloud.ctlplane
+
+  192.168.24.115  overcloud.ctlplane.ooo.test
+
+  10.0.0.135  overcloud.ooo.test
+
+  172.17.0.15  overcloud.internalapi.ooo.test
+
+  172.18.0.231  overcloud.storage.ooo.test
+
+  172.19.0.164  overcloud.storagemgmt.ooo.test
+
+  172.17.0.46 overcloud-controller-0.ooo.test overcloud-controller-0
+
+  10.0.0.116 overcloud-controller-0.external.ooo.test overcloud-controller-0.external
+
+  172.17.0.46 overcloud-controller-0.internalapi.ooo.test overcloud-controller-0.internalapi
+
+  172.18.0.185 overcloud-controller-0.storage.ooo.test overcloud-controller-0.storage
+
+  172.19.0.107 overcloud-controller-0.storagemgmt.ooo.test overcloud-controller-0.storagemgmt
+
+  172.16.0.72 overcloud-controller-0.tenant.ooo.test overcloud-controller-0.tenant
+
+  192.168.24.122 overcloud-controller-0.ctlplane.ooo.test overcloud-controller-0.ctlplane
+
+
+  172.17.0.110 overcloud-novacompute-0.ooo.test overcloud-novacompute-0
+
+  172.17.0.110 overcloud-novacompute-0.internalapi.ooo.test overcloud-novacompute-0.internalapi
+
+  172.18.0.243 overcloud-novacompute-0.storage.ooo.test overcloud-novacompute-0.storage
+
+  172.16.0.195 overcloud-novacompute-0.tenant.ooo.test overcloud-novacompute-0.tenant
+
+  192.168.24.128 overcloud-novacompute-0.ctlplane.ooo.test overcloud-novacompute-0.ctlplane
+
+
+
+    '
+  roles:
+    - name: tripleo_ipa_dns
+  environment:
+    IPA_USER: admin
+    IPA_HOST: ipa.example.test
+    IPA_PASS: password123
diff --git a/tripleo_ipa/molecule/default/tests/test_default.py b/tripleo_ipa/molecule/default/tests/test_default.py
index c6a5f8c..8e7a2bd 100644
--- a/tripleo_ipa/molecule/default/tests/test_default.py
+++ b/tripleo_ipa/molecule/default/tests/test_default.py
@@ -1,3 +1,4 @@
+import ipaddress
 import os
 
 import pytest
@@ -166,3 +167,63 @@ def test_services(host, service, subhost):
     'Roles: Nova Host Manager' in result
     assert 'Managed by: test-0.{}.example.test, test-0.example.test'.format(
         subhost) in result
+
+
+@pytest.mark.parametrize('ip, name', [
+    ('2001:0db8:85a3:0000:0000:8a2e:0370:7333', 'foo'),
+    ('2001:0db8:85a3:0000:0000:8a2e:0370:7333', 'bar'),
+    ('192.168.24.111', 'bar'),
+    ('192.168.24.1', 'undercloud.ctlplane'),
+    ('192.168.24.115', 'overcloud.ctlplane'),
+    ('10.0.0.135', 'overcloud'),
+    ('172.17.0.15', 'overcloud.internalapi'),
+    ('172.18.0.231', 'overcloud.storage'),
+    ('172.19.0.164', 'overcloud.storagemgmt'),
+    ('172.17.0.46', 'overcloud-controller-0'),
+    ('10.0.0.116', 'overcloud-controller-0.external'),
+    ('172.17.0.46', 'overcloud-controller-0.internalapi'),
+    ('172.18.0.185', 'overcloud-controller-0.storage'),
+    ('172.19.0.107', 'overcloud-controller-0.storagemgmt'),
+    ('172.16.0.72', 'overcloud-controller-0.tenant'),
+    ('192.168.24.122', 'overcloud-controller-0.ctlplane'),
+    ('172.17.0.110', 'overcloud-novacompute-0'),
+    ('172.17.0.110', 'overcloud-novacompute-0.internalapi'),
+    ('172.18.0.243', 'overcloud-novacompute-0.storage'),
+    ('172.16.0.195', 'overcloud-novacompute-0.tenant'),
+    ('192.168.24.128', 'overcloud-novacompute-0.ctlplane')])
+def test_dns(host, ip, name):
+    result = host.check_output(
+        'ipa dnsrecord-find ooo.test --name={}'.format(
+            name))
+    assert 'record: {}'.format(ip) in result
+
+
+@pytest.mark.parametrize('ip, name', [
+    ('2001:0db8:85a3:0000:0000:8a2e:0370:7334', 'foo'),
+    ('2001:0db8:85a3:0000:0000:8a2e:0370:7333', 'bar'),
+    ('192.168.24.111', 'bar'),
+    ('192.168.24.1', 'undercloud.ctlplane'),
+    ('192.168.24.115', 'overcloud.ctlplane'),
+    ('10.0.0.135', 'overcloud'),
+    ('172.17.0.15', 'overcloud.internalapi'),
+    ('172.18.0.231', 'overcloud.storage'),
+    ('172.19.0.164', 'overcloud.storagemgmt'),
+    ('172.17.0.46', 'overcloud-controller-0'),
+    ('10.0.0.116', 'overcloud-controller-0.external'),
+    ('172.17.0.46', 'overcloud-controller-0.internalapi'),
+    ('172.18.0.185', 'overcloud-controller-0.storage'),
+    ('172.19.0.107', 'overcloud-controller-0.storagemgmt'),
+    ('172.16.0.72', 'overcloud-controller-0.tenant'),
+    ('192.168.24.122', 'overcloud-controller-0.ctlplane'),
+    ('172.17.0.110', 'overcloud-novacompute-0'),
+    ('172.17.0.110', 'overcloud-novacompute-0.internalapi'),
+    ('172.18.0.243', 'overcloud-novacompute-0.storage'),
+    ('172.16.0.195', 'overcloud-novacompute-0.tenant'),
+    ('192.168.24.128', 'overcloud-novacompute-0.ctlplane')])
+def test_reverse_dns(host, ip, name):
+    reverse = ipaddress.ip_address(ip).reverse_pointer
+    record, zone = reverse.split('.', 1)
+    result = host.check_output(
+        'ipa dnsrecord-find {} --name={}'.format(
+            zone, record))
+    assert 'record: {}'.format(name) in result
diff --git a/tripleo_ipa/roles/tripleo_ipa_dns/defaults/main.yml b/tripleo_ipa/roles/tripleo_ipa_dns/defaults/main.yml
new file mode 100644
index 0000000..a24b7b9
--- /dev/null
+++ b/tripleo_ipa/roles/tripleo_ipa_dns/defaults/main.yml
@@ -0,0 +1,23 @@
+---
+# Copyright 2020 Red Hat, Inc.
+# All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+
+# All variables intended for modification should place placed in this file.
+
+# All variables within this role should have a prefix of "tripleo_ipa"
+
+tripleo_ipa_ptr_zone_split_ipv4: 1
+tripleo_ipa_ptr_zone_split_ipv6: 1
diff --git a/tripleo_ipa/roles/tripleo_ipa_dns/meta/main.yml b/tripleo_ipa/roles/tripleo_ipa_dns/meta/main.yml
new file mode 100644
index 0000000..53c0186
--- /dev/null
+++ b/tripleo_ipa/roles/tripleo_ipa_dns/meta/main.yml
@@ -0,0 +1,44 @@
+---
+# Copyright 2020 Red Hat, Inc.
+# All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+
+galaxy_info:
+  author: OpenStack
+  description: TripleO OpenStack Role -- tripleo_ipa_dns
+  company: Red Hat
+  license: Apache-2.0
+  min_ansible_version: 2.7
+  #
+  # Provide a list of supported platforms, and for each platform a list of versions.
+  # If you don't wish to enumerate all versions for a particular platform, use 'all'.
+  # To view available platforms and versions (or releases), visit:
+  # https://galaxy.ansible.com/api/v1/platforms/
+  #
+  platforms:
+    - name: Fedora
+      versions:
+        - 28
+    - name: CentOS
+      versions:
+        - 7
+
+  galaxy_tags:
+    - tripleo
+
+
+# List your role dependencies here, one per line. Be sure to remove the '[]' above,
+# if you add dependencies to this list.
+dependencies: []
diff --git a/tripleo_ipa/roles/tripleo_ipa_dns/tasks/dns.yaml b/tripleo_ipa/roles/tripleo_ipa_dns/tasks/dns.yaml
new file mode 100644
index 0000000..b5f0987
--- /dev/null
+++ b/tripleo_ipa/roles/tripleo_ipa_dns/tasks/dns.yaml
@@ -0,0 +1,67 @@
+---
+# Copyright 2020 Red Hat, Inc.
+# All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+
+- name: set record_value and record_name
+  set_fact:
+    record_value: "{{ item.split()[0] }}"
+    record_name: "{{ item.split()[1]|regex_replace('(.*).' + cloud_domain + '$', '\\1') }}"
+
+- name: set record type
+  set_fact:
+    record_type: "{{ 'A' if record_value| ipv4 else 'AAAA' }}"
+
+- name: get reverse record data
+  set_fact:
+    reverse_addr: "{{ record_value | ipaddr('revdns') }}"
+
+- name: set reverse record entries for ipv4
+  set_fact:
+    reverse_record_zone: "{{ reverse_addr.split('.', tripleo_ipa_ptr_zone_split_ipv4)[-1] }}"
+    reverse_record_name: "{{ '.'.join(reverse_addr.split('.', tripleo_ipa_ptr_zone_split_ipv4)[:-1]) }}"
+  when: record_type == 'A'
+
+- name: set reverse record entries for ipv6
+  set_fact:
+    reverse_record_zone: "{{ reverse_addr.split('.', tripleo_ipa_ptr_zone_split_ipv6)[-1] }}"
+    reverse_record_name: "{{ '.'.join(reverse_addr.split('.', tripleo_ipa_ptr_zone_split_ipv6)[:-1]) }}"
+  when: record_type == 'AAAA'
+
+- name: add forward dns record
+  ipa_dnsrecord:
+    zone_name: "{{ cloud_domain }}"
+    record_name: "{{ record_name }}"
+    record_type: "{{ record_type }}"
+    record_value: "{{ record_value }}"
+
+- name: add reverse record dns zone
+  ipa_dnszone:
+    zone_name: "{{ reverse_record_zone }}"
+  register: reverse_zone_result
+  failed_when:
+    - "'zone' not in reverse_zone_result"
+    - "'already exists in DNS' not in reverse_zone_result.msg"
+
+- name: add reverse dns record
+  ipa_dnsrecord:
+    zone_name: "{{ reverse_record_zone }}"
+    record_name: "{{ reverse_record_name }}"
+    record_value: "{{ record_name }}.{{ cloud_domain }}."
+    record_type: "PTR"
+  register: reverse_record_result
+  failed_when:
+    - "'record' not in reverse_record_result"
+    - "'DNS zone not found' not in reverse_record_result.msg"
diff --git a/tripleo_ipa/roles/tripleo_ipa_dns/tasks/main.yml b/tripleo_ipa/roles/tripleo_ipa_dns/tasks/main.yml
new file mode 100644
index 0000000..6250c0a
--- /dev/null
+++ b/tripleo_ipa/roles/tripleo_ipa_dns/tasks/main.yml
@@ -0,0 +1,36 @@
+---
+# Copyright 2020 Red Hat, Inc.
+# All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+#
+# This role adds a host entries to FreeIPA, as defined in the host_entry variable.
+#
+# The following variables are required:
+# - cloud_domain (Base domain, eg. example.com)
+# - host_entry   (host entries string, in a format similar to /etc/hosts)
+
+
+- name: split host entries
+  set_fact:
+    hosts_entries_list: "{{ hosts_entry.splitlines() }}"
+
+- name: add cloud_domain dns zone
+  ipa_dnszone:
+    zone_name: "{{ cloud_domain }}"
+
+- name: add dns records
+  include_tasks:
+    file: dns.yaml
+  loop: "{{ hosts_entries_list }}"
+  when: item != ''