psobject results not as expected

Hi all

I have the script below, running against PowerShell v2.0. Unfortunately I can’t get the filtering working correctly and I’m at my limit in terms of my understanding of PowerShell. For example the DriveLetter custom property returns all the WMI members of Win32_Volume and I only want the drive letter in this particular example. Plus the same thing is happening with other WMI queries, and I really don’t know what I am doing wrong.

Thanks to all for your help in advance…

$ServerBuildresults = @()

$hosts = ‘localhost’

ForEach($h in $hosts) {

$Services = Get-WmiObject Win32_Service
$LogicalDisks = Get-WmiObject Win32_Volume -Filter “DriveType=‘3’”
$WindowsVersion = Get-WmiObject Win32_OperatingSystem | Select-Object name
$OSVersion = Get-WmiObject Win32_OperatingSystem
$Domain = Get-WmiObject Win32_ComputerSystem
$LocalGroups = Get-WmiObject win32_group -Filter “LocalAccount=‘True’”

$obj = new-object psobject
$obj | Add-Member -membertype NoteProperty -Name DriveLetter -Value (($LogicalDisks | Where-object { $.DriveLetter } ) | out-string).Trim()
$obj | Add-Member -membertype NoteProperty -Name DriveSize -Value (($LogicalDisks | Select-Object capacity | % {$
.Capacity / 1GB -as [int]}) | out-string).Trim()
$obj | Add-Member -membertype NoteProperty -Name Label -Value (($LogicalDisks.Label) | out-string).Trim()
$obj | Add-Member -membertype NoteProperty -Name FileSystem -Value (($LogicalDisks.FileSystem) | out-string).Trim()
$obj | Add-Member -membertype NoteProperty -Name BlockSize -Value (($LogicalDisks.Blocksize) | out-string).Trim()
$obj | Add-Member -membertype NoteProperty -Name Monitoring.health -Value ($Services | Where-Object { $.Name -eq ‘healthservice’})
$obj | Add-Member -membertype NoteProperty -Name Monitoring.ccmexec -Value ($Services | Where-Object { $
.Name -eq ‘ccmexec’ })
$obj | Add-Member -membertype NoteProperty -Name Monitoring.masvc -Value ($Services | Where-Object { $.Name -eq ‘masvc’ })
$obj | Add-Member -membertype NoteProperty -Name SQLServices -Value (($Services | Where-Object { $
.StartName -notlike ‘Local’ -and $_.Name -like ‘SQL’ }) | out-string).Trim()
$obj | Add-Member -membertype NoteProperty -Name WindowsVersion -Value (($OSVersion.Caption) | out-string).Trim()
$obj | Add-Member -membertype NoteProperty -Name MemberDomain -Value (($Domain.Domain) | out-string).Trim()
$obj | Add-Member -membertype NoteProperty -Name LocalGroups.Caption -Value (($LocalGroups.Caption) | out-string).Trim()
$obj | Add-Member -membertype NoteProperty -Name LocalGroups.Name -Value (($LocalGroups.name) | out-string).Trim()

$ServerBuildresults += $obj

}

$ServerBuildresults

In this part of your code: $LogicalDisks | Where-object { $_.DriveLetter }, what you ask for is every object in $LogicalDisks that contains a value for Driveletter, so what you get in return is the entire object. Simply running $LogicalDisks.DriveLetter will give you every drive letter for every object in $LogicalDisks.

You can update your WMI filter to exclude drives without a drive letter.

Get-WmiObject Win32_Volume -Filter “DriveType=‘3’ AND DriveLetter IS NOT NULL”

Thanks for the suggestion Erik but I’ve tried that and it doesn’t work. I’m stuck on version 2.0 of powershell.

Daniel your suggestion returns error ‘invalid query’

Take a look at this example:

$computers = @($env:COMPUTERNAME)

#The services filter is a bit lengthy, so to make it more readable
#use a here string to format it.  The filter is the same regardless of
#the computer, so it can be placed outside of the for construct.  One gotcha
#is the first and last lines cannot be indented, but it's a must for WQL\SQL query
#formatting and script readability
$servicesFilter = @"
    Name = 'healthservice' 
    or 
    Name = 'ccmexec' 
    or 
    Name = 'masvc' 
    or 
    (Name Like '*SQL*' And NOT Name Like '*LOCAL*')
"@

#Place a variable ($results) at the top of your for loop structure to collect
#all enumerated information.

$results = foreach ( $computer in $computers ) {
    #Disks are a collection of information.  The code provided you
    #are breaking the properties up into different columns as strings,
    #which isn't the best approach.  Also, take a look the last line, 
    #this is a calculated expression, which is a great way to convert
    #or calculate values or even rename a property name
    $disks = Get-WmiObject Win32_Volume -Filter "DriveType='3' AND DriveLetter IS NOT NULL" -ComputerName $computer | 
             Select DriveLetter, 
             Label, 
             FileSystem, 
             BlockSize, 
             @{Name="Capacity";Expression={$_.Capacity / 1GB -as [int]}}
    #Above I mentioned readability. Look at this remarked like with the filter typed
    #out.  You can read it, but you're probably going to be scrolling when you can just
    #do some formatting to make it simple to read and edit
    #$services = Get-WmiObject -Class Win32_Service -Filter "Name = 'XblGameSave' or Name = 'ccmexec' or Name = 'masvc' or (Name Like '*SQL*' And NOT Name Like '*LOCAL*')" -Property Name, State -ComputerName $computer
    $services = Get-WmiObject -Class Win32_Service -Filter $servicesFilter -Property Name, State -ComputerName $computer

    $os = Get-WmiObject -Class Win32_OperatingSystem  -ComputerName $computer| 
          Select Caption, 
                 Version

    $computersys = Get-WmiObject -Class Win32_ComputerSystem -ComputerName $computer | 
                   Select Domain

    #Personally, not a fan of Add-Member.  It has it's place doing some advanced property settings,
    #but there are much cleaner ways of generating objects.  For Powershell v2, you can create a hash
    #table and use New-Object to generate the object.  The New-Object gets passed back to the $results var
    $props = @{
        ComputerName = $computer
        OperatingSystem = $os | Select -ExpandProperty Caption
        Version = $os | Select -ExpandProperty Version
        Disks = $disks
        Services = $services
    }

    New-Object -TypeName PSObject -Property $props
}

Output:

Services        : Win32_Service.Name="XblGameSave"
ComputerName    : COMPUTER123
Disks           : {@{DriveLetter=E:; Label=LRS_ESP; FileSystem=FAT32; BlockSize=4096; Capacity=1}, @{DriveLetter=C:; Label=Windows8_OS; FileSystem=NTFS; BlockSize=4096; Capacity=448}, @{DriveLetter=D:; 
                  Label=LENOVO; FileSystem=NTFS; BlockSize=4096; Capacity=25}, @{DriveLetter=V:; Label=Virtual Machines; FileSystem=NTFS; BlockSize=4096; Capacity=403}}
Version         : 10.0.14393
OperatingSystem : Microsoft Windows 10 Home

For the Services and Disks, these are collections and are nested objects. You can access these by getting an object (the [0] is the first object by index):

PS C:\Users\Rob> $results[0].Disks


DriveLetter : E:
Label       : LRS_ESP
FileSystem  : FAT32
BlockSize   : 4096
Capacity    : 1

DriveLetter : C:
Label       : Windows8_OS
FileSystem  : NTFS
BlockSize   : 4096
Capacity    : 448

DriveLetter : D:
Label       : LENOVO
FileSystem  : NTFS
BlockSize   : 4096
Capacity    : 25

DriveLetter : V:
Label       : Virtual Machines
FileSystem  : NTFS
BlockSize   : 4096
Capacity    : 403

Now, I did not do the groups so you can attempt to figure it out yourself. Additionally, you want to use Test-Connection (basically a ping) and place error handling on your first WMI query so that if WMI cannot connect, you fail your first query and don’t do 5 failed queries. In the eBooks like above, check out the Big Book of Error Handling

Thanks for the example Rob you’ve taken a bit of time to provide an alternative and document it, appreciate it.

I’ll take a look into this one.

Thanks again.