This commit is contained in:
Alex Anders 2024-02-19 10:57:36 -07:00
Родитель 6cdf0659ac
Коммит 176ab4c510
33 изменённых файлов: 2130 добавлений и 5812 удалений

Различия файлов скрыты, потому что одна или несколько строк слишком длинны

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

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

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

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

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

@ -2,7 +2,7 @@
param($Timer)
#Define global variables/parameters.
$ErrorActionPreference = 'Stop'
#$ErrorActionPreference = 'Stop'
$lawResourceId = $env:LawResourceId
$dcrImmutableId = $env:DcrImmutableId
$dceUri = $env:DceUri
@ -100,13 +100,13 @@ function Import-Data {
$totalObjectsReceived += $objectsReceived
#Send received data to Azure Monitor.
Send-DataToAzureMonitor -Data $content -BatchSize $BatchSize -TableName "Custom-$Table" -JsonDepth $JsonDepth
Send-AzMonData -LogsIngestionClient $azMonLogsIngestionClient -Data $content -BatchSize $BatchSize -TableName "Custom-$Table" -JsonDepth $JsonDepth -DcrImmutableId $dcrImmutableId
#If there was configuration data included in NIST data, send that to seperate table in Azure Monitor.
if ($configurations.Count -ne 0) {
$configurationObjectsReceived = $configurations.Count
$totalConfigurationObjectsReceived += $configurationObjectsReceived
Write-Host "Sending NIST Configurations KB..."
Send-DataToAzureMonitor -Data $configurations -BatchSize 30000 -TableName "Custom-MDVMNISTConfigurations_CL" -JsonDepth $JsonDepth
Send-AzMonData -LogsIngestionClient $azMonLogsIngestionClient -Data $configurations -BatchSize 30000 -TableName "Custom-MDVMNISTConfigurations_CL" -JsonDepth $JsonDepth -DcrImmutableId $dcrImmutableId
}
else {
$configurationObjectsReceived = 0
@ -124,29 +124,12 @@ function Import-Data {
if ($null -eq $SourceUri) { $loopDone = $true }
}
#Check the status of the async Azure Monitor ingestion jobs and write error if there are any unsuccessful jobs.
Get-FailedJobs -Jobs $azMonJobs
#Check how much time has elapsed since the last source API request and sleep if we are exceeding the API throttling/delay limits.
$timeDiff = New-TimeSpan -Start $startTime -End (Get-Date)
if ( $timeDiff.Milliseconds -lt $DelayTime -and $loopDone -ne $true) { Start-Sleep -Milliseconds ($DelayTime - $timeDiff.Milliseconds) }
$count += 1
} until ($loopDone -eq $true)
#After all source API data has been processed, wait for any pending/running Azure Monitor ingestion jobs to complete.
$pendingAzMonJobs = $azMonJobs | Where-Object { ($_.IsCompleted -eq $false) -or ($_.IsCompletedSuccessfully -eq $false) }
while ($pendingAzMonJobs.Count -ne 0) {
Start-Sleep -Seconds 1
$secondsSpent += 1
Get-FailedJobs -Jobs $pendingAzMonJobs
$pendingAzMonJobs = $azMonJobs | Where-Object { ($_.IsCompleted -eq $false) -or ($_.IsCompletedSuccessfully -eq $false) }
if ($secondsSpent -eq 120) {
Write-Error ("Azure Monitor async jobs have not completed after 2 minutes:" + ($pendingAzMonJobs | Where-Object { ($_.IsCompleted -eq $false) -or ($_.IsCompletedSuccessfully -eq $false) } | Select-Object Id, IsCompleted, Status, Exception | Format-Table | Out-String)) -ErrorAction Continue
break
}
}
$azMonJobs.Clear()
#Update total objects received metrics so we can compare later to total number of objects written to Azure Monitor and check for any mismatches.
if ($tableStats | Where-Object TableName -eq $Table) {
@ -166,19 +149,6 @@ function Import-Data {
}
}
#Function to split data into specified batch sizes (so we do not exceed the maximum body size) and send to Azure Monitor.
function Send-DataToAzureMonitor {
param ($Data, $BatchSize, $TableName, $JsonDepth)
$skip = 0
do {
$batchedData = $Data | Select-Object -Skip $skip | Select-Object -First $BatchSize
$azMonJobs.Add($logIngestionClient.UploadAsync($dcrImmutableId, $TableName, ($batchedData | ConvertTo-Json -Depth $JsonDepth -AsArray))) | Out-Null
$skip += $BatchSize
} until (
$skip -ge $Data.Count
)
}
#Function to lookup and add Azure Resource ID based on MDVM device name.
function Add-AzureResourceId {
param($Data, $AzureResources)
@ -194,34 +164,15 @@ function Add-AzureResourceId {
return $Data
}
#Function to check the status of the async Azure Monitor ingestion jobs and write error if there are any unsuccessful jobs.
function Get-FailedJobs {
param ($Jobs)
$failedJobs = $Jobs | Where-Object { ($_.IsCompleted -eq $true) -and ($_.IsCompletedSuccessfully -eq $false) }
if ($failedJobs) {
foreach ($job in ($failedJobs)) {
Write-Error ("Error on Azure Monitor async job ID: " + $job.Id + ". Error Details: " + $job.Exception.message) -ErrorAction Continue
$azMonJobs.Remove($job)
}
}
}
#Add required .Net assemblies to handle the Azure Monitor ingestion.
Add-Type -Path .\GetMDVMData\libs\Azure.Monitor.Ingestion.dll
Add-Type -Path .\GetMDVMData\libs\Azure.Identity.dll
#Connect Azure Powershell via User Assigned Managed Identity.
Connect-AzAccount -Identity -AccountId $uamiClientID -Subscription $lawResourceId.Split('/')[2] | Out-Null
#Create Azure.Identity credential via User Assigned Managed Identity.
$credential = New-Object Azure.Identity.ManagedIdentityCredential($uamiClientId)
#Create LogsIngestionClient to handle sending data to Azure Monitor.
$logIngestionClient = New-Object Azure.Monitor.Ingestion.LogsIngestionClient($dceURI, $credential)
#Initiate objects needed to send data to Azure Monitor.
$azMonCredential = Get-AzMonCredential -UamiClientId $uamiClientId
$azMonLogsIngestionClient = Get-AzMonLogsIngestionClient -DceUri $dceUri -AzMonCredential $azMonCredential
#Create array to hold object counts for each data source so we can compare to total records written later.
$tableStats = New-Object System.Collections.ArrayList
#Create array to hold Azure Monitor jobs status.
$azMonJobs = New-Object System.Collections.ArrayList
#Get Log Analytics workspace Id to be used later when querying data in the workspace.
$lawId = (Get-AzOperationalInsightsWorkspace -ResourceGroupName $lawResourceId.Split('/')[4] -Name $lawResourceId.Split('/')[8]).CustomerId

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

@ -0,0 +1,123 @@
@{
# Script module or binary module file associated with this manifest.
RootModule = 'AzMon.Ingestion'
# Version number of this module.
ModuleVersion = '0.0.2'
# Supported PSEditions
# CompatiblePSEditions = @()
# ID used to uniquely identify this module
GUID = '7d5541c1-76cb-425d-9b68-24167c1087b2'
# Author of this module
#Author = ''
# Company or vendor of this module
#CompanyName = ''
# Copyright statement for this module
#Copyright = ''
# Description of the functionality provided by this module
# Description = ''
# Minimum version of the PowerShell engine required by this module
# PowerShellVersion = ''
# Name of the PowerShell host required by this module
# PowerShellHostName = ''
# Minimum version of the PowerShell host required by this module
# PowerShellHostVersion = ''
# Minimum version of Microsoft .NET Framework required by this module. This prerequisite is valid for the PowerShell Desktop edition only.
# DotNetFrameworkVersion = ''
# Minimum version of the common language runtime (CLR) required by this module. This prerequisite is valid for the PowerShell Desktop edition only.
# CLRVersion = ''
# Processor architecture (None, X86, Amd64) required by this module
# ProcessorArchitecture = ''
# Modules that must be imported into the global environment prior to importing this module
# RequiredModules = @()
# Assemblies that must be loaded prior to importing this module
RequiredAssemblies = @("Azure.Monitor.Ingestion.dll", "Azure.Identity.dll")
# Script files (.ps1) that are run in the caller's environment prior to importing this module.
# ScriptsToProcess = @()
# Type files (.ps1xml) to be loaded when importing this module
# TypesToProcess = @()
# Format files (.ps1xml) to be loaded when importing this module
# FormatsToProcess = @()
# Modules to import as nested modules of the module specified in RootModule/ModuleToProcess
# NestedModules = @()
# Functions to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no functions to export.
FunctionsToExport = @("Get-AzMonCredential", "Get-AzMonLogsIngestionClient", "Send-AzMonData")
# Cmdlets to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no cmdlets to export.
CmdletsToExport = @()
# Variables to export from this module
VariablesToExport = ''
# Aliases to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no aliases to export.
AliasesToExport = @()
# DSC resources to export from this module
# DscResourcesToExport = @()
# List of all modules packaged with this module
# ModuleList = @()
# List of all files packaged with this module
# FileList = @()
# Private data to pass to the module specified in RootModule/ModuleToProcess. This may also contain a PSData hashtable with additional module metadata used by PowerShell.
PrivateData = @{
PSData = @{
# Tags applied to this module. These help with module discovery in online galleries.
# Tags = @()
# A URL to the license for this module.
# LicenseUri = ''
# A URL to the main website for this project.
# ProjectUri = ''
# A URL to an icon representing this module.
# IconUri = ''
# ReleaseNotes of this module
# ReleaseNotes = ''
# Prerelease string of this module
# Prerelease = ''
# Flag to indicate whether the module requires explicit user acceptance for install/update/save
RequireLicenseAcceptance = $false
# External dependent modules of this module
# ExternalModuleDependencies = @()
} # End of PSData hashtable
} # End of PrivateData hashtable
# HelpInfo URI of this module
# HelpInfoURI = ''
# Default prefix for commands exported from this module. Override the default prefix using Import-Module -Prefix.
# DefaultCommandPrefix = ''
}

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

@ -0,0 +1,157 @@
<#
.Synopsis
Gets Entra ID credential to authenticate to Azure Monitor.
.Description
Gets Entra ID credential to authenticate to Azure Monitor. Leverages the Azure.Identity.ManagedIdentityCredential .Net class.
.Parameter UamiClientId
The user-assigned managed identity client ID that will be used to authenticate to Azure Monitor.
.Example
# Send an array of objects to Azure Monitor Logs.
Get-AzMonCredential -UamiClientId [User-Assigned Managed Identity ID]'
#>
function Get-AzMonCredential {
param (
[string] $UamiClientId
)
#Create Azure.Identity credential via User Assigned Managed Identity.
return New-Object Azure.Identity.ManagedIdentityCredential($UamiClientId)
}
<#
.Synopsis
Gets new LogsIngestionClient object to be used to send data to Azure Monitor.
.Description
Gets new LogsIngestionClient object to be used to send data to Azure Monitor. This leverages the Azure.Monitor.Ingestion.LogsIngestionClient .Net class.
.Parameter DceUri
The Azure Monitor data collection endpoint URI that will be used to send the data to.
.Parameter AzMonCredential
The Azure.Identity.ManagedIdentityCredential object obtained via the Get-AzMonCredential cmdlet.
.Example
# Send an array of objects to Azure Monitor Logs.
Get-AzMonCredential -UamiClientId [User-Assigned Managed Identity ID]'
#>
function Get-AzMonLogsIngestionClient {
param (
[string] $DceUri,
[object] $AzMonCredential
)
return New-Object Azure.Monitor.Ingestion.LogsIngestionClient($DceUri, $AzMonCredential)
}
<#
.Synopsis
Sends mutiple events/objects to Azure Monitor Logs.
.Description
Leveraging the Azure Monitor Ingestion client library for .Net, this module sends an array of objects to Azure Monitor Logs via the Logs Ingestion API and a Data Collection Rule (DCR). This function includes logic to properly split the array of data into chunks that are accepted by Azure Monitor.
.Parameter Data
The data to be sent to Azure Monitor. This can be a PowerShell object or an array of objects.
.Parameter BatchSize
(Optional) The number of objects within the arrary to send to the Azure Monitor API in a single request. If not specified, the entire array will be sent in a single request.
.Parameter JsonDepth
(Optional) Specifies how many levels of contained objects are included in the JSON. Default is 100.
.Parameter LogsIngestionClient
The Azure.Monitor.Ingestion.LogsIngestionClient object obtained via the Get-AzMonLogsIngestionClient cmdlet.
.Parameter TableName
Name of Azure Monitor Logs table that the data will be sent to. The name needs to include the proper prefix suffix as specified in the DCR (e.g., Custom-TableName_CL).
.Parameter DcrImmutableId
The Azure monitor data collection rule immutable ID.
.Parameter DataAlreadyGzipEncoded
(Optional) Specifies if the data to be sent is already Gzip encoded (compressed). Default value is false, in which the data will be compressed via Gzip before sending to Azure Monitor.
.Parameter SortBySize
(Optional) If set to true, the objects in the array will be sorted from smallest to largest before sending to Azure Monitor. This can increase throughput for arrays that have objects that vary greatly in size. Default is true.
.Parameter MaxRetries
(Optional) Specified how many times the request will be retried in the event an error occurs during transmission. Default value is 5.
.Parameter EventIdPropertyName
(Optional) The property within the object that represents the unique id of the object/event. If specified, this property will be logged in the event an object is too large to send to Azure Monitor.
.Example
# Send an array of objects to Azure Monitor Logs.
Send-AzMonData -Data $array -TableName "Custom-TableName_CL" -LogsIngestionClient $logsIngestionClient -JsonDepth 100 -dcrImmutableId $dcrImmutableId -DataAlreadyGZipEncoded $false -SortBySize $true -DelayInMilliseconds 0 -BatchSize 10000 -EventIdPropertyName 'Identifier'
#>
function Send-AzMonData {
param (
$Data,
[int] $BatchSize = 0,
[string] $TableName,
[int] $JsonDepth = 100,
[object]$LogsIngestionClient,
[string] $DcrImmutableId,
[boolean] $DataAlreadyGZipEncoded = $false,
[boolean] $SortBySize = $false,
[int] $Delay = 0,
[int] $MaxRetries = 5,
[string] $EventIdPropertyName
)
$skip = 0
$errorCount = 0
if ($BatchSize -eq 0) { $BatchSize = $Data.Count }
#Sort data by size, smallest to largest to get optimal batching.
if ($SortBySize -eq $true) {
Write-Host "Sorting data..."
$getSize = { ($_ | ConvertTo-Json -Depth $JsonDepth).Length }
$Data = $Data | Sort-Object -Property $getSize
}
#Enter error handling loop to send data.
Write-Host ("Sending " + $Data.Count + " events/objects to Azure Monitor...")
do {
try {
do {
if ($Data.Count -lt $BatchSize) { $BatchSize = $Data.Count }
$batchedData = $Data | Select-Object -Skip $skip -First $BatchSize
if ($batchedData.Count -eq 0) { return }
#Send data to Azure Monitor
if ($DataAlreadyGZipEncoded -eq $false) { $LogsIngestionClient.Upload($DcrImmutableId, $TableName, ($batchedData | ConvertTo-Json -Depth $JsonDepth -AsArray)) | Out-Null }
else { $LogsIngestionClient.Upload($DcrImmutableId, $TableName, ($batchedData | ConvertTo-Json -Depth $JsonDepth -AsArray), 'gzip') | Out-Null }
$skip += $BatchSize
Start-Sleep -Milliseconds $Delay
} until ($skip -ge $Data.Count)
Write-Host "Completed sending data to Azure Monitor."
return
}
catch {
if ($_.Exception.InnerException.Message -like "*ErrorCode: ContentLengthLimitExceeded*") {
if ($BatchSize -eq 1) {
Write-Error ("Event ID: " + $batchedData[0].$EventIdPropertyName + " is too large to submit to Azure Monitor. JSON Length: " + ($batchedData[0] | ConvertTo-Json -Depth $JsonDepth).Length + ". $_") -ErrorAction Continue
if ($skip -lt ($Data.Count - 1 )) {
$skip++
}
else {
$errorCount = $MaxRetries + 1
}
}
else {
$BatchSize = [math]::Round($BatchSize / 2)
if ($BatchSize -lt 1) { $BatchSize = 1 }
Write-Host ("Data too large, reducing batch size to: $BatchSize.")
}
}
else {
Write-Error $_ -ErrorAction Continue
$errorCount++
if ($errorCount -gt $MaxRetries) { Write-Error "Max number of retries reached, aborting." -ErrorAction Continue }
}
}
} until ($errorCount -gt $MaxRetries)
}
Export-ModuleMember -Function Get-AzMonCredential
Export-ModuleMember -Function Get-AzMonLogsIngestionClient
Export-ModuleMember -Function Send-AzMonData

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

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

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

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

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

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

@ -1,113 +1,82 @@
@description('Globally unique name for Key Vault used to store Function App secrets.')
param KeyVaultName string = 'kv-mdvm-${uniqueString(resourceGroup().id)}'
@description('Name for Function App resource that will be deployed.')
var deploymentVersion = '1.1.1'
@description('A globally unique name for the Function App to be created which will run the code to ingest MDVM data into Sentinel.')
param FunctionAppName string = 'fa-mdvm-${uniqueString(resourceGroup().id)}'
@description('Name for User Assigned Managed Identity that the Function App will use to authenticate to the Defender API and other Azure resources.')
param UserAssignedManagedIdentityName string = 'uami-mdvm-${uniqueString(resourceGroup().id)}'
@description('Select to enable Application Insights for the Function App. This will allow you to monitor the status of the Function App for any errors. The Log Analytics Workspace specified in the "Log Analytics Resource Id" Parameter will be used to store the Application Insights data.')
param DeployApplicationInsights bool = true
@description('Name for the Applications Insights resource that will be used by the Function App if enabled in the DeployApplicationInsights parameter.')
param AppInsightsName string = 'ai-mdvm-${uniqueString(resourceGroup().id)}'
@description('Uri where the Function App package is located. Use default value unless you are hosting the package somewhere else.')
param FunctionAppPackageUri string = 'https://raw.githubusercontent.com/Azure/Azure-Sentinel/master/DataConnectors/M365Defender-VulnerabilityManagement/functionPackage.zip'
@description('Uri where the post deployment script is located. This is used to publish the Function App code after the resources have been deploted. Use default value unless you are hosting the script somewhere else.')
param DeploymentScriptUri string = 'https://raw.githubusercontent.com/Azure/Azure-Sentinel/master/DataConnectors/M365Defender-VulnerabilityManagement/deploymentScript.ps1'
@description('Name for App Service Plan resource that will be deployed. This is where the Function App will run.')
param AppServicePlanName string = 'asp-mdvm-${uniqueString(resourceGroup().id)}'
@description('Globally unique name for the Storage Account used by the Function App.')
@description('A globally unique name for the Key Vault to be created which will store Function App secrets.')
param KeyVaultName string = 'kv-mdvm-${uniqueString(resourceGroup().id)}'
@description('A globally unique name for the Function App Storage Account. Must be between 3 and 24 characters in length and use numbers and lower-case letters only.')
param StorageAccountName string = 'samdvm${uniqueString(resourceGroup().id)}'
@description('Name of custom role to be created at the Log Analytics resource group level. The name needs to be unique across the entire tenant. This role provides the Function App read access to the MDVM custom tables.')
param CustomRoleName string = 'Custom Role - Sentinel MDVM Table Reader'
@description('Name for Data Collection Endpoint used to ingest data into Log Analytics workspace.')
param DataCollectionEndpointName string = 'dce-mdvm-${uniqueString(resourceGroup().id)}'
@description('Name for Data Collection Rule used to ingest data into Log Analytics workspace.')
param DataCollectionRuleName string = 'dcr-mdmv-${uniqueString(resourceGroup().id)}'
@description('Azure Resource Id of the Log Analytics Workspace where you like the MDVM and optional Function App Application Insights data to reside. The format is: "/subscriptions/xxxxxxxx-xxxxxxxx-xxxxxxxx-xxxxxxxx-xxxxxxxx/resourcegroups/xxxxxxxx/providers/microsoft.operationalinsights/workspaces/xxxxxxxx"')
param LogAnalyticsWorkspaceResourceId string
@description('Azure location/region of the Log Analytics Workspace referenced in the LogAnalyticsWorkspaceResourceId parameter.')
@allowed(
[
'asia'
'asiapacific'
'australia'
'australiacentral'
'australiacentral2'
'australiaeast'
'australiasoutheast'
'brazil'
'brazilsouth'
'brazilsoutheast'
'canada'
'canadacentral'
'canadaeast'
'centralindia'
'centralus'
'centraluseuap'
'eastasia'
'eastus'
'eastus2'
'eastus2euap'
'europe'
'france'
'francecentral'
'francesouth'
'germany'
'germanynorth'
'germanywestcentral'
'global'
'india'
'japan'
'japaneast'
'japanwest'
'korea'
'koreacentral'
'koreasouth'
'northcentralus'
'northeurope'
'norway'
'norwayeast'
'norwaywest'
'qatarcentral'
'southafrica'
'southafricanorth'
'southafricawest'
'southcentralus'
'southeastasia'
'southindia'
'swedencentral'
'switzerland'
'switzerlandnorth'
'switzerlandwest'
'uaecentral'
'uaenorth'
'uksouth'
'ukwest'
'unitedstates'
'westcentralus'
'westeurope'
'westindia'
'westus'
'westus2'
'westus3'
]
)
param LogAnalyticsWorkspaceLocation string
@description('Azure Resource ID (NOT THE WORKSPACE ID) of the existing Log Analytics Workspace where you would like the MDVM data and optional Function App Application Insights data to reside. This can be found by clicking the "JSON View" link within the Overview page of the Log Analytics workspace resource. The format is: "/subscriptions/xxxxxxxx-xxxxxxxx-xxxxxxxx-xxxxxxxx-xxxxxxxx/resourcegroups/xxxxxxxx/providers/microsoft.operationalinsights/workspaces/xxxxxxxx"')
param LogAnalyticsWorkspaceResourceID string = '/subscriptions/xxxxxxxx-xxxxxxxx-xxxxxxxx-xxxxxxxx-xxxxxxxx/resourcegroups/xxxxxxxx/providers/microsoft.operationalinsights/workspaces/xxxxxxxx'
@description('Deploy Azure workbooks to help visualize the MDVM data.')
param DeployWorkbooks bool = true
@description('Use the Azure Deployment Script resource to automatically deploy the Function App code. This requires the Microsoft.ContainerInstance resource provider to be registred on the subsription.')
param DeployFunctionCode bool = true
@description('GitHub repo where Azure Function package and post deployment script is located. Leave the default value unless you are using content from a different location. This is not applicable if the Deploy Function Code parameter is set to false.')
param RepoUri string = 'https://raw.githubusercontent.com/Azure/Azure-Sentinel/master'
@description('Recommended when processing a large number of events but increases cost.')
param EnableElasticPremiumPlan bool = false
@description('Enabling Private Networking will restrict public access to the Function App for additional security. A Virtual Network with the below address space and subnets, along with an NSG, Private Endpoints, and Private DNS Zones will be deployed to support this configuration. This will also leverage the Dedicated App Service Premium plan (P0v3) instead of the Consumption plan (If the Elastic Premium Plan is selected, it will be used instead of the Dedicated App Service Premium Plan.).')
param EnablePrivateNetworking bool = true
@description('If enabling Private Networking, choose the desired address space for the Virtual Network or leave the default.')
param PrivateNetworkAddressSpace string = '10.0.0.0/24'
@description('If enabling Private Networking, choose the desired address space for the Private Endpoints subnet or leave the default. Do not make subnets any smaller than the default.')
param PrivateEndpointsSubnet string = '10.0.0.0/26'
@description('If enabling Private Networking, choose the desired address space for the Function App vnet integration subnet or leave the default. Do not make subnets any smaller than the default.')
param FunctionAppSubnet string = '10.0.0.64/26'
var Location = resourceGroup().location
var location = resourceGroup().location
var functionAppPackageUri = '${RepoUri}/DataConnectors/M365Defender-VulnerabilityManagement/functionPackage.zip'
var deploymentScriptUri = '${RepoUri}/DataConnectors/M365Defender-VulnerabilityManagement/deploymentScript.ps1'
var roleIdOwner = '/providers/Microsoft.Authorization/roleDefinitions/8e3af657-a8ff-443c-a75c-2fe8c4bcb635'
resource law 'Microsoft.OperationalInsights/workspaces@2022-10-01' existing = {
name: split(LogAnalyticsWorkspaceResourceID, '/')[8]
scope: resourceGroup(split(LogAnalyticsWorkspaceResourceID, '/')[2], split(LogAnalyticsWorkspaceResourceID, '/')[4])
}
resource userAssignedMi 'Microsoft.ManagedIdentity/userAssignedIdentities@2022-01-31-preview' = {
name: UserAssignedManagedIdentityName
location: Location
name: 'uami-${FunctionAppName}'
location: location
}
module createCustomTables 'modules/customDcrTables.bicep' = {
name: 'createCustomTables'
params: {
LogAnalyticsWorkspaceLocation: law.location
LogAnalyticsWorkspaceResourceId: law.id
DataCollectionEndpointName: DataCollectionEndpointName
DataCollectionRuleName: DataCollectionRuleName
ServicePrincipalId: userAssignedMi.properties.principalId
}
}
resource storageAccount 'Microsoft.Storage/storageAccounts@2021-08-01' = {
name: StorageAccountName
location: Location
sku: {
name: 'Standard_LRS'
}
dependsOn: [
createCustomTables
]
location: location
kind: 'StorageV2'
sku: {
name: 'Standard_LRS'
}
properties: {
allowBlobPublicAccess: false
}
allowBlobPublicAccess: false
publicNetworkAccess: EnablePrivateNetworking == true ? 'Disabled' : 'Enabled'
minimumTlsVersion: 'TLS1_2'
networkAcls: {
defaultAction: EnablePrivateNetworking == true ? 'Deny' : 'Allow'
bypass: 'AzureServices'
}
}
}
resource fileShare 'Microsoft.Storage/storageAccounts/fileServices/shares@2022-09-01' = {
@ -116,7 +85,7 @@ resource fileShare 'Microsoft.Storage/storageAccounts/fileServices/shares@2022-0
resource keyVault 'Microsoft.KeyVault/vaults@2022-07-01' = {
name: KeyVaultName
location: Location
location: location
properties: {
sku: {
family: 'A'
@ -137,21 +106,11 @@ resource keyVault 'Microsoft.KeyVault/vaults@2022-07-01' = {
tenantId: subscription().tenantId
}
]
}
}
module keyVaultUpdateNetworAcl 'modules/keyVault.bicep' = {
name: 'keyVaultUpdateNetworkAcl'
params: {
kvName: keyVault.name
location: keyVault.location
skuFamily: keyVault.properties.sku.family
skuName: keyVault.properties.sku.name
principalId: userAssignedMi.properties.principalId
aclBypass: 'None'
aclDefaultAction: 'Deny'
aclIpRules: functionApp.properties.possibleOutboundIpAddresses
secretPermissions: keyVault.properties.accessPolicies[0].permissions.secrets
publicNetworkAccess: EnablePrivateNetworking == true ? 'Disabled' : 'Enabled'
networkAcls: {
defaultAction: EnablePrivateNetworking == true ? 'Deny' :'Allow'
bypass: EnablePrivateNetworking == true ? 'None' : 'AzureServices'
}
}
}
@ -164,44 +123,34 @@ resource keyVaultSecretStorageAccountConnectionString 'Microsoft.KeyVault/vaults
}
resource hostingPlan 'Microsoft.Web/serverfarms@2021-03-01' = {
name: AppServicePlanName
location: Location
name: FunctionAppName
location: location
sku: {
name: 'P1v2'
tier: 'PremiumV2'
}
kind: 'linux'
properties: {
reserved: true
name: EnableElasticPremiumPlan == true ? 'EP1' : EnablePrivateNetworking == true ? 'P0v3' : 'Y1'
tier: EnableElasticPremiumPlan == true ? 'ElasticPremium' : EnablePrivateNetworking == true ? 'PremiumV3' : 'Dynamic'
}
}
resource functionApp 'Microsoft.Web/sites@2022-03-01' = {
name: FunctionAppName
location: Location
dependsOn: [
keyVaultSecretStorageAccountConnectionString
fileShare
]
kind: 'functionapp'
location: location
identity: {
type: 'UserAssigned'
userAssignedIdentities: {
'${userAssignedMi.id}': {}
}
}
kind: 'functionapp'
properties: {
serverFarmId: hostingPlan.id
keyVaultReferenceIdentity: userAssignedMi.id
httpsOnly: true
clientCertEnabled: true
clientCertMode: 'OptionalInteractiveUser'
virtualNetworkSubnetId: EnablePrivateNetworking == true ? privateNetwork.outputs.functionAppSubnetId : (null)
vnetContentShareEnabled: EnablePrivateNetworking == true ? true : false
vnetRouteAllEnabled: EnablePrivateNetworking == true ? true : false
siteConfig: {
alwaysOn: true
linuxFxVersion: 'PowerShell|7.2'
use32BitWorkerProcess: false
ftpsState: 'Disabled'
minTlsVersion: '1.2'
appSettings: [
{
name: 'AzureWebJobsStorage'
@ -217,7 +166,7 @@ resource functionApp 'Microsoft.Web/sites@2022-03-01' = {
}
{
name: 'AzureWebJobsSecretStorageKeyVaultUri'
value: 'https://${KeyVaultName}.vault.azure.net/'
value: 'https://${KeyVaultName}${environment().suffixes.keyvaultDns}/'
}
{
name: 'AzureWebJobsSecretStorageKeyVaultClientId'
@ -247,9 +196,13 @@ resource functionApp 'Microsoft.Web/sites@2022-03-01' = {
name: 'WEBSITE_RUN_FROM_PACKAGE'
value: '1'
}
{
name: 'WEBSITE_CONTENTOVERVNET'
value: EnablePrivateNetworking == true ? '1' : '0'
}
{
name: 'LawResourceId'
value: LogAnalyticsWorkspaceResourceId
value: law.id
}
{
name: 'DcrImmutableId'
@ -268,55 +221,99 @@ resource functionApp 'Microsoft.Web/sites@2022-03-01' = {
value: '0'
}
]
powerShellVersion: '7.2'
minTlsVersion: '1.2'
ftpsState: 'Disabled'
http20Enabled: true
alwaysOn: EnablePrivateNetworking != true || EnableElasticPremiumPlan == true ? false : true
publicNetworkAccess: 'Enabled'
}
}
dependsOn: [
keyVaultSecretStorageAccountConnectionString
storageAccount
fileShare
]
}
resource applicationInsights 'Microsoft.Insights/components@2020-02-02' = if (DeployApplicationInsights == true) {
name: AppInsightsName
location: Location
name: 'appInsights-${FunctionAppName}'
location: location
kind: 'web'
properties: {
Application_Type: 'web'
Request_Source: 'rest'
WorkspaceResourceId: LogAnalyticsWorkspaceResourceId
WorkspaceResourceId: law.id
}
}
module createCustomTables 'modules/customDcrTables.bicep' = {
name: 'createCustomTables'
params: {
LogAnalyticsWorkspaceLocation: LogAnalyticsWorkspaceLocation
LogAnalyticsWorkspaceResourceId: LogAnalyticsWorkspaceResourceId
DataCollectionEndpointName: DataCollectionEndpointName
DataCollectionRuleName: DataCollectionRuleName
ServicePrincipalId: userAssignedMi.properties.principalId
}
}
module roleAssignmentLaw 'modules/lawRoleAssignment.bicep' = {
scope: resourceGroup(split(LogAnalyticsWorkspaceResourceId, '/')[2], split(LogAnalyticsWorkspaceResourceId, '/')[4])
scope: resourceGroup(split(law.id, '/')[2], split(law.id, '/')[4])
dependsOn: [
createCustomTables
]
name: 'rbacAssignmentLaw'
params: {
principalId: userAssignedMi.properties.principalId
roleDefId: '/providers/Microsoft.Authorization/roleDefinitions/43d0d8ad-25c7-4714-9337-8ba259a9fe05'
scopedResourceName: split(LogAnalyticsWorkspaceResourceId, '/')[8]
PrincipalId: userAssignedMi.properties.principalId
LawName: split(law.id, '/')[8]
RoleName: CustomRoleName
}
}
resource roleAssignmentFa 'Microsoft.Authorization/roleAssignments@2022-04-01' = {
resource roleAssignmentFa 'Microsoft.Authorization/roleAssignments@2022-04-01' = if(DeployFunctionCode == true) {
name: guid(subscription().id, resourceGroup().id, functionApp.id)
scope: functionApp
properties: {
principalId: userAssignedMi.properties.principalId
roleDefinitionId: '/providers/Microsoft.Authorization/roleDefinitions/8e3af657-a8ff-443c-a75c-2fe8c4bcb635'
roleDefinitionId: roleIdOwner
principalType: 'ServicePrincipal'
}
}
resource deploymentScript 'Microsoft.Resources/deploymentScripts@2020-10-01' = {
module sentinelWorkbooks 'modules/sentinelWorkbooks.bicep' = if(DeployWorkbooks == true) {
name: 'sentinelWorkbooks'
scope: resourceGroup(split(law.id, '/')[2], split(law.id, '/')[4])
dependsOn: [
createCustomTables
functionApp
]
params: {
WorkbookSourceId: law.id
Location: law.location
}
}
module privateNetwork 'modules/privateNetwork.bicep' = if(EnablePrivateNetworking == true) {
name: 'privateNetwork'
params: {
FunctionAppName: FunctionAppName
KeyVaultId: keyVault.id
location: location
StorageAccountId: storageAccount.id
StorageAccountName: StorageAccountName
KeyVaultName: KeyVaultName
PrivateNetworkAddressSpace: PrivateNetworkAddressSpace
FunctionAppSubnet: FunctionAppSubnet
PrivateEndpointsSubnet: PrivateEndpointsSubnet
PrincipalId: userAssignedMi.properties.principalId
DeployCode: DeployFunctionCode
}
}
module functionAppPe 'modules/functionAppPE.bicep' = if(EnablePrivateNetworking == true) {
name: 'functionAppPe'
params: {
Location: location
FunctionAppId: functionApp.id
FunctionAppName: FunctionAppName
PrivateEndpointSubnetId: privateNetwork.outputs.privateEndpointSubnetId
VnetId: privateNetwork.outputs.vnetId
}
}
resource deploymentScript 'Microsoft.Resources/deploymentScripts@2020-10-01' = if(DeployFunctionCode == true) {
name: 'deployCode'
location: Location
location: location
kind: 'AzurePowerShell'
identity: {
type: 'UserAssigned'
@ -325,12 +322,12 @@ resource deploymentScript 'Microsoft.Resources/deploymentScripts@2020-10-01' = {
}
}
properties: {
azPowerShellVersion: '10.0'
azPowerShellVersion: '11.0'
retentionInterval: 'PT1H'
timeout: 'PT15M'
cleanupPreference: 'Always'
primaryScriptUri: DeploymentScriptUri
arguments: '-PackageUri ${FunctionAppPackageUri} -SubscriptionId ${split(subscription().id, '/')[2]} -ResourceGroupName ${resourceGroup().name} -FunctionAppName ${functionApp.name} -FAScope ${functionApp.id} -UAMIPrincipalId ${userAssignedMi.properties.principalId}'
timeout: 'PT10M'
cleanupPreference: 'Always'
primaryScriptUri: deploymentScriptUri
arguments: EnablePrivateNetworking == true ? '-PackageUri ${functionAppPackageUri} -SubscriptionId ${split(subscription().id, '/')[2]} -ResourceGroupName ${resourceGroup().name} -FunctionAppName ${functionAppPe.outputs.functionAppName} -FAScope ${functionApp.id} -UAMIPrincipalId ${userAssignedMi.properties.principalId} -VnetScope ${privateNetwork.outputs.vnetId} -RestrictedIPs "None"' : '-PackageUri ${functionAppPackageUri} -SubscriptionId ${split(subscription().id, '/')[2]} -ResourceGroupName ${resourceGroup().name} -FunctionAppName ${functionApp.name} -FAScope ${functionApp.id} -UAMIPrincipalId ${userAssignedMi.properties.principalId}'
}
}

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

@ -1,576 +0,0 @@
@description('Globally unique name for Key Vault used to store Function App secrets.')
param KeyVaultName string = 'kv-mdvm-${uniqueString(resourceGroup().id)}'
@description('Name for Key Vault Private Endpoint')
param KeyVaultPrivateEndpointName string = 'pe-kv-mdvm-${uniqueString(resourceGroup().id)}'
@description('Name for Function App resource that will be deployed.')
param FunctionAppName string = 'fa-mdvm-${uniqueString(resourceGroup().id)}'
@description('Name for User Assigned Managed Identity that the Function App will use to authenticate to the Defender API and other Azure resources.')
param UserAssignedManagedIdentityName string = 'uami-mdvm-${uniqueString(resourceGroup().id)}'
@description('Select to enable Application Insights for the Function App. This will allow you to monitor the status of the Function App for any errors. The Log Analytics Workspace specified in the "Log Analytics Resource Id" Parameter will be used to store the Application Insights data.')
param DeployApplicationInsights bool = true
@description('Name for the Applications Insights resource that will be used by the Function App if enabled in the DeployApplicationInsights parameter.')
param AppInsightsName string = 'ai-mdvm-${uniqueString(resourceGroup().id)}'
@description('Uri where the Function App package is located. Use default value unless you are hosting the package somewhere else.')
param FunctionAppPackageUri string = 'https://raw.githubusercontent.com/Azure/Azure-Sentinel/master/DataConnectors/M365Defender-VulnerabilityManagement/functionPackage.zip'
@description('Uri where the post deployment script is located. This is used to publish the Function App code after the resources have been deploted. Use default value unless you are hosting the script somewhere else.')
param DeploymentScriptUri string = 'https://raw.githubusercontent.com/Azure/Azure-Sentinel/master/DataConnectors/M365Defender-VulnerabilityManagement/deploymentScript.ps1'
@description('Name for App Service Plan resource that will be deployed. This is where the Function App will run.')
param AppServicePlanName string = 'asp-mdvm-${uniqueString(resourceGroup().id)}'
@description('Globally unique name for the Storage Account used by the Function App.')
param StorageAccountName string = 'samdvm${uniqueString(resourceGroup().id)}'
@description('Name for Storage Account - Blob Private Endpoint')
param StorageAccountBlobPrivateEndpointName string = 'pe-blob-mdvm-${uniqueString(resourceGroup().id)}'
@description('Name for Storage Account - File Private Endpoint')
param StorageAccountFilePrivateEndpointName string = 'pe-file-mdvm-${uniqueString(resourceGroup().id)}'
@description('Name for Data Collection Endpoint used to ingest data into Log Analytics workspace.')
param DataCollectionEndpointName string = 'dce-mdvm-${uniqueString(resourceGroup().id)}'
@description('Name for Data Collection Rule used to ingest data into Log Analytics workspace.')
param DataCollectionRuleName string = 'dcr-mdvm-${uniqueString(resourceGroup().id)}'
@description('Azure Resource Id of the Log Analytics Workspace where you like the MDVM and optional Function App Application Insights data to reside. The format is: "/subscriptions/xxxxxxxx-xxxxxxxx-xxxxxxxx-xxxxxxxx-xxxxxxxx/resourcegroups/xxxxxxxx/providers/microsoft.operationalinsights/workspaces/xxxxxxxx"')
param LogAnalyticsWorkspaceResourceId string
@description('Azure location/region of the Log Analytics Workspace referenced in the LogAnalyticsWorkspaceResourceId parameter.')
@allowed(
[
'asia'
'asiapacific'
'australia'
'australiacentral'
'australiacentral2'
'australiaeast'
'australiasoutheast'
'brazil'
'brazilsouth'
'brazilsoutheast'
'canada'
'canadacentral'
'canadaeast'
'centralindia'
'centralus'
'centraluseuap'
'eastasia'
'eastus'
'eastus2'
'eastus2euap'
'europe'
'france'
'francecentral'
'francesouth'
'germany'
'germanynorth'
'germanywestcentral'
'global'
'india'
'japan'
'japaneast'
'japanwest'
'korea'
'koreacentral'
'koreasouth'
'northcentralus'
'northeurope'
'norway'
'norwayeast'
'norwaywest'
'qatarcentral'
'southafrica'
'southafricanorth'
'southafricawest'
'southcentralus'
'southeastasia'
'southindia'
'swedencentral'
'switzerland'
'switzerlandnorth'
'switzerlandwest'
'uaecentral'
'uaenorth'
'uksouth'
'ukwest'
'unitedstates'
'westcentralus'
'westeurope'
'westindia'
'westus'
'westus2'
'westus3'
]
)
param LogAnalyticsWorkspaceLocation string
@description('Specify a comma separated list of CIDR formatted IP address ranges to restrict connecting to the Function App from (i.e. 192.168.1.0/24,172.16.2.5/32).')
param TrustedIPAddressRanges string = '0.0.0.0/0'
@description('Name for Virtual Network resource that will be deployed.')
param VirtualNetworkName string = 'vnet-mdvm'
@description('Name for Virtual Network resource that will be deployed.')
param VirtualNetworkIPAddressPrefix string = '10.0.0.0/16'
@description('Azure Resource Id of the Virtual Network to place private endpoints and Function App VNet integration.')
param PrivateEndpointSubnetIPAddressPrefix string = '10.0.0.0/24'
@description('Azure Resource Id of the Virtual Network to place private endpoints and Function App VNet integration.')
param VNetIntegrationSubnetIPAddressPrefix string = '10.0.1.0/24'
var Location = resourceGroup().location
resource userAssignedMi 'Microsoft.ManagedIdentity/userAssignedIdentities@2022-01-31-preview' = {
name: UserAssignedManagedIdentityName
location: Location
}
resource storageAccount 'Microsoft.Storage/storageAccounts@2021-08-01' = {
name: StorageAccountName
location: Location
sku: {
name: 'Standard_LRS'
}
kind: 'StorageV2'
properties: {
allowBlobPublicAccess: false
networkAcls: {
defaultAction: 'Deny'
bypass: 'None'
}
publicNetworkAccess: 'Disabled'
}
}
resource fileShare 'Microsoft.Storage/storageAccounts/fileServices/shares@2022-09-01' = {
name: '${storageAccount.name}/default/${toLower(FunctionAppName)}'
}
resource keyVault 'Microsoft.KeyVault/vaults@2022-07-01' = {
name: KeyVaultName
location: Location
properties: {
sku: {
family: 'A'
name: 'premium'
}
tenantId: subscription().tenantId
accessPolicies: [
{
objectId: userAssignedMi.properties.principalId
permissions: {
secrets: [
'get'
'set'
'list'
'delete'
]
}
tenantId: subscription().tenantId
}
]
networkAcls: {
bypass: 'None'
defaultAction: 'Deny'
}
publicNetworkAccess: 'Disabled'
}
}
resource keyVaultSecretStorageAccountConnectionString 'Microsoft.KeyVault/vaults/secrets@2022-07-01' = {
parent: keyVault
name: 'StorageAccountConnectionString'
properties: {
value: 'DefaultEndpointsProtocol=https;AccountName=${StorageAccountName};EndpointSuffix=${environment().suffixes.storage};AccountKey=${storageAccount.listKeys().keys[0].value}'
}
}
resource hostingPlan 'Microsoft.Web/serverfarms@2021-03-01' = {
name: AppServicePlanName
location: Location
sku: {
name: 'P1v2'
tier: 'PremiumV2'
}
kind: 'linux'
properties: {
reserved: true
}
}
resource functionApp 'Microsoft.Web/sites@2022-03-01' = {
name: FunctionAppName
location: Location
dependsOn: [
keyVaultSecretStorageAccountConnectionString
fileShare
peBlob
peFile
peKeyVault
privateDnsZoneBlob
privateDnsZoneFile
privateDnsZoneKeyVault
]
kind: 'functionapp'
identity: {
type: 'UserAssigned'
userAssignedIdentities: {
'${userAssignedMi.id}': {}
}
}
properties: {
serverFarmId: hostingPlan.id
keyVaultReferenceIdentity: userAssignedMi.id
httpsOnly: true
clientCertEnabled: true
clientCertMode: 'OptionalInteractiveUser'
virtualNetworkSubnetId: virtualNetwork.properties.subnets[1].id
siteConfig: {
alwaysOn: true
linuxFxVersion: 'PowerShell|7.2'
use32BitWorkerProcess: false
ftpsState: 'Disabled'
minTlsVersion: '1.2'
vnetRouteAllEnabled: true
http20Enabled: true
appSettings: [
{
name: 'AzureWebJobsStorage'
value: '@Microsoft.KeyVault(VaultName=${KeyVaultName};SecretName=StorageAccountConnectionString)'
}
{
name: 'WEBSITE_CONTENTAZUREFILECONNECTIONSTRING'
value: '@Microsoft.KeyVault(VaultName=${KeyVaultName};SecretName=StorageAccountConnectionString)'
}
{
name: 'AzureWebJobsSecretStorageType'
value: 'keyvault'
}
{
name: 'AzureWebJobsSecretStorageKeyVaultUri'
value: 'https://${KeyVaultName}.vault.azure.net/'
}
{
name: 'AzureWebJobsSecretStorageKeyVaultClientId'
value: userAssignedMi.properties.clientId
}
{
name: 'WEBSITE_CONTENTSHARE'
value: toLower(FunctionAppName)
}
{
name: 'WEBSITE_SKIP_CONTENTSHARE_VALIDATION'
value: '1'
}
{
name: 'WEBSITE_DNS_SERVER'
value: '168.63.129.16'
}
{
name: 'WEBSITE_CONTENTOVERVNET'
value: '1'
}
{
name: 'FUNCTIONS_EXTENSION_VERSION'
value: '~4'
}
{
name: 'APPINSIGHTS_INSTRUMENTATIONKEY'
value: DeployApplicationInsights == true ? applicationInsights.properties.InstrumentationKey : ''
}
{
name: 'FUNCTIONS_WORKER_RUNTIME'
value: 'powershell'
}
{
name: 'WEBSITE_RUN_FROM_PACKAGE'
value: '1'
}
{
name: 'LawResourceId'
value: LogAnalyticsWorkspaceResourceId
}
{
name: 'DcrImmutableId'
value: createCustomTables.outputs.DcrImmutableId
}
{
name: 'DceUri'
value: createCustomTables.outputs.DceUri
}
{
name: 'UamiClientId'
value: userAssignedMi.properties.clientId
}
{
name: 'FullImport'
value: '0'
}
]
}
}
}
resource applicationInsights 'Microsoft.Insights/components@2020-02-02' = if (DeployApplicationInsights == true) {
name: AppInsightsName
location: Location
kind: 'web'
properties: {
Application_Type: 'web'
Request_Source: 'rest'
WorkspaceResourceId: LogAnalyticsWorkspaceResourceId
}
}
resource virtualNetwork 'Microsoft.Network/virtualNetworks@2022-07-01' = {
name: VirtualNetworkName
location: Location
properties: {
addressSpace: {
addressPrefixes: [
VirtualNetworkIPAddressPrefix
]
}
subnets: [
{
name: 'privateEndpoints'
properties: {
addressPrefix: PrivateEndpointSubnetIPAddressPrefix
}
}
{
name: 'functionAppVnetIntegration'
properties: {
addressPrefix: VNetIntegrationSubnetIPAddressPrefix
delegations: [
{
name: 'delegation'
properties: {
serviceName: 'Microsoft.Web/serverFarms'
}
}
]
}
}
]
}
}
resource peKeyVault 'Microsoft.Network/privateEndpoints@2022-07-01' = {
name: KeyVaultPrivateEndpointName
location: Location
properties: {
subnet: {
id: virtualNetwork.properties.subnets[0].id
}
privateLinkServiceConnections: [
{
name: KeyVaultPrivateEndpointName
properties: {
privateLinkServiceId: keyVault.id
groupIds: [
'vault'
]
}
}
]
}
}
resource peBlob 'Microsoft.Network/privateEndpoints@2022-07-01' = {
name: StorageAccountBlobPrivateEndpointName
location: Location
properties: {
subnet: {
id: virtualNetwork.properties.subnets[0].id
}
privateLinkServiceConnections: [
{
name: StorageAccountBlobPrivateEndpointName
properties: {
privateLinkServiceId: storageAccount.id
groupIds: [
'blob'
]
}
}
]
}
}
resource peFile 'Microsoft.Network/privateEndpoints@2022-07-01' = {
name: StorageAccountFilePrivateEndpointName
location: Location
properties: {
subnet: {
id: virtualNetwork.properties.subnets[0].id
}
privateLinkServiceConnections: [
{
name: StorageAccountFilePrivateEndpointName
properties: {
privateLinkServiceId: storageAccount.id
groupIds: [
'file'
]
}
}
]
}
}
resource privateDnsZoneBlob 'Microsoft.Network/privateDnsZones@2020-06-01' = {
name: 'privatelink.blob.core.windows.net'
location: 'global'
dependsOn: [
virtualNetwork
]
}
resource privateDnsZoneFile 'Microsoft.Network/privateDnsZones@2020-06-01' = {
name: 'privatelink.file.core.windows.net'
location: 'global'
dependsOn: [
virtualNetwork
]
}
resource privateDnsZoneKeyVault 'Microsoft.Network/privateDnsZones@2020-06-01' = {
name: 'privatelink.vaultcore.azure.net'
location: 'global'
dependsOn: [
virtualNetwork
]
}
resource privateDnsZoneLinkBlob 'Microsoft.Network/privateDnsZones/virtualNetworkLinks@2020-06-01' = {
name: '${privateDnsZoneBlob.name}-link'
parent: privateDnsZoneBlob
location: 'global'
properties: {
registrationEnabled: false
virtualNetwork: {
id: virtualNetwork.id
}
}
}
resource privateDnsZoneLinkFile 'Microsoft.Network/privateDnsZones/virtualNetworkLinks@2020-06-01' = {
name: '${privateDnsZoneFile.name}-link'
parent: privateDnsZoneFile
location: 'global'
properties: {
registrationEnabled: false
virtualNetwork: {
id: virtualNetwork.id
}
}
}
resource privateDnsZoneLinkKeyVault 'Microsoft.Network/privateDnsZones/virtualNetworkLinks@2020-06-01' = {
name: '${privateDnsZoneKeyVault.name}-link'
parent: privateDnsZoneKeyVault
location: 'global'
properties: {
registrationEnabled: false
virtualNetwork: {
id: virtualNetwork.id
}
}
}
resource peDnsGroupBlob 'Microsoft.Network/privateEndpoints/privateDnsZoneGroups@2022-07-01' = {
name: '${peBlob.name}/dnsGroup'
properties: {
privateDnsZoneConfigs: [
{
name: 'config1'
properties: {
privateDnsZoneId: privateDnsZoneBlob.id
}
}
]
}
}
resource peDnsGroupFile 'Microsoft.Network/privateEndpoints/privateDnsZoneGroups@2022-07-01' = {
name: '${peFile.name}/dnsGroup'
properties: {
privateDnsZoneConfigs: [
{
name: 'config1'
properties: {
privateDnsZoneId: privateDnsZoneFile.id
}
}
]
}
}
resource peDnsGroupKeyVault 'Microsoft.Network/privateEndpoints/privateDnsZoneGroups@2022-07-01' = {
name: '${peKeyVault.name}/dnsGroup'
properties: {
privateDnsZoneConfigs: [
{
name: 'config1'
properties: {
privateDnsZoneId: privateDnsZoneKeyVault.id
}
}
]
}
}
module createCustomTables 'modules/customDcrTables.bicep' = {
name: 'createCustomTables'
params: {
LogAnalyticsWorkspaceLocation: LogAnalyticsWorkspaceLocation
LogAnalyticsWorkspaceResourceId: LogAnalyticsWorkspaceResourceId
DataCollectionEndpointName: DataCollectionEndpointName
DataCollectionRuleName: DataCollectionRuleName
ServicePrincipalId: userAssignedMi.properties.principalId
}
}
module roleAssignmentLaw 'modules/lawRoleAssignment.bicep' = {
scope: resourceGroup(split(LogAnalyticsWorkspaceResourceId, '/')[2], split(LogAnalyticsWorkspaceResourceId, '/')[4])
name: 'rbacAssignmentLaw'
params: {
principalId: userAssignedMi.properties.principalId
roleDefId: '/providers/Microsoft.Authorization/roleDefinitions/43d0d8ad-25c7-4714-9337-8ba259a9fe05'
scopedResourceName: split(LogAnalyticsWorkspaceResourceId, '/')[8]
}
}
resource roleAssignmentFa 'Microsoft.Authorization/roleAssignments@2022-04-01' = {
name: guid(subscription().id, resourceGroup().id, functionApp.id)
scope: functionApp
properties: {
principalId: userAssignedMi.properties.principalId
roleDefinitionId: '/providers/Microsoft.Authorization/roleDefinitions/8e3af657-a8ff-443c-a75c-2fe8c4bcb635'
principalType: 'ServicePrincipal'
}
}
resource roleAssignmentVnet 'Microsoft.Authorization/roleAssignments@2022-04-01' = {
name: guid(subscription().id, resourceGroup().id, virtualNetwork.id)
scope: virtualNetwork
properties: {
principalId: userAssignedMi.properties.principalId
roleDefinitionId: '/providers/Microsoft.Authorization/roleDefinitions/8e3af657-a8ff-443c-a75c-2fe8c4bcb635'
principalType: 'ServicePrincipal'
}
}
resource deploymentScript 'Microsoft.Resources/deploymentScripts@2020-10-01' = {
name: 'deployCode'
location: Location
kind: 'AzurePowerShell'
identity: {
type: 'UserAssigned'
userAssignedIdentities: {
'${userAssignedMi.id}': {}
}
}
properties: {
azPowerShellVersion: '10.0'
retentionInterval: 'PT1H'
timeout: 'PT15M'
cleanupPreference: 'Always'
primaryScriptUri: DeploymentScriptUri
arguments: '-PackageUri ${FunctionAppPackageUri} -SubscriptionId ${split(subscription().id, '/')[2]} -ResourceGroupName ${resourceGroup().name} -FunctionAppName ${functionApp.name} -FAScope ${functionApp.id} -VnetScope ${virtualNetwork.id} -UAMIPrincipalId ${userAssignedMi.properties.principalId} -RestrictedIPs ${TrustedIPAddressRanges}'
}
}
output UserAssignedManagedIdentityPrincipalId string = userAssignedMi.properties.principalId
output UserAssignedManagedIdentityPrincipalName string = userAssignedMi.name

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

@ -1,6 +1,3 @@
Compress-Archive -Path .\DataConnectors\M365Defender-VulnerabilityManagement\functionPackage\* -DestinationPath .\DataConnectors\M365Defender-VulnerabilityManagement\functionPackage.zip -Force
bicep build .\DataConnectors\M365Defender-VulnerabilityManagement\main.bicep --outfile .\DataConnectors\M365Defender-VulnerabilityManagement\azureDeploy.json
bicep build .\DataConnectors\M365Defender-VulnerabilityManagement\mainNetworkRestricted.bicep --outfile .\DataConnectors\M365Defender-VulnerabilityManagement\azureDeployNetworkRestricted.json
bicep build .\DataConnectors\M365Defender-VulnerabilityManagement\modules\customDcrTables.bicep --outfile .\DataConnectors\M365Defender-VulnerabilityManagement\maintenance\customDcrTables.json
bicep build .\DataConnectors\M365Defender-VulnerabilityManagement\workbooks\main.bicep --outfile .\DataConnectors\M365Defender-VulnerabilityManagement\workbooks\azureDeploy.json
bicep build .\DataConnectors\M365Defender-VulnerabilityManagement\main.bicep --outfile .\DataConnectors\M365Defender-VulnerabilityManagement\azureDeploy.json

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

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

@ -1,76 +1,10 @@
@description('Name for Data Collection Endpoint used to ingest data into Log Analytics workspace.')
param DataCollectionEndpointName string = 'dce-mdvm-${uniqueString(resourceGroup().id)}'
param DataCollectionEndpointName string = 'dce-${uniqueString(resourceGroup().id)}'
@description('Name for Data Collection Rule used to ingest data into Log Analytics workspace.')
param DataCollectionRuleName string = 'dcr-mdmv-${uniqueString(resourceGroup().id)}'
@description('Azure Resource Id of the Log Analytics Workspace where you like the MDVM and optional Function App Application Insights data to reside. The format is: "/subscriptions/xxxxxxxx-xxxxxxxx-xxxxxxxx-xxxxxxxx-xxxxxxxx/resourcegroups/xxxxxxxx/providers/microsoft.operationalinsights/workspaces/xxxxxxxx"')
param DataCollectionRuleName string = 'dcr-${uniqueString(resourceGroup().id)}'
@description('Azure Resource Id of the Log Analytics Workspace where you like the data and optional Function App Application Insights data to reside. The format is: "/subscriptions/xxxxxxxx-xxxxxxxx-xxxxxxxx-xxxxxxxx-xxxxxxxx/resourcegroups/xxxxxxxx/providers/microsoft.operationalinsights/workspaces/xxxxxxxx"')
param LogAnalyticsWorkspaceResourceId string
@description('Azure location/region of the Log Analytics Workspace referenced in the LogAnalyticsWorkspaceResourceId parameter.')
@allowed(
[
'asia'
'asiapacific'
'australia'
'australiacentral'
'australiacentral2'
'australiaeast'
'australiasoutheast'
'brazil'
'brazilsouth'
'brazilsoutheast'
'canada'
'canadacentral'
'canadaeast'
'centralindia'
'centralus'
'centraluseuap'
'eastasia'
'eastus'
'eastus2'
'eastus2euap'
'europe'
'france'
'francecentral'
'francesouth'
'germany'
'germanynorth'
'germanywestcentral'
'global'
'india'
'japan'
'japaneast'
'japanwest'
'korea'
'koreacentral'
'koreasouth'
'northcentralus'
'northeurope'
'norway'
'norwayeast'
'norwaywest'
'qatarcentral'
'southafrica'
'southafricanorth'
'southafricawest'
'southcentralus'
'southeastasia'
'southindia'
'swedencentral'
'switzerland'
'switzerlandnorth'
'switzerlandwest'
'uaecentral'
'uaenorth'
'uksouth'
'ukwest'
'unitedstates'
'westcentralus'
'westeurope'
'westindia'
'westus'
'westus2'
'westus3'
]
)
param LogAnalyticsWorkspaceLocation string
@description('Optional: Managed Identity or Service Principal ID to be assigned the Metrics Publisher role on the data collection rule.')
param ServicePrincipalId string = ''
@ -98,6 +32,7 @@ resource dcr 'Microsoft.Insights/dataCollectionRules@2022-06-01' = {
tableMDVMVulnerabilitiesByDevice
tableMDVMNistCveKb
tableMDVMSecureConfigurationsByDevice
tableMDVMNistConfigurations
]
name: DataCollectionRuleName
location: LogAnalyticsWorkspaceLocation
@ -474,6 +409,10 @@ resource dcr 'Microsoft.Insights/dataCollectionRules@2022-06-01' = {
name: 'transactionId'
type: 'string'
}
{
name: 'cveSupportability'
type: 'string'
}
]
}
'Custom-MDVMNISTCVEKB_CL': {
@ -698,11 +637,10 @@ module tableMDVMCveKb 'lawCustomTable.bicep' = {
name: 'tableMDVMCveKb'
scope: resourceGroup(split(LogAnalyticsWorkspaceResourceId, '/')[2], split(LogAnalyticsWorkspaceResourceId, '/')[4])
params: {
lawName: split(LogAnalyticsWorkspaceResourceId, '/')[8]
tableName: 'MDVMCVEKB_CL'
plan: 'Analytics'
retention: 90
columns: [
LawName: split(LogAnalyticsWorkspaceResourceId, '/')[8]
TableName: 'MDVMCVEKB_CL'
Plan: 'Analytics'
Columns: [
{
name: 'cvssV3'
type: 'string'
@ -759,6 +697,10 @@ module tableMDVMCveKb 'lawCustomTable.bicep' = {
name: 'transactionId'
type: 'string'
}
{
name: 'cveSupportability'
type: 'string'
}
]
}
}
@ -767,11 +709,10 @@ module tableMDVMNistCveKb 'lawCustomTable.bicep' = {
name: 'tableMDVMNistCveKb'
scope: resourceGroup(split(LogAnalyticsWorkspaceResourceId, '/')[2], split(LogAnalyticsWorkspaceResourceId, '/')[4])
params: {
lawName: split(LogAnalyticsWorkspaceResourceId, '/')[8]
tableName: 'MDVMNISTCVEKB_CL'
plan: 'Analytics'
retention: 90
columns: [
LawName: split(LogAnalyticsWorkspaceResourceId, '/')[8]
TableName: 'MDVMNISTCVEKB_CL'
Plan: 'Analytics'
Columns: [
{
name: 'cveId'
type: 'string'
@ -856,11 +797,10 @@ module tableMDVMNistConfigurations 'lawCustomTable.bicep' = {
name: 'tableMDVMNistConfigurations'
scope: resourceGroup(split(LogAnalyticsWorkspaceResourceId, '/')[2], split(LogAnalyticsWorkspaceResourceId, '/')[4])
params: {
lawName: split(LogAnalyticsWorkspaceResourceId, '/')[8]
tableName: 'MDVMNISTConfigurations_CL'
plan: 'Analytics'
retention: 90
columns: [
LawName: split(LogAnalyticsWorkspaceResourceId, '/')[8]
TableName: 'MDVMNISTConfigurations_CL'
Plan: 'Analytics'
Columns: [
{
name: 'configurationOperator'
type: 'string'
@ -929,10 +869,10 @@ module tableMDVMRecommendations 'lawCustomTable.bicep' = {
name: 'tableMDVMRecommendations'
scope: resourceGroup(split(LogAnalyticsWorkspaceResourceId, '/')[2], split(LogAnalyticsWorkspaceResourceId, '/')[4])
params: {
lawName: split(LogAnalyticsWorkspaceResourceId, '/')[8]
tableName: 'MDVMRecommendations_CL'
plan: 'Analytics'
columns: [
LawName: split(LogAnalyticsWorkspaceResourceId, '/')[8]
TableName: 'MDVMRecommendations_CL'
Plan: 'Analytics'
Columns: [
{
name: 'associatedThreats'
type: 'dynamic'
@ -1041,10 +981,10 @@ module tableMDVMSecureConfigurationsByDevice 'lawCustomTable.bicep' = {
name: 'tableMDVMSecureConfigurationsByDevice'
scope: resourceGroup(split(LogAnalyticsWorkspaceResourceId, '/')[2], split(LogAnalyticsWorkspaceResourceId, '/')[4])
params: {
lawName: split(LogAnalyticsWorkspaceResourceId, '/')[8]
tableName: 'MDVMSecureConfigurationsByDevice_CL'
plan: 'Analytics'
columns: [
LawName: split(LogAnalyticsWorkspaceResourceId, '/')[8]
TableName: 'MDVMSecureConfigurationsByDevice_CL'
Plan: 'Analytics'
Columns: [
{
name: 'deviceId'
type: 'string'
@ -1129,10 +1069,10 @@ module tableMDVMVulnerabilitiesByDevice 'lawCustomTable.bicep' = {
name: 'tableMDVMVulnerabilitiesByDevice'
scope: resourceGroup(split(LogAnalyticsWorkspaceResourceId, '/')[2], split(LogAnalyticsWorkspaceResourceId, '/')[4])
params: {
lawName: split(LogAnalyticsWorkspaceResourceId, '/')[8]
tableName: 'MDVMVulnerabilitiesByDevice_CL'
plan: 'Analytics'
columns: [
LawName: split(LogAnalyticsWorkspaceResourceId, '/')[8]
TableName: 'MDVMVulnerabilitiesByDevice_CL'
Plan: 'Analytics'
Columns: [
{
name: 'cveId'
type: 'string'

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

@ -0,0 +1,60 @@
param FunctionAppName string
param FunctionAppId string
param PrivateEndpointSubnetId string
param Location string
param VnetId string
resource peFunctionApp 'Microsoft.Network/privateEndpoints@2022-07-01' = {
name: 'pe-${FunctionAppName}'
location: Location
properties: {
subnet: {
id: PrivateEndpointSubnetId
}
privateLinkServiceConnections: [
{
name: 'pe-${FunctionAppName}'
properties: {
privateLinkServiceId: FunctionAppId
groupIds: [
'sites'
]
}
}
]
}
}
resource privateDnsZoneFunctionApp 'Microsoft.Network/privateDnsZones@2020-06-01' = {
name: 'privatelink.azurewebsites.net'
location: 'global'
}
resource privateDnsZoneLinkFunctionApp 'Microsoft.Network/privateDnsZones/virtualNetworkLinks@2020-06-01' = {
name: '${privateDnsZoneFunctionApp.name}-link'
parent: privateDnsZoneFunctionApp
location: 'global'
properties: {
registrationEnabled: false
virtualNetwork: {
id: VnetId
}
}
}
resource peDnsGroupFunctionApp 'Microsoft.Network/privateEndpoints/privateDnsZoneGroups@2022-07-01' = {
name: 'dnsGroup'
parent: peFunctionApp
properties: {
privateDnsZoneConfigs: [
{
name: 'config1'
properties: {
privateDnsZoneId: privateDnsZoneFunctionApp.id
}
}
]
}
}
output functionAppName string = FunctionAppName

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

@ -1,35 +0,0 @@
param kvName string
param location string
param skuFamily string
param skuName string
param principalId string
param secretPermissions array
param aclIpRules string = ''
param aclBypass string = 'None'
param aclDefaultAction string = 'AzureServices'
resource keyVault 'Microsoft.KeyVault/vaults@2022-07-01' = {
name: kvName
location: location
properties: {
sku: {
family: skuFamily
name: skuName
}
tenantId: subscription().tenantId
accessPolicies: [
{
objectId: principalId
permissions: {
secrets: secretPermissions
}
tenantId: subscription().tenantId
}
]
networkAcls: {
bypass: aclBypass
defaultAction: aclDefaultAction
ipRules: aclIpRules == '' ? [] : json('${'[{"value": "'}${replace(aclIpRules, ',', '"},{"value": "')}${'"}]'}')
}
}
}

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

@ -1,22 +1,22 @@
param lawName string
param tableName string
param plan string
param columns array
param retention int = -1
param LawName string
param TableName string
param Plan string
param Columns array
param Retention int = -1
resource logAnalyticsWorkspace 'Microsoft.OperationalInsights/workspaces@2022-10-01' existing = {
name: lawName
name: LawName
}
resource table 'Microsoft.OperationalInsights/workspaces/tables@2022-10-01' = {
parent: logAnalyticsWorkspace
name: tableName
name: TableName
properties: {
schema: {
name: tableName
columns: columns
name: TableName
columns: Columns
}
plan: plan
retentionInDays: retention != -1 ? retention : ''
plan: Plan
retentionInDays: Retention != -1 ? Retention : null
}
}

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

@ -1,17 +1,144 @@
param scopedResourceName string
param roleDefId string
param principalId string
param LawName string
param PrincipalId string
param RoleName string
@description('Array of actions for the roleDefinition')
param Actions array = [
'Microsoft.OperationalInsights/workspaces/read'
'Microsoft.OperationalInsights/workspaces/query/read'
'Microsoft.OperationalInsights/workspaces/analytics/query/action'
'Microsoft.OperationalInsights/workspaces/search/action'
]
resource scopedResource 'Microsoft.OperationalInsights/workspaces@2022-10-01' existing = {
name: scopedResourceName
@description('Array of notActions for the roleDefinition')
param NotActions array = [
'Microsoft.OperationalInsights/workspaces/sharedKeys/read'
]
var roleDescription = 'Provides access to query Sentinel Watchlists and alert rules. Also provides limited permissions to read workspace details and run a query in the workspace, but not to read data from any tables.'
var roleDefName = guid(resourceGroup().id, string(Actions), string(NotActions))
resource law 'Microsoft.OperationalInsights/workspaces@2022-10-01' existing = {
name: LawName
}
resource tableMDVMCveKb 'Microsoft.OperationalInsights/workspaces/tables@2022-10-01' existing = {
parent: law
name: 'MDVMCVEKB_CL'
}
resource tableMDVMRecommendations 'Microsoft.OperationalInsights/workspaces/tables@2022-10-01' existing = {
parent: law
name: 'MDVMRecommendations_CL'
}
resource tableMDVMVulnerabilitiesByDevice 'Microsoft.OperationalInsights/workspaces/tables@2022-10-01' existing = {
parent: law
name: 'MDVMVulnerabilitiesByDevice_CL'
}
resource tableMDVMNistCveKb 'Microsoft.OperationalInsights/workspaces/tables@2022-10-01' existing = {
parent: law
name: 'MDVMNistCveKb_CL'
}
resource tableMDVMNISTConfigurations 'Microsoft.OperationalInsights/workspaces/tables@2022-10-01' existing = {
parent: law
name: 'MDVMNISTConfigurations_CL'
}
resource tableMDVMSecureConfigurationsByDevice'Microsoft.OperationalInsights/workspaces/tables@2022-10-01' existing = {
parent: law
name: 'MDVMSecureConfigurationsByDevice_CL'
}
resource roleDef 'Microsoft.Authorization/roleDefinitions@2022-04-01' = {
name: roleDefName
properties: {
roleName: RoleName
description: roleDescription
type: 'customRole'
permissions: [
{
actions: Actions
notActions: NotActions
}
]
assignableScopes: [
resourceGroup().id
]
}
}
resource roleAssignment 'Microsoft.Authorization/roleAssignments@2020-10-01-preview' = {
name: guid(scopedResource.id, roleDefId, principalId)
scope: scopedResource
resource roleAssignmentWorkspace 'Microsoft.Authorization/roleAssignments@2022-04-01' = {
name: guid(law.id, roleDef.id, PrincipalId)
scope: law
properties: {
roleDefinitionId: roleDefId
principalId: principalId
roleDefinitionId: roleDef.id
principalId: PrincipalId
principalType: 'ServicePrincipal'
}
}
var roleIdReader = '/providers/Microsoft.Authorization/roleDefinitions/acdd72a7-3385-48ef-bd42-f606fba81ae7'
resource roleAssignmentMDVMCveKb 'Microsoft.Authorization/roleAssignments@2022-04-01' = {
name: guid(tableMDVMCveKb.id, roleIdReader, PrincipalId)
scope: tableMDVMCveKb
properties: {
roleDefinitionId: roleIdReader
principalId: PrincipalId
principalType: 'ServicePrincipal'
}
}
resource roleMDVMVulnerabilitiesByDevice 'Microsoft.Authorization/roleAssignments@2022-04-01' = {
name: guid(tableMDVMVulnerabilitiesByDevice.id, roleIdReader, PrincipalId)
scope: tableMDVMVulnerabilitiesByDevice
properties: {
roleDefinitionId: roleIdReader
principalId: PrincipalId
principalType: 'ServicePrincipal'
}
}
resource roleMDVMRecommendations 'Microsoft.Authorization/roleAssignments@2022-04-01' = {
name: guid(tableMDVMRecommendations.id, roleIdReader, PrincipalId)
scope: tableMDVMRecommendations
properties: {
roleDefinitionId: roleIdReader
principalId: PrincipalId
principalType: 'ServicePrincipal'
}
}
resource roleMDVMNistCveKb 'Microsoft.Authorization/roleAssignments@2022-04-01' = {
name: guid(tableMDVMNistCveKb.id, roleIdReader, PrincipalId)
scope: tableMDVMNistCveKb
properties: {
roleDefinitionId: roleIdReader
principalId: PrincipalId
principalType: 'ServicePrincipal'
}
}
resource roleMDVMSecureConfigurationsByDevice 'Microsoft.Authorization/roleAssignments@2022-04-01' = {
name: guid(tableMDVMSecureConfigurationsByDevice.id, roleIdReader, PrincipalId)
scope: tableMDVMSecureConfigurationsByDevice
properties: {
roleDefinitionId: roleIdReader
principalId: PrincipalId
principalType: 'ServicePrincipal'
}
}
resource roleMDVMNISTConfigurations 'Microsoft.Authorization/roleAssignments@2022-04-01' = {
name: guid(tableMDVMNISTConfigurations.id, roleIdReader, PrincipalId)
scope: tableMDVMNISTConfigurations
properties: {
roleDefinitionId: roleIdReader
principalId: PrincipalId
principalType: 'ServicePrincipal'
}
}

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

@ -0,0 +1,329 @@
param PrivateNetworkAddressSpace string
param PrivateEndpointsSubnet string
param FunctionAppSubnet string
param FunctionAppName string
param StorageAccountName string
param StorageAccountId string
param KeyVaultName string
param KeyVaultId string
param location string
param PrincipalId string
param DeployCode bool
resource virtualNetwork 'Microsoft.Network/virtualNetworks@2022-07-01' = {
name: 'vnet-${FunctionAppName}'
location: location
properties: {
addressSpace: {
addressPrefixes: [
PrivateNetworkAddressSpace
]
}
subnets: [
{
name: 'privateEndpoints'
properties: {
addressPrefix: PrivateEndpointsSubnet
networkSecurityGroup: {
id: nsg.id
}
}
}
{
name: 'functionAppVnetIntegration'
properties: {
addressPrefix: FunctionAppSubnet
delegations: [
{
name: 'delegation'
properties: {
serviceName: 'Microsoft.Web/serverFarms'
}
}
]
networkSecurityGroup: {
id: nsg.id
}
}
}
]
}
}
resource peKeyVault 'Microsoft.Network/privateEndpoints@2022-07-01' = {
name: 'pe-${KeyVaultName}'
location: location
properties: {
subnet: {
id: virtualNetwork.properties.subnets[0].id
}
privateLinkServiceConnections: [
{
name: 'pe-${KeyVaultName}'
properties: {
privateLinkServiceId: KeyVaultId
groupIds: [
'vault'
]
}
}
]
}
}
resource peBlob 'Microsoft.Network/privateEndpoints@2022-07-01' = {
name: 'pe-blob-${StorageAccountName}'
location: location
properties: {
subnet: {
id: virtualNetwork.properties.subnets[0].id
}
privateLinkServiceConnections: [
{
name: 'pe-blob-${StorageAccountName}'
properties: {
privateLinkServiceId: StorageAccountId
groupIds: [
'blob'
]
}
}
]
}
}
resource peQueue 'Microsoft.Network/privateEndpoints@2022-07-01' = {
name: 'pe-queue-${StorageAccountName}'
location: location
properties: {
subnet: {
id: virtualNetwork.properties.subnets[0].id
}
privateLinkServiceConnections: [
{
name: 'pe-queue-${StorageAccountName}'
properties: {
privateLinkServiceId: StorageAccountId
groupIds: [
'queue'
]
}
}
]
}
}
resource peFile 'Microsoft.Network/privateEndpoints@2022-07-01' = {
name: 'pe-file-${StorageAccountName}'
location: location
properties: {
subnet: {
id: virtualNetwork.properties.subnets[0].id
}
privateLinkServiceConnections: [
{
name: 'pe-file-${StorageAccountName}'
properties: {
privateLinkServiceId: StorageAccountId
groupIds: [
'file'
]
}
}
]
}
}
resource privateDnsZoneBlob 'Microsoft.Network/privateDnsZones@2020-06-01' = {
name: 'privatelink.blob.${environment().suffixes.storage}'
location: 'global'
dependsOn: [
virtualNetwork
]
}
resource privateDnsZoneFile 'Microsoft.Network/privateDnsZones@2020-06-01' = {
name: 'privatelink.file.${environment().suffixes.storage}'
location: 'global'
dependsOn: [
virtualNetwork
]
}
resource privateDnsZoneQueue 'Microsoft.Network/privateDnsZones@2020-06-01' = {
name: 'privatelink.queue.${environment().suffixes.storage}'
location: 'global'
dependsOn: [
virtualNetwork
]
}
resource privateDnsZoneKeyVault 'Microsoft.Network/privateDnsZones@2020-06-01' = {
name: 'privatelink.vaultcore.azure.net'
location: 'global'
dependsOn: [
virtualNetwork
]
}
resource privateDnsZoneLinkBlob 'Microsoft.Network/privateDnsZones/virtualNetworkLinks@2020-06-01' = {
name: '${privateDnsZoneBlob.name}-link'
parent: privateDnsZoneBlob
location: 'global'
properties: {
registrationEnabled: false
virtualNetwork: {
id: virtualNetwork.id
}
}
}
resource privateDnsZoneLinkFile 'Microsoft.Network/privateDnsZones/virtualNetworkLinks@2020-06-01' = {
name: '${privateDnsZoneFile.name}-link'
parent: privateDnsZoneFile
location: 'global'
properties: {
registrationEnabled: false
virtualNetwork: {
id: virtualNetwork.id
}
}
}
resource privateDnsZoneLinkQueue 'Microsoft.Network/privateDnsZones/virtualNetworkLinks@2020-06-01' = {
name: '${privateDnsZoneQueue.name}-link'
parent: privateDnsZoneQueue
location: 'global'
properties: {
registrationEnabled: false
virtualNetwork: {
id: virtualNetwork.id
}
}
}
resource privateDnsZoneLinkKeyVault 'Microsoft.Network/privateDnsZones/virtualNetworkLinks@2020-06-01' = {
name: '${privateDnsZoneKeyVault.name}-link'
parent: privateDnsZoneKeyVault
location: 'global'
properties: {
registrationEnabled: false
virtualNetwork: {
id: virtualNetwork.id
}
}
}
resource peDnsGroupBlob 'Microsoft.Network/privateEndpoints/privateDnsZoneGroups@2022-07-01' = {
name: 'dnsGroup'
parent: peBlob
properties: {
privateDnsZoneConfigs: [
{
name: 'config1'
properties: {
privateDnsZoneId: privateDnsZoneBlob.id
}
}
]
}
}
resource peDnsGroupFile 'Microsoft.Network/privateEndpoints/privateDnsZoneGroups@2022-07-01' = {
name: 'dnsGroup'
parent: peFile
properties: {
privateDnsZoneConfigs: [
{
name: 'config1'
properties: {
privateDnsZoneId: privateDnsZoneFile.id
}
}
]
}
}
resource peDnsGroupQueue 'Microsoft.Network/privateEndpoints/privateDnsZoneGroups@2022-07-01' = {
name: 'dnsGroup'
parent: peQueue
properties: {
privateDnsZoneConfigs: [
{
name: 'config1'
properties: {
privateDnsZoneId: privateDnsZoneQueue.id
}
}
]
}
}
resource peDnsGroupKeyVault 'Microsoft.Network/privateEndpoints/privateDnsZoneGroups@2022-07-01' = {
name: 'dnsGroup'
parent: peKeyVault
properties: {
privateDnsZoneConfigs: [
{
name: 'config1'
properties: {
privateDnsZoneId: privateDnsZoneKeyVault.id
}
}
]
}
}
var roleIdOwner = '/providers/Microsoft.Authorization/roleDefinitions/8e3af657-a8ff-443c-a75c-2fe8c4bcb635'
resource roleAssignmentVnet 'Microsoft.Authorization/roleAssignments@2022-04-01' = if(DeployCode == true) {
name: guid(subscription().id, resourceGroup().id, virtualNetwork.id)
scope: virtualNetwork
properties: {
principalId: PrincipalId
roleDefinitionId: roleIdOwner
principalType: 'ServicePrincipal'
}
}
resource nsg 'Microsoft.Network/networkSecurityGroups@2023-05-01' = {
name: 'nsg-sentinelmdvm'
location: location
/*
properties: {
securityRules: [
{
name: 'AllowOutboundAzureCloud'
properties: {
protocol: '*'
sourcePortRange: '*'
destinationPortRange: '*'
sourceAddressPrefix: '*'
destinationAddressPrefix: 'AzureCloud'
access: 'Allow'
priority: 100
direction: 'Outbound'
}
}
{
name: 'DenyOutboundInternet'
properties: {
protocol: '*'
sourcePortRange: '*'
destinationPortRange: '*'
sourceAddressPrefix: '*'
destinationAddressPrefix: 'Internet'
access: 'Deny'
priority: 110
direction: 'Outbound'
}
}
]
}
*/
}
output functionAppSubnetId string = virtualNetwork.properties.subnets[1].id
output privateEndpointSubnetId string = virtualNetwork.properties.subnets[0].id
output vnetId string = virtualNetwork.id

Различия файлов скрыты, потому что одна или несколько строк слишком длинны

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

@ -9,6 +9,7 @@ This custom data connector uses a Function App to pull Microsoft Defender Vulner
- *MDVMSecureConfigurationsByDevice_CL* - Secure configuration assessment details for each device.
- *MDVMVulnerabilitiesByDevice_CL* - Vulnerability assessment details for each device.
[![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%2FM365Defender-VulnerabilityManagement%2FazureDeploy.json)
## **Pre-requisites**
1. An Azure Subscription
@ -22,7 +23,7 @@ This custom data connector uses a Function App to pull Microsoft Defender Vulner
## **Deployment Process**
## 1. Deploy Azure Resources
1. Click the appropriate **Deploy to Azure** button below.
1. Click the **Deploy to Azure** button above.
2. Once in the Azure Portal, select the **Subscription** and **Resource Group** to deploy the resources into.
3. Populate the required **Log Analytics Workspace ID** and **Location** parameters. Modify the default parameters as needed but most users can leave these alone.
4. Click **Review and Create**.
@ -51,32 +52,25 @@ foreach ($appRole in $appRoles) {
(Invoke-AzRestMethod -Method POST -Uri ("https://graph.microsoft.com/v1.0/servicePrincipals/" + $resource.Id + "/appRoleAssignedTo") -Payload (ConvertTo-Json $body)).Content | ConvertFrom-Json
}
```
2. Assign the User Assigned Managed Identity Reader access to all management groups/subscriptions that contain Virtual Machine or Arc Server resources. Do this by:
2. Create a custom role to limit the level of access to only read VM's and Arc Servers. Do this by:
1. Navigate to the appropriate management group/subscription in the Azure portal.
2. Select the **Access Control (IAM)** menu.
3. Select **Add** => **Add Role Assignment**.
4. Select the **Reader** Role and click Next.
5. Select **Managed Identity**, Select **Members**, and search for the **User Assigned Managed Identity** created during the deployment. The name was capture in step 6 above.
6. Click Next, then Review and Assign.
3. Select **Add** => **Custom role**
4. Give the custom role a unique name within the tenant.
5. Select the **JSON** tab, and replace the **Actions** section with the below:
```
"actions": [
"Microsoft.Compute/*/read",
"Microsoft.HybridCompute/*/read"
],
```
6. Complete the custom role creation by selecting the **Review + Create** tab, and then clicking **Create**.
3. Assign the User Assigned Managed Identity access to all management groups/subscriptions that contain Virtual Machine or Arc Server resources. Do this by:
1. Navigate to the appropriate management group/subscription in the Azure portal.
2. Select **Add** => **Add Role Assignment**.
3. Select the **Custom Role** that was created in the previous step and click Next.
4. Select **Managed Identity**, Select **Members**, and search for the **User Assigned Managed Identity** created during the deployment. The name was capture in step 6 above.
5. Click Next, then Review and Assign.
<br>
### Non-Network Restricted Deployment
No virtual network or Private Endpoints are deployed and public network access to the Function App and Storage Account is unrestricted. The Key Vault is restricted to only allow access from Function App public IP addresses. Use this for test environments or if you prefer to implement network restrictions yourself after deployment.
[![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%2FM365Defender-VulnerabilityManagement%2FazureDeploy.json)
### Network Restricted Deployment
Function App public access is restricted and a virtual network along with the appropriate Private DNS Zones are created to provide out of the box Private Endpoint connectivity between the Function App and its dependencies (Key Vault and Storage Account).
[![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%2FM365Defender-VulnerabilityManagement%2FazureDeployNetworkRestricted.json)
### Workbook Deployment
A modified version of the Defender for Cloud "Vulnerability Assessment Findings" workbook to include the MDVM data collected by this connector.
[![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%2FM365Defender-VulnerabilityManagement%2Fworkbooks%2FazureDeploy.json)
![image](https://user-images.githubusercontent.com/50784041/232255325-974cce56-b0ca-41df-827e-f97f65589e33.png)
![image](https://user-images.githubusercontent.com/50784041/232255372-23ec5de4-8970-4ee3-9445-dfdf520fe1bc.png)

Различия файлов скрыты, потому что одна или несколько строк слишком длинны

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

@ -1,11 +0,0 @@
# Microsoft Defender Vulnerability Management Workbooks (Preview)
Author: Alex Anders
This set of Azure Workbooks can be used to visualize and filter the MDVM data ingested.
### Worbook Deployment
Click the below Deploy to Azure button to deploy all workbooks into the specified resource group.
[![Deploy to Azure](https://aka.ms/deploytoazurebutton)](https://portal.azure.com/#create/Microsoft.Template/uri/https%3A%2F%2Fraw.githubusercontent.com%2Fanders-alex%2FAzure-Sentinel%2FDataConnector-M365Defender-VulnerabilityManagement%2FDataConnectors%2FM365Defender-VulnerabilityManagement%2Fworkbooks%2FazureDeploy.json)