Putting a specific Powershell Get-Counter Statistic into a variable - powershell

I'd like to pull a specific statistics from a _Total (the CounterSamples) into a variable, in order to further parse it.
This is what I've tried.
Trying to index with [1] gives error further below.
Looping didn't seem to get me very far either.
cls
$statToCollect = '\Process(_Total)\IO Data Operations/sec'
Get-Counter $statToCollect
Write-Host "================"
$saveStats = Get-Counter $statToCollect
$ctrSamples = $saveStats[1].CounterSamples
Write-Host "$ctrSamples"
Write-Host "$($saveStats)"
Write-Host "================"
$diskStats = Get-Counter $statToCollect
$diskStatsLoopCounter = 1
foreach ($diskStat in $diskStats)
{
if ($diskStatsLoopCounter -eq 1)
{
write-host "$($diskStat.CounterSamples)"
}
$diskStatsLoopCounter = $diskStatsLoopCounter + 1
}
Results:
Timestamp CounterSamples
--------- --------------
12/29/2014 9:27:49 AM \\mpcname\process(_total)\io data operations/sec :
970.6265098029
================
Unable to index into an object of type Microsoft.PowerShell.Commands.GetCounter.PerformanceCo
unterSampleSet.
At C:\Users\neal.walters\Documents\DiskUtil.ps1:6 char:26
+ $ctrSamples = $saveStats[ <<<< 1].CounterSamples
+ CategoryInfo : InvalidOperation: (1:Int32) [], RuntimeException
+ FullyQualifiedErrorId : CannotIndex
Microsoft.PowerShell.Commands.GetCounter.PerformanceCounterSampleSet
================
Microsoft.PowerShell.Commands.GetCounter.PerformanceCounterSample

In your particular case $saveStats is only the one element.
PS C:\Users\mcameron> $saveStats.Count
1
Which is why this command would have returned null output as there is not a second.
PS C:\Users\mcameron> $saveStats[1]
Since there was only the one element either of the following options would have worked for this case.
PS C:\Users\mcameron> $saveStats[0]
PS C:\Users\mcameron> $saveStats
Also as for the line Write-Host "$($saveStats)" since $saveStats is an object and not a string it would not expand the way you expect. Pretty sure this occure because the ToString() is not overloaded to handle this so just the object type is outputted. Simply having $saveStats on its own would allow powershell to format it properly using its own built-in cmdlets.
PS C:\Users\mcameron> $saveStats
Timestamp CounterSamples
--------- --------------
12/29/2014 10:56:53 AM \\c3935\process(_total)\io data operations/sec :
27.7291444862573
Similar issue with the line write-host "$($diskStat.CounterSamples)" which has the same response as above.
As the other commenters and posters have said you most likely want one of the properties like CookedValue which can be easily converted to a string.
write-host "$($diskStat.CounterSamples.CookedValue)"

Using PowerShell version 4 on Windows 8.1:
Get-Counter returns a PerformanceCounterSampleSet and you can access the CounterSamples property to get an array of PerformanceCounterSample objects.
The particular property that you're interested in is CookedValue:
$statToCollect = '\Process(_Total)\IO Data Operations/sec'
$total = (Get-Counter $statToCollect).CounterSamples.CookedValue
This gets you the result as a double:
PS> $total
28.9450419770711
PS> $total.GetType()
IsPublic IsSerial Name BaseType
-------- -------- ---- --------
True True Double System.ValueType

Related

Cannot convert 'System.Object[]' to the type 'System.String' required by parameter 'Name'

I have a hashtable which contains keys and values like below,
$hostVM = [ordered]#{3 = {hostpc-1, hostpc-3, hostpc-5} ; 2 = {hostpc-2,hostpc-4}}
Here is my code to read the values from $jumpHash hashtable,
$esxarray = #(10.91.91.XX7, 10.91.91.XX8)
$vmf = 0
$hostVM.GetEnumerator() | foreach {
Import-VApp -Source $hostpath -Name $_.Value -DiskStorageFormat Thin -VMHost $esxarray[$vmf] -Datastore $storage
$vmf = $vmf+1
}
Using GetEnumerator() method I have enumerated all the values in $hostvm, but I'm not able to invoke the values from the hashtable to be used in -Name parameter and also receiving the below error.
Import-VApp : Cannot convert 'System.Object[]' to the type 'System.String' required by parameter 'Name'. Specified method is not
supported.
At C:\ONTAP_Hashtable.ps1:65 char:45
+ Import-VApp -Source $hostvmpath -Name $_.Value -DiskStorageForm ...
Please suggest a way to invoke the values. Thanks in advance
I do not have a VMWare environment to test the below.
However, the base part of this is all syntactically wrong. If you open this in the ISE/VSCode, or if you scan it with PSCriptAnalyzer, it will show you this.
• Best Practices for aliases
Best Practice for Using Aliases in PowerShell Scripts
https://devblogs.microsoft.com/scripting/best-practice-for-using-aliases-in-powershell-scripts
https://devblogs.microsoft.com/scripting/using-powershell-aliases-best-practices
Why worry about aliases in the first place?
What is the big deal about using aliases anyway? If they make the code
easier to type, what is the harm in using them in scripts? There are
two things at work when it comes to a script. The first is that no
alias is guaranteed to exist—even aliases that are created by Windows
PowerShell.
Invoke-ScriptAnalyzer -Path .\CannotConvert.ps1
RuleName Severity ScriptName Line Message
-------- -------- ---------- ---- -------
MissingArgument ParseError CannotConv 1 Missing argument in parameter list.
ert.ps1
MissingArgument ParseError CannotConv 1 Missing argument in parameter list.
ert.ps1
MissingPropertyName ParseError CannotConv 3 Missing property name after reference operator.
ert.ps1
PSAvoidUsingCmdletAliases Warning CannotConv 5 'foreach' is an alias of 'ForEach-Object'. Alias can
ert.ps1 introduce possible problems and make scripts hard to
maintain. Please consider changing alias to its full
content.
PSUseCompatibleSyntax Warning CannotConv 3 The dynamic member invocation syntax '10.91.91.XX7,
ert.ps1 10.91.91.XX8' is not available by default in PowerShell
versions 3
The ISE, VSCode, have addons/features, to auto-expand aliases. So, you can use them in your dev effort, but on check-ins and production releases, expand them. Interactive command stuff and throw away code is the target use case for them.
You cannot use a hash table, or ForEach this way. Individually or together.
A real good article on the hash table use cases.
Everything you wanted to know about hashtables
Check your results at each step and make sure you are getting what you'd expect before moving to the next. Using poor-mans' debug, via PowerShell variable squeezing to assign to a variable and outputting to the screen simultaneously
Yours will error out immediatly:
($hostVM = [ordered]#{3 = {hostpc-1, hostpc-3, hostpc-5} ; 2 = {hostpc-2,hostpc-4}})
# Results
<#
At line:1 char:36
+ ($hostVM = [ordered]#{3 = {hostpc-1, hostpc-3, hostpc-5} ; 2 = {hostp ...
+ ~
Missing argument in parameter list.
At line:1 char:73
+ ... ordered]#{3 = {hostpc-1, hostpc-3, hostpc-5} ; 2 = {hostpc-2,hostpc-4 ...
+ ~
Missing argument in parameter list.
+ CategoryInfo : ParserError: (:) [], ParentContainsErrorRecordException
+ FullyQualifiedErrorId : MissingArgument
#>
Vs, doing this:
($hostVM = [ordered]#{
3 = 'hostpc-1, hostpc-3, hostpc-5'
2 = 'hostpc-2,hostpc-4'
})
# Results
<#
Name Value
---- -----
3 hostpc-1, hostpc-3, hostpc-5
2 hostpc-2,hostpc-4
#>
You cannot use a dotted string in an array the way you are trying. Another article.
Everything you wanted to know about arrays
Same issue as the aforementioned:
($esxarray = #(10.91.91.XX7, 10.91.91.XX8))
# Results
<#
At line:1 char:22
+ ($esxarray = #(10.91.91.XX7, 10.91.91.XX8))
+ ~
Missing property name after reference operator.
+ CategoryInfo : ParserError: (:) [], ParentContainsErrorRecordException
+ FullyQualifiedErrorId : MissingPropertyName
#>
Vs doing this:
($esxarray = #('10.91.91.XX7', '10.91.91.XX8'))
# Results
<#
10.91.91.XX7
10.91.91.XX8
#>
$vmf = 0
$hostVM.GetEnumerator() |
foreach {
Import-VApp -Source $hostpath -Name $_.Value -DiskStorageFormat Thin -VMHost $esxarray[$vmf] -Datastore $storage
$vmf = $vmf+1
}
Update
As per your comment...
...
If you mean...
$hostVM.SomeValue
... then no, because, it's a string, not an array. You'd have to do this.
((($hostVM.Values)[0]) -split ',')[0]
# Results
<#
hostpc-1
#>
Or in your code, you'd have to refactor to this.
($hostVM = [ordered]#{
3 = #('hostpc-1', 'hostpc-3', 'hostpc-5')
2 = #('hostpc-2','hostpc-4')
})
# Results
<#
Name Value
---- -----
3 {hostpc-1, hostpc-3, hostpc-5}
2 {hostpc-2, hostpc-4}
#>
$hostVM.Keys
# Results
<#
3
2
#>
$hostVM.Values
# Results
<#
hostpc-1
hostpc-3
hostpc-5
hostpc-2
hostpc-4
#>
$hostVM[0].GetValue(0)
# Results
<#
hostpc-1
#>
$hostVM[0].GetValue(2)
# Results
<#
hostpc-5
#>

getting property value in powershell when running convertto-html

I'm still new to powershell, and recently i came to know about get-member properties. However when i pipe the command to convertto-html the value disappear.
i have a data which is called link.csv and below is the content of the file
Target Node Address Status
------ ---- ------- ------
server01 0:2:3 20230002AC0153AF Up
server01 0:2:4 20240002AC0153AF Up
server01 1:2:3 21230002AC0153AF Up
server01 1:2:4 21240002AC0153AF Up
I'm able to get the property value as per below.
PS C:\Report\script\Temp> $a= Import-Csv .\link.csv | select Target,Node,Address,Status
PS C:\Report\script\Temp> $a.Node[1]
0:2:4
PS C:\Report\script\Temp> $a.Status[1]
Up
PS C:\Report\script\Temp> $a.Target[1]
server01
However when i output it to Convertto-Html
PS C:\Report\script\Temp> $a= Import-Csv .\link.csv | select Target,Node,Address,Status | ConvertTo-Html -Fragment -PreContent "<font color=`"Black`"><H4>Remote Copy Group - $b</H4></font> "
PS C:\Report\script\Temp> $a.Target[1]
Cannot index into a null array.
At line:1 char:1
+ $a.Target[1]
+ ~~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: (:) [], RuntimeException
+ FullyQualifiedErrorId : NullArray
How can i get the property value while running convertto-html command.
Hope you can help me with the issue.
Thank You in advance.
If you want to capture intermediate results in the pipeline, use the -OutVariable / -ov common parameter with a variable name in order to store these results in that variable:
$html = Import-Csv .\link.csv |
Select-Object Target, Node, Address, Status -OutVariable a |
ConvertTo-Html -Fragment -PreContent "<font color=`"Black`"><H4>Remote Copy Group - $b</H4></font> "
$a.Target[1]
Note how the pipeline overall returns HTML (as an array of lines), whereas the intermediate custom objects filtered from the CSV input are stored in variable $a via -OutVariable, for later use - note how you must pass just the variable name to -OutVariable, without the $ sigil, i.e., just a rather than $a.

PowerShell Convert output to hashtable array (data type conversion)

This a data type conversion issue.
I am trying to get the names of computers out of SCCM and feed it into SCCM Report. The report commandlet receives hashtable where the variable must be named "Computer Name". The value is the computer name.
ex. $computer = #{"Computer Name" = "MyComp01"}
Outputing Commandlet example (get computer names from SCCM) This works.
$unknownoutputtype = Get-CMDevice -CollectionName "My Collection Computers" | select #{N="Computer Name";E={$_.Name}}
--Output--
Computer Name
-------------
MyComp01
MyComp02
MyComp03
MyComp04
MyComp05
MyComp06
PS> $unknownoutputtype.gettype()
IsPublic IsSerial Name BaseType
-------- -------- ---- --------
True True Object[] System.Array
Recieving Commandlet example (process the query) This works:
$computer = #{"Computer Name" = "MyComp01"}
invoke-cmreport -ReportPath "Software - Companies and Products/Software registered in Add Remove Programs on a specific computer" -reportparameter $Computer -OutputFormat excel
I need the "Get-CMDevice" line to output as the type below.
PS> $Computer.gettype()
IsPublic IsSerial Name BaseType
-------- -------- ---- --------
True True Hashtable System.Object
My failed attempt
PS> Get-CMDevice -CollectionName "My Collection Computers" |select #{N="Computer Name";E={$_.Name}} | Foreach-object {invoke-cmreport -ReportPath "Software - Companies and Products/Software registered in Add Remove Programs on a specific computer" -SiteCode "MySite" -reportparameter $_ -OutputFormat Excel}
Error output:
Invoke-CMReport : Cannot bind parameter 'ReportParameter'. Cannot convert value "#{Computer Name=MyComp01}" to type "System.Collections.Hashtable". Error: "Cannot convert the "#{Computer Name=MyComp01}" value of type
"Selected.Microsoft.ConfigurationManagement.ManagementProvider.WqlQueryEngine.WqlResultObject" to type "System.Collections.Hashtable"."
At line:1 char:280
+ ... s on a specific computer" -SiteCode "{removed}" -reportparameter $_ -Output ...
+ ~~
+ CategoryInfo : InvalidArgument: (:) [Invoke-CMReport], ParameterBindingException
+ FullyQualifiedErrorId : CannotConvertArgumentNoMessage,Microsoft.ConfigurationManagement.Cmdlets.Reporting.Commands.InvokeReportCommand
Select-Object will always output a PSCustomObject, not a hashtable.
Just construct the hashtable inside the ForEach-Object body before calling Invoke-CMReport:
Get-CMDevice -CollectionName "My Collection Computers" |Select-Object -ExpandProperty Name | Foreach-object {
$ReportParameter = #{ 'Computer Name' = $_ }
invoke-cmreport -ReportPath "Software - Companies and Products/Software registered in Add Remove Programs on a specific computer" -SiteCode "MySite" -reportparameter $ReportParameter -OutputFormat Excel
}

Store output value in variable and do arithmetic operation in powershell

I am doing powershell script for getting memory value of some process in powershell also i need to divide the value by 1024 in order to convert KB/MB.
For example
PS >>$memory = Get-Process nginx | Select-Object WS | format-wide -Column 1
PS >>$memory
62541824
This value is in bytes. I need to convert this value into KB/MB. so I performed div operation after that.
PS >> $memory = $memory / 1024
Method invocation failed because [System.Object[]] doesn't contain a method named 'op_Division'.
At C:\script\tomcat-mem.ps1:3 char:13
+ $mem= $mem / <<<< 1024
+ CategoryInfo : InvalidOperation: (op_Division:String) [], RuntimeException
+ FullyQualifiedErrorId : MethodNotFound
Any one help me to resolve this error.
Because you're piping to format-wide, you're returning an object rather than a type that can be trivially cast to an int. The following would be more suitable:
$memory = Get-Process nginx | Select-Object -ExpandProperty WS
$memory / 1024
Proof:
$m = get-process explorer | select -expandproperty ws
$m.gettype()
IsPublic IsSerial Name BaseType
-------- -------- ---- --------
True True Int32 System.ValueType
$m = get-process explorer | select ws | format-wide -Column 1
$m.gettype()
IsPublic IsSerial Name BaseType
-------- -------- ---- --------
True True Object[] System.Array
You can see that the first version has been implicitly converted to an Int32, therefore it's possible to run a division operation on it.

PowerShell's Write-Debug won't output arrays, but Write-Output does. Is this on purpose?

Shown below, an array works fine as input for Write-Output but not for Write-Debug (I expected them to be more similar than that).
PS C:\> [string[]]$test = #("test1", "test2", "test3")
PS C:\> Write-Output $test
test1
test2
test3
PS C:\> $DebugPreference = "Inquire"
PS C:\> Write-Debug $test
Write-Debug : Cannot convert 'System.String[]' to the type 'System.String' required by parameter 'Message'. Specified method is not supported.
At line:1 char:12
+ Write-Debug <<<< $test
+ CategoryInfo : InvalidArgument: (:) [Write-Debug], ParameterBindingException
+ FullyQualifiedErrorId : CannotConvertArgument,Microsoft.PowerShell.Commands.WriteDebugCommand
PS C:\>
I'm thinking this is just an unfortunate design, but hoping for a sensible explanation. Am I using Write-Debug correctly? If so, anyone have a favorite simple workaround?
I kept having the same problem, and none of the solutions I found above or anywhere else would work in the general case.
For example, the first answer above works only because the array is an array of strings. If it's an array of anything else, that solution breaks, and Write-Debug will output the object type, and not its value as one would expect.
Finally I found a general solution: The key point is to first convert the input object to a string using the Out-String command. Once everything is a string, the above solution works. Using "Out-String -stream" improves the output alignment.
Example:
PS C:\> gwmi win32_bios
SMBIOSBIOSVersion : 786F3 v01.34
Manufacturer : Hewlett-Packard
Name : Default System BIOS
SerialNumber : CZC8196Q8S
Version : HPQOEM - 20120709
PS C:\> gwmi win32_bios | ft -auto
SMBIOSBIOSVersion Manufacturer Name SerialNumber Version
----------------- ------------ ---- ------------ -------
786F3 v01.34 Hewlett-Packard Default System BIOS CZC8196Q8S HPQOEM - ...
PS C:\> $DebugPreference = "Continue"
PS C:\> gwmi win32_bios | ft -auto | Write-Debug
DEBUG: Microsoft.PowerShell.Commands.Internal.Format.FormatStartData
DEBUG: Microsoft.PowerShell.Commands.Internal.Format.GroupStartData
DEBUG: Microsoft.PowerShell.Commands.Internal.Format.FormatEntryData
DEBUG: Microsoft.PowerShell.Commands.Internal.Format.GroupEndData
PS C:\> gwmi win32_bios | ft -auto | Out-String | Write-Debug
DEBUG: SMBIOSBIOSVersion Manufacturer Name SerialNumber Version
----------------- ------------ ---- ------------ -------
786F3 v01.34 Hewlett-Packard Default System BIOS CZC8196Q8S HPQOEM - ...
PS C:\> gwmi win32_bios | ft | Out-String -stream | Write-Debug
DEBUG:
DEBUG: SMBIOSBIOSVersi Manufacturer Name SerialNumber Version
DEBUG: on
DEBUG: --------------- ------------ ---- ------------ -------
DEBUG: 786F3 v01.34 Hewlett-Packard Default Syst... CZC8196Q8S HPQOEM - 201...
DEBUG:
DEBUG:PS C:\>
If you want write-debug to handle each one separately:
[string[]]$test = #("test1", "test2", "test3")
Write-Output $test
test1
test2
test3
$DebugPreference = "Inquire"
$test | Write-Debug
DEBUG: test1
DEBUG: test2
DEBUG: test3
Write-Debug is designed for outputting simple messages when debug preferences are set in a particular way. It takes only a string, not just anything like Write-Host does (and magically formats). You will have to format your output yourself into a single string.
You could combine Write-Host and Write-Debug if you have extra info to output before prompting the user:
if ($DebugPreference -ne 'SilentlyContinue') {
Write-Host 'such-and-such array:' $array
}
Write-Debug 'such-and-such array dumped'
Write-Host is used because it will always write to the console host, rather than to the script's output, as Write-Output does. If you where redirecting standard output of the script to a file, Write-Output would end up in the file, while Write-Host would still be seen in the console.
You could also try doing something like this if your array is of simply enough types that an automatic call to ToString() on them (if they're not strings already) gets you what you want:
$array = 'Alice','Bob','Charlie'
Write-Debug ([String]::Join("`n", $array))
Write-Debug:
Write-Debug [-Message] <string> [<CommonParameters>]
It expects a string. It is unable to convert a string array to a string as the error says. The reason why it expects a string is because it writes debug messages to the console from a script or command.
Note that Write-Output and Write-Host take an object:
Write-Output [-InputObject] <PSObject[]> [<CommonParameters>]
and
Write-Host [[-Object] <Object>] ...