Repository: ansible/ansible

Issue:
azure_rm_networkinterface and azure_rm_virtualmachine inconsistent "location" param
<!---
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
<!---
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_networkinterface

##### ANSIBLE VERSION
<!--- Paste verbatim output from "ansible --version" between quotes below -->
```
ansible 2.4.2.0
  config file = None
  configured module search path = [u'/home/108752/.ansible/plugins/modules', u'/usr/share/ansible/plugins/modules']
  ansible python module location = /home/108752/bin/p27_venv/lib/python2.7/site-packages/ansible
  executable location = /home/108752/bin/p27_venv/bin/ansible
  python version = 2.7.5 (default, May  3 2017, 07:55:04) [GCC 4.8.5 20150623 (Red Hat 4.8.5-14)]

```

##### CONFIGURATION
<!---
If using Ansible 2.4 or above, paste 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).
-->
[defaults]
hostfile = inventory
become=True
become_user=root
deprecation_warnings=False
system_warnings=False
host_key_checking=False

##### OS / ENVIRONMENT
<!---
Mention 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.
-->
Red Hat Enterprise Linux 7.4
##### SUMMARY
<!--- Explain the problem briefly -->
The "location" parameter between azure_rm_networkinterface and azure_rm_virtualmachine is inconsistent. 

azure_rm_networkinterface accepts:
   location: "East US 2"

azure_rm_virtualmachine accepts:
   location: "eastus2"

Using "East US 2" in azure_rm_virtualmachine returns an error about invalid characters.

##### 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

- name: Create NIC
  azure_rm_networkinterface:
    name: "vm-klltest01-nic"
    resource_group: "klltest-rg"
    virtual_network_name: "{{ virtual_network }}"
    location: "East US 2"
    subnet_name: "{{ private_subnet }}"
    security_group_name: "{{ security_group }}"
    public_ip: no

- name: Create virtual machine
  azure_rm_virtualmachine:
    resource_group: "{{ resource_group }}"
    name: "{{ machine_name }}"
    vm_size: Basic_A0
    admin_username: deploy
    ssh_password_enabled: false
    ssh_public_keys:
      - path: /home/deploy/.ssh/authorized_keys
        key_data: "{{ public_key }}"
    network_interfaces: "{{ machine_name }}-nic"
    location: "East US 2"
    image:
      offer: "CentOS"
      publisher: "OpenLogic"
      sku: "7.4"
      version: "latest"

```

<!--- You can also paste gist.github.com links for larger files -->

##### EXPECTED RESULTS
<!--- What did you expect to happen when running the steps above? -->
Both azure_rm_networkinterface and azure_rm_virtualmachine should accept the same "location" format. Either use the short version or the long to be consistent.

##### ACTUAL RESULTS
<!--- What actually happened? If possible run with extra verbosity (-vvvv) -->

<!--- Paste verbatim command output between quotes below -->
```
(p27_venv)[108752@rhldakll075 baseline]$ ansible-playbook playbooks/ansible_feature.yml -e "machine_name=rhldaklltest902"

PLAY [Azure instance] ****************************************************************************************************************************************

TASK [Gathering Facts] ***************************************************************************************************************************************
ok: [localhost]

TASK [Create NIC] ********************************************************************************************************************************************
changed: [localhost]

TASK [Create virtual machine] ********************************************************************************************************************************
An exception occurred during task execution. To see the full traceback, use -vvv. The error was: msrest.exceptions.ValidationError: Parameter 'location' must conform to the following pattern: '^[-\\w\\._]+$'.
fatal: [localhost]: FAILED! => {"changed": false, "module_stderr": "Traceback (most recent call last):\n  File \"/tmp/ansible_xePoUi/ansible_module_azure_rm_virtualmachine.py\", line 1554, in <module>\n    main()\n  File \"/tmp/ansible_xePoUi/ansible_module_azure_rm_virtualmachine.py\", line 1551, in main\n    AzureRMVirtualMachine()\n  File \"/tmp/ansible_xePoUi/ansible_module_azure_rm_virtualmachine.py\", line 651, in __init__\n    supports_check_mode=True)\n  File \"/tmp/ansible_xePoUi/ansible_modlib.zip/ansible/module_utils/azure_rm_common.py\", line 285, in __init__\n  File \"/tmp/ansible_xePoUi/ansible_module_azure_rm_virtualmachine.py\", line 679, in exec_module\n    if self.vm_size and not self.vm_size_is_valid():\n  File \"/tmp/ansible_xePoUi/ansible_module_azure_rm_virtualmachine.py\", line 1368, in vm_size_is_valid\n    for size in sizes:\n  File \"/home/108752/bin/p27_venv/lib/python2.7/site-packages/msrest/paging.py\", line 109, in __next__\n    self.advance_page()\n  File \"/home/108752/bin/p27_venv/lib/python2.7/site-packages/msrest/paging.py\", line 95, in advance_page\n    self._response = self._get_next(self.next_link)\n  File \"/home/108752/bin/p27_venv/lib/python2.7/site-packages/azure/mgmt/compute/compute/v2017_03_30/operations/virtual_machine_sizes_operations.py\", line 63, in internal_paging\n    'location': self._serialize.url(\"location\", location, 'str', pattern=r'^[-\\w\\._]+$'),\n  File \"/home/108752/bin/p27_venv/lib/python2.7/site-packages/msrest/serialization.py\", line 487, in url\n    data = self.validate(data, name, required=True, **kwargs)\n  File \"/home/108752/bin/p27_venv/lib/python2.7/site-packages/msrest/serialization.py\", line 567, in validate\n    raise ValidationError(key, name, value)\nmsrest.exceptions.ValidationError: Parameter 'location' must conform to the following pattern: '^[-\\\\w\\\\._]+$'.\n", "module_stdout": "", "msg": "MODULE FAILURE", "rc": 0}
        to retry, use: --limit @/home/108752/src/ansible/baseline/playbooks/ansible_feature.retry

PLAY RECAP ***************************************************************************************************************************************************
localhost                  : ok=2    changed=1    unreachable=0    failed=1


```



Discussion:
Files identified in the description:
* [lib/ansible/modules/cloud/azure/azure_rm_networkinterface.py](https://github.com/ansible/ansible/blob/devel/lib/ansible/modules/cloud/azure/azure_rm_networkinterface.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 --->
@kwanlowe I will look into this issue by the end of the week.
@zikalino cheers mate
@kwanlowe sorry it took a little bit longer to finally check this issue...
The inconsistency is not in Ansible itself, but below REST API level, and Ansible is just passing location as it is to the REST APIs.
All the ansible modules accept lowercase location like "eastus2".

So there are 3 possible solutions:

(1) automatically make location lowercase + remove spaces in all modules
(2) automatically make location lowercase + remove spaces in virtual machine module
(3) do the strict check in all Ansible modules

Probably at this point (2) makes most sense as it won't affect any other modules and incomststency will be fixed.
I will create a fix soon, in a meanwhile pls use lowercase location when creating vm.

@zikalino Thank you for looking at this. 

Maybe related, I noticed that the location default for azure_rm_networkinterface defaults to that of the resource group rather than the subnet. The RG is not location specific though, so it's possible to have an EastUS2 subnet in an EastUS RG. As 'location' is an optional field this will generate a resource not found error. Possible to make this pull the subnet region instead of the RG region or make 'location' a required field?

Resolving patch:
diff --git a/lib/ansible/modules/cloud/azure/azure_rm_networkinterface.py b/lib/ansible/modules/cloud/azure/azure_rm_networkinterface.py
--- a/lib/ansible/modules/cloud/azure/azure_rm_networkinterface.py
+++ b/lib/ansible/modules/cloud/azure/azure_rm_networkinterface.py
@@ -311,7 +311,7 @@
     # This is handled in azure_rm_common
     pass
 
-from ansible.module_utils.azure_rm_common import AzureRMModuleBase, azure_id_to_dict
+from ansible.module_utils.azure_rm_common import AzureRMModuleBase, azure_id_to_dict, normalize_location_name
 from ansible.module_utils._text import to_native
 
 
@@ -446,6 +446,7 @@ def exec_module(self, **kwargs):
         if not self.location:
             # Set default location
             self.location = resource_group.location
+        self.location = normalize_location_name(self.location)
 
         # parse the virtual network resource group and name
         self.virtual_network = self.parse_resource_to_dict(self.virtual_network)
diff --git a/lib/ansible/modules/cloud/azure/azure_rm_virtualmachine.py b/lib/ansible/modules/cloud/azure/azure_rm_virtualmachine.py
--- a/lib/ansible/modules/cloud/azure/azure_rm_virtualmachine.py
+++ b/lib/ansible/modules/cloud/azure/azure_rm_virtualmachine.py
@@ -614,7 +614,7 @@
     pass
 
 from ansible.module_utils.basic import to_native, to_bytes
-from ansible.module_utils.azure_rm_common import AzureRMModuleBase, azure_id_to_dict
+from ansible.module_utils.azure_rm_common import AzureRMModuleBase, azure_id_to_dict, normalize_location_name
 
 
 AZURE_OBJECT_CLASS = 'VirtualMachine'
@@ -742,6 +742,8 @@ def exec_module(self, **kwargs):
             # Set default location
             self.location = resource_group.location
 
+        self.location = normalize_location_name(self.location)
+
         if self.state == 'present':
             # Verify parameters and resolve any defaults
 


Test patch: