Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
ENHANCEMENTS:
* Specify default_outbound_access_enabled = false setting for all subnets ([#4757](https://github.com/microsoft/AzureTRE/pull/4757))
* Pin all GitHub Actions workflow steps to full commit SHAs to prevent supply chain attacks plus update to latest releases ([#4886](https://github.com/microsoft/AzureTRE/pull/4886))
* Allow numeric CIDR masks in `address_space_size` (e.g. "25") when requesting auto-assigned address spaces; accepts numeric strings and validates the mask range. ([#4733](https://github.com/microsoft/AzureTRE/issues/4733))
* Add Windows Server 2025 image support to Guacamole. ([#4890](https://github.com/microsoft/AzureTRE/issues/4890))

## (0.28.0) (March 2, 2026)
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.16"
__version__ = "0.25.17"
18 changes: 15 additions & 3 deletions api_app/db/repositories/workspaces.py
Original file line number Diff line number Diff line change
Expand Up @@ -140,16 +140,28 @@ def automatically_create_application_registration(self, workspace_properties: di

async def get_address_space_based_on_size(self, workspace_properties: dict):
# Default the address space to 'small' if not supplied.
address_space_size = workspace_properties.get("address_space_size", "small").lower()
address_space_size = workspace_properties.get("address_space_size", "small").strip().lower()

# 773 allow custom sized networks to be requested
if (address_space_size == "custom"):
if address_space_size == "custom":
if (await self.validate_address_space(workspace_properties.get("address_space"))):
return workspace_properties.get("address_space")
else:
raise InvalidInput("The custom 'address_space' you requested does not fit in the current network.")

# Default mask is 24 (small)
# If a numeric cidr was provided (e.g. as a string like "25"), accept it
try:
if address_space_size.isdigit():
cidr_netmask = int(address_space_size)
# basic validation for reasonable CIDR mask values
if cidr_netmask < 16 or cidr_netmask > 29:
raise InvalidInput("'address_space_size' numeric value must be between 16 and 29")
return await self.get_new_address_space(cidr_netmask)
except ValueError:
# fall through to predefined handling
pass

# Default mask is 24 (small). Keep backwards compatibility with presets.
cidr_netmask = WorkspaceRepository.predefined_address_spaces.get(address_space_size, 24)
return await self.get_new_address_space(cidr_netmask)

Expand Down
4 changes: 2 additions & 2 deletions api_app/models/schemas/workspace_template.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ def get_sample_workspace_template_object(template_name: str = "tre-workspace-bas
"address_space_size": Property(
type="string",
default="small",
description="This can have a value of small, medium, large or custom. If you specify custom, then you need to specify a VNet address space in 'address_space' (e.g. 10.2.1.0/24)")
description="This can have a value of small, medium, large, a numeric CIDR mask (e.g. \"25\") or custom. If you specify custom, then you need to specify a VNet address space in 'address_space' (e.g. 10.2.1.0/24)")
},
Comment thread
JC-wk marked this conversation as resolved.
customActions=[
CustomAction()
Expand Down Expand Up @@ -73,7 +73,7 @@ class Config:
"address_space_size": {
"type": "string",
"title": "Address space size",
"description": "Network address size (small, medium, large or custom) to be used by the workspace"
"description": "This can have a value of small, medium, large, a numeric CIDR mask (e.g. \"25\") or custom. If you specify custom, then you need to specify a VNet address space in 'address_space' (e.g. 10.2.1.0/24)"
},
"address_space": {
"type": "string",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -342,3 +342,43 @@ async def test_is_workspace_storage_account_available_when_name_not_available(mo

mock_storage_client.return_value.storage_accounts.check_name_availability.assert_called_once_with({"name": f"stgws{workspace_id[-4:]}"})
assert result is False


@pytest.mark.asyncio
@patch('core.config.RESOURCE_LOCATION', "useast2")
@patch('core.config.TRE_ID', "9876")
@patch('core.config.CORE_ADDRESS_SPACE', "10.1.0.0/22")
@patch('core.config.TRE_ADDRESS_SPACE', "10.0.0.0/12")
async def test_get_address_space_based_on_size_with_string_19(workspace_repo, basic_workspace_request):
workspace_to_create = basic_workspace_request
# request a /19
workspace_to_create.properties["address_space_size"] = "19"
address_space = await workspace_repo.get_address_space_based_on_size(workspace_to_create.properties)
assert address_space.endswith('/19')
Comment thread
JC-wk marked this conversation as resolved.


@pytest.mark.asyncio
@patch('core.config.RESOURCE_LOCATION', "useast2")
@patch('core.config.TRE_ID', "9876")
@patch('core.config.CORE_ADDRESS_SPACE', "10.1.0.0/22")
@patch('core.config.TRE_ADDRESS_SPACE', "10.0.0.0/12")
async def test_get_address_space_based_on_size_with_string_29(workspace_repo, basic_workspace_request):
workspace_to_create = basic_workspace_request
# request a /29
workspace_to_create.properties["address_space_size"] = "29"
address_space = await workspace_repo.get_address_space_based_on_size(workspace_to_create.properties)
assert address_space.endswith('/29')


@pytest.mark.asyncio
@patch('core.config.RESOURCE_LOCATION', "useast2")
@patch('core.config.TRE_ID', "9876")
@patch('core.config.CORE_ADDRESS_SPACE', "10.1.0.0/22")
@patch('core.config.TRE_ADDRESS_SPACE', "10.0.0.0/12")
@pytest.mark.parametrize("invalid_size", ["15", "30"])
async def test_get_address_space_based_on_size_with_invalid_string_raises_error(workspace_repo, basic_workspace_request, invalid_size):
workspace_to_create = basic_workspace_request
workspace_to_create.properties["address_space_size"] = invalid_size
with pytest.raises(InvalidInput) as ex:
await workspace_repo.get_address_space_based_on_size(workspace_to_create.properties)
assert str(ex.value) == "'address_space_size' numeric value must be between 16 and 29"
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,7 @@ Some workspace services may require additional address spaces to be provisioned.
To request an additional address space, the workspace service bundle must define an `address_space` parameter in the `porter.yaml` file. The value of this parameter will be provided by API to the resource processor.

The size of the `address_space` will default to `/24`, however other sizes can be requested by including an `address_space_size` as part of the workspace service template.
This parameter accepts the presets `small` (/24), `medium` (/22), `large` (/16), the literal value `custom` together with an explicit `address_space` CIDR (e.g. `10.2.1.0/25`), or a numeric CIDR mask as a string from "16" to "29" (e.g. `"25"`) to ask the system to auto-select an available `/25`.

The `address_space` allocation will only take place during the install phase of a deployment, as this is a breaking change to your template you should increment the major version of your template, this means a you must deploy a new resource instead of upgrading an existing one.

Expand Down
Loading