Skip to main content

Client Onboarding & Key Vault Setup

Every client (CPL, AFF, AXENDO, CEVA, CJ, DENMAR, deugro, IMISK, LEOCH, LDC, LOGISWIFT, PERFORMANS, BARRICK, PBCU, QUANTA, ...) gets its own deployment of Eos.Api/Eos.Workers (and, where applicable, Eos.Api.External), each pointed at that client's own database via its own connection string. This page is the practical runbook for standing up a new client+environment (Sandbox, UAT, Production); the full checklist with per-client status tracking lives in POST_ROLLOUT_ACTIONS.md at the repo root — this page explains how to do each step, that file tracks which clients have already had it done.

:::note Scope This page covers infra provisioning (Key Vault, Application Insights, IIS site) for a client+environment that already has a database. Standing up a brand-new client's database itself is a separate, not-yet-built item — see TASKS.md's "New-instance installation process" entry. :::

Order of operations

Secrets never live in source control or in plaintext Azure DevOps variables for long — they flow through Key Vault, read at startup via KeyVault:Uri (AddEosSecrets in SecretConfigurationExtensions.cs). Provisioning has a strict order because each script's output feeds the next:

1. provision-shared-workspace.sh (ONCE EVER, not per client)
2. provision-app-insights.sh (once per client+environment)
3. provision-keyvault.sh (once per client+environment)
4. Add/update the ADO variable group, redeploy, confirm /health
5. Stand up the IIS site/app pool for this client+environment (if it doesn't exist yet)

All scripts live under pipelines/scripts/. Run them from a shell with the Azure CLI (az login first, against the subscription that owns the target resource group).

1. Shared Log Analytics workspace — once, ever

./pipelines/scripts/monitoring-provisioning/provision-shared-workspace.sh \
rg-eos-shared law-eos-shared eastus

Every client's Application Insights resource feeds into this one shared workspace (operational logs only — requests/exceptions/traces; business data always stays per-client in EosDb). Skip this step entirely if the shared workspace already exists — check POST_ROLLOUT_ACTIONS.md before running it again.

2. Application Insights — once per client+environment

./pipelines/scripts/monitoring-provisioning/provision-app-insights.sh \
rg-eos-<client> ai-eos-<client>-<environment> eastus <shared-workspace-id>

Use a name that clearly identifies the client (ai-eos-ceva-prod, not a generic name) — every cross-client KQL query in the log-aggregation cookbook groups/filters by this resource's name. Copy the printed connection string; step 3 needs it.

3. Key Vault — once per client+environment

./pipelines/scripts/keyvault-provisioning/provision-keyvault.sh \
rg-eos-<client> kv-eos-<client>-<environment> vm-eos-<client>-<environment> eastus

This creates the vault, assigns the target VM's system-assigned managed identity the Key Vault Secrets User role, and prompts (input hidden, nothing kept in shell history) for:

  • ConnectionStrings:EosDb
  • Jwt:SigningKey
  • ApplicationInsights:ConnectionString (paste step 2's output; leave blank if this deployment has none)

If this client+environment also runs Logiswift.Eos.Api.External, its PartnerJwt:SigningKey/ Issuer/Audience secrets are not covered by this script (added after the External API split) — seed those into the same vault by hand with az keyvault secret set.

4. Wire up the variable group and redeploy

One Azure DevOps variable group per client+environment instance — named eos-<client>-<environment> (e.g. eos-ceva-sandbox) — is shared across every deploy target for that instance: backend, Logiswift.Eos.Api.External (if enabled), the frontend, and the docs site. This is not one group per app-type; provision-client.sh creates only this single group. Point variableGroupName (and variableGroupNameExternal, if applicable) in azure-pipelines.yml, azure-pipelines-frontend.yml, and azure-pipelines-docs.yml all at this same group name for the client+environment.

The script prints the vault URI. Add it as KeyVault.Uri (not a secret) to the group, redeploy, and confirm /health reports healthy before doing anything else. Only once that's confirmed, delete the old plaintext ConnectionStrings.EosDb/Jwt.SigningKey/ ApplicationInsights.ConnectionString variables from that group — that's the step that actually removes the secrets from Azure DevOps.

5. IIS site/app pool

pipelines/templates/deploy-iis.yml (and its frontend/docs equivalents) creates the IIS site/app pool on first deploy if it doesn't already exist (see iisWebsiteName/ iisAppPoolName/iisWebsitePhysicalPath parameters in pipelines/azure-pipelines.yml, azure-pipelines-frontend.yml, and azure-pipelines-docs.yml) — nothing extra to do here beyond making sure the target VM is registered under the right Azure DevOps Environment first. If this client+environment also exposes Logiswift.Eos.Api.External, it deploys to its own site/app pool (Deploy_Sandbox_External-style stage, same shared variable group) — that VM still needs to exist and be registered before that stage will succeed.

Reference

  • pipelines/scripts/monitoring-provisioning/provision-shared-workspace.sh
  • pipelines/scripts/monitoring-provisioning/provision-app-insights.sh
  • pipelines/scripts/keyvault-provisioning/provision-keyvault.sh
  • POST_ROLLOUT_ACTIONS.md — per-client status tracking for every step above
  • pipelines/azure-pipelines.yml — the deploy stages these steps feed into

:::danger Do not

  • Do not paste real secret values into this doc, a commit message, or a chat log.
  • Do not skip the shared-workspace check in step 1 — creating a second shared workspace splits cross-client KQL queries and defeats the point of "shared."
  • Do not delete the old plaintext variable-group secrets until /health has confirmed the app is reading successfully from Key Vault. :::