diff --git a/kms/snippets/delete_key.py b/kms/snippets/delete_key.py new file mode 100644 index 00000000000..e1a48091e81 --- /dev/null +++ b/kms/snippets/delete_key.py @@ -0,0 +1,52 @@ +# Copyright 2026 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# [START kms_delete_key] +from google.cloud import kms + + +def delete_key( + project_id: str, location_id: str, key_ring_id: str, key_id: str +) -> None: + """ + Delete the given key. + + Args: + project_id (str): Google Cloud project ID (e.g. 'my-project'). + location_id (str): Cloud KMS location (e.g. 'us-east1'). + key_ring_id (str): ID of the Cloud KMS key ring (e.g. 'my-key-ring'). + key_id (str): ID of the key to use (e.g. 'my-key'). + + Returns: + None + + """ + + # Create the client. + client = kms.KeyManagementServiceClient() + + # Build the key name. + key_name = client.crypto_key_path(project_id, location_id, key_ring_id, key_id) + + # Call the API. + # Note: delete_crypto_key returns a long-running operation. + operation = client.delete_crypto_key(request={"name": key_name}) + + # Wait for the operation to complete. + operation.result() + + print(f"Deleted key: {key_name}") + + +# [END kms_delete_key] diff --git a/kms/snippets/delete_key_version.py b/kms/snippets/delete_key_version.py new file mode 100644 index 00000000000..c960447ca21 --- /dev/null +++ b/kms/snippets/delete_key_version.py @@ -0,0 +1,55 @@ +# Copyright 2026 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# [START kms_delete_key_version] +from google.cloud import kms + + +def delete_key_version( + project_id: str, location_id: str, key_ring_id: str, key_id: str, version_id: str +) -> None: + """ + Delete the given key version. + + Args: + project_id (str): Google Cloud project ID (e.g. 'my-project'). + location_id (str): Cloud KMS location (e.g. 'us-east1'). + key_ring_id (str): ID of the Cloud KMS key ring (e.g. 'my-key-ring'). + key_id (str): ID of the key to use (e.g. 'my-key'). + version_id (str): ID of the key version to delete (e.g. '1'). + + Returns: + None + + """ + + # Create the client. + client = kms.KeyManagementServiceClient() + + # Build the key version name. + key_version_name = client.crypto_key_version_path( + project_id, location_id, key_ring_id, key_id, version_id + ) + + # Call the API. + # Note: delete_crypto_key_version returns a long-running operation. + operation = client.delete_crypto_key_version(request={"name": key_version_name}) + + # Wait for the operation to complete. + operation.result() + + print(f"Deleted key version: {key_version_name}") + + +# [END kms_delete_key_version] diff --git a/kms/snippets/get_retired_resource.py b/kms/snippets/get_retired_resource.py new file mode 100644 index 00000000000..48042d7fa9f --- /dev/null +++ b/kms/snippets/get_retired_resource.py @@ -0,0 +1,50 @@ +# Copyright 2026 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# [START kms_get_retired_resource] +from google.cloud import kms + + +def get_retired_resource( + project_id: str, location_id: str, retired_resource_id: str +) -> kms.RetiredResource: + """ + Get the details of a retired resource. + + Args: + project_id (str): Google Cloud project ID (e.g. 'my-project'). + location_id (str): Cloud KMS location (e.g. 'us-east1'). + resource_id (str): ID of the retired resource to get. + + Returns: + kms.RetiredResource: The requested retired resource. + + """ + + # Create the client. + client = kms.KeyManagementServiceClient() + + # Build the retired resource name. + # Note: Retired resources are tied to a Location, not a KeyRing. + # The name is like projects/{project}/locations/{location}/retiredResources/{id} + name = client.retired_resource_path(project_id, location_id, retired_resource_id) + + # Call the API. + response = client.get_retired_resource(request={"name": name}) + + print(f"Got retired resource: {response.name}") + return response + + +# [END kms_get_retired_resource] diff --git a/kms/snippets/list_retired_resources.py b/kms/snippets/list_retired_resources.py new file mode 100644 index 00000000000..9393b34de1c --- /dev/null +++ b/kms/snippets/list_retired_resources.py @@ -0,0 +1,50 @@ +# Copyright 2026 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# [START kms_list_retired_resources] +from typing import List + +from google.cloud import kms + + +def list_retired_resources(project_id: str, location_id: str) -> List[kms.RetiredResource]: + """ + List the retired resources in a location. + + Args: + project_id (str): Google Cloud project ID (e.g. 'my-project'). + location_id (str): Cloud KMS location (e.g. 'us-east1'). + + Returns: + list[kms.RetiredResource]: The list of retired resources. + """ + + # Create the client. + client = kms.KeyManagementServiceClient() + + # Build the parent location name. + parent = client.common_location_path(project_id, location_id) + + # Call the API. + # The API paginates, but the Python client library handles that for us. + resources_list = list(client.list_retired_resources(request={"parent": parent})) + + # Iterate over the resources and print them. + for resource in resources_list: + print(f"Retired resource: {resource.name}") + + return resources_list + + +# [END kms_list_retired_resources] diff --git a/kms/snippets/requirements.txt b/kms/snippets/requirements.txt index 6e15391cfd6..167c2a25011 100644 --- a/kms/snippets/requirements.txt +++ b/kms/snippets/requirements.txt @@ -1,4 +1,4 @@ -google-cloud-kms==3.2.1 +google-cloud-kms==3.11.0 cryptography==45.0.1 crcmod==1.7 jwcrypto==1.5.6 \ No newline at end of file diff --git a/kms/snippets/snippets_test.py b/kms/snippets/snippets_test.py index 970cf13dfe6..002ab499269 100644 --- a/kms/snippets/snippets_test.py +++ b/kms/snippets/snippets_test.py @@ -52,6 +52,7 @@ from create_key_version import create_key_version from decrypt_asymmetric import decrypt_asymmetric from decrypt_symmetric import decrypt_symmetric +from delete_key import delete_key from destroy_key_version import destroy_key_version from disable_key_version import disable_key_version from enable_key_version import enable_key_version @@ -62,10 +63,12 @@ from get_key_version_attestation import get_key_version_attestation from get_public_key import get_public_key from get_public_key_jwk import get_public_key_jwk +from get_retired_resource import get_retired_resource from iam_add_member import iam_add_member from iam_get_policy import iam_get_policy from iam_remove_member import iam_remove_member from import_manually_wrapped_key import import_manually_wrapped_key +from list_retired_resources import list_retired_resources from quickstart import quickstart from restore_key_version import restore_key_version from sign_asymmetric import sign_asymmetric @@ -886,3 +889,41 @@ def test_verify_mac( def test_quickstart(project_id: str, location_id: str) -> None: key_rings = quickstart(project_id, location_id) assert key_rings + + +def test_delete_key_and_retired_resources( + client: kms.KeyManagementServiceClient, + project_id: str, + location_id: str, + key_ring_id: str, +) -> None: + # We can test key deletion and retired resources by first creating a key. + key_id = f"delete-key-{uuid.uuid4()}" + key_ring_name = client.key_ring_path(project_id, location_id, key_ring_id) + key = client.create_crypto_key( + request={ + "parent": key_ring_name, + "crypto_key_id": key_id, + "crypto_key": { + "purpose": kms.CryptoKey.CryptoKeyPurpose.ENCRYPT_DECRYPT, + }, + "skip_initial_version_creation": True, + } + ) + + # Delete the key. + delete_key(project_id, location_id, key_ring_id, key_id) + + # List retired resources and filter to just our deleted key. + all_retired = list_retired_resources(project_id, location_id) + filtered_retired = [r for r in all_retired if r.original_resource == key.name] + + # Make sure the len is 1 + assert len(filtered_retired) == 1 + + # Get the retired resource + resource_id = filtered_retired[0].name.split("/")[-1] + retrieved = get_retired_resource(project_id, location_id, resource_id) + + # See if the result is the same as retired resource list[0] + assert retrieved.name == filtered_retired[0].name