Skip to content
KitploitKITPLOIT
ToolsBlog
Submit
ToolsBlog
Submit

Hacking, PenTest, and Cybersecurity Tools for Your Security Arsenal!

Kitploit is a directory of hacking, cybersecurity, and pentesting tools. Discover the latest project updates to find vulnerabilities, analyze systems, automate testing, and strengthen your security.

··Feeds·Contact·Privacy·© 2026 Kitploit

Tool Directory

Categories

View all categories
Loading categories
log-horizon — Microsoft Sentinel SIEM Log Source Analyzer | Kitploit
Tools/GitHubGitHub/lnfernux/log-horizon
Cloud Infrastructure SecurityConfiguration AuditingCloud SecurityDevSecOpsThreat IntelligenceIncident ResponseLog Analysis
GitHublnfernux/log-horizon

log-horizon

Microsoft Sentinel SIEM Log Source Analyzer

View Repository
28317h 57m agoReviewed by Kitploit

Most Popular

View all →

Discover the most used tools by our community.

Explore all tools

Browse our collection of tools

View all tools →
Website
Share

Microsoft Sentinel SIEM Log Source Analyzer

PowerShell 7+ Module Version


I've had to answer "what are we actually getting out of these logs?" or "what is the recommended logs for Microsoft Sentinel" more times than I can count. The answer always depend on so many things, but we can be generic. So I built this thingy right here.

Log Horizon connects to your Microsoft Sentinel workspace (and optionally Defender XDR), goes through every log table you're ingesting, and tells you whether you're getting security value from it or just burning money. It classifies tables, scores them against your detection rules, and gives you concrete recommendations with savings estimates.

Important: This is a generic approach. If you know a log source is important to your environment, that context always takes precedence over what this tool tells you. The classifications are a starting point, not gospel.

Want to read more? I have some posts about Log Horizon on my blog:

  1. Tool Release: Log Horizon
  2. Update: Log Horizon v0.5.0
  3. Building a practical log baseline and how Log Horizon helps you do that
  4. How to use Log Horizon

Features

Download Tool
FeatureDescription
Classification Engine481-entry knowledge base covering 240+ connectors, 22 categories, with lifecycle status (deprecated/legacy plus replacement tables) and automatic heuristic fallback for unknown tables
Cost-Value ScoringPer-table cost tier vs detection tier matrix with combined assessment (High Value to Low Value), priced per observed plan (Analytics, Basic, Data Lake)
Recommendations13 prioritised action types: data lake or Basic candidates, zero-detection tables, XDR streaming waste, ingest-time filtering, split candidates, plan usage, deprecated sources, retention shortfalls, XDR Checker and Detection Analyzer findings, each with savings estimates
Detection MappingMaps analytics rules, hunting queries, and XDR detections to each table to spot coverage gaps
Correlation TagsDetects #DONT_CORR# / #INC_CORR# tags in rule descriptions and flags rules excluded from Defender correlation
Retention ComplianceCompares actual retention against recommended minimums based on industry standards and security best practices
SOC OptimisationPulls Microsoft's own SOC improvement recommendations from the Security Insights API
Keyword Gap AnalysisFlag tables you should be ingesting but aren't based on vendor/product keywords
Transform DiscoveryDiscovers Data Collection Rules (DCRs) targeting the workspace (subscription list filtered on destination, the workspace transformation DCR, and workspace associations), parses inline and multi-stage transforms, and labels every operation (filter, projection, column removal, enrichment, aggregation)
Split Table DetectionIdentifies _SPLT_CL split tables and links them back to parent tables in the classification engine
Split KQL GeneratorGenerates portal-ready split KQL from a curated knowledge base, live rule analysis, and community field frequency stats -- condition-only format that pastes straight into the Sentinel split rule editor. Field lists are intersected with the table's live schema; anything not present is reported as dropped
Detection AnalyzerScores analytic rules for potential noisiness using incident outcomes (auto-close ratio, false positive ratio, and incident volume percentiles)

Disclaimer

[!CAUTION] Disclaimer

This tool is developed and maintained with the help of AI. Please exercise caution when using this solution and always understand what are you running before you run it in production. The developer assumes no liability for any vulnerabilities or issues.

By downloading, installing, or using this tool, you acknowledge that you have read, understood, and agree to these terms.

Prerequisites

What you needVersion
PowerShell7.0+
Az modulesAz.Accounts
Other modulesPwshSpectreConsole 2.6.3+
OptionalMicrosoft.Graph.Authentication (for -IncludeDefenderXDR as a signed-in user)

Endpoints follow the Azure environment of the current Connect-AzAccount session, so Azure Government and Azure China workspaces work without extra parameters. The public cloud values are used when no environment is available.

If you're not already logged into Azure, the module will fire up Connect-AzAccount for you. If you are, it'll just carry on.

Permissions: Log Analytics Reader and Microsoft Sentinel Reader on the workspace cover the analysis. Transform discovery also needs Microsoft.Insights/dataCollectionRules/read (Monitoring Reader) on the subscription or resource group; without it the run continues and prints a warning naming the missing permission. -IncludeDefenderXDR uses Microsoft Graph with CustomDetection.Read.All, which for a signed-in user means the optional Microsoft.Graph.Authentication module.

Getting started

Pretty straight forward:

root@kitploit:~
# Grab the dependencies
Install-Module -Name Az.Accounts -Scope CurrentUser
Install-Module -Name PwshSpectreConsole -Scope CurrentUser

# Clone and import
git clone https://github.com/lnfernux/log-horizon
Import-Module ./log-horizon/LogHorizon.psd1

Usage

The basics

Start by connecting to Azure and making sure you select the right account and subscription:

root@kitploit:~
Connect-AzAccount

Then we can invoke the tool:

root@kitploit:~
Invoke-LogHorizon -SubscriptionId '00000000-0000-0000-0000-000000000000' -ResourceGroup 'rg-sentinel' -WorkspaceName 'my-sentinel-ws'

Output should look something like this:

{F4FFA929-B24F-490C-BD3D-F75E214BCD93}

Also has a menu to dig deeper into other outputs:

{83CE9E6E-F373-49CD-BE05-182DB69F36BE}

Keyword gaps + Defender XDR

Want to know if you're missing tables related to specific vendors? Throw in some keywords. Add -IncludeDefenderXDR if you want the XDR analysis too.

root@kitploit:~
Invoke-LogHorizon -SubscriptionId '...' -ResourceGroup 'rg' -WorkspaceName 'ws' -Keywords 'CrowdStrike','AWS','Okta' -IncludeDefenderXDR

Detection Analyzer

Enable rule quality/noise analysis based on incidents and automation rules:

root@kitploit:~
Invoke-LogHorizon -SubscriptionId '...' -ResourceGroup 'rg' -WorkspaceName 'ws' -IncludeDetectionAnalyzer -DetectionLookbackDays 90

Export a report

root@kitploit:~
# JSON
Invoke-LogHorizon -SubscriptionId '...' -ResourceGroup 'rg' -WorkspaceName 'ws' -Output json -OutputPath ./report.json

# Markdown
Invoke-LogHorizon -SubscriptionId '...' -ResourceGroup 'rg' -WorkspaceName 'ws' -Output markdown -OutputPath ./report.md

# Static HTML (self-contained, no JS, works offline)
Invoke-LogHorizon -SubscriptionId '...' -ResourceGroup 'rg' -WorkspaceName 'ws' -Output html -OutputPath ./report.html

# Auto-generate timestamped filename by pointing at a directory
Invoke-LogHorizon -SubscriptionId '...' -ResourceGroup 'rg' -WorkspaceName 'ws' -Output html -OutputPath ./reports/

Manage table retention and type

You can now update table retention and table type directly from the interactive TUI:

  • Open Invoke-LogHorizon normally, then choose Manage table retention and type from the main menu for bulk retention or type updates.
  • Open Log Tuning / Transforms > Evaluate specific table and choose Manage retention/type for this table for a single-table change.

For scripting or automation, use the dedicated public command:

root@kitploit:~
# Preview a single-table change (prints a Table / Plan / Interactive / Total / Status / Reason table, applies nothing)
Set-LogHorizonTableRetention -SubscriptionId '...' -ResourceGroupName 'rg' -WorkspaceName 'ws' `
  -TableName 'SigninLogs' -TotalRetentionInDays 365 -WhatIf

# Switch tables to Basic and set total retention
Set-LogHorizonTableRetention -SubscriptionId '...' -ResourceGroupName 'rg' -WorkspaceName 'ws' `
  -TableName 'AzureDiagnostics','VMConnection' -TargetPlan Basic -TotalRetentionInDays 730

# Use -1 for inherit/default semantics
# RetentionInDays = inherit workspace default
# TotalRetentionInDays = remove long-term retention
Set-LogHorizonTableRetention -SubscriptionId '...' -ResourceGroupName 'rg' -WorkspaceName 'ws' `
  -TableName 'SigninLogs' -RetentionInDays -1 -TotalRetentionInDays -1

Non-interactive / CI mode

Skip the interactive TUI and export straight to a file, useful for pipelines or scheduled runs:

root@kitploit:~
Invoke-LogHorizon -SubscriptionId '...' -ResourceGroup 'rg' -WorkspaceName 'ws' -NonInteractive -Output json -OutputPath ./reports/

If you omit -Output, the analysis object is returned to the pipeline so you can pipe it into your own logic.

Collection cache

The data collection phase (usage, rules, incidents, tables, DCRs) is cached by default so re-running against the same workspace, for example to export a second format or to reopen the TUI, takes seconds instead of minutes. The cache lives under $env:LOCALAPPDATA\LogHorizon\cache (override with -CachePath), one file per combination of subscription, resource group, workspace, -DaysBack, -DetectionLookbackDays, -IncludeDefenderXDR, -IncludeDetectionAnalyzer, the three price parameters and the module version. Entries older than -CacheMaxAgeMinutes (default 60) are ignored, and every save deletes expired entries so the folder does not accumulate files from other parameter sets or older versions. Tokens are never written to the cache; authentication runs on every invocation so the retention wizard always has live credentials.

The cache file is plaintext Clixml. With -IncludeDetectionAnalyzer it contains incident titles, numbers, status and classification from your workspace; incident owners, rule authors and assigned-owner identities are not collected. Use -NoCache on shared machines or point -CachePath at a location with the access control you need.

root@kitploit:~
# Force a fresh collection and refresh the cache
Invoke-LogHorizon -SubscriptionId '...' -ResourceGroup 'rg' -WorkspaceName 'ws' -RefreshCache

# Never read or write the cache
Invoke-LogHorizon -SubscriptionId '...' -ResourceGroup 'rg' -WorkspaceName 'ws' -NoCache

# Accept cached data for up to a day
Invoke-LogHorizon -SubscriptionId '...' -ResourceGroup 'rg' -WorkspaceName 'ws' -CacheMaxAgeMinutes 1440

Log tuning and split KQL

The interactive TUI includes a Log Tuning / Transforms menu with live tuning suggestions (field usage from your deployed rules and hunting queries), knowledge-base split KQL, and a per-table evaluator. Every KQL block is condition-only and pastes straight into the Sentinel split rule editor, with source attribution (knowledge base, rule analysis, or combined).

Custom pricing

Default prices are West Europe Simplified PAYG in USD: 5.59 $/GB Analytics, 1.15 $/GB Basic, 0.20 $/GB Data Lake (0.07 ingestion + 0.13 processing). Each table is priced by the plan observed in the Usage table, and free status comes from Usage.IsBillable. Volumes use billing GB (1000 MB). If your commitment tier is different:

root@kitploit:~
Invoke-LogHorizon -SubscriptionId '...' -ResourceGroup 'rg' -WorkspaceName 'ws' -PricePerGB 4.61 -BasicPricePerGB 1.15 -LakePricePerGB 0.20

All parameters

ParameterTypeRequiredDefaultDescription
-SubscriptionIdstringYes-Azure subscription ID
-ResourceGroupstringYes-Resource group containing the Sentinel workspace
-WorkspaceNamestringYes-Log Analytics workspace name
-WorkspaceIdstringNo-Workspace ID (auto-resolved if omitted)
-OutputstringNo-Export format: json, markdown / md, or html (alias -o)
-OutputPathstringNo-File or directory path for export. An existing directory or a trailing separator gets a timestamped file name; a file name without an extension gets the format's extension; missing directories are created
-Keywordsstring[]No-Keywords for gap analysis (e.g. 'AWS','CrowdStrike', alias -kw)
-IncludeDefenderXDRswitchNo-Include Defender XDR custom detection analysis
-IncludeDetectionAnalyzerswitchNo-Include per-rule noisy detection analysis using incidents and automation rules
-DetectionLookbackDaysintNo90Query window for incident/automation-based detection analysis (1-365 days)
-DaysBackintNo90Query window for usage data (1-365 days)
-PricePerGBdecimalNo5.59Sentinel Analytics tier ingestion price per GB (alias -ppgb)
-BasicPricePerGB

Under the hood

So there's four phases.

1. Data collection

The module connects to Azure and pulls data from the Log Analytics, Security Insights, Azure Monitor and Microsoft Graph APIs. Every call goes through a retry wrapper (429, 5xx and transport errors) and, unless -NoCache is set, the collected data is cached locally for the next run.

Data SourceAPIWhat we grab
Table usageUsage table (KQL)Ingestion volume, plan and billable flag per table over your query window
Analytics rulesSecurity Insights RESTActive detection rules + which tables they hit + correlation tags, plus implicit table consumers for non-KQL rule kinds
Hunting queriesLog Analytics saved searches RESTSaved hunting queries + referenced tables
Data connectorsSecurity Insights RESTInstalled connector inventory
SOC optimisationSecurity Insights REST (preview)Microsoft's built-in SOC recommendations
Table retentionLog Analytics Tables RESTPer-table retention, archive, plan (Analytics/Basic/Auxiliary), inherited-default flags and schema columns
Data collection rulesAzure Monitor RESTDCRs targeting the workspace (subscription list, workspace transformation DCR, associations) and their transforms
Defender XDRMicrosoft GraphXDR custom detection rules (optional; delegated CustomDetection.Read.All or an Az Graph token)
IncidentsSecurity Insights RESTIncident outcomes (status/classification), timing, and rule-linking hints for rule quality scoring
Automation rulesSecurity Insights RESTRule-level close-incident actions and title matching conditions for auto-close attribution
SentinelHealthLog Analytics KQLAutomation rule run events with incident numbers for definitive auto-close attribution (optional, requires health monitoring)

2. Classification

Every table gets classified through two passes:

First, a direct lookup against the 481-entry knowledge base in Data/log-classifications.json. Each entry carries the connector name, primary/secondary classification, security category, MITRE data source mappings, a recommended pricing tier and retention, and optional lifecycle flags (deprecated/legacy with replacement tables, XDR streamability, platform).

If there's no match, heuristic rules kick in, in this order:

  • Name contains a security token such as Alert, Incident, Threat, Signin, Logon, Audit, Risk, Detection (matched at PascalCase word starts) -> primary
  • Name looks like infra telemetry: Flow, Metric, Diagnostic, Perf, Heartbeat, Health, Inventory, Usage -> secondary
  • Has active analytics rules pointing at it -> primary
  • Built-in table with a Microsoft prefix (AAD, Microsoft, Azure, Defender, Purview, Entra, Sentinel, Office, Intune, Windows ...) -> primary, flagged for review and addition to the database
  • Generic *Log/*Logs name -> secondary
  • High volume (>10 GB/mo) with nothing detecting on it -> secondary
  • None of the above -> unknown

3. Cost-value scoring

Each table gets scored on a few dimensions:

  • Cost tier: Free / Low (<1 GB) / Medium (1-10 GB) / High (10-50 GB) / Very High (>50 GB)
  • Detection tier: None / Low (1-2 rules) / Medium (3-9 rules) / High (10+ rules)
  • Assessment: High Value / Good Value / Missing Coverage / Optimize / Low Value / Underutilized / Free Tier / Platform
  • Coverage %: Percentage of tables with at least one analytics rule or hunting query referencing them, calculated as tablesWithRules / totalTables * 100. Per-table coverage sums analytics rules + hunting queries found by parsing KQL for table names.
  • Implicit coverage: Rule kinds that carry no KQL still consume tables. Data/implicit-consumers.json maps them (Threat Intelligence matching -> ThreatIntelIndicators/ThreatIntelObjects, Fusion -> SecurityAlert/Anomalies, UEBA -> BehaviorAnalytics/UserPeerAnalytics/IdentityInfo, Microsoft incident creation -> SecurityAlert). Enabled rules of those kinds count toward effective coverage, and each table reports a CoverageSource of kql, xdr, implicit, platform or none. Platform tables Sentinel writes for itself (SecurityIncident, SentinelHealth, Watchlist, Usage ...) are never flagged as missing coverage and get the Platform assessment.
  • Only enabled analytics rules and enabled Defender custom detections count toward coverage.

Then the module generates recommendations (13 types):

TypeWhen it firesWhat to do
Data LakeSecondary + high cost + few detections, and the table supports the Auxiliary plan (falls back to a Basic plan suggestion when only Basic is supported)Move to Auxiliary/Data Lake tier; savings are current cost minus the same volume at the lake (or Basic) rate
Low ValueHigh cost + zero detectionsAdd rules, filter, or move to data lake
XDR OptimiseXDR-streamed + 0 Sentinel rules + XDR rules existStop streaming, use the unified XDR portal instead
Missing CoveragePrimary + zero detections (not platform tables)Write analytics rules to get value from the data
Ingest-time FilterPrimary + >20 GB + <=3 detectionsApply ingest-time transformation to cut volume
Split CandidatePrimary + high volume + detections + no existing transformSplit the table so high-value rows stay on Analytics and the rest goes to Data Lake
Plan UsageUsage rows show more than one plan, or the configured plan differs from what Usage observedReview whether the plan transition was expected
Deprecated SourceA table marked deprecated or legacy in the database is still ingestingMigrate detections to the replacement tables, then retire the old connector. Informational: no savings are claimed because the ingestion moves rather than disappears
Retention ShortfallWorkspace or table retention below the 90-day baselineIncrease total/archive retention to meet regulatory guidance
Retention ImprovementPaid, non-platform table meets 90d but sits below the category recommendationConsider longer total retention
Interactive Below BaselineAnalytics table with interactive (hot) retention under 90 daysRaise interactive retention to the 90 days Sentinel includes, unless the short hot window is deliberate
XDR CheckerKnown Defender XDR table not streamed, streamed without coverage, not forwarded to Data Lake, or below the one-year advisoryReview streaming and retention for XDR telemetry
Detection AnalyzerRule scores >= 70 with at least 5 incidents (with )

Recommendations are sorted once, High > Medium > Low and then by estimated savings, and every output (JSON, Markdown, HTML, TUI) keeps that order.

4. Detection Analyzer (noisiness scoring)

When you pass -IncludeDetectionAnalyzer, the module fetches recent incidents and automation rules, then scores every enabled analytics rule for potential noisiness.

Per-rule metrics (computed from incident data):

MetricHow it's calculated
Incidents totalCount of incidents linked to the rule
AutoClose ratioIncidents closed by automation rules ÷ total incidents. Primary source: SentinelHealth table (automation rule runs by enabled close-incident or playbook rules, matched on incident number). Fallback: automation rule condition matching (analytic rule id, title and severity conditions, ANDed like Sentinel does). Rules whose conditions are only status/tactics/entities are treated as applying to every incident.
FalsePositive ratioIncidents classified as false positive ÷ total incidents

Noisiness score formula:

Each metric is converted to a percentile rank across all rules that have at least one incident. The composite score is a weighted blend:

root@kitploit:~
Score = (Volume_percentile × 0.35) + (AutoClose_percentile × 0.40) + (FalsePositive_percentile × 0.25)
  • Volume percentile (35%): how many incidents a rule generates relative to other rules.
  • AutoClose percentile (40%): how often incidents are auto-closed by automation rules (highest weight because automated closure is the strongest signal of low-value alerts).
  • FalsePositive percentile (25%): how often analysts classify the outcome as false positive.

Score thresholds:

ScoreLabelMeaning
≥ 70NoisyRule likely needs tuning or disabling
≥ 50WatchRule shows early signs of noisiness
< 50HealthyRule is within normal range
N/A-Rule has no correlated incidents, or fewer than 3 rules have incidents so there is nothing to rank against

Incidents are bucketed by analytic rule id (falling back to rule name, then title), so two rules sharing a display name are scored separately.

Rules with a score ≥ 70 and at least 5 incidents are automatically surfaced as High-priority recommendations in the Recommendations view.

5. Interactive dashboard

The main menu offers these views:

  • Dashboard: overview stats, top 10 costliest tables (deprecated and legacy sources carry a badge), coverage bar, retention compliance summary, correlation exclusion callout
  • View Recommendations: prioritised actions with estimated monthly savings, expandable to show the full list when there are more than 10
  • View Detection Assessment: cost-value matrix summary, per-table rule and hunting query coverage, primary/secondary drill-down, correlation-excluded rule listing
  • View Detection Analyzer: percentile-based noisy rule ranking with closure quality indicators (when you used -IncludeDetectionAnalyzer), searchable rule browser
  • View SOC Optimization: Microsoft's own improvement suggestions with drill-down
  • View Retention Assessment: tables below recommended minimums with current vs recommended retention, plan type, and shortfall, plus XDR advisory rows
  • View Data Transforms: DCR transform inventory with transform type classification and full KQL per table
  • Log Tuning / Transforms: live tuning suggestions, knowledge-base split KQL, and a per-table evaluator with a single-table retention/type change
  • View All Tables: the full list with classification, plans, cost, rules, retention (colour-coded), and assessment; pick a table for a detail panel (coverage sources, plan support, retention, status, its recommendations)
  • Manage table retention and type: bulk retention and plan wizard with preview and apply
  • Dictionary: every term the tool uses (classification, cost and detection tiers, assessments, coverage sources, the 13 recommendation types, Detection Analyzer metrics and score labels, table plans, lifecycle status, XDR states, transform types) with the same definitions as this README, served from Data/dictionary.json
  • Export Report: pick a format, then a path (directory for a timestamped file, or a file name; Enter keeps the current directory) and write JSON, Markdown or HTML right from the menu
  • XDR Analysis appears on the dashboard when you used -IncludeDefenderXDR

The classification database

Sitting at Data/log-classifications.json. 481 entries, 243 connectors, 22 categories.

What's in each entry

FieldWhat it holds
tableNameLog Analytics table name (SecurityEvent, SigninLogs, etc.)
connectorWhich data connector produces this table
classificationprimary (security value) or secondary (supporting telemetry)
categorySecurity category: Identity & Access, Network Security, etc.
descriptionPlain-English summary of what's in the table
keywordsTerms for keyword gap analysis matching
mitreSourcesMITRE ATT&CK data source mappings
recommendedTieranalytics (hot tier) or datalake (auxiliary candidate)
recommendedRetentionDaysMinimum recommended total retention in days (regulatory guidance)
isFreeWhether Microsoft ingests this one for free (runtime uses Usage.IsBillable first)
statusOptional. deprecated (connector retired or ingestion stopped) or legacy (older collection path with a documented successor)
replacedByOptional. Table names to migrate to; present whenever status is set (may be empty)
xdrStreamableOptional, Defender tables only. true for the 21 tables the Defender XDR connector streams; false for portal-only and TVM tables
platformOptional. true for tables Sentinel itself consumes (SecurityIncident, Usage, Watchlist, ...) which never need analytics rules

Tables with a status show a badge in the TUI and the reports, and any that still ingest raise a DeprecatedSource recommendation naming the replacement. Tables with xdrStreamable: false are never treated as XDR streaming candidates by the XDR Checker.

Primary vs secondary security data

Primary (278 entries): the tables you're actually building detections on. Sign-in logs, security alerts, threat intel, audit trails, vulnerability findings, firewall hits, EDR telemetry.

Secondary (203 entries): supporting stuff. Perf metrics, infrastructure diagnostics, network flow volumes, inventory snapshots, config baselines, health checks.

Categories at a glance

CategoryCountExamples
Identity & Access51SigninLogs, MicrosoftServicePrincipalSignInLogs, OktaSSO
Network Security49AZFWNetworkRule, NSPAccessLogs, DarktraceModelAlerts_CL
Cloud Control Plane37AzureActivity, AZKVAuditLogs, GoogleWorkspaceReports
Network Flow36NTANetAnalytics, CommonSecurityLog, AZFWFatFlow
Endpoint Detection33DeviceProcessEvents, CrowdStrikeAuditEvents, SentinelOneAlertsV2_CL
Application Logs32AppServiceHTTPLogs, AppServiceAuditLogs, DynatraceAttacksV2_CL
Email Security29EmailEvents, CampaignInfo, Ttp_Url_CL
Security Alerts29SecurityAlert, SentinelBehaviorInfo, DisruptionAndResponseEvents
Vulnerability Mgmt23DeviceTvmSoftwareVulnerabilities, Rapid7InsightVMCloudVulnerabilities
Cloud Security20EnrichedMicrosoft365AuditLogs, OAuthAppInfo, PowerAppsActivity
Endpoint Telemetry18DeviceInfo, Windows365NetworkLogs, SentinelOneAgents_CL
Posture Management17ExposureGraphNodes, SecurityNestedRecommendation, ZTSMetadata
Data Security16

Table plan support

Data/basic-plan-tables.json and Data/auxiliary-plan-tables.json list the built-in tables that the Azure Monitor table feature matrix marks as supporting the Basic and Auxiliary (Data Lake) plans. The retention wizard uses the Basic list to validate plan switches, and the DataLake recommendation only proposes the lake tier for tables that support it, falling back to a Basic plan suggestion where that is the lowest supported tier. DCR-based custom tables support both plans; Classic custom tables support neither. Both files are regenerated from that matrix for each release.

Custom classifications

You can provide your own classification file to add entries for tables not in the built-in database, or override existing entries when the defaults don't match your environment. Custom entries take precedence over built-in ones when the same tableName appears in both.

root@kitploit:~
Invoke-LogHorizon -SubscriptionId '...' -ResourceGroup 'rg' -WorkspaceName 'ws' `
    -CustomClassificationPath './my-classifications.json'

The custom file uses the same schema as Data/log-classifications.json, an array of objects:

root@kitploit:~
[
  {
    "tableName": "MyCustomApp_CL",
    "connector": "Custom Logs (DCR)",
    "classification": "primary",
    "category": "Application Logs",
    "description": "Security-relevant audit events from an internal application",
    "keywords": ["custom", "internal", "audit"],
    "mitreSources": [],
    "recommendedTier": "analytics",
    "isFree": false
  },
  {
    "tableName": "AzureMetrics",
    "connector": "Azure Monitor",
    "classification": "primary",
    "category": "Infrastructure Diag",
    "description": "Override: promoted to primary because we detect on Azure resource metrics in this environment",
    "keywords": ["metrics", "azure", "infrastructure", "monitoring"],
    "mitreSources": [],
    "recommendedTier": "analytics",
    "isFree": false
  }
]

See Data/custom-classifications-example.json for a ready-to-use template.

How the classifications were built

The primary/secondary grading was done partially by the author and then by feeding Microsoft's data connector and table definitions into AI with a human grading baseline, using Microsoft best practices and industry standards as the classification criteria.If something looks off for your environment, trust your own context over the tool - AI can make mistakes, and context is king.

The classification criteria were drawn from the following sources:

ACSC (Australian Signals Directorate, Australian Cyber Security Centre)

  • ACSC: Best practices for event logging and threat detection (Aug 2024)
  • ACSC: Priority logs for SIEM ingestion - Practitioner guidance (May 2025)

CISA (Cybersecurity and Infrastructure Security Agency)

  • CISA: Guidance for Implementing M-21-31: Improving the Federal Government's Investigative and Remediation Capabilities
  • CISA: Microsoft Expanded Cloud Logs Implementation Playbook (2025)

Microsoft

  • Microsoft Sentinel data connectors reference
  • Microsoft Sentinel tables & connectors reference
  • Azure-Sentinel GitHub repo (community analytics rules, connector definitions, solution templates)
  • Microsoft Sentinel billing
  • Microsoft Sentinel data tier management

MITRE

  • MITRE ATT&CK Data Sources

NIST (National Institute of Standards and Technology)

  • NIST SP 800-92: Guide to Computer Security Log Management

NSA (National Security Agency)

  • NSA Cyber Event Forwarding Guidance

NCSC-UK (National Cyber Security Centre - United Kingdom)

  • NCSC-UK's "What exactly should we be logging?"

Google Cloud

  • Google Cloud Audit Logs overview
  • Google Cloud Audit Logs best practices

Other sources were also used, along with the authors "expertise" if you can categorize it as such.

Project layout

root@kitploit:~
LogHorizon.psd1              Module manifest (v0.9.0)
LogHorizon.psm1              Module loader
Public/
  Invoke-LogHorizon.ps1              Entry point, the main orchestrator
  Set-LogHorizonTableRetention.ps1   Scriptable table retention and plan changes
Private/
  Connect-Sentinel.ps1       Azure auth + workspace resolution
  Get-TableUsage.ps1         KQL query for ingestion volumes
  Get-AnalyticsRules.ps1     Analytics rules + table/field extraction + correlation tags
  Get-HuntingQueries.ps1     Hunting queries + table extraction
  Get-DataConnectors.ps1     Data connector inventory
  Get-DataTransforms.ps1     DCR transform discovery, split KQL generation
  Get-DefenderXDR.ps1        Defender XDR analysis (optional)
  Get-Incidents.ps1          Incident fetch + SentinelHealth auto-close attribution
  Get-AutomationRules.ps1    Automation rule inventory + close-logic attribution
  Get-SocOptimization.ps1    SOC improvement recommendations
  Get-TableRetention.ps1     Per-table retention, archive, and plan type
  Get-CollectionCache.ps1    Collection cache (key, path, read, write)
  Get-LogHorizonEndpoint.ps1 ARM / Log Analytics / Graph endpoints for the signed-in Azure environment
  Get-LogHorizonDictionary.ps1 Loads the term dictionary shown by the Dictionary menu
  Invoke-AzRestWithRetry.ps1 REST wrapper: retry on 429/5xx/transport errors, async operation polling
  Invoke-Classification.ps1  Static DB + heuristic classification + _SPLT_CL detection + custom file validation
  Invoke-Analysis.ps1        Cost-value matrix + recommendations + Detection Analyzer + XDR Checker + split suggestions
  Set-TableRetention.ps1     Retention change set, preview, Tables API apply engine, plan support lookups
  Write-Report.ps1           Spectre.Console TUI rendering
  Export-Report.ps1          JSON / Markdown / static HTML export with shared section renderer
Data/
  log-classifications.json              481-entry classification knowledge base
  basic-plan-tables.json                Built-in tables that support the Basic plan (from the Azure Monitor feature matrix)
  auxiliary-plan-tables.json            Built-in tables that support the Auxiliary / Data Lake plan
  implicit-consumers.json               Non-KQL rule kinds to tables, plus platform tables
  dictionary.json                       Term definitions for the Dictionary menu (kept 1:1 with this README, enforced by tests)
  high-value-fields.json                Split KQL knowledge base: 165 tables with curated fields and split hints
  field-frequency-stats.json            Community field frequency stats mined from the Azure-Sentinel rule corpus
  custom-classifications-example.json   Example custom classification override file
  ReportTemplate.html                   Static HTML report template (pure-CSS tabs, zero JS, CSP meta)
Tests/
  LogHorizon.Tests.ps1       437 Pester v5 unit tests

How the knowledge bases are generated

The split KQL knowledge bases, high-value-fields.json and field-frequency-stats.json, are built offline from the public Azure/Azure-Sentinel repository (Solutions/, Detections/, Hunting Queries/, roughly 3,800 YAML rule files). Each query is run through the same Get-TablesFromKql and Get-FieldsFromKql the module uses at runtime to build per-table field frequency counts, from which three tiers of fallback fields are derived:

  • Universal fields: fields appearing in more than 50% of all tables (for example TimeGenerated)
  • Category defaults: fields appearing in more than 40% of tables within a classification category
  • Per-table stats: raw field frequency counts for tables with at least 3 referencing rules

Mined fields are merged into the curated high-value-fields.json (curated entries are kept, newly discovered tables with at least 3 rules and 3 meaningful fields are added).

At runtime, Get-SplitKql uses a fallback hierarchy: curated KB entry -> live rule/hunting field analysis -> community per-table stats -> category defaults -> universal fields. Frequency of fields is not a perfect method, but it's useful to know.

Tests

437 Pester v5 tests, no Azure connectivity required. Run them from a plain PowerShell session rather than the VS Code integrated terminal:

root@kitploit:~
Invoke-Pester ./Tests/LogHorizon.Tests.ps1 -Output Detailed

License

GPL-3.0. See LICENSE.

Version history

VersionDateChanges
0.9.02026-09-06Remediation release from a full code and data review. Correctness: plan-aware pricing from Usage.Plan and Usage.IsBillable with Basic and Data Lake rates and billing GB (1000 MB), Detection Analyzer auto-close attribution restricted to enabled close/playbook rules (triggeringLogic.isEnabled), incidents via 2025-09-01 with $top=1000, implicit coverage for non-KQL rule kinds and platform tables (implicit-consumers.json), interactive-retention baseline check, single recommendation sort. Transforms: DCR discovery at subscription scope filtered on destination workspace plus the workspace transformation DCR and associations, with a visible status and warning when a permission is missing; workspace and multi-stage transform parsing; split KQL intersected with the live table schema. Robustness: collection cache on by default (-NoCache, -RefreshCache, -CacheMaxAgeMinutes, -CachePath), authentication before the spinner with warnings printed afterwards, escaped TUI and Markdown output, export path resolution that creates directories and returns the written path, CSP meta in HTML, REST retries on transport errors and Location-style async completion, workspace resolution over REST (Az.Resources dropped), custom classification validation, PascalCase-aware heuristics with a Microsoft first-party fallback, regex timeouts. Endpoints follow the signed-in Azure environment (Government, China) and API versions moved to SecurityInsights 2025-09-01, OperationalInsights 2025-07-01, recommendations 2025-10-01-preview. Data: classification database 345 -> 481 entries with status/replacedBy/xdrStreamable/platform keys, 80+ first-party and 35 successor tables, connector label fixes, isFree corrections; regenerated basic-plan-tables.json and new auxiliary-plan-tables.json from the Azure Monitor table feature matrix; DeprecatedSource recommendation, plan-aware Data Lake recommendation with Basic fallback, XDR Checker honours streamability, lifecycle badges in TUI and exports. Review pass: cache key covers pricing and module version, custom classification booleans and tiers parsed rather than cast, severity-aware auto-close attribution, split KQL predicates checked against the live schema, XDR fetch status surfaced instead of a silent , output paths without an extension are files, incident owner identities no longer collected. Dictionary menu in the TUI with every term the tool uses, backed by and pinned to the code by tests. Automation rule and Defender custom detection objects are projected to the consumed fields, so author identities (createdBy, lastModifiedBy, assigned owners) never reach the cache or exports. GPL-3.0 licence. 437 tests

Known issues

PwshSpectreConsole UTF-8 encoding warning

To enable UTF-8 output in your terminal, add the following line at the top of your PowerShell $PROFILE file and restart the terminal:

root@kitploit:~
$OutputEncoding = [console]::InputEncoding = [console]::OutputEncoding = [System.Text.UTF8Encoding]::new()

The module sets this automatically on import, but depending on your session the warning may still appear. It's cosmetic and doesn't affect functionality.

Contributing

If you have issues or want to contribute, create a PR.

Like the tool?


Buy Me a Coffee at ko-fi.com

XDR CheckerAdds an XDR-focused advisory layer: streaming coverage checks and one-year Data Lake retention guidance for XDR-related telemetry
Custom ClassificationsProvide your own JSON to add or override the built-in classification database
Collection CacheCollected workspace data is cached locally (default 60 minutes) so re-runs and re-exports take seconds; opt out with -NoCache
Sovereign CloudsARM, Log Analytics and Graph endpoints follow the signed-in Azure environment (public, US Government, China)
Interactive TUISpectre.Console dashboard with menus, colour-coded tables, drill-downs, retention wizard and ASCII art
ExportJSON, Markdown, or static HTML report for sharing with the team
decimal
No
1.15
Basic Logs price per GB
-LakePricePerGBdecimalNo0.20Auxiliary / Data Lake tier price per GB (ingestion + processing)
-NonInteractiveswitchNo-Skip the TUI dashboard and export directly (or return data to pipeline if -Output is omitted)
-CustomClassificationPathstringNo-Path to a custom JSON file to add or override classifications (alias -clf)
-NoCacheswitchNo-Do not read or write the collection cache
-RefreshCacheswitchNo-Collect fresh data and overwrite the cache entry
-CacheMaxAgeMinutesintNo60Maximum age of a cache entry to reuse (1-10080)
-CachePathstringNo$env:LOCALAPPDATA\LogHorizon\cacheDirectory for cache files
-IncludeDetectionAnalyzer
Tune or disable the noisy rule
PurviewDataSensitivityLogs, DataSecurityEvents, PowerPlatformDlpActivity
Data Platform16SQLSecurityAuditEvents, CDBControlPlaneRequests, SnowflakeLogin_CL
Platform Health14SentinelHealth, Usage, SecurityCaseEvent
Container & K8s13AKSAudit, CloudProcessEvents, GKEAudit
Infrastructure Diag13AzureMetrics, AGWPerformanceLogs, ContainerAppSystemLogs
Threat Intelligence10ThreatIntelIndicators, ThreatIntelObjects, CybleVisionAlerts_CL
Configuration Mgmt8ConfigurationData, AVNMRuleCollectionChange
SAP Security7ABAPAuditLog, SAPBTPAuditLog_CL, Onapsis_Defend_CL
Storage Access6StorageBlobLogs, CloudStorageAggregatedEvents, AWSS3ServerAccess
IoT/OT Security4RadiflowEvent, DragosAlerts_CL, Phosphorus_CL
$null
Data/dictionary.json
0.8.02026-05-26Added interactive table retention management with a new bulk TUI flow and single-table update entry point, plus the public Set-LogHorizonTableRetention command. Added Tables API PATCH apply engine with validation, Azure async-operation polling, and two-step fallback (combined PATCH, then plan-only plus retention-only) for resilient retention updates. Added focused Pester coverage for validation, payload shape, fallback, and public command mapping. Also fixes an edge-case/bug where users would get recommendations to change data lake tables to data lake tier if they had analytics data still in Sentinel
0.7.12026-05-15Added plan-awareness from Usage.Plan without replacing the configured table plan: analysis now tracks observed plan history, flags multi-plan usage and configured-vs-observed mismatches, and surfaces plan data in the dashboard, table drill-down, View All Tables, retention assessment, and exports. Fixed Detection Analyzer auto-close attribution so the timing heuristic only applies when no enabled automation rules exist. 203 tests passing
0.7.02026-04-16Detection Assessment updated with cost-value matrix summary table (Primary/Secondary x7 assessment categories with color coding), drill-down submenu for primary/secondary tables with cost/detection tier columns. Detection Analyzer updated with GB-weighted volume coverage bars (detection/hunting/combined GB as percentage of total ingestion alongside existing table-count bars). Adaptive display improvements for Detection Analyzer (dynamic bar width, rule name truncation, conditional column hiding based on console width). 193 tests passing
0.6.32026-04-11Minor update for PSGallery
0.6.22026-04-11Log Tuning / Transforms menu: live data tuning analysis (per-table field usage from deployed rules/hunting queries, filter/project/combined KQL generation, savings estimates), schema column extraction from Tables API, Get-SplitKql fallback hierarchy (community stats → category defaults → universal fields), comprehensive table evaluator with field usage matrix, unified KB + live tuning export sections, Build-FieldKnowledgeBase.ps1 mining script for Azure-Sentinel GitHub rule corpus. Detection Analyzer: SentinelHealth-based auto-close attribution (primary) with operator-aware rule-matching fallback, Boolean condition wrapper parsing, Resolved status detection, GUID-tail matching for ARM resource IDs. Coverage now table-count-based across all tables (including free tier). Scoring disclaimer added to TUI and exports. 174 tests
0.6.12026-04-10Bug fixes: $kqlKeywords filtering now shared at file scope (was undefined in Get-TablesFromKql), [CmdletBinding()] added to all helper functions, Defender unified check simplified, removed ghost -RuleCount test param. Robustness: Get-HuntingQueries pagination, Invoke-AzRestWithRetry retry wrapper with exponential backoff for 429/5xx, PricePerGB validation, Write-Verbose in key functions. Docs: version badge, DB count, prerequisites aligned with manifest
0.6.02026-04-10Dynamic XDR streaming detection with 21 KnownXDRTables (was hardcoded 18), per-table XDRState (NotStreaming/Analytics/Basic/Auxiliary), Auxiliary recognized as data lake tier, not-streamed XDR tables surfaced as Information/Low recommendations with NotStreamedCount, retention analyzer shows not-streamed XDR tables as "XDR only (30d)", overview tier breakdown (analytics/basic/data lake + not streamed), Export-Report Auxiliary→"data lake" labels, classification DB updated to 345 entries (+DeviceNetworkInfo, DeviceInfo→secondary/datalake, DeviceImageLoadEvents and IdentityQueryEvents→datalake tier), 15 new Pester tests (121 total)
0.5.02026-04-03Static HTML export with pure-CSS tabs (zero JS, no CDN, fully self-contained), unified MD/HTML section renderer, complete JSON data capture (dataTransforms, correlationExcluded/Included, streamingTables), -NonInteractive switch for CI/pipeline usage, md format alias, datetime-stamped auto-filenames, full KQL display in DCR transforms (no truncation), multiline KQL handling in markdown tables, fixed regex $-backreference corruption in HTML token replacement, renamed internal helpers to avoid PowerShell alias conflicts (h→hEnc, md→mdEsc), 33 new Pester tests (106 total)
0.4.12026-04-03Security & stability fixes - added token memory sanitization, output path validation & XSS protection, REST API pagination limits, fixed module loader error masking, and resolved PSScriptAnalyzer warnings
0.4.02026-04-02Transform discovery (DCR listing + transform type classification), split table detection (_SPLT_CL), split KQL helper with 15-table knowledge base (high-value-fields.json) + rule-analysis fallback, portal-ready condition-only KQL output, expandable recommendations list, split KQL suggestions TUI menu
0.3.02026-04-02Log retention compliance analysis (CISA M-21-31, NIST SP 800-92, NCSC-UK, ASD ACSC, NSA), correlation tag detection (#DONT_CORR#/#INC_CORR#), retention assessment menu view, retention column in All Tables, recommendedRetentionDays in classification schema
0.2.22026-04-02SOC optimization table hides Detail column on narrow consoles
0.2.12026-04-02Custom classification support (-CustomClassificationPath), enriched SOC optimization recommendations with API suggestions/drill-down, active-only default view, UTF-8 encoding warning suppression
0.2.0-Initial public release with classification engine, cost-value scoring, Spectre.Console TUI, export to JSON/Markdown
0.1.0-Internal version for development