diff --git a/lib/ansible/module_utils/azure_rm_common.py b/lib/ansible/module_utils/azure_rm_common.py index 64db8b8350c..20e19629dec 100644 --- a/lib/ansible/module_utils/azure_rm_common.py +++ b/lib/ansible/module_utils/azure_rm_common.py @@ -170,6 +170,8 @@ try: from azure.mgmt.containerinstance import ContainerInstanceManagementClient from azure.mgmt.loganalytics import LogAnalyticsManagementClient import azure.mgmt.loganalytics.models as LogAnalyticsModels + from azure.mgmt.automation import AutomationClient + import azure.mgmt.automation.models as AutomationModel except ImportError as exc: HAS_AZURE_EXC = traceback.format_exc() HAS_AZURE = False @@ -308,6 +310,7 @@ class AzureRMModuleBase(object): self._resource = None self._log_analytics_client = None self._servicebus_client = None + self._automation_client = None self.check_mode = self.module.check_mode self.api_profile = self.module.params.get('api_profile') @@ -1005,6 +1008,18 @@ class AzureRMModuleBase(object): def servicebus_models(self): return ServicebusModel + @property + def automation_client(self): + self.log('Getting automation client') + if not self._automation_client: + self._automation_client = self.get_mgmt_svc_client(AutomationClient, + base_url=self._cloud_environment.endpoints.resource_manager) + return self._automation_client + + @property + def automation_models(self): + return AutomationModel + class AzureRMAuthException(Exception): pass diff --git a/lib/ansible/modules/cloud/azure/azure_rm_automationaccount.py b/lib/ansible/modules/cloud/azure/azure_rm_automationaccount.py new file mode 100644 index 00000000000..42628ea60ee --- /dev/null +++ b/lib/ansible/modules/cloud/azure/azure_rm_automationaccount.py @@ -0,0 +1,174 @@ +#!/usr/bin/python +# +# Copyright (c) 2017 Yuwei Zhou, +# +# 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_automationaccount +version_added: "2.9" +short_description: Manage Azure Automation account +description: + - Create, delete an Azure Automation account. +options: + resource_group: + description: + - Name of resource group. + type: str + required: true + name: + description: + - Name of the automation account. + type: str + required: true + state: + description: + - State of the automation account. Use C(present) to create or update a automation account and C(absent) to delete an automation account. + type: str + default: present + choices: + - absent + - present + location: + description: + - Location of the resource. + - If not specified, use resource group location. + type: str + +extends_documentation_fragment: + - azure + - azure_tags + +author: + - Yuwei Zhou (@yuwzho) + +''' + +EXAMPLES = ''' +- name: Create an automation account + azure_rm_automationaccount: + name: Testing + resource_group: myResourceGroup + +- name: Create an automation account + azure_rm_automationaccount: + name: Testing + resource_group: myResourceGroup + location: eastus +''' + +RETURN = ''' +id: + description: + - Automation account resource ID. + type: str + returned: success + sample: "/subscriptions/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/resourceGroups/myResourceGroup/providers/Microsoft.Automation/automationAccounts/Testing" +''' # NOQA + +from ansible.module_utils.azure_rm_common import AzureRMModuleBase + + +class AzureRMAutomationAccount(AzureRMModuleBase): + + def __init__(self): + + self.module_arg_spec = dict( + resource_group=dict(type='str', required=True), + name=dict(type='str', required=True), + state=dict(type='str', default='present', choices=['present', 'absent']), + location=dict(type='str') + ) + + self.results = dict( + changed=False, + id=None + ) + + self.resource_group = None + self.name = None + self.state = None + self.location = None + + super(AzureRMAutomationAccount, self).__init__(self.module_arg_spec, supports_check_mode=True) + + def exec_module(self, **kwargs): + + for key in list(self.module_arg_spec.keys()) + ['tags']: + setattr(self, key, kwargs[key]) + + account = self.get_account() + changed = False + if self.state == 'present': + if not account: + if not self.location: + resource_group = self.get_resource_group(self.resource_group) + self.location = resource_group.location + param = self.automation_models.AutomationAccountCreateOrUpdateParameters( + location=self.location, + sku=self.automation_models.Sku(name='Basic'), + tags=self.tags + ) + changed = True + if not self.check_mode: + account = self.create_or_update(param) + elif self.tags: + update_tags, tags = self.update_tags(account.tags) + if update_tags: + changed = True + param = self.automation_models.AutomationAccountUpdateParameters( + tags=tags + ) + changed = True + if not self.check_mode: + self.update_account_tags(param) + if account: + self.results['id'] = account.id + elif account: + changed = True + if not self.check_mode: + self.delete_account() + self.results['changed'] = changed + return self.results + + def get_account(self): + try: + return self.automation_client.automation_account.get(self.resource_group, self.name) + except self.automation_models.ErrorResponseException: + pass + + def create_or_update(self, param): + try: + return self.automation_client.automation_account.create_or_update(self.resource_group, self.name, param) + except self.automation_models.ErrorResponseException as exc: + self.fail('Error when creating automation account {0}: {1}'.format(self.name, exc.message)) + + def update_account_tags(self, param): + try: + return self.automation_client.automation_account.update(self.resource_group, self.name, param) + except self.automation_models.ErrorResponseException as exc: + self.fail('Error when updating automation account {0}: {1}'.format(self.name, exc.message)) + + def delete_account(self): + try: + return self.automation_client.automation_account.delete(self.resource_group, self.name) + except self.automation_models.ErrorResponseException as exc: + self.fail('Error when deleting automation account {0}: {1}'.format(self.name, exc.message)) + + +def main(): + AzureRMAutomationAccount() + + +if __name__ == '__main__': + main() diff --git a/lib/ansible/modules/cloud/azure/azure_rm_automationaccount_facts.py b/lib/ansible/modules/cloud/azure/azure_rm_automationaccount_facts.py new file mode 100644 index 00000000000..1520f4b95dd --- /dev/null +++ b/lib/ansible/modules/cloud/azure/azure_rm_automationaccount_facts.py @@ -0,0 +1,377 @@ +#!/usr/bin/python +# +# Copyright (c) 2017 Yuwei Zhou, +# +# 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_automationaccount_facts +version_added: '2.9' +short_description: Get Azure automation account facts +description: + - Get facts of automation account. + +options: + resource_group: + description: + - The name of the resource group. + type: str + required: True + name: + description: + - The name of the automation account. + type: str + tags: + description: + - Limit results by providing a list of tags. Format tags as 'key' or 'key:value'. + type: list + list_statistics: + description: + - List statistics details for a automation account. + - Note this will cost network overhead, suggest only used when I(name) set. + type: bool + list_usages: + description: + - List usage details for a automation account. + - Note this will cost network overhead, suggest only used when I(name) set. + type: bool + list_keys: + description: + - List keys for a automation account. + - Note this will cost network overhead, suggest only used when I(name) set. + type: bool + +extends_documentation_fragment: + - azure + +author: + - Yuwei Zhou (@yuwzho) + +''' + +EXAMPLES = ''' +- name: Get details of an automation account + azure_rm_automationaccount_facts: + name: Testing + resource_group: myResourceGroup + list_statistics: yes + list_usages: yes + list_keys: yes + +- name: List automation account in a resource group + azure_rm_automationaccount_facts: + resource_group: myResourceGroup + +- name: List automation account in a resource group + azure_rm_automationaccount_facts: +''' + +RETURN = ''' +automation_accounts: + description: + - List of automation account dicts. + returned: always + type: complex + contains: + id: + description: + - Resource ID. + type: str + returned: always + sample: "/subscriptions/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/resourceGroups + /myResourceGroup/providers/Microsoft.Automation/automationAccounts/Testing" + resource_group: + description: + - Resource group name. + type: str + returned: always + sample: myResourceGroup + name: + description: + - Resource name. + type: str + returned: always + sample: Testing + location: + description: + - Resource location. + type: str + returned: always + sample: eastus + creation_time: + description: + - Resource creation date time. + type: str + returned: always + sample: "2019-04-26T02:55:16.500Z" + last_modified_time: + description: + - Resource last modified date time. + type: str + returned: always + sample: "2019-04-26T02:55:16.500Z" + state: + description: + - Resource state. + type: str + returned: always + sample: ok + keys: + description: + - Resource keys. + type: complex + returned: always + contains: + key_name: + description: + - Name of the key. + type: str + returned: always + sample: Primary + permissions: + description: + - Permission of the key. + type: str + returned: always + sample: Full + value: + description: + - Value of the key. + type: str + returned: always + sample: "MbepKTO6IyGwml0GaKBkKN" + statistics: + description: + - Resource statistics. + type: complex + returned: always + contains: + counter_property: + description: + - Property value of the statistic. + type: str + returned: always + sample: New + counter_value: + description: + - Value of the statistic. + type: int + returned: always + sample: 0 + end_time: + description: + - EndTime of the statistic. + type: str + returned: always + sample: "2019-04-26T06:29:43.587518Z" + id: + description: + - ID of the statistic. + type: str + returned: always + sample: "/subscriptions/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/resourceGroups + /myResourceGroup/providers/Microsoft.Automation/automationAccounts/Testing/statistics/New" + start_time: + description: + - StartTime of the statistic. + type: str + returned: always + sample: "2019-04-26T06:29:43.587518Z" + usages: + description: + - Resource usages. + type: complex + returned: always + contains: + current_value: + description: + - Current usage. + type: float + returned: always + sample: 0.0 + limit: + description: + - Max limit, C(-1) for unlimited. + type: long + returned: always + sample: -1 + name: + description: + - Usage counter name. + type: complex + returned: always + contains: + localized_value: + description: + - Localized name. + type: str + returned: always + sample: "SubscriptionUsage" + value: + description: + - Name value. + type: str + returned: always + sample: "SubscriptionUsage" + unit: + description: + - Usage unit name. + type: str + returned: always + sample: "Minute" + throttle_status: + description: + - Usage throttle status. + type: str + returned: always + sample: "NotThrottled" + +''' + +from ansible.module_utils.azure_rm_common import AzureRMModuleBase + +try: + from msrestazure.tools import parse_resource_id +except ImportError: + pass + + +class AzureRMAutomationAccountFacts(AzureRMModuleBase): + def __init__(self): + # define user inputs into argument + self.module_arg_spec = dict( + resource_group=dict( + type='str', + required=True + ), + name=dict( + type='str' + ), + tags=dict( + type='list' + ), + list_statistics=dict( + type='bool' + ), + list_usages=dict( + type='bool' + ), + list_keys=dict( + type='bool' + ) + ) + # store the results of the module operation + self.results = dict() + self.resource_group = None + self.name = None + self.tags = None + self.list_statistics = None + self.list_usages = None + self.list_keys = None + super(AzureRMAutomationAccountFacts, self).__init__(self.module_arg_spec, supports_tags=False, facts_module=True) + + def exec_module(self, **kwargs): + for key in list(self.module_arg_spec): + setattr(self, key, kwargs[key]) + + if self.resource_group and self.name: + accounts = [self.get()] + elif self.resource_group: + accounts = self.list_by_resource_group() + else: + accounts = self.list_all() + self.results['automation_accounts'] = [self.to_dict(x) for x in accounts if self.has_tags(x.tags, self.tags)] + return self.results + + def to_dict(self, account): + if not account: + return None + id_dict = parse_resource_id(account.id) + result = account.as_dict() + result['resource_group'] = id_dict['resource_group'] + if self.list_statistics: + result['statistics'] = self.get_statics(id_dict['resource_group'], account.name) + if self.list_usages: + result['usages'] = self.get_usages(id_dict['resource_group'], account.name) + if self.list_keys: + result['keys'] = self.list_account_keys(id_dict['resource_group'], account.name) + return result + + def get(self): + try: + return self.automation_client.automation_account.get(self.resource_group, self.name) + except self.automation_models.ErrorResponseException as exc: + self.fail('Error when getting automation account {0}: {1}'.format(self.name, exc.message)) + + def list_by_resource_group(self): + result = [] + try: + resp = self.automation_client.automation_account.list_by_resource_group(self.resource_group) + while True: + result.append(resp.next()) + except StopIteration: + pass + except self.automation_models.ErrorResponseException as exc: + self.fail('Error when listing automation account in resource group {0}: {1}'.format(self.resource_group, exc.message)) + return result + + def list_all(self): + result = [] + try: + resp = self.automation_client.automation_account.list() + while True: + result.append(resp.next()) + except StopIteration: + pass + except self.automation_models.ErrorResponseException as exc: + self.fail('Error when listing automation account: {0}'.format(exc.message)) + return result + + def get_statics(self, resource_group, name): + result = [] + try: + resp = self.automation_client.statistics.list_by_automation_account(resource_group, name) + while True: + result.append(resp.next().as_dict()) + except StopIteration: + pass + except self.automation_models.ErrorResponseException as exc: + self.fail('Error when getting statics for automation account {0}/{1}: {2}'.format(resource_group, name, exc.message)) + return result + + def get_usages(self, resource_group, name): + result = [] + try: + resp = self.automation_client.usages.list_by_automation_account(resource_group, name) + while True: + result.append(resp.next().as_dict()) + except StopIteration: + pass + except self.automation_models.ErrorResponseException as exc: + self.fail('Error when getting usage for automation account {0}/{1}: {2}'.format(resource_group, name, exc.message)) + return result + + def list_account_keys(self, resource_group, name): + try: + resp = self.automation_client.keys.list_by_automation_account(resource_group, name) + return [x.as_dict() for x in resp.keys] + except self.automation_models.ErrorResponseException as exc: + self.fail('Error when listing keys for automation account {0}/{1}: {2}'.format(resource_group, name, exc.message)) + + +def main(): + AzureRMAutomationAccountFacts() + + +if __name__ == '__main__': + main() diff --git a/packaging/requirements/requirements-azure.txt b/packaging/requirements/requirements-azure.txt index 8be65d203dd..3b7fa69b937 100644 --- a/packaging/requirements/requirements-azure.txt +++ b/packaging/requirements/requirements-azure.txt @@ -35,3 +35,4 @@ azure-mgmt-cosmosdb==0.5.2 azure-mgmt-hdinsight==0.1.0 azure-mgmt-devtestlabs==3.0.0 azure-mgmt-loganalytics==0.2.0 +azure-mgmt-automation==0.1.1 diff --git a/test/integration/targets/azure_rm_automationaccount/aliases b/test/integration/targets/azure_rm_automationaccount/aliases new file mode 100644 index 00000000000..04a2c98ae73 --- /dev/null +++ b/test/integration/targets/azure_rm_automationaccount/aliases @@ -0,0 +1,4 @@ +cloud/azure +shippable/azure/group4 +destructive +azure_rm_automationaccount_facts diff --git a/test/integration/targets/azure_rm_automationaccount/meta/main.yml b/test/integration/targets/azure_rm_automationaccount/meta/main.yml new file mode 100644 index 00000000000..95e1952f989 --- /dev/null +++ b/test/integration/targets/azure_rm_automationaccount/meta/main.yml @@ -0,0 +1,2 @@ +dependencies: + - setup_azure diff --git a/test/integration/targets/azure_rm_automationaccount/tasks/main.yml b/test/integration/targets/azure_rm_automationaccount/tasks/main.yml new file mode 100644 index 00000000000..882693999a8 --- /dev/null +++ b/test/integration/targets/azure_rm_automationaccount/tasks/main.yml @@ -0,0 +1,88 @@ +- name: Prepare random number + set_fact: + rpfx: "{{ resource_group | hash('md5') | truncate(7, True, '') }}{{ 1000 | random }}" + name: "account{{ resource_group | hash('md5') | truncate(7, True, '') }}{{ 1000 | random }}" + run_once: yes + +- name: Create automation account + azure_rm_automationaccount: + name: "{{ name }}" + resource_group: "{{ resource_group }}" + check_mode: yes + register: output + +- assert: + that: + - output.changed + +- name: Create automation account + azure_rm_automationaccount: + name: "{{ name }}" + resource_group: "{{ resource_group }}" + register: output + +- assert: + that: + - output.changed + - output.id + +- name: Create automation account + azure_rm_automationaccount: + name: "{{ name }}" + resource_group: "{{ resource_group }}" + register: output + +- assert: + that: + - not output.changed + +- name: Get automation account + azure_rm_automationaccount_facts: + name: "{{ name }}" + resource_group: "{{ resource_group }}" + list_statistics: yes + list_usages: yes + list_keys: yes + register: facts + +- assert: + that: + - facts.automation_accounts | length == 1 + - facts.automation_accounts[0].keys + - facts.automation_accounts[0].usages + - facts.automation_accounts[0].statistics + - facts.automation_accounts[0].state == "Ok" + +- name: Delete account + azure_rm_automationaccount: + name: "{{ name }}" + resource_group: "{{ resource_group }}" + state: absent + check_mode: yes + register: output + +- assert: + that: + - output.changed + +- name: Delete account + azure_rm_automationaccount: + name: "{{ name }}" + resource_group: "{{ resource_group }}" + state: absent + register: output + +- assert: + that: + - output.changed + +- name: Delete account + azure_rm_automationaccount: + name: "{{ name }}" + resource_group: "{{ resource_group }}" + state: absent + register: output + +- assert: + that: + - not output.changed diff --git a/test/lib/ansible_test/_data/requirements/integration.cloud.azure.txt b/test/lib/ansible_test/_data/requirements/integration.cloud.azure.txt index 8be65d203dd..3b7fa69b937 100644 --- a/test/lib/ansible_test/_data/requirements/integration.cloud.azure.txt +++ b/test/lib/ansible_test/_data/requirements/integration.cloud.azure.txt @@ -35,3 +35,4 @@ azure-mgmt-cosmosdb==0.5.2 azure-mgmt-hdinsight==0.1.0 azure-mgmt-devtestlabs==3.0.0 azure-mgmt-loganalytics==0.2.0 +azure-mgmt-automation==0.1.1