Is there a way to put a counter column when doing Get commands in PowerShell? - 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++)}}

Related

fetch particular column in PowerShell

I am creating a PowerShell Script wherein I have a csv file which consist of few parameters wherein there is a parameter called as status .The ask is whenever the status is Fail ,I want to get the corresponding row. I am using Import-Csv cmdlet in order to fetch the csv file and checking the status if the status is fail or not and based on that I am fetching the corresponding details using the split function but when I am using the split method it is giving me error as
Method invocation failed because
[System.Management.Automation.PSCustomObject] does not contain a
method named 'split'.
Using the below code
$Report=(Import-Csv "C:\Users\Documents\Optim_Config_Report_20210216170900.csv")
foreach($i in $Report)
{
if($i.Status -eq "Fail")
{
$RULE_ID= $i.split(',')[0]
Write-output $RULE_ID
}
}
Can someone please help me how can I get the corresponding Row details for which the Status is fail?
Report is something like this in a csv format
Rule,Id,Category,Sub_Category,System_Value,Risk,Status
1,Operations,Access,Login,High,Pass
2,Operations,Logging,AccessControl,Medium,Pass
3,Operations,encryptions,certificate,High,Fail,
4,Security,Encryption,protcolo,High,Fail
Thanks in Advance!
When you Import-CSV, you are creating an array of [PSCustomObject]s, each of which has properties corresponding to the names of the columns in the CSV. For your example, those properties will be Rule, ID, Category, Sub_Category, System_Value, Risk, and Status.
It is not clear from your example what information you wish to report in the event of a status of Fail, but all you need to do is reference the properties that contain that information - for example, if I wanted to report the Category and Sub_Category, I might use
...
if ($i.status -eq "Fail") {
Write-Host $i.Category, $i.Sub_Category
}
...
Which would, for your sample data, output
encryptions certificate
Encryption protcolo
For a CSV file named "test.csv" with content like this:
"Rule","Id","Category","Sub_Category","System_Value","Risk","Status"
1,Operations,Access,Login,1,High,Pass
2,Operations,Logging,AccessControl,1,Medium,Pass
3,Operations,encryptions,certificate,1,High,Fail
4,Security,Encryption,protcolo,1,High,Fail
This script:
Import-Csv -Path ./test.csv | Where-Object Status -eq Fail | Format-Table
Returns:
Rule Id Category Sub_Category System_Value Risk Status
---- -- -------- ------------ ------------ ---- ------
3 Operations encryptions certificate 1 High Fail
4 Security Encryption protcolo 1 High Fail

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

Editing ADPropertyValueCollection (IList)

From AD, I obtained a variable with ADPropertyValueCollection type with System.Security.Cryptography.X509Certificates.X509Certificate inside.
Looks like this:
> $test
Handle Issuer Subject
1234 CA1 CN=user1
2345 CA2 CN=user2
3456 CA3 CN=user3
I want to remove one position from the list, and add another one. Unfortunately I have no idea on how to do it - I found that this is an IList, which supports Remove method, but it seems I don't know how to use it.
I thought that maybe PS supports something like
$test[Handle = 1234], but apparently it doesn't.
You invoke IList.Remove() by passing the object that you want removed to it as an argument, so you could do something like:
$objectToRemove = $test |Where-Object Handle -eq 1234 |Select-Object -First 1
$test.Remove($objectToRemove)

Multiple Write-Output

I'm trying to write a PowerShell script that will compare two variables, one containing a list of services currently running and the other a pre-defined list of ones that should to see the differences.
I want to display the differences whilst also displaying the current services running.
$compared = Compare-Object $spServices $spServices23 -SyncWindow 0
Write-Output $compared
if($sTask -eq "Read")
{
foreach ($spService in $spServices)
{
$out = new-object psobject
$out | add-member noteproperty TypeName $spService.TypeName
$out | add-member noteproperty Status $spService.Status
Write-Output $out
}
}
However, when I output the Compare-Object results it shows them but then comes up blank for the output of the $out variable.
Any help on how I can do this whilst keeping the output formatted.
PowerShell ALWAYS does its best to try to make sure it converts output into the most useful format. One of the ways it does this is by seeing the type of object that it is first displaying in a function, and ensuring that all future objects also match this format. Sometimes it's possible, and sometimes it's not.
In the case of your code, PowerShell executes and then tries to emit the results of Compare-Object, and succeeds. 'Compare-Object' emits an object that has these properties.
Name MemberType Definition
---- ---------- ----------
Equals Method bool Equals(System.Object obj)
GetHashCode Method int GetHashCode()
GetType Method type GetType()
ToString Method string ToString()
InputObject NoteProperty System.ServiceProcess.ServiceController InputObject=AdobeARMservice
SideIndicator NoteProperty string SideIndicator==>
These properties set the stage for what can also be emitted within this command, unless you do some fancy tricks. The reason you're not seeing the output of your later commands is that they don't also output the same properties.
To illustrate this quirk in action, see this code:
function Ham2{
[pscustomobject]#{Name='FoxDeploy';Job="Coder"}
[pscustomobject]#{Name='Ham';Profession="Coder"}
}
When this executes, the properties of the FIRST object emitted determine what gets displayed later on in the code. For example:
>ham2
Name Job
---- ---
FoxDeploy Coder
Ham
Working around this
There are a few ways to work around this.
First and foremost, a PowerShell best practice is that your scripts should ONLY emit one type of object. This is why functions have an .OUTPUT declaration available in their Help and [CmdletBinding()], PowerShell expects a command to issue only one type of object, plus maybe some -Verbose, or ErrorStream messages.
If you REALLY want to emit two types of objects, you could ensure that the first object has all of the properties you might ever want to display. Going back to my earlier example, if I added a Profession property to the first object, now my second object's Profession property will now become visible.
function Ham2{
[pscustomobject]#{Name='FoxDeploy';Job="Coder";Profession=''}
[pscustomobject]#{Name='Ham';Profession="Coder"}
}
PS C:\Users\Stephen> ham2
Name Job Profession
---- --- ----------
FoxDeploy Coder
Ham Coder
Probably what you want but not recommended
If you REALLY want to emit two or more different types of objects (which you surely don't, right?) then you can get around this quirk by using Format-List or Format-Table. Be warned: these convert the output into text formatting commands and you'll lose Object properties and people will generally think it was a hacky thing to do. But it will work.
function iFeelIcky{
$spservices = gsv AdobeARMservice,bits
$compared = Compare-Object (get-service bits,winrm) $spservices -SyncWindow 0
$compared | Format-Table
foreach ($spService in $spServices)
{
$out = new-object psobject
$out | add-member noteproperty DisplayName $spService.DisplayName
$out | add-member noteproperty Status $spService.Status
$out
}
}
C:\Users\Stephen> iFeelIcky
InputObject SideIndicator
----------- -------------
AdobeARMservice =>
bits <=
bits =>
winrm <=
DisplayName Status
----------- ------
Adobe Acrobat Update Service Running
Background Intelligent Transfer Service Running
}
I hope that helped! Let me know if you'd like me to dive deeper or for some reason want me to stay up on this soap box :-)

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