Skip to content
Open
7 changes: 3 additions & 4 deletions sdk/keyvault/azure-security-keyvault-secrets/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,17 +1,16 @@
# Release History

## 4.11.0-beta.1 (Unreleased)
## 4.11.0-beta.1 (2026-03-19)

### Features Added

### Breaking Changes
- Added `previousVersion` property to `KeyVaultSecret` and related response models, which returns the version of the previous secret, if applicable. This field is only populated for secrets created after June 1, 2025.
- Added `outContentType` query parameter support to `getSecret` operations, enabling PFX to PEM format conversion for certificate-backed secrets. Available in service version `2025-07-01` and later.
Comment on lines +3 to +8
Copy link

Copilot AI Mar 19, 2026

Choose a reason for hiding this comment

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

The top CHANGELOG entry was changed from "(Unreleased)" to a specific date. In this repo, Key Vault packages keep the next version marked as "Unreleased" until the release is finalized (see azure-security-keyvault-keys/CHANGELOG.md). Also, this section removed the standard empty subsections (Breaking Changes/Other Changes). Consider reverting to the standard format and keeping the entry as Unreleased.

Copilot uses AI. Check for mistakes.

### Bugs Fixed

- Fixed an issue where certain `HttpResponseException.getResponse()` calls could cause a `NullPointerException`. ([#47801](https://github.com/Azure/azure-sdk-for-java/issues/47801))

### Other Changes

## 4.10.5 (2026-01-29)

### Other Changes
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -331,7 +331,7 @@ Response<BinaryData> getDeletedSecretSync(@HostParam("vaultBaseUrl") String vaul
@UnexpectedResponseExceptionType(HttpResponseException.class)
Mono<Response<Void>> purgeDeletedSecret(@HostParam("vaultBaseUrl") String vaultBaseUrl,
@QueryParam("api-version") String apiVersion, @PathParam("secret-name") String secretName,
@HeaderParam("Accept") String accept, RequestOptions requestOptions, Context context);
RequestOptions requestOptions, Context context);

@Delete("/deletedsecrets/{secret-name}")
@ExpectedResponses({ 204 })
Expand All @@ -341,7 +341,7 @@ Mono<Response<Void>> purgeDeletedSecret(@HostParam("vaultBaseUrl") String vaultB
@UnexpectedResponseExceptionType(HttpResponseException.class)
Response<Void> purgeDeletedSecretSync(@HostParam("vaultBaseUrl") String vaultBaseUrl,
@QueryParam("api-version") String apiVersion, @PathParam("secret-name") String secretName,
@HeaderParam("Accept") String accept, RequestOptions requestOptions, Context context);
RequestOptions requestOptions, Context context);

@Post("/deletedsecrets/{secret-name}/recover")
@ExpectedResponses({ 200 })
Expand Down Expand Up @@ -516,6 +516,7 @@ Response<BinaryData> getDeletedSecretsNextSync(@PathParam(value = "nextLink", en
* }
* kid: String (Optional)
* managed: Boolean (Optional)
* previousVersion: String (Optional)
* }
* }
* </pre>
Expand Down Expand Up @@ -591,6 +592,7 @@ public Mono<Response<BinaryData>> setSecretWithResponseAsync(String secretName,
* }
* kid: String (Optional)
* managed: Boolean (Optional)
* previousVersion: String (Optional)
* }
* }
* </pre>
Expand Down Expand Up @@ -641,6 +643,7 @@ public Response<BinaryData> setSecretWithResponse(String secretName, BinaryData
* }
* kid: String (Optional)
* managed: Boolean (Optional)
* previousVersion: String (Optional)
* recoveryId: String (Optional)
* scheduledPurgeDate: Long (Optional)
* deletedDate: Long (Optional)
Expand Down Expand Up @@ -691,6 +694,7 @@ public Mono<Response<BinaryData>> deleteSecretWithResponseAsync(String secretNam
* }
* kid: String (Optional)
* managed: Boolean (Optional)
* previousVersion: String (Optional)
* recoveryId: String (Optional)
* scheduledPurgeDate: Long (Optional)
* deletedDate: Long (Optional)
Expand Down Expand Up @@ -764,6 +768,7 @@ public Response<BinaryData> deleteSecretWithResponse(String secretName, RequestO
* }
* kid: String (Optional)
* managed: Boolean (Optional)
* previousVersion: String (Optional)
* }
* }
* </pre>
Expand Down Expand Up @@ -839,6 +844,7 @@ public Mono<Response<BinaryData>> updateSecretWithResponseAsync(String secretNam
* }
* kid: String (Optional)
* managed: Boolean (Optional)
* previousVersion: String (Optional)
* }
* }
* </pre>
Expand Down Expand Up @@ -867,6 +873,17 @@ public Response<BinaryData> updateSecretWithResponse(String secretName, String s
*
* The GET operation is applicable to any secret stored in Azure Key Vault. This operation requires the secrets/get
* permission.
* <p><strong>Query Parameters</strong></p>
* <table border="1">
* <caption>Query Parameters</caption>
* <tr><th>Name</th><th>Type</th><th>Required</th><th>Description</th></tr>
* <tr><td>outContentType</td><td>String</td><td>No</td><td>The media type (MIME type) of the certificate. If a
* supported format is specified, the certificate content is converted to the requested format. Currently, only PFX
* to PEM conversion is supported. If an unsupported format is specified, the request is rejected. If not specified,
* the certificate is returned in its original format without conversion. Allowed values: "application/x-pkcs12",
* "application/x-pem-file".</td></tr>
* </table>
* You can add these to a request with {@link RequestOptions#addQueryParam}
* <p><strong>Response Body Schema</strong></p>
*
* <pre>
Expand All @@ -889,6 +906,7 @@ public Response<BinaryData> updateSecretWithResponse(String secretName, String s
* }
* kid: String (Optional)
* managed: Boolean (Optional)
* previousVersion: String (Optional)
* }
* }
* </pre>
Expand Down Expand Up @@ -919,6 +937,17 @@ public Mono<Response<BinaryData>> getSecretWithResponseAsync(String secretName,
*
* The GET operation is applicable to any secret stored in Azure Key Vault. This operation requires the secrets/get
* permission.
* <p><strong>Query Parameters</strong></p>
* <table border="1">
* <caption>Query Parameters</caption>
* <tr><th>Name</th><th>Type</th><th>Required</th><th>Description</th></tr>
* <tr><td>outContentType</td><td>String</td><td>No</td><td>The media type (MIME type) of the certificate. If a
* supported format is specified, the certificate content is converted to the requested format. Currently, only PFX
* to PEM conversion is supported. If an unsupported format is specified, the request is rejected. If not specified,
* the certificate is returned in its original format without conversion. Allowed values: "application/x-pkcs12",
* "application/x-pem-file".</td></tr>
* </table>
* You can add these to a request with {@link RequestOptions#addQueryParam}
* <p><strong>Response Body Schema</strong></p>
*
* <pre>
Expand All @@ -941,6 +970,7 @@ public Mono<Response<BinaryData>> getSecretWithResponseAsync(String secretName,
* }
* kid: String (Optional)
* managed: Boolean (Optional)
* previousVersion: String (Optional)
* }
* }
* </pre>
Expand Down Expand Up @@ -1652,6 +1682,7 @@ public PagedIterable<BinaryData> getDeletedSecrets(RequestOptions requestOptions
* }
* kid: String (Optional)
* managed: Boolean (Optional)
* previousVersion: String (Optional)
* recoveryId: String (Optional)
* scheduledPurgeDate: Long (Optional)
* deletedDate: Long (Optional)
Expand Down Expand Up @@ -1705,6 +1736,7 @@ public Mono<Response<BinaryData>> getDeletedSecretWithResponseAsync(String secre
* }
* kid: String (Optional)
* managed: Boolean (Optional)
* previousVersion: String (Optional)
* recoveryId: String (Optional)
* scheduledPurgeDate: Long (Optional)
* deletedDate: Long (Optional)
Expand Down Expand Up @@ -1747,9 +1779,8 @@ public Response<BinaryData> getDeletedSecretWithResponse(String secretName, Requ
*/
@ServiceMethod(returns = ReturnType.SINGLE)
public Mono<Response<Void>> purgeDeletedSecretWithResponseAsync(String secretName, RequestOptions requestOptions) {
final String accept = "application/json";
return FluxUtil.withContext(context -> service.purgeDeletedSecret(this.getVaultBaseUrl(),
this.getServiceVersion().getVersion(), secretName, accept, requestOptions, context));
this.getServiceVersion().getVersion(), secretName, requestOptions, context));
}

/**
Expand All @@ -1769,9 +1800,8 @@ public Mono<Response<Void>> purgeDeletedSecretWithResponseAsync(String secretNam
*/
@ServiceMethod(returns = ReturnType.SINGLE)
public Response<Void> purgeDeletedSecretWithResponse(String secretName, RequestOptions requestOptions) {
final String accept = "application/json";
return service.purgeDeletedSecretSync(this.getVaultBaseUrl(), this.getServiceVersion().getVersion(), secretName,
accept, requestOptions, Context.NONE);
requestOptions, Context.NONE);
}

/**
Expand Down Expand Up @@ -1801,6 +1831,7 @@ public Response<Void> purgeDeletedSecretWithResponse(String secretName, RequestO
* }
* kid: String (Optional)
* managed: Boolean (Optional)
* previousVersion: String (Optional)
* }
* }
* </pre>
Expand Down Expand Up @@ -1849,6 +1880,7 @@ public Mono<Response<BinaryData>> recoverDeletedSecretWithResponseAsync(String s
* }
* kid: String (Optional)
* managed: Boolean (Optional)
* previousVersion: String (Optional)
* }
* }
* </pre>
Expand Down Expand Up @@ -1966,6 +1998,7 @@ public Response<BinaryData> backupSecretWithResponse(String secretName, RequestO
* }
* kid: String (Optional)
* managed: Boolean (Optional)
* previousVersion: String (Optional)
* }
* }
* </pre>
Expand Down Expand Up @@ -2025,6 +2058,7 @@ public Mono<Response<BinaryData>> restoreSecretWithResponseAsync(BinaryData para
* }
* kid: String (Optional)
* managed: Boolean (Optional)
* previousVersion: String (Optional)
* }
* }
* </pre>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
// Code generated by Microsoft (R) TypeSpec Code Generator.

package com.azure.security.keyvault.secrets.implementation.models;

import com.azure.core.annotation.Generated;
import com.azure.core.util.ExpandableStringEnum;
import java.util.Collection;

/**
* The media type (MIME type).
*/
public final class ContentType extends ExpandableStringEnum<ContentType> {
/**
* The PKCS#12 file format.
*/
@Generated
public static final ContentType PFX = fromString("application/x-pkcs12");

/**
* The PEM file format.
*/
@Generated
public static final ContentType PEM = fromString("application/x-pem-file");

/**
* Creates a new instance of ContentType value.
*
* @deprecated Use the {@link #fromString(String)} factory method.
*/
@Generated
@Deprecated
public ContentType() {
}

/**
* Creates or finds a ContentType from its string representation.
*
* @param name a name to look for.
* @return the corresponding ContentType.
*/
@Generated
public static ContentType fromString(String name) {
return fromString(name, ContentType.class);
}

/**
* Gets known ContentType values.
*
* @return known ContentType values.
*/
@Generated
public static Collection<ContentType> values() {
return values(ContentType.class);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,13 @@ public final class DeletedSecretBundle implements JsonSerializable<DeletedSecret
@Generated
private Boolean managed;

/*
* The version of the previous certificate, if applicable. Applies only to certificates created after June 1, 2025.
* Certificates created before this date are not retroactively updated.
*/
@Generated
private String previousVersion;

Comment on lines +70 to +75
Copy link

Copilot AI Mar 19, 2026

Choose a reason for hiding this comment

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

The new previousVersion documentation here refers to the "previous certificate", but the same feature is described in this PR's CHANGELOG as the "previous secret". Please verify the intended meaning from the service spec and make the wording consistent across docs (model field + getter + CHANGELOG), e.g., clarify whether this applies only to certificate-backed secrets or all secrets.

Copilot uses AI. Check for mistakes.
/*
* The url of the recovery object, used to identify and recover the deleted secret.
*/
Expand Down Expand Up @@ -163,6 +170,17 @@ public Boolean isManaged() {
return this.managed;
}

/**
* Get the previousVersion property: The version of the previous certificate, if applicable. Applies only to
* certificates created after June 1, 2025. Certificates created before this date are not retroactively updated.
*
* @return the previousVersion value.
*/
@Generated
public String getPreviousVersion() {
return this.previousVersion;
}

/**
* Get the recoveryId property: The url of the recovery object, used to identify and recover the deleted secret.
*
Expand Down Expand Up @@ -211,6 +229,7 @@ public JsonWriter toJson(JsonWriter jsonWriter) throws IOException {
jsonWriter.writeStringField("contentType", this.contentType);
jsonWriter.writeJsonField("attributes", this.attributes);
jsonWriter.writeMapField("tags", this.tags, (writer, element) -> writer.writeString(element));
jsonWriter.writeStringField("previousVersion", this.previousVersion);
jsonWriter.writeStringField("recoveryId", this.recoveryId);
return jsonWriter.writeEndObject();
}
Expand Down Expand Up @@ -246,6 +265,8 @@ public static DeletedSecretBundle fromJson(JsonReader jsonReader) throws IOExcep
deserializedDeletedSecretBundle.kid = reader.getString();
} else if ("managed".equals(fieldName)) {
deserializedDeletedSecretBundle.managed = reader.getNullable(JsonReader::getBoolean);
} else if ("previousVersion".equals(fieldName)) {
deserializedDeletedSecretBundle.previousVersion = reader.getString();
} else if ("recoveryId".equals(fieldName)) {
deserializedDeletedSecretBundle.recoveryId = reader.getString();
} else if ("scheduledPurgeDate".equals(fieldName)) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,13 @@ public final class SecretBundle implements JsonSerializable<SecretBundle> {
@Generated
private Boolean managed;

/*
* The version of the previous certificate, if applicable. Applies only to certificates created after June 1, 2025.
* Certificates created before this date are not retroactively updated.
Comment on lines +66 to +67
Copy link

Copilot AI Mar 19, 2026

Choose a reason for hiding this comment

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

The new previousVersion documentation here refers to the "previous certificate", but the same feature is described in this PR's CHANGELOG as the "previous secret". Please verify the intended meaning from the service spec and make the wording consistent across docs (model field + getter + CHANGELOG), e.g., clarify whether this applies only to certificate-backed secrets or all secrets.

Suggested change
* The version of the previous certificate, if applicable. Applies only to certificates created after June 1, 2025.
* Certificates created before this date are not retroactively updated.
* The version identifier of the previous secret, if applicable. Applies only to secrets created after June 1, 2025.
* Secrets created before this date are not retroactively updated.

Copilot uses AI. Check for mistakes.
*/
@Generated
private String previousVersion;

/**
* Creates an instance of SecretBundle class.
*/
Expand Down Expand Up @@ -141,6 +148,17 @@ public Boolean isManaged() {
return this.managed;
}

/**
* Get the previousVersion property: The version of the previous certificate, if applicable. Applies only to
* certificates created after June 1, 2025. Certificates created before this date are not retroactively updated.
*
* @return the previousVersion value.
*/
@Generated
public String getPreviousVersion() {
return this.previousVersion;
}

/**
* {@inheritDoc}
*/
Expand All @@ -153,6 +171,7 @@ public JsonWriter toJson(JsonWriter jsonWriter) throws IOException {
jsonWriter.writeStringField("contentType", this.contentType);
jsonWriter.writeJsonField("attributes", this.attributes);
jsonWriter.writeMapField("tags", this.tags, (writer, element) -> writer.writeString(element));
jsonWriter.writeStringField("previousVersion", this.previousVersion);
return jsonWriter.writeEndObject();
}

Expand Down Expand Up @@ -187,6 +206,8 @@ public static SecretBundle fromJson(JsonReader jsonReader) throws IOException {
deserializedSecretBundle.kid = reader.getString();
} else if ("managed".equals(fieldName)) {
deserializedSecretBundle.managed = reader.getNullable(JsonReader::getBoolean);
} else if ("previousVersion".equals(fieldName)) {
deserializedSecretBundle.previousVersion = reader.getString();
} else {
reader.skipChildren();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ SecretClientBuilder getClientBuilder(HttpClient httpClient, String testTenantId,
credential = new MockTokenCredential();
List<TestProxyRequestMatcher> customMatchers = new ArrayList<>();
customMatchers.add(new BodilessMatcher());
customMatchers.add(new CustomMatcher().setExcludedHeaders(Collections.singletonList("Authorization")));
customMatchers.add(new CustomMatcher().setExcludedHeaders(Arrays.asList("Authorization", "Accept")));
interceptorManager.addMatchers(customMatchers);
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
directory: specification/keyvault/Security.KeyVault.Secrets
commit: 396ab529763b7195ab089f58e2eefb011e1b290d
directory: specification/keyvault/data-plane/Secrets
commit: f6bd06be22baf3a18504ffef0f590230850953e5
repo: Azure/azure-rest-api-specs
cleanup: true
additionalDirectories:
- specification/keyvault/Security.KeyVault.Common/
- specification/keyvault/data-plane/Secrets/common
Comment on lines +1 to +5
Copy link

Copilot AI Mar 19, 2026

Choose a reason for hiding this comment

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

tsp-location.yaml diverges from the other Key Vault modules: it drops cleanup: true and the additionalDirectories entry no longer ends with a trailing /. Other Key Vault libraries keep cleanup: true and use trailing slashes for additionalDirectories paths (for example azure-security-keyvault-keys/tsp-location.yaml). Consider restoring cleanup: true and using specification/keyvault/data-plane/Secrets/common/ for consistency and to avoid tooling differences when resolving directory paths.

Copilot uses AI. Check for mistakes.
Loading