Repository: ansible/ansible

Issue:
azure_rm_virtualmachine appending tags does not work
##### ISSUE TYPE
 - Bug Report

##### COMPONENT NAME
azure_rm_virtualmachine

##### ANSIBLE VERSION
```
ansible 2.3.0.0
  config file = /home/ubuntu/.ansible.cfg
  configured module search path = Default w/o overrides
  python version = 2.7.12 (default, Nov 19 2016, 06:48:10) [GCC 5.4.0 20160609]
```
##### OS / ENVIRONMENT
I think this is NA but I'm running it on Linux ubuntu-xenial 4.4.0-71-generic

##### SUMMARY
the azure_rm_virtualmachine module 
https://docs.ansible.com/ansible/azure_rm_virtualmachine_module.html
has documented a `append_tags` parameter, which should append tags if set to true, but does not.

There are actually two issues that prevent this from working correctly,  the first is, I believe, the same as issue number #20444, which I believe causes *any* change to an existing VM to fail with a `KeyError: 'vmId'` (which is what happens when running the below playbook without appylying the [fix](https://github.com/ansible/ansible/pull/22974) for that, which unfortunately has not yet been merged.  However, after applying that fix, the playbook runs successfully, but results in the tags from the second task replacing the ones set at creation time, rather than being appended.

##### STEPS TO REPRODUCE
run the following playbook, specifying `vmname` and `rg` on the command line using --extra-vars
<!--- Paste example playbooks or commands between quotes below -->
```yaml
---
- name: "create and update the tags on a VM"
  hosts: localhost
  tasks: 
  - name: Create {{vmname}} VM 
    azure_rm_virtualmachine:
      resource_group: '{{ rg }}'
      name: '{{vmname}}'
      vm_size: Standard_DS1_v2
      admin_username: yoda
      admin_password: do0rDoNot
      image:
        offer: UbuntuServer
        publisher: Canonical
        sku: '16.04-LTS'
        version: latest
      append_tags: no
      tags: 
        'foo': 'bar'

  - name: Append tags to {{vmname}}
    azure_rm_virtualmachine:
      resource_group: '{{ rg }}'
      name: '{{vmname}}'
      vm_size: Standard_DS1_v2
      append_tags: yes
      tags: 
        'derp': 'snort'
```

<!--- You can also paste gist.github.com links for larger files -->

##### EXPECTED RESULTS
VM should be created and tagged with both `'foo':'bar'` and `'derp':'snort' `

##### ACTUAL RESULTS
The resulting VM has only `'derp':'snort'` tags (though it's clearly created with `'foo':'bar'`, it just gets overridden



Discussion:

Resolving patch:
diff --git a/lib/ansible/module_utils/azure_rm_common.py b/lib/ansible/module_utils/azure_rm_common.py
--- a/lib/ansible/module_utils/azure_rm_common.py
+++ b/lib/ansible/module_utils/azure_rm_common.py
@@ -464,17 +464,20 @@ def update_tags(self, tags):
         :return: bool, dict
         '''
         new_tags = copy.copy(tags) if isinstance(tags, dict) else dict()
+        param_tags = self.module.params.get('tags') if isinstance(self.module.params.get('tags'), dict) else dict()
+        append_tags = self.module.params.get('append_tags') if self.module.params.get('append_tags') is not None else True
         changed = False
-        if isinstance(self.module.params.get('tags'), dict):
-            for key, value in self.module.params['tags'].items():
-                if not new_tags.get(key) or new_tags[key] != value:
+        # check add or update
+        for key, value in param_tags.items():
+            if not new_tags.get(key) or new_tags[key] != value:
+                changed = True
+                new_tags[key] = value
+        # check remove
+        if not append_tags:
+            for key, value in tags.items():
+                if not param_tags.get(key):
+                    new_tags.pop(key)
                     changed = True
-                    new_tags[key] = value
-            if isinstance(tags, dict):
-                for key, value in tags.items():
-                    if not self.module.params['tags'].get(key):
-                        new_tags.pop(key)
-                        changed = True
         return changed, new_tags
 
     def has_tags(self, obj_tags, tag_list):


Test patch:
diff --git a/test/integration/targets/azure_rm_publicipaddress/tasks/main.yml b/test/integration/targets/azure_rm_publicipaddress/tasks/main.yml
--- a/test/integration/targets/azure_rm_publicipaddress/tasks/main.yml
+++ b/test/integration/targets/azure_rm_publicipaddress/tasks/main.yml
@@ -33,9 +33,6 @@
       name: "pip{{ rpfx }}"
       allocation_method: Static
       domain_name: "{{ domain_name }}"
-      tags:
-          testing: testing
-          delete: on-exit
   register: output
 
 - assert:
@@ -45,8 +42,8 @@
   azure_rm_publicipaddress:
       resource_group: "{{ resource_group }}"
       name: "pip{{ rpfx }}"
+      append_tags: yes
       tags:
-          testing: testing
           delete: never
           foo: bar
   register: output
@@ -70,7 +67,7 @@
   azure_rm_publicipaddress:
       resource_group: "{{ resource_group }}"
       name: "pip{{ rpfx }}"
-      tags: {}
+      append_tags: no
   register: output
 
 - assert:
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
@@ -94,6 +94,7 @@
           testing: testing
           delete: never
           baz: bar
+      append_tags: false
   register: output
 
 - assert:
@@ -105,6 +106,7 @@
   azure_rm_securitygroup:
       resource_group: "{{ resource_group }}"
       name: "{{ secgroupname }}"
+      append_tags: false
       tags:
           testing: testing
           delete: on-exit
diff --git a/test/integration/targets/azure_rm_storageaccount/tasks/main.yml b/test/integration/targets/azure_rm_storageaccount/tasks/main.yml
--- a/test/integration/targets/azure_rm_storageaccount/tasks/main.yml
+++ b/test/integration/targets/azure_rm_storageaccount/tasks/main.yml
@@ -24,6 +24,7 @@
        resource_group: "{{ resource_group }}" 
        name: "{{ storage_account }}"
        account_type: Standard_LRS
+       append_tags: no
        tags:
            test: test
            galaxy: galaxy
@@ -72,6 +73,7 @@
    azure_rm_storageaccount:
        resource_group: "{{ resource_group }}"
        name: "{{ storage_account }}"
+       append_tags: no
        tags:
            testing: testing
            delete: never
@@ -87,6 +89,7 @@
    azure_rm_storageaccount:
        resource_group: "{{ resource_group }}"
        name: "{{ storage_account }}"
+       append_tags: no
        tags:
            testing: testing
            delete: never
diff --git a/test/integration/targets/azure_rm_virtualnetwork/tasks/main.yml b/test/integration/targets/azure_rm_virtualnetwork/tasks/main.yml
--- a/test/integration/targets/azure_rm_virtualnetwork/tasks/main.yml
+++ b/test/integration/targets/azure_rm_virtualnetwork/tasks/main.yml
@@ -102,6 +102,7 @@
 - name: Purge tags
   azure_rm_virtualnetwork:
     name: "{{ vnetname }}"
+    append_tags: no
     tags:
       testing: 'always'
     resource_group: "{{ resource_group }}"