Back to blog
AzureBest PracticesCloud SecurityMonitoring & LoggingNetworking

Azure NSG Has No Diagnostic Settings: Why It Matters and How to Fix It

Azure NSGs log nothing by default. Learn why missing NSG diagnostic settings cripple incident response, plus CLI, Terraform, and Azure Policy fixes.

TL;DR

This check flags Azure Network Security Groups that have no diagnostic settings, meaning rule counter and event logs are never sent anywhere. Without those logs you cannot trace which NSG rule blocked or allowed traffic during an incident. Fix it by attaching a diagnostic setting that ships NetworkSecurityGroupEvent and NetworkSecurityGroupRuleCounter logs to a Log Analytics workspace.

Network Security Groups sit at the heart of Azure network access control. They decide which packets reach your VMs, subnets, and NICs. When something goes wrong, a service that suddenly cannot reach its database, or an attacker probing an exposed port, the NSG logs are often the first place you look. The problem is that NSGs do not log anything by default. If diagnostic settings were never configured, that history simply does not exist.

The monitor_nsgnodiagnostics check identifies NSGs in your Azure subscriptions that have zero diagnostic settings attached. It is a small gap that tends to go unnoticed until the moment you need the data most.


What this check detects

Lensix inspects each Network Security Group and queries its diagnostic settings collection. If the collection is empty, the NSG is flagged. A passing NSG has at least one diagnostic setting that routes its logs to a destination such as a Log Analytics workspace, a storage account, or an Event Hub.

The two log categories that matter for an NSG are:

  • NetworkSecurityGroupEvent — records when rules are applied to traffic, based on MAC address and direction.
  • NetworkSecurityGroupRuleCounter — records how many times each rule was matched (hit counts), which is invaluable for spotting unexpected allow or deny activity.

Note: NSG diagnostic logs are not the same as NSG flow logs. Flow logs (configured through Network Watcher) capture per-flow IP and port data and go to a storage account. The diagnostic settings covered by this check capture rule-level events and counters. Mature setups usually want both.


Why it matters

An NSG without diagnostics is a security control that produces no evidence. That creates problems across three areas.

Incident investigation grinds to a halt

Imagine a production app loses connectivity to a backend service. Was it a recent rule change, a priority conflict, or a denied port? With rule counter logs you can see exactly which rule is matching the traffic and how often. Without them, you are reduced to guessing and re-creating the scenario by hand, often while the outage is still active.

The same applies to a suspected intrusion. If an attacker reaches a port that should have been closed, NSG event logs help you confirm whether the NSG allowed it and when. No logs means no timeline, and no timeline means a much harder forensic story.

Compliance frameworks expect it

CIS Azure Foundations, PCI DSS, SOC 2, and ISO 27001 all expect network access controls to be logged and retained. An NSG with no diagnostic settings is a common audit finding, and it is the kind that auditors flag quickly because it is binary: either logs exist or they do not.

Warning: Diagnostic logs only capture events from the moment the setting is enabled. There is no backfill. If you turn this on today, you have no data about yesterday's incident. That is the single biggest reason to enable it proactively rather than reactively.

You lose visibility into rule effectiveness

Rule counter data tells you which rules are actually doing work. A rule that has never matched in 90 days might be stale and safe to remove. A deny rule that matches thousands of times an hour might indicate scanning activity worth alerting on. Without diagnostics, your NSG rules are a black box.


How to fix it

You attach a diagnostic setting to the NSG and point it at a destination. A Log Analytics workspace is the most useful target because it lets you query the data with KQL and build alerts.

Option 1: Azure Portal

  1. Open the Network Security Group in the Azure Portal.
  2. In the left menu, select Monitoring > Diagnostic settings.
  3. Click Add diagnostic setting.
  4. Give it a name, for example nsg-diagnostics.
  5. Under Logs, check both NetworkSecurityGroupEvent and NetworkSecurityGroupRuleCounter (or select allLogs).
  6. Under Destination details, choose Send to Log Analytics workspace and pick your workspace.
  7. Click Save.

Option 2: Azure CLI

First find the resource ID of the NSG and the target workspace, then create the setting.

# Grab the NSG resource ID
NSG_ID=$(az network nsg show \
  --resource-group my-rg \
  --name my-nsg \
  --query id -o tsv)

# Grab the Log Analytics workspace ID
WORKSPACE_ID=$(az monitor log-analytics workspace show \
  --resource-group monitoring-rg \
  --workspace-name central-logs \
  --query id -o tsv)

# Create the diagnostic setting
az monitor diagnostic-settings create \
  --name nsg-diagnostics \
  --resource "$NSG_ID" \
  --workspace "$WORKSPACE_ID" \
  --logs '[
    {"category": "NetworkSecurityGroupEvent", "enabled": true},
    {"category": "NetworkSecurityGroupRuleCounter", "enabled": true}
  ]'

Option 3: Terraform

If you manage infrastructure as code, define the diagnostic setting alongside the NSG so it can never drift back to an undefined state.

resource "azurerm_monitor_diagnostic_setting" "nsg" {
  name                       = "nsg-diagnostics"
  target_resource_id         = azurerm_network_security_group.this.id
  log_analytics_workspace_id = azurerm_log_analytics_workspace.central.id

  enabled_log {
    category = "NetworkSecurityGroupEvent"
  }

  enabled_log {
    category = "NetworkSecurityGroupRuleCounter"
  }
}

Tip: Send all NSG diagnostics to one central Log Analytics workspace rather than per-team workspaces. A single workspace makes cross-NSG queries and alerting far simpler, and it keeps retention policy in one place.

Warning: Log Analytics ingestion and retention are billed by volume. NSG event and rule counter logs are usually low volume, but if you have hundreds of busy NSGs the cost adds up. Set a sensible retention period (30 to 90 days for active investigation, with longer-term archive to a storage account if compliance requires it).


How to prevent it from happening again

Fixing one NSG by hand does not scale. New NSGs get created constantly, and each one starts with no diagnostics. The durable answer is to enforce diagnostics automatically.

Use Azure Policy with DeployIfNotExists

Azure ships a built-in policy that deploys diagnostic settings to NSGs and routes them to a Log Analytics workspace. Assigning it means any new NSG, anywhere in scope, gets diagnostics configured without anyone remembering to do it.

# Look up the built-in policy that deploys NSG diagnostics to Log Analytics
az policy definition list \
  --query "[?contains(displayName, 'network security groups') && policyType=='BuiltIn'].{name:displayName, id:name}" \
  -o table

# Assign it at the subscription scope, passing the target workspace
az policy assignment create \
  --name deploy-nsg-diagnostics \
  --scope "/subscriptions/" \
  --policy "" \
  --params '{"logAnalytics": {"value": ""}}' \
  --location eastus \
  --mi-system-assigned

Note: A DeployIfNotExists policy needs a managed identity with permission to create diagnostic settings and write to the workspace. The --mi-system-assigned flag creates that identity, but you still need to grant it the right roles (Monitoring Contributor and Log Analytics Contributor) before remediation tasks can run.

Gate infrastructure in CI/CD

If you provision NSGs through Terraform or Bicep, add a check to your pipeline that fails when an NSG is defined without an accompanying diagnostic setting. Tools like Checkov, tfsec, and Open Policy Agent can enforce this before code ever reaches Azure.

# Example: run Checkov against a Terraform plan in CI
checkov -d ./infra --framework terraform \
  --check CKV_AZURE_NSG_DIAGNOSTICS || exit 1

Run a remediation task across existing resources

Policy assignments only auto-deploy to new and updated resources. For NSGs that already exist, trigger a remediation task so they get backfilled with the setting.

az policy remediation create \
  --name remediate-nsg-diagnostics \
  --policy-assignment deploy-nsg-diagnostics \
  --resource-discovery-mode ReEvaluateCompliance

Best practices

  • Enable both log categories. Event logs and rule counter logs answer different questions. Capture both so you are not missing half the picture during an investigation.
  • Pair diagnostics with NSG flow logs. Rule-level diagnostics tell you which rule fired; flow logs tell you the actual source and destination IPs and ports. Together they give a complete view.
  • Centralize the destination. One Log Analytics workspace per environment keeps queries, alerts, and retention consistent and avoids scattered, half-monitored NSGs.
  • Set retention deliberately. Keep hot data in Log Analytics for active use and archive to a cheaper storage account or table-level archive for long-term compliance retention.
  • Build alerts on rule counters. A sudden spike in deny-rule matches can be an early signal of scanning or a misconfigured deployment. Wire a KQL alert to catch it.
  • Enforce, do not remind. Manual processes drift. Use Azure Policy and CI/CD gates so diagnostics are a default property of every NSG, not a task someone has to remember.

An NSG with no diagnostic settings is a quiet gap. It costs nothing while everything is working and becomes very expensive the moment you need to explain what happened. Turn it on once, enforce it everywhere, and you keep the audit trail you will eventually be glad you have.