diff --git a/ansible/baremetal-compute-introspection-data-save.yml b/ansible/baremetal-compute-introspection-data-save.yml
new file mode 100644
index 0000000000000000000000000000000000000000..4c63d10c8be8f3b34403bb7174009cd4e7c3f22c
--- /dev/null
+++ b/ansible/baremetal-compute-introspection-data-save.yml
@@ -0,0 +1,68 @@
+---
+- name: Ensure dependencies are installed
+  hosts: controllers[0]
+  gather_facts: true
+  vars:
+    venv: "{{ virtualenv_path }}/openstack-cli"
+  pre_tasks:
+    - name: Set up openstack cli virtualenv
+      pip:
+        virtualenv: "{{ venv }}"
+        virtualenv_command: python3 -m venv
+        name:
+          - python-openstackclient
+          - python-ironic-inspector-client
+        state: latest
+        extra_args: "{% if pip_upper_constraints_file %}-c {{ pip_upper_constraints_file }}{% endif %}"
+
+- name: Ensure the baremetal compute nodes' hardware introspection data is saved
+  hosts: baremetal-compute
+  gather_facts: False
+  vars:
+    venv: "{{ virtualenv_path }}/openstack-cli"
+    controller_host: "{{ groups['controllers'][0] }}"
+    output_dir: "{{ lookup('env', 'PWD') }}/baremetal-compute-introspection-data"
+    output_format: json
+  tasks:
+    - name: Query baremetal compute nodes' hardware introspection data
+      command: >
+        {{ venv }}/bin/openstack baremetal introspection data save {{ inventory_hostname }}
+      register: save_result
+      changed_when: False
+      # Ignore errors, log a message later.
+      failed_when: False
+      delegate_to: "{{ controller_host }}"
+      environment: "{{ openstack_auth_env }}"
+      vars:
+        # NOTE: Without this, the controller's ansible_host variable will not
+        # be respected when using delegate_to.
+        ansible_host: "{{ hostvars[controller_host].ansible_host | default(controller_host) }}"
+
+    - name: Ensure introspection data output directory exists
+      local_action:
+        module: file
+        path: "{{ output_dir }}"
+        state: directory
+
+    - name: Ensure introspection data is saved locally
+      local_action:
+        module: copy
+        content: "{{ introspection_data_map[output_format | lower] }}"
+        dest: "{{ output_dir }}/{{ inventory_hostname }}.{{ output_format | lower }}"
+      when: save_result.rc == 0
+      vars:
+        introspection_data: "{{ save_result.stdout_lines | join('\n') | from_json }}"
+        introspection_data_json: "{{ introspection_data | to_nice_json(indent=4) }}"
+        introspection_data_yaml: "{{ introspection_data | to_nice_yaml }}"
+        introspection_data_map:
+          json: "{{ introspection_data_json }}"
+          yaml: "{{ introspection_data_yaml }}"
+
+    - name: Log when introspection data could not be queried
+      debug:
+        msg: >
+          Could not query hardware introspection data for
+          {{ inventory_hostname }}.
+          Stdout: {{ save_result.stdout }}.
+          Stderr: {{ save_result.stderr }}.
+      when: save_result.rc != 0
diff --git a/doc/source/administration/bare-metal.rst b/doc/source/administration/bare-metal.rst
index 2d80694580e356c7b88723932c10d6af636696a8..fe8490064121af30899e09de749d0465ec073cf7 100644
--- a/doc/source/administration/bare-metal.rst
+++ b/doc/source/administration/bare-metal.rst
@@ -43,6 +43,18 @@ compute nodes::
 
     (kayobe) $ kayobe baremetal compute inspect
 
+Saving Hardware Introspection Data
+----------------------------------
+
+Introspection data will be stored in the nginx service within the
+``inspection_store`` container. This data may be saved to the control host::
+
+    (kayobe) $ kayobe baremetal compute introspection data save
+
+``--output-dir`` may be used to specify the directory in which introspection
+data files will be saved. ``--output-format`` may be used to set the format of
+the files.
+
 Rename
 ------
 
diff --git a/kayobe/cli/commands.py b/kayobe/cli/commands.py
index cf2deaab865b13b649a452315ae9b44b3bec45d1..24c412722d8fc63690d5a52c6316fae5fccefd75 100644
--- a/kayobe/cli/commands.py
+++ b/kayobe/cli/commands.py
@@ -1869,6 +1869,41 @@ class BaremetalComputeInspect(KayobeAnsibleMixin, VaultMixin, Command):
         self.run_kayobe_playbooks(parsed_args, playbooks)
 
 
+class BaremetalComputeIntrospectionDataSave(KayobeAnsibleMixin, VaultMixin, Command):
+    """Save hardware introspection data for the baremetal compute nodes.
+
+    Save hardware introspection data from the overcloud's ironic inspector
+    service to the Ansible control host.
+    """
+
+    def get_parser(self, prog_name):
+        parser = super(BaremetalComputeIntrospectionDataSave, self).get_parser(
+            prog_name)
+        group = parser.add_argument_group("Introspection data")
+        # Defaults for these are applied in the playbook.
+        group.add_argument("--output-dir", type=str,
+                           help="Path to directory in which to save "
+                                "introspection data. Default: "
+                                "$PWD/baremetal-compute-introspection-data")
+        group.add_argument("--output-format", type=str,
+                           help="Format in which to save output data. One of "
+                                "JSON or YAML. Default: JSON",
+                           choices=["JSON", "YAML"])
+        return parser
+
+    def take_action(self, parsed_args):
+        self.app.LOG.debug("Saving introspection data")
+        extra_vars = {}
+        if parsed_args.output_dir:
+            extra_vars['output_dir'] = parsed_args.output_dir
+        if parsed_args.output_format:
+            extra_vars['output_format'] = parsed_args.output_format
+        playbooks = _build_playbook_list(
+            "baremetal-compute-introspection-data-save")
+        self.run_kayobe_playbooks(parsed_args, playbooks,
+                                  extra_vars=extra_vars)
+
+
 class BaremetalComputeManage(KayobeAnsibleMixin, VaultMixin, Command):
     """Put baremetal compute nodes into the manageable provision state."""
 
diff --git a/releasenotes/notes/add-baremetal-compute-introspection-data-save-55d25300e5fa4219.yaml b/releasenotes/notes/add-baremetal-compute-introspection-data-save-55d25300e5fa4219.yaml
new file mode 100644
index 0000000000000000000000000000000000000000..a01b9c10d0fa884d61b9f5742370654766dfbe13
--- /dev/null
+++ b/releasenotes/notes/add-baremetal-compute-introspection-data-save-55d25300e5fa4219.yaml
@@ -0,0 +1,6 @@
+---
+features:
+  - |
+    Adds the command ``kayobe baremetal introspection data save`` to save the
+    hardware introspection data gathered by ``kayobe baremetal compute
+    inspect``.
diff --git a/setup.cfg b/setup.cfg
index d1d7b3bb2a2ed5e981f6259bc9a6e0a748413580..e1a3ca85add2e2450fe1fd93e65050fb44218635 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -40,6 +40,7 @@ console_scripts=
 
 kayobe.cli=
     baremetal_compute_inspect = kayobe.cli.commands:BaremetalComputeInspect
+    baremetal_compute_introspection_data_save = kayobe.cli.commands:BaremetalComputeIntrospectionDataSave
     baremetal_compute_manage = kayobe.cli.commands:BaremetalComputeManage
     baremetal_compute_provide = kayobe.cli.commands:BaremetalComputeProvide
     baremetal_compute_rename = kayobe.cli.commands:BaremetalComputeRename
@@ -107,6 +108,8 @@ kayobe.cli=
 
 kayobe.cli.baremetal_compute_inspect =
     hooks = kayobe.cli.commands:HookDispatcher
+kayobe.cli.baremetal_compute_introspection_data_save =
+    hooks = kayobe.cli.commands:HookDispatcher
 kayobe.cli.baremetal_compute_manage =
     hooks = kayobe.cli.commands:HookDispatcher
 kayobe.cli.baremetal_compute_provide =