From 6e6c46f136c36384d3ea2b9576e8bbf461852338 Mon Sep 17 00:00:00 2001 From: Wei Lim Date: Mon, 24 Nov 2025 17:27:44 -0800 Subject: [PATCH 1/2] add rg checks to deployment state calc --- cli/azd/pkg/azapi/resource_service.go | 15 +++++++++++ .../provisioning/bicep/bicep_provider.go | 27 ++++++++++++++++--- 2 files changed, 39 insertions(+), 3 deletions(-) diff --git a/cli/azd/pkg/azapi/resource_service.go b/cli/azd/pkg/azapi/resource_service.go index aadfdb88e2d..1afcfef3d66 100644 --- a/cli/azd/pkg/azapi/resource_service.go +++ b/cli/azd/pkg/azapi/resource_service.go @@ -96,6 +96,21 @@ func (rs *ResourceService) GetResource( }, nil } +func (rs *ResourceService) CheckExistenceByID( + ctx context.Context, resourceId arm.ResourceID, apiVersion string) (bool, error) { + client, err := rs.createResourcesClient(ctx, resourceId.SubscriptionID) + if err != nil { + return false, err + } + + response, err := client.CheckExistenceByID(ctx, resourceId.String(), apiVersion, nil) + if err != nil { + return false, fmt.Errorf("checking resource existence by id: %w", err) + } + + return response.Success, nil +} + func (rs *ResourceService) GetRawResource( ctx context.Context, resourceId arm.ResourceID, apiVersion string) (string, error) { client, err := rs.createResourcesClient(ctx, resourceId.SubscriptionID) diff --git a/cli/azd/pkg/infra/provisioning/bicep/bicep_provider.go b/cli/azd/pkg/infra/provisioning/bicep/bicep_provider.go index 5d9c01ee0d1..c461d00d84c 100644 --- a/cli/azd/pkg/infra/provisioning/bicep/bicep_provider.go +++ b/cli/azd/pkg/infra/provisioning/bicep/bicep_provider.go @@ -588,8 +588,29 @@ func (p *BicepProvider) Deploy(ctx context.Context) (*provisioning.DeployResult, } if !p.ignoreDeploymentState && parametersHashErr == nil { - deploymentState, err := p.deploymentState(ctx, planned, deployment, currentParamsHash) - if err == nil { + deploymentState, stateErr := p.deploymentState(ctx, planned, deployment, currentParamsHash) + if stateErr == nil { + // As a heuristic, we also check the existence of all resource groups + // created by the deployment to validate the deployment state. + // This handles the scenario of resource group(s) being deleted outside of azd, + // which is quite common. + // This check adds ~100ms per resource group to the deployment time. + for _, res := range deploymentState.Resources { + if res != nil && res.ID != nil { + resId, err := arm.ParseResourceID(*res.ID) + if err == nil && resId.ResourceType.Type == arm.ResourceGroupResourceType.Type { + exists, err := p.resourceService.CheckExistenceByID(ctx, *resId, "2025-03-01") + if err == nil && !exists { + stateErr = fmt.Errorf( + "resource group %s no longer exists, invalidating deployment state", resId.ResourceGroupName) + break + } + } + } + } + } + + if stateErr == nil { result.Outputs = provisioning.OutputParametersFromArmOutputs( planned.Template.Outputs, azapi.CreateDeploymentOutput(deploymentState.Outputs), @@ -600,7 +621,7 @@ func (p *BicepProvider) Deploy(ctx context.Context) (*provisioning.DeployResult, SkippedReason: provisioning.DeploymentStateSkipped, }, nil } - logDS("%s", err.Error()) + logDS("%s", stateErr.Error()) } deploymentTags := map[string]*string{ From 0f6f6bb0cff50b14637aa8e2a51e6b27fbc3f1cc Mon Sep 17 00:00:00 2001 From: Wei Lim Date: Tue, 25 Nov 2025 17:02:01 -0800 Subject: [PATCH 2/2] do not fail test in recorder, let error propagate --- cli/azd/test/recording/recording.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cli/azd/test/recording/recording.go b/cli/azd/test/recording/recording.go index 067698dfa4f..da091b264ea 100644 --- a/cli/azd/test/recording/recording.go +++ b/cli/azd/test/recording/recording.go @@ -344,7 +344,7 @@ func Start(t *testing.T, opts ...Options) *Session { return } - t.Fatal("recorderProxy: " + msg) + t.Log("recorderProxy: " + msg) }, Recorder: vcr, },