Get-WmiObject using AND or OR (they are working in the opposite manner) - filtering

It seems like WMI filtering using multiple criteria (AND and OR) is not working properly. In fact, the AND behaves like OR should, and the OR behaves like the AND should. Maybe it's just me, but I am wondering if anyone else is seeing this behavior.
I am trying to get a list of Windows shares using PowerShell and Get-WMIObject, which works great: Get-WmiObject -Class Win32_Share
My brain has exploded because using either a WQL statement or a WMI filter, for me, results in the AND and OR operators behaving in the exact opposite manner they should.
Take these shares as an example: Admin$, C$, D$, TestShare
If I wanted to exclude C$ and D$ from my results, I could filter it using a WQL statement
("SELECT * from Win32_Share WHERE Name !='C$' OR Name !='D$'") or a WMI filter (-Filter "Name <>'C$' OR Name <> 'D$'")
Unfortunately, when running either option, say this one:
Get-WmiObject -Class Win32_Share -Filter "Name <>'C$' OR Name <> 'D$'"
PowerShell returns a list of all four shares, Admin$, C$, D$, and TestShare. It is as if PowerShell is treating the OR as an AND, and therefore the criteria doesn't match, so all results are returned.
The crazy thing is, if I replace the OR with an AND, I get the results I want. Running this:
Get-WmiObject -Class Win32_Share -Filter "Name <>'C$' AND Name <> 'D$'"
gives me a list of shares: Admin$ and TestShare, excluding C$ and D$. This seems like PowerShell is treating the AND as an OR. I tested this in PowerShell 2, 3, and 5; same result in each.
Looking online for WMI filtering using OR examples, they are all exactly as one would expect, so why the heck am I seeing this backwards behavior?
Can anyone else re-create my issue, or figure out why I am seeing this in my environment? Thanks

Go back to propositional logic and boolean algebra basics. In particular, De Morgan's laws can be expressed in English as:
the negation of a disjunction is the conjunction of the negations; and
the negation of a conjunction is the disjunction of the negations.
Take these shares as an example: print$, IPC$, Test, TestC:
PS D:\PShell> Get-WmiObject -Class Win32_Share
Name Path Description
---- ---- -----------
IPC$ Remote IPC
print$ C:\Windows\system32\spool\drivers Ovladače tiskárny
test D:\test
testC C:\testC
To excerpt only print$ and IPC$, then your filter should be "Name='IPC$' or Name='print$'":
PS D:\PShell> Get-WmiObject -Class Win32_Share -Filter "Name='IPC$' or Name='print$'"
Name Path Description
---- ---- -----------
IPC$ Remote IPC
print$ C:\Windows\system32\spool\drivers Ovladače tiskárny
Vice versa, let's exclude both print$ and IPC$ from the result i.e. take the filter negation:
PS D:\PShell> Get-WmiObject -Class Win32_Share -Filter "not (Name='IPC$' or Name='print$')"
Name Path Description
---- ---- -----------
test D:\test
testC C:\testC
The latter result is the same for the following filters (apply propositional logic rules successively):
"not (Name = 'IPC$' or Name = 'print$')": above original
"not (Name = 'IPC$') AND not (Name = 'print$')": De Morgan Laws applied
" Name <> 'IPC$' AND Name <> 'print$'"  : comparison operators negated

Related

Get-WmiObject -Filter OR not working with NULL

These cmdlets work:
Get-WmiObject Win32_PNPSignedDriver -Filter "DriverProviderName <> NULL"
Get-WmiObject Win32_PNPSignedDriver -Filter "DriverProviderName <> 'Microsoft'"
I can't seem to combine them with a OR statement & I don't know why:
Get-WmiObject Win32_PNPSignedDriver -Filter "DriverProviderName <> 'Microsoft' OR DriverProviderName <> NULL"
The command above runs, but doesn't exclude the NULL entries last one is Fax on my Win10 PC.
The answer to this similar question seems to imply I have it marked right, however theirs is matching to a string whereas I need to exclude a NULL object. I suspect it has to do with the NULL and/or lack of single quotes.
It's about translating from English sentences into logical form: When translating from English sentences into logical form, …, and the phrase "neither A nor B" is translated as "not A and not B". Use
Get-WmiObject Win32_PNPSignedDriver -Filter `
"DriverProviderName <> 'Microsoft' AND DriverProviderName IS NOT NULL"
Note: see Translating “neither…nor” into a mathematical logical expression as well; applying de Morgan's laws, the following code surprisingly works although uses undocumented NOT logical operator in a WQL query:
Get-WmiObject Win32_PNPSignedDriver -Filter `
"NOT (DriverProviderName = 'Microsoft' OR DriverProviderName IS NULL)"
In above PowerShell code examples is used a backtick to split commands over multiple lines for better readability…

How to put computers on a network into a variable in PowerShell?

I've been using these lines of code:
$204computernames = Get-ADComputer -searchbase $sb -filter * | ?{$_.name -like "ptfg*-061*"} | select name
$onlineComputers = $204computernames |Where-Object { Test-Connection $_.name -Count 1 -Quiet }
to grab all of my computers on my network and put them into a variable so I can push all of my documents, updates, etc to them so that I dont have to go to each computer individually to get the files I want where I want. When I take the variable and put it into a line of code like this
Test-Connection $onlineComputers
I get errors like this:
Test-Connection : Testing connection to computer '#{name=PTFGW-0613618TN}' failed: A non-recoverable error occurred during a database lookup
At line:1 char:1
+ Test-Connection $onlineComputers
I'm assuming after extensive testing in different codes that there is a problem with the way my variable stores its values. Does anyone know how I can fix this issue?
As #boxdog already pointed out in the comments, with | select name you get objects with the single property Name. Therefore, you don't get a list of computer names, but a list of objects that have the computer name in the Name property. You can work with that and access each computer name like .Name.
But to solve your problem, you can replace | select name (which stands for | Select-Object -Property Name) by | Select-Object -ExpandProperty Name. That way, you filter out only the computer name and expand the result to just this property. After that, you really have just a list of computernames (an array of string objects).

Filter cim instance class by property name

I'm trying to filter results by property Name. I cannot use piping in my script.
Get-CimInstance -ClassName Win32_Product -Property Name -Filter "Microsoft*"
Returns the error: Get-CimInstance : Invalid query.
I'm trying to get output similar to this command:
Get-CimInstance -ClassName Win32_Product | ? {$_.Name -like 'Microsoft*'}
But without piping to Where-Object.
What am I doing wrong?
If you look at Get-Help Get-CimInstance -Full, you will find the following -
-Filter [<String>]
Specifies a where clause to use as a filter. Specify the clause in either the WQL or the CQL query language.
Note: Do not include the where keyword in the value of the parameter.
Required? false
Position? named
Default value none
Accept pipeline input? True (ByPropertyName)
Accept wildcard characters? false
You don't have to include the Where-Object here, and you need to write your code as a query. The -filter parameter will take the Property(Name in this case) in form of the Windows Query Language. You don't need to define explicitly mention the -Property parameter while making use of the -filter parameter. Furthermore, since you are using WQL, your wildcard search would change from * to %, much like that in SQL. Keeping these points in mind, you can use the below query -
Get-CimInstance -ClassName Win32_Product -Filter 'Name like "Microsoft%"'

How can I get a plaintext list/array of local users on my system in powershell?

I need to get a plaintext list or array of all the local users on my system so I can loop through them and preform certain actions. By "plaintext" I mean just the user's names. Nothing else. No fancy formatting, no titles, no groups, nothing but the user's names.
I've googled around and tried several solutions, (for e.g. this one powershell - list local users and their groups) but they all have extraneous data that makes looping through the users impossible.
Is there any way I can get just a plain list of users? I wouldn't mind a cmd solution if that's what you have. Note that I have already tried net users, but like I stated earlier, it has this extraneous data.
VBScript:
Dim ADsContainer, User
Set ADsContainer = GetObject("WinNT://.,Computer")
ADsContainer.Filter = Array("User")
For Each User In ADsContainer
WScript.Echo User.Name
Next
Paste the above lines into GetLocalUsers.vbs and run it like this:
cscript //nologo GetLocalUsers.vbs
If you want to use WMI directly in PowerShell, you can use this:
get-wmiobject Win32_UserAccount -filter 'LocalAccount=TRUE' |
select-object -expandproperty Name
You have to write it like this to get it formatted as you requested:
Get-WmiObject -Class Win32_UserAccount | Format-wide -property name -column 1
Prints:
Administrator
Guest
and so on..
After a bit more experimentation, reading, etc, I found a rather simple solution:
$accounts = Get-WmiObject -Class Win32_UserAccount | Select name
Returns:
name
----
Administrator
Guest
John Doe
Other User
Not quite what I want, but if I loop through it like this:
foreach ($i in $accounts) { Write-host $i.name }
It prints:
Administrator
Guest
John Doe
Other User
This is messy, so I shortened it down to one loop:
foreach ($i in Get-WmiObject -Class Win32_UserAccount | Select name) {
# refer to the looped user as $i.name now
}
and per Bill_Stewart's comment, it is a good idea to filter by local account:
foreach ($i in Get-WmiObject -Class Win32_UserAccount -filter 'LocalAccount=true' | Select name) {
# refer to the looped user as $i.name now
}
Still less than optimal, but for now it suits my needs.

Powershell - How to use result of one WMI query in another WMI query?

Trying to list the user permissions of shares on a server, where the share's path has a common file path element in it.
I have a script that successfully uses the Win32_LogicalShareSecuritySetting WMI class to enumerate the share permissions of all the shares on the server, but unfortunately that class does not have the file path of the share as an attribute... I can use the Win32_Share class and do something like:
$FinShares = Get-WmiObject -Class Win32_Share -Filter "Path LIKE '%Finance%'" -ComputerName $computername
and I do get a list of the desired shares. But how to pass that list into the next Get-WmiObject statement? I've tried something like:
$FinShares = (Get-WmiObject -Class Win32_Share -Filter "Path LIKE '%Finance%'" -ComputerName $computername | Select-Object Name)
foreach ($ShareInst in $FinShares)
{
$FinShareSS = Get-WmiObject -Class Win32_LogicalShareSecuritySetting -Filter "Name = '$ShareInst'" -ComputerName $computername
$SecurityDescriptor = $FinShareSS.GetSecurityDescriptor()
(...)
When I try that, the variable $FinShareSS remains null... Can someone give me a pointer (or some kind of better way altogether) as to how I can do this?
The problem is your filter using $ShareInst; it's not working, because it is not returning the Name like you expect. Try just putting "$ShareInst" inside your foreach loop; you should see things like:
\COMPUTERNAME\root\cimv2:Win32_Share.Name="ADMIN$"
Which is the WMI object's PATH, not it's name. What you have in $ShareInst is an object of type System.Management.ManagementObject#root\cimv2\Win32_Share, not a string. When you put that variable inside double quotes, PowerShell expands the variable into a string using the objects .ToString() method. Which in the case of this Win32_Share object, returns the PATH of the object, not the name.
So basically you just need to get the actual name string in the -Filter string, so that it will actually return the share security object you are looking for. There are several ways to do this:
Embed the Property name in the string, like so:
-Filter "Name = '$($ShareInst.Name)'"
The $() wrapper tells PowerShell to evaluate the .Name property before expanding the variable into that long PATH value, so you get just the short name you're looking for.
If you just need the Win32_Share objects for the Name, then you can just change the foreach line to look like this:
foreach ($ShareInst in ($FinShares | Select-Object -ExpandProperty Name))
The -ExpandProperty parameter of the Select-Object tells PowerShell to get the Name property of each object and just return that, instead of the full object. Now $ShareInst will be just the name of the Win32_Share, so you can leave your filter as-is.
There are any number of other ways to resolve this issue, but those two seem the most straight-forward to me.