CPU_MEM Output

Hello Scripters,
I created a post about a month ago, and only got one reply from Rob Simmers (if you’re reading this Rob…Thank you!). I just got approval to test the new script. I like the new format, but the problem I’m having is that it only outputs to the Command Prompt and not the output file I have in the script. Here’s a copy of the script:

$servers = Get-Content .\server_list.txt
$fileName = "..\..\scriptOutputFiles\get_CPU_MEM_output_$(get-date -f yyyy-MM-dd).txt"

$results = foreach($server in $servers)
{
    if (!(Test-Connection $server -quiet))
    {
        New-Object -TypeName PSObject -Property @{
            "ComputerName"           = $server
            "Proc#"                  = $null
            "Percent_Used"           = $null
            "Percent_Idle"           = $null
            "Percent_Commited_Bytes" = $null
            "Available_MBytes"       = $null
            "Committed_MBytes"       = $null
            "Status"                 = "Failed: Unable to ping."
        }
    }
    else
    {
        $perfProc = Get-WmiObject -ComputerName $server -Class Win32_PerfFormattedData_PerfOS_Processor | 
        Select -Property @{Name="Proc#"; Expression = {($_.Name)}},
                         @{Name="Percent_Used"; Expression = {($_.PercentProcessorTime)}},
                         @{Name="Percent_Idle"; Expression = {($_.PercentIdleTime)}}

        $perfOS = Get-WmiObject -ComputerName $server -Class Win32_PerfFormattedData_PerfOS_Memory | 
        Select -Property @{Name="Percent_Commited_Bytes"; Expression = {($_.PercentCommittedBytesInUse)}},
                         @{Name="Available_MBytes"; Expression = {($_.AvailableMBytes)}},
                         @{Name="Committed_MBytes"; Expression = {[Math]::Round($_.CommittedBytes/1mb, 1)}}
        
        
        New-Object -TypeName PSObject -Property @{
            "ComputerName"           = $server
            "Proc#"                  = $perfProc."Proc#"
            "Percent_Used"           = $perfProc."Percent_Used"
            "Percent_Idle"           = $perfProc."Percent_Idle"
            "Percent_Commited_Bytes" = $perfOS."Percent_Commited_Bytes"
            "Available_MBytes"       = $perfOS."Available_MBytes"
            "Committed_MBytes"       = $perfOS."Committed_MBytes"
            "Status"                 = "Success"
        }
    }
}

$results

Can anyone help me with this? I just need the results to output to a txt file and not just show up on the command prompt screen. Thank you.

Link to my previous post:

That’s because you’re just outputting $results. That output goes into the pipeline.

$results | Out-File $filename

Is perhaps what you wanted.

I’ll point out, however, that a true Toolmaker would build this as a function that just output objects to the pipeline, which is exactly what your present script is doing (meaning, just wrap it in a function and you’re done). You’re only seeing it “on the command line” because that’s where pipeline objects go when you don’t send them anywhere else. Once in a function, you get a lot more flexibility.

Get-MyWhatever | Out-File something.txt
Get-MyWhatever | ConvertTo-HTML | Out-File something.html
Get-MyWhatever | Format-Table | Out-File something.txt

And so on. See Learn PowerShell Toolmaking in a Month of Lunches if you’re interested. I’ll point out, also, that what you’re doing - accumulating the output in a variable - is a really poor practice. See https://devopscollective.gitbooks.io/the-big-book-of-powershell-gotchas/content/manuscript/accumulating-output-in-a-function.html. Personally, I wouldn’t do that. I’d just remove your $results altogether.

$fileName = "..\..\scriptOutputFiles\get_CPU_MEM_output_$(get-date -f yyyy-MM-dd).txt"

function Get-MySysInfo {
[CmdletBinding()]
param (
 [Parameter(ValueFromPipeline=$true)][string[]]$ComputerName
)
foreach($server in $ComputerName)
{
    if (!(Test-Connection $server -quiet))
    {
        New-Object -TypeName PSObject -Property @{
            "ComputerName"           = $server
            "Proc#"                  = $null
            "Percent_Used"           = $null
            "Percent_Idle"           = $null
            "Percent_Commited_Bytes" = $null
            "Available_MBytes"       = $null
            "Committed_MBytes"       = $null
            "Status"                 = "Failed: Unable to ping."
        }
    }
    else
    {
        $perfProc = Get-WmiObject -ComputerName $server -Class Win32_PerfFormattedData_PerfOS_Processor | 
        Select -Property @{Name="Proc#"; Expression = {($_.Name)}},
                         @{Name="Percent_Used"; Expression = {($_.PercentProcessorTime)}},
                         @{Name="Percent_Idle"; Expression = {($_.PercentIdleTime)}}

        $perfOS = Get-WmiObject -ComputerName $server -Class Win32_PerfFormattedData_PerfOS_Memory | 
        Select -Property @{Name="Percent_Commited_Bytes"; Expression = {($_.PercentCommittedBytesInUse)}},
                         @{Name="Available_MBytes"; Expression = {($_.AvailableMBytes)}},
                         @{Name="Committed_MBytes"; Expression = {[Math]::Round($_.CommittedBytes/1mb, 1)}}
        
        
        New-Object -TypeName PSObject -Property @{
            "ComputerName"           = $server
            "Proc#"                  = $perfProc."Proc#"
            "Percent_Used"           = $perfProc."Percent_Used"
            "Percent_Idle"           = $perfProc."Percent_Idle"
            "Percent_Commited_Bytes" = $perfOS."Percent_Commited_Bytes"
            "Available_MBytes"       = $perfOS."Available_MBytes"
            "Committed_MBytes"       = $perfOS."Committed_MBytes"
            "Status"                 = "Success"
        }
    }
}
} #function

Get-Content .\server_list.txt | Get-MySysInfo | Out-File $filename

You get a much more native-style experience that way. BTW, I adore how you’re outputting the same object structure for a failed connection - very good practice. Just keep in mind that ping isn’t actually a test of whether WMI will work - your WMI calls could still fail, and you’re not handling that. They will fail against Win2012R2 and later, in fact, since WMI is disabled by default.

Thank you Don for your help and all the great information!

Your first suggestion fixed the primary problem I was having, and I’m glad it was a simple fix. Also, for the positive comments you made regarding the object structure of my script, I can’t take credit for that. Rob Simmers, from this site, wrote it that way. Here is the original script that my friend and I put together:

$servers = Get-Content .\server_list.txt
$fileName = "..\..\scriptOutputFiles\get_CPU_MEM_output_$(get-date -f yyyy-MM-dd).txt"

foreach($server in $servers)
{
    if (!(Test-Connection $server -quiet))
    {
        Write-Output "******************* Problem connecting to $server *****************************" | Out-File -FilePath $fileName -Width 75 -Append
    }
    else
    {
        Write-Output "Server details for: $server" | Out-File -FilePath $fileName -Width 75 -Append

        Write-Output "PROCINFO" | Out-File -FilePath $fileName -Width 75 -Append
        Get-WmiObject -ComputerName $server -Class Win32_PerfFormattedData_PerfOS_Processor | Format-Table @{Name="Proc#"; Expression = {($_.Name)}},
                        @{Name="Percent_Used"; Expression = {($_.PercentProcessorTime)}},
                        @{Name="Percent_Idle"; Expression = {($_.PercentIdleTime)}} | Out-File -FilePath $fileName -width 75 -append

        Write-Output "MEMINFO" | Out-File -FilePath $fileName -Width 75 -Append
        Get-WmiObject -ComputerName $server -Class Win32_PerfFormattedData_PerfOS_Memory | Format-Table @{Name="Percent_Commited_Bytes"; Expression = {($_.PercentCommittedBytesInUse)}},
                        @{Name="Available_MBytes"; Expression = {($_.AvailableMBytes)}},
                        @{Name="Committed_MBytes"; Expression = {[Math]::Round($_.CommittedBytes/1024/1024, 1)}} | Out-File -FilePath $fileName -width 75 -append
    }
}

It’s definitely an improvement in the output (visually), but I am still missing some information. Here is the output of the script:

ComputerName : Server-001
Proc# :
Percent_Commited_Bytes : 3
Available_MBytes : 119437
Percent_Idle :
Percent_Used :
Status : Success
Committed_MBytes : 9934

There are still two problems here. First, the order changed from what the script is requesting vs what the output is. For example:

Script Order:

New-Object -TypeName PSObject -Property @{
            "ComputerName"           = $server
            "Proc#"                  = $perfProc."Proc#"
            "Percent_Used"           = $perfProc."Percent_Used"
            "Percent_Idle"           = $perfProc."Percent_Idle"
            "Percent_Commited_Bytes" = $perfOS."Percent_Commited_Bytes"
            "Available_MBytes"       = $perfOS."Available_MBytes"
            "Committed_MBytes"       = $perfOS."Committed_MBytes"
            "Status"                 = "Success"

Output Order:
ComputerName : Server-001
Proc# :
Percent_Commited_Bytes : 3
Available_MBytes : 119437
Percent_Idle :
Percent_Used :
Status : Success
Committed_MBytes : 9934

The second problem is that some of the information is missing (Proc#, Percent Idle, Percent Used). Do you have any ideas on why that might be? Thank you again!

Phil

Change your code to

$props =  [ordered]@{
            "ComputerName"           = $server
            "Proc#"                  = $perfProc."Proc#"
            "Percent_Used"           = $perfProc."Percent_Used"
            "Percent_Idle"           = $perfProc."Percent_Idle"
            "Percent_Commited_Bytes" = $perfOS."Percent_Commited_Bytes"
            "Available_MBytes"       = $perfOS."Available_MBytes"
            "Committed_MBytes"       = $perfOS."Committed_MBytes"
            "Status"                 = "Success"
}


New-Object -TypeName PSObject -Property $props

If you want to preserve property order

Richard,
You suggestion doesn’t seem to work…I’ve tried to changed it several different ways with no luck. Would you mind modifying my script and paste it here how you would change it? Thank you.