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)
- The service account must be able to create
TempPathand each environment'sBasePath(e.g.F:\Acceptatie,F:\Productie), and to administer IIS — the agent creates and repairs the environment sites itself at startup (see What the agent does at startup); do not create them by hand - Inbound firewall rule on port
5100(or your configured port), and on each environment'sBindingport - 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, in an elevated PowerShell:
The script:
- Registers the service
Benefits-Mgmt-Agent(startup type Automatic). An existing service keeps its registration and account, so the same command updates an installed agent after new binaries were copied over. - Sets service recovery: restart on every failure (after 5 s, 30 s, then every minute; the count resets after a day), with
failureflag 1. A restart on request from CommandCenter depends on it: the agent exits non-zero and recovery starts it again. The agent refuses a restart request while recovery is not set, so runInstall.ps1once more on agents installed before this version. - With
-AppConfigEndpoint, sets up shared configuration (below). - Starts the service, or restarts it when it was running.
| Parameter | |
|---|---|
-ServiceName |
Default Benefits-Mgmt-Agent. Another name is written to Agent:ServiceName too, so the agent can find its own service. |
-AgentName |
Agent:Name; default the machine name. |
-AppConfigEndpoint, -KeyVaultUri |
The App Configuration store and the Key Vault its references point to. |
-Identity |
managed-identity or certificate. Without it the script asks, and suggests a managed identity on an Azure VM or an Arc-connected server. |
-ManagedIdentityClientId |
A user-assigned identity; empty for the system-assigned one. |
-TenantId, -ClientId, -CertificateSubject |
For certificate: the app registration and the subject of the machine certificate in LocalMachine\My. |
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#
The settings live in appsettings.json (and appsettings.Production.json for production overrides) next to the executable. Restart the service after a change there. With shared configuration, the settings below can also be managed centrally, and some of them apply without a restart.
Shared configuration (App Configuration)#
An agent can read its settings from the same Azure App Configuration store as CommandCenter, so they are managed in one place instead of on each server. It is optional: an agent without Configuration:AppConfig behaves exactly as before.
flowchart LR
subgraph Server["Windows server"]
Agent["Benefits.Agent<br/>(Windows service)"]
Local["appsettings.json<br/>(base and fallback)"]
Env["Service environment<br/>Configuration:* (Install.ps1)"]
end
AC["App Configuration<br/>platform/agent<br/>platform/agent/{name}"]
KV["Key Vault"]
SB["Service Bus<br/>agent.state.v1"]
CC["CommandCenter<br/>/config/agents"]
Local --> Agent
Env --> Agent
Agent -- "managed identity or certificate" --> AC
AC -. "Key Vault references" .-> KV
Agent -- "heartbeat + configuration state" --> SB --> CC
What it reads. Label platform/agent (every agent), then platform/agent/{name} (this agent; wins), where {name} is Agent:Name in lower case. It does not read platform or unlabelled keys: those hold the gateway's and the backends' settings. Order, later wins: appsettings.json → App Configuration → environment variables → command line. A local environment variable therefore still overrides the store, and is reported as a shadowed key. Agent:Name and Configuration:* always stay local: they choose the store and the label.
Bootstrap (Configuration:*, written by Install.ps1 as environment variables of the service):
| Key | |
|---|---|
Configuration:AppConfig:Endpoint |
The store. |
Configuration:KeyVault:Uri |
The only vault whose references resolve; any other reads as empty and is reported. |
Configuration:Identity:Mode |
managed-identity or certificate. Empty: DefaultAzureCredential, as before. |
Configuration:Identity:ManagedIdentityClientId |
A user-assigned managed identity. |
Configuration:Identity:TenantId, ClientId, CertificateSubject |
A service principal that signs in with the newest valid certificate of that subject in LocalMachine\My, chosen again every hour and after a failed sign-in, so a renewed certificate needs no change. Never pinned by thumbprint. |
Configuration:AppConfig:RefreshInterval |
How often the sentinel is checked; default 30 s. |
Configuration:AppConfig:StartupTimeout |
How long the first load may take; default 10 s. |
The identity is used for everything the agent reaches in Azure: App Configuration (App Configuration Data Reader), Key Vault (Key Vault Secrets User), Service Bus and blob storage. On an Arc-connected server the managed identity works for services running as LocalSystem (the default) or as a member of Hybrid agent extension applications.
When a change applies. The agent reloads when Settings:PlatformSentinel (label platform) moves, and sends a heartbeat right away. These settings apply without a restart:
| Setting | |
|---|---|
Agent:MonitoringInterval |
Heartbeat interval. |
Agent:ProtectedAppPools |
App pools the cleanup never removes. |
ClientAuthentication:AllowedSubjects, TrustedIssuers |
Who may call the agent over HTTPS. |
Serilog:MinimumLevel:Default, Serilog:MinimumLevel:Override:* |
Log levels. |
Everything else (port, Service Bus, certificates, package store, environments) is read at start: a change is reported as restart-pending until the agent restarts.
Lists are one value. In the store, a list setting is one JSON array without a JSON content type, for example ["CN=cc-webapi.example.nl"]. It replaces the local list. Stored as separate :0, :1 keys it would merge with the local entries by index, and a list could then be widened centrally but never shortened.
When the store is away. The agent starts on its local settings after StartupTimeout, reports Unreachable, and loads the store as soon as it answers (typically within a minute). It never refuses to start over it.
When a value is wrong. A value the agent cannot use (a duration typed as 5m) is logged (EventId 2205) and reported in configuration.lastError; the heartbeat keeps its last good settings, and the agent keeps running until the value is corrected. Whatever else reads that setting fails until then (a request that checks the client-certificate lists is refused, for example). An agent that stops because a background service failed exits with code 1 (EventId 2213), so service recovery restarts it and it loads the corrected store.
What it reports. The heartbeat and GET /api/agent/info carry configuration: the source (LocalOnly, AppConfiguration or Unreachable), the revision (the sentinel's value) and when it was applied, the revision it started with, restart-pending and shadowed key names, unresolved Key Vault references, and whether it can restart on request (restartSupported, with the reason when not). Key names only, never a value.
Restart on request#
POST /api/agent/restart (HTTP) and the Service Bus command Restart restart the agent without anyone signing in to the server. In CommandCenter this is the Restart button on the Configuration tab of /config/agents, where the agent settings are edited too (Agents):
sequenceDiagram
participant CC as CommandCenter
participant A as Agent
participant SCM as Service Control Manager
CC->>A: Restart
alt no service recovery, or a deployment running or queued
A-->>CC: 409 with the reason
else
A-->>CC: 202 Accepted
Note over A: the message is completed first,<br/>so it is not delivered again
A->>A: graceful stop (workers, web server)
A->>SCM: exits with code 1
SCM->>A: recovery starts the service
A-->>CC: heartbeat at start: new revision, nothing restart-pending
end
A restart is refused (409) when the agent does not run as its Windows service (Agent:ServiceName, default Benefits-Mgmt-Agent), when that service's recovery does not restart it on every failure (run Install.ps1), or while a deployment is running or queued. A Service Bus Restart that is delivered a second time, or was sent more than five minutes before the agent started, is ignored, so a redelivered message cannot restart it twice.
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",
"Binding": { "Host": "*", "Port": 5002, "UseHttps": false },
"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; also the IIS site's physical path |
Binding |
— (required) | The IIS site's HTTP binding: Host is * (any host name) or one host name, Port 1–65535. UseHttps must be false — HTTPS bindings are added by an operator, the agent does not manage certificates for sites. A binding that another site already uses is refused, never taken over |
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 |
Agent:PrepareEnvironmentsOnStartup (default true) switches the startup preparation below off where IIS is managed by something else; the agent then only reports what it finds.
What the agent does at startup#
Deployments land as IIS applications under a site named after the environment (Acceptatie, Productie), so that site has to exist before the first deploy. A new or existing agent makes sure of that itself, once, when the service starts — an operator no longer creates the sites by hand:
TempPathand every environment'sBasePathare created if missing.- For each environment, the IIS site named after it is created if missing — with the
Binding,BasePathas physical path and a root application pool of the same name (ApplicationPoolIdentity) — or repaired if it exists but its physical path, root pool or binding differ from the configuration. Repairs are logged as warnings (EventId 2043) so a change to IIS is never silent. A binding that would collide with another site fails that environment instead of stealing it. - Every application pool on the site is granted read-and-execute on the path it serves (traverse-only on
BasePathfor applications elsewhere). Existing rules are kept; an explicit deny fails the environment.
Each environment is prepared independently. A failure does not stop the agent and does not affect the other environments; it is recorded and shows up in three places:
/health(and therefore/api/agent/infoand the heartbeat) reports Degraded with the failed environments and the reason — CommandCenter shows it on/config/agents. Degraded, not Unhealthy: an unhealthy host refuses every deployment, a brokenAcceptatiesite is no reason to stop deploying toProductie.- A deployment to the failed environment is refused up front (HTTP 503 on
/api/deploy,Failedon the Service Bus reply) with the recorded reason. - The log:
Prepared IIS environment(2040) per environment,Could not prepare(2041) with the exception,Created IIS site(2042),Repaired IIS site(2043),Granted … read access(2044),Could not create Agent:TempPath(2045).
Fix the cause — configuration, IIS permissions, a port in use — and restart the service; preparation runs at startup only, there is no endpoint to re-run it.
Authentication#
How registration, the two channels and certificate trust fit together — with context, data-flow and user-journey diagrams — is on Agents: registration, transport and authentication.
The Agent's HTTP API accepts mTLS client certificates and, when configured, Entra ID tokens. The API key is deprecated and off by default. Service Bus remains the preferred channel; everything below concerns HTTP only.
mTLS (the caller is CommandCenter)
The agent trusts an issuer and a subject, never one certificate, so CommandCenter's certificate renews (Key Vault, auto-enrollment, a replaced file) without anyone touching the server:
"ServerCertificate": {
"Source": "Store",
"StoreLocation": "LocalMachine",
"StoreName": "My",
"Subject": "tas-bm02.bcs.nl"
},
"ClientAuthentication": {
"AllowedSubjects": ["CN=commandcenter-webapi, O=BCS"],
"TrustedIssuers": ["BCS Issuing CA"],
"TrustedRootFiles": [],
"RevocationMode": "NoCheck"
}
With the BCS PKI ([Root CA] → [Intermediate] → [short-lived client certificate], the Root and the
Intermediates installed on every machine):
TrustedRootFilesstays empty: the Root is in the machine store, and the chain is built from the machine's Intermediate Certification Authorities store.TrustedIssuersnames the client intermediate (e.g.BCS Client Issuing CA), not the Root. The PKI has several intermediate types under one Root; trusting the Root alone would let a certificate from the server intermediate authenticate as a client.RevocationModeisNoCheck, because the BCS CAs publish no CRL or OCSP yet: the short lifetime of the client certificates is the control. Switch toOnlineonce revocation is published (#311; for examplesmallstep/step-ca, or a service around Key Vault) —Onlineagainst a CA without reachable revocation data refuses every certificate as "revocation status unknown". The code default isOnline, so a CA that does publish revocation is checked unless configured otherwise.-
CommandCenter sends its intermediate in the handshake (its PFX carries the chain), so an agent without the intermediate installed still validates it.
-
AllowedSubjects— a distinguished name (compared whole) or a plain name (compared with the common name and DNS names). Required: with no subjects and no thumbprints, no certificate is accepted. (Before, an empty list accepted any certificate, self-signed ones included.) TrustedIssuers— the issuing CA, by distinguished or common name, that must appear above the certificate in its chain: the public CA's issuing certificate, or the internal BCS CA. Chosen per environment.TrustedRootFiles— root certificates (PEM/DER) to trust instead of the machine's store, for an internal CA that is not installed there.AllowedThumbprints— optional extra pin on exact certificates; a renewal changes the thumbprint.RevocationMode—Online(the code default),OfflineorNoCheck(where the CA publishes no reachable revocation data; shipped asNoCheckfor the BCS PKI, see below).- Self-signed client certificates are never accepted.
The server certificate is chosen at every TLS handshake, re-read hourly: with Subject, the
newest valid certificate of that subject (with its private key) in the store, so a renewed one is
used without a restart. Thumbprint (one exact certificate) and KeyVault (KeyVaultUrl,
CertificateName) still work. With a server certificate the agent listens on HTTPS on
Agent.Port; mTLS needs https:// in CommandCenter's agent address.
Source value |
Required fields |
|---|---|
Store (default) |
StoreLocation, StoreName, and Subject (preferred) or Thumbprint |
KeyVault |
KeyVaultUrl, CertificateName |
On the CommandCenter side the certificate is Agents:ClientCertificate in WebApi (Source
KeyVault / Store / File), refreshed hourly and chosen per connection.
API key (deprecated)
The scheme is registered only when ApiKey is set, and the agent logs a deprecation warning
(EventId 2031). It remains for BMO's own CommandCenter, which still calls agents with it, until that
is retired. One shared secret cannot roll over without touching every server. The key that was
committed in appsettings.Production.json (123-abcd) was removed; treat it as known and replace it
wherever it is still in use.
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
ServiceBusDeployWorkerthat consumes deploy commands fromCommandsTopicand publishes deploy replies toRepliesTopic. - Starts an
AgentCommandsConsumerthat consumes operator commands (service control, IIS, uninstall, certificate operations, status queries) fromAgentCommandsTopicand publishes replies toAgentRepliesTopic.
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#
Start-Service Benefits-Mgmt-Agent- Open
http://<host>:5100/health— expectHealthy(plain text, no credentials needed). - Open
http://<host>:5100/in a browser — the management dashboard should load. - Call
GET http://<host>:5100/api/installationswithX-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:
- Checks the health of every registered installation (service status, IIS pool status).
- Logs a warning for any component in
StoppedorErrorhealth. - Removes registry entries whose folder is physically missing (ghost entries).
- Publishes an
AgentStateSnapshotfor 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. Healthy and Degraded answer 200; only Unhealthy answers 503. Authenticated callers receive JSON with per-check detail.
Health decides one thing: an agent whose host is measured Unhealthy (under 5% free memory or disk) refuses to deploy BenefitManager versions — POST /api/deploy and /api/deploy/reference answer 503 with the reason, and a deployment over Service Bus is replied to as failed. Everything else (status, installations, service control, IIS, certificates, uninstall) keeps working, because that is how an operator frees the machine. Degraded (under 10%) changes nothing. A health check that could not run at all (WMI unavailable) is logged and does not block deployments.
API endpoints reference#
All /api/* routes require authorization.
| Method | Path | Description |
|---|---|---|
GET |
/health |
Health status. Anonymous: plain text. Authenticated: JSON. |
GET |
/api/agent/info |
Who this agent is: name (Agent:Name, default the machine name), hostname, health, its IIS site bindings, and its configuration state (shared configuration). CommandCenter reads it when an HTTP agent is added or verified on /config/agents. |
POST |
/api/agent/restart |
Restart on request: 202, then the agent stops and service recovery starts it again; 409 with the reason when it cannot. |
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/403 from CommandCenter |
Client certificate not accepted | Check ClientAuthentication.AllowedSubjects / TrustedIssuers against WebApi's certificate, that the agent's address is https://, and the agent log for the rejection reason |
401 Unauthorized with an API key |
The key scheme is off by default | Set Authentication.ApiKey only while a caller still needs it; prefer mTLS |
| Server cert not loaded | Wrong subject/thumbprint or store, or no private key | Check with Get-ChildItem Cert:\LocalMachine\My; with Subject, a valid certificate of that name with a private key must be present |
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 |
| Restart refused: "Service recovery is not configured" | Installed before Install.ps1 set recovery |
Run Install.ps1 again (it keeps the existing service) |
Configuration source Unreachable |
The store cannot be reached or the identity has no access | Check the endpoint, the network path to *.azconfig.io, and App Configuration Data Reader for the agent identity; the reason is in configuration.lastError and the agent log (EventId 2200) |
| A central change has no effect | A local setting overrides it | The key is listed as shadowed; remove it from appsettings.json or the service environment |
configuration.lastError: "A changed setting could not be applied" |
A value in App Configuration does not fit its setting | Correct it; the agent log (EventId 2205) names the key |
Operator checklist#
- [ ]
Install.ps1run; service appears in Services as Benefits Management Agent, startup type Automatic, andsc.exe qfailure Benefits-Mgmt-Agentshows threeRESTARTactions - [ ] For shared configuration:
Install.ps1 -AppConfigEndpoint … -Identity …run, the identity has App Configuration Data Reader (and Key Vault Secrets User), andGET /api/agent/infoshowsconfiguration.sourceAppConfiguration - [ ]
Agent.Port,TempPath, and allEnvironmentsentries updated inappsettings.json - [ ]
Authentication.ApiKeyleft empty unless a caller still needs it (deprecated) - [ ]
appsettings.Production.jsondeployed alongsideappsettings.jsonon production servers - [ ] For HTTPS/mTLS:
ServerCertificate.Subjectset and a certificate of that subject, with its private key, in the Windows cert store - [ ] For mTLS client auth:
ClientAuthentication.AllowedSubjectsandTrustedIssuersset to CommandCenter's certificate subject and issuer - [ ] For blob deploys:
PackageStore.AccountUriset and managed identity or SAS configured - [ ]
GET /healthreturnsHealthy - [ ] Dashboard loads at
http://<host>:<port>/ - [ ]
GET /api/installationssucceeds with CommandCenter's client certificate - [ ] First deployment from
bmoor the web Deploy wizard succeeds