1#!/usr/local/bin/python3.8 2# 3# Copyright (c) 2017 Yawei Wang, <yaweiw@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 10DOCUMENTATION = ''' 11--- 12module: azure_rm_containerregistry 13version_added: "0.1.2" 14short_description: Manage an Azure Container Registry 15description: 16 - Create, update and delete an Azure Container Registry. 17 18options: 19 resource_group: 20 description: 21 - Name of a resource group where the Container Registry exists or will be created. 22 required: true 23 type: str 24 name: 25 description: 26 - Name of the Container Registry. 27 required: true 28 type: str 29 state: 30 description: 31 - Assert the state of the container registry. Use C(present) to create or update an container registry and C(absent) to delete it. 32 default: present 33 type: str 34 choices: 35 - absent 36 - present 37 location: 38 description: 39 - Valid azure location. Defaults to location of the resource group. 40 type: str 41 admin_user_enabled: 42 description: 43 - If enabled, you can use the registry name as username and admin user access key as password to docker login to your container registry. 44 type: bool 45 default: no 46 sku: 47 description: 48 - Specifies the SKU to use. Currently can be either C(Basic), C(Standard) or C(Premium). 49 default: Standard 50 type: str 51 choices: 52 - Basic 53 - Standard 54 - Premium 55 56extends_documentation_fragment: 57 - azure.azcollection.azure 58 - azure.azcollection.azure_tags 59 60author: 61 - Yawei Wang (@yaweiw) 62 63''' 64 65EXAMPLES = ''' 66 - name: Create an azure container registry 67 azure_rm_containerregistry: 68 name: myRegistry 69 location: eastus 70 resource_group: myResourceGroup 71 admin_user_enabled: true 72 sku: Premium 73 tags: 74 Release: beta1 75 Environment: Production 76 77 - name: Remove an azure container registry 78 azure_rm_containerregistry: 79 name: myRegistry 80 resource_group: myResourceGroup 81 state: absent 82''' 83RETURN = ''' 84id: 85 description: 86 - Resource ID. 87 returned: always 88 type: str 89 sample: /subscriptions/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/resourceGroups/myResourceGroup/providers/Microsoft.ContainerRegistry/registries/myRegistry 90name: 91 description: 92 - Registry name. 93 returned: always 94 type: str 95 sample: myregistry 96location: 97 description: 98 - Resource location. 99 returned: always 100 type: str 101 sample: westus 102admin_user_enabled: 103 description: 104 - Is admin user enabled. 105 returned: always 106 type: bool 107 sample: true 108sku: 109 description: 110 - The SKU name of the container registry. 111 returned: always 112 type: str 113 sample: Standard 114provisioning_state: 115 description: 116 - Provisioning state. 117 returned: always 118 type: str 119 sample: Succeeded 120login_server: 121 description: 122 - Registry login server. 123 returned: always 124 type: str 125 sample: myregistry.azurecr.io 126credentials: 127 description: 128 - Passwords defined for the registry. 129 returned: always 130 type: complex 131 contains: 132 password: 133 description: 134 - password value. 135 returned: when registry exists and C(admin_user_enabled) is set 136 type: str 137 sample: pass1value 138 password2: 139 description: 140 - password2 value. 141 returned: when registry exists and C(admin_user_enabled) is set 142 type: str 143 sample: pass2value 144tags: 145 description: 146 - Tags assigned to the resource. Dictionary of string:string pairs. 147 returned: always 148 type: dict 149''' 150 151from ansible_collections.azure.azcollection.plugins.module_utils.azure_rm_common import AzureRMModuleBase 152 153try: 154 from msrestazure.azure_exceptions import CloudError 155 from azure.mgmt.containerregistry.models import ( 156 Registry, 157 RegistryUpdateParameters, 158 StorageAccountProperties, 159 Sku, 160 SkuName, 161 SkuTier, 162 ProvisioningState, 163 PasswordName, 164 WebhookCreateParameters, 165 WebhookUpdateParameters, 166 WebhookAction, 167 WebhookStatus 168 ) 169 from azure.mgmt.containerregistry import ContainerRegistryManagementClient 170except ImportError as exc: 171 # This is handled in azure_rm_common 172 pass 173 174 175def create_containerregistry_dict(registry, credentials): 176 ''' 177 Helper method to deserialize a ContainerRegistry to a dict 178 :param: registry: return container registry object from Azure rest API call 179 :param: credentials: return credential objects from Azure rest API call 180 :return: dict of return container registry and it's credentials 181 ''' 182 results = dict( 183 id=registry.id if registry is not None else "", 184 name=registry.name if registry is not None else "", 185 location=registry.location if registry is not None else "", 186 admin_user_enabled=registry.admin_user_enabled if registry is not None else "", 187 sku=registry.sku.name if registry is not None else "", 188 provisioning_state=registry.provisioning_state if registry is not None else "", 189 login_server=registry.login_server if registry is not None else "", 190 credentials=dict(), 191 tags=registry.tags if registry is not None else "" 192 ) 193 if credentials: 194 results['credentials'] = dict( 195 password=credentials.passwords[0].value, 196 password2=credentials.passwords[1].value 197 ) 198 199 return results 200 201 202class Actions: 203 NoAction, Create, Update = range(3) 204 205 206class AzureRMContainerRegistry(AzureRMModuleBase): 207 """Configuration class for an Azure RM container registry resource""" 208 209 def __init__(self): 210 self.module_arg_spec = dict( 211 resource_group=dict( 212 type='str', 213 required=True 214 ), 215 name=dict( 216 type='str', 217 required=True 218 ), 219 state=dict( 220 type='str', 221 default='present', 222 choices=['present', 'absent'] 223 ), 224 location=dict( 225 type='str' 226 ), 227 admin_user_enabled=dict( 228 type='bool', 229 default=False 230 ), 231 sku=dict( 232 type='str', 233 default='Standard', 234 choices=['Basic', 'Standard', 'Premium'] 235 ) 236 ) 237 238 self.resource_group = None 239 self.name = None 240 self.location = None 241 self.state = None 242 self.sku = None 243 self.tags = None 244 245 self.results = dict(changed=False, state=dict()) 246 247 super(AzureRMContainerRegistry, self).__init__( 248 derived_arg_spec=self.module_arg_spec, 249 supports_check_mode=True, 250 supports_tags=True) 251 252 def exec_module(self, **kwargs): 253 """Main module execution method""" 254 for key in list(self.module_arg_spec.keys()) + ['tags']: 255 setattr(self, key, kwargs[key]) 256 257 resource_group = None 258 response = None 259 to_do = Actions.NoAction 260 261 resource_group = self.get_resource_group(self.resource_group) 262 if not self.location: 263 self.location = resource_group.location 264 265 # Check if the container registry instance already present in the RG 266 if self.state == 'present': 267 response = self.get_containerregistry() 268 269 if not response: 270 to_do = Actions.Create 271 else: 272 self.log('Results : {0}'.format(response)) 273 self.results.update(response) 274 if response['provisioning_state'] == "Succeeded": 275 to_do = Actions.NoAction 276 if (self.location is not None) and self.location != response['location']: 277 to_do = Actions.Update 278 elif (self.sku is not None) and self.sku != response['sku']: 279 to_do = Actions.Update 280 else: 281 to_do = Actions.NoAction 282 283 self.log("Create / Update the container registry instance") 284 if self.check_mode: 285 return self.results 286 287 self.results.update(self.create_update_containerregistry(to_do)) 288 if to_do != Actions.NoAction: 289 self.results['changed'] = True 290 else: 291 self.results.update(response) 292 self.results['changed'] = False 293 294 self.log("Container registry instance created or updated") 295 elif self.state == 'absent': 296 if self.check_mode: 297 return self.results 298 self.delete_containerregistry() 299 self.log("Container registry instance deleted") 300 301 return self.results 302 303 def create_update_containerregistry(self, to_do): 304 ''' 305 Creates or updates a container registry. 306 307 :return: deserialized container registry instance state dictionary 308 ''' 309 self.log("Creating / Updating the container registry instance {0}".format(self.name)) 310 311 try: 312 if to_do != Actions.NoAction: 313 if to_do == Actions.Create: 314 name_status = self.containerregistry_client.registries.check_name_availability(self.name) 315 if name_status.name_available: 316 poller = self.containerregistry_client.registries.create( 317 resource_group_name=self.resource_group, 318 registry_name=self.name, 319 registry=Registry( 320 location=self.location, 321 sku=Sku( 322 name=self.sku 323 ), 324 tags=self.tags, 325 admin_user_enabled=self.admin_user_enabled 326 ) 327 ) 328 else: 329 raise Exception("Invalid registry name. reason: " + name_status.reason + " message: " + name_status.message) 330 else: 331 registry = self.containerregistry_client.registries.get(self.resource_group, self.name) 332 if registry is not None: 333 poller = self.containerregistry_client.registries.update( 334 resource_group_name=self.resource_group, 335 registry_name=self.name, 336 registry_update_parameters=RegistryUpdateParameters( 337 sku=Sku( 338 name=self.sku 339 ), 340 tags=self.tags, 341 admin_user_enabled=self.admin_user_enabled 342 ) 343 ) 344 else: 345 raise Exception("Update registry failed as registry '" + self.name + "' doesn't exist.") 346 response = self.get_poller_result(poller) 347 if self.admin_user_enabled: 348 credentials = self.containerregistry_client.registries.list_credentials(self.resource_group, self.name) 349 else: 350 self.log('Cannot perform credential operations as admin user is disabled') 351 credentials = None 352 else: 353 response = None 354 credentials = None 355 except (CloudError, Exception) as exc: 356 self.log('Error attempting to create / update the container registry instance.') 357 self.fail("Error creating / updating the container registry instance: {0}".format(str(exc))) 358 return create_containerregistry_dict(response, credentials) 359 360 def delete_containerregistry(self): 361 ''' 362 Deletes the specified container registry in the specified subscription and resource group. 363 364 :return: True 365 ''' 366 self.log("Deleting the container registry instance {0}".format(self.name)) 367 try: 368 self.containerregistry_client.registries.delete(self.resource_group, self.name).wait() 369 except CloudError as e: 370 self.log('Error attempting to delete the container registry instance.') 371 self.fail("Error deleting the container registry instance: {0}".format(str(e))) 372 373 return True 374 375 def get_containerregistry(self): 376 ''' 377 Gets the properties of the specified container registry. 378 379 :return: deserialized container registry state dictionary 380 ''' 381 self.log("Checking if the container registry instance {0} is present".format(self.name)) 382 found = False 383 try: 384 response = self.containerregistry_client.registries.get(self.resource_group, self.name) 385 found = True 386 self.log("Response : {0}".format(response)) 387 self.log("Container registry instance : {0} found".format(response.name)) 388 except CloudError as e: 389 if e.error.error == 'ResourceNotFound': 390 self.log('Did not find the container registry instance: {0}'.format(str(e))) 391 else: 392 self.fail('Error while trying to get container registry instance: {0}'.format(str(e))) 393 response = None 394 if found is True and self.admin_user_enabled is True: 395 try: 396 credentials = self.containerregistry_client.registries.list_credentials(self.resource_group, self.name) 397 except CloudError as e: 398 self.fail('List registry credentials failed: {0}'.format(str(e))) 399 credentials = None 400 elif found is True and self.admin_user_enabled is False: 401 credentials = None 402 else: 403 return None 404 return create_containerregistry_dict(response, credentials) 405 406 407def main(): 408 """Main execution""" 409 AzureRMContainerRegistry() 410 411 412if __name__ == '__main__': 413 main() 414