diff --git a/ansible/group_vars/all/controllers b/ansible/group_vars/all/controllers
index eda317abb1b2a0b4832e8feddfa7399c52a84ea6..7fe0ade8f03b58612317873dbe222e1b12d35363 100644
--- a/ansible/group_vars/all/controllers
+++ b/ansible/group_vars/all/controllers
@@ -6,6 +6,36 @@
 # to setup the Kayobe user account.
 controller_bootstrap_user: "{{ lookup('env', 'USER') }}"
 
+###############################################################################
+# Controller node BIOS configuration.
+
+# Dict of controller BIOS options. Format is same as that used by stackhpc.drac
+# role.
+controller_bios_config: "{{ controller_bios_config_default | combine(controller_bios_config_extra) }}"
+
+# Dict of default controller BIOS options. Format is same as that used by
+# stackhpc.drac role.
+controller_bios_config_default: {}
+
+# Dict of additional controller BIOS options. Format is same as that used by
+# stackhpc.drac role.
+controller_bios_config_extra: {}
+
+###############################################################################
+# Controller node RAID configuration.
+
+# List of controller RAID volumes. Format is same as that used by stackhpc.drac
+# role.
+controller_raid_config: "{{ controller_raid_config_default + controller_raid_config_extra }}"
+
+# List of default controller RAID volumes. Format is same as that used by
+# stackhpc.drac role.
+controller_raid_config_default: []
+
+# List of additional controller RAID volumes. Format is same as that used by
+# stackhpc.drac role.
+controller_raid_config_extra: []
+
 ###############################################################################
 # Controller node LVM configuration.
 
diff --git a/ansible/group_vars/all/inspector b/ansible/group_vars/all/inspector
index 1f23e034d219ec424fed4ce2aec25673b6baeaa8..a36a4ce91c6b834e9dc13c28ad739aab2d2de433 100644
--- a/ansible/group_vars/all/inspector
+++ b/ansible/group_vars/all/inspector
@@ -19,13 +19,11 @@ inspector_manage_firewall: False
 
 # List of of default inspector processing plugins.
 inspector_processing_hooks_default:
-  - root_disk_selection
+  - ramdisk_error
   - scheduler
   - validate_interfaces
-  - ramdisk_error
   - capabilities
   - pci_devices
-  - raid_device
   - local_link_connection
   - lldp_basic
 
diff --git a/ansible/overcloud-bios-raid.yml b/ansible/overcloud-bios-raid.yml
new file mode 100644
index 0000000000000000000000000000000000000000..bb04c4a679c805a16c9b3df382a00d096c774f67
--- /dev/null
+++ b/ansible/overcloud-bios-raid.yml
@@ -0,0 +1,90 @@
+---
+- name: Group controller hosts by their BMC type
+  hosts: controllers
+  gather_facts: no
+  tasks:
+    - name: Group controller hosts by their BMC type
+      group_by:
+        key: "controllers_with_bmcs_of_type_{{ bmc_type | default('unknown') }}"
+
+- name: Check whether any changes to controller BIOS and RAID configuration are required
+  hosts: controllers_with_bmcs_of_type_idrac
+  gather_facts: no
+  vars:
+    # Set this to False to avoid rebooting the nodes after configuration.
+    drac_reboot: True
+  roles:
+    - role: stackhpc.drac
+      drac_address: "{{ ipmi_address }}"
+      drac_username: "{{ ipmi_username }}"
+      drac_password: "{{ ipmi_password }}"
+      drac_bios_config: "{{ controller_bios_config }}"
+      drac_raid_config: "{{ controller_raid_config }}"
+      drac_check_mode: True
+  tasks:
+    - name: Set a fact about whether the configuration changed
+      set_fact:
+        bios_or_raid_change: "{{ drac_result | changed }}"
+
+- name: Ensure that controller BIOS and RAID volumes are configured
+  hosts: controllers_with_bmcs_of_type_idrac
+  gather_facts: no
+  vars:
+    # Set this to False to avoid rebooting the nodes after configuration.
+    drac_reboot: True
+  pre_tasks:
+    - name: Set the controller nodes' maintenance mode
+      command: >
+        docker exec bifrost_deploy
+        bash -c '. env-vars &&
+        export OS_URL=$IRONIC_URL &&
+        export OS_TOKEN=$OS_AUTH_TOKEN &&
+        export BIFROST_INVENTORY_SOURCE=ironic &&
+        ansible baremetal
+        --connection local
+        --inventory /etc/bifrost/inventory/
+        -e @/etc/bifrost/bifrost.yml
+        -e @/etc/bifrost/dib.yml
+        --limit {{ inventory_hostname }}
+        -m command
+        -a "openstack baremetal node maintenance set {% raw %}{{ inventory_hostname }}{% endraw %} --reason BIOS-RAID"'
+      register: show_result
+      # We use this convoluted construct to work around Ansible's limitations
+      # in evaluation of the delegate_to keyword.
+      delegate_to: "{{ item }}"
+      with_items:
+        - "{{ hostvars[groups['seed'][0]].ansible_host }}"
+      when: "{{ bios_or_raid_change | bool }}"
+
+  roles:
+    - role: stackhpc.drac
+      drac_address: "{{ ipmi_address }}"
+      drac_username: "{{ ipmi_username }}"
+      drac_password: "{{ ipmi_password }}"
+      drac_bios_config: "{{ controller_bios_config }}"
+      drac_raid_config: "{{ controller_raid_config }}"
+      when: "{{ bios_or_raid_change | bool }}"
+
+  tasks:
+    - name: Unset the controller nodes' maintenance mode
+      command: >
+        docker exec bifrost_deploy
+        bash -c '. env-vars &&
+        export OS_URL=$IRONIC_URL &&
+        export OS_TOKEN=$OS_AUTH_TOKEN &&
+        export BIFROST_INVENTORY_SOURCE=ironic &&
+        ansible baremetal
+        --connection local
+        --inventory /etc/bifrost/inventory/
+        -e @/etc/bifrost/bifrost.yml
+        -e @/etc/bifrost/dib.yml
+        --limit {{ inventory_hostname }}
+        -m command
+        -a "openstack baremetal node maintenance unset {% raw %}{{ inventory_hostname }}{% endraw %}"'
+      register: show_result
+      # We use this convoluted construct to work around Ansible's limitations
+      # in evaluation of the delegate_to keyword.
+      delegate_to: "{{ item }}"
+      with_items:
+        - "{{ hostvars[groups['seed'][0]].ansible_host }}"
+      when: "{{ bios_or_raid_change | bool }}"
diff --git a/doc/source/usage.rst b/doc/source/usage.rst
index 3b7be37534f0305582b3617a02d9c09d3cd5c9fc..55d11578c37da89eeb1a57eff3c52819d5aef814 100644
--- a/doc/source/usage.rst
+++ b/doc/source/usage.rst
@@ -285,6 +285,20 @@ to add them to the Kayobe and bifrost Ansible inventories::
 
     (kayobe-venv) $ kayobe overcloud inventory discover
 
+BIOS and RAID Configuration
+---------------------------
+
+.. note::
+
+   BIOS and RAID configuration may require one or more power cycles of the
+   hardware to complete the operation.  These will be performed automatically.
+
+Configuration of BIOS settings and RAID volumes is currently performed out of
+band as a separate task from hardware provisioning.  To configure the BIOS and
+RAID::
+
+    (kayobe-venv) $ kayobe overcloud bios raid configure
+
 Provisioning
 ------------
 
diff --git a/etc/kayobe/controllers.yml b/etc/kayobe/controllers.yml
index 41641b40a4c230acffda304b99486f9a072146c8..e7cc2f646fd9898e1ed664de1e8c45638613266a 100644
--- a/etc/kayobe/controllers.yml
+++ b/etc/kayobe/controllers.yml
@@ -15,6 +15,36 @@
 # List of extra networks to which controller nodes are attached.
 #controller_extra_network_interfaces:
 
+###############################################################################
+# Controller node BIOS configuration.
+
+# Dict of controller BIOS options. Format is same as that used by stackhpc.drac
+# role.
+#controller_bios_config:
+
+# Dict of default controller BIOS options. Format is same as that used by
+# stackhpc.drac role.
+#controller_bios_config_default:
+
+# Dict of additional controller BIOS options. Format is same as that used by
+# stackhpc.drac role.
+#controller_bios_config_extra:
+
+###############################################################################
+# Controller node RAID configuration.
+
+# List of controller RAID volumes. Format is same as that used by stackhpc.drac
+# role.
+#controller_raid_config:
+
+# List of default controller RAID volumes. Format is same as that used by
+# stackhpc.drac role.
+#controller_raid_config_default:
+
+# List of additional controller RAID volumes. Format is same as that used by
+# stackhpc.drac role.
+#controller_raid_config_extra:
+
 ###############################################################################
 # Controller node LVM configuration.
 
diff --git a/kayobe/cli/commands.py b/kayobe/cli/commands.py
index a187ca4d8fa1b6bdf0b178984490724ae226153d..1444241a1492a10e8219764e217ccfbb703284b3 100644
--- a/kayobe/cli/commands.py
+++ b/kayobe/cli/commands.py
@@ -293,25 +293,22 @@ class OvercloudInventoryDiscover(KayobeAnsibleMixin, Command):
                                  tags="config")
 
 
-class OvercloudProvision(KayobeAnsibleMixin, Command):
-    """Provision the overcloud."""
+class OvercloudBIOSRAIDConfigure(KayobeAnsibleMixin, Command):
+    """Configure BIOS and RAID for the overcloud hosts."""
 
     def take_action(self, parsed_args):
-        self.app.LOG.debug("Provisioning overcloud")
-        self._configure_network(parsed_args)
-        self._configure_bios_and_raid(parsed_args)
-        self._deploy_servers(parsed_args)
+        self.app.LOG.debug("Configure overcloud BIOS and RAID")
+        playbooks = _build_playbook_list("overcloud-bios-raid")
+        self.run_kayobe_playbooks(parsed_args, playbooks)
 
-    def _configure_network(self, parsed_args):
-        self.app.LOG.debug("TODO: configure overcloud network")
 
-    def _configure_bios_and_raid(self, parsed_args):
-        self.app.LOG.debug("TODO: configure overcloud BIOS and RAID")
+class OvercloudProvision(KayobeAnsibleMixin, Command):
+    """Provision the overcloud."""
 
-    def _deploy_servers(self, parsed_args):
-        self.app.LOG.debug("Deploying overcloud servers via Bifrost")
-        self.run_kayobe_playbook(parsed_args,
-                                 "ansible/overcloud-provision.yml")
+    def take_action(self, parsed_args):
+        self.app.LOG.debug("Provisioning overcloud")
+        playbooks = _build_playbook_list("overcloud-provision")
+        self.run_kayobe_playbooks(parsed_args, playbooks)
 
 
 class OvercloudDeprovision(KayobeAnsibleMixin, Command):
@@ -319,8 +316,8 @@ class OvercloudDeprovision(KayobeAnsibleMixin, Command):
 
     def take_action(self, parsed_args):
         self.app.LOG.debug("Deprovisioning overcloud")
-        self.run_kayobe_playbook(parsed_args,
-                                 "ansible/overcloud-deprovision.yml")
+        playbooks = _build_playbook_list("overcloud-deprovision")
+        self.run_kayobe_playbooks(parsed_args, playbooks)
 
 
 class OvercloudHostConfigure(KollaAnsibleMixin, KayobeAnsibleMixin, Command):
diff --git a/setup.py b/setup.py
index 2ec816c122b8ecc7c469f3ab7eda714675ddd2a4..ffa0051a9b4624d04210d3f9de5756c053ebdcc8 100644
--- a/setup.py
+++ b/setup.py
@@ -40,6 +40,7 @@ setup(
             'control_host_bootstrap = kayobe.cli.commands:ControlHostBootstrap',
             'configuration_dump = kayobe.cli.commands:ConfigurationDump',
             'kolla_ansible_run = kayobe.cli.commands:KollaAnsibleRun',
+            'overcloud_bios_raid_configure = kayobe.cli.commands:OvercloudBIOSRAIDConfigure',
             'overcloud_container_image_build = kayobe.cli.commands:OvercloudContainerImageBuild',
             'overcloud_container_image_pull = kayobe.cli.commands:OvercloudContainerImagePull',
             'overcloud_deprovision = kayobe.cli.commands:OvercloudDeprovision',