1#!/usr/bin/python
2#
3# Copyright (c) 2019 Yuwei Zhou, <yuwzho@microsoft.com>
4#
5# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
6
7from __future__ import absolute_import, division, print_function
8__metaclass__ = type
9
10
11ANSIBLE_METADATA = {'metadata_version': '1.1',
12                    'status': ['preview'],
13                    'supported_by': 'community'}
14
15
16DOCUMENTATION = '''
17---
18module: azure_rm_lock_info
19version_added: "2.9"
20short_description: Manage Azure locks
21description:
22    - Create, delete an Azure lock.
23options:
24    name:
25        description:
26            - Name of the lock.
27        type: str
28        required: true
29    managed_resource_id:
30        description:
31            - ID of the resource where need to manage the lock.
32            - Get this via facts module.
33            - Cannot be set mutual with I(resource_group).
34            - Manage subscription if both I(managed_resource_id) and I(resource_group) not defined.
35            - "'/subscriptions/{subscriptionId}' for subscriptions."
36            - "'/subscriptions/{subscriptionId}/resourcegroups/{resourceGroupName}' for resource groups."
37            - "'/subscriptions/{subscriptionId}/resourcegroups/{resourceGroupName}/providers/{namespace}/{resourceType}/{resourceName}' for resources."
38            - Can get all locks with 'child scope' for this resource, use I(managed_resource_id) in response for further management.
39        type: str
40    resource_group:
41        description:
42            - Resource group name where need to manage the lock.
43            - The lock is in the resource group level.
44            - Cannot be set mutual with I(managed_resource_id).
45            - Query subscription if both I(managed_resource_id) and I(resource_group) not defined.
46            - Can get all locks with 'child scope' in this resource group, use the I(managed_resource_id) in response for further management.
47        type: str
48
49extends_documentation_fragment:
50    - azure
51
52author:
53    - Yuwei Zhou (@yuwzho)
54
55'''
56
57EXAMPLES = '''
58- name: Get myLock details of myVM
59  azure_rm_lock_info:
60    name: myLock
61    managed_resource_id: /subscriptions/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/resourcegroups/myResourceGroup/providers/Microsoft.Compute/virtualMachines/myVM
62
63- name: List locks of myVM
64  azure_rm_lock_info:
65    managed_resource_id: /subscriptions/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/resourcegroups/myResourceGroup/providers/Microsoft.Compute/virtualMachines/myVM
66
67- name: List locks of myResourceGroup
68  azure_rm_lock_info:
69    resource_group: myResourceGroup
70
71- name: List locks of myResourceGroup
72  azure_rm_lock_info:
73    managed_resource_id: /subscriptions/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/resourcegroups/myResourceGroup
74
75- name: List locks of mySubscription
76  azure_rm_lock_info:
77
78- name: List locks of mySubscription
79  azure_rm_lock_info:
80    managed_resource_id: /subscriptions/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
81'''
82
83RETURN = '''
84locks:
85    description:
86        - List of locks dicts.
87    returned: always
88    type: complex
89    contains:
90        id:
91            description:
92                - ID of the Lock.
93            returned: always
94            type: str
95            sample: "/subscriptions/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/resourceGroups/myResourceGroup/providers/Microsoft.Authorization/locks/myLock"
96        name:
97            description:
98                - Name of the lock.
99            returned: always
100            type: str
101            sample: myLock
102        level:
103            description:
104                - Type level of the lock.
105            returned: always
106            type: str
107            sample: can_not_delete
108        notes:
109            description:
110                - Notes of the lock added by creator.
111            returned: always
112            type: str
113            sample: "This is a lock"
114'''  # NOQA
115
116import json
117import re
118from ansible.module_utils.common.dict_transformations import _camel_to_snake
119from ansible.module_utils.azure_rm_common import AzureRMModuleBase
120from ansible.module_utils.azure_rm_common_rest import GenericRestClient
121
122try:
123    from msrestazure.azure_exceptions import CloudError
124except ImportError:
125    # This is handled in azure_rm_common
126    pass
127
128
129class AzureRMLockInfo(AzureRMModuleBase):
130
131    def __init__(self):
132
133        self.module_arg_spec = dict(
134            name=dict(type='str'),
135            resource_group=dict(type='str'),
136            managed_resource_id=dict(type='str')
137        )
138
139        self.results = dict(
140            changed=False,
141            locks=[]
142        )
143
144        mutually_exclusive = [['resource_group', 'managed_resource_id']]
145
146        self.name = None
147        self.resource_group = None
148        self.managed_resource_id = None
149        self._mgmt_client = None
150        self._query_parameters = {'api-version': '2016-09-01'}
151        self._header_parameters = {'Content-Type': 'application/json; charset=utf-8'}
152
153        super(AzureRMLockInfo, self).__init__(self.module_arg_spec, facts_module=True, mutually_exclusive=mutually_exclusive, supports_tags=False)
154
155    def exec_module(self, **kwargs):
156
157        is_old_facts = self.module._name == 'azure_rm_lock_facts'
158        if is_old_facts:
159            self.module.deprecate("The 'azure_rm_lock_facts' module has been renamed to 'azure_rm_lock_info'", version='2.13')
160
161        for key in self.module_arg_spec.keys():
162            setattr(self, key, kwargs[key])
163
164        self._mgmt_client = self.get_mgmt_svc_client(GenericRestClient, base_url=self._cloud_environment.endpoints.resource_manager)
165        changed = False
166        # construct scope id
167        scope = self.get_scope()
168        url = '/{0}/providers/Microsoft.Authorization/locks'.format(scope)
169        if self.name:
170            url = '{0}/{1}'.format(url, self.name)
171        locks = self.list_locks(url)
172        resp = locks.get('value') if 'value' in locks else [locks]
173        self.results['locks'] = [self.to_dict(x) for x in resp]
174        return self.results
175
176    def to_dict(self, lock):
177        resp = dict(
178            id=lock['id'],
179            name=lock['name'],
180            level=_camel_to_snake(lock['properties']['level']),
181            managed_resource_id=re.sub('/providers/Microsoft.Authorization/locks/.+', '', lock['id'])
182        )
183        if lock['properties'].get('notes'):
184            resp['notes'] = lock['properties']['notes']
185        if lock['properties'].get('owners'):
186            resp['owners'] = [x['application_id'] for x in lock['properties']['owners']]
187        return resp
188
189    def list_locks(self, url):
190        try:
191            resp = self._mgmt_client.query(url=url,
192                                           method='GET',
193                                           query_parameters=self._query_parameters,
194                                           header_parameters=self._header_parameters,
195                                           body=None,
196                                           expected_status_codes=[200],
197                                           polling_timeout=None,
198                                           polling_interval=None)
199            return json.loads(resp.text)
200        except CloudError as exc:
201            self.fail('Error when finding locks {0}: {1}'.format(url, exc.message))
202
203    def get_scope(self):
204        '''
205        Get the resource scope of the lock management.
206        '/subscriptions/{subscriptionId}' for subscriptions,
207        '/subscriptions/{subscriptionId}/resourcegroups/{resourceGroupName}' for resource groups,
208        '/subscriptions/{subscriptionId}/resourcegroups/{resourceGroupName}/providers/{namespace}/{resourceType}/{resourceName}' for resources.
209        '''
210        if self.managed_resource_id:
211            return self.managed_resource_id
212        elif self.resource_group:
213            return '/subscriptions/{0}/resourcegroups/{1}'.format(self.subscription_id, self.resource_group)
214        else:
215            return '/subscriptions/{0}'.format(self.subscription_id)
216
217
218def main():
219    AzureRMLockInfo()
220
221
222if __name__ == '__main__':
223    main()
224