Merge branch 'master' into shaharBranch2
|
@ -39,8 +39,12 @@ namespace Kqlvalidations.Tests
|
|||
var validationContext = new ValidationContext(templateObject);
|
||||
Validator.ValidateObject(templateObject, validationContext, true);
|
||||
});
|
||||
|
||||
exception.Should().BeNull();
|
||||
string exceptionToDisplay = string.Empty;
|
||||
if (exception != null)
|
||||
{
|
||||
exceptionToDisplay = $"In template {detectionsYamlFileName} there was an error while parsing: {exception.Message}";
|
||||
}
|
||||
exception.Should().BeNull(exceptionToDisplay);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
|
|
|
@ -23,6 +23,9 @@ namespace Microsoft.Azure.Sentinel.Analytics.Management.AnalyticsTemplatesServic
|
|||
[StringLength(10000, MinimumLength = 1)]
|
||||
public string Query { get; set; }
|
||||
|
||||
[JsonProperty("version", Required = Required.Always)]
|
||||
public string Version { get; set; }
|
||||
|
||||
[JsonProperty("queryFrequency", Required = Required.Always)]
|
||||
[JsonConverter(typeof(ScheduledTemplateTimeSpanConverter))]
|
||||
[RangeTimeSpanIsoFormat("00:05:00", "14.00:00:00")]
|
||||
|
|
|
@ -0,0 +1,4 @@
|
|||
.git*
|
||||
.vscode
|
||||
local.settings.json
|
||||
test
|
|
@ -0,0 +1,6 @@
|
|||
|
||||
# Azure Functions artifacts
|
||||
bin
|
||||
obj
|
||||
appsettings.json
|
||||
local.settings.json
|
Двоичные данные
DataConnectors/MCASActivityFunction/AzureFunctionMCASActivity/MCASActivityTimerTrigger.zip
Normal file
|
@ -0,0 +1,10 @@
|
|||
{
|
||||
"bindings": [
|
||||
{
|
||||
"name": "Timer",
|
||||
"type": "timerTrigger",
|
||||
"direction": "in",
|
||||
"schedule": "0 */5 * * * *"
|
||||
}
|
||||
]
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
# TimerTrigger - PowerShell
|
||||
|
||||
The `TimerTrigger` makes it incredibly easy to have your functions executed on a schedule. This sample demonstrates a simple use case of calling your function every 5 minutes.
|
||||
|
||||
## How it works
|
||||
|
||||
For a `TimerTrigger` to work, you provide a schedule in the form of a [cron expression](https://en.wikipedia.org/wiki/Cron#CRON_expression)(See the link for full details). A cron expression is a string with 6 separate expressions which represent a given schedule via patterns. The pattern we use to represent every 5 minutes is `0 */5 * * * *`. This, in plain text, means: "When seconds is equal to 0, minutes is divisible by 5, for any hour, day of the month, month, day of the week, or year".
|
||||
|
||||
## Learn more
|
||||
|
||||
<TODO> Documentation
|
|
@ -0,0 +1,304 @@
|
|||
<#
|
||||
Title: MCAS Activity Data Connector
|
||||
Language: PowerShell
|
||||
Version: 1.0
|
||||
Author: Nicholas DiCola
|
||||
Last Modified: 05/12/2021
|
||||
|
||||
DESCRIPTION
|
||||
This Function App calls the MCAS Activity REST API (https://docs.microsoft.com/cloud-app-security/api-activities) to pull the MCAS
|
||||
Activity logs. The response from the MCAS API is recieved in JSON format. This function will build the signature and authorization header
|
||||
needed to post the data to the Log Analytics workspace via the HTTP Data Connector API. The Function App will post the data to MCASActivity_CL.
|
||||
#>
|
||||
# Input bindings are passed in via param block.
|
||||
param($Timer)
|
||||
|
||||
# Get the current universal time in the default string format
|
||||
$currentUTCtime = (Get-Date).ToUniversalTime()
|
||||
|
||||
# The 'IsPastDue' porperty is 'true' when the current function invocation is later than scheduled.
|
||||
if ($Timer.IsPastDue) {
|
||||
Write-Host "PowerShell timer is running late!"
|
||||
}
|
||||
|
||||
# Write an information log with the current time.
|
||||
Write-Host "PowerShell timer trigger function ran! TIME: $currentUTCtime"
|
||||
|
||||
# Main
|
||||
if ($env:MSI_SECRET -and (Get-Module -ListAvailable Az.Accounts)){
|
||||
Connect-AzAccount -Identity
|
||||
}
|
||||
|
||||
#Wait-Debugger
|
||||
|
||||
$AzureWebJobsStorage = $env:AzureWebJobsStorage
|
||||
$MCASAPIToken = $env:MCASAPIToken
|
||||
$workspaceId = $env:WorkspaceId
|
||||
$workspaceKey = $env:WorkspaceKey
|
||||
$Lookback = $env:Lookback
|
||||
$MCASURL = $env:MCASURL
|
||||
$LAURI = $env:LAURI
|
||||
$storageAccountContainer = "mcasactivity-logs"
|
||||
$fileName = "lastrun-MCAS.json"
|
||||
|
||||
$StartTime = (get-date).ToUniversalTime()
|
||||
$currentStartTime = $StartTime | get-date -Format yyyy-MM-ddTHH:mm:ss:ffffffZ
|
||||
|
||||
if (-Not [string]::IsNullOrEmpty($LAURI)){
|
||||
if($LAURI.Trim() -notmatch 'https:\/\/([\w\-]+)\.ods\.opinsights\.azure.([a-zA-Z\.]+)$')
|
||||
{
|
||||
Write-Error -Message "MCASActivity-SecurityEvents: Invalid Log Analytics Uri." -ErrorAction Stop
|
||||
Exit
|
||||
}
|
||||
}
|
||||
|
||||
function Write-OMSLogfile {
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Inputs a hashtable, date and workspace type and writes it to a Log Analytics Workspace.
|
||||
.DESCRIPTION
|
||||
Given a value pair hash table, this function will write the data to an OMS Log Analytics workspace.
|
||||
Certain variables, such as Customer ID and Shared Key are specific to the OMS workspace data is being written to.
|
||||
This function will not write to multiple OMS workspaces. BuildSignature and post-analytics function from Microsoft documentation
|
||||
at https://docs.microsoft.com/azure/log-analytics/log-analytics-data-collector-api
|
||||
.PARAMETER DateTime
|
||||
date and time for the log. DateTime value
|
||||
.PARAMETER Type
|
||||
Name of the logfile or Log Analytics "Type". Log Analytics will append _CL at the end of custom logs String Value
|
||||
.PARAMETER LogData
|
||||
A series of key, value pairs that will be written to the log. Log file are unstructured but the key should be consistent
|
||||
withing each source.
|
||||
.INPUTS
|
||||
The parameters of data and time, type and logdata. Logdata is converted to JSON to submit to Log Analytics.
|
||||
.OUTPUTS
|
||||
The Function will return the HTTP status code from the Post method. Status code 200 indicates the request was received.
|
||||
.NOTES
|
||||
Version: 2.0
|
||||
Author: Travis Roberts
|
||||
Creation Date: 7/9/2018
|
||||
Purpose/Change: Crating a stand alone function
|
||||
#>
|
||||
[cmdletbinding()]
|
||||
Param(
|
||||
[Parameter(Mandatory = $true, Position = 0)]
|
||||
[datetime]$dateTime,
|
||||
[parameter(Mandatory = $true, Position = 1)]
|
||||
[string]$type,
|
||||
[Parameter(Mandatory = $true, Position = 2)]
|
||||
[psobject]$logdata,
|
||||
[Parameter(Mandatory = $true, Position = 3)]
|
||||
[string]$CustomerID,
|
||||
[Parameter(Mandatory = $true, Position = 4)]
|
||||
[string]$SharedKey
|
||||
)
|
||||
Write-Verbose -Message "DateTime: $dateTime"
|
||||
Write-Verbose -Message ('DateTimeKind:' + $dateTime.kind)
|
||||
Write-Verbose -Message "Type: $type"
|
||||
write-Verbose -Message "LogData: $logdata"
|
||||
|
||||
# Supporting Functions
|
||||
# Function to create the auth signature
|
||||
function BuildSignature ($CustomerID, $SharedKey, $Date, $ContentLength, $method, $ContentType, $resource) {
|
||||
$xheaders = 'x-ms-date:' + $Date
|
||||
$stringToHash = $method + "`n" + $contentLength + "`n" + $contentType + "`n" + $xHeaders + "`n" + $resource
|
||||
$bytesToHash = [text.Encoding]::UTF8.GetBytes($stringToHash)
|
||||
$keyBytes = [Convert]::FromBase64String($SharedKey)
|
||||
$sha256 = New-Object System.Security.Cryptography.HMACSHA256
|
||||
$sha256.key = $keyBytes
|
||||
$calculateHash = $sha256.ComputeHash($bytesToHash)
|
||||
$encodeHash = [convert]::ToBase64String($calculateHash)
|
||||
$authorization = 'SharedKey {0}:{1}' -f $CustomerID, $encodeHash
|
||||
return $authorization
|
||||
}
|
||||
# Function to create and post the request
|
||||
Function PostLogAnalyticsData ($CustomerID, $SharedKey, $Body, $Type) {
|
||||
$method = "POST"
|
||||
$ContentType = 'application/json'
|
||||
$resource = '/api/logs'
|
||||
$rfc1123date = ($dateTime).ToString('r')
|
||||
$ContentLength = $Body.Length
|
||||
$signature = BuildSignature `
|
||||
-customerId $CustomerID `
|
||||
-sharedKey $SharedKey `
|
||||
-date $rfc1123date `
|
||||
-contentLength $ContentLength `
|
||||
-method $method `
|
||||
-contentType $ContentType `
|
||||
-resource $resource
|
||||
|
||||
# Compatible with previous version
|
||||
if ([string]::IsNullOrEmpty($LAURI)){
|
||||
$LAURI = "https://" + $CustomerId + ".ods.opinsights.azure.com" + $resource + "?api-version=2016-04-01"
|
||||
}
|
||||
else
|
||||
{
|
||||
$LAURI = $LAURI + $resource + "?api-version=2016-04-01"
|
||||
}
|
||||
|
||||
$headers = @{
|
||||
"Authorization" = $signature;
|
||||
"Log-Type" = $type;
|
||||
"x-ms-date" = $rfc1123date
|
||||
"time-generated-field" = $dateTime
|
||||
}
|
||||
$response = Invoke-WebRequest -Uri $LAURI -Method $method -ContentType $ContentType -Headers $headers -Body $Body -UseBasicParsing
|
||||
Write-Verbose -message ('Post Function Return Code ' + $response.statuscode)
|
||||
return $response.statuscode
|
||||
}
|
||||
|
||||
# Check if time is UTC, Convert to UTC if not.
|
||||
# $dateTime = (Get-Date)
|
||||
if ($dateTime.kind.tostring() -ne 'Utc') {
|
||||
$dateTime = $dateTime.ToUniversalTime()
|
||||
Write-Verbose -Message $dateTime
|
||||
}
|
||||
|
||||
# Add DateTime to hashtable
|
||||
#$logdata.add("DateTime", $dateTime)
|
||||
$logdata | Add-Member -MemberType NoteProperty -Name "DateTime" -Value $dateTime
|
||||
|
||||
#Build the JSON file
|
||||
$logMessage = ($logdata | ConvertTo-Json -Depth 20)
|
||||
Write-Verbose -Message $logMessage
|
||||
|
||||
#Submit the data
|
||||
$returnCode = PostLogAnalyticsData -CustomerID $CustomerID -SharedKey $SharedKey -Body $logMessage -Type $type
|
||||
Write-Verbose -Message "Post Statement Return Code $returnCode"
|
||||
return $returnCode
|
||||
}
|
||||
|
||||
function SendToLogA ($Data, $customLogName) {
|
||||
#Test Size; Log A limit is 30MB
|
||||
$tempdata = @()
|
||||
$tempDataSize = 0
|
||||
|
||||
if ((($Data | Convertto-json -depth 20).Length) -gt 25MB) {
|
||||
Write-Host "Upload is over 25MB, needs to be split"
|
||||
foreach ($record in $Data) {
|
||||
$tempdata += $record
|
||||
$tempDataSize += ($record | ConvertTo-Json -depth 20).Length
|
||||
if ($tempDataSize -gt 25MB) {
|
||||
Write-OMSLogfile -dateTime (Get-Date) -type $customLogName -logdata $tempdata -CustomerID $workspaceId -SharedKey $workspaceKey
|
||||
write-Host "Sending data = $TempDataSize"
|
||||
$tempdata = $null
|
||||
$tempdata = @()
|
||||
$tempDataSize = 0
|
||||
}
|
||||
}
|
||||
Write-Host "Sending left over data = $Tempdatasize"
|
||||
Write-OMSLogfile -dateTime (Get-Date) -type $customLogName -logdata $tempdata -CustomerID $workspaceId -SharedKey $workspaceKey
|
||||
}
|
||||
Else {
|
||||
#Send to Log A as is
|
||||
Write-OMSLogfile -dateTime (Get-Date) -type $customLogName -logdata $Data -CustomerID $workspaceId -SharedKey $workspaceKey
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
# header for API calls
|
||||
$headers = @{
|
||||
Authorization = "Token $MCASAPIToken"
|
||||
'Content-Type' = "application/json"
|
||||
}
|
||||
|
||||
$EndEpoch = ([int64]((Get-Date -Date $StartTime) - (get-date "1/1/1970")).TotalMilliseconds)
|
||||
|
||||
#check for last run file
|
||||
$storageAccountContext = New-AzStorageContext -ConnectionString $AzureWebJobsStorage
|
||||
$checkBlob = Get-AzStorageBlob -Blob $fileName -Container $storageAccountContainer -Context $storageAccountContext
|
||||
if($checkBlob -ne $null){
|
||||
#Blob found get data
|
||||
Get-AzStorageBlobContent -Blob $fileName -Container $storageAccountContainer -Context $storageAccountContext -Destination "$env:temp\$fileName" -Force
|
||||
$lastRunContext = Get-Content "$env:temp\$fileName" | ConvertFrom-Json
|
||||
$StartEpoch = $lastRunContext.lastRunEpoch
|
||||
$lastRunContext.lastRunEpoch = $EndEpoch
|
||||
$lastRunContext | ConvertTo-Json | out-file "$env:temp\$fileName"
|
||||
}
|
||||
else {
|
||||
#no blob create the context
|
||||
#$StartEpoch = ([int64]((Get-Date -Date $StartTime).AddMinutes(-$Lookback) - (get-date "1/1/1970")).TotalMilliseconds)
|
||||
$StartEpoch = ([int64]((Get-Date -Date $StartTime).AddDays(-$Lookback) - (get-date "1/1/1970")).TotalMilliseconds)
|
||||
$lastRunContent = @"
|
||||
{
|
||||
"lastRun": "$CurrentStartTime",
|
||||
"lastRunEpoch": $EndEpoch
|
||||
}
|
||||
"@
|
||||
$lastRunContent | Out-File "$env:temp\$fileName"
|
||||
$lastRunContext = $lastRunContent | ConvertFrom-Json
|
||||
}
|
||||
|
||||
|
||||
|
||||
#Build query
|
||||
$body = @"
|
||||
{
|
||||
"filters": {
|
||||
"date": {
|
||||
"range": {
|
||||
"end": $EndEpoch,
|
||||
"start": $StartEpoch
|
||||
}
|
||||
}
|
||||
},
|
||||
"isScan": true
|
||||
}
|
||||
"@
|
||||
|
||||
|
||||
|
||||
#Get the Activities
|
||||
Write-Host "Starting to process Tenant: $MCASURL"
|
||||
$uri = $MCASURL+"/api/v1/activities/"
|
||||
$loopAgain = $true
|
||||
$totalRecords = 0
|
||||
do {
|
||||
$results = $null
|
||||
$results = Invoke-RestMethod -Method Post -Uri $uri -Body $body -Headers $headers -ContentType "application/json" -UseBasicParsing
|
||||
try {
|
||||
$results = $results | ConvertFrom-Json
|
||||
}
|
||||
catch {
|
||||
Write-Verbose "One or more property name collisions were detected in the response. An attempt will be made to resolve this by renaming any offending properties."
|
||||
$results = $results.Replace('"Level":', '"Level_2":')
|
||||
$results = $results.Replace('"EventName":', '"EventName_2":')
|
||||
try {
|
||||
$results = $results | ConvertFrom-Json # Try the JSON conversion again, now that we hopefully fixed the property collisions
|
||||
}
|
||||
catch {
|
||||
throw $_
|
||||
}
|
||||
Write-Verbose "Any property name collisions appear to have been resolved."
|
||||
}
|
||||
if(($results.data).Count -ne 0){
|
||||
#write to log A to be added later
|
||||
Write-Host "Got some results: "($results.data.Count)
|
||||
$totalRecords += ($results.data.Count)
|
||||
Write-Host $totalRecords
|
||||
#SendToLogA -Data ($results.data) -customLogName "MCASActivity"
|
||||
}
|
||||
else{
|
||||
Write-Host "No new logs"
|
||||
}
|
||||
$loopAgain = $results.hasNext
|
||||
|
||||
if($loopAgain -ne $false){
|
||||
# if there is more data update the query
|
||||
$newBody = $body | ConvertFrom-Json
|
||||
If($newBody.filters.date.lte -eq $null){
|
||||
$newBody.filters.date | Add-Member -Name lte -Value ($results.nextQueryFilters.date.lte) -MemberType NoteProperty
|
||||
}
|
||||
else {
|
||||
$newBody.filters.date.lte = ($results.nextQueryFilters.date.lte)
|
||||
}
|
||||
$Body = $newBody | ConvertTo-Json -Depth 4
|
||||
Write-Host $body
|
||||
}
|
||||
else {
|
||||
# no more data write last run to az storage
|
||||
Set-AzStorageBlobContent -Blob $fileName -Container $storageAccountContainer -Context $storageAccountContext -File "$env:temp\$fileName" -Force
|
||||
}
|
||||
} until ($loopAgain -eq $false)
|
||||
|
||||
#clear the temp folder
|
||||
Remove-Item $env:temp\* -Recurse -Force -ErrorAction SilentlyContinue
|
|
@ -0,0 +1,18 @@
|
|||
{
|
||||
"version": "2.0",
|
||||
"logging": {
|
||||
"applicationInsights": {
|
||||
"samplingSettings": {
|
||||
"isEnabled": true,
|
||||
"excludedTypes": "Request"
|
||||
}
|
||||
}
|
||||
},
|
||||
"extensionBundle": {
|
||||
"id": "Microsoft.Azure.Functions.ExtensionBundle",
|
||||
"version": "[1.*, 2.0.0)"
|
||||
},
|
||||
"managedDependency": {
|
||||
"enabled": true
|
||||
}
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
# Azure Functions profile.ps1
|
||||
#
|
||||
# This profile.ps1 will get executed every "cold start" of your Function App.
|
||||
# "cold start" occurs when:
|
||||
#
|
||||
# * A Function App starts up for the very first time
|
||||
# * A Function App starts up after being de-allocated due to inactivity
|
||||
#
|
||||
# You can define helper functions, run commands, or specify environment variables
|
||||
# NOTE: any variables defined that are not environment variables will get reset after the first execution
|
||||
|
||||
# Authenticate with Azure PowerShell using MSI.
|
||||
# Remove this if you are not planning on using MSI or Azure PowerShell.
|
||||
if ($env:MSI_SECRET) {
|
||||
Disable-AzContextAutosave -Scope Process | Out-Null
|
||||
Connect-AzAccount -Identity
|
||||
}
|
||||
|
||||
# Uncomment the next line to enable legacy AzureRm alias in Azure PowerShell.
|
||||
# Enable-AzureRmAlias
|
||||
|
||||
# You can also define functions or aliases that can be referenced in any of your PowerShell functions.
|
|
@ -0,0 +1,4 @@
|
|||
{
|
||||
"$schema": "http://json.schemastore.org/proxies",
|
||||
"proxies": {}
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
# This file enables modules to be automatically managed by the Functions service.
|
||||
# See https://aka.ms/functionsmanageddependency for additional information.
|
||||
#
|
||||
@{
|
||||
# For latest supported version, go to 'https://www.powershellgallery.com/packages/Az'.
|
||||
# To use the Az module in your function app, please uncomment the line below.
|
||||
'Az' = '5.*'
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
## 1.0
|
||||
- Converted MCAS Acitivyt Data connector from Logic Apps to Azure Function
|
||||
- Splitting the data if it is more than 25MB
|
|
@ -0,0 +1,127 @@
|
|||
{
|
||||
"id": "MCASActivity",
|
||||
"title": "MCAS Activity(Preview)",
|
||||
"publisher": "Microsoft",
|
||||
"descriptionMarkdown": "The MCAS Activity connector provides the capability to ingest Activity logs from Microsoft Cloud App Security into Azure Sentinel. The connector provides visibility into these log types in Azure Sentinel to view dashboards, create custom alerts, and to improve monitoring and investigation capabilities.",
|
||||
"graphQueries": [
|
||||
{
|
||||
"metricName": "Total data received",
|
||||
"legend": "MCAS Activity Logs",
|
||||
"baseQuery": "MCASActivity_CL"
|
||||
}
|
||||
],
|
||||
"sampleQueries": [
|
||||
{
|
||||
"description": "Top 10 MCAS Activities",
|
||||
"query": "MCASActivity_CL \n| where TimeGenerated > ago(24h) \n| limit 10"
|
||||
}
|
||||
],
|
||||
"connectivityCriterias": [
|
||||
{
|
||||
"type": "IsConnectedQuery",
|
||||
"value": [ "MCASActivity_CL \n| summarize LastLogReceived = max(TimeGenerated) \n| project IsConnected = LastLogReceived > ago(30d)"]
|
||||
}
|
||||
],
|
||||
"dataTypes": [
|
||||
{
|
||||
"name": "MCASActivity_CL",
|
||||
"lastDataReceivedQuery": "MCASActivity_CL \n| summarize Time = max(TimeGenerated) \n| where isnotempty(Time)"
|
||||
}
|
||||
],
|
||||
"availability": {
|
||||
"status": 1,
|
||||
"isPreview": true
|
||||
},
|
||||
"permissions": {
|
||||
"resourceProvider": [
|
||||
{
|
||||
"provider": "Microsoft.OperationalInsights/workspaces",
|
||||
"permissionsDisplayText": "read and write permissions are required.",
|
||||
"providerDisplayName": "Workspace",
|
||||
"scope": "Workspace",
|
||||
"requiredPermissions": {
|
||||
"write": true,
|
||||
"read": true,
|
||||
"delete": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"provider": "Microsoft.OperationalInsights/workspaces/sharedKeys",
|
||||
"permissionsDisplayText": "read permissions to shared keys for the workspace are required. [See the documentation to learn more about workspace keys](https://docs.microsoft.com/azure/azure-monitor/platform/agent-windows#obtain-workspace-id-and-key)",
|
||||
"providerDisplayName": "Keys",
|
||||
"scope": "Workspace",
|
||||
"requiredPermissions": {
|
||||
"action": true
|
||||
}
|
||||
}
|
||||
],
|
||||
"customs": [
|
||||
{
|
||||
"name": "Microsoft.Web/sites permissions",
|
||||
"description": "Read and write permissions to Azure Functions to create a Function App is required. [See the documentation to learn more about Azure Functions](https://docs.microsoft.com/azure/azure-functions/)."
|
||||
},
|
||||
{
|
||||
"name": "MCAS API Token ()",
|
||||
"description": "A MCAS API Token is required. See the documentation to learn more about the [API Token](https://docs.microsoft.com/cloud-app-security/api-authentication)."
|
||||
}
|
||||
]
|
||||
},
|
||||
"instructionSteps": [
|
||||
{
|
||||
"title": "",
|
||||
"description": ">**NOTE:** This connector uses Azure Functions to connect to MCAS to pull its logs into Azure Sentinel. This might result in additional data ingestion costs. Check the [Azure Functions pricing page](https://azure.microsoft.com/pricing/details/functions/) for details."
|
||||
},
|
||||
{
|
||||
"title": "",
|
||||
"description": ">**(Optional Step)** Securely store workspace and API authorization key(s) or token(s) in Azure Key Vault. Azure Key Vault provides a secure mechanism to store and retrieve key values. [Follow these instructions](https://docs.microsoft.com/azure/app-service/app-service-key-vault-references) to use Azure Key Vault with an Azure Function App."
|
||||
},
|
||||
{
|
||||
"title": "",
|
||||
"description": "**STEP 1 - Configuration steps for the API Token**\n\n[Follow these instructions](https://docs.microsoft.com/cloud-app-security/api-authentication) to create an API Token."
|
||||
},
|
||||
{
|
||||
"title": "",
|
||||
"description": "**STEP 2 - Choose ONE from the following two deployment options to deploy the connector and the associated Azure Function**\n\n>**IMPORTANT:** Before deploying the MCAS Activity Data connector, have the Workspace ID and Workspace Primary Key (can be copied from the following), as well as the API Token and MCAS URL, readily available.",
|
||||
"instructions": [
|
||||
{
|
||||
"parameters": {
|
||||
"fillWith": [
|
||||
"WorkspaceId"
|
||||
],
|
||||
"label": "Workspace ID"
|
||||
},
|
||||
"type": "CopyableLabel"
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"fillWith": [
|
||||
"PrimaryKey"
|
||||
],
|
||||
"label": "Primary Key"
|
||||
},
|
||||
"type": "CopyableLabel"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"title": "Option 1 - Azure Resource Manager (ARM) Template",
|
||||
"description": "This method provides an automated deployment of the MCAS Activity data connector using an ARM Tempate.\n\n1. Click the **Deploy to Azure** button below. \n\n\t[![Deploy To Azure](https://aka.ms/deploytoazurebutton)](https://portal.azure.com/#create/Microsoft.Template/uri/https%3A%2F%2Fraw.githubusercontent.com%2FAzure%2FAzure-Sentinel%2Fmaster%2FDataConnectors%2FMCASActivityFunction%2Fazuredeploy.json)\n\n2. Select the preferred **Subscription**, **Resource Group** and **Location**. \n3. Enter the **Workspace ID**, **Workspace Key**, **API Token**, **MCAS URL**, **Lookback** \n> - The default **Time Interval** is set to pull the last five (5) minutes of data. If the time interval needs to be modified, it is recommended to change the Function App Timer Trigger accordingly (in the function.json file, post deployment) to prevent overlapping data ingestion. \n> - Note: If using Azure Key Vault secrets for any of the values above, use the`@Microsoft.KeyVault(SecretUri={Security Identifier})`schema in place of the string values. Refer to [Key Vault references documentation](https://docs.microsoft.com/azure/app-service/app-service-key-vault-references) for further details. \n4. Mark the checkbox labeled **I agree to the terms and conditions stated above**. \n5. Click **Purchase** to deploy."
|
||||
},
|
||||
{
|
||||
"title": "Option 2 - Manual Deployment of Azure Functions",
|
||||
"description": "Use the following step-by-step instructions to deploy the MCAS Activity Data connector manually with Azure Functions."
|
||||
},
|
||||
{
|
||||
"title": "",
|
||||
"description": "**1. Create a Function App**\n\n1. From the Azure Portal, navigate to [Function App](https://portal.azure.com/#blade/HubsExtension/BrowseResource/resourceType/Microsoft.Web%2Fsites/kind/functionapp), and select **+ Add**.\n2. In the **Basics** tab, ensure Runtime stack is set to **Powershell Core**. \n3. In the **Hosting** tab, ensure the **Consumption (Serverless)** plan type is selected.\n4. Make other preferrable configuration changes, if needed, then click **Create**."
|
||||
},
|
||||
{
|
||||
"title": "",
|
||||
"description": "**2. Import Function App Code**\n\n1. In the newly created Function App, select **Functions** on the left pane and click **+ Add**.\n2. Select **Timer Trigger**.\n3. Enter a unique Function **Name** and leave the default cron schedule of every 5 minutes, then click **Create**.\n4. Click on **Code + Test** on the left pane. \n5. Copy the [Function App Code](https://portal.azure.com/#create/Microsoft.Template/uri/https%3A%2F%2Fraw.githubusercontent.com%2Fazure%2FAzure-Sentinel%2FDataConnectors%2FMCASActivityFunction%2Fazuredeploy.json) and paste into the Function App `run.ps1` editor.\n5. Click **Save**."
|
||||
},
|
||||
{
|
||||
"title": "",
|
||||
"description": "**3. Configure the Function App**\n\n1. In the Function App, select the Function App Name and select **Configuration**.\n2. In the **Application settings** tab, select **+ New application setting**.\n3. Add each of the following five (5) application settings individually, with their respective string values (case-sensitive): \n\t\tAPIToken\n\t\tMCASURL\n\t\tLookback\n\t\tTimeInterval\n\t\tWorkspaceID\n\t\tWorkspaceKey\n - Set the `timeInterval` (in minutes) to the default value of `5` to correspond to the default Timer Trigger of every `5` minutes. Note that modifying time interval will require modifying the Function App Timer Trigger accordingly to prevent overlapping data.\n - Note: If using Azure Key Vault secrets for any of the values above, use the`@Microsoft.KeyVault(SecretUri={Security Identifier})`schema in place of the string values. Refer to [Key Vault references documentation](https://docs.microsoft.com/azure/app-service/app-service-key-vault-references) for further details. \n4. Once all application settings have been entered, click **Save**."
|
||||
}
|
||||
]
|
||||
}
|
|
@ -0,0 +1,4 @@
|
|||
{
|
||||
"lastRun": "",
|
||||
"lastRunEpoch": 0
|
||||
}
|
|
@ -0,0 +1,328 @@
|
|||
{
|
||||
"$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
|
||||
"contentVersion": "1.0.0.0",
|
||||
"parameters": {
|
||||
"FunctionName": {
|
||||
"defaultValue": "MCASActivity",
|
||||
"type": "string",
|
||||
"metadata": {
|
||||
"description": "Specifies the name of the Function App."
|
||||
}
|
||||
},
|
||||
"APIToken": {
|
||||
"type": "securestring",
|
||||
"metadata": {
|
||||
"description": "Specifies MCAS API Token."
|
||||
}
|
||||
},
|
||||
"MCASURL": {
|
||||
"type": "string",
|
||||
"metadata": {
|
||||
"description": "Specifies MCAS URL."
|
||||
}
|
||||
},
|
||||
"WorkspaceId": {
|
||||
"type": "string",
|
||||
"metadata": {
|
||||
"description": "Specifies the Log Analytics Workspace Id."
|
||||
}
|
||||
},
|
||||
"WorkspaceKey": {
|
||||
"type": "securestring",
|
||||
"metadata": {
|
||||
"description": "Specifies the Log Analytics Workspace Key."
|
||||
}
|
||||
},
|
||||
"FunctionSchedule": {
|
||||
"type": "string",
|
||||
"defaultValue": "0 */10 * * * *",
|
||||
"metadata": {
|
||||
"description": "For a `TimerTrigger` to work, you provide a schedule in the form of a [cron expression](https://en.wikipedia.org/wiki/Cron#CRON_expression)(See the link for full details). A cron expression is a string with 6 separate expressions which represent a given schedule via patterns. The pattern we use to represent every 1 hour is `0 0 * * * *`. This, in plain text, means: When seconds is equal to 0, minutes is divisible by 5, for any hour, day of the month, month, day of the week, or year"
|
||||
}
|
||||
},
|
||||
"Lookback": {
|
||||
"type": "string",
|
||||
"defaultValue": "10",
|
||||
"metadata": {
|
||||
"description": "Match the Lookback to the number of minutes between runs. If cron schedule is 10 mins, use 10. If its set to every hour, use 60."
|
||||
}
|
||||
}
|
||||
},
|
||||
"variables": {
|
||||
"FunctionName": "[concat(toLower(parameters('FunctionName')),'-fn', uniqueString(resourceGroup().id, subscription().id))]",
|
||||
"StorageAccountName": "[concat(substring(variables('FunctionName'), 0, 7), '-sa-', uniqueString(resourceGroup().id, subscription().id))]",
|
||||
"KeyVaultName": "[concat(substring(variables('FunctionName'), 0, 7), '-kv-', uniqueString(resourceGroup().id, subscription().id))]",
|
||||
"MCASAPIToken": "MCASAPIToken",
|
||||
"LogAnalyticsWorkspaceKey": "LogAnalyticsWorkspaceKey",
|
||||
"StorageContainerName": "mcasactivity-logs",
|
||||
"StorageSuffix": "[environment().suffixes.storage]",
|
||||
"LogAnaltyicsUri": "[replace(environment().portal, 'https://portal', concat('https://', toLower(parameters('WorkspaceId')), '.ods.opinsights'))]"
|
||||
},
|
||||
"resources": [
|
||||
{
|
||||
"type": "Microsoft.Insights/components",
|
||||
"apiVersion": "2015-05-01",
|
||||
"name": "[variables('FunctionName')]",
|
||||
"location": "[resourceGroup().location]",
|
||||
"kind": "web",
|
||||
"properties": {
|
||||
"Application_Type": "web",
|
||||
"ApplicationId": "[variables('FunctionName')]"
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "Microsoft.Storage/storageAccounts",
|
||||
"apiVersion": "2019-06-01",
|
||||
"name": "[variables('StorageAccountName')]",
|
||||
"location": "[resourceGroup().location]",
|
||||
"sku": {
|
||||
"name": "Standard_LRS",
|
||||
"tier": "Standard"
|
||||
},
|
||||
"kind": "StorageV2",
|
||||
"properties": {
|
||||
"networkAcls": {
|
||||
"bypass": "AzureServices",
|
||||
"virtualNetworkRules": [],
|
||||
"ipRules": [],
|
||||
"defaultAction": "Allow"
|
||||
},
|
||||
"supportsHttpsTrafficOnly": true,
|
||||
"encryption": {
|
||||
"services": {
|
||||
"file": {
|
||||
"keyType": "Account",
|
||||
"enabled": true
|
||||
},
|
||||
"blob": {
|
||||
"keyType": "Account",
|
||||
"enabled": true
|
||||
}
|
||||
},
|
||||
"keySource": "Microsoft.Storage"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "Microsoft.Web/serverfarms",
|
||||
"apiVersion": "2018-02-01",
|
||||
"name": "[variables('FunctionName')]",
|
||||
"location": "[resourceGroup().location]",
|
||||
"sku": {
|
||||
"name": "Y1",
|
||||
"tier": "Dynamic"
|
||||
},
|
||||
"kind": "functionapp",
|
||||
"properties": {
|
||||
"name": "[variables('FunctionName')]",
|
||||
"workerSize": "0",
|
||||
"workerSizeId": "0",
|
||||
"numberOfWorkers": "1"
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "Microsoft.Storage/storageAccounts/blobServices",
|
||||
"apiVersion": "2019-06-01",
|
||||
"name": "[concat(variables('StorageAccountName'), '/default')]",
|
||||
"dependsOn": [
|
||||
"[resourceId('Microsoft.Storage/storageAccounts', variables('StorageAccountName'))]"
|
||||
],
|
||||
"sku": {
|
||||
"name": "Standard_LRS",
|
||||
"tier": "Standard"
|
||||
},
|
||||
"properties": {
|
||||
"cors": {
|
||||
"corsRules": []
|
||||
},
|
||||
"deleteRetentionPolicy": {
|
||||
"enabled": false
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "Microsoft.Storage/storageAccounts/fileServices",
|
||||
"apiVersion": "2019-06-01",
|
||||
"name": "[concat(variables('StorageAccountName'), '/default')]",
|
||||
"dependsOn": [
|
||||
"[resourceId('Microsoft.Storage/storageAccounts', variables('StorageAccountName'))]"
|
||||
],
|
||||
"sku": {
|
||||
"name": "Standard_LRS",
|
||||
"tier": "Standard"
|
||||
},
|
||||
"properties": {
|
||||
"cors": {
|
||||
"corsRules": []
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "Microsoft.Web/sites",
|
||||
"apiVersion": "2018-11-01",
|
||||
"name": "[variables('FunctionName')]",
|
||||
"location": "[resourceGroup().location]",
|
||||
"dependsOn": [
|
||||
"[resourceId('Microsoft.Storage/storageAccounts', variables('StorageAccountName'))]",
|
||||
"[resourceId('Microsoft.Web/serverfarms', variables('FunctionName'))]",
|
||||
"[resourceId('Microsoft.Insights/components', variables('FunctionName'))]"
|
||||
],
|
||||
"kind": "functionapp",
|
||||
"identity": {
|
||||
"type": "SystemAssigned"
|
||||
},
|
||||
"properties": {
|
||||
"name": "[variables('FunctionName')]",
|
||||
"serverFarmId": "[resourceId('Microsoft.Web/serverfarms', variables('FunctionName'))]",
|
||||
"httpsOnly": true,
|
||||
"clientAffinityEnabled": true,
|
||||
"alwaysOn": true,
|
||||
"siteConfig": {
|
||||
"powerShellVersion": "~7"
|
||||
}
|
||||
},
|
||||
"resources": [
|
||||
{
|
||||
"apiVersion": "2018-11-01",
|
||||
"type": "config",
|
||||
"name": "appsettings",
|
||||
"dependsOn": [
|
||||
"[concat('Microsoft.Web/sites/', variables('FunctionName'))]",
|
||||
"[resourceId('Microsoft.KeyVault/vaults/', variables('KeyVaultName'))]",
|
||||
"[resourceId('Microsoft.KeyVault/vaults/secrets', variables('KeyVaultName'), variables('MCASAPIToken'))]",
|
||||
"[resourceId('Microsoft.KeyVault/vaults/secrets', variables('KeyVaultName'), variables('LogAnalyticsWorkspaceKey'))]"
|
||||
],
|
||||
"properties": {
|
||||
"FUNCTIONS_EXTENSION_VERSION": "~3",
|
||||
"FUNCTIONS_WORKER_RUNTIME": "powershell",
|
||||
"APPINSIGHTS_INSTRUMENTATIONKEY": "[reference(resourceId('Microsoft.insights/components', variables('FunctionName')), '2015-05-01').InstrumentationKey]",
|
||||
"APPLICATIONINSIGHTS_CONNECTION_STRING": "[reference(resourceId('microsoft.insights/components', variables('FunctionName')), '2015-05-01').ConnectionString]",
|
||||
"AzureWebJobsStorage": "[concat('DefaultEndpointsProtocol=https;AccountName=', toLower(variables('StorageAccountName')),';AccountKey=',listKeys(resourceId('Microsoft.Storage/storageAccounts', variables('StorageAccountName')), '2019-06-01').keys[0].value, ';EndpointSuffix=',toLower(variables('StorageSuffix')))]",
|
||||
"WEBSITE_CONTENTAZUREFILECONNECTIONSTRING": "[concat('DefaultEndpointsProtocol=https;AccountName=', toLower(variables('StorageAccountName')),';AccountKey=', listKeys(resourceId('Microsoft.Storage/storageAccounts', variables('StorageAccountName')), '2019-06-01').keys[0].value, ';EndpointSuffix=',toLower(variables('StorageSuffix')))]",
|
||||
"WEBSITE_CONTENTSHARE": "[toLower(variables('FunctionName'))]",
|
||||
"MCASAPIToken": "[concat('@Microsoft.KeyVault(SecretUri=', reference(variables('MCASAPIToken')).secretUriWithVersion, ')')]",
|
||||
"WorkspaceId": "[parameters('WorkspaceId')]",
|
||||
"WorkspaceKey": "[concat('@Microsoft.KeyVault(SecretUri=', reference(variables('LogAnalyticsWorkspaceKey')).secretUriWithVersion, ')')]",
|
||||
"Schedule": "[parameters('FunctionSchedule')]",
|
||||
"Lookback": "[parameters('Lookback')]",
|
||||
"MCASURL": "[parameters('MCASURL')]",
|
||||
"LAURI": "[variables('LogAnaltyicsUri')]",
|
||||
"WEBSITE_RUN_FROM_PACKAGE": "https://aka.ms/mcasactivityazurefunctionzip"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "Microsoft.KeyVault/vaults",
|
||||
"apiVersion": "2016-10-01",
|
||||
"name": "[variables('KeyVaultName')]",
|
||||
"location": "[resourceGroup().location]",
|
||||
"dependsOn": [
|
||||
"[resourceId('Microsoft.Web/sites', variables('FunctionName'))]"
|
||||
],
|
||||
"properties": {
|
||||
"sku": {
|
||||
"family": "A",
|
||||
"name": "Standard"
|
||||
},
|
||||
"tenantId": "[subscription().tenantId]",
|
||||
"accessPolicies": [
|
||||
{
|
||||
"tenantId": "[subscription().tenantId]",
|
||||
"objectId": "[reference(resourceId('Microsoft.Web/sites', variables('FunctionName')),'2019-08-01', 'full').identity.principalId]",
|
||||
"permissions": {
|
||||
"secrets": [
|
||||
"get",
|
||||
"list"
|
||||
]
|
||||
}
|
||||
}
|
||||
],
|
||||
"enabledForDeployment": false,
|
||||
"enabledForDiskEncryption": false,
|
||||
"enabledForTemplateDeployment": true,
|
||||
"enableSoftDelete": true
|
||||
},
|
||||
"resources": [
|
||||
{
|
||||
"type": "secrets",
|
||||
"apiVersion": "2016-10-01",
|
||||
"name": "[variables('MCASAPIToken')]",
|
||||
"dependsOn": [
|
||||
"[resourceId('Microsoft.KeyVault/vaults/', variables('KeyVaultName'))]"
|
||||
],
|
||||
"properties": {
|
||||
"value": "[parameters('APIToken')]",
|
||||
"contentType": "string",
|
||||
"attributes": {
|
||||
"enabled": true
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "secrets",
|
||||
"apiVersion": "2016-10-01",
|
||||
"name": "[variables('LogAnalyticsWorkspaceKey')]",
|
||||
"dependsOn": [
|
||||
"[resourceId('Microsoft.KeyVault/vaults/', variables('KeyVaultName'))]"
|
||||
],
|
||||
"properties": {
|
||||
"value": "[parameters('WorkspaceKey')]",
|
||||
"contentType": "string",
|
||||
"attributes": {
|
||||
"enabled": true
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "Microsoft.Storage/storageAccounts/blobServices/containers",
|
||||
"apiVersion": "2019-06-01",
|
||||
"name": "[concat(variables('StorageAccountName'), '/default/azure-webjobs-hosts')]",
|
||||
"dependsOn": [
|
||||
"[resourceId('Microsoft.Storage/storageAccounts/blobServices', variables('StorageAccountName'), 'default')]",
|
||||
"[resourceId('Microsoft.Storage/storageAccounts', variables('StorageAccountName'))]"
|
||||
],
|
||||
"properties": {
|
||||
"publicAccess": "None"
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "Microsoft.Storage/storageAccounts/blobServices/containers",
|
||||
"apiVersion": "2019-06-01",
|
||||
"name": "[concat(variables('StorageAccountName'), '/default/azure-webjobs-secrets')]",
|
||||
"dependsOn": [
|
||||
"[resourceId('Microsoft.Storage/storageAccounts/blobServices', variables('StorageAccountName'), 'default')]",
|
||||
"[resourceId('Microsoft.Storage/storageAccounts', variables('StorageAccountName'))]"
|
||||
],
|
||||
"properties": {
|
||||
"publicAccess": "None"
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "Microsoft.Storage/storageAccounts/blobServices/containers",
|
||||
"apiVersion": "2019-06-01",
|
||||
"name": "[concat(variables('StorageAccountName'), concat('/default/', variables('StorageContainerName')))]",
|
||||
"dependsOn": [
|
||||
"[resourceId('Microsoft.Storage/storageAccounts/blobServices', variables('StorageAccountName'), 'default')]",
|
||||
"[resourceId('Microsoft.Storage/storageAccounts', variables('StorageAccountName'))]"
|
||||
],
|
||||
"properties": {
|
||||
"publicAccess": "None"
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "Microsoft.Storage/storageAccounts/fileServices/shares",
|
||||
"apiVersion": "2019-06-01",
|
||||
"name": "[concat(variables('StorageAccountName'), '/default/', tolower(variables('StorageAccountName')))]",
|
||||
"dependsOn": [
|
||||
"[resourceId('Microsoft.Storage/storageAccounts/fileServices', variables('StorageAccountName'), 'default')]",
|
||||
"[resourceId('Microsoft.Storage/storageAccounts', variables('StorageAccountName'))]"
|
||||
],
|
||||
"properties": {
|
||||
"shareQuota": 5120
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
|
@ -0,0 +1,70 @@
|
|||
# Ingest MCAS Activity Log
|
||||
Author: Nicholas DiCola
|
||||
|
||||
This function ingests MCAS Activities via (API)[https://docs.microsoft.com/cloud-app-security/api-activities-list] and writes them to a custom log table called MCASActivity_CL.
|
||||
|
||||
Following are the configuration steps to deploy Function App.
|
||||
|
||||
## **Pre-requisites**
|
||||
|
||||
A MCAS API Token is required. See the documentation to learn more about the [API Token](https://docs.microsoft.com/cloud-app-security/api-authentication).
|
||||
|
||||
|
||||
## Configuration Steps to Deploy Function App
|
||||
1. Click on Deploy to Azure (For both Commercial & Azure GOV)
|
||||
<a href="https://portal.azure.com/#create/Microsoft.Template/uri/https%3A%2F%2Fraw.githubusercontent.com%2FAzure%2FAzure-Sentinel%2Fmaster%2FDataConnectors%2FMCASActivityFunction%2Fazuredeploy.json" target="_blank">
|
||||
<img src="https://aka.ms/deploytoazurebutton"/>
|
||||
</a>
|
||||
|
||||
|
||||
2. Select the preferred **Subscription**, **Resource Group** and **Location**
|
||||
**Note**
|
||||
Best practice : Create new Resource Group while deploying - all the resources of your custom Data connector will reside in the newly created Resource
|
||||
Group
|
||||
|
||||
3. Enter the following value in the ARM template deployment
|
||||
```
|
||||
"APIToken": This is the MCAS API Token
|
||||
"MCASURL": This is the MCAS URL. See About in the portal for specfici url.
|
||||
"Workspace Id": The Sentinel Log Analytics Workspace Id
|
||||
"Workspace Key": The Sentinel Log Analytics Workspace Key
|
||||
"Function Schedule": The `TimerTrigger` makes it incredibly easy to have your functions executed on a schedule
|
||||
"Lookback": The number of minutes between runs
|
||||
```
|
||||
|
||||
## Post Deployment Steps
|
||||
1. There is a json file (lastrun-MCAS.json) in Function Dependencies folder
|
||||
2. Upload the file to the storage account "mcasactivity-logs" container from
|
||||
4. API Token and Workspace Key will be placed as "Secrets" in the Azure KeyVault `<<Function App Name>><<uniqueid>>` with only Azure Function access policy. If you want to see/update these secrets,
|
||||
|
||||
```
|
||||
a. Go to Azure KeyVault `<<Function App Name>><<uniqueid>>`
|
||||
b. Click on "Access Policies" under Settings
|
||||
c. Click on "Add Access Policy"
|
||||
i. Configure from template : Secret Management
|
||||
ii. Key Permissions : GET, LIST, SET
|
||||
iii. Select Prinicpal : <<Your Account>>
|
||||
iv. Add
|
||||
d. Click "Save"
|
||||
|
||||
```
|
||||
|
||||
6. The `TimerTrigger` makes it incredibly easy to have your functions executed on a schedule. This sample demonstrates a simple use case of calling your function based on your schedule provided while deploying. If you want to change
|
||||
the schedule
|
||||
```
|
||||
a. Click on Function App "Configuration" under Settings
|
||||
b. Click on "Schedule" under "Application Settings"
|
||||
c. Update your own schedule using cron expression.
|
||||
```
|
||||
**Note: For a `TimerTrigger` to work, you provide a schedule in the form of a [cron expression](https://en.wikipedia.org/wiki/Cron#CRON_expression)(See the link for full details). A cron expression is a string with 6 separate expressions which represent a given schedule via patterns. The pattern we use to represent every 5 minutes is `0 */5 * * * *`. This, in plain text, means: "When seconds is equal to 0, minutes is divisible by 5, for any hour, day of the month, month, day of the week, or year".**
|
||||
|
||||
7. If you change the TimerTigger you need to configure the Lookback setting to match the number of minutes between runs. If you want to change
|
||||
the Lookback
|
||||
```
|
||||
a. Click on Function App "Configuration" under Settings
|
||||
b. Click on "Lookback" under "Application Settings"
|
||||
c. Update your Lookback using a number of minutes (e.g 10).
|
||||
```
|
||||
|
||||
|
||||
Note: there are parsers (here)[https://github.com/Azure/Azure-Sentinel/blob/master/Parsers/MCAS] to make the logs useful
|
После Ширина: | Высота: | Размер: 30 KiB |
После Ширина: | Высота: | Размер: 34 KiB |
После Ширина: | Высота: | Размер: 7.7 KiB |
После Ширина: | Высота: | Размер: 25 KiB |
После Ширина: | Высота: | Размер: 34 KiB |
После Ширина: | Высота: | Размер: 41 KiB |
После Ширина: | Высота: | Размер: 24 KiB |
После Ширина: | Высота: | Размер: 27 KiB |
После Ширина: | Высота: | Размер: 66 KiB |
После Ширина: | Высота: | Размер: 61 KiB |
После Ширина: | Высота: | Размер: 56 KiB |
После Ширина: | Высота: | Размер: 30 KiB |
После Ширина: | Высота: | Размер: 51 KiB |
После Ширина: | Высота: | Размер: 34 KiB |
После Ширина: | Высота: | Размер: 34 KiB |
После Ширина: | Высота: | Размер: 42 KiB |
После Ширина: | Высота: | Размер: 41 KiB |
После Ширина: | Высота: | Размер: 76 KiB |
После Ширина: | Высота: | Размер: 56 KiB |
После Ширина: | Высота: | Размер: 31 KiB |
|
@ -1,28 +1,83 @@
|
|||
# Deploy Function App for getting Office 365 Management API data into Azure Sentinel
|
||||
This function app will poll O365 Activity Managment API every 5 mins for logs. It is designed to get Audit.General and DLP.All events.
|
||||
|
||||
## Deployment and Configuration
|
||||
### Add AAD App Permissions
|
||||
1. Go to Azure Active Directory / App Registrations
|
||||
2. Create +New Registration
|
||||
3. Call it "O365APItoAzureSentinel". Click Register.
|
||||
4. Click API Permissions Blade.
|
||||
5. Click Add a Permission.
|
||||
6. Click Office 365 Management APIs.
|
||||
7. Click Appplication Permissions
|
||||
8. Check all permissions for each category. Click Add permissions.
|
||||
9. Click grant admin consent for domain.com
|
||||
10. Click Certificates and Secrets
|
||||
11. Click New Client Secret
|
||||
12. Enter a description, select never. Click Add.
|
||||
13. IMPORTANT. Click copy next to the new secret and paste it somewhere temporaily. You can not come back to get the secret once you leave the blade.
|
||||
14. Copy the client Id from the application properties and paste it somewhere.
|
||||
15. Also copy the tenant Id from the AAD directory properties blade.
|
||||
## How to Ingest Office 365 Audit.General and DLP.All Activity Logs into Azure Sentinel
|
||||
The Office 365 data connector in Azure Sentinel supports ongoing user and admin activity logs for Microsoft 365 workloads, Exchange Online, SharePoint Online and Microsoft Teams. The activity logs include details of action such as file downloads, access request send, change to group event, mailbox operations. Once the activity logs are ingested into Azure Sentinel, it can be used for custom analytics rules, hunting, visualization as well as for investigation process.
|
||||
|
||||
### Create O365 API Subscription
|
||||
1. Open Powershell
|
||||
2. Run the following commands
|
||||
The Azure Sentinel data connector for Office 365 uses the [Office 365 Activity Management API](https://docs.microsoft.com/office/office-365-management-api/office-365-management-activity-api-reference). Below is a summary of which content types are part of the Office 365 Activity Management API and their mapping with Azure Sentinel.
|
||||
|
||||
|
||||
|
||||
| Content Type | Description | Azure Sentinel Mapping |
|
||||
| ------------ | ----------- | ---------------------- |
|
||||
| Audit.AzureActiveDirectory | Azure Active Directory logs that’s relates to Office 365 only | Supported with the default connector for [Office 365](https://docs.microsoft.com/azure/sentinel/connect-office-365) in Azure Sentinel |
|
||||
| Audit.Exchange | User and Admin Activities in Exchange Online | Supported with the default connector for [Office 365](https://docs.microsoft.com/azure/sentinel/connect-office-365) in Azure Sentinel |
|
||||
| Audit.SharePoint | User and Admin Activities in SharePoint Online | Supported with the default connector for [Office 365](https://docs.microsoft.com/azure/sentinel/connect-office-365) in Azure Sentinel |
|
||||
| Audit.General | Includes all other workloads not included in the previous content types | Not supported with the default connector for Office 365 in Azure Sentinel |
|
||||
| DLP.All | DLP events only for all workloads | Not supported with the default connector for Office 365 in Azure Sentinel |
|
||||
|
||||
Specifically, Audit.General activity logs could be of interest in SIEM if there is a need for correlation with alerts from Defender for Office 365 and alerts from Security and Compliance Center. As follow most asked use cases are:
|
||||
- Usage of Security and Compliance Center alerts
|
||||
- Alerts generated by Defender for Office 365
|
||||
- Safe Links time-of-block and block override
|
||||
- Phishing and malware alerts for files in SharePoint Online, OneDrive for Business, and Microsoft Teams
|
||||
- Usage of Phishing and malware events
|
||||
|
||||
This document covers the required steps to ingest Audit.General and DLP.All activity logs from the Office 365 Management Activity API into Azure Sentinel and how to use the ingested alerts. For the ingestion of activity logs I will use an Azure Function App connector. The Azure Function App is published [here](https://github.com/Azure/Azure-Sentinel/tree/master/DataConnectors/O365%20Data).
|
||||
|
||||
|
||||
The Azure Function App uses a PowerShell script to collect Office 365 Audit.General and DLP.All Activity logs and ingests into a custom table in Azure Sentinel (custom tables end with _CL when created in Log Analytics). The secrets for the required connections are stored in Azure Key Vault.
|
||||
|
||||
![Function App](./images/picture1.png)
|
||||
|
||||
Let’s get started with the configuration!
|
||||
|
||||
### Preparation
|
||||
The following tasks describe the necessary preparation and configurations steps.
|
||||
- Onboard Azure Sentinel
|
||||
- Register an application in Azure AD
|
||||
- Create an Office 365 Management Activity API Subscription
|
||||
- Deploy the Azure Function App
|
||||
- Post Configuration Steps for the Function App and Key Vault
|
||||
- How to Use the Activity Logs in Azure Sentinel
|
||||
|
||||
## Onboarding Azure Sentinel
|
||||
|
||||
Onboarding Azure Sentinel is not part of this document post. However, required guidance can be found [here](https://docs.microsoft.com/azure/sentinel/quickstart-onboard).
|
||||
|
||||
### Register an application in Azure AD
|
||||
The Azure AD app is later required to use it as service principle for the [Azure Funtion App](https://github.com/Azure/Azure-Sentinel/tree/master/DataConnectors/O365%20Data) app.
|
||||
|
||||
1. Go to **Azure Active Directory** / **App Registrations**
|
||||
2. Create **New Registration**
|
||||
![App Registration](./images/picture2.png)
|
||||
3. Call it "O365APItoAzureSentinel". Click **Register**.
|
||||
4. Click **API Permissions** Blade.
|
||||
5. Click **Add a Permission**.
|
||||
6. Click **Office 365 Management APIs**.
|
||||
7. Click **Appplication Permissions**
|
||||
8. Check **ActivityFeed.Read** and **ActivityFeed.ReadDlp**. Click **Add permissions**.
|
||||
![Permissions](./images/picture5.png)
|
||||
9. Click **Grant admin consent for ...**.
|
||||
![Admin Consent](./images/picture6.png)
|
||||
10. Click **Certificates and Secrets** blade.
|
||||
11. Click **New Client Secret**.
|
||||
12. Enter a description, select **never**. Click **Add**.
|
||||
![Secret](./images/picture3.png)
|
||||
13. **IMPORTANT**. Click **copy** next to the new secret and paste it somewhere temporaily. You can not come back to get the secret once you leave the blade.
|
||||
14. Copy the **client Id** from the application properties and paste it somewhere.
|
||||
15. Also copy the **tenant Id** from the AAD directory properties blade.
|
||||
|
||||
For the deployment of [Azure Funtion App](https://github.com/Azure/Azure-Sentinel/tree/master/DataConnectors/O365%20Data), make a note of following settings:
|
||||
- The Azure AD Application ID
|
||||
- The Azure AD Application Secret
|
||||
- The Tenant ID
|
||||
- The Tenant Domain
|
||||
|
||||
### Create an Office 365 Management Activity API Subscription
|
||||
After successfully creating the service principles, run the following PowerShell script to register the API subscription.
|
||||
1. Open a PowerShell terminal.
|
||||
2. Run the following, replacing variables with strings from the previous steps.
|
||||
```powerhshell
|
||||
$ClientID = "<GUID> from AAD App Registration"
|
||||
$ClientSecret = "<clientSecret> from AAD App Registrtion"
|
||||
|
@ -35,106 +90,78 @@ $oauth = Invoke-RestMethod -Method Post -Uri $loginURL/$tenantdomain/oauth2/toke
|
|||
$headerParams = @{'Authorization'="$($oauth.token_type) $($oauth.access_token)"}
|
||||
$publisher = "<randomGuid>" Get a guid from https://guidgenerator.com/
|
||||
```
|
||||
|
||||
* Run this command to enable Audit.General Subscription.
|
||||
3. Run this command to enable **Audit.General** Subscription.
|
||||
```powershell
|
||||
Invoke-WebRequest -Method Post -Headers $headerParams -Uri "https://manage.office.com/api/v1.0/$tenantGuid/activity/feed/subscriptions/start?contentType=Audit.General&PublisherIdentifier=$Publisher"
|
||||
```
|
||||
* Run this command to enable DLP.ALL subscription
|
||||
4. Run this command to enable **DLP.ALL** subscription
|
||||
```powershell
|
||||
Invoke-WebRequest -Method Post -Headers $headerParams -Uri "https://manage.office.com/api/v1.0/$tenantGuid/activity/feed/subscriptions/start?contentType=DLP.ALL&PublisherIdentifier=$Publisher"
|
||||
```
|
||||
5. A successful output looks like as below.
|
||||
![Output](./images/picture7.png)
|
||||
|
||||
### Deploy the Function App
|
||||
There are 2 deployment Options.
|
||||
|
||||
#### 1: Deploy via Azure ARM Template
|
||||
1. Deploy the template.
|
||||
### Deploy the Azure Function App
|
||||
Thanks to the published ARM template the deployment of the [Azure Funtion App](https://github.com/Azure/Azure-Sentinel/tree/master/DataConnectors/O365%20Data) is done with just a few clicks.
|
||||
1. Click to **Deploy the template / Deploy to Azure** below.
|
||||
|
||||
<a href="https://portal.azure.com/#create/Microsoft.Template/uri/https%3A%2F%2Fraw.githubusercontent.com%2FAzure%2FAzure-Sentinel%2Fmaster%2FDataConnectors%2FO365%20Data%2Fazuredeploy.json" target="_blank">
|
||||
<img src="https://aka.ms/deploytoazurebutton""/>
|
||||
</a>
|
||||
|
||||
2. Deploy permissions for the function to the Key Vault.
|
||||
2. Now it is time to use the noted details from previous steps.
|
||||
- Select the right **Subscription**, **Resource Group** and **Region** where you what to deploy the Azure Funtion App.
|
||||
- Fill the Instance Details **Client ID**, **Client Secret**, **Tenant Domain**, **Publisher Guid**.
|
||||
- There is also a need of **Workspace ID** and **Workspace Key** from where Azure Sentinel is deployed.
|
||||
- The Content Types you can leave as default with **Audit.General**, or you can also add **DLP.All** as well. Or use only **DLP.All**.
|
||||
![Deployment](./images/picture9.png)
|
||||
3. Click to **Review + create**, review the configuration and click **Create**.
|
||||
4. Now the deployment of ARM template is completed.
|
||||
![Complete](./images/picture10.png)
|
||||
|
||||
### Post Configuration Steps for the Azure Key Vault
|
||||
The used credentials, Client Secret and Workspace Key within the Function App is secured in Azure Key Vault, in final stage and operations. Follow the configuration steps to finalize the post configurations steps for the Key Vault.
|
||||
1. Click to **Deploy the template / Deploy to Azure** below.
|
||||
|
||||
<a href="https://portal.azure.com/#create/Microsoft.Template/uri/https%3A%2F%2Fraw.githubusercontent.com%2FAzure%2FAzure-Sentinel%2Fmaster%2FDataConnectors%2FO365%20Data%2Fazuredeploy2.json" target="_blank">
|
||||
<img src="https://aka.ms/deploytoazurebutton""/>
|
||||
</a>
|
||||
|
||||
#### 1: Deploy via VS Code
|
||||
Note: You will need to prepare VS code for Azure function development. See https://docs.microsoft.com/en-us/azure/azure-functions/functions-create-first-function-powershell#prerequisites
|
||||
1. Download the [Zip](https://github.com/Azure/Azure-Sentinel/blob/master/DataConnectors/O365%20Data/O365APItoAS-Template.zip?raw=true) file of the Azure Funciton app from Github.
|
||||
2. Extract to location on your machine.
|
||||
3. Open VS Code
|
||||
4. Click File -> Open Folder
|
||||
5. Select the Top folder from extracted files.
|
||||
6. Type Crtl+Shift+P.
|
||||
7. Click Azure Functions: Deploy to function app. You maybe asked to sign in to azure.
|
||||
8. Click Create New function app in Azure (advanced)
|
||||
9. Provide a unique name like "O365APItoAS". Press Enter
|
||||
10. Click Windows
|
||||
11. Click Consumption
|
||||
12. Click PowerShell
|
||||
13. Click Create new Resource Group
|
||||
14. Press enter to accept the name
|
||||
15. Click Create a new storage Account
|
||||
16. Press enter to accept the name
|
||||
17. Click Create new Application Insights resource
|
||||
18. Press enter to accept the name
|
||||
19. Pick a location
|
||||
20. Deployment will begin.
|
||||
21. Wait for the deployment to complete, then click upload settings in the bottom right
|
||||
22. Click yes to all to upload.
|
||||
23. Go to the Azure Portal.
|
||||
24. Go to the resource group that was created. Click the Function.
|
||||
25. Click Stop.
|
||||
26. Click Platform Features Tab.
|
||||
27. Click Identity
|
||||
28. Click On under system assigned. Click Save. Click Yes.
|
||||
2. Select the right **Subscription**, **Resource Group** and fill the **Function App Name**. Click to **Review + create**, review the configuration and click **Create**.
|
||||
![Deployment](./images/picture12.png)
|
||||
3. Once the configuration is done, review the configuration.
|
||||
4. To validate the configuration open the **Azure Portal** and navigate to **Azure Key Vault** > The name of the Function App > **Access Policy**. In the **Access Policy** blade, you should see the Function App name **O365APItoAS** as an application and with required access policies to work with the Azure Key Vault.
|
||||
![Policy](./images/picture14.png)
|
||||
5. From the Access policies page review the generated Secrets by the ARM template.
|
||||
6. And now make a note of both Secret Identifiers later for the Azure Function App post configuration.
|
||||
![Secrets](./images/picture15.png)
|
||||
|
||||
### Create a Key Vault
|
||||
1. Go to the Azure Portal.
|
||||
2. Go to the resource group that was created. Click Add.
|
||||
3. Type Key Vault.
|
||||
4. Create a Key vault.
|
||||
5. Go to the resource created.
|
||||
6. Click Access Policies.
|
||||
7. Click Add Access Policy
|
||||
8. Select Secret Management from Configure from template
|
||||
9. Click Select Principal
|
||||
10. Search for the name of the function app. Click Select.
|
||||
11. Click Add.
|
||||
12. Click Save
|
||||
13. Click Secrets
|
||||
14. Click Generate
|
||||
15. Enter O365Tenant1_clientsecret. Paste the AAD app secret. Click Create.
|
||||
16. Click Generate
|
||||
17. Enter O365workspaceKey. Paste the Azure Sentinel Workspace Key. Click Create.
|
||||
18. Click O365clientsecret and copy the current version string to a temp location.
|
||||
19. Click O365workspaceKey and copy the current version stringto a temp location.
|
||||
20. Go to the Overiew blade. Copy the DNS Name to a temp location.
|
||||
Note: you will need to create additional keys if you have multiple tenants.
|
||||
### Post Configuration Steps for the Azure Function App
|
||||
1. For the final configuration of Azure Function App open the Azure Portal and navigate to **Azure Function App** > The name of the Function App > **Configuration**.
|
||||
![configuration](./images/picture17.png)
|
||||
2. In the **Configuration** edit the **clientSecret** and **workspaceKey** settings. Click to Edit and paste the noted Secret Identifiers as value and **Save** the configuration. The Secret Identifiers should have this format:
|
||||
- @Microsoft.KeyVault(SecretUri=https:///secrets/O365Tenant1_clientSecret/).
|
||||
3. Once the configuration is finished the **clientSecret** and **workspaceKey** settings should have a green checkmark.
|
||||
|
||||
### Confiugure Settings for the Function
|
||||
1. Go to the Azure Portal.
|
||||
2. Go to the resource group that was created. Click the Function.
|
||||
3. Click Platform Features Tab.
|
||||
4. Click Configuration under General.
|
||||
5. click edit next to clientSecret.
|
||||
6. Update the value using your copied properties.
|
||||
* @Microsoft.KeyVault(SecretUri=https://<dnsname>/secrets/O365Tenant1_clientSecret/<versionstring>)
|
||||
7. Click Ok.
|
||||
8. click edit next to workspaceKey.
|
||||
9. Update the value using your copied properties
|
||||
* @Microsoft.KeyVault(SecretUri=https://<dnsname>/secrets/O365workspacekey/<versionstring>)
|
||||
10. Click Ok.
|
||||
11. Update each setting
|
||||
* clientID = AAD app registration id
|
||||
* contentTypes = Audit.General or Audit.General,DLP.All or DLP.All
|
||||
* domain = <domain> from <domain>.onmicrosoft.com
|
||||
* publisher is a random guid for throttling that we used in steps to create subscription.
|
||||
* recordTypes This can be 0 or a list of record types comma seperated like 28,40,41 (see https://docs.microsoft.com/en-us/office/office-365-management-api/office-365-management-activity-api-schema#auditlogrecordtype)
|
||||
* tenantGuid is your AAD tenant guid.
|
||||
* workspaceId is your Azure Sentinel workspace id
|
||||
12. Click Save
|
||||
13. Go back to the function and click start under the overview blade.
|
||||
## How to use the Activity Logs in Azure Sentinel
|
||||
Once the Azure Function App is functional you can query the General.Audit and DLP.All activity logs. The activity will reside in a Custom Table as configured in the Azure Function App above. The following table includes sample Kusto Language Queries (KQL). You can see these are using the Custom Logs (Custom log tables always end in “_CL”) and the values we mentioned earlier.
|
||||
![Review](./images/picture19.png)
|
||||
|
||||
***Note***: Custom Logs are a billable data source. The record types that are important have been added below, as simple starting queries.
|
||||
|
||||
| Member Name | Kusto Language Query (KQL) |
|
||||
| ----------- | -------------------------- |
|
||||
| ThreatIntelligence | O365_CL \| where RecordType_d == "28" |
|
||||
| ThreatIntelligenceUrl | O365_CL \| where RecordType_d == "41" |
|
||||
| ThreatIntelligenceAtpContent | O365_CL \| where RecordType_d == "47" |
|
||||
| SecurityComplianceAlerts | O365_CL \| where RecordType_d == "40" |
|
||||
|
||||
An example results for the Defender for Office Safe Attachment block detection alert.
|
||||
```KQL
|
||||
O365_CL
|
||||
| where RecordType_d == "28"
|
||||
```
|
||||
![Query](./images/picture20.png)
|
||||
### Summary
|
||||
In this document I have shown how you can onboard Office 365 Management Activity API General.Audit and DLP.All activity logs, and some basics queries for you to start to build out your use cases with Defender for Office and Security and Compliance Center alerts. This solution helps you extend, correlate and enrich the data you have with the existing O365 connector, giving you more insights.
|
||||
|
|
@ -35,3 +35,4 @@ entityMappings:
|
|||
fieldMappings:
|
||||
- identifier: Address
|
||||
columnName: IPCustomEntity
|
||||
version: 1.0.0
|
|
@ -37,3 +37,4 @@ entityMappings:
|
|||
fieldMappings:
|
||||
- identifier: Address
|
||||
columnName: IPCustomEntity
|
||||
version: 1.0.0
|
|
@ -36,3 +36,4 @@ entityMappings:
|
|||
fieldMappings:
|
||||
- identifier: Address
|
||||
columnName: IPCustomEntity
|
||||
version: 1.0.0
|
|
@ -39,3 +39,4 @@ entityMappings:
|
|||
fieldMappings:
|
||||
- identifier: Address
|
||||
columnName: IPCustomEntity
|
||||
version: 1.0.0
|
|
@ -36,3 +36,4 @@ entityMappings:
|
|||
fieldMappings:
|
||||
- identifier: Address
|
||||
columnName: IPCustomEntity
|
||||
version: 1.0.0
|
|
@ -69,3 +69,4 @@ entityMappings:
|
|||
fieldMappings:
|
||||
- identifier: Address
|
||||
columnName: IPCustomEntity
|
||||
version: 1.0.0
|
|
@ -36,3 +36,4 @@ entityMappings:
|
|||
fieldMappings:
|
||||
- identifier: Address
|
||||
columnName: IPCustomEntity
|
||||
version: 1.0.0
|
|
@ -37,3 +37,4 @@ entityMappings:
|
|||
fieldMappings:
|
||||
- identifier: Address
|
||||
columnName: IPCustomEntity
|
||||
version: 1.0.0
|
|
@ -26,4 +26,5 @@ query: |
|
|||
afad_parser
|
||||
| where MessageType == 0 and Codename in~ (codeNameList)
|
||||
| lookup kind=leftouter SeverityTable on Severity
|
||||
| order by Level
|
||||
| order by Level
|
||||
version: 1.0.0
|
|
@ -18,3 +18,4 @@ relevantTechniques:
|
|||
query: |
|
||||
afad_parser
|
||||
| where MessageType == 2 and Codename == "DCShadow"
|
||||
version: 1.0.0
|
|
@ -18,3 +18,4 @@ relevantTechniques:
|
|||
query: |
|
||||
afad_parser
|
||||
| where MessageType == 2 and Codename == "DCSync"
|
||||
version: 1.0.0
|
|
@ -18,3 +18,4 @@ relevantTechniques:
|
|||
query: |
|
||||
afad_parser
|
||||
| where MessageType == 2 and Codename == "Golden Ticket"
|
||||
version: 1.0.0
|
|
@ -26,3 +26,4 @@ query: |
|
|||
| where MessageType == 2
|
||||
| lookup kind=leftouter SeverityTable on Severity
|
||||
| order by Level
|
||||
version: 1.0.0
|
|
@ -25,4 +25,5 @@ query: |
|
|||
afad_parser
|
||||
| where MessageType == 0
|
||||
| lookup kind=leftouter SeverityTable on Severity
|
||||
| order by Level
|
||||
| order by Level
|
||||
version: 1.0.0
|
|
@ -18,3 +18,4 @@ relevantTechniques:
|
|||
query: |
|
||||
afad_parser
|
||||
| where MessageType == 2 and Codename == "OS Credential Dumping: LSASS Memory"
|
||||
version: 1.0.0
|
|
@ -18,3 +18,4 @@ relevantTechniques:
|
|||
query: |
|
||||
afad_parser
|
||||
| where MessageType == 2 and Codename == "Password Guessing"
|
||||
version: 1.0.0
|
|
@ -26,4 +26,5 @@ query: |
|
|||
afad_parser
|
||||
| where MessageType == 0 and Codename in~ (codeNameList)
|
||||
| lookup kind=leftouter SeverityTable on Severity
|
||||
| order by Level
|
||||
| order by Level
|
||||
version: 1.0.0
|
|
@ -18,3 +18,4 @@ relevantTechniques:
|
|||
query: |
|
||||
afad_parser
|
||||
| where MessageType == 2 and Codename == "Password Spraying"
|
||||
version: 1.0.0
|
|
@ -26,4 +26,5 @@ query: |
|
|||
afad_parser
|
||||
| where MessageType == 0 and Codename in~ (codeNameList)
|
||||
| lookup kind=leftouter SeverityTable on Severity
|
||||
| order by Level
|
||||
| order by Level
|
||||
version: 1.0.0
|
|
@ -26,4 +26,5 @@ query: |
|
|||
afad_parser
|
||||
| where MessageType == 0 and Codename in~ (codeNameList)
|
||||
| lookup kind=leftouter SeverityTable on Severity
|
||||
| order by Level
|
||||
| order by Level
|
||||
version: 1.0.0
|
|
@ -57,3 +57,4 @@ entityMappings:
|
|||
fieldMappings:
|
||||
- identifier: Address
|
||||
columnName: IPCustomEntity
|
||||
version: 1.0.0
|
|
@ -65,4 +65,5 @@ entityMappings:
|
|||
- entityType: IP
|
||||
fieldMappings:
|
||||
- identifier: Address
|
||||
columnName: IPCustomEntity
|
||||
columnName: IPCustomEntity
|
||||
version: 1.0.0
|
|
@ -55,3 +55,4 @@ entityMappings:
|
|||
fieldMappings:
|
||||
- identifier: Address
|
||||
columnName: IPCustomEntity
|
||||
version: 1.0.0
|
|
@ -53,4 +53,5 @@ entityMappings:
|
|||
- entityType: IP
|
||||
fieldMappings:
|
||||
- identifier: Address
|
||||
columnName: IPCustomEntity
|
||||
columnName: IPCustomEntity
|
||||
version: 1.0.0
|
|
@ -69,4 +69,5 @@ entityMappings:
|
|||
- entityType: IP
|
||||
fieldMappings:
|
||||
- identifier: Address
|
||||
columnName: IPCustomEntity
|
||||
columnName: IPCustomEntity
|
||||
version: 1.0.0
|
|
@ -69,4 +69,5 @@ entityMappings:
|
|||
- entityType: IP
|
||||
fieldMappings:
|
||||
- identifier: Address
|
||||
columnName: IPCustomEntity
|
||||
columnName: IPCustomEntity
|
||||
version: 1.0.0
|
|
@ -56,4 +56,5 @@ entityMappings:
|
|||
- entityType: IP
|
||||
fieldMappings:
|
||||
- identifier: Address
|
||||
columnName: IPCustomEntity
|
||||
columnName: IPCustomEntity
|
||||
version: 1.0.0
|
|
@ -73,3 +73,4 @@ entityMappings:
|
|||
fieldMappings:
|
||||
- identifier: Address
|
||||
columnName: IPCustomEntity
|
||||
version: 1.0.0
|
|
@ -68,3 +68,4 @@ entityMappings:
|
|||
fieldMappings:
|
||||
- identifier: Address
|
||||
columnName: IPCustomEntity
|
||||
version: 1.0.0
|
|
@ -52,3 +52,4 @@ entityMappings:
|
|||
fieldMappings:
|
||||
- identifier: FullName
|
||||
columnName: AccountCustomEntity
|
||||
version: 1.0.0
|
|
@ -60,3 +60,4 @@ entityMappings:
|
|||
fieldMappings:
|
||||
- identifier: Address
|
||||
columnName: IPCustomEntity
|
||||
version: 1.0.0
|
|
@ -42,3 +42,4 @@ entityMappings:
|
|||
fieldMappings:
|
||||
- identifier: Address
|
||||
columnName: IPCustomEntity
|
||||
version: 1.0.0
|
|
@ -45,3 +45,4 @@ entityMappings:
|
|||
fieldMappings:
|
||||
- identifier: Address
|
||||
columnName: IPCustomEntity
|
||||
version: 1.0.0
|
|
@ -33,4 +33,5 @@ entityMappings:
|
|||
- entityType: IP
|
||||
fieldMappings:
|
||||
- identifier: Address
|
||||
columnName: IPCustomEntity
|
||||
columnName: IPCustomEntity
|
||||
version: 1.0.0
|
|
@ -46,3 +46,4 @@ entityMappings:
|
|||
fieldMappings:
|
||||
- identifier: Address
|
||||
columnName: IPCustomEntity
|
||||
version: 1.0.0
|
|
@ -51,3 +51,4 @@ entityMappings:
|
|||
fieldMappings:
|
||||
- identifier: Address
|
||||
columnName: IPCustomEntity
|
||||
version: 1.0.0
|
|
@ -19,3 +19,4 @@ entityMappings:
|
|||
fieldMappings:
|
||||
- identifier: FullName
|
||||
columnName: HostCustomEntity
|
||||
version: 1.0.0
|
|
@ -19,3 +19,4 @@ entityMappings:
|
|||
fieldMappings:
|
||||
- identifier: FullName
|
||||
columnName: HostCustomEntity
|
||||
version: 1.0.0
|
|
@ -44,4 +44,5 @@ entityMappings:
|
|||
- entityType: IP
|
||||
fieldMappings:
|
||||
- identifier: Address
|
||||
columnName: IPCustomEntity
|
||||
columnName: IPCustomEntity
|
||||
version: 1.0.0
|
|
@ -26,4 +26,5 @@ entityMappings:
|
|||
- entityType: IP
|
||||
fieldMappings:
|
||||
- identifier: Address
|
||||
columnName: IPCustomEntity
|
||||
columnName: IPCustomEntity
|
||||
version: 1.0.0
|
|
@ -29,4 +29,5 @@ entityMappings:
|
|||
- entityType: IP
|
||||
fieldMappings:
|
||||
- identifier: Address
|
||||
columnName: IPCustomEntity
|
||||
columnName: IPCustomEntity
|
||||
version: 1.0.0
|
|
@ -26,4 +26,5 @@ entityMappings:
|
|||
- entityType: IP
|
||||
fieldMappings:
|
||||
- identifier: Address
|
||||
columnName: IPCustomEntity
|
||||
columnName: IPCustomEntity
|
||||
version: 1.0.0
|
|
@ -53,4 +53,5 @@ entityMappings:
|
|||
- entityType: IP
|
||||
fieldMappings:
|
||||
- identifier: Address
|
||||
columnName: IPCustomEntity
|
||||
columnName: IPCustomEntity
|
||||
version: 1.0.0
|
|
@ -27,4 +27,5 @@ entityMappings:
|
|||
- entityType: IP
|
||||
fieldMappings:
|
||||
- identifier: Address
|
||||
columnName: IPCustomEntity
|
||||
columnName: IPCustomEntity
|
||||
version: 1.0.0
|
|
@ -33,3 +33,4 @@ entityMappings:
|
|||
fieldMappings:
|
||||
- identifier: Address
|
||||
columnName: IPCustomEntity
|
||||
version: 1.0.0
|
|
@ -41,4 +41,5 @@ entityMappings:
|
|||
- entityType: IP
|
||||
fieldMappings:
|
||||
- identifier: Address
|
||||
columnName: IPCustomEntity
|
||||
columnName: IPCustomEntity
|
||||
version: 1.0.0
|
|
@ -38,3 +38,4 @@ entityMappings:
|
|||
fieldMappings:
|
||||
- identifier: Address
|
||||
columnName: IPCustomEntity
|
||||
version: 1.0.0
|
|
@ -39,3 +39,4 @@ entityMappings:
|
|||
fieldMappings:
|
||||
- identifier: Address
|
||||
columnName: IPCustomEntity
|
||||
version: 1.0.0
|
|
@ -58,3 +58,4 @@ entityMappings:
|
|||
fieldMappings:
|
||||
- identifier: FullName
|
||||
columnName: AccountCustomEntity
|
||||
version: 1.0.0
|
|
@ -41,3 +41,4 @@ entityMappings:
|
|||
fieldMappings:
|
||||
- identifier: Address
|
||||
columnName: IPCustomEntity
|
||||
version: 1.0.0
|
|
@ -56,3 +56,4 @@ entityMappings:
|
|||
fieldMappings:
|
||||
- identifier: Address
|
||||
columnName: DeletingIP
|
||||
version: 1.0.0
|
|
@ -37,4 +37,5 @@ query: |
|
|||
| extend link = iif(
|
||||
Type == "Build", strcat('https://dev.azure.com/', OrganizationName, '/', ProjectName, '/_build?definitionId=', DefId),
|
||||
strcat('https://dev.azure.com/', OrganizationName, '/', ProjectName, '/_release?_a=releases&view=mine&definitionId=', DefId))
|
||||
| extend timestamp = StartTime
|
||||
| extend timestamp = StartTime
|
||||
version: 1.0.0
|
|
@ -43,4 +43,5 @@ entityMappings:
|
|||
- entityType: IP
|
||||
fieldMappings:
|
||||
- identifier: Address
|
||||
columnName: IPCustomEntity
|
||||
columnName: IPCustomEntity
|
||||
version: 1.0.0
|
|
@ -60,4 +60,5 @@ entityMappings:
|
|||
- entityType: IP
|
||||
fieldMappings:
|
||||
- identifier: Address
|
||||
columnName: IPCustomEntity
|
||||
columnName: IPCustomEntity
|
||||
version: 1.0.0
|
|
@ -39,3 +39,4 @@ entityMappings:
|
|||
fieldMappings:
|
||||
- identifier: Address
|
||||
columnName: IPCustomEntity
|
||||
version: 1.0.0
|
|
@ -40,3 +40,4 @@ entityMappings:
|
|||
fieldMappings:
|
||||
- identifier: Address
|
||||
columnName: IPCustomEntity
|
||||
version: 1.0.0
|
|
@ -52,3 +52,4 @@ entityMappings:
|
|||
fieldMappings:
|
||||
- identifier: Address
|
||||
columnName: IPCustomEntity
|
||||
version: 1.0.0
|
|
@ -56,3 +56,4 @@ entityMappings:
|
|||
fieldMappings:
|
||||
- identifier: Address
|
||||
columnName: IPCustomEntity
|
||||
version: 1.0.0
|
|
@ -73,3 +73,4 @@ entityMappings:
|
|||
fieldMappings:
|
||||
- identifier: Address
|
||||
columnName: IPCustomEntity
|
||||
version: 1.0.0
|
|
@ -43,3 +43,4 @@ entityMappings:
|
|||
fieldMappings:
|
||||
- identifier: Url
|
||||
columnName: URLCustomEntity
|
||||
version: 1.0.0
|