docs: def security_rule_present(name, access, direction, priority, protocol, security_group, resource_group,
                            destination_address_prefix=None, destination_port_range=None, source_address_prefix=None,
                            source_port_range=None, description=None, destination_address_prefixes=None,
                            destination_port_ranges=None, source_address_prefixes=None, source_port_ranges=None,
                            connection_auth=None, **kwargs):
      '''
      .. versionadded:: 2019.2.0
  
      Ensure a security rule exists.
  
      :param name:
          Name of the security rule.
  
      :param access:
          'allow' or 'deny'
  
      :param direction:
          'inbound' or 'outbound'
  
      :param priority:
          Integer between 100 and 4096 used for ordering rule application.
  
      :param protocol:
          'tcp', 'udp', or '*'
  
      :param security_group:
          The name of the existing network security group to contain the security rule.
  
      :param resource_group:
          The resource group assigned to the network security group.
  
      :param description:
          Optional description of the security rule.
  
      :param destination_address_prefix:
          The CIDR or destination IP range. Asterix '*' can also be used to match all destination IPs.
          Default tags such as 'VirtualNetwork', 'AzureLoadBalancer' and 'Internet' can also be used.
          If this is an ingress rule, specifies where network traffic originates from.
  
      :param destination_port_range:
          The destination port or range. Integer or range between 0 and 65535. Asterix '*'
          can also be used to match all ports.
  
      :param source_address_prefix:
          The CIDR or source IP range. Asterix '*' can also be used to match all source IPs.
          Default tags such as 'VirtualNetwork', 'AzureLoadBalancer' and 'Internet' can also be used.
          If this is an ingress rule, specifies where network traffic originates from.
  
      :param source_port_range:
          The source port or range. Integer or range between 0 and 65535. Asterix '*'
          can also be used to match all ports.
  
      :param destination_address_prefixes:
          A list of destination_address_prefix values. This parameter overrides destination_address_prefix
          and will cause any value entered there to be ignored.
  
      :param destination_port_ranges:
          A list of destination_port_range values. This parameter overrides destination_port_range
          and will cause any value entered there to be ignored.
  
      :param source_address_prefixes:
          A list of source_address_prefix values. This parameter overrides source_address_prefix
          and will cause any value entered there to be ignored.
  
      :param source_port_ranges:
          A list of source_port_range values. This parameter overrides source_port_range
          and will cause any value entered there to be ignored.
  
      :param connection_auth:
          A dict with subscription and authentication parameters to be used in connecting to the
          Azure Resource Manager API.
  
      Example usage:
  
      .. code-block:: yaml
  
          Ensure security rule exists:
              azurearm_network.security_rule_present:
                  - name: nsg1_rule2
                  - security_group: nsg1
                  - resource_group: group1
                  - priority: 101
                  - protocol: tcp
                  - access: allow
                  - direction: inbound
                  - source_address_prefix: internet
                  - destination_address_prefix: virtualnetwork
                  - source_port_range: '*'
                  - destination_port_ranges:
                    - '80'
                    - '443'
                  - connection_auth: {{ profile }}
                  - require:
                    - azurearm_network: Ensure network security group exists
  
      '''
      ret = {
          'name': name,
          'result': False,
          'comment': '',
          'changes': {}
      }
  
      if not isinstance(connection_auth, dict):
          ret['comment'] = 'Connection information must be specified via connection_auth dictionary!'
          return ret
  
      exclusive_params = [
          ('source_port_ranges', 'source_port_range'),
          ('source_address_prefixes', 'source_address_prefix'),
          ('destination_port_ranges', 'destination_port_range'),
          ('destination_address_prefixes', 'destination_address_prefix'),
      ]
  
      for params in exclusive_params:
          # pylint: disable=eval-used
          if not eval(params[0]) and not eval(params[1]):
              ret['comment'] = 'Either the {0} or {1} parameter must be provided!'.format(params[0], params[1])
              return ret
          # pylint: disable=eval-used
          if eval(params[0]):
              # pylint: disable=eval-used
              if not isinstance(eval(params[0]), list):
                  ret['comment'] = 'The {0} parameter must be a list!'.format(params[0])
                  return ret
              # pylint: disable=exec-used
              exec('{0} = None'.format(params[1]))
  
      rule = __salt__['azurearm_network.security_rule_get'](
          name,
          security_group,
          resource_group,
          azurearm_log_level='info',
          **connection_auth
      )
  
      if 'error' not in rule:
          # access changes
          if access.capitalize() != rule.get('access'):
              ret['changes']['access'] = {
                  'old': rule.get('access'),
                  'new': access
              }
  
          # description changes
          if description != rule.get('description'):
              ret['changes']['description'] = {
                  'old': rule.get('description'),
                  'new': description
              }
  
          # direction changes
          if direction.capitalize() != rule.get('direction'):
              ret['changes']['direction'] = {
                  'old': rule.get('direction'),
                  'new': direction
              }
  
          # priority changes
          if int(priority) != rule.get('priority'):
              ret['changes']['priority'] = {
                  'old': rule.get('priority'),
                  'new': priority
              }
  
          # protocol changes
          if protocol.lower() != rule.get('protocol', '').lower():
              ret['changes']['protocol'] = {
                  'old': rule.get('protocol'),
                  'new': protocol
              }
  
          # destination_port_range changes
          if destination_port_range != rule.get('destination_port_range'):
              ret['changes']['destination_port_range'] = {
                  'old': rule.get('destination_port_range'),
                  'new': destination_port_range
              }
  
          # source_port_range changes
          if source_port_range != rule.get('source_port_range'):
              ret['changes']['source_port_range'] = {
                  'old': rule.get('source_port_range'),
                  'new': source_port_range
              }
  
          # destination_port_ranges changes
          if sorted(destination_port_ranges or []) != sorted(rule.get('destination_port_ranges', [])):
              ret['changes']['destination_port_ranges'] = {
                  'old': rule.get('destination_port_ranges'),
                  'new': destination_port_ranges
              }
  
          # source_port_ranges changes
          if sorted(source_port_ranges or []) != sorted(rule.get('source_port_ranges', [])):
              ret['changes']['source_port_ranges'] = {
                  'old': rule.get('source_port_ranges'),
                  'new': source_port_ranges
              }
  
          # destination_address_prefix changes
          if (destination_address_prefix or '').lower() != rule.get('destination_address_prefix', '').lower():
              ret['changes']['destination_address_prefix'] = {
                  'old': rule.get('destination_address_prefix'),
                  'new': destination_address_prefix
              }
  
          # source_address_prefix changes
          if (source_address_prefix or '').lower() != rule.get('source_address_prefix', '').lower():
              ret['changes']['source_address_prefix'] = {
                  'old': rule.get('source_address_prefix'),
                  'new': source_address_prefix
              }
  
          # destination_address_prefixes changes
          if sorted(destination_address_prefixes or []) != sorted(rule.get('destination_address_prefixes', [])):
              if len(destination_address_prefixes or []) != len(rule.get('destination_address_prefixes', [])):
                  ret['changes']['destination_address_prefixes'] = {
                      'old': rule.get('destination_address_prefixes'),
                      'new': destination_address_prefixes
                  }
              else:
                  local_dst_addrs, remote_dst_addrs = (sorted(destination_address_prefixes),
                                                       sorted(rule.get('destination_address_prefixes')))
                  for idx in six_range(0, len(local_dst_addrs)):
                      if local_dst_addrs[idx].lower() != remote_dst_addrs[idx].lower():
                          ret['changes']['destination_address_prefixes'] = {
                              'old': rule.get('destination_address_prefixes'),
                              'new': destination_address_prefixes
                          }
                          break
  
          # source_address_prefixes changes
          if sorted(source_address_prefixes or []) != sorted(rule.get('source_address_prefixes', [])):
              if len(source_address_prefixes or []) != len(rule.get('source_address_prefixes', [])):
                  ret['changes']['source_address_prefixes'] = {
                      'old': rule.get('source_address_prefixes'),
                      'new': source_address_prefixes
                  }
              else:
                  local_src_addrs, remote_src_addrs = (sorted(source_address_prefixes),
                                                       sorted(rule.get('source_address_prefixes')))
                  for idx in six_range(0, len(local_src_addrs)):
                      if local_src_addrs[idx].lower() != remote_src_addrs[idx].lower():
                          ret['changes']['source_address_prefixes'] = {
                              'old': rule.get('source_address_prefixes'),
                              'new': source_address_prefixes
                          }
                          break
  
          if not ret['changes']:
              ret['result'] = True
              ret['comment'] = 'Security rule {0} is already present.'.format(name)
              return ret
  
          if __opts__['test']:
              ret['result'] = None
              ret['comment'] = 'Security rule {0} would be updated.'.format(name)
              return ret
  
      else:
          ret['changes'] = {
              'old': {},
              'new': {
                  'name': name,
                  'access': access,
                  'description': description,
                  'direction': direction,
                  'priority': priority,
                  'protocol': protocol,
                  'destination_address_prefix': destination_address_prefix,
                  'destination_address_prefixes': destination_address_prefixes,
                  'destination_port_range': destination_port_range,
                  'destination_port_ranges': destination_port_ranges,
                  'source_address_prefix': source_address_prefix,
                  'source_address_prefixes': source_address_prefixes,
                  'source_port_range': source_port_range,
                  'source_port_ranges': source_port_ranges,
              }
          }
  
      if __opts__['test']:
          ret['comment'] = 'Security rule {0} would be created.'.format(name)
          ret['result'] = None
          return ret
  
      rule_kwargs = kwargs.copy()
      rule_kwargs.update(connection_auth)
  
      rule = __salt__['azurearm_network.security_rule_create_or_update'](
          name=name,
          access=access,
          description=description,
          direction=direction,
          priority=priority,
          protocol=protocol,
          security_group=security_group,
          resource_group=resource_group,
          destination_address_prefix=destination_address_prefix,
          destination_address_prefixes=destination_address_prefixes,
          destination_port_range=destination_port_range,
          destination_port_ranges=destination_port_ranges,
          source_address_prefix=source_address_prefix,
          source_address_prefixes=source_address_prefixes,
          source_port_range=source_port_range,
          source_port_ranges=source_port_ranges,
          **rule_kwargs
      )
  
      if 'error' not in rule:
          ret['result'] = True
          ret['comment'] = 'Security rule {0} has been created.'.format(name)
          return ret
  
      ret['comment'] = 'Failed to create security rule {0}! ({1})'.format(name, rule.get('error'))
      return ret