Repository: ansible/ansible

Issue:
azure_rm_securitygroup only adds a single rule when run in a loop
<!---
Verify first that your issue/request is not already reported on GitHub.
THIS FORM WILL BE READ BY A MACHINE, COMPLETE ALL SECTIONS AS DESCRIBED.
Also test if the latest release, and devel branch are affected too.
ALWAYS add information AFTER (OUTSIDE) these html comments.
Otherwise it may end up being automatically closed by our bot. -->

##### SUMMARY
<!--- Explain the problem briefly -->
When I try to create an Azure security group, only a single rule ends up being registered in Azure.

If `purge_rules` is enabled only last rule will show up in the Azure security group.
If `purge_rules` is not enabled only first rule will show up in the Azure security group.

##### ISSUE TYPE
 - Bug Report

##### COMPONENT NAME
<!--- Insert, BELOW THIS COMMENT, the name of the module, plugin, task or feature.
Do not include extra details here, e.g. "vyos_command" not "the network module vyos_command" or the full path-->
- azure_rm_securitygroup

##### ANSIBLE VERSION
<!--- Paste, BELOW THIS COMMENT, verbatim output from "ansible --version" between quotes below -->
```
ansible 2.6.2
  config file = /home/pedro/Projects/github.com/ansible-azure-platform/ansible.cfg
  configured module search path = [u'/opt/ansible/ansible-azure-platform/library']
  ansible python module location = /usr/local/lib/python2.7/dist-packages/ansible
  executable location = /usr/local/bin/ansible
  python version = 2.7.12 (default, Dec  4 2017, 14:50:18) [GCC 5.4.0 20160609]
```

##### CONFIGURATION
<!--- If using Ansible 2.4 or above, paste, BELOW THIS COMMENT, the results of "ansible-config dump --only-changed"
Otherwise, mention any settings you have changed/added/removed in ansible.cfg
(or using the ANSIBLE_* environment variables).-->

```
Name: azure
Version: 3.0.0
Summary: Microsoft Azure Client Libraries for Python
Home-page: https://github.com/Azure/azure-sdk-for-python
Author: Microsoft Corporation
Author-email: azpysdkhelp@microsoft.com
License: MIT License
Location: /usr/local/lib/python2.7/dist-packages
Requires: azure-eventgrid, azure-cosmosdb-table, azure-mgmt, azure-storage-queue, azure-storage-file, azure-servicebus, azure-batch, azure-datalake-store, azure-storage-blob, azure-keyvault, azure-graphrbac, azure-servicemanagement-legacy, azure-servicefabric
Required-by:
```
```
azure-cli (2.0.43)

acr (2.1.2)
acs (2.2.2)
advisor (0.6.0)
ams (0.2.1)
appservice (0.2.1)
backup (1.2.0)
batch (3.3.1)
batchai (0.4.0)
billing (0.2.0)
cdn (0.1.0)
cloud (2.1.0)
cognitiveservices (0.2.0)
command-modules-nspkg (2.0.2)
configure (2.0.18)
consumption (0.4.0)
container (0.3.2)
core (2.0.35)
cosmosdb (0.2.0)
dla (0.2.0)
dls (0.1.0)
dms (0.1.0)
eventgrid (0.2.0)
eventhubs (0.2.1)
extension (0.2.1)
feedback (2.1.4)
find (0.2.12)
interactive (0.3.27)
iot (0.2.0)
keyvault (2.2.1)
lab (0.1.0)
monitor (0.2.1)
network (2.2.2)
nspkg (3.0.2)
profile (2.1.1)
rdbms (0.3.0)
redis (0.3.0)
reservations (0.3.1)
resource (2.1.1)
role (2.1.2)
servicebus (0.2.1)
servicefabric (0.1.0)
sql (2.1.1)
storage (2.1.1)
vm (2.2.0)

Python location '/usr/bin/python'
Extensions directory '/home/pedro/.azure/cliextensions'

Python (Linux) 2.7.12 (default, Dec  4 2017, 14:50:18) 
[GCC 5.4.0 20160609]

Legal docs and information: aka.ms/AzureCliLegal
```


##### OS / ENVIRONMENT
<!--- Mention, BELOW THIS COMMENT, the OS you are running Ansible from, and the OS you are
managing, or say "N/A" for anything that is not platform-specific.
Also mention the specific version of what you are trying to control,
e.g. if this is a network bug the version of firmware on the network device.-->
- Ubuntu 16.04.5 LTS

##### STEPS TO REPRODUCE
<!--- For bugs, show exactly how to reproduce the problem, using a minimal test-case.
For new features, show how the feature would be used. -->

<!--- Paste example playbooks or commands between quotes below -->
```yaml
- hosts: localhost
  connection: local
  gather_facts: no

  tasks:
    - name: create security group
      azure_rm_securitygroup:
        name: nsg-prd-api-westeurope
        resource_group: production-westeurope
        purge_default_rules: no
        purge_rules: yes
        rules:
          - name: "{{ item.name }}"
            protocol: "{{ item.protocol }}"
            destination_port_range: "{{ item.destination_port_range }}"
            access: "{{ item.access }}"
            priority: "{{ item.priority }}"
            direction: "{{ item.direction }}"
        tags:
          env: production
      with_items:
        - {"name": "AllowSSH", "protocol": "Tcp", "destination_port_range": "22", "access": "Allow", "priority": 100, "direction": "Inbound"}
        - {"name": "AllowFTP", "protocol": "Tcp", "destination_port_range": "21", "access": "Allow", "priority": 101, "direction": "Inbound"}
        - {"name": "AllowHTTP", "protocol": "Tcp", "destination_port_range": "80", "access": "Allow", "priority": 102, "direction": "Inbound"}

```

<!--- You can also paste gist.github.com links for larger files -->

##### EXPECTED RESULTS
<!--- What did you expect to happen when running the steps above? -->
Create all rules in security group.

##### ACTUAL RESULTS
<!--- What actually happened? If possible run with extra verbosity (-vvvv) -->

<!--- Paste verbatim command output between quotes below -->
Only last rule ends up in security group when `purge_rules` is enabled.

```
ansible-playbook -c local tmp.yml -vvvv
ansible-playbook 2.6.2
  config file = /etc/ansible/ansible.cfg
  configured module search path = [u'/home/pedro/.ansible/plugins/modules', u'/usr/share/ansible/plugins/modules']
  ansible python module location = /usr/local/lib/python2.7/dist-packages/ansible
  executable location = /usr/local/bin/ansible-playbook
  python version = 2.7.12 (default, Dec  4 2017, 14:50:18) [GCC 5.4.0 20160609]
Using /etc/ansible/ansible.cfg as config file
setting up inventory plugins
Parsed /etc/ansible/hosts inventory source with ini plugin
 [WARNING]: provided hosts list is empty, only localhost is available. Note that the implicit localhost does not match 'all'

Loading callback plugin yaml of type stdout, v2.0 from /usr/local/lib/python2.7/dist-packages/ansible/plugins/callback/yaml.pyc
Loading callback plugin profile_tasks of type aggregate, v2.0 from /usr/local/lib/python2.7/dist-packages/ansible/plugins/callback/profile_tasks.pyc
Loading callback plugin timer of type aggregate, v2.0 from /usr/local/lib/python2.7/dist-packages/ansible/plugins/callback/timer.pyc

PLAYBOOK: tmp.yml *************************************************************************************************************************************************************************************************
1 plays in tmp.yml

PLAY [localhost] **************************************************************************************************************************************************************************************************
META: ran handlers

TASK [create security group] **************************************************************************************************************************************************************************************
task path: /home/pedro/Projects/github.com/ansible-azure-platform/inventory/production/playbooks/tmp.yml:6
Thursday 02 August 2018  15:23:35 +0200 (0:00:00.047)       0:00:00.047 ******* 
Using module file /usr/local/lib/python2.7/dist-packages/ansible/modules/cloud/azure/azure_rm_securitygroup.py
<127.0.0.1> ESTABLISH LOCAL CONNECTION FOR USER: pedro
<127.0.0.1> EXEC /bin/sh -c '/usr/bin/python && sleep 0'
Using module file /usr/local/lib/python2.7/dist-packages/ansible/modules/cloud/azure/azure_rm_securitygroup.py
<127.0.0.1> EXEC /bin/sh -c '/usr/bin/python && sleep 0'
changed: [localhost] => (item={u'priority': 100, u'direction': u'Inbound', u'protocol': u'Tcp', u'name': u'AllowSSH', u'access': u'Allow', u'destination_port_range': u'22'}) => changed=true 
  invocation:
    module_args:
      ad_user: null
      adfs_authority_url: null
      api_profile: latest
      append_tags: true
      auth_source: auto
      cert_validation_mode: null
      client_id: null
      cloud_environment: AzureCloud
      default_rules: null
      location: null
      name: nsg-prd-api-westeurope
      password: null
      profile: null
      purge_default_rules: false
      purge_rules: true
      resource_group: production-westeurope
      rules:
      - access: Allow
        description: null
        destination_address_prefix: '*'
        destination_port_range: '22'
        direction: Inbound
        name: AllowSSH
        priority: 100
        protocol: Tcp
        source_address_prefix: '*'
        source_port_range: '*'
      secret: null
      state: present
      subscription_id: null
      tags:
        env: production
      tenant: null
  item:
    access: Allow
    destination_port_range: '22'
    direction: Inbound
    name: AllowSSH
    priority: 100
    protocol: Tcp
  state:
    default_rules:
    - access: Allow
      description: Allow inbound traffic from all VMs in VNET
      destination_address_prefix: VirtualNetwork
      destination_address_prefixes: []
      destination_port_range: '*'
      destination_port_ranges: []
      direction: Inbound
      etag: W/"ID"
      id: /subscriptions/ID/resourceGroups/production-westeurope/providers/Microsoft.Network/networkSecurityGroups/nsg-prd-api-westeurope/defaultSecurityRules/AllowVnetInBound
      name: AllowVnetInBound
      priority: 65000
      protocol: '*'
      provisioning_state: Succeeded
      source_address_prefix: VirtualNetwork
      source_address_prefixes: []
      source_port_range: '*'
      source_port_ranges: []
    - access: Allow
      description: Allow inbound traffic from azure load balancer
      destination_address_prefix: '*'
      destination_address_prefixes: []
      destination_port_range: '*'
      destination_port_ranges: []
      direction: Inbound
      etag: W/"ID"
      id: /subscriptions/ID/resourceGroups/production-westeurope/providers/Microsoft.Network/networkSecurityGroups/nsg-prd-api-westeurope/defaultSecurityRules/AllowAzureLoadBalancerInBound
      name: AllowAzureLoadBalancerInBound
      priority: 65001
      protocol: '*'
      provisioning_state: Succeeded
      source_address_prefix: AzureLoadBalancer
      source_address_prefixes: []
      source_port_range: '*'
      source_port_ranges: []
    - access: Deny
      description: Deny all inbound traffic
      destination_address_prefix: '*'
      destination_address_prefixes: []
      destination_port_range: '*'
      destination_port_ranges: []
      direction: Inbound
      etag: W/"ID"
      id: /subscriptions/ID/resourceGroups/production-westeurope/providers/Microsoft.Network/networkSecurityGroups/nsg-prd-api-westeurope/defaultSecurityRules/DenyAllInBound
      name: DenyAllInBound
      priority: 65500
      protocol: '*'
      provisioning_state: Succeeded
      source_address_prefix: '*'
      source_address_prefixes: []
      source_port_range: '*'
      source_port_ranges: []
    - access: Allow
      description: Allow outbound traffic from all VMs to all VMs in VNET
      destination_address_prefix: VirtualNetwork
      destination_address_prefixes: []
      destination_port_range: '*'
      destination_port_ranges: []
      direction: Outbound
      etag: W/"ID"
      id: /subscriptions/ID/resourceGroups/production-westeurope/providers/Microsoft.Network/networkSecurityGroups/nsg-prd-api-westeurope/defaultSecurityRules/AllowVnetOutBound
      name: AllowVnetOutBound
      priority: 65000
      protocol: '*'
      provisioning_state: Succeeded
      source_address_prefix: VirtualNetwork
      source_address_prefixes: []
      source_port_range: '*'
      source_port_ranges: []
    - access: Allow
      description: Allow outbound traffic from all VMs to Internet
      destination_address_prefix: Internet
      destination_address_prefixes: []
      destination_port_range: '*'
      destination_port_ranges: []
      direction: Outbound
      etag: W/"ID"
      id: /subscriptions/ID/resourceGroups/production-westeurope/providers/Microsoft.Network/networkSecurityGroups/nsg-prd-api-westeurope/defaultSecurityRules/AllowInternetOutBound
      name: AllowInternetOutBound
      priority: 65001
      protocol: '*'
      provisioning_state: Succeeded
      source_address_prefix: '*'
      source_address_prefixes: []
      source_port_range: '*'
      source_port_ranges: []
    - access: Deny
      description: Deny all outbound traffic
      destination_address_prefix: '*'
      destination_address_prefixes: []
      destination_port_range: '*'
      destination_port_ranges: []
      direction: Outbound
      etag: W/"ID"
      id: /subscriptions/ID/resourceGroups/production-westeurope/providers/Microsoft.Network/networkSecurityGroups/nsg-prd-api-westeurope/defaultSecurityRules/DenyAllOutBound
      name: DenyAllOutBound
      priority: 65500
      protocol: '*'
      provisioning_state: Succeeded
      source_address_prefix: '*'
      source_address_prefixes: []
      source_port_range: '*'
      source_port_ranges: []
    id: /subscriptions/ID/resourceGroups/production-westeurope/providers/Microsoft.Network/networkSecurityGroups/nsg-prd-api-westeurope
    location: westeurope
    name: nsg-prd-api-westeurope
    network_interfaces: []
    rules:
    - access: Allow
      description: null
      destination_address_prefix: '*'
      destination_address_prefixes: []
      destination_port_range: '22'
      destination_port_ranges: []
      direction: Inbound
      etag: W/"ID"
      id: /subscriptions/ID/resourceGroups/production-westeurope/providers/Microsoft.Network/networkSecurityGroups/nsg-prd-api-westeurope/securityRules/AllowSSH
      name: AllowSSH
      priority: 100
      protocol: Tcp
      provisioning_state: Succeeded
      source_address_prefix: '*'
      source_address_prefixes: []
      source_port_range: '*'
      source_port_ranges: []
    subnets: []
    tags:
      env: production
    type: Microsoft.Network/networkSecurityGroups
Using module file /usr/local/lib/python2.7/dist-packages/ansible/modules/cloud/azure/azure_rm_securitygroup.py
<127.0.0.1> EXEC /bin/sh -c '/usr/bin/python && sleep 0'
changed: [localhost] => (item={u'priority': 101, u'direction': u'Inbound', u'protocol': u'Tcp', u'name': u'AllowFTP', u'access': u'Allow', u'destination_port_range': u'21'}) => changed=true 
  invocation:
    module_args:
      ad_user: null
      adfs_authority_url: null
      api_profile: latest
      append_tags: true
      auth_source: auto
      cert_validation_mode: null
      client_id: null
      cloud_environment: AzureCloud
      default_rules: null
      location: null
      name: nsg-prd-api-westeurope
      password: null
      profile: null
      purge_default_rules: false
      purge_rules: true
      resource_group: production-westeurope
      rules:
      - access: Allow
        description: null
        destination_address_prefix: '*'
        destination_port_range: '21'
        direction: Inbound
        name: AllowFTP
        priority: 101
        protocol: Tcp
        source_address_prefix: '*'
        source_port_range: '*'
      secret: null
      state: present
      subscription_id: null
      tags:
        env: production
      tenant: null
  item:
    access: Allow
    destination_port_range: '21'
    direction: Inbound
    name: AllowFTP
    priority: 101
    protocol: Tcp
  state:
    default_rules:
    - access: Allow
      description: Allow inbound traffic from all VMs in VNET
      destination_address_prefix: VirtualNetwork
      destination_address_prefixes: []
      destination_port_range: '*'
      destination_port_ranges: []
      direction: Inbound
      etag: W/"ID"
      id: /subscriptions/ID/resourceGroups/production-westeurope/providers/Microsoft.Network/networkSecurityGroups/nsg-prd-api-westeurope/defaultSecurityRules/AllowVnetInBound
      name: AllowVnetInBound
      priority: 65000
      protocol: '*'
      provisioning_state: Succeeded
      source_address_prefix: VirtualNetwork
      source_address_prefixes: []
      source_port_range: '*'
      source_port_ranges: []
    - access: Allow
      description: Allow inbound traffic from azure load balancer
      destination_address_prefix: '*'
      destination_address_prefixes: []
      destination_port_range: '*'
      destination_port_ranges: []
      direction: Inbound
      etag: W/"ID"
      id: /subscriptions/ID/resourceGroups/production-westeurope/providers/Microsoft.Network/networkSecurityGroups/nsg-prd-api-westeurope/defaultSecurityRules/AllowAzureLoadBalancerInBound
      name: AllowAzureLoadBalancerInBound
      priority: 65001
      protocol: '*'
      provisioning_state: Succeeded
      source_address_prefix: AzureLoadBalancer
      source_address_prefixes: []
      source_port_range: '*'
      source_port_ranges: []
    - access: Deny
      description: Deny all inbound traffic
      destination_address_prefix: '*'
      destination_address_prefixes: []
      destination_port_range: '*'
      destination_port_ranges: []
      direction: Inbound
      etag: W/"ID"
      id: /subscriptions/ID/resourceGroups/production-westeurope/providers/Microsoft.Network/networkSecurityGroups/nsg-prd-api-westeurope/defaultSecurityRules/DenyAllInBound
      name: DenyAllInBound
      priority: 65500
      protocol: '*'
      provisioning_state: Succeeded
      source_address_prefix: '*'
      source_address_prefixes: []
      source_port_range: '*'
      source_port_ranges: []
    - access: Allow
      description: Allow outbound traffic from all VMs to all VMs in VNET
      destination_address_prefix: VirtualNetwork
      destination_address_prefixes: []
      destination_port_range: '*'
      destination_port_ranges: []
      direction: Outbound
      etag: W/"ID"
      id: /subscriptions/ID/resourceGroups/production-westeurope/providers/Microsoft.Network/networkSecurityGroups/nsg-prd-api-westeurope/defaultSecurityRules/AllowVnetOutBound
      name: AllowVnetOutBound
      priority: 65000
      protocol: '*'
      provisioning_state: Succeeded
      source_address_prefix: VirtualNetwork
      source_address_prefixes: []
      source_port_range: '*'
      source_port_ranges: []
    - access: Allow
      description: Allow outbound traffic from all VMs to Internet
      destination_address_prefix: Internet
      destination_address_prefixes: []
      destination_port_range: '*'
      destination_port_ranges: []
      direction: Outbound
      etag: W/"ID"
      id: /subscriptions/ID/resourceGroups/production-westeurope/providers/Microsoft.Network/networkSecurityGroups/nsg-prd-api-westeurope/defaultSecurityRules/AllowInternetOutBound
      name: AllowInternetOutBound
      priority: 65001
      protocol: '*'
      provisioning_state: Succeeded
      source_address_prefix: '*'
      source_address_prefixes: []
      source_port_range: '*'
      source_port_ranges: []
    - access: Deny
      description: Deny all outbound traffic
      destination_address_prefix: '*'
      destination_address_prefixes: []
      destination_port_range: '*'
      destination_port_ranges: []
      direction: Outbound
      etag: W/"ID"
      id: /subscriptions/ID/resourceGroups/production-westeurope/providers/Microsoft.Network/networkSecurityGroups/nsg-prd-api-westeurope/defaultSecurityRules/DenyAllOutBound
      name: DenyAllOutBound
      priority: 65500
      protocol: '*'
      provisioning_state: Succeeded
      source_address_prefix: '*'
      source_address_prefixes: []
      source_port_range: '*'
      source_port_ranges: []
    id: /subscriptions/ID/resourceGroups/production-westeurope/providers/Microsoft.Network/networkSecurityGroups/nsg-prd-api-westeurope
    location: westeurope
    name: nsg-prd-api-westeurope
    network_interfaces: []
    rules:
    - access: Allow
      description: null
      destination_address_prefix: '*'
      destination_address_prefixes: []
      destination_port_range: '21'
      destination_port_ranges: []
      direction: Inbound
      etag: W/"ID"
      id: /subscriptions/ID/resourceGroups/production-westeurope/providers/Microsoft.Network/networkSecurityGroups/nsg-prd-api-westeurope/securityRules/AllowFTP
      name: AllowFTP
      priority: 101
      protocol: Tcp
      provisioning_state: Succeeded
      source_address_prefix: '*'
      source_address_prefixes: []
      source_port_range: '*'
      source_port_ranges: []
    subnets: []
    tags:
      env: production
    type: Microsoft.Network/networkSecurityGroups
changed: [localhost] => (item={u'priority': 102, u'direction': u'Inbound', u'protocol': u'Tcp', u'name': u'AllowHTTP', u'access': u'Allow', u'destination_port_range': u'80'}) => changed=true 
  invocation:
    module_args:
      ad_user: null
      adfs_authority_url: null
      api_profile: latest
      append_tags: true
      auth_source: auto
      cert_validation_mode: null
      client_id: null
      cloud_environment: AzureCloud
      default_rules: null
      location: null
      name: nsg-prd-api-westeurope
      password: null
      profile: null
      purge_default_rules: false
      purge_rules: true
      resource_group: production-westeurope
      rules:
      - access: Allow
        description: null
        destination_address_prefix: '*'
        destination_port_range: '80'
        direction: Inbound
        name: AllowHTTP
        priority: 102
        protocol: Tcp
        source_address_prefix: '*'
        source_port_range: '*'
      secret: null
      state: present
      subscription_id: null
      tags:
        env: production
      tenant: null
  item:
    access: Allow
    destination_port_range: '80'
    direction: Inbound
    name: AllowHTTP
    priority: 102
    protocol: Tcp
  state:
    default_rules:
    - access: Allow
      description: Allow inbound traffic from all VMs in VNET
      destination_address_prefix: VirtualNetwork
      destination_address_prefixes: []
      destination_port_range: '*'
      destination_port_ranges: []
      direction: Inbound
      etag: W/"ID"
      id: /subscriptions/ID/resourceGroups/production-westeurope/providers/Microsoft.Network/networkSecurityGroups/nsg-prd-api-westeurope/defaultSecurityRules/AllowVnetInBound
      name: AllowVnetInBound
      priority: 65000
      protocol: '*'
      provisioning_state: Succeeded
      source_address_prefix: VirtualNetwork
      source_address_prefixes: []
      source_port_range: '*'
      source_port_ranges: []
    - access: Allow
      description: Allow inbound traffic from azure load balancer
      destination_address_prefix: '*'
      destination_address_prefixes: []
      destination_port_range: '*'
      destination_port_ranges: []
      direction: Inbound
      etag: W/"ID"
      id: /subscriptions/ID/resourceGroups/production-westeurope/providers/Microsoft.Network/networkSecurityGroups/nsg-prd-api-westeurope/defaultSecurityRules/AllowAzureLoadBalancerInBound
      name: AllowAzureLoadBalancerInBound
      priority: 65001
      protocol: '*'
      provisioning_state: Succeeded
      source_address_prefix: AzureLoadBalancer
      source_address_prefixes: []
      source_port_range: '*'
      source_port_ranges: []
    - access: Deny
      description: Deny all inbound traffic
      destination_address_prefix: '*'
      destination_address_prefixes: []
      destination_port_range: '*'
      destination_port_ranges: []
      direction: Inbound
      etag: W/"ID"
      id: /subscriptions/ID/resourceGroups/production-westeurope/providers/Microsoft.Network/networkSecurityGroups/nsg-prd-api-westeurope/defaultSecurityRules/DenyAllInBound
      name: DenyAllInBound
      priority: 65500
      protocol: '*'
      provisioning_state: Succeeded
      source_address_prefix: '*'
      source_address_prefixes: []
      source_port_range: '*'
      source_port_ranges: []
    - access: Allow
      description: Allow outbound traffic from all VMs to all VMs in VNET
      destination_address_prefix: VirtualNetwork
      destination_address_prefixes: []
      destination_port_range: '*'
      destination_port_ranges: []
      direction: Outbound
      etag: W/"ID"
      id: /subscriptions/ID/resourceGroups/production-westeurope/providers/Microsoft.Network/networkSecurityGroups/nsg-prd-api-westeurope/defaultSecurityRules/AllowVnetOutBound
      name: AllowVnetOutBound
      priority: 65000
      protocol: '*'
      provisioning_state: Succeeded
      source_address_prefix: VirtualNetwork
      source_address_prefixes: []
      source_port_range: '*'
      source_port_ranges: []
    - access: Allow
      description: Allow outbound traffic from all VMs to Internet
      destination_address_prefix: Internet
      destination_address_prefixes: []
      destination_port_range: '*'
      destination_port_ranges: []
      direction: Outbound
      etag: W/"ID"
      id: /subscriptions/ID/resourceGroups/production-westeurope/providers/Microsoft.Network/networkSecurityGroups/nsg-prd-api-westeurope/defaultSecurityRules/AllowInternetOutBound
      name: AllowInternetOutBound
      priority: 65001
      protocol: '*'
      provisioning_state: Succeeded
      source_address_prefix: '*'
      source_address_prefixes: []
      source_port_range: '*'
      source_port_ranges: []
    - access: Deny
      description: Deny all outbound traffic
      destination_address_prefix: '*'
      destination_address_prefixes: []
      destination_port_range: '*'
      destination_port_ranges: []
      direction: Outbound
      etag: W/"ID"
      id: /subscriptions/ID/resourceGroups/production-westeurope/providers/Microsoft.Network/networkSecurityGroups/nsg-prd-api-westeurope/defaultSecurityRules/DenyAllOutBound
      name: DenyAllOutBound
      priority: 65500
      protocol: '*'
      provisioning_state: Succeeded
      source_address_prefix: '*'
      source_address_prefixes: []
      source_port_range: '*'
      source_port_ranges: []
    id: /subscriptions/ID/resourceGroups/production-westeurope/providers/Microsoft.Network/networkSecurityGroups/nsg-prd-api-westeurope
    location: westeurope
    name: nsg-prd-api-westeurope
    network_interfaces: []
    rules:
    - access: Allow
      description: null
      destination_address_prefix: '*'
      destination_address_prefixes: []
      destination_port_range: '80'
      destination_port_ranges: []
      direction: Inbound
      etag: W/"ID"
      id: /subscriptions/ID/resourceGroups/production-westeurope/providers/Microsoft.Network/networkSecurityGroups/nsg-prd-api-westeurope/securityRules/AllowHTTP
      name: AllowHTTP
      priority: 102
      protocol: Tcp
      provisioning_state: Succeeded
      source_address_prefix: '*'
      source_address_prefixes: []
      source_port_range: '*'
      source_port_ranges: []
    subnets: []
    tags:
      env: production
    type: Microsoft.Network/networkSecurityGroups
META: ran handlers
META: ran handlers

PLAY RECAP ********************************************************************************************************************************************************************************************************
localhost                  : ok=1    changed=1    unreachable=0    failed=0   

Thursday 02 August 2018  15:24:14 +0200 (0:00:38.879)       0:00:38.927 ******* 
=============================================================================== 
create security group ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- 38.88s
/home/pedro/Projects/github.com/ansible-azure-platform/inventory/production/playbooks/tmp.yml:6 ------------------------------------------------------------------------------------------------------------------
Playbook run took 0 days, 0 hours, 0 minutes, 38 seconds
```



Discussion:
Files identified in the description:
* [lib/ansible/modules/cloud/azure/azure_rm_securitygroup.py](https://github.com/ansible/ansible/blob/devel/lib/ansible/modules/cloud/azure/azure_rm_securitygroup.py)

If these files are inaccurate, please update the `component name` section of the description or use the `!component` bot command.

[click here for bot help](https://github.com/ansible/ansibullbot/blob/master/ISSUE_HELP.md)
<!--- boilerplate: components_banner --->
cc @haroldwongms @nitzmahone @trstringer @xscript @yuwzho @zikalino
[click here for bot help](https://github.com/ansible/ansibullbot/blob/master/ISSUE_HELP.md)
<!--- boilerplate: notify --->
Hi @aksum,

Thank you for the issue, just so you are aware we have a dedicated Working Group for azure.
You can find other people interested in this in #ansible-azure on Freenode IRC
For more information about communities, meetings and agendas see https://github.com/ansible/community

[click here for bot help](https://github.com/ansible/ansibullbot/blob/master/ISSUE_HELP.md)
<!--- boilerplate: community_workgroups --->
Hi @aksum It may not a bug, you can try below method to create again, Thanks !
```e.g.
- hosts: localhost
  connection: local
  gather_facts: no

  tasks:
    - name: create security group
      azure_rm_securitygroup:
        name: nsg-prd-api-westeurope
        resource_group: myRG
        purge_default_rules: no
        purge_rules: yes
        rules:
          - name: AllowSSH
            protocol: Tcp
            destination_port_range: 22
            access: Allow
            priority: 100
            direction: Inbound
          - name: AllowFTP
            protocol: Tcp
            destination_port_range: 21
            access: Allow
            priority: 101
            direction: Inbound
          - name: AllowHTTP
            protocol: Tcp
            destination_port_range: 20
            access: Allow
            priority: 102
            direction: Inbound

        tags:
          env: production
```
Result as below:
```e.g.
ok: [127.0.0.1] => {
    "changed": false, 
    "invocation": {
        "module_args": {
            "ad_user": null, 
            "api_profile": "latest", 
            "append_tags": true, 
            "auth_source": null, 
            "cert_validation_mode": null, 
            "client_id": null, 
            "cloud_environment": null, 
            "default_rules": null, 
            "location": null, 
            "name": "nsg-prd-api-westeurope", 
            "password": null, 
            "profile": null, 
            "purge_default_rules": false, 
            "purge_rules": true, 
            "resource_group": "myRG", 
            "rules": [
                {
                    "access": "Allow", 
                    "destination_address_prefix": "*", 
                    "destination_port_range": 22, 
                    "direction": "Inbound", 
                    "name": "AllowSSH", 
                    "priority": 100, 
                    "protocol": "Tcp", 
                    "source_address_prefix": "*", 
                    "source_port_range": "*"
                }, 
                {
                    "access": "Allow", 
                    "destination_address_prefix": "*", 
                    "destination_port_range": 21, 
                    "direction": "Inbound", 
                    "name": "AllowFTP", 
                    "priority": 101, 
                    "protocol": "Tcp", 
                    "source_address_prefix": "*", 
                    "source_port_range": "*"
                }, 
                {
                    "access": "Allow", 
                    "destination_address_prefix": "*", 
                    "destination_port_range": 20, 
                    "direction": "Inbound", 
                    "name": "AllowHTTP", 
                    "priority": 102, 
                    "protocol": "Tcp", 
                    "source_address_prefix": "*", 
                    "source_port_range": "*"
                }
            ], 
            "secret": null, 
            "state": "present", 
            "subscription_id": null, 
            "tags": {
                "env": "production"
            }, 
            "tenant": null
        }
    }, 
    "state": {
        "default_rules": [
            {
                "access": "Allow", 
                "description": "Allow inbound traffic from all VMs in VNET", 
                "destination_address_prefix": "VirtualNetwork", 
                "destination_port_range": "*", 
                "direction": "Inbound", 
                "etag": "W/\"a9b5ce39-da20-453e-8b68-e340f2cb3ddc\"", 
                "id": "/subscriptions/ID/resourceGroups/myRG/providers/Microsoft.Network/networkSecurityGroups/nsg-prd-api-westeurope/defaultSecurityRules/AllowVnetInBound", 
                "name": "AllowVnetInBound", 
                "priority": 65000, 
                "protocol": "*", 
                "provisioning_state": "Succeeded", 
                "source_address_prefix": "VirtualNetwork", 
                "source_port_range": "*"
            }, 
            {
                "access": "Allow", 
                "description": "Allow inbound traffic from azure load balancer", 
                "destination_address_prefix": "*", 
                "destination_port_range": "*", 
                "direction": "Inbound", 
                "etag": "W/\"a9b5ce39-da20-453e-8b68-e340f2cb3ddc\"", 
                "id": "/subscriptions/ID/resourceGroups/myRG/providers/Microsoft.Network/networkSecurityGroups/nsg-prd-api-westeurope/defaultSecurityRules/AllowAzureLoadBalancerInBound", 
                "name": "AllowAzureLoadBalancerInBound", 
                "priority": 65001, 
                "protocol": "*", 
                "provisioning_state": "Succeeded", 
                "source_address_prefix": "AzureLoadBalancer", 
                "source_port_range": "*"
            }, 
            {
                "access": "Deny", 
                "description": "Deny all inbound traffic", 
                "destination_address_prefix": "*", 
                "destination_port_range": "*", 
                "direction": "Inbound", 
                "etag": "W/\"a9b5ce39-da20-453e-8b68-e340f2cb3ddc\"", 
                "id": "/subscriptions/ID/resourceGroups/myRG/providers/Microsoft.Network/networkSecurityGroups/nsg-prd-api-westeurope/defaultSecurityRules/DenyAllInBound", 
                "name": "DenyAllInBound", 
                "priority": 65500, 
                "protocol": "*", 
                "provisioning_state": "Succeeded", 
                "source_address_prefix": "*", 
                "source_port_range": "*"
            }, 
            {
                "access": "Allow", 
                "description": "Allow outbound traffic from all VMs to all VMs in VNET", 
                "destination_address_prefix": "VirtualNetwork", 
                "destination_port_range": "*", 
                "direction": "Outbound", 
                "etag": "W/\"a9b5ce39-da20-453e-8b68-e340f2cb3ddc\"", 
                "id": "/subscriptions/ID/resourceGroups/myRG/providers/Microsoft.Network/networkSecurityGroups/nsg-prd-api-westeurope/defaultSecurityRules/AllowVnetOutBound", 
                "name": "AllowVnetOutBound", 
                "priority": 65000, 
                "protocol": "*", 
                "provisioning_state": "Succeeded", 
                "source_address_prefix": "VirtualNetwork", 
                "source_port_range": "*"
            }, 
            {
                "access": "Allow", 
                "description": "Allow outbound traffic from all VMs to Internet", 
                "destination_address_prefix": "Internet", 
                "destination_port_range": "*", 
                "direction": "Outbound", 
                "etag": "W/\"a9b5ce39-da20-453e-8b68-e340f2cb3ddc\"", 
                "id": "/subscriptions/ID/resourceGroups/myRG/providers/Microsoft.Network/networkSecurityGroups/nsg-prd-api-westeurope/defaultSecurityRules/AllowInternetOutBound", 
                "name": "AllowInternetOutBound", 
                "priority": 65001, 
                "protocol": "*", 
                "provisioning_state": "Succeeded", 
                "source_address_prefix": "*", 
                "source_port_range": "*"
            }, 
            {
                "access": "Deny", 
                "description": "Deny all outbound traffic", 
                "destination_address_prefix": "*", 
                "destination_port_range": "*", 
                "direction": "Outbound", 
                "etag": "W/\"a9b5ce39-da20-453e-8b68-e340f2cb3ddc\"", 
                "id": "/subscriptions/ID/resourceGroups/myRG/providers/Microsoft.Network/networkSecurityGroups/nsg-prd-api-westeurope/defaultSecurityRules/DenyAllOutBound", 
                "name": "DenyAllOutBound", 
                "priority": 65500, 
                "protocol": "*", 
                "provisioning_state": "Succeeded", 
                "source_address_prefix": "*", 
                "source_port_range": "*"
            }
        ], 
        "id": "/subscriptions/ID/resourceGroups/myRG/providers/Microsoft.Network/networkSecurityGroups/nsg-prd-api-westeurope", 
        "location": "westeurope", 
        "name": "nsg-prd-api-westeurope", 
        "network_interfaces": [], 
        "rules": [
            {
                "access": "Allow", 
                "description": null, 
                "destination_address_prefix": "*", 
                "destination_port_range": "22", 
                "direction": "Inbound", 
                "etag": "W/\"a9b5ce39-da20-453e-8b68-e340f2cb3ddc\"", 
                "id": "/subscriptions/ID/resourceGroups/myRG/providers/Microsoft.Network/networkSecurityGroups/nsg-prd-api-westeurope/securityRules/AllowSSH", 
                "name": "AllowSSH", 
                "priority": 100, 
                "protocol": "Tcp", 
                "provisioning_state": "Succeeded", 
                "source_address_prefix": "*", 
                "source_port_range": "*"
            }, 
            {
                "access": "Allow", 
                "description": null, 
                "destination_address_prefix": "*", 
                "destination_port_range": "21", 
                "direction": "Inbound", 
                "etag": "W/\"a9b5ce39-da20-453e-8b68-e340f2cb3ddc\"", 
                "id": "/subscriptions/ID/resourceGroups/myRG/providers/Microsoft.Network/networkSecurityGroups/nsg-prd-api-westeurope/securityRules/AllowFTP", 
                "name": "AllowFTP", 
                "priority": 101, 
                "protocol": "Tcp", 
                "provisioning_state": "Succeeded", 
                "source_address_prefix": "*", 
                "source_port_range": "*"
            }, 
            {
                "access": "Allow", 
                "description": null, 
                "destination_address_prefix": "*", 
                "destination_port_range": "20", 
                "direction": "Inbound", 
                "etag": "W/\"a9b5ce39-da20-453e-8b68-e340f2cb3ddc\"", 
                "id": "/subscriptions/2085065b-00f8-4cba-9675-ba15f4d4ab66/resourceGroups/myRG/providers/Microsoft.Network/networkSecurityGroups/nsg-prd-api-westeurope/securityRules/AllowHTTP", 
                "name": "AllowHTTP", 
                "priority": 102, 
                "protocol": "Tcp", 
                "provisioning_state": "Succeeded", 
                "source_address_prefix": "*", 
                "source_port_range": "*"
            }
        ], 
        "subnets": [], 
        "tags": {
            "env": "production"
        }, 
        "type": "Microsoft.Network/networkSecurityGroups"
    }
}


Hi @Fred-sun,

I need to be able to add rules to security groups via vars files. This used to work...

Thanks!



Update: You should set "purge_rules: no", if the parameter is yes, it will remove any existing rules not matching those defined in the rules parameters. you can get the info from this link: https://docs.ansible.com/ansible/latest/modules/azure_rm_securitygroup_module.html?highlight=azure_rm_securitygroup
```e.g.
---
- hosts: localhost
  connection: local
  gather_facts: no
  tasks:
    - name: create security group
      azure_rm_securitygroup:
        name: nsg-prd-api-westeurope
        resource_group: production-westeurope
        purge_default_rules: no
        purge_rules: no
        rules:
          - name: "{{ item.name }}"
            protocol: "{{ item.protocol }}"
            destination_port_range: "{{ item.destination_port_range }}"
            access: "{{ item.access }}"
            priority: "{{ item.priority }}"
            direction: "{{ item.direction }}"
        tags:
          env: production
      with_items:
        - {"name": "AllowSSH", "protocol": "Tcp", "destination_port_range": "22", "access": "Allow", "priority": 100, "direction": "Inbound"}
        - {"name": "AllowFTP", "protocol": "Tcp", "destination_port_range": "21", "access": "Allow", "priority": 101, "direction": "Inbound"}
        - {"name": "AllowHTTP", "protocol": "Tcp", "destination_port_range": "80", "access": "Allow", "priority": 102, "direction": "Inbound"}

Hi @Fred-sun,

If `purge_rules` is enabled only last rule will show up in the Azure security group.
If `purge_rules` is not enabled only first rule will show up in the Azure security group.
Hi @aksum ,Thanks your report this bug, You are right, It get successfully result on ansible 2.5.6, but fail on ansible 2.6.2. Now, if you want to use this function, you can update your ansible to 2.5.6/2.5.7, method: sudo pip install ansible[azure]==2.5.6
Anyway, I will tracking this bug until it been fixed, Thanks !

Resolving patch:
diff --git a/lib/ansible/modules/cloud/azure/azure_rm_securitygroup.py b/lib/ansible/modules/cloud/azure/azure_rm_securitygroup.py
--- a/lib/ansible/modules/cloud/azure/azure_rm_securitygroup.py
+++ b/lib/ansible/modules/cloud/azure/azure_rm_securitygroup.py
@@ -348,6 +348,7 @@
 
 from ansible.module_utils.azure_rm_common import AzureRMModuleBase
 from ansible.module_utils.six import integer_types
+from ansible.module_utils._text import to_native
 
 
 def validate_rule(self, rule, rule_type=None):
@@ -386,6 +387,11 @@ def compare_rules_change(old_list, new_list, purge_list):
             new_list.append(old_rule)
         else:  # one rule is removed
             changed = True
+    # Compare new list and old list is the same? here only compare names
+    if not changed:
+        new_names = [to_native(x['name']) for x in new_list]
+        old_names = [to_native(x['name']) for x in old_list]
+        changed = (set(new_names) != set(old_names))
     return changed, new_list
 
 


Test patch:
diff --git a/test/integration/targets/azure_rm_securitygroup/tasks/main.yml b/test/integration/targets/azure_rm_securitygroup/tasks/main.yml
--- a/test/integration/targets/azure_rm_securitygroup/tasks/main.yml
+++ b/test/integration/targets/azure_rm_securitygroup/tasks/main.yml
@@ -187,6 +187,30 @@
 - assert:
       that: not output.changed
 
+- name: Add a single one group
+  azure_rm_securitygroup:
+      resource_group: "{{ resource_group }}"
+      name: "{{ secgroupname }}"
+      tags:
+          testing: testing
+          delete: on-exit
+          foo: bar
+      rules:
+          - name: DenySSH
+            protocol: Tcp
+            source_address_prefix:
+            - 54.120.120.240
+            destination_port_range: 22
+            access: Deny
+            priority: 102
+            direction: Inbound
+  register: output
+
+- assert:
+     that: 
+      - output.changed
+      - "{{ output.state.rules | length }} == 2"
+
 - name: Delete all security groups
   azure_rm_securitygroup:
       resource_group: "{{ resource_group }}"