Skip to main content
emnode
Compliance

Turn on diagnostic logging for Azure resources

One capability across the estate: make sure every resource that can be attacked is also writing the logs you would need to reconstruct what happened, so an incident leaves a trail instead of a blank.

14 min·10 sections·AZURE

Last reviewed

Diagnostic logging: the basics

What does a resource with logging switched off actually cost you?

Most Azure resources can emit two very different kinds of telemetry. Metrics are numbers over time: request counts, CPU, response times. Diagnostic logs are the detailed event records: every HTTP request to an App Service, every console line an app writes, every authentication and audit event. By default a resource emits metrics but throws its diagnostic logs away. Nothing breaks, the service runs exactly the same, and the only thing you lose is the one thing you cannot recreate after the fact: the record of what actually happened.

Diagnostic settings are the switch that changes that. A diagnostic setting on a resource names a set of log categories and a destination: a Log Analytics workspace to query them, an Event Hub to stream them to a SIEM, or a storage account to retain them cheaply. Until a diagnostic setting exists, the categories that matter for security, the HTTP logs, the console output, the audit records, are generated and discarded. Defender for Cloud raises 'Diagnostic logs in App Service should be enabled' for exactly this reason: an App Service with no diagnostic setting cannot tell you, after an incident, who reached it, what they asked for, or what the app did in response.

This is almost always drift rather than a decision. Nobody chooses to be blind; the diagnostic setting was simply never added when the resource was created, because the resource works fine without it. The capability here is to find every resource of a type that matters, App Service first because it is internet-facing, confirm it has a diagnostic setting pointed at a destination you retain, and then make that the default so new resources are never created without one. It is the difference between an incident you can investigate and one you can only guess at.

In this lesson you will learn what Azure diagnostic settings actually capture, why a resource with logging off looks healthy right up until you need it, and how to turn logging on across the resources that matter without an open-ended bill. The Controls this lesson covers section lists every Defender for Cloud recommendation in this capability, starting with diagnostic logs for App Service, each linking to a deep page with the exact check and a copy-and-paste fix.

Fun fact

The breach that left no footprints

In a great many published incident reports the most damaging line is not what the attacker did, it is what the defenders could not see. Investigators routinely find that the relevant logs were never being collected, so the dwell time, the scope and the data accessed can only be estimated. The reason is mundane: diagnostic logging is off by default on most Azure resources, the service runs perfectly well without it, and the missing setting goes unnoticed precisely because nothing is broken. The log you most need in an incident is the one you have to decide to keep before the incident, because once it has been discarded there is no recovering it.

Finding resources running with logging off

Priya is the security lead at a SaaS company heading into a SOC 2 Type II audit, where the auditor will ask for evidence that activity on customer-facing systems is logged and retained. Defender for Cloud shows 'Diagnostic logs in App Service should be enabled' across most of the company's web apps.

Rather than enable everything blindly and worry about the bill later, Priya starts by listing which App Services have no diagnostic setting at all, so the internet-facing ones can be prioritised and pointed at a retained destination first.

List every App Service and check whether it has any diagnostic setting at all. The ones with none are recording nothing.

$ for id in $(az webapp list --query "[].id" -o tsv); do n=$(basename "$id"); c=$(az monitor diagnostic-settings list --resource "$id" --query "length(value)" -o tsv); echo "$n settings=$c"; done
checkout-api-prod settings=0
marketing-site settings=0
internal-admin settings=1
# checkout-api-prod is internet-facing and recording nothing. Fix it first.

An internet-facing App Service with zero diagnostic settings is the highest-value target: it is the most likely to be attacked and the least able to tell you what happened.

How Defender for Cloud decides a resource is not loggingdeep dive

Under the recommendation 'Diagnostic logs in App Service should be enabled' is a simple check: does the App Service have at least one diagnostic setting that enables the relevant log categories. App Service exposes several, including AppServiceHTTPLogs (every inbound request), AppServiceConsoleLogs and AppServiceAppLogs (what the application writes), AppServiceAuditLogs and AppServiceIPSecAuditLogs (publishing and access events), and AppServicePlatformLogs. A resource with no diagnostic setting emits none of these to any durable destination, so the check fails. Note that this is distinct from the older App Service logging configured with 'az webapp log config', which writes to the filesystem or a blob: the modern, security-relevant path is an Azure Monitor diagnostic setting routed to a workspace, Event Hub or storage account.

Defender for Cloud evaluates the Microsoft Cloud Security Benchmark control LT-3, enable logging for security investigation, on a periodic cycle, so adding a diagnostic setting does not flip the recommendation to Healthy the instant you save it. Note that adding a diagnostic setting writes app settings to the App Service, which triggers an app restart, so there is a brief restart before logs begin flowing, and the posture only catches up on the next assessment. This matters when you are assembling audit evidence against a deadline: turn logging on with lead time so both the logs and the green recommendation exist before the auditor looks.

The strongest position pairs the per-resource setting with an Azure Policy backstop. Built-in 'deploy if not exists' policies, such as the App Service diagnostic-settings policies, will automatically create a diagnostic setting pointed at your chosen workspace on any resource that lacks one. That converts logging from a thing each team must remember into a guarantee the platform enforces, which is the difference between confirming everything is logging today and ensuring everything logs tomorrow.

What is the impact of running with logging off?

The direct impact lands at the worst moment: during an incident. With no diagnostic logs, you cannot reconstruct who reached an App Service, what they requested, or how the application responded. You cannot establish dwell time, scope or whether data left the building. The investigation has no source material, so you are forced to assume the worst case, which drives the most expensive response: broadest notification, widest credential rotation, deepest forensic engagement, all because you could not narrow the blast radius with evidence you chose not to keep.

The second-order impact is that logging is uniquely unrecoverable. You can tighten a firewall after the fact and be safe going forward, but you cannot retroactively produce a log for an event that was never recorded. The decision to keep a log has to be made before the event it would capture. That asymmetry is why this control is worth acting on proactively rather than after a near miss, and why it is so often the line in an incident report that turns a contained problem into an open question.

On the compliance side, every serious framework, ISO 27001, SOC 2, HIPAA, PCI DSS and the UK and EU data-protection regimes, requires that activity on systems handling sensitive data is logged, retained for a defined period, and available for investigation. The Microsoft Cloud Security Benchmark codifies this as LT-3. A diagnostic setting on every relevant resource, routing to a retained destination, is the artefact those audits ask for, and the cheapest way to be able to answer 'what happened?' with a record instead of a guess.

How do you turn logging on without an open-ended bill?

Work the capability as one loop rather than chasing individual findings. The order matters: turn on the high-value categories on the most exposed resources first, route each category to a destination that matches its value, then make the setting a default so new resources are never created blind.

1. Inventory every resource by logging state

List the resources of each type that matters and check whether each has a diagnostic setting at all. Start with App Service because it is internet-facing, then extend the same loop to other resource types Defender flags. A resource with zero diagnostic settings is recording nothing, regardless of how healthy it otherwise looks.

2. Prioritise by exposure, then route by value

Internet-facing resources holding or fronting sensitive data come first. For each, decide which categories matter: HTTP and audit logs are the security-relevant ones for App Service and justify a queryable destination such as Log Analytics. Bulkier, lower-value categories can go to cheap storage for retention or be left off, so cost tracks the value of the evidence.

3. Create the diagnostic setting, highest exposure first

Add a diagnostic setting that enables the security-relevant categories and points them at your chosen destination. Adding the setting writes app settings to the App Service, which triggers a brief app restart before logs begin flowing, and Defender re-assesses to Healthy on its own cycle. Prioritise resources that front data over internal tooling. Confirm retention on the destination meets your framework's requirement.

4. Make it the default with Azure Policy

Assign the built-in 'deploy if not exists' diagnostic-settings policies so any new App Service, or other resource type, automatically gets a diagnostic setting pointed at your central workspace. This needs a managed identity with permission to deploy the setting. The backstop converts logging from something teams must remember into something the platform guarantees.

# Turn on the security-relevant App Service logs and send them to a workspace.
WS=$(az monitor log-analytics workspace show \
  -g central-logging -n estate-logs --query id -o tsv)

for id in $(az webapp list --query "[].id" -o tsv); do
  has=$(az monitor diagnostic-settings list --resource "$id" \
        --query "length(value)" -o tsv)
  if [ "$has" = "0" ]; then
    az monitor diagnostic-settings create \
      --name to-log-analytics \
      --resource "$id" \
      --workspace "$WS" \
      --logs '[{"category":"AppServiceHTTPLogs","enabled":true},
               {"category":"AppServiceAuditLogs","enabled":true},
               {"category":"AppServiceConsoleLogs","enabled":true}]'
    echo "$(basename "$id"): diagnostic logging enabled to estate-logs"
  fi
done

# Ratchet it shut: built-in policy adds the setting to any new App Service.
az policy assignment create \
  --name deploy-appservice-diag \
  --policy 0c6cd767-1a1d-484b-a897-68e398c03aeb \
  --location uksouth \
  --mi-system-assigned \
  --scope "/subscriptions/00000000-0000-0000-0000-000000000000"

Quick quiz

Question 1 of 5

Defender for Cloud raises 'Diagnostic logs in App Service should be enabled' on several apps. What is the most efficient way to think about it?

You can now treat diagnostic logging as one capability rather than a scatter of findings: inventory which resources have no diagnostic setting, prioritise the internet-facing ones such as App Service, route each category to a destination that matches its value, and make logging a default with an Azure Policy backstop. The Controls this lesson covers section below links every recommendation in this group to its deep page and fix.

Back to the library

Controls this lesson covers

One capability, many Microsoft Defender for Cloud recommendations. This lesson is the shared playbook; each control below keeps its own deep page with the exact check, severity and a copy-and-paste fix.