Author Posts

September 19, 2016 at 5:25 am

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

September 19, 2016 at 8:27 am

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.

September 19, 2016 at 8:40 am

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"

September 19, 2016 at 11:01 pm

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'

  • This reply was modified 1 year, 10 months ago by  Russ J.

September 20, 2016 at 1:19 am

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

September 21, 2016 at 12:54 am

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.