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

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.

Related

Modify local users property description in Powershell 4.0

I would like to modify the description property from local users.
I can retrieve all local users name and description in two ways.
One using WMI Win32_UserAccount class:
Get-CimInstance -ClassName Win32_UserAccount -Filter "LocalAccount='True'" | Select-Object -Property name, description | FL
And one using ADSI:
$Computername = $env:COMPUTERNAME
$adsi = [ADSI]"WinNT://$Computername"
$Users = $adsi.Children | Where-Object {$_.SchemaClassName -eq 'user'}
ForEach ($u in $Users) {
$u | Select-Object -Property Name, Description
}
However, I can't figure out how using commands similar to the ones above I can modify the local user property description. I did research and only found out how to do GETS and not SETS.
My final objective is to put this code in an Ansible playbook and run it in several remote servers. If you have an idea of how to solve this or how to help me I would be grateful.
Working with ADSI can be very tricky but, its super helpful since it usually doesnt rely on 3rd party modules.
Without going super in depth on ADSI, heres the easiest way you can change, or add a value to a property, the description property in this case:
$adsi = [ADSI]"WinNT://$env:Computername"
$User = $adsi.Children.Find('Abraham')
$User.Description = "Hi, this is a description"
$User.SetInfo()
Using $User.SetInfo method, we can write the changes to the database. It's not a method you would get either when piping to a Get-Member. Unfortunately, it's one that you would need to know already. Using Dot Notation we can reference the property you'd like to change, then assigning it a value just like we would when assigning a value to a variable: $var = value.

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).

Find missing prefix for computer name

I am trying to write a script that will automatically find the full asset tag based on the ID of the computer.
For example:
The PC ID is: PC0001
$computerID = PC0001;
But the full asset tag that I need is: WKSPC0001
But these asset tags might have different prefixes, for example DSTPC0002, TBLPC0003 etc. But they all have common pattern: Prefix+PC+ID (the ID is unique and there is no repetition).
I am trying to figure out how to write a query for that in PowerShell but I cant figure it out. I've tried:
$current = Get-ADComputer -Filter {Name -like "*$computerId.text"} |
Select -Property Name
But I was having issues to get it to work.
What am I doing wrong?
A few observations.
You want $computerId to be a string, so $computerID = "PC0001";
The filter expression for Get-ADComputer is expected to be a string also, using curly braces here is wrong. So Get-ADComputer -Filter "..."
Powershell can expand variable values in strings, but it only does that in double-quoted strings. "Name -like '$variable'" will work, but 'Name -like "$variable"' won't.
There is no .text in your $computerId variable. It's a plain string.
Knowing that, try:
$current = Get-ADComputer -Filter "Name -like '*$computerId'"
Other notes
Don't do ... | Select Name unless you really must. Storing the computer object itself will be more versatile. You can do $current.Name when you need it anytime.
Querying the AD with a filter that begins with a wildcard is slow. Try to avoid.
If you have a fixed number of possible prefixes, an LDAP filter like the following will be much faster:
$current = Get-ADComputer -LDAPFilter "(|(cn=WKS$computerId)(cn=DST$computerId)(cn=TBL$computerId))"

Get-ADComputer save results as string array?

What is the output format of Get-ADComputer? I'm trying to do something like this to take an inventory.
[string[]]$server_list = Get-ADComputer -Filter * -Property Name # Select-Object Name
However, when I use $server_list in a foreach, I see the object curly brackets like so
foreach ($machine_name in $server_list) {
"processing : $machine_name";
}
output:
#{Name=some-machine-name-123-here}
I just need the actual name value, how do i get that?
The -Property Name parameter is unnecessary, as Get-ADComputer always retrieves the Name property. (This doesn't select only the Name property, as you seem to have thought.)
Rather than -Property Name, append | Select-Object -ExpandProperty Name to your Get-ADComputer command line.
So, my query returned a different result. Thus you might need to do a little work on this.
Basically, -Property says that you want an object with a certain field. Select does more or less the same thing.
If you want to keep your existing solution, expand the property instead of selecting it. This is the best way to do that:
[string[]]$server_list = (Get-ADComputer -Filter * -Property Name).Name
You returned #{} which is powershell's way of representing an object inside a string. In this case, an object that contains properties about your ADComputers, of which you chose to only include "Name". If you included additional properties, you would see a larger comma separated list of type=value.
You can actually remove -Property Name, however this will take longer to run, since you're filtering on the right. (Gathering a lot of data and THEN filtering it to only Name.)

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.