Introduction
When you set up Microsoft Sentinel and install items from the Content Hub – such as data connectors, workbooks, and analytics rules – it’s tempting to think it’s a one-and-done job. In reality, these resources evolve. New versions get published to the Microsoft Sentinel Content Hub catalog, updates appear on GitHub repositories, and improved analytics rules help you respond better to evolving threats. If you never review or update what’s installed, you could miss critical enhancements that strengthen your security posture.
I’ve found that regularly checking for new versions of these packages ensures my SIEM environment, driven by Microsoft Sentinel’s SOAR capabilities, remains effective. Stale or outdated connectors and content can limit visibility and responsiveness. Manually sifting through lists is inefficient. That’s why I developed two PowerShell scripts: one to identify what’s installed, and another to pinpoint which solutions need updates. From there, it’s straightforward to act on available updates, ensuring your Microsoft Azure Sentinel setup is always leveraging the latest innovations.
By running these checks on a schedule, you prevent your environment from falling behind. This process becomes even easier if you integrate it into an Azure Automation runbook. Instead of occasionally remembering to run manual queries, you can automate the entire cycle – detecting what needs attention, producing a clear HTML and JSON summary, and then taking action. Over time, this proactive approach helps maintain a healthier Microsoft Sentinel deployment, supports continuous improvement in your analytics rules and data connectors, and ensures that you are never caught off-guard by outdated content.
The Scripts
Below are the two scripts. The first one inventories your current Content Hub solutions. The second compares installed solutions against what’s available and outputs both an HTML report (for human-friendly review) and a JSON file (for programmatic use). With this information, you’ll quickly see which updates are needed – no guesswork or missed opportunities.
Export-InstalledSentinelContentHubPackages
<#
=================================================================================
DISCLAIMER:
This script is provided "as-is" with no warranties. Usage of this script is at
your own risk. The author is not liable for any damages or losses arising from
using this script. Please review the full legal disclaimer at:
https://kaidojarvemets.com/legal-disclaimer/
=================================================================================
#>
# Variables for Azure environment
$SubscriptionId = ""
$ResourceGroupName = ""
$WorkspaceName = ""
$ApiVersion = "2024-03-01"
# Construct the API URL
$BaseUri = "https://management.azure.com"
$ResourcePath = "/subscriptions/$SubscriptionId/resourceGroups/$ResourceGroupName/providers/Microsoft.OperationalInsights/workspaces/$WorkspaceName/providers/Microsoft.SecurityInsights"
$Endpoint = "contentPackages"
$ApiVersionParam = "?api-version=$ApiVersion"
$FullUri = "$BaseUri$ResourcePath/$Endpoint$ApiVersionParam"
# Function to get installed Content Hub solutions
function Get-InstalledContentHubSolutions {
param (
[string]$Uri
)
try {
$Response = Invoke-AzRestMethod -Method GET -Uri $Uri -ErrorAction Stop
if ($Response.StatusCode -eq 200) {
$Content = $Response.Content | ConvertFrom-Json
return $Content.value
}
else {
Write-Error "API call failed with status code: $($Response.StatusCode)"
return $null
}
}
catch {
Write-Error "An error occurred: $($Error[0])"
return $null
}
}
# Get the installed solutions and format output
$InstalledSolutions = Get-InstalledContentHubSolutions -Uri $FullUri
$InstalledSolutions | Select-Object @{
Name = 'DisplayName'; Expression = { $PSITEM.properties.displayName }
}, @{
Name = 'ContentId'; Expression = { $PSITEM.properties.contentId }
}, @{
Name = 'Version'; Expression = { $PSITEM.properties.version }
}, @{
Name = 'ContentKind'; Expression = { $PSITEM.properties.contentKind }
} | Format-Table -AutoSize
Create-InstalledSentinelContentHubPackagesReports
<#
=================================================================================
DISCLAIMER:
This script is provided "as-is" with no warranties. Usage of this script is at
your own risk. The author is not liable for any damages or losses arising from
using this script. Please review the full legal disclaimer at:
https://kaidojarvemets.com/legal-disclaimer/
=================================================================================
#>
# Variables for Azure environment
$SubscriptionId = ""
$ResourceGroupName = ""
$WorkspaceName = ""
$ApiVersion = "2024-03-01"
# Construct the API URLs
$BaseUri = "https://management.azure.com"
$ResourcePath = "/subscriptions/$SubscriptionId/resourceGroups/$ResourceGroupName/providers/Microsoft.OperationalInsights/workspaces/$WorkspaceName/providers/Microsoft.SecurityInsights"
$InstalledEndpoint = "contentPackages"
$AvailableEndpoint = "contentProductPackages"
$ApiVersionParam = "?api-version=$ApiVersion"
$InstalledUri = "$BaseUri$ResourcePath/$InstalledEndpoint$ApiVersionParam"
$AvailableUri = "$BaseUri$ResourcePath/$AvailableEndpoint$ApiVersionParam"
function Get-ContentHubSolutions {
param (
[string]$Uri
)
try {
$Response = Invoke-AzRestMethod -Method GET -Uri $Uri -ErrorAction Stop
if ($Response.StatusCode -eq 200) {
$Content = $Response.Content | ConvertFrom-Json
return $Content.value
}
else {
Write-Error "API call failed with status code: $($Response.StatusCode)"
return $null
}
}
catch {
Write-Error "An error occurred: $($Error[0])"
return $null
}
}
# Get both installed and available solutions
$InstalledSolutions = Get-ContentHubSolutions -Uri $InstalledUri
$AvailableSolutions = Get-ContentHubSolutions -Uri $AvailableUri
# Find solutions that need updates
$UpdateNeeded = foreach ($Installed in $InstalledSolutions) {
$Available = $AvailableSolutions | Where-Object {
$PSITEM.properties.displayName -eq $Installed.properties.displayName
}
if ($Available.properties.version -gt $Installed.properties.version) {
[PSCustomObject]@{
DisplayName = $Installed.properties.displayName
CurrentVersion = $Installed.properties.version
AvailableVersion = $Available.properties.version
ContentId = $Installed.properties.contentId
}
}
}
function Export-ContentHubUpdatesToHtml {
param (
[Parameter(Mandatory = $true)]
[Array]$UpdateData,
[Parameter(Mandatory = $true)]
[string]$FilePath
)
try {
$HtmlHeader = @"
"@
$HtmlBody = @"
Microsoft Sentinel Content Hub Updates Report
Report Generated: $(Get-Date -Format 'yyyy-MM-dd HH:mm:ss')
Total Solutions Requiring Updates: $($UpdateData.Count)
"@
$TableData = $UpdateData | ConvertTo-Html -Fragment -As Table
$FullHtml = @"
$HtmlHeader
$HtmlBody
$TableData
"@
$FullHtml | Out-File -FilePath $FilePath -ErrorAction Stop
Write-Output "HTML report exported to: $FilePath"
}
catch {
Write-Error "Failed to export HTML report: $($Error[0])"
}
}
function Export-ContentHubUpdatesToJson {
param (
[Parameter(Mandatory = $true)]
[Array]$UpdateData,
[Parameter(Mandatory = $true)]
[string]$FilePath
)
try {
$JsonObject = [PSCustomObject]@{
ReportGenerated = (Get-Date -Format 'yyyy-MM-dd HH:mm:ss')
TotalUpdatesNeeded = $UpdateData.Count
Solutions = $UpdateData
}
$JsonObject | ConvertTo-Json -Depth 10 | Out-File -FilePath $FilePath -ErrorAction Stop
Write-Output "JSON export completed to: $FilePath"
}
catch {
Write-Error "Failed to export JSON file: $($Error[0])"
}
}
# Example usage:
$ReportPath = "C:\Temp\ContentHubUpdates_$(Get-Date -Format 'yyyyMMdd').html"
$JsonPath = "C:\Temp\ContentHubUpdates_$(Get-Date -Format 'yyyyMMdd').json"
Export-ContentHubUpdatesToHtml -UpdateData $UpdateNeeded -FilePath $ReportPath
Export-ContentHubUpdatesToJson -UpdateData $UpdateNeeded -FilePath $JsonPath
I recommend integrating these scripts into your CI/CD pipeline or, even better, an Azure Automation runbook. Doing so creates a self-sustaining cycle of checks and updates. The HTML report can guide your team’s review process, and the JSON output can feed into other systems or workflows. This approach ensures you stay aligned with the Microsoft Sentinel Content Hub’s evolving catalog and never let your data connectors, analytics rules, or workbooks gather dust.
Conclusion
By regularly identifying and applying Microsoft Sentinel Content Hub updates, you move from a static, potentially outdated security configuration to a dynamic, continuously improving one. No more combing through lists, no more missing important new capabilities. Just streamlined updates and a more resilient Microsoft Azure Sentinel environment that’s always ready for the latest threats and opportunities.