Skip to content
Notifications
Clear all

Guide: Automating Boundary host set updates from your CMDB

2 Posts
2 Users
0 Reactions
0 Views
(@code_weaver_anna)
Reputable Member
Joined: 5 months ago
Posts: 250
Topic starter   [#23298]

A common operational friction point when deploying Boundary is maintaining accurate host sets for dynamic infrastructure. Manually updating IPs and tags defeats the purpose of a zero-trust network. This guide outlines a practical pattern for synchronizing Boundary host catalogs with an external CMDB or inventory source, using Boundary's Go SDK and a simple reconciler pattern.

The core concept is a control loop that fetches the current desired state from your CMDB (e.g., via its API) and reconciles it with the existing host sets in a designated Boundary scope. We'll use a service account with appropriate permissions in Boundary, stored via a `boundary` auth method. Here is the essential reconciliation logic in Go:

```go
package main

import (
"context"
"github.com/hashicorp/boundary/api"
"github.com/hashicorp/boundary/api/hosts"
"github.com/hashicorp/boundary/api/hostsets"
)

func syncHostSet(cmdbHosts []CMDBHost, boundaryClient *api.Client, hostCatalogId, hostSetId string) error {
ctx := context.Background()

// 1. Read existing hosts in the catalog
hClient := hosts.NewClient(boundaryClient)
hostList, err := hClient.List(ctx, hostCatalogId)
if err != nil { return err }

// 2. Map existing hosts by external ID (from CMDB)
existingHosts := make(map[string]*hosts.Host)
for _, host := range hostList.Items {
if host.ExternalId != "" {
existingHosts[host.ExternalId] = host
}
}

// 3. Determine creates, updates, deletes
for _, cmdbHost := range cmdbHosts {
if _, exists := existingHosts[cmdbHost.ID]; !exists {
// Create new host in Boundary catalog
_, err := hClient.Create(ctx, hostCatalogId,
hosts.WithName(cmdbHost.Name),
hosts.WithHostAddresses(cmdbHost.IP),
hosts.WithExternalId(cmdbHost.ID))
if err != nil { /* handle */ }
}
delete(existingHosts, cmdbHost.ID)
}

// 4. Delete hosts no longer in CMDB
for _, toDelete := range existingHosts {
_, err := hClient.Delete(ctx, toDelete.Id)
if err != nil { /* handle */ }
}

// 5. Re-fetch all host IDs and update the host set membership
updatedHostList, _ := hClient.List(ctx, hostCatalogId)
var hostIds []string
for _, host := range updatedHostList.Items {
hostIds = append(hostIds, host.Id)
}

hsClient := hostsets.NewClient(boundaryClient)
_, err = hsClient.SetHosts(ctx, hostSetId, 0, hostIds)
return err
}
```

Key implementation notes:
* The `ExternalId` attribute is crucial for idempotent mapping between CMDB entities and Boundary hosts.
* Always use the version field (`0` in the example) for the `SetHosts` call to manage concurrency; fetch the current version from the host set object in a real implementation.
* Run this reconciler as a periodic job within your CI/CD pipeline or as a dedicated microservice. The Boundary service account should have `ids=*;actions=*` permissions on the host catalog and host set.
* For large inventories, implement batch operations and consider rate limits.

This approach reduces drift and ensures that Boundary access policies are consistently enforced against your current infrastructure baseline, a significant improvement over static configuration.

benchmark or bust


benchmark or bust


   
Quote
(@brianl)
Reputable Member
Joined: 3 weeks ago
Posts: 195
 

This is exactly the kind of pattern we've been looking to implement, thank you for putting it together. I'm curious about one practical aspect, though. In a manufacturing context, our CMDB often has hosts that are temporarily offline for maintenance or in a decommissioning queue. Does your reconciliation logic account for a soft delete or a status flag, or would it simply remove those hosts from the Boundary host set entirely? I'm thinking we'd need to preserve them in Boundary but perhaps adjust their attributes or move them to a separate "quarantine" host set based on the CMDB state. How would you extend the example to handle that transition gracefully without losing the host object?



   
ReplyQuote