Repository: ansible/ansible

Issue:
azure_rm_deployment deletes Resource Group tags
##### ISSUE TYPE
Bug Report

##### COMPONENT NAME
azure_rm_deployment

##### ANSIBLE VERSION
```
2.3.1.0
```

##### CONFIGURATION
*nope*

##### OS / ENVIRONMENT

macOS Sierra

##### SUMMARY

The `azure_rm_resourcegroup` module deletes the tags of the Resource Group it is deployed to.

##### STEPS TO REPRODUCE

1. Download the [example ARM templates](https://github.com/Azure/azure-quickstart-templates/tree/master/101-container-registry) to the local ansible directory.

2. Run the following playbook.

```yaml
- name: Create infra
  hosts: localhost
  gather_facts: False

  tasks:


    - name: Create resource group
      azure_rm_resourcegroup:
        name: foobar
        location: westeurope
        tags:
          team: myteam
          tier: mytier
      register: reg_rg

  #
  # after this task the tags *do* exist on the Resource Group
  #

    - name: Deploy Docker registry
      azure_rm_deployment:
        deployment_name: registry
        resource_group_name: '{{ reg_rg.state.name }}'
        location: '{{ reg_rg.state.location }}'
        template: "{{ lookup('file', './azuredeploy.json') }}"
        parameters: "{{ lookup('file', './azuredeploy.parameters.json') }}"
        wait_for_deployment_completion: False

  #
  # after this task they *don't*
  #
```

##### EXPECTED RESULTS
The `tags` already present on the Resource Group are persisted. 

##### ACTUAL RESULTS
All `tags` are deleted.

The output of the `azure_rm_deployment` task shows:

`azure_rm_resourcegroup` task

```json
    "invocation": {
        "module_args": {
            ... 
            "tags": {
                "team": "myteam", 
                "tier": "mytier"
            }
```

`azure_rm_deployment` task
```json
    "invocation": {
        "module_args": {
            ...
            "resource_group_name": "foobar",  
            "tags": null
```



Discussion:
cc @devigned @lmazuel
[click here for bot help](https://github.com/ansible/ansibullbot/blob/master/ISSUE_HELP.md)
<!--- boilerplate: notify --->
cc @haroldwongms @tstringer
Hi, did you check in the Azure portal that the tags are still here? (I think they are). My current guess is that it's just an Anisble issue on output.
@lmazuel yes I did, they really aren't.

Output from `az group list`:
```json
[
  {
    "id": "/...",
    "location": "westeurope",
    "managedBy": null,
    "name": "foobar",
    "properties": {
      "provisioningState": "Succeeded"
    },
    "tags": null
  }
]
```
I took some time to investigate today:

1. [`tags` ](https://github.com/ansible/ansible/blob/5b03e5544045749fca87daa05380592901c0eca1/lib/ansible/modules/cloud/azure/azure_rm_deployment.py#L437) is initialized to None
2. the [`params` dict](https://github.com/ansible/ansible/blob/5b03e5544045749fca87daa05380592901c0eca1/lib/ansible/modules/cloud/azure/azure_rm_deployment.py#L499) is composed with this None `tags`
3. the [`create_or_update()`](https://github.com/ansible/ansible/blob/5b03e5544045749fca87daa05380592901c0eca1/lib/ansible/modules/cloud/azure/azure_rm_deployment.py#L502) method [pushes the new configuration as is](https://github.com/Azure/azure-sdk-for-python/blob/b58ffd82ea775f1e8b5677be9a1aeec60840faac/azure-mgmt-resource/azure/mgmt/resource/resources/v2016_09_01/operations/resource_groups_operations.py#L221)

We should check if the group exists, collect its tags, and then update.

Resolving patch:
diff --git a/lib/ansible/modules/cloud/azure/azure_rm_deployment.py b/lib/ansible/modules/cloud/azure/azure_rm_deployment.py
--- a/lib/ansible/modules/cloud/azure/azure_rm_deployment.py
+++ b/lib/ansible/modules/cloud/azure/azure_rm_deployment.py
@@ -422,6 +422,7 @@ def __init__(self):
         self.wait_for_deployment_completion = None
         self.wait_for_deployment_polling_period = None
         self.tags = None
+        self.append_tags = None
 
         self.results = dict(
             deployment=dict(),
@@ -435,7 +436,7 @@ def __init__(self):
 
     def exec_module(self, **kwargs):
 
-        for key in list(self.module_arg_spec.keys()) + ['tags']:
+        for key in list(self.module_arg_spec.keys()) + ['append_tags', 'tags']:
             setattr(self, key, kwargs[key])
 
         if self.state == 'present':
@@ -460,10 +461,14 @@ def exec_module(self, **kwargs):
             self.results['changed'] = True
             self.results['msg'] = 'deployment succeeded'
         else:
-            if self.resource_group_exists(self.resource_group_name):
-                self.destroy_resource_group()
-                self.results['changed'] = True
-                self.results['msg'] = "deployment deleted"
+            try:
+                if self.get_resource_group(self.resource_group_name):
+                    self.destroy_resource_group()
+                    self.results['changed'] = True
+                    self.results['msg'] = "deployment deleted"
+            except CloudError:
+                # resource group does not exist
+                pass
 
         return self.results
 
@@ -490,6 +495,15 @@ def deploy_template(self):
                 uri=self.template_link
             )
 
+        if self.append_tags and self.tags:
+            try:
+                rg = self.get_resource_group(self.resource_group_name)
+                if rg.tags:
+                    self.tags = dict(self.tags, **rg.tags)
+            except CloudError:
+                # resource group does not exist
+                pass
+
         params = self.rm_models.ResourceGroup(location=self.location, tags=self.tags)
 
         try:
@@ -537,19 +551,6 @@ def destroy_resource_group(self):
                 self.fail("Delete resource group and deploy failed with status code: %s and message: %s" %
                           (e.status_code, e.message))
 
-    def resource_group_exists(self, resource_group):
-        '''
-        Return True/False based on existence of requested resource group.
-
-        :param resource_group: string. Name of a resource group.
-        :return: boolean
-        '''
-        try:
-            self.rm_client.resource_groups.get(resource_group)
-        except CloudError:
-            return False
-        return True
-
     def _get_failed_nested_operations(self, current_operations):
         new_operations = []
         for operation in current_operations:


Test patch: