Azure Security Center v2

Unified security management and advanced threat protection across hybrid cloud workloads.

Analytics & SIEM · Microsoft Defender for Cloud

Details

IDAzure Security Center v2
ProviderMicrosoft
CategoryAnalytics & SIEM
From Version5.0.0
Docker Imagedemisto/crypto:1.0.0.10120494
Supported ModulesAgentix XSIAM

README

Unified security management and advanced threat protection across hybrid cloud workloads.
For more information see Azure Security Center documentation

Use Case

With Security Center, you can apply security policies across your workloads, limit your exposure to threats, and detect and respond to attacks.

Authentication

For more details about the authentication used in this integration, see Microsoft Integrations - Authentication .

  • After authorizing the Demisto app, you will get an ID, Token, and Key, which should be inserted in the integration instance configuration’s corresponding fields. After giving consent, the application has to have a role assigned so it can access the relevant resources per subscription.
  • In order to assign a role to the application after consent was given:
    • Go to the Azure Portal UI.
    • Go to Subscriptions, and then Access Control (IAM).
    • Click Add.
    • Select a role that includes the following permissions:
      • Microsoft.Security/locations/read
      • Microsoft.Security/alerts/read
      • Microsoft.Security/locations/alerts/read
      • Microsoft.Storage/storageAccounts/read
      • Microsoft.Management/managementGroups/read
      • Microsoft.Security/advancedThreatProtectionSettings/*
      • Microsoft.Security/informationProtectionPolicies/read
      • Microsoft.Security/locations/jitNetworkAccessPolicies/*
      • Microsoft.Security/locations/jitNetworkAccessPolicies/initiate/action
    • Select the Azure Security Center application.

Configure Azure Security Center v2 on Cortex XSOAR

  1. Navigate to Settings > Integrations  > Servers & Services.
  2. Search for Azure Security Center v2.
  3. Click Add instance to create and configure a new integration instance.
    • Name: a textual name for the integration instance.
    • Microsoft Azure Management URL
    • ID (received from the admin consent - see Detailed Instructions (?)
    • Token (received from the admin consent - see Detailed Instructions (?) section)
    • Key (received from the admin consent - see Detailed Instructions (?)
    • Trust any certificate (not secure)
    • Use system proxy settings
    • Default subscription ID to use
  4. Click Test to validate the new instance.

Commands

Subscription ID

Some commands require a subscription ID parameter in order to run.
You can find your organization’s subscriptions list in the Microsoft Azure Portal > Subscriptions or by running the azure-list-subscriptions command.

You can execute these commands from the Cortex XSOAR CLI, as part of an automation, or in a playbook.
After you successfully execute a command, a DBot message appears in the War Room with the command details.

  1. azure-sc-list-alert
  2. azure-sc-update-atp
  3. azure-sc-get-atp
  4. azure-sc-update-aps
  5. azure-sc-get-aps
  6. azure-sc-list-aps
  7. azure-sc-list-jit
  8. azure-sc-list-storage
  9. azure-list-subscriptions
  10. azure-sc-list-location
  11. azure-sc-get-alert
  12. azure-get-secure-score

1. azure-sc-list-alert


Lists alerts for the subscription according to the specified filters.
The OData select, expand, and filter arguments are deprecated, as they are no longer supported by Microsoft Defender for Cloud and have no effect.

Require Subscription ID

Base Command

azure-sc-list-alert

Input
Argument Name Description Required
resource_group_name The name of the resource group within the user’s subscription. The name is case insensitive. Optional
asc_location The location where Azure Security Center stores the data of the subscription. Run the ‘azure-sc-list-location’ command to get the ascLocation. This command requires the resourceGroupName argument. Optional
subscription_id Subscription ID to use. Can be retrieved from the azure-sc-list-subscriptions command. If not specified, the default subscripton ID will be used. Optional
Context Output
Path Type Description
AzureSecurityCenter.Alert.DisplayName string The display name of the alert.
AzureSecurityCenter.Alert.CompromisedEntity string The entity on which the incident occurred.
AzureSecurityCenter.Alert.Description string Description of the suspicious activity that was detected.
AzureSecurityCenter.Alert.DetectedTime date The time the vendor detected the incident.
AzureSecurityCenter.Alert.ReportedSeverity string The estimated severity of this alert.
AzureSecurityCenter.Alert.State string The alert state (Active, Dismissed, etc.).
AzureSecurityCenter.Alert.ID string The alert ID.
Command Example

!azure-sc-list-alert

Context Example
{
    "AzureSecurityCenter.Alert": [
        {
            "CompromisedEntity": "alerts",
            "Description": "Azure security center has detected incoming traffic from IP addresses, which have been identified as IP addresses that should be blocked by the Adaptive Network Hardening control",
            "DetectedTime": "2019-10-27T00:00:00Z",
            "DisplayName": "Traffic from unrecommended IP addresses was detected",
            "ID": "2518301663999999999_d1521d81-f4c1-40ae-b224-01456637790c",
            "ReportedSeverity": "Information",
            "State": "Active"
        }
    ]
}
Human Readable Output

Azure Security Center - List Alerts

DisplayName CompromisedEntity DetectedTime ReportedSeverity State Description ID
Traffic from unrecommended IP addresses was detected alerts 2019-10-27T00:00:00Z Information Active Azure security center has detected incoming traffic from IP addresses, which have been identified as IP addresses that should be blocked by the Adaptive Network Hardening control 2518301663999999999_d1521d81-f4c1-40ae-b224-01456637790c

2. azure-sc-update-atp


Updates Advanced Threat Detection settings.

Require Subscription ID

Base Command

azure-sc-update-atp

Input
Argument Name Description Required
resource_group_name Resource group name Required
setting_name Name of the Advanced Threat Detection setting, default is ‘current’. Optional
storage_account Storage name in your Azure account Required
is_enabled Indicates whether Advanced Threat Protection is enabled. Required
subscription_id Subscription ID to use. Can be retrieved from the azure-sc-list-subscriptions command. If not specified, the default subscripton ID will be used. Optional
Context Output
Path Type Description
AzureSecurityCenter.AdvancedThreatProtection.ID string Resource ID
AzureSecurityCenter.AdvancedThreatProtection.Name string Resource Name
AzureSecurityCenter.AdvancedThreatProtection.IsEnabled string Indicates whether Advanced Threat Protection is enabled
Command Example

!azure-sc-update-atp resource_group_name=recouce_name

3. azure-sc-get-atp


Returns the Advanced Threat Protection setting.

Require Subscription ID

Base Command

azure-sc-get-atp

Input
Argument Name Description Required
resource_group_name Name of the resource group. Required
setting_name Name of Advanced Threat Detection setting, default setting’s name is ‘current’. Optional
storage_account Name of a storage in your azure account. Required
subscription_id Subscription ID to use. Can be retrieved from the azure-sc-list-subscriptions command. If not specified, the default subscripton ID will be used. Optional
Context Output
Path Type Description
AzureSecurityCenter.AdvancedThreatProtection.ID string Resource ID
AzureSecurityCenter.AdvancedThreatProtection.Name string Resource name
AzureSecurityCenter.AdvancedThreatProtection.IsEnabled string Indicates whether Advanced Threat Protection is enabled
Command Example

!azure-sc-get-atp resource_group_name=resource_group storage_account=st_acc1

4. azure-sc-update-aps


Updates a specific auto provisioning setting.

Require Subscription ID

Base Command

azure-sc-update-aps

Input
Argument Name Description Required
setting_name Name of the auto provisioning setting, default setting’s name is ‘default’ Required
auto_provision Describes the type of security agent provisioning action to take (On or Off) Required
subscription_id Subscription ID to use. Can be retrieved from the azure-sc-list-subscriptions command. If not specified, the default subscripton ID will be used. Optional
Context Output
Path Type Description
AzureSecurityCenter.AutoProvisioningSetting.Name string Setting display name
AzureSecurityCenter.AutoProvisioningSetting.AutoProvision string Display the type of security agent provisioning action to take (On or Off)
AzureSecurityCenter.AutoProvisioningSetting.ID string Setting resource ID
Command Example

!azure-sc-update-aps setting_name=default auto_provision=Off

Context Example
{
    "AzureSecurityCenter.AutoProvisioningSetting": [
        {
            "AutoProvision": null,
            "ID": "/subscriptions/xxxx-xxxx-xxxx-xxxx-xxxx/providers/Microsoft.Security/autoProvisioningSettings/default",
            "Name": "default"
        }
    ]
}
Human Readable Output

Azure Security Center - Update Auto Provisioning Setting

Name ID
default /subscriptions/xxxx-xxxx-xxxx-xxxx-xxxx/providers/Microsoft.Security/autoProvisioningSettings/default

5. azure-sc-get-aps


Returns details of a specific auto provisioning setting.

Require Subscription ID

Base Command

azure-sc-get-aps

Input
Argument Name Description Required
setting_name Name of the auto provisioning setting Required
subscription_id Subscription ID to use. Can be retrieved from the azure-sc-list-subscriptions command. If not specified, the default subscripton ID will be used. Optional
Context Output
Path Type Description
AzureSecurityCenter.AutoProvisioningSetting.Name string Setting display name
AzureSecurityCenter.AutoProvisioningSetting.AutoProvision string Display the type of security agent provisioning action to take (On or Off)
AzureSecurityCenter.AutoProvisioningSetting.ID string Set resource ID
Command Example

!azure-sc-get-aps setting_name=default

Context Example
{
    "AzureSecurityCenter.AutoProvisioningSetting": [
        {
            "AutoProvision": "Off",
            "ID": "/subscriptions/0xxxx-xxxx-xxxx-xxxx-xxxx/providers/Microsoft.Security/autoProvisioningSettings/default",
            "Name": "default"
        }
    ]
}
Human Readable Output

Azure Security Center - Get Auto Provisioning Setting

Name AutoProvision ID
default Off /subscriptions/xxxx-xxxx-xxxx-xxxx-xxxx/providers/Microsoft.Security/autoProvisioningSettings/default

6. azure-sc-list-aps


Lists auto provisioning settings in the subscription.

Require Subscription ID

Base Command

azure-sc-list-aps

Input
Argument Name Description Required
subscription_id Subscription ID to use. Can be retrieved from the azure-sc-list-subscriptions command. If not specified, the default subscripton ID will be used. Optional
Context Output
Path Type Description
AzureSecurityCenter.AutoProvisioningSetting.Name string Setting display name
AzureSecurityCenter.AutoProvisioningSetting.AutoProvision string Display the type of security agent provisioning action to take (On or Off)
AzureSecurityCenter.AutoProvisioningSetting.ID string Setting resource ID
Command Example

!azure-sc-list-aps

Context Example
{
    "AzureSecurityCenter.AutoProvisioningSetting": [
        {
            "AutoProvision": "Off",
            "ID": "/subscriptions/xxxx-xxxx-xxxx-xxxx-xxxx/providers/Microsoft.Security/autoProvisioningSettings/default",
            "Name": "default"
        }
    ]
}
Human Readable Output

Azure Security Center - List Auto Provisioning Settings

Name AutoProvision ID
default Off /subscriptions/xxxx-xxxx-xxxx-xxxx-xxxx/providers/Microsoft.Security/autoProvisioningSettings/default

7. azure-sc-list-jit


Lists all policies for protecting resources using Just-in-Time access control.

Require Subscription ID

Base Command

azure-sc-list-jit

Input
Argument Name Description Required
asc_location The location where Azure Security Center stores the data of the subscription. Run the ‘azure-sc-list-location’ command to get the asc_location. Optional
resource_group_name The name of the resource group within the user’s subscription. The name is case insensitive. Optional
subscription_id Subscription ID to use. Can be retrieved from the azure-sc-list-subscriptions command. If not specified, the default subscripton ID will be used. Optional
Context Output
Path Type Description
AzureSecurityCenter.JITPolicy.Name string Poliyc display name
AzureSecurityCenter.JITPolicy.Rules string CSV list of access rules for Microsoft.Compute/virtualMachines resource, in the format (VMName: allowPort1,…)
AzureSecurityCenter.JITPolicy.Location string Location where the resource is stored
AzureSecurityCenter.JITPolicy.Kind string Policy resource type
Command Example

!azure-sc-list-jit

8. azure-sc-list-storage


Lists all the storage accounts available under the subscription.

Require Subscription ID

Base Command

azure-sc-list-storage

Input
Argument Name Description Required
subscription_id Subscription ID to use. Can be retrieved from the azure-sc-list-subscriptions command. If not specified, the default subscripton ID will be used. Optional
Context Output
Path Type Description
AzureSecurityCenter.Storage.Name string Name of the storage account
AzureSecurityCenter.Storage.ResourceGroupName string Names of the attached resource group
AzureSecurityCenter.Storage.Location string The geo-location where the resource resides
Command Example

!azure-sc-list-storage

Context Example
{
    "AzureSecurityCenter.Storage": [
        {
            "ID": "/subscriptions/xxxx-xxxx-xxxx-xxxx-xxxx/resourceGroups/cloud-shell-storage-eastus/providers/Microsoft.Storage/storageAccounts/cs20f907ea4bc8bx4c11x9d7",
            "Location": "eastus",
            "Name": "cs20f907ea4bc8bx4c11x9d7",
            "ResourceGroupName": "cloud-shell-storage-eastus"
        }
    ]
}
Human Readable Output

Azure Security Center - List Storage Accounts

Name ResourceGroupName Location
cs20f907ea4bc8bx4c11x9d7 cloud-shell-storage-eastus eastus
useastrgdiag204 us-east-rg eastus
demistodevops cloud-shell-storage-eastus westeurope

9. azure-list-subscriptions


List available subscriptions for this application.

Base Command

azure-list-subscriptions

Input

There are no input arguments for this command.

Context Output
Path Type Description
Azure.Subscription.ID String Subscription ID
Azure.Subscription.Name String Subscription Name
Azure.Subscription.Enabled String Subscription state
Command Example

!azure-list-subscriptions

Context Example
{
    "Azure.Subscription": [
        {
            "ID": "/subscriptions/xxxx-xxxx-xxxx-xxxx-xxxx",
            "Name": "Pay-As-You-Go",
            "State": "Enabled"
        }
    ]
}
Human Readable Output

Azure Security Center - Subscriptions

ID Name State
/subscriptions/xxxx-xxxx-xxxx-xxxx-xxxx Pay-As-You-Go Enabled

List of Subscriptions

ID Name State
/subscriptions/xxxx-xxxx-xxxx-xxxx-xxxx Pay-As-You-Go Enabled

10. azure-sc-list-location


The location of the responsible ASC of the specific subscription. For each subscription there is only one responsible location.

Require Subscription ID

Base Command

azure-sc-list-location

Input

There are no input arguments for this command.

Context Output

There are no context output for this command.

Command Example

!azure-sc-list-location

Context Example
{
    "AzureSecurityCenter.Location": [
        {
            "HomeRegionName": "centralus",
            "ID": "/subscriptions/xxxx-xxxx-xxxx-xxxx-xxxx/providers/Microsoft.Security/locations/centralus",
            "Name": "centralus"
        }
    ]
}
Human Readable Output

Azure Security Center - List Locations

HomeRegionName Name ID
centralus centralus /subscriptions/xxxx-xxxx-xxxx-xxxx-xxxx/providers/Microsoft.Security/locations/centralus

11. azure-sc-get-alert


Get an alert that is associated a resource group or a subscription.

Require Subscription ID

Base Command

azure-sc-get-alert

Input
Argument Name Description Required
resource_group_name The name of the resource group within the user’s subscription. The name is case insensitive. Optional
asc_location The location where Azure Security Center stores the data of the subscription. Run the ‘azure-sc-list-location’ command to get the ascLocation. This command requires the resourceGroupName argument. Required
alert_id The alert ID. Optional
Context Output
Path Type Description
AzureSecurityCenter.Alert.DisplayName string The display name of the alert.
AzureSecurityCenter.Alert.CompromisedEntity string The entity on which the incident occurred.
AzureSecurityCenter.Alert.DetectedTime date The time the vendor detected the incident.
AzureSecurityCenter.Alert.ReportedSeverity string The estimated severity of the alert.
AzureSecurityCenter.Alert.State string The alert state (Active, Dismissed, etc.).
AzureSecurityCenter.Alert.RemediationSteps string Recommended steps to remediate the incident.
AzureSecurityCenter.Alert.VendorName string Name of the vendor that discovered the incident.
AzureSecurityCenter.Alert.AlertName string Name of the alert type.
AzureSecurityCenter.Alert.ID string The alert ID.
AzureSecurityCenter.Alert.Description string Description of the incident and what it means.
AzureSecurityCenter.Alert.ExtendedProperties string Changing set of properties depending on the alert type.
AzureSecurityCenter.Alert.Entities string Objects that are related to the alert.
Command Example

!azure-sc-get-alert asc_location="location" alert_id="alert_id"

Additional Information

For more information regarding roles, see the microsoft documentation.

12. azure-get-secure-score


Retrieve the Secure Score for the provided subscription and score name

Base Command

azure-get-secure-score

Input

Argument Name Description Required
secure_score_name description. Possible values are: . Default is ascScore. Optional
subscription_id The subscription ID to use. Can be retrieved from the azure-sc-list-subscriptions command. If not specified, the default subscription ID is used. Possible values are: . Optional

Context Output

Path Type Description
Azure.Securescore.displayName String The initiative’s name.
Azure.Securescore.score.max String The max score of the Securescore.
Azure.Securescore.score.current String The current score of the Securescore.
Azure.Securescore.score.percentage String The Ratio of the current score divided by the maximum.
Azure.Securescore.weight String The relative weight for each subscription.

Command Example


#### Context Example

```json
{
    "Azure": {
        "Securescore": {
            "displayName": "ASC score",
            "score": {
                "current": 14.51,
                "max": 58,
                "percentage": 0.2502
            },
            "weight": 199
        }
    }
}

Human Readable Output

Azure Security Center - Secure Score

displayName score weight
ASC score max: 58
current: 14.51
percentage: 0.2502
199

13. azure-sc-update-alert


Update an alert’s state.

Base Command

azure-sc-update-alert

Input

Argument Name Description Required
resource_group_name The name of the resource group within the user’s subscription. The name is case insensitive. Optional
asc_location The location where Azure Security Center stores the data of the subscription. Run the ‘azure-sc-list-location’ command to get the ascLocation. This command requires the resourceGroupName argument. Required
alert_id The alert ID. Required
subscription_id The subscription ID to use. Can be retrieved from the azure-sc-list-subscriptions command. If not specified, the default subscription ID is used. Optional
alert_update_action_type The update action type. Possible values are: dismiss. Required

Context Output

Path Type Description
AzureSecurityCenter.Alert.ActionTaken string The action that was taken on the alert.
AzureSecurityCenter.Alert.ID string The alert ID.

azure-sc-auth-reset


Run this command if for some reason you need to rerun the authentication process.

Base Command

azure-sc-auth-reset

Input

There are no input arguments for this command.

Context Output

There is no context output for this command.

azure-resource-group-list


List all resource groups for a subscription.

Base Command

azure-resource-group-list

Input

Argument Name Description Required
subscription_id The subscription ID to use. Can be retrieved from the azure-sc-list-subscriptions command. If not specified, the default subscription ID is used. Optional
limit Limit on the number of resource groups to return. Default is 50. Optional
tag A single tag in the form of ‘{“Tag Name”:”Tag Value”}’ to filter the list by. Optional

Context Output

Path Type Description
Azure.ResourceGroupName.name String Resource group name.
Azure.ResourceGroupName.location String Resource group location.
Azure.ResourceGroupName.tags Unknown Resource group tags.
Azure.ResourceGroupName.properties.provisioningState unknown Resource group provisioning state.

Configuration parameters

  • server_url — Microsoft Azure Management URL
  • auth_id — ID (received from the admin consent - see Detailed Instructions (?)
  • credentials_auth_id
  • tenant_id — Token (received from the admin consent - see Detailed Instructions (?) section)
  • credentials_tenant_id
  • enc_key — Key (received from the admin consent - see Detailed Instructions (?)
  • credentials_enc_key
  • certificate_thumbprint — Certificate Thumbprint
  • credentials_certificate_thumbprint
  • private_key — Private Key
  • default_sub_id — Default subscription ID to use
  • credentials_default_sub_id
  • resource_group_name — Default Resource Group Name
  • use_managed_identities — Use Azure Managed Identities
  • managed_identities_client_id
  • unsecure — Trust any certificate (not secure)
  • proxy — Use system proxy settings
  • self_deployed — Use a self-deployed Azure Application

Commands (15)

  • azure-get-secure-score

    Retrieve the Secure Score for the provided subscription and score name.

  • azure-list-subscriptions

    Lists available subscriptions for this application.

  • azure-resource-group-list

    List all resource groups for a subscription.

  • azure-sc-auth-reset

    Run this command if for some reason you need to rerun the authentication process.

  • azure-sc-get-alert

    Gets an alert that is associated with a resource group or a subscription. The subscription_id argument is required in case it was not defined in the integration's configuration.

  • azure-sc-get-aps

    Returns details of a specific auto provisioning setting.

  • azure-sc-get-atp

    Returns the Advanced Threat Protection setting.

  • azure-sc-list-alert

    Lists alerts for the subscription according to the specified filters.

  • azure-sc-list-aps

    Lists auto provisioning settings in the subscription.

  • azure-sc-list-jit

    Lists all policies for protecting resources using Just-in-Time access control.

  • azure-sc-list-location

    The location of the responsible ASC of the specific subscription. For each subscription there is only one responsible location.

  • azure-sc-list-storage

    Lists all the storage accounts available under the subscription.

  • azure-sc-update-alert

    Update an alert's state.

  • azure-sc-update-aps

    Updates a specific auto provisioning setting.

  • azure-sc-update-atp

    Updates Advanced Threat Detection settings.

import ast

import demistomock as demisto
from CommonServerPython import *
from MicrosoftApiModule import *  # noqa: E402

""" GLOBAL VARS """

APP_NAME = "ms-azure-sc"
SUB_ID_REQUIRING_CMD = (
    "azure-sc-get-alert",
    "azure-sc-list-alert",
    "azure-sc-update-alert",
    "azure-sc-list-location",
    "azure-sc-update-atp",
    "azure-sc-get-atp",
    "azure-sc-update-aps",
    "azure-sc-list-aps",
    "azure-sc-get-aps",
    "azure-sc-list-jit",
    "azure-sc-get-jit",
    "azure-sc-initiate-jit",
    "azure-sc-delete-jit",
    "azure-sc-list-storage",
)
RESOURCE_GROUP_REQUIRING_CMD = ("azure-sc-update-atp", "azure-sc-get-atp")
DEFAULT_LIMIT = 50
# API Versions
SUBSCRIPTION_API_VERSION = "2015-01-01"
ALERT_API_VERSION = "2022-01-01"
LOCATION_API_VERSION = "2015-06-01-preview"
ATP_API_VERSION = "2017-08-01-preview"
APS_API_VERSION = "2017-08-01-preview"
IPP_API_VERSION = "2017-08-01-preview"
JIT_API_VERSION = "2015-06-01-preview"
STORAGE_API_VERSION = "2018-07-01"
SECURE_STORES_API_VERSION = "2020-01-01"
LIST_RESOURCE_GROUP_VERSION = "2021-04-01"

""" HELPER FUNCTIONS """


# Format ports in JIT access policy rule to (portNum, protocol, allowedAddress, maxDuration)
def format_jit_port_rule(ports):
    port_array = []
    for port in ports:
        # for each item in unicode, has to use str to decode to ascii
        p_num = str(port.get("number"))
        p_src_addr = str(port.get("allowedSourceAddressPrefix")) if port.get("allowedSourceAddressPrefix") != "*" else "any"
        p_protocol = str(port.get("protocol")) if port.get("protocol") != "*" else "any"
        p_max_duration = str(port.get("maxRequestAccessDuration"))
        port_array.append(str((p_num, p_protocol, p_src_addr, p_max_duration)))
    return ", ".join(port_array)


# Format ports in JIT access request to (portNum, allowedAddress, endTime, status)
def format_jit_port_request(ports):
    port_array = []
    for port in ports:
        # for each item in unicode, has to use str to decode to ascii
        p_num = str(port.get("number"))
        p_src_addr = str(port.get("allowedSourceAddressPrefix")) if port.get("allowedSourceAddressPrefix") != "*" else "any"
        p_status = str(port.get("status"))
        p_end_time = str(port.get("endTimeUtc"))
        port_array.append(str((p_num, p_src_addr, p_end_time, p_status)))
    return ", ".join(port_array)


def normalize_context_key(string):
    """Normalize context keys
    Function will normalize the string (remove white spaces and tailings)
    Args:
        string (str):
    Returns:
        Normalized string
    """
    tmp = string[:1].upper() + string[1:]
    return tmp.replace(" ", "")


class MsClient:
    """
    Microsoft Client enables authorized access to Azure Security Center.
    """

    def __init__(
        self,
        tenant_id,
        auth_id,
        enc_key,
        app_name,
        server,
        verify,
        proxy,
        self_deployed,
        subscription_id,
        ok_codes,
        certificate_thumbprint,
        private_key,
        resource_group_name=None,
        managed_identities_client_id=None,
    ):
        base_url_with_subscription = f"{server}subscriptions/{subscription_id}/"
        self.ms_client = MicrosoftClient(
            tenant_id=tenant_id,
            auth_id=auth_id,
            enc_key=enc_key,
            app_name=app_name,
            base_url=base_url_with_subscription,
            verify=verify,
            proxy=proxy,
            self_deployed=self_deployed,
            ok_codes=ok_codes,
            scope="https://management.azure.com/.default",
            certificate_thumbprint=certificate_thumbprint,
            private_key=private_key,
            managed_identities_client_id=managed_identities_client_id,
            managed_identities_resource_uri=Resources.management_azure,
            command_prefix="azure-sc",
        )
        self.server = server
        self.subscription_id = subscription_id
        self.resource_group_name = resource_group_name

    def get_alert(self, asc_location, alert_id):
        """
        Args:
            asc_location (str): Azure Security Center location
            alert_id (str): Alert ID

        Returns:
            response body (dict)

        """
        cmd_url = f"/resourceGroups/{self.resource_group_name}" if self.resource_group_name else ""
        cmd_url += f"/providers/Microsoft.Security/locations/{asc_location}/alerts/{alert_id}"
        params = {"api-version": ALERT_API_VERSION}
        return self.ms_client.http_request(method="GET", url_suffix=cmd_url, params=params)

    def list_alerts(self, asc_location, filter_query, select_query, expand_query):
        """Listing alerts

        Args:
            asc_location (str): Azure Security Center location
            filter_query (str): what to filter
            select_query (str): what to select
            expand_query (str): what to expand

        Returns:
            dict: contains response body
        """
        if self.resource_group_name:
            cmd_url = f"/resourceGroups/{self.resource_group_name}/providers/Microsoft.Security"
            # ascLocation must be using with specifying resourceGroupName
            if asc_location:
                cmd_url += f"/locations/{asc_location}"
            cmd_url += "/alerts"
        else:
            cmd_url = "/providers/Microsoft.Security/alerts"

        params = {"api-version": ALERT_API_VERSION}
        if filter_query:
            params["$filter"] = filter_query
        if select_query:
            params["$select"] = select_query
        if expand_query:
            params["$expand"] = expand_query

        return self.ms_client.http_request(method="GET", url_suffix=cmd_url, params=params)

    def update_alert(self, asc_location, alert_id, alert_update_action_type):
        """
        Args:
            asc_location (str): Azure Security Center Location
            alert_id (str): Alert ID
            alert_update_action_type (str): What update type need to update

        Returns:
            dict: response body
        """
        cmd_url = f"/resourceGroups/{self.resource_group_name}" if self.resource_group_name else ""
        cmd_url += f"/providers/Microsoft.Security/locations/{asc_location}/alerts/{alert_id}/{alert_update_action_type}"
        params = {"api-version": ALERT_API_VERSION}
        #  Using resp_type=response to avoid parsing error.
        self.ms_client.http_request(method="POST", url_suffix=cmd_url, params=params, resp_type="response")

    def list_locations(self):
        """
        Returns:
            dict: response body
        """
        cmd_url = "/providers/Microsoft.Security/locations"
        params = {"api-version": LOCATION_API_VERSION}
        return self.ms_client.http_request(method="GET", url_suffix=cmd_url, params=params)

    def update_atp(self, storage_account, setting_name, is_enabled):
        """
        Args:
            storage_account (str): Storange Account
            setting_name (str):  Setting Name
            is_enabled (str): true/false

        Returns:
            dict: respones body
        """
        cmd_url = (
            f"/resourceGroups/{self.resource_group_name}/providers/Microsoft.Storage/storageAccounts/"
            f"{storage_account}/providers/Microsoft.Security/advancedThreatProtectionSettings/{setting_name}"
        )
        params = {"api-version": ATP_API_VERSION}
        data = {
            "id": f"/subscriptions/{self.subscription_id}/resourceGroups/{self.resource_group_name}/providers/"
            f"Microsoft.Storage/storageAccounts/{storage_account}/providers/Microsoft.Security/"
            f"advancedThreatProtectionSettings/{setting_name}",
            "name": setting_name,
            "type": "Microsoft.Security/advancedThreatProtectionSettings",
            "properties": {"isEnabled": is_enabled},
        }

        #  Using resp_type=response to avoid parsing error.
        return self.ms_client.http_request(method="PUT", url_suffix=cmd_url, json_data=data, params=params)

    def get_atp(self, storage_account, setting_name):
        """
        Args:
            storage_account (str): Storange Account
            setting_name (str):  Setting Name

        Returns:

        """
        cmd_url = (
            f"/resourceGroups/{self.resource_group_name}/providers/Microsoft.Storage/storageAccounts"
            f"/{storage_account}/providers/Microsoft.Security/advancedThreatProtectionSettings/{setting_name}"
        )
        params = {"api-version": ATP_API_VERSION}
        return self.ms_client.http_request(method="GET", url_suffix=cmd_url, params=params)

    def update_aps(self, setting_name, auto_provision):
        """
        Args:
            setting_name (str): Setting name
            auto_provision (str): Auto provision setting (On/Off)

        Returns:
            dict: response body
        """
        cmd_url = f"/providers/Microsoft.Security/autoProvisioningSettings/{setting_name}"
        params = {"api-version": APS_API_VERSION}

        data = {"properties": {"autoProvision": auto_provision}}

        return self.ms_client.http_request(method="PUT", url_suffix=cmd_url, json_data=data, params=params)

    def list_aps(self):
        """
        Returns:
            dict: response body
        """
        cmd_url = "/providers/Microsoft.Security/autoProvisioningSettings"
        params = {"api-version": APS_API_VERSION}
        return self.ms_client.http_request(method="GET", url_suffix=cmd_url, params=params)

    def get_aps(self, setting_name):
        """
        Args:
            setting_name: Setting name

        Returns:
            dict: response body
        """
        cmd_url = f"/providers/Microsoft.Security/autoProvisioningSettings/{setting_name}"
        params = {"api-version": APS_API_VERSION}

        return self.ms_client.http_request(method="GET", url_suffix=cmd_url, params=params)

    def list_ipp(self, management_group=None):
        """
        Args:
            management_group: Managment group to pull (if needed)

        Returns:
            dict: response body

        """
        params = {"api-version": IPP_API_VERSION}
        cmd_url = "/providers/Microsoft.Security/informationProtectionPolicies"
        if management_group:
            full_url = f"{self.server}/providers/Microsoft.Management/managementGroups/{management_group}"
            full_url += cmd_url
            return self.ms_client.http_request(method="GET", full_url=full_url, url_suffix="", params=params)
        if not self.subscription_id:
            raise DemistoException("A subscription ID must be provided.")
        return self.ms_client.http_request(method="GET", url_suffix=cmd_url, params=params)

    def get_ipp(self, policy_name, management_group):
        """
        Args:
            policy_name (str): Policy name
            management_group (str): Managment group

        Returns:
            dict: respone body
        """
        params = {"api-version": IPP_API_VERSION}

        cmd_url = f"/providers/Microsoft.Security/informationProtectionPolicies/{policy_name}"
        if management_group:
            full_url = f"{self.server}/providers/Microsoft.Management/managementGroups/{management_group}"
            full_url += cmd_url
            return self.ms_client.http_request(method="GET", full_url=full_url, url_suffix="", params=params)
        return self.ms_client.http_request(method="GET", url_suffix=cmd_url, params=params)

    def list_jit(self, asc_location):
        """
        Args:
            asc_location: Machine location

        Returns:
            dict: response body
        """
        params = {"api-version": JIT_API_VERSION}
        cmd_url = f"/resourceGroups/{self.resource_group_name}" if self.resource_group_name else ""
        cmd_url += f"/providers/Microsoft.Security/locations/{asc_location}" if asc_location else ""
        cmd_url += "/providers/Microsoft.Security/jitNetworkAccessPolicies"
        return self.ms_client.http_request(method="GET", url_suffix=cmd_url, params=params)

    def get_jit(self, policy_name, asc_location, resource_group_name):
        """
        Args:
            policy_name: Policy name
            asc_location: Machine location
            resource_group_name: Resource name group

        Returns:
            dict: response body
        """
        cmd_url = (
            f"/resourceGroups/{resource_group_name}/providers/Microsoft.Security/locations/{asc_location}/"
            f"jitNetworkAccessPolicies/{policy_name}"
        )
        params = {"api-version": JIT_API_VERSION}

        return self.ms_client.http_request(method="GET", url_suffix=cmd_url, params=params)

    def initiate_jit(self, resource_group_name, asc_location, policy_name, vm_id, port, source_address, duration):
        """Starting new Just-in-time machine

        Args:
            resource_group_name: Resource group name
            asc_location: Machine location
            policy_name: Policy name
            vm_id: Virtual Machine ID
            port: ports to be used
            source_address: Source address
            duration: Time in

        Returns:
            dict: response body
        """
        cmd_url = (
            f"/resourceGroups/{resource_group_name}/providers/Microsoft.Security/locations/{asc_location}/"
            f"jitNetworkAccessPolicies/{policy_name}/initiate"
        )
        params = {"api-version": JIT_API_VERSION}

        # only supports init access for one vm and one port now
        data = {
            "virtualMachines": [
                {
                    "ID": vm_id,
                    "ports": [
                        {
                            "number": port,
                            "duration": duration,
                            "allowedSourceAddressPrefix": source_address,
                        }
                    ],
                }
            ]
        }
        # response code should be 202 Accepted
        return self.ms_client.http_request(method="POST", url_suffix=cmd_url, json_data=data, params=params, resp_type="response")

    def delete_jit(self, asc_location, resource_group_name, policy_name):
        """
        Args:
            asc_location: Machine location
            resource_group_name: Resource group name
            policy_name: Policy name
        """
        cmd_url = (
            f"/resourceGroups/{resource_group_name}/providers/Microsoft.Security/locations/{asc_location}/"
            f"jitNetworkAccessPolicies/{policy_name}"
        )

        params = {"api-version": JIT_API_VERSION}

        #  Using resp_type=text to avoid parsing error. response should be 204
        self.ms_client.http_request(method="DELETE", url_suffix=cmd_url, params=params, resp_type="text")

    def list_sc_storage(self):
        """
        Returns:
            dict: response body

        """
        cmd_url = "/providers/Microsoft.Storage/storageAccounts"
        params = {"api-version": STORAGE_API_VERSION}
        return self.ms_client.http_request(method="GET", url_suffix=cmd_url, params=params)

    def list_sc_subscriptions(self):
        """
        Returns:
            dict: response body

        """
        full_url = f"{self.server}/subscriptions"
        params = {"api-version": SUBSCRIPTION_API_VERSION}
        return self.ms_client.http_request(method="GET", full_url=full_url, url_suffix="", params=params)

    def get_secure_scores(self, secure_score_name):
        """
        Returns:
            dict: response body

        """

        cmd_url = f"/providers/Microsoft.Security/secureScores/{secure_score_name}"
        params = {"api-version": SECURE_STORES_API_VERSION}
        return self.ms_client.http_request(method="GET", url_suffix=cmd_url, params=params)

    def list_resource_groups(self, tag: str, limit: int, full_url: Optional[str] = None) -> dict:
        """
        List all resource groups.

        Args:
            tag str: Tag to filter by.
            limit (int): Maximum number of resource groups to retrieve. Default is 50.

        Returns:
            List[dict]: API response from Azure.
        """
        filter_by_tag = azure_tag_formatter(tag) if tag else None
        params = {"$filter": filter_by_tag, "$top": limit, "api-version": LIST_RESOURCE_GROUP_VERSION} if not full_url else {}
        return self.ms_client.http_request(method="GET", url_suffix="resourcegroups", params=params, full_url=full_url)


""" FUNCTIONS """

""" Alert Start """


def get_alert_command(client: MsClient, args: dict):
    """Getting specified alert from API
    Args
        args (dict): dictionary containing commands args
    """
    asc_location = args.get("asc_location")
    alert_id = args.get("alert_id")
    alert = client.get_alert(asc_location, alert_id)
    final_output = []

    # Basic Property Table
    properties = alert.get("properties")
    if properties:
        basic_table_output = [
            {
                "DisplayName": properties.get("alertDisplayName"),
                "CompromisedEntity": properties.get("compromisedEntity"),
                "Description": properties.get("description"),
                "DetectedTime": properties.get("timeGeneratedUtc"),
                "ReportedSeverity": properties.get("severity"),
                "State": properties.get("status"),
                "RemediationSteps": properties.get("remediationSteps"),
                "VendorName": properties.get("vendorName"),
                "ID": alert.get("name"),
                "ExtendedProperties": properties.get("extendedProperties"),
                "Entities": properties.get("entities"),
            }
        ]

        md = tableToMarkdown(
            "Azure Security Center - Get Alert - Basic Property",
            basic_table_output,
            [
                "DisplayName",
                "CompromisedEntity",
                "Description",
                "DetectedTime",
                "ReportedSeverity",
                "State",
                "RemediationSteps",
                "VendorName",
                "AlertName",
                "ID",
            ],
            removeNull=True,
        )

        ec = {"AzureSecurityCenter.Alert(val.ID && val.ID === obj.ID)": basic_table_output}

        basic_table_entry = {
            "Type": entryTypes["note"],
            "Contents": alert,
            "ContentsFormat": formats["json"],
            "ReadableContentsFormat": formats["markdown"],
            "HumanReadable": md,
            "EntryContext": ec,
        }
        final_output.append(basic_table_entry)

        # Extended Properties Table
        if alert.get("properties") and alert.get("properties") and alert.get("properties").get("extendedProperties"):
            extended_properties = {}
            properties = alert.get("properties")
            if isinstance(properties.get("extendedProperties"), dict):
                for key, value in alert["properties"]["extendedProperties"].items():
                    extended_properties[normalize_context_key(key)] = value
                extended_table_entry = {
                    "Type": entryTypes["note"],
                    "Contents": alert["properties"]["extendedProperties"],
                    "ContentsFormat": formats["json"],
                    "ReadableContentsFormat": formats["markdown"],
                    "HumanReadable": tableToMarkdown(
                        "Azure Security Center - Get Alert - Extended Property",
                        extended_properties,
                        removeNull=True,
                    ),
                }
                final_output.append(extended_table_entry)

            # Entities Table
            entities = properties.get("entities")
            if entities and isinstance(entities, dict):
                entities_table_output = []
                for entity in entities:
                    entities_table_output.append(
                        {
                            "Content": ast.literal_eval(str(entity)),
                            "Type": entity["type"],
                        }
                    )

                md = tableToMarkdown(
                    "Azure Security Center - Get Alert - Entity",
                    entities_table_output,
                    removeNull=True,
                )

                entities_table_entry = {
                    "Type": entryTypes["note"],
                    "Contents": alert.get("properties").get("entities"),
                    "ContentsFormat": formats["json"],
                    "ReadableContentsFormat": formats["markdown"],
                    "HumanReadable": md,
                }
                final_output.append(entities_table_entry)
    return final_output


def list_alerts_command(client: MsClient, args: dict):
    """Getting all alerts

    Args:
        client:
        args (dict): usually demisto.args()
    """
    asc_location = args.get("asc_location")
    filter_query = args.get("filter")
    select_query = args.get("select")
    expand_query = args.get("expand")

    alerts = client.list_alerts(asc_location, filter_query, select_query, expand_query).get("value")
    outputs = []
    for alert in alerts:
        properties = alert.get("properties")
        if properties:
            outputs.append(
                {
                    "DisplayName": properties.get("alertDisplayName"),
                    "CompromisedEntity": properties.get("compromisedEntity"),
                    "Description": properties.get("description"),
                    "ID": alert.get("name"),
                    "DetectedTime": properties.get("timeGeneratedUtc"),
                    "ReportedSeverity": properties.get("severity"),
                    "State": properties.get("status"),
                }
            )

    md = tableToMarkdown(
        "Azure Security Center - List Alerts",
        outputs,
        [
            "DisplayName",
            "CompromisedEntity",
            "DetectedTime",
            "ReportedSeverity",
            "State",
            "Description",
            "ID",
        ],
        removeNull=True,
    )
    ec = {"AzureSecurityCenter.Alert(val.ID && val.ID === obj.ID)": outputs}
    return md, ec, alerts


# There's a Microsoft API bug for reactivate alert -
# https://social.msdn.microsoft.com/Forums/windows/en-US/c2139e1b-b26c-4264-a558-fa4b180b70e7/issue-while-setting-security-alert-state-from-dismiss-to-active?forum=AzureSecurityCenter
def update_alert_command(client: MsClient, args: dict):
    """Update given alert

    Args:
        client: MsClient
        args (dict): usually demisto.args()
    """
    asc_location = args.get("asc_location")
    alert_id = args.get("alert_id")
    alert_update_action_type = args.get("alert_update_action_type")
    if alert_update_action_type == "in_progress":
        alert_update_action_type = "inProgress"
    client.update_alert(asc_location, alert_id, alert_update_action_type)
    outputs = {"ID": alert_id, "ActionTaken": alert_update_action_type}

    ec = {"AzureSecurityCenter.Alert(val.ID && val.ID === obj.ID)": outputs}
    return f"Alert - {alert_id} has been set to {alert_update_action_type}.", ec, None


""" Alert End """

""" Location Start """


def list_locations_command(client: MsClient):
    """Getting all locations"""
    locations = client.list_locations().get("value")
    outputs = []
    if locations:
        for location in locations:
            if location.get("properties") and location.get("properties").get("homeRegionName"):
                home_region_name = location.get("properties").get("homeRegionName")
            else:
                home_region_name = None
            outputs.append(
                {
                    "HomeRegionName": home_region_name,
                    "Name": location.get("name"),
                    "ID": location.get("id"),
                }
            )
            md = tableToMarkdown(
                "Azure Security Center - List Locations",
                outputs,
                ["HomeRegionName", "Name", "ID"],
                removeNull=True,
            )
            ec = {"AzureSecurityCenter.Location(val.ID && val.ID === obj.ID)": outputs}
            return md, ec, locations
        return None
    else:
        return "No locations found", None, None


""" Location End """

""" Advanced Threat Protection Start """


def update_atp_command(client: MsClient, args: dict):
    """Updating given Advanced Threat Protection (enable/disable)

    Args:
        client:
        args (dict): usually demisto.args()
    """
    setting_name = args.get("setting_name")
    is_enabled = args.get("is_enabled")
    storage_account = args.get("storage_account")
    response = client.update_atp(storage_account, setting_name, is_enabled)
    outputs = {
        "ID": response.get("id"),
        "Name": response.get("name"),
        "IsEnabled": response.get("properties").get("is_enabled"),
    }
    md = tableToMarkdown(
        "Azure Security Center - Update Advanced Threat Detection Setting",
        outputs,
        ["ID", "Name", "IsEnabled"],
        removeNull=True,
    )
    ec = {"AzureSecurityCenter.AdvancedThreatProtection(val.ID && val.ID === obj.ID)": outputs}
    return md, ec, response


def get_atp_command(client: MsClient, args: dict):
    """Get given Advanced Threat Protection settings

    Args:
        client:
        args (dict): usually demisto.args()
    """
    setting_name = args.get("setting_name")
    storage_account = args.get("storage_account")
    response = client.get_atp(storage_account, setting_name)
    outputs = {
        "ID": response.get("id"),
        "Name": response.get("name"),
        "IsEnabled": response["properties"]["isEnabled"]
        if response.get("properties") and response.get("properties").get("isEnabled")
        else None,
    }
    md = tableToMarkdown(
        "Azure Security Center - Get Advanced Threat Detection Setting",
        outputs,
        ["ID", "Name", "IsEnabled"],
        removeNull=True,
    )
    ec = {"AzureSecurityCenter.AdvancedThreatProtection(val.ID && val.ID === obj.ID)": outputs}
    return md, ec, response


""" Advanced Threat Protection End """

""" Auto Provisioning Settings Start """


def update_aps_command(client: MsClient, args: dict):
    """Updating Analytics Platform System

    Args:
        client:
        args (dict): usually demisto.args()
    """
    setting_name = args.get("setting_name")
    auto_provision = args.get("auto_provision")
    setting = client.update_aps(setting_name, auto_provision)
    outputs = [
        {
            "Name": setting.get("name"),
            "AutoProvision": setting["properties"]["auto_provision"]
            if setting.get("properties") and setting.get("properties").get("auto_provision")
            else None,
            "ID": setting.get("id"),
        }
    ]

    md = tableToMarkdown(
        "Azure Security Center - Update Auto Provisioning Setting",
        outputs,
        ["Name", "AutoProvision", "ID"],
        removeNull=True,
    )
    ec = {"AzureSecurityCenter.AutoProvisioningSetting(val.ID && val.ID === obj.ID)": outputs}
    return md, ec, setting


def list_aps_command(client: MsClient):
    """List all Analytics Platform System"""
    settings = client.list_aps().get("value")
    outputs = []
    for setting in settings:
        outputs.append(
            {
                "Name": setting.get("name"),
                "AutoProvision": setting.get("properties").get("autoProvision")
                if setting.get("properties") and setting.get("properties").get("autoProvision")
                else None,
                "ID": setting.get("id"),
            }
        )

    md = tableToMarkdown(
        "Azure Security Center - List Auto Provisioning Settings",
        outputs,
        ["Name", "AutoProvision", "ID"],
        removeNull=True,
    )

    ec = {"AzureSecurityCenter.AutoProvisioningSetting(val.ID && val.ID === obj.ID)": outputs}
    return md, ec, settings


def get_aps_command(client: MsClient, args: dict):
    """Get given Analytics Platform System setting

    Args:
        client:
        args (dict): usually demisto.args()
    """
    setting_name = args.get("setting_name")
    setting = client.get_aps(setting_name)
    outputs = [
        {
            "Name": setting.get("name"),
            "AutoProvision": setting.get("properties").get("autoProvision")
            if setting.get("properties") and setting.get("properties").get("autoProvision")
            else None,
            "ID": setting["id"],
        }
    ]
    md = tableToMarkdown(
        "Azure Security Center - Get Auto Provisioning Setting",
        outputs,
        ["Name", "AutoProvision", "ID"],
        removeNull=True,
    )
    ec = {"AzureSecurityCenter.AutoProvisioningSetting(val.ID && val.ID === obj.ID)": outputs}

    return md, ec, setting


""" Auto Provisioning Settings End """

""" Information Protection Policies Start """


# Unsupported command. issue: issues/24583
def list_ipp_command(client: MsClient, args: dict):
    """Listing all Internet Presence Provider

    Args:
        client:
        args (dict): usually demisto.args()
    """
    management_group = args.get("management_group")
    policies = client.list_ipp(management_group).get("value")
    outputs = []
    if policies:
        for policy in policies:
            if policy.get("properties") and policy.get("properties").get("labels"):
                label_names = ", ".join([label.get("displayName") for label in policy["properties"]["labels"].values()])
                information_type_names = ", ".join(
                    [it["displayName"] for it in policy["properties"]["informationTypes"].values()]
                )
            else:
                label_names, information_type_names = "", ""
            outputs.append(
                {
                    "Name": policy.get("name"),
                    "Labels": label_names,
                    "InformationTypeNames": information_type_names,
                    "InformationTypes": policy.get("properties").get("informationTypes")
                    if policy.get("properties") and policy.get("properties").get("informationTypes")
                    else None,
                    "ID": policy["id"],
                }
            )
        md = tableToMarkdown(
            "Azure Security Center - List Information Protection Policies",
            outputs,
            ["Name", "Labels", "InformationTypeNames", "ID"],
            removeNull=True,
        )

        ec = {"AzureSecurityCenter.InformationProtectionPolicy(val.ID && val.ID === obj.ID)": outputs}

        entry = {
            "Type": entryTypes["note"],
            "Contents": policies,
            "ContentsFormat": formats["json"],
            "ReadableContentsFormat": formats["markdown"],
            "HumanReadable": md,
            "EntryContext": ec,
        }
        demisto.results(entry)
    else:
        demisto.results("No policies found")


# Unsupported command. issue: issues/24583
def get_ipp_command(client: MsClient, args: dict):
    """Getting Internet Presence Provider information
    Args:
        client:
        args (dict): usually demisto.args()
    """
    policy_name = args.get("policy_name")
    management_group = args.get("management_group")
    policy = client.get_ipp(policy_name, management_group)
    properties = policy.get("properties")
    labels = properties.get("labels")
    if properties and isinstance(labels, dict):
        # Basic Property table
        labels = ", ".join([(str(label.get("displayName")) + str(label.get("enabled"))) for label in labels.values()])
        basic_table_output = [{"Name": policy.get("name"), "Labels": labels, "ID": policy.get("id")}]

        md = tableToMarkdown(
            "Azure Security Center - Get Information Protection Policy - Basic Property",
            basic_table_output,
            ["Name", "Labels", "ID"],
            removeNull=True,
        )
        ec = {"AzureSecurityCenter.InformationProtectionPolicy(val.ID && val.ID === obj.ID)": basic_table_output}

        basic_table_entry = {
            "Type": entryTypes["note"],
            "Contents": policy,
            "ContentsFormat": formats["json"],
            "ReadableContentsFormat": formats["markdown"],
            "HumanReadable": md,
            "EntryContext": ec,
        }

        # Information Type table
        info_type_table_output = []
        for information_type_data in properties.get("informationTypes").values():
            keywords = ", ".join(
                [
                    (str(keyword.get("displayName")) + str(keyword.get("custom")) + str(keyword.get("canBeNumeric")))
                    for keyword in information_type_data.get("keywords", [])
                ]
            )
            info_type_table_output.append(
                {
                    "DisplayName": information_type_data.get("displayname"),
                    "Enabled": information_type_data("enabled"),
                    "Custom": information_type_data("custom"),
                    "Keywords": keywords,
                    "RecommendedLabelID": information_type_data("recommendedLabelId"),
                }
            )
        md = tableToMarkdown(
            "Azure Security Center - Get Information Protection Policy - Information Types",
            info_type_table_output,
            ["DisplayName", "Enabled", "Custom", "Keywords", "RecommendedLabelID"],
            removeNull=True,
        )
        info_type_table_entry = {
            "Type": entryTypes["note"],
            "Contents": properties.get("informationTypes"),
            "ContentsFormat": formats["json"],
            "ReadableContentsFormat": formats["markdown"],
            "HumanReadable": md,
        }
        demisto.results([basic_table_entry, info_type_table_entry])
    else:
        demisto.results(f"No properties found in {management_group}")


""" Information Protection Policies End """

""" Jit Network Access Policies Start """


def list_jit_command(client: MsClient, args: dict):
    """Lists all Just-in-time Virtual Machines

    Args:
        client:
        args (dict): usually demisto.args()
    """
    asc_location = args.get("asc_location")
    policies = client.list_jit(asc_location)["value"]
    outputs = []
    for policy in policies:
        # summarize rules in (VMName: allowPort,...) format
        if policy.get("properties") and policy.get("properties").get("virtualMachines"):
            rules_data = policy["properties"]["virtualMachines"]
            rules_summary_array = []
            for rule in rules_data:
                ID = rule.get("id")
                vm_name = ID.split("/")[-1] if isinstance(ID, str) else None
                vm_ports = [str(port.get("number")) for port in rule.get("ports")]
                rules_summary_array.append("({}: {})".format(vm_name, ", ".join(vm_ports)))
            rules = ", ".join(rules_summary_array)

            outputs.append(
                {
                    "Name": policy.get("name"),
                    "Rules": rules,
                    "Location": policy.get("location"),
                    "Kind": policy.get("kind"),
                    "ID": policy.get("id"),
                }
            )
    md = tableToMarkdown(
        "Azure Security Center - List JIT Access Policies",
        outputs,
        ["Name", "Rules", "Location", "Kind"],
        removeNull=True,
    )
    ec = {"AzureSecurityCenter.JITPolicy(val.ID && val.ID === obj.ID)": outputs}
    return md, ec, policies


# Unsupported command. issue: issues/24583
def get_jit_command(client: MsClient, args: dict):
    """Getting given Just-in-time machine

    Args:
        client:
        args (dict): usually demisto.args()
    """
    policy_name = args.get("policy_name")
    asc_location = args.get("asc_location")
    resource_group_name = args.get("resource_group_name")
    policy = client.get_jit(policy_name, asc_location, resource_group_name)

    # Property table
    property_table_output = [
        {
            "Name": policy.get("name"),
            "Kind": policy.get("kind"),
            "ProvisioningState": policy.get("properties").get("provisioningState")
            if policy.get("properties") and policy.get("properties", {}).get("provisioningState")
            else None,
            "Location": policy.get("location"),
            "Rules": policy.get("properties").get("virtualMachines")
            if policy.get("properties") and policy.get("properties", {}).get("virtualMachines")
            else None,
            "Requests": policy.get("properties").get("requests")
            if policy.get("properties") and policy.get("properties", {}).get("requests")
            else None,
            "ID": policy.get("id"),
        }
    ]
    md = tableToMarkdown(
        "Azure Security Center - Get JIT Access Policy - Properties",
        property_table_output,
        ["Name", "Kind", "ProvisioningState", "Location", "ID"],
        removeNull=True,
    )

    ec = {"AzureSecurityCenter.JITPolicy(val.ID && val.ID === obj.ID)": property_table_output}

    property_table_entry = {
        "Type": entryTypes["note"],
        "Contents": policy,
        "ContentsFormat": formats["json"],
        "ReadableContentsFormat": formats["markdown"],
        "HumanReadable": md,
        "EntryContext": ec,
    }

    # Rules table
    rules_table_output = []
    properties = policy.get("properties")
    virtual_machines = properties.get("virtualMachines")
    if isinstance(properties, dict) and virtual_machines:
        for rule in virtual_machines:
            rules_table_output.append(
                {
                    "VmID": rule.get("id"),
                    "Ports": format_jit_port_rule(rule.get("ports")),
                }
            )
        md = tableToMarkdown(
            "Azure Security Center - Get JIT Access Policy - Rules",
            rules_table_output,
            ["VmID", "Ports"],
            removeNull=True,
        )
        rules_table_entry = {
            "Type": entryTypes["note"],
            "Contents": properties.get("virtualMachines"),
            "ContentsFormat": formats["json"],
            "ReadableContentsFormat": formats["markdown"],
            "HumanReadable": md,
        }

        # Requests table
        requests_table_output = []

        for requestData in properties.get("requests", []):
            vms = []
            for vm in requestData.get("virtualMachines"):
                vm_name = vm["id"].split("/")[-1]
                vm_ports = format_jit_port_request(vm.get("ports"))
                vms.append(f"[{vm_name}: {vm_ports}]")
            requests_table_output.append(
                {
                    "VirtualMachines": ", ".join(vms),
                    "Requestor": requestData.get("requestor") if requestData.get("requestor") else "service-account",
                    "StartTimeUtc": requestData.get("startTimeUtc"),
                }
            )
        md = tableToMarkdown(
            "Azure Security Center - Get JIT Access Policy - Requests",
            requests_table_output,
            ["VirtualMachines", "Requestor", "StartTimeUtc"],
            removeNull=True,
        )

        requests_table_entry = {
            "Type": entryTypes["note"],
            "Contents": properties.get("requests"),
            "ContentsFormat": formats["json"],
            "ReadableContentsFormat": formats["markdown"],
            "HumanReadable": md,
        }
        demisto.results([property_table_entry, rules_table_entry, requests_table_entry])


# Unsupported command. issue: issues/24583
def initiate_jit_command(client: MsClient, args: dict):
    resource_group_name = args.get("resource_group_name")
    asc_location = args.get("asc_location")
    policy_name = args.get("policy_name")
    vm_id = args.get("vmID")
    port = args.get("port")
    source_address = args.get("source_address")
    duration = args.get("duration")
    response = client.initiate_jit(
        resource_group_name,
        asc_location,
        policy_name,
        vm_id,
        port,
        source_address,
        duration,
    )
    policy_id = (
        f"/subscriptions/{client.subscription_id}/resourceGroups/{resource_group_name}/providers/"
        f"Microsoft.Security/locations/{asc_location}/jitNetworkAccessPolicies/{policy_name}"
    )
    virtual_machines = response.get("virtualMachines")
    if virtual_machines and len(virtual_machines) > 0:
        machine = virtual_machines[0]
        port = machine.get("ports")[0]

        outputs = {
            "VmID": machine.get("id"),
            "PortNum": port.get("number"),
            "AllowedSourceAddress": port.get("allowedSourceAddressPrefix"),
            "EndTimeUtc": port.get("endTimeUtc"),
            "Status": port.get("status"),
            "Requestor": response.get("requestor"),
            "PolicyID": policy_id,
        }

        md = tableToMarkdown(
            "Azure Security Center - Initiate JIT Access Request",
            outputs,
            [
                "VmID",
                "PortNum",
                "AllowedSourceAddress",
                "EndTimeUtc",
                "Status",
                "Requestor",
            ],
            removeNull=True,
        )

        ec = {
            "AzureSecurityCenter.JITPolicy(val.ID && val.ID ="
            f"== obj.{policy_id}).Initiate(val.endTimeUtc === obj.EndTimeUtc)": outputs
        }

        demisto.results(
            {
                "Type": entryTypes["note"],
                "Contents": response,
                "ContentsFormat": formats["json"],
                "ReadableContentsFormat": formats["markdown"],
                "HumanReadable": md,
                "EntryContext": ec,
            }
        )


# Unsupported command. issue: issues/24583
def delete_jit_command(client: MsClient, args: dict):
    """Deletes a Just-in-time machine

    Args:
        client:
        args (dict): usually demisto.args()
    """
    asc_location = args.get("asc_location")
    resource_group_name = args.get("resource_group_name")
    policy_name = args.get("policy_name")
    client.delete_jit(asc_location, resource_group_name, policy_name)

    policy_id = (
        f"/subscriptions/{client.subscription_id}/resourceGroups/{resource_group_name}/providers/"
        f"Microsoft.Security/locations/{asc_location}/jitNetworkAccessPolicies/{policy_name}"
    )

    outputs = {"ID": policy_id, "Action": "deleted"}

    ec = {"AzureSecurityCenter.JITPolicy(val.ID && val.ID === obj.ID)": outputs}
    demisto.results(
        {
            "Type": entryTypes["note"],
            "Contents": f"Policy - {policy_name} has been deleted sucessfully.",
            "ContentsFormat": formats["text"],
            "EntryContext": ec,
        }
    )


""" Jit Network Access Policies End """

""" Storage Start """


# Add this command to security center integration because ATP-related command requires storage account info
def list_sc_storage_command(client: MsClient):
    """Listing all Security Center Storages"""
    accounts = client.list_sc_storage().get("value")
    outputs = []
    for account in accounts:
        account_id_array = account.get("id", "").split("/")
        resource_group_name = account_id_array[account_id_array.index("resourceGroups") + 1]
        outputs.append(
            {
                "Name": account.get("name"),
                "ResourceGroupName": resource_group_name,
                "Location": account.get("location"),
                "ID": account.get("id"),
            }
        )
    md = tableToMarkdown(
        "Azure Security Center - List Storage Accounts",
        outputs,
        ["Name", "ResourceGroupName", "Location"],
        removeNull=True,
    )
    ec = {"AzureSecurityCenter.Storage(val.ID && val.ID === obj.ID)": outputs}

    return md, ec, accounts


""" Storage End """

""" Subscriptions Start """


def list_sc_subscriptions_command(client: MsClient):
    """Listing Subscriptions for this application"""
    subscriptions = client.list_sc_subscriptions().get("value")
    outputs = []
    for sub in subscriptions:
        outputs.append(
            {
                "Name": sub.get("displayName"),
                "State": sub.get("state"),
                "ID": sub.get("id"),
            }
        )
    md = tableToMarkdown(
        "Azure Security Center - Subscriptions",
        outputs,
        ["ID", "Name", "State"],
        removeNull=True,
    )
    ec = {"Azure.Subscription(val.ID && val.ID === obj.ID)": outputs}

    return md, ec, subscriptions


""" Subscriptions end """

""" Secure Score Start"""


def get_secure_scores_command(client: MsClient, args: dict):
    secure_score_name = args.get("secure_score_name", "ascScore")

    securescore = client.get_secure_scores(secure_score_name)

    md = tableToMarkdown("Azure Security Center - Secure Score", securescore["properties"])

    ec = {"Azure.Securescore(val.ID && val.ID === obj.ID)": securescore["properties"]}

    return md, ec, securescore


""" Secure Scores End"""


def list_resource_groups_command(client: MsClient, args: dict[str, Any]) -> CommandResults:
    """
    List all resource groups in the subscription.

    Args:
        client (KeyVaultClient):  Azure Key Vault API client.
        args (Dict[str, Any]): command arguments.

    Returns:
        CommandResults: Command results with raw response, outputs and readable outputs.

    """
    tag = args.get("tag", "")
    limit = arg_to_number(args.get("limit")) or DEFAULT_LIMIT

    raw_responses = []
    resource_groups: List[dict] = []

    next_link: bool | str = True
    while next_link and len(resource_groups) < limit:
        full_url = next_link if isinstance(next_link, str) else None
        response = client.list_resource_groups(tag=tag, limit=limit, full_url=full_url)
        value = response.get("value", [])
        next_link = response.get("nextLink", "")

        raw_responses.extend(value)
        for resource_group in value:
            resource_group_context = {
                "Name": resource_group.get("name"),
                "Location": resource_group.get("location"),
                "Tags": resource_group.get("tags"),
                "Provisioning State": resource_group.get("properties", {}).get("provisioningState"),
            }
            resource_groups.append(resource_group_context)

    raw_responses = raw_responses[:limit]
    resource_groups = resource_groups[:limit]
    readable_output = tableToMarkdown("Resource Groups List", resource_groups, removeNull=True)

    return CommandResults(
        outputs_prefix="Azure.ResourceGroupName",
        outputs_key_field="id",
        outputs=raw_responses,
        raw_response=raw_responses,
        readable_output=readable_output,
    )


def test_module(client: MsClient):
    """
    Performs basic GET request to check if the API is reachable and authentication is successful.
    Returns ok if successful.
    """
    if client.subscription_id:
        client.list_locations()
    else:
        client.list_sc_subscriptions()
    demisto.results("ok")


def main():
    params: dict = demisto.params()
    args = demisto.args()
    command = demisto.command()

    try:
        server = params.get("server_url", "").rstrip("/") + "/"
        tenant = params.get("credentials_tenant_id", {}).get("password") or params.get("tenant_id")
        auth_and_token_url = params.get("credentials_auth_id", {}).get("password") or params.get("auth_id", "")
        if not auth_and_token_url:
            raise DemistoException("ID must be provided.")
        enc_key = params.get("credentials_enc_key", {}).get("password") or params.get("enc_key")
        use_ssl = not params.get("unsecure", False)
        proxy = params.get("proxy", False)
        subscription_id = (
            args.get("subscription_id")
            or params.get("credentials_default_sub_id", {}).get("password")
            or params.get("default_sub_id")
        )
        resource_group_name = args.get("resource_group_name") or params.get("resource_group_name")
        ok_codes = (200, 201, 202, 204)
        certificate_thumbprint = params.get("credentials_certificate_thumbprint", {}).get("password") or params.get(
            "certificate_thumbprint"
        )
        private_key = params.get("private_key")
        managed_identities_client_id = get_azure_managed_identities_client_id(params)
        self_deployed: bool = params.get("self_deployed", False) or managed_identities_client_id is not None

        if not managed_identities_client_id:
            if not (tenant and auth_and_token_url):
                raise DemistoException(
                    "Token and ID must be provided. For further information see "
                    "https://xsoar.pan.dev/docs/reference/articles/microsoft-integrations---authentication"
                )

            if not self_deployed and not enc_key:
                raise DemistoException(
                    "Key must be provided. For further information see "
                    "https://xsoar.pan.dev/docs/reference/articles/microsoft-integrations---authentication"
                )
            elif not enc_key and not (certificate_thumbprint and private_key):
                raise DemistoException(
                    "Key or Certificate Thumbprint and Private Key must be provided."
                    "For further information see "
                    "https://xsoar.pan.dev/docs/reference/articles/microsoft-integrations---authentication"
                )

        if command in SUB_ID_REQUIRING_CMD and not subscription_id:
            raise DemistoException("A subscription ID must be provided.")
        if command in RESOURCE_GROUP_REQUIRING_CMD and not resource_group_name:
            raise DemistoException("A Resource Group Name must be provided.")
        client = MsClient(
            tenant_id=tenant,
            auth_id=auth_and_token_url,
            enc_key=enc_key,
            app_name=APP_NAME,
            proxy=proxy,
            server=server,
            verify=use_ssl,
            self_deployed=self_deployed,
            subscription_id=subscription_id,
            ok_codes=ok_codes,
            certificate_thumbprint=certificate_thumbprint,
            private_key=private_key,
            resource_group_name=resource_group_name,
            managed_identities_client_id=managed_identities_client_id,
        )

        if command == "test-module":
            # If the command will fail, error will be thrown from the request itself
            test_module(client)
        elif command == "azure-sc-get-alert":
            return_results(get_alert_command(client, args))
        elif command == "azure-sc-list-alert":
            return_outputs(*list_alerts_command(client, args))
        elif command == "azure-sc-update-alert":
            return_outputs(*update_alert_command(client, args))
        elif command == "azure-sc-list-location":
            return_outputs(*list_locations_command(client))
        elif command == "azure-sc-update-atp":
            return_outputs(*update_atp_command(client, args))
        elif command == "azure-sc-get-atp":
            return_outputs(*get_atp_command(client, args))
        elif command == "azure-sc-update-aps":
            return_outputs(*update_aps_command(client, args))
        elif command == "azure-sc-list-aps":
            return_outputs(*list_aps_command(client))
        elif command == "azure-sc-get-aps":
            return_outputs(*get_aps_command(client, args))
        elif command == "azure-sc-list-ipp":
            list_ipp_command(client, args)
        elif command == "azure-sc-get-ipp":
            get_ipp_command(client, args)
        elif command == "azure-sc-list-jit":
            return_outputs(*list_jit_command(client, args))
        elif command == "azure-sc-get-jit":
            get_jit_command(client, args)
        elif command == "azure-sc-initiate-jit":
            initiate_jit_command(client, args)
        elif command == "azure-sc-delete-jit":
            delete_jit_command(client, args)
        elif command == "azure-sc-list-storage":
            return_outputs(*list_sc_storage_command(client))
        elif command == "azure-list-subscriptions":
            return_outputs(*list_sc_subscriptions_command(client))
        elif command == "azure-get-secure-score":
            return_outputs(*get_secure_scores_command(client, args))
        elif command == "azure-resource-group-list":
            return_results(list_resource_groups_command(client, args))
        elif command == "azure-sc-auth-reset":
            return_results(reset_auth())
    except Exception as err:
        return_error(f"Failed to execute {command} command. Error: {err!s}")


if __name__ in ["__main__", "builtin", "builtins"]:
    main()