Repository: ansible/ansible
Pull request: Fixes #34913: azure_rm_loadbalance should accpet a list of rules instead of one
##### SUMMARY
<!--- Describe the change, including rationale and design decisions -->
Fixes #34913

<!---
If you are fixing an existing issue, please include "Fixes #nnn" in your
commit message and your description; but you should still explain what
the change does.
-->

##### ISSUE TYPE
<!--- Pick one below and delete the rest: -->
 - Feature Pull Request

##### COMPONENT NAME
<!--- Name of the module, plugin, module or task -->

##### ANSIBLE VERSION
<!--- Paste verbatim output from "ansible --version" between quotes below -->
```
2.4
```


##### ADDITIONAL INFORMATION
<!---
Include additional information to help people understand the change here.
For bugs that don't have a linked bug report, a step-by-step reproduction
of the problem is helpful.
  -->

<!--- Paste verbatim command output below, e.g. before and after your change -->
```

```


Problem statement:
azure_rm_loadbalance should accpet a list of rules instead of one
<!---
Verify first that your issue/request is not already reported on GitHub.
Also test if the latest release, and master branch are affected too.
-->

##### ISSUE TYPE
<!--- Pick one below and delete the rest -->
 - Bug Report

##### COMPONENT NAME
azure_rm_loadbalance

##### ANSIBLE VERSION
2.4
2.5

##### CONFIGURATION
N/A

##### OS / ENVIRONMENT
N/A

##### SUMMARY
The load balancer module. Currently it can only accept one load balancing rule and one NAT rule.  It should be able to accept a list of rules. 

##### STEPS TO REPRODUCE
##### EXPECTED RESULTS
##### ACTUAL RESULTS

Hints from issue discussion:
@yuwzho , could you please look at it? Thanks.
!component =lib/ansible/modules/cloud/azure/azure_rm_loadbalancer.py
cc @tstringer
[click here for bot help](https://github.com/ansible/ansibullbot/blob/master/ISSUE_HELP.md)
<!--- boilerplate: notify --->
I m working on this issue!
cc @haroldwongms @nitzmahone @trstringer @xscript @zikalino
[click here for bot help](https://github.com/ansible/ansibullbot/blob/master/ISSUE_HELP.md)
<!--- boilerplate: notify --->

Commit message: fix

Patch to review:
diff --git a/lib/ansible/modules/cloud/azure/azure_rm_loadbalancer.py b/lib/ansible/modules/cloud/azure/azure_rm_loadbalancer.py
index 8b8e7bd5a1216d..d34f7c2effa84a 100755
--- a/lib/ansible/modules/cloud/azure/azure_rm_loadbalancer.py
+++ b/lib/ansible/modules/cloud/azure/azure_rm_loadbalancer.py
@@ -61,9 +61,153 @@
             - Valid azure location. Defaults to location of the resource group.
         default: resource_group location
         required: false
+    frontend_ip_configurations:
+        description: Object representing the frontend IPs to be used for the load balancer.
+        suboptions:
+            name:
+                description: name of the frontend ip configurations.
+                required: True
+            public_ip_address:
+                description: Name of an existing public IP address object to associate with the security group.
+                required: True
+        version_added: 2.5
+    backend_address_pools:
+        description: Collection of backend address pools used by a load balancer.
+        suboptions:
+            name:
+                description: name of the backend address pool.
+                required: True
+        version_added: 2.5
+    probes:
+        description: Collection of probe objects used in the load balancer.
+        suboptions:
+            name:
+                description: name of the probe.
+                required: True
+            port:
+                description: The port for communicating the probe. Possible values range from 1 to 65535, inclusive.
+                required: True
+            protocol:
+                description:
+                    - The protocol of the end point.
+                    - "Possible values are: 'Http' or 'Tcp'."
+                    - If 'Tcp' is specified, a received ACK is required for the probe to be successful.
+                    - If 'Http' is specified, a 200 OK response from the specifies URI is required for the probe to be successful.
+                choices:
+                    - Tcp
+                    - Http
+                default: Tcp
+            interval:
+                description:
+                    - The interval, in seconds, for how frequently to probe the endpoint for health status.
+                    - Slightly less than half the allocated timeout period, which allows two full probes before taking the instance out of rotation.
+                    - The default value is 15, the minimum value is 5.
+                default: 15
+            number_of_probes:
+                description:
+                    - The number of probes where if no response, will result in stopping further traffic from being delivered to the endpoint.
+                    - This values allows endpoints to be taken out of rotation faster or slower than the typical times used in Azure.
+                default: 3
+                aliases:
+                    - fail_count
+            request_path:
+                description:
+                    - The URI used for requesting health status from the VM.
+                    - Path is required if a protocol is set to http. Otherwise, it is not allowed.
+        version_added: 2.5
+    inbound_nat_pools:
+        description:
+            - Defines an external port range for inbound NAT to a single backend port on NICs associated with a load balancer.
+            - Inbound NAT rules are created automatically for each NIC associated with the Load Balancer using an external port from this range.
+            - Defining an Inbound NAT pool on your Load Balancer is mutually exclusive with defining inbound Nat rules.
+            - Inbound NAT pools are referenced from virtual machine scale sets.
+            - NICs that are associated with individual virtual machines cannot reference an inbound NAT pool.
+            - They have to reference individual inbound NAT rules.
+        suboptions:
+            name:
+                description: name of the inbound nat pool.
+                required: True
+            frontend_ip_configuration_name:
+                description: A reference to frontend IP addresses.
+                required: True
+            protocol:
+                description: "Possible values include: 'Udp', 'Tcp', 'All'."
+                choices:
+                    - Tcp
+                    - Udp
+                    - All
+                default: Tcp
+            frontend_port_range_start:
+                description:
+                    - The first port number in the range of external ports that will be used to provide Inbound Nat to NICs associated with a load balancer.
+                    - Acceptable values range between 1 and 65534.
+                required: True
+            frontend_port_range_end:
+                description:
+                    - The last port number in the range of external ports that will be used to provide Inbound Nat to NICs associated with a load balancer.
+                    - Acceptable values range between 1 and 65535.
+                required: True
+            backend_port:
+                description:
+                    - The port used for internal connections on the endpoint.
+                    - Acceptable values are between 1 and 65535.
+        version_added: 2.5
+    load_balancing_rules:
+        description:
+            - Object collection representing the load balancing rules Gets the provisioning.
+        suboptions:
+            name:
+                description: name of the load balancing rule.
+                required: True
+            frontend_ip_configuration:
+                description: A reference to frontend IP addresses.
+                required: True
+            backend_address_pool:
+                description: A reference to a pool of DIPs. Inbound traffic is randomly load balanced across IPs in the backend IPs.
+                required: True
+            probe:
+                description: The reference of the load balancer probe used by the load balancing rule.
+                required: True
+            protocol:
+                description: "Possible values include: 'Udp', 'Tcp', 'All'."
+                choices:
+                    - Tcp
+                    - Udp
+                    - All
+                default: Tcp
+            load_distribution:
+                description:
+                    - The load distribution policy for this rule.
+                    - Possible values are 'Default', 'SourceIP', and 'SourceIPProtocol'.
+                choices:
+                    - Default
+                    - SourceIP
+                    - SourceIPProtocol
+                default: Default
+            frontend_port:
+                description:
+                    - The port for the external endpoint.
+                    - Port numbers for each rule must be unique within the Load Balancer.
+                    - Acceptable values are between 0 and 65534.
+                    - Note that value 0 enables "Any Port"
+            backend_port:
+                description:
+                    - The port used for internal connections on the endpoint.
+                    - Acceptable values are between 0 and 65535. Note that value 0 enables "Any Port"
+            idle_timeout:
+                description:
+                    - The timeout for the TCP idle connection.
+                    - The value can be set between 4 and 30 minutes.
+                    - The default value is 4 minutes.
+                    - This element is only used when the protocol is set to TCP.
+            enable_floating_ip:
+                description:
+                    - Configures SNAT for the VMs in the backend pool to use the publicIP address specified in the frontend of the load balancing rule.
+        version_added: 2.5
     public_ip_address_name:
         description:
-            - Name of an existing public IP address object to associate with the security group.
+            - (Deprecate) Name of an existing public IP address object to associate with the security group.
+            - This option will be deprecated in 2.9, use I(frontend_ip_configurations) instead.
         aliases:
             - public_ip_address
             - public_ip_name
@@ -71,39 +215,46 @@
         required: false
     probe_port:
         description:
-            - The port that the health probe will use.
+            - (Deprecate) The port that the health probe will use.
+            - This option will be deprecated in 2.9, use I(probes) instead.
         required: false
     probe_protocol:
         description:
-            - The protocol to use for the health probe.
+            - (Deprecate) The protocol to use for the health probe.
+            - This option will be deprecated in 2.9, use I(probes) instead.
         required: false
         choices:
             - Tcp
             - Http
     probe_interval:
         description:
-            - How much time (in seconds) to probe the endpoint for health.
+            - (Deprecate) How much time (in seconds) to probe the endpoint for health.
+            - This option will be deprecated in 2.9, use I(probes) instead.
         default: 15
         required: false
     probe_fail_count:
         description:
-            - The amount of probe failures for the load balancer to make a health determination.
+            - (Deprecate) The amount of probe failures for the load balancer to make a health determination.
+            - This option will be deprecated in 2.9, use I(probes) instead.
         default: 3
         required: false
     probe_request_path:
         description:
-            - The URL that an HTTP probe will use (only relevant if probe_protocol is set to Http).
+            - (Deprecate) The URL that an HTTP probe will use (only relevant if probe_protocol is set to Http).
+            - This option will be deprecated in 2.9, use I(probes) instead.
         required: false
     protocol:
         description:
-            - The protocol (TCP or UDP) that the load balancer will use.
+            - (Deprecate) The protocol (TCP or UDP) that the load balancer will use.
+            - This option will be deprecated in 2.9, use I(load_balancing_rules) instead.
         required: false
         choices:
             - Tcp
             - Udp
     load_distribution:
         description:
-            - The type of load distribution that the load balancer will employ.
+            - (Deprecate) The type of load distribution that the load balancer will employ.
+            - This option will be deprecated in 2.9, use I(load_balancing_rules) instead.
         required: false
         choices:
             - Default
@@ -111,32 +262,39 @@
             - SourceIPProtocol
     frontend_port:
         description:
-            - Frontend port that will be exposed for the load balancer.
+            - (Deprecate) Frontend port that will be exposed for the load balancer.
+            - This option will be deprecated in 2.9, use I(load_balancing_rules) instead.
         required: false
     backend_port:
         description:
-            - Backend port that will be exposed for the load balancer.
+            - (Deprecate) Backend port that will be exposed for the load balancer.
+            - This option will be deprecated in 2.9, use I(load_balancing_rules) instead.
         required: false
     idle_timeout:
         description:
-            - Timeout for TCP idle connection in minutes.
+            - (Deprecate) Timeout for TCP idle connection in minutes.
+            - This option will be deprecated in 2.9, use I(load_balancing_rules) instead.
         default: 4
         required: false
     natpool_frontend_port_start:
         description:
-            - Start of the port range for a NAT pool.
+            - (Deprecate) Start of the port range for a NAT pool.
+            - This option will be deprecated in 2.9, use I(inbound_nat_pools) instead.
         required: false
     natpool_frontend_port_end:
         description:
-            - End of the port range for a NAT pool.
+            - (Deprecate) End of the port range for a NAT pool.
+            - This option will be deprecated in 2.9, use I(inbound_nat_pools) instead.
         required: false
     natpool_backend_port:
         description:
-            - Backend port used by the NAT pool.
+            - (Deprecate) Backend port used by the NAT pool.
+            - This option will be deprecated in 2.9, use I(inbound_nat_pools) instead.
         required: false
     natpool_protocol:
         description:
-            - The protocol for the NAT pool.
+            - (Deprecate) The protocol for the NAT pool.
+            - This option will be deprecated in 2.9, use I(inbound_nat_pools) instead.
         required: false
 extends_documentation_fragment:
     - azure
@@ -144,6 +302,7 @@
 
 author:
     - "Thomas Stringer (@tstringer)"
+    - "Yuwei Zhou (@yuwzho)"
 '''
 
 EXAMPLES = '''
@@ -183,12 +342,134 @@
 from ansible.module_utils.azure_rm_common import AzureRMModuleBase
 
 try:
+    from msrestazure.tools import parse_resource_id
     from msrestazure.azure_exceptions import CloudError
 except ImportError:
     # This is handled in azure_rm_common
     pass
 
 
+frontend_ip_configuration_spec = dict(
+    name=dict(
+        type='str',
+        required=True
+    ),
+    public_ip_address=dict(
+        type='str',
+        required=True
+    )
+)
+
+
+backend_address_pool_spec = dict(
+    name=dict(
+        type='str',
+        required=True
+    )
+)
+
+
+probes_spec = dict(
+    name=dict(
+        type='str',
+        required=True
+    ),
+    port=dict(
+        type='int',
+        required=True
+    ),
+    protocol=dict(
+        type='str',
+        choices=['Tcp', 'Http'],
+        default='Tcp'
+    ),
+    interval=dict(
+        type='int',
+        default=15
+    ),
+    number_of_probes=dict(
+        type='int',
+        default=3,
+        aliases=['fail_count']
+    ),
+    request_path=dict(
+        type='str'
+    )
+)
+
+
+inbound_nat_pool_spec = dict(
+    name=dict(
+        type='str',
+        required=True
+    ),
+    frontend_ip_configuration_name=dict(
+        type='str',
+        required=True
+    ),
+    protocol=dict(
+        type='str',
+        choices=['Tcp', 'Udp', 'All'],
+        default='Tcp'
+    ),
+    frontend_port_range_start=dict(
+        type='int',
+        required=True
+    ),
+    frontend_port_range_end=dict(
+        type='int',
+        required=True
+    ),
+    backend_port=dict(
+        type='int',
+        required=True
+    )
+)
+
+
+load_balancing_rule_spec = dict(
+    name=dict(
+        type='str',
+        required=True
+    ),
+    frontend_ip_configuration=dict(
+        type='str',
+        required=True
+    ),
+    backend_address_pool=dict(
+        type='str',
+        required=True
+    ),
+    probe=dict(
+        type='str',
+        required=True
+    ),
+    protocol=dict(
+        type='str',
+        choices=['Tcp', 'Udp', 'All'],
+        default='Tcp'
+    ),
+    load_distribution=dict(
+        type='str',
+        choices=['Default', 'SourceIP', 'SourceIPProtocol'],
+        default='Default'
+    ),
+    frontend_port=dict(
+        type='int'
+    ),
+    backend_port=dict(
+        type='int'
+    ),
+    idle_timeout=dict(
+        type='int',
+        default=4
+    ),
+    enable_floating_ip=dict(
+        type='bool'
+    )
+)
+
+
 class AzureRMLoadBalancer(AzureRMModuleBase):
     """Configuration class for an Azure RM load balancer resource"""
 
@@ -204,26 +485,46 @@ def __init__(self):
             ),
             state=dict(
                 type='str',
-                required=False,
                 default='present',
                 choices=['present', 'absent']
             ),
             location=dict(
-                type='str',
-                required=False
+                type='str'
+            ),
+            frontend_ip_configurations=dict(
+                type='list',
+                elements='dict',
+                options=frontend_ip_configuration_spec
+            ),
+            backend_address_pools=dict(
+                type='list',
+                elements='dict',
+                options=backend_address_pool_spec
+            ),
+            probes=dict(
+                type='list',
+                elements='dict',
+                options=probes_spec
+            ),
+            inbound_nat_pools=dict(
+                type='list',
+                elements='dict',
+                options=inbound_nat_pool_spec
+            ),
+            load_balancing_rules=dict(
+                type='list',
+                elements='dict',
+                options=load_balancing_rule_spec
             ),
             public_ip_address_name=dict(
                 type='str',
-                required=False,
                 aliases=['public_ip_address', 'public_ip_name', 'public_ip']
             ),
             probe_port=dict(
-                type='int',
-                required=False
+                type='int'
             ),
             probe_protocol=dict(
                 type='str',
-                required=False,
                 choices=['Tcp', 'Http']
             ),
             probe_interval=dict(
@@ -235,26 +536,21 @@ def __init__(self):
                 default=3
             ),
             probe_request_path=dict(
-                type='str',
-                required=False
+                type='str'
             ),
             protocol=dict(
                 type='str',
-                required=False,
                 choices=['Tcp', 'Udp']
             ),
             load_distribution=dict(
                 type='str',
-                required=False,
                 choices=['Default', 'SourceIP', 'SourceIPProtocol']
             ),
             frontend_port=dict(
-                type='int',
-                required=False
+                type='int'
             ),
             backend_port=dict(
-                type='int',
-                required=False
+                type='int'
             ),
             idle_timeout=dict(
                 type='int',
@@ -277,6 +573,11 @@ def __init__(self):
         self.resource_group = None
         self.name = None
         self.location = None
+        self.frontend_ip_configurations = None
+        self.backend_address_pools = None
+        self.probes = None
+        self.inbound_nat_pools = None
+        self.load_balancing_rules = None
         self.public_ip_address_name = None
         self.state = None
         self.probe_port = None
@@ -296,12 +597,9 @@ def __init__(self):
 
         self.results = dict(changed=False, state=dict())
 
-        required_if = [('state', 'present', ['public_ip_address_name'])]
-
         super(AzureRMLoadBalancer, self).__init__(
             derived_arg_spec=self.module_args,
-            supports_check_mode=True,
-            required_if=required_if
+            supports_check_mode=True
         )
 
     def exec_module(self, **kwargs):
@@ -309,164 +607,194 @@ def exec_module(self, **kwargs):
         for key in self.module_args.keys():
             setattr(self, key, kwargs[key])
 
-        results = dict()
         changed = False
-        pip = None
-        load_balancer_props = dict()
 
         resource_group = self.get_resource_group(self.resource_group)
         if not self.location:
             self.location = resource_group.location
-        load_balancer_props['location'] = self.location
-
-        if self.state == 'present':
-            # handle present status
-
-            frontend_ip_config_name = random_name('feipconfig')
-            frontend_ip_config_id = frontend_ip_configuration_id(
-                subscription_id=self.subscription_id,
-                resource_group_name=self.resource_group,
-                load_balancer_name=self.name,
-                name=frontend_ip_config_name
-            )
-            if self.public_ip_address_name:
-                pip = self.get_public_ip_address(self.public_ip_address_name)
-                load_balancer_props['frontend_ip_configurations'] = [
-                    self.network_models.FrontendIPConfiguration(
-                        name=frontend_ip_config_name,
-                        public_ip_address=pip
-                    )
-                ]
-        elif self.state == 'absent':
-            try:
-                self.network_client.load_balancers.delete(
-                    resource_group_name=self.resource_group,
-                    load_balancer_name=self.name
-                ).wait()
-                changed = True
-            except CloudError:
-                changed = False
-
-            self.results['changed'] = changed
-            return self.results
-
-        try:
-            # before we do anything, we need to attempt to retrieve the load balancer
-            # knowing whether or not it exists will tell us what to do in the future
-            self.log('Fetching load balancer {}'.format(self.name))
-            load_balancer = self.network_client.load_balancers.get(self.resource_group, self.name)
-
-            self.log('Load balancer {} exists'.format(self.name))
-            self.check_provisioning_state(load_balancer, self.state)
-            results = load_balancer_to_dict(load_balancer)
-            self.log(results, pretty_print=True)
-
-            if self.state == 'present':
-                update_tags, results['tags'] = self.update_tags(results['tags'])
 
-                if update_tags:
-                    changed = True
-        except CloudError:
-            self.log('Load balancer {} does not exist'.format(self.name))
-            if self.state == 'present':
-                self.log(
-                    'CHANGED: load balancer {} does not exist but requested status \'present\''
-                    .format(self.name)
-                )
-                changed = True
-
-        backend_address_pool_name = random_name('beap')
-        backend_addr_pool_id = backend_address_pool_id(
-            subscription_id=self.subscription_id,
-            resource_group_name=self.resource_group,
-            load_balancer_name=self.name,
-            name=backend_address_pool_name
-        )
-        load_balancer_props['backend_address_pools'] = [self.network_models.BackendAddressPool(name=backend_address_pool_name)]
-
-        probe_name = random_name('probe')
-        prb_id = probe_id(
-            subscription_id=self.subscription_id,
-            resource_group_name=self.resource_group,
-            load_balancer_name=self.name,
-            name=probe_name
-        )
+        load_balancer = self.get_load_balancer()
 
-        if self.probe_protocol:
-            load_balancer_props['probes'] = [
-                self.network_models.Probe(
-                    name=probe_name,
-                    protocol=self.probe_protocol,
+        if self.state == 'present':
+            # compatiable parameters
+            if not self.frontend_ip_configurations and not self.backend_address_pools and not self.probes and not self.inbound_nat_pools:
+                self.deprecate('Flatten setting is deprecated and will be removed.'
+                               ' Using frontend_ip_configurations, backend_address_pools, probes, inbound_nat_pools list to define', version='2.9')
+                frontend_ip_name = 'frontendip0'
+                backend_address_pool_name = 'backendaddrp0'
+                prob_name = 'prob0'
+                inbound_nat_pool_name = 'inboundnatp0'
+                lb_rule_name = 'lbr'
+                self.frontend_ip_configurations = [dict(
+                    name=frontend_ip_name,
+                    public_ip_address=self.public_ip_address_name
+                )]
+                self.backend_address_pools = [dict(
+                    name=backend_address_pool_name
+                )]
+                self.probes = [dict(
+                    name=prob_name,
                     port=self.probe_port,
-                    interval_in_seconds=self.probe_interval,
+                    protocol=self.probe_protocol,
+                    interval=self.probe_interval,
                     number_of_probes=self.probe_fail_count,
                     request_path=self.probe_request_path
-                )
-            ]
-
-        load_balancing_rule_name = random_name('lbr')
-        if self.protocol:
-            load_balancer_props['load_balancing_rules'] = [
-                self.network_models.LoadBalancingRule(
-                    name=load_balancing_rule_name,
-                    frontend_ip_configuration=self.network_models.SubResource(id=frontend_ip_config_id),
-                    backend_address_pool=self.network_models.SubResource(id=backend_addr_pool_id),
-                    probe=self.network_models.SubResource(id=prb_id),
-                    protocol=self.protocol,
-                    load_distribution=self.load_distribution,
-                    frontend_port=self.frontend_port,
-                    backend_port=self.backend_port,
-                    idle_timeout_in_minutes=self.idle_timeout,
-                    enable_floating_ip=False
-                )
-            ]
-
-        inbound_nat_pool_name = random_name('inp')
-        if frontend_ip_config_id and self.natpool_protocol:
-            load_balancer_props['inbound_nat_pools'] = [
-                self.network_models.InboundNatPool(
+                )] if self.probe_protocol else None
+                self.inbound_nat_pools = [dict(
                     name=inbound_nat_pool_name,
-                    frontend_ip_configuration=self.network_models.Subnet(id=frontend_ip_config_id),
+                    frontend_ip_configuration_name=frontend_ip_name,
                     protocol=self.natpool_protocol,
                     frontend_port_range_start=self.natpool_frontend_port_start,
                     frontend_port_range_end=self.natpool_frontend_port_end,
                     backend_port=self.natpool_backend_port
-                )
-            ]
+                )] if self.natpool_protocol else None
+                self.load_balancing_rules = [dict(
+                    name=lb_rule_name,
+                    frontend_ip_configuration=frontend_ip_name,
+                    backend_address_pool=backend_address_pool_name,
+                    probe=prob_name,
+                    protocol=self.protocol,
+                    load_distribution=self.load_distribution,
+                    frontend_port=self.frontend_port,
+                    backend_port=self.backend_port,
+                    idle_timeout=self.idle_timeout,
+                    enable_floating_ip=False
+                )] if self.protocol else None
+            if load_balancer:
+                # check update, NIE
+                changed = False
+            else:
+                changed = True
+        elif self.state == 'absent' and load_balancer:
+            changed = True
 
+        self.results['state'] = load_balancer_to_dict(load_balancer)
         self.results['changed'] = changed
-        self.results['state'] = (
-            results if results
-            else load_balancer_to_dict(self.network_models.LoadBalancer(**load_balancer_props))
-        )
 
-        if self.check_mode:
-            return self.results
+        if self.state == 'present' and changed:
+            # create or update
+            frontend_ip_configurations_param = [self.network_models.FrontendIPConfiguration(
+                name=item.get('name'),
+                public_ip_address=self.get_public_ip_address(item.get('public_ip_address'))
+            ) for item in self.frontend_ip_configurations] if self.frontend_ip_configurations else None
+
+            backend_address_pools_param = [self.network_models.BackendAddressPool(
+                name=item.get('name')
+            ) for item in self.backend_address_pools] if self.backend_address_pools else None
+
+            probes_param = [self.network_models.Probe(
+                name=item.get('name'),
+                port=item.get('port'),
+                protocol=item.get('protocol'),
+                interval_in_seconds=item.get('interval'),
+                request_path=item.get('request_path'),
+                number_of_probes=item.get('number_of_probes')
+            ) for item in self.probes] if self.probes else None
+
+            inbound_nat_pools_param = [self.network_models.InboundNatPool(
+                name=item.get('name'),
+                frontend_ip_configuration=self.network_models.SubResource(
+                    frontend_ip_configuration_id(
+                        self.subscription_id,
+                        self.resource_group,
+                        self.name,
+                        item.get('frontend_ip_configuration_name'))),
+                protocol=item.get('protocol'),
+                frontend_port_range_start=item.get('frontend_port_range_start'),
+                frontend_port_range_end=item.get('frontend_port_range_end'),
+                backend_port=item.get('backend_port')
+            ) for item in self.inbound_nat_pools] if self.inbound_nat_pools else None
+
+            load_balancing_rules_param = [self.network_models.LoadBalancingRule(
+                name=item.get('name'),
+                frontend_ip_configuration=self.network_models.SubResource(
+                    frontend_ip_configuration_id(
+                        self.subscription_id,
+                        self.resource_group,
+                        self.name,
+                        item.get('frontend_ip_configuration')
+                    )
+                ),
+                backend_address_pool=self.network_models.SubResource(
+                    backend_address_pool_id(
+                        self.subscription_id,
+                        self.resource_group,
+                        self.name,
+                        item.get('backend_address_pool')
+                    )
+                ),
+                probe=self.network_models.SubResource(
+                    probe_id(
+                        self.subscription_id,
+                        self.resource_group,
+                        self.name,
+                        item.get('probe')
+                    )
+                ),
+                protocol=item.get('protocol'),
+                load_distribution=item.get('load_distribution'),
+                frontend_port=item.get('frontend_port'),
+                backend_port=item.get('backend_port'),
+                idle_timeout_in_minutes=item.get('idle_timeout'),
+                enable_floating_ip=item.get('enable_floating_ip')
+            ) for item in self.load_balancing_rules] if self.load_balancing_rules else None
+
+            param = self.network_models.LoadBalancer(
+                location=self.location,
+                frontend_ip_configurations=frontend_ip_configurations_param,
+                backend_address_pools=backend_address_pools_param,
+                probes=probes_param,
+                inbound_nat_pools=inbound_nat_pools_param
+            )
 
-        try:
-            self.network_client.load_balancers.create_or_update(
-                resource_group_name=self.resource_group,
-                load_balancer_name=self.name,
-                parameters=self.network_models.LoadBalancer(**load_balancer_props)
-            ).wait()
-        except CloudError as err:
-            self.fail('Error creating load balancer {}'.format(err))
+            self.create_or_update_load_balancer(param)
+        elif self.state == 'absent' and changed:
+            self.delete_load_balancer()
+            self.results['state'] = None
 
         return self.results
 
-    def get_public_ip_address(self, name):
+    def get_public_ip_address(self, id):
         """Get a reference to the public ip address resource"""
-
-        self.log('Fetching public ip address {}'.format(name))
+        self.log('Fetching public ip address {}'.format(id))
+        pip_dict = parse_resource_id(id)
+        resource_group = pip_dict.get('resource_group', self.resource_group)
+        name = pip_dict.get('name')
         try:
-            public_ip = self.network_client.public_ip_addresses.get(self.resource_group, name)
+            return self.network_client.public_ip_addresses.get(resource_group, name)
         except CloudError as err:
             self.fail('Error fetching public ip address {} - {}'.format(name, str(err)))
-        return public_ip
+
+    def get_load_balancer(self):
+        """Get a load balancer"""
+        self.log('Fetching loadbalancer {0}'.format(self.name))
+        try:
+            return self.network_client.load_balancers.get(self.resource_group, self.name)
+        except CloudError:
+            return None
+
+    def delete_load_balancer(self):
+        """Delete a load balancer"""
+        self.log('Deleting loadbalancer {0}'.format(self.name))
+        try:
+            poller = self.network_client.load_balancers.delete(self.resource_group, self.name)
+            return self.get_poller_result(poller)
+        except CloudError as exc:
+            self.fail("Error deleting loadbalancer {0} - {1}".format(self.name, str(exc)))
+
+    def create_or_update_load_balancer(self, param):
+        try:
+            poller = self.network_client.load_balancers.create_or_update(self.resource_group, self.name, param)
+            new_lb = self.get_poller_result(poller)
+            return load_balancer_to_dict(new_lb)
+        except CloudError as exc:
+            self.fail("Error creating or updating load balancer {0} - {1}".format(self.name, str(exc)))
 
 
 def load_balancer_to_dict(load_balancer):
     """Seralialize a LoadBalancer object to a dict"""
+    if not load_balancer:
+        return dict()
 
     result = dict(
         id=load_balancer.id,
@@ -583,11 +911,6 @@ def load_balancer_to_dict(load_balancer):
     return result
 
 
-def random_name(prefix):
-    """Generate a random name with a specific prefix"""
-    return '{}{}'.format(prefix, random.randint(10000, 99999))
-
-
 def frontend_ip_configuration_id(subscription_id, resource_group_name, load_balancer_name, name):
     """Generate the id for a frontend ip configuration"""
     return '/subscriptions/{}/resourceGroups/{}/providers/Microsoft.Network/loadBalancers/{}/frontendIPConfigurations/{}'.format(
diff --git a/test/integration/targets/azure_rm_loadbalancer/tasks/main.yml b/test/integration/targets/azure_rm_loadbalancer/tasks/main.yml
index 91e4c3c3506732..c6575fda8e0229 100644
--- a/test/integration/targets/azure_rm_loadbalancer/tasks/main.yml
+++ b/test/integration/targets/azure_rm_loadbalancer/tasks/main.yml
@@ -3,6 +3,12 @@
     name: ansiblepip3
     resource_group: '{{ resource_group }}'
 
+- name: clear load balancer
+  azure_rm_loadbalancer:
+    resource_group: '{{ resource_group }}'
+    name: lbtestfromansible
+    state: absent
+
 - name: create load balancer
   azure_rm_loadbalancer:
     resource_group: '{{ resource_group }}'
@@ -25,6 +31,17 @@
   assert:
     that: output.changed
 
+- name: delete load balancer (idempotent)
+  azure_rm_loadbalancer:
+    resource_group: '{{ resource_group }}'
+    name: lbtestfromansible
+    state: absent
+  register: output
+
+- name: assert load balancer deleted (idempotent)
+  assert:
+    that: not output.changed
+
 - name: create another load balancer with more options
   azure_rm_loadbalancer:
     resource_group: '{{ resource_group }}'
@@ -55,6 +72,41 @@
     name: lbtestfromansible
     state: absent
 
+- name: create load balancer with mutilple parameters
+  azure_rm_loadbalancer:
+    resource_group: '{{ resource_group }}'
+    name: lbtestfromansible
+    frontend_ip_configurations:
+      - name: frontendipconf0
+        public_ip_address: ansiblepip3
+    backend_address_pools:
+      - name: backendaddrpool0
+    probes:
+      - name: prob0
+        port: 80
+    inbound_nat_pools:
+      - name: inboundnatpool0
+        frontend_ip_configuration_name: frontendipconf0
+        protocol: Tcp
+        frontend_port_range_start: 80
+        frontend_port_range_end: 81
+        backend_port: 8080
+    load_balancing_rules:
+      - name: lbrbalancingrule0
+        frontend_ip_configuration: frontendipconf0
+        backend_address_pool: backendaddrpool0
+        probe: prob0
+
+- name: assert complex load balancer created
+  assert:
+    that: output.changed
+
+- name: delete load balancer
+  azure_rm_loadbalancer:
+    resource_group: '{{ resource_group }}'
+    name: lbtestfromansible
+    state: absent
+
 - name: cleanup public ip
   azure_rm_publicipaddress:
     name: ansiblepip3


Code review comments:
diff_hunk: @@ -61,89 +61,248 @@
             - Valid azure location. Defaults to location of the resource group.
         default: resource_group location
         required: false
+    frontend_ip_configurations:
+        description: Object representing the frontend IPs to be used for the load balancer.
+        suboptions:
+            name:
+                description: name of the frontend ip configurations.
+                required: True
+            public_ip_address:
+                description: Name of an existing public IP address object to associate with the security group.
+                required: True
+        version_added: 2.5
+    backend_address_pools:
+        description: Collection of backend address pools used by a load balancer.
+        suboptions:
+            name:
+                description: name of the backend address pool.
+                required: True
+        version_added: 2.5
+    probes:
+        description: Collection of probe objects used in the load balancer.
+        suboptions:
+            name:
+                description: name of the probe.
+                required: True
+            port:
+                description: The port for communicating the probe. Possible values range from 1 to 65535, inclusive.
+                required: True
+            protocol:
+                description:
+                    - The protocol of the end point.
+                    - "Possible values are: 'Http' or 'Tcp'."
+                    - If 'Tcp' is specified, a received ACK is required for the probe to be successful.
+                    - If 'Http' is specified, a 200 OK response from the specifies URI is required for the probe to be successful.
+                choices:
+                    - Tcp
+                    - Http
+                default: Tcp
+            interval:
+                description:
+                    - The interval, in seconds, for how frequently to probe the endpoint for health status.
+                    - Slightly less than half the allocated timeout period, which allows two full probes before taking the instance out of rotation.
+                    - The default value is 15, the minimum value is 5.
+                default: 15
+            number_of_probes:
+                description:
+                    - The number of probes where if no response, will result in stopping further traffic from being delivered to the endpoint.
+                    - This values allows endpoints to be taken out of rotation faster or slower than the typical times used in Azure.
+                default: 3
+                aliases:
+                    - fail_count
+            request_path:
+                description:
+                    - The URI used for requesting health status from the VM.
+                    - Path is required if a protocol is set to http. Otherwise, it is not allowed.
+        version_added: 2.5
+    inbound_nat_pools:
+        description:
+            - Defines an external port range for inbound NAT to a single backend port on NICs associated with a load balancer.
+            - Inbound NAT rules are created automatically for each NIC associated with the Load Balancer using an external port from this range.
+            - Defining an Inbound NAT pool on your Load Balancer is mutually exclusive with defining inbound Nat rules.
+            - Inbound NAT pools are referenced from virtual machine scale sets.
+            - NICs that are associated with individual virtual machines cannot reference an inbound NAT pool.
+            - They have to reference individual inbound NAT rules.
+        suboptions:
+            name:
+                description: name of the inbound nat pool.
+                required: True
+            frontend_ip_configuration_name:
+                description: A reference to frontend IP addresses.
+                required: True
+            protocol:
+                description: "Possible values include: 'Udp', 'Tcp', 'All'."
+                choices:
+                    - Tcp
+                    - Udp
+                    - All
+                default: Tcp
+            frontend_port_range_start:
+                description:
+                    - The first port number in the range of external ports that will be used to provide Inbound Nat to NICs associated with a load balancer.
+                    - Acceptable values range between 1 and 65534.
+                required: True
+            frontend_port_range_end:
+                description:
+                    - The last port number in the range of external ports that will be used to provide Inbound Nat to NICs associated with a load balancer.
+                    - Acceptable values range between 1 and 65535.
+                required: True
+            backend_port:
+                description:
+                    - The port used for internal connections on the endpoint.
+                    - Acceptable values are between 1 and 65535.
+        version_added: 2.5
+    load_balancing_rules:
+        description:
+            - Object collection representing the load balancing rules Gets the provisioning.
+        suboptions:
+            name:
+                description: name of the load balancing rule.
+                required: True
+            frontend_ip_configuration:
+                description: A reference to frontend IP addresses.
+                required: True
+            backend_address_pool:
+                description: A reference to a pool of DIPs. Inbound traffic is randomly load balanced across IPs in the backend IPs.
+                required: True
+            probe:
+                description: The reference of the load balancer probe used by the load balancing rule.
+                required: True
+            protocol:
+                description: "Possible values include: 'Udp', 'Tcp', 'All'."
+                choices:
+                    - Tcp
+                    - Udp
+                    - All
path: lib/ansible/modules/cloud/azure/azure_rm_loadbalancer.py
text: @user1:
also the recent rule is to have choice values in snake_lower_case format

@author:
These are strings accepted by SDK.