Powershell 5.0 / ISE - powershell

I have an example code snippet that suggests using
(Get-Process | Where-Object {$_.WorkingSet64 -gt 20mb}).Count
to return the count of all processess using > 20Mb.
It works, but when typing, neither Intellisense or the "Tab" key shows this property, rather they show the properties of an individual process - which I find misleading.
I understand, that specifying an item property will give me the list of that property only, but is there a way to easily see, in general, what ALL the valid propeties are, including list aggregates etc?
Even assigning to a variable
$processes = Get-Process | Where-Object {$_.WorkingSet64 -gt 20mb}
does not show me "Count" as a valid property of $processes until AFTER the assignment has been actually run and the value assigned - when writing the script it still shows the properties for an individual item.
For me, Intellisense / Tab help that does not cover all the options kind of defeats the purpose ... (not having to remember hundreds objects/functions and their properties / parameters).
Is there any way to improve this situation? Some syntax trick have I missed?

The correct way to find out all of the properties of an object is to pipe the output to Get-Member:
Get-Process | Get-Member
Sometimes there are hidden properties and methods that can only be seen if you add the -force switch:
Get-Process | Get-Member -Force
The count property is an automatic property that is always usable on any collection object but that isn't explicitly listed as a property. Another example of an automatic property is length.

Using #() to force an array type is handy when that is what is wanted.
e.g. $processes = #(Get-Process | Where-Object {$_.WorkingSet64 -gt 20mb}). will show you "Count" and the other array properties.
Other than that, let's say the Intellisense has various limitations / shortcomings that I will just have to learn... sigh.

Related

Script has two variables when done, but when I pipe to SELECT-object only first one returns data to console

I am trying to query multiple servers with WMI, but I don't always have access to the servers.
The code is below. Alas, it returns "access is denied" to the console, but I can't seem to get rid of it. Oh well.
However, I am trapping the servers that I can't connect to, so that I can tell someone else to look at them, or request access.
But when I run the code, it only returns the first list of servers; even if $failed_servers has values, nothing is returned. If I tell both to pipe to ogv, then two windows pop up.
Why won't both "$variable|select" work? If I remove the select on $failed_servers, then it shows up, albeit just sitting immediately underneath the successful ones. Which is okay-but-not-great.
$list = ("servera","serverb","serverc")
$failed_servers = #()
$final = foreach ($server_instance in $list)
{
$errors=#()
gwmi -query "select * from win32_service where name like '%SQLSERVER%'" -cn $server_instance -ErrorVariable +errors -ErrorAction SilentlyContinue
if ($errors.Count -gt 0) {$failed_servers += $server_instance
}
}
$final|select pscomputername, name, startmode, state |where {$_.pscomputername -ne $null}
$failed_servers |select #{N='Failed Servers'; E={$_}}
What you're experiencing is merely a display problem:
Both your Select-Object calls produce output objects with 4 or fewer properties whose types do not have explicit formatting data associated with them (as reported by Get-FormatData).
This causes PowerShell's for-display output formatting system to implicitly render them via the Format-Table cmdlet.
The display columns that Format-Table uses are locked in based on the properties of the very first object that Format-Table receives.
Therefore, your second Select-Object call, whose output objects share no properties with the objects output by the first one, effectively produces no visible output - however, the objects are sent to the success output stream and are available for programmatic processing.
A simple demonstration:
& {
# This locks in Month and Year as the display columns of the output table.
Get-Date | Select-Object Month, Year
# This command's output will effectively be invisible,
# because the property set Name, Attributes does not overlap with
# Month, Year
Get-Item \ | Select-Object Name, Attributes
}
The output will look something like this - note how the second statement's output is effectively invisible (save for an extra blank line):
Month Year
----- ----
9 2021
Note the problem can even affect a single statement that outputs objects of disparate types (whose types don't have associated formatting data); e.g.:
(Get-Date | Select-Object Year), (Get-Item \ | Select-Object Name)
Workarounds:
Applying | Format-List to the command above makes all objects visible, though obviously changes the display format.
Intra-script you could pipe each Select-Object pipeline to Out-Host to force instant, pipeline-specific formatting, but - given that the results are sent directly to the host rather than to the success output stream - this technique precludes further programmatic processing.
Potential future improvements:
GitHub issue #7871 proposes at least issuing a warning if output objects effectively become invisible.

Use a variable returned by Get-Variable

Hopefully this answer isn't above me. I've created a custom object with properties and methods. I create several of them on the fly, depending on what the user selects at the beginning.
So for this example, the script might create $PRD1, $PRD2, $TST1 and $TST4.
$PRD1, $PRD2, $TST1 and $TST4 will have some properties like DebugMode, DisableAppsStartTime, DisableAppsStopTime. They'll have some methods like DisableApps(), EnableApps().
How can I find out which variables the script ended up creating? I can use Get-Variable to know the ones it created (plus I DO still have the initial list of names to create). My issue is that I'm having trouble figuring out to call the ones I've created, in a manner that allows me to use the methods and properties, without a ridiculous mash up of nested foreach/if/switch commands.
I certainly hope that made sense.
Thanks in advance,
SS
I DO still have the initial list of names to create
Assuming that $list contains this list, the following creates an (ordered) hash table of those variables that were actually created from that list:
$variableMap = [ordered] #{}
(Get-Variable -ErrorAction Ignore -Scope Local $list).
ForEach({ $variableMap[$_.Name] = $_.Value })
Note: -Scope Local limits the lookup to the current scope[1]; omit it to target all variables visible in the current scope, which includes those from ancestral (parent) scopes.
You can then loop over $variableMap.Keys to process them all, or access one by name selectively, e.g., $variableMap.PRD1 (or $variableMap['PRD1']).
You then use regular dot notation to access properties and methods of these entries; e.g., $variableMap.PRD1.DisableApps().
[1] This includes variables created with the AllScope option, e.g., $HOME, because they are copied to every scope, as the name suggests. You can find all such variables with
Get-Variable | Where-Object Options -match 'AllScope'
I just did this with the where-object cmdlet and the -like operator with an foreach loop.
foreach($var in (get-variable | Where-object {$_.name -like '*PRD*' -or $_.name -like '*TST*'})){
$var
}

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

How Does Member Enumeration Work in PowerShell 3?

In PoweShell 2 we did:
Get-ChildItem | ForEach-Object {$_.LastWriteTime} | Sort-Object
In Powershell 3 we do:
(Get-ChildItem).LastWriteTime | Sort-Object
But how does it work, i read this blog post on MSDN and they say that its faster because the foreach loop isnt running? So how does it enumerate the properties then ?
PowerShell is doing the hard work for us and it loops over the collection internally. I like to call this "implicit foreach". Assuming the member you specified is present on each object, if the member you specified is a property, you get back its value. If it's a method, it invokes the method on the each object.
In v2, to get all process names you had to take care of looping yourself:
Get-Process | Foreach-Object {$_.Name}
In v3, the equivalent would be:
(Get-Process).Name
Same applies to methods. To kill all processes with name starting with note*:
(Get-Process note*).Kill()
The blog says foreach-object cmdlet is not running. Now it is taken care of by the language engine and not a cmdlet, making it faster. How it EXACTLY works is internal implementation detail and I think that is not what you really want to know.