From e62c1cd0502350e79f13c57a5b8e0d3e192bdbb5 Mon Sep 17 00:00:00 2001 From: Ian Philpot Date: Tue, 6 Feb 2018 23:40:15 -0500 Subject: [PATCH] Az mod keyvault ops (#33606) * keyvault_operations module init + template * Created KeyVault Secrets Module. No Tests * Passing 2.7/3.x sanity tests * added intergration tests for keyvault secrets noop * integration test pass/need keyvault module * Added az env cleanup to intergration tests * Updated auth to 0.3.6 ver * fixed typo in integration tests * Added check_mode * Added tags * Updated tags test, sanity + integration passed * checked mode * updated tests * fixing sanity * fix copyright --- .../cloud/azure/azure_rm_keyvaultsecret.py | 199 ++++++++++++++++++ .../targets/azure_rm_keyvaultsecret/aliases | 3 + .../azure_rm_keyvaultsecret/meta/main.yml | 2 + .../azure_rm_keyvaultsecret/tasks/main.yml | 64 ++++++ 4 files changed, 268 insertions(+) create mode 100644 lib/ansible/modules/cloud/azure/azure_rm_keyvaultsecret.py create mode 100644 test/integration/targets/azure_rm_keyvaultsecret/aliases create mode 100644 test/integration/targets/azure_rm_keyvaultsecret/meta/main.yml create mode 100644 test/integration/targets/azure_rm_keyvaultsecret/tasks/main.yml diff --git a/lib/ansible/modules/cloud/azure/azure_rm_keyvaultsecret.py b/lib/ansible/modules/cloud/azure/azure_rm_keyvaultsecret.py new file mode 100644 index 00000000000..dfb55d4056b --- /dev/null +++ b/lib/ansible/modules/cloud/azure/azure_rm_keyvaultsecret.py @@ -0,0 +1,199 @@ +#!/usr/bin/python +# Copyright: Ansible Project +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + + +ANSIBLE_METADATA = {'metadata_version': '1.1', + 'status': ['preview'], + 'supported_by': 'community'} + + +DOCUMENTATION = ''' +--- +module: azure_rm_keyvaultsecret +version_added: 2.5 +short_description: Use Azure KeyVault Secrets. +description: + - Create or delete a secret within a given keyvault. By using Key Vault, you can encrypt + keys and secrets (such as authentication keys, storage account keys, data encryption keys, .PFX files, and passwords). +options: + keyvault_uri: + description: + - URI of the keyvault endpoint. + required: true + secret_name: + description: + - Name of the keyvault secret. + required: true + secret_value: + description: + - Secret to be secured by keyvault. + required: false + aliases: + - secret + state: + description: + - Assert the state of the subnet. Use 'present' to create or update a secret and + 'absent' to delete a secret . + required: false + default: present + choices: + - absent + - present + +extends_documentation_fragment: + - azure + - azure_tags + +author: + - "Ian Philpot (@tripdubroot)" + +''' + +EXAMPLES = ''' + - name: Create a secret + azure_rm_keyvaultsecret: + secret_name: MySecret + secret_value: My_Pass_Sec + keyvault_uri: https://contoso.vault.azure.net/ + tags: + testing: testing + delete: never + + - name: Delete a secret + azure_rm_keyvaultsecret: + secret_name: MySecret + keyvault_uri: https://contoso.vault.azure.net/ + state: absent +''' + +RETURN = ''' +state: + description: Current state of the secret. + returned: success + type: complex + contains: + secret_id: + description: Secret resource path. + type: str + example: https://contoso.vault.azure.net/secrets/hello/e924f053839f4431b35bc54393f98423 +''' + +from ansible.module_utils.azure_rm_common import AzureRMModuleBase + +try: + from azure.keyvault import KeyVaultClient, KeyVaultAuthentication, KeyVaultId + from azure.common.credentials import ServicePrincipalCredentials + from azure.keyvault.models.key_vault_error import KeyVaultErrorException +except ImportError: + # This is handled in azure_rm_common + pass + + +class AzureRMKeyVaultSecret(AzureRMModuleBase): + ''' Module that creates or deletes secrets in Azure KeyVault ''' + + def __init__(self): + + self.module_arg_spec = dict( + secret_name=dict(type='str', required=True), + secret_value=dict(type='str', aliases=['secret'], no_log=True), + keyvault_uri=dict(type='str', required=True), + state=dict(type='str', default='present', choices=['present', 'absent']) + ) + + required_if = [ + ('state', 'present', ['secret_value']) + ] + + self.results = dict( + changed=False, + state=dict() + ) + + self.secret_name = None + self.secret_value = None + self.keyvault_uri = None + self.state = None + self.data_creds = None + self.client = None + self.tags = None + + super(AzureRMKeyVaultSecret, self).__init__(self.module_arg_spec, + supports_check_mode=True, + required_if=required_if, + supports_tags=True) + + def exec_module(self, **kwargs): + + for key in list(self.module_arg_spec.keys()) + ['tags']: + setattr(self, key, kwargs[key]) + + # Create KeyVault Client using KeyVault auth class and auth_callback + self.client = KeyVaultClient(self.azure_credentials) + + results = dict() + changed = False + + try: + results['secret_id'] = self.get_secret(self.secret_name) + + # Secret exists and will be deleted + if self.state == 'absent': + changed = True + + except KeyVaultErrorException: + # Secret doesn't exist + if self.state == 'present': + changed = True + + self.results['changed'] = changed + self.results['state'] = results + + if not self.check_mode: + # Create secret + if self.state == 'present' and changed: + results['secret_id'] = self.create_secret(self.secret_name, self.secret_value, self.tags) + self.results['state'] = results + self.results['state']['status'] = 'Created' + # Delete secret + elif self.state == 'absent' and changed: + results['secret_id'] = self.delete_secret(self.secret_name) + self.results['state'] = results + self.results['state']['status'] = 'Deleted' + else: + if self.state == 'present' and changed: + self.results['state']['status'] = 'Created' + elif self.state == 'absent' and changed: + self.results['state']['status'] = 'Deleted' + + return self.results + + def get_secret(self, name, version=''): + ''' Gets an existing secret ''' + secret_bundle = self.client.get_secret(self.keyvault_uri, name, version) + if secret_bundle: + secret_id = KeyVaultId.parse_secret_id(secret_bundle.id) + return secret_id.id + + def create_secret(self, name, secret, tags): + ''' Creates a secret ''' + secret_bundle = self.client.set_secret(self.keyvault_uri, name, secret, tags) + secret_id = KeyVaultId.parse_secret_id(secret_bundle.id) + return secret_id.id + + def delete_secret(self, name): + ''' Deletes a secret ''' + deleted_secret = self.client.delete_secret(self.keyvault_uri, name) + secret_id = KeyVaultId.parse_secret_id(deleted_secret.id) + return secret_id.id + + +def main(): + AzureRMKeyVaultSecret() + +if __name__ == '__main__': + main() diff --git a/test/integration/targets/azure_rm_keyvaultsecret/aliases b/test/integration/targets/azure_rm_keyvaultsecret/aliases new file mode 100644 index 00000000000..d6ff84111cd --- /dev/null +++ b/test/integration/targets/azure_rm_keyvaultsecret/aliases @@ -0,0 +1,3 @@ +cloud/azure +posix/ci/cloud/azure +destructive diff --git a/test/integration/targets/azure_rm_keyvaultsecret/meta/main.yml b/test/integration/targets/azure_rm_keyvaultsecret/meta/main.yml new file mode 100644 index 00000000000..95e1952f989 --- /dev/null +++ b/test/integration/targets/azure_rm_keyvaultsecret/meta/main.yml @@ -0,0 +1,2 @@ +dependencies: + - setup_azure diff --git a/test/integration/targets/azure_rm_keyvaultsecret/tasks/main.yml b/test/integration/targets/azure_rm_keyvaultsecret/tasks/main.yml new file mode 100644 index 00000000000..dd7facc371d --- /dev/null +++ b/test/integration/targets/azure_rm_keyvaultsecret/tasks/main.yml @@ -0,0 +1,64 @@ +- name: Prepare random number + set_fact: + rpfx: "{{ resource_group | hash('md5') | truncate(7, True, '') }}{{ 1000 | random }}" + run_once: yes + +- name: Create instance of Key Vault + azure_rm_keyvault: + resource_group: "{{ resource_group }}" + vault_name: "vault{{ rpfx }}" + enabled_for_deployment: yes + vault_tenant: "{{ azure_tenant }}" + sku: + name: standard + family: A + access_policies: + - tenant_id: "{{ azure_tenant }}" + object_id: 97567bfa-cf13-4217-8fa3-cc56bc1867fe + keys: + - get + - list + - update + - create + - import + - delete + - recover + - backup + - restore + secrets: + - get + - list + - set + - delete + - recover + - backup + - restore + register: output + +- name: create a kevyault secret + block: + - azure_rm_keyvaultsecret: + keyvault_uri: https://vault{{ rpfx }}.vault.azure.net + secret_name: testsecret + secret_value: 'mysecret' + tags: + testing: test + delete: on-exit + register: output + - assert: + that: output.changed + rescue: + - azure_rm_keyvaultsecret: + keyvault_uri: https://vault{{ rpfx }}.vault.azure.net + state: absent + secret_name: testsecret + +- name: delete a kevyault secret + azure_rm_keyvaultsecret: + keyvault_uri: https://vault{{ rpfx }}.vault.azure.net + state: absent + secret_name: testsecret + register: output + +- assert: + that: output.changed \ No newline at end of file