DefangLabs / DefangLabs/defang
Azure: config names differing only in case silently collide in Key Vault
Nobody has claimed this yet.
- Dominant language
- Go
- Stars
- 166
- Forks
- 24
- Avg merge
- 10h 8m
- Merged PRs (30d)
- 33
Description
Summary
On the azure stack, two config keys that differ only in case — e.g. RECALL_API_KEY vs Recall_Api_Key — are stored as the same Azure Key Vault secret and silently overwrite
each other. defang config set reports success for both, but only the last write survives. Its recorded key becomes the surviving casing, so ListConfig no longer returns the
original name and deploy fails with a misleading error:
Error: missing configs ["RECALL_API_KEY"] (https://s.defang.io/config)
This is azure-only. AWS (SSM Parameter Store) and GCP (Secret Manager) store config names case-sensitively, so the same two names coexist correctly there.
Root cause
Azure Key Vault secret names are case-insensitive. Defang preserves case when encoding a config key into a secret name, and never checks for a collision before writing — so two
differently-cased keys collapse onto one secret on Azure's side.
- Encoding preserves case —
ToSecretNameonly maps_→-; it does not change case. (The case-folding is done by Key Vault, not Defang.)
src/pkg/clouds/azure/keyvault/keyvault.go:48func ToSecretName(key string) string { return strings.ReplaceAll(key, "_", "-") } RECALL_API_KEY → RECALL-API-KEY and Recall_Api_Key → Recall-Api-Key are distinct strings, but Key Vault treats them as the same secret. - Write does no collision check — last write wins. SetSecret overwrites the existing secret and its defang-config tag.
src/pkg/clouds/azure/keyvault/keyvault.go:311 (PutSecret), src/pkg/cli/client/byoc/azure/byoc.go:730-742 (PutConfig) - ListConfig returns the surviving tag's casing (Recall_Api_Key, not RECALL_API_KEY).
src/pkg/cli/client/byoc/azure/byoc.go:685-709 - Validation matches case-sensitively, so the original compose reference is reported missing.
src/pkg/cli/compose/validation.go:473 (slices.Contains), also src/pkg/cli/configResolution.go:48
Reproduction
On the azure stack, with a compose file referencing ${RECALL_API_KEY}:
defang config set RECALL_API_KEY # value A
defang config set Recall_Api_Key # value B (different case) — silently overwrites A
defang config # lists only ONE entry
defang compose up # Error: missing configs ["RECALL_API_KEY"]
Expected behavior
Since Key Vault cannot represent both names, defang config set should fail loudly on the collision (e.g. config "Recall_Api_Key" conflicts with existing "RECALL_API_KEY") instead of
silently overwriting and failing later at deploy.
Contributor guide
No contributing guide indexed for this repository
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start in src/pkg/clouds/azure/keyvault/keyvault.go at ToSecretName and PutSecret, then trace PutConfig in src/pkg/cli/client/byoc/azure/byoc.go. Confirm how ListConfig records casing and how validation handles names in validation.go and configResolution.go; done means defang config set rejects a case-only collision instead of overwriting the existing Azure secret.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- azure, go
- Domain
- cli, cloud
- Issue type
- Bug
- Difficulty
- 3/5
- Estimated time
- 1-2 days
- Activity status
- Quiet
- Clarity
- Clearly specified
- Newbie friendliness
- 74/100