PowerShell: calculate delta between successive values of the rawvalue property for each counter - powershell

'evening,
I'm brand new at PowerShell scripting (and to this site) and need to write a script that:
gathers performance counters (in this case, TCP) several times
calculates the difference between successive values of the rawvalue property of each counter
Stipulating that every interval T the script iterates through the performance counters N times, and said performance counters are "A" and "B", and we're counting from 0, it needs to perform the following calculations:
A[1st] - A[0th],
A[2nd] - A[1st],
A[3rd] - A[2nd]
...
At present, the script only iterates through the counters, twice (i.e. N = 2 in this case). The goal is to be able to be able to iterate through these counters "many" (e.g. a couple hundred) times.
Currently the script reads the raw value of each counter into a single array. Here it is:
$cntr = (get-counter -listset tcpv4).paths
$arry = #()
for ($i=0; $i -lt 2; $i++) {
write-host "`nThis is iteration $i`n"
foreach ($elmt in $cntr) {
$z = (get-counter -counter $elmt).countersamples[0].rawvalue
$arry = $arry + $z
write-host "$elmt is: $z`n"
}
}
When I run this script, I get output just like the following:
This is iteration 0
\TCPv4\Segments/sec is: 24723
\TCPv4\Connections Established is: 27
\TCPv4\Connections Active is: 796
\TCPv4\Connections Passive is: 47
\TCPv4\Connection Failures is: 158
\TCPv4\Connections Reset is: 412
\TCPv4\Segments Received/sec is: 14902
\TCPv4\Segments Sent/sec is: 9822
\TCPv4\Segments Retransmitted/sec is: 199
This is iteration 1
\TCPv4\Segments/sec is: 24727
\TCPv4\Connections Established is: 27
\TCPv4\Connections Active is: 798
\TCPv4\Connections Passive is: 47
\TCPv4\Connection Failures is: 159
\TCPv4\Connections Reset is: 412
\TCPv4\Segments Received/sec is: 14903
\TCPv4\Segments Sent/sec is: 9824
\TCPv4\Segments Retransmitted/sec is: 200
e.g. The two values for the rawvalue property for the "\TCPv4\Segments Retransmitted/sec" counter are $arry[8] and $arry[17] respectively. To derive the difference between the two I'm using:
write-host "The difference between the successive counters for $($cntr[-1]) is $($($arry[17]) - $($arry[8]))."
Any help would be greatly appreciated.

I poked at it some, and this fell out:
$cntr = (get-counter -listset tcpv4).paths
$LastValue = #{}
Get-Counter $cntr -SampleInterval 2 -MaxSamples 5 |
foreach {
foreach ($Sample in $_.CounterSamples)
{
$ht = [Ordered]#{
Counter = $Sample.path.split('\')[-1]
TimeStamp = $_.TimeStamp
RawValue = $Sample.RawValue
LastValue = $LastValue[$Sample.Path]
Change = $Sample.RawValue - $LastValue[$Sample.Path]
}
if ($LastValue.ContainsKey($Sample.path))
{ [PSCustomObject]$ht }
$LastValue[$Sample.Path] = $Sample.RawValue
}
}
Edit:
This should work on V2:
$cntr = (get-counter -listset tcpv4).paths
$LastValue = #{}
Get-Counter $cntr -SampleInterval 10 -MaxSamples 3 |
foreach {
foreach ($Sample in $_.CounterSamples)
{
$Object = '' |
Select-Object Counter,TimeStamp,RawValue,LastValue,Change
$Object.Counter = $Sample.path.split('\')[-1]
$Object.TimeStamp = $_.TimeStamp
$Object.RawValue = $Sample.RawValue
$Object.LastValue = $LastValue[$Sample.Path]
$Object.Change = $Sample.RawValue - $LastValue[$Sample.Path]
if ($LastValue.ContainsKey($Sample.path))
{ $Object }
$LastValue[$Sample.Path] = $Sample.RawValue
}
}

Ok. Well lets work with this then
$cntr = (get-counter -listset tcpv4).paths
$arry = #()
$maximumIterations = 2 # Variable based since you intended to change this.
# Cycle the counters while recording the values. Once completed we will calculate change.
for ($i=1; $i -le $maximumIterations; $i++) {
foreach ($elmt in $cntr) {
$arry += New-Object -TypeName PsCustomObject -Property #{
Iteration = $i
Counter = $elmt
RawValue = (get-counter -counter $elmt).countersamples[0].rawvalue
Change = $null
}
}
}
# Now that we have all the values lets calculate the rate of change over each iteration.
$arry | Where-Object{$_.Iteration -gt 1} | ForEach-Object{
$previousIteration = $_.Iteration - 1
$thisCounter = $_.Counter
$thisValue = $_.RawValue
$previousValue = ($arry | Where-Object{$_.Counter -eq $thisCounter -and $_.Iteration -eq $previousIteration}).RawValue
$_.Change = $thisValue - $previousValue
}
$arry | Select Iteration,Counter,RawValue,Change
Not that we have to but I collected all of the counters data along with their iteration value like you were putting against Write-Host. You will notice that I create a placeholder for Change but do not populate it. Before the calculations $arry would have data like the following. Note: the output is truncated
Iteration Change Counter RawValue
--------- ------ ------- --------
1 \TCPv4\Segments/sec 28324837
1 \TCPv4\Connections Established 120
. .............................. .....
2 \TCPv4\Segments/sec 28325441
2 \TCPv4\Connections Established 125
Once all of that data is collected into $arry we get all the iterations that are not the first and process each item individually. Using the data of the current item in the pipeline we match it up with the previous iterations value. Using the same values as above we get the changes you were hoping to monitor
Iteration Counter RawValue Change
--------- ------- -------- ------
1 \TCPv4\Segments/sec 28324837
1 \TCPv4\Connections Established 120
. .............................. .....
2 \TCPv4\Segments/sec 28325441 604
2 \TCPv4\Connections Established 125 5

Related

Powershell - Group and count unique values from CSV file based on a column

I am trying to get total completed and failed job for each days. If a job failed for any specific VM (Name field) for any specific day, it will retry the operation.If it complete in second attempt, I want o ignore the failed count for that and reduce the total count accordingly
Example:
My code
$csv =ConvertFrom-Csv #"
Name,Start_time,Status
vm1,20-03-2022,Completed
vm2,20-03-2022,Completed
vm1,21-03-2022,Failed
vm1,21-03-2022,Completed
vm2,21-03-2022,Completed
vm1,22-03-2022,Completed
vm2,22-03-2022,Failed
vm2,22-03-2022,Failed
"#
$Results = #()
foreach ($group in $csv | Group Start_time)
{
$Completed = ($group.Group | group status | ? Name -eq Completed).Count
$Failed = ($group.Group | group status | ? Name -eq Failed).Count
$row = "" | Select Date,Total,Completed,Failed,"Success %"
$row.Date = $group.Name
$row.Total = $group.Count
$row.Completed = $Completed
$row.Failed = $Failed
$row."Success %" = [math]::Round($Completed / $row.Total * 100,2)
$Results += $row
}
Above code will give me output as :
Date Total Completed Failed Success %
20-03-2022 2 2 0 100
21-03-2022 3 2 1 66.67
22-03-2022 3 1 2 33.33
But I am looking for the unique value for each VM for each day and ignore the failure, any retry shows as completed
Date Total Completed Failed Success %
20-03-2022 2 2 0 100 -> Job completed for vm1 and vm2
21-03-2022 2 2 0 100 -> job failed for vm1 first, but in second try it completed. same day 2 entries for vm1(Failed and Completed. Ignore failure and take only completed)
22-03-2022 2 1 1 50 -> vm2 failed on both attempt. so it has to take as 1 entry. ignore the duplicate run.
This seems to work, needless to say, you're displaying the information in an quite unorthodox way. I believe the code you currently have is how the information should be displayed.
Using this CSV for demonstration:
$csv = ConvertFrom-Csv #"
Name,Start_time,Status
vm1,20-03-2022,Completed
vm2,20-03-2022,Completed
vm1,21-03-2022,Failed
vm1,21-03-2022,Completed
vm2,21-03-2022,Completed
vm1,22-03-2022,Completed
vm2,22-03-2022,Failed
vm2,22-03-2022,Failed
vm1,23-03-2022,Failed
vm1,23-03-2022,Failed
vm2,23-03-2022,Failed
vm2,23-03-2022,Failed
"#
Code:
$csv | Group-Object Start_Time | ForEach-Object {
$completed = 0; $failed = 0
$thisGroup = $_.Group | Group-Object Name
foreach($group in $thisGroup) {
if('Completed' -in $group.Group.Status) {
$completed++
continue
}
$failed++
}
$total = $completed + $failed
[pscustomobject]#{
Start_Date = $_.Name
Total = $total
Completed = $completed
Failed = $failed
Success = ($completed / $total).ToString('P0')
}
} | Format-Table
Result:
Start_Date Total Completed Failed Success
---------- ----- --------- ------ -------
20-03-2022 2 2 0 100%
21-03-2022 2 2 0 100%
22-03-2022 2 1 1 50%
23-03-2022 2 0 2 0%

Input validation/data filtering with Powershell

I've been working on this for a little while, basically all I'm doing is grabbing the licensing from our clients' Office 365 tenant and displaying it in a more readable manner since Office 365 Powershell doesn't output subscription names in a common name. This works perfectly when every subscription is in my CSV of subscription names, however on occasions where the SKU is not in my list (brand new or legacy offerings) the licensing table doesn't populate correctly because it can't find the friendlyname in my CSV (licensing quantities don't match the subscription name because of a blank record when it failed to find the SKU).
What I'm trying to have it do is display the skupartnumber in place of the friendlyname in the event that the subscription is not in my CSV instead of breaking the output. The first snippet below is my current working script that only works if the SKU is in my CSV, the one below it is the my best attempt at trying some input validation but I just can't get it to work. Everything displays correctly except the Subscription column which is blank (I also notice that it takes about 5x as long to run as normal), I would greatly appreciate any assistance offered; thanks!
Works as long as subscription is in my CSV:
$sku = Get-MsolAccountSku | select-object skupartnumber,ActiveUnits,suspendedUnits,ConsumedUnits | sort-object -property skupartnumber
$skudata = import-csv -Header friendlyname,skupartnumber "C:\PShell\cspcatalogalphabet.csv" | where-object {$sku.skupartnumber -eq $_.skupartnumber} | sort-object -property skupartnumber
$result = for ($n = 0; $n -lt #($skudata).Count; $n++) {
[PsCustomObject]#{
Subscription = #($skudata.friendlyname)[$n]
Active = $sku.ActiveUnits[$n]
Suspended = $sku.SuspendedUnits[$n]
Assigned = $sku.ConsumedUnits[$n]
}
}
$result | Format-Table -AutoSize
# Output:
Subscription Active Suspended Assigned
------------ ------ --------- --------
Microsoft Flow Free 10000 0 1
Power Bi (Free) 1000000 0 1
Microsoft Teams Exploratory 100 0 6
My best attempt at input validation which results in no data being read into $skulist.Subscription:
$sku = Get-MsolAccountSku | select-object skupartnumber,ActiveUnits,suspendedUnits,ConsumedUnits
$skulist = import-csv -Header friendlyname,skupartnumber "C:\PShell\cspcatalogalphabet.csv"
$skuname = for ($c = 0; $c -lt #($sku).count; $c++) {
if ($sku.skupartnumber[$c] -in $skulist.skupartnumber) {
[PsCustomObject]#{
Subscription = $skulist.friendlyname | where-object {$sku.skupartnumber[$c] -eq $skulist.skupartnumber}
}
}
else {
[PSCustomObject]#{
Subscription = #($sku.skupartnumber)[$c]
}
}
}
$table = for ($n = 0; $n -lt #($sku).Count; $n++) {
[PsCustomObject]#{
Subscription = #($skuname.Subscription)[$n]
Active = $sku.ActiveUnits[$n]
Suspended = $sku.SuspendedUnits[$n]
Assigned = $sku.ConsumedUnits[$n]
}
}
$table | format-table -AutoSize
# Output:
Subscription Active Suspended Assigned
------------ ------ --------- --------
10000 0 1
1000000 0 1
100 0 6
# An example of data I am grabbing from our clients' accounts:
$sku = Get-MsolAccountSku | select-object skupartnumber,ActiveUnits,suspendedUnits,ConsumedUnits
$sku
# Output:
SkuPartNumber ActiveUnits SuspendedUnits ConsumedUnits
------------- ----------- -------------- -------------
FLOW_FREE 10000 0 1
POWER_BI_STANDARD 1000000 0 1
TEAMS_EXPLORATORY 100 0 6
Instead of relying on the two arrays being aligned - that is, index $n in $sku must correspond to the item at index $n in $skulist for you code to work - you'll want to be able to resolve a value in $skulist based on the actual $SKU.SkuPartNumber value instead.
So how does one do that?!
Feed your $skulist into a [hashtable] instead:
$skulist = #{}
Import-Csv -Header friendlyname,skupartnumber "C:\PShell\cspcatalogalphabet.csv" |ForEach-Object {
$skulist[$_.skupartnumber] = $_.friendlyname
}
And then iterate over $skudata like this (notice there's no need for a for(;;) loop anymore, we don't need to care about array alignment!):
foreach($skuEntry in $sku){
[pscustomobject]#{
Subscription = if($skuList.ContainsKey($skuEntry.SKUPartNumber){$skuList[$skuEntry.SKUPartNumber]}else{$skuEntry.SKUPartNumber})
Active = $skuEntry.ActiveUnits
Suspended = $skuEntry.SuspendedUnits
Assigned = $skuEntry.ConsumedUnits
}
}

Powershell Get-Random with Constraints

I'm currently using the Get-Random function of Powershell to randomly pull a set number of rows from a csv. I need to create a constraint that says if one id is pulled, find the other ids that match it and pull their value.
Here is what I currently have:
$chosenOnes = Import-CSV C:\Temp\pk2.csv | sort{Get-Random} | Select -first 6
$i = 1
$count = $chosenOnes | Group-Object householdID
foreach ($row in $count)
{
if ($row.count -gt 1)
{
$students = $row.Group.Student
foreach ($student in $students)
{
$name = $student.tostring()
#...do something
$i = $i + 1
}
}
else
{
$name = $row.Group.Student
if($i -le 5)
{
#...do something
}
else
{
#...do something
}
$i = $i + 1
}
}
Example dataset
ID,name
165,Ernest Hemingway
1204,Mark Twain
1578,Stephen King
1634,Charles Dickens
1726,George Orwell
7751,John Doe
7751,Tim Doe
In this example, there are 7 rows but I'm randomly selecting 6 in my code. What needs to happen is when ID=7751 then I must return both rows where ID=7751. The IDs cannot not be statically set in the code.
Use Get-Random directly, with -Count, to extract a given number of random elements from a collection.
$allRows = Import-CSV C:\Temp\pk2.csv
$chosenHouseholdIDs = ($allRows | Get-Random -Count 6).householdID
Then filter all rows by whether their householdID column contains one of the 6 randomly selected rows' householdID values (PSv3+ syntax), using the -in array-containment operator:
$allRows | Where-Object householdID -in $chosenHouseholdIDs
Optional reading: performance considerations:
$allRows | Get-Random -Count 6 is not only conceptually simpler, but also much faster than $allRows | Sort-Object { Get-Random } | Select-Object -First 6
Using the Time-Command function to compare the performance of two approaches, using a 1000-row test file with 10 columns yields the following sample timings on my Windows 10 VM in Windows PowerShell - note that the Sort-Object { Get-Random }-based solution is more than 15(!) times slower:
Factor Secs (100-run avg.) Command TimeSpan
------ ------------------- ------- --------
1.00 0.007 $allRows | Get-Random -Count 6 00:00:00.0072520
15.65 0.113 $allRows | Sort-Object { Get-Random } | Select-Object -First 6 00:00:00.1134909
Similarly, a single pass through all rows to find matching IDs via array-containment operator -in performs much better than looping over the randomly selected IDs and searching all rows for each.
I tried sticking with your beginning and came up with this.
$Array = Import-CSV C:\test\StudtentTest.csv
$Array | Sort{Get-Random} | select -first 2 | %{
$id = $_.id
$Array | ?{$_.id -eq $id} | %{
$_
}
}
$Array will be your parsed CSV
We pipe in and sort by random select -first 2 (in this case)
Save the ID of the object into $id and then search the array for that ID and dispaly each that matches
If same ID does match you end up with something like
ID name
-- ----
7751 John Doe
7751 Tim Doe
1634 Charles Dickens

Get random items from hashtable but the total of values has to be equal to a set number

I'm trying to build a simple "task distributor" for the house tasks between me and my wife. Although the concept will be really useful at work too so I need to learn it properly.
My hashtable:
$Taches = #{
"Balayeuse plancher" = 20
"Moppe plancher" = 20
"Douche" = 15
"Litières" = 5
"Poele" = 5
"Comptoir" = 5
"Lave-Vaisselle" = 10
"Toilette" = 5
"Lavabos" = 10
"Couvertures lit" = 5
"Poubelles" = 5
}
The total value for all the items is 105 (minutes).
So roughly 50mins each of we split it in two.
My goal:
I want to select random items from that hashtable and build two different hashtables - one for me and my wife, each having a total value of 50 (So it's fair). For example 20+20+10 or 5+5+5+15+20, etc. The hard part is that ALL tasks have to be accounted for between the two hashtables and they can only be present ONCE in each of them (no use in cleaning the same thing twice!).
What would be the best option?
For now I successfully achieved a random hashtable of a total value of 50 like this:
do {
$Me = $null
$sum = $null
$Me = #{}
$Me = $Taches.GetEnumerator() | Get-Random -Count 5
$Me | ForEach-Object { $Sum += $_.value }
} until ($sum -eq 50)
Result example :
Name Value
---- -----
Poubelles 5
Balayeuse plancher 20
Douche 15
Poele 5
Toilette 5
It works but boy does it feel like it's a roundabout and crooked way of doing it. I'm sure there is a better approach? Plus I'm lacking important things. ALL the tasks have to be accounted for and not be present twice. This is quite complicated although it looked simple at first!
You can not maximise randomness and fairness at the same time so one has to give. I think you should not risk being unfair to your wife and so fairness must prevail!
Fairness at the expense of randomness
This approach sorts the items in descending time order and then randomly assigns them items to each person unless that assignment would be unfair.
The fairness calculation here is that the maximum time difference should be at most the duration of the quickest task.
$DescendingOrder = $Taches.Keys | Sort-Object -Descending { $Taches[$_] }
$Measures = $Taches.Values | Measure-Object -Sum -Minimum
$UnfairLimit = ($Measures.Sum + $Measures.Minimum) / 2
$Person1 = #{}
$Person2 = #{}
$Total1 = 0
$Total2 = 0
foreach ($Item in $DescendingOrder) {
$Time = $Taches[$Item]
$Choice = Get-Random 2
if (($Choice -eq 0) -and (($Total1 + $Time) -gt $UnfairLimit)) {
$Choice = 1
}
if (($Choice -eq 1) -and (($Total2 + $Time) -gt $UnfairLimit)) {
$Choice = 0
}
if ($Choice -eq 0) {
$Person1[$Item] = $Time
$Total1 += $Time
} else {
$Person2[$Item] = $Time
$Total2 += $Time
}
}
An example run:
PS> $Person1 | ConvertTo-Json
{
"Comptoir": 5,
"Lavabos": 10,
"Litières": 5,
"Couvertures lit": 5,
"Douche": 15,
"Lave-Vaisselle": 10
}
and the other person:
PS> $Person2 | ConvertTo-Json
{
"Moppe plancher": 20,
"Toilette": 5,
"Balayeuse plancher": 20,
"Poubelles": 5,
"Poele": 5
}
Randomness at the expense of fairness
This approach is to randomize the list, go through each item and then assign it to the person who has the least time allocated to them so far.
Earlier decisions might mean that later decisions end up being unfair.
$RandomOrder = $Taches.Keys | Sort-Object { Get-Random }
$Person1 = #{}
$Person2 = #{}
$Total1 = 0
$Total2 = 0
foreach ($Item in $RandomOrder) {
$Time = $Taches[$Item]
if ($Total1 -lt $Total2) {
$Person1[$Item] = $Time
$Total1 += $Time
} else {
$Person2[$Item] = $Time
$Total2 += $Time
}
}
An example run:
PS> $Person1 | ConvertTo-Json
{
"Poele": 5,
"Douche": 15,
"Couvertures lit": 5,
"Lave-Vaisselle": 10,
"Balayeuse plancher": 20,
"Toilette": 5
}
and the other person:
PS> $Person2 | ConvertTo-Json
{
"Lavabos": 10,
"Comptoir": 5,
"Poubelles": 5,
"Litières": 5,
"Moppe plancher": 20
}
You should probably write the algorithm to always have you take the extra task in a rounding error (Happy Wife, Happy Life).
This is probably over-engineered, but I was intrigued by the question, and learned some French in the process.
$Taches = #{
"Balayeuse plancher" = 20
"Moppe plancher" = 20
"Douche" = 15
"Litières" = 5
"Poele" = 5
"Comptoir" = 5
"Lave-Vaisselle" = 10
"Toilette" = 5
"Lavabos" = 10
"Couvertures lit" = 5
"Poubelles" = 5
}
$target = 0
$epsilon = 5
# copy if you don't want to destroy original list (not needed probably)
# put all entries in first list.
# randomly move entry to p2 if count over target +/- epsilon
# randomly move entry from p2 if count under target +/- epsilon
# (unless you know you can always get exactly target and not loop forever trying)
$p1 = #{} # person 1
$p2 = #{} # person 2
$p1Total = 0 # optimizaton to not have to walk entire list and recalculate constantly
$p2Total = 0 # might as well track this too...
$Taches.Keys | % {
$p1.Add($_, $Taches[$_])
$p1Total += $Taches[$_]
$target += $Taches[$_]
}
$target = $target / 2
$done = $false
while (-not $done)
{
if ($p1Total -gt ($target+$epsilon))
{
$item = $p1.Keys | Get-Random
$value = $p1[$item]
$p1.Remove($item)
$p2.Add($item, $value)
$p1Total -= $value
$p2Total += $value
continue
}
elseif ($p1Total -lt ($target-$epsilon))
{
$item = $p2.Keys | Get-Random
$value = $p2[$item]
$p2.Remove($item)
$p1.Add($item, $value)
$p1Total += $value
$p2Total -= $value
continue
}
$done = $true
}
"Final result"
"p1"
$p1Total
$p1
"`np2"
$p2Total
$p2
Yet another approach:
$MinSum = ($Taches.Values | Measure-Object -Minimum ).Minimum
$HalfSum = ($Taches.Values | Measure-Object -Sum ).Sum / 2
do {
$sum = 0
$All = $Taches.GetEnumerator() |
Get-Random -Count $Taches.Keys.Count
$Me = $All | ForEach-Object {
if ( $Sum -lt $HalfSum - $MinSum ) {
$Sum += $_.value
#{ $_.Key = $_.Value }
}
}
Write-Host "$sum " -NoNewline # debugging output
} until ($sum -eq 50 )
$Em = $Taches.Keys | ForEach-Object {
if ( $_ -notin $Me.Keys ) {
#{ $_ = $Taches.$_ }
}
}
# show "fairness" (task count vs. task cost)
$Me.Values | Measure-Object -Sum | Select-Object -Property Count, Sum
$Em.Values | Measure-Object -Sum | Select-Object -Property Count, Sum
Sample output(s):
PS D:\PShell> D:\PShell\SO\54610011.ps1
50
Count Sum
----- ---
4 50
7 55
PS D:\PShell> D:\PShell\SO\54610011.ps1
65 65 50
Count Sum
----- ---
6 50
5 55
Great answers guys, learned a lot. Here is what I ended up doing thanks to "Fischfreund" on Reddit (https://www.reddit.com/r/PowerShell/comments/aovs8s/get_random_items_from_hashtable_but_the_total_of/eg3ytds).
His approach is amazingly simple yet I didn't think of it at all.
First hashtable : Get a random count of 5 until the sum is 50. Then create a second hashtable where the items are not in the first hashtable! I assign that first hahstable containing 5 items to my wife so I'm the one who always has an extra task (like suggested by Kory ;)). Phew i'm safe.
$Taches = #{
"Balayeuse plancher" = 20
"Moppe plancher" = 20
"Douche" = 15
"Litières" = 5
"Poele" = 5
"Comptoir" = 5
"Lave-Vaisselle" = 10
"Toilette" = 5
"Lavabos" = 10
"Couvertures lit" = 5
"Poubelles" = 5
}
do {
$Selection1 = $Taches.GetEnumerator() | Get-Random -Count 5
} until (($Selection1.Value | measure -Sum ).Sum -eq 50)
$Selection2 = $Taches.GetEnumerator() | Where-Object {$_ -notin $Selection1}
$Selection1 | select-object #{Name="Personne";expression={"Wife"} },Name,Value
""
$Selection2 | select-object #{Name="Personne";expression={"Me"} },Name,Value

How do I loop through get-counter results to get to make a decision?

I'm trying to get the following counter over the span of 5 minutes. If it returns 100, 5 times in a row, I'd like to receive an email.
get-counter -Counter "\Processor(_Total)\% Processor Time" -SampleInterval 60 -MaxSamples 5
I've tried to assign that block of code to a variable, so for example:
$value = get-counter -Counter "\Processor(_Total)\% Processor Time" -SampleInterval 60 -MaxSamples 5
if($value eq 100)
{
$counter = $counter + 1
}
if($counter > 4)
{
"send an email"
}
The problem is that I can't get the actual counter value to get assigned to the $value variable. Furthermore I'd like the counter to increment every time the code fires (before it hits the maxsamples value).
Any help would be much appreciated.
Get-Counter returns an array of Microsoft.PowerShell.Commands.GetCounter.PerformanceCounterSampleSet type so you have to work with it as follows:
a little bit more generic solution:
$sampleInterval = 60 # in seconds
$samples = 50 # samples taken
$inRowSamples = 5 # 5 samples in a row
$shouldntExceed = 0.95 # 95%
$values = get-counter -Counter "\Processor(_Total)\% Processor Time" -SampleInterval $sampleInterval -MaxSamples $samples
$count = 0
foreach ($value in $values)
{
if ($value.CounterSamples[0].CookedValue -gt $shouldntExceed)
{
$count++;
}
else
{
$count = 0;
}
if ($count -ge $inRowSamples)
{
# send email
break;
}
}
I think you want something like this:
$value = get-counter -Counter "\Processor(_Total)\% Processor Time" -SampleInterval 1 -MaxSamples 5
$Total = $Value | % { $_.CounterSamples[0].CookedValue } | Measure -Sum;
if ($Total.Sum -ge 490) {
# Total CPU usage was 490+
}
Or, if you want to do it in context of percentages, do this:
$value = get-counter -Counter "\Processor(_Total)\% Processor Time" -SampleInterval 1 -MaxSamples 5
$Total = $Value | % { $_.CounterSamples[0].CookedValue } | Measure -Average;
if ($Total.Average -ge 95) {
# Total CPU usage was 95%+
}
Try this:
$maxCount = 5;
$maxValue = 100;
$count = 0;
get-counter -Counter "\Processor(_Total)\% Processor Time" -SampleInterval 1 -MaxSamples 5 | %{
if($_.CounterSamples[0].CookedValue -ge $maxValue){
$count++;
}
#handling the n times in a row condition
else{
$count=0
}
if($count -eq $maxCount){
write-host "exceeded"
#send email
break
}
}
Since we are using pipeline, the above will send email as soon as you have 5 continuous values greater than or equal to 100.