tracking-microsoft-defender-pua-policy-changes-with-intune-custom-attributes

Tracking Microsoft Defender PUA Policy Changes with Intune Custom Attributes

Last Updated on March 4, 2025 by Oktay Sari

In the ever-evolving battleground of endpoint security, Microsoft Defender for Endpoint stands as a vigilant guardian against the rising tide of cyber threats. But even the best guardians can sometimes be a bit… forgetful. What happens when your Microsoft Defender PUA Policy settings start changing mysteriously on your macOS devices? In my previous post we looked at Advanced macOS Protection with Microsoft Intune and I think this post is all about that principle.

The Mystery of the Changing PUA Policies

🚨 Before you sound the security alarm—don’t worry! We’ve tested in multiple tenants, and this only occurs in one specific tenant, strongly suggesting a unique configuration issue rather than a widespread bug or security risk. So no need to cancel your weekend plans or dig up that end-of-the-world playlist from 2012. The investigation is still ongoing, but this approach might help others facing similar configuration mysteries! And on a personal note; I keep on learning…

Shout-out to my partner in crime (and my personal Inspector Gadget) Melvin Luijten for helping with the investigation!

The Problem

Picture this scenario: You’ve configured and deployed your Microsoft Defender for Endpoint PUA Policy (Potentially Unwanted Application) across your macOS fleet. Everything is running smoothly until one day, by sheer luck, you notice that some devices have different settings than what you deployed through Intune. No policy changes were made on your end, yet somehow, the settings drifted on their own. Like finding your furniture rearranged overnight!

macos-defender-for-endpoint-pua-configuration

After some investigation, we discovered that on certain macOS devices, the Microsoft Defender PUA Policy would occasionally flip between “audit” and “off” modes without any administrator intervention. This ghost in the machine wasn’t just a curiosity but represented a significant security concern. Were our policies being compromised? Was there an issue with Defender for Endpoint itself? Or perhaps an Intune configuration profile problem?

The Challenge

The problem was clear, but the solution not so much. Intune doesn’t natively provide detailed historical tracking of policy changes at the device level, so we needed a way to:

  1. Monitor the current PUA policy state on each device
  2. Track when configuration changes occurred
  3. Report this info back to Intune for visibility

In other words, we needed a lightweight, digital private investigator like Sherlock Holmes, but without the pipe-smoking. From the terminal, you can fire the command âžś ~ mdatp threat policy list to see the current status.

mdatp threat policy list

I’ll update this blog with more information about what logfiles to check while investigating this behavior.

Who doesn’t love command line adventures?

I know what you’re thinking: “There are probably a dozen solutions that could tackle this problem with fancy UIs and colorful dashboards.” And you’d be right! But where’s the fun in that?

I’m on this quest to improve my bash scripting skills (a journey that oscillates between moments of “I am a command line wizard!” and “Why isn’t this working? I hate computers.“). So, when faced with mysteriously changing PUA configurations, obviously my first thought was “this sounds like a perfect excuse to write some more bash!

Before you start climbing to your rooftop to shout about better alternatives, I’ll acknowledge there are certainly other approaches. Scroll down to the end of this post to read about some alternatives I considered. I also invite you to share your insights on how you would tackle this peculiar problem.

And yes, we’re working with Microsoft to investigate this odd Microsoft Defender PUA Policy changes further. In the mean time, I got carried away…

The Two-Script Solution

To tackle this challenge, I’ve developed a two-script system that works together to provide comprehensive tracking of PUA policy changes:

  1. Enhanced MDATP Policy Checker: Monitors Defender’s PUA policy and logs changes. The Watson to your Holmes.
  2. Custom Attribute Script: Reports these changes to Intune for centralized visibility. The Inspector Lestrade who actually listens (for those who didn’t watched Sherlock Holmes—It might be an age thingy…).

Let’s break down how each component works and how they collaborate to solve our mystery.

Enhanced MDATP Policy Checker: The Detective

The first script in our duo is responsible for checking the current state of Microsoft Defender PUA policy and maintaining a history of changes. Think of it as the detective who’s constantly gathering clues, minus the caffeine addiction and troubled personal life common to TV detectives.

What It Does

This script performs several functions:

  • Verifies that Microsoft Defender processes are running
  • Locates and validates the MDATP command-line tool
  • Retrieves the current Microsoft Defender PUA policy configuration
  • Logs both the current state and a history of changes

The Heart of the Script

The core functionality revolves around running the mdatp threat policy list command and analyzing its output like a digital lie detector test:

#-------------------------------------------------------------------------------
# Function: analyze_pua_configuration
# Purpose: Analyze the PUA configuration from the threat policy
# Uses global variable PUACHECK
#-------------------------------------------------------------------------------
analyze_pua_configuration() {
    log "INFO" "Analyzing PUA configuration"
    
    if echo "$PUACHECK" | grep -q "Threat type: potentially_unwanted_application"; then
        # PUA policy exists, determine the action setting
        if echo "$PUACHECK" | grep -q "Action: audit"; then
            log "INFO" "PUA status: Audit (PUAs are detected but not blocked)"
            
            # Store for historical tracking
            echo "$(date +"%Y-%m-%d %H:%M:%S") - PUA_ACTION=audit" >> "$POLICY_FILE"
            echo "audit" > "$CURRENT_POLICY_FILE"
            
            if [ "$DEBUG" -eq 1 ]; then
                echo "=== Enhanced MDATP Policy & Health Checker Completed Successfully ==="
            fi
            
            echo "PASSED: PUA in Audit Mode"
            return 0
        elif echo "$PUACHECK" | grep -q "Action: block"; then
            log "INFO" "PUA status: Block (PUAs are actively blocked)"
            
            # Store for historical tracking
            echo "$(date +"%Y-%m-%d %H:%M:%S") - PUA_ACTION=block" >> "$POLICY_FILE"
            echo "block" > "$CURRENT_POLICY_FILE"
            
            if [ "$DEBUG" -eq 1 ]; then
                echo "=== Enhanced MDATP Policy & Health Checker Completed Successfully ==="
            fi
            
            echo "PASSED: PUA in Block Mode"
            return 0
        else
            log "WARNING" "PUA status: Unknown (policy exists but action is undefined)"
            
            # Store for historical tracking
            echo "$(date +"%Y-%m-%d %H:%M:%S") - PUA_ACTION=unknown" >> "$POLICY_FILE"
            echo "unknown" > "$CURRENT_POLICY_FILE"
            
            if [ "$DEBUG" -eq 1 ]; then
                echo "=== Enhanced MDATP Policy & Health Checker Completed with Warnings ==="
            fi
            
            echo "FAILED: PUA status Unknown"
            return 1
        fi
    else
        log "WARNING" "No PUA configuration found"
        
        # Store for historical tracking
        echo "$(date +"%Y-%m-%d %H:%M:%S") - [WARNING] No PUA configuration found" >> "$POLICY_FILE"
        echo "NOT_CONFIGURED" > "$CURRENT_POLICY_FILE"
        
        if [ "$DEBUG" -eq 1 ]; then
            echo "=== Enhanced MDATP Policy & Health Checker Completed with Warnings ==="
        fi
        
        echo "FAILED: PUA Not Configured"
        return 1
    fi
}

This snippet shows how the script extracts the PUA policy action (audit/block/of) and stores it in two different ways:

  • In a historical log that maintains a record of all checks
  • In a current state file that contains only the latest value

You can download the full scripts from my Github Repository.

The Log Files

The script maintains several log files, each with a specific purpose. It’s like having separate containers for recycling. a place for everything, and everything in its place:

  1. Main Log (${SCRIPT_NAME%.*}.log): Contains all general script execution information. The day-to-day diary.
  2. Error Log (${SCRIPT_NAME%.*}_error.log): Just the errors for easier troubleshooting. The “things that went wrong” highlight reel.
  3. Policy History Log (pua_policy.log): A historical record of all policy states and changes.
  4. Current Policy File (current_pua_policy.txt): Contains only the latest policy state.

To prevent these logs from growing indefinitely, the script implements log rotation, moving files that exceed 1MB to a .old extension.

Note: For now, it will only keep 1 .old file for historical purposes. These scripts were meant to be used as a troubleshooting mechanism, but could run for as long as you want.

Custom Attribute Script: The Reporter

The second script is our reporter, taking the detective’s findings and turning them into actionable intelligence for Intune admins.

What It Does

This Custom Attribute script:

  • Reads the current policy state from log files created by the first script
  • Compares it with the previous state to detect changes
  • Formats the info for Intune’s Custom Attribute system
  • Maintains a record of when changes occurred

The Heart of the Script

The most crucial part of this script is its change detection and reporting logic:

# Format output for Intune Custom Attribute
debug "Formatting output for Intune Custom Attribute"
if [ "$PREVIOUS_STATE" = "INITIAL_CHECK" ]; then
    debug "Initial check - reporting current state only"
    echo "PUA_Policy=$CURRENT_STATE"
elif [ "$CURRENT_STATE" != "$PREVIOUS_STATE" ]; then
    debug "State change detected - reporting change"
    echo "PUA_Policy=Changed from $PREVIOUS_STATE to $CURRENT_STATE on $(date '+%Y-%m-%d')"
else
    debug "No change in state - reporting with last change date"
    echo "PUA_Policy=$CURRENT_STATE (unchanged since $(date -r "$PREVIOUS_STATE_FILE" '+%Y-%m-%d'))"
fi

if [ "$DEBUG" -eq 1 ]; then
    echo "=== MDATP Custom Attribute Script Completed ===" >&2
fi

This elegant yet simple logic handles three different scenarios:

  1. First run: Simply report the current state. “Hello, World!”
  2. When a change is detected: Report what changed and when. “It’s Complicated.”
  3. No change: Report the current state and when it was last modified. “Still married after all these years.”
  4. Report any errors that come along

Fallback Mechanism for Resiliency

What happens if something goes wrong with our primary data source? The Custom Attribute script includes a smart fallback mechanism:

# First check if the current policy file exists and has content
    if [ -f "$CURRENT_POLICY_FILE" ] && [ -s "$CURRENT_POLICY_FILE" ]; then
        local current_state=$(cat "$CURRENT_POLICY_FILE")
        debug "Found current policy file with state: $current_state"
        echo "$current_state"
        return
    fi
    
    debug "Current policy file not found, checking policy log file"
    
    # Fallback to parsing the log file if current policy file is unavailable
    if [ ! -f "$POLICY_FILE" ]; then
        debug "Policy log file not found, returning NOT_CONFIGURED"
        echo "NOT_CONFIGURED"
        return
    fi

This ensures the scripts remains resilient even if one of the log files is unavailable or corrupted.

How the Scripts Work Together

The beauty of this solution lies in how these two scripts cooperate like a well synchronized dance, but with fewer sequins and more bash commands (I don’t dance anyway):

  1. The Enhanced MDATP Script runs periodically (e.g., every 15 minutes/1 hour/3 hours), checking Defender’s PUA configuration and logging the results
  2. The Custom Attribute Script is executed by Intune when collecting custom attributes, reading the logs created by the first script and reporting changes. Normally this is every 8 hours or so.

This separation of tasks creates a system where:

  • The heavy lifting is done locally on the device. (checking Defender PUA status is not that hard but hey, just because we can…)
  • Only the summarized results are reported back to Intune
  • A complete history is maintained locally on the macOS device for troubleshooting
  • These logs can also be collected using Intune.

If only all our monitoring systems were so considerate…

What You See in Intune

When the scripts are deployed, here’s what administrators will see in the Intune console under Devices > macOS > Custom Attributes > [pua_custom_attribute_script] :

macos-defender-for-endpoint-pua-configuration-custom-attributes

This provides visibility into:

  • Current policy state on each device
  • When policies have changed
  • If the script itself is working correctly

Remote macOS Log Collection with Intune: No Access Required!

Did you know you can remotely collect logs from macOS devices using Microsoft Intune? This powerful functionality is ideal for troubleshooting issues on devices when you don’t have physical access, or when you’re just too comfortable in your chair to walk across the office.

Sign in to the Microsoft Intune admin center

  • Navigate to Devices > macOS > Scripts and select a macOS shell script
  • In the Device status report, select your target device
  • Select Collect logs

remote-macos-log-collection-with-intune-collect-log

  • Enter the paths you want to gather

remote-macos-log-collection-with-intune-file-path

Example: /Library/Logs/Microsoft/IntuneScripts/mdatp/ pua_policy.log

  • Click OK to initiate the collection

The logs will be collected during the next check-in of the Intune management agent, which typically happens every 8 hours.

When the logs are available in Intune, you can collect them by clicking on “Download logs”

remote-macos-log-collection-with-intune-download

Bonus Information

When you collect logs, Intune automatically includes its own agent logs from:

  • /Library/Logs/Microsoft/Intune
  • ~/Library/Logs/Microsoft/Intune

These additional logs (named IntuneMDMDaemon date–time.log and IntuneMDMAgent date–time.log) can be used for troubleshooting the management agent itself.

If any files you specified cannot be found or have incorrect extensions, you’ll find them listed in a file called LogCollectionInfo.txt in the download. Happy hunting!

Remote macOS Log Collection with Intune

Read more on the Microsoft Learn pages

Debug Mode: Peeking Under the Hood

Both scripts include a helpful debug mode that can be activated with a simple environment variable.

sudo DEBUG=1 bash ./enhanced_mdatp_pua.sh

In debug mode, you’ll see a wealth of information printed to the console:

=== Enhanced MDATP Policy & Health Checker Started (Debug Mode) ===
[INFO] Enhanced MDATP Policy & Health Checker started
[INFO] Checking if Microsoft Defender for Endpoint processes are running
[INFO] Process [wdavdaemon_enterprise] is running
[INFO] Process [wdavdaemon_unprivileged] is running
[INFO] Process [wdavdaemon] is running
[INFO] Microsoft Defender is running properly (all processes verified)
[INFO] Current user: root
[INFO] PATH environment: /usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin
[INFO] Searching for mdatp location...
[INFO] mdatp location from which: /usr/local/bin/mdatp
[INFO] Found mdatp at: /usr/local/bin/mdatp
[INFO] Checking for Microsoft Defender command-line tool (mdatp)
[INFO] Microsoft Defender command-line tool (mdatp) is available at /usr/local/bin/mdatp
[INFO] Retrieving threat policy information
[INFO] Successfully retrieved threat policy information
[INFO] Analyzing PUA configuration
[INFO] PUA status: Audit (PUAs are detected but not blocked)
=== Enhanced MDATP Policy & Health Checker Completed Successfully ===
PASSED: PUA in Audit Mode

This verbose output is great for troubleshooting and understanding exactly what’s happening at each step of the process when you’re working physically on the device.

What Happens If…?

the Enhanced Script Isn’t Deployed

If only the Custom Attribute script is deployed, it will report:

PUA_Policy=No information available yet – MDATP checker has not run

This makes it easy to identify devices where the scripts aren’t fully deployed (yet). It could also mean that the first script did not run first, and the custom attribute script did run. We don’t use a lock-file check with these scripts.

the Custom Attribute Script Isn’t Deployed

if only the Enhanced script is deployed, you won’t see any information in Intune’s Custom Attributes, but the local logs will still be maintained on the device for manual inspection if needed.

Defender Has an Issue

The Enhanced script includes error checking. If Defender processes aren’t running or the MDATP tool isn’t available, this will be reflected in both the logs and the Custom Attribute:

PUA_Policy=DEFENDER_ERROR

Alternatives to Consider

Are there other ways to monitor Defender policies? Yes, but each has limitations, much like diet options – technically there are alternatives to chocolate cake, but are they really as satisfying?

  1. Microsoft Defender for Endpoint (Advanced Hunting) Portal: Shows current compliance status but lacks detailed (historical) change tracking.
  2. Microsoft Sentinel: Can collect and analyze Defender logs but requires additional licensing and setup complexity.
  3. Third-party monitoring tools: Often come with high costs and may not integrate as seamlessly with Intune.

This solution bridges the gap by providing targeted monitoring with minimal overhead, using technologies you already have deployed.

Putting It All Together

By implementing this two-script solution, we went from a state of uncertainty about our Defender PUA policies to having complete visibility and change tracking. When policies changed unexpectedly, we could see:

  • Which devices were affected
  • When the change occurred
  • What the change was (from/to)

Suspicion or Suspect: The Plot Thickens

As our investigation continues, we’ve narrowed down some potential suspects in our policy-changing mystery. The primary person of interest? Perhaps an old (unassigned) policy configuration that might be playing poorly with newer settings?

We’re looking at old configurations that might be haunting our current setup like digital ghosts. Although we can’t find a trace of these older policies on the devices. What makes this particularly puzzling is that these mysterious changes only manifest on macOS devices, while Windows devices remains blissfully unaffected.

We’ve thoroughly examined the evidence and found no signs of intrusion or malware. No fingerprints at the digital crime scene, if you will. This strongly suggests we’re dealing with a system configuration issue rather than a malicious actor.

What further confirms our suspicion is that we’ve tested in multiple tenants and found this phenomenon only occurs in one specific tenant, strongly suggesting a unique configuration issue rather than a widespread bug or security concern. So no need to sound the security alarm, cancel your weekend plans, or prepare that end-of-the-world playlist you’ve been curating since 2012

The case remains open, and our detective work continues. The monitoring scripts described in this article have become our vigilant deputies, keeping watch and documenting any further unexpected changes while we dig deeper into macOS specific policy behaviors and potential configuration conflicts.

Conclusion

Like any good detective story, sometimes the investigation takes unexpected turns. I’ll update this post as we uncover more clues and hopefully, eventually, crack the case. Until then, our scripts stand guard, ensuring that no policy change goes unnoticed or undocumented.

Get the Scripts

The scripts discussed in this article are available in my GitHub repository. Feel free to adapt them to your environment and specific monitoring needs.

5 1 vote
Article Rating

Oktay Sari

#Microsoft365 | #Intune |#MEM | #Security | Father | #Diver | #RC Pilot & #Magician in spare time | Microsoft MVP

Subscribe
Notify of
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

0 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments