diff --git a/ansible/group_vars/all/bifrost b/ansible/group_vars/all/bifrost
index 5b4e53aeabd68c6514de92b1c958a955a6e2f1ef..dcd1e4de645f4ec79018355c9da6c42c360ae17c 100644
--- a/ansible/group_vars/all/bifrost
+++ b/ansible/group_vars/all/bifrost
@@ -37,6 +37,7 @@ kolla_bifrost_enable_ipmitool_drivers: true
 
 # List of of inspector processing plugins.
 kolla_bifrost_inspector_processing_hooks:
+  - root_disk_selection
   - scheduler
   - validate_interfaces
   - ramdisk_error
@@ -55,7 +56,33 @@ kolla_bifrost_inspector_extra_kernel_options:
   - "ipa-collect-lldp=1"
 
 # List of introspection rules for Bifrost's Ironic Inspector service.
-kolla_bifrost_inspector_rules: []
+kolla_bifrost_inspector_rules:
+  - "{{ inspector_rule_ipmi_credentials }}"
+  - "{{ inspector_rule_deploy_kernel }}"
+  - "{{ inspector_rule_deploy_ramdisk }}"
+  - "{{ inspector_rule_root_hint_serial }}"
+  - "{{ inspector_rule_save_data }}"
+
+# Ironic inspector IPMI username to set.
+kolla_bifrost_inspector_ipmi_username:
+
+# Ironic inspector IPMI password to set.
+kolla_bifrost_inspector_ipmi_password:
+
+# Ironic inspector deployment kernel location.
+kolla_bifrost_inspector_deploy_kernel: "http://{{ provision_oc_net_name | net_ip }}:8080/ipa.vmlinuz"
+
+# Ironic inspector deployment ramdisk location.
+kolla_bifrost_inspector_deploy_ramdisk: "http://{{ provision_oc_net_name | net_ip }}:8080/ipa.initramfs"
+
+###############################################################################
+# Ironic Python Agent (IPA) configuration.
+
+# URL of Ironic Python Agent (IPA) kernel image.
+kolla_bifrost_ipa_kernel_upstream_url: "https://tarballs.openstack.org/ironic-python-agent/coreos/files/coreos_production_pxe-stable-ocata.vmlinuz"
+
+# URL of Ironic Python Agent (IPA) ramdisk image.
+kolla_bifrost_ipa_ramdisk_upstream_url: "https://tarballs.openstack.org/ironic-python-agent/coreos/files/coreos_production_pxe_image-oem-stable-ocata.cpio.gz"
 
 ###############################################################################
 # Inventory configuration.
diff --git a/ansible/group_vars/all/inspector b/ansible/group_vars/all/inspector
new file mode 100644
index 0000000000000000000000000000000000000000..ed543299b166cd336e2a8b22adaded05ac56f65f
--- /dev/null
+++ b/ansible/group_vars/all/inspector
@@ -0,0 +1,88 @@
+---
+###############################################################################
+# Ironic inspector configuration.
+
+# Ironic inspector IPMI username to set.
+inspector_ipmi_username:
+
+# Ironic inspector IPMI password to set.
+inspector_ipmi_password:
+
+# Ironic inspector deployment kernel location.
+inspector_deploy_kernel:
+
+# Ironic inspector deployment ramdisk location.
+inspector_deploy_ramdisk:
+
+###############################################################################
+# Ironic inspector introspection rules configuration.
+
+# Inspector rules use python string formatting. To escape a curly bracket we
+# repeat it, but this also happens to be the Jinja2 variable start sequence.
+# These variables make escaping rules slightly less hairy.
+inspector_rule_escaped_left_curly: "{{ '{{' | replace('{{', '{{ \"{{\" }}') }}"
+inspector_rule_escaped_right_curly: "{{ '}}' | replace('}}', '{{ \"}}\" }}') }}"
+
+# Ironic inspector rule to set IPMI credentials.
+inspector_rule_ipmi_credentials:
+  description: "Set IPMI driver_info if no credentials"
+  conditions:
+    - field: "node://driver_info.ipmi_username"
+      op: "is-empty"
+    - field: "node://driver_info.ipmi_password"
+      op: "is-empty"
+  actions:
+    - action: "set-attribute"
+      path: "driver_info/ipmi_username"
+      value: "{{ inspector_ipmi_username }}"
+    - action: "set-attribute"
+      path: "driver_info/ipmi_password"
+      value: "{{ inspector_ipmi_password }}"
+
+# Ironic inspector rule to set deployment kernel.
+inspector_rule_deploy_kernel:
+  description: "Set deploy kernel"
+  conditions:
+    - field: "node://driver_info.deploy_kernel"
+      op: "is-empty"
+  actions:
+    - action: "set-attribute"
+      path: "driver_info/deploy_kernel"
+      value: "{{ inspector_deploy_kernel }}"
+
+# Ironic inspector rule to set deployment ramdisk.
+inspector_rule_deploy_ramdisk:
+  description: "Set deploy ramdisk"
+  conditions:
+    - field: "node://driver_info.deploy_ramdisk"
+      op: "is-empty"
+  actions:
+    - action: "set-attribute"
+      path: "driver_info/deploy_ramdisk"
+      value: "{{ inspector_deploy_ramdisk }}"
+
+# Ironic inspector rule to set serial root device hint.
+inspector_rule_root_hint_serial:
+  description: "Set serial root device hint"
+  conditions:
+    - field: "node://properties.root_device"
+      op: "is-empty"
+    - field: "data://root_disk.serial"
+      op: "is-empty"
+      invert: True
+  actions:
+    - action: "set-attribute"
+      path: "properties/root_device"
+      #value: "{{ '{{' | replace('{{', '{{ \"{{\" }}') }}\"serial\": \"{data[root_disk][serial]}\"{{ '}}' | replace('}}', '{{ \"}}\" }}') }}"
+      # We need to suppoly a JSON encoded object of root device hints, escaping
+      # the curly brackets.
+      value: "{{ inspector_rule_escaped_left_curly }}\"serial\": \"{data[root_disk][serial]}\"{{ inspector_rule_escaped_right_curly }}"
+
+# Ironic inspector rule to save introspection data to the node.
+inspector_rule_save_data:
+  description: "Save introspection data to Ironic node"
+  conditions: []
+  actions:
+    - action: "set-attribute"
+      path: "extra/introspection_data"
+      value: "{data}"
diff --git a/ansible/roles/ironic-inspector-rules/library/os_ironic_inspector_rule.py b/ansible/roles/ironic-inspector-rules/library/os_ironic_inspector_rule.py
index f5070410ca254319aa29357398a992e6244a8619..3d5f051887a022201893caff6ae64a581262e680 100644
--- a/ansible/roles/ironic-inspector-rules/library/os_ironic_inspector_rule.py
+++ b/ansible/roles/ironic-inspector-rules/library/os_ironic_inspector_rule.py
@@ -68,7 +68,8 @@ def _build_client(module):
     session = cloud.cloud_config.get_session()
     client = ironic_inspector_client.v1.ClientV1(
         inspector_url=module.params['inspector_url'],
-        session=session, region_name=module.params['region_name'])
+        session=session, region_name=module.params['region_name'],
+        api_version=ironic_inspector_client.v1.MAX_API_VERSION)
     return client
 
 
diff --git a/ansible/roles/kolla-bifrost/defaults/main.yml b/ansible/roles/kolla-bifrost/defaults/main.yml
index ece1323342ddc9d9c3ef5da954740c70ee6448a7..2ac0cc8555793c61f571e7a3fa58771a2be9c1a3 100644
--- a/ansible/roles/kolla-bifrost/defaults/main.yml
+++ b/ansible/roles/kolla-bifrost/defaults/main.yml
@@ -52,6 +52,12 @@ kolla_bifrost_inspector_port_addition:
 # List of extra kernel parameters for the inspector default PXE configuration.
 kolla_bifrost_inspector_extra_kernel_options:
 
+# URL of Ironic Python Agent (IPA) kernel image.
+kolla_bifrost_ipa_kernel_upstream_url:
+
+# URL of Ironic Python Agent (IPA) ramdisk image.
+kolla_bifrost_ipa_ramdisk_upstream_url:
+
 # Server inventory to be configured in {{ kolla_node_custom_config_path }}/bifrost/servers.yml.
 kolla_bifrost_servers: {}
 
diff --git a/ansible/roles/kolla-bifrost/templates/bifrost.yml.j2 b/ansible/roles/kolla-bifrost/templates/bifrost.yml.j2
index 7ff95c61c70fecc4ac1ad3b0e8f127649bcf537e..b870eab52a411ef1e8d4e9ab90a3724a068c5415 100644
--- a/ansible/roles/kolla-bifrost/templates/bifrost.yml.j2
+++ b/ansible/roles/kolla-bifrost/templates/bifrost.yml.j2
@@ -43,6 +43,16 @@ inspector_port_addition: "{{ kolla_bifrost_inspector_port_addition }}"
 inspector_extra_kernel_options: "{{ kolla_bifrost_inspector_extra_kernel_options | join(' ') }}"
 {% endif %}
 
+{% if kolla_bifrost_ipa_kernel_upstream_url %}
+# URL of Ironic Python Agent (IPA) kernel image.
+ipa_kernel_upstream_url: "{{ kolla_bifrost_ipa_kernel_upstream_url }}"
+{% endif %}
+
+{% if kolla_bifrost_ipa_ramdisk_upstream_url %}
+# URL of Ironic Python Agent (IPA) ramdisk image.
+ipa_ramdisk_upstream_url: "{{ kolla_bifrost_ipa_ramdisk_upstream_url }}"
+{% endif %}
+
 {% if kolla_bifrost_extra_globals %}
 ###############################################################################
 # Extra configuration
diff --git a/ansible/seed-introspection-rules.yml b/ansible/seed-introspection-rules.yml
index b577b3927bb5058feab8cdde07a4f4a6e1e31787..4d46b074df6ba9c263f9377cca5fd637e786ad92 100644
--- a/ansible/seed-introspection-rules.yml
+++ b/ansible/seed-introspection-rules.yml
@@ -9,3 +9,8 @@
       ironic_inspector_auth: {}
       ironic_inspector_url: "http://localhost:5050"
       ironic_inspector_rules: "{{ kolla_bifrost_inspector_rules }}"
+      # These variables may be referenced in the introspection rules.
+      inspector_ipmi_username: "{{ kolla_bifrost_inspector_ipmi_username }}"
+      inspector_ipmi_password: "{{ kolla_bifrost_inspector_ipmi_password }}"
+      inspector_deploy_kernel: "{{ kolla_bifrost_inspector_deploy_kernel }}"
+      inspector_deploy_ramdisk: "{{ kolla_bifrost_inspector_deploy_ramdisk }}"
diff --git a/etc/kayobe/bifrost.yml b/etc/kayobe/bifrost.yml
index d028e55c2f8a60e5b21c352d5d54fc85167bf924..9e2dd34b6f361d723367f7e26ed872cdaf37c1d5 100644
--- a/etc/kayobe/bifrost.yml
+++ b/etc/kayobe/bifrost.yml
@@ -44,6 +44,18 @@
 # List of introspection rules for Bifrost's Ironic Inspector service.
 #kolla_bifrost_inspector_rules:
 
+# Ironic inspector IPMI username to set.
+#kolla_bifrost_inspector_ipmi_username:
+
+# Ironic inspector IPMI password to set.
+#kolla_bifrost_inspector_ipmi_password:
+
+# Ironic inspector deployment kernel location.
+#kolla_bifrost_inspector_deploy_kernel:
+
+# Ironic inspector deployment ramdisk location.
+#kolla_bifrost_inspector_deploy_ramdisk:
+
 ###############################################################################
 # Inventory configuration.