MicrosoftDefenderThreatIntelligence

Use the Microsoft Defender Threat Intelligence integration to query enriched threat intelligence data such as articles, threat actor profiles, WHOIS records, and host-related infrastructure.

Data Enrichment & Threat Intelligence · MicrosoftDefenderThreatIntelligence

Details

IDMicrosoftDefenderThreatIntelligence
ProviderMicrosoft
CategoryData Enrichment & Threat Intelligence
From Version6.1.0
Docker Imagedemisto/crypto:1.0.0.10120494
Supported ModulesAgentix XSIAM

README

This integration requires Defender Threat Intelligence—premium version

Use the Microsoft Defender Threat Intelligence integration to query enriched threat intelligence data such as articles, threat actor profiles, WHOIS records, and host-related infrastructure.

Configure MicrosoftDefenderThreatIntelligence in Cortex

Parameter Description Required
Application ID (Client ID for Client credentials mode)   False
Tenant ID (required for Client Credentials mode)   False
Client Secret (required for Client Credentials mode) Client Secret. Required for Client Credentials mode) False
Certificate Thumbprint Used for certificate authentication. As appears in the “Certificates & secrets” page of the app. False
Private Key   False
Use Azure Managed Identities Relevant only if the integration is running on Azure VM. If selected, authenticates based on the value provided for the Azure Managed Identities Client ID field. If no value is provided for the Azure Managed Identities Client ID field, authenticates based on the System Assigned Managed Identity. For additional information, see the Help tab. False
Azure Managed Identities Client ID The Managed Identities client ID for authentication - relevant only if the integration is running on Azure VM. False
Azure AD endpoint Azure AD endpoint associated with a national cloud. False
Incident type   False
Use Client Credentials Authorization Flow Use a self-deployed Azure application and authenticate using the Client Credentials flow. False
Trust any certificate (not secure)   False
Use system proxy settings   False

Commands

You can execute these commands from the 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.

msg-defender-threat-intel-auth-start


Run this command to start the authorization process and follow the instructions in the command results.

Base Command

msg-defender-threat-intel-auth-start

Input

There are no input arguments for this command.

Context Output

There is no context output for this command.

msg-defender-threat-intel-auth-complete


Run this command to complete the authorization process.\nShould be used after running the msg-defender-threat-intel-auth-start command.

Base Command

msg-defender-threat-intel-auth-complete

Input

There are no input arguments for this command.

Context Output

There is no context output for this command.

msg-defender-threat-intel-auth-test


Run this command to test if the authorization process is successful.

Base Command

msg-defender-threat-intel-auth-test

Input

There are no input arguments for this command.

Context Output

There is no context output for this command.

msg-defender-threat-intel-auth-reset


Run this command to reset the authorization process.

Base Command

msg-defender-threat-intel-auth-reset

Input

There are no input arguments for this command.

Context Output

There is no context output for this command.

msg-defender-threat-intel-article-list


Get articles including their properties and relationships.

Base Command

msg-defender-threat-intel-article-list

Input

Argument Name Description Required
article_id Article ID to retrieve specific article details. Optional
limit Number of incidents in the list. Optional
odata Use “odata” query to customize the response Optional

Context Output

Path Type Description
MSGDefenderThreatIntel.Article.id String The unique identifier of the threat intelligence article.
MSGDefenderThreatIntel.Article.title String The title of the Microsoft Defender Threat Intelligence article.

msg-defender-threat-intel-article-indicators-list


Get indicators of threat or compromise related to the contents of an article.

Base Command

msg-defender-threat-intel-article-indicators-list

Input

Argument Name Description Required
article_id The unique identifier of the article. Optional
article_indicator_id The unique identifier of a specific indicator within the article. Optional
limit Number of incidents in the list. Optional
odata Use “odata” query to customize the response Optional

Context Output

Path Type Description
MSGDefenderThreatIntel.ArticleIndicator.id String The unique identifier of the indicator associated with the article.
MSGDefenderThreatIntel.ArticleIndicator.artifact.id String The unique identifier of the artifact (e.g., file, domain, IP) related to the indicator.

msg-defender-threat-intel-profile-list


Get Intelligence Profiles including their properties and relationships.

Base Command

msg-defender-threat-intel-profile-list

Input

Argument Name Description Required
intel_profile_id The unique identifier of the intelligence profile. Optional
limit Number of incidents in the list. Optional
odata Use “odata” query to customize the response Optional

Context Output

Path Type Description
MSGDefenderThreatIntel.Profile.id unknown The unique identifier of the threat actor profile.
MSGDefenderThreatIntel.Profile.title String The title or name of the threat actor profile.

msg-defender-threat-intel-profile-indicators-list


Get Intelligence Profiles Indicators and their properties.

Base Command

msg-defender-threat-intel-profile-indicators-list

Input

Argument Name Description Required
intel_profile_id The unique identifier of the intelligence profile. Optional
intel_profile_indicator_id The unique identifier of a specific indicator related to an intelligence profile. Optional
limit Number of incidents in the list. Optional
odata Use “odata” query to customize the response Optional

Context Output

Path Type Description
MSGDefenderThreatIntel.ProfileIndicator.id unknown The unique identifier of the indicator associated with the threat actor profile.
MSGDefenderThreatIntel.ProfileIndicator.artifact.id String The unique identifier of the artifact (e.g., IP address, domain, file hash) linked to the indicator.

msg-defender-threat-intel-host


Read the properties and relationships of a host object.

Base Command

msg-defender-threat-intel-host

Input

Argument Name Description Required
host_id The unique identifier of the host. Required
odata Use “odata” query to customize the response Optional

Context Output

Path Type Description
MSGDefenderThreatIntel.Host.id unknown The unique identifier of the host object in Microsoft Defender Threat Intelligence.
MSGDefenderThreatIntel.Host.registrar String The name of the domain registrar responsible for registering the host.
MSGDefenderThreatIntel.Host.registrant String The entity (person or organization) that registered the host domain.

msg-defender-threat-intel-host-whois


Get the specified whoisRecord resource.

Base Command

msg-defender-threat-intel-host-whois

Input

Argument Name Description Required
host_id hostname or IP address. Optional
whois_record_id The unique identifier of a specific WHOIS record. Optional
odata Use “odata” query to customize the response. Optional
limit Number of records in the list. Optional

Context Output

Path Type Description
MSGDefenderThreatIntel.Whois.id unknown The unique identifier of the WHOIS record in Microsoft Defender Threat Intelligence.
MSGDefenderThreatIntel.Whois.whoisServer String The WHOIS server that provided the domain registration information.
MSGDefenderThreatIntel.Whois.domainStatus String The current status of the domain (e.g., active, clientHold, expired) as reported in the WHOIS record.

msg-defender-threat-intel-host-whois-history


Get the history for a whoisRecord, as represented by a collection of whoisHistoryRecord resources.

Base Command

msg-defender-threat-intel-host-whois-history

Input

Argument Name Description Required
host_id hostname or IP address. Optional
whois_record_id The unique identifier of the WHOIS record whose history you want to retrieve. Optional
odata Use “odata” query to customize the response. Optional
limit Number of records in the list. Optional

Context Output

Path Type Description
MSGDefenderThreatIntel.WhoisHistory.id unknown The unique identifier of the historical WHOIS record.
MSGDefenderThreatIntel.WhoisHistory.whoisServer String The WHOIS server that provided the historical domain registration data.
MSGDefenderThreatIntel.WhoisHistory.domainStatus String The domain’s status at the time of the historical WHOIS record (e.g., clientTransferProhibited, inactive).

msg-defender-threat-intel-host


Read the properties and relationships of a host object.

Base Command

msg-defender-threat-intel-host-reputation

Input

Argument Name Description Required
host_id The unique identifier of the host. Required
odata Use “odata” query to customize the response. Optional

Context Output

Path Type Description
MSGDefenderThreatIntel.HostReputation.id unknown The unique identifier of the host object in Microsoft Defender Threat Intelligence.
MSGDefenderThreatIntel.HostReputation.classification String The reputation classification of the host (e.g., Malicious, Suspicious, Unknown).
MSGDefenderThreatIntel.HostReputation.score String TA numerical score representing the confidence or severity of the host’s reputation.

Configuration parameters

  • app_id — Application ID (Client ID for Client Credentials mode)
  • tenant_id — Tenant ID (required for Client Credentials mode)
  • credentials
  • creds_certificate — Certificate Thumbprint
  • use_managed_identities — Use Azure Managed Identities
  • managed_identities_client_id
  • azure_ad_endpoint — Azure AD endpoint
  • client_credentials — Use Client Credentials Authorization Flow
  • insecure — Trust any certificate (not secure)
  • proxy — Use system proxy settings

Commands (12)

  • msg-defender-threat-intel-article-indicators-list

    Get indicators of threat or compromise related to the contents of an article.

  • msg-defender-threat-intel-article-list

    Get articles including their properties and relationships.

  • msg-defender-threat-intel-auth-complete

    Completes the authorization process. Should be used after running the msg-defender-threat-intel-auth-start command.

  • msg-defender-threat-intel-auth-reset

    Reruns the authentication process.

  • msg-defender-threat-intel-auth-start

    Starts the authorization process. Follow the instructions in the command results.

  • msg-defender-threat-intel-auth-test

    Tests connectivity to Microsoft.

  • msg-defender-threat-intel-host

    Read the properties and relationships of a host object.

  • msg-defender-threat-intel-host-reputation

    Retrieves the reputation details, properties, and related information for a specified host.

  • msg-defender-threat-intel-host-whois

    Get the specified whoisRecord resource.

  • msg-defender-threat-intel-host-whois-history

    Get the history for a whoisRecord, as represented by a collection of whoisHistoryRecord resources.

  • msg-defender-threat-intel-profile-indicators-list

    Get Intelligence Profiles Indicators and their properties.

  • msg-defender-threat-intel-profile-list

    Get Intelligence Profiles including their properties and relationships.

from typing import Any

import demistomock as demisto
import urllib3
from CommonServerPython import *  # noqa # pylint: disable=unused-wildcard-import

# from CommonServerUserPython import *  # noqa
from MicrosoftApiModule import *  # noqa: E402

# Disable insecure warnings
urllib3.disable_warnings()

""" CONSTANTS """

DATE_FORMAT = "%Y-%m-%dT%H:%M:%SZ"  # ISO8601 format with UTC, default in XSOAR

REQUIRED_PERMISSIONS = (
    "offline_access",  # allows device-flow login
    "ThreatIntelligence.Read.All",  # Threat Intelligence specific permission
)

""" CLIENT CLASS """


class Client:
    def __init__(
        self,
        app_id: str,
        verify: bool,
        proxy: bool,
        azure_ad_endpoint: str = "https://login.microsoftonline.com",
        client_credentials: bool = False,
        tenant_id: str = None,
        enc_key: str = None,
        managed_identities_client_id: Optional[str] = None,
        private_key: Optional[str] = None,
        certificate_thumbprint: Optional[str] = None,
    ):
        if app_id and "@" in app_id:
            app_id, refresh_token = app_id.split("@")
            integration_context = get_integration_context()
            integration_context["current_refresh_token"] = refresh_token
            set_integration_context(integration_context)
        elif client_credentials and (not enc_key and not (certificate_thumbprint and private_key)):
            raise DemistoException(
                "Either enc_key or (Certificate Thumbprint and Private Key) must be provided. For further "
                "information see "
                "https://xsoar.pan.dev/docs/reference/articles/microsoft-integrations---authentication"
            )
        args = {
            "azure_ad_endpoint": azure_ad_endpoint,
            "self_deployed": True,
            "auth_id": app_id,
            "grant_type": CLIENT_CREDENTIALS if client_credentials else DEVICE_CODE,
            "base_url": "https://graph.microsoft.com",
            "verify": verify,
            "proxy": proxy,
            "tenant_id": tenant_id,
            "enc_key": enc_key,
            "managed_identities_client_id": managed_identities_client_id,
            "managed_identities_resource_uri": Resources.graph,
            "certificate_thumbprint": certificate_thumbprint,
            "private_key": private_key,
            "command_prefix": "msg-defender-threat-intel",
        }
        if not client_credentials:
            args["scope"] = " ".join(REQUIRED_PERMISSIONS)
            args["token_retrieval_url"] = "https://login.microsoftonline.com/organizations/oauth2/v2.0/token"
        self.ms_client = MicrosoftClient(**args)  # type: ignore

    def article_list(self, article_id: str, odata: str, limit: int) -> list:
        """
        Retrieve threat intelligence articles from Microsoft Defender.
        Docs:
        https://learn.microsoft.com/en-us/graph/api/security-threatintelligence-list-articles?view=graph-rest-1.0&tabs=http
        https://learn.microsoft.com/en-us/graph/api/security-article-get?view=graph-rest-1.0&tabs=http

        Args:
            article_id (str): Specific article ID to retrieve. If empty, retrieves all articles.
            odata (str): Additional OData query parameters for filtering and sorting.
            limit (int): Maximum number of articles to return.

        Returns:
            list: List of article objects. If article_id is provided, returns a single article
                    in a list format. If article_id is empty, returns multiple articles from
                    the 'value' field of the response.
        """
        odata_query = ""

        if not article_id:
            odata_query = f"?$top={limit}&{odata}"
            response = self.ms_client.http_request(
                method="GET",
                url_suffix=f"v1.0/security/threatIntelligence/articles{odata_query}",
            )
            return response.get("value", [])

        if odata:
            odata_query += f"?{odata}"

        return [
            self.ms_client.http_request(
                method="GET",
                url_suffix=f"v1.0/security/threatIntelligence/articles/{article_id}{odata_query}",
            )
        ]

    def article_indicator_list(self, article_id: str, article_indicator_id: str, odata: str, limit: int) -> list:
        """
        Retrieve threat intelligence indicators from Microsoft Defender.
        Docs:
        https://learn.microsoft.com/en-us/graph/api/security-articleindicator-get?view=graph-rest-1.0&tabs=http
        https://learn.microsoft.com/en-us/graph/api/security-article-list-indicators?view=graph-rest-1.0&tabs=http

        Args:
            article_id (str): The ID of the article to retrieve indicators from.
            article_indicator_id (str): The specific indicator ID to retrieve.
            odata (str): Additional OData query parameters for filtering and sorting.
            limit (int): Maximum number of indicators to return.

        Returns:
            list: List of article indicator objects from the 'value' field of the response.
        """

        odata_query = ""

        if not article_id:
            if odata:
                odata_query += f"?{odata}"

            return [
                self.ms_client.http_request(
                    method="GET",
                    url_suffix=f"v1.0/security/threatIntelligence/articleIndicators/{article_indicator_id}{odata_query}",
                )
            ]

        odata_query = f"?$top={limit}&{odata}"

        response = self.ms_client.http_request(
            method="GET",
            url_suffix=f"v1.0/security/threatIntelligence/articles/{article_id}/indicators{odata_query}",
        )

        return response.get("value", [])

    def profile_list(self, intel_profile_id: str, odata: str, limit: int) -> list:
        """
        Retrieve threat intelligence profiles from Microsoft Defender.
        Docs:
        https://learn.microsoft.com/en-us/graph/api/security-threatintelligence-list-intelprofiles?view=graph-rest-1.0&tabs=http
        https://learn.microsoft.com/en-us/graph/api/security-intelligenceprofile-get?view=graph-rest-1.0&tabs=http

        Args:
            intel_profile_id (str): The ID of the specific intelligence profile to retrieve.
            odata (str): Additional OData query parameters for filtering and sorting.
            limit (int): Maximum number of profiles to return.

        Returns:
            list: List of intelligence profile objects from the 'value' field of the response.
        """
        odata_query = ""

        if intel_profile_id:
            if odata:
                odata_query += f"?{odata}"

            return [
                self.ms_client.http_request(
                    method="GET",
                    url_suffix=f"v1.0/security/threatIntelligence/intelProfiles/{intel_profile_id}{odata_query}",
                )
            ]

        odata_query = f"?$top={limit}&{odata}"

        response = self.ms_client.http_request(
            method="GET",
            url_suffix=f"v1.0/security/threatIntelligence/intelProfiles{odata_query}",
        )

        return response.get("value", [])

    def profile_indicators_list(self, intel_profile_id: str, intel_profile_indicator_id: str, odata: str, limit: int) -> list:
        """
        Retrieve intelligence profile indicators.
        Docs:
        https://learn.microsoft.com/en-us/graph/api/security-intelligenceprofileindicator-get?view=graph-rest-1.0&tabs=http
        https://learn.microsoft.com/en-us/graph/api/security-intelligenceprofile-list-indicators?view=graph-rest-1.0&tabs=http

        Args:
            intel_profile_id (str): The ID of the intelligence profile.
            intel_profile_indicator_id (str): The ID of a specific intelligence profile indicator.
            odata (str): OData query parameters for filtering and formatting the response.
            limit (int): Maximum number of indicators to return.

        Returns:
            list: A list of intelligence profile indicators, each containing information such as
                    ID, source, first/last seen timestamps, and associated artifacts.
        """

        odata_query = ""

        if not intel_profile_indicator_id:
            odata_query = f"?$top={limit}&{odata}"

            response = self.ms_client.http_request(
                method="GET",
                url_suffix=f"v1.0/security/threatIntelligence/intelProfiles/{intel_profile_id}/indicators{odata_query}",
            )
            return response.get("value", [])

        if odata:
            odata_query += f"?{odata}"

        return [
            self.ms_client.http_request(
                method="GET",
                url_suffix=f"v1.0/security/threatIntelligence/intelligenceProfileIndicators/{intel_profile_indicator_id}{odata_query}",
            )
        ]

    def host(self, host_id: str, odata: str) -> dict:
        """
        Retrieve host information by host ID.
        Docs: https://learn.microsoft.com/en-us/graph/api/security-host-get?view=graph-rest-1.0&tabs=http

        Args:
            host_id (str): The ID (host name or ip address) of the host to retrieve information for.
            odata (str): OData query parameters for filtering and formatting the response.

        Returns:
            dict: A dictionary containing host information including ID, first/last seen timestamps,
                    registrar, and registrant details.
        """
        odata_query = f"?{odata}" if odata else ""

        return self.ms_client.http_request(
            method="GET",
            url_suffix=f"v1.0/security/threatIntelligence/hosts/{host_id}{odata_query}",
        )

    def host_whois(self, host_id: str, whois_record_id: str, odata: str) -> dict:
        """
        Retrieve WHOIS record information for a host or specific WHOIS record.
        Docs: https://learn.microsoft.com/en-us/graph/api/security-whoisrecord-get?view=graph-rest-1.0&tabs=http

        Args:
            host_id (str): The ID (host name or ip address) of the host to retrieve WHOIS information for.
            whois_record_id (str): The specific WHOIS record ID to retrieve.
            odata (str): OData query parameters for filtering and formatting the response.

        Returns:
            dict: A dictionary containing WHOIS record information including registration details,
                    contact information (admin, technical, registrant), nameservers, and timestamps.
        """

        odata_query = f"?{odata}" if odata else ""

        if host_id:
            return self.ms_client.http_request(
                method="GET",
                url_suffix=f"v1.0/security/threatIntelligence/hosts/{host_id}/whois{odata_query}",
            )

        return self.ms_client.http_request(
            method="GET",
            url_suffix=f"v1.0/security/threatIntelligence/whoisRecords/{whois_record_id}{odata_query}",
        )

    def host_whois_history(self, host_id: str, whois_record_id: str, odata: str, limit: str) -> list:
        """
        Retrieves WHOIS history records for a host or specific WHOIS record.
        Docs:
        https://learn.microsoft.com/en-us/graph/api/security-whoisrecord-list-history?view=graph-rest-1.0&tabs=http
        https://learn.microsoft.com/en-us/graph/api/security-whoishistoryrecord-get?view=graph-rest-1.0

        Args:
            host_id (str): The ID (host name or ip address) of the host to get WHOIS history for.
            whois_record_id (str): The ID of a specific WHOIS record to get history for.
            odata (str): OData query parameters for filtering and formatting results.
            limit (str): Maximum number of records to return (used when whois_history_record_id is not provided).

        Returns:
            list: A list of WHOIS history records containing registration details, contact information,
                    nameservers, and other domain registration data.
        """
        odata_query = f"?$top={limit}&{odata}"

        if host_id:
            response = self.ms_client.http_request(
                method="GET",
                url_suffix=f"v1.0/security/threatIntelligence/hosts/{host_id}/whois/history{odata_query}",
            )

            return response.get("value", [])

        response = self.ms_client.http_request(
            method="GET",
            url_suffix=f"v1.0/security/threatIntelligence/whoisRecords/{whois_record_id}/history{odata_query}",
        )

        return response.get("value", [])

    def host_reputation(self, host_id: str, odata: str) -> dict:
        """
        Retrieve host reputation information by host ID.
        Docs: https://learn.microsoft.com/en-us/graph/api/security-host-get-reputation?view=graph-rest-1.0&tabs=http

        Args:
            host_id (str): The ID (host name or ip address) of the host to retrieve reputation information for.
            odata (str): OData query parameters for filtering and formatting the response.

        Returns:
            dict: A dictionary containing host reputation information including reputation score,
                    classification, rules, and other reputation-related details.
        """
        odata_query = f"?{odata}" if odata else ""

        return self.ms_client.http_request(
            method="GET",
            url_suffix=f"v1.0/security/threatIntelligence/hosts/{host_id}/reputation{odata_query}",
        )


""" HELPER FUNCTIONS """


def ensure_only_one_argument_provided(**kwargs):
    """
    Ensures that exactly one of the provided keyword arguments has a non-empty value.

    Raises:
        ValueError: If none or more than one arguments are provided.
    """
    provided_keys = [key for key, value in kwargs.items() if value]
    total_provided = len(provided_keys)
    allowed_keys = ", ".join(kwargs.keys())

    if total_provided == 0:
        raise ValueError(f"You must provide one of the following arguments: {allowed_keys}.\nNone were provided.")
    elif total_provided > 1:
        raise ValueError(
            f"Only one of the following arguments should be provided: {allowed_keys}.\n"
            f"Currently provided: {', '.join(provided_keys)}."
        )


""" COMMAND FUNCTIONS """


def start_auth(client: Client) -> CommandResults:  # pragma: no cover
    """
    Initiates the authentication process for Microsoft Defender Threat Intelligence.

    Args:
        client (Client): The client instance used for authentication.

    Returns:
        CommandResults: Command results containing the authentication start response.
    """
    result = client.ms_client.start_auth("!msg-defender-threat-intel-auth-complete")
    return CommandResults(readable_output=result)


def complete_auth(client: Client) -> str:  # pragma: no cover
    """
    Completes the authentication process for Microsoft Defender Threat Intelligence.

    Args:
        client (Client): The client instance used for authentication.

    Returns:
        str: Success message indicating that authorization was completed successfully.
    """
    client.ms_client.get_access_token()
    return "✅ Authorization completed successfully."


def test_connection(client: Client) -> str:  # pragma: no cover
    """
    Tests the connection to Microsoft Defender Threat Intelligence by attempting to retrieve an access token.

    Args:
        client (Client): The client instance used for connection testing.

    Returns:
        str: Success message indicating that the connection test passed.
    """
    client.ms_client.get_access_token()
    return "✅ Success!"


def article_list_command(client: Client, args: dict[str, Any]) -> CommandResults:
    """
    Retrieves a list of articles from Microsoft Defender Threat Intelligence.

    Args:
        client (Client): The client instance used for API communication.
        args (dict[str, Any]): Command arguments containing:
            - article_id (str, optional): Specific article ID to retrieve.
            - odata (str, optional): OData query parameters for filtering.
            - limit (int, optional): Maximum number of articles to return. Defaults to 50.

    Returns:
        CommandResults: Command results containing the list of articles with ID and title information.
    """
    article_id = args.get("article_id", "")
    odata = args.get("odata", "")
    limit = args.get("limit", 50)

    response = client.article_list(article_id, odata, limit)

    if len(response) == 0:
        return CommandResults(readable_output="No articles were found.")

    display_data = [{"Article Id": article.get("id"), "Title": article.get("title")} for article in response]
    return CommandResults(
        "MSGDefenderThreatIntel.Article",
        "id",
        outputs=response,
        readable_output=tableToMarkdown(
            "Articles:",
            display_data,
            removeNull=True,
        ),
    )


def article_indicators_list_command(client: Client, args: dict[str, Any]) -> CommandResults:
    """
    Retrieves a list of indicators associated with articles from Microsoft Defender Threat Intelligence.

    Args:
        client (Client): The client instance used for API communication.
        args (dict[str, Any]): Command arguments containing:
            - article_id (str, optional): Specific article ID to retrieve indicators for.
            - article_indicator_id (str, optional): Specific article indicator ID to retrieve.
            - odata (str, optional): OData query parameters for filtering.
            - limit (int, optional): Maximum number of indicators to return. Defaults to 50.

    Returns:
        CommandResults: Command results containing the list of article indicators with ID and artifact ID information.
    """
    article_id = args.get("article_id", "")
    article_indicator_id = args.get("article_indicator_id", "")
    odata = args.get("odata", "")
    limit = args.get("limit", 50)
    ensure_only_one_argument_provided(article_id=article_id, article_indicator_id=article_indicator_id)
    response = client.article_indicator_list(article_id, article_indicator_id, odata, limit)

    if len(response) == 0:
        return CommandResults(readable_output="No article indicators were found.")

    display_data = [
        {
            "ID": indicator.get("id"),
            "Artifact Id": indicator.get("artifact").get("id") if isinstance(indicator.get("artifact"), dict) else None,
        }
        for indicator in response
    ]

    return CommandResults(
        "MSGDefenderThreatIntel.ArticleIndicator",
        "id",
        outputs=response,
        readable_output=tableToMarkdown(
            "Indicators:",
            display_data,
            removeNull=True,
        ),
    )


def profile_list_command(client: Client, args: dict[str, Any]) -> CommandResults:
    """
    Retrieves a list of intelligence profiles from Microsoft Defender Threat Intelligence.

    Args:
        client (Client): The client instance used for API communication.
        args (dict[str, Any]): Command arguments containing:
            - intel_profile_id (str, optional): Specific intelligence profile ID to retrieve.
            - odata (str, optional): OData query parameters for filtering.
            - limit (int, optional): Maximum number of profiles to return. Defaults to 50.

    Returns:
        CommandResults: Command results containing the list of intelligence profiles with ID and title information.
    """
    intel_profile_id = args.get("intel_profile_id", "")
    odata = args.get("odata", "")
    limit = args.get("limit", 50)

    response = client.profile_list(intel_profile_id, odata, limit)

    if len(response) == 0:
        return CommandResults(readable_output="No profiles were found.")

    display_data = [{"Profile ID": profile.get("id"), "Title": profile.get("title")} for profile in response]

    return CommandResults(
        "MSGDefenderThreatIntel.Profile",
        "id",
        outputs=response,
        readable_output=tableToMarkdown(
            "Profiles:",
            display_data,
            removeNull=True,
        ),
    )


def profile_indicators_list_command(client: Client, args: dict[str, Any]) -> CommandResults:
    """
    Retrieves a list of intelligence profile indicators from Microsoft Defender Threat Intelligence.

    Args:
        client (Client): The client instance used for API communication.
        args (dict[str, Any]): Command arguments containing:
            - intel_profile_id (str, optional): Specific intelligence profile ID to retrieve indicators for.
            - intel_profile_indicator_id (str, optional): Specific intelligence profile indicator ID to retrieve.
            - odata (str, optional): OData query parameters for filtering.
            - limit (int, optional): Maximum number of profile indicators to return. Defaults to 50.

    Returns:
        CommandResults: Command results containing the list of intelligence profile indicators
        with ID and artifact ID information.
    """
    intel_profile_id = args.get("intel_profile_id", "")
    intel_profile_indicator_id = args.get("intel_profile_indicator_id", "")
    odata = args.get("odata", "")
    limit = args.get("limit", 50)
    ensure_only_one_argument_provided(intel_profile_id=intel_profile_id, intel_profile_indicator_id=intel_profile_indicator_id)
    response = client.profile_indicators_list(intel_profile_id, intel_profile_indicator_id, odata, limit)

    if len(response) == 0:
        return CommandResults(readable_output="No profile indicators were found.")

    display_data = [
        {
            "ID": profileIndicator.get("id"),
            "Artifact Id": profileIndicator.get("artifact").get("id")
            if isinstance(profileIndicator.get("artifact"), dict)
            else None,
        }
        for profileIndicator in response
    ]

    return CommandResults(
        "MSGDefenderThreatIntel.ProfileIndicator",
        "id",
        outputs=response,
        readable_output=tableToMarkdown(
            "Profiles Indicators:",
            display_data,
            removeNull=True,
        ),
    )


def host_command(client: Client, args: dict[str, Any]) -> CommandResults:
    """
    Retrieves host information from Microsoft Defender Threat Intelligence.

    Args:
        client (Client): The client instance used for API communication.
        args (dict[str, Any]): Command arguments containing:
            - host_id (str, required): Specific host ID to retrieve information for.
            - odata (str, optional): OData query parameters for filtering.

    Returns:
        CommandResults: Command results containing the host information with ID, registrar, and registrant details.
    """
    host_id = args.get("host_id", "")
    odata = args.get("odata", "")
    response = client.host(host_id, odata)
    display_data = [
        {
            "Host Id": response.get("id"),
            "Host Registrar": response.get("registrar"),
            "Host Registrant": response.get("registrant"),
        }
    ]

    return CommandResults(
        "MSGDefenderThreatIntel.Host",
        "id",
        outputs=response,
        readable_output=tableToMarkdown(
            "Host:",
            display_data,
            removeNull=True,
        ),
    )


def host_whois_command(client: Client, args: dict[str, Any]) -> CommandResults:
    """
    Retrieves WHOIS record information for a host from Microsoft Defender Threat Intelligence.

    Args:
        client (Client): The client instance used for API communication.
        args (dict[str, Any]): Command arguments containing:
            - host_id (str, optional): Specific host ID to retrieve WHOIS information for.
            - whois_record_id (str, optional): Specific WHOIS record ID to retrieve.
            - odata (str, optional): OData query parameters for filtering.

    Returns:
        CommandResults: Command results containing the WHOIS record information with ID, WHOIS server, and domain status details.
    """
    host_id = args.get("host_id", "")
    whois_record_id = args.get("whois_record_id", "")
    odata = args.get("odata", "")
    ensure_only_one_argument_provided(host_id=host_id, whois_record_id=whois_record_id)
    response = client.host_whois(host_id, whois_record_id, odata)
    display_data = [
        {"Id": response.get("id"), "Whois Server": response.get("whoisServer"), "Domain Status": response.get("domainStatus")}
    ]

    return CommandResults(
        "MSGDefenderThreatIntel.Whois",
        "id",
        outputs=response,
        readable_output=tableToMarkdown(
            "Whois:",
            display_data,
            removeNull=True,
        ),
    )


def host_whois_history_command(client: Client, args: dict[str, Any]) -> CommandResults:
    """
    Retrieves WHOIS history records for a host from Microsoft Defender Threat Intelligence.

    Args:
        client (Client): The client instance used for API communication.
        args (dict[str, Any]): Command arguments containing:
            - host_id (str, optional): Specific host ID to retrieve WHOIS history for.
            - whois_record_id (str, optional): Specific WHOIS record ID to retrieve history for.
            - odata (str, optional): OData query parameters for filtering.
            - limit (int, optional): Maximum number of records to return. Defaults to 50.

    Returns:
        CommandResults: Command results containing the WHOIS history records with ID, WHOIS server, and domain status details.
    """
    host_id = args.get("host_id", "")
    whois_record_id = args.get("whois_record_id", "")
    odata = args.get("odata", "")
    limit = args.get("limit", 50)

    ensure_only_one_argument_provided(host_id=host_id, whois_record_id=whois_record_id)

    response = client.host_whois_history(host_id, whois_record_id, odata, limit)

    if len(response) == 0:
        return CommandResults(readable_output="No WHOIS history records found.")

    display_data = [
        {
            "Id": whois_record.get("id"),
            "Whois Server": whois_record.get("whoisServer"),
            "Domain Status": whois_record.get("domainStatus"),
        }
        for whois_record in response
    ]

    return CommandResults(
        "MSGDefenderThreatIntel.WhoisHistory",
        "id",
        outputs=response,
        readable_output=tableToMarkdown(
            "Whois:",
            display_data,
            removeNull=True,
        ),
    )


def host_reputation_command(client: Client, args: dict[str, Any]) -> CommandResults:
    """
    Retrieves host reputation information from Microsoft Defender Threat Intelligence.

    Args:
        client (Client): The client instance used for API communication.
        args (dict[str, Any]): Command arguments containing:
            - host_id (str, required): Specific host ID to retrieve reputation information for.
            - odata (str, optional): OData query parameters for filtering.

    Returns:
        CommandResults: Command results containing the host reputation information with ID, classification, and score details.
    """
    host_id = args.get("host_id", "")
    odata = args.get("odata", "")
    response = client.host_reputation(host_id, odata)
    display_data = [
        {
            "Host Id": response.get("id"),
            "Host Classification": response.get("classification"),
            "Host Score": response.get("score"),
        }
    ]

    return CommandResults(
        "MSGDefenderThreatIntel.HostReputation",
        "id",
        outputs=response,
        readable_output=tableToMarkdown(
            "Host Reputation:",
            display_data,
            removeNull=True,
        ),
    )


def main():
    command = demisto.command()
    demisto.debug(f"Command being called is {command}")
    try:
        params = demisto.params()
        args = demisto.args()
        handle_proxy()
        client = Client(
            app_id=params.get("app_id"),
            verify=not params.get("insecure", False),
            proxy=params.get("proxy", False),
            azure_ad_endpoint=params.get("azure_ad_endpoint", "https://login.microsoftonline.com")
            or "https://login.microsoftonline.com",
            tenant_id=params.get("tenant_id"),
            client_credentials=params.get("client_credentials", False),
            enc_key=(params.get("credentials") or {}).get("password"),
            managed_identities_client_id=get_azure_managed_identities_client_id(params),
            certificate_thumbprint=params.get("creds_certificate", {}).get("identifier"),
            private_key=(replace_spaces_in_credential(params.get("creds_certificate", {}).get("password"))),
        )
        if command == "test-module":
            if client.ms_client.managed_identities_client_id or client.ms_client.grant_type == CLIENT_CREDENTIALS:
                test_connection(client=client)
                return_results("ok")
            else:
                return_results("The test module is not functional, run the msg-defender-threat-intel-auth-start command instead.")
        elif command == "msg-defender-threat-intel-auth-start":
            return_results(start_auth(client))
        elif command == "msg-defender-threat-intel-auth-complete":
            return_results(complete_auth(client))
        elif command == "msg-defender-threat-intel-auth-test":
            return_results(test_connection(client))
        elif command == "msg-defender-threat-intel-auth-reset":
            return_results(reset_auth())
        elif command == "msg-defender-threat-intel-article-list":
            return_results(article_list_command(client, args))
        elif command == "msg-defender-threat-intel-article-indicators-list":
            return_results(article_indicators_list_command(client, args))
        elif command == "msg-defender-threat-intel-profile-list":
            return_results(profile_list_command(client, args))
        elif command == "msg-defender-threat-intel-profile-indicators-list":
            return_results(profile_indicators_list_command(client, args))
        elif command == "msg-defender-threat-intel-host":
            return_results(host_command(client, args))
        elif command == "msg-defender-threat-intel-host-whois":
            return_results(host_whois_command(client, args))
        elif command == "msg-defender-threat-intel-host-whois-history":
            return_results(host_whois_history_command(client, args))
        elif command == "msg-defender-threat-intel-host-reputation":
            return_results(host_reputation_command(client, args))
        else:
            raise NotImplementedError(f"Command {command} is not implemented")

    except Exception as e:
        return_error(f"Failed to execute {command} command.\nError:\n{str(e)}")


if __name__ in ("__main__", "__builtin__", "builtins"):  # pragma: no cover
    main()