From e90a5f5e40d79c35ede6e04c540e06cc5ff47ac9 Mon Sep 17 00:00:00 2001 From: s-ddavydenko <113129076+s-ddavydenko@users.noreply.github.com> Date: Mon, 15 Jun 2026 15:47:12 -0700 Subject: [PATCH 1/7] v2 workspace support --- src/quantum/HISTORY.rst | 4 ++++ src/quantum/azext_quantum/_help.py | 4 ++++ src/quantum/azext_quantum/_params.py | 2 ++ .../templates/create-workspace-and-assign-role.json | 10 +++++++++- src/quantum/azext_quantum/operations/workspace.py | 6 ++++-- src/quantum/setup.py | 2 +- 6 files changed, 24 insertions(+), 4 deletions(-) diff --git a/src/quantum/HISTORY.rst b/src/quantum/HISTORY.rst index 3d9d908164e..6cd6d1e8a39 100644 --- a/src/quantum/HISTORY.rst +++ b/src/quantum/HISTORY.rst @@ -3,6 +3,10 @@ Release History =============== +1.0.0b17 ++++++++++++++++ +* Added ``--workspace-kind`` parameter to ``az quantum workspace create`` to support creating V2 workspaces. + 1.0.0b16 +++++++++++++++ * Removed deprecated ``--location``/``-l`` parameter from ``quantum execute``, ``quantum run``, ``quantum job submit``, ``quantum job cancel``, ``quantum job list``, ``quantum job output``, ``quantum job show``, ``quantum job wait``, ``quantum target list``, ``quantum workspace set``, and ``quantum workspace quotas`` commands. diff --git a/src/quantum/azext_quantum/_help.py b/src/quantum/azext_quantum/_help.py index addd20e00da..5707ce1b7df 100644 --- a/src/quantum/azext_quantum/_help.py +++ b/src/quantum/azext_quantum/_help.py @@ -260,6 +260,10 @@ -r "MyProvider1 / MySKU1, MyProvider2 / MySKU2" --skip-autoadd -a MyStorageAccountName\n To display a list of available providers and their SKUs, use the following command: az quantum offerings list -l MyLocation -o table + - name: Create a new Azure Quantum V2 workspace. + text: |- + az quantum workspace create -g MyResourceGroup -w MyWorkspace -l MyLocation \\ + -a MyStorageAccountName --workspace-kind V2 """ helps['quantum workspace delete'] = """ diff --git a/src/quantum/azext_quantum/_params.py b/src/quantum/azext_quantum/_params.py index db958b7b54a..2ff0dd82601 100644 --- a/src/quantum/azext_quantum/_params.py +++ b/src/quantum/azext_quantum/_params.py @@ -55,6 +55,7 @@ def load_arguments(self, _): # pylint: disable=too-many-locals job_output_format_type = CLIArgumentType(help='The expected job output format') entry_point_type = CLIArgumentType(help='The entry point for the QIR program or circuit. Required for some provider QIR jobs.') skip_autoadd_type = CLIArgumentType(help='If specified, the plans that offer free credits will not automatically be added.') + workspace_kind_type = CLIArgumentType(options_list=['--workspace-kind'], help='The kind of the workspace to create. Allowed values: V1, V2.') key_type = CLIArgumentType(options_list=['--key-type'], help='The api keys to be regenerated, should be Primary and/or Secondary.') enable_key_type = CLIArgumentType(options_list=['--enable-api-key'], help='Enable or disable API key authentication.') job_type_type = CLIArgumentType(options_list=['--job-type'], help='Job type to be listed, example "QuantumComputing".') @@ -75,6 +76,7 @@ def load_arguments(self, _): # pylint: disable=too-many-locals c.argument('provider_sku_list', provider_sku_list_type) c.argument('auto_accept', auto_accept_type) c.argument('skip_autoadd', skip_autoadd_type) + c.argument('workspace_kind', workspace_kind_type) with self.argument_context('quantum target') as c: c.argument('workspace_name', workspace_name_type) diff --git a/src/quantum/azext_quantum/operations/templates/create-workspace-and-assign-role.json b/src/quantum/azext_quantum/operations/templates/create-workspace-and-assign-role.json index 32d1fedef93..8c837087461 100644 --- a/src/quantum/azext_quantum/operations/templates/create-workspace-and-assign-role.json +++ b/src/quantum/azext_quantum/operations/templates/create-workspace-and-assign-role.json @@ -69,6 +69,13 @@ "metadata": { "description": "Whether to allow shared key access on the storage account. Defaults to false (secure) for new accounts; existing accounts should pass their current value to avoid breaking changes." } + }, + "workspaceKind": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "The kind of workspace to create: V1 or V2. Empty string means the service default (V1)." + } } }, "functions": [], @@ -88,7 +95,8 @@ }, "properties": { "providers": "[parameters('providers')]", - "storageAccount": "[parameters('storageAccountId')]" + "storageAccount": "[parameters('storageAccountId')]", + "workspaceKind": "[if(empty(parameters('workspaceKind')), json('null'), parameters('workspaceKind'))]" } }, { diff --git a/src/quantum/azext_quantum/operations/workspace.py b/src/quantum/azext_quantum/operations/workspace.py index 77d58c4bff2..f75b55417f3 100644 --- a/src/quantum/azext_quantum/operations/workspace.py +++ b/src/quantum/azext_quantum/operations/workspace.py @@ -195,7 +195,7 @@ def _validate_storage_account(tier_or_kind_msg_text, tier_or_kind, supported_tie def create(cmd, resource_group_name, workspace_name, location, storage_account, skip_role_assignment=False, - provider_sku_list=None, auto_accept=False, skip_autoadd=False): + provider_sku_list=None, auto_accept=False, skip_autoadd=False, workspace_kind=None): """ Create a new Azure Quantum workspace. """ @@ -215,6 +215,7 @@ def create(cmd, resource_group_name, workspace_name, location, storage_account, if skip_role_assignment: _add_quantum_providers(cmd, quantum_workspace, provider_sku_list, auto_accept, skip_autoadd) quantum_workspace.properties.api_key_enabled = True + quantum_workspace.properties.workspace_kind = workspace_kind poller = client.begin_create_or_update(info.resource_group, info.name, quantum_workspace, polling=False) while not poller.done(): time.sleep(POLLING_TIME_DURATION) @@ -269,7 +270,8 @@ def create(cmd, resource_group_name, workspace_name, location, storage_account, 'storageAccountSku': storage_account_sku, 'storageAccountKind': storage_account_kind, 'storageAccountAllowSharedKeyAccess': storage_account_allow_shared_key_access, - 'storageAccountDeploymentName': "Microsoft.StorageAccount-" + time.strftime("%d-%b-%Y-%H-%M-%S", time.gmtime()) + 'storageAccountDeploymentName': "Microsoft.StorageAccount-" + time.strftime("%d-%b-%Y-%H-%M-%S", time.gmtime()), + 'workspaceKind': workspace_kind or '' } parameters = {k: {'value': v} for k, v in parameters.items()} diff --git a/src/quantum/setup.py b/src/quantum/setup.py index bfb9fcd560d..a8046ab6621 100644 --- a/src/quantum/setup.py +++ b/src/quantum/setup.py @@ -17,7 +17,7 @@ # This version should match the latest entry in HISTORY.rst # Also, when updating this, please review the version used by the extension to # submit requests, which can be found at './azext_quantum/__init__.py' -VERSION = '1.0.0b16' +VERSION = '1.0.0b17' # The full list of classifiers is available at # https://pypi.python.org/pypi?%3Aaction=list_classifiers From c076f5970b75e189bc80266c9fec32a91d2fe4b8 Mon Sep 17 00:00:00 2001 From: s-ddavydenko <113129076+s-ddavydenko@users.noreply.github.com> Date: Mon, 15 Jun 2026 15:51:37 -0700 Subject: [PATCH 2/7] unit test for v2 workspace --- .../tests/latest/test_quantum_workspace.py | 34 +++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/src/quantum/azext_quantum/tests/latest/test_quantum_workspace.py b/src/quantum/azext_quantum/tests/latest/test_quantum_workspace.py index f204e172b25..2d0ad1baa83 100644 --- a/src/quantum/azext_quantum/tests/latest/test_quantum_workspace.py +++ b/src/quantum/azext_quantum/tests/latest/test_quantum_workspace.py @@ -319,3 +319,37 @@ def test_autoadd_providers(self): workspace_location = None _autoadd_providers(cmd, providers_in_region, providers_selected, workspace_location, True) assert providers_selected[0] == {"provider_id": "foo", "sku": "foo_credits_for_all_plan", "offer_id": "foo_offer", "publisher_id": "foo0123456789"} + + def test_workspace_kind(self): + print("test_workspace_kind") + from ...vendored_sdks.azure_mgmt_quantum.models import WorkspaceResourceProperties, WorkspaceKind + + # V1 workspace: workspace_kind is None by default (field omitted from request, service defaults to V1) + props = WorkspaceResourceProperties() + assert props.workspace_kind is None + serialized = props.serialize() + assert serialized.get("workspaceKind") is None + + # V1 workspace: explicitly setting V1 serializes to "workspaceKind": "V1" in the JSON body + props = WorkspaceResourceProperties(workspace_kind=WorkspaceKind.V1) + assert props.workspace_kind == "V1" + serialized = props.serialize() + assert serialized.get("workspaceKind") == "V1" + + # V2 workspace: serializes to "workspaceKind": "V2" in the JSON body + props = WorkspaceResourceProperties(workspace_kind=WorkspaceKind.V2) + assert props.workspace_kind == "V2" + serialized = props.serialize() + assert serialized.get("workspaceKind") == "V2" + + # V2 workspace: workspace_kind can be set after construction (as done in the skip_role_assignment path) + props = WorkspaceResourceProperties() + props.workspace_kind = WorkspaceKind.V2 + assert props.workspace_kind == "V2" + serialized = props.serialize() + assert serialized.get("workspaceKind") == "V2" + + # ARM template parameter expression: workspace_kind or '' gives '' for None/V1-default, value otherwise + assert (None or '') == '' + assert (WorkspaceKind.V1 or '') == "V1" + assert (WorkspaceKind.V2 or '') == "V2" From 7e6aea98e1daa29c3c47c8f43a3d0fa77eedcb79 Mon Sep 17 00:00:00 2001 From: s-ddavydenko <113129076+s-ddavydenko@users.noreply.github.com> Date: Wed, 17 Jun 2026 15:06:56 -0700 Subject: [PATCH 3/7] omit workspaceKind rather than set to null --- .../create-workspace-and-assign-role.json | 8 +--- .../tests/latest/test_quantum_workspace.py | 37 +++++-------------- 2 files changed, 11 insertions(+), 34 deletions(-) diff --git a/src/quantum/azext_quantum/operations/templates/create-workspace-and-assign-role.json b/src/quantum/azext_quantum/operations/templates/create-workspace-and-assign-role.json index 8c837087461..0686ade8c08 100644 --- a/src/quantum/azext_quantum/operations/templates/create-workspace-and-assign-role.json +++ b/src/quantum/azext_quantum/operations/templates/create-workspace-and-assign-role.json @@ -74,7 +74,7 @@ "type": "string", "defaultValue": "", "metadata": { - "description": "The kind of workspace to create: V1 or V2. Empty string means the service default (V1)." + "description": "The kind of workspace to create: V1 or V2. Empty string omits the property entirely, letting the service apply its default (V1)." } } }, @@ -93,11 +93,7 @@ "identity": { "type": "SystemAssigned" }, - "properties": { - "providers": "[parameters('providers')]", - "storageAccount": "[parameters('storageAccountId')]", - "workspaceKind": "[if(empty(parameters('workspaceKind')), json('null'), parameters('workspaceKind'))]" - } + "properties": "[union(createObject('providers', parameters('providers'), 'storageAccount', parameters('storageAccountId')), if(empty(parameters('workspaceKind')), createObject(), createObject('workspaceKind', parameters('workspaceKind'))))]" }, { "apiVersion": "2019-10-01", diff --git a/src/quantum/azext_quantum/tests/latest/test_quantum_workspace.py b/src/quantum/azext_quantum/tests/latest/test_quantum_workspace.py index 2d0ad1baa83..92321b5bbd6 100644 --- a/src/quantum/azext_quantum/tests/latest/test_quantum_workspace.py +++ b/src/quantum/azext_quantum/tests/latest/test_quantum_workspace.py @@ -321,35 +321,16 @@ def test_autoadd_providers(self): assert providers_selected[0] == {"provider_id": "foo", "sku": "foo_credits_for_all_plan", "offer_id": "foo_offer", "publisher_id": "foo0123456789"} def test_workspace_kind(self): - print("test_workspace_kind") from ...vendored_sdks.azure_mgmt_quantum.models import WorkspaceResourceProperties, WorkspaceKind - # V1 workspace: workspace_kind is None by default (field omitted from request, service defaults to V1) - props = WorkspaceResourceProperties() - assert props.workspace_kind is None - serialized = props.serialize() - assert serialized.get("workspaceKind") is None - - # V1 workspace: explicitly setting V1 serializes to "workspaceKind": "V1" in the JSON body - props = WorkspaceResourceProperties(workspace_kind=WorkspaceKind.V1) - assert props.workspace_kind == "V1" - serialized = props.serialize() - assert serialized.get("workspaceKind") == "V1" - - # V2 workspace: serializes to "workspaceKind": "V2" in the JSON body - props = WorkspaceResourceProperties(workspace_kind=WorkspaceKind.V2) - assert props.workspace_kind == "V2" - serialized = props.serialize() - assert serialized.get("workspaceKind") == "V2" - - # V2 workspace: workspace_kind can be set after construction (as done in the skip_role_assignment path) + # No kind set: field must be absent from the serialized body (not null) + assert "workspaceKind" not in WorkspaceResourceProperties().serialize() + + # V1 and V2 serialize to their expected wire values + assert WorkspaceResourceProperties(workspace_kind=WorkspaceKind.V1).serialize().get("workspaceKind") == "V1" + assert WorkspaceResourceProperties(workspace_kind=WorkspaceKind.V2).serialize().get("workspaceKind") == "V2" + + # workspace_kind can be assigned after construction (skip_role_assignment path) props = WorkspaceResourceProperties() props.workspace_kind = WorkspaceKind.V2 - assert props.workspace_kind == "V2" - serialized = props.serialize() - assert serialized.get("workspaceKind") == "V2" - - # ARM template parameter expression: workspace_kind or '' gives '' for None/V1-default, value otherwise - assert (None or '') == '' - assert (WorkspaceKind.V1 or '') == "V1" - assert (WorkspaceKind.V2 or '') == "V2" + assert props.serialize().get("workspaceKind") == "V2" From 8408499d5058c64cac77d1b5671ba11f0880ce56 Mon Sep 17 00:00:00 2001 From: s-ddavydenko <113129076+s-ddavydenko@users.noreply.github.com> Date: Wed, 17 Jun 2026 15:07:42 -0700 Subject: [PATCH 4/7] style --- .../azext_quantum/tests/latest/test_quantum_workspace.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/quantum/azext_quantum/tests/latest/test_quantum_workspace.py b/src/quantum/azext_quantum/tests/latest/test_quantum_workspace.py index 92321b5bbd6..ae84e729383 100644 --- a/src/quantum/azext_quantum/tests/latest/test_quantum_workspace.py +++ b/src/quantum/azext_quantum/tests/latest/test_quantum_workspace.py @@ -333,4 +333,4 @@ def test_workspace_kind(self): # workspace_kind can be assigned after construction (skip_role_assignment path) props = WorkspaceResourceProperties() props.workspace_kind = WorkspaceKind.V2 - assert props.serialize().get("workspaceKind") == "V2" + assert props.serialize().get("workspaceKind") == "V2" \ No newline at end of file From e4b105468c2b7c070b850439ad2e262e8394c7e1 Mon Sep 17 00:00:00 2001 From: s-ddavydenko <113129076+s-ddavydenko@users.noreply.github.com> Date: Wed, 17 Jun 2026 15:11:58 -0700 Subject: [PATCH 5/7] enforce specific choices for workspaceKind --- src/quantum/azext_quantum/_params.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/quantum/azext_quantum/_params.py b/src/quantum/azext_quantum/_params.py index 2ff0dd82601..95f272297f7 100644 --- a/src/quantum/azext_quantum/_params.py +++ b/src/quantum/azext_quantum/_params.py @@ -55,7 +55,7 @@ def load_arguments(self, _): # pylint: disable=too-many-locals job_output_format_type = CLIArgumentType(help='The expected job output format') entry_point_type = CLIArgumentType(help='The entry point for the QIR program or circuit. Required for some provider QIR jobs.') skip_autoadd_type = CLIArgumentType(help='If specified, the plans that offer free credits will not automatically be added.') - workspace_kind_type = CLIArgumentType(options_list=['--workspace-kind'], help='The kind of the workspace to create. Allowed values: V1, V2.') + workspace_kind_type = CLIArgumentType(options_list=['--workspace-kind'], help='The kind of the workspace to create.', choices=['V1', 'V2']) key_type = CLIArgumentType(options_list=['--key-type'], help='The api keys to be regenerated, should be Primary and/or Secondary.') enable_key_type = CLIArgumentType(options_list=['--enable-api-key'], help='Enable or disable API key authentication.') job_type_type = CLIArgumentType(options_list=['--job-type'], help='Job type to be listed, example "QuantumComputing".') From 25dcf5e7502405721acd2aec61eb2dba8ff316b7 Mon Sep 17 00:00:00 2001 From: s-ddavydenko <113129076+s-ddavydenko@users.noreply.github.com> Date: Tue, 23 Jun 2026 15:36:31 -0700 Subject: [PATCH 6/7] Default to V1 --- .../templates/create-workspace-and-assign-role.json | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/quantum/azext_quantum/operations/templates/create-workspace-and-assign-role.json b/src/quantum/azext_quantum/operations/templates/create-workspace-and-assign-role.json index 0686ade8c08..befefd495e8 100644 --- a/src/quantum/azext_quantum/operations/templates/create-workspace-and-assign-role.json +++ b/src/quantum/azext_quantum/operations/templates/create-workspace-and-assign-role.json @@ -74,7 +74,7 @@ "type": "string", "defaultValue": "", "metadata": { - "description": "The kind of workspace to create: V1 or V2. Empty string omits the property entirely, letting the service apply its default (V1)." + "description": "The kind of workspace to create: V1 or V2. Empty string defaults to V1." } } }, @@ -93,7 +93,11 @@ "identity": { "type": "SystemAssigned" }, - "properties": "[union(createObject('providers', parameters('providers'), 'storageAccount', parameters('storageAccountId')), if(empty(parameters('workspaceKind')), createObject(), createObject('workspaceKind', parameters('workspaceKind'))))]" + "properties": { + "providers": "[parameters('providers')]", + "storageAccount": "[parameters('storageAccountId')]", + "workspaceKind": "[if(empty(parameters('workspaceKind')), 'V1', parameters('workspaceKind'))]" + } }, { "apiVersion": "2019-10-01", From 4c45ba1c416f4a964b12a46907f8246039f87475 Mon Sep 17 00:00:00 2001 From: s-ddavydenko <113129076+s-ddavydenko@users.noreply.github.com> Date: Thu, 25 Jun 2026 15:46:33 -0700 Subject: [PATCH 7/7] fixing default behavior --- .../templates/create-workspace-and-assign-role.json | 6 +++--- src/quantum/azext_quantum/operations/workspace.py | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/quantum/azext_quantum/operations/templates/create-workspace-and-assign-role.json b/src/quantum/azext_quantum/operations/templates/create-workspace-and-assign-role.json index befefd495e8..c82e4937c5a 100644 --- a/src/quantum/azext_quantum/operations/templates/create-workspace-and-assign-role.json +++ b/src/quantum/azext_quantum/operations/templates/create-workspace-and-assign-role.json @@ -72,9 +72,9 @@ }, "workspaceKind": { "type": "string", - "defaultValue": "", + "defaultValue": "V1", "metadata": { - "description": "The kind of workspace to create: V1 or V2. Empty string defaults to V1." + "description": "The kind of workspace to create: V1 or V2. Defaults to V1." } } }, @@ -96,7 +96,7 @@ "properties": { "providers": "[parameters('providers')]", "storageAccount": "[parameters('storageAccountId')]", - "workspaceKind": "[if(empty(parameters('workspaceKind')), 'V1', parameters('workspaceKind'))]" + "workspaceKind": "[parameters('workspaceKind')]" } }, { diff --git a/src/quantum/azext_quantum/operations/workspace.py b/src/quantum/azext_quantum/operations/workspace.py index f75b55417f3..b581d74cac5 100644 --- a/src/quantum/azext_quantum/operations/workspace.py +++ b/src/quantum/azext_quantum/operations/workspace.py @@ -215,7 +215,7 @@ def create(cmd, resource_group_name, workspace_name, location, storage_account, if skip_role_assignment: _add_quantum_providers(cmd, quantum_workspace, provider_sku_list, auto_accept, skip_autoadd) quantum_workspace.properties.api_key_enabled = True - quantum_workspace.properties.workspace_kind = workspace_kind + quantum_workspace.properties.workspace_kind = workspace_kind or 'V1' poller = client.begin_create_or_update(info.resource_group, info.name, quantum_workspace, polling=False) while not poller.done(): time.sleep(POLLING_TIME_DURATION) @@ -271,7 +271,7 @@ def create(cmd, resource_group_name, workspace_name, location, storage_account, 'storageAccountKind': storage_account_kind, 'storageAccountAllowSharedKeyAccess': storage_account_allow_shared_key_access, 'storageAccountDeploymentName': "Microsoft.StorageAccount-" + time.strftime("%d-%b-%Y-%H-%M-%S", time.gmtime()), - 'workspaceKind': workspace_kind or '' + 'workspaceKind': workspace_kind or 'V1' } parameters = {k: {'value': v} for k, v in parameters.items()}