PowerShell

Using Win32_UserAccount WMI filter in PowerShell/Group Policies and what to avoid

Some months ago, I created PowerShell Script to create local administrative users on workstations – Create a local user or administrator account in Windows using PowerShell. It's a bit overcomplicated, but the goal was it should work for Windows 7 and up, and that means supporting PowerShell 2.0. As part of that exercise, I've been using Win32_UserAccount WMI based query to find local users and manage them to an extent. While Get-LocalUser exists, it's not suitable for the PowerShell 2.0 scenario. I also use the same query in GPO for WMI filtering. You can say it's been a good friend of mine – until today! Let's take a look at this basic WMI query:

Get-WmiObject -Query "SELECT * FROM Win32_UserAccount WHERE LocalAccount=true" | Format-Table


It can also give more relevant data, such as if the account is enabled or disabled.

Get-WmiObject -Query "SELECT * FROM Win32_UserAccount Where LocalAccount = true" | Format-Table Caption, Domain, Name, PasswordChangeable, PasswordRequired, Disabled

I've been using this WMI query both in PowerShell scripts that need to support PowerShell 2.0 (Windows 7) and in GPO WMI filtering when I apply GPO that only should execute if a given user exists.

💡 WMI Win32_UserAccount – Why even mention it?

Now you may be wondering why I even mention this? I was checking for the existence of a single local user on a workstation rather than asking for multiple users. So by merely adding Name = 'Administrator' I'm making sure my query outputs only a single user.

Get-WmiObject -Query "SELECT * FROM Win32_UserAccount Where LocalAccount = true AND Name = 'Administrator'" | ft

Except now, instead of 1 second, it takes 2 minutes.

💡 WMI Win32_UserAccount – Why that happens?

To understand why would there be a difference between those two queries

Get-WmiObject -Query 'Select * FROM Win32_UserAccount WHERE LocalAccount = true' | ft
Get-WmiObject -Query "SELECT * FROM Win32_UserAccount Where LocalAccount = true AND Name = 'Administrator'" | ft

I've run those, but without any WHERE filtering.

Get-WmiObject -Query 'Select * FROM Win32_UserAccount' | ft

And suddenly, everything made sense. Without WHERE filtering, it queries not only local users but Active Directory users as well. This explains why a simple query would take 2 minutes – I have over 50000 users in my AD. Well, it only sort of makes sense because we're using an almost same query, which has a very subtle difference that shouldn't impact query in that way. If anything, it should be faster because we added condition limiting our output.

So, while I could probably find some way to work around this issue in PowerShell, it doesn't solve my problem when using the very same query in WMI filtering for Group Policies. It means that each time the GPO gets executed, it takes 2 minutes+ to do an assessment, whether it's valid for the current workstation or not. Not to mention, it impacts the performance of an AD.

💡 WMI Win32_UserAccount – Workaround

What's the fix? Changing equal (=) to LIKE. Consider those two queries:

Get-WmiObject -Query "SELECT * FROM Win32_UserAccount Where LocalAccount = true AND Name Like 'Administrator'" | ft
Get-WmiObject -Query "SELECT * FROM Win32_UserAccount Where LocalAccount = true AND Name = 'Administrator'" | ft

Almost the same, but the first one takes 1 second, the second one 2 minutes. So for some weird reason, the equal sign is causing WMI provider to go nuts, and changing it to LIKE resolves the issue. While I don't know why that happens, I'm pretty happy with this solution. I'm pretty sure I will forget about it in a few days so that this blog post will be my reminder to not take things for granted and that even subtle difference requires extensive testing.

This post was last modified on 7 czerwca, 2025 18:25

Przemyslaw Klys

System Architect with over 14 years of experience in the IT field. Skilled, among others, in Active Directory, Microsoft Exchange and Office 365. Profoundly interested in PowerShell. Software geek.

Share
Published by
Przemyslaw Klys

Recent Posts

Supercharging Your Network Diagnostics with Globalping for NET

Ever wondered how to run network diagnostics like Ping, Traceroute, or DNS queries from probes…

2 tygodnie ago

Automating Network Diagnostics with Globalping PowerShell Module

Are you tired of manually running network diagnostics like Ping, Traceroute, or DNS queries? The…

2 tygodnie ago

Enhanced Dashboards with PSWriteHTML – Introducing InfoCards and Density Options

Discover new features in the PSWriteHTML PowerShell module – including New-HTMLInfoCard, improved layout controls with…

3 tygodnie ago

Mastering Active Directory Hygiene: Automating SIDHistory Cleanup with CleanupMonster

Security Identifier (SID) History is a useful mechanism in Active Directory (AD) migrations. It allows…

3 tygodnie ago

Upgrade Azure Active Directory Connect fails with unexpected error

Today, I made the decision to upgrade my test environment and update the version of…

3 tygodnie ago

Mastering Active Directory Hygiene: Automating Stale Computer Cleanup with CleanupMonster

Have you ever looked at your Active Directory and wondered, "Why do I still have…

3 tygodnie ago