{"record":{"id":"6da7268e472bad1b","repo":"microsoft/aspire","slug":"the-new-azure-sandbox-deployment-resource-name-succeeded-but","errorCode":null,"errorMessage":"The new Azure sandbox deployment '{resource.Name}' succeeded, but the previous generation could not be removed after a security-relevant endpoint change. The new deployment state was preserved, but the deployment is reported as failed so the older endpoint is not silently treated as secured.","messagePattern":"The new Azure sandbox deployment '(.+?)' succeeded, but the previous generation could not be removed after a security-relevant endpoint change\\. The new deployment state was preserved, but the deployment is reported as failed so the older endpoint is not silently treated as secured\\.","errorType":"exception","errorClass":"InvalidOperationException","httpStatus":null,"severity":"critical","filePath":"src/Aspire.Hosting.Azure.Sandboxes/AzureSandboxContainerDeployment.cs","lineNumber":490,"sourceCode":"                if (securityConfigurationChanged ||\n                    pendingOwnerCleanupIds.Count > 0 ||\n                    pendingLegacyDeploymentCleanup is not null)\n                {\n                    stateSection.Data[\"PendingSecurityCleanup\"] = false;\n                    stateSection.Data.Remove(\"PendingOwnerCleanupIds\");\n                    stateSection.Data.Remove(\"PendingLegacyDeploymentCleanup\");\n                    await deploymentStateManager.SaveSectionAsync(stateSection, context.CancellationToken).ConfigureAwait(false);\n                }\n            }\n            catch (Exception ex) when (ex is not OperationCanceledException)\n            {\n                context.Logger.LogWarning(\n                    ex,\n                    \"Best-effort pruning failed after Azure sandbox deployment '{ResourceName}' completed. The new deployment remains active and its state was preserved.\",\n                    resource.Name);\n                if (securityConfigurationChanged)\n                {\n                    throw new InvalidOperationException(\n                        $\"The new Azure sandbox deployment '{resource.Name}' succeeded, but the previous generation could not be removed after a security-relevant endpoint change. The new deployment state was preserved, but the deployment is reported as failed so the older endpoint is not silently treated as secured.\",\n                        ex);\n                }\n            }\n\n            if (portStates.FirstOrDefault() is JsonObject firstPort && firstPort[\"Url\"]?.GetValue<string>() is { } publicUrl)\n            {\n                var retainedUrl = securityConfigurationChanged || ownerChanged\n                    ? null\n                    : GetFirstStateUrl(previousStateSection);\n                context.Summary.Add(resource.TargetResource.Name, new MarkdownString(CreateSandboxUrlSummary(publicUrl, retainedUrl)));\n            }\n            else\n            {\n                context.Summary.Add(resource.TargetResource.Name, new MarkdownString($\"Sandbox `{sandboxId}`\"));\n            }\n        }\n        catch","sourceCodeStart":472,"sourceCodeEnd":508,"githubUrl":"https://github.com/microsoft/aspire/blob/25830f84bd145686607ad00c057b3f84e2e51d43/src/Aspire.Hosting.Azure.Sandboxes/AzureSandboxContainerDeployment.cs#L472-L508","documentation":"After a successful new sandbox deployment, Aspire prunes the previous generation as a best-effort step. If pruning fails AND the deployment involved a security-relevant endpoint change, the method rethrows as InvalidOperationException so the failure is visible; otherwise the old (possibly insecure) endpoint would remain reachable while the deployment reports success.","triggerScenarios":"DeployAsync succeeds and creates the new deployment, then the best-effort cleanup of the prior generation throws (e.g. Azure API error deleting the old deployment), while securityConfigurationChanged is true because an endpoint security setting changed between generations.","commonSituations":"Rotating an endpoint from public to secured (or changing auth) and hitting a transient Azure API failure or permission gap when deleting the old deployment generation.","solutions":["Inspect the inner exception (logged as a warning) to see why pruning failed — usually RBAC permissions or a transient ARM error — and fix that.","Manually delete the old generation/endpoint in the Azure portal, then re-run the deployment.","Verify the service principal/contributor has delete permissions on the sandbox resource group; retry the deployment after permissions are granted."],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":"// before deploying with an endpoint security change, verify delete rights:\n// check role assignments include Contributor/Owner on the sandbox resource group","typeGuard":null,"tryCatchPattern":"try { await deployment.DeployAsync(...); }\ncatch (InvalidOperationException ex) when (ex.Message.Contains(\"previous generation could not be removed\"))\n{\n    // new deployment is ACTIVE despite failure report; inspect inner ex,\n    // manually remove the old generation/endpoint, then redeploy or verify manually\n}","preventionTips":["Grant the deploying identity delete permissions on the resource group before rotating endpoint security.","After changing endpoint security settings, monitor deployments closely and check for leftover old endpoints in the portal.","Read the inner exception — pruning failures are usually transient ARM errors or RBAC gaps."],"tags":["azure","sandboxes","deployment","security"],"backgroundTag":"internal-invariant-violation","analyzedSha":"25830f84bd145686607ad00c057b3f84e2e51d43","analyzedAt":"2026-09-16T11:10:06.193Z","contentChangedAt":"2026-09-16T11:10:06.193Z","schemaVersion":2},"datasetVersion":"2026-09-21T04:17:39.646Z"}