Repository: ansible/ansible

Issue:
azure_rm_securitygroup fails with templated int values
 **ISSUE TYPE:** Bug Report

 **COMPONENT NAME**: azure_rm_securitygroup

**ANSIBLE VERSION:** 2.2.1.0

**OS / ENVIRONMENT:** Ubuntu 16.04

**SUMMARY:**
When i try to create security group in Azure and specifying that the number of priority will be generated randomly, an error appears
```
failed: [localhost] (item={u'DestAdrPrefix': u'*', u'DestPortRange': 3389, u'SourceAdrPrefix': u'10.10.10.0/24', u'RulePriority': 700, u'Ruledirection': u'Inbound', u'RuleProtocol': u'Tcp', u'SecGroupName': u'VLAN100-SecGr', u'RuleAccess': u'Allow', u'RuleName': u'RDP-Rule234fdsf', u'SourcePortRange': u'*'}) => {"failed": true, "item": {"DestAdrPrefix": "*", "DestPortRange": 3389, "RuleAccess": "Allow", "RuleName": "RDP-Rule234fdsf", "RulePriority": 700, "RuleProtocol": "Tcp", "Ruledirection": "Inbound", "SecGroupName": "VLAN100-SecGr", "SourceAdrPrefix": "10.10.10.0/24", "SourcePortRange": "*"}, "msg": "Error validating rule {'access': 'Allow', 'direction': 'Inbound', 'protocol': 'Tcp', 'name': 'RDP-Rule234fdsf', 'destination_address_prefix': '*', 'priority': '930', 'source_address_prefix': '10.10.10.0/24', 'destination_port_range': '3389'} - Rule priority attribute must be an integer."}
```
But when i specifying the priority as an example 600, all is ok.

**STEPS TO REPRODUCE:**
```
- name: Create a Security group
  hosts: localhost
  connection: local
  gather_facts: false

  vars_files:
    - '{{ ENV }}'

  vars:
    PriorityNum: "{{ 1000|random(100, 10)}}"

  tasks:
    - azure_rm_securitygroup:
        resource_group: '{{ RSGName }}'
        name: "{{ item.SecGroupName }}"
        rules:
          - source_address_prefix: "{{ item.SourceAdrPrefix }}"
            destination_address_prefix: "{{ item.DestAdrPrefix }}"
            protocol: "{{ item.RuleProtocol }}"
            priority: "{{ PriorityNum }}"
            name: "{{ item.RuleName }}"
            direction: "{{ item.Ruledirection }}"
            access: "{{ item.RuleAccess }}"
            destination_port_range: "{{ item.DestPortRange }}"
      with_items: '{{ SecGroups }}'
```


**EXPECTED RESULTS:**
```
failed: [localhost] (item={u'DestAdrPrefix': u'*', u'DestPortRange': 3389, u'SourceAdrPrefix': u'10.10.10.0/24', u'RulePriority': 700, u'Ruledirection': u'Inbound', u'RuleProtocol': u'Tcp', u'SecGroupName': u'VLAN100-SecGr', u'RuleAccess': u'Allow', u'RuleName': u'RDP-Rule234fdsf', u'SourcePortRange': u'*'}) => {"failed": true, "item": {"DestAdrPrefix": "*", "DestPortRange": 3389, "RuleAccess": "Allow", "RuleName": "RDP-Rule234fdsf", "RulePriority": 700, "RuleProtocol": "Tcp", "Ruledirection": "Inbound", "SecGroupName": "VLAN100-SecGr", "SourceAdrPrefix": "10.10.10.0/24", "SourcePortRange": "*"}, "msg": "Error validating rule {'access': 'Allow', 'direction': 'Inbound', 'protocol': 'Tcp', 'name': 'RDP-Rule234fdsf', 'destination_address_prefix': '*', 'priority': '930', 'source_address_prefix': '10.10.10.0/24', 'destination_port_range': '3389'} - Rule priority attribute must be an integer."}
```


Discussion:
We do explicit type conversion for top-level args to work around this, but until recently, argspec hasn't supported validation/canonicalization of nested structures. It does as of 2.3, so we should be able to go back and do the implicit argspec conversion in 2.3, and maybe need to do it explicitly for 2.2.
this happens with Ansible 2.3.1.0 installed with `pip install ansible --ugprade` in a virtualenv created with `pyenv` too. I am creating the security group on this way:

>         priority: "{{ 100 + item.0 | int }}"
>         ....
>       with_indexed_items: "{{ prod_network_subnets }}"

my idea was to assign the priority dinamically too.
cc @haroldwongms @tstringer
[click here for bot help](https://github.com/ansible/ansibullbot/blob/master/ISSUE_HELP.md)
<!--- boilerplate: notify --->
@ysolis just for troubleshooting purposes, can you run `ansible --version` pre/post repro of this issue?
ok, check @tstringer:
```
 (ansible) ~/devops/client_machines   master  ansible --version
  ansible 2.3.1.0
  config file = /home/yonsy/devops/client_machines/ansible.cfg
  configured module search path = Default w/o overrides
  python version = 2.7.13 (default, Feb 20 2017, 09:57:58) [GCC 5.4.0 20160609]
```

Ok cool thanks. Is there a way you can share this playbook via a gist, or if small enough copy/paste it here? I'd like to repro this and investigate.

Thank you! 👍 😄 
this playbook segment from task/role can give you one idea that you can replicate (the original playbook that I use is bigger and use vault vars files)

```
- name: production security group base
  azure_rm_securitygroup:
    name: ProductionSecurityGroupBase
    resource_group: ProductionResourceGroup
    rules:
      - name: "{{ item.name }}"
        protocol: Tcp
        destination_port_range: "{{ item.destination_port_range }}"
        access: Allow
        priority: "{{ item.priority | int }}"
        direction: Inbound
  with_items:
    - { 'name': 'ProductionAllowSSH', 'destination_port_range': 22, 'priority': 101 }
    - { 'name': 'ProductionAllowHTTP', 'destination_port_range': 80, 'priority': 102 }
    - { 'name': 'ProductionAllowHTTPS', 'destination_port_range': 443, 'priority': 103 }
    - { 'name': 'ProductionAllowyMySQL', 'destination_port_range': 3306, 'priority': 104 }
```

you can see the problem with `priority`, the normal way will be:

```    
    priority: "{{ item.priority }}"
```
my workaround is:
```    
    priority: "{{ item.priority | int }}"
```


Thank you for providing that information! Unfortunately my core Ansible knowledge on how it handles these parameters is falling short here. @nitzmahone, can you assist here?
Just fixing this issue in the existing code is a one-line fix, but there's a new 2.4 feature that should let us fix it "properly" by deleting a bunch of validation code. More to come...

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
@@ -367,7 +367,11 @@ def validate_rule(rule, rule_type=None):
     if not priority:
         raise Exception("Rule priority is required.")
     if not isinstance(priority, integer_types):
-        raise Exception("Rule priority attribute must be an integer.")
+        try:
+            priority = int(priority)
+            rule['priority'] = priority
+        except:
+            raise Exception("Rule priority attribute must be an integer.")
     if rule_type != 'default' and (priority < 100 or priority > 4096):
         raise Exception("Rule priority must be between 100 and 4096")
 


Test patch: