Merge branch 'master' into shaharBranch2

This commit is contained in:
t-shaviv 2021-05-19 10:12:21 +03:00
Родитель 4741982a7c 6263e88786
Коммит 0c6c4fecb2
373 изменённых файлов: 16143 добавлений и 1669 удалений

Просмотреть файл

@ -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

Двоичный файл не отображается.

Просмотреть файл

@ -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

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Двоичные данные
DataConnectors/O365 Data/images/Picture1.png Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 30 KiB

Двоичные данные
DataConnectors/O365 Data/images/Picture10.png Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 34 KiB

Двоичные данные
DataConnectors/O365 Data/images/Picture11.png Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 7.7 KiB

Двоичные данные
DataConnectors/O365 Data/images/Picture12.png Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 25 KiB

Двоичные данные
DataConnectors/O365 Data/images/Picture13.png Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 34 KiB

Двоичные данные
DataConnectors/O365 Data/images/Picture14.png Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 41 KiB

Двоичные данные
DataConnectors/O365 Data/images/Picture15.png Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 24 KiB

Двоичные данные
DataConnectors/O365 Data/images/Picture16.png Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 27 KiB

Двоичные данные
DataConnectors/O365 Data/images/Picture17.png Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 66 KiB

Двоичные данные
DataConnectors/O365 Data/images/Picture18.png Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 61 KiB

Двоичные данные
DataConnectors/O365 Data/images/Picture19.png Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 56 KiB

Двоичные данные
DataConnectors/O365 Data/images/Picture2.png Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 30 KiB

Двоичные данные
DataConnectors/O365 Data/images/Picture20.png Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 51 KiB

Двоичные данные
DataConnectors/O365 Data/images/Picture3.png Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 34 KiB

Двоичные данные
DataConnectors/O365 Data/images/Picture4.png Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 34 KiB

Двоичные данные
DataConnectors/O365 Data/images/Picture5.png Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 42 KiB

Двоичные данные
DataConnectors/O365 Data/images/Picture6.png Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 41 KiB

Двоичные данные
DataConnectors/O365 Data/images/Picture7.png Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 76 KiB

Двоичные данные
DataConnectors/O365 Data/images/Picture8.png Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 56 KiB

Двоичные данные
DataConnectors/O365 Data/images/Picture9.png Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 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 thats 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)
Lets 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

Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше