Skip to content
2 changes: 1 addition & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
* _No changes yet_

ENHANCEMENTS:
* _No changes yet_
* `address_spaces` will now be removed from a workspace when a workspace service that uses an `address_space` is deleted to prevent IP address range exhaustion ([#4727](https://github.com/microsoft/AzureTRE/issues/4727))

Comment on lines 6 to 8
Copy link

Copilot AI Feb 10, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The CHANGELOG entry for address space deallocation reads like a bug fix (existing behavior leaking address ranges) but is currently listed under ENHANCEMENTS. Please move this bullet under the BUG FIXES section in the Unreleased block to match the project’s changelog categorization.

Copilot uses AI. Check for mistakes.
BUG FIXES:
* Fix property substitution not occuring where there is only a main step in the pipeline ([#4824](https://github.com/microsoft/AzureTRE/issues/4824))
Expand Down
2 changes: 1 addition & 1 deletion api_app/_version.py
Original file line number Diff line number Diff line change
@@ -1 +1 @@
__version__ = "0.25.14"
__version__ = "0.25.15"
33 changes: 32 additions & 1 deletion api_app/service_bus/deployment_status_updater.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
from pydantic import ValidationError, parse_obj_as

from api.routes.resource_helpers import get_timestamp
from models.domain.resource import Output
from models.domain.resource import Output, ResourceType
from db.repositories.resources_history import ResourceHistoryRepository
from models.domain.request_action import RequestAction
from db.repositories.resource_templates import ResourceTemplateRepository
Expand All @@ -21,6 +21,9 @@
from models.domain.operation import DeploymentStatusUpdateMessage, Operation, OperationStep, Status
from resources import strings
from services.logging import logger, tracer
from db.repositories.workspaces import WorkspaceRepository
from models.schemas.resource import ResourcePatch
from azure.cosmos.exceptions import CosmosAccessConditionFailedError


class DeploymentStatusUpdater():
Expand Down Expand Up @@ -187,6 +190,34 @@ async def update_status_in_database(self, message: DeploymentStatusUpdateMessage
next_step.status = Status.UpdatingFailed
await self.update_overall_operation_status(operation, next_step, is_last_step)
await self.operations_repo.update_item(operation)
# If the 'main' step succeeded for an uninstall operation, free any allocated address space
# owned by a WorkspaceService resource. We trigger cleanup when the step with templateStepId == 'main'
# is successful; this ensures the primary resource has been destroyed successfully before attempting to free the ip address space
try:
# if the step that just succeeded is the main step for this operation, and this is an uninstall,
# proceed with post-uninstall cleanup. No need to scan the operation.steps list again.
if step_to_update.templateStepId == "main" and step_to_update.is_success() and operation.action == RequestAction.UnInstall:
if resource_to_persist.get("resourceType") == ResourceType.WorkspaceService:
address_to_free = resource_to_persist.get("properties", {}).get("address_space")
parent_workspace_id = resource_to_persist.get("workspaceId")
if address_to_free and parent_workspace_id:
try:
workspace_repo = await WorkspaceRepository.create()
workspace = await workspace_repo.get_workspace_by_id(parent_workspace_id)
workspace_address_spaces = workspace.properties.get("address_spaces", [])
if address_to_free in workspace_address_spaces:
new_address_spaces = [a for a in workspace_address_spaces if a != address_to_free]
workspace_patch = ResourcePatch()
workspace_patch.properties = {"address_spaces": new_address_spaces}
try:
await workspace_repo.patch_workspace(workspace, workspace_patch, workspace.etag, self.resource_template_repo, self.resource_history_repo, operation.user, False)
logger.info(f"Freed address space {address_to_free} from workspace {parent_workspace_id} after successful uninstall of {resource_id}")
Comment on lines +193 to +214
Copy link

Copilot AI Feb 10, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This PR adds new post-uninstall behavior (removing the workspace-service's address_space from the parent workspace’s address_spaces). There are already service bus updater tests under api_app/tests_ma/test_service_bus/; please add a focused unit test covering this new cleanup path (successful uninstall main step for a WorkspaceService) and asserting the workspace patch is applied (and not applied when address_space/workspaceId are missing).

Copilot generated this review using guidance from repository custom instructions.
except CosmosAccessConditionFailedError:
logger.exception("ETag conflict when freeing workspace address space after successful uninstall")
Comment on lines +212 to +216
Copy link

Copilot AI Feb 10, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If patching the workspace fails with an ETag conflict here, the code only logs and then continues. That can permanently leak the address space (no retry and the uninstall pipeline will keep running). Consider reusing the existing retry logic used elsewhere for Cosmos ETag mismatches (or implement a small bounded retry) so address space cleanup is resilient under concurrent workspace updates.

Copilot uses AI. Check for mistakes.
except Exception:
logger.exception("Failed to free workspace address space after successful uninstall")
except Exception:
logger.exception("Unexpected error during post-uninstall address space cleanup")

result = True

Expand Down
2 changes: 1 addition & 1 deletion templates/workspace_services/azureml/porter.yaml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
---
schemaVersion: 1.0.0
name: tre-service-azureml
version: 1.1.2
version: 1.1.3
description: "An Azure TRE service for Azure Machine Learning"
registry: azuretre
dockerfile: Dockerfile.tmpl
Expand Down
7 changes: 7 additions & 0 deletions templates/workspace_services/azureml/template_schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -417,6 +417,13 @@
},
{
"stepId": "main"
},
{
"stepId": "f720975a-c81e-477e-854e-53fde86e5e57",
"stepTitle": "Upgrade to ensure workspace is aware of address space removal",
"resourceType": "workspace",
"resourceAction": "upgrade",
"properties": []
}
]
}
Expand Down
2 changes: 1 addition & 1 deletion templates/workspace_services/databricks/porter.yaml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
---
schemaVersion: 1.0.0
name: tre-service-databricks
version: 1.0.14
version: 1.0.15
description: "An Azure TRE service for Azure Databricks."
registry: azuretre
dockerfile: Dockerfile.tmpl
Expand Down
39 changes: 31 additions & 8 deletions templates/workspace_services/databricks/template_schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,9 @@
"name": "databricks",
"description": "Communication with Azure Databricks dependancies.",
"source_addresses": "{{ resource.properties.databricks_address_prefixes }}",
"destination_addresses": [ "AzureDatabricks"],
"destination_addresses": [
"AzureDatabricks"
],
"destination_ports": [
"443"
],
Expand Down Expand Up @@ -114,9 +116,15 @@
"name": "AzureAD",
"description": "AAD access",
"source_addresses": "{{ resource.properties.workspace_address_spaces }}",
"destination_addresses": ["AzureActiveDirectory"],
"destination_ports": ["*"],
"protocols": ["TCP"]
"destination_addresses": [
"AzureActiveDirectory"
],
"destination_ports": [
"*"
],
"protocols": [
"TCP"
]
}
]
}
Expand Down Expand Up @@ -212,7 +220,9 @@
"name": "databricks",
"description": "Communication with Azure Databricks dependancies.",
"source_addresses": "{{ resource.properties.databricks_address_prefixes }}",
"destination_addresses": [ "AzureDatabricks"],
"destination_addresses": [
"AzureDatabricks"
],
"destination_ports": [
"443"
],
Expand Down Expand Up @@ -248,9 +258,15 @@
"name": "AzureAD",
"description": "AAD access",
"source_addresses": "{{ resource.properties.workspace_address_spaces }}",
"destination_addresses": ["AzureActiveDirectory"],
"destination_ports": ["*"],
"protocols": ["TCP"]
"destination_addresses": [
"AzureActiveDirectory"
],
"destination_ports": [
"*"
],
"protocols": [
"TCP"
]
}
]
}
Expand Down Expand Up @@ -352,6 +368,13 @@
},
{
"stepId": "main"
},
{
"stepId": "9c4dc64b-8fbf-4e77-a7f6-48fb33423504",
"stepTitle": "Upgrade to ensure workspace is aware of address space removal",
"resourceType": "workspace",
"resourceAction": "upgrade",
"properties": []
}
]
}
Expand Down
Loading