1#!/usr/bin/python 2# -*- coding: utf-8 -*- 3 4# Copyright: (c) 2016, Thomas Stringer <tomstr@microsoft.com> 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_loadbalancer 19 20version_added: "2.4" 21 22short_description: Manage Azure load balancers 23 24description: 25 - Create, update and delete Azure load balancers. 26 27options: 28 resource_group: 29 description: 30 - Name of a resource group where the load balancer exists or will be created. 31 required: true 32 name: 33 description: 34 - Name of the load balancer. 35 required: true 36 state: 37 description: 38 - Assert the state of the load balancer. Use C(present) to create/update a load balancer, or C(absent) to delete one. 39 default: present 40 choices: 41 - absent 42 - present 43 location: 44 description: 45 - Valid Azure location. Defaults to location of the resource group. 46 sku: 47 description: 48 - The load balancer SKU. 49 choices: 50 - Basic 51 - Standard 52 version_added: '2.6' 53 frontend_ip_configurations: 54 description: 55 - List of frontend IPs to be used. 56 suboptions: 57 name: 58 description: 59 - Name of the frontend ip configuration. 60 required: True 61 public_ip_address: 62 description: 63 - Name of an existing public IP address object in the current resource group to associate with the security group. 64 private_ip_address: 65 description: 66 - The reference of the Public IP resource. 67 version_added: '2.6' 68 private_ip_allocation_method: 69 description: 70 - The Private IP allocation method. 71 choices: 72 - Static 73 - Dynamic 74 version_added: '2.6' 75 subnet: 76 description: 77 - The reference of the subnet resource. 78 - Should be an existing subnet's resource id. 79 version_added: '2.6' 80 version_added: '2.5' 81 backend_address_pools: 82 description: 83 - List of backend address pools. 84 suboptions: 85 name: 86 description: 87 - Name of the backend address pool. 88 required: True 89 version_added: '2.5' 90 probes: 91 description: 92 - List of probe definitions used to check endpoint health. 93 suboptions: 94 name: 95 description: 96 - Name of the probe. 97 required: True 98 port: 99 description: 100 - Probe port for communicating the probe. Possible values range from 1 to 65535, inclusive. 101 required: True 102 protocol: 103 description: 104 - The protocol of the end point to be probed. 105 - If C(Tcp) is specified, a received ACK is required for the probe to be successful. 106 - If C(Http) or C(Https) is specified, a 200 OK response from the specified URL is required for the probe to be successful. 107 choices: 108 - Tcp 109 - Http 110 - Https 111 interval: 112 description: 113 - The interval, in seconds, for how frequently to probe the endpoint for health status. 114 - Slightly less than half the allocated timeout period, which allows two full probes before taking the instance out of rotation. 115 - The default value is C(15), the minimum value is C(5). 116 default: 15 117 fail_count: 118 description: 119 - The number of probes where if no response, will result in stopping further traffic from being delivered to the endpoint. 120 - This values allows endpoints to be taken out of rotation faster or slower than the typical times used in Azure. 121 default: 3 122 aliases: 123 - number_of_probes 124 request_path: 125 description: 126 - The URI used for requesting health status from the VM. 127 - Path is required if I(protocol=Http) or I(protocol=Https). Otherwise, it is not allowed. 128 version_added: '2.5' 129 inbound_nat_pools: 130 description: 131 - Defines an external port range for inbound NAT to a single backend port on NICs associated with a load balancer. 132 - Inbound NAT rules are created automatically for each NIC associated with the Load Balancer using an external port from this range. 133 - Defining an Inbound NAT pool on your Load Balancer is mutually exclusive with defining inbound Nat rules. 134 - Inbound NAT pools are referenced from virtual machine scale sets. 135 - NICs that are associated with individual virtual machines cannot reference an inbound NAT pool. 136 - They have to reference individual inbound NAT rules. 137 suboptions: 138 name: 139 description: 140 - Name of the inbound NAT pool. 141 required: True 142 frontend_ip_configuration_name: 143 description: 144 - A reference to frontend IP addresses. 145 required: True 146 protocol: 147 description: 148 - IP protocol for the NAT pool. 149 choices: 150 - Tcp 151 - Udp 152 - All 153 frontend_port_range_start: 154 description: 155 - The first port in the range of external ports that will be used to provide inbound NAT to NICs associated with the load balancer. 156 - Acceptable values range between 1 and 65534. 157 required: True 158 frontend_port_range_end: 159 description: 160 - The last port in the range of external ports that will be used to provide inbound NAT to NICs associated with the load balancer. 161 - Acceptable values range between 1 and 65535. 162 required: True 163 backend_port: 164 description: 165 - The port used for internal connections on the endpoint. 166 - Acceptable values are between 1 and 65535. 167 version_added: '2.5' 168 load_balancing_rules: 169 description: 170 - Object collection representing the load balancing rules Gets the provisioning. 171 suboptions: 172 name: 173 description: 174 - Name of the load balancing rule. 175 required: True 176 frontend_ip_configuration: 177 description: 178 - A reference to frontend IP addresses. 179 required: True 180 backend_address_pool: 181 description: 182 - A reference to a pool of DIPs. Inbound traffic is randomly load balanced across IPs in the backend IPs. 183 required: True 184 probe: 185 description: 186 - The name of the load balancer probe this rule should use for health checks. 187 required: True 188 protocol: 189 description: 190 - IP protocol for the load balancing rule. 191 choices: 192 - Tcp 193 - Udp 194 - All 195 load_distribution: 196 description: 197 - The session persistence policy for this rule; C(Default) is no persistence. 198 choices: 199 - Default 200 - SourceIP 201 - SourceIPProtocol 202 default: Default 203 frontend_port: 204 description: 205 - The port for the external endpoint. 206 - Frontend port numbers must be unique across all rules within the load balancer. 207 - Acceptable values are between 0 and 65534. 208 - Note that value 0 enables "Any Port". 209 backend_port: 210 description: 211 - The port used for internal connections on the endpoint. 212 - Acceptable values are between 0 and 65535. 213 - Note that value 0 enables "Any Port". 214 idle_timeout: 215 description: 216 - The timeout for the TCP idle connection. 217 - The value can be set between 4 and 30 minutes. 218 - The default value is C(4) minutes. 219 - This element is only used when the protocol is set to TCP. 220 enable_floating_ip: 221 description: 222 - Configures SNAT for the VMs in the backend pool to use the publicIP address specified in the frontend of the load balancing rule. 223 version_added: '2.5' 224 inbound_nat_rules: 225 description: 226 - Collection of inbound NAT Rules used by a load balancer. 227 - Defining inbound NAT rules on your load balancer is mutually exclusive with defining an inbound NAT pool. 228 - Inbound NAT pools are referenced from virtual machine scale sets. 229 - NICs that are associated with individual virtual machines cannot reference an Inbound NAT pool. 230 - They have to reference individual inbound NAT rules. 231 suboptions: 232 name: 233 description: 234 - name of the inbound nat rule. 235 required: True 236 frontend_ip_configuration: 237 description: 238 - A reference to frontend IP addresses. 239 required: True 240 protocol: 241 description: 242 - IP protocol for the inbound nat rule. 243 choices: 244 - Tcp 245 - Udp 246 - All 247 frontend_port: 248 description: 249 - The port for the external endpoint. 250 - Frontend port numbers must be unique across all rules within the load balancer. 251 - Acceptable values are between 0 and 65534. 252 - Note that value 0 enables "Any Port". 253 backend_port: 254 description: 255 - The port used for internal connections on the endpoint. 256 - Acceptable values are between 0 and 65535. 257 - Note that value 0 enables "Any Port". 258 idle_timeout: 259 description: 260 - The timeout for the TCP idle connection. 261 - The value can be set between 4 and 30 minutes. 262 - The default value is C(4) minutes. 263 - This element is only used when I(protocol=Tcp). 264 enable_floating_ip: 265 description: 266 - Configures a virtual machine's endpoint for the floating IP capability required to configure a SQL AlwaysOn Availability Group. 267 - This setting is required when using the SQL AlwaysOn Availability Groups in SQL server. 268 - This setting can't be changed after you create the endpoint. 269 enable_tcp_reset: 270 description: 271 - Receive bidirectional TCP Reset on TCP flow idle timeout or unexpected connection termination. 272 - This element is only used when I(protocol=Tcp). 273 version_added: '2.8' 274 public_ip_address_name: 275 description: 276 - (deprecated) Name of an existing public IP address object to associate with the security group. 277 - This option has been deprecated, and will be removed in 2.9. Use I(frontend_ip_configurations) instead. 278 aliases: 279 - public_ip_address 280 - public_ip_name 281 - public_ip 282 probe_port: 283 description: 284 - (deprecated) The port that the health probe will use. 285 - This option has been deprecated, and will be removed in 2.9. Use I(probes) instead. 286 probe_protocol: 287 description: 288 - (deprecated) The protocol to use for the health probe. 289 - This option has been deprecated, and will be removed in 2.9. Use I(probes) instead. 290 choices: 291 - Tcp 292 - Http 293 - Https 294 probe_interval: 295 description: 296 - (deprecated) Time (in seconds) between endpoint health probes. 297 - This option has been deprecated, and will be removed in 2.9. Use I(probes) instead. 298 default: 15 299 probe_fail_count: 300 description: 301 - (deprecated) The amount of probe failures for the load balancer to make a health determination. 302 - This option has been deprecated, and will be removed in 2.9. Use I(probes) instead. 303 default: 3 304 probe_request_path: 305 description: 306 - (deprecated) The URL that an HTTP probe or HTTPS probe will use (only relevant if I(probe_protocol=Http) or I(probe_protocol=Https)). 307 - This option has been deprecated, and will be removed in 2.9. Use I(probes) instead. 308 protocol: 309 description: 310 - (deprecated) The protocol (TCP or UDP) that the load balancer will use. 311 - This option has been deprecated, and will be removed in 2.9. Use I(load_balancing_rules) instead. 312 choices: 313 - Tcp 314 - Udp 315 load_distribution: 316 description: 317 - (deprecated) The type of load distribution that the load balancer will employ. 318 - This option has been deprecated, and will be removed in 2.9. Use I(load_balancing_rules) instead. 319 choices: 320 - Default 321 - SourceIP 322 - SourceIPProtocol 323 frontend_port: 324 description: 325 - (deprecated) Frontend port that will be exposed for the load balancer. 326 - This option has been deprecated, and will be removed in 2.9. Use I(load_balancing_rules) instead. 327 backend_port: 328 description: 329 - (deprecated) Backend port that will be exposed for the load balancer. 330 - This option has been deprecated, and will be removed in 2.9. Use I(load_balancing_rules) instead. 331 idle_timeout: 332 description: 333 - (deprecated) Timeout for TCP idle connection in minutes. 334 - This option has been deprecated, and will be removed in 2.9. Use I(load_balancing_rules) instead. 335 default: 4 336 natpool_frontend_port_start: 337 description: 338 - (deprecated) Start of the port range for a NAT pool. 339 - This option has been deprecated, and will be removed in 2.9. Use I(inbound_nat_pools) instead. 340 natpool_frontend_port_end: 341 description: 342 - (deprecated) End of the port range for a NAT pool. 343 - This option has been deprecated, and will be removed in 2.9. Use I(inbound_nat_pools) instead. 344 natpool_backend_port: 345 description: 346 - (deprecated) Backend port used by the NAT pool. 347 - This option has been deprecated, and will be removed in 2.9. Use I(inbound_nat_pools) instead. 348 natpool_protocol: 349 description: 350 - (deprecated) The protocol for the NAT pool. 351 - This option has been deprecated, and will be removed in 2.9. Use I(inbound_nat_pools) instead. 352extends_documentation_fragment: 353 - azure 354 - azure_tags 355 356author: 357 - Thomas Stringer (@trstringer) 358 - Yuwei Zhou (@yuwzho) 359''' 360 361EXAMPLES = ''' 362- name: create load balancer 363 azure_rm_loadbalancer: 364 resource_group: myResourceGroup 365 name: testloadbalancer1 366 frontend_ip_configurations: 367 - name: frontendipconf0 368 public_ip_address: testpip 369 backend_address_pools: 370 - name: backendaddrpool0 371 probes: 372 - name: prob0 373 port: 80 374 inbound_nat_pools: 375 - name: inboundnatpool0 376 frontend_ip_configuration_name: frontendipconf0 377 protocol: Tcp 378 frontend_port_range_start: 80 379 frontend_port_range_end: 81 380 backend_port: 8080 381 load_balancing_rules: 382 - name: lbrbalancingrule0 383 frontend_ip_configuration: frontendipconf0 384 backend_address_pool: backendaddrpool0 385 frontend_port: 80 386 backend_port: 80 387 probe: prob0 388 inbound_nat_rules: 389 - name: inboundnatrule0 390 backend_port: 8080 391 protocol: Tcp 392 frontend_port: 8080 393 frontend_ip_configuration: frontendipconf0 394''' 395 396RETURN = ''' 397state: 398 description: 399 - Current state of the load balancer. 400 returned: always 401 type: dict 402changed: 403 description: 404 - Whether or not the resource has changed. 405 returned: always 406 type: bool 407''' 408 409import random 410from ansible.module_utils.azure_rm_common import AzureRMModuleBase, format_resource_id 411from ansible.module_utils._text import to_native 412try: 413 from msrestazure.tools import parse_resource_id 414 from msrestazure.azure_exceptions import CloudError 415except ImportError: 416 # This is handled in azure_rm_common 417 pass 418 419 420frontend_ip_configuration_spec = dict( 421 name=dict( 422 type='str', 423 required=True 424 ), 425 public_ip_address=dict( 426 type='str' 427 ), 428 private_ip_address=dict( 429 type='str' 430 ), 431 private_ip_allocation_method=dict( 432 type='str' 433 ), 434 subnet=dict( 435 type='str' 436 ) 437) 438 439 440backend_address_pool_spec = dict( 441 name=dict( 442 type='str', 443 required=True 444 ) 445) 446 447 448probes_spec = dict( 449 name=dict( 450 type='str', 451 required=True 452 ), 453 port=dict( 454 type='int', 455 required=True 456 ), 457 protocol=dict( 458 type='str', 459 choices=['Tcp', 'Http', 'Https'] 460 ), 461 interval=dict( 462 type='int', 463 default=15 464 ), 465 fail_count=dict( 466 type='int', 467 default=3, 468 aliases=['number_of_probes'] 469 ), 470 request_path=dict( 471 type='str' 472 ) 473) 474 475 476inbound_nat_pool_spec = dict( 477 name=dict( 478 type='str', 479 required=True 480 ), 481 frontend_ip_configuration_name=dict( 482 type='str', 483 required=True 484 ), 485 protocol=dict( 486 type='str', 487 choices=['Tcp', 'Udp', 'All'] 488 ), 489 frontend_port_range_start=dict( 490 type='int', 491 required=True 492 ), 493 frontend_port_range_end=dict( 494 type='int', 495 required=True 496 ), 497 backend_port=dict( 498 type='int', 499 required=True 500 ) 501) 502 503 504inbound_nat_rule_spec = dict( 505 name=dict( 506 type='str', 507 required=True 508 ), 509 frontend_ip_configuration=dict( 510 type='str', 511 required=True 512 ), 513 protocol=dict( 514 type='str', 515 choices=['Tcp', 'Udp', 'All'] 516 ), 517 frontend_port=dict( 518 type='int', 519 required=True 520 ), 521 idle_timeout=dict( 522 type='int' 523 ), 524 backend_port=dict( 525 type='int', 526 required=True 527 ), 528 enable_floating_ip=dict( 529 type='bool' 530 ), 531 enable_tcp_reset=dict( 532 type='bool' 533 ) 534) 535 536 537load_balancing_rule_spec = dict( 538 name=dict( 539 type='str', 540 required=True 541 ), 542 frontend_ip_configuration=dict( 543 type='str', 544 required=True 545 ), 546 backend_address_pool=dict( 547 type='str', 548 required=True 549 ), 550 probe=dict( 551 type='str', 552 required=True 553 ), 554 protocol=dict( 555 type='str', 556 choices=['Tcp', 'Udp', 'All'] 557 ), 558 load_distribution=dict( 559 type='str', 560 choices=['Default', 'SourceIP', 'SourceIPProtocol'], 561 default='Default' 562 ), 563 frontend_port=dict( 564 type='int', 565 required=True 566 ), 567 backend_port=dict( 568 type='int' 569 ), 570 idle_timeout=dict( 571 type='int', 572 default=4 573 ), 574 enable_floating_ip=dict( 575 type='bool' 576 ) 577) 578 579 580class AzureRMLoadBalancer(AzureRMModuleBase): 581 """Configuration class for an Azure RM load balancer resource""" 582 583 def __init__(self): 584 self.module_args = dict( 585 resource_group=dict( 586 type='str', 587 required=True 588 ), 589 name=dict( 590 type='str', 591 required=True 592 ), 593 state=dict( 594 type='str', 595 default='present', 596 choices=['present', 'absent'] 597 ), 598 location=dict( 599 type='str' 600 ), 601 sku=dict( 602 type='str', 603 choices=['Basic', 'Standard'] 604 ), 605 frontend_ip_configurations=dict( 606 type='list', 607 elements='dict', 608 options=frontend_ip_configuration_spec 609 ), 610 backend_address_pools=dict( 611 type='list', 612 elements='dict', 613 options=backend_address_pool_spec 614 ), 615 probes=dict( 616 type='list', 617 elements='dict', 618 options=probes_spec 619 ), 620 inbound_nat_rules=dict( 621 type='list', 622 elements='dict', 623 options=inbound_nat_rule_spec 624 ), 625 inbound_nat_pools=dict( 626 type='list', 627 elements='dict', 628 options=inbound_nat_pool_spec 629 ), 630 load_balancing_rules=dict( 631 type='list', 632 elements='dict', 633 options=load_balancing_rule_spec 634 ), 635 public_ip_address_name=dict( 636 type='str', 637 aliases=['public_ip_address', 'public_ip_name', 'public_ip'] 638 ), 639 probe_port=dict( 640 type='int' 641 ), 642 probe_protocol=dict( 643 type='str', 644 choices=['Tcp', 'Http', 'Https'] 645 ), 646 probe_interval=dict( 647 type='int', 648 default=15 649 ), 650 probe_fail_count=dict( 651 type='int', 652 default=3 653 ), 654 probe_request_path=dict( 655 type='str' 656 ), 657 protocol=dict( 658 type='str', 659 choices=['Tcp', 'Udp'] 660 ), 661 load_distribution=dict( 662 type='str', 663 choices=['Default', 'SourceIP', 'SourceIPProtocol'] 664 ), 665 frontend_port=dict( 666 type='int' 667 ), 668 backend_port=dict( 669 type='int' 670 ), 671 idle_timeout=dict( 672 type='int', 673 default=4 674 ), 675 natpool_frontend_port_start=dict( 676 type='int' 677 ), 678 natpool_frontend_port_end=dict( 679 type='int' 680 ), 681 natpool_backend_port=dict( 682 type='int' 683 ), 684 natpool_protocol=dict( 685 type='str' 686 ) 687 ) 688 689 self.resource_group = None 690 self.name = None 691 self.location = None 692 self.sku = None 693 self.frontend_ip_configurations = None 694 self.backend_address_pools = None 695 self.probes = None 696 self.inbound_nat_rules = None 697 self.inbound_nat_pools = None 698 self.load_balancing_rules = None 699 self.public_ip_address_name = None 700 self.state = None 701 self.probe_port = None 702 self.probe_protocol = None 703 self.probe_interval = None 704 self.probe_fail_count = None 705 self.probe_request_path = None 706 self.protocol = None 707 self.load_distribution = None 708 self.frontend_port = None 709 self.backend_port = None 710 self.idle_timeout = None 711 self.natpool_frontend_port_start = None 712 self.natpool_frontend_port_end = None 713 self.natpool_backend_port = None 714 self.natpool_protocol = None 715 self.tags = None 716 717 self.results = dict(changed=False, state=dict()) 718 719 super(AzureRMLoadBalancer, self).__init__( 720 derived_arg_spec=self.module_args, 721 supports_check_mode=True 722 ) 723 724 def exec_module(self, **kwargs): 725 """Main module execution method""" 726 for key in list(self.module_args.keys()) + ['tags']: 727 setattr(self, key, kwargs[key]) 728 729 changed = False 730 731 resource_group = self.get_resource_group(self.resource_group) 732 if not self.location: 733 self.location = resource_group.location 734 735 load_balancer = self.get_load_balancer() 736 737 if self.state == 'present': 738 # compatible parameters 739 is_compatible_param = not self.frontend_ip_configurations and not self.backend_address_pools and not self.probes and not self.inbound_nat_pools 740 is_compatible_param = is_compatible_param and not load_balancer # the instance should not be exist 741 is_compatible_param = is_compatible_param or self.public_ip_address_name or self.probe_protocol or self.natpool_protocol or self.protocol 742 if is_compatible_param: 743 self.deprecate('Discrete load balancer config settings are deprecated and will be removed.' 744 ' Use frontend_ip_configurations, backend_address_pools, probes, inbound_nat_pools lists instead.', version='2.9') 745 frontend_ip_name = 'frontendip0' 746 backend_address_pool_name = 'backendaddrp0' 747 prob_name = 'prob0' 748 inbound_nat_pool_name = 'inboundnatp0' 749 lb_rule_name = 'lbr' 750 self.frontend_ip_configurations = [dict( 751 name=frontend_ip_name, 752 public_ip_address=self.public_ip_address_name 753 )] 754 self.backend_address_pools = [dict( 755 name=backend_address_pool_name 756 )] 757 self.probes = [dict( 758 name=prob_name, 759 port=self.probe_port, 760 protocol=self.probe_protocol, 761 interval=self.probe_interval, 762 fail_count=self.probe_fail_count, 763 request_path=self.probe_request_path 764 )] if self.probe_protocol else None 765 self.inbound_nat_pools = [dict( 766 name=inbound_nat_pool_name, 767 frontend_ip_configuration_name=frontend_ip_name, 768 protocol=self.natpool_protocol, 769 frontend_port_range_start=self.natpool_frontend_port_start, 770 frontend_port_range_end=self.natpool_frontend_port_end, 771 backend_port=self.natpool_backend_port 772 )] if self.natpool_protocol else None 773 self.load_balancing_rules = [dict( 774 name=lb_rule_name, 775 frontend_ip_configuration=frontend_ip_name, 776 backend_address_pool=backend_address_pool_name, 777 probe=prob_name, 778 protocol=self.protocol, 779 load_distribution=self.load_distribution, 780 frontend_port=self.frontend_port, 781 backend_port=self.backend_port, 782 idle_timeout=self.idle_timeout, 783 enable_floating_ip=False 784 )] if self.protocol else None 785 786 # create new load balancer structure early, so it can be easily compared 787 frontend_ip_configurations_param = [self.network_models.FrontendIPConfiguration( 788 name=item.get('name'), 789 public_ip_address=self.get_public_ip_address_instance(item.get('public_ip_address')) if item.get('public_ip_address') else None, 790 private_ip_address=item.get('private_ip_address'), 791 private_ip_allocation_method=item.get('private_ip_allocation_method'), 792 subnet=self.network_models.Subnet(id=item.get('subnet')) if item.get('subnet') else None 793 ) for item in self.frontend_ip_configurations] if self.frontend_ip_configurations else None 794 795 backend_address_pools_param = [self.network_models.BackendAddressPool( 796 name=item.get('name') 797 ) for item in self.backend_address_pools] if self.backend_address_pools else None 798 799 probes_param = [self.network_models.Probe( 800 name=item.get('name'), 801 port=item.get('port'), 802 protocol=item.get('protocol'), 803 interval_in_seconds=item.get('interval'), 804 request_path=item.get('request_path'), 805 number_of_probes=item.get('fail_count') 806 ) for item in self.probes] if self.probes else None 807 808 inbound_nat_pools_param = [self.network_models.InboundNatPool( 809 name=item.get('name'), 810 frontend_ip_configuration=self.network_models.SubResource( 811 id=frontend_ip_configuration_id( 812 self.subscription_id, 813 self.resource_group, 814 self.name, 815 item.get('frontend_ip_configuration_name'))), 816 protocol=item.get('protocol'), 817 frontend_port_range_start=item.get('frontend_port_range_start'), 818 frontend_port_range_end=item.get('frontend_port_range_end'), 819 backend_port=item.get('backend_port') 820 ) for item in self.inbound_nat_pools] if self.inbound_nat_pools else None 821 822 load_balancing_rules_param = [self.network_models.LoadBalancingRule( 823 name=item.get('name'), 824 frontend_ip_configuration=self.network_models.SubResource( 825 id=frontend_ip_configuration_id( 826 self.subscription_id, 827 self.resource_group, 828 self.name, 829 item.get('frontend_ip_configuration') 830 ) 831 ), 832 backend_address_pool=self.network_models.SubResource( 833 id=backend_address_pool_id( 834 self.subscription_id, 835 self.resource_group, 836 self.name, 837 item.get('backend_address_pool') 838 ) 839 ), 840 probe=self.network_models.SubResource( 841 id=probe_id( 842 self.subscription_id, 843 self.resource_group, 844 self.name, 845 item.get('probe') 846 ) 847 ), 848 protocol=item.get('protocol'), 849 load_distribution=item.get('load_distribution'), 850 frontend_port=item.get('frontend_port'), 851 backend_port=item.get('backend_port'), 852 idle_timeout_in_minutes=item.get('idle_timeout'), 853 enable_floating_ip=item.get('enable_floating_ip') 854 ) for item in self.load_balancing_rules] if self.load_balancing_rules else None 855 856 inbound_nat_rules_param = [self.network_models.InboundNatRule( 857 name=item.get('name'), 858 frontend_ip_configuration=self.network_models.SubResource( 859 id=frontend_ip_configuration_id( 860 self.subscription_id, 861 self.resource_group, 862 self.name, 863 item.get('frontend_ip_configuration') 864 ) 865 ) if item.get('frontend_ip_configuration') else None, 866 protocol=item.get('protocol'), 867 frontend_port=item.get('frontend_port'), 868 backend_port=item.get('backend_port'), 869 idle_timeout_in_minutes=item.get('idle_timeout'), 870 enable_tcp_reset=item.get('enable_tcp_reset'), 871 enable_floating_ip=item.get('enable_floating_ip') 872 ) for item in self.inbound_nat_rules] if self.inbound_nat_rules else None 873 874 # construct the new instance, if the parameter is none, keep remote one 875 self.new_load_balancer = self.network_models.LoadBalancer( 876 sku=self.network_models.LoadBalancerSku(name=self.sku) if self.sku else None, 877 location=self.location, 878 tags=self.tags, 879 frontend_ip_configurations=frontend_ip_configurations_param, 880 backend_address_pools=backend_address_pools_param, 881 probes=probes_param, 882 inbound_nat_pools=inbound_nat_pools_param, 883 load_balancing_rules=load_balancing_rules_param, 884 inbound_nat_rules=inbound_nat_rules_param 885 ) 886 887 self.new_load_balancer = self.assign_protocol(self.new_load_balancer, load_balancer) 888 889 if load_balancer: 890 self.new_load_balancer = self.object_assign(self.new_load_balancer, load_balancer) 891 load_balancer_dict = load_balancer.as_dict() 892 new_dict = self.new_load_balancer.as_dict() 893 if not default_compare(new_dict, load_balancer_dict, ''): 894 changed = True 895 else: 896 changed = False 897 else: 898 changed = True 899 elif self.state == 'absent' and load_balancer: 900 changed = True 901 902 self.results['state'] = load_balancer.as_dict() if load_balancer else {} 903 if 'tags' in self.results['state']: 904 update_tags, self.results['state']['tags'] = self.update_tags(self.results['state']['tags']) 905 if update_tags: 906 changed = True 907 else: 908 if self.tags: 909 changed = True 910 self.results['changed'] = changed 911 912 if self.state == 'present' and changed: 913 self.results['state'] = self.create_or_update_load_balancer(self.new_load_balancer).as_dict() 914 elif self.state == 'absent' and changed: 915 self.delete_load_balancer() 916 self.results['state'] = None 917 918 return self.results 919 920 def get_public_ip_address_instance(self, id): 921 """Get a reference to the public ip address resource""" 922 self.log('Fetching public ip address {0}'.format(id)) 923 resource_id = format_resource_id(id, self.subscription_id, 'Microsoft.Network', 'publicIPAddresses', self.resource_group) 924 return self.network_models.PublicIPAddress(id=resource_id) 925 926 def get_load_balancer(self): 927 """Get a load balancer""" 928 self.log('Fetching loadbalancer {0}'.format(self.name)) 929 try: 930 return self.network_client.load_balancers.get(self.resource_group, self.name) 931 except CloudError: 932 return None 933 934 def delete_load_balancer(self): 935 """Delete a load balancer""" 936 self.log('Deleting loadbalancer {0}'.format(self.name)) 937 try: 938 poller = self.network_client.load_balancers.delete(self.resource_group, self.name) 939 return self.get_poller_result(poller) 940 except CloudError as exc: 941 self.fail("Error deleting loadbalancer {0} - {1}".format(self.name, str(exc))) 942 943 def create_or_update_load_balancer(self, param): 944 try: 945 poller = self.network_client.load_balancers.create_or_update(self.resource_group, self.name, param) 946 new_lb = self.get_poller_result(poller) 947 return new_lb 948 except CloudError as exc: 949 self.fail("Error creating or updating load balancer {0} - {1}".format(self.name, str(exc))) 950 951 def object_assign(self, patch, origin): 952 attribute_map = set(self.network_models.LoadBalancer._attribute_map.keys()) - set(self.network_models.LoadBalancer._validation.keys()) 953 for key in attribute_map: 954 if not getattr(patch, key): 955 setattr(patch, key, getattr(origin, key)) 956 return patch 957 958 def assign_protocol(self, patch, origin): 959 attribute_map = ['probes', 'inbound_nat_rules', 'inbound_nat_pools', 'load_balancing_rules'] 960 for attribute in attribute_map: 961 properties = getattr(patch, attribute) 962 if not properties: 963 continue 964 references = getattr(origin, attribute) if origin else [] 965 for item in properties: 966 if item.protocol: 967 continue 968 refs = [x for x in references if to_native(x.name) == item.name] 969 ref = refs[0] if len(refs) > 0 else None 970 item.protocol = ref.protocol if ref else 'Tcp' 971 return patch 972 973 974def default_compare(new, old, path): 975 if isinstance(new, dict): 976 if not isinstance(old, dict): 977 return False 978 for k in new.keys(): 979 if not default_compare(new.get(k), old.get(k, None), path + '/' + k): 980 return False 981 return True 982 elif isinstance(new, list): 983 if not isinstance(old, list) or len(new) != len(old): 984 return False 985 if len(old) == 0: 986 return True 987 if isinstance(old[0], dict): 988 key = None 989 if 'id' in old[0] and 'id' in new[0]: 990 key = 'id' 991 elif 'name' in old[0] and 'name' in new[0]: 992 key = 'name' 993 new = sorted(new, key=lambda x: x.get(key, None)) 994 old = sorted(old, key=lambda x: x.get(key, None)) 995 else: 996 new = sorted(new) 997 old = sorted(old) 998 for i in range(len(new)): 999 if not default_compare(new[i], old[i], path + '/*'): 1000 return False 1001 return True 1002 else: 1003 return new == old 1004 1005 1006def frontend_ip_configuration_id(subscription_id, resource_group_name, load_balancer_name, name): 1007 """Generate the id for a frontend ip configuration""" 1008 return '/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Network/loadBalancers/{2}/frontendIPConfigurations/{3}'.format( 1009 subscription_id, 1010 resource_group_name, 1011 load_balancer_name, 1012 name 1013 ) 1014 1015 1016def backend_address_pool_id(subscription_id, resource_group_name, load_balancer_name, name): 1017 """Generate the id for a backend address pool""" 1018 return '/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Network/loadBalancers/{2}/backendAddressPools/{3}'.format( 1019 subscription_id, 1020 resource_group_name, 1021 load_balancer_name, 1022 name 1023 ) 1024 1025 1026def probe_id(subscription_id, resource_group_name, load_balancer_name, name): 1027 """Generate the id for a probe""" 1028 return '/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Network/loadBalancers/{2}/probes/{3}'.format( 1029 subscription_id, 1030 resource_group_name, 1031 load_balancer_name, 1032 name 1033 ) 1034 1035 1036def main(): 1037 """Main execution""" 1038 AzureRMLoadBalancer() 1039 1040 1041if __name__ == '__main__': 1042 main() 1043