Skip to main content

What are provisioners?

Provisioners handle post-allocation machine setup - installing packages, deploying guest plugins, running configuration scripts, and taking snapshots. After a provider allocates a machine, provisioning steps prepare it for analysis. Provisioners are transport-agnostic: each provisioner manages its own connectivity to the machine (WinRM, SSH, etc.). Like providers, provisioners register themselves at compile time using the inventory crate and can be developed independently.

Running provisioning

Provisioning is driven through the malbox CLI. Each invocation runs a single provisioning step against a machine via the daemon’s HTTP API (POST /v1/machines/{id}/provision).
Machines can be referenced by name or numeric ID. If omitted, Malbox shows an interactive fuzzy selector. You can view provisioning history with:

What happens during a provision step

  1. The machine transitions to provisioning state (prevents concurrent provisioning)
  2. The VM is reverted to the --revert-to snapshot (defaults to "base") for a clean starting state
  3. The VM is started and the daemon waits for the management service (WinRM on port 5986 for Windows, SSH on port 22 for Linux) to become reachable
  4. The provisioner runs (e.g. Ansible executes the playbook)
  5. The VM is stopped
  6. If --snapshot was specified, a provider-level snapshot is taken and recorded in the database
  7. The machine transitions back to ready

Multi-step provisioning

To build up a machine in layers, run multiple provision commands, each snapshotting after completion. Later steps can use --revert-to to start from a previous step’s snapshot:

Architecture

The provisioner system follows the same pattern as providers:
  • Provisioner trait - defines the contract all provisioners must implement: an async provision(context) method and a name() method.
  • ProvisionerMetadata - registered at compile time via the inventory crate. Contains the provisioner name and a factory function.
  • ProvisionContext - passed to the provisioner with the machine’s network endpoint (IP, platform) and the step’s TOML config.
  • ProvisionResult - returned by the provisioner with a status (Success or Failed) and optional output text.
Provisioners are looked up by name at runtime using create_provisioner(name, config).

Supported provisioners

Ansible

The built-in Ansible provisioner runs Ansible playbooks against allocated machines. It auto-generates dynamic inventory when needed and injects plugin metadata as extra variables.

Configuration

Dynamic inventory

When inventory = "dynamic" (the default), the provisioner generates a temporary inventory file containing the machine’s IP address. For Windows machines, it automatically adds WinRM connection variables:
For Linux machines, only the IP is added (SSH is the default connection).

Guest plugin injection

When plugins are specified (either via --plugins on the CLI or guest_plugins in the pipeline), the Ansible provisioner resolves each plugin’s plugin.toml manifest. It then injects a guest_plugins extra variable as a JSON array. Each entry includes:
The paths are resolved from the plugin’s [runtime.paths] in plugin.toml, so playbooks can create the correct directories on the guest without hardcoding paths.

Bundled playbooks

Malbox ships a modular provision.yml playbook for Windows that composes two steps:
  1. Auto-login configuration - configures the sandbox user for interactive sample execution
  2. Guest plugin deployment - copies plugin binaries and manifests, creates runtime directories, sets up scheduled tasks to start plugins on login
Each sub-step can be skipped via extra variables (skip_plugins, skip_autologin):

Next steps

  • Read about Providers for machine allocation
  • Read about Packer for building machine images