PowerShell - Emit object for each array member [duplicate] - powershell

This question already has answers here:
powershell json conversion problem correct results only when saving to a variable
(1 answer)
Read multiple JSON files into an array of Powershell objects and filter out those with the same value for a property
(1 answer)
Closed 1 year ago.
I'm trying to solve a problem for a customer. I don't have access to the same software as they do. So, I've created this demonstration code.
What I would like to suggest is for them to consume JSON. But, what I found for ConvertFrom-Json on PowerShell 5.1 is that it is emitting a single array as output.
They want to use Where-Object. But, in my testing, that will return the whole array.
Normally, I would expect PowerShell to emit single objects. I'm not sure why I'm getting one object (the entire array).
I noticed if I shut down the pipeline (Example One) then I get the behavior I want.
I'm aware of where(). But, I want to take advantage of the customer's muscle memory--<# objects #> | Where-Object { <# test #> }--and use the pipeline.
This also seemed like it could be improved: | ForEach-Object { $_.ForEach({ $_ }) } |
Question
Is there a better way to emit individual objects without shutting down the pipeline?
> # Example One
> (& '.\Stack Overflow Demo 03.ps1' | ConvertTo-JSON | ConvertFrom-Json) | Select-Object -First 1
Id : ba7fab67-5cd7-401a-9c88-c4a2e88f6932
Name : Development
Description : Default development device group
ProductId : 556957b4-aa39-4e5a-aa4c-d3208c277f3c
OsFeedType : Retail
UpdatePolicy : Accept only system software updates. Don't accep
AllowCrashDumpsCollection : False
CurrentDeployment : None
> # Example Two
> & '.\Stack Overflow Demo 03.ps1' | ConvertTo-JSON | ConvertFrom-Json | Select-Object -First 1
Id : ba7fab67-5cd7-401a-9c88-c4a2e88f6932
Name : Development
Description : Default development device group
ProductId : 556957b4-aa39-4e5a-aa4c-d3208c277f3c
OsFeedType : Retail
UpdatePolicy : Accept only system software updates. Don't accep
AllowCrashDumpsCollection : False
CurrentDeployment : None
Id : 45acb705-a591-4e41-ba1e-4b3dd062e1ec
Name : Field Test
Description : FieldTest
ProductId : 556957b4-aa39-4e5a-aa4c-d3208c277f3c
OsFeedType : Retail
UpdatePolicy : Accept all updates from the Azure Sphere Securit
AllowCrashDumpsCollection : False
CurrentDeployment : d684d491-9229-484f-a2eb-fda0168f7e27
...

Related

Is there a way to put a counter column when doing Get commands in PowerShell?

I need to extract a Get command results into a CSV. The order column should be automatically generated upon a call and give each object its counter upon its appearance in the list. Would this be possible?
For example, when I'd do something like
Get-VMHost | Select #{N="Order";E={$suborder++}}, Name, Version, Build | Export-Csv -path .\smth.csv
I would like to get a result like
Order Name Version Build
----- ---- ------- -----
1 servername1 1.1.1 11111111
2 servername2 1.1.1 11111111
3 servername3 1.1.1 11111111
Would this be possible without using an array?
There are two problems with the current approach:
Unary ++ doesn't output anything by default
Select-Object runs property expressions in their own scope, so you're not actually updating $suborder, you're creating a new local variable every time.
The first problem can be solved by wrapping the operation in the grouping operator (...):
... | Select #{N="Order";E={($suborder++)}}, ...
The second problem can be solved by obtaining a reference to an object that exposes the suborder value as a property.
You can either use a hashtable or a custom object to "host" the counter:
$counter = #{ suborder = 1 }
... | Select #{N="Order";E={($counter['suborder']++)}}
... or you can make PowerShell wrap the original variable in a PSRef-wrapper by using the [ref] keyword:
$suborder = 1
... | Select #{N="Order";E={(([ref]$suborder).Value++)}}

Getting from multi dimensional Array with named colums to array

The story: I read a json configuration file and somewhere in this, i get conversion challenges.
The json file
"FwRules":[{ "IP" : "222.33.44.00/27", "Comment" : "Santa" },
{ "IP" : "223.44.11.0/24", "Comment" : "Claus" },
Reading this file info a object gives me:
IP Comment
-- -------
222.33.44.00/27, Santa
223.44.11.0/24, Claus
I need to get to:
222.33.44.00/27
223.44.11.0/24
As you might guess, I want to use this in
New-NetFirewallRule -RemoteAddress $HERE
However column headers need to be removed.
The obvious way
$config.smtpservers.fwrules | select-object IP
returns what I want, but with column headers. And I really do not want to go the "foreach way", just because of OCD reasons.
So, is there a bright mind out there who can learn me how to strip of the column headers, because I cannot figure this out.
This should do the trick:
$config.smtpservers.fwrules.ip
With dot syntax, Powershell automatically builds an array from properties that exist for each array element.
Alternatively:
$config.smtpservers.fwrules | select-object -ExpandProperty IP

Get the Data values from the registry using powershell

I am trying to add an application in 'DisallowRun' registry key to avoid running application to specific users. Need to add the application if not exist. Consider 'TestApp3.exe' in this case.
Used below query to get the list of items in the key. It is giving Name(Key Name), Property(Name Value : Data Value)
Get-Item -Path "Registry::HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Policies\Explorer\DisallowRun"
Output
Name Property
---- --------
DisallowRun 1 : TestApp1.exe
DisallowRun 2 : TestApp2.exe
DisallowRun 3 : TestApp3.exe
DisallowRun 4 : TestApp4.exe
When i use .Property in the code, getting only Name Values and not Data Values
(Get-Item -Path "Registry::HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Policies\Explorer\DisallowRun").Property
Output
1
2
3
4
If i get only the Data Values (similar to below) then i could have use contains function to check the specific application is already available or not. Please help me to get only Data Value or is there anyway to check the application is exist in the registry key.
TestApp1.exe
TestApp2.exe
TestApp3.exe
TestApp4.exe
Try this out.
(Get-ItemProperty -Path HKCU:\Software\Microsoft\Windows\CurrentVersion\Policies\Explorer\DisallowRun).PSObject.Properties | Where-Object {
$_.Name -notmatch "^PS"
} | Select-Object -ExpandProperty Value
Try Get-ItemProperty -Path Registry::HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Policies\Explorer\DisallowRun

How can I summarize an object the same way Format-List does?

For example, looking at a processes threads shows something like this:
PS C:\> (Get-Process)[0] | Format-List -Property Threads
Threads : {1548, 1600, 15940, 13996}
But if you actually grab that property directly, it looks like this:
PS C:\> (Get-Process)[0].Threads
BasePriority : 8
CurrentPriority : 9
Id : 1548
IdealProcessor :
PriorityBoostEnabled :
PriorityLevel :
PrivilegedProcessorTime :
StartAddress : 8790537024736
StartTime :
ThreadState : Wait
TotalProcessorTime :
UserProcessorTime :
WaitReason : UserRequest
ProcessorAffinity :
Site :
Container :
BasePriority : 8
... etc
Format list obviously has a method to summarize objects intelligently. It took a list of objects, pulled out a representative property from each one, and displayed it as a short array. I cannot find a method or cmdlet that allows me to summarize an collection of objects in the same manner.
I want to be able to pass an arbitrary collection of objects to a method and have it summarize. This is used when listing email addresses in Exchange objects, listing groups in AD objects, and many other places... I doubt these are all special cases.
To expand (after learning more from #JoelSmith's comments):
.NET Objects have formatting definitions that are used by Powershell when formatting output. Additional details are available using help about_Format.ps1xml[1]. These definitions are generic and can be accessed by any command, but by default there are no functions in Powershell to directly retrieve the output of an object property directly as it would be displayed in Format-List.
One hackish workaround is to split and strip the output like so:
(Get-Mailbox user | Format-List -Property Languages | Out-String).Split(':')[1].Trim()
# => {en-US,fr-CA}
However this method is extremely fragile, and will fail when the output spans multiple lines or contains a colon in the output:
(Get-Mailbox user | Format-List -Property EmailAddresses | Out-String).Split(':')[1].Trim()
# => {smtp
What is needed is a method that reads the formatting definition defined for the object and retrieves it directly, then use it to output the desired string. I have failed to find any example online.
You can use the
PSStandardMembers.DefaultDisplayProperty
and
PSStandardMembers.DefaultDisplayPropertySet
properties of your objects to determine the default properties that should be displayed for each type. You can read more about it here. We've run into a similar problem recently in our like PowerShell project. You can find this discussion we've had helpful. There are some subtle differences between PS v2 and v3 which we debate on that thread.
Usually .ToString() works but sometimes they forget to implement that method.
(Get-Process)[0] | %{$_.Threads.Id}
EDIT: to answer your comment below
(Get-Process)[0] | Format-List -Property Threads | Out-String
Unfortunately not all cmdlets are the same.
Are you looking for something like this?
(Get-Process)[0].Threads | Format-Table -Property ID -AutoSize
Id
--
13060
13064
13068
13072
13076
13080
13084
This needs to be customized for each cmdlet depending on what the output is and what fields you need. The reason it doesn't work with just (Get-Process)[0] | Format-Table -Property Threads -AutoSize is because Threads returns thread-objects, and an array of objects are displayed like your first sample (string-presentation of your objects in a collection { .. }) .
Here's what I can tell so far:
The Id property is the default display property for a thread object (System.Diagnostics.ProcessThread).
I couldn't find any tracks of this in any of PowerShell's type files but I can change the way Format-* display threads (requires PowerShell 3.0).
By default the format cmdlets prints the Id value of each thread object:
Threads : {1548, 1600, 15940, 13996}
Formatting cmdlets checks the value of the $FormatEnumerationLimit variable (default is 4) to decide how to format the object.
If the result is one object (scalar) only it will print as:
Threads : 1548
If it's a collection of items and the collection count is up to the value of $FormatEnumerationLimit (4) it will display as:
Threads : {1548, 1600, 15940, 13996}
A count greater than $FormatEnumerationLimit will look like (... indicates that there are more objects):
Threads : {1548, 1600, 15940, 13996...}
I can tell Id is the default property in use because I can change it to another property and see its value reflecting in the output.
For example, Here I'm setting the ThreadState as the default display property:
PS> Update-TypeData -TypeName System.Diagnostics.ProcessThread -DefaultDisplayProperty ThreadState -Force
PS> (Get-Process)[0] | Format-List -Property Threads
Threads : {Wait, Wait, Wait, Wait...}
# revert back
PS> Update-TypeData -TypeName System.Diagnostics.ProcessThread -DefaultDisplayProperty Id -Force
Hope that helps

REST "dry-run" option for PUT or POST

Is there an idiomatic way of achieving this:
I need to PUT/POST a given entity. However, before actually putting it I need to do some changes on a more volatile system, and if that works I will go on.
So I will first ask if the PUT/POST is acceptable and then later actually do the PUT/POST.
I've thought of just using a "dry-run" query-parameter, but it doesn't feel like the right way.
Update: Trying to clarify my problem. The point is that the first PUT is just for verification of the entity.
Me System A Volatile System X
| Dry PUT | :
|-------------->| :
| | :
| 20x / 40x | :
|<--------------| :
| : :
| Upon PUT OK do some related work :
|----------------------------------->|
| : |
| Work completely |
|<-----------------------------------|
| :
|PUT (for real) :
|-------------->|
| |
| 20x |
|<--------------|
Logically I feel that this could perhaps be solved by a some kind of state property. If you are using JSON, you might for example consider adding a property like this:
{
"draft" : true
}
The first time you do the PUT request, you mark the item as draft. It stores the item but doesn't do anything else with it .
After your server accepted your request, you can then do your 'related work' elsewhere, and if that succeeded as well you can submit another PUT request to the same resource, this time setting draft to false.