Formatting output help.

Hi all,

below is script I’ve been working on and I’m hoping for a little assistance formatting the output. I’d like to change 2 things with the output.

  1. to have the field name and the output on a single line for each field, for example ‘Server Name: MyServer’.
  2. to round the numbers up to ‘natural’ numbers not points, for example 7.8 to be 8.

I’ve include an example of my current output from the script below the script.

Any help or advice welcome.

$Computername = $env:Computername
$Date = get-date -format ‘MM-dd-yyyy’
$Path = “C:”
$Filename = “$Computername-Server_Spec-$date.txt”
$Filepath = $path + $filename

$colItems = Get-WmiObject Win32_ComputerSystem
foreach($objItem in $colItems)
{
write-output "Server Name: " $objItem.name | Format-List | out-file $Filepath -append
write-output "Domain: " $objItem.Domain | Format-List | out-file $Filepath -append
}

$colItems = Get-WmiObject Win32_processor
foreach($objItem in $colItems)
{
write-output "CPU Type/Model: " $objItem.name | Format-List | out-file $Filepath -append
}

$colItems = Get-WmiObject Win32_ComputerSystem
foreach($objItem in $colItems)
{
write-output “Total Memory: ” $mem ($objItem.TotalPhysicalMemory / 1gb) “Gb” | Format-List | out-file $Filepath -append
}

$colItems = Get-WmiObject Win32_LogicalDisk -Filter “DriveType = 3”
foreach($objItem in $colItems)
{
write-output “HDD Letter:” $objItem.deviceID
write-output "HDD Size: " $HDD ($objItem.size / 1gb) “GB” | Format-List | out-file $Filepath -append
}

Output sample:
Server Name:
mycomputer
Domain:
mydomain
CPU Type/Model:
Intel(R) Core™ i5-3380M CPU @ 2.90GHz
Total Memory:
7.87633514404297
Gb
HDD Size:
118.898433685303
GB

Wow. You are making this way too hard. This should be a lot simpler. For instance …

$colItems = Get-WmiObject Win32_ComputerSystem
foreach($objItem in $colItems)
{
write-output “Server Name: $($objItem.name)"
write-output “Domain: $($objItem.Domain)"
}

$colItems = Get-WmiObject Win32_processor
foreach($objItem in $colItems)
{
write-output “CPU Type/Model: $($objItem.name)"
}

You’re only writing the info to the console not to text file as I’m doing.

Typically, you’ll pipe a whole object to Format-List and tell it which properties to display, rather than trying to construct each line yourself. Something like this, for the Win32_ComputerSystem parts. (The properties array doesn’t have to be defined before passing it to Format-List; I just do that for readability in the pipeline.)

$props = @(
    @{Label = 'Server Name'; Expression = { $_.Name } }
    'Domain'
    @{Label = 'Total Memory'; Expression = { '{0}GB' -f ([Math]::Round($_.TotalPhysicalMemory / 1GB)) } }
)

Get-WmiObject Win32_ComputerSystem |
Format-List -Property $props |
Out-File -FilePath $Filepath -Append

Thanks Dave, that looks more like what I want to achieve. I’ll work on the rest of my script and see how things turn out.

I know when I’m working with PowerShell that more often than not there’s a better way than how I’m doing it. Sadly I don’t have the skills (yet!) to achieve exactly what I want. The good thing is, because I know what I want I will persevere until I get there.

Thanks again.

Sorry, my point was not a code-complete example, but to show how to get to the data easier. You really should, as much as possible, collect all the data and then concentrate on outputting.

Another couple of questions/problems :slight_smile:

I’ve been through my script and made some adjustments as suggested by Dave. I’m now getting the output to my text file in the format I require. The problem is the about amount of blank lines between each output. For example one part of my script gives CPU details, followed by quite a few blank lines, then the next output. Is there a way to reduce these blank lines?

My second problem is that I planned on running this script on multiple remote servers using ‘invoke-command’. I thought I could run the script-block, creating a text file on the remote server and then copy the file over to my laptop using ‘copy-item’. When testing this the text file creation is fine but the copy fails with ‘access denied’ due to permission problems. If I run the script on the ‘remote’ server (not from my laptop) the file is created and the copy completes successfully. I only chose this method because I was having trouble getting information out of my script-block and into a text file on my laptop. This something I’ve had trouble with for some time.

Any suggestions?

Yeah, Format-List does put a lot of padding around its output for some reason. You can fiddle with that by piping to Out-String first, manipulating the string and then sending it on to Out-File or Add-Content. Here’s a very basic example that just uses the String.Trim() method:

$props = @(
    @{Label = 'Server Name'; Expression = { $_.Name } }
    'Domain'
    @{Label = 'Total Memory'; Expression = { '{0}GB' -f ([Math]::Round($_.TotalPhysicalMemory / 1GB)) } }
)

$string = Get-WmiObject Win32_ComputerSystem |
          Format-List -Property $props |
          Out-String

$string.Trim() | Out-File -FilePath $Filepath -Append

Using this method worked perfectly and I’m able to produce the txt file I want. However I’m unable to copy the file from the server to my desktop/share (using copy-item) due to ‘access denied’. If I run the script on the server it produces the txt file and copies it over fine. I’m a but confused as to why?

Perhaps this is a question for another thread but is it possible to output the data from a scriptblock? Not from within the scriptblock itself but similar to piping it out.

You haven’t come right out and said this, but I gather that you’re executing this code on a remote server via Invoke-Command; is that right? If so, you’re running into the “second hop” scenario when you try to copy the file from your remote computer up to a network share via Invoke-Command. There are ways of getting around this. You could use CredSSP as mentioned in the Secrets of PowerShell Remoting ebook (https://powershell.org/ebooks/), but in this case, I’d probably send the results back to the calling computer and then put it into a text file from there. For example, here’s one way you could use the existing code to build a string for each server, send the string back to the caller, and let the file creation happen locally on your computer (which conveniently avoids the second-hop scenario as well):

$scriptBlock = {
    $stringBuilder = New-Object System.Text.StringBuilder

    $props = @(
        @{Label = 'Server Name'; Expression = { $_.Name } }
        'Domain'
        @{Label = 'Total Memory'; Expression = { '{0}GB' -f ([Math]::Round($_.TotalPhysicalMemory / 1GB)) } }
    )

    $string = Get-WmiObject Win32_ComputerSystem |
              Format-List -Property $props |
              Out-String

    $null = $stringBuilder.Append($string.Trim())

    # Add other information to the StringBuilder with other WMI classes.

    # Send the complete string back to the caller
    $stringBuilder.ToString()
}

Invoke-Command -ComputerName $computerList -ScriptBlock $scriptBlock |
ForEach-Object {
    $computerName = $_.PSComputerName
    $date = Get-Date -Format 'MM-dd-yyyy'

    $_ | Out-File -FilePath "$computerName-Server_Spec-$date.txt"
}

Notice that Invoke-Command automatically adds a PSComputerName property to the objects that are returned by the remote script blocks. Making the remote script blocks only return a single object is my own personal preference; it keeps network chatter to a minimum (and in this case, disk activity as well.)

Hi Dave,

you’re correct about what I’m trying to achieve. Believe it or not I was originally trying to pull the data over to my laptop but having problems getting it across (due to lack of skills). I’ll work on what you’ve provide above and see how I get on.

Thanks very much.