1#!/usr/bin/python 2# 3# Copyright (c) 2017 Zim Kalinowski, <zikalino@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_sqlserver 19version_added: "2.5" 20short_description: Manage SQL Server instance 21description: 22 - Create, update and delete instance of SQL Server. 23 24options: 25 resource_group: 26 description: 27 - The name of the resource group that contains the resource. You can obtain this value from the Azure Resource Manager API or the portal. 28 required: True 29 name: 30 description: 31 - The name of the server. 32 required: True 33 location: 34 description: 35 - Resource location. 36 admin_username: 37 description: 38 - Administrator username for the server. Once created it cannot be changed. 39 admin_password: 40 description: 41 - The administrator login password (required for server creation). 42 version: 43 description: 44 - The version of the server. For example C(12.0). 45 identity: 46 description: 47 - The identity type. Set this to C(SystemAssigned) in order to automatically create and assign an Azure Active Directory principal for the resource. 48 - Possible values include C(SystemAssigned). 49 state: 50 description: 51 - State of the SQL server. Use C(present) to create or update a server and use C(absent) to delete a server. 52 default: present 53 choices: 54 - absent 55 - present 56 57extends_documentation_fragment: 58 - azure 59 - azure_tags 60 61author: 62 - Zim Kalinowski (@zikalino) 63 64''' 65 66EXAMPLES = ''' 67 - name: Create (or update) SQL Server 68 azure_rm_sqlserver: 69 resource_group: myResourceGroup 70 name: server_name 71 location: westus 72 admin_username: mylogin 73 admin_password: Testpasswordxyz12! 74''' 75 76RETURN = ''' 77id: 78 description: 79 - Resource ID. 80 returned: always 81 type: str 82 sample: /subscriptions/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/resourceGroups/myResourceGroup/providers/Microsoft.Sql/servers/sqlcrudtest-4645 83version: 84 description: 85 - The version of the server. 86 returned: always 87 type: str 88 sample: 12.0 89state: 90 description: 91 - The state of the server. 92 returned: always 93 type: str 94 sample: state 95fully_qualified_domain_name: 96 description: 97 - The fully qualified domain name of the server. 98 returned: always 99 type: str 100 sample: sqlcrudtest-4645.database.windows.net 101''' 102 103import time 104from ansible.module_utils.azure_rm_common import AzureRMModuleBase 105 106try: 107 from msrestazure.azure_exceptions import CloudError 108 from msrest.polling import LROPoller 109 from azure.mgmt.sql import SqlManagementClient 110 from msrest.serialization import Model 111except ImportError: 112 # This is handled in azure_rm_common 113 pass 114 115 116class Actions: 117 NoAction, Create, Update, Delete = range(4) 118 119 120class AzureRMSqlServer(AzureRMModuleBase): 121 """Configuration class for an Azure RM SQL Server resource""" 122 123 def __init__(self): 124 self.module_arg_spec = dict( 125 resource_group=dict( 126 type='str', 127 required=True 128 ), 129 name=dict( 130 type='str', 131 required=True 132 ), 133 location=dict( 134 type='str' 135 ), 136 admin_username=dict( 137 type='str' 138 ), 139 admin_password=dict( 140 type='str', 141 no_log=True 142 ), 143 version=dict( 144 type='str' 145 ), 146 identity=dict( 147 type='str' 148 ), 149 state=dict( 150 type='str', 151 default='present', 152 choices=['present', 'absent'] 153 ) 154 ) 155 156 self.resource_group = None 157 self.name = None 158 self.parameters = dict() 159 self.tags = None 160 161 self.results = dict(changed=False) 162 self.state = None 163 self.to_do = Actions.NoAction 164 165 super(AzureRMSqlServer, self).__init__(derived_arg_spec=self.module_arg_spec, 166 supports_check_mode=True, 167 supports_tags=True) 168 169 def exec_module(self, **kwargs): 170 """Main module execution method""" 171 172 for key in list(self.module_arg_spec.keys()) + ['tags']: 173 if hasattr(self, key): 174 setattr(self, key, kwargs[key]) 175 elif kwargs[key] is not None: 176 if key == "location": 177 self.parameters.update({"location": kwargs[key]}) 178 elif key == "admin_username": 179 self.parameters.update({"administrator_login": kwargs[key]}) 180 elif key == "admin_password": 181 self.parameters.update({"administrator_login_password": kwargs[key]}) 182 elif key == "version": 183 self.parameters.update({"version": kwargs[key]}) 184 elif key == "identity": 185 self.parameters.update({"identity": {"type": kwargs[key]}}) 186 187 old_response = None 188 response = None 189 results = dict() 190 191 resource_group = self.get_resource_group(self.resource_group) 192 193 if "location" not in self.parameters: 194 self.parameters["location"] = resource_group.location 195 196 old_response = self.get_sqlserver() 197 198 if not old_response: 199 self.log("SQL Server instance doesn't exist") 200 if self.state == 'absent': 201 self.log("Old instance didn't exist") 202 else: 203 self.to_do = Actions.Create 204 else: 205 self.log("SQL Server instance already exists") 206 if self.state == 'absent': 207 self.to_do = Actions.Delete 208 elif self.state == 'present': 209 self.log("Need to check if SQL Server instance has to be deleted or may be updated") 210 update_tags, newtags = self.update_tags(old_response.get('tags', dict())) 211 if update_tags: 212 self.tags = newtags 213 self.to_do = Actions.Update 214 215 if (self.to_do == Actions.Create) or (self.to_do == Actions.Update): 216 self.log("Need to Create / Update the SQL Server instance") 217 218 if self.check_mode: 219 self.results['changed'] = True 220 return self.results 221 222 self.parameters['tags'] = self.tags 223 response = self.create_update_sqlserver() 224 response.pop('administrator_login_password', None) 225 226 if not old_response: 227 self.results['changed'] = True 228 else: 229 self.results['changed'] = old_response.__ne__(response) 230 self.log("Creation / Update done") 231 elif self.to_do == Actions.Delete: 232 self.log("SQL Server instance deleted") 233 self.results['changed'] = True 234 235 if self.check_mode: 236 return self.results 237 238 self.delete_sqlserver() 239 # make sure instance is actually deleted, for some Azure resources, instance is hanging around 240 # for some time after deletion -- this should be really fixed in Azure 241 while self.get_sqlserver(): 242 time.sleep(20) 243 else: 244 self.log("SQL Server instance unchanged") 245 self.results['changed'] = False 246 response = old_response 247 248 if response: 249 self.results["id"] = response["id"] 250 self.results["version"] = response["version"] 251 self.results["state"] = response["state"] 252 self.results["fully_qualified_domain_name"] = response["fully_qualified_domain_name"] 253 254 return self.results 255 256 def create_update_sqlserver(self): 257 ''' 258 Creates or updates SQL Server with the specified configuration. 259 260 :return: deserialized SQL Server instance state dictionary 261 ''' 262 self.log("Creating / Updating the SQL Server instance {0}".format(self.name)) 263 264 try: 265 response = self.sql_client.servers.create_or_update(self.resource_group, 266 self.name, 267 self.parameters) 268 if isinstance(response, LROPoller): 269 response = self.get_poller_result(response) 270 271 except CloudError as exc: 272 self.log('Error attempting to create the SQL Server instance.') 273 self.fail("Error creating the SQL Server instance: {0}".format(str(exc))) 274 return response.as_dict() 275 276 def delete_sqlserver(self): 277 ''' 278 Deletes specified SQL Server instance in the specified subscription and resource group. 279 280 :return: True 281 ''' 282 self.log("Deleting the SQL Server instance {0}".format(self.name)) 283 try: 284 response = self.sql_client.servers.delete(self.resource_group, 285 self.name) 286 except CloudError as e: 287 self.log('Error attempting to delete the SQL Server instance.') 288 self.fail("Error deleting the SQL Server instance: {0}".format(str(e))) 289 290 return True 291 292 def get_sqlserver(self): 293 ''' 294 Gets the properties of the specified SQL Server. 295 296 :return: deserialized SQL Server instance state dictionary 297 ''' 298 self.log("Checking if the SQL Server instance {0} is present".format(self.name)) 299 found = False 300 try: 301 response = self.sql_client.servers.get(self.resource_group, 302 self.name) 303 found = True 304 self.log("Response : {0}".format(response)) 305 self.log("SQL Server instance : {0} found".format(response.name)) 306 except CloudError as e: 307 self.log('Did not find the SQL Server instance.') 308 if found is True: 309 return response.as_dict() 310 311 return False 312 313 314def main(): 315 """Main execution""" 316 AzureRMSqlServer() 317 318 319if __name__ == '__main__': 320 main() 321