
A practical, battle-tested guide to installing the Bindplane CLI, managing collector fleets declaratively, and automating bulk fleet migrations across Linux, macOS, and Windows.
When you are managing a handful of OpenTelemetry collectors, clicking through a web UI to tweak sources or reassign configurations feels effortless. When that number grows to hundreds or thousands of collectors across Windows domain controllers, Linux application servers, and Kubernetes nodes, point-and-click administration quickly becomes a bottleneck.
That is where the Bindplane CLI (bindplane) and Fleets shine. Instead of treating each collector as an isolated pet, Fleets let you group agents by label selectors so they automatically inherit a standardized telemetry pipeline—while the CLI gives you scriptable, GitOps-friendly control from your terminal or CI/CD runner.
In this guide, we will walk through:
- Installing and configuring the Bindplane CLI across Linux, macOS, and Windows (including a subtle .bindplane directory gotcha on Windows).
- How Fleets work under the hood — including configuration priority, fallback behavior, and declarative YAML creation.
- A complete, real-world production use case: migrating a specific list of 50+ agents to a new Fleet in bulk using tested Bash and PowerShell automation scripts — plus how to avoid a subtle CLI exit-code trap.
Part 1: Installing and Configuring the Bindplane CLI
The bindplane client binary lets you remotely inspect collectors, manage labels, apply YAML configurations, and orchestrate phased rollouts against either Bindplane Cloud or a Self-Hosted Enterprise server.
Step 1: Install the Bindplane CLI
For Linux (amd64): Run the following commands to download the latest release archive, install the binary into /usr/local/bin, and create the required ~/.bindplane/ directory used by the CLI's local logger:
mkdir -p ~/bindplane
curl -L -o ~/bindplane/bindplane.zip https://storage.googleapis.com/bindplane-op-releases/bindplane/latest/bindplane-ee-linux-amd64.zip
unzip -o ~/bindplane/bindplane.zip -d ~/bindplane/
sudo mv ~/bindplane/bindplane /usr/local/bin/bindplane
mkdir -p ~/.bindplane/
For macOS (Apple Silicon arm64 & Intel amd64): Automatically detect your Mac architecture and install the binary:
mkdir -p ~/bindplane
ARCH=$(uname -m | sed 's/x86_64/amd64/')
curl -L -o ~/bindplane/bindplane.zip "https://storage.googleapis.com/bindplane-op-releases/bindplane/latest/bindplane-ee-darwin-$
New-Item -Path $installPath -ItemType Directory .zip"
unzip -o ~/bindplane/bindplane.zip -d ~/bindplane/
sudo mv ~/bindplane/bindplane /usr/local/bin/bindplane
mkdir -p ~/.bindplane/
For Windows (amd64): Open PowerShell as Administrator and run the script below. Note that we explicitly create $env:USERPROFILE\.bindplane—without this folder, the CLI logger will fail to initialize on its first run:
$installPath = "$env:ProgramFiles\bindplane-cli"
if (-not (Test-Path -Path $installPath))
New-Item -Path $configPath -ItemType Directory
$configPath = Join-Path -Path $env:USERPROFILE -ChildPath ".bindplane"
if (-not (Test-Path -Path $configPath))
New-Item -Path $ConfigDir -ItemType Directory
$currentMachinePath = [System.Environment]::GetEnvironmentVariable("Path", "Machine")
if ($currentMachinePath -notlike "*$installPath*") Out-Null
$downloadPath = Join-Path -Path "$env:USERPROFILE\Downloads" -ChildPath "bindplane-ee-windows-amd64.zip"
Invoke-WebRequest -Uri "https://storage.googleapis.com/bindplane-op-releases/bindplane/latest/bindplane-ee-windows-amd64.zip" -OutFile $downloadPath
Expand-Archive -Path $downloadPath -DestinationPath $installPath -Force
Step 2: Generate a Project API Key
To authenticate the CLI with Bindplane Cloud or Bindplane Enterprise Self-Hosted, generate an API key from the Web UI:
- Log into your Bindplane Web UI and open the Project page from the top-right settings menu.
- Select the API Key tab and click Generate New API Key.
- Copy the key immediately — it is only displayed once.



Step 3: Configure Your CLI Profile
Bindplane CLI supports named profiles so you can easily switch between staging, production, or self-hosted environments. Configure your default profile and test your connection:
# 1. Attach your API key to the default profile
bindplane profile set default --api-key YOUR_API_KEY_HERE
# 2. Set the Remote URL (use https://app.bindplane.com for Bindplane Cloud,
# or http://<your-server-ip>:3001 for Self-Hosted Enterprise)
bindplane profile set default --remote-url https://app.bindplane.com
# Optional: If using a Scoped API Key (starts with bps_), also set --project:
# bindplane profile set default --remote-url https://app.bindplane.com \
# --api-key bps_YOUR_SCOPED_KEY --project YOUR_PROJECT_ID
# 3. Activate the default profile
bindplane profile use default
# 4. Verify client & server connectivity
bindplane version
bindplane get source-types
Part 2: How Bindplane Fleets Work Under the Hood
Before running bulk operations, it helps to understand the four rules that govern Fleets in Bindplane:
- Configuration Priority: Collectors automatically inherit the configuration assigned to their Fleet. A Fleet configuration always takes priority over any individual configuration assigned to a collector.
- Safe Fallback on Removal: Collectors can join or leave a Fleet without altering the Fleet’s configuration. If an agent leaves a Fleet, it automatically reverts to its previous individual fallback configuration (or a safe empty no-op configuration if none existed).
- Label Selector Binding: A Fleet matches agents via selector.matchLabels (by convention, fleet: <fleet-name>). Labeling an agent with fleet=<fleet-name> immediately associates it with that Fleet.
- Single-Fleet Membership: An agent can belong to only one Fleet at a time. When moving an agent from an existing Fleet to a new one via the CLI, you must pass –overwrite.
Inspecting and Filtering Collectors
You can list all collectors (aliased as agents or collectors in the CLI) and filter them using Bindplane's token-based query syntax (–query):
# List all agents and display all attached labels
bindplane get agents --show-all-labels
# Filter connected Windows agents
bindplane get agents --query "platform:windows status:Connected"
# Filter agents currently belonging to the 'windows' fleet
bindplane get agents --query "fleet:windows"
Creating a Fleet Declaratively via YAML
To manage Fleets as code, define a Fleet manifest (fleet-windows-secops-prod.yaml) and apply it with bindplane apply -f:
apiVersion: bindplane.observiq.com/v1
kind: Fleet
metadata:
name: windows-secops-prod
displayName: Windows SecOps Production Fleet
description: Standardized Windows Event Log telemetry pipeline for production servers
labels:
platform: windows
agent-type: observiq-otel-collector
spec:
configuration: windows-security-baseline
selector:
matchLabels:
fleet: windows-secops-prod
Apply the Fleet and ensure its assigned configuration has been rolled out:
bindplane apply -f fleet-windows-secops-prod.yaml
bindplane get fleets
bindplane rollout start windows-security-baseline
bindplane rollout status windows-security-baseline
Pro Tip: Always verify that the Fleet’s configuration has been rolled out. Until the rollout is active, collectors in the Fleet will continue running their individual configurations.
Part 3: Real-World Use Case — Bulk Updating the Fleet for a List of Agents
Imagine your Security Operations (SecOps) team has just finalized a hardened Windows telemetry pipeline under the Fleet windows-secops-prod. You are given a change ticket containing a list of 50 specific production agents (exported from a CMDB or maintenance wave) that are currently scattered across a legacy fleet (fleet=legacy-windows) or running standalone configs. Your goal is to migrate this exact list of agents to windows-secops-prod, log every change for compliance, and verify fleet health.
A Subtle CLI Trap to Watch Out For
When we tested bindplane label agent <id> fleet=windows-secops-prod –overwrite against the Bindplane API using a non-existent or mistyped Agent ID, we uncovered an important behavior: the server returns HTTP 200 with
New-Item -Path $ConfigDir -ItemType Directory , and the CLI exits with exit code 0 while printing:
0 collectors labeled
Meanwhile, a valid agent match prints:
1 collector labeled
If your automation script only checks if bindplane label agent …; then (or $LASTEXITCODE -eq 0 in PowerShell), mistyped or stale Agent IDs will silently report as SUCCESS! In the scripts below, we explicitly assert both a zero exit code and that the output matches ^[1-9][0-9]* collector.
Step 1: Prepare Your Target Agent List
Create a text file named target_agents.txt with one Agent ID per line (comments starting with # and blank lines are ignored):
# Production Wave 1 - Windows Server Agent IDs
0195a3b0-eeaf-xxxx-898d-001122334455
0195a3b0-eeaf-xxxx-898d-001122334456
0195a3b0-eeaf-xxxx-898d-001122334457
What if your change ticket only lists hostnames (hostnames.txt) instead of Agent UUIDs? Because bindplane get agents -o json returns a clean JSON array of agent objects, you can resolve hostnames to Agent IDs automatically with jq:
while IFS= read -r host || [[ -n "$host" ]]; do
host=$(echo "$host" | tr -d '[:space:]')
[[ -z "$host" || "$host" =~ ^# ]] && continue
bindplane get agents --query "hostname:$host" -o json | jq -r '.[].id'
done < hostnames.txt > target_agents.txt
Method A: Audited Bulk Migration Script in Bash (Linux / macOS)
Save the following script as update_fleet_bulk.sh. It iterates through target_agents.txt, overwrites each agent's fleet label, verifies that at least 1 collector labeled was returned, writes a timestamped audit log, and queries the final Fleet state:
#!/usr/bin/env bash
set -euo pipefail
AGENT_LIST_FILE="$ Out-Null
"
TARGET_FLEET="${2:-windows-secops-prod}"
LOG_FILE="fleet_migration_$(date +%Y%m%d_%H%M%S).log"
# Ensure ~/.bindplane directory exists for CLI logger
mkdir -p ~/.bindplane
if [[ ! -f "$AGENT_LIST_FILE" ]]; then
echo "Error: Agent list file '$AGENT_LIST_FILE' not found." >&2
exit 1
fi
echo "Starting bulk fleet update to fleet='$TARGET_FLEET'..." | tee -a "$LOG_FILE"
SUCCESS_COUNT=0
FAIL_COUNT=0
while IFS= read -r raw_line || [[ -n "$raw_line" ]]; do
# Strip whitespace/CR and skip empty lines or comments
agent_id=$(echo "$raw_line" | tr -d '[:space:]')
[[ -z "$agent_id" || "$agent_id" =~ ^# ]] && continue
echo -n "Updating agent [$agent_id] -> fleet=$TARGET_FLEET ... "
if output=$(bindplane label agent "$agent_id" "fleet=$TARGET_FLEET" --overwrite 2>&1) && [[ "$output" =~ ^[1-9][0-9]*[[:space:]]+collector ]]; then
echo "SUCCESS ($output)"
echo "[SUCCESS] $agent_id: $output" >> "$LOG_FILE"
SUCCESS_COUNT=$((SUCCESS_COUNT + 1))
else
echo "FAILED ($output)"
echo "[ERROR] $agent_id: $output" >> "$LOG_FILE"
FAIL_COUNT=$((FAIL_COUNT + 1))
fi
done < "$AGENT_LIST_FILE"
echo ""
echo "Migration Summary: $SUCCESS_COUNT succeeded, $FAIL_COUNT failed. Full log: $LOG_FILE"
# Verify agents now reporting under the target fleet
echo "Verifying current agents in fleet '$TARGET_FLEET':"
bindplane get agents --query "fleet:$TARGET_FLEET"
Run the script:
chmod +x update_fleet_bulk.sh
./update_fleet_bulk.sh target_agents.txt windows-secops-prod
Fast Single-API-Call Alternative: Under the hood, bindplane label agent calls PATCH /v1/agents/labels and accepts multiple Agent IDs in a single command. If you don't need per-agent audit logging, you can update all IDs in target_agents.txt in a single HTTP request: bindplane label agent $(grep -vE '^\s*(#|$)' target_agents.txt | tr -d '\r') fleet=windows-secops-prod –overwrite
Method B: Audited Bulk Migration Script in PowerShell (Windows)
For Windows administrators, save the following script as Update-BindplaneFleet.ps1. It updates each agent in target_agents.txt and exports a structured CSV audit trail (fleet_migration_audit.csv):
param (
[string]$AgentListFile = ".\target_agents.txt",
[string]$TargetFleet = "windows-secops-prod"
)
$ConfigDir = Join-Path -Path $env:USERPROFILE -ChildPath ".bindplane"
if (-not (Test-Path -Path $ConfigDir)) {
New-Item -Path $ConfigDir -ItemType Directory | Out-Null
}
if (-not (Test-Path -Path $AgentListFile)) {
Write-Error "Agent list file not found: $AgentListFile"
exit 1
}
$SuccessCount = 0
$FailCount = 0
$Results = @()
$AgentIds = Get-Content -Path $AgentListFile | Where-Object {
$_.Trim() -ne "" -and -not $_.Trim().StartsWith("#")
}
Write-Host "Updating $($AgentIds.Count) agents to fleet=$TargetFleet..." -ForegroundColor Cyan
foreach ($RawId in $AgentIds) {
$AgentId = $RawId.Trim()
Write-Host -NoNewline "Assigning agent [$AgentId] -> fleet=$TargetFleet ... "
$Output = (& bindplane label agent $AgentId "fleet=$TargetFleet" --overwrite 2>&1) -join " "
if (($LASTEXITCODE -eq 0) -and ($Output -match "^[1-9]\d*\s+collector")) {
Write-Host "SUCCESS ($Output)" -ForegroundColor Green
$SuccessCount++
$Status = "Success"
} else {
Write-Host "FAILED ($Output)" -ForegroundColor Red
$FailCount++
$Status = "Failed"
}
$Results += [PSCustomObject]@{
Timestamp = (Get-Date -Format "yyyy-MM-dd HH:mm:ss")
AgentId = $AgentId
TargetFleet = $TargetFleet
Status = $Status
Details = $Output
}
}
$Results | Export-Csv -Path ".\fleet_migration_audit.csv" -NoTypeInformation
Write-Host "`nCompleted: $SuccessCount succeeded, $FailCount failed. Audit saved to fleet_migration_audit.csv" -ForegroundColor Cyan
# Verify updated agents in the target fleet
bindplane get agents --query "fleet:$TargetFleet"
Method C: Dynamic Bulk Updates Using CLI Queries and Selectors
When your target collectors share common metadata — such as an existing Fleet name, OS platform, environment label, or hostname pattern — you don’t even need an input file. Use –query or –selector directly with bindplane label agent:
# 1. Preview the exact agents matching your filter first
bindplane get agents --query "platform:windows fleet:legacy-windows status:Connected"
# 2. Reassign all matching agents to the new fleet in one command
bindplane label agent \
--query "platform:windows fleet:legacy-windows status:Connected" \
fleet=windows-secops-prod \
--overwrite
# Alternative: Use a Kubernetes-style label selector
bindplane label agent \
--selector "environment=production,role=domain-controller" \
fleet=windows-secops-prod \
--overwrite
Post-Migration Verification & Instant Rollback
Once your agents have been reassigned, run these three checks to confirm every collector picked up the new Fleet configuration cleanly:
# 1. Confirm all migrated agents are listed in the new fleet
bindplane get agents --query "fleet:windows-secops-prod" --show-all-labels
# 2. Check if any agent in the fleet reported a configuration error
bindplane get agents --query "fleet:windows-secops-prod status:Error"
# 3. Inspect rollout progress
bindplane rollout status windows-security-baseline
Need to roll back an agent to its previous Fleet or remove its Fleet association altogether?
# Roll back an agent to its previous fleet
bindplane label agent <agent-id> fleet=legacy-windows --overwrite
# Or remove the fleet label entirely (reverts to individual fallback config)
bindplane label agent <agent-id> fleet- --overwrite
Part 4: 5 Best Practices for Bindplane Fleet Management
- Design One Fleet per Shared Telemetry Profile: Group machines that share the same OS platform and collection sources (e.g., linux-k8s-nodes, windows-domain-controllers, linux-nginx-edge).
- Assign Fleets at Install Time: Include the fleet=<fleet-name> label in your collector installation command so newly provisioned hosts join their target Fleet and inherit its pipeline immediately on first heartbeat.
- Use Phased Rollouts for Fleet Changes: Never edit Fleet agents individually. Update the Fleet’s configuration and use progressive rollouts (bindplane rollout start <config> –initial 5 –multiplier 2 –max-errors 3) to catch incompatibilities early.
- Standardize Label Taxonomy: Keep label keys consistent (fleet, environment, region, service) so CLI –query and –selector filters stay predictable across teams.
- Version-Control Your Fleets and Configs: Export configurations before making changes (bindplane get configuration <name> –export -o yaml > backup.yaml) and store both Fleet and Configuration YAML manifests in Git.
Further Reading
- Bindplane CLI Command Reference
- Bindplane CLI Installation Guide
- Bindplane Fleet Management Guide
- Filtering Collectors with Query Syntax
Mastering Bindplane CLI and Fleet Management at Scale: From Zero to Bulk Agent Migrations was originally published in Google Cloud – Community on Medium, where people are continuing the conversation by highlighting and responding to this story.
Source Credit: https://medium.com/google-cloud/mastering-bindplane-cli-and-fleet-management-at-scale-from-zero-to-bulk-agent-migrations-aae79d018e2d?source=rss—-e52cf94d98af—4
