~/2014/01/21/powershell-gather-netapp-snapshot-details.md
PowerShell: NetApp – Gather Snapshot Details
--- author: Tom Lasswell date: updated: read: 1 min in: [dotps, ps, scripts] tags: [netapp, powershell, snapshots] ---
$ grep -n '^#' post.md
Gathering snapshot statistics is a tedious task when looking at autosupports and cli output. I needed to gather information about oldest snapshot, average number of snaps per day, total snapshots, and other various information.
This PowerShell script uses the Data ONTAP PowerShell Toolkit to collect that information for every online volume on each 7-Mode controller and writes a summary CSV and a detail CSV.
Requirements
- Data ONTAP PowerShell Toolkit 1.2 or higher (the
DataONTAPmodule) - NetApp 7-Mode controllers reachable over HTTPS
- An account that can list volumes and snapshots on each controller
- Write access to the folder the script runs from (the CSV files are saved next to the script)
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
nodes | Array | Yes | Controllers to query (host names or IPs). If omitted, PowerShell prompts for one per line; press Enter on an empty line to continue. |
username | String | Yes | Account used to connect, for example domain\username. |
password | SecureString | Yes | Password for the account. Prompted for if omitted. |
IsVerbose | Switch | No | Also prints a detail table of every snapshot per volume. |
Usage
Pass the controllers and account on the command line. The password is prompted for:
& '.\NetApp-Gather Snapshot Information v1.1.ps1' -nodes filer1.company.biz,filer2.company.biz -username "domain\username" -IsVerbose
Or run it with no parameters and answer the prompts:
& '.\NetApp-Gather Snapshot Information v1.1.ps1'
cmdlet NetApp-Gather Snapshot Information v1.1.ps1 at command pipeline position 1
Supply values for the following parameters:
nodes[0]: filer1.company.biz
nodes[1]: filer2.company.biz
nodes[2]:
username: domain\username
password: *************
Script
Original (v1.1)
<#
.SYNOPSIS
NetApp-Gather Snapshot Information
.DESCRIPTION
Collects snapshot information, summary and detail, for every online volume
on each 7-Mode controller, including created time, total snapshots, total
days, average per day and oldest snapshot. Saves a Summary and a Detail CSV
next to the script.
.PARAMETER nodes
Controllers to query, one per entry.
.PARAMETER username
Account used to connect, for example domain\username.
.PARAMETER password
Password for the account, as a SecureString.
.PARAMETER IsVerbose
Display snapshot detail for each volume.
.EXAMPLE
& '.\NetApp-Gather Snapshot Information v1.1.ps1' -nodes filer1.company.biz,filer2.company.biz -username "domain\username" -IsVerbose
.EXAMPLE
& '.\NetApp-Gather Snapshot Information v1.1.ps1'
.NOTES
Author : Thomas Lasswell (https://www.techcolumnist.com)
Version : 1.2 (2026-09-20)
Requires: Data ONTAP PowerShell Toolkit 1.2 or higher
History : 1.0 2014-01-17 Initial version.
1.1 2014-01-21 Added CSV export, days old column fixed.
1.2 2026-09-20 Fixed the timestamp in the CSV file names (it used minutes
where the month belongs).
#>
# Parameters: nodes is each node; when finished, press Enter and it will continue.
param(
[Parameter(Mandatory = $true)]
[Array]$nodes,
[Parameter(Mandatory = $true)]
[String]$username,
[Parameter(Mandatory = $true, ParameterSetName = 'Secret')]
[Security.SecureString]$password,
[switch]$IsVerbose
)
# Create output file names
$exedir = Split-Path -Path $MyInvocation.MyCommand.Definition -Parent
$currentDate = (Get-Date -Format yyyyMMdd.HHmmss)
$outsummary = ($exedir + "\" + "GatherSnapshotInformation_" + $currentDate + "_Summary.csv")
$outdetails = ($exedir + "\" + "GatherSnapshotInformation_" + $currentDate + "_Detail.csv")
# Load the ONTAP PowerShell Toolkit
$module = Get-Module DataONTAP
if ($module -eq $null) {
Import-Module DataONTAP
}
try {
$requiredVersion = New-Object System.Version(1.2)
if ((Get-NaToolkitVersion).CompareTo($requiredVersion) -lt 0) { throw }
} catch [Exception] {
Write-Host "`nThis script requires Data ONTAP PowerShell Toolkit 1.2 or higher`n" -ForegroundColor Red
return
}
$cred = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList $username, $password
# Calculate the difference between two dates
function Get-DateDiff {
param (
[CmdletBinding()]
[parameter(Mandatory = $true)]
[datetime]$date1,
[parameter(Mandatory = $true)]
[datetime]$date2
)
if ($date2 -gt $date1) {
$diff = $date2 - $date1
} else {
$diff = $date1 - $date2
}
$diff
}
# Declare object arrays
$objDetail = @()
$objSummary = @()
# Connect to each node individually
foreach ($node in $nodes) {
Write-Host "connecting to node $node..."
$conn = Connect-NaController -Name $node -HTTPS -Credential $cred
if ($conn -ne $null) {
Write-Host "node connected, continuing on to snapshot calculations..."
Write-Host "gathering node volumes..."
$vols = Get-NaVol | Where-Object { $_.state -eq "online" -and $_.raidstatus -notmatch "read-only" }
if ($vols -ne $null) {
foreach ($vol in $vols) {
Write-Host "gathering snapshots for volume $vol..."
# Get snapshots
$snaps = Get-NaSnapshot -TargetName $vol
# Group to count snapshots per day
$snapsdatecount = $snaps | Group-Object { ((Get-Date) - $_.Created).Days } -NoElement | Sort-Object Name -Descending
# Measure to get count and average; the average is snapshots per day
$totalavgdays = $snapsdatecount | Measure-Object Count -Average | Select-Object Count, Average
$avgdays = $totalavgdays.Average
$totaldays = $totalavgdays.Count
# Sum snapshots for total snapshot consumption
$totalsize = $snaps | Measure-Object Total -Sum | Select-Object Count, Sum
$ftotalsize = ConvertTo-FormattedNumber $totalsize.sum DataSize "0.0"
# Get oldest snapshot
$foldestsnap = $snaps | Sort-Object Created -Descending | Select-Object -Last 1
# Build array for summary
if ($avgdays -ne $null) {
$summaryprop = @{
'Node' = $node
'Volume' = $vol
'TotalSnaps' = $snaps.length
'TotalDays' = $totaldays
'AvgPerDay' = $avgdays
'TotalSize' = $ftotalsize
'Oldest' = $foldestsnap.created
}
$objectS = New-Object -TypeName PSObject -Property $summaryprop
$objSummary += $objectS
$objectS
foreach ($snap in $snaps) {
$daysold = Get-DateDiff (Get-Date) $snap.created
$ftotal = ConvertTo-FormattedNumber $snap.total DataSize "0.0"
$fcumulative = ConvertTo-FormattedNumber $snap.CumulativeTotal DataSize "0.0"
# Build array for details
$detailprop = @{
'Node' = $node
'Volume' = $vol
'Name' = $snap.name
'Created' = $snap.created
'DaysOld' = $daysold.days
'TotalSize' = $ftotal
'CumulativeTotal' = $fcumulative
}
$objectD = New-Object -TypeName PSObject -Property $detailprop
$objDetail += $objectD
}
if ($IsVerbose) {
$snaps | Format-Table `
@{Expression = {$node}; Label = "Node name"; Width = 20},`
@{Expression = {$vol}; Label = "Volume"; Width = 40},`
@{Expression = {$_.Name}; Label = "Name"; Width = 150},`
@{Expression = {$_.Created.ToShortDateString()}; Label = "Created"; Width = 12},`
@{Expression = {'{0} Days' -f (Get-DateDiff (Get-Date) $_.created).days}; Label = "Days Old"; Width = 20},`
@{Expression = {ConvertTo-FormattedNumber $_.Total DataSize "0.0"}; Label = "Total"; Width = 15},`
@{Expression = {ConvertTo-FormattedNumber $_.CumulativeTotal DataSize "0.0"}; Label = "Cumulative"; Width = 10} -AutoSize `
| Out-String -Width 1000 | Write-Host
}
}
if ($avgdays -eq $null) { Write-Host "`tNo Snapshots Exist on $vol..." }
}
}
}
}
# Save files
$objSummary | Select-Object Node, Volume, TotalSnaps, TotalDays, AvgPerDay, TotalSize, Oldest | Export-Csv -Path $outsummary -NoTypeInformation
$objDetail | Select-Object Node, Volume, Name, Created, DaysOld, TotalSize, CumulativeTotal | Export-Csv -Path $outdetails -NoTypeInformation
ChatGPT rewrite
I was curious to see what ChatGPT code interpreter would do with this code and this is its output.
<#
.SYNOPSIS
NetApp-Gather Snapshot Information
.DESCRIPTION
Collects snapshot information, summary and detail, including created time,
total snapshots, total days, average per day and oldest snapshot.
.PARAMETER nodes
Controllers to query, one per entry.
.PARAMETER username
Account used to connect, for example domain\username.
.PARAMETER password
Password for the account, as a SecureString.
.PARAMETER IsVerbose
Display snapshot detail for each volume.
.NOTES
Author : Thomas Lasswell (https://www.techcolumnist.com)
Version : 1.2 (2026-09-20), rewritten by ChatGPT
Requires: Data ONTAP PowerShell Toolkit 1.2 or higher
#>
param(
[Parameter(Mandatory = $true)]
[Array]$nodes,
[Parameter(Mandatory = $true)]
[String]$username,
[Parameter(Mandatory = $true, ParameterSetName = 'Secret')]
[Security.SecureString]$password,
[switch]$IsVerbose
)
$exedir = Split-Path -Path $MyInvocation.MyCommand.Definition -Parent
$currentDate = (Get-Date -Format "yyyyMMdd.HHmmss")
$outsummary = -join ($exedir, "\GatherSnapshotInformation_", $currentDate, "_Summary.csv")
$outdetails = -join ($exedir, "\GatherSnapshotInformation_", $currentDate, "_Detail.csv")
Import-Module DataONTAP -ErrorAction SilentlyContinue
try {
$requiredVersion = New-Object System.Version(1.2)
if ((Get-NaToolkitVersion).CompareTo($requiredVersion) -lt 0) { throw "This script requires Data ONTAP PowerShell Toolkit 1.2 or higher." }
} catch {
Write-Host "`nThis script requires Data ONTAP PowerShell Toolkit 1.2 or higher`n" -ForegroundColor Red
return
}
$cred = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList $username, $password
function Get-DateDiff {
param (
[parameter(Mandatory = $true)]
[datetime]$date1,
[parameter(Mandatory = $true)]
[datetime]$date2
)
return [Math]::Abs(($date2 - $date1).Days)
}
$objDetail = @()
$objSummary = @()
foreach ($node in $nodes) {
Write-Host "Connecting to node $node..."
$conn = Connect-NaController -Name $node -HTTPS -Credential $cred
if ($conn) {
Write-Host "Node connected, continuing on to snapshot calculations..."
$vols = Get-NaVol | Where-Object { $_.state -eq "online" -and $_.raidstatus -notmatch "read-only" }
foreach ($vol in $vols) {
$snaps = Get-NaSnapshot -TargetName $vol
$snapsdatecount = $snaps | Group-Object { ((Get-Date) - $_.Created).Days } -NoElement | Sort-Object Name -Descending
$totalavgdays = $snapsdatecount | Measure-Object Count -Average | Select-Object Count, Average
$totalsize = $snaps | Measure-Object Total -Sum | Select-Object Count, Sum
$foldestsnap = $snaps | Sort-Object Created -Descending | Select-Object -Last 1
if ($totalavgdays.Average) {
$summaryprop = @{
'Node' = $node
'Volume' = $vol
'TotalSnaps' = $snaps.length
'TotalDays' = $totalavgdays.Count
'AvgPerDay' = $totalavgdays.Average
'TotalSize' = [System.Math]::Round($totalsize.Sum, 2)
'Oldest' = $foldestsnap.created
}
$objSummary += New-Object -TypeName PSObject -Property $summaryprop
foreach ($snap in $snaps) {
$daysold = Get-DateDiff (Get-Date) $snap.created
$detailprop = @{
'Node' = $node
'Volume' = $vol
'Name' = $snap.name
'Created' = $snap.created
'DaysOld' = $daysold
'TotalSize' = [System.Math]::Round($snap.total, 2)
'CumulativeTotal' = [System.Math]::Round($snap.CumulativeTotal, 2)
}
$objDetail += New-Object -TypeName PSObject -Property $detailprop
}
} else {
Write-Host "`tNo Snapshots Exist on $vol..."
}
}
}
}
$objSummary | Export-Csv -Path $outsummary -NoTypeInformation
$objDetail | Export-Csv -Path $outdetails -NoTypeInformation
Notes
- Output is two CSV files next to the script:
GatherSnapshotInformation_<date>_Summary.csv(one row per volume) andGatherSnapshotInformation_<date>_Detail.csv(one row per snapshot). - The ChatGPT version is a straight rewrite of the original and has not been run against a filer. It is not a drop-in replacement: it drops the
-IsVerbosetable output and formats sizes with[Math]::Roundinstead ofConvertTo-FormattedNumber. - Update 2026-09-20 (v1.2): the CSV file name timestamp was
yyyymmdd.Hm.s, wheremmis minutes, so it never contained the month. It is nowyyyyMMdd.HHmmss. The ChatGPT rewrite also grouped snapshots by$_.Created.Days, which does not exist on a date, so every snapshot landed in one group; it now groups by age in days like the original.