Skip to content

[SQL] Add support for versionless TDE keys#32764

Open
viparek wants to merge 2 commits intoAzure:devfrom
viparek:viparek/versionlesskeys
Open

[SQL] Add support for versionless TDE keys#32764
viparek wants to merge 2 commits intoAzure:devfrom
viparek:viparek/versionlesskeys

Conversation

@viparek
Copy link
Contributor

@viparek viparek commented Feb 10, 2026

Related command

  • az sql server key create
  • az sql server key show
  • az sql server tde-key set
  • az sql server tde-key show
  • az sql db create
  • az sql db show
  • az sql db update

Description
This PR adds the support for versionless TDE AKV keys for server and database level CMK

Testing Guide

History Notes


This checklist is used to make sure that common guidelines for a pull request are followed.

@azure-client-tools-bot-prd
Copy link

Validation for Azure CLI Full Test Starting...

Thanks for your contribution!

@azure-client-tools-bot-prd
Copy link

Validation for Breaking Change Starting...

Thanks for your contribution!

@yonzhan
Copy link
Collaborator

yonzhan commented Feb 10, 2026

Thank you for your contribution! We will review the pull request and get back to you soon.

@github-actions
Copy link

The git hooks are available for azure-cli and azure-cli-extensions repos. They could help you run required checks before creating the PR.

Please sync the latest code with latest dev branch (for azure-cli) or main branch (for azure-cli-extensions).
After that please run the following commands to enable git hooks:

pip install azdev --upgrade
azdev setup -c <your azure-cli repo path> -r <your azure-cli-extensions repo path>

@yonzhan yonzhan assigned calvinhzy and unassigned evelyn-ys Feb 10, 2026
@yonzhan yonzhan removed the request for review from evelyn-ys February 10, 2026 09:38
@viparek viparek marked this pull request as ready for review February 13, 2026 20:57
Copilot AI review requested due to automatic review settings February 13, 2026 20:57
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This pull request adds support for versionless Transparent Data Encryption (TDE) Azure Key Vault (AKV) keys for SQL server and database-level Customer Managed Keys (CMK). This enhancement allows users to specify AKV keys without a version identifier, enabling automatic key rotation.

Changes:

  • Updates azure-mgmt-sql dependency from 4.0.0b22 to 4.0.0b23 across all platform-specific requirements files
  • Modifies _get_server_key_name_from_uri function to parse and handle versionless key URIs
  • Adds comprehensive test coverage for versionless key operations (create, show, set, delete)

Reviewed changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated 6 comments.

Show a summary per file
File Description
requirements.py3.windows.txt Updates azure-mgmt-sql dependency to 4.0.0b23 for Windows
requirements.py3.Linux.txt Updates azure-mgmt-sql dependency to 4.0.0b23 for Linux
requirements.py3.Darwin.txt Updates azure-mgmt-sql dependency to 4.0.0b23 for macOS
custom.py Modifies URI parsing logic and regex to support versionless AKV keys, adds mdep.azure.net domain
test_sql_commands.py Adds comprehensive test coverage for versionless key CRUD operations

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

# Segments = ["/", "keys/", "someKey/", "01234567890123456789012345678901"]
# Therefore, a versionless key uri will have a segment array of length 3 and a versioned key uri will have a segment array of length 4.
#
isVersionlessKeyId = uri.Segments.Length == 3
Copy link

Copilot AI Feb 13, 2026

Choose a reason for hiding this comment

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

This line contains invalid Python syntax. In Python, strings don't have a Segments attribute or a Length property. This appears to be C# code accidentally included in the Python file. To determine if the URI is versionless, you should check if the URI ends with '/' or if splitting by '/' yields the key name as the last element (without a version). For example: version = uri.rstrip('/').split('/')[-1] and then check if version == key or if the URI ends with the key name.

Copilot uses AI. Check for mistakes.
Comment on lines 4820 to 4822
vault = uri.split('.')[0].split('/')[-1]
key = uri.split('/')[-2]
version = uri.split('/')[-1]
Copy link

Copilot AI Feb 13, 2026

Choose a reason for hiding this comment

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

When the URI ends with a trailing slash (e.g., "https://vault.azure.net/keys/keyname/"), line 4822 will set version to an empty string, and line 4821 will incorrectly extract the key. For a versionless URI ending without trailing slash (e.g., "https://vault.azure.net/keys/keyname"), version will equal key, which will also cause issues. The logic needs to check whether the URI is versionless before extracting the version variable.

Copilot uses AI. Check for mistakes.
import re

match = re.match(r'https://(.)+\.(managedhsm.azure.net|managedhsm-preview.azure.net|vault.azure.net|vault-int.azure-int.net|vault.azure.cn|managedhsm.azure.cn|vault.usgovcloudapi.net|managedhsm.usgovcloudapi.net|vault.microsoftazure.de|managedhsm.microsoftazure.de|vault.cloudapi.eaglex.ic.gov|vault.cloudapi.microsoft.scloud)(:443)?\/keys/[^\/]+\/[0-9a-zA-Z]+$', uri)
match = re.match(r'^https://(?!.*\.\.)[a-zA-Z0-9][a-zA-Z0-9.-]+[a-zA-Z0-9]\.(managedhsm.azure.net|managedhsm-preview.azure.net|vault.azure.net|vault-int.azure-int.net|vault.azure.cn|managedhsm.azure.cn|vault.usgovcloudapi.net|managedhsm.usgovcloudapi.net|vault.microsoftazure.de|managedhsm.microsoftazure.de|vault.cloudapi.eaglex.ic.gov|vault.cloudapi.microsoft.scloud|mdep.azure.net)(:443)?\/keys/[^\/]+(\/[0-9a-zA-Z]+|\/|)$', uri)
Copy link

Copilot AI Feb 13, 2026

Choose a reason for hiding this comment

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

The regex pattern allows URIs ending with '/' (via the \/| option), but this creates ambiguity in parsing. When the URI is "https://vault.azure.net/keys/keyname/", the pattern matches but uri.split('/')[-2] will get "keyname" and uri.split('/')[-1] will get an empty string. However, the comment in the error message says versionless keys should be "https://YourVaultName.vault.azure.net/keys/YourKeyName" (without trailing slash). Consider removing the \/| option from the regex to enforce the format without trailing slash, which will make parsing more straightforward.

Copilot uses AI. Check for mistakes.
Comment on lines +4824 to +4829
# The AKV segments is an array of the uri components.
# For AKV uri "https://someVault.vault.azure.net/keys/someKey/01234567890123456789012345678901", the segment contents are as follows:
# Segments = ["/", "keys/", "someKey/", "01234567890123456789012345678901"]
# Therefore, a versionless key uri will have a segment array of length 3 and a versioned key uri will have a segment array of length 4.
#
isVersionlessKeyId = uri.Segments.Length == 3
Copy link

Copilot AI Feb 13, 2026

Choose a reason for hiding this comment

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

The comment describes "Segments" array and references C# URI properties (uri.Segments.Length), but this is Python code. The comment should be updated to accurately describe Python string manipulation. For example: "For AKV uri 'https://someVault.vault.azure.net/keys/someKey/01234567890123456789012345678901', splitting by '/' yields ['https:', '', 'someVault.vault.azure.net', 'keys', 'someKey', '01234567890123456789012345678901']. A versionless key uri will have fewer segments after splitting."

Suggested change
# The AKV segments is an array of the uri components.
# For AKV uri "https://someVault.vault.azure.net/keys/someKey/01234567890123456789012345678901", the segment contents are as follows:
# Segments = ["/", "keys/", "someKey/", "01234567890123456789012345678901"]
# Therefore, a versionless key uri will have a segment array of length 3 and a versioned key uri will have a segment array of length 4.
#
isVersionlessKeyId = uri.Segments.Length == 3
# For an AKV or Managed HSM key id, we can split the URI on '/' to inspect its components.
# For example, for AKV uri
# "https://someVault.vault.azure.net/keys/someKey/01234567890123456789012345678901"
# splitting by '/' yields:
# ["https:", "", "someVault.vault.azure.net", "keys", "someKey", "01234567890123456789012345678901"]
# A versionless key uri such as "https://someVault.vault.azure.net/keys/someKey"
# becomes:
# ["https:", "", "someVault.vault.azure.net", "keys", "someKey"]
# Therefore, after stripping any trailing '/', a versionless key id has 5 segments and
# a versioned key id has 6 segments.
#
uri_segments = uri.rstrip('/').split('/')
isVersionlessKeyId = len(uri_segments) == 5

Copilot uses AI. Check for mistakes.
Comment on lines +4829 to +4831
isVersionlessKeyId = uri.Segments.Length == 3

if isVersionlessKeyId:
Copy link

Copilot AI Feb 13, 2026

Choose a reason for hiding this comment

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

Variable name isVersionlessKeyId does not follow Python naming conventions. Python uses snake_case for variable names, not camelCase. This should be is_versionless_key_id.

Suggested change
isVersionlessKeyId = uri.Segments.Length == 3
if isVersionlessKeyId:
is_versionless_key_id = uri.Segments.Length == 3
if is_versionless_key_id:

Copilot uses AI. Check for mistakes.
Comment on lines +4631 to +4639
# create a versionless key
versionless_key_name = self.create_random_name(resource_prefix + 'vless', 32)
versionless_key_resp = self.cmd('keyvault key create -n {} -p software --vault-name {}'
.format(versionless_key_name, key_vault)).get_output_in_json()
versioned_kid = versionless_key_resp['key']['kid']

# extract versionless key identifier (remove the version part)
# kid format: https://{vault}.vault.azure.net/keys/{keyname}/{version}
versionless_kid = '/'.join(versioned_kid.split('/')[:-1])
Copy link

Copilot AI Feb 13, 2026

Choose a reason for hiding this comment

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

The test only covers versionless key URIs without a trailing slash (line 4639 creates URIs like "https://vault.azure.net/keys/keyname"). However, the regex pattern in custom.py (line 4812) also accepts URIs with trailing slashes or even just ending with "/keys/keyname/". Consider adding test cases for these edge cases to ensure the parsing logic handles all accepted formats correctly, or update the regex to be more restrictive if these formats should not be supported.

Copilot uses AI. Check for mistakes.
@yonzhan
Copy link
Collaborator

yonzhan commented Feb 13, 2026

Please fix CI issues

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Auto-Assign Auto assign by bot SQL az sql

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants