Azure 38: Variable Groups & Service Connections — the secret backbone
easy⏱ 5 mincourseazure
Service Connections — typed credentials
A Service Connection is a named credential of a specific type. Lythia maintains five families: ARM-Driveway-{env} (Service Principal for Azure CLI / Terraform), AKS-apps-k8s-{env} (kubeconfig for helm upgrade / kubectl), ACR-{env} (Docker push), Azure Artifacts feed connections, and 1Password Connect. Pipelines reference them by string name — the secret never leaves ADO.
# pipeline-templates/variables/global.yaml
variables:
armDrivewayProdSC: ARM-Driveway-prod
armDrivewayDevSC: ARM-Driveway-dev
k8sAppsProdSC: AKS-apps-k8s-prod
k8sAppsDevSC: AKS-apps-k8s-dev
acrContainerProd: drivewayprodcontainerregistry.azurecr.io
acrContainerDev: drivewaydevcontainerregistry.azurecr.io
Variable Groups — env + service scoped
Variable Groups come in two flavours: plain (key/value, the values themselves can be secret) and Key-Vault-linked (variables pulled at run time from a vault). Naming convention at Lythia: {service}-{env} for per-service secrets (e.g. cart-service-prod), {capability}-{env} for shared concerns (datadog-agents-prod). A consumer pipeline lists which groups to load.
extends:
template: pipelines/backend/delivery.yml@pipelineTemplates
parameters:
environmentName: prod
variableGroups:
- datadog-agents-prod # shared APM key
- cart-service-prod # SPRING_DATA_MONGODB_URI, AVAILABILITY_TOPIC_CONNECTION_STRING, …
How a secret reaches a pod
The full chain: (1) Variable Group is linked to a Key Vault → (2) at pipeline runtime the values are fetched and exposed as $() macros, masked in logs → (3) the helm step does --set secrets.SPRING_DATA_MONGODB_URI=\"$(SPRING_DATA_MONGODB_URI)\" → (4) the chart's secret.yaml template creates a Secret object → (5) the Deployment mounts it as env vars. Five hops, one rule: each hop is owned by exactly one team.
Build a credential resolver
Write a resolveCredentials(env, service) that returns an object with armSC, aksSC, acrSC (string names) and an array variableGroups containing both the shared group datadog-agents-{env} and the per-service group {service}-{env}. Throw if the env is not one of dev/test/uat/prod. Log the resolved credentials for two services in two environments.
Never read a Service Connection's identity inside a job
Service Connections are scoped to stages in ADO, not jobs. If you azureSubscription: $(armDrivewayProdSC) inside a job that runs in a stage that doesn't have approval on that Service Connection, the run silently uses the default identity (often dev). Always set the Service Connection at the stage's pool/environment level so the identity is auditable.
Quiz: who has the prod ARM SPN's secret?
A developer is debugging a terraform apply that's failing in prod. Can they read the prod Service Principal's client secret to test it locally? No. ADO Service Connections store the secret encrypted; even the SC owner cannot retrieve it after creation. The right move is az login --service-principal -u <id> from a sandbox SPN with the same role assignments, NOT exfiltrating the prod secret.