Is there a way to use a calculated property in the same select statement for another calculated property. Or do you have to have another separate select for this?
I'm using PowerCLI from VMware to try to produce some memory stats (total, provisioned, % provisioned) for hosts.
get-vmhost | sort Parent |
select Parent, Name, #{Name="MemoryTotalGB";E={[math]::Round($_.MemoryTotalGB)}}, #{Name="MemoryProvisionedGB";Expression={$_ | get-vm | measure -sum MemoryGB | select -ExpandProperty Sum}} |
select Parent, Name, MemoryTotalGB, MemoryProvisionedGB, #{Name="MemoryProvisionedPercentage";E={[math]::Round($_.MemoryProvisionedGB / $_.MemoryTotalGB * 100)}} | ft
In the first select statement, I calculate the sum of VM memory running on the host as MemoryProvisionedGB. In the 2nd I do the % provisioned using this sum.
Is it possible to somehow reference the calculated property MemoryProvisionedGB in the first select statement to produce the % calculation?
Why don't you have the collection calculated just once (Although, BenH's ForEach-Object suggestion is more elegant)?
Get-VMHost |
Sort-Object -Property 'Parent' |
Select-Object -Property #(
'Parent'
'Name'
#{ N = 'MemoryTotalGB'
E = {[Math]::Round($PSItem.MemoryTotalGB)}
}
#{ N = 'MemoryProvisionedGB'
E = {($PSItem | Get-VM | Measure-Object -Sum 'MemoryGB').Sum}
}
#{ N = 'MemoryProvisionedPercentage'
E = {[Math]::Round(($PSItem | Get-VM | Measure-Object -Sum 'MemoryGB').Sum /
[Math]::Round($PSItem.MemoryTotalGB) * 100)
}
}
) | Format-Table
Rather than using calculated properties, you could use a ForEach-Object loop and create a [pscustomobject]. This would allow you to create several variables to reuse.
Get-VMHost |
ForEach-Object {
$MemoryTotalGB = [math]::Round($_.MemoryTotalGB)
$MemoryProvisionedGB = ($_ | Get-VM | Measure-Object -Sum MemoryGB).Sum
[PSCustomObject]#{
'Parent' = $_.Parent
'MemoryTotalGB' = $MemoryTotalGB
'MemoryProvisionedGB' = $MemoryProvisionedGB
'MemoryProvisionedPercentage' = [math]::Round($MemoryProvisionedGB / $MemoryTotalGB * 100)
}
} |
Sort-Object Parent |
Format-Table
Related
Get-VM |
Select Name, Operatingsystem, VMHost, PowerState,
#{N="Datastore"; E={$_ |
Get-Datastore}} |
Out-gridview
I ran this command. It will return and output a grid view with all rows filled in.
However, the field "OperatingSystem" returns a blank column, nothing there.
Untested, but I think you can do this:
Get-VM | Select-Object Name,
#{Name = 'Operatingsystem'; Expression = {$_.Guest.OsFullName}},
VMHost, PowerState,
#{Name = 'Datastore'; Expression = {$_ | Get-Datastore}} |
Out-GridView
You're looking for the "Guest" property I think. FYI you can also do this to view everything if you're not sure of the exact name
Get-VM |
Select-Object -Property * |
Out-gridview
I am trying to find Latency for a datastore.
below is the code
$vmName = ""
$stat = "datastore.totalReadLatency.average","datastore.totalWriteLatency.average"
$entity = Get-VM -Name $vmName | select -Unique
$start = (Get-Date).AddHours(-1)
$dsTab = #{}
$dsTab = Get-Datastore | Where {$_.Type -eq "VMFS"} | %{
$key = $_.ExtensionData.Info.Vmfs.Uuid
if(!$dsTab.ContainsKey($key)){
$dsTab.Add($key,$_.Name)
}
else{
"Datastore $($_.Name) with UUID $key already in hash table"
}
}
Get-Stat -Entity $entity -Stat $stat -Start $start |
Group-Object -Property {$_.Entity.Name} | %{
$vmName = $_.Values[0]
$VMReadLatency = $_.Group |
where {$_.MetricId -eq "datastore.totalReadLatency.average"} |
Measure-Object -Property Value -Average |
Select -ExpandProperty Average
$VMWriteLatency = $_.Group |
where {$_.MetricId -eq "datastore.totalWriteLatency.average"} |
Measure-Object -Property Value -Average |
Select -ExpandProperty Average
$VMReadIOPSAverage = $_.Group |
where {$_.MetricId -eq "datastore.numberReadAveraged.average"} |
Measure-Object -Property Value -Average |
Select -ExpandProperty Average
$VMWriteIOPSAverage = $_.Group |
where {$_.MetricId -eq "datastore.numberWriteAveraged.average"} |
Measure-Object -Property Value -Average |
Select -ExpandProperty Average
$_.Group | Group-Object -Property Instance | %{
New-Object PSObject -Property #{
VM = $vmName
Host = $_.Group[0].Entity.Host.Name
Datastore = $dsTab[$($_.Values[0])]
Start = $start
DSReadLatencyAvg = [math]::Round(($_.Group |
where {$_.MetricId -eq "datastore.totalReadLatency.average"} |
Measure-Object -Property Value -Average |
Select -ExpandProperty Average),2)
DSWriteLatencyAvg = [math]::Round(($_.Group |
where {$_.MetricId -eq "datastore.totalWriteLatency.average"} |
Measure-Object -Property Value -Average |
Select -ExpandProperty Average),2)
VMReadLatencyAvg = [math]::Round($VMReadLatency,2)
VMWriteLatencyAvg = [math]::Round($VMWriteLatency,2)
VMReadIOPSAvg = [math]::Round($VMReadIOPSAverage,2)
VMWriteIOPSAvg = [math]::Round($VMWriteIOPSAverage,2)
}
}
} | Export-Csv c:\report.csv -NoTypeInformation -UseCulture
When I check with any datastore, I am not able to find stat "datastore.totalReadLatency.average","datastore.totalWriteLatency.average"
Please let me know what is wrong I am doing or is there anything which needs to be done ( any update/Installation )
Running your
Get-Stat -Entity $entity -Stat $stat -Start $start
in PowerCLI v12.4 I see errors like below and no data returned
The metric counter "datastore.totalreadlatency.average" doesn't exist for
entity <$vmname>
which led me to this thread where the solution code looks close to yours. Anyway, using -Realtime seems to fix that piece for your last-hour scenario. Offhand I'm not sure if something changed in recent versions, or what.
Not finding this documented, but Get-Stat appears to ignore -Start when -Realtime is specified. So try
Get-Stat -Entity $entity -Stat $stat -Realtime
(Maybe you grabbed the $stat definition from the first answer in that thread, but grabbed the remaining code from the accepted answer? You're attempting to parse results for numberReadAveraged & numberWriteAveraged without gathering those results.)
To look beyond realtime/last-hour, you'd need collection level 3 or higher for totalReadLatency and totalWriteLatency. Increasing stat levels will grow the vCenter db.
I want to use the powershell to return values, likes task manager.
However, the item of network usage cannot return correctly.
How can I modify the code? Thanks a lot.
$RAM= Get-WMIObject Win32_PhysicalMemory | Measure -Property capacity -Sum | %{$_.sum/1Mb}
$cores = (Get-WmiObject Win32_Processor).NumberOfLogicalProcessors
while ($true) {
$tmp = Get-WmiObject Win32_PerfFormattedData_PerfProc_Process |
select-object -property Name, #{Name = "CPU"; Expression = {($_.PercentProcessorTime/$cores)}}, #{Name = "PID"; Expression = {$_.IDProcess}}, #{"Name" = "Memory(MB)"; Expression = {[int]($_.WorkingSetPrivate/1mb)}}, #{"Name" = "Memory(%)"; Expression = {([math]::Round(($_.WorkingSetPrivate/1Mb)/$RAM*100,2))}}, #{Name="Disk(MB)"; Expression = {[Math]::Round(($_.IODataOperationsPersec / 1mb),2)}}, #{"Name"="Network"; Expression = {get-counter "\Process($_.Name)\IO Read Bytes/sec"}} |
Where-Object {$_.Name -notmatch "^(idle|_total|system)$"} |
Sort-Object -Property CPU -Descending|
Select-Object -First 5;
cls
$tmp | Format-Table -Autosize -Property Name, CPU, PID, "Memory(MB)", "Memory(%)", "Disk(MB)", "Network";
Start-Sleep 3
}
This code is modified from articles as follows:
1. CPU and memory usage in percentage for all processes in Powershell
2. https://superuser.com/questions/1314534/windows-powershell-displaying-cpu-percentage-and-memory-usage#new-answer?newreg=a9290345d72946db9c7f8fd2af10de0a
3. Powershell Script - list process with cpu, memory, disk usage
I add a variable $process to show the owner of each process, but there are missing some parameters. I cannot find any documents about Get-WmiObject Win32_PerfFormattedData_PerfProc_Process from microsoft, is that normal? The modified code as follows:
$RAM= Get-WMIObject Win32_PhysicalMemory | Measure -Property capacity -Sum | %{$_.sum/1Mb}
$cores = (Get-WmiObject Win32_Processor).NumberOfLogicalProcessors
$Process = Get-Wmiobject Win32_process -computername "myComputerName" | select *,#{Name='Owner';Expression={($_.GetOwner()).User}}
while ($true) {
$tmp = Get-WmiObject Win32_PerfFormattedData_PerfProc_Process |
select-object -property Name, #{Name = "CPU"; Expression = {($_.PercentProcessorTime/$cores)}}, #{Name = "PID"; Expression = {$_.IDProcess}}, #{"Name" = "Memory(MB)"; Expression = {[int]($_.WorkingSetPrivate/1mb)}}, #{"Name" = "Memory(%)"; Expression = {([math]::Round(($_.WorkingSetPrivate/1Mb)/$RAM*100,2))}}, #{Name="Disk(MB)"; Expression = {[Math]::Round(($_.IODataOperationsPersec / 1mb),2)}}, #{"Name"="Network"; Expression = { $_.IOReadBytesPersec }}, #{Name="Username"; Expression = {($($Process | ?{$_.ProcessId -eq $Item.IDProcess})).Owner} |
Where-Object {$_.Name -notmatch "^(idle|_total|system)$"} |
Sort-Object -Property CPU -Descending|
Select-Object -First 15;
cls
$tmp | Format-Table -Autosize -Property Name, CPU, PID, "Memory(MB)", "Memory(%)", "Disk(MB)", "Network", "Username";
Start-Sleep 1
}
The modified part is referenced from https://powershell.org/forums/topic/getting-percentage-cpu-time-and-owner-for-processes/
Guo-Jyun Zeng, welcome to SO.
First and foremost, the reason your 'Network' section isn't showing anything is because variable expansion is failing. Let me explain--while PowerShell understands basic variables to expand inside of double-quoted strings, it does not directly understand slightly more complex objects that have their own properties. So the problem you're having is here:
#{"Name"="Network"; Expression = {get-counter "\Process($_.Name)\IO Read Bytes/sec"}}
Since '$_.Name' is inside the double-quotes, PowerShell doesn't know what to do with it. There are a couple of ways you can easily accommodate this:
#{"Name"="Network"; Expression = {get-counter "\Process($($_.Name))\IO Read Bytes/sec"}}
or using string tokens:
#{"Name"="Network"; Expression = {get-counter ("\Process({0})\IO Read Bytes/sec" -f $_.Name)}}
However, in my tests to reproduce, simply running Get-Counter returns a complex object. That is, an object that has it's own properties and methods so this shouldn't work anyway. In targeting the resulting property I thought you'd be after (Get-Counter "\Process(ProcessName)\IO Read Bytes/sec").CounterSamples.CookedValue, the script never returned data at all--like the Get-Counters were taking too long or something.
However, 'Win32_PerfFormattedData_PerfProc_Process' already appears to have an equivalent property for the counter you seem to want data for: IOReadBytesPersec.
The only thing I'd note is that both that property and the Process-specific performance counter for 'IO Read Bytes/sec' count ALL I/O, not just network. In fact, looking at the processor object, I couldn't find any counters that were specific to Network-only.
Your CPU shows nothing when I run it because you're trying to divide by 0 in a lot of cases. Looking at PercentProcessorTime, it doesn't specify anything about an aggregate number of all processors. I wouldn't assume the load is divided across the cores as your code does--I could be wrong tho.
Finally, as a point of performance, you can significantly lessen the hardware impact of your script by rearranging some of the things you're doing:
Explicitly request the desired properties from 'Win32_PerfFormattedData_PerfProc_Process' so it doesn't default to all properties.
Consolidate the Select-Object -First 5 with your first Select-Object so you're only working on those 5 in the rest of the loop.
Here is some slightly modified code to show some of the changes I described:
$RAM= Get-WMIObject Win32_PhysicalMemory | Measure -Property capacity -Sum | %{$_.sum/1Mb}
$cores = (Get-WmiObject Win32_Processor).NumberOfLogicalProcessors
while ($true) {
$tmp = Get-WmiObject Win32_PerfFormattedData_PerfProc_Process |
select-object -First 5 -Property Name, #{Name = "CPU"; Expression = {($_.PercentProcessorTime)}}, #{Name = "PID"; Expression = {$_.IDProcess}}, #{"Name" = "Memory(MB)"; Expression = {[int]($_.WorkingSetPrivate/1mb)}}, #{"Name" = "Memory(%)"; Expression = {([math]::Round(($_.WorkingSetPrivate/1Mb)/$RAM*100,2))}}, #{Name="Disk(MB)"; Expression = {[Math]::Round(($_.IODataOperationsPersec / 1mb),2)}}, #{"Name"="Network"; Expression = { $_.IOReadBytesPersec }} |
Where-Object {$_.Name -notmatch "^(idle|_total|system)$"} |
Sort-Object -Property CPU -Descending
cls
$tmp | Format-Table -Autosize -Property Name, CPU, PID, "Memory(MB)", "Memory(%)", "Disk(MB)", "Network";
Start-Sleep 3
}
if I have two arrays and region column has no same values
like
$data1=
Region Type
------ -----------
EuropeWest Operational
EuropeWest Operational
EuropeWest Operational
EuropeNorth Operational
USCentral Operational
USCentral Operational
AsiaEast Operational
AsiaEast Operational
AsiaEast Operational
$data2=
Region Type
------ -----------
EuropeWest MigrateSource
EuropeWest MigrateSource
EuropeNorth MigrateSource
USCentral MigrateSource
USEast MigrateSource
output should be as:
Region Operational MigrateSource
------ ----------- -----------
EuropeWest 4 2
EuropeNorth 1 1
USCentral 2 0
AsiaEast 3 1
Useast 0 1
Any help much appreciated?
I was able to group it but did'nt get any clue how to use foreach loop here:
$data1 | group -Property region | select name,#{n='Operationaclcount';e={$_.count}}
$data2 | group -Property region | select name,#{n='Migratesourcecountt';e={$_.count}}
Since you have two objects with a property name, if we combine these together we have a full list of names. With a ForEach loop, we'll loop over these names and use a Where-Object to filter each of the two objects you created for the count. We'll then create a new object with [pscustomobject]. Finally a quick test if a name is missing from a group that means the count was zero.
$OpCount = $data1 |
Group-Object -Property region |
Select-Object name,#{n='Operationalcount';e={$_.count}}
$MigCount = $data2 |
Group-Object -Property region |
Select-Object name,#{n='Migratesourcecount';e={$_.count}}
$CombinedNames = $OpCount.name + $MigCount.name
Foreach ($Name in $CombinedNames) {
$entry = [pscustomobject]#{
Operational = $OpCount |
Where-Object {$_.name -eq $Name} |
Select-Object -Expand Count
MigrateSource = $MigCount |
Where-Object {$_.name -eq $Name} |
Select-Object -Expand Count
}
if ($entry.Operational -eq $null) { $entry.Operational = 0 }
if ($entry.MigrateSource -eq $null) { $entry.MigrateSource = 0 }
$entry
}
If you want to combine two arrays just use a plus. Something like this:
$data1 + $data2 | Group-Object region | % {
New-Object psobject -property #{
Name = $_.Name
Operational = $_.Count
MigrateSource = #($_.Group | Select Type -Unique).Count
}
}
I need to decide the columns' orders of my table. My actual command is that one:
$tab | Sort-Object "Pourcentage" -Descending |
Format-Table -AutoSize |
Out-String -Width 4000 |
Out-File -Append O:\sigdci\public\parcoursArborescence\bilanAnalyse.txt
It gives me that order:
Derniere modification Categorie recherchee Dernier acces Dossier Pourcentage
But I need "Dossier" to be first, then "Categorie recherchee" and "Pourcentage" shall be 2nd and 3rd. How shall I proceed?
Specify the column headers in the desired order:
$tab | Sort-Object "Pourcentage" -Descending |
Format-Table 'Dossier', 'Categorie recherchee', 'Pourcentage',
'Derniere modification', 'Dernier acces' -AutoSize |
Out-String -Width 4000 |
Out-File -Append 'O:\sigdci\public\parcoursArborescence\bilanAnalyse.txt'
If you need to dynamically determine the column names you could do it like this:
$headers = $tab[0].PSObject.Properties |
Where-Object MemberType -eq NoteProperty |
Select-Object -Expand Name
However, you'd have to bring that list into your desired order somehow. Perhaps you could do it like this:
$allHeaders = 'Dossier', 'Categorie recherchee', 'Pourcentage',
'Derniere modification', 'Dernier acces'
$actualHeaders = $tab[0].PSObject.Properties |
Where-Object { MemberType -eq NoteProperty } |
Select-Object -Expand Name
$headers = $allHeaders | Where-Object { $actualHeaders -contains $_ }
$allHeaders is an array that contains all headers in the correct order. Then you remove all items that aren't present in $actualHeaders from that list, preserving the order of the remaining headers.