Skip to content

[Key Vault] Add External Key Manager (EKM) support for Managed HSM (Preview)#33651

Open
notyashhh wants to merge 5 commits into
devfrom
yash/ekm-hsm-public-preview
Open

[Key Vault] Add External Key Manager (EKM) support for Managed HSM (Preview)#33651
notyashhh wants to merge 5 commits into
devfrom
yash/ekm-hsm-public-preview

Conversation

@notyashhh

@notyashhh notyashhh commented Jun 25, 2026

Copy link
Copy Markdown
Member

Related command
az keyvault ekm-connection (create/update/show/check/delete + certificate show)
az keyvault key create --external-key-id

Description
Adds public-preview support for Managed HSM External Key Manager (EKM) to az keyvault.

What's added:

  • New command group az keyvault ekm-connection to wire a Managed HSM to an external key
    manager proxy: create, update, show, check, delete, and certificate show.
  • New --external-key-id flag on az keyvault key create to create EKM-backed external keys.

All new commands and the --external-key-id argument are marked preview (is_preview=True).

This builds on the private-preview implementation and moves it to the public preview SDKs:

  • azure-keyvault-keys==4.12.0b2 — uses the new create_external_key() API and the public
    ExternalKey model.
  • azure-keyvault-administration==4.8.0b1KeyVaultEkmClient / KeyVaultEkmConnection.

Both SDKs are published on PyPI and require Python >=3.10, which matches the CLI's floor
(python_requires>=3.10), so there is no dependency-resolution impact on CI.

Notes:

  • External keys reject client-specified key type/size/curve/ops; the CLI routes them to the
    dedicated create_external_key() path and omits those attributes.
  • --external-key-id is validated to the public SDK contract ([A-Za-z0-9-], max 64 chars).
  • Removed private-preview artifacts (bundled SDK wheels, temp cert) that were used for the
    internal 1P drop.

Testing Guide
Unit (offline):

azdev test keyvault --tests "Ekm or external_key"

Live smoke (Managed HSM required):

MHSM_ID="https://<your-mhsm>.managedhsm.azure.net"
az keyvault ekm-connection create --id "$MHSM_ID" --host <ekm-proxy-fqdn[:port]> \
  --server-ca-certificate ./chain.pem [--path-prefix /api/v1] [--server-cn <cn>]
az keyvault ekm-connection show  --id "$MHSM_ID"
az keyvault ekm-connection check --id "$MHSM_ID"
az keyvault ekm-connection certificate show --id "$MHSM_ID"

# external (EKM-backed) key
az keyvault key create --id "$MHSM_ID/keys/ekm-key-1" --external-key-id "<ekm-key-id>"
az keyvault key show   --id "$MHSM_ID/keys/ekm-key-1"
az keyvault key delete --id "$MHSM_ID/keys/ekm-key-1"

# regression: normal key still works
az keyvault key create --id "$MHSM_ID/keys/normal-1" --kty RSA --size 2048 --protection hsm
az keyvault key delete --id "$MHSM_ID/keys/normal-1"

History Notes
[Key Vault] az keyvault ekm-connection: Add command group to manage External Key Manager (EKM) connections for Managed HSM (Preview)
[Key Vault] az keyvault key create: Add --external-key-id to create EKM-backed external keys on Managed HSM (Preview)


  • The PR title and description has followed the guideline in Submitting Pull Requests.
  • I adhere to the Command Guidelines.
  • I adhere to the Error Handling Guidelines.

…review)

- Add 'az keyvault ekm-connection' (create/update/show/check/delete + certificate show) as preview.
- Add --external-key-id on 'az keyvault key create' to create EKM-backed external keys (preview).
- Use public SDKs: azure-keyvault-keys==4.12.0b2 (create_external_key + public ExternalKey) and azure-keyvault-administration==4.8.0b1.
- Enforce external-key-id <=64 chars per public SDK contract.
- Add --server-cn alias to satisfy option-length linter.
Copilot AI review requested due to automatic review settings June 25, 2026 05:21
@azure-client-tools-bot-prd

azure-client-tools-bot-prd Bot commented Jun 25, 2026

Copy link
Copy Markdown
️✔️AzureCLI-FullTest
️✔️acr
️✔️latest
️✔️3.12
️✔️3.14
️✔️acs
️✔️latest
️✔️3.12
️✔️3.14
️✔️advisor
️✔️latest
️✔️3.12
️✔️3.14
️✔️ams
️✔️latest
️✔️3.12
️✔️3.14
️✔️apim
️✔️latest
️✔️3.12
️✔️3.14
️✔️appconfig
️✔️latest
️✔️3.12
️✔️3.14
️✔️appservice
️✔️latest
️✔️3.12
️✔️3.14
️✔️aro
️✔️latest
️✔️3.12
️✔️3.14
️✔️backup
️✔️latest
️✔️3.12
️✔️3.14
️✔️batch
️✔️latest
️✔️3.12
️✔️3.14
️✔️batchai
️✔️latest
️✔️3.12
️✔️3.14
️✔️billing
️✔️latest
️✔️3.12
️✔️3.14
️✔️botservice
️✔️latest
️✔️3.12
️✔️3.14
️✔️cloud
️✔️latest
️✔️3.12
️✔️3.14
️✔️cognitiveservices
️✔️latest
️✔️3.12
️✔️3.14
️✔️compute_recommender
️✔️latest
️✔️3.12
️✔️3.14
️✔️computefleet
️✔️latest
️✔️3.12
️✔️3.14
️✔️config
️✔️latest
️✔️3.12
️✔️3.14
️✔️configure
️✔️latest
️✔️3.12
️✔️3.14
️✔️consumption
️✔️latest
️✔️3.12
️✔️3.14
️✔️container
️✔️latest
️✔️3.12
️✔️3.14
️✔️containerapp
️✔️latest
️✔️3.12
️✔️3.14
️✔️core
️✔️latest
️✔️3.12
️✔️3.14
️✔️cosmosdb
️✔️latest
️✔️3.12
️✔️3.14
️✔️databoxedge
️✔️latest
️✔️3.12
️✔️3.14
️✔️dls
️✔️latest
️✔️3.12
️✔️3.14
️✔️dms
️✔️latest
️✔️3.12
️✔️3.14
️✔️eventgrid
️✔️latest
️✔️3.12
️✔️3.14
️✔️eventhubs
️✔️latest
️✔️3.12
️✔️3.14
️✔️feedback
️✔️latest
️✔️3.12
️✔️3.14
️✔️find
️✔️latest
️✔️3.12
️✔️3.14
️✔️hdinsight
️✔️latest
️✔️3.12
️✔️3.14
️✔️identity
️✔️latest
️✔️3.12
️✔️3.14
️✔️iot
️✔️latest
️✔️3.12
️✔️3.14
️✔️keyvault
️✔️latest
️✔️3.12
️✔️3.14
️✔️lab
️✔️latest
️✔️3.12
️✔️3.14
️✔️managedservices
️✔️latest
️✔️3.12
️✔️3.14
️✔️maps
️✔️latest
️✔️3.12
️✔️3.14
️✔️marketplaceordering
️✔️latest
️✔️3.12
️✔️3.14
️✔️monitor
️✔️latest
️✔️3.12
️✔️3.14
️✔️mysql
️✔️latest
️✔️3.12
️✔️3.14
️✔️netappfiles
️✔️latest
️✔️3.12
️✔️3.14
️✔️network
️✔️latest
️✔️3.12
️✔️3.14
️✔️policyinsights
️✔️latest
️✔️3.12
️✔️3.14
️✔️postgresql
️✔️latest
️✔️3.12
️✔️3.14
️✔️privatedns
️✔️latest
️✔️3.12
️✔️3.14
️✔️profile
️✔️latest
️✔️3.12
️✔️3.14
️✔️rdbms
️✔️latest
️✔️3.12
️✔️3.14
️✔️redis
️✔️latest
️✔️3.12
️✔️3.14
️✔️relay
️✔️latest
️✔️3.12
️✔️3.14
️✔️resource
️✔️latest
️✔️3.12
️✔️3.14
️✔️role
️✔️latest
️✔️3.12
️✔️3.14
️✔️search
️✔️latest
️✔️3.12
️✔️3.14
️✔️security
️✔️latest
️✔️3.12
️✔️3.14
️✔️servicebus
️✔️latest
️✔️3.12
️✔️3.14
️✔️serviceconnector
️✔️latest
️✔️3.12
️✔️3.14
️✔️servicefabric
️✔️latest
️✔️3.12
️✔️3.14
️✔️signalr
️✔️latest
️✔️3.12
️✔️3.14
️✔️sql
️✔️latest
️✔️3.12
️✔️3.14
️✔️sqlvm
️✔️latest
️✔️3.12
️✔️3.14
️✔️storage
️✔️latest
️✔️3.12
️✔️3.14
️✔️synapse
️✔️latest
️✔️3.12
️✔️3.14
️✔️telemetry
️✔️latest
️✔️3.12
️✔️3.14
️✔️util
️✔️latest
️✔️3.12
️✔️3.14
️✔️vm
️✔️latest
️✔️3.12
️✔️3.14

@azure-client-tools-bot-prd

Copy link
Copy Markdown

Hi @notyashhh,
Since the current milestone time is less than 7 days, this pr will be reviewed in the next milestone.

@azure-client-tools-bot-prd

azure-client-tools-bot-prd Bot commented Jun 25, 2026

Copy link
Copy Markdown
⚠️AzureCLI-BreakingChangeTest
⚠️keyvault
rule cmd_name rule_message suggest_message
⚠️ 1011 - SubgroupAdd keyvault ekm-connection sub group keyvault ekm-connection added
⚠️ 1006 - ParaAdd keyvault key create cmd keyvault key create added parameter external_key_id

@yonzhan

yonzhan commented Jun 25, 2026

Copy link
Copy Markdown
Collaborator

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

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Adds public-preview Managed HSM External Key Manager (EKM) support to az keyvault by introducing a new az keyvault ekm-connection command group and enabling creation of EKM-backed external keys via az keyvault key create --external-key-id. The implementation updates Key Vault SDK dependencies to public preview versions, adds validators/serialization helpers for EKM connection/cert handling, and exposes external key metadata in key output.

Changes:

  • Added az keyvault ekm-connection (create/update/show/check/delete + certificate show) backed by a new EKM data-plane client factory and validators.
  • Added preview --external-key-id to az keyvault key create, routing external-key creation to the new SDK create_external_key() path.
  • Updated Key Vault SDK dependency pins and improved key output transformations (including external key id; plus hex output normalization for encrypt results).

Reviewed changes

Copilot reviewed 12 out of 12 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
src/azure-cli/setup.py Bumps Key Vault SDK dependency pins to public-preview versions needed for EKM/external keys.
src/azure-cli/requirements.py3.windows.txt Updates Windows pinned Key Vault SDK versions to match setup dependency changes.
src/azure-cli/requirements.py3.Linux.txt Updates Linux pinned Key Vault SDK versions to match setup dependency changes.
src/azure-cli/requirements.py3.Darwin.txt Updates macOS pinned Key Vault SDK versions to match setup dependency changes.
src/azure-cli/azure/cli/command_modules/keyvault/tests/latest/test_keyvault_commands.py Adds unit tests for external key id validation and EKM certificate serialization behavior.
src/azure-cli/azure/cli/command_modules/keyvault/custom.py Implements external key creation routing and adds EKM connection/certificate custom handlers.
src/azure-cli/azure/cli/command_modules/keyvault/commands.py Registers new preview keyvault ekm-connection command groups and wires validators/client factory.
src/azure-cli/azure/cli/command_modules/keyvault/_validators.py Adds validation for --external-key-id and EKM connection inputs (host, path prefix, CA certs).
src/azure-cli/azure/cli/command_modules/keyvault/_transformers.py Exposes external key id in key outputs and normalizes encrypt output fields to strings.
src/azure-cli/azure/cli/command_modules/keyvault/_params.py Adds CLI parameters for --external-key-id and new EKM connection command arguments.
src/azure-cli/azure/cli/command_modules/keyvault/_help.py Adds help entries for the new keyvault ekm-connection commands.
src/azure-cli/azure/cli/command_modules/keyvault/_client_factory.py Adds a data-plane KeyVaultEkmClient factory for EKM connection operations.

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

Comment on lines +737 to +746
# External keys are backed by EKM and the service rejects client-specified key type/size/curve.
# Avoid the defaulting behavior in validate_key_type (RSA) when --external-key-id is present.
if getattr(ns, 'external_key_id', None):
setattr(ns, 'kty', None)
setattr(ns, 'key_size', None)
setattr(ns, 'curve', None)
setattr(ns, 'protection', None)
else:
validate_key_type(ns)

Comment thread src/azure-cli/azure/cli/command_modules/keyvault/_validators.py Outdated
Comment on lines +359 to +363
@@ -360,6 +360,9 @@ class CLISecurityDomainOperation(str, Enum):
help='The type of key to create. For valid values, see: https://learn.microsoft.com/rest/api/keyvault/keys/create-key/create-key#jsonwebkeytype')
c.argument('curve', arg_type=get_enum_type(KeyCurveName),
help='Elliptic curve name. For valid values, see: https://learn.microsoft.com/rest/api/keyvault/keys/create-key/create-key#jsonwebkeycurvename')
c.extra('external_key_id', options_list=['--external-key-id'], arg_group='External Key',
@notyashhh

Copy link
Copy Markdown
Member Author

/azp run

@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines successfully started running 3 pipeline(s).

…ey args, wrap cert IO errors)

- key create: when --external-key-id is set, fail fast with a clear error if key-shape args (--kty/--size/--curve/--ops/--protection/--exportable) are provided, instead of silently ignoring them.
- Remove arg-level validate_key_type validator from --kty so an explicitly provided value is detectable (no more default-then-undo).
- _load_certificates_as_der_bytes: wrap file IO in CLIError for missing/unreadable certificate files.
… lifecycle

Live-only, env-var-gated scenario test (KeyVaultEkmScenarioTest) covering ekm-connection create/show/check/certificate-show, external key create/show/list-versions/delete, the fail-fast guard, and a normal-key regression. Skips unless AZURE_CLI_TEST_EKM_* env vars are set, so it is safe in CI playback and ready to run/record against a real MHSM+EKM proxy.
…n-slate)

Validated the scenario test by actually running it live against a real MHSM + EKM proxy. Two fixes from that run:
- Support optional AZURE_CLI_TEST_EKM_PATH_PREFIX and pass --path-prefix; without it the connection check fails on proxies that require a path prefix (e.g. /api/v1).
- Delete any pre-existing connection before create: the service returns 'EKM connection is already setup' if one exists, so start from a clean slate.
Test now passes end-to-end live and still skips cleanly in CI.
…ify multi-cert input

Addresses review feedback from Chandan on the EKM connection commands:
- --server-ca-certificate is now a required argument on 'ekm-connection create' so help shows [Required]. The validator's non-empty check is kept as a safety net for files that parse to zero certs. Left optional on 'update'.
- Clarified the help text to document both supported input forms.
- Added unit tests proving both certificate input combinations: (1) a single file containing a multi-cert PEM chain is split into separate DER blobs, and (2) multiple space-separated file paths (PEM and/or DER, each possibly multi-block) are all loaded in order. Also added a test that create rejects a missing certificate.
Both forms were additionally verified live end-to-end against a Managed HSM + EKM proxy (create -> show shows 2 CAs -> check passes), then the connection was restored.
@notyashhh notyashhh assigned msarfraz and unassigned notyashhh Jun 25, 2026
'Release policies are mutable by default.')

with self.argument_context('keyvault key create') as c:
c.argument('kty', arg_type=get_enum_type(JsonWebKeyType), validator=validate_key_type,

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Why removing the validator?

help='The type of key to create. For valid values, see: https://learn.microsoft.com/rest/api/keyvault/keys/create-key/create-key#jsonwebkeytype')
c.argument('curve', arg_type=get_enum_type(KeyCurveName),
help='Elliptic curve name. For valid values, see: https://learn.microsoft.com/rest/api/keyvault/keys/create-key/create-key#jsonwebkeycurvename')
c.extra('external_key_id', options_list=['--external-key-id'], arg_group='External Key',

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

What does .extra do differently from .argument?

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants