Skip to content

Commit 735c7de

Browse files
ncamillucciNicola Camillucci
andauthored
[Key Vault] Added ExternalKey model, create_external_key method, external_key property (#47200)
* Regenerated SDK * Added ExternalKey and create_external_key * Added samples * Added tests * Updated recording and changelog * Updated READMEs --------- Co-authored-by: Nicola Camillucci <ncamillucci@microsoft.com>
1 parent 2d9f963 commit 735c7de

19 files changed

Lines changed: 888 additions & 60 deletions

sdk/keyvault/azure-keyvault-keys/CHANGELOG.md

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,12 @@
11
# Release History
22

3-
## 4.12.0b2 (Unreleased)
3+
## 4.12.0b2 (2026-05-29)
44

55
### Features Added
66

7-
### Breaking Changes
8-
9-
### Bugs Fixed
10-
11-
### Other Changes
7+
- Added the `ExternalKey` model and the new `KeyClient.create_external_key` method
8+
for registering a Key Vault key whose material is held in an external HSM [#47200](https://github.com/Azure/azure-sdk-for-python/pull/47200).
9+
- Added the `KeyProperties.external_key` read-only property.
1210

1311
## 4.12.0b1 (2026-05-26)
1412

sdk/keyvault/azure-keyvault-keys/README.md

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,7 @@ This section contains code snippets covering common tasks:
8686
* [Update an existing key](#update-an-existing-key)
8787
* [Delete a key](#delete-a-key)
8888
* [Configure automatic key rotation](#configure-automatic-key-rotation)
89+
* [Register external keys](#register-external-keys-managed-hsm-only)
8990
* [List keys](#list-keys)
9091
* [Perform cryptographic operations](#cryptographic-operations)
9192
* [Async API](#async-api)
@@ -204,6 +205,29 @@ print(f"Rotated the key on-demand; new version is {rotated_key.properties.versio
204205

205206
<!-- END SNIPPET -->
206207

208+
### Register external keys (Managed HSM only)
209+
[create_external_key](https://aka.ms/azsdk/python/keyvault-keys/docs#azure.keyvault.keys.KeyClient.create_external_key)
210+
registers an external key with a Managed HSM that is configured to use External Key Management (EKM). The external HSM
211+
owns the key material; the Managed HSM stores only a reference to the key.
212+
213+
> **NOTE:** External keys are only supported on Managed HSM, not regular Key Vault. The Managed HSM must be configured
214+
> with an external HSM source.
215+
216+
```python
217+
from azure.identity import DefaultAzureCredential
218+
from azure.keyvault.keys import ExternalKey, KeyClient
219+
220+
credential = DefaultAzureCredential()
221+
222+
key_client = KeyClient(vault_url="https://my-managed-hsm.managedhsm.azure.net/", credential=credential)
223+
224+
external_key = ExternalKey(id="external-key-reference-id")
225+
key = key_client.create_external_key("external-key-name", external_key=external_key)
226+
227+
print(key.name)
228+
print(key.properties.external_key.id)
229+
```
230+
207231
### List keys
208232
[list_properties_of_keys](https://aka.ms/azsdk/python/keyvault-keys/docs#azure.keyvault.keys.KeyClient.list_properties_of_keys)
209233
lists the properties of all of the keys in the client's vault.

sdk/keyvault/azure-keyvault-keys/assets.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,5 +2,5 @@
22
"AssetsRepo": "Azure/azure-sdk-assets",
33
"AssetsRepoPrefixPath": "python",
44
"TagPrefix": "python/keyvault/azure-keyvault-keys",
5-
"Tag": "python/keyvault/azure-keyvault-keys_e47dc10e22"
5+
"Tag": "python/keyvault/azure-keyvault-keys_652b9e1d39"
66
}

sdk/keyvault/azure-keyvault-keys/azure/keyvault/keys/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
from ._shared.client_base import ApiVersion
1313
from ._models import (
1414
DeletedKey,
15+
ExternalKey,
1516
JsonWebKey,
1617
KeyAttestation,
1718
KeyProperties,
@@ -27,6 +28,7 @@
2728
__all__ = [
2829
"ApiVersion",
2930
"KeyClient",
31+
"ExternalKey",
3032
"JsonWebKey",
3133
"KeyAttestation",
3234
"KeyVaultKey",

sdk/keyvault/azure-keyvault-keys/azure/keyvault/keys/_client.py

Lines changed: 82 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,15 @@
1717
from ._models import JsonWebKey, KeyRotationLifetimeAction
1818
from ._shared import KeyVaultClientBase
1919
from ._shared._polling import DeleteRecoverPollingMethod, KeyVaultOperationPoller
20-
from ._models import DeletedKey, KeyVaultKey, KeyProperties, KeyReleasePolicy, KeyRotationPolicy, ReleaseKeyResult
20+
from ._models import (
21+
DeletedKey,
22+
ExternalKey,
23+
KeyVaultKey,
24+
KeyProperties,
25+
KeyReleasePolicy,
26+
KeyRotationPolicy,
27+
ReleaseKeyResult,
28+
)
2129

2230

2331
def _get_key_id(vault_url, key_name, version=None):
@@ -57,6 +65,7 @@ def _get_attributes(
5765
not_before: Optional[datetime],
5866
expires_on: Optional[datetime],
5967
exportable: Optional[bool] = None,
68+
external_key: Optional[ExternalKey] = None,
6069
) -> Optional[KeyAttributes]:
6170
"""Return a KeyAttributes object if non-None attributes are provided, or None otherwise.
6271
@@ -68,13 +77,25 @@ def _get_attributes(
6877
:type expires_on: ~datetime.datetime or None
6978
:param exportable: Whether the private key can be exported.
7079
:type exportable: bool or None
80+
:param external_key: A reference to an external key, when registering an external key.
81+
:type external_key: ~azure.keyvault.keys.ExternalKey or None
7182
7283
:returns: An autorest-generated model of the key's attributes.
7384
:rtype: KeyAttributes
7485
"""
75-
if enabled is not None or not_before is not None or expires_on is not None or exportable is not None:
86+
if (
87+
enabled is not None
88+
or not_before is not None
89+
or expires_on is not None
90+
or exportable is not None
91+
or external_key is not None
92+
):
7693
return self._models.KeyAttributes(
77-
enabled=enabled, not_before=not_before, expires=expires_on, exportable=exportable
94+
enabled=enabled,
95+
not_before=not_before,
96+
expires=expires_on,
97+
exportable=exportable,
98+
external_key=external_key._to_generated() if external_key is not None else None,
7899
)
79100
return None
80101

@@ -398,6 +419,64 @@ def create_oct_key(
398419
**kwargs,
399420
)
400421

422+
@distributed_trace
423+
def create_external_key(
424+
self,
425+
name: str,
426+
external_key: ExternalKey,
427+
*,
428+
enabled: Optional[bool] = None,
429+
tags: Optional[Dict[str, str]] = None,
430+
not_before: Optional[datetime] = None,
431+
expires_on: Optional[datetime] = None,
432+
release_policy: Optional[KeyReleasePolicy] = None,
433+
**kwargs: Any,
434+
) -> KeyVaultKey:
435+
"""Register a Managed HSM key that points at material managed by an external HSM.
436+
437+
Requires the keys/create permission. Only available with API version
438+
``2026-01-01-preview`` and newer, and only supported on Managed HSM.
439+
440+
:param str name: The name for the new key.
441+
:param external_key: A reference identifying the external key material.
442+
:type external_key: ~azure.keyvault.keys.ExternalKey
443+
444+
:keyword enabled: Whether the key is enabled for use.
445+
:paramtype enabled: bool or None
446+
:keyword tags: Application specific metadata in the form of key-value pairs.
447+
:paramtype tags: dict[str, str] or None
448+
:keyword not_before: Not before date of the key in UTC.
449+
:paramtype not_before: ~datetime.datetime or None
450+
:keyword expires_on: Expiry date of the key in UTC.
451+
:paramtype expires_on: ~datetime.datetime or None
452+
:keyword release_policy: The policy rules under which the key can be exported.
453+
:paramtype release_policy: ~azure.keyvault.keys.KeyReleasePolicy or None
454+
455+
:returns: The created key.
456+
:rtype: ~azure.keyvault.keys.KeyVaultKey
457+
458+
:raises ~azure.core.exceptions.HttpResponseError:
459+
"""
460+
attributes = self._get_attributes(
461+
enabled=enabled, not_before=not_before, expires_on=expires_on, external_key=external_key
462+
)
463+
464+
policy = release_policy
465+
if policy is not None:
466+
policy = self._models.KeyReleasePolicy(
467+
encoded_policy=policy.encoded_policy, content_type=policy.content_type, immutable=policy.immutable
468+
)
469+
# External keys are mutually exclusive with `kty`. The generated overload requires `kty`,
470+
# but the runtime constructor accepts arbitrary kwargs.
471+
parameters = self._models.KeyCreateParameters( # type: ignore[call-overload]
472+
key_attributes=attributes,
473+
tags=tags,
474+
release_policy=policy,
475+
)
476+
477+
bundle = self._client.create_key(key_name=name, parameters=parameters, **kwargs)
478+
return KeyVaultKey._from_key_bundle(bundle)
479+
401480
@distributed_trace
402481
def begin_delete_key( # pylint:disable=bad-option-value,delete-operation-wrong-return-type
403482
self, name: str, **kwargs: Any

0 commit comments

Comments
 (0)