Track Database File Sizes with PowerShell: A Quick Guide for DBAs

Database files (mdf, nds, ldfs, etc.) can get out of hand quickly and easily, leading to issues down the road. This can be due to improper planning, large data loads, and bad code run in production, just to list a few. As unglamorous as disk space monitoring is, we need to do our part as DBAs. This post is a quick guide on how to track database file sizes with PowerShell.

Getting Started

If you haven’t already, make sure you have DBATools installed. We are going to be using a command called Get-DbaDbFile to gather the information for us. This command only requires one parameter, -SqlInstance. Here is a snippet of what I get when I run it against my local dev instance:

A screenshot of a computerAI-generated content may be incorrect.
Figure 1: Really Kyle? Data files on the C: drive?

 

This is a bit more information than we really need right now, so let’s trim it down and get some parameters added. For this script, we really only need SqlInstance, Database, TypeDescription, LogicalName, PhysicalName, Size, UsedSpace, and AvailableSpace. We can also exclude system databases from the result set. For readability, I am going to use Format-Table. I will also start to add in some parameters:

$SqlInstance = "SSG-LT-KBURWELL"
 
Get-DbaDbFile -SqlInstance $SqlInstance
 
Get-DbaDbFile -SqlInstance $SqlInstance -ExcludeDatabase master,model,msdb,tempdb |
 
    Select-Object SqlInstance, Database, TypeDescription, LogicalName, PhysicalName, Size, UsedSpace, AvailableSpace | Format-Table

track database file sizes with Powershell

Using Custom Objects

This will be much easier to work with, but it is important to be aware that the columns related to file size have GB, MB, and KB values, depending on what is most appropriate for that file size. This is great when just looking at the data like this, but can be a bit problematic for mathematical comparisons, which we will be doing later. To get around this, we can just use some custom objects:

$SqlInstance = "SSG-LT-KBURWELL"
 
Get-DbaDbFile -SqlInstance $SqlInstance -ExcludeDatabase master,model,msdb,tempdb |
 
    Select-Object SqlInstance, TypeDescription, LogicalName, PhysicalName,
 
     @{Name='DatabaseName'; Expression={$_.Database}},
 
     @{Name='SizeMB'; Expression={ [math]::Round($_.Size / 1MB, 2) }},
 
     @{Name='UsedSpaceMB'; Expression={ [math]::Round($_.UsedSpace / 1MB, 2) }},
 
     @{Name='AvailableSpaceMB'; Expression={ [math]::Round($_.AvailableSpace / 1MB, 2) }},
 
     @{Name='FreeSpacePercent'; Expression={
 
    if ($_.Size -gt 0) {
 
        $sizeMB = [math]::Round($_.Size / 1MB, 2)
 
        $availMB = [math]::Round($_.AvailableSpace / 1MB, 2)
 
        [math]::Round(($availMB / $sizeMB) * 100, 2)
 
        } else { 0 }
 
    }},
 
    @{Name='CaptureTimestamp'; Expression={ Get-Date -Format "yyyy-MM-dd HH:mm:ss" }}
 
    | Format-Table

This converts all sizes to MB, which will make any mathematical operations much easier. I also included a custom object for DatabaseName, FileSpacePercent, and CaptureTimestamp. DatabaseName really just acts as a custom column name, which is necessary when loading the data into SQL.

On To The Comparisons

Now that we are getting only the data we need, we need to save it to a table so we can do point in time comparisons. As with everything else, DBATools makes this ridiculously easy. We just need to point it to an instance and a database, it will take care of the rest. Instead of piping our results to Format-Table, I am going to pipe them to a command that will build and load a SQL table:

$SqlInstance = "SSG-LT-KBURWELL"
 
$WriteInstance = "SSG-LT-KBURWELL"
 
$WriteDB = "DBA"
 
$WriteTable = "FileSizesAndUsage"
 
Get-DbaDbFile -SqlInstance $SqlInstance -ExcludeDatabase master,model,msdb,tempdb |
 
    Select-Object SqlInstance, TypeDescription, LogicalName, PhysicalName,
 
     @{Name='DatabaseName'; Expression={$_.Database}},
 
     @{Name='SizeMB'; Expression={ [math]::Round($_.Size / 1MB, 2) }},
 
     @{Name='UsedSpaceMB'; Expression={ [math]::Round($_.UsedSpace / 1MB, 2) }},
 
     @{Name='AvailableSpaceMB'; Expression={ [math]::Round($_.AvailableSpace / 1MB, 2) }},
 
     @{Name='FreeSpacePercent'; Expression={
 
    if ($_.Size -gt 0) {
 
        $sizeMB = [math]::Round($_.Size / 1MB, 2)
 
        $availMB = [math]::Round($_.AvailableSpace / 1MB, 2)
 
        [math]::Round(($availMB / $sizeMB) * 100, 2)
 
        } else { 0 }
 
    }},
 
    @{Name='CaptureTimestamp'; Expression={ Get-Date -Format "yyyy-MM-dd HH:mm:ss" }}
 
    | Write-DbaDbTableData -SqlInstance $WriteInstance -Database $WriteDB -Table $WriteTable -AutoCreateTable

Now I have the data in SQL Server:

track database file sizes with Powershell

With this information being collected regularly, via scheduled task or agent job, we can easily trend out how much our database files are growing. It will also make potential misconfigurations stand out, such as a 1TB mdf with 99% free space. Personally, I use it in client environments to find over-allocated files that can be shrunk to reclaim some space. I know, “shrink bad”, but sometimes you just need to do it. As you can see, it’s quite straightforward and rather easy to track database file sizes with Powershell!

Please share this

This Post Has One Comment

  1. Thomas

    Why would anybody want to use PowerShell to accomplish this?? 🤷‍♂️ You have a SQL Server – you got T-SQL.

Leave a Reply

Related Articles

Full-Text Index issues

Fixing Full-Text Index Issues

We’ve had Full-Text index issues come up with surprising frequency lately, so I thought I’d share the fix with all of you lovely people who read this blog. For reference, the customer in this example asked us to look at this problem on a server that is not managed by us.

Read More »