scenario-notebooks/Automated-Notebooks/AutomationGallery-CredentialScanOnAzureLogAnalytics.ipynb (488 lines of code) (raw):
{
"cells": [
{
"cell_type": "markdown",
"source": [
"# Automation Gallery - Credential Scan on Azure Log Analytics\n",
"\n",
"__Notebook Version:__ 1.0<br>\n",
"__Python Version:__ Python 3.8<br>\n",
"__Apache Spark Version:__ 3.1<br>\n",
"__Required Packages:__ azure-monitor-query, azure-mgmt-loganalytics<br>\n",
"__Platforms Supported:__ Azure Synapse Analytics\n",
" \n",
"__Data Source Required:__ Log Analytics tables \n",
" \n",
"### Description\n",
"This notebook provides step-by-step instructions and sample code to detect credential leak into Azure Log Analytics using Azure SDK for Python and KQL.<br>\n",
"*** Please run the cells sequentially to avoid errors. Please do not use \"run all cells\". *** <br>\n",
"Need to know more about KQL? [Getting started with Kusto Query Language](https://docs.microsoft.com/azure/data-explorer/kusto/concepts/).\n",
"\n",
"## Table of Contents\n",
"1. Warm-up\n",
"2. Azure Authentication\n",
"3. Azure Log Analytics Data Queries\n",
"4. Save result to Microsoft Sentinel Dynamic Summaries"
],
"metadata": {}
},
{
"cell_type": "markdown",
"source": [
"## 1. Warm-up"
],
"metadata": {}
},
{
"cell_type": "code",
"source": [
"# Load Python libraries that will be used in this notebook\n",
"from azure.mgmt.loganalytics import LogAnalyticsManagementClient\n",
"from azure.monitor.query import LogsQueryClient, MetricsQueryClient, LogsQueryStatus\n",
"from azure.identity import AzureCliCredential, DefaultAzureCredential, ClientSecretCredential\n",
"from azure.core.exceptions import HttpResponseError \n",
"\n",
"from datetime import datetime, timezone, timedelta\n",
"import pandas as pd\n",
"import json\n",
"import re\n",
"import ipywidgets\n",
"from IPython.display import display, HTML, Markdown"
],
"outputs": [],
"execution_count": null,
"metadata": {}
},
{
"cell_type": "code",
"source": [
"# Functions will be used in this notebook \n",
"def get_credscan_kql_where_clause(column_name):\n",
" \"This function return the KQL where clause for credscan\"\n",
" where_clause = \" | where TimeGenerated > ago({0}) | where {1} \"\n",
" time_range = \"7d\"\n",
" regex_string = \"\"\n",
" regex_list = [\n",
" r\"(?i)(ida:password|IssuerSecret|(api|client|app(lication)?)[_\\\\- ]?(key|secret)[^,a-z]|\\\\.azuredatabricks\\\\.net).{0,10}(dapi)?[a-z0-9/+]{22}\",\n",
" r\"(?i)(x-api-(key|token).{0,10}[a-z0-9/+]{40}|v1\\\\.[a-z0-9/+]{40}[^a-z0-9/+])\",\n",
" r\"(?-i)\\\\WAIza(?i)[a-z0-9_\\\\\\\\\\\\-]{35}\\\\W\",\n",
" r\"(?i)(\\\\Wsig\\\\W|Secret(Value)?|IssuerSecret|(\\\\Wsas|primary|secondary|management|Shared(Access(Policy)?)?).?Key|\\\\.azure\\\\-devices\\\\.net|\\\\.(core|servicebus|redis\\\\.cache|accesscontrol|mediaservices)\\\\.(windows\\\\.net|chinacloudapi\\\\.cn|cloudapi\\\\.de|usgovcloudapi\\\\.net)|New\\\\-AzureRedisCache).{0,100}([a-z0-9/+]{43}=)\",\n",
" r\"(?i)visualstudio\\\\.com.{1,100}\\\\W(?-i)[a-z2-7]{52}\\\\W\",\n",
" r\"(?i)se=2021.+sig=[a-z0-9%]{43,63}%3d\",\n",
" r\"(?i)(x-functions-key|ApiKey|Code=|\\\\.azurewebsites\\\\.net/api/).{0,100}[a-z0-9/\\\\+]{54}={2}\",\n",
" r\"(?i)code=[a-z0-9%]{54,74}(%3d){2}\",\n",
" r\"(?i)(userpwd|publishingpassword).{0,100}[a-z0-9/\\\\+]{60}\\\\W\",\n",
" r\"(?i)[^a-z0-9/\\\\+][a-z0-9/\\\\+]{86}==\",\n",
" r\"(?-i)\\\\-{5}BEGIN( ([DR]SA|EC|OPENSSH|PGP))? PRIVATE KEY( BLOCK)?\\\\-{5}\",\n",
" r\"(?i)(app(lication)?|client)[_\\\\- ]?(key(url)?|secret)([\\\\s=:>]{1,10}|[\\\\s\\\"':=|>\\\\]]{3,15}|[\\\"'=:\\\\(]{2})[^\\\\-]\",\n",
" r\"(?i)refresh[_\\\\-]?token([\\\\s=:>]{1,10}|[\\\\s\\\"':=|>\\\\]]{3,15}|[\\\"'=:\\\\(]{2})(\\\"data:text/plain,.+\\\"|[a-z0-9/+=_.-]{20,200})\",\n",
" r\"(?i)AccessToken(Secret)?([\\\\s\\\"':=|>\\\\]]{3,15}|[\\\"'=:\\\\(]{2}|[\\\\s=:>]{1,10})[a-z0-9/+=_.-]{20,200}\",\n",
" r\"(?i)[a-z0-9]{3,5}://[^%:\\\\s\\\"'/][^:\\\\s\\\"'/\\\\$]+[^:\\\\s\\\"'/\\\\$%]:([^%\\\\s\\\"'/][^@\\\\s\\\"'/]{0,100}[^%\\\\s\\\"'/])@[\\\\$a-z0-9:\\\\.\\\\-_%\\\\?=/]+\",\n",
" r\"(?i)snmp(\\\\-server)?\\\\.exe.{0,100}(priv|community)\",\n",
" r\"(?i)(ConvertTo\\\\-?SecureString\\\\s*((\\\\(|\\\\Wstring)\\\\s*)?['\\\"]+)\",\n",
" r\"(?i)(Consumer|api)[_\\\\- ]?(Secret|Key)([\\\\s=:>]{1,10}|[\\\\s\\\"':=|>,\\\\]]{3,15}|[\\\"'=:\\\\(]{2})[^\\\\s]{5,}\",\n",
" r\"(?i)authorization[,\\\\[:= \\\"']+([dbaohmnsv])\",\n",
" r\"(?i)-u\\\\s+.{2,100}-p\\\\s+[^\\\\-/]\",\n",
" r\"(?i)(amqp|ssh|(ht|f)tps?)://[^%:\\\\s\\\"'/][^:\\\\s\\\"'/\\\\$]+[^:\\\\s\\\"'/\\\\$%]:([^%\\\\s\\\"'/][^@\\\\s\\\"'/]{0,100}[^%\\\\s\\\"'/])@[\\\\$a-z0-9:\\\\.\\\\-_%\\\\?=/]+\",\n",
" r\"(?i)(\\\\Waws|amazon)?.{0,5}(secret|access.?key).{0,10}\\\\W[a-z0-9/\\\\+]{40}\",\n",
" r\"(?-i)(eyJ0eXAiOiJKV1Qi|eyJhbGci)\",\n",
" r\"(?i)@(\\\\.(on)?)?microsoft\\\\.com[ -~\\\\s]{1,100}?(\\\\w?pass\\\\w?)\",\n",
" r\"(?i)net(\\\\.exe)?.{1,5}(user\\\\s+|share\\\\s+/user:|user-?secrets? set)\\\\s+[a-z0-9]\",\n",
" r\"(?i)xox[pbar]\\\\-[a-z0-9]\",\n",
" r\"(?i)[\\\":\\\\s=]((x?corp|extranet(test)?|ntdev)(\\\\.microsoft\\\\.com)?|corp|redmond|europe|middleeast|northamerica|southpacific|southamerica|fareast|africa|exchange|extranet(test)?|partners|parttest|ntdev|ntwksta)\\\\W.{0,100}(password|\\\\Wpwd|\\\\Wpass|\\\\Wpw\\\\W|userpass)\",\n",
" r\"(?i)(sign_in|SharePointOnlineAuthenticatedContext|(User|Exchange)Credentials?|password)[ -~\\\\s]{0,100}?@([a-z0-9.]+\\\\.(on)?)?microsoft\\\\.com['\\\"]?\",\n",
" r\"(?i)(\\\\.database\\\\.azure\\\\.com|\\\\.database(\\\\.secure)?\\\\.windows\\\\.net|\\\\.cloudapp\\\\.net|\\\\.database\\\\.usgovcloudapi\\\\.net|\\\\.database\\\\.chinacloudapi\\\\.cn|\\\\.database.cloudapi.de).{0,100}(DB_PASS|(sql|service)?password|\\\\Wpwd\\\\W)\",\n",
" r\"(?i)(secret(.?key)?|password)[\\\"']?\\\\s*[:=]\\\\s*[\\\"'][^\\\\s]+?[\\\"']\",\n",
" r\"(?i)[^a-z\\\\$](DB_USER|user id|uid|(sql)?user(name)?|service\\\\s?account)\\\\s*[^\\\\w\\\\s,]([ -~\\\\s]{2,120}?|[ -~]{2,30}?)([^a-z\\\\s\\\\$]|\\\\s)\\\\s*(DB_PASS|(sql|service)?password|pwd)\",\n",
" r\"(?i)(password|secret(key)?)[ \\\\t]*[=:]+[ \\\\t]*([^:\\\\s\\\"';,<]{2,200})\",\n",
" ]\n",
"\n",
" for (i, re_str) in enumerate(regex_list):\n",
" if i != 0:\n",
" if i == 27:\n",
" regex_string += \" and \"\n",
" else:\n",
" regex_string += \" or \" \n",
"\n",
" if column_name == \"*\":\n",
" regex_string += \" \" + column_name + \" matches regex \\\"\" + re_str + \"\\\"\"\n",
" else:\n",
" regex_string += \" tostring(\" + column_name + \") matches regex \\\"\" + re_str + \"\\\"\"\n",
"\n",
" return where_clause.format(time_range, regex_string)\n",
"\n",
"def filter_column(comumn_name):\n",
" \"This function will be used to filter out columns that you don't want to run KQL against (True). You may customize the filter to meet your requirements\"\n",
" if column_name.find('Description') >= 0:\n",
" return False\n",
" elif column_name.find('Id') >= 0 or column_name.find('TimeGenerated') >= 0:\n",
" return True\n",
" else:\n",
" regex_str = '_[a-z]'\n",
" re.compile(regex_str)\n",
" results = re.findall(regex_str, comumn_name)\n",
" if results:\n",
" return True\n"
],
"outputs": [],
"execution_count": null,
"metadata": {}
},
{
"cell_type": "code",
"source": [
"import uuid\r\n",
"import requests\r\n",
"\r\n",
"class DynamicSummary():\r\n",
" \"\"\" Dynamic Summary object model \"\"\"\r\n",
" \r\n",
" @staticmethod\r\n",
" def get_new_guid():\r\n",
" \"\"\" generate new GUID \"\"\"\r\n",
" return uuid.uuid4()\r\n",
"\r\n",
" def __init__(self, summary_id):\r\n",
" self.summary_id = summary_id\r\n",
"\r\n",
" def serialize(self):\r\n",
" serialized_str = '\"summaryId\": \"' + self.summary_id + '\", \"summaryName\": \"' + self.summary_name + '\", \"azureTenantId\": \"' + self.azure_tenant_id + '\", \"summaryDescription\": \"' + self.summary_description + '\"'\r\n",
" if hasattr(self, 'relation_name') and self.relation_name != None:\r\n",
" serialized_str += ', \"relationName\": \"' + self.relation_name + '\"'\r\n",
" if hasattr(self, 'relation_id') and self.relation_id != None:\r\n",
" serialized_str += ', \"relationId\": \"' + self.relation_id + '\"'\r\n",
" if hasattr(self, 'search_key') and self.search_key != None:\r\n",
" serialized_str += ', \"searchKey\": \"' + self.search_key + '\"'\r\n",
" if hasattr(self, 'tactics') and self.tactics != None:\r\n",
" serialized_str += ', \"tactics\": \"' + self.tactics + '\"'\r\n",
" if hasattr(self, 'techniques') and self.techniques != None:\r\n",
" serialized_str += ', \"techniques\": \"' + self.techniques + '\"'\r\n",
" if hasattr(self, 'source_info') and self.source_info != None:\r\n",
" serialized_str += ', \"sourceInfo\": \"' + self.source_info + '\"'\r\n",
" if hasattr(self, 'summary_items') and self.summary_items != None:\r\n",
" serialized_str += ', \"rawContent\": \"[' + DynamicSummary.serializeItems(self.summary_items) + ']\"'\r\n",
"\r\n",
" return serialized_str\r\n",
"\r\n",
" def serializeItems(items):\r\n",
" raw_content = ''\r\n",
" isFirst = True\r\n",
" for item in items:\r\n",
" if isFirst == True:\r\n",
" isFirst = False\r\n",
" else:\r\n",
" raw_content += ','\r\n",
" \r\n",
" raw_content += json.dumps(DynamicSummary.serializeItem(item)).strip('\"')\r\n",
" return raw_content\r\n",
"\r\n",
" def serializeItem(item):\r\n",
" serialized_item_tsr = '{'\r\n",
" serialized_item_tsr += '\"summaryItemId\": \"' + item.summary_item_id.urn[9:] + '\"'\r\n",
"\r\n",
" if hasattr(item, 'relation_name') and item.relation_name != None:\r\n",
" serialized_item_tsr += ', \"relationName\": \"' + item.relation_name + '\"'\r\n",
" if hasattr(item, 'relation_id') and item.relation_id != None:\r\n",
" seriserialized_item_tsralized_str += ', \"relationId\" :\"' + item.relation_id + '\"'\r\n",
" if hasattr(item, 'search_key') and item.search_key != None:\r\n",
" serialized_item_tsr += ', \"searchKey\": \"' + item.search_key + '\"'\r\n",
" if hasattr(item, 'tactics') and item.tactics != None:\r\n",
" serialized_item_tsr += ', \"tactics\": \"' + item.tactics + '\"'\r\n",
" if hasattr(item, 'techniques') and item.techniques != None:\r\n",
" serialized_item_tsr += ', \"techniques\": \"' + item.techniques + '\"'\r\n",
" if hasattr(item, 'event_time_utc') and item.event_time_utc != None:\r\n",
" serialized_item_tsr += ', \"eventTimeUTC\" :\"' + item.event_time_utc.isoformat() + 'Z\"'\r\n",
" if hasattr(item, 'observable_type') and item.observable_type != None:\r\n",
" serialized_item_tsr += ', \"observableType\": \"' + item.observable_type + '\"'\r\n",
" if hasattr(item, 'observable_value') and item.observable_value != None:\r\n",
" serialized_item_tsr += ', \"observableValue\": \"' + item.observable_value + '\"'\r\n",
" if hasattr(item, 'packed_content') and item.packed_content != None:\r\n",
" serialized_item_tsr += ', \"packedContent\": ' + item.packed_content\r\n",
" serialized_item_tsr += '}'\r\n",
" \r\n",
" return serialized_item_tsr\r\n",
"\r\n",
" def construct_summary(self, tenant_id, summary_name, summary_description, items, \\\r\n",
" relation_name=None, relation_id=None, search_key=None, tactics=None, techniques=None, source_info=None, **kwargs):\r\n",
" \"\"\" Building summary level data object \"\"\"\r\n",
" self.summary_name = summary_name\r\n",
" self.azure_tenant_id = tenant_id\r\n",
" self.summary_description = summary_description\r\n",
" if relation_name != None:\r\n",
" self.relation_name = relation_name\r\n",
" if relation_id != None:\r\n",
" self.relation_id = relation_id\r\n",
" if search_key != None:\r\n",
" self.search_key = search_key\r\n",
" if tactics != None:\r\n",
" self.tactics = tactics\r\n",
" if techniques != None:\r\n",
" self.techniques = techniques\r\n",
" if source_info != None:\r\n",
" self.source_info = source_info\r\n",
" if summary_items != None:\r\n",
" self.summary_items = items\r\n",
"\r\n",
" def construct_summary_item(self, summary_item_id, \\\r\n",
" relation_name=None, relation_id=None, search_key=None, tactics=None, techniques=None, event_time_utc=None, observable_type=None, observable_value=None, packed_content=None, **kwargs):\r\n",
" \"\"\" Building summary item level data object \"\"\"\r\n",
" \r\n",
" item = DynamicSummary(self.summary_id)\r\n",
" item.summary_item_id = summary_item_id\r\n",
" if relation_name != None:\r\n",
" item.relation_name = relation_name\r\n",
" if relation_id != None:\r\n",
" item.relation_id = relation_id\r\n",
" if search_key != None:\r\n",
" item.search_key = search_key\r\n",
" if tactics != None:\r\n",
" item.tactics = tactics\r\n",
" if techniques != None:\r\n",
" item.techniques = techniques\r\n",
" if event_time_utc != None:\r\n",
" item.event_time_utc = event_time_utc\r\n",
" if observable_type != None:\r\n",
" item.observable_type = observable_type\r\n",
" if observable_value != None:\r\n",
" item.observable_value = observable_value\r\n",
" if packed_content != None:\r\n",
" item.packed_content = packed_content\r\n",
"\r\n",
" return item\r\n",
" \r\n",
" def construct_arm_rest_url(subscription_id, resource_group, workspace_name, summary_guid):\r\n",
" \"Build URL for Sentinel Dynamic Summaries REST API\"\r\n",
" api_version = \"2023-03-01-preview\"\r\n",
" provider_name = \"Microsoft.OperationalInsights\"\r\n",
" workspace_provider_name = \"Microsoft.SecurityInsights/dynamicSummaries\"\r\n",
" root_url = \"https://management.azure.com\"\r\n",
" arm_rest_url_template = \"{0}/subscriptions/{1}/resourceGroups/{2}/providers/{3}/workspaces/{4}/providers/{5}/{6}?api-version={7}\"\r\n",
" return arm_rest_url_template.format(root_url, subscription_id, resource_group, provider_name, workspace_name, workspace_provider_name, summary_guid, api_version)\r\n",
"\r\n",
"\r\n",
" def call_azure_rest_api_for_creating_dynamic_summary(token, arm_rest_url, summary):\r\n",
" \"Calling Microsoft Sentinel REST API\"\r\n",
" bearer_token = \"Bearer \" + token\r\n",
" headers = {\"Authorization\": bearer_token, \"content-type\":\"application/json\" }\r\n",
" response = requests.put(arm_rest_url, headers=headers, data=summary, verify=True)\r\n",
" return response\r\n",
"\r\n",
" def display_result(response):\r\n",
" \"Display the result set as pandas.DataFrame\"\r\n",
" if response != None:\r\n",
" df = pd.DataFrame(response.json()[\"value\"])\r\n",
" display(df)"
],
"outputs": [],
"execution_count": null,
"metadata": {}
},
{
"cell_type": "markdown",
"source": [
"## 2. Azure Authentication"
],
"metadata": {}
},
{
"cell_type": "code",
"source": [
"tenant_id = ''\r\n",
"subscription_id = ''\r\n",
"akv_name = ''\r\n",
"akv_link_name = ''\r\n",
"workspace_id = ''\r\n",
"client_id_name = ''\r\n",
"client_secret_name = ''\r\n",
"resource_group_name_for_dynamic_summaries = ''\r\n",
"sentinel_workspace_name_for_dynamic_summaries = ''\r\n",
"dynamic_summary_name = ''\r\n",
"dynamic_summary_guid = ''"
],
"outputs": [],
"execution_count": null,
"metadata": {
"tags": [
"parameters"
]
}
},
{
"cell_type": "code",
"source": [
"# You may need to change resource_uri for various cloud environments.\r\n",
"resource_uri = \"https://api.loganalytics.io\"\r\n",
"client_id = mssparkutils.credentials.getSecret(akv_name, client_id_name, akv_link_name)\r\n",
"client_secret = mssparkutils.credentials.getSecret(akv_name, client_secret_name, akv_link_name)\r\n",
"\r\n",
"credential = ClientSecretCredential(\r\n",
" tenant_id=tenant_id, \r\n",
" client_id=client_id, \r\n",
" client_secret=client_secret)\r\n",
"access_token = credential.get_token(resource_uri + \"/.default\")\r\n",
"token = access_token[0]\r\n",
"la_data_client = LogsQueryClient(credential=credential)"
],
"outputs": [],
"execution_count": null,
"metadata": {}
},
{
"cell_type": "markdown",
"source": [
"## 3. Azure Log Analytics Data Queries"
],
"metadata": {}
},
{
"cell_type": "code",
"source": [
"# Get all tables available using Kusto query language. If you need to know more about KQL, please check out the link provided at the introductory section.\r\n",
"tables_result = None\r\n",
"table_list = None\r\n",
"end_time = datetime.now(timezone.utc)\r\n",
"start_time = end_time - timedelta(1)\r\n",
"\r\n",
"all_tables_query = \"union withsource = SentinelTableName * | distinct SentinelTableName | sort by SentinelTableName asc\"\r\n",
"tables_result = la_data_client.query_workspace(\r\n",
" workspace_id=workspace_id,\r\n",
" query=all_tables_query,\r\n",
" timespan=(start_time, end_time))\r\n",
"\r\n",
"if tables_result.status == LogsQueryStatus.SUCCESS:\r\n",
" df_table = pd.DataFrame(data=tables_result.tables[0].rows, columns=tables_result.tables[0].columns)\r\n",
" table_list = list(df_table[\"SentinelTableName\"])\r\n",
" column_name = \"*\"\r\n",
" df_total = pd.DataFrame()\r\n",
" df_list = []\r\n",
" \r\n",
" for table_name in table_list:\r\n",
" print('Table name: ' + table_name)\r\n",
" column_name = \"*\"\r\n",
" kql_where_clause = get_credscan_kql_where_clause(column_name)\r\n",
" table_query = \"{0} {1}\".format(table_name, kql_where_clause)\r\n",
"\r\n",
" # Run query\r\n",
" try:\r\n",
" try_result = la_data_client.query_workspace(\r\n",
" workspace_id=workspace_id,\r\n",
" query=table_query,\r\n",
" timespan=(start_time, end_time))\r\n",
"\r\n",
" df_try = pd.DataFrame(data=try_result.tables[0].rows, columns=try_result.tables[0].columns)\r\n",
" if not df_try.empty:\r\n",
" all_columns_query = \"let ColumnList = \" + table_name + \" | getschema | project ColumnName; ColumnList \"\r\n",
" columns_result = la_data_client.query_workspace(\r\n",
" workspace_id=workspace_id,\r\n",
" query=all_columns_query,\r\n",
" timespan=(start_time, end_time))\r\n",
" df_column = pd.DataFrame(data=columns_result.tables[0].rows, columns=columns_result.tables[0].columns)\r\n",
" column_list = list(df_column[\"ColumnName\"])\r\n",
"\r\n",
" for column_name in column_list:\r\n",
" # Now checking each column\r\n",
" if filter_column(column_name):\r\n",
" continue\r\n",
" \r\n",
" kql_where_clause = get_credscan_kql_where_clause(column_name)\r\n",
" col_query = \"{0} {1} | extend ColumnName='{2}', RegexResult={2} | project ColumnName, RegexResult\".format(table_name, kql_where_clause, column_name)\r\n",
"\r\n",
" # Run query\r\n",
" try:\r\n",
" single_column_result = la_data_client.query_workspace(\r\n",
" workspace_id=workspace_id,\r\n",
" query=col_query,\r\n",
" timespan=(start_time, end_time))\r\n",
"\r\n",
" # process result\r\n",
" df_single_col = pd.DataFrame(data=single_column_result.tables[0].rows, columns=single_column_result.tables[0].columns)\r\n",
" if not df_single_col.empty:\r\n",
" print('Column name: ' + column_name)\r\n",
" df_total = df_total.append(df_single_col)\r\n",
" except Exception as ex:\r\n",
" print(\"=============Exception========\")\r\n",
" print(ex)\r\n",
" print(\"==============================\")\r\n",
" else:\r\n",
" print(\"Not leak found.\")\r\n",
" except HttpResponseError as error:\r\n",
" print(\"==============================\")\r\n",
" print(\" This table got http error:\")\r\n",
" print(\" message:\" + error.message)\r\n",
" print(\" reason:\" + error.reason)\r\n",
" print(\"==============================\")\r\n",
" if not df_total.empty:\r\n",
" print('results:')\r\n",
" pd.options.display.max_columns = None\r\n",
" display(df_total)\r\n",
" else:\r\n",
" print('--- No leak ---')\r\n"
],
"outputs": [],
"execution_count": null,
"metadata": {}
},
{
"cell_type": "markdown",
"source": [
"## 4. Save result to Microsoft Sentinel Dynamic Summaries"
],
"metadata": {}
},
{
"cell_type": "code",
"source": [
"if not df_total.empty and dynamic_summary_name != None and dynamic_summary_name != '':\r\n",
" summary = DynamicSummary(dynamic_summary_guid)\r\n",
" summary_description = \"This summary is generated from notebook - AutomationGallery-CredentialScanOnAzureLogAnalytics.\"\r\n",
"\r\n",
" summary_items = []\r\n",
" for index, row in df_total.iterrows():\r\n",
" packed_content = df_total.iloc[index].to_json()\r\n",
" summary_items.append(summary.construct_summary_item(DynamicSummary.get_new_guid(), None, None, None, None, None, datetime.utcnow(), None, None, packed_content))\r\n",
"\r\n",
" summary.construct_summary(tenant_id, dynamic_summary_name, summary_description, summary_items)\r\n",
" summary_json = \"{ \\\"properties\\\": {\" + summary.serialize() + \"}}\"\r\n",
"\r\n",
" print(summary_json)"
],
"outputs": [],
"execution_count": null,
"metadata": {}
},
{
"cell_type": "code",
"source": [
"if not df_total.empty and dynamic_summary_name != None and dynamic_summary_name != '':\r\n",
" dyn_sum_api_url = DynamicSummary.construct_arm_rest_url(subscription_id, resource_group_name_for_dynamic_summaries, sentinel_workspace_name_for_dynamic_summaries, dynamic_summary_guid)\r\n",
" response = DynamicSummary.call_azure_rest_api_for_creating_dynamic_summary(token, dyn_sum_api_url, summary_json)\r\n",
"\r\n",
" print(response.status_code)"
],
"outputs": [],
"execution_count": null,
"metadata": {}
}
],
"metadata": {
"language_info": {
"name": "python"
},
"kernelspec": {
"name": "synapse_pyspark",
"language": "Python",
"display_name": "Synapse PySpark"
},
"kernel_info": {
"name": "synapse_pyspark"
},
"description": null,
"save_output": true,
"synapse_widget": {
"version": "0.1",
"state": {}
}
},
"nbformat": 4,
"nbformat_minor": 2
}