Question
Dr. Scripto finally has the budget to buy a few new virtualization host servers, but he needs to make some room in the data center to accommodate them. He thinks it makes sense to get rid of his lowest-powered old servers first… but he needs to figure out which ones those are.All of the virtualization hosts run Windows Server, but some of them don’t haveWindows PowerShell installed, and they’re all running different OS versions. Theo ldest OS version is Windows 2000 Server (he knows, and he’s embarrassed, but he’s just been so darn busy). The good news is that they all belong to the same domain, and that you can rely on having a Domain Admin account to work with. The good Doctor has asked you to write a PowerShell command or script that can show him each server’s name, installed version of Windows, amount of installed physical memory, and number of installed processors. For processors, he’ll be happy getting a count of cores, or sockets, or even both — whatever you can reliably provide across all these different versions of Windows. To help you out, he’s given you a text file, C:\IPList.txt, that contains one server IP address per line. If you can write this as a one-liner — awesome! If not, try to keep your answer is concise and compact as possible (although it’s perfectly okay to use full command and parameter names).
My Answer
Get-Content C:\IPList.txt | Foreach-object { Get-WmiObject -Namespace root\CImv2 -Class Win32_ComputerSystem -ComputerName $_ | Select-object Name, @{Label="OS";Expression={(Get-WmiObject -Namespace root\CImv2 -Class Win32_OperatingSystem).Caption}}, TotalPhysicalMemory, NumberOfProcessors, NumberOfLogicalProcessors } | Format-Table -AutoSize
To make PS_2 compatible use __Servername instead of Name
My answer, but rounding up the size calculations
Using the format ( f{}) commands
Get-Content C:\IPList.txt | Foreach-object {Get-WmiObject -Namespace root\CImv2 -Class Win32_ComputerSystem -ComputerName $_ | Select-object Name, @{Label="OS";Expression={(Get-WmiObject -Namespace root\CImv2 -Class Win32_OperatingSystem -ComputerName $_.Name).Caption}}, @{Label="Mem in GB";Expression={"{0:N0}" -f($_.TotalPhysicalMemory / 1GB)}}, NumberOfProcessors, NumberOfLogicalProcessors} | Format-Table –AutoSize
Using the .Net Math class
Get-Content C:\IPList.txt | Foreach-object {Get-WmiObject -Namespace root\CImv2 -Class Win32_ComputerSystem -ComputerName $_ | Select-object Name, @{Label="OS";Expression={(Get-WmiObject -Namespace root\CImv2 -Class Win32_OperatingSystem -ComputerName $_.Name).Caption}}, @{Label="MEM in GB";expression={[System.Math]::Round(($_.TotalPhysicalMemory / 1GB),1)}}, NumberOfProcessors, NumberOfLogicalProcessors} | Format-Table –AutoSize
Learning Points
Splatting (Boe Prox)
Use splatting if you plan to use the same info over and over again in a script, in fact it’s not a bad idea to do it all the time
- Instead of
Send-MailMessage -To user@domain.com -From user1@domain.com -Subject Test -SMTPServer server@domain.com -Body "This is a test"
- Do
$email = @{ To = 'user@domain.com' From = 'user1@domain.com' Subject = 'Test' SMTPServer = 'server.domain.com' }
Send-MailMessage @email
Stop using $ErrorActionPreference (Boe Prox)
This changes it for the entire script, use it as needed for each command using -ErrorAction
Using Passthru to combine different objects (taygibb‘s entry)
The PassThru parameter is one of the common parameters of most PowerShell cmdlets and returns an object when a cmdlet normally wouldn’t return an object. For example, The Copy-Item cmdlet will perform an action but not return any information on the copied object but when specifying the Passthru parameter you can return the copied object and perform further work on it. In TayGibbs script he uses this technique to add a new member to the management object returned from query the 1st set of required WMI information. Normally Add-Member wouldn’t return information to the console. But when using the Passthru cmdlet it will return the new changed object
Get-WmiObject -Class "Win32_ComputerSystem" -ComputerName localhost | Select-Object -Property Name, TotalPhysicalMemory, NumberOfLogicalProcessors, NumberOfProcessors | Add-Member -Name "OS Version" -Value $(Get-WmiObject -Class Win32_OperatingSystem -ComputerName localhost | Select-Object -ExpandProperty Caption) -MemberType NoteProperty -PassThru
Advanced Notes (Art Beane)
- If your entry calls for array entry in the parameter then make sure to add it, e.g.
-
[string[]]$ComputerName
-
- Match your parameter names to the properties of the expected item (e.g. ComputerName and not Server)
- When working in V3 with CIM why not Try{WSMAN} Catch{DCOM}
Misc Advanced Notes
- Uses #Requires -version X to show the version your script works with
- Be careful with shorthand parameter declarations, for examle
- This works in v2 and v3
-
[Parameter(Position=0,ValueFromPipeline=$True,Mandatory=$True)]
-
- This only works in V3
-
[Parameter(Position=0,ValueFromPipeline,Mandatory)]
-
- This works in v2 and v3