Powershell dot notation not selecting data - powershell

I'm having a problem getting Powershell to behave the way I'm expecting.
I'm trying to use get-wmiobject win32_networkconnection to list the mapped drives for the current user, so I can loop through the drives.
When I run $var = get-wmiobject win32_networkconnection | select -expand localname I get exactly what I expect: a list of the drive letters for the mapped network connections.
However, when I run $var = (get-wmiobject win32_networkconnection).localname I get nothing. It doesn't seem to be selecting the property correctly.
This is problematic, because, ideally, I'd like to loop over all the drives, and then select the various properties for each drive. Instead, it seems like I'll be forced to kludge together an iterator, and then iterate over all the variables one at a time (not very elegant, in my opinion).
I'm not super experienced with Powershell, so there may be something I'm missing. However, from what I've read, this should be working. Is this a limitation of get-wmiobject?

What you're trying to do only works in PowerShell 3.0 and newer versions. The official documentation is very vague:
What's New in Windows PowerShell 3.0
Windows PowerShell Language Enhancements
Windows PowerShell 3.0
includes many features [...] The improvements include
property enumeration, count and length properties on scalar objects,
new redirection operators [...]
This blog post goes a bit more into depth: New V3 Language Features

Yes, this is a limitation of PowerShell 2.0.
Your call to Get-WmiObject is returning an array. In PS2, you would need to pipe the array into something like Select-Object or otherwise iterate over it and reference each individual item.
In PS3+, you can use $array.PropertyName and it does that for you, returning an array of properties.

intead of select propertyName, you can use select -exp propertyName

Related

Is there an easy way to have Powershell always show the actual value?

These things drive me nuts:
Is there an easy way to have Powershell just show me the empty string and the list with an empty string in it?
For a while I am maintaining a ConvertTo-Expression which converts an (complex) object into a PowerShell expression which eventually can be used to rebuild most objects. It might useful in situations as comparing test results but also to reveal objects. For details see: readme.
Source and installation
The ConvertTo-Expression script can be installed from the PowerShell Gallery:
Install-Script -Name ConvertTo-Expression
As it concerns a standalone script, installation isn't really required. If you don't have administrator rights, you might just download the script (or copy it) to the required location. You might than simply invoke the script using PowerShell dot sourcing:
. .\ConvertTo-Expression.ps1
Example
The following command outputs the same expression as used to build the object:
$Object = [Ordered]#{
'Null' = $Null
'EmptyString' = ''
'EmptyArray' = #()
'SingleItem' = ,''
'EmptyHashtable' = #{}
}
ConvertTo-Expression $Object
Note the comment from #Mathias there's no functional difference between "one string" and "an array of one string", the pipeline consumes 1 string either way. PowerShell is not node which is described here: PowerShell enumerate an array that contains only one inner array. Some objects might be really different than you expect.
See also: Save hash table in PowerShell object notation (PSON)
This is PowerShell, not Node. So it's not JavaScript or JSON. Also, PowerShell is not Bash or CMD any other regular text-based shell. PowerShell works with objects. .NET objects, in particular. And how objects are represented as text is ... quite a matter of taste. How to represent null? Of course: nothing. How to represent an empty string? Nothing, either. An empty array ... you get my point.
All pipeline output is by default send to Out-Default. In general, the way objects are represented can be controlled by format files: about_Format.ps1xml and about_Types.ps1xml. From PowerShell 6 upwards, the default formats are compiled into the source code, but you can extend them. How you do so, depends on your personal taste. Some options were already mentioned ConvertTo-Json "", ConvertTo-Json #("")), but this would be veryyy JSON-specific.
tl;dr Don't care too much about how objects are represented textually. As you see, there are many possible ways to do so, and also some others. Just make sure your scripts are always object-oriented.
You mean like Python's repr() function? A serialization? "Give me a canonical representation of the object that, when property evaluated, will return an object of the type originally passed?" No, that's not possible unless you write it yourself or you serialize it to XML, JSON, or similar. That's what the *-CliXml and ConvertTo-*/ConvertFrom-* commands are for.
On Powershell 7:
PS C:\> ''.Split(',') | ConvertTo-Json -Compress -AsArray
[""]
PS C:\> '1,2,3,,5'.Split(',') | ConvertTo-Json -Compress -AsArray
["1","2","3","","5"]
The closest would be the ToString() common method. However, that's intended for formatting output and typecasting, not canonical representations or serializations. Not every object even has a string representation that can be converted back into that object. The language isn't designed with that in mind. It's based on C#, a compiled language that favors binary serializations. Javascript requires essentially everything to have a string serialization in order to fulfill it's original design. Python, too, has string representations as fundamental to it's design which is why the repr() function is foundational for serialization and introspection and so on.
C# and .Net go the other way. In a .Net application, if you want to specify a literal empty string you're encouraged to use String.Empty ([String]::Empty in Powershell) because it's easier to see that it's explicit. Why would you ever want a compiled application to tell the user how the canonical representation of an object in memory? You see why that's not a useful thing for C# even if it might be for Powershell?

Get UpTime from powershell into a usable way, but can't get it to work

I've been making this program where i need to send a command to powershell and in return it gives me the sys UpTime (minutes work better but not mandatory)
As i'm still not used to using powershell, i'm having a lot of problems in getting this intel.
This is what i tryed:
(get-date) - (gcim Win32_OperatingSystem).LastBootUpTime
Gives me the uptime, but i still have no idea how to work with that, so i still need to somehow add something like:
| Select-String -Pattern "TotalMinutes"
But then i need (somehow) to make that powershell gives me that time as return so i can work with it.
maybe to clipboard?
| clip
But if i add all those up, none will work.
Putting in the clipboard is just a way i made to get this info, others might also work.
I'm still very new to this, sorry if i hurt your intellect with stupid questions.
Thanks in advance
By subtracting two [datetime] (System.DateTime) instances, you get a [timespan] (System.TimeSpan) instance, which you can store in a variable:
$timeSpanSinceBoot = (Get-Date) - (Get-CimInstance Win32_OperatingSystem).LastBootUpTime
You can then access its properties as needed, such as .TotalMinutes:
$timeSpanSinceBoot.TotalMinutes
To examine the members of the time-span value's type, use the Get-Member cmdlet:
$timeSpanSinceBoot | Get-Member # lists properties and methods

Multiple Select-Object as dot-notation

Problem
I want to know if there is a easy way to search multiple objects in a WMI object or CIM instance.
I'm aware it's possible with commands like
Get-CimInstance Win32_BaseBoard | Select-Object Manufacturer,Product
But I want a command with a dot notation where you can set multiple objects for a search like (Get-CimInstance Win32_BaseBoard).Manufacturer with more than one object.
Something like (Get-CimInstance Win32_BaseBoard).Object1.Obejct2.Object3
Ben's solution from the comments will work but note that it calls Get-CimInstance once for each property you want, even though that's unnecessary (could take a while depending on the call you're making).
Let's look at it a few other ways. We'll start by storing the wanted property names in an array.
$properties = 'Manufacturer', 'Product'
Now we can do something similar to what Ben did:
$allValues = $properties |
ForEach-Object -Begin {
$bb = Get-CimInstance Win32_Baseboard
} -Process {
$bb.$_
}
That keeps his approach but does the CIM call once.
If you want to do with dot notation purely, you can use the .ForEach() method and the .PSObject hidden property to get at the properties:
(Get-CimInstance Win32Baseboard).ForEach({$_.PSObject.Properties.Where({$_.Name -in $properties}).Value})
As noted, DotNotation (Intellisense) is 1:1 / single node / collection thing. You can validate this, by pushing the result of the call to XML and walking the nodes.
(Get-CimInstance Win32_BaseBoard | ConvertTo-Xml).Objects.Object.Property
(Get-CimInstance Win32_BaseBoard | ConvertTo-Xml).Objects.Object.Property.name
(Get-CimInstance Win32_BaseBoard | ConvertTo-Xml).Objects.Object.Property.'#text'
The only other option off the top of my head is constructing a proxy that uses enums and switches for all possible properties for the namespace you are using.
That's way more unneeded effort and code to just do what BenH has pointed out, specifically because you'd have to do that for every class.
Now, if you just wanted to shorthand this, maybe do this
$Base = Get-CimInstance Win32_BaseBoard
$Base.Manufacturer;$base.Model;$base.Name;$Base.PartNumber
But that is just unwieldy, especially, since all this really doing is single commands set on a single line with the command break separator, the semi-colon. Which is a wrong thing to do. IMHO.
---small rant ---
If you need the separator, then just put the next thing on a new line and avoid the semi-colon. I mean, I can see that semi-colon use as the PoSH console host, but in a real script, function, module, well, just, no. Again, IMHO
--- small rant ---
Lastly, depending on what your target PoSH version is, DotNotation had issues in v2 - v3 days

Finding the index of a key and value within an array using a wildcard in Powershell

I have been working on an issue and I was able to get done what I need to get done in a relatively decent and acceptable way but I am curious as to why I ran into some of the problems I did.
I'm not too savvy with PowerShell or C# but I have some experience with Java, C++, and a few others. So, if I overlook something really simple, you'll have to forgive me. I'm not looking for a critique of my solution, just some insight into some of the blockades I came across.
What I needed to do was use Powershell to query an LDAP setting. I needed to know MaxConnIdleTime and I needed that either assigned to a variable or accessible through a subroutine [sic] (ex $ldapPolicies.MaxConnIdleTime) so that I could run it through a conditional statement.
Here is how I accomplished it:
$ldap = Get-ADObject -SearchBase "CN=Query-Policies,CN=Directory Service,CN=Windows NT,CN=Services,CN=Configuration,DC=$ENV:COMPUTERNAME,DC=$dc" -Filter 'ObjectClass -like "queryPolicy"' -Properties ldapadminlimits
$ldap = #($ldap.ldapadminlimits)
$ldap | %{
if($_.startswith("MaxConnIdleTime")) {
$match = $_
}
}
I tried NTDSUtil.exe, however, I couldn't redirect the "Show Values" to a text file to read from later and I couldn't write it to a variable in PowerShell.
I tried Start-Transcript and then ran NTDSUtil but it only recorded what occurred within PowerShell and not what happened in NTDSUtil.
Also, I tried giving all of the commands to NTDSUtil at once (NTDSUtil "ldap policies" "connections" "connect to server $ENV:COMPUTERNAME" q "Show Values") but PowerShell doesn't show anything in the console and I have press the exit sequence to return back to PS>.
I know that I could use LDP but I'm not too familiar with ADSI. Research appeared to say that going about attempting to get an LDPdump is a bit antiquated and I pretty much abandoned that attempt.
One of the issues that I had that caused me a small bit of frustration (and the reason I am asking this question) is why can I not search an array and find the index of an item using a wildcard? I tried doing this:
$ldap.IndexOf("MaxConnIdleTime*")
AND
$ldap.IndexOf($ldap -like "MaxConnIdleTime*")
but it always returned -1.
It would work correctly if I tried:
$ldap.IndexOf("MaxConnIdleTime=100")
given that the value was indeed 100. But I am validating that the value was correct.
I know that I could just do something like this:
if($ldap -contains "MaxConnIdleTime=100") {
DO SOMETHING...
} else {
DO SOMETHING ELSE...
}
Why is it that I can't search an array using a wildcard operator? There was no ambiguity, so, it should have worked, right?
I'm not looking for a critique of how I accomplished this, I'm just wanting to understand why it behaved like it did.
Thanks.
I don't think there's a straightforward "search an array by wildcard and return an index" cmdlet, method, statement, etc. in PowerShell.
.IndexOf is not designed to work with a wildcard.
When you used the -like operator on the array, you likely found only a single matching object, but -like returns an array of matches when used on an array.
Passing the array into .IndexOf() then looks for an array element that is itself an array, even if that array only has one object.
This would work:
$ldap.IndexOf(($ldap -like "MaxConnIdleTime*")[0])
As long as you always wanted to find the first one.

How to change default output Formatting of Powershell to use Format-Table -autosize?

How can I enforce powershell to use
Format-Table -auto
as a default formatting when writing a returned array of objects to the console?
Thanks
If you are OK calling the cmdlet every time and if you have at least PowerShell v3.0 then you can set a $PSDefaultParameterValues which you can read more about at about_Parameters_Default_Values.
The syntax that would satisfy your need would be:
$PSDefaultParameterValues=#{"<CmdletName>:<ParameterName>"="<DefaultValue>"}
So we add in the switch by setting it to $true.
$PSDefaultParameterValues = #{"Format-Table:Autosize"=$true}
To remove this you would have to do it much the same as you would a hashtable element
$PSDefaultParameterValues.Remove("Format-Table:Autosize")
From the aforementioned article here is some pertinent information as to how to deal with these.
$PSDefaultParameterValues is a preference variable, so it exists only in the session
in which it is set. It has no default value.
To set $PSDefaultParameterValues, type the variable name and one or more key-value pairs
at the command line.
If you type another $PSDefaultParameterValues command, its value replaces the original
value. The original is not retained.
To save $PSDefaultParameterValues for future sessions, add a $PSDefaultParameterValues
command to your Windows PowerShell profile. For more information, see about_Profiles.
Outside of that I am not sure as it would be difficult to change in a dynamic sense. You would want to be sure that data sent to the stream appears on screen in the same way that format-table -auto does but you would have to make sure that it does not affect data so that you could not capture it or send it down the pipe.
You are looking at creating custom output format files, like Frode F. talks about, then you would need to consider looking at about_Format.ps1xml but you would need to configure this for every object that you would want to display this way.
FileSystem.format.ps1xml, for example, would govern the output from Get-ChildItem. Format-Table is more dynamic and I don't think you can say just use Format-Table in that file.