def run_sample(): # Instantiate a secret client that will be used to call the service. # Notice that the client is using default Azure credentials. # To make default credentials work, ensure that environment variables 'AZURE_CLIENT_ID', # 'AZURE_CLIENT_SECRET' and 'AZURE_TENANT_ID' are set with the service principal credentials. VAULT_URL = os.environ["VAULT_URL"] credential = DefaultAzureCredential() client = SecretClient(vault_url=VAULT_URL, credential=credential) try: # Let's create secrets holding storage and bank accounts credentials. If the secret # already exists in the Key Vault, then a new version of the secret is created. print("\n1. Create Secret") bank_secret = client.set_secret("recoverPurgeBankSecretName", "recoverPurgeSecretValue1") storage_secret = client.set_secret("recoverPurgeStorageSecretName", "recoverPurgeSecretValue2") print("Secret with name '{0}' was created.".format(bank_secret.name)) print("Secret with name '{0}' was created.".format( storage_secret.name)) # The storage account was closed, need to delete its credentials from the Key Vault. print("\n2. Delete a Secret") secret = client.delete_secret(bank_secret.name) time.sleep(20) print("Secret with name '{0}' was deleted on date {1}.".format( secret.name, secret.deleted_date)) # We accidentally deleted the bank account secret. Let's recover it. # A deleted secret can only be recovered if the Key Vault is soft-delete enabled. print("\n3. Recover Deleted Secret") recovered_secret = client.recover_deleted_secret(bank_secret.name) print("Recovered Secret with name '{0}'.".format( recovered_secret.name)) # Let's delete storage account now. # If the keyvault is soft-delete enabled, then for permanent deletion deleted secret needs to be purged. client.delete_secret(storage_secret.name) # To ensure secret is deleted on the server side. print("\nDeleting Storage Secret...") time.sleep(20) # To ensure permanent deletion, we might need to purge the secret. print("\n4. Purge Deleted Secret") client.purge_deleted_secret(storage_secret.name) print("Secret has been permanently deleted.") except HttpResponseError as e: if "(NotSupported)" in e.message: print( "\n{0} Please enable soft delete on Key Vault to perform this operation." .format(e.message)) else: print("\nrun_sample has caught an error. {0}".format(e.message)) finally: print("\nrun_sample done")
class KeyVault(): def __init__(self, vault_url): self.credential = DefaultAzureCredential() self.secret_client = SecretClient(vault_url=vault_url, credential=self.credential) def get_secret(self, secret_name): secret = self.secret_client.get_secret(secret_name) print(secret.enabled) return secret.value def update_secret(self, secret_name, secret_data): secret = self.secret_client.set_secret(secret_name, secret_data) return secret.properties.version def delete_secret(self, secret_name): secret = self.secret_client.begin_delete_secret(secret_name).wait() self.secret_client.purge_deleted_secret(secret_name) print(secret)
# We accidentally deleted the bank account secret. Let's recover it. # A deleted secret can only be recovered if the Key Vault is soft-delete enabled. print("\n.. Recover Deleted Secret") recovered_secret = client.recover_deleted_secret(bank_secret.name) print("Recovered Secret with name '{0}'.".format(recovered_secret.name)) # Let's delete storage account now. # If the keyvault is soft-delete enabled, then for permanent deletion deleted secret needs to be purged. client.delete_secret(storage_secret.name) # To ensure secret is deleted on the server side. print("\nDeleting Storage Secret...") time.sleep(20) # To ensure permanent deletion, we might need to purge the secret. print("\n.. Purge Deleted Secret") client.purge_deleted_secret(storage_secret.name) print("Secret has been permanently deleted.") except HttpResponseError as e: if "(NotSupported)" in e.message: print( "\n{0} Please enable soft delete on Key Vault to perform this operation." .format(e.message)) else: print("\nrun_sample has caught an error. {0}".format(e.message)) finally: print("\nrun_sample done")
print("Secret with name '{0}' created with value '{1}'".format( secret.name, secret.value)) # Backups are good to have, if in case secrets gets deleted accidentally. # For long term storage, it is ideal to write the backup to a file. print("\n.. Create a backup for an existing Secret") secret_backup = client.backup_secret(secret.name) print("Backup created for secret with name '{0}'.".format(secret.name)) # The storage account secret is no longer in use, so you delete it. print("\n.. Deleting secret...") delete_operation = client.begin_delete_secret(secret.name) deleted_secret = delete_operation.result() print("Deleted secret with name '{0}'".format(deleted_secret.name)) # Wait for the deletion to complete before purging the secret. # The purge will take some time, so wait before restoring the backup to avoid a conflict. delete_operation.wait() print("\n.. Purge the secret") client.purge_deleted_secret(deleted_secret.name) time.sleep(60) print("Purged secret with name '{0}'".format(deleted_secret.name)) # In the future, if the secret is required again, we can use the backup value to restore it in the Key Vault. print("\n.. Restore the secret using the backed up secret bytes") secret = client.restore_secret_backup(secret_backup) print("Restored secret with name '{0}'".format(secret.name)) except HttpResponseError as e: print("\nThis sample has caught an error. {0}".format(e.message))
class AzureKeyVault: headers = None kvName = None token = None tracer = None uri = None def __init__(self, tracer: logging.Logger, kvName: str, msiClientId: Optional[str] = None): self.tracer = tracer self.tracer.info("initializing KeyVault %s" % kvName) self.kvName = kvName self.uri = "https://%s.vault.azure.net" % kvName self.token = ManagedIdentityCredential(client_id=msiClientId) self.kv_client = SecretClient(vault_url=self.uri, credential=self.token) # Set a secret in the KeyVault def setSecret(self, secretName: str, secretValue: str) -> bool: self.tracer.info("setting KeyVault secret for secretName=%s" % secretName) try: self.kv_client.set_secret(secretName, secretValue) except Exception as e: self.tracer.critical("could not set KeyVault secret (%s)" % e) sys.exit(ERROR_SETTING_KEYVAULT_SECRET) return True # Delete a secret from the KeyVault def deleteSecret(self, secretName: str) -> bool: self.tracer.info("deleting KeyVault secret %s" % secretName) try: poller = self.kv_client.begin_delete_secret(secretName) poller.wait() self.kv_client.purge_deleted_secret(secretName) except Exception as e: self.tracer.critical("could not delete KeyVault secret (%s)" % e) return False return True # Get the current version of a specific secret in the KeyVault def getSecret(self, secretId: str, version: Optional[str] = None) -> KeyVaultSecret: self.tracer.info("getting KeyVault secret for secretId=%s" % secretId) secret = None try: secret = self.kv_client.get_secret(secretId, version) except Exception as e: self.tracer.error( "could not get KeyVault secret for secretId=%s (%s)" % (secretId, e)) return secret # Get the current versions of all secrets inside the customer KeyVault def getCurrentSecrets(self) -> Dict[str, str]: self.tracer.info("getting current KeyVault secrets") secrets = {} try: kvSecrets = self.kv_client.list_properties_of_secrets() for k in kvSecrets: secrets[k.name] = self.kv_client.get_secret(k.name).value except Exception as e: self.tracer.error("could not get current KeyVault secrets (%s)" % e) return secrets # Check if a KeyVault with a specified name exists def exists(self) -> bool: self.tracer.info("checking if KeyVault %s exists" % self.kvName) try: kvSecrets = self.kv_client.list_properties_of_secrets( max_page_size=1) if kvSecrets: self.tracer.info("KeyVault %s exists" % self.kvName) return True except Exception as e: self.tracer.error( "could not determine is KeyVault %s exists (%s)" % (self.kvName, e)) self.tracer.info("KeyVault %s does not exist" % self.kvName) return False
def deleted_secret_recovery(self): """ a sample of enumerating, retrieving, recovering and purging deleted secrets from a key vault """ # create a vault enabling the soft delete feature vault = self.create_vault() # create a secret client credential = DefaultAzureCredential() secret_client = SecretClient(vault_url=vault.properties.vault_uri, credential=credential) # create secrets in the vault secret_to_recover = get_name('secret') secret_to_purge = get_name('secret') secret = secret_client.set_secret(secret_to_recover, "secret to restore") print('created secret {}'.format(secret.name)) secret = secret_client.set_secret(secret_to_purge, "secret to purge") print('created secret {}'.format(secret.name)) # list the name of all of the secrets in the client's vault secret_properties = secret_client.list_properties_of_secrets() print("all of the secrets in the client's vault:") for secret_property in secret_properties: print(secret_property.name) # delete the secrets delete_secret_poller = secret_client.begin_delete_secret( secret_to_recover) deleted_secret = delete_secret_poller.result() delete_secret_poller.wait() print('deleted secret {}'.format(deleted_secret.name)) delete_secret_poller = secret_client.begin_delete_secret( secret_to_purge) deleted_secret = delete_secret_poller.result() delete_secret_poller.wait() print('deleted secret {}'.format(deleted_secret.name)) # list the deleted secrets deleted_secrets = secret_client.list_deleted_secrets() print("all of the deleted secrets in the client's vault:") for deleted_secret in deleted_secrets: print(deleted_secret.name) # recover a deleted secret recover_secret_poller = secret_client.begin_recover_deleted_secret( secret_to_recover) recovered_secret = recover_secret_poller.result() print('recovered secret {}'.format(recovered_secret.name)) # purge a deleted secret secret_client.purge_deleted_secret(secret_to_purge) time.sleep(50) print('purged secret {}'.format(secret_to_purge)) # list the name of all of the secrets in the client's vault secret_properties = secret_client.list_properties_of_secrets() print("all of the secrets in the client's vault:") for secret_property in secret_properties: print(secret_property.name)
class AzureKeyVault(AzureApi): """Azure KeyVault API to set/get secrets from a given Keyvault on Azure""" def __init__(self, keyvault_name: str, auth_path: Path): """Initialize the Azure Keyvault client Args: keyvault_name (str, optional): Name of the keyvault. Defaults to None. auth_path (Path, optional): Authentication json file path. Defaults to None. """ super(AzureKeyVault, self).__init__(auth_path) self.name = keyvault_name self.uri = f"https://{self.name}.vault.azure.net" self.secret_client = SecretClient( vault_url=self.uri, credential=self.client_secret_credentials, version="7.0") self.keyvault_client = KeyVaultManagementClient( self.client_secret_credentials, self.subscription_id) self.exists = self.name in [ i.name for i in self.keyvault_client.vaults.list() ] self.logger.debug(f"Keyvault manager ready: {self.name}") def get_secret(self, name: str) -> str: """Get secret from keyvault Args: name (str): name of the secret to get from keyvault Returns: str: The secret value """ try: secret = self.secret_client.get_secret(name) self.logger.debug(f"Retrieved secret: {secret.id}") except CloudError as e: self.logger.error(f"Could not get secret: {name}") raise e return secret.value def set_secret(self, name: str, value: str) -> str: """Set secret value in keyvault Args: name (str): name of the secret to get from keyvault value (str): value of the keyvault secret """ try: secret = self.secret_client.set_secret(name, value) self.logger.debug(f"Set secret: {secret.id}") except CloudError as e: self.logger.error(f"Could not set secret: {name}") raise e return secret.value def delete_secret(self, name: str, purge: bool = True) -> None: """Delete a secret from the key vault Args: name (str): name of the secret purge (bool, optional): whether to purge the secret or soft-delete. Defaults to True. """ poller = self.secret_client.begin_delete_secret(name) poller.wait() if purge: self.secret_client.purge_deleted_secret(name) def grant_access(self, rsg: str, subnet_ids: List[str] = None) -> None: """Grant access to a keyvault from a list of subnets Args: rsg (str): Resource group of the key vault subnet_ids (List[str], optional): list of subnet IDs. Defaults to None. """ self.logger.info(f"Grant access to KeyVault running for: {self.name}") tenant_id = self._get_tenant_id() vault = self.keyvault_client.vaults.get(rsg, self.name) props = vault.properties tenant_update = False if (tenant_id not in [ x.tenant_id for x in vault.properties.access_policies ]) or vault.properties.tenant_id != tenant_id: tenant_update = True props.tenant_id = tenant_id props.access_policies.append({ "tenant_id": tenant_id, "object_id": self.get_object_id(), "permissions": { "secrets": ["all"] }, }) if subnet_ids: props.network_acls = NetworkRuleSet( default_action="Deny", ip_rules=[], virtual_network_rules=[ VirtualNetworkRule(id=i) for i in subnet_ids ], ) if subnet_ids or tenant_update: kv = self.keyvault_client.vaults.create_or_update( rsg, self.name, { "location": vault.location, "properties": props }, ) kv.wait() def delete_keyvault(self, rsg: str, location: str, purge: bool = True, fail_ok: bool = True) -> None: """Delete a key vault Args: rsg (str): resource group of the vault location (str): location of the vault purge (bool, optional): whether to purge the vault. Defaults to True. """ self.logger.info(f"Deleting keyvault {self.name}") try: d = self.keyvault_client.vaults.delete(rsg, self.name) if purge: self.logger.info(f"Purging keyvault {self.name}") p = self.keyvault_client.vaults.begin_purge_deleted( self.name, location) p.wait() except Exception as e: if not fail_ok: raise e self.exists = False def create_keyvault(self, rsg: str, location: str, soft_delete: bool = True, subnet_ids: List[str] = None): """Creates a key vault object in Azure Args: soft_delete (bool, optional): turn on soft-delete. Defaults to True. subnet_ids (List[str], optional): subnet IDs to grant access to. Defaults to None. Raises: RuntimeError: Keyvault name is already taken. """ self.logger.info(f"Create or update KeyVault running for: {self.name}") if not self.check_name_available(): raise RuntimeError( "Keyvault name is taken by deleted keyvault or is being used.") configuration = { "location": location, "properties": { "sku": { "name": "standard", "family": "A" }, "tenant_id": self._get_tenant_id(), "enable_soft_delete": soft_delete, "access_policies": [{ "tenant_id": self._get_tenant_id(), "object_id": self.get_object_id(), "permissions": { "keys": ["all"], "secrets": ["all", "purge"] }, }], }, } if subnet_ids: configuration["properties"]["network_acls"] = NetworkRuleSet( default_action="Deny", ip_rules=[], virtual_network_rules=[ VirtualNetworkRule(id=i) for i in subnet_ids ]) kv = self.keyvault_client.vaults.begin_create_or_update( rsg, self.name, configuration) kv.wait() while True: try: self.set_secret("test", "x") except: self.logger.warning( "Waiting for keyvault to come up. Please check connection to the VNET." ) time.sleep(1) else: self.delete_secret("test") break self.exists = True def check_name_available(self) -> bool: """Check if keyvault name is available Args: name (str): name of the keyvault Returns: bool: whether the name is available or not """ deleted_vaults = self.keyvault_client.vaults.list_deleted() names = [i.name for i in deleted_vaults] print(names) available = self.keyvault_client.vaults.check_name_availability( VaultCheckNameAvailabilityParameters(name=self.name)) print(available) if (self.name in names) or not available.name_available: return False return True
if c.name.startswith("livekvtest") ] for certificate in deleted_test_certificates: cert_client.purge_deleted_certificate(certificate.name) test_keys = [ k for k in key_client.list_properties_of_keys() if k.name.startswith("livekvtest") ] for key in test_keys: key_client.begin_delete_key(key.name).wait() deleted_test_keys = [ k for k in key_client.list_deleted_keys() if k.name.startswith("livekvtest") ] for key in deleted_test_keys: key_client.purge_deleted_key(key.name) test_secrets = [ s for s in secret_client.list_properties_of_secrets() if s.name.startswith("livekvtest") ] for secret in test_secrets: secret_client.begin_delete_secret(secret.name).wait() deleted_test_secrets = [ s for s in secret_client.list_deleted_secrets() if s.name.startswith("livekvtest") ] for secret in deleted_test_secrets: secret_client.purge_deleted_secret(secret.name)