Troubleshooting9 min read

App Control event IDs, and reading the CodeIntegrity log properly

Event 3076, 3077, 3089, 8028 and 8029 explained, which of the two logs each lives in, and how to correlate a block with the signature information that explains it.

Most App Control troubleshooting is a reading exercise. The information you need is almost always in the event log already, in a shape that is not obvious, split across two logs that are not next to each other.

If you are searching for WDAC events, this is the same thing. Windows Defender Application Control was renamed App Control for Business, and the logs, the channels and the event IDs did not change with it.

This is what the events mean and how to put them together.

There are two logs, not one

App Control writes to two locations in Event Viewer, and which one depends on what was blocked.

Applications and Services Logs, Microsoft, Windows, CodeIntegrity, Operational covers policy activation and the control of executables, DLLs and drivers.

Applications and Services Logs, Microsoft, Windows, AppLocker, MSI and Script covers MSI installers, scripts and COM objects.

That second log is the one people miss. If a PowerShell script or an MSI is being refused and the CodeIntegrity log looks quiet, you are reading the wrong log. Note also that the AppLocker MSI and Script events are not present on Windows Server Core edition.

The events you will actually use

In the CodeIntegrity Operational log

Event What it means
3076 The main block event for a policy in audit mode. The file would have been blocked if the policy were enforced.
3077 The main block event for an enforced policy. The file did not pass and was blocked.
3089 Signature information for a file that was blocked or audit blocked. One event per signature.
3033 The file did not meet policy requirements. Often means the signature is revoked, or a signature with the Lifetime Signing EKU has expired. Should appear alongside a 3077 if App Control caused it.
3034 The audit mode equivalent of 3033. Not common.
3004 Usually a kernel driver trying to load with an invalid signature. Can occur with or without an App Control policy present.
3099 A policy has been loaded. The details include the policy options, which makes this the event to read when you want to know what is actually active.

In the AppLocker MSI and Script log

Event What it means
8028 A script host asked App Control about a file it was about to run, and the policy was in audit mode. The file ran, but would not have passed an enforced policy.
8029 The enforced equivalent of 8028.
8036 A COM object was blocked.
8037 A script host checked a script and it passed the policy.
8038 Signature information correlated with an 8028 or 8029.
8039 A packaged app was allowed to install or run because the policy is in audit mode, but would have been blocked if enforced.
8040 A packaged app was prevented from installing or running.

The detail that changes how you read 8029

Event 8029 says a script was blocked. That is not quite what happens.

Microsoft's own note on it: "While this event says that a script was blocked, the script hosts control the actual script enforcement behaviour. The script host may allow the file to run with restrictions and not block the file outright."

The important case is PowerShell, which runs a script not allowed by your policy in Constrained Language Mode rather than refusing it. So an 8029 for a PowerShell script does not mean the script did not run. It means it ran without the capabilities Constrained Language Mode removes, which is a different failure and produces different symptoms. A script that half works, or fails on one specific call, is often this.

Correlating a block with its signature information

A 3077 on its own tells you a file was blocked. It does not tell you why the signature was not acceptable. That is what 3089 is for, and the two are joined by an identifier that is easy to overlook.

Each 3089 event carries the signature information for one signature on the file, plus a total count and an index. An unsigned file produces exactly one 3089 with a TotalSignatureCount of 0, which is itself useful: it confirms the file is unsigned rather than signed by something you do not trust.

The events are correlated using the Correlation ActivityID found in the System portion of the event, and 3089 correlates with 3004, 3033, 3034, 3076 and 3077. In the script log, 8038 correlates with 8028 and 8029 the same way.

Practically, that means the useful query is not "show me the 3077s". It is "show me the 3077s and the 3089s, grouped by correlation".

# Blocks and their signature events, from the last day
$events = Get-WinEvent -FilterHashtable @{
    LogName   = 'Microsoft-Windows-CodeIntegrity/Operational'
    Id        = 3076, 3077, 3089
    StartTime = (Get-Date).AddDays(-1)
}

# Pull the correlation id out of the event XML
function Get-Correlation {
    param($Event)
    ([xml]$Event.ToXml()).Event.System.Correlation.ActivityID
}

$events |
    Group-Object { Get-Correlation $_ } |
    ForEach-Object {
        Write-Host "`nCorrelation $($_.Name)" -ForegroundColor Cyan
        $_.Group |
            Sort-Object Id |
            Select-Object TimeCreated, Id, Message |
            Format-List
    }

That grouping is the difference between a list of blocks and an explanation of them.

Diagnostic events for managed installer and reputation

If your policy uses managed installer or the Intelligent Security Graph, three more events explain why something was or was not authorised by them.

Event What it means
3090 Optional. A file was allowed to run based purely on ISG or managed installer.
3091 A file did not have ISG or managed installer authorisation, and the policy is in audit mode.
3092 The enforced equivalent of 3091.

The details on these carry the fields that actually answer the question: ManagedInstallerEnabled, PassesManagedInstaller, SmartlockerEnabled, PassesSmartlocker, AuditEnabled and PolicyName. If you are debugging why an application your deployment tool installed is still being refused, PassesManagedInstaller is the field to look at.

Two operational notes on these:

3090 is off by default. To turn on the allow events, create a TestFlags value and restart:

$ci = 'hklm\system\currentcontrolset\control\ci'
reg add $ci -v TestFlags -t REG_DWORD -d 0x300

Events 3091 and 3092 are inactive on some Windows versions and are also turned on by that command.

Managed installer is noisy. Microsoft warns that with managed installer enabled, 3091 events can fire in large numbers, and that anyone forwarding to Log Analytics may need to filter them out to avoid high cost. If you are shipping these to a SIEM, decide what you are keeping before you turn it on.

These events are also reported per active policy, so multiple events for the same file are expected rather than duplicates.

Policy activation events, and why 3095 matters

When a policy will not apply, the activation events tell you why.

Event What it means
3095 The policy cannot be refreshed and must be rebooted instead.
3096 The policy was not refreshed because it is already up to date. Details include the policy options.
3097 The policy cannot be refreshed.
3099 A policy has been loaded.
3100 The policy was refreshed but was unsuccessfully activated. Retry.
3101 and 3102 A refresh started and finished, for N policies.
3103 The system is ignoring the refresh, for example an inbox Windows policy that does not meet the conditions for activation.

3095 is the one that answers the most common complaint, which is that a policy change was deployed and nothing happened. A refresh is not always possible, and when it is not, the device needs a restart. Option 16 Enabled:Update Policy No Reboot is what allows future updates to apply without one, on Windows 10 1709 and later or Windows Server 2019 and later.

The block message users report

The dialog a person sees does not name the event, so it is worth knowing what maps to what. Depending on the Windows version they may report a message referring to Windows Defender Application Control, or an older one naming Device Guard.

Your organization used Windows Defender Application Control to block this app.

Either way, the device side evidence is a 3077 in the CodeIntegrity Operational log if the policy is enforced, or an 8029 in the AppLocker MSI and Script log if it was a script. Ask for the time and the executable name and you can find the correlated 3089 in seconds.

What App Control does not log

Worth being explicit, because plans get built on the assumption that it does.

Allowed files produce no events by default. App Control does not generate events when a binary is allowed. If you want an allowlist audit trail of everything that ran, this is not it. The optional 3090 covers files authorised by managed installer or ISG, not everything.

COM object failures from GUID mismatches are silent. .NET performs an extra check that the registered COM object GUID matches the calculated one, and does not load the object if they differ. Microsoft states there is no logging or other events when a COM object fails to load due to that check, it only happens when policy is enforced on user mode code, and you cannot detect it in audit mode. If an application starts behaving oddly only under enforcement and nothing appears in either log, this is a candidate.

A reading order that works

When something is blocked and you need an answer quickly:

  1. Establish which log. Executable, DLL or driver goes to CodeIntegrity Operational. Script, MSI or COM goes to AppLocker MSI and Script.
  2. Find the block. 3077 or 3076 in the first, 8029 or 8028 in the second. That gives you the file and the policy.
  3. Take the Correlation ActivityID and pull the 3089, or 8038, events. That gives you the signature position, including whether the file is signed at all.
  4. If the policy uses managed installer or ISG, check for 3091 or 3092 and read PassesManagedInstaller.
  5. Check 3099 for what policy is actually loaded and with which options, rather than what you believe you deployed.

Step 5 is the one that resolves arguments. What is on the device and what is in your deployment tool are not always the same thing.

At fleet scale this stops being a per-device exercise, which is the whole reason we built a control plane for it. If you are working through it manually, why Intune deployed apps still get blocked covers the most common single cause.

Reading these events one at a time is workable on a handful of machines and stops being workable quickly. PoliEze turns them into something you can act on, with each event already attached to an application and the team that owns it, so a block is a decision waiting rather than a hash somebody has to identify first.

Sources

Questions about this

Where do I find WDAC events in Windows?
In two places, which is the part that catches people. WDAC, now called App Control for Business, writes executable, DLL and driver decisions to Applications and Services Logs, Microsoft, Windows, CodeIntegrity, Operational. Scripts, MSI installers and COM objects go to a separate AppLocker MSI and Script log. A blocked script will never appear in the CodeIntegrity log no matter how long you stare at it.
What is the difference between event 3076 and 3077?
3076 is the main block event for a policy in audit mode, meaning the file would have been blocked if the policy were enforced. 3077 is the same event for an enforced policy, meaning the file was actually blocked. Seeing 3076 tells you what enforcement would break.
Why is my blocked script not in the CodeIntegrity log?
Because scripts, MSI installers and COM objects are logged somewhere else. They appear in Applications and Services Logs, Microsoft, Windows, AppLocker, MSI and Script, as events 8028, 8029, 8036 and 8038. Only executables, DLLs and drivers go to the CodeIntegrity Operational log.
What does event 3089 mean?
It carries the signature information for a file that was blocked or audit blocked, one event per signature on the file. An unsigned file produces a single 3089 with a TotalSignatureCount of 0. Match it to the block event using the Correlation ActivityID in the System part of the event.
Does App Control log files that it allows?
Not by default. App Control does not generate events when a binary is allowed. You can turn on allow events for files authorised by a managed installer or the Intelligent Security Graph, which appear as event 3090.

Related reading

Stuck on the thing this article describes?

If you are working through this on a real fleet and it is not going the way the documentation suggests, that is the conversation we are best at. Send us the block events.