Yes, to those loyal readers of my blogs, this is AEDB (Another Event Driven Blog 😆). Am I getting tired already of writing about event-driven architecture stuff? Hell no! As you all know, I’m a big fan, and the more people I can inspire about this, the better!
So in today’s post, we are diving into the registry of your Windows computer (or server). As you probably know, the registry is basically a big database that stores configuration settings and options for Windows to operate and tells applications how to operate.
So, make sure you have a good seat, have a nice cup of coffee (or tea 🤢), and relax! Because we’re going to build something cool! We are going to attach events to listen to Registry changes and act accordingly!
🎬 See this one? Time to stop relaxing and get your hands dirty!
📒 This one? Well this one is there for highlighting things to you. Whether I believe extra information is required about a section, I’ll post it here.
Enough smooth talk! Let’s get started!
Today’s Setup
Windows doesn’t just live in files, it lives in the Registry as well!. Critical configurations, startup items, and even application settings are tucked away in this central database. Monitoring changes here can give you valuable insight into what’s happening on your system, from legitimate software updates to suspicious persistence attempts.
So, how do you catch these changes as they happen? Instead of writing a loop that constantly checks the Registry (which wastes CPU cycles), you can use event-driven scripting in PowerShell. The secret weapon: WMI event subscriptions.
📒 Under the hood:
The RegistryKeyChangeEvent WMI class fires whenever a key, or any of its subkeys, is modified, created, or deleted. By subscribing to this event, your script becomes “reactive,” kicking into action only when there’s something worth noticing.
The Core Concepts
Registering the Event;
Use Register-WmiEvent to tell PowerShell which Registry key to watch.
Handling the Event;
Define the -Action parameter with a script block, this is your event handler. Every time the key changes, the code inside runs automatically.
Time to code! 😉
Prepare
🎬 Make sure you have the registry open. You can do so by running ‘regedit’ from a command prompt;

You should see the registry as shown below;

📒 The registry is different per operating system; some operating systems contain different keys and subsets compared to others. In this blog, I’m focusing on Windows 11 build 26100
📒 Note that not all hives (directories on the top level) are capable of being monitored correctly. Some like the ‘HKEY_CURRENT_USER’ don’t always support events as they reflect, for instance, the NTUSER.DAT. (For this I’ll create a separate blog)
Start monitoring the registry
Now it’s time to really get our hands dirty! We are going to pimp up the registry by implementing some monitoring on some of its settings!
📒 NOTE! I’ve created this all for PowerShell 7+. PowerShell 5 has a slight different syntax. Keep this in mind!
🎬 Put the code below in your Favorite editor:
# Tweek these vars to your liking
$registryHive = "HKEY_LOCAL_MACHINE"
$registryKeyPath = "SOFTWARE\Microsoft\Windows\CurrentVersion\Run"
$registryPath = $("$($registryHive):\$($registryKeyPath)")
Write-Host "Registry monitoring started (PowerShell 7 CIM Events)..." -ForegroundColor Green
Write-Host "Monitoring: $registryPath" -ForegroundColor Green
Write-Host "Press Ctrl+C to stop monitoring! Oh and dont forget to unregister the event!" -ForegroundColor Yellow
$action = {
$event = $Event.SourceEventArgs.NewEvent
Write-Host ">>> Registry key changed at $(Get-Date)" -ForegroundColor Yellow
Write-Host " Hive: $($event.Hive)" -ForegroundColor Cyan
Write-Host " KeyPath: $($event.KeyPath)" -ForegroundColor Cyan
write-host $($event | Out-String)
try {
if (Test-Path $registryPath) {
$values = Get-ItemProperty -Path $registryPath
Write-Host "Current registry values:" -ForegroundColor Cyan
$values.PSObject.Properties | Where-Object { $_.Name -notlike "PS*" } | ForEach-Object {
Write-Host " $($_.Name) = $($_.Value)" -ForegroundColor Green
}
}
}
catch {
Write-Host " Could not read registry values" -ForegroundColor Red
}
}
$registryKeyPath = $registryKeyPath.Replace("\", "\\")
$query = "SELECT * FROM RegistryKeyChangeEvent WHERE Hive='$registryHive' AND KeyPath='$registryKeyPath'"
Register-CimIndicationEvent -Namespace "root/default" -Query $query -SourceIdentifier "RunKeyChange" -Action $action
Write-Host "CIM event registered successfully!" -ForegroundColor Green
Write-Host "Waiting for registry events..." -ForegroundColor Green
🎬 Now either run the script directly or modify the variables on top and run if after that.
You now should see the script output as shown below:

🎬 Now go with regedit to the path you defined in the variable “registryKeyPath” and make a new key there.
In my example I made a simple “test” key:

🎬 Check the code! You should see that the event got triggered:

So how does this work?
Our PowerShell script is like having a security guard for your Registry that never sleeps! 🛡️ Instead of constantly checking if something changed (which would be like a guard walking around every second), it sits quietly and only springs into action when something actually happens.
The Magic Behind the Scenes!
Here’s what happens when you run the script:
Event Registration: The script registers a CIM event subscription that watches the HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Run key (the famous startup programs location!)
Silent Monitoring: Your script goes into “listening mode”, it’s not doing anything, just waiting patiently for the Registry to whisper “Hey, something changed!”
Instant Response: The moment ANY change happens to that Registry key, whether it’s:
✅ A new startup program being added
🔄 An existing entry being modified
❌ A startup item being removed
BOOM! Your event handler fires immediately!
The Beautiful Part
This is TRUE event-driven monitoring! Your script isn’t wasting CPU cycles checking every few seconds. Instead, Windows itself tells your script “Something happened!” and your code reacts instantly. It’s like having a direct hotline from the Registry to your PowerShell session!
- Detecting malware trying to persist via startup entries
- Monitoring legitimate software installations
- Debugging application configuration changes
- Security auditing and compliance
The script essentially turns your PowerShell session into a Registry change detector that’s both efficient and responsive, exactly what event-driven architecture is all about! 🚀
Pimping it up, seeing what changed!
Now lets spice it up a little bit. Imagine we want to see operations as;
- Create
- Update
- Delete
It is possible, but our event doesn’t contain all the required information to show us this directly. So with some creative ways around we can ‘snapshot’ the key we are monitoring and based on that determine what happened.
🎬Modify the script variables below to your wishes and run it from your shell
# Tweek these vars to your liking
$registryHive = "HKEY_LOCAL_MACHINE"
$registryKeyPath = "SOFTWARE\Microsoft\Windows\CurrentVersion\Run"
$registryPath = $("$($registryHive):\$($registryKeyPath)")
#This one we need for PowerShell to work correctly so make sure you fit it (If u want you can make a translation between the reg abbrivations and the full CIM formatting)
$Global:regPath = "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Run"
# Take initial snapshot of registry values
$Global:previousValues = @{}
if (Test-Path $registryPath) {
$values = Get-ItemProperty -Path $registryPath
$values.PSObject.Properties | Where-Object { $_.Name -notlike "PS*" } | ForEach-Object {
$Global:previousValues[$_.Name] = $_.Value
}
}
Write-Host "Registry monitoring started (PowerShell 7 CIM Events)..." -ForegroundColor Green
Write-Host "Monitoring: $registryPath" -ForegroundColor Green
Write-Host "Initial snapshot taken with $($Global:previousValues.Count) values" -ForegroundColor Green
Write-Host "Press Ctrl+C to stop monitoring! Oh and dont forget to unregister the event!" -ForegroundColor Yellow
$action = {
Write-Host ">>> Registry change detected at $(Get-Date)" -ForegroundColor Yellow
$currentValues = @{}
if (Test-Path $Global:regPath) {
$values = Get-ItemProperty -Path $regPath
$values.PSObject.Properties | Where-Object { $_.Name -notlike "PS*" } | ForEach-Object {
$currentValues[$_.Name] = $_.Value
}
}
$previousValues = $Global:previousValues
$hasChanges = $false
foreach ($name in $currentValues.Keys) {
if (-not $previousValues.ContainsKey($name)) {
Write-Host " ✅ ADDED: '$name' = '$($currentValues[$name])'" -ForegroundColor Green
$hasChanges = $true
}
elseif ($previousValues[$name] -ne $currentValues[$name]) {
Write-Host " 🔄 MODIFIED: '$name' = '$($currentValues[$name])' (was: '$($previousValues[$name])')" -ForegroundColor Cyan
$hasChanges = $true
}
}
foreach ($name in $previousValues.Keys) {
if (-not $currentValues.ContainsKey($name)) {
Write-Host " ❌ DELETED: '$name' (was: '$($previousValues[$name])')" -ForegroundColor Red
$hasChanges = $true
}
}
if (-not $hasChanges) {
Write-Host " No specific changes detected (might be permission or metadata change)" -ForegroundColor Yellow
}
$Global:previousValues = $currentValues.Clone()
}
$registryKeyPath = $registryKeyPath.Replace("\", "\\")
$query = "SELECT * FROM RegistryKeyChangeEvent WHERE Hive='$registryHive' AND KeyPath='$registryKeyPath'"
Register-CimIndicationEvent -Namespace "root/default" -Query $query -SourceIdentifier "RunKeyChange" -Action $action
Write-Host "CIM event registered successfully!" -ForegroundColor Green
Write-Host "Waiting for registry events..." -ForegroundColor Green
🎬 Now play around in the path of the registry you mentioned in the variable.
Do some adding, removing, value setting, changing and see what happens.
You should get the output as shown below:



😉 Now we built our own registry watcher! How cool is that?!
Summary
In this post we built our very own Registry watcher the event-driven way! 🚀
Instead of burning CPU cycles with endless polling loops, we let Windows itself whisper to our PowerShell script whenever something changed.
We started by wiring up a CIM event subscription with RegistryKeyChangeEvent, targeting the famous HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Run key.
From there:
Every change fired our event handler instantly, no delay, no waste.
✅ New values showed up right away.
🔄 Modifications were detected live.
❌ Deletions were caught on the spot.
And by snapshotting values before and after, we were even able to see exactly what was added, updated, or removed. Pretty cool, right? 😎
This turns your PowerShell session into a security guard for the Registry 🛡️ always listening, always ready. Perfect for:
Spotting malware persistence attempts or changes on locations you dont want
Debugging config changes
Auditing what software does under the hood
No loops, no manual checks… just pure event-driven magic ✨.
Now you’ve got the foundation for your own registry monitoring toolkit. So go ahead, tweak it, expand it, and have fun watching the Registry in real-time!
And remember, have some fun doing so 😉