Jupyter-Notebooks/MDEASM_Python_Notebook.ipynb (1,447 lines of code) (raw):
{
"cells": [
{
"attachments": {},
"cell_type": "markdown",
"metadata": {},
"source": [
"## Microsoft Defender for External Attack Surface Management (MDEASM) Python Jupyter Notebook\n",
"***\n",
"This is a sample notebook for querying your MDEASM inventory. A few prerequisites must be completed before you can use it properly:\n",
"1. You must complete an initial __[Discovery](https://learn.microsoft.com/en-us/azure/external-attack-surface-management/using-and-managing-discovery)__ in your MDEASM workspace to which you have, at a minimum, _read_ permissions\n",
"2. You will also need your _Subscription Id_, _Tenant Id_, _Resource Group Name_, _Workspace Name_, & the Azure _Region_ your workspace was initialized in\n",
"3. You must have a registered __[Service Principal](https://learn.microsoft.com/en-us/rest/api/defenderforeasm/authentication#client-service-principal)__ application that is properly configured to interact with our REST API\n",
"4. To create a bearer token with your _Service Principal_ you will need the app _Client Id_, _Client Secret_, & _Tenant Id_ \n",
"5. A minimum of a 3.X Python installation with an installed Jupyter module OR and IDE such as VSCode with a Jupyter Notebook extension installed\n",
"***\n",
"\n",
"> Note for simplicity's sake, we are putting the auth variables here in the notebook for demo purposes only. In reality, this should __never__ take place in a document/script you share with others, or publish online. Storage of clientIds and clientSecrets should always leverage a more secure solution such as Key Vaults or encrypted storage of some kind. If you share this notebook with others after usage, clear outputs of all cells and restart your kernel after you have deleted these sensetive variables from your code."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"import requests, time, json, re\n",
"\n",
"try:\n",
" import mdeasm_creds\n",
" clientId = mdeasm_creds.clientId\n",
" clientSecret = mdeasm_creds.clientSecret\n",
" tenantId = mdeasm_creds.tenantId\n",
" subscriptionId = mdeasm_creds.subscriptionId\n",
" resourceGroupName = mdeasm_creds.resourceGroupName\n",
" resourceName = mdeasm_creds.resourceName\n",
" region = mdeasm_creds.region\n",
"except ImportError:\n",
" print(\"Please create a file called mdeasm_creds.py and add your credentials to it. Or enter in your credentials below.\")\n",
"\n",
"# Important variables to set\n",
"bearerToken = None # This will be set by the azure_auth() function otherwise enter the token as a string in side quotes ''\n",
"bearerTokenExpires = 0 # This will be set by the azure_auth() function otherwise enter the epoch time in seconds as an integer\n",
"## clientId = '' # ex. 'a8c5a9e0-0000-0000-0000-000000000000' => Application (client) ID - string [required]\n",
"## clientSecret = '' # ex. 'ABCDE~123456789abcdefg_9876543210~ZyXwVu' => Application (client) secret - string [required]\n",
"## tenantId = '' # ex. 'a8c5a9e0-0000-0000-0000-000000000000' => Directory (tenant) ID - string [required]\n",
"## subscriptionId = '' # ex. 'a8c5a9e0-0000-0000-0000-000000000000' => Subscription ID - string [required]\n",
"## resourceGroupName = '' # ex. 'myMDEASMAPItest-rg' => Resource group name - string [required]\n",
"## resourceName = '' # ex. 'myMDEASMworkspace' => Resource/Workspace name - string [required]\n",
"## region = '' # ex. 'eastus' => Region - string [required]\n",
"# Leave these variables unchanged until you need to change them\n",
"resource = 'https://easm.defender.microsoft.com/' # => API Resource - string [supplied]\n",
"apiVersion = '2022-11-01-preview' # => API Version - string [supplied]\n",
"maxpagesize = 25 # => Max page size - int [required]\n",
"mark = '*' # => Mark - string [optional]\n",
"postBody = '' # => Post body - JSON formatted string [optional]\n",
"# Utilitiy variables\n",
"uuidRegex = '[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}'\n",
"encodingRegex = \"[\\s\\!\\@\\#\\$\\^\\*\\(\\)\\_\\=\\-\\[\\]\\{\\}\\;\\:\\'\\\"\\,\\.\\<\\>\\/\\?\\|\\\\\\]\"\n",
"whoisFilter = ['privacy', 'redacted', 'godaddy', 'private', 'protected', 'proxy', 'masking', 'privateregistration', 'safenames.net', 'domaincontrol.com', 'enom.com', 'verisign.com', 'domainhasexpired.com', 'ultradns.net', 'web.com', 'cscglobal.com', 'namecheap', 'hichina', 'amazon', 'google', 'ultrahost', 'aws', 'nsone.net', 'hugedomains', 'gdpr']\n",
"results = set({})\n",
"nextUrl = None\n",
"planeType = 'data' # => Plane type - string [required] - 'data' or 'control'\n",
"currentPlane = 'data'\n",
"\n",
"# Function to take a filter and check if it is URL encoded, if not, then encode it\n",
"def check_url_encoding(filter):\n",
" if filter != '' and re.search(encodingRegex, filter):\n",
" query = requests.utils.quote(filter, safe='+')\n",
" else:\n",
" query = filter\n",
" return query\n",
"\n",
"# Helper Function to authenticate and obtain a bearer token to be run before every request\n",
"def azure_auth():\n",
" # Check if required variables are set\n",
" global bearerToken, bearerTokenExpires, planeType, currentPlane\n",
" planeType = planeType.lower()\n",
" if planeType == 'data':\n",
" resource = 'scope=https%3A%2F%2Feasm.defender.microsoft.com%2F.default'\n",
" elif planeType == 'control':\n",
" resource = 'scope=https%3A%2F%2Fmanagement.azure.com%2F.default'\n",
" else:\n",
" print(\"Invalid plane type must be either 'data' or 'control'\")\n",
" return None\n",
"\n",
" if clientId == None or clientSecret == None or tenantId == None:\n",
" print(\"Please provide a client ID, client secret, and tenant ID or if you acquired your bearer token via the CLI please enter it in the bearerToken and bearerTokenExpires parameters above.\")\n",
" return None\n",
" # Check if bearer token is set and not expired, else return a new token\n",
" if bearerToken == None or bearerToken == '' and bearerTokenExpires == 0 or time.time() > bearerTokenExpires or currentPlane != planeType or currentPlane == None:\n",
" auth_url = f\"https://login.microsoftonline.com/{tenantId}/oauth2/v2.0/token\"\n",
" auth_payload=f'grant_type=client_credentials&client_id={clientId}&client_secret={clientSecret}&{resource}'\n",
" auth_headers = {'Content-Type': 'application/x-www-form-urlencoded'}\n",
" response = requests.request(\"POST\", auth_url, headers=auth_headers, data=auth_payload)\n",
" if response.status_code != 200:\n",
" print(\"Error acquiring bearer token\")\n",
" print(response.text)\n",
" return None\n",
" else:\n",
" bearerToken = json.loads(response.text)['access_token']\n",
" bearerTokenExpires = time.time() + json.loads(response.text)['expires_in']\n",
" currentPlane = planeType\n",
" return bearerToken, bearerTokenExpires\n",
"\n",
"# Make an initial request for a bearer token\n",
"azure_auth()\n",
"if bearerToken != None and bearerTokenExpires == 0 or time.time() > bearerTokenExpires:\n",
" print(\"Error acquiring bearer token\")\n",
"else:\n",
" print(\"Bearer token acquired successfully\")"
]
},
{
"attachments": {},
"cell_type": "markdown",
"metadata": {},
"source": [
"### Assets - List"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"## Assets List returns##\n",
"def get_asset_list(query):\n",
" ''' Assets List endpoint - takes a URL encoded query string '''\n",
" check_url_encoding(query)\n",
" global planeType\n",
" planeType = 'data'\n",
" azure_auth()\n",
" nextUrl = None\n",
" url = f\"https://{region}.easm.defender.microsoft.com/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/workspaces/{resourceName}/assets?api-version={apiVersion}&maxpagesize={maxpagesize}&filter={query}&mark={mark}\"\n",
"\n",
" payload={}\n",
" headers = {\n",
" 'User-Agent': 'MDEASM Python Notebook',\n",
" 'Authorization': f'Bearer {bearerToken}'\n",
" }\n",
" \n",
"\n",
" response = requests.request(\"GET\", url, headers=headers, data=payload)\n",
" if response.status_code != 200:\n",
" print(\"Error getting asset list\")\n",
" print(response.text)\n",
" return None\n",
" if len(response.text) == 0:\n",
" print(\"No assets found matching your query\")\n",
" return None\n",
" else:\n",
" responsejson = json.loads(response.text)\n",
" responseresults = responsejson['content']\n",
" for i in responseresults:\n",
" results.add(i['name'])\n",
" if 'nextLink' in responsejson and nextUrl != responsejson['nextLink']:\n",
" nextUrl = responsejson['nextLink']\n",
" else:\n",
" nextUrl = None\n",
" #print(responsejson)\n",
" \n",
" while nextUrl:\n",
" azure_auth()\n",
" response = requests.request(\"GET\", nextUrl, headers=headers, data=payload)\n",
" responsejson = json.loads(response.text)\n",
" responseresults = responsejson['content']\n",
" for i in responseresults:\n",
" results.add(i['name'])\n",
" if 'nextLink' not in responsejson:\n",
" nextUrl = None\n",
" else:\n",
" time.sleep(1)\n",
" nextUrl = responsejson['nextLink']\n",
" \n",
" if len(results) == 0:\n",
" print(\"No assets found matching your query\")\n",
" else:\n",
" print(\"Number of results: \" + str(len(results)))\n",
" print(results)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"query = 'state = confirmed AND kind in (\"host\",\"ipAddress\") AND webComponentType = Server AND webComponentName ^=in (\"Apache\", \"Microsoft IIS\") AND cvss3BaseScore >= 7'\n",
"get_asset_list(query)"
]
},
{
"attachments": {},
"cell_type": "markdown",
"metadata": {},
"source": [
"### Asset - Get"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# Retrieves a single asset by assetId\n",
"def get_asset(assetId):\n",
" ''' Asset Get endpoint - takes a UUID formatted AssetId '''\n",
" if assetId!= None and re.search(uuidRegex, assetId) != None:\n",
" global planeType\n",
" planeType = 'data'\n",
" azure_auth()\n",
" url = f\"https://{region}.easm.defender.microsoft.com/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/workspaces/{resourceName}/assets/{assetId}?api-version={apiVersion}\"\n",
" payload={}\n",
" headers = {\n",
" 'User-Agent': 'MDEASM Python Notebook',\n",
" 'Authorization': f'Bearer {bearerToken}'\n",
" }\n",
" response = requests.request(\"GET\", url, headers=headers, data=payload)\n",
" if response.status_code != 200:\n",
" print(\"Error getting asset details\")\n",
" print(response.text)\n",
" return None\n",
" if len(response.text) == 0:\n",
" print(\"No asset found matching your assetId\")\n",
" return None\n",
" else:\n",
" responsejson = json.loads(response.text)\n",
" \n",
" # simplified output of asset names, adjust to your needs\n",
" print(responsejson)\n",
" else:\n",
" print(\"Please provide a valid assetId\")"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"get_asset('a8c5a9e0-0000-0000-0000-000000000000')"
]
},
{
"attachments": {},
"cell_type": "markdown",
"metadata": {},
"source": [
"### Assets - Update"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# Update labels, state, or externalId for a set of assets returned by a query\n",
"def update_assets(query, postBody):\n",
" ''' Function to access the Assets Update endpoint - requires a URL encoded query string \n",
" and a post body containing the asset properties to update \n",
" example post body: {\"labels\": {\"Label1\": True}, \"state\": \"confirmed\", \"transfers\":[\"sslCert\"], \"externalId\": \"123456\"}\n",
" accepted transfers values: contact, domain, ipBlock, host, page, sslCert, as, ipAddress'''\n",
" check_url_encoding(query)\n",
" global planeType\n",
" planeType = 'data'\n",
" azure_auth()\n",
" url = f\"https://{region}.easm.defender.microsoft.com/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/workspaces/{resourceName}/assets?filter={query}&api-version={apiVersion}\"\n",
" payload = json.loads(json.dumps(postBody))\n",
" headers = {\n",
" 'User-Agent': 'MDEASM Python Notebook',\n",
" 'Authorization': f'Bearer {bearerToken}',\n",
" 'Content-Type': 'application/json',\n",
" 'Accept': 'application/json'\n",
" }\n",
" response = requests.request(\"POST\", url, headers=headers, data=payload)\n",
" if response.status_code != 200:\n",
" print(\"Error updating assets related to supplied query\")\n",
" print(response.text)\n",
" return None\n",
" else:\n",
" responsejson = json.loads(response.text)\n",
" print(responsejson)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"update_query = 'state = confirmed AND domain in (\"contoso.com\")'\n",
"update_body = '{\"state\":\"dismissed\",\"transfers\":[\"sslCert\"]}'\n",
"update_assets(update_query, update_body)"
]
},
{
"attachments": {},
"cell_type": "markdown",
"metadata": {},
"source": [
"### Tasks - Get"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# Retrieves a single task by taskId\n",
"def get_task(taskId):\n",
" ''' Task Get endpoint - requires a UUID formatted TaskId '''\n",
" if taskId!= None and re.search(uuidRegex, taskId) != None:\n",
" global planeType\n",
" planeType = 'data'\n",
" azure_auth()\n",
" url = f\"https://{region}.easm.defender.microsoft.com/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/workspaces/{resourceName}/tasks/{taskId}?api-version={apiVersion}\"\n",
" headers = {\n",
" 'User-Agent': 'MDEASM Python Notebook',\n",
" 'Authorization': f'Bearer {bearerToken}'\n",
" }\n",
" response = requests.request(\"GET\", url, headers=headers)\n",
" if response.status_code != 200:\n",
" print(\"Error getting task details\")\n",
" print(response.text)\n",
" return None\n",
" if len(response.text) == 0:\n",
" print(\"No task found matching your taskId\")\n",
" return None\n",
" else:\n",
" responsejson = json.loads(response.text)\n",
" \n",
" # simplified output of asset names, adjust to your needs\n",
" print(responsejson)\n",
" else:\n",
" print(\"Please provide a valid taskId\")"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"get_task('a8c5a9e0-0000-0000-0000-000000000000')"
]
},
{
"attachments": {},
"cell_type": "markdown",
"metadata": {},
"source": [
"### Tasks - List"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# Retrieves a list of tasks by workspace name\n",
"def get_task_list():\n",
" ''' Tasks List endpoint - requires a valid resource name (the name of your workspace) '''\n",
" global planeType\n",
" planeType = 'data'\n",
" azure_auth()\n",
" url = f\"https://{region}.easm.defender.microsoft.com/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/workspaces/{resourceName}/tasks?api-version={apiVersion}\"\n",
" headers = {\n",
" 'User-Agent': 'MDEASM Python Notebook',\n",
" 'Authorization': f'Bearer {bearerToken}'\n",
" }\n",
" response = requests.request(\"GET\", url, headers=headers)\n",
" if response.status_code != 200:\n",
" print(\"Error getting task list, check the resource name\")\n",
" print(response.text)\n",
" return None\n",
" if len(response.text) == 0:\n",
" print(\"No tasks found matching your resource name\")\n",
" return None\n",
" else:\n",
" responsejson = json.loads(response.text)\n",
" \n",
" # simplified output of workspace tasks, adjust to your needs\n",
" print(responsejson)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"get_task_list()"
]
},
{
"attachments": {},
"cell_type": "markdown",
"metadata": {},
"source": [
"### Tasks - Cancel"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# Cancels a running task by taskId\n",
"def cancel_task(taskId):\n",
" ''' Task Cancel endpoint - requires a UUID formatted TaskId '''\n",
" if taskId!= None:\n",
" global planeType\n",
" planeType = 'data'\n",
" azure_auth()\n",
" url = f\"https://{region}.easm.defender.microsoft.com/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/workspaces/{resourceName}/tasks/{taskId}:cancel?api-version={apiVersion}\"\n",
" headers = {\n",
" 'User-Agent': 'MDEASM Python Notebook',\n",
" 'Authorization': f'Bearer {bearerToken}'\n",
" }\n",
" response = requests.request(\"POST\", url, headers=headers)\n",
" if response.status_code != 200:\n",
" print(\"Error getting cancellation request details\")\n",
" print(response.text)\n",
" return None\n",
" if len(response.text) == 0:\n",
" print(\"No task found matching your taskId\")\n",
" return None\n",
" else:\n",
" responsejson = json.loads(response.text)\n",
" \n",
" # simplified output of asset names, adjust to your needs\n",
" print(responsejson)\n",
" else:\n",
" print(\"Please provide a valid taskId\")"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"cancel_task('a8c5a9e0-0000-0000-0000-000000000000')"
]
},
{
"attachments": {},
"cell_type": "markdown",
"metadata": {},
"source": [
"### Helper functions:\n",
"### Get Common Assets"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# Function to gather all the common assets/attributes for a given query\n",
"# Change includeNS=True if you want to include nameservers\n",
"def get_common_assets(query, includeNS=False):\n",
" check_url_encoding(query)\n",
" global planeType, nextUrl\n",
" planeType = 'data'\n",
" azure_auth()\n",
" nextUrl = None\n",
" commonAssetsList = []\n",
" \n",
" def commonFunc(assetName, assetType):\n",
" if any(assetName in x.values() for x in commonAssetsList):\n",
" for i in commonAssetsList:\n",
" if assetName == str(i['name']):\n",
" i['count'] += 1\n",
" else:\n",
" if len(assetName) > 0:\n",
" if next((f for f in whoisFilter if f in assetName.lower()), None):\n",
" pass\n",
" else:\n",
" commonAssetsList.append({\"name\": str(assetName), \"assetType\": assetType, \"count\": 1})\n",
"\n",
" url = f\"https://{region}.easm.defender.microsoft.com/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/workspaces/{resourceName}/assets?api-version={apiVersion}&maxpagesize={maxpagesize}&filter={query}&mark={mark}\"\n",
"\n",
" payload={}\n",
" headers = {\n",
" 'User-Agent': 'MDEASM Python Notebook',\n",
" 'Authorization': f'Bearer {bearerToken}'\n",
" }\n",
"\n",
" response = requests.request(\"GET\", url, headers=headers, data=payload)\n",
" if response.status_code != 200:\n",
" print(\"Error getting asset list\")\n",
" print(response.text)\n",
" return None\n",
" if len(response.text) == 0:\n",
" print(\"No assets found matching your query\")\n",
" return None\n",
" else:\n",
" responsejson = json.loads(response.text)\n",
" responseresults = responsejson['content'] \n",
" for i in responseresults:\n",
" if i['kind'] == 'domain':\n",
" if i['asset']['domain'] != None or i['asset']['domain'] != '':\n",
" commonFunc(str(i['asset']['domain']), 'domain')\n",
" if 'adminContacts' in i['asset'].keys():\n",
" for x in i['asset']['adminContacts']:\n",
" if 'recent' in x.keys():\n",
" if x['recent']:\n",
" if x['recent'] == True and x['value'] != None or x['value'] != '':\n",
" commonFunc(str(x['value']), 'whoisContact')\n",
" if 'adminNames' in i['asset'].keys():\n",
" for x in i['asset']['adminNames']:\n",
" if 'recent' in x.keys():\n",
" if x['recent'] == True and x['value'] != None or x['value'] != '':\n",
" commonFunc(str(x['value']), 'whoisName')\n",
" if 'adminPhones' in i['asset'].keys():\n",
" for x in i['asset']['adminPhones']:\n",
" if 'recent' in x.keys():\n",
" if x['recent'] == True and x['value'] != None or x['value'] != '':\n",
" commonFunc(str(x['value']), 'whoisPhone')\n",
" if 'adminOrgs' in i['asset'].keys():\n",
" for x in i['asset']['adminOrgs']:\n",
" if 'recent' in x.keys():\n",
" if x['recent'] == True and x['value'] != None or x['value'] != '':\n",
" commonFunc(str(x['value']), 'whoisOrg')\n",
" if 'registrantContacts' in i['asset'].keys():\n",
" for x in i['asset']['registrantContacts']:\n",
" if 'recent' in x.keys():\n",
" if x['recent'] == True and x['value'] != None or x['value'] != '':\n",
" commonFunc(str(x['value']), 'whoisContact')\n",
" if 'registrantNames' in i['asset'].keys():\n",
" for x in i['asset']['registrantNames']:\n",
" if 'recent' in x.keys():\n",
" if x['recent'] == True and x['value'] != None or x['value'] != '':\n",
" commonFunc(str(x['value']), 'whoisName')\n",
" if 'registrantPhones' in i['asset'].keys():\n",
" for x in i['asset']['registrantPhones']:\n",
" if 'recent' in x.keys():\n",
" if x['recent'] == True and x['value'] != None or x['value'] != '':\n",
" commonFunc(str(x['value']), 'whoisPhone')\n",
" if 'registrantOrgs' in i['asset'].keys():\n",
" for x in i['asset']['registrantOrgs']:\n",
" if 'recent' in x.keys():\n",
" if x['recent'] == True and x['value'] != None or x['value'] != '':\n",
" commonFunc(str(x['value']), 'whoisOrg')\n",
" if includeNS == True and 'nameServers' in i['asset'].keys() and len(i['asset']['nameServers']) > 0:\n",
" for x in i['asset']['nameServers']:\n",
" if 'value' in x.keys() and 'recent' in x.keys():\n",
" if x['recent'] == True and x['value'] != None or x['value'] != '':\n",
" commonFunc(x['value'], 'nameServer')\n",
" \n",
" elif i['kind'] == 'host' or i['kind'] == 'page':\n",
" if i['asset']['domain'] != None or i['asset']['domain'] != '':\n",
" commonFunc(str(i['asset']['domain']), 'domain')\n",
" if 'domainAsset' in i['asset'].keys():\n",
" if 'adminContacts' in i['asset']['domainAsset'].keys():\n",
" for x in i['asset']['domainAsset']['adminContacts']:\n",
" if 'recent' in x.keys():\n",
" if x['recent'] == True and x['value'] != None or x['value'] != '':\n",
" commonFunc(str(x['value']), 'whoisContact')\n",
"\n",
" if 'adminNames' in i['asset']['domainAsset'].keys():\n",
" for x in i['asset']['domainAsset']['adminNames']:\n",
" if 'recent' in x.keys():\n",
" if x['recent'] == True and x['value'] != None or x['value'] != '':\n",
" commonFunc(str(x['value']), 'whoisName')\n",
"\n",
" if 'adminPhones' in i['asset']['domainAsset'].keys():\n",
" for x in i['asset']['domainAsset']['adminPhones']:\n",
" if 'recent' in x.keys():\n",
" if x['recent'] == True and x['value'] != None or x['value'] != '':\n",
" commonFunc(str(x['value']), 'whoisPhone')\n",
"\n",
" if 'adminOrgs' in i['asset']['domainAsset'].keys():\n",
" for x in i['asset']['domainAsset']['adminOrgs']:\n",
" if 'recent' in x.keys():\n",
" if x['recent'] == True and x['value'] != None or x['value'] != '':\n",
" commonFunc(str(x['value']), 'whoisOrg')\n",
"\n",
" if 'registrantContacts' in i['asset']['domainAsset'].keys():\n",
" for x in i['asset']['domainAsset']['registrantContacts']:\n",
" if 'recent' in x.keys():\n",
" if x['recent'] == True and x['value'] != None or x['value'] != '':\n",
" commonFunc(str(x['value']), 'whoisContact')\n",
"\n",
" if 'registrantNames' in i['asset']['domainAsset'].keys():\n",
" for x in i['asset']['domainAsset']['registrantNames']:\n",
" if 'recent' in x.keys():\n",
" if x['recent'] == True and x['value'] != None or x['value'] != '':\n",
" commonFunc(str(x['value']), 'whoisName')\n",
"\n",
" if 'registrantPhones' in i['asset']['domainAsset'].keys():\n",
" for x in i['asset']['domainAsset']['registrantPhones']:\n",
" if 'recent' in x.keys():\n",
" if x['recent'] == True and x['value'] != None or x['value'] != '':\n",
" commonFunc(str(x['value']), 'whoisPhone')\n",
"\n",
" if 'registrantOrgs' in i['asset']['domainAsset'].keys():\n",
" for x in i['asset']['domainAsset']['registrantOrgs']:\n",
" if 'recent' in x.keys():\n",
" if x['recent'] == True and x['value'] != None or x['value'] != '':\n",
" commonFunc(str(x['value']), 'whoisOrg')\n",
" if includeNS == True and 'domainAsset' in i['asset'].keys():\n",
" if 'nameServers' in i['asset']['domainAsset'].keys():\n",
" for x in i['asset']['domainAsset']['nameServers']:\n",
" if 'recent' in x.keys():\n",
" if x['recent'] == True and x['value'] != None or x['value'] != '':\n",
" commonFunc(x['value'], 'nameServer') \n",
" \n",
" elif i['kind'] == 'ipAddress':\n",
" if 'ipBlocks' in ['asset'].keys():\n",
" for x in i['asset']['ipBlocks']:\n",
" if 'recent' in x.keys():\n",
" if x['recent'] == True and x['value'] != None or x['value'] != '':\n",
" commonFunc(str(x['ipBlock']), 'ipBlock')\n",
" if 'asns' in ['asset'].keys():\n",
" for x in i['asset']['asns']:\n",
" if 'recent' in x.keys():\n",
" if x['recent'] == True and x['value'] != None or x['value'] != '':\n",
" commonFunc(str(x['value']))\n",
" \n",
" elif i['kind'] == 'ipBlock':\n",
" if 'netNames' in i['asset'].keys():\n",
" for x in i['asset']['netNames']:\n",
" if 'recent' in x.keys():\n",
" if x['recent'] == True and x['value'] != None or x['value'] != '':\n",
" commonFunc(str(x['value']), 'netName')\n",
" if 'adminContacts' in i['asset'].keys():\n",
" for x in i['asset']['adminContacts']:\n",
" if 'recent' in x.keys():\n",
" if x['recent']:\n",
" if x['recent'] == True and x['value'] != None or x['value'] != '':\n",
" commonFunc(str(x['value']), 'whoisContact')\n",
" if 'adminNames' in i['asset'].keys():\n",
" for x in i['asset']['adminNames']:\n",
" if 'recent' in x.keys():\n",
" if x['recent'] == True and x['value'] != None or x['value'] != '':\n",
" commonFunc(str(x['value']), 'whoisName')\n",
" if 'adminPhones' in i['asset'].keys():\n",
" for x in i['asset']['adminPhones']:\n",
" if 'recent' in x.keys():\n",
" if x['recent'] == True and x['value'] != None or x['value'] != '':\n",
" commonFunc(str(x['value']), 'whoisPhone')\n",
" if 'adminOrgs' in i['asset'].keys():\n",
" for x in i['asset']['adminOrgs']:\n",
" if 'recent' in x.keys():\n",
" if x['recent'] == True and x['value'] != None or x['value'] != '':\n",
" commonFunc(str(x['value']), 'whoisOrg')\n",
" if 'technicalContacts' in i['asset'].keys():\n",
" for x in i['asset']['technicalContacts']:\n",
" if 'recent' in x.keys():\n",
" if x['recent'] == True and x['value'] != None or x['value'] != '':\n",
" commonFunc(str(x['value']), 'whoisContact')\n",
" if 'technicalNames' in i['asset'].keys():\n",
" for x in i['asset']['technicalNames']:\n",
" if 'recent' in x.keys():\n",
" if x['recent'] == True and x['value'] != None or x['value'] != '':\n",
" commonFunc(str(x['value']), 'whoisName')\n",
" if 'technicalPhones' in i['asset'].keys():\n",
" for x in i['asset']['technicalPhones']:\n",
" if 'recent' in x.keys():\n",
" if x['recent'] == True and x['value'] != None or x['value'] != '':\n",
" commonFunc(str(x['value']), 'whoisPhone')\n",
" if 'technicalOrgs' in i['asset'].keys():\n",
" for x in i['asset']['technicalOrgs']:\n",
" if 'recent' in x.keys():\n",
" if x['recent'] == True and x['value'] != None or x['value'] != '':\n",
" commonFunc(str(x['value']), 'whoisOrg')\n",
"\n",
" if 'nextLink' in responsejson and nextUrl != responsejson['nextLink']:\n",
" nextUrl = responsejson['nextLink']\n",
" else:\n",
" nextUrl = None\n",
" \n",
" while nextUrl:\n",
" azure_auth()\n",
" time.sleep(1)\n",
" response = requests.request(\"GET\", nextUrl, headers=headers, data=payload)\n",
" responsejson = json.loads(response.text)\n",
" responseresults = responsejson['content']\n",
" for i in responseresults:\n",
" if i['kind'] == 'domain':\n",
" if i['asset']['domain'] != None or i['asset']['domain'] != '':\n",
" commonFunc(str(i['asset']['domain']), 'domain')\n",
" if 'adminContacts' in i['asset'].keys():\n",
" for x in i['asset']['adminContacts']:\n",
" if 'recent' in x.keys():\n",
" if x['recent']:\n",
" if x['recent'] == True and x['value'] != None or x['value'] != '':\n",
" commonFunc(str(x['value']), 'whoisContact')\n",
" if 'adminNames' in i['asset'].keys():\n",
" for x in i['asset']['adminNames']:\n",
" if 'recent' in x.keys():\n",
" if x['recent'] == True and x['value'] != None or x['value'] != '':\n",
" commonFunc(str(x['value']), 'whoisName')\n",
" if 'adminPhones' in i['asset'].keys():\n",
" for x in i['asset']['adminPhones']:\n",
" if 'recent' in x.keys():\n",
" if x['recent'] == True and x['value'] != None or x['value'] != '':\n",
" commonFunc(str(x['value']), 'whoisPhone')\n",
" if 'adminOrgs' in i['asset'].keys():\n",
" for x in i['asset']['adminOrgs']:\n",
" if 'recent' in x.keys():\n",
" if x['recent'] == True and x['value'] != None or x['value'] != '':\n",
" commonFunc(str(x['value']), 'whoisOrg')\n",
" if 'registrantContacts' in i['asset'].keys():\n",
" for x in i['asset']['registrantContacts']:\n",
" if 'recent' in x.keys():\n",
" if x['recent'] == True and x['value'] != None or x['value'] != '':\n",
" commonFunc(str(x['value']), 'whoisContact')\n",
" if 'registrantNames' in i['asset'].keys():\n",
" for x in i['asset']['registrantNames']:\n",
" if 'recent' in x.keys():\n",
" if x['recent'] == True and x['value'] != None or x['value'] != '':\n",
" commonFunc(str(x['value']), 'whoisName')\n",
" if 'registrantPhones' in i['asset'].keys():\n",
" for x in i['asset']['registrantPhones']:\n",
" if 'recent' in x.keys():\n",
" if x['recent'] == True and x['value'] != None or x['value'] != '':\n",
" commonFunc(str(x['value']), 'whoisPhone')\n",
" if 'registrantOrgs' in i['asset'].keys():\n",
" for x in i['asset']['registrantOrgs']:\n",
" if 'recent' in x.keys():\n",
" if x['recent'] == True and x['value'] != None or x['value'] != '':\n",
" commonFunc(str(x['value']), 'whoisOrg')\n",
" if includeNS == True and 'nameServers' in i['asset'].keys() and len(i['asset']['nameServers']) > 0:\n",
" for x in i['asset']['nameServers']:\n",
" if 'value' in x.keys() and 'recent' in x.keys():\n",
" if x['recent'] == True and x['value'] != None or x['value'] != '':\n",
" commonFunc(x['value'], 'nameServer')\n",
" \n",
" elif i['kind'] == 'host' or i['kind'] == 'page':\n",
" if i['asset']['domain'] != None or i['asset']['domain'] != '':\n",
" commonFunc(str(i['asset']['domain']), 'domain')\n",
" if 'domainAsset' in i['asset'].keys():\n",
" if 'adminContacts' in i['asset']['domainAsset'].keys():\n",
" for x in i['asset']['domainAsset']['adminContacts']:\n",
" if 'recent' in x.keys():\n",
" if x['recent'] == True and x['value'] != None or x['value'] != '':\n",
" commonFunc(str(x['value']), 'whoisContact')\n",
"\n",
" if 'adminNames' in i['asset']['domainAsset'].keys():\n",
" for x in i['asset']['domainAsset']['adminNames']:\n",
" if 'recent' in x.keys():\n",
" if x['recent'] == True and x['value'] != None or x['value'] != '':\n",
" commonFunc(str(x['value']), 'whoisName')\n",
"\n",
" if 'adminPhones' in i['asset']['domainAsset'].keys():\n",
" for x in i['asset']['domainAsset']['adminPhones']:\n",
" if 'recent' in x.keys():\n",
" if x['recent'] == True and x['value'] != None or x['value'] != '':\n",
" commonFunc(str(x['value']), 'whoisPhone')\n",
"\n",
" if 'adminOrgs' in i['asset']['domainAsset'].keys():\n",
" for x in i['asset']['domainAsset']['adminOrgs']:\n",
" if 'recent' in x.keys():\n",
" if x['recent'] == True and x['value'] != None or x['value'] != '':\n",
" commonFunc(str(x['value']), 'whoisOrg')\n",
"\n",
" if 'registrantContacts' in i['asset']['domainAsset'].keys():\n",
" for x in i['asset']['domainAsset']['registrantContacts']:\n",
" if 'recent' in x.keys():\n",
" if x['recent'] == True and x['value'] != None or x['value'] != '':\n",
" commonFunc(str(x['value']), 'whoisContact')\n",
"\n",
" if 'registrantNames' in i['asset']['domainAsset'].keys():\n",
" for x in i['asset']['domainAsset']['registrantNames']:\n",
" if 'recent' in x.keys():\n",
" if x['recent'] == True and x['value'] != None or x['value'] != '':\n",
" commonFunc(str(x['value']), 'whoisName')\n",
"\n",
" if 'registrantPhones' in i['asset']['domainAsset'].keys():\n",
" for x in i['asset']['domainAsset']['registrantPhones']:\n",
" if 'recent' in x.keys():\n",
" if x['recent'] == True and x['value'] != None or x['value'] != '':\n",
" commonFunc(str(x['value']), 'whoisPhone')\n",
"\n",
" if 'registrantOrgs' in i['asset']['domainAsset'].keys():\n",
" for x in i['asset']['domainAsset']['registrantOrgs']:\n",
" if 'recent' in x.keys():\n",
" if x['recent'] == True and x['value'] != None or x['value'] != '':\n",
" commonFunc(str(x['value']), 'whoisOrg')\n",
" if includeNS == True and 'domainAsset' in i['asset'].keys():\n",
" if 'nameServers' in i['asset']['domainAsset'].keys():\n",
" for x in i['asset']['domainAsset']['nameServers']:\n",
" if 'recent' in x.keys():\n",
" if x['recent'] == True and x['value'] != None or x['value'] != '':\n",
" commonFunc(x['value'], 'nameServer') \n",
" \n",
" elif i['kind'] == 'ipAddress':\n",
" if 'ipBlocks' in ['asset'].keys():\n",
" for x in i['asset']['ipBlocks']:\n",
" if 'recent' in x.keys():\n",
" if x['recent'] == True and x['value'] != None or x['value'] != '':\n",
" commonFunc(str(x['ipBlock']), 'ipBlock')\n",
" if 'asns' in ['asset'].keys():\n",
" for x in i['asset']['asns']:\n",
" if 'recent' in x.keys():\n",
" if x['recent'] == True and x['value'] != None or x['value'] != '':\n",
" commonFunc(str(x['value']))\n",
" \n",
" elif i['kind'] == 'ipBlock':\n",
" if 'netNames' in i['asset'].keys():\n",
" for x in i['asset']['netNames']:\n",
" if 'recent' in x.keys():\n",
" if x['recent'] == True and x['value'] != None or x['value'] != '':\n",
" commonFunc(str(x['value']), 'netName')\n",
" if 'adminContacts' in i['asset'].keys():\n",
" for x in i['asset']['adminContacts']:\n",
" if 'recent' in x.keys():\n",
" if x['recent']:\n",
" if x['recent'] == True and x['value'] != None or x['value'] != '':\n",
" commonFunc(str(x['value']), 'whoisContact')\n",
" if 'adminNames' in i['asset'].keys():\n",
" for x in i['asset']['adminNames']:\n",
" if 'recent' in x.keys():\n",
" if x['recent'] == True and x['value'] != None or x['value'] != '':\n",
" commonFunc(str(x['value']), 'whoisName')\n",
" if 'adminPhones' in i['asset'].keys():\n",
" for x in i['asset']['adminPhones']:\n",
" if 'recent' in x.keys():\n",
" if x['recent'] == True and x['value'] != None or x['value'] != '':\n",
" commonFunc(str(x['value']), 'whoisPhone')\n",
" if 'adminOrgs' in i['asset'].keys():\n",
" for x in i['asset']['adminOrgs']:\n",
" if 'recent' in x.keys():\n",
" if x['recent'] == True and x['value'] != None or x['value'] != '':\n",
" commonFunc(str(x['value']), 'whoisOrg')\n",
" if 'technicalContacts' in i['asset'].keys():\n",
" for x in i['asset']['technicalContacts']:\n",
" if 'recent' in x.keys():\n",
" if x['recent'] == True and x['value'] != None or x['value'] != '':\n",
" commonFunc(str(x['value']), 'whoisContact')\n",
" if 'technicalNames' in i['asset'].keys():\n",
" for x in i['asset']['technicalNames']:\n",
" if 'recent' in x.keys():\n",
" if x['recent'] == True and x['value'] != None or x['value'] != '':\n",
" commonFunc(str(x['value']), 'whoisName')\n",
" if 'technicalPhones' in i['asset'].keys():\n",
" for x in i['asset']['technicalPhones']:\n",
" if 'recent' in x.keys():\n",
" if x['recent'] == True and x['value'] != None or x['value'] != '':\n",
" commonFunc(str(x['value']), 'whoisPhone')\n",
" if 'technicalOrgs' in i['asset'].keys():\n",
" for x in i['asset']['technicalOrgs']:\n",
" if 'recent' in x.keys():\n",
" if x['recent'] == True and x['value'] != None or x['value'] != '':\n",
" commonFunc(str(x['value']), 'whoisOrg')\n",
"\n",
" if 'nextLink' not in responsejson:\n",
" nextUrl = None\n",
" else:\n",
" nextUrl = responsejson['nextLink']\n",
" commonAssetsList = sorted(commonAssetsList, key=lambda k: k['count'], reverse=True)\n",
" for i in commonAssetsList:\n",
" print(str(i))"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"query = 'state = confirmed AND kind in (\"domain\")'\n",
"get_common_assets(query)"
]
},
{
"attachments": {},
"cell_type": "markdown",
"metadata": {},
"source": [
"### Discovery Group - Get"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# Retrieves a discovery group with a groupName\n",
"def get_disco_group(groupName):\n",
" ''' Discovery Group Get endpoint - takes a string formatted discovery group name '''\n",
" if groupName != None:\n",
" global planeType\n",
" planeType = 'data'\n",
" azure_auth()\n",
" url = f\"https://{region}.easm.defender.microsoft.com/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/workspaces/{resourceName}/discoGroups/{groupName}?api-version={apiVersion}\"\n",
" headers = {\n",
" 'User-Agent': 'MDEASM Python Notebook',\n",
" 'Authorization': f'Bearer {bearerToken}'\n",
" }\n",
" response = requests.request(\"GET\", url, headers=headers)\n",
" if response.status_code != 200:\n",
" print(\"Error getting asset details\")\n",
" print(response.text)\n",
" return None\n",
" if len(response.text) == 0:\n",
" print(f\"No discovery group found matching {groupName}\")\n",
" return None\n",
" else:\n",
" responsejson = json.loads(response.text)\n",
" \n",
" # simplified output of asset names, adjust to your needs\n",
" print(responsejson)\n",
" else:\n",
" print(\"Please provide a valid groupName\")"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"get_disco_group('myDiscoGroupName')"
]
},
{
"attachments": {},
"cell_type": "markdown",
"metadata": {},
"source": [
"### Discovery Groups - List"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# Retrieves a list of discovery groups\n",
"def get_disco_groups_list(filter=None):\n",
" ''' Discovery Group Get List endpoint '''\n",
" global planeType\n",
" planeType = 'data'\n",
" azure_auth()\n",
" url = f\"https://{region}.easm.defender.microsoft.com/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/workspaces/{resourceName}/discoGroups?api-version={apiVersion}\"\n",
" headers = {\n",
" 'User-Agent': 'MDEASM Python Notebook',\n",
" 'Authorization': f'Bearer {bearerToken}'\n",
" }\n",
" if filter != None:\n",
" url = url + f\"&filter={filter}\"\n",
" response = requests.request(\"GET\", url, headers=headers)\n",
" if response.status_code != 200:\n",
" print(\"Error getting discovery groups\")\n",
" print(response.text)\n",
" return None\n",
" if len(response.text) == 0:\n",
" print(\"No discovery groups found\")\n",
" return None\n",
" else:\n",
" responsejson = json.loads(response.text)\n",
" \n",
" # simplified output of asset names, adjust to your needs\n",
" print(responsejson)\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"get_disco_groups_list()"
]
},
{
"attachments": {},
"cell_type": "markdown",
"metadata": {},
"source": [
"### Discovery Groups - List Runs"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# Retrieve a collection of discovery run results for a discovery group with a given groupName.\n",
"def get_disco_group_runs(groupName, filter=None, skip=None):\n",
" ''' Discovery Group Get Runs endpoint '''\n",
" if groupName != None:\n",
" global planeType\n",
" planeType = 'data'\n",
" azure_auth()\n",
" url = f\"https://{region}.easm.defender.microsoft.com/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/workspaces/{resourceName}/discoGroups/{groupName}?api-version={apiVersion}\"\n",
" headers = {\n",
" 'User-Agent': 'MDEASM Python Notebook',\n",
" 'Authorization': f'Bearer {bearerToken}'\n",
" }\n",
" if filter != None:\n",
" url = url + f\"&filter={filter}\"\n",
" if skip != None:\n",
" url = url + f\"&skip={skip}\"\n",
" response = requests.request(\"GET\", url, headers=headers)\n",
" if response.status_code != 200:\n",
" print(f\"Error getting discovery group {groupName}\")\n",
" print(response.text)\n",
" return None\n",
" if len(response.text) == 0:\n",
" print(f\"No discovery group found matching: {groupName}\")\n",
" return None\n",
" else:\n",
" responsejson = json.loads(response.text)\n",
" \n",
" # simplified output of asset names, adjust to your needs\n",
" print(responsejson)\n",
" else:\n",
" print(\"Please provide a valid groupName\")"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"get_disco_group_runs('myDiscoGroupName')"
]
},
{
"attachments": {},
"cell_type": "markdown",
"metadata": {},
"source": [
"### Discovery Group - Delete"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# Delete a discovery group with a given groupName.\n",
"def delete_disco_group(groupName):\n",
" ''' Discovery Group Delete endpoint '''\n",
" if groupName != None:\n",
" global planeType\n",
" planeType = 'data'\n",
" azure_auth()\n",
" url = f\"https://{region}.easm.defender.microsoft.com/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/workspaces/{resourceName}/discoGroups/{groupName}?api-version={apiVersion}\"\n",
" headers = {\n",
" 'User-Agent': 'MDEASM Python Notebook',\n",
" 'Authorization': f'Bearer {bearerToken}'\n",
" }\n",
" response = requests.request(\"DELETE\", url, headers=headers)\n",
" if response.status_code != 200:\n",
" print(f\"Error deleting discovery group {groupName}\")\n",
" print(response.text)\n",
" return None\n",
" if len(response.text) == 0:\n",
" print(f\"No discovery group found matching: {groupName}\")\n",
" return None\n",
" else:\n",
" responsejson = json.loads(response.text)\n",
" \n",
" # simplified output of asset names, adjust to your needs\n",
" print(responsejson)\n",
" else:\n",
" print(\"Please provide a valid groupName\")"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"delete_disco_group('myDiscoGroupName')"
]
},
{
"attachments": {},
"cell_type": "markdown",
"metadata": {},
"source": [
"### Discovery Group - Run"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# Run a discovery group with a given groupName.\n",
"def run_disco_group(groupName):\n",
" ''' Discovery Group Run endpoint '''\n",
" if groupName != None:\n",
" global planeType\n",
" planeType = 'data'\n",
" azure_auth()\n",
" url = f\"https://{region}.easm.defender.microsoft.com/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/workspaces/{resourceName}/discoGroups/{groupName}:run?api-version={apiVersion}\"\n",
" headers = {\n",
" 'User-Agent': 'MDEASM Python Notebook',\n",
" 'Authorization': f'Bearer {bearerToken}'\n",
" }\n",
" response = requests.request(\"POST\", url, headers=headers)\n",
" if response.status_code != 200 and response.status_code != 202 and response.status_code != 204:\n",
" print(f\"Error running discovery group {groupName}: response code {response.status_code}\")\n",
" print(response.text)\n",
" return None\n",
" else:\n",
" print(f\"Discovery group {groupName} run successfully\")\n",
" else:\n",
" print(\"Please provide a valid groupName\")"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"run_disco_group('myDiscoGroupName')"
]
},
{
"attachments": {},
"cell_type": "markdown",
"metadata": {},
"source": [
"### Discovery Group - Create"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# Create a discovery group with a given groupName.\n",
"def create_disco_group(groupName, requestBody):\n",
" ''' Discovery Group Run endpoint '''\n",
" if groupName != None:\n",
" global planeType\n",
" planeType = 'data'\n",
" azure_auth()\n",
" url = f\"https://{region}.easm.defender.microsoft.com/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/workspaces/{resourceName}/discoGroups/{groupName}?api-version={apiVersion}\"\n",
" requestBody = json.loads(json.dumps(requestBody))\n",
" headers = {\n",
" 'User-Agent': 'MDEASM Python Notebook',\n",
" 'Authorization': f'Bearer {bearerToken}'\n",
" }\n",
" response = requests.request(\"PUT\", url, headers=headers, body=requestBody)\n",
" if response.status_code != 200:\n",
" print(f\"Error creating discovery group {groupName}\")\n",
" print(response.text)\n",
" return None\n",
" else:\n",
" responsejson = json.loads(response.text)\n",
" \n",
" # simplified output of asset names, adjust to your needs\n",
" print(responsejson)\n",
" else:\n",
" print(\"Please provide a valid groupName and the requestBody parameters\")"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"requestBody = '{\"description\":\"myDiscoGroupDescription\", \"displayName\":\"myDiscoGroupDisplayName\", \"frequencyMilliseconds\":604800000, \"tier\":\"advanced\", , seeds: [{name: \"thisisatest.microsoft.com\", kind: \"host\"} {name: \"hotels.contoso.com\", kind: \"host\"}]}'\n",
"create_disco_group('myDiscoGroupName')"
]
},
{
"attachments": {},
"cell_type": "markdown",
"metadata": {},
"source": [
"### Control Plane Endpoints\n",
"### Labels - Create And Update"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# Labels Create And Update endpoint - requires a URL encoded query string\n",
"# and a post body containing the label properties to create or update \n",
"# example labelBody: '{\"properties\": {\"color\": \"red\", \"displayName\": \"This is a test label - #1\"}}'\n",
"\n",
"def create_labels(labelName, labelBody):\n",
" ''' Labels Create And Update endpoint - requires a URL encoded query string \n",
" and a post body containing the label properties to create or update \n",
" example labelBody: {\"properties\": {\"color\": \"red\", \"displayName\": \"This is a test label - #1\"}} '''\n",
" global planeType\n",
" planeType = 'control'\n",
" azure_auth()\n",
" url = f\"https://management.azure.com/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.Easm/workspaces/{resourceName}/labels/{labelName}?api-version=2022-04-01-preview\"\n",
" payload = json.loads(json.dumps(labelBody))\n",
" headers = {\n",
" 'User-Agent': 'MDEASM Python Notebook',\n",
" 'Authorization': f'Bearer {bearerToken}',\n",
" 'Content-Type': 'application/json',\n",
" 'Accept': 'application/json'\n",
" }\n",
" response = requests.request(\"PUT\", url, headers=headers, data=payload)\n",
" if response.status_code != 200:\n",
" print(f\"Error creating label. Response code: {response.status_code}\")\n",
" print(response.text)\n",
" return None\n",
" else:\n",
" responsejson = json.loads(response.text)\n",
" print(responsejson)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"create_labels('TestLabel1', '{\"properties\": {\"color\": \"red\", \"displayName\": \"This is a test label - #1\"}}')"
]
},
{
"attachments": {},
"cell_type": "markdown",
"metadata": {},
"source": [
"### Labels - List By Workspace"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# Returns a list of labels in a workspace\n",
"def list_labels():\n",
" ''' Returns a list of labels in a workspace '''\n",
" url = f\"https://management.azure.com/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.Easm/workspaces/{resourceName}/labels?api-version=2022-04-01-preview\"\n",
" global planeType\n",
" planeType = 'control'\n",
" azure_auth()\n",
" headers = {\n",
" 'User-Agent': 'MDEASM Python Notebook',\n",
" 'Authorization': f'Bearer {bearerToken}'\n",
" }\n",
" response = requests.request(\"GET\", url, headers=headers)\n",
" if response.status_code != 200:\n",
" print(\"Error getting labels list, check the resource name\")\n",
" print(response.text)\n",
" return None\n",
" else:\n",
" responsejson = json.loads(response.text)\n",
" print(responsejson)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"list_labels()"
]
},
{
"attachments": {},
"cell_type": "markdown",
"metadata": {},
"source": [
"### Labels - Get"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# Returns a label in a workspace\n",
"def get_label(labelName):\n",
" ''' Returns a label in a workspace '''\n",
" labelName = labelName.replace(' ', '')\n",
" global planeType\n",
" planeType = 'control'\n",
" azure_auth()\n",
" url = f\"https://management.azure.com/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.Easm/workspaces/{resourceName}/labels/{labelName}?api-version=2022-04-01-preview\"\n",
" headers = {\n",
" 'User-Agent': 'MDEASM Python Notebook',\n",
" 'Authorization': f'Bearer {bearerToken}'\n",
" }\n",
" response = requests.request(\"GET\", url, headers=headers)\n",
" if response.status_code != 200:\n",
" print(\"Error getting labels list, check the resource name\")\n",
" print(response.text)\n",
" return None\n",
" else:\n",
" responsejson = json.loads(response.text)\n",
" print(responsejson)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"get_label('TestLabel1')"
]
},
{
"attachments": {},
"cell_type": "markdown",
"metadata": {},
"source": [
"### Labels - Delete"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# Deletes a label in a workspace\n",
"def delete_label(labelName):\n",
" ''' Deletes a label in a workspace '''\n",
" labelName = labelName.replace(' ', '')\n",
" global planeType\n",
" planeType = 'control'\n",
" azure_auth()\n",
" url = f\"https://management.azure.com/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.Easm/workspaces/{resourceName}/labels/{labelName}?api-version=2022-04-01-preview\"\n",
" headers = {\n",
" 'User-Agent': 'MDEASM Python Notebook',\n",
" 'Authorization': f'Bearer {bearerToken}'\n",
" }\n",
" response = requests.request(\"DELETE\", url, headers=headers)\n",
" #responsejson = json.loads(response.text)\n",
" print(response)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"delete_label('TestLabel1')"
]
},
{
"attachments": {},
"cell_type": "markdown",
"metadata": {},
"source": [
"### Labels - Update"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# Labels Update endpoint - requires a URL encoded query string\n",
"# and a post body containing the label properties to update \n",
"# example labelBody: '{\"properties\": {\"color\": \"blue\", \"displayName\": \"This is a test label - #2\"}}'\n",
"\n",
"def update_labels(labelName, labelBody):\n",
" ''' Labels Update endpoint - requires a URL encoded query string \n",
" and a post body containing the label properties to update \n",
" example labelBody: {\"properties\": {\"color\": \"blue\", \"displayName\": \"This is a test label - #2\"}} '''\n",
" labelName = labelName.replace(' ', '')\n",
" global planeType\n",
" planeType = 'control'\n",
" azure_auth()\n",
" url = f\"https://management.azure.com/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.Easm/workspaces/{resourceName}/labels/{labelName}?api-version=2022-04-01-preview\"\n",
" payload = json.loads(json.dumps(labelBody))\n",
" headers = {\n",
" 'User-Agent': 'MDEASM Python Notebook',\n",
" 'Authorization': f'Bearer {bearerToken}',\n",
" 'Content-Type': 'application/json'\n",
" }\n",
" response = requests.request(\"PATCH\", url, headers=headers, data=payload)\n",
" if response.status_code != 200:\n",
" print(\"Error creating label\")\n",
" print(response.text)\n",
" return None\n",
" else:\n",
" responsejson = json.loads(response.text)\n",
" print(responsejson)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"update_labels('TestLabel1', '{\"properties\": {\"color\": \"blue\", \"displayName\": \"This is a test label - #2\"}}')"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "py311",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.11.0"
},
"orig_nbformat": 4,
"vscode": {
"interpreter": {
"hash": "0d018a8ee72f608539112e5522c45c89337082679eff50d46482ba7640036752"
}
}
},
"nbformat": 4,
"nbformat_minor": 2
}