From 6d1cd040f1cbecf5b9003e7acd4fc4920b30f20d Mon Sep 17 00:00:00 2001 From: nicholasmhughes Date: Fri, 20 Mar 2020 12:39:43 -0400 Subject: [PATCH 01/13] general cleanup --- idem_provider_azurerm/exec/utils/azurerm.py | 1 + 1 file changed, 1 insertion(+) diff --git a/idem_provider_azurerm/exec/utils/azurerm.py b/idem_provider_azurerm/exec/utils/azurerm.py index b326a8a2..86410c30 100644 --- a/idem_provider_azurerm/exec/utils/azurerm.py +++ b/idem_provider_azurerm/exec/utils/azurerm.py @@ -31,6 +31,7 @@ try: from six.moves import range as six_range + import six except ImportError: six_range = range From 531e2613402b7f1d1e5f7458ef26b3fff10016c2 Mon Sep 17 00:00:00 2001 From: Ajnbro <50883410+Ajnbro@users.noreply.github.com> Date: Fri, 20 Mar 2020 08:20:08 -0400 Subject: [PATCH 02/13] Began creation of key execs --- .../exec/azurerm/keyvault/key.py | 150 ++++++++++++++++++ 1 file changed, 150 insertions(+) create mode 100644 idem_provider_azurerm/exec/azurerm/keyvault/key.py diff --git a/idem_provider_azurerm/exec/azurerm/keyvault/key.py b/idem_provider_azurerm/exec/azurerm/keyvault/key.py new file mode 100644 index 00000000..a7b1ee67 --- /dev/null +++ b/idem_provider_azurerm/exec/azurerm/keyvault/key.py @@ -0,0 +1,150 @@ +# -*- coding: utf-8 -*- +''' +Azure Resource Manager (ARM) Key Vault Execution Module + +.. versionadded:: 1.0.0 + +:maintainer: +:maturity: new +:depends: + * `azure `_ >= 4.0.0 + * `azure-common `_ >= 1.1.23 + * `azure-mgmt `_ >= 4.0.0 + * `azure-mgmt-compute `_ >= 4.6.2 + * `azure-mgmt-network `_ >= 4.0.0 + * `azure-mgmt-resource `_ >= 2.2.0 + * `azure-mgmt-storage `_ >= 2.0.0 + * `azure-mgmt-web `_ >= 0.35.0 + * `azure-storage `_ >= 0.36.0 + * `msrestazure `_ >= 0.6.1 +:platform: linux + +:configuration: This module requires Azure Resource Manager credentials to be passed as keyword arguments + to every function in order to work properly. + + Required provider parameters: + + if using username and password: + * ``subscription_id`` + * ``username`` + * ``password`` + + if using a service principal: + * ``subscription_id`` + * ``tenant`` + * ``client_id`` + * ``secret`` + + Optional provider parameters: + +**cloud_environment**: Used to point the cloud driver to different API endpoints, such as Azure GovCloud. + Possible values: + * ``AZURE_PUBLIC_CLOUD`` (default) + * ``AZURE_CHINA_CLOUD`` + * ``AZURE_US_GOV_CLOUD`` + * ``AZURE_GERMAN_CLOUD`` + +''' +# Python libs +from __future__ import absolute_import +import logging + +# Azure libs +HAS_LIBS = False +try: + import azure.keyvault.keys.models # pylint: disable=unused-import + from azure.keyvault.keys.aio import KeyClient + from msrest.exceptions import SerializationError + from msrestazure.azure_exceptions import CloudError + HAS_LIBS = True +except ImportError: + pass + +log = logging.getLogger(__name__) + + +async def _get_key_client(hub, vault_url, **kwargs): + ''' + .. versionadded:: VERSION + + Load the key client and return a KeyClient object. + + :param vault_url: The URL of the vault that the client will access. + + ''' + credentials, subscription_id, cloud_env = await hub.exec.utils.azurerm.determine_auth(kwargs) + key_client = KeyClient(vault_url, credentials) + return key_client + + +async def backup_key(hub, name, vault_url, **kwargs): + ''' + .. versionadded:: VERSION + + Back up a key in a protected form useable only by Azure Key Vault. Requires key/backup permission. This is intended + to allow copying a key from one vault to another. Both vaults must be owned by the same Azure subscription. + Also, backup / restore cannot be performed across geopolitical boundaries. For example, a backup from a vault + in a USA region cannot be restored to a vault in an EU region. + + :param name: The name of the key to back up. + + :param vault_url: The URL of the vault that the client will access. + + CLI Example: + + .. code-block:: bash + + azurerm.keyvault.key.backup_key test_name test_vault + + ''' + ''' + result = {} + kconn = await _get_key_client(vault_url, **kwargs) + + try: + key = kconn.keys.backup_key( + name=name, + ) + + result = key + except CloudError as exc: + await hub.exec.utils.azurerm.log_cloud_error('key', str(exc), **kwargs) + result = {'error': str(exc)} + + return result + ''' + pass + + +async def create_ec_key(hub, name, vault_url, **kwargs): + ''' + .. versionadded:: VERSION + + Create a new elliptic curve key or, if name is already in use, create a new version of the key. Requires the + keys/create permission. + + :param name: The name of the new key. + + :param vault_url: The URL of the vault that the client will access. + + CLI Example: + + .. code-block:: bash + + azurerm.keyvault.key.create_ec_key test_name test_vault + + ''' + result = {} + kconn = await _get_key_client(hub, vault_url, **kwargs) + + try: + key = kconn.keys.create_ec_key( + name=name, + ) + + result = key + except CloudError as exc: + await hub.exec.utils.azurerm.log_cloud_error('key', str(exc), **kwargs) + result = {'error': str(exc)} + + return result From 7cf3167b4eae0d7826e9da07018db53e5702c93f Mon Sep 17 00:00:00 2001 From: nicholasmhughes Date: Fri, 20 Mar 2020 17:33:25 -0400 Subject: [PATCH 03/13] splat --- idem_provider_azurerm/exec/azurerm/keyvault/key.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/idem_provider_azurerm/exec/azurerm/keyvault/key.py b/idem_provider_azurerm/exec/azurerm/keyvault/key.py index a7b1ee67..0009845d 100644 --- a/idem_provider_azurerm/exec/azurerm/keyvault/key.py +++ b/idem_provider_azurerm/exec/azurerm/keyvault/key.py @@ -72,7 +72,7 @@ async def _get_key_client(hub, vault_url, **kwargs): :param vault_url: The URL of the vault that the client will access. ''' - credentials, subscription_id, cloud_env = await hub.exec.utils.azurerm.determine_auth(kwargs) + credentials, subscription_id, cloud_env = await hub.exec.utils.azurerm.determine_auth(**kwargs) key_client = KeyClient(vault_url, credentials) return key_client From e5ee987c5cae307b70ab620fc4761394a55468cc Mon Sep 17 00:00:00 2001 From: nicholasmhughes Date: Sat, 21 Mar 2020 16:26:28 -0400 Subject: [PATCH 04/13] working keyclient implementation --- .../exec/azurerm/keyvault/key.py | 103 +++++++++++++++--- 1 file changed, 90 insertions(+), 13 deletions(-) diff --git a/idem_provider_azurerm/exec/azurerm/keyvault/key.py b/idem_provider_azurerm/exec/azurerm/keyvault/key.py index 0009845d..a605917d 100644 --- a/idem_provider_azurerm/exec/azurerm/keyvault/key.py +++ b/idem_provider_azurerm/exec/azurerm/keyvault/key.py @@ -2,7 +2,7 @@ ''' Azure Resource Manager (ARM) Key Vault Execution Module -.. versionadded:: 1.0.0 +.. versionadded:: VERSION :maintainer: :maturity: new @@ -47,15 +47,15 @@ ''' # Python libs from __future__ import absolute_import +import datetime import logging +import os # Azure libs HAS_LIBS = False try: - import azure.keyvault.keys.models # pylint: disable=unused-import - from azure.keyvault.keys.aio import KeyClient - from msrest.exceptions import SerializationError - from msrestazure.azure_exceptions import CloudError + from azure.keyvault.keys._shared._generated.v7_0.models._models_py3 import KeyVaultErrorException + from azure.keyvault.keys import KeyClient HAS_LIBS = True except ImportError: pass @@ -63,20 +63,62 @@ log = logging.getLogger(__name__) -async def _get_key_client(hub, vault_url, **kwargs): +async def get_key_client(hub, vault_url, **kwargs): ''' .. versionadded:: VERSION Load the key client and return a KeyClient object. :param vault_url: The URL of the vault that the client will access. - ''' - credentials, subscription_id, cloud_env = await hub.exec.utils.azurerm.determine_auth(**kwargs) - key_client = KeyClient(vault_url, credentials) + credential = await hub.exec.utils.azurerm.get_identity_credentials(**kwargs) + + key_client = KeyClient(vault_url=vault_url, credential=credential) + return key_client +def _key_as_dict(key): + result = {} + attrs = [ + 'id', + 'key_operations', + 'key_type', + 'name', + 'properties' + ] + for attr in attrs: + val = getattr(key, attr) + if attr == 'properties': + val = _key_properties_as_dict(val) + result[attr] = val + return result + + +def _key_properties_as_dict(key_properties): + result = {} + props = [ + 'created_on', + 'enabled', + 'expires_on', + 'id', + 'managed', + 'name', + 'not_before', + 'recovery_level', + 'tags', + 'updated_on', + 'vault_url', + 'version' + ] + for prop in props: + val = getattr(key_properties, prop) + if isinstance(val, datetime.datetime): + val = val.isoformat() + result[prop] = val + return result + + async def backup_key(hub, name, vault_url, **kwargs): ''' .. versionadded:: VERSION @@ -135,15 +177,50 @@ async def create_ec_key(hub, name, vault_url, **kwargs): ''' result = {} - kconn = await _get_key_client(hub, vault_url, **kwargs) + kconn = await hub.exec.azurerm.keyvault.key.get_key_client(hub, vault_url, **kwargs) try: - key = kconn.keys.create_ec_key( + key = kconn.create_ec_key( name=name, ) - result = key - except CloudError as exc: + result = _key_as_dict(key) + + log.debug('Key return: %s', result) + except KeyVaultErrorException as exc: + await hub.exec.utils.azurerm.log_cloud_error('key', str(exc), **kwargs) + result = {'error': str(exc)} + + return result + + +async def list_(hub, vault_url, **kwargs): + ''' + .. versionadded:: VERSION + + List identifiers and properties of all keys in the vault. Requires keys/list permission. + + :param vault_url: The URL of the vault that the client will access. + + CLI Example: + + .. code-block:: bash + + azurerm.keyvault.key.list test_vault + + ''' + result = {} + + kconn = await hub.exec.azurerm.keyvault.key.get_key_client(hub, vault_url, **kwargs) + + try: + keys = kconn.list_properties_of_keys() + + for key in keys: + result[key.name] = _key_properties_as_dict(key) + + log.debug('Key listing: %s', result) + except KeyVaultErrorException as exc: await hub.exec.utils.azurerm.log_cloud_error('key', str(exc), **kwargs) result = {'error': str(exc)} From 644e799cb306f8730a80593144465b188ada7cbe Mon Sep 17 00:00:00 2001 From: Ajnbro <50883410+Ajnbro@users.noreply.github.com> Date: Tue, 24 Mar 2020 04:52:33 -0400 Subject: [PATCH 05/13] Added all key exec modules --- .../exec/azurerm/keyvault/key.py | 499 +++++++++++++++++- 1 file changed, 480 insertions(+), 19 deletions(-) diff --git a/idem_provider_azurerm/exec/azurerm/keyvault/key.py b/idem_provider_azurerm/exec/azurerm/keyvault/key.py index a605917d..615c09db 100644 --- a/idem_provider_azurerm/exec/azurerm/keyvault/key.py +++ b/idem_provider_azurerm/exec/azurerm/keyvault/key.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- ''' -Azure Resource Manager (ARM) Key Vault Execution Module +Azure Resource Manager (ARM) Key Execution Module .. versionadded:: VERSION @@ -11,6 +11,8 @@ * `azure-common `_ >= 1.1.23 * `azure-mgmt `_ >= 4.0.0 * `azure-mgmt-compute `_ >= 4.6.2 + * `azure-keyvault `_ >= 1.1.0 + * `azure-keyvault-keys `_ >= 4.0.0 * `azure-mgmt-network `_ >= 4.0.0 * `azure-mgmt-resource `_ >= 2.2.0 * `azure-mgmt-storage `_ >= 2.0.0 @@ -54,8 +56,10 @@ # Azure libs HAS_LIBS = False try: - from azure.keyvault.keys._shared._generated.v7_0.models._models_py3 import KeyVaultErrorException from azure.keyvault.keys import KeyClient + from azure.keyvault.keys._shared._generated.v7_0.models._models_py3 import KeyVaultErrorException + from azure.core.exceptions import ResourceNotFoundError, HttpResponseError, ResourceExistsError + from msrest.exceptions import ValidationError, SerializationError HAS_LIBS = True except ImportError: pass @@ -139,23 +143,88 @@ async def backup_key(hub, name, vault_url, **kwargs): azurerm.keyvault.key.backup_key test_name test_vault ''' + result = {} + kconn = await hub.exec.azurerm.keyvault.key.get_key_client(vault_url, **kwargs) + + try: + backup = kconn.backup_key( + name=name, + ) + + result = backup + except (KeyVaultErrorException, ResourceNotFoundError) as exc: + result = {'error': str(exc)} + + return result + + +async def begin_delete_key(hub, name, vault_url, **kwargs): + ''' + .. versionadded:: VERSION + + Delete all versions of a key and its cryptographic material. Requires keys/delete permission. When this method + returns Key Vault has begun deleting the key. Deletion may take several seconds in a vault with soft-delete + enabled. This method therefore returns a poller enabling you to wait for deletion to complete. + + :param name: The name of the key to delete. + + :param vault_url: The URL of the vault that the client will access. + + CLI Example: + + .. code-block:: bash + + azurerm.keyvault.key.begin_delete_key test_name test_vault + ''' result = {} - kconn = await _get_key_client(vault_url, **kwargs) + kconn = await hub.exec.azurerm.keyvault.key.get_key_client(vault_url, **kwargs) try: - key = kconn.keys.backup_key( + key = kconn.begin_delete_key( name=name, ) - result = key - except CloudError as exc: - await hub.exec.utils.azurerm.log_cloud_error('key', str(exc), **kwargs) + result = _key_as_dict(key.result()) + except (KeyVaultErrorException, ResourceNotFoundError) as exc: result = {'error': str(exc)} return result + + +async def begin_recover_deleted_key(hub, name, vault_url, **kwargs): ''' - pass + .. versionadded:: VERSION + + Recover a deleted key to its latest version. Possible only in a vault with soft-delete enabled. Requires + keys/recover permission. When this method returns Key Vault has begun recovering the key. Recovery may take + several seconds. This method therefore returns a poller enabling you to wait for recovery to complete. Waiting + is only necessary when you want to use the recovered key in another operation immediately. + + :param name: The name of the deleted key to recover. + + :param vault_url: The URL of the vault that the client will access. + + CLI Example: + + .. code-block:: bash + + azurerm.keyvault.key.begin_recover_deleted_key test_name test_vault + + ''' + result = {} + kconn = await hub.exec.azurerm.keyvault.key.get_key_client(vault_url, **kwargs) + + try: + key = kconn.begin_recover_deleted_key( + name=name, + ) + + result = _key_as_dict(key.result()) + except (KeyVaultErrorException, HttpResponseError) as exc: + result = {'error': str(exc)} + + return result async def create_ec_key(hub, name, vault_url, **kwargs): @@ -163,9 +232,9 @@ async def create_ec_key(hub, name, vault_url, **kwargs): .. versionadded:: VERSION Create a new elliptic curve key or, if name is already in use, create a new version of the key. Requires the - keys/create permission. + keys/create permission. Key properties can be specified as keyword arguments. - :param name: The name of the new key. + :param name: The name of the new key. Key names can only contain alphanumeric characters and dashes. :param vault_url: The URL of the vault that the client will access. @@ -177,7 +246,7 @@ async def create_ec_key(hub, name, vault_url, **kwargs): ''' result = {} - kconn = await hub.exec.azurerm.keyvault.key.get_key_client(hub, vault_url, **kwargs) + kconn = await hub.exec.azurerm.keyvault.key.get_key_client(vault_url, **kwargs) try: key = kconn.create_ec_key( @@ -185,10 +254,239 @@ async def create_ec_key(hub, name, vault_url, **kwargs): ) result = _key_as_dict(key) + except (KeyVaultErrorException, ValidationError, HttpResponseError) as exc: + result = {'error': str(exc)} + + return result + +async def create_key(hub, name, type, vault_url, **kwargs): + ''' + .. versionadded:: VERSION + + Create a key or, if name is already in use, create a new version of the key. Requires keys/create permission. + Key properties can be specified as keyword arguments. + + :param name: The name of the new key. Key names can only contain alphanumeric characters and dashes. + + :param type: The type of key to create. Possible values include: 'RSA', 'EC'. + + :param vault_url: The URL of the vault that the client will access. - log.debug('Key return: %s', result) - except KeyVaultErrorException as exc: - await hub.exec.utils.azurerm.log_cloud_error('key', str(exc), **kwargs) + CLI Example: + + .. code-block:: bash + + azurerm.keyvault.key.create_key test_name test_type test_vault + + ''' + result = {} + kconn = await hub.exec.azurerm.keyvault.key.get_key_client(vault_url, **kwargs) + + try: + key = kconn.create_key( + name=name, + key_type=type, + ) + + result = _key_as_dict(key) + except (KeyVaultErrorException, ValidationError, HttpResponseError) as exc: + result = {'error': str(exc)} + + return result + + +async def create_rsa_key(hub, name, vault_url, **kwargs): + ''' + .. versionadded:: VERSION + + Create a new RSA key or, if name is already in use, create a new version of the key. Requires the keys/create + permission. Key properties can be specified as keyword arguments. + + :param name: The name of the new key. Key names can only contain alphanumeric characters and dashes. + + :param vault_url: The URL of the vault that the client will access. + + CLI Example: + + .. code-block:: bash + + azurerm.keyvault.key.create_rsa_key test_name test_vault + + ''' + result = {} + kconn = await hub.exec.azurerm.keyvault.key.get_key_client(vault_url, **kwargs) + + try: + key = kconn.create_rsa_key( + name=name, + ) + + result = _key_as_dict(key) + except (KeyVaultErrorException, ValidationError, HttpResponseError) as exc: + result = {'error': str(exc)} + + return result + + +async def get_deleted_key(hub, name, vault_url, **kwargs): + ''' + .. versionadded:: VERSION + + Get a deleted key. Possible only in a vault with soft-delete enabled. Requires keys/get permission. + + :param name: The name of the key. + + :param vault_url: The URL of the vault that the client will access. + + CLI Example: + + .. code-block:: bash + + azurerm.keyvault.key.get_deleted_key test_name test_vault + + ''' + result = {} + kconn = await hub.exec.azurerm.keyvault.key.get_key_client(vault_url, **kwargs) + + try: + key = kconn.get_deleted_key( + name=name, + ) + + result = _key_as_dict(key) + except (KeyVaultErrorException, ResourceNotFoundError) as exc: + result = {'error': str(exc)} + + return result + + +async def get_key(hub, name, vault_url, version=None, **kwargs): + ''' + .. versionadded:: VERSION + + Get a key's attributes and, if it's an asymmetric key, its public material. Requires keys/get permission. + + :param name: The name of the key to get. + + :param vault_url: The URL of the vault that the client will access. + + :param version: An optional parameter used to specify the version of the key to get. If not specified, gets the + latest version of the key. + + CLI Example: + + .. code-block:: bash + + azurerm.keyvault.key.get_key test_name test_vault test_version + + ''' + result = {} + kconn = await hub.exec.azurerm.keyvault.key.get_key_client(vault_url, **kwargs) + + try: + key = kconn.get_key( + name=name, + version=version, + ) + + result = _key_as_dict(key) + except (KeyVaultErrorException, ResourceNotFoundError) as exc: + result = {'error': str(exc)} + + return result + + +async def import_key(hub, name, vault_url, kid=None, kty=None, key_ops=None, n=None, e=None, d=None, dp=None, dq=None, + qi=None, p=None, q=None, k=None, t=None, crv=None, x=None, y=None, **kwargs): + ''' + .. versionadded:: VERSION + + Import a key created externally. Requires keys/import permission. If name is already in use, the key will be + imported as a new version. Parameters used to build a JSONWebKey object will be passed to this module. More + information about some of those parameters can be found at the following link: + https://tools.ietf.org/html/draft-ietf-jose-json-web-key-18. + + :param name: The name of the imported key. + + :param vault_url: The URL of the vault that the client will access. + + :param kid: Key identifier. + + :param kty: Key type. Possible values inclide: 'ec', 'ec_hsm', 'oct', 'rsa', 'rsa_hsm'. + + :param key_ops: A list of allow operations for the key. Possible elements of the list include: 'decrypt', 'encrypt', + 'sign', 'unwrap_key', 'verify', 'wrap_key' + + :param n: RSA modulus. + + :param e: RSA public exponent. + + :param d: RSA private exponent, or the D component of the EC private key. + + :param dp: RSA private key parameter. + + :param dq: RSA private key parameter. + + :param qi: RSA private key parameter. + + :param p: RSA secret prime. + + :param q: RSA secret prime, with p < q. + + :param k: Symmetric key. + + :param t: HSM Token, used with 'Bring Your Own Key'. + + :param crv: Elliptic curve name. Posible values include: 'p_256', 'p_256_k', 'p_384', and 'p_521'. + + :param x: X component of an EC public key. + + :param y: Y component of an EC public key. + + CLI Example: + + .. code-block:: bash + + azurerm.keyvault.key.import_key test_name test_vault test_key_params + + ''' + result = {} + kconn = await hub.exec.azurerm.keyvault.key.get_key_client(vault_url, **kwargs) + + try: + keymodel = await hub.exec.utils.azurerm.create_object_model( + 'keyvault-keys', + 'JsonWebKey', + kid=kid, + kty=kty, + key_ops=key_ops, + n=n, + e=e, + d=d, + dp=dp, + dq=dq, + qi=qi, + p=p, + q=q, + k=k, + t=t, + crv=crv, + x=x, + y=y, + **kwargs + ) + except TypeError as exc: + result = {'error': 'The object model could not be built. ({0})'.format(str(exc))} + return result + + try: + key = kconn.import_key( + name=name, + key=keymodel, + ) + + result = _key_as_dict(key) + except (KeyVaultErrorException, ResourceNotFoundError) as exc: result = {'error': str(exc)} return result @@ -210,18 +508,181 @@ async def list_(hub, vault_url, **kwargs): ''' result = {} - - kconn = await hub.exec.azurerm.keyvault.key.get_key_client(hub, vault_url, **kwargs) + kconn = await hub.exec.azurerm.keyvault.key.get_key_client(vault_url, **kwargs) try: keys = kconn.list_properties_of_keys() for key in keys: result[key.name] = _key_properties_as_dict(key) + except (KeyVaultErrorException, ResourceNotFoundError) as exc: + result = {'error': str(exc)} + + return result + + +async def list_properties_of_key_versions(hub, name, vault_url, **kwargs): + ''' + .. versionadded:: VERSION + + List the identifiers and properties of a key's versions. Requires keys/list permission. + + :param name: The name of the key. + + :param vault_url: The URL of the vault that the client will access. + + CLI Example: + + .. code-block:: bash + + azurerm.keyvault.key.list_properties_of_key_versions test_name test_vault + + ''' + result = {} + kconn = await hub.exec.azurerm.keyvault.key.get_key_client(vault_url, **kwargs) + + try: + keys = kconn.list_properties_of_key_versions( + name=name, + ) + + for key in keys: + result[key.name] = _key_properties_as_dict(key) + except (KeyVaultErrorException, ResourceNotFoundError) as exc: + result = {'error': str(exc)} + + return result + + +async def list_deleted_keys(hub, vault_url, **kwargs): + ''' + .. versionadded:: VERSION + + List all deleted keys, including the public part of each. Possible only in a vault with soft-delete enabled. + Requires keys/list permission. + + :param vault_url: The URL of the vault that the client will access. + + CLI Example: + + .. code-block:: bash + + azurerm.keyvault.key.list_deleted_keys test_vault - log.debug('Key listing: %s', result) - except KeyVaultErrorException as exc: - await hub.exec.utils.azurerm.log_cloud_error('key', str(exc), **kwargs) + ''' + result = {} + kconn = await hub.exec.azurerm.keyvault.key.get_key_client(vault_url, **kwargs) + + try: + keys = kconn.list_deleted_keys() + + for key in keys: + result[key.name] = _key_as_dict(key) + except (KeyVaultErrorException, ResourceNotFoundError) as exc: + result = {'error': str(exc)} + + return result + + +async def purge_deleted_key(hub, name, vault_url, **kwargs): + ''' + .. versionadded:: VERSION + + Permanently deletes a deleted key. Only possible in a vault with soft-delete enabled. Performs an irreversible + deletion of the specified key, without possibility for recovery. The operation is not available if the + recovery_level does not specify 'Purgeable'. This method is only necessary for purging a key before its + scheduled_purge_date. Requires keys/purge permission. + + :param vault_url: The URL of the vault that the client will access. + + CLI Example: + + .. code-block:: bash + + azurerm.keyvault.key.purge_deleted_key test_name test_vault + + ''' + result = False + kconn = await hub.exec.azurerm.keyvault.key.get_key_client(vault_url, **kwargs) + + try: + key = kconn.purge_deleted_key( + name=name, + ) + + result = True + except (KeyVaultErrorException, HttpResponseError) as exc: + result = {'error': str(exc)} + + return result + + +async def restore_key_backup(hub, backup, vault_url, **kwargs): + ''' + .. versionadded:: VERSION + + Restore a key backup to the vault. This imports all versions of the key, with its name, attributes, and access + control policies. If the key's name is already in use, restoring it will fail. Also, the target vault must be + owned by the same Microsoft Azure subscription as the source vault. Requires keys/restore permission. + + :param backup: A key backup as returned by the backup_key execution module. + + :param vault_url: The URL of the vault that the client will access. + + CLI Example: + + .. code-block:: bash + + azurerm.keyvault.key.restore_key_backup test_backup test_vault + + ''' + result = {} + kconn = await hub.exec.azurerm.keyvault.key.get_key_client(vault_url, **kwargs) + + try: + key = kconn.restore_key_backup( + backup=backup, + ) + + result = _key_as_dict(key) + except (KeyVaultErrorException, ResourceExistsError, SerializationError) as exc: + result = {'error': str(exc)} + + return result + + +async def update_key_properties(hub, name, vault_url, version=None, **kwargs): + ''' + .. versionadded:: VERSION + + Change a key's properties (not its cryptographic material). Requires keys/update permission. Key properties that + need to be updated can be specified as keyword arguments. + + :param name: The name of the key to update. + + :param vault_url: The URL of the vault that the client will access. + + :param version: An optional parameter used to specify the version of the key to update. If no version is specified, + the latest version of the key will be updated. + + CLI Example: + + .. code-block:: bash + + azurerm.keyvault.key.update_key_properties test_name test_vault test_version + + ''' + result = {} + kconn = await hub.exec.azurerm.keyvault.key.get_key_client(vault_url, **kwargs) + + try: + key = kconn.update_key_properties( + name=name, + version=version, + ) + + result = _key_as_dict(key) + except (KeyVaultErrorException, ResourceNotFoundError) as exc: result = {'error': str(exc)} return result From 27a54c55cb1d8fa5b1f02da3b1a93944d0fd3a39 Mon Sep 17 00:00:00 2001 From: Ajnbro <50883410+Ajnbro@users.noreply.github.com> Date: Tue, 24 Mar 2020 05:20:00 -0400 Subject: [PATCH 06/13] Fixed dependencies and typos --- idem_provider_azurerm/states/azurerm/keyvault/vault.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/idem_provider_azurerm/states/azurerm/keyvault/vault.py b/idem_provider_azurerm/states/azurerm/keyvault/vault.py index 8b25d333..96ccbb21 100644 --- a/idem_provider_azurerm/states/azurerm/keyvault/vault.py +++ b/idem_provider_azurerm/states/azurerm/keyvault/vault.py @@ -11,6 +11,7 @@ * `azure-common `_ >= 1.1.23 * `azure-mgmt `_ >= 4.0.0 * `azure-mgmt-compute `_ >= 4.6.2 + * `azure-mgmt-keyvault `_ >= 1.1.0 * `azure-mgmt-network `_ >= 4.0.0 * `azure-mgmt-resource `_ >= 2.2.0 * `azure-mgmt-storage `_ >= 2.0.0 @@ -90,8 +91,6 @@ async def present(hub, ctx, name, resource_group, location, tenant_id, sku, acce :param resource_group: The name of the resource group to which the vault belongs. - :param resource_group: The name of the resource group to which the vault belongs. - :param location: The supported Azure location where the key vault should be created. :param tenant_id: The Azure Active Direction tenant ID that should be used for authenticating requests to From 4ddbe04323744526d780c739522f97596809ebfa Mon Sep 17 00:00:00 2001 From: Ajnbro <50883410+Ajnbro@users.noreply.github.com> Date: Tue, 24 Mar 2020 21:02:16 -0400 Subject: [PATCH 07/13] Prepped for PR --- .../exec/azurerm/keyvault/key.py | 128 +++++-- .../states/azurerm/keyvault/key.py | 334 ++++++++++++++++++ 2 files changed, 434 insertions(+), 28 deletions(-) create mode 100644 idem_provider_azurerm/states/azurerm/keyvault/key.py diff --git a/idem_provider_azurerm/exec/azurerm/keyvault/key.py b/idem_provider_azurerm/exec/azurerm/keyvault/key.py index 615c09db..6d88f7b2 100644 --- a/idem_provider_azurerm/exec/azurerm/keyvault/key.py +++ b/idem_provider_azurerm/exec/azurerm/keyvault/key.py @@ -9,10 +9,11 @@ :depends: * `azure `_ >= 4.0.0 * `azure-common `_ >= 1.1.23 - * `azure-mgmt `_ >= 4.0.0 - * `azure-mgmt-compute `_ >= 4.6.2 * `azure-keyvault `_ >= 1.1.0 * `azure-keyvault-keys `_ >= 4.0.0 + * `azure-mgmt `_ >= 4.0.0 + * `azure-mgmt-compute `_ >= 4.6.2 + * `azure-mgmt-keyvault `_ >= 1.1.0 * `azure-mgmt-network `_ >= 4.0.0 * `azure-mgmt-resource `_ >= 2.2.0 * `azure-mgmt-storage `_ >= 2.0.0 @@ -227,7 +228,8 @@ async def begin_recover_deleted_key(hub, name, vault_url, **kwargs): return result -async def create_ec_key(hub, name, vault_url, **kwargs): +async def create_ec_key(hub, name, vault_url, key_ops=None, enabled=None, expires_on=None, not_before=None, tags=None, + **kwargs): ''' .. versionadded:: VERSION @@ -238,6 +240,19 @@ async def create_ec_key(hub, name, vault_url, **kwargs): :param vault_url: The URL of the vault that the client will access. + :param key_ops: A list of permitted key operations. Possible values include: 'decrypt', 'encrypt', 'sign', + 'unwrap_key', 'verify', 'wrap_key'. + + :param enabled: Whether the key is enabled for use. + + :param expires_on: When the key will expire, in UTC. This parameter must be a string representation of a Datetime + object in ISO-8601 format. + + :param not_before: The time before which the key can not be used, in UTC. This parameter must be a string + representation of a Datetime object in ISO-8601 format. + + :param tags: Application specific metadata in the form of key-value pairs. + CLI Example: .. code-block:: bash @@ -251,6 +266,11 @@ async def create_ec_key(hub, name, vault_url, **kwargs): try: key = kconn.create_ec_key( name=name, + key_operations=key_ops, + enabled=enabled, + expires_on=expires_on, + not_before=not_before, + tags=tags, ) result = _key_as_dict(key) @@ -259,7 +279,9 @@ async def create_ec_key(hub, name, vault_url, **kwargs): return result -async def create_key(hub, name, type, vault_url, **kwargs): + +async def create_key(hub, name, key_type, vault_url, key_ops=None, enabled=None, expires_on=None, not_before=None, tags=None, + **kwargs): ''' .. versionadded:: VERSION @@ -268,10 +290,23 @@ async def create_key(hub, name, type, vault_url, **kwargs): :param name: The name of the new key. Key names can only contain alphanumeric characters and dashes. - :param type: The type of key to create. Possible values include: 'RSA', 'EC'. + :param key_type: The type of key to create. Possible values include: 'ec', 'ec_hsm', 'oct', 'rsa', 'rsa_hsm'. :param vault_url: The URL of the vault that the client will access. + :param key_ops: A list of permitted key operations. Possible values include: 'decrypt', 'encrypt', 'sign', + 'unwrap_key', 'verify', 'wrap_key'. + + :param enabled: Whether the key is enabled for use. + + :param expires_on: When the key will expire, in UTC. This parameter must be a string representation of a Datetime + object in ISO-8601 format. + + :param not_before: The time before which the key can not be used, in UTC. This parameter must be a string + representation of a Datetime object in ISO-8601 format. + + :param tags: Application specific metadata in the form of key-value pairs. + CLI Example: .. code-block:: bash @@ -282,10 +317,18 @@ async def create_key(hub, name, type, vault_url, **kwargs): result = {} kconn = await hub.exec.azurerm.keyvault.key.get_key_client(vault_url, **kwargs) + if key_type != 'oct': + key_type = key_type.upper().replace('_', '-') + try: key = kconn.create_key( name=name, - key_type=type, + key_type=key_type, + enabled=enabled, + expires_on=expires_on, + not_before=not_before, + tags=tags, + key_operations=key_ops, ) result = _key_as_dict(key) @@ -295,7 +338,7 @@ async def create_key(hub, name, type, vault_url, **kwargs): return result -async def create_rsa_key(hub, name, vault_url, **kwargs): +async def create_rsa_key(hub, name, vault_url, key_ops=None, enabled=None, expires_on=None, not_before=None, tags=None, **kwargs): ''' .. versionadded:: VERSION @@ -306,6 +349,19 @@ async def create_rsa_key(hub, name, vault_url, **kwargs): :param vault_url: The URL of the vault that the client will access. + :param key_ops: A list of permitted key operations. Possible values include: 'decrypt', 'encrypt', 'sign', + 'unwrap_key', 'verify', 'wrap_key'. + + :param enabled: Whether the key is enabled for use. + + :param expires_on: When the key will expire, in UTC. This parameter must be a string representation of a Datetime + object in ISO-8601 format. + + :param not_before: The time before which the key can not be used, in UTC. This parameter must be a string + representation of a Datetime object in ISO-8601 format. + + :param tags: Application specific metadata in the form of key-value pairs. + CLI Example: .. code-block:: bash @@ -319,6 +375,11 @@ async def create_rsa_key(hub, name, vault_url, **kwargs): try: key = kconn.create_rsa_key( name=name, + key_operations=key_ops, + enabled=enabled, + expires_on=expires_on, + not_before=not_before, + tags=tags ) result = _key_as_dict(key) @@ -396,8 +457,7 @@ async def get_key(hub, name, vault_url, version=None, **kwargs): return result -async def import_key(hub, name, vault_url, kid=None, kty=None, key_ops=None, n=None, e=None, d=None, dp=None, dq=None, - qi=None, p=None, q=None, k=None, t=None, crv=None, x=None, y=None, **kwargs): +async def import_key(hub, name, vault_url, **kwargs): ''' .. versionadded:: VERSION @@ -410,6 +470,10 @@ async def import_key(hub, name, vault_url, kid=None, kty=None, key_ops=None, n=N :param vault_url: The URL of the vault that the client will access. + Additional parameters passed as keyword arguments are used to build a JSONWebKey object will be passed to this + module. Below some of those parameters are defined. More information about some of those parameters can be + found at the following link: https://tools.ietf.org/html/draft-ietf-jose-json-web-key-18. + :param kid: Key identifier. :param kty: Key type. Possible values inclide: 'ec', 'ec_hsm', 'oct', 'rsa', 'rsa_hsm'. @@ -447,32 +511,19 @@ async def import_key(hub, name, vault_url, kid=None, kty=None, key_ops=None, n=N .. code-block:: bash - azurerm.keyvault.key.import_key test_name test_vault test_key_params + azurerm.keyvault.key.import_key test_name test_vault test_webkey_params ''' result = {} kconn = await hub.exec.azurerm.keyvault.key.get_key_client(vault_url, **kwargs) + if kwargs['key_type'] != 'oct': + kwargs['key_type'] = kwargs.get('key_type').upper().replace('_', '-') + try: keymodel = await hub.exec.utils.azurerm.create_object_model( 'keyvault-keys', 'JsonWebKey', - kid=kid, - kty=kty, - key_ops=key_ops, - n=n, - e=e, - d=d, - dp=dp, - dq=dq, - qi=qi, - p=p, - q=q, - k=k, - t=t, - crv=crv, - x=x, - y=y, **kwargs ) except TypeError as exc: @@ -593,6 +644,8 @@ async def purge_deleted_key(hub, name, vault_url, **kwargs): recovery_level does not specify 'Purgeable'. This method is only necessary for purging a key before its scheduled_purge_date. Requires keys/purge permission. + :param name: The name of the deleted key to purge. + :param vault_url: The URL of the vault that the client will access. CLI Example: @@ -623,7 +676,7 @@ async def restore_key_backup(hub, backup, vault_url, **kwargs): Restore a key backup to the vault. This imports all versions of the key, with its name, attributes, and access control policies. If the key's name is already in use, restoring it will fail. Also, the target vault must be - owned by the same Microsoft Azure subscription as the source vault. Requires keys/restore permission. + owned by the same Microsoft Azure subscription as the source vault. Requires keys/restore permission. :param backup: A key backup as returned by the backup_key execution module. @@ -651,7 +704,8 @@ async def restore_key_backup(hub, backup, vault_url, **kwargs): return result -async def update_key_properties(hub, name, vault_url, version=None, **kwargs): +async def update_key_properties(hub, name, vault_url, version=None, key_ops=None, enabled=None, expires_on=None, not_before=None, + tags=None, **kwargs): ''' .. versionadded:: VERSION @@ -665,6 +719,19 @@ async def update_key_properties(hub, name, vault_url, version=None, **kwargs): :param version: An optional parameter used to specify the version of the key to update. If no version is specified, the latest version of the key will be updated. + :param key_ops: A list of permitted key operations. Possible values include: 'decrypt', 'encrypt', 'sign', + 'unwrap_key', 'verify', 'wrap_key'. + + :param enabled: Whether the key is enabled for use. + + :param expires_on: When the key will expire, in UTC. This parameter must be a string representation of a Datetime + object in ISO-8601 format. + + :param not_before: The time before which the key can not be used, in UTC. This parameter must be a string + representation of a Datetime object in ISO-8601 format. + + :param tags: Application specific metadata in the form of key-value pairs. + CLI Example: .. code-block:: bash @@ -679,6 +746,11 @@ async def update_key_properties(hub, name, vault_url, version=None, **kwargs): key = kconn.update_key_properties( name=name, version=version, + key_operations=key_ops, + enabled=enabled, + expires_on=expires_on, + not_before=not_before, + tags=tags, ) result = _key_as_dict(key) diff --git a/idem_provider_azurerm/states/azurerm/keyvault/key.py b/idem_provider_azurerm/states/azurerm/keyvault/key.py new file mode 100644 index 00000000..22b028ea --- /dev/null +++ b/idem_provider_azurerm/states/azurerm/keyvault/key.py @@ -0,0 +1,334 @@ +# -*- coding: utf-8 -*- +''' +Azure Resource Manager (ARM) Key State Module + +.. versionadded:: VERSION + +:maintainer: +:maturity: new +:depends: + * `azure `_ >= 4.0.0 + * `azure-common `_ >= 1.1.23 + * `azure-keyvault `_ >= 1.1.0 + * `azure-keyvault-keys `_ >= 4.0.0 + * `azure-mgmt `_ >= 4.0.0 + * `azure-mgmt-compute `_ >= 4.6.2 + * `azure-mgmt-keyvault `_ >= 1.1.0 + * `azure-mgmt-network `_ >= 4.0.0 + * `azure-mgmt-resource `_ >= 2.2.0 + * `azure-mgmt-storage `_ >= 2.0.0 + * `azure-mgmt-web `_ >= 0.35.0 + * `azure-storage `_ >= 0.36.0 + * `msrestazure `_ >= 0.6.1 +:platform: linux + +:configuration: This module requires Azure Resource Manager credentials to be passed as a dictionary of + keyword arguments to the ``connection_auth`` parameter in order to work properly. Since the authentication + parameters are sensitive, it's recommended to pass them to the states via pillar. + + Required provider parameters: + + if using username and password: + * ``subscription_id`` + * ``username`` + * ``password`` + + if using a service principal: + * ``subscription_id`` + * ``tenant`` + * ``client_id`` + * ``secret`` + + Optional provider parameters: + + **cloud_environment**: Used to point the cloud driver to different API endpoints, such as Azure GovCloud. Possible values: + * ``AZURE_PUBLIC_CLOUD`` (default) + * ``AZURE_CHINA_CLOUD`` + * ``AZURE_US_GOV_CLOUD`` + * ``AZURE_GERMAN_CLOUD`` + + Example Pillar for Azure Resource Manager authentication: + + .. code-block:: yaml + + azurerm: + user_pass_auth: + subscription_id: 3287abc8-f98a-c678-3bde-326766fd3617 + username: fletch + password: 123pass + mysubscription: + subscription_id: 3287abc8-f98a-c678-3bde-326766fd3617 + tenant: ABCDEFAB-1234-ABCD-1234-ABCDEFABCDEF + client_id: ABCDEFAB-1234-ABCD-1234-ABCDEFABCDEF + secret: XXXXXXXXXXXXXXXXXXXXXXXX + cloud_environment: AZURE_PUBLIC_CLOUD + +''' +# Python libs +from __future__ import absolute_import +import logging + +log = logging.getLogger(__name__) + +TREQ = { + 'present': { + 'require': [ + 'states.azurerm.keyvault.vault.present', + ] + } +} + + +async def present(hub, ctx, name, key_type, vault_url, version=None, key_ops=None, enabled=None, expires_on=None, + not_before=None, tags=None, connection_auth=None, **kwargs): + ''' + .. versionadded:: VERSION + + Ensure the specified key exists within the given key vault. Requires keys/create permission. Key properties can be + specified as keyword arguments. + + :param name: The name of the new key. Key names can only contain alphanumeric characters and dashes. + + :param key_type: The type of key to create. Possible values include: 'ec', 'ec_hsm', 'oct', 'rsa', 'rsa_hsm'. + + :param vault_url: The URL of the vault that the client will access. + + :param version: An optional parameter used to specify the version of the key (if the key already exists). + If no version is specified, the latest version of the key will be updated. + + :param key_ops: A list of permitted key operations. Possible values include: 'decrypt', 'encrypt', 'sign', + 'unwrap_key', 'verify', 'wrap_key'. + + :param enabled: Whether the key is enabled for use. + + :param expires_on: When the key will expire, in UTC. This parameter must be a string representation of a Datetime + object in ISO-8601 format. + + :param not_before: The time before which the key can not be used, in UTC. This parameter must be a string + representation of a Datetime object in ISO-8601 format. + + :param tags: A dictionary of strings can be passed as tag metadata to the key. + + :param connection_auth: A dict with subscription and authentication parameters to be used in connecting to the + Azure Resource Manager API. + + Example usage: + + .. code-block:: yaml + + Ensure key exists: + azurerm.keyvault.key.present: + - name: my_key + - key_type: my_type + - vault_url: my_vault + - tags: + contact_name: Elmer Fudd Gantry + - connection_auth: {{ profile }} + + ''' + ret = { + 'name': name, + 'result': False, + 'comment': '', + 'changes': {} + } + + if not isinstance(connection_auth, dict): + ret['comment'] = 'Connection information must be specified via connection_auth dictionary!' + return ret + + key = await hub.exec.azurerm.keyvault.key.get_key( + name=name, + vault_url=vault_url, + version=version, + azurearm_log_level='info', + **connection_auth + ) + + new_key = True + + if key_type != 'oct': + key_type = key_type.upper().replace('_', '-') + + if 'error' not in key: + new_key = False + + if tags: + tag_changes = await hub.exec.utils.dictdiffer.deep_diff(key.get('properties').get('tags', {}), tags or {}) + if tag_changes: + ret['changes']['tags'] = tag_changes + + if key_ops is not None: + if key_ops.sort() != key.get('key_operations').sort(): + ret['changes']['key_operations'] = { + 'old': key.get('key_operations'), + 'new': key_ops + } + + if enabled is not None: + if enabled != key.get('properties').get('enabled'): + ret['changes']['enabled'] = { + 'old': key.get('properties').get('enabled'), + 'new': enabled + } + + if expires_on: + if expires_on != key.get('properties').get('expires_on'): + ret['changes']['expires_on'] = { + 'old': key.get('properties').get('expires_on'), + 'new': expires_on + } + + if not_before: + if not_before != key.get('properties').get('not_before'): + ret['changes']['not_before'] = { + 'old': key.get('properties').get('not_before'), + 'new': not_before + } + + if not ret['changes']: + ret['result'] = True + ret['comment'] = 'Key {0} is already present.'.format(name) + return ret + + if ctx['test']: + ret['result'] = None + ret['comment'] = 'Key {0} would be updated.'.format(name) + return ret + + else: + ret['changes'] = { + 'old': {}, + 'new': { + 'name': name, + 'key_type': key_type + } + } + + if tags: + ret['changes']['new']['tags'] = tags + if key_ops is not None: + ret['changes']['new']['key_operations'] = key_ops + if enabled is not None: + ret['changes']['new']['enabled'] = enabled + if expires_on: + ret['changes']['new']['expires_on'] = expires_on + if not_before: + ret['changes']['new']['not_before'] = not_before + + if ctx['test']: + ret['comment'] = 'Key {0} would be created.'.format(name) + ret['result'] = None + return ret + + key_kwargs = kwargs.copy() + key_kwargs.update(connection_auth) + + if new_key: + key = await hub.exec.azurerm.keyvault.key.create_key( + name=name, + vault_url=vault_url, + key_type=key_type, + tags=tags, + key_ops=key_ops, + enabled=enabled, + not_before=not_before, + expires_on=expires_on, + **key_kwargs + ) + else: + key = await hub.exec.azurerm.keyvault.key.update_key_properties( + name=name, + vault_url=vault_url, + tags=tags, + key_ops=key_ops, + enabled=enabled, + not_before=not_before, + expires_on=expires_on, + **key_kwargs + ) + + if 'error' not in key: + ret['result'] = True + ret['comment'] = 'Key {0} has been created.'.format(name) + return ret + + ret['comment'] = 'Failed to create Key {0}! ({1})'.format(name, key.get('error')) + if not ret['result']: + ret['changes'] = {} + return ret + + +async def absent(hub, ctx, name, vault_url, connection_auth=None): + ''' + .. versionadded:: 1.0.0 + + Ensure the specified key does not exist within the given key vault. + + :param name: The name of the key to delete. + + :param vault_url: The URL of the vault that the client will access. + + :param connection_auth: A dict with subscription and authentication parameters to be used in connecting to the + Azure Resource Manager API. + + Example usage: + + .. code-block:: yaml + + Ensure key is absent: + azurerm.keyvault.key.absent: + - name: my_key + - vault_url: my_vault + - connection_auth: {{ profile }} + + ''' + ret = { + 'name': name, + 'result': False, + 'comment': '', + 'changes': {} + } + + if not isinstance(connection_auth, dict): + ret['comment'] = 'Connection information must be specified via connection_auth dictionary!' + return ret + + key = await hub.exec.azurerm.keyvault.key.get_key( + name=name, + vault_url=vault_url, + azurearm_log_level='info', + **connection_auth + ) + + if 'error' in key: + ret['result'] = True + ret['comment'] = 'Key {0} was not found.'.format(name) + return ret + + elif ctx['test']: + ret['comment'] = 'Key {0} would be deleted.'.format(name) + ret['result'] = None + ret['changes'] = { + 'old': key, + 'new': {}, + } + return ret + + deleted = await hub.exec.azurerm.keyvault.key.begin_delete_key( + name=name, + vault_url=vault_url, + **connection_auth + ) + + if deleted: + ret['result'] = True + ret['comment'] = 'Key {0} has been deleted.'.format(name) + ret['changes'] = { + 'old': key, + 'new': {} + } + return ret + + ret['comment'] = 'Failed to delete Key {0}!'.format(name) + return ret From 0def1bc54c3f84cd391e4dc420a2fd84de79979a Mon Sep 17 00:00:00 2001 From: Ajnbro <50883410+Ajnbro@users.noreply.github.com> Date: Thu, 26 Mar 2020 00:21:56 -0400 Subject: [PATCH 08/13] Ready for PR --- .../exec/azurerm/keyvault/key.py | 6 +-- .../states/azurerm/keyvault/key.py | 50 ++++++------------- 2 files changed, 16 insertions(+), 40 deletions(-) diff --git a/idem_provider_azurerm/exec/azurerm/keyvault/key.py b/idem_provider_azurerm/exec/azurerm/keyvault/key.py index 6d88f7b2..8e4e496e 100644 --- a/idem_provider_azurerm/exec/azurerm/keyvault/key.py +++ b/idem_provider_azurerm/exec/azurerm/keyvault/key.py @@ -704,7 +704,7 @@ async def restore_key_backup(hub, backup, vault_url, **kwargs): return result -async def update_key_properties(hub, name, vault_url, version=None, key_ops=None, enabled=None, expires_on=None, not_before=None, +async def update_key_properties(hub, name, vault_url, version=None, enabled=None, expires_on=None, not_before=None, tags=None, **kwargs): ''' .. versionadded:: VERSION @@ -719,9 +719,6 @@ async def update_key_properties(hub, name, vault_url, version=None, key_ops=None :param version: An optional parameter used to specify the version of the key to update. If no version is specified, the latest version of the key will be updated. - :param key_ops: A list of permitted key operations. Possible values include: 'decrypt', 'encrypt', 'sign', - 'unwrap_key', 'verify', 'wrap_key'. - :param enabled: Whether the key is enabled for use. :param expires_on: When the key will expire, in UTC. This parameter must be a string representation of a Datetime @@ -746,7 +743,6 @@ async def update_key_properties(hub, name, vault_url, version=None, key_ops=None key = kconn.update_key_properties( name=name, version=version, - key_operations=key_ops, enabled=enabled, expires_on=expires_on, not_before=not_before, diff --git a/idem_provider_azurerm/states/azurerm/keyvault/key.py b/idem_provider_azurerm/states/azurerm/keyvault/key.py index 22b028ea..262778bf 100644 --- a/idem_provider_azurerm/states/azurerm/keyvault/key.py +++ b/idem_provider_azurerm/states/azurerm/keyvault/key.py @@ -79,8 +79,8 @@ } -async def present(hub, ctx, name, key_type, vault_url, version=None, key_ops=None, enabled=None, expires_on=None, - not_before=None, tags=None, connection_auth=None, **kwargs): +async def present(hub, ctx, name, key_type, vault_url, key_ops=None, enabled=None, expires_on=None, not_before=None, + tags=None, connection_auth=None, **kwargs): ''' .. versionadded:: VERSION @@ -93,9 +93,6 @@ async def present(hub, ctx, name, key_type, vault_url, version=None, key_ops=Non :param vault_url: The URL of the vault that the client will access. - :param version: An optional parameter used to specify the version of the key (if the key already exists). - If no version is specified, the latest version of the key will be updated. - :param key_ops: A list of permitted key operations. Possible values include: 'decrypt', 'encrypt', 'sign', 'unwrap_key', 'verify', 'wrap_key'. @@ -140,26 +137,21 @@ async def present(hub, ctx, name, key_type, vault_url, version=None, key_ops=Non key = await hub.exec.azurerm.keyvault.key.get_key( name=name, vault_url=vault_url, - version=version, azurearm_log_level='info', **connection_auth ) - new_key = True - if key_type != 'oct': key_type = key_type.upper().replace('_', '-') if 'error' not in key: - new_key = False - if tags: tag_changes = await hub.exec.utils.dictdiffer.deep_diff(key.get('properties').get('tags', {}), tags or {}) if tag_changes: ret['changes']['tags'] = tag_changes if key_ops is not None: - if key_ops.sort() != key.get('key_operations').sort(): + if sorted(key_ops) != sorted(key.get('key_operations')): ret['changes']['key_operations'] = { 'old': key.get('key_operations'), 'new': key_ops @@ -224,29 +216,17 @@ async def present(hub, ctx, name, key_type, vault_url, version=None, key_ops=Non key_kwargs = kwargs.copy() key_kwargs.update(connection_auth) - if new_key: - key = await hub.exec.azurerm.keyvault.key.create_key( - name=name, - vault_url=vault_url, - key_type=key_type, - tags=tags, - key_ops=key_ops, - enabled=enabled, - not_before=not_before, - expires_on=expires_on, - **key_kwargs - ) - else: - key = await hub.exec.azurerm.keyvault.key.update_key_properties( - name=name, - vault_url=vault_url, - tags=tags, - key_ops=key_ops, - enabled=enabled, - not_before=not_before, - expires_on=expires_on, - **key_kwargs - ) + key = await hub.exec.azurerm.keyvault.key.create_key( + name=name, + vault_url=vault_url, + key_type=key_type, + tags=tags, + key_ops=key_ops, + enabled=enabled, + not_before=not_before, + expires_on=expires_on, + **key_kwargs + ) if 'error' not in key: ret['result'] = True @@ -261,7 +241,7 @@ async def present(hub, ctx, name, key_type, vault_url, version=None, key_ops=Non async def absent(hub, ctx, name, vault_url, connection_auth=None): ''' - .. versionadded:: 1.0.0 + .. versionadded:: VERSION Ensure the specified key does not exist within the given key vault. From 23a5d06a366e6a86530f36c8f4a89ccac28a8427 Mon Sep 17 00:00:00 2001 From: Ajnbro <50883410+Ajnbro@users.noreply.github.com> Date: Thu, 26 Mar 2020 00:31:33 -0400 Subject: [PATCH 09/13] Fixed additional dependency list --- idem_provider_azurerm/exec/azurerm/keyvault/vault.py | 1 + 1 file changed, 1 insertion(+) diff --git a/idem_provider_azurerm/exec/azurerm/keyvault/vault.py b/idem_provider_azurerm/exec/azurerm/keyvault/vault.py index 62025274..088c6ff2 100644 --- a/idem_provider_azurerm/exec/azurerm/keyvault/vault.py +++ b/idem_provider_azurerm/exec/azurerm/keyvault/vault.py @@ -11,6 +11,7 @@ * `azure-common `_ >= 1.1.23 * `azure-mgmt `_ >= 4.0.0 * `azure-mgmt-compute `_ >= 4.6.2 + * `azure-mgmt-keyvault `_ >= 1.1.0 * `azure-mgmt-network `_ >= 4.0.0 * `azure-mgmt-resource `_ >= 2.2.0 * `azure-mgmt-storage `_ >= 2.0.0 From 3ef4eddcd5cd0e34a49862d1da276c08a91757f7 Mon Sep 17 00:00:00 2001 From: Ajnbro <50883410+Ajnbro@users.noreply.github.com> Date: Thu, 26 Mar 2020 14:59:29 -0400 Subject: [PATCH 10/13] Fixes PR comments --- .../states/azurerm/keyvault/key.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/idem_provider_azurerm/states/azurerm/keyvault/key.py b/idem_provider_azurerm/states/azurerm/keyvault/key.py index 262778bf..3321e49f 100644 --- a/idem_provider_azurerm/states/azurerm/keyvault/key.py +++ b/idem_provider_azurerm/states/azurerm/keyvault/key.py @@ -146,35 +146,35 @@ async def present(hub, ctx, name, key_type, vault_url, key_ops=None, enabled=Non if 'error' not in key: if tags: - tag_changes = await hub.exec.utils.dictdiffer.deep_diff(key.get('properties').get('tags', {}), tags or {}) + tag_changes = await hub.exec.utils.dictdiffer.deep_diff(key.get('properties', {}).get('tags', {}), tags or {}) if tag_changes: ret['changes']['tags'] = tag_changes if key_ops is not None: - if sorted(key_ops) != sorted(key.get('key_operations')): + if sorted(key_ops or []) != sorted(key.get('key_operations', [])): ret['changes']['key_operations'] = { 'old': key.get('key_operations'), 'new': key_ops } if enabled is not None: - if enabled != key.get('properties').get('enabled'): + if enabled != key.get('properties', {}).get('enabled'): ret['changes']['enabled'] = { - 'old': key.get('properties').get('enabled'), + 'old': key.get('properties', {}).get('enabled'), 'new': enabled } if expires_on: - if expires_on != key.get('properties').get('expires_on'): + if expires_on != key.get('properties', {}).get('expires_on'): ret['changes']['expires_on'] = { - 'old': key.get('properties').get('expires_on'), + 'old': key.get('properties', {}).get('expires_on'), 'new': expires_on } if not_before: - if not_before != key.get('properties').get('not_before'): + if not_before != key.get('properties', {}).get('not_before'): ret['changes']['not_before'] = { - 'old': key.get('properties').get('not_before'), + 'old': key.get('properties', {}).get('not_before'), 'new': not_before } From 1eab07628b235e0a2e5573bfb9c3effdfd1c0539 Mon Sep 17 00:00:00 2001 From: Ajnbro <50883410+Ajnbro@users.noreply.github.com> Date: Thu, 26 Mar 2020 15:13:06 -0400 Subject: [PATCH 11/13] key_ops fix --- idem_provider_azurerm/states/azurerm/keyvault/key.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/idem_provider_azurerm/states/azurerm/keyvault/key.py b/idem_provider_azurerm/states/azurerm/keyvault/key.py index 3321e49f..217c3809 100644 --- a/idem_provider_azurerm/states/azurerm/keyvault/key.py +++ b/idem_provider_azurerm/states/azurerm/keyvault/key.py @@ -150,8 +150,8 @@ async def present(hub, ctx, name, key_type, vault_url, key_ops=None, enabled=Non if tag_changes: ret['changes']['tags'] = tag_changes - if key_ops is not None: - if sorted(key_ops or []) != sorted(key.get('key_operations', [])): + if instance(key_ops, list): + if sorted(key_ops) != sorted(key.get('key_operations', [])): ret['changes']['key_operations'] = { 'old': key.get('key_operations'), 'new': key_ops From a7b484d1a296e4cb28a7ed0b9666a2e84afa9505 Mon Sep 17 00:00:00 2001 From: Alex Rothman <50883410+Ajnbro@users.noreply.github.com> Date: Thu, 26 Mar 2020 15:18:16 -0400 Subject: [PATCH 12/13] Fixed typo --- idem_provider_azurerm/states/azurerm/keyvault/key.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/idem_provider_azurerm/states/azurerm/keyvault/key.py b/idem_provider_azurerm/states/azurerm/keyvault/key.py index 217c3809..3609be86 100644 --- a/idem_provider_azurerm/states/azurerm/keyvault/key.py +++ b/idem_provider_azurerm/states/azurerm/keyvault/key.py @@ -150,7 +150,7 @@ async def present(hub, ctx, name, key_type, vault_url, key_ops=None, enabled=Non if tag_changes: ret['changes']['tags'] = tag_changes - if instance(key_ops, list): + if isinstance(key_ops, list): if sorted(key_ops) != sorted(key.get('key_operations', [])): ret['changes']['key_operations'] = { 'old': key.get('key_operations'), From 47351e558f193663964e1cb20246357093592891 Mon Sep 17 00:00:00 2001 From: nicholasmhughes Date: Thu, 26 Mar 2020 15:23:28 -0400 Subject: [PATCH 13/13] handle case where tags key is present but contains None --- idem_provider_azurerm/states/azurerm/keyvault/key.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/idem_provider_azurerm/states/azurerm/keyvault/key.py b/idem_provider_azurerm/states/azurerm/keyvault/key.py index 3609be86..62f59b92 100644 --- a/idem_provider_azurerm/states/azurerm/keyvault/key.py +++ b/idem_provider_azurerm/states/azurerm/keyvault/key.py @@ -146,7 +146,10 @@ async def present(hub, ctx, name, key_type, vault_url, key_ops=None, enabled=Non if 'error' not in key: if tags: - tag_changes = await hub.exec.utils.dictdiffer.deep_diff(key.get('properties', {}).get('tags', {}), tags or {}) + tag_changes = await hub.exec.utils.dictdiffer.deep_diff( + key.get('properties', {}).get('tags', {}) or {}, + tags or {} + ) if tag_changes: ret['changes']['tags'] = tag_changes