diff --git a/ansible/roles/ironic/tasks/bootstrap.yml b/ansible/roles/ironic/tasks/bootstrap.yml
index 90e149feb0..5ad3ebf5e7 100644
--- a/ansible/roles/ironic/tasks/bootstrap.yml
+++ b/ansible/roles/ironic/tasks/bootstrap.yml
@@ -10,12 +10,13 @@
       name: "{{ item.database_name }}"
   register: database
   run_once: True
-  delegate_to: "{{ item.delegate_to }}"
+  delegate_to: "{{ groups[item.group][0] }}"
   with_items:
     - database_name: "{{ ironic_database_name }}"
-      delegate_to: "{{ groups['ironic-api'][0] }}"
+      group: "ironic-api"
     - database_name: "{{ ironic_inspector_database_name }}"
-      delegate_to: "{{ groups['ironic-inspector'][0] }}"
+      group: "ironic-inspector"
+  when: inventory_hostname in groups[item.group]
 
 - name: Creating Ironic database user and setting permissions
   kolla_toolbox:
@@ -31,16 +32,17 @@
       priv: "{{ item.database_name }}.*:ALL"
       append_privs: "yes"
   run_once: True
-  delegate_to: "{{ item.delegate_to }}"
+  delegate_to: "{{ groups[item.group][0] }}"
   with_items:
     - database_name: "{{ ironic_database_name }}"
       database_user: "{{ ironic_database_user }}"
       database_password: "{{ ironic_database_password }}"
-      delegate_to: "{{ groups['ironic-api'][0] }}"
+      group: "ironic-api"
     - database_name: "{{ ironic_inspector_database_name }}"
       database_user: "{{ ironic_inspector_database_user }}"
       database_password: "{{ ironic_inspector_database_password }}"
-      delegate_to: "{{ groups['ironic-inspector'][0] }}"
+      group: "ironic-inspector"
+  when: inventory_hostname in groups[item.group]
 
 - include: bootstrap_service.yml
   when: database.changed
@@ -62,4 +64,4 @@
       - "{{ node_config_directory }}/ironic-pxe/:{{ container_config_directory }}/:ro"
       - "/etc/localtime:/etc/localtime:ro"
       - "ironic_pxe:/tftpboot/"
-  when: "{{ inventory_hostname in groups['ironic-pxe'] }}"
+  when: inventory_hostname in groups['ironic-pxe']
diff --git a/ansible/roles/ironic/tasks/bootstrap_service.yml b/ansible/roles/ironic/tasks/bootstrap_service.yml
index b25da1de34..1920984755 100644
--- a/ansible/roles/ironic/tasks/bootstrap_service.yml
+++ b/ansible/roles/ironic/tasks/bootstrap_service.yml
@@ -17,6 +17,7 @@
       - "/etc/localtime:/etc/localtime:ro"
   run_once: True
   delegate_to: "{{ groups['ironic-api'][0] }}"
+  when: inventory_hostname in groups['ironic-api']
 
 - name: Running Ironic Inspector bootstrap container
   kolla_docker:
@@ -36,3 +37,4 @@
       - "/etc/localtime:/etc/localtime:ro"
   run_once: True
   delegate_to: "{{ groups['ironic-inspector'][0] }}"
+  when: inventory_hostname in groups['ironic-inspector']
diff --git a/ansible/roles/ironic/tasks/config.yml b/ansible/roles/ironic/tasks/config.yml
index 9907fd5a50..417a9b42fb 100644
--- a/ansible/roles/ironic/tasks/config.yml
+++ b/ansible/roles/ironic/tasks/config.yml
@@ -52,6 +52,7 @@
       - "{{ node_custom_config }}/ironic-inspector/inspector.conf"
       - "{{ node_custom_config }}/ironic-inspector/{{ inventory_hostname }}/inspector.conf"
     dest: "{{ node_config_directory }}/ironic-inspector/inspector.conf"
+  when: inventory_hostname in groups['ironic-inspector']
 
 - name: Copying over dnsmasq.conf
   template:
@@ -71,6 +72,10 @@
     - "{{ node_custom_config }}/ironic/pxelinux.default"
     - "{{ node_custom_config }}/ironic/{{ inventory_hostname }}/pxelinux.default"
     - "pxelinux.default.j2"
+  when:
+    # Only required when Ironic inspector is in use.
+    - groups['ironic-inspector'] | length > 0
+    - inventory_hostname in groups['ironic-pxe']
 
 - name: Copying ironic-agent kernel and initramfs
   copy:
@@ -79,6 +84,10 @@
   with_items:
     - "ironic-agent.kernel"
     - "ironic-agent.initramfs"
+  when:
+    # Only required when Ironic inspector is in use.
+    - groups['ironic-inspector'] | length > 0
+    - inventory_hostname in groups['ironic-pxe']
 
 - name: Check if policies shall be overwritten
   local_action: stat path="{{ node_custom_config }}/ironic/policy.json"
diff --git a/ansible/roles/ironic/tasks/precheck.yml b/ansible/roles/ironic/tasks/precheck.yml
index 5403e918af..5456ea6d30 100644
--- a/ansible/roles/ironic/tasks/precheck.yml
+++ b/ansible/roles/ironic/tasks/precheck.yml
@@ -34,6 +34,8 @@
   register: result
   failed_when: not result.stat.exists
   when:
+    # Only required when Ironic inspector is in use.
+    - groups['ironic-inspector'] | length > 0
     - inventory_hostname in groups['ironic-pxe']
   with_items:
     - "ironic-agent.kernel"
diff --git a/ansible/roles/ironic/templates/ironic-pxe.json.j2 b/ansible/roles/ironic/templates/ironic-pxe.json.j2
index 7e07a14af5..b56286fb0c 100644
--- a/ansible/roles/ironic/templates/ironic-pxe.json.j2
+++ b/ansible/roles/ironic/templates/ironic-pxe.json.j2
@@ -1,6 +1,7 @@
 {
     "command": "/usr/sbin/in.tftpd --verbose --foreground --user root --address 0.0.0.0:69 --map-file /map-file /tftpboot",
     "config_files": [
+{% if groups['ironic-inspector'] | length > 0 %}
         {
             "source": "{{ container_config_directory }}/ironic-agent.kernel",
             "dest": "/tftpboot/ironic-agent.kernel",
@@ -19,6 +20,7 @@
             "owner": "root",
             "perm": "0644"
         }
+{% endif %}
     ],
     "permissions": [
         {
diff --git a/tools/validate-all-file.py b/tools/validate-all-file.py
index b40df3e6f3..c1555466e5 100755
--- a/tools/validate-all-file.py
+++ b/tools/validate-all-file.py
@@ -74,6 +74,10 @@ def check_json_j2():
     def hostvars():
         return collections.defaultdict(hostvars)
 
+    # Mock Ansible groups variable, which is a dict of lists.
+    def groups():
+        return collections.defaultdict(list)
+
     def validate_json_j2(root, filename):
         env = jinja2.Environment(  # nosec: not used to render HTML
             loader=jinja2.FileSystemLoader(root))
@@ -82,6 +86,7 @@ def check_json_j2():
         # Mock ansible variables.
         context = {
             'hostvars': hostvars(),
+            'groups': groups(),
             'cluster_interface': 'cluster_interface',
             'storage_interface': 'storage_interface',
             'inventory_hostname': 'hostname'