Azure-Sentinel/DataConnectors/M365Defender-VulnerabilityM.../main.bicep

340 строки
14 KiB
Bicep

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('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('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-mdvm-${uniqueString(resourceGroup().id)}'
@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 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: '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
dependsOn: [
createCustomTables
]
location: location
kind: 'StorageV2'
sku: {
name: 'Standard_LRS'
}
properties: {
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' = {
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
}
]
publicNetworkAccess: EnablePrivateNetworking == true ? 'Disabled' : 'Enabled'
networkAcls: {
defaultAction: EnablePrivateNetworking == true ? 'Deny' :'Allow'
bypass: EnablePrivateNetworking == true ? 'None' : 'AzureServices'
}
}
}
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: FunctionAppName
location: location
sku: {
name: EnableElasticPremiumPlan == true ? 'EP1' : EnablePrivateNetworking == true ? 'P0v3' : 'P0v3'
tier: EnableElasticPremiumPlan == true ? 'ElasticPremium' : EnablePrivateNetworking == true ? 'PremiumV3' : 'PremiumV3'
}
}
resource functionApp 'Microsoft.Web/sites@2022-03-01' = {
name: FunctionAppName
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: {
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}${environment().suffixes.keyvaultDns}/'
}
{
name: 'AzureWebJobsSecretStorageKeyVaultClientId'
value: userAssignedMi.properties.clientId
}
{
name: 'WEBSITE_CONTENTSHARE'
value: toLower(FunctionAppName)
}
{
name: 'WEBSITE_SKIP_CONTENTSHARE_VALIDATION'
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: 'WEBSITE_CONTENTOVERVNET'
value: EnablePrivateNetworking == true ? '1' : '0'
}
{
name: 'LawResourceId'
value: law.id
}
{
name: 'DcrImmutableId'
value: createCustomTables.outputs.DcrImmutableId
}
{
name: 'DceUri'
value: createCustomTables.outputs.DceUri
}
{
name: 'UamiClientId'
value: userAssignedMi.properties.clientId
}
{
name: 'FullImport'
value: '0'
}
{
name: 'DeploymentVersion'
value: deploymentVersion
}
]
powerShellVersion: '7.2'
minTlsVersion: '1.2'
ftpsState: 'Disabled'
http20Enabled: true
alwaysOn: true
publicNetworkAccess: 'Enabled'
}
}
dependsOn: [
keyVaultSecretStorageAccountConnectionString
storageAccount
fileShare
]
}
resource applicationInsights 'Microsoft.Insights/components@2020-02-02' = if (DeployApplicationInsights == true) {
name: 'appInsights-${FunctionAppName}'
location: location
kind: 'web'
properties: {
Application_Type: 'web'
Request_Source: 'rest'
WorkspaceResourceId: law.id
}
}
module roleAssignmentLaw 'modules/lawRoleAssignment.bicep' = {
scope: resourceGroup(split(law.id, '/')[2], split(law.id, '/')[4])
dependsOn: [
createCustomTables
]
name: 'rbacAssignmentLaw'
params: {
PrincipalId: userAssignedMi.properties.principalId
LawName: split(law.id, '/')[8]
RoleName: CustomRoleName
}
}
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: roleIdOwner
principalType: 'ServicePrincipal'
}
}
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: EnablePrivateNetworking == true ? privateNetwork.outputs.privateEndpointSubnetId : (null)
VnetId: EnablePrivateNetworking == true ? privateNetwork.outputs.vnetId : (null)
}
}
resource deploymentScript 'Microsoft.Resources/deploymentScripts@2020-10-01' = if(DeployFunctionCode == true) {
name: 'deployCode'
location: location
kind: 'AzurePowerShell'
identity: {
type: 'UserAssigned'
userAssignedIdentities: {
'${userAssignedMi.id}': {}
}
}
properties: {
azPowerShellVersion: '11.0'
retentionInterval: 'PT1H'
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}'
}
}
output UserAssignedManagedIdentityPrincipalId string = userAssignedMi.properties.principalId
output UserAssignedManagedIdentityPrincipalName string = userAssignedMi.name