Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
Azure
GitHub Repository: Azure/Azure-Sentinel-Notebooks
Path: blob/master/tutorials-and-examples/how-tos/TroubleShootingNotebooks.ipynb
3253 views
Kernel: Python 3.10 - SDK v2

Troubleshooting Microsoft Sentinel Notebooks

If you are having trouble with Jupyter notebooks run this notebook to help identify where the problem might be.

Select the notebook menu item Cell->Run All - check for any warnings or errors.

Read the text above the cell(s) that produce errors - the text contains links to resources that describe how to fix the error.

Important: you can run the cells individually but please run the Python Version Check cell first, since this contains some function definitions used by the other cells.

Python Version Check

Note You can set the default Python version in Azure Notebooks project settings. For details on how to do this see AzureNotebooks-ConfigurePythonVersion

If you are using a Data Science Virtual Machine as your Azure Notebooks compute you should read Provisioning a DSVM

import sys from IPython.display import display, HTML, Markdown MIN_REQ_PYTHON = (3, 10) errors = [] warns = [] info = [] def setup_err(mssg): display(Markdown("<h3><font color='red'>Setup Error</font></h3>")) display(Markdown("<h4><font color='red'>%s</font></h4>" % mssg)) errors.append(mssg) def setup_ok(mssg): display(Markdown("<h4><font color='blue'>%s - OK</font></h4>" % mssg)) info.append(mssg) def setup_warn(mssg): display(Markdown("<h4><font color='orange'>%s</font></h4>" % mssg)) warns.append(mssg) display(Markdown("#### Checking Python version...")) if sys.version_info < MIN_REQ_PYTHON: setup_err("Python version") display(Markdown('Check the Kernel->Change Kernel menu and ensure that Python 3.10')) display(Markdown('or later is selected as the active kernel.')) else: setup_ok( "Python version {}.{}.{}".format( sys.version_info[0], sys.version_info[1], sys.version_info[2] ) )

Checking Python version...

Python version 3.7.10 - OK

Package Import Check

This section checks the import of msticpy and its dependent packages.

Note If you are repeatedly seeing packages going missing when working in Azure Notebooks this may be because the docker containers running the Python kernel are recycled after a few hours when not in use. This causes the environments to reset to defaults. To prevent this you should configure you Azure Notebooks project with a requirements.txt file that is automatically run (and packages installed) when the contain is initialized.

For details on how to do this see AzureNotebooks-ConfigurePythonVersion

import importlib import pkg_resources import sys import warnings from IPython.display import display, HTML, Markdown MSTICPY_REQ_VERSION = "2.12.0" display(Markdown("#### Checking msticpy...")) warn_mssg = [] err_mssg = [] restart_req = False def _get_pkg_version(version): if isinstance(version, str): return pkg_resources.parse_version(version) elif isinstance(version, tuple): return pkg_resources.parse_version(".".join(str(ver) for ver in version)) raise TypeError(f"Unparseable type version {version}") MISSING_PKG_ERR = """ <h3><font color='red'>Warning {package} is not installed or has an unsupported version</h3></font> """ need_update = False try: import msticpy mp_version = _get_pkg_version(msticpy.__version__) required_ver = _get_pkg_version(MSTICPY_REQ_VERSION) if mp_version < required_ver: setup_err( f"Installed version of msticpy is {mp_version}. " f"msticpy {required_ver} or later is required." ) need_update = True else: setup_ok(f"msticpy version {msticpy.__version__}") except ImportError: display(HTML(MISSING_PKG_ERR.format(package="msticpy"))) need_update = True if need_update: resp = input("Install the package now? (y/n)") if resp.casefold().startswith("y"): %pip install --upgrade msticpy if "msticpy" in sys.modules: importlib.reload(sys.modules["msticpy"]) else: import msticpy print(f"msticpy installed - version {msticpy.__version__}") else: setup_warn("msticpy missing or out-of-date.") display(Markdown("Please run `pip install --upgrade msticpy` to upgrade/install msticpy")) try: import msticpy msticpy.check_version() except ImportError: pass

Checking msticpy...

msticpy version 1.1.0 - OK

msticpy version installed: 1.1.0 latest published: 1.0.0 Latest version is installed.

Pandas Version Check

Many of the notebooks and msticpy features require a mininum pandas version of 0.25.0.

display(Markdown("#### Checking pandas...")) PANDAS_REQ_VERSION = (1, 45, 0) need_update = False try: import pandas as pd pd_version = tuple([int(v) for v in pd.__version__.split(".")]) if pd_version < PANDAS_REQ_VERSION: setup_err("pandas %s.%s.%s or later is required." % PANDAS_REQ_VERSION) need_update = True except ImportError: display(HTML(MISSING_PKG_ERR.format(package="pandas"))) need_update = True else: setup_ok(f"Pandas version {pd.__version__}") if need_update: resp = input("Install the package now? (y/n)") if resp.casefold().startswith("y"): %pip install --upgrade pandas if "pandas" in sys.modules: importlib.reload(pd) else: import pandas as pd print(f"pandas installed - version {pd.__version__}") else: setup_warn("pandas missing or out-of-date.") display(Markdown("Please run `pip install --upgrade pandas` to upgrade/install pandas"))

Checking pandas...

Pandas version 1.1.3 - OK

Workspace Configuration Check

This section checks for presence of configuration files config.json and msticpyconfig.yaml

The msticpyconfig.yaml can store the workspace and tenant information for your Microsoft Sentinel workspace. It can also store values for multiple workspaces. If you have the values configured in this file you do not need to worry about the values in config.json.

You can specify the location of your msticpyconfig.yaml in the environment variable MSTICPYCONFIG. This will make the file accessible to all notebooks running on the system. For more information on configuring msticpyconfig.yaml see the next cell mstipcy Configuration

If you want to transfer your workspace settings to msticpyconfig.yaml from config.json, simply copy the value of the tenant_id and workspace_id settings to the relevant section.

Note the value names in msticpyconfig.yaml use slightly different naming conventions:

WorkspaceId: 0cd830ff-60dc-40d1-8045-11d2b7b277e1 TenantId: aff2102d-1d6c-4501-9efb-6053ab7efb19

Workspace Configuration - config.json

Creating a Microsoft Notebooks project from Microsoft Sentinel will automatically create a config.json file in the root of your Azure Notebooks project and populate values for your Microsoft Sentinel workspace.

If you have copied the notebooks elsewhere (e.g. to run them locally, or you are running them on a Data Science Virtual machine) you should copy this original config.json to the folder from which you are running notebooks.

Note if you are using a msticpyconfig.yaml to store your workspace settings, most notebooks will take values from that. As with config.json - you must have a locally accessible copy of this file, so you will need to copy it to other systems if you are running notebooks from there.

If you are using the config.json (default config for Microsoft Sentinel with Azure Notebooks), your config.json should look something like this

{ "tenant_id": "aff2102d-1d6c-4501-9efb-6053ab7efb19", "subscription_id": "9ce7caeb-1f42-4141-b076-7f448a00aceb", "resource_group": "MyResourceGroup", "workspace_id": "0cd830ff-60dc-40d1-8045-11d2b7b277e1", "workspace_name": "MyResourceSubscription" }

The tenant_id and workspace_id values must be configured, other values are optional but recommended.

import os import json from pathlib import Path import uuid import yaml def valid_uuid(uuid_str): try: uuid.UUID(uuid_str) except (ValueError, TypeError): return False return True def check_mp_config_ws(config_file): with open(config_file, "r") as mp_yml: mp_config = yaml.safe_load(mp_yml) mp_errors = [] as_settings = mp_config.get("AzureSentinel", {}) if not as_settings: mp_errors.append(f"Missing or empty 'AzureSentinel' section in {config_file}") ws_settings = as_settings.get("Workspaces", {}) if not ws_settings: mp_errors.append(f"Missing or empty 'Workspaces' section in {config_file}") no_default = True for ws, ws_settings in ws_settings.items(): if ws == "Default": no_default = False ws_id = ws_settings.get("WorkspaceId") if not ws_id and not valid_uuid(ws_id): mp_errors.append(f"Invalid GUID for WorkspaceId in {ws} section") ten_id = ws_settings.get("TenantId") if not ten_id and not valid_uuid(ten_id): mp_errors.append(f"Invalid GUID for TenantId in {ws} section") warnings = ["No default workspace set"] if no_default else [] return mp_errors, warnings try: from msticpy.common.pkg_config import validate_config except ImportError: # Fall back on local check if we cannot import from MP validate_config = check_mp_config_ws def check_json_config(json_path): j_conf_errs = [] with open(json_path, "r") as json_file: conf_json = json.load(json_file) conf_tenant = conf_json.get("tenant_id") if conf_tenant == "{{cookiecutter.tenant_id}}": j_conf_errs.append("Tenant Id is set to default value") elif not valid_uuid(conf_tenant): j_conf_errs.append("Tenant ID is not a valid GUID.") conf_ws = conf_json.get("workspace_id") if conf_ws == "{{cookiecutter.workspace_id}}": j_conf_errs.append("Workspace Id is set to default value") elif not valid_uuid(conf_ws): j_conf_errs.append("Workspace ID is not a valid GUID.") return j_conf_errs def get_aml_user_folder(): """Return the root of the user folder.""" user_path = Path("/") path_parts = Path(".").absolute().parts for idx, part in enumerate(path_parts): if part.casefold() == "users": user_path = user_path.joinpath(part).joinpath(path_parts[idx + 1]) break user_path = user_path.joinpath(part) return user_path mp_warnings = [] display(Markdown("#### Checking Microsoft Sentinel Workspace config...")) mp_path = os.environ.get("MSTICPYCONFIG", "./msticpyconfig.yaml") if not Path(mp_path).exists(): if Path(get_aml_user_folder()).joinpath("msticpyconfig.yaml"): setup_warn( "A 'msticpyconfig.yaml' was found in the root of your user folder" + " but the MSTICPYCONFIG variable is not set. This file will be" + " used by default." ) mp_path = str(Path(get_aml_user_folder()).joinpath("msticpyconfig.yaml")) if Path(mp_path).exists(): mp_errs, mp_warnings = validate_config(config_file=mp_path) else: mp_errs = [f"{mp_path} not found"] DEF_CONF_JSON = "./config.json" jc_errs = [] if Path(DEF_CONF_JSON).exists(): jc_errs = check_json_config(DEF_CONF_JSON) if jc_errs and mp_errs: setup_err("No valid workspace configuration found.") if jc_errs: print(jc_errs) if mp_errs: print(mp_errs) else: if not jc_errs: setup_ok(f"Workspace configuration found in '{DEF_CONF_JSON}'") if not mp_errs: setup_ok(f"Workspace configuration found in '{mp_path}'") else: setup_warn(f"Workspace configuration: Cannot find msticpy config file {mp_path}") if mp_warnings: display(Markdown(f"<h5><font color='orange'>{', '.join(mp_warnings)}</font></h5>"))

Checking Microsoft Sentinel Workspace config...

No errors found. No warnings found.

Workspace configuration found in 'e:\src\microsoft\msticpyconfig.yaml' - OK

msticpy Initialization

This section duplicates the setup cells of most of the notebooks. It may duplicate warnings seen in the previous cell (since it runs some of the same checks).

For more information on msticpy configuration file settings, please refer to the following items:

from pathlib import Path from IPython.display import display, HTML REQ_PYTHON_VER = "3.10" REQ_MSTICPY_VER = "2.12.0" REQ_MP_EXTRAS = [] display(HTML("<h3>Starting Notebook setup...</h3>")) # If not using Azure Notebooks, install msticpy with # %pip install msticpy import msticpy as mp nb_init_result = mp.init_notebook( namespace=globals(), verbose=True ) if nb_init_result: setup_ok("MSTICPy initialization ran without critical errors.")
msticpy version installed: 1.1.0 latest published: 1.0.0 Latest version is installed. Processing imports.... pandas imported (alias=pd) get_ipython imported from IPython (alias=None) display imported from IPython.display (alias=None) HTML imported from IPython.display (alias=None) Markdown imported from IPython.display (alias=None) ipywidgets imported (alias=widgets) Path imported from pathlib (alias=None) matplotlib.pyplot imported (alias=plt) MatplotlibDeprecationWarning imported from matplotlib (alias=None) seaborn imported (alias=sns) numpy imported (alias=np) pandas imported version 1.1.3 QueryProvider imported from msticpy.data (alias=None) FoliumMap imported from msticpy.nbtools.foliummap (alias=None) md imported from msticpy.common.utility (alias=None) md_warn imported from msticpy.common.utility (alias=None) WorkspaceConfig imported from msticpy.common.wsconfig (alias=None) Pivot imported from msticpy.datamodel.pivot (alias=None) entities imported from msticpy.datamodel (alias=None) All items imported from msticpy.nbtools All items imported from msticpy.sectools Imported: pd (pandas); IPython.get_ipython; IPython.display.display; IPython.display.HTML; IPython.display.Markdown; widgets (ipywidgets); pathlib.Path; plt (matplotlib.pyplot); matplotlib.MatplotlibDeprecationWarning; sns (seaborn); np (numpy); msticpy.data.QueryProvider; msticpy.nbtools.foliummap.FoliumMap; msticpy.common.utility.md; msticpy.common.utility.md_warn; msticpy.common.wsconfig.WorkspaceConfig; msticpy.datamodel.pivot.Pivot; msticpy.datamodel.entities Checking configuration.... No errors found. No warnings found. Setting notebook options.... Friendly exceptions enabled.

MSTICPy configuration ran without critical errors. - OK

msticpy general troubleshooting

To help determine the cause of a problem you can turn on the following settings:

  1. MSTICPY debug logging

  2. Verbose reporting of Exceptions in Jupyter notebooks

How to do these is covered in the code below

# MSTICPy debug logging import msticpy as mp mp.set_logging_level("DEBUG") # Then re-run the cell(s) that you are having problems with

From the logging you can see more detail of where an operation in the code is failing.

2024-06-17 13:27:23,906: INFO - Read 3 queries from E:\src\msticpy\msticpy\data\queries\m365d\kql_m365_identity.yaml (data_query_reader#89) 2024-06-17 13:27:23,910: INFO - Read 5 queries from E:\src\msticpy\msticpy\data\queries\m365d\kql_m365_network.yaml (data_query_reader#89) 2024-06-17 13:27:23,925: INFO - Read 4 queries from E:\src\msticpy\msticpy\data\queries\m365d\kql_m365_process.yaml (data_query_reader#89) 2024-06-17 13:27:23,946: INFO - Read 4 queries from E:\src\msticpy\msticpy\data\queries\m365d\kql_m365_user.yaml (data_query_reader#89) 2024-06-17 13:27:23,946: INFO - Adding query functions to provider (data_providers#137) 2024-06-17 13:27:23,966: INFO - Initialization complete. (data_providers#140) 2024-06-17 13:27:23,967: INFO - Calling connect on driver (data_providers#188) 2024-06-17 13:27:23,968: INFO - WorkspaceConfig created from workspace name None (azure_monitor_driver#440) Attempting connection to Key Vault using cli credentials... 2024-06-17 13:27:23,974: INFO - az_connect_core - using global cloud and endpoint: https://login.microsoftonline.com/ (azure_auth_core#290) 2024-06-17 13:27:23,974: INFO - TenantId: None, requested auth methods: cli (azure_auth_core#294) 2024-06-17 13:27:23,975: INFO - Cred types added to chained credential: AzureCliCredential (azure_auth_core#382) done 2024-06-17 13:27:29,407: INFO - az_connect_core - using global cloud and endpoint: https://login.microsoftonline.com/ (azure_auth_core#290) 2024-06-17 13:27:29,407: INFO - TenantId: 72f988bf-86f1-41af-91ab-2d7cd011db47, requested auth methods: cli, interactive (azure_auth_core#294) 2024-06-17 13:27:29,407: INFO - Cred types added to chained credential: AzureCliCredential, InteractiveBrowserCredential (azure_auth_core#382) 2024-06-17 13:27:29,407: INFO - Created query client. Auth type: <class 'azure.identity._credentials.chained.ChainedTokenCredential'>, Url: https://api.loganalytics.io/v1, Proxies: None (azure_monitor_driver#400) 2024-06-17 13:27:29,407: INFO - az_connect_core - using global cloud and endpoint: https://login.microsoftonline.com/ (azure_auth_core#290) 2024-06-17 13:27:29,407: INFO - TenantId: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx, requested auth methods: cli, interactive (azure_auth_core#294) 2024-06-17 13:27:29,407: INFO - Cred types added to chained credential: AzureCliCredential, InteractiveBrowserCredential (azure_auth_core#382) 2024-06-17 13:27:33,668: INFO - Schema request to https://management.azure.com/subscriptions/xxxxxxxxxxxxxxxxxxxxxxxxxxx/resourcegroups/soc/providers/Microsoft.OperationalInsights/workspaces/CyberSecuritySOC/tables?api-version=2021-12-01-preview (azure_monitor_driver#607) 2024-06-17 13:27:36,531: INFO - Schema retrieved from workspace. 723 tables found. (azure_monitor_driver#618) 2024-06-17 13:27:36,547: INFO - Adding query pivot functions (data_providers#213) connected
# Set verbose exception reporting %xmode Verbose
Exception reporting mode: Verbose
--------------------------------------------------------------------------- ValueError Traceback (most recent call last) Cell In[6], line 13 10 my_param = "test" 11 func_with_error(param=my_param) ---> 13 func_calling_error() Cell In[6], line 11, in func_calling_error() 9 def func_calling_error(): 10 my_param = "test" ---> 11 func_with_error(param=my_param) my_param = 'test' Cell In[6], line 7, in func_with_error(*args=(), **kwargs={'param': 'test'}) 5 def func_with_error(*args, **kwargs): 6 param = kwargs.get("param") ----> 7 raise ValueError("This is a deliberate error") ValueError: This is a deliberate error

Verbose Exception tracing

Notice that in verbose mode you can see the values passed as parameters to functions, which helps a lot when diagnosing what has gone wrong

Example code with a failure

# Example def func_with_error(*args, **kwargs): param = kwargs.get("param") raise ValueError("This is a deliberate error") def func_calling_error(): my_param = "test" func_with_error(param=my_param) func_calling_error()

Resultant traceback

--------------------------------------------------------------------------- ValueError Traceback (most recent call last) Cell In[6], line 13 10 my_param = "test" 11 func_with_error(param=my_param) ---> 13 func_calling_error() Cell In[6], line 11 9 def func_calling_error(): 10 my_param = "test" ---> 11 func_with_error(param=my_param) my_param = 'test' Cell In[6], line 7 5 def func_with_error(*args, **kwargs): 6 param = kwargs.get("param") ----> 7 raise ValueError("This is a deliberate error") ValueError: This is a deliberate error

Summary

if errors: display(Markdown(f"<h3><font color='red'><u>{len(errors)} errors:</u></font>")) for mssg in errors: display(Markdown(f"<font color='red'>{mssg}</font>")) if warns: display(Markdown(f"<h3><font color='orange'><u>{len(warns)} warnings:</u></font>")) for mssg in warns: display(Markdown(f"<font color='orange'>{mssg}</font>")) display(Markdown(f"<h3><font color='blue'><u>Info/Success:</u></font>")) for mssg in info: display(Markdown(f"<font color='blue'>{mssg}</font>"))

Info/Success:

Python version 3.7.10

MSTICPy configuration ran with errors.