Powershell query results list - powershell

The Powershell script provides results table below. We need to find out if VM is reachable or not, so I would like to check if "Time" column is not empty.
The question:
How to check if "Time(ms)" has value? how to query any column or row in such a cases in general?
$result = Test-ConnectionAsync "MyVm"
C:\$result
Source Destination IPV4Address IPV6Address Bytes Time(ms)
------ ----------- ----------- ----------- ----- --------
SourceVM MyVM 10.20.20.10 32 207
SourceVM MyVM 10.20.20.10 32 207
SourceVM MyVM 10.20.20.10 32 207
SourceVM MyVM 10.20.20.10 32 207
thank you!

You can use the Where-Object functionality that checks the value of any column of the given row. If you want to find out what column names are supposed to be, use Get-Member to get all the properties or methods of that object.
# Get all the properties of $result[0]
$result[0] | Get-Member
# Among other things, you will ResponseTime as one of the properties you can use.
# Check if Time(ms) has any value. Following will give you only those rows that match the condition
#Original where statement
#$result | Where-Object { $_.ResponseTime -ne $null }
#Updated where statement
$result | Where-Object { $null -ne $_.ResponseTime }

Related

Getting value from CSV

I'm writing a PowerShell script to read a CSV. I have everything working so far, it's able to find the value from user input (branch number) and finds the values that have "Y" in the available. Please see 2nd picture.
First picture is my CSV file.
This is what I need help with. How would I get the value of the first available CoreID? In this example, FMD354800000. Once I get the first available CoreId, I want to change the Available to N
$Find = $ImportCSV | Select-String -Pattern $GetBranchNum
$Find -match "Y"
New-ItemProperty -path $CoreIP -name "TTable ID" -PropertyType String -Value "Test" -Force
Use Import-Csv to import the CSV:
$csv = Import-Csv 'C:\path\to\your.csv'
Since the file seems to be fixed width you'll need to Trim() fields before checking their value.
$branchNumber = '8000'
$first = $csv | Where-Object {
$_.'Branch Number'.Trim() -eq $branchNumber -and
$_.Available.Trim() -eq 'y'
} | Select-Object -First 1
This filters the CSV for records with the given branch number that have a value Y in the field Available and selects the first matching record.
Then change the value of the Available field of that record:
$first.Available = 'N'
Demonstration:
PS C:\> $csv = Import-Csv 'C:\path\to\sample.csv'
PS C:\> $csv
Branch Number CoreID Available
------------- ------ ---------
8000 FMD354800000 Y
8000 FMD354800001 Y
8000 FMD354800002 N
PS C:\> $first = $csv | Where-Object {
>> $_.'Branch Number'.Trim() -eq '8000' -and
>> $_.Available.Trim() -eq 'y'
>> } | Select-Object -First 1
>>
PS C:\> $first
Branch Number CoreID Available
------------- ------ ---------
8000 FMD354800000 Y
PS C:\> $first.Available = 'N'
PS C:\> $first
Branch Number CoreID Available
------------- ------ ---------
8000 FMD354800000 N
PS C:\> $csv
Branch Number CoreID Available
------------- ------ ---------
8000 FMD354800000 N
8000 FMD354800001 Y
8000 FMD354800002 N
Neither Select-String nor the -match operator are particularly useful in this context, so don't use them.

How to Add Operations in Powershell Command

I am using the following Powershell command in order to extract the name, the assigned RAM and RAM usage of each VMs in the server.
Get-VM | ft Name, Memorydemand, Memoryassigned
However the result of the memorydemand and memoryassigned are in Bytes but I want them to be in Megabytes. Is there a way for me to divide the results of the memorydemand and memoryassigned by 1048576 so that I can get their corresponding MB?
Also, is it also possible to get the average RAM Usage of a certain VM for the last one or two months? Even though Hyper-V is assigning dynamic memory, I just want to double-check.
There's a couple different approaches that I can think of to achieve this.
Use Select-Object to create calculated properties
Use the Select-Object command to create custom, calculated properties.
Get-VM | Select-Object -Property `
Name,
#{ Name = 'MemoryDemandMB'; Expression = { $PSItem.MemoryDemand/1MB } },
#{ Name = 'MemoryAssignedMB'; Expression = { $PSItem.MemoryAssigned/1MB } } |
Format-Table -Property Name, MemorydemandMB, MemoryassignedMB -AutoSize
Use Add-Member to augment the objects
You can use the Add-Member command to add two new properties to the objects. This actually augments the objects, rather than simply appending the properties for the lifetime of the pipeline.
Get-VM |
Add-Member -MemberType ScriptProperty -Name MemoryDemandMB -Value { $this.MemoryDemand/1MB } -PassThru |
Add-Member -MemberType ScriptProperty -Name MemoryAssignedMB -Value { $this.MemoryAssigned/1MB } -PassThru |
Format-Table -Property Name, MemorydemandMB, MemoryassignedMB -AutoSize
Results
Here's what the output looks like on my system.
Name MemoryDemandMB MemoryAssignedMB
---- -------------- ----------------
agent01 0 0
agent02 0 0
dc01 878 1058
denver01 0 0
london01 877 1070
MobyLinuxVM 0 0
munich01 1228 1638
sccm01 2213 2604
swarm01 0 0
UbuntuDesktop 0 0

Create Union of Multiple Tables in Powershell

I'm trying to create a union of multiple tables in Powershell to output in a user-friendly format as a report, similar to a UNION query in SQL.
I have the following code:
$ft = #{auto=$true; Property=#("MachineName", "Status", "Name", "DisplayName")}
$hosts = #("svr001", "svr002")
$report = #()
ForEach ($h in $hosts) {
$results = Get-Service -CN $h -Name MyService
$report += $results | Format-Table #ft
# Other things occur here, which is why I'm creating $report for output later.
}
Write-Output $report
The output of this code is as follows:
MachineName Status Name DisplayName
----------- ------ ---- -----------
svr001 Running MyService MyServiceDisplayName
MachineName Status Name DisplayName
----------- ------ ---- -----------
svr002 Running MyService MyServiceDisplayName
Since you simply add arrays to do a union in Powershell (i.e.,
$union = #(0, 1, 2) + #(3, 4, 5)), my initial thought was that I should
get the following output:
MachineName Status Name DisplayName
----------- ------ ---- -----------
svr001 Running MyService MyServiceDisplayName
svr002 Running MyService MyServiceDisplayName
In retrospect, I think I understand why I do not get this output, but I'm
unclear how to create a union of the two tables from the first output example into a single table as in the second, and I haven't been able to locate anything in the docs or examples online that would send me in the right direction.
Move the Format-Table to the last command. Format-* cmdlets create special format-objects that you can't work with manually so theres no point in saving them. When you save the result of Format-* to an array, you're saving the "report" which is why you get two tables in the output (array consists of two reports).
Collect the data first, then use Format-Table when you want to display the results.
$ft = #{auto=$true; Property=#("MachineName", "Status", "Name", "DisplayName")}
$hosts = #("svr001", "svr002")
$report = #()
ForEach ($h in $hosts) {
$results = Get-Service -ComputerName $h -Name MyService
$report += $results
# Other things occur here, which is why I'm creating $report for output later.
}
#Write-Output is not necessary as it is default behaviour
$report | Format-Table #ft
Sample output (used wuauserv as servicename):
MachineName Status Name DisplayName
----------- ------ ---- -----------
localhost Stopped wuauserv Windows Update
frode-pc Stopped wuauserv Windows Update
The Get-Service Cmdlet also supports an array of strings for the -ComputerName parameter. This works for me:
Get-Service -CN $hosts -Name MyService | Format-Table #ft
Sample Output using wuauserv:
MachineName Status Name DisplayName
----------- ------ ---- -----------
Tim-SRV1 Running wuauserv Windows Update
Tim-SRV2 Stopped wuauserv Windows Update

Powershell counting lines in file incorrectly when there is only one line in the file?

I am writing a powershell script to calculate summary stats for a csv file with 100,000+ rows.
In my foreach loop, one of my lines is:
$count = $Not_10000.count
Where "$Not_10000" is the result after filtering a csv, which was read using the import-csv command and filtered using
where {$_.ifhighspeed -eq 10000}.
I found that the value of "$count" is correct whenever "$Not_10000" has more than one line. However, when "$Not_10000" only has one line, the result is that $count is empty. I tried going into the powershell prompt and doing
$count = $Not_10000 | Measure-Object -lines
But it shows 0 lines even though it has one line. The output of
$Not_10000[0]
is
DATE ENTITYNAME IFHIGHSPEED
---- ---------- -----------
8/25/2014 12:00:00 AM SF15-0326 1000
Why wouldn't this one line output be counted correctly? I manually changed the filters to make "$Not_10000" contain 15 lines, and this was counted correctly.
I seem to have trouble in general giving the full picture, so let me know if you need more info or clarification.
Matt gives a good answer, but is only kind of the answer to your question.
The .Count property is a member of the Array object type that is being referenced when the number of results is more than one. When you only have one result then what is returned is not an array, it is a String, or an Object, or an Integer or something, but not an array unless you specifically make it one. In order for this to always come back with a number:
$count = $Not_10000.count
You need to cast it as an array, which is most simply done by enclosing it in #()
$count = #($Not_10000).count
This is most easily seen by using the .PSObject member of any object, when used on an array. Let's create an array, and look at the Type to verify that it's an array, and then look at the PSObject members (filtering for just properties, since it has a lot of members we don't care about) for that array object.
PS C:\Users> $test = #("Red","Blue","Green")
PS C:\Users> $test.GetType()
IsPublic IsSerial Name BaseType
-------- -------- ---- --------
True True Object[] System.Array
PS C:\Users> $test.PSObject.Members | Where{$_.MemberType -match "property"}|FT Name,MemberType,Value,ReferencedMemberName
Name MemberType Value ReferencedMemberName
---- ---------- ----- --------------------
Count AliasProperty 3 Length
Length Property 3
LongLength Property 3
Rank Property 1
SyncRoot Property {Red, Blue, Green}
IsReadOnly Property False
IsFixedSize Property True
IsSynchronized Property False
Item ParameterizedProperty ...int index) {get;set;}
What we see here is that it is an AliasProperty for the member Length, which on an array gives the number of records, just like it's alias Count does.
Use the Count property of Measure-Object. Lines, Words, Characters are things you use to measure text not objects
$count = $Not_10000 | Measure-Object | Select-Object -ExpandProperty Count
or
$count = ($Not_10000 | Measure-Object).Count
More Explanation
I have a csv with a header and 7 entries.
Path Data Files
---- ---- -----
\\someserver\somepath1 100 1
\\someserver\somepath2 150 4
\\someserver\somepath1 200 5
\\someserver\somepath3 450 8
\\someserver\somepath4 200 23
\\someserver\somepath1 350 2
\\someserver\somepath2 800 9
When i do the following (-lines is not a valid switch for Measure-Object )
Import-Csv E:\temp\stack.csv | Where-Object{$_.Data -gt 300} | Measure-Object -line
Since this is not text there are no lines to measure. I get this output regardless of how many entries in the file or filtered object. You would expect 3 but you actually get 0
Lines Words Characters Property
----- ----- ---------- --------
0
If i read the file as text i would get a result for lines
Get-Content E:\temp\stack.csv | Measure-Object -line
Lines
-----
8

PS: Filter selected rows with only max values as output?

I have a variable results ($result) of several rows of data or object like this:
PS> $result | ft -auto;
name value
---- -----
a 1
a 2
b 30
b 20
....
what I need to get all the rows of name and max(value) like this filtered output:
PS> $result | ? |ft -auto
name value
---- -----
a 2
b 30
....
Not sure what command or filters available (as ? in above) so that I can get each name and only the max value for the name out?
$result | group name | select name,#{n='value';e={ ($_.group | measure value -max).maximum}}
This should do the trick:
PS> $result | Foreach {$ht=#{}} `
{if ($_.Value -gt $ht[$_.name].Value) {$ht[$_.Name]=$_}} `
{$ht.Values}
This is essentially using the Begin/Process/End scriptblock parameters of the Foreach-Object cmdlet to stash input objects with a max value based on a key into a hashtable.
Note: watch out for extra spaces after the line continuation character (`) - there shouldn't be any.