none
PowerShell: Disk Space List

    Question

  • Hi All,

    I'm new to PowerShell and struggling to find a way forward with using the win32_logicaldisk class within my basic script.

    I have found that when I set the properties for my new object below for the win32_logicaldisk class, I will get multiple results for each property specified which is expected, and the Operating System and Computer System classes work fine as there is only one result returned

    What I am trying to achieve is a result for each computer to list all available drives, their total size, free space and percentage of free space.

    I don't want to filter out the device ID when getting info from the win32_logicaldisk class as all device ID's could be any letter, so I would like them all to be identified, then used for when I name my properties.

    The end result would be something like this (values entered are just for the example):

    C: Drive Total Size :  600.00 GB

    C: Drive Free Space : 300.00 GB

    C: Drive Percentage Free : 50.00%

    R: Drive Total Size :  600.00 GB

    R: Drive Free Space : 300.00 GB

    R: Drive Percentage Free : 50.00%

    I am assuming I need to run another Foreach construct for the disks and I would need to use a variable for each device ID found to use in the naming of my properties, but just can't get my head around it.

    Hope someone can help.

     
    PROCESS {
            foreach ($computer in $Computername) {
                $os = Get-WmiObject -Class win32_operatingsystem -Computername $Computer
                $cs = Get-WmiObject -Class win32_computersystem -Computername $Computer
                $disks = Get-WmiObject -Class win32_logicaldisk -Computername $Computer
                $props = [ordered]@{'ComputerName'=$Computer;
                    'OSVersion'=$os.version;
                    'OSBuild'=$os.buildnumber;
                    'SPVersion'=$os.servicepackmajorversion;
                    'Model'=$cs.model;
                    'Manufacturer'=$cs.manufacturer;
                    'RAM'=$cs.totalphysicalmemory / 1GB -as [int];
                    'Sockets'=$cs.numberofprocessors;
                    'Cores'=$cs.numberoflogicalprocessors;
                    'SystemType'=$cs.SystemType;
                    }
                $obj = New-Object -TypeName PSObject -Property $props
                Write-Output $obj
            }
        }


    • Edited by Omegamon Sunday, July 07, 2013 8:51 AM
    Sunday, July 07, 2013 8:36 AM

Answers

  • You have a one to many problem.  You must generate an object for each disk or you must alter the code to store multiple disks.  The solution you choose depends on what output you requires.


    ¯\_(ツ)_/¯

    Sunday, July 07, 2013 11:31 AM

All replies

  • I have gotten a little further with this (see below script), however what I am finding now is it only selecting the last disk found in the foreach construct and errors out on all the disks found prior.

    The results are right now though, its just not showing all disks.

    PROCESS {
            foreach ($computer in $Computername) {
                $os = Get-WmiObject -Class win32_operatingsystem -Computername $Computer
                $cs = Get-WmiObject -Class win32_computersystem -Computername $Computer
                $disks = Get-WmiObject -Class win32_logicaldisk -Computername $Computer
                foreach ($disk in $disks) {
                    $props = [ordered]@{'ComputerName'=$Computer;
                        'OSVersion'=$os.version;
                        'OSBuild'=$os.buildnumber;
                        'SPVersion'=$os.servicepackmajorversion;
                        'Model'=$cs.model;
                        'Manufacturer'=$cs.manufacturer;
                        'RAM'=$cs.totalphysicalmemory / 1GB -as [int];
                        'Sockets'=$cs.numberofprocessors;
                        'Cores'=$cs.numberoflogicalprocessors;
                        'SystemType'=$cs.SystemType;
                        "$($disk.deviceid)DriveTotalSize"=[math]::Round($disk.size /1gb,2);
                        "$($disk.deviceid)DriveFreeSpace"=[math]::Round($disk.freespace /1gb,2);
                        "$($disk.deviceid)DrivePercentageFree"=[math]::Round(($disk.freespace/$disk.size)*100,2)
                        }
                }
                $obj = New-Object -TypeName PSObject -Property $props
                Write-Output $obj
            }
        }

    Sunday, July 07, 2013 9:50 AM
  • You have a one to many problem.  You must generate an object for each disk or you must alter the code to store multiple disks.  The solution you choose depends on what output you requires.


    ¯\_(ツ)_/¯

    Sunday, July 07, 2013 11:31 AM
  • Thanks for your response jrv, I am going for the code to store multiple disks.

    From your response I am still unsure which direction to take?

    I've minimised the script to just contain the relevant disk data and I still have the same issue where it only picks up the last drive - For example, on my local computer I should be seeing output for the drives c: d: f: and h: and I am only getting the results for h:

    PROCESS {
            foreach ($computer in $Computername) {
                $disks = Get-WmiObject -Class win32_logicaldisk -filter "drivetype='3'" -Computername $Computer
                foreach ($disk in $disks) {
                    $props = [ordered]@{
                        "$($disk.deviceid)DriveTotalSize"=[math]::Round($disk.size / 1gb,2);
                        "$($disk.deviceid)DriveFreeSpace"=[math]::Round($disk.freespace / 1gb,2);
                        "$($disk.deviceid)DrivePercentageFree"=[math]::Round(($disk.freespace/$disk.size)*100,2)
                        }
                }
                $obj = New-Object -TypeName PSObject -Property $props
                Write-Output $obj
            }
        }

    Sunday, July 07, 2013 11:59 AM
  • Like this:

    foreach ($computer in $Computername) {
        $disks = Get-WmiObject -Class win32_logicaldisk -filter "drivetype='3'" -Computername $Computer
        foreach ($disk in $disks) {
            $props = [ordered]@{
                ComputerName=$computer
                DriveName=$disk.DeviceID
                DriveTotalSize=[math]::Round($disk.size / 1gb,2)
                DriveFreeSpace=[math]::Round($disk.freespace / 1gb,2)
                DrivePercentageFree=[math]::Round(($disk.freespace/$disk.size)*100,2)
                }
        		New-Object -TypeName PSObject -Property $props
        }
    }
    
    


    ¯\_(ツ)_/¯

    Sunday, July 07, 2013 12:33 PM
  • Thanks again, that does work.....however, now writing the objects properties out to the pipeline are affected.

    If I do a get-member on this, only the first disk drives NoteProperties are shown and not the other disks.

    This was my reason behind using:

    $obj = New-Object -TypeName PSObject -Property $props
    Write-Output $obj

    Sunday, July 07, 2013 12:58 PM
  • That makes no sense.  The extra lines change nothing.

    You have this embedded in a function so the output will be in the pipeline:

     function Whatever{
       Param(
           [string[]]$computername=$env:computername
           )
           
    	foreach ($computer in $Computername) {
    	    $disks = Get-WmiObject -Class win32_logicaldisk -filter "drivetype='3'" -Computername $Computer
    	    foreach ($disk in $disks) {
    	        $props = [ordered]@{
    	            ComputerName=$computer
    	            DriveName=$disk.DeviceID
    	            DriveTotalSize=[math]::Round($disk.size / 1gb,2)
    	            DriveFreeSpace=[math]::Round($disk.freespace / 1gb,2)
    	            DrivePercentageFree=[math]::Round(($disk.freespace/$disk.size)*100,2)
    	            }
    	    		New-Object -TypeName PSObject -Property $props
    	    }
    	}
    }
    

    $disks=whatever $computerlist

    $disk | ft -auto

    Works as expected.


    ¯\_(ツ)_/¯

    Sunday, July 07, 2013 1:05 PM
  • You should also drop the [ordered] as it will not do what you expect.

    ¯\_(ツ)_/¯

    Sunday, July 07, 2013 1:06 PM
  • It might be best if I could explain what my desired results are - I could be going down the wrong path altogether

    Using the script:

    PROCESS {
            foreach ($computer in $Computername) {
                $os = Get-WmiObject -Class win32_operatingsystem -Computername $Computer
                $cs = Get-WmiObject -Class win32_computersystem -Computername $Computer
                $disks = Get-WmiObject -Class win32_logicaldisk -filter "drivetype='3'" -Computername $Computer
                foreach ($disk in $disks) {
                    $props = [ordered]@{
                        'ComputerName'=$Computer;
                        'OSVersion'=$os.version;
                        'OSBuild'=$os.buildnumber;
                        'SPVersion'=$os.servicepackmajorversion;
                        'Model'=$cs.model;
                        'Manufacturer'=$cs.manufacturer;
                        'RAM'=$cs.totalphysicalmemory / 1GB -as [int];
                        'Sockets'=$cs.numberofprocessors;
                        'Cores'=$cs.numberoflogicalprocessors;
                        'SystemType'=$cs.SystemType;
                        "$($disk.deviceid)DriveTotalSize"=[math]::Round($disk.size / 1gb,2);
                        "$($disk.deviceid)DriveFreeSpace"=[math]::Round($disk.freespace / 1gb,2);
                        "$($disk.deviceid)DrivePercentageFree"=[math]::Round(($disk.freespace/$disk.size)*100,2)
                        }
                        New-Object -TypeName PSObject -Property $props
                }
            }
        }

    My expected results are:

    ComputerName          : xxxx
    OSVersion             : xxxx
    OSBuild               : xxxx
    SPVersion             : xxxx
    Model                 : xxxx
    Manufacturer          : xxxx
    RAM                   : xxxx
    Sockets               : xxxx
    Cores                 : xxxx
    SystemType            : xxxx
    C:DriveTotalSize      : 100.00
    C:DriveFreeSpace      : 50.00
    C:DrivePercentageFree : 50.00
    D:DriveTotalSize      : 100.00
    D:DriveFreeSpace      : 50.00
    D:DrivePercentageFree : 50.00
    F:DriveTotalSize      : 100.00
    F:DriveFreeSpace      : 50.00
    F:DrivePercentageFree : 50.00
    H:DriveTotalSize      : 100.00
    H:DriveFreeSpace      : 50.00
    H:DrivePercentageFree : 50.00

    However, currently the results are coming out like this:

    ComputerName          : xxxx
    OSVersion             : xxxx
    OSBuild               : xxxx
    SPVersion             : xxxx
    Model                 : xxxx
    Manufacturer          : xxxx
    RAM                   : xxxx
    Sockets               : xxxx
    Cores                 : xxxx
    SystemType            : xxxx
    C:DriveTotalSize      : 100.00
    C:DriveFreeSpace      : 50.00
    C:DrivePercentageFree : 50.00

    ComputerName          : xxxx
    OSVersion             : xxxx
    OSBuild               : xxxx
    SPVersion             : xxxx
    Model                 : xxxx
    Manufacturer          : xxxx
    RAM                   : xxxx
    Sockets               : xxxx
    Cores                 : xxxx
    SystemType            : xxxx
    D:DriveTotalSize      : 100.00
    D:DriveFreeSpace      : 50.00
    D:DrivePercentageFree : 50.00

    ComputerName          : xxxx
    OSVersion             : xxxx
    OSBuild               : xxxx
    SPVersion             : xxxx
    Model                 : xxxx
    Manufacturer          : xxxx
    RAM                   : xxxx
    Sockets               : xxxx
    Cores                 : xxxx
    SystemType            : xxxx
    F:DriveTotalSize      : 100.00
    F:DriveFreeSpace      : 50.00
    F:DrivePercentageFree : 50.00

    ComputerName          : xxxx
    OSVersion             : xxxx
    OSBuild               : xxxx
    SPVersion             : xxxx
    Model                 : xxxx
    Manufacturer          : xxxx
    RAM                   : xxxx
    Sockets               : xxxx
    Cores                 : xxxx
    SystemType            : xxxx
    H:DriveTotalSize      : 100.00
    H:DriveFreeSpace      : 50.00
    H:DrivePercentageFree : 50.00

    Sunday, July 07, 2013 1:22 PM
  • That is not the script I posted.  Please try to understand that what you are doing is wrong.  I am trying to get you going in the right direstion.

    I am sorry that I am new at PowerShell.  I have only been using it for 8 years and you guy are so much better.  If you think your way is correct then you do not need to ask for our help.

    I suggest that you look closely at what I posted and the issues I raised.  If you do not understand what I am referring to then ask a specific question.

    What you are trying for cannot be done the way you are approaching it. It is an issue of one-to-many relations.  We need to de-normalize the data due to this relationship. 

    If you do it your way you will end up with broken objects.  If you are just trying for a grouped report then use grouping to get your report.

    whatever $computerlist| ft  -group computername -auto

    You can also write a custom report formatter to get your format.

    Rules: collect data into objects, select properties, format output.

    That is the way of PowerShell and all object programming. Once you learn how to structure a program and how to get the components of PowerShell to work correctly these things will become easy.  By trying to cobble your way through this without seeing what is happening you will become frustrated.

    Look at your original script.  You are enumerating disks and assigning things arbitrarily to an object.  In objects the output will be restricted to the components of the first object in the collection.  If you add arbitrary properties along the way they will become somewhat inaccessible.  The de-normalized view gives you everything like a SQL table and you can then sue output query methods to arrange it in a report.

    Of course if you just want text to look at the just use Write-Host to output the exact text you want and forget about objects.


    ¯\_(ツ)_/¯

    Sunday, July 07, 2013 1:42 PM
  • Here is what you are looking for mostly:

    function Whatever{
       Param(
           [string[]]$computername=$env:computername
           )
           
    	foreach ($computer in $Computername) {
    	    Write-Host "ComputerName=$computer"
    	    $disks = Get-WmiObject -Class win32_logicaldisk -filter "drivetype='3'" -Computername $Computer
    	    foreach ($disk in $disks) {
    	            Write-Host "`tDriveName=$($disk.DeviceID)"
    	            Write-Host "`DriveTotalSize=$([math]::Round($disk.size / 1gb,2))"
    	            Write-Host "`DriveFreeSpace=$([math]::Round($disk.freespace / 1gb,2))"
    	            Write-Host "`DrivePercentageFree=$([math]::Round(($disk.freespace/$disk.size)*100,2))"
    	    }
    	}
    }
    

    This gives you all of the text no matter how many drives you have. It is not a good PowerShell approach but is is good enough if you just want the text.

    ¯\_(ツ)_/¯

    Sunday, July 07, 2013 1:51 PM
  • To clarify - this usage is wrong for what you need to do.

    This:

    $props = [ordered]@{
                        'ComputerName'=$Computer;
                        'OSVersion'=$os.version;
                        'OSBuild'=$os.buildnumber;
                        'SPVersion'=$os.servicepackmajorversion;
                        'Model'=$cs.model;
                        'Manufacturer'=$cs.manufacturer;
                        'RAM'=$cs.totalphysicalmemory / 1GB -as [int];
                        'Sockets'=$cs.numberofprocessors;
                        'Cores'=$cs.numberoflogicalprocessors;
                        'SystemType'=$cs.SystemType;
                        "$($disk.deviceid)DriveTotalSize"=[math]::Round($disk.size / 1gb,2);
                        "$($disk.deviceid)DriveFreeSpace"=[math]::Round($disk.freespace / 1gb,2);
                        "$($disk.deviceid)DrivePercentageFree"=[math]::Round(($disk.freespace/$disk.size)*100,2)
                        }
    

    Must be this:

    $props=@{
    	ComputerName=$Computer
    	OSVersion=$os.version
    	OSBuild=$os.buildnumber
    	SPVersion=$os.servicepackmajorversion
    	$DriveID=$disk.DriveID
    	Model'=$cs.model
    	Manufacturer=$cs.manufacturer
    	RAM=$cs.totalphysicalmemory / 1GB -as [int]
    	Sockets=$cs.numberofprocessors
    	Cores=$cs.numberoflogicalprocessors
    	SystemType=$cs.SystemType
    	DriveTotalSize=[math]::Round($disk.size / 1gb,2)
    	DriveFreeSpace=[math]::Round($disk.freespace / 1gb,2)
    	DrivePercentageFree=[math]::Round(($disk.freespace/$disk.size)*100,2
    }
    

    Now try and understand why I chaged that the way I did. Try and see what you were doing, why it was unnecessary and what was wrong.

    Hint: You cannot change the name of fields in an object dynamically and have the objects behave consistently.  That is OOP 101.

    You can build a hash dynamically but you cannot use a dynamic hash to build objects.  The has used for creating an object must be identical every time you generate an object to the pipeline or only the first object will be used as a property builder. (Posh 101)


    ¯\_(ツ)_/¯

    Sunday, July 07, 2013 2:13 PM
  • Sorry if I am coming across as ignoring what you are trying to help me with - as I mentioned in my OP, I am very new to PowerShell and my understanding is very well below yours...believe me.

    What I am trying to achieve is to create a function, that creates a new object, that has a number of custom defined properties that can all be seen when you run get-member on the function itself. This will then allow anyone to format the data however they would like by running the function and using select-object and their own custom expressions.

    Using your example, my desired result would be all of the below to be NoteProperty member types:

    ComputerName          : MyComputer
    C:DriveTotalSize      : 100.00
    C:DriveFreeSpace      : 50.00
    C:DrivePercentageFree : 50.00
    D:DriveTotalSize      : 100.00
    D:DriveFreeSpace      : 50.00
    D:DrivePercentageFree : 50.00
    F:DriveTotalSize      : 100.00
    F:DriveFreeSpace      : 50.00
    F:DrivePercentageFree : 50.00
    H:DriveTotalSize      : 100.00
    H:DriveFreeSpace      : 50.00
    H:DrivePercentageFree : 50.00

    Do I need to create separate objects in the one function, and then link them together somehow to make a single object?

    Sunday, July 07, 2013 2:13 PM
  • Sorry if I am coming across as ignoring what you are trying to help me with - as I mentioned in my OP, I am very new to PowerShell and my understanding is very well below yours...believe me.

    What I am trying to achieve is to create a function, that creates a new object, that has a number of custom defined properties that can all be seen when you run get-member on the function itself. This will then allow anyone to format the data however they would like by running the function and using select-object and their own custom expressions.

    Using your example, my desired result would be all of the below to be NoteProperty member types:

    ComputerName          : MyComputer
    C:DriveTotalSize      : 100.00
    C:DriveFreeSpace      : 50.00
    C:DrivePercentageFree : 50.00
    D:DriveTotalSize      : 100.00
    D:DriveFreeSpace      : 50.00
    D:DrivePercentageFree : 50.00
    F:DriveTotalSize      : 100.00
    F:DriveFreeSpace      : 50.00
    F:DrivePercentageFree : 50.00
    H:DriveTotalSize      : 100.00
    H:DriveFreeSpace      : 50.00
    H:DrivePercentageFree : 50.00

    Do I need to create separate objects in the one function, and then link them together somehow to make a single object?

    Again - you cannot do what you are trying to do. It is not possible to build objects in the pipeline the way you are trying to do it.  Please read my earlier posts very carefully.  GO back and spend some more time with the basics of the PowerShell pipeline.

    In OOP two objects that have different properties are not the same class but are of two different classes.


    ¯\_(ツ)_/¯

    Sunday, July 07, 2013 2:17 PM
  • While I agree that the OP may be a little bit confused about the order of relations, what he asked for is in fact very much possible:

    Set-StrictMode -Version 2
    
    Function Get-LogicalDiskInfo
    {
    Param(
    	[parameter(ValueFromPipeline=$true)]
    	[String]$computerName = $env:COMPUTERNAME
    )
    Process
    {
    	$os = Get-WmiObject -Query:'SELECT Version, BuildNumber, ServicePackMajorVersion FROM Win32_OperatingSystem' -Computername:"$computerName"
    	$cs = Get-WmiObject -Query:'SELECT Model, Manufacturer, TotalPhysicalMemory, NumberOfProcessors, NumberOfLogicalProcessors, SystemType FROM Win32_ComputerSystem' -Computername:"$computerName"
    	$props = [ordered]@{
    		'ComputerName' = $computerName;
    		'OSVersion' = $os.version;
    		'OSBuild' = $os.buildnumber;
    		'SPVersion' = $os.servicepackmajorversion;
    		'Model' = $cs.model;
    		'Manufacturer' = $cs.manufacturer;
    		'RAM' = '{0} GB' -f $($cs.totalphysicalmemory / 1GB -as [int]);
    		'Sockets' = $cs.numberofprocessors;
    		'Cores' = $cs.numberoflogicalprocessors;
    		'SystemType' = $cs.SystemType;
    	}
    	$disks = Get-WmiObject -Query:'SELECT DeviceID, Size, FreeSpace FROM Win32_LogicalDisk WHERE DriveType=3' -Computername:"$computerName"
    	foreach ($disk in $disks)
    	{
    		if ($disk.Size -gt 0)
    		{
    			$props["$($disk.DeviceID)Drive_TotalSize"] = '{0:0.00} GB' -f [math]::Round($disk.Size / 1GB, 2)
    			$props["$($disk.DeviceID)Drive_FreeSpace"] = '{0:0.00} GB' -f [math]::Round($disk.FreeSpace / 1GB, 2)
    			$props["$($disk.DeviceID)Drive_%Free"] = '{0:0.00}%' -f [math]::Round(($disk.FreeSpace/$disk.Size)*100, 2)
    		}
    	}
    	New-Object -TypeName PSObject -Property $props
    }
    }
    
    cls
    $computerNames | Get-LogicalDiskInfo




    Monday, July 08, 2013 5:38 AM
  • Still not legal object generation and it will produce a ragged object collection.  This cannot be used correctly in a pipeline.  THis is exactly the same issue as originally existed.

    You cannot generate ragged objects into a pipeline and have them be usable later.


    ¯\_(ツ)_/¯

    Monday, July 08, 2013 5:48 AM