Powershell: Gather NetApp Snapshot Details

Problem:

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.

Solution:

A powershell script using the Data ONTAP PS Library. Read more for the script.

.SYNOPSIS
NetApp-Gather Snapshot Information

.DESCRIPTION
This script will collect snapshot information, summary and detail
Including Created Time, Total Snaps, Total Days, Avg Per Day, Oldest, etc

.EXAMPLE
PS S:\102 – Scripts> & ‘.\NetApp-Gather Snapshot Information v1.1.ps1’ -nodes filer1.company.biz,filer2.company.biz -username “domain\username” -IsVerbose

.EXAMPLE
PS S:\102 – Scripts> & ‘.\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: *************

.PARAMETER (IsVerbose)
Use this parameter to display snap detail ( & ‘.\NetApp-Gather Snapshot Information v1.1.ps1’ -IsVerbose )

.NOTES
Written by Thomas Lasswell, TechColumnist
Version 1.1

# #############################################################################<br>
# iVision - SCRIPT - POWERSHELL - NETAPP 7-MODE COMMANDS<br>
# NAME: NetApp-Gather Snapshot Information<br>
#<br>
# AUTHOR:  Thomas Lasswell, TechColumnist<br>
# DATE:  2014/01/21<br>
# EMAIL: lasswellt at TechColumnist.com<br>
#<br>
# COMMENT:  This script will collect snapshot information, summary and detail<br>
#			Including Created Time, Total Snaps, Total Days, Avg Per Day, Oldest, etc<br>
#<br>
# VERSION HISTORY<br>
# 1.0 2014.01.17 Initial Version.<br>
# 1.1 2014.01.21 Added CSV export, days old column fixed<br>
#<br>
# TO ADD<br>
#<br>
# #############################################################################</p>
<p>&amp;lt;#<br>
.SYNOPSIS<br>
	NetApp-Gather Snapshot Information</p>
<p>.DESCRIPTION<br>
	This script will collect snapshot information, summary and detail<br>
	Including Created Time, Total Snaps, Total Days, Avg Per Day, Oldest, etc</p>
<p>.EXAMPLE<br>
PS S:\102 - Scripts&amp;gt; &amp;amp; '.\NetApp-Gather Snapshot Information v1.1.ps1' -nodes filer1.company.biz,filer2.company.biz -username &amp;quot;domain\username&amp;quot; -IsVerbose</p>
<p>.EXAMPLE<br>
PS S:\102 - Scripts&amp;gt; &amp;amp; '.\NetApp-Gather Snapshot Information v1.1.ps1'</p>
<p>cmdlet NetApp-Gather Snapshot Information v1.1.ps1 at command pipeline position 1<br>
Supply values for the following parameters:<br>
nodes[0]: filer1.company.biz<br>
nodes[1]: filer2.company.biz<br>
nodes[2]:<br>
username: domain\username<br>
password: *************</p>
<p>.PARAMETER (IsVerbose)<br>
	Use this parameter to display snap detail ( &amp;amp; '.\NetApp-Gather Snapshot Information v1.1.ps1' -IsVerbose )</p>
<p>.NOTES<br>
Written by Thomas Lasswell, iVision<br>
Version 1.1</p>
<p>.LINK</p>
<p>#&amp;gt;<br>
#Get Parameters -- nodes is each node, when finished, hit enter and it will continue<br>
Param (<br>
    [Parameter(Mandatory=$True)]<br>
    [Array]$nodes,<br>
    [Parameter(Mandatory=$True)]<br>
    [String]$username,<br>
    [Parameter(Mandatory=$True,ParameterSetName = 'Secret')]<br>
    [Security.SecureString]$password,<br>
	[switch]$IsVerbose<br>
)<br>
#create outfile information<br>
$exedir= Split-Path -Path $MyInvocation.MyCommand.Definition -Parent<br>
$currentDate= (get-date -format yyyymmdd.Hm.s)<br>
$outsummary= ($exedir + &amp;quot;\&amp;quot; + &amp;quot;GatherSnapshotInformation_&amp;quot; + $currentDate + &amp;quot;_Summary.csv&amp;quot;)<br>
$outdetails= ($exedir + &amp;quot;\&amp;quot; + &amp;quot;GatherSnapshotInformation_&amp;quot; + $currentDate + &amp;quot;_Detail.csv&amp;quot;)</p>
<p>#Load ONTAP PowerShell Toolkit<br>
$module = Get-Module DataONTAP<br>
if ($module -EQ $NULL)<br>
{<br>
	Import-Module DataONTAP<br>
}</p>
<p>try<br>
{<br>
    $requiredVersion = New-Object System.Version(1.2)<br>
    if ((Get-NaToolkitVersion).CompareTo($requiredVersion) -LT 0) { throw }<br>
}<br>
catch [Exception]<br>
{<br>
    Write-Host &amp;quot;`nThis script requires Data ONTAP PowerShell Toolkit 1.2 or higher`n&amp;quot; -ForegroundColor Red<br>
    return<br>
}<br>
$cred = new-object -typename System.Management.Automation.PSCredential -argumentlist $username, $password</p>
<p>#function to calculate difference in dates<br>
function Get-DateDiff {<br>
	param (<br>
		[CmdletBinding()]<br>
		[parameter(Mandatory=$true)]<br>
		[datetime]$date1,<br>
		[parameter(Mandatory=$true)]<br>
		[datetime]$date2<br>
	)<br>
		if ($date2 -gt $date1){$diff = $date2 - $date1}<br>
		else {$diff = $date1 - $date2}<br>
		$diff<br>
}<br>
#declare object arrays<br>
$objDetail = @()<br>
$objSummary = @()</p>
<p>#connect to each node individually<br>
foreach ($node in $nodes){<br>
	Write-Host &amp;quot;connecting to node $node...&amp;quot;<br>
	$conn = Connect-NaController -name $node -HTTPS -Credential $cred<br>
	$snapinfoS = @()<br>
	$snapinfoD = @()</p>
<p>	if ( $conn -ne $null ) {<br>
		Write-host &amp;quot;node connected, continuing on to snapshot calculations...&amp;quot;<br>
		Write-host &amp;quot;gathering node volumes...&amp;quot;<br>
		$vols =  Get-NaVol | ? {$_.state -eq &amp;quot;online&amp;quot; -and $_.raidstatus -notmatch &amp;quot;read-only&amp;quot;}<br>
		if ( $vols -ne $null ) {<br>
			foreach ($vol in $vols) {<br>
				Write-host &amp;quot;gathering snapshots for volume $vol...&amp;quot;<br>
				#get snapshots<br>
				$snaps= Get-NaSnapshot -targetname $vol</p>
<p>				#group to count snapshots per day<br>
				$snapsdatecount= $snaps | Group { ((Get-date) - $_.Created).Days } -NoElement | Sort Name -Descending<br>
				#measure to get count and average, average is average snapshots per day<br>
				$totalavgdays= $snapsdatecount | Measure-Object Count -ave | Select Count,Average<br>
				$avgdays= $totalavgdays.Average<br>
				$totaldays= $totalavgdays.Count</p>
<p>				#calculate sum of snapshots for total snapshot consumption<br>
				$totalsize= $snaps | Measure-Object Total -Sum | Select Count,Sum<br>
				$ftotalsize= ConvertTo-FormattedNumber $totalsize.sum DataSize &amp;quot;0.0&amp;quot;</p>
<p>				#get oldest snapshot<br>
				$foldestsnap= $snaps | Sort Created -Descending | Select-Object -Last 1</p>
<p>				#build array for summary<br>
				if ($avgdays -ne $null){<br>
					$summaryprop = @{'Node'=$node;<br>
									'Volume'=$vol;<br>
									'TotalSnaps'=$snaps.length;<br>
									'TotalDays'=$totaldays;<br>
									'AvgPerDay'=$avgdays;<br>
									'TotalSize'=$ftotalsize;<br>
									'Oldest'=$foldestsnap.created}<br>
					$objectS = New-Object -TypeName PSObject -Prop $summaryprop<br>
					$objSummary += $objectS<br>
					$objectS</p>
<p>					foreach ($snap in $snaps){<br>
						$daysold= Get-DateDiff (get-date) $snap.created;<br>
						$ftotal= ConvertTo-FormattedNumber $snap.total DataSize &amp;quot;0.0&amp;quot;;<br>
						$fcumulative= ConvertTo-FormattedNumber $snap.CumulativeTotal DataSize &amp;quot;0.0&amp;quot;;</p>
<p>						#build array for details<br>
						$detailprop = @{'Node'=$node;<br>
										'Volume'=$vol;<br>
										'Name'=$snap.name;<br>
										'Created'=$snap.created;<br>
										'DaysOld'=$daysold.days;<br>
										'TotalSize'=$ftotal;<br>
										'CumulativeTotal'=$fcumulative}<br>
						$objectD = New-Object -TypeName PSObject -Prop $detailprop<br>
						$objDetail += $objectD<br>
					}<br>
					if ($IsVerbose){<br>
						$snaps | ft `<br>
							@{Expression={$node};Label=&amp;quot;Node name&amp;quot;;Width=20},`<br>
							@{Expression={$vol};Label=&amp;quot;Volume&amp;quot;;Width=40},`<br>
							@{Expression={$_.Name};Label=&amp;quot;Name&amp;quot;;Width=150},`<br>
							@{Expression={$_.Created.ToShortDateString()};Label=&amp;quot;Created&amp;quot;;Width=12},`<br>
							@{E = {'{0} Days' -f (Get-DateDiff (get-date) $_.created).days};L=&amp;quot;Days Old&amp;quot;;W=20},`<br>
							@{Expression={ConvertTo-FormattedNumber $_.Total DataSize &amp;quot;0.0&amp;quot;};Label=&amp;quot;Total&amp;quot;;Width=15},`<br>
							@{Expression={ConvertTo-FormattedNumber $_.CumulativeTotal DataSize &amp;quot;0.0&amp;quot;};Label=&amp;quot;Cumulative&amp;quot;;Width=10} -autosize `<br>
							|  Out-String -Width 1000 | Write-Host<br>
					}<br>
				}<br>
				if ($avgdays -eq $null){ Write-Host &amp;quot;`tNo Snapshots Exist on $vol...&amp;quot;}<br>
			}<br>
		}<br>
	}<br>
}</p>
<p>#save files<br>
$objSummary | Select Node, Volume, TotalSnaps, TotalDays, AvgPerDay, TotalSize, Oldest | Export-CSV -Path $outsummary -NoTypeInformation<br>
$objDetail | Select Node, Volume, Name, Created, DaysOld, TotalSize, CumulativeTotal | Export-CSV -Path $outdetails -NoTypeInformation<br>

I was curious to see what ChatGPT code interpreter would do with this code and this is it’s output.

<#
.SYNOPSIS
NetApp-Gather Snapshot Information

.DESCRIPTION
This script collects snapshot information, summary, and detail.
Including Created Time, Total Snaps, Total Days, Avg Per Day, Oldest, etc.

.NOTES
Written by Thomas Lasswell, TechColumnist
Version 1.1
#>

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.Hms")
$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 { ($_.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

TomLasswell

Share
Published by
TomLasswell

Recent Posts

Autotask: PowerShell: Enable Client Portal for all users

This is a quick one, it's been forever since I've posted here. After moving back…

2 years ago

PowerShell :: Get Exchange Mailboxes Over XXGB

Simple command turned crazy. I ended up coming up with this due to the fact…

3 years ago

PowerShell: ConnectWise Documents API, Uploading a document or attachment to a ticket

Phew, this one took a minute to figure out. ConnectWise has a form based documents…

5 years ago

PowerShell: ConnectWise REST API Query Contacts by Email Address

I've found myself at a new job, recreating many of the processes that I spent…

5 years ago

First post in a long time — changing hosting providers

Wow, it's been a while since I've done a real post on this site. I've…

6 years ago

Powershell: AutoTask – Get Picklist Values

When using AutoTask's API it's required to lookup a various amount of picklist values that…

9 years ago