From b0cd555ac5bc28cfd0631bb1019342a3f5359cef Mon Sep 17 00:00:00 2001 From: Zulfat Nutfullin Date: Mon, 23 Mar 2026 13:46:33 -0700 Subject: [PATCH 1/5] Update create new workspace template, fix storage handling bug in submit --- src/quantum/azext_quantum/operations/job.py | 10 +-------- .../create-workspace-and-assign-role.json | 22 +++++++++++++++++-- 2 files changed, 21 insertions(+), 11 deletions(-) diff --git a/src/quantum/azext_quantum/operations/job.py b/src/quantum/azext_quantum/operations/job.py index a2bed9a417a..f2afb7d2c08 100644 --- a/src/quantum/azext_quantum/operations/job.py +++ b/src/quantum/azext_quantum/operations/job.py @@ -280,19 +280,11 @@ def submit(cmd, resource_group_name, workspace_name, target_id, job_input_file, except (IOError, OSError) as e: raise FileOperationError(f"An error occurred opening the input file: {job_input_file}") from e - # Upload the input file to the workspace's storage account - if storage is None: - from .workspace import get as ws_get - ws = ws_get(cmd, resource_group_name, workspace_name) - if ws.properties.storage_account is None: - raise RequiredArgumentMissingError("No storage account specified or linked with workspace.") - storage = ws.properties.storage_account.split('/')[-1] job_id = str(uuid.uuid4()) blob_name = "inputData" - resource_id = "/subscriptions/" + ws_info.subscription + "/resourceGroups/" + ws_info.resource_group + "/providers/Microsoft.Quantum/Workspaces/" + ws_info.name credential = _get_data_credentials(cmd.cli_ctx, ws_info.subscription) - workspace = Workspace(resource_id=resource_id, credential=credential) + workspace = Workspace(resource_id=resource_id, credential=credential, storage=storage) container_uri = workspace.get_container_uri(job_id=job_id) container_client = ContainerClient.from_container_url(container_uri) 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 ba6ef109e19..b6260dad0e0 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 @@ -65,7 +65,10 @@ } }, "functions": [], - "variables": {}, + "variables": { + "storageAccountContributorRoleId": "17d1049b-9a84-46fb-8f53-869881c3d3ab", + "storageBlobDataContributorRoleId": "ba92f5b4-2d11-453d-a403-e96b0029c9fe" + }, "resources": [ { "type": "Microsoft.Quantum/workspaces", @@ -105,6 +108,7 @@ "kind": "[parameters('storageAccountKind')]", "properties": { "allowBlobPublicAccess": false, + "allowSharedKeyAccess": false, "minimumTlsVersion": "TLS1_2" }, "resources": [ @@ -149,7 +153,21 @@ "type": "Microsoft.Storage/storageAccounts/providers/roleAssignments", "location": "[parameters('storageAccountLocation')]", "properties": { - "roleDefinitionId": "[resourceId('Microsoft.Authorization/roleDefinitions', '17d1049b-9a84-46fb-8f53-869881c3d3ab')]", + "roleDefinitionId": "[resourceId('Microsoft.Authorization/roleDefinitions', variables('storageAccountContributorRoleId'))]", + "principalId": "[reference(concat('Microsoft.Quantum/Workspaces/', parameters('quantumWorkspaceName')), '2019-11-04-preview', 'Full').identity.principalId]", + "principalType": "ServicePrincipal" + }, + "dependsOn": [ + "[parameters('storageAccountId')]" + ] + }, + { + "apiVersion": "2020-04-01-preview", + "name": "[concat(parameters('storageAccountName'), '/Microsoft.Authorization/', guid(reference(concat('Microsoft.Quantum/Workspaces/', parameters('quantumWorkspaceName')), '2019-11-04-preview', 'Full').identity.principalId, variables('storageBlobDataContributorRoleId')))]", + "type": "Microsoft.Storage/storageAccounts/providers/roleAssignments", + "location": "[parameters('storageAccountLocation')]", + "properties": { + "roleDefinitionId": "[resourceId('Microsoft.Authorization/roleDefinitions', variables('storageBlobDataContributorRoleId'))]", "principalId": "[reference(concat('Microsoft.Quantum/Workspaces/', parameters('quantumWorkspaceName')), '2019-11-04-preview', 'Full').identity.principalId]", "principalType": "ServicePrincipal" }, From 259562bc8b470d24f56d575f1142379f9ae40559 Mon Sep 17 00:00:00 2001 From: Zulfat Nutfullin Date: Mon, 23 Mar 2026 17:12:37 -0700 Subject: [PATCH 2/5] Updated History and setup --- src/quantum/HISTORY.rst | 5 +++++ src/quantum/setup.py | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/src/quantum/HISTORY.rst b/src/quantum/HISTORY.rst index b0e6da8c957..25dc66d320b 100644 --- a/src/quantum/HISTORY.rst +++ b/src/quantum/HISTORY.rst @@ -3,6 +3,11 @@ Release History =============== +1.0.0b13 ++++++++++++++++ +* Updated workspace create command to support creation of user delegation SAS for linked storage account with disabled access keys +* Fixed bug where getting linked storage account from the service was used when storage connection string was provided in job submission related commands + 1.0.0b12 +++++++++++++++ * Added support for Data Plane v2 including specifying priority parameter as part of job params when submitting a job diff --git a/src/quantum/setup.py b/src/quantum/setup.py index fbbfaffc3e7..7aa92c04d90 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.0b12' +VERSION = '1.0.0b13' # The full list of classifiers is available at # https://pypi.python.org/pypi?%3Aaction=list_classifiers From 5124d310bdbd7c20772fa45c75289c3ccd1607a8 Mon Sep 17 00:00:00 2001 From: Zulfat Nutfullin Date: Tue, 24 Mar 2026 11:51:43 -0700 Subject: [PATCH 3/5] Disable access keys only for new storage accounts, change default storage kind to v2 --- .../templates/create-workspace-and-assign-role.json | 9 ++++++++- src/quantum/azext_quantum/operations/workspace.py | 8 +++++++- 2 files changed, 15 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 b6260dad0e0..ce42d0364f3 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 @@ -62,6 +62,13 @@ "metadata": { "description": "Deployment name for role assignment operation" } + }, + "storageAccountAllowSharedKeyAccess": { + "type": "bool", + "defaultValue": false, + "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." + } } }, "functions": [], @@ -108,7 +115,7 @@ "kind": "[parameters('storageAccountKind')]", "properties": { "allowBlobPublicAccess": false, - "allowSharedKeyAccess": false, + "allowSharedKeyAccess": "[parameters('storageAccountAllowSharedKeyAccess')]", "minimumTlsVersion": "TLS1_2" }, "resources": [ diff --git a/src/quantum/azext_quantum/operations/workspace.py b/src/quantum/azext_quantum/operations/workspace.py index 113afb218f1..d7656d3e503 100644 --- a/src/quantum/azext_quantum/operations/workspace.py +++ b/src/quantum/azext_quantum/operations/workspace.py @@ -31,7 +31,7 @@ DEFAULT_WORKSPACE_LOCATION = 'westus' DEFAULT_STORAGE_SKU = 'Standard_LRS' DEFAULT_STORAGE_SKU_TIER = 'Standard' -DEFAULT_STORAGE_KIND = 'Storage' +DEFAULT_STORAGE_KIND = 'StorageV2' SUPPORTED_STORAGE_SKU_TIERS = ['Standard'] SUPPORTED_STORAGE_KINDS = ['Storage', 'StorageV2'] DEPLOYMENT_NAME_PREFIX = 'Microsoft.AzureQuantum-' @@ -240,6 +240,7 @@ def create(cmd, resource_group_name, workspace_name, location, storage_account, storage_account_sku_tier = DEFAULT_STORAGE_SKU_TIER storage_account_kind = DEFAULT_STORAGE_KIND storage_account_location = location + storage_account_allow_shared_key_access = False # Secure default for new accounts # Look for info on existing storage account storage_account_list = list_storage_accounts(cmd, resource_group_name) @@ -250,6 +251,10 @@ def create(cmd, resource_group_name, workspace_name, location, storage_account, storage_account_sku_tier = storage_account_info.sku.tier storage_account_kind = storage_account_info.kind storage_account_location = storage_account_info.location + # Preserve the existing account's setting to avoid breaking customers + # who rely on shared-key auth/connection strings for that account. + if storage_account_info.allow_shared_key_access is not None: + storage_account_allow_shared_key_access = storage_account_info.allow_shared_key_access break # Validate the storage account SKU tier and kind @@ -266,6 +271,7 @@ def create(cmd, resource_group_name, workspace_name, location, storage_account, 'storageAccountLocation': storage_account_location, '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()) } parameters = {k: {'value': v} for k, v in parameters.items()} From 2c64b815acc4edf86e67b02416d26d2e1355302e Mon Sep 17 00:00:00 2001 From: Zulfat Nutfullin Date: Tue, 24 Mar 2026 11:56:38 -0700 Subject: [PATCH 4/5] Updated History.rst --- src/quantum/HISTORY.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/src/quantum/HISTORY.rst b/src/quantum/HISTORY.rst index 25dc66d320b..277a63e31e7 100644 --- a/src/quantum/HISTORY.rst +++ b/src/quantum/HISTORY.rst @@ -7,6 +7,7 @@ Release History +++++++++++++++ * Updated workspace create command to support creation of user delegation SAS for linked storage account with disabled access keys * Fixed bug where getting linked storage account from the service was used when storage connection string was provided in job submission related commands +* Changed new storage account to be v2 because of the retirement of General-Purpose v1 (GPv1) storage accounts in October 13, 2026. 1.0.0b12 +++++++++++++++ From b2e82ec45ac6283cc5650d334e9d625c26c7a3c5 Mon Sep 17 00:00:00 2001 From: Zulfat Nutfullin Date: Tue, 24 Mar 2026 15:09:52 -0700 Subject: [PATCH 5/5] Fixed get provider test --- .../azext_quantum/tests/latest/test_quantum_targets.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/quantum/azext_quantum/tests/latest/test_quantum_targets.py b/src/quantum/azext_quantum/tests/latest/test_quantum_targets.py index 12d90c4d531..4ec06eb7cf7 100644 --- a/src/quantum/azext_quantum/tests/latest/test_quantum_targets.py +++ b/src/quantum/azext_quantum/tests/latest/test_quantum_targets.py @@ -66,12 +66,12 @@ def test_get_provider(self): test_target = get_test_target_target() test_expected_provider = get_test_target_provider() - test_returned_provider = get_provider(self, test_target, test_resource_group, test_workspace_temp, test_location) + test_returned_provider = get_provider(self, test_target, test_resource_group, test_workspace_temp) assert test_returned_provider == test_expected_provider test_target = "nonexistant.target" test_expected_provider = None - test_returned_provider = get_provider(self, test_target, test_resource_group, test_workspace_temp, test_location) + test_returned_provider = get_provider(self, test_target, test_resource_group, test_workspace_temp) assert test_returned_provider == test_expected_provider self.cmd(f'az quantum workspace delete -g {test_resource_group} -w {test_workspace_temp}')