Skip to content

Benefits Agent — Setup and Operations#

The Agent is a self-hosted Windows Service that runs on each deployment target. It exposes an HTTP API that bmo calls to upload and activate packages in IIS. A built-in SPA dashboard lets operators manage installations, Windows services, and IIS app pools from a browser without needing the CLI.


Prerequisites#

  • Windows Server with IIS installed (Web Server role enabled)
  • .NET 10 Runtime (or higher)
  • Write access to F:\Temp (configurable) for staging uploaded packages
  • Write access to each environment's BasePath (e.g. F:\Acceptatie, F:\Productie)
  • Inbound firewall rule on port 5100 (or your configured port)
  • A server certificate if you want HTTPS or mTLS

Installation#

Publish or copy the Benefits.Agent binaries to a local directory, then run the install script from that directory:

cd C:\Benefits\Agent
.\Install.ps1

The script registers and starts the service:

$binPath = Resolve-Path .\Benefits.Agent.exe | Select-Object -ExpandProperty Path
New-Service -Name Benefits-Mgmt-Agent -BinaryPathName $binPath -DisplayName "Benefits Management Agent" -StartupType Automatic
Start-Service -Name Benefits-Mgmt-Agent

The service runs under LocalSystem by default. If your deployment paths or SQL connections require a named account, change it in the Services MMC snap-in after installation.

Start, stop, restart:

Start-Service   Benefits-Mgmt-Agent
Stop-Service    Benefits-Mgmt-Agent
Restart-Service Benefits-Mgmt-Agent

Configuration#

All settings live in appsettings.json (and appsettings.Production.json for production overrides) next to the executable. Restart the service after any change.

Agent section#

"Agent": {
  "Port": 5100,
  "TempPath": "F:\\Temp",
  "MaxRequestBodySizeBytes": 524288000,
  "MonitoringInterval": "00:05:00",
  "Name": "",
  "WriteDeploymentRecords": true,
  "ProtectedAppPools": []
}
Setting Default Notes
Port 5100 Kestrel listen port
TempPath F:\Temp Scratch space for uploaded zips. Must be writable by the service account.
MaxRequestBodySizeBytes 524288000 (500 MB) Max upload size for --via http deploys
MonitoringInterval 00:05:00 (5 min) How often StateMonitorService scans installations
Name MachineName Agent identifier reported to the Orchestrator. Override when multiple agents share a hostname.
WriteDeploymentRecords true Persist deployment audit records to the database
ProtectedAppPools [] IIS app pools that management API calls are blocked from touching

Environments#

One entry per deployment environment. The key (Acceptatie, Productie) must match the environment name used in bmo deploy.

"Agent": {
  "Environments": {
    "Productie": {
      "BasePath": "F:\\Productie",
      "CommandCenterUrl": "http://vmbenefitsccp1:8000",
      "ConnectionStrings": {
        "BenefitManager": "Data Source=localhost;Initial Catalog=BenefitManager;Integrated Security=True",
        "CommandCenter": "Data Source=localhost;Initial Catalog=ReverseProxy;Integrated Security=True"
      },
      "NamingStrategy": "PerMajorMinor",
      "AppPoolPrefix": null
    }
  }
}
Setting Default Notes
BasePath Root folder for this environment's deployments
CommandCenterUrl Internal URL of the CommandCenter reverse-proxy for this environment
ConnectionStrings.BenefitManager SQL connection for BenefitManager data
ConnectionStrings.CommandCenter SQL connection for the reverse-proxy database
NamingStrategy PerMajorMinor IIS App Pool naming convention. See pipeline-migration-bmo-package-push.md for the full strategy table.
AppPoolPrefix null (uses env name) Override the prefix portion of the generated pool name

Authentication#

The Agent accepts three inbound auth schemes simultaneously. At least one must be active.

ApiKey (default)

"Authentication": {
  "ApiKey": "your-secret-key"
}

Callers set the X-Api-Key request header. Set the matching value in bmo via bmo config auth.

mTLS

"ServerCertificate": {
  "Source": "Store",
  "StoreLocation": "LocalMachine",
  "StoreName": "My",
  "Thumbprint": "AABB..."
},
"ClientAuthentication": {
  "AllowedThumbprints": ["CCDD..."]
}
Source value Required fields
Store (default) StoreLocation, StoreName, Thumbprint
File FilePath, Password
KeyVault KeyVaultUrl, CertificateName

AllowedThumbprints is the allowlist of Orchestrator client certificate thumbprints. An empty array disables mTLS client authentication.

Entra ID (JWT)

"AzureAd": {
  "Instance": "https://login.microsoftonline.com/",
  "TenantId": "your-tenant-id",
  "ClientId": "your-client-id"
}

The JWT scheme is only registered when ClientId is non-empty. Leave all AzureAd fields empty to disable it.

Package Store (blob, for --via blob deploys)#

"PackageStore": {
  "AccountUri": "https://<storageaccount>.blob.core.windows.net/",
  "ContainerName": "deployments"
}

When AccountUri is set, the Agent downloads packages from blob storage for --via blob deploys. If the URL contains a query string it is treated as a SAS URL; otherwise DefaultAzureCredential (managed identity) is used. Leave AccountUri empty for HTTP-only mode.

Service Bus transport (optional)#

"Agent": {
  "ServiceBus": {
    "Namespace": "my-namespace.servicebus.windows.net",
    "StateTopic": "agent.state.v1",
    "SnapshotContainerUrl": "https://...",
    "CommandsTopic": "deploy.commands.v1",
    "RepliesTopic": "deploy.replies.v1",
    "AgentCommandsTopic": "agent.commands.v1",
    "AgentRepliesTopic": "agent.replies.v1"
  }
}

Leave Namespace empty (the default) to use HTTP-only transport. When set, the Agent:

  • Starts a ServiceBusDeployWorker that consumes deploy commands from CommandsTopic and publishes deploy replies to RepliesTopic.
  • Starts an AgentCommandsConsumer that consumes operator commands (service control, IIS, uninstall, certificate operations, status queries) from AgentCommandsTopic and publishes replies to AgentRepliesTopic.

The agent creates a durable per-agent subscription agent-{Name} on AgentCommandsTopic with a SQL filter targetServer = '{Name}', so only commands addressed to this agent are delivered.

In SB mode the agent host does not need an inbound firewall rule from the Command Center. Only outbound HTTPS (port 443) to <your-namespace>.servicebus.windows.net is required. The HTTP server still runs for the local management web UI and for direct deployments that don't use the SB path.

The Orchestrator must be configured with a matching ServiceBus.Namespace.

Production log path override#

appsettings.Production.json overrides the log path and adds a Windows EventLog sink:

"Serilog": {
  "WriteTo": [
    { "Name": "Console" },
    {
      "Name": "File",
      "Args": {
        "path": "D:\\Logs\\Benefits.Agent\\agent-.log",
        "rollingInterval": "Day",
        "retainedFileCountLimit": 30
      }
    },
    {
      "Name": "EventLog",
      "Args": { "source": "Benefits.Agent", "logName": "Application" }
    }
  ]
},
"Agent": {
  "TempPath": "D:\\BenefitManager\\Temp"
}

Deploy both files side-by-side. The Production file overrides only the keys it declares; everything else comes from the base appsettings.json.


First-run smoke test#

  1. Start-Service Benefits-Mgmt-Agent
  2. Open http://<host>:5100/health — expect Healthy (plain text, no credentials needed).
  3. Open http://<host>:5100/ in a browser — the management dashboard should load.
  4. Call GET http://<host>:5100/api/installations with X-Api-Key: <key> — on a fresh box it returns [].

Web dashboard#

Open http://<host>:5100/ in a browser. The sidebar has five tabs:

Dashboard#

Lists all known installations. Each row shows environment, version, service health, and IIS pool health. The page auto-refreshes every 5 seconds.

Per-installation actions:

  • Start / Stop / Restart — controls the Windows Service for that version.
  • Recycle / Start / Stop — controls the IIS App Pool.
  • Remove — uninstalls the service, removes the IIS application, deletes the version folder, and unregisters the entry from installations.json. This is irreversible.

Deploy#

Step-by-step wizard to trigger a new deployment from the browser. Equivalent to running bmo deploy --via http from the Orchestrator.

Audit#

Read-only log of past deployment and management actions recorded on this agent.

Security#

Certificate management — view, upload, download, and delete certificates managed by the Agent.

Settings#

Under development.


Operations#

Logs#

Environment Path
Default C:\Logs\Benefits.Agent\agent-<date>.log
Production D:\Logs\Benefits.Agent\agent-<date>.log + Windows Application EventLog

Logs rotate daily. The production config retains 30 days of rolling files.

Installation registry#

The Agent tracks known installations in installations.json next to the executable. On startup, if the file is missing or empty, StateMonitorService performs a best-effort filesystem crawl and rebuilds it automatically.

On each monitoring tick (default every 5 minutes) the service:

  1. Checks the health of every registered installation (service status, IIS pool status).
  2. Logs a warning for any component in Stopped or Error health.
  3. Removes registry entries whose folder is physically missing (ghost entries).
  4. Publishes an AgentStateSnapshot for the Service Bus integration (no-op when Service Bus is not configured).

Health endpoint#

GET /health — no credentials required. Returns Healthy, Degraded, or Unhealthy as plain text with HTTP 200 or 503. Authenticated callers receive JSON with per-check detail.

API endpoints reference#

All /api/* routes require authorization.

Method Path Description
GET /health Health status. Anonymous: plain text. Authenticated: JSON.
GET /api/installations List all known installations on this agent.
POST /api/deploy Upload zip (multipart/form-data). Returns 202 Accepted with a statusUrl.
POST /api/deploy/reference Deploy by blob reference. Returns 202 Accepted.
GET /api/deploy/status/{id} Poll job status by GUID from the accept response.
POST /api/remove Uninstall service + IIS app + delete folder + unregister.
POST /api/services/{env}/{version}/action Start, Stop, or Restart the Windows Service for a version.
POST /api/iis/pool/{poolName}/action Recycle, Start, or Stop an IIS App Pool.
POST /api/iis/pools/cleanup Delete IIS app pools no longer in the registry.

Troubleshooting#

Symptom Likely cause Fix
Service fails to start Port 5100 already in use Change Agent.Port in appsettings.json
401 Unauthorized ApiKey mismatch Ensure Authentication.ApiKey matches the value in bmo config show
Server cert not loaded Wrong thumbprint or store Check with Get-ChildItem Cert:\LocalMachine\My and verify the thumbprint
Blob deploy fails with 403 Managed identity not granted Assign Storage Blob Data Reader to the service account on the storage account, or switch to a SAS URL
Ghost installations in dashboard Files deleted manually without using Remove They clear automatically on the next monitoring tick (within 5 minutes)
Upload returns 413 Package larger than MaxRequestBodySizeBytes Increase the setting or use --via blob transport
Entra ID auth not working AzureAd.ClientId is empty Fill in TenantId and ClientId; the JWT scheme activates only when ClientId is set

Operator checklist#

  • [ ] Install.ps1 run; service appears in Services as Benefits Management Agent, startup type Automatic
  • [ ] Agent.Port, TempPath, and all Environments entries updated in appsettings.json
  • [ ] Authentication.ApiKey set to a non-empty secret
  • [ ] appsettings.Production.json deployed alongside appsettings.json on production servers
  • [ ] For HTTPS/mTLS: ServerCertificate.Thumbprint set and cert present in the Windows cert store
  • [ ] For mTLS client auth: ClientAuthentication.AllowedThumbprints populated with the Orchestrator's cert thumbprint
  • [ ] For blob deploys: PackageStore.AccountUri set and managed identity or SAS configured
  • [ ] GET /health returns Healthy
  • [ ] Dashboard loads at http://<host>:<port>/
  • [ ] GET /api/installations returns with API key header
  • [ ] First deployment from bmo or the web Deploy wizard succeeds