Deploying Microsoft Sentinel using Bicep - Infrastructure as Code
The Code
Want to skip straight to the good stuff? Just view the source on Github using the button!
Introduction
Microsoft Sentinel is becoming more and more popular, particularly in the cloud space and for usage by MSSPs. If you don’t know what Microsoft Sentinel is, Microsoft has their explanation here: What is Microsoft Sentinel?. If you’re not interested in wading through Microsoft Learn the short answer is this:
Sentinel is a cloud-native Security information and event management (SIEM) and Security orchestration, automation, and response (SOAR) platform. This essentially boils down to the collection of logs and events, analysing those events either retrospectively or proactively (Threat Hunting) with the ability to layer an automated review and response on top of that data. Sentinel can ingest a variety of different logs out of the box with connectors and even more if you are willing to set up your own collector and forward logs in a suitable format.
If you are deploying Sentinel for yourself or your customers it can be hard to keep track of. This is something better managed through code.
Azure Infrastructure as Code
An overview of both Azure Bicep and ARM Templates are available in an earlier post.
Azure Bicep
If you’ve read this blog before you’ll know I have a personal preference for Azure Bicep. In this post we will deploy the resources using Bicep, but part two will venture further into ARM.
Considerations
Sentinel sits on top of a ‘Log Analytics Workspace’. This resource has its own costs and settings so be sure to factor this into your plan. The deployment here includes a 5GB daily ingestion limit so there is a ceiling on the potential ingestion and the associated costs.
Deployment File
The deployment is relatively simple, initially parameters are defined and the two resources deployed afterwards. To ensure that Azure does not try and deploy Sentinel before the Log Analytics Workspace is ready, we are using an implicit dependency, the workspaceResourceID is declared as ‘logAnalyticsWorkspace.id’ which references the deployment name for the Log Analytics Workspace and specifically the ‘.id’ value of that resource:
workspaceResourceId: logAnalyticsWorkspace.id
Biceps views this as an implicit dependency, the deployment of this resource depends upon another. This is opposed to an explicit dependency which is declared using the dependsOn property. Deployment File:
// Parameters
param orgName string = 'default'
param LogAnalyticsWorkspaceName string = '${orgName}-LAW'
param retentionPeriod int = 90
param logIngestionCap int = 5
param allowPublicIngestion string = 'enabled'
param rgLocation string = resourceGroup().location
@allowed([
'Test'
'Development'
'Production'
])
param UsageTagValue string = 'Test'
param purposeTagValue string = 'SIEM'
param tagValues object = {
Usage: UsageTagValue
Purpose: purposeTagValue
}
// Resources
resource logAnalyticsWorkspace 'Microsoft.OperationalInsights/workspaces@2022-10-01' = {
name: LogAnalyticsWorkspaceName
location: rgLocation
tags: tagValues
identity: {
type: 'SystemAssigned'
}
properties: {
publicNetworkAccessForIngestion: allowPublicIngestion
publicNetworkAccessForQuery: allowPublicIngestion
retentionInDays: retentionPeriod
sku: {
name: 'PerGB2018'
}
workspaceCapping: {
dailyQuotaGb: logIngestionCap
}
}
}
resource Sentinel 'Microsoft.OperationsManagement/solutions@2015-11-01-preview' = {
name: 'SecurityInsights(${LogAnalyticsWorkspaceName})'
location: rgLocation
tags: tagValues
properties: {
workspaceResourceId: logAnalyticsWorkspace.id // IMPLICIT Dependency
}
plan: {
name: 'SecurityInsights(${LogAnalyticsWorkspaceName})'
product: 'OMSGallery/SecurityInsights'
promotionCode: '' // This value is required by the API
publisher: 'Microsoft'
}
}
Parameter File
For this deployment I will be using a parameter file, this allows for simple management of parameters for deployment within a repo. Given this deployment is a fairly simple deployment there is not much to it, I use the organisation name to name the resources and a few items that might be configured such as a custom retention period, location, and tags.
Parameter file:
using './main.bicep'
param orgName = 'jmacleandev' // This value is mandatory.
param LogAnalyticsWorkspaceName = '${orgName}-LAW'
param retentionPeriod = 90 // This is the period of retention in days that logs will be retained for, when Sentinel is added on-top of a LAW 90 days is included for free!
param logIngestionCap = 5 // This value is optional // This is the cap in GB of ingested logs per day.
param allowPublicIngestion = 'enabled'
param rgLocation = 'australiaeast' // This value is optional. Bicep will use the default value of the resource group location if the line is commented out or deleted.
param UsageTagValue = 'Test' // Accepted Values are Test, Development, Production
param purposeTagValue = 'SIEM'
param tagValues = {
Usage: UsageTagValue
Purpose: purposeTagValue
}
Deployment via PowerShell
With both Bicep files within the same directory deployment to an existing resource group within Azure is simple. PowerShell Deployment:
New-AzResourceGroupDeployment -Resourcegroupname SentinelTest -TemplateFile ./main.bicep -TemplateParameterFile ./main.bicepparam
Summary
This post has highlighted how to keep a consistent Microsoft Sentinel deployment using Microsoft’s Bicep IaC language. This time we’ve added in a parameter file to allow for specific deployment parameters to be more cleanly managed whilst keeping the deployment 100% consistent.
Overall, this deployment has been fairly simple and leaves us with a clean but empty deployment, to leverage the full value of Sentinel we need to have logs ingested, alerts available and more. All of which will be explored in part two, with a change of pace to using ARM templates.