From dc6c0cb9f853dfdfca515ffabf3eea0e0a64c586 Mon Sep 17 00:00:00 2001
From: Yuwei Zhou <yuwzho@microsoft.com>
Date: Thu, 28 Mar 2019 08:04:40 +0800
Subject: [PATCH] Add workspace module for azure (#53731)

* add workspace

* add other properties

* add facts modules

* add test

* add doc

* fix lint

* fix lint

* rename the module

* fix docs
---
 lib/ansible/module_utils/azure_rm_common.py   |  16 +
 .../azure/azure_rm_loganalyticsworkspace.py   | 319 ++++++++++++++++++
 .../azure_rm_loganalyticsworkspace_facts.py   | 262 ++++++++++++++
 packaging/requirements/requirements-azure.txt |   1 +
 .../targets/azure_rm_workspace/aliases        |   5 +
 .../targets/azure_rm_workspace/meta/main.yml  |   2 +
 .../targets/azure_rm_workspace/tasks/main.yml | 128 +++++++
 .../requirements/integration.cloud.azure.txt  |   1 +
 8 files changed, 734 insertions(+)
 create mode 100644 lib/ansible/modules/cloud/azure/azure_rm_loganalyticsworkspace.py
 create mode 100644 lib/ansible/modules/cloud/azure/azure_rm_loganalyticsworkspace_facts.py
 create mode 100644 test/integration/targets/azure_rm_workspace/aliases
 create mode 100644 test/integration/targets/azure_rm_workspace/meta/main.yml
 create mode 100644 test/integration/targets/azure_rm_workspace/tasks/main.yml

diff --git a/lib/ansible/module_utils/azure_rm_common.py b/lib/ansible/module_utils/azure_rm_common.py
index 2e6331d51dc..eace003da1a 100644
--- a/lib/ansible/module_utils/azure_rm_common.py
+++ b/lib/ansible/module_utils/azure_rm_common.py
@@ -166,6 +166,8 @@ try:
     from azure.mgmt.rdbms.mariadb import MariaDBManagementClient
     from azure.mgmt.containerregistry import ContainerRegistryManagementClient
     from azure.mgmt.containerinstance import ContainerInstanceManagementClient
+    from azure.mgmt.loganalytics import LogAnalyticsManagementClient
+    import azure.mgmt.loganalytics.models as LogAnalyticsModels
 except ImportError as exc:
     HAS_AZURE_EXC = traceback.format_exc()
     HAS_AZURE = False
@@ -302,6 +304,7 @@ class AzureRMModuleBase(object):
         self._traffic_manager_management_client = None
         self._monitor_client = None
         self._resource = None
+        self._log_analytics_client = None
 
         self.check_mode = self.module.check_mode
         self.api_profile = self.module.params.get('api_profile')
@@ -974,6 +977,19 @@ class AzureRMModuleBase(object):
                                                             base_url=self._cloud_environment.endpoints.resource_manager)
         return self._monitor_client
 
+    @property
+    def log_analytics_client(self):
+        self.log('Getting log analytics client')
+        if not self._log_analytics_client:
+            self._log_analytics_client = self.get_mgmt_svc_client(LogAnalyticsManagementClient,
+                                                                  base_url=self._cloud_environment.endpoints.resource_manager)
+        return self._log_analytics_client
+
+    @property
+    def log_analytics_models(self):
+        self.log('Getting log analytics models')
+        return LogAnalyticsModels
+
 
 class AzureRMAuthException(Exception):
     pass
diff --git a/lib/ansible/modules/cloud/azure/azure_rm_loganalyticsworkspace.py b/lib/ansible/modules/cloud/azure/azure_rm_loganalyticsworkspace.py
new file mode 100644
index 00000000000..2aba9670dd6
--- /dev/null
+++ b/lib/ansible/modules/cloud/azure/azure_rm_loganalyticsworkspace.py
@@ -0,0 +1,319 @@
+#!/usr/bin/python
+#
+# Copyright (c) 2019 Yuwei Zhou, <yuwzho@microsoft.com>
+#
+# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+from __future__ import absolute_import, division, print_function
+__metaclass__ = type
+
+
+ANSIBLE_METADATA = {'metadata_version': '1.1',
+                    'status': ['preview'],
+                    'supported_by': 'community'}
+
+
+DOCUMENTATION = '''
+---
+module: azure_rm_loganalyticsworkspace
+version_added: "2.8"
+short_description: Manage Azure Log Analytics workspaces.
+description:
+    - Create, delete Azure Log Analytics workspaces.
+options:
+    resource_group:
+        description:
+            - Name of resource group.
+        required: true
+    name:
+        description:
+            - Name of the workspace.
+        required: true
+    state:
+        description:
+            - Assert the state of the image. Use C(present) to create or update a image and C(absent) to delete an image.
+        default: present
+        choices:
+            - absent
+            - present
+    location:
+        description:
+            - Resource location.
+    sku:
+        description:
+            - The SKU of the workspace
+        choices:
+            - free
+            - standard
+            - premium
+            - unlimited
+            - per_node
+            - per_gb2018
+            - standalone
+        default: per_gb2018
+    retention_in_days:
+        description:
+            - The workspace data retention in days.
+            - -1 means Unlimited retention for the C(unlimited) C(sku).
+            - 730 days is the maximum allowed for all other C(sku)s.
+    intelligence_packs:
+        description:
+            - Manage intelligence packs possible for this workspace.
+            - "Enable one pack by setting it to C(true). E.g. {'Backup': true}."
+            - "Disable one pack by setting it to C(false). E.g. {'Backup': false}."
+            - Other intelligence packs not list in this property will not be changed.
+        type: dict
+extends_documentation_fragment:
+    - azure
+    - azure_tags
+
+author:
+    - "Yuwei Zhou (@yuwzho)"
+'''
+
+EXAMPLES = '''
+- name: Create a workspace with backup enabled
+  azure_rm_loganalyticsworkspace:
+    resource_group: foo
+    name: bar
+    intelligence_pack:
+        Backup: true
+'''
+
+RETURN = '''
+id:
+    description: Workspace resource path.
+    type: str
+    returned: success
+    example: "/subscriptions/XXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXX/resourceGroups/foo/providers/Microsoft.OperationalInsights/workspaces/bar"
+location:
+    description:
+        - Resource location.
+    type: str
+    returned: success
+    example: "eastus"
+sku:
+    description:
+        - The SKU of the workspace
+    type: str
+    returned: success
+    example: "per_gb2018"
+retention_in_days:
+    description:
+        - The workspace data retention in days.
+        - -1 means Unlimited retention for the C(unlimited) C(sku).
+        - 730 days is the maximum allowed for all other C(sku)s.
+    type: int
+    returned: success
+    example: 40
+intelligence_packs:
+    description:
+        - Lists all the intelligence packs possible and whether they are enabled or disabled for a given workspace.
+    type: list
+    returned: success
+    example: ['name': 'CapacityPerformance', 'enabled': true]
+management_groups:
+    description:
+        - List of management groups connected to the workspace.
+    type: list
+    returned: success
+    example: "{'value': []}"
+shared_keys:
+    description:
+        - Shared keys for the workspace.
+    type: list
+    returned: success
+    example: "{
+                'primarySharedKey': 'BozLY1JnZbxu0jWUQSY8iRPEM8ObmpP8rW+8bUl3+HpDJI+n689SxXgTgU7k1qdxo/WugRLxechxbolAfHM5uA==',
+                'secondarySharedKey': '7tDt5W0JBrCQKtQA3igfFltLSzJeyr9LmuT+B/ibzd8cdC1neZ1ePOQLBx5NUzc0q2VUIK0cLhWNyFvo/hT8Ww=='
+              }"
+usages:
+    description:
+        - List of usage metrics for the workspace.
+    type: list
+    returned: success
+    example: "{
+                'value': [
+                    {
+                    'name': {
+                        'value': 'DataAnalyzed',
+                        'localizedValue': 'Data Analyzed'
+                    },
+                    'unit': 'Bytes',
+                    'currentValue': 0,
+                    'limit': 524288000,
+                    'nextResetTime': '2017-10-03T00:00:00Z',
+                    'quotaPeriod': 'P1D'
+                    }
+                ]
+              }"
+'''  # NOQA
+
+from ansible.module_utils.azure_rm_common import AzureRMModuleBase, format_resource_id
+from ansible.module_utils.common.dict_transformations import _snake_to_camel, _camel_to_snake
+
+try:
+    from msrestazure.tools import parse_resource_id
+    from msrestazure.azure_exceptions import CloudError
+except ImportError:
+    # This is handled in azure_rm_common
+    pass
+
+
+class AzureRMLogAnalyticsWorkspace(AzureRMModuleBase):
+
+    def __init__(self):
+
+        self.module_arg_spec = dict(
+            resource_group=dict(type='str', required=True),
+            name=dict(type='str', required=True),
+            state=dict(type='str', default='present', choices=['present', 'absent']),
+            location=dict(type='str'),
+            sku=dict(type='str', default='per_gb2018', choices=['free', 'standard', 'premium', 'unlimited', 'per_node', 'per_gb2018', 'standalone']),
+            retention_in_days=dict(type='int'),
+            intelligence_packs=dict(type='dict')
+        )
+
+        self.results = dict(
+            changed=False,
+            id=None
+        )
+
+        self.resource_group = None
+        self.name = None
+        self.state = None
+        self.location = None
+        self.sku = None
+        self.retention_in_days = None
+        self.intelligence_packs = None
+
+        super(AzureRMLogAnalyticsWorkspace, self).__init__(self.module_arg_spec, supports_check_mode=True)
+
+    def exec_module(self, **kwargs):
+
+        for key in list(self.module_arg_spec.keys()) + ['tags']:
+            setattr(self, key, kwargs[key])
+
+        self.results = dict()
+        changed = False
+
+        if not self.location:
+            resource_group = self.get_resource_group(self.resource_group)
+            self.location = resource_group.location
+
+        if self.sku == 'per_gb2018':
+            self.sku = 'PerGB2018'
+        else:
+            self.sku = _snake_to_camel(self.sku)
+        workspace = self.get_workspace()
+        if not workspace and self.state == 'present':
+            changed = True
+            workspace = self.log_analytics_models.Workspace(sku=self.log_analytics_models.Sku(name=self.sku),
+                                                            retention_in_days=self.retention_in_days,
+                                                            location=self.location)
+            if not self.check_mode:
+                workspace = self.create_workspace(workspace)
+        elif workspace and self.state == 'absent':
+            changed = True
+            workspace = None
+            if not self.check_mode:
+                self.delete_workspace()
+        if workspace and workspace.id:
+            self.results = self.to_dict(workspace)
+            self.results['intelligence_packs'] = self.list_intelligence_packs()
+            self.results['management_groups'] = self.list_management_groups()
+            self.results['usages'] = self.list_usages()
+            self.results['shared_keys'] = self.get_shared_keys()
+        # handle the intelligence pack
+        if workspace and workspace.id and self.intelligence_packs:
+            intelligence_packs = self.results['intelligence_packs']
+            for key in self.intelligence_packs.keys():
+                enabled = self.intelligence_packs[key]
+                for x in intelligence_packs:
+                    if x['name'].lower() == key.lower():
+                        if x['enabled'] != enabled:
+                            changed = True
+                            if not self.check_mode:
+                                self.change_intelligence(x['name'], enabled)
+                                x['enabled'] = enabled
+                        break
+        self.results['changed'] = changed
+        return self.results
+
+    def create_workspace(self, workspace):
+        try:
+            poller = self.log_analytics_client.workspaces.create_or_update(self.resource_group, self.name, workspace)
+            return self.get_poller_result(poller)
+        except CloudError as exc:
+            self.fail('Error when creating workspace {0} - {1}'.format(self.name, exc.message or str(exc)))
+
+    def get_workspace(self):
+        try:
+            return self.log_analytics_client.workspaces.get(self.resource_group, self.name)
+        except CloudError:
+            pass
+
+    def delete_workspace(self):
+        try:
+            self.log_analytics_client.workspaces.delete(self.resource_group, self.name)
+        except CloudError as exc:
+            self.fail('Error when deleting workspace {0} - {1}'.format(self.name, exc.message or str(exc)))
+
+    def to_dict(self, workspace):
+        result = workspace.as_dict()
+        result['sku'] = _camel_to_snake(workspace.sku.name)
+        return result
+
+    def list_intelligence_packs(self):
+        try:
+            response = self.log_analytics_client.workspaces.list_intelligence_packs(self.resource_group, self.name)
+            return [x.as_dict() for x in response]
+        except CloudError as exc:
+            self.fail('Error when listing intelligence packs {0}'.format(exc.message or str(exc)))
+
+    def change_intelligence(self, key, value):
+        try:
+            if value:
+                self.log_analytics_client.workspaces.enable_intelligence_pack(self.resource_group, self.name, key)
+            else:
+                self.log_analytics_client.workspaces.disable_intelligence_pack(self.resource_group, self.name, key)
+        except CloudError as exc:
+            self.fail('Error when changing intelligence pack {0} - {1}'.format(key, exc.message or str(exc)))
+
+    def list_management_groups(self):
+        result = []
+        try:
+            response = self.log_analytics_client.workspaces.list_management_groups(self.resource_group, self.name)
+            while True:
+                result.append(response.next().as_dict())
+        except StopIteration:
+            pass
+        except CloudError as exc:
+            self.fail('Error when listing management groups {0}'.format(exc.message or str(exc)))
+        return result
+
+    def list_usages(self):
+        result = []
+        try:
+            response = self.log_analytics_client.workspaces.list_usages(self.resource_group, self.name)
+            while True:
+                result.append(response.next().as_dict())
+        except StopIteration:
+            pass
+        except CloudError as exc:
+            self.fail('Error when listing usages {0}'.format(exc.message or str(exc)))
+        return result
+
+    def get_shared_keys(self):
+        try:
+            return self.log_analytics_client.workspaces.get_shared_keys(self.resource_group, self.name).as_dict()
+        except CloudError as exc:
+            self.fail('Error when getting shared key {0}'.format(exc.message or str(exc)))
+
+
+def main():
+    AzureRMLogAnalyticsWorkspace()
+
+
+if __name__ == '__main__':
+    main()
diff --git a/lib/ansible/modules/cloud/azure/azure_rm_loganalyticsworkspace_facts.py b/lib/ansible/modules/cloud/azure/azure_rm_loganalyticsworkspace_facts.py
new file mode 100644
index 00000000000..ddef6661c46
--- /dev/null
+++ b/lib/ansible/modules/cloud/azure/azure_rm_loganalyticsworkspace_facts.py
@@ -0,0 +1,262 @@
+#!/usr/bin/python
+#
+# Copyright (c) 2019 Yuwei Zhou, <yuwzho@microsoft.com>
+#
+# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+from __future__ import absolute_import, division, print_function
+__metaclass__ = type
+
+
+ANSIBLE_METADATA = {'metadata_version': '1.1',
+                    'status': ['preview'],
+                    'supported_by': 'community'}
+
+
+DOCUMENTATION = '''
+---
+module: azure_rm_loganalyticsworkspace_facts
+version_added: "2.8"
+short_description: Get facts of Azure Log Analytics workspaces.
+description:
+    - Get, query Azure Log Analytics workspaces.
+options:
+    resource_group:
+        description:
+            - Name of resource group.
+        required: True
+    name:
+        description:
+            - Name of the workspace.
+    tags:
+        description:
+            - Limit results by providing a list of tags. Format tags as 'key' or 'key:value'.
+    show_intelligence_packs:
+        description:
+            - Show the intelligence packs for a workspace.
+            - Note this will cost one more network overhead for each workspace, expected slow response.
+    show_management_groups:
+        description:
+            - Show the management groups for a workspace.
+            - Note this will cost one more network overhead for each workspace, expected slow response.
+    show_shared_keys:
+        description:
+            - Show the shared keys for a workspace.
+            - Note this will cost one more network overhead for each workspace, expected slow response.
+    show_usages:
+        description:
+            - Show the list of usages for a workspace.
+            - Note this will cost one more network overhead for each workspace, expected slow response.
+extends_documentation_fragment:
+    - azure
+
+author:
+    - "Yuwei Zhou (@yuwzho)"
+
+'''
+
+EXAMPLES = '''
+- name: Query a workspace
+  azure_rm_loganalyticsworkspace_facts:
+      resource_group: foo
+      name: bar
+      show_intelligence_packs: true
+      show_management_groups: true
+      show_shared_keys: true
+      show_usages: true
+'''
+
+RETURN = '''
+id:
+    description: Workspace resource path.
+    type: str
+    returned: success
+    example: "/subscriptions/XXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXX/resourceGroups/foo/providers/Microsoft.OperationalInsights/workspaces/bar"
+location:
+    description:
+        - Resource location.
+    type: str
+    returned: success
+    example: "eastus"
+sku:
+    description:
+        - The SKU of the workspace
+    type: str
+    returned: success
+    example: "per_gb2018"
+retention_in_days:
+    description:
+        - The workspace data retention in days.
+        - -1 means Unlimited retention for the C(unlimited) C(sku).
+        - 730 days is the maximum allowed for all other C(sku)s.
+    type: int
+    returned: success
+    example: 40
+intelligence_packs:
+    description:
+        - Lists all the intelligence packs possible and whether they are enabled or disabled for a given workspace.
+    type: list
+    returned: success
+    example: ['name': 'CapacityPerformance', 'enabled': true]
+management_groups:
+    description:
+        - List of management groups connected to the workspace.
+    type: list
+    returned: success
+    example: "{'value': []}"
+shared_keys:
+    description:
+        - Shared keys for the workspace.
+    type: list
+    returned: success
+    example: "{
+                'primarySharedKey': 'BozLY1JnZbxu0jWUQSY8iRPEM8ObmpP8rW+8bUl3+HpDJI+n689SxXgTgU7k1qdxo/WugRLxechxbolAfHM5uA==',
+                'secondarySharedKey': '7tDt5W0JBrCQKtQA3igfFltLSzJeyr9LmuT+B/ibzd8cdC1neZ1ePOQLBx5NUzc0q2VUIK0cLhWNyFvo/hT8Ww=='
+              }"
+usages:
+    description:
+        - List of usage metrics for the workspace.
+    type: list
+    returned: success
+    example: "{
+                'value': [
+                    {
+                    'name': {
+                        'value': 'DataAnalyzed',
+                        'localizedValue': 'Data Analyzed'
+                    },
+                    'unit': 'Bytes',
+                    'currentValue': 0,
+                    'limit': 524288000,
+                    'nextResetTime': '2017-10-03T00:00:00Z',
+                    'quotaPeriod': 'P1D'
+                    }
+                ]
+              }"
+'''  # NOQA
+
+from ansible.module_utils.azure_rm_common import AzureRMModuleBase, format_resource_id
+from ansible.module_utils.common.dict_transformations import _snake_to_camel, _camel_to_snake
+
+try:
+    from msrestazure.tools import parse_resource_id
+    from msrestazure.azure_exceptions import CloudError
+except ImportError:
+    # This is handled in azure_rm_common
+    pass
+
+
+class AzureRMLogAnalyticsWorkspaceFact(AzureRMModuleBase):
+
+    def __init__(self):
+
+        self.module_arg_spec = dict(
+            resource_group=dict(type='str', required=True),
+            name=dict(type='str'),
+            tags=dict(type='list'),
+            show_shared_keys=dict(type='bool'),
+            show_intelligence_packs=dict(type='bool'),
+            show_usages=dict(type='bool'),
+            show_management_groups=dict(type='bool')
+        )
+
+        self.results = dict(
+            changed=False,
+            workspaces=[]
+        )
+
+        self.resource_group = None
+        self.name = None
+        self.tags = None
+        self.show_intelligence_packs = None
+        self.show_shared_keys = None
+        self.show_usages = None
+        self.show_management_groups = None
+
+        super(AzureRMLogAnalyticsWorkspaceFact, self).__init__(self.module_arg_spec, supports_tags=False, facts_module=True)
+
+    def exec_module(self, **kwargs):
+
+        for key in list(self.module_arg_spec.keys()):
+            setattr(self, key, kwargs[key])
+
+        if self.name:
+            item = self.get_workspace()
+            response = [item] if item else []
+        else:
+            response = self.list_by_resource_group()
+
+        self.results['workspaces'] = [self.to_dict(x) for x in response if self.has_tags(x.tags, self.tags)]
+        return self.results
+
+    def get_workspace(self):
+        try:
+            return self.log_analytics_client.workspaces.get(self.resource_group, self.name)
+        except CloudError:
+            pass
+        return None
+
+    def list_by_resource_group(self):
+        try:
+            return self.log_analytics_client.workspaces.list_by_resource_group(self.resource_group)
+        except CloudError:
+            pass
+        return []
+
+    def list_intelligence_packs(self):
+        try:
+            response = self.log_analytics_client.workspaces.list_intelligence_packs(self.resource_group, self.name)
+            return [x.as_dict() for x in response]
+        except CloudError as exc:
+            self.fail('Error when listing intelligence packs {0}'.format(exc.message or str(exc)))
+
+    def list_management_groups(self):
+        result = []
+        try:
+            response = self.log_analytics_client.workspaces.list_management_groups(self.resource_group, self.name)
+            while True:
+                result.append(response.next().as_dict())
+        except StopIteration:
+            pass
+        except CloudError as exc:
+            self.fail('Error when listing management groups {0}'.format(exc.message or str(exc)))
+        return result
+
+    def list_usages(self):
+        result = []
+        try:
+            response = self.log_analytics_client.workspaces.list_usages(self.resource_group, self.name)
+            while True:
+                result.append(response.next().as_dict())
+        except StopIteration:
+            pass
+        except CloudError as exc:
+            self.fail('Error when listing usages {0}'.format(exc.message or str(exc)))
+        return result
+
+    def get_shared_keys(self):
+        try:
+            return self.log_analytics_client.workspaces.get_shared_keys(self.resource_group, self.name).as_dict()
+        except CloudError as exc:
+            self.fail('Error when getting shared key {0}'.format(exc.message or str(exc)))
+
+    def to_dict(self, workspace):
+        result = workspace.as_dict()
+        result['sku'] = _camel_to_snake(workspace.sku.name)
+        if self.show_intelligence_packs:
+            result['intelligence_packs'] = self.list_intelligence_packs()
+        if self.show_management_groups:
+            result['management_groups'] = self.list_management_groups()
+        if self.show_shared_keys:
+            result['shared_keys'] = self.get_shared_keys()
+        if self.show_usages:
+            result['usages'] = self.list_usages()
+        return result
+
+
+def main():
+    AzureRMLogAnalyticsWorkspaceFact()
+
+
+if __name__ == '__main__':
+    main()
diff --git a/packaging/requirements/requirements-azure.txt b/packaging/requirements/requirements-azure.txt
index 59485beb47a..d0eb4fadc98 100644
--- a/packaging/requirements/requirements-azure.txt
+++ b/packaging/requirements/requirements-azure.txt
@@ -32,3 +32,4 @@ azure-graphrbac==0.40.0
 azure-mgmt-cosmosdb==0.5.2
 azure-mgmt-hdinsight==0.1.0
 azure-mgmt-devtestlabs==3.0.0
+azure-mgmt-loganalytics==0.2.0
diff --git a/test/integration/targets/azure_rm_workspace/aliases b/test/integration/targets/azure_rm_workspace/aliases
new file mode 100644
index 00000000000..15133fe639d
--- /dev/null
+++ b/test/integration/targets/azure_rm_workspace/aliases
@@ -0,0 +1,5 @@
+cloud/azure
+shippable/azure/group4
+destructive
+azure_rm_workspace
+azure_rm_workspace_facts
diff --git a/test/integration/targets/azure_rm_workspace/meta/main.yml b/test/integration/targets/azure_rm_workspace/meta/main.yml
new file mode 100644
index 00000000000..95e1952f989
--- /dev/null
+++ b/test/integration/targets/azure_rm_workspace/meta/main.yml
@@ -0,0 +1,2 @@
+dependencies:
+  - setup_azure
diff --git a/test/integration/targets/azure_rm_workspace/tasks/main.yml b/test/integration/targets/azure_rm_workspace/tasks/main.yml
new file mode 100644
index 00000000000..05efd3567cc
--- /dev/null
+++ b/test/integration/targets/azure_rm_workspace/tasks/main.yml
@@ -0,0 +1,128 @@
+- name: Prepare random number
+  set_fact:
+    name: "workspace{{ resource_group | hash('md5') | truncate(7, True, '') }}{{ 1000 | random }}"
+
+- name: Create workspace (check mode)
+  azure_rm_loganalyticsworkspace:
+      name: "{{ name }}"
+      resource_group: "{{ resource_group }}"
+      retention_in_days: 40
+  check_mode: yes
+  register: output
+
+- assert:
+      that:
+          - output.changed
+
+- name: Get workspace
+  azure_rm_loganalyticsworkspace_facts:
+      name: "{{ name }}"
+      resource_group: "{{ resource_group }}"
+  register: facts
+
+- assert:
+      that:
+          - facts.workspaces | length == 0
+
+- name: Create workspace
+  azure_rm_loganalyticsworkspace:
+      name: "{{ name }}"
+      resource_group: "{{ resource_group }}"
+      retention_in_days: 40
+  register: output
+
+- assert:
+    that:
+        - output.retention_in_days == 40
+        - output.changed
+        - output.intelligence_packs
+
+- name: Create workspace (idempontent)
+  azure_rm_loganalyticsworkspace:
+      name: "{{ name }}"
+      resource_group: "{{ resource_group }}"
+      retention_in_days: 40
+  register: output
+
+- assert:
+    that:
+        - not output.changed
+
+- name: Get workspace
+  azure_rm_loganalyticsworkspace_facts:
+      name: "{{ name }}"
+      resource_group: "{{ resource_group }}"
+  register: facts
+
+- assert:
+      that:
+          - facts.workspaces | length == 1
+          - facts.workspaces[0].id == output.id
+
+- set_fact:
+        pack: "{{ pack | default({}) | combine({output.intelligence_packs[0].name: not output.intelligence_packs[0].enabled}) }}"
+
+- name: Update intelligence pack
+  azure_rm_loganalyticsworkspace:
+      name: "{{ name }}"
+      resource_group: "{{ resource_group }}"
+      intelligence_packs: "{{ pack }}"
+  register: intelligence
+
+- assert:
+      that:
+          - intelligence.intelligence_packs[0].enabled != output.intelligence_packs[0].enabled
+
+- name: Remove workspace (check mode)
+  azure_rm_loganalyticsworkspace:
+      name: "{{ name }}"
+      resource_group: "{{ resource_group }}"
+      state: absent
+  check_mode: yes
+  register: output
+
+- assert:
+      that:
+          - output.changed
+
+- name: Get workspace
+  azure_rm_loganalyticsworkspace_facts:
+      name: "{{ name }}"
+      resource_group: "{{ resource_group }}"
+  register: facts
+
+- assert:
+      that:
+          - facts.workspaces | length == 1
+
+- name: Remove workspace
+  azure_rm_loganalyticsworkspace:
+      name: "{{ name }}"
+      resource_group: "{{ resource_group }}"
+      state: absent
+  register: output
+
+- assert:
+      that:
+          - output.changed
+
+- name: Get workspace
+  azure_rm_loganalyticsworkspace_facts:
+      name: "{{ name }}"
+      resource_group: "{{ resource_group }}"
+  register: facts
+
+- assert:
+      that:
+          - facts.workspaces | length == 0
+
+- name: Remove workspace (idempontent)
+  azure_rm_loganalyticsworkspace:
+      name: "{{ name }}"
+      resource_group: "{{ resource_group }}"
+      state: absent
+  register: output
+
+- assert:
+      that:
+          - not output.changed
diff --git a/test/runner/requirements/integration.cloud.azure.txt b/test/runner/requirements/integration.cloud.azure.txt
index 59485beb47a..d0eb4fadc98 100644
--- a/test/runner/requirements/integration.cloud.azure.txt
+++ b/test/runner/requirements/integration.cloud.azure.txt
@@ -32,3 +32,4 @@ azure-graphrbac==0.40.0
 azure-mgmt-cosmosdb==0.5.2
 azure-mgmt-hdinsight==0.1.0
 azure-mgmt-devtestlabs==3.0.0
+azure-mgmt-loganalytics==0.2.0