Parent tracking issue: #9005 (⭐ Priority 0 → P0-1)
Summary
Add a local-preflight validation check (via the extension WithValidationCheck capability) to azure.ai.agents that detects an immutable resource-group region conflict before provisioning starts, and stops azd with a clear note + remediation suggestion instead of a slow, cryptic deploy-time ARM failure.
This is the #1 production error from the top-5 ARM error report (87 operations): a subscription-scoped Bicep deployment fails with InvalidResourceGroupLocation because the target resource group already exists in a different region than AZURE_LOCATION.
Why users hit this (flow)
The extension writes a stable, salted resource-group name (rg-{env}-{salt}) to AZURE_RESOURCE_GROUP at init. Two independent location vars exist:
AZURE_LOCATION — resource-group region (immutable once the RG exists)
AZURE_AI_DEPLOYMENTS_LOCATION — model/project region (can change independently)
The conflict arises when AZURE_LOCATION is later changed while the salted RG still exists in the original region, e.g.:
- Re-running
azd ai init when the scope location is empty/unsupported → re-prompt overwrites AZURE_LOCATION to a new region.
- Region-list / model-set changes make a prior location "unsupported" → forced re-pick.
--project-id adoption derives the region from the Foundry project (different from the existing RG).
- Manual
azd env set AZURE_LOCATION <new>.
- Confusion between the two location vars.
Because the RG name is stable, the pre-existing RG in the old region collides with the new AZURE_LOCATION, and the subscription-scoped deployment fails at ARM. The region of a resource group cannot be changed, so proceeding is a guaranteed failure — hence the check is a blocking Error (not a proceed-anyway warning).
Proposed check
- Rule ID:
resource_group_location_mismatch
- CheckType:
local-preflight
- Severity: Error (blocking)
- Logic (best-effort / non-blocking on lookups):
- Read
AZURE_SUBSCRIPTION_ID, AZURE_LOCATION (prefer ValidationContext.EnvLocation()), AZURE_RESOURCE_GROUP (fallback rg-{env}).
- If subscription or location empty → skip.
LookupTenant → AzureDeveloperCLICredential.
armresources.ResourceGroupsClient.Get. On 404 (RG not created yet) or any other error → skip silently.
- If the RG exists and its region differs from
AZURE_LOCATION (case-insensitive) → emit a blocking ValidationCheckResult with message + suggestion.
- Escape hatch: best-effort skip on any resolution/auth/API failure (never blocks validation itself).
Message + suggestion (what the user sees)
Resource group "rg-myenv-abc123" already exists in region "eastus", but AZURE_LOCATION is set to "westus2". A resource group's region cannot be changed, so provisioning would fail with the ARM error "InvalidResourceGroupLocation".
Suggestion (3 remediation paths):
- Use the existing region:
azd env set AZURE_LOCATION eastus
- Target a different resource group:
azd env set AZURE_RESOURCE_GROUP <new-name>
- Delete the resource group if it is no longer needed:
az group delete --name rg-myenv-abc123 --subscription <sub>
Repro steps (verify the check stops azd)
Fast manual repro (forces the conflict without a full first provision):
- Create/select an agent project env:
- Point the env at a subscription and a resource group that exists in one region:
azd env set AZURE_SUBSCRIPTION_ID <your-sub-id>
azd env set AZURE_RESOURCE_GROUP rg-rgloc-test
az group create --name rg-rgloc-test --location eastus --subscription <your-sub-id>
- Set a conflicting deployment region:
azd env set AZURE_LOCATION westus2
- Run provisioning (or
azd up). During the validation phase, azd should stop with the message + suggestion above and not attempt the deployment:
Expected: azd reports the resource_group_location_mismatch preflight error, prints the 3 remediation options, and aborts before any ARM deployment call.
Natural repro (the real-world flow):
azd ai init + azd up in region eastus (creates rg-{env}-{salt} in eastus).
- Change region:
azd env set AZURE_LOCATION westus2 (or re-run init and pick a different region).
azd provision → the check stops early with guidance (previously: slow ARM InvalidResourceGroupLocation failure).
Negative cases (should NOT block): RG doesn't exist yet (fresh env), AZURE_LOCATION matches the RG region (incl. different casing), or subscription/location unset.
Implementation
Implemented in the azure.ai.agents extension:
internal/project/resource_group_location_check.go — ResourceGroupLocationCheck implementing azdext.ValidationCheckProvider.
- Registered in
internal/cmd/listen.go via WithValidationCheck({CheckType: "local-preflight", RuleID: "resource_group_location_mismatch", ...}).
- Unit tests for the pure comparison/message logic.
PR: linked below.
Parent tracking issue: #9005 (⭐ Priority 0 → P0-1)
Summary
Add a local-preflight validation check (via the extension
WithValidationCheckcapability) toazure.ai.agentsthat detects an immutable resource-group region conflict before provisioning starts, and stopsazdwith a clear note + remediation suggestion instead of a slow, cryptic deploy-time ARM failure.This is the #1 production error from the top-5 ARM error report (87 operations): a subscription-scoped Bicep deployment fails with
InvalidResourceGroupLocationbecause the target resource group already exists in a different region thanAZURE_LOCATION.Why users hit this (flow)
The extension writes a stable, salted resource-group name (
rg-{env}-{salt}) toAZURE_RESOURCE_GROUPat init. Two independent location vars exist:AZURE_LOCATION— resource-group region (immutable once the RG exists)AZURE_AI_DEPLOYMENTS_LOCATION— model/project region (can change independently)The conflict arises when
AZURE_LOCATIONis later changed while the salted RG still exists in the original region, e.g.:azd ai initwhen the scope location is empty/unsupported → re-prompt overwritesAZURE_LOCATIONto a new region.--project-idadoption derives the region from the Foundry project (different from the existing RG).azd env set AZURE_LOCATION <new>.Because the RG name is stable, the pre-existing RG in the old region collides with the new
AZURE_LOCATION, and the subscription-scoped deployment fails at ARM. The region of a resource group cannot be changed, so proceeding is a guaranteed failure — hence the check is a blocking Error (not a proceed-anyway warning).Proposed check
resource_group_location_mismatchlocal-preflightAZURE_SUBSCRIPTION_ID,AZURE_LOCATION(preferValidationContext.EnvLocation()),AZURE_RESOURCE_GROUP(fallbackrg-{env}).LookupTenant→AzureDeveloperCLICredential.armresources.ResourceGroupsClient.Get. On 404 (RG not created yet) or any other error → skip silently.AZURE_LOCATION(case-insensitive) → emit a blockingValidationCheckResultwith message + suggestion.Message + suggestion (what the user sees)
Suggestion (3 remediation paths):
azd env set AZURE_LOCATION eastusazd env set AZURE_RESOURCE_GROUP <new-name>az group delete --name rg-myenv-abc123 --subscription <sub>Repro steps (verify the check stops azd)
Fast manual repro (forces the conflict without a full first provision):
azd up). During the validation phase, azd should stop with the message + suggestion above and not attempt the deployment:Expected:
azdreports theresource_group_location_mismatchpreflight error, prints the 3 remediation options, and aborts before any ARM deployment call.Natural repro (the real-world flow):
azd ai init+azd upin regioneastus(createsrg-{env}-{salt}in eastus).azd env set AZURE_LOCATION westus2(or re-run init and pick a different region).azd provision→ the check stops early with guidance (previously: slow ARMInvalidResourceGroupLocationfailure).Negative cases (should NOT block): RG doesn't exist yet (fresh env),
AZURE_LOCATIONmatches the RG region (incl. different casing), or subscription/location unset.Implementation
Implemented in the
azure.ai.agentsextension:internal/project/resource_group_location_check.go—ResourceGroupLocationCheckimplementingazdext.ValidationCheckProvider.internal/cmd/listen.goviaWithValidationCheck({CheckType: "local-preflight", RuleID: "resource_group_location_mismatch", ...}).PR: linked below.