NetAppEfficiencyCheck.ps1

<#
 
.NOTES
 
File name: NetAppEfficiencyCheck.ps1
 
 
.COMPONENT
 
-NetApp PowerShell Toolkit: https://www.powershellgallery.com/packages/NetApp.ONTAP
 
-ImportExcel module: https://www.powershellgallery.com/packages/ImportExcel
 
 
.SYNOPSIS
 
Version:
 
1.0 original release
 
.DESCRIPTION
 
This script will check for the following for a NetApp ONTAP 9.5 or newer cluster.
 
Volume Information:
-Aggregate: Must reside on an AFF model.
-Thin Provisioned: Volume space guarantee set to none and the space guarantee is enabled.
-Encryption at Aggregate Level or Off: Either encryption is disabled or if enabled the volume is set to aggregate encryption-type
-Efficiency Enabled: Volume efficiency is on.
-Data Compaction Enabled
-Compression Enabled
-Inline Compression Enabled
-Inline Dedupe Enabled
-Cross Volume Background Dedupe Enabled
-Cross Volume Inline Dedupe Enabled
-Schedule Blank: The volume efficiency schedule is set to "-"
-Auto Policy: The volume efficiency policy is set to "auto"
-Adaptive Compression Type: The volume efficiency compression-type is set to adaptive.
-Is Prioritized: The volume has not been deprioritized with the auto policy configuration.
-Compression Algorithm: Current algorithm in use for the volume compression.
 
Aggregate Information:
-Name: The name of the aggregate
-Node: The node where the aggregate resides
-Aggregate Data Reduction Storage Efficiency Ratio: Displays the storage efficiency ratio of the aggregate
-Volume Data Reduction Storage Efficiency Ratio: Displays the storage efficiency ratio of all the volumes in the aggregate
-Total Data Reduction Storage Efficiency Ratio: Displays the total storage efficiency ratio of the aggregate
-Used Space in TiB: Actual used space measured in TiB
-Total Space in TiB: Total space of aggregate measured in TiB
-Effective Capacity of Used Space Multiplied by Total Data Reduction SE Ration in TiB: The "Used Space in TiB" multiplied by the first number in the "Total Data Reduction Storage Efficiency Ratio" field.
 
 
.PARAMETER Cluster
The cluster management LIF IP address or resolvable DNS name for the cluster to connect to.
 
.PARAMETER Username
Username to use to connect to the cluster.
 
.PARAMETER Password
Password used to connect to the cluster. This is in clear text. Unavailable if the Username parameter is not passed. If this variable is not provided you will be prompted for the password during the script and it will be obfuscated.
 
.PARAMETER VerboseOutput
See full details of the script action in addition to the Excel file output.
 
 
.EXAMPLE
 
.\NetAppEfficiencyCheck.ps1
 
Running without any parameters will prompt for all necessary values
 
.EXAMPLE
 
.\NetAppEfficiencyCheck.ps1 -Cluster NetApp1 -Username admin -Password MyPassword
 
Connects to the cluster named NetApp1 with the provided credentials.
 
 
#>





<#PSScriptInfo
 
.VERSION 1.0
 
.GUID b1d582ad-fa69-4cc0-99c1-548acfc3e577
 
.AUTHOR Tim McGue
 
.COMPANYNAME
 
.COPYRIGHT
 
.TAGS
 
.LICENSEURI
 
.PROJECTURI
 
.ICONURI
 
.EXTERNALMODULEDEPENDENCIES NetApp.ONTAP,ImportExcel
 
.REQUIREDSCRIPTS
 
.EXTERNALSCRIPTDEPENDENCIES
 
.RELEASENOTES
 
 
.PRIVATEDATA
 
#>


<#
 
.DESCRIPTION
 
This script will check for the following for a NetApp ONTAP 9.5 or newer cluster.
 
Volume Information:
-Aggregate: Must reside on an AFF model.
-Thin Provisioned: Volume space guarantee set to none and the space guarantee is enabled.
-Encryption at Aggregate Level or Off: Either encryption is disabled or if enabled the volume is set to aggregate encryption-type
-Efficiency Enabled: Volume efficiency is on.
-Data Compaction Enabled
-Compression Enabled
-Inline Compression Enabled
-Inline Dedupe Enabled
-Cross Volume Background Dedupe Enabled
-Cross Volume Inline Dedupe Enabled
-Schedule Blank: The volume efficiency schedule is set to "-"
-Auto Policy: The volume efficiency policy is set to "auto"
-Adaptive Compression Type: The volume efficiency compression-type is set to adaptive.
-Is Prioritized: The volume has not been deprioritized with the auto policy configuration.
-Compression Algorithm: Current algorithm in use for the volume compression.
 
Aggregate Information:
-Name: The name of the aggregate
-Node: The node where the aggregate resides
-Aggregate Data Reduction Storage Efficiency Ratio: Displays the storage efficiency ratio of the aggregate
-Volume Data Reduction Storage Efficiency Ratio: Displays the storage efficiency ratio of all the volumes in the aggregate
-Total Data Reduction Storage Efficiency Ratio: Displays the total storage efficiency ratio of the aggregate
-Used Space in TiB: Actual used space measured in TiB
-Total Space in TiB: Total space of aggregate measured in TiB
-Effective Capacity of Used Space Multiplied by Total Data Reduction SE Ration in TiB: The "Used Space in TiB" multiplied by the first number in the "Total Data Reduction Storage Efficiency Ratio" field.
 
#>
 



#region Parameters and Variables
[CmdletBinding(PositionalBinding=$False)]
Param(

  [Parameter(Mandatory=$False)]
   [string]$Cluster,

  [Parameter(Mandatory=$False)]
   [string]$Username,

  [Parameter(Mandatory=$False)]
   [string]$Password,

  [Parameter(Mandatory=$False)]
   [switch]$VerboseOutput

)

#Import modules
If (-Not (Get-Module NetApp.ONTAP)) {
        
    Import-Module NetApp.ONTAP

}

If (-Not (Get-Module ImportExcel)) {
        
    Import-Module ImportExcel

}

#endregion

#region Main Body
If (!$VerboseOutput) {

    $VerboseChoice = $host.ui.PromptForChoice("Do you wish to see verbose output?","Please select full or summary",[System.Management.Automation.Host.ChoiceDescription[]]("&Full","&Summary"),1)

    Switch ($VerboseChoice) {

        0 {$VerboseOutputSelection = $true}

        1 {$VerboseOutputSelection = $false}

    }

} else {

    $VerboseOutputSelection = $true

}

#Connect to the cluster
If ($Cluster.Length -eq 0) {

    $Cluster = Read-host "Enter the cluster management LIF DNS name or IP address"

}

$Cluster = $Cluster.Trim()

If ($Username.Length -eq 0) {

    $Username = ""

}

If ($Password.Length -eq 0) {

    $SecurePassword = ""

} else {

    $SecurePassword = New-Object -TypeName System.Security.SecureString

    $Password.ToCharArray() | ForEach-Object {$SecurePassword.AppendChar($_)}

}

#Preparing credential object to pass
If ($Username.Length -ne 0 -and $SecurePassword.Length -ne 0) {

    $Credentials = new-object -typename System.Management.Automation.PSCredential -argumentlist $Username, $SecurePassword

} else {

    $Credentials = $Username

}

Write-Host "Attempting connection to $Cluster"

$ClusterConnection = Connect-NcController -name $Cluster -Credential $Credentials

#Only proceeding with valid connection to cluster
If (!$ClusterConnection) {

    Write-Host "Unable to connect to NetApp cluster, please ensure all supplied information is correct and try again" -ForegroundColor Yellow

    Exit        

}

#Get basic information
$Nodes = Get-NcNode

$NumberOfNodes = $Nodes.Length

$Vservers = Get-NcVserver

$DataVservers = Get-NcVserver | Where-Object {$_.VserverType -eq "data"}

$ONTAPIFullVersion = Get-NcSystemOntapiVersion

$ClusterInformation = Get-NcCluster

$NodeInformation = Get-NcNode

$ExecutionDate = Get-Date

If ($VerboseOutputSelection) {

    Write-Host "Execution date and time:" $ExecutionDate

    Write-Host "Working with cluster:" $ClusterInformation.ClusterName

    Write-Host "With serial number of:" $ClusterInformation.ClusterSerialNumber

    Write-Host "Which contains the following Nodes:" $NodeInformation.Node

    Write-Host "With serial numbers of:" $NodeInformation.NodeSerialNumber

    Write-Host ""

}

$DateStamp = get-date -uformat "%Y-%m-%d@%H-%M-%S"
                
$Global:OutputFile = $ClusterInformation.ClusterName + "_" + $DateStamp + ".xlsx"

$OntapVersonInfo = Get-NcSystemVersionInfo

$FullOTAPVersionDetails = $OntapVersonInfo.Version

$FullOTAPVersionDetails = $FullOTAPVersionDetails.ToString()

$FullONTAPVersion = $OntapVersonInfo.VersionTuple

Write-Host "ONTAP version:" $FullONTAPVersion

$SegmentOntapVersion = $FullONTAPVersion -split '\.'

$FamilyRelease = $SegmentOntapVersion[0]

$MajorRelease = $SegmentOntapVersion[1]

$MinorRelease = $SegmentOntapVersion[2]

#Only run on ONTAP 9.5 or greater
If ($FamilyRelease -eq 9 -and $MajorRelease -ge 5) {

    #Separate AFF and FAS models
    $CheckNodeModels = Get-NcNodeInfo

    $AFFNodesArray = @()

    $FASNodesArray = @()

    ForEach ($CheckNodeModel in $CheckNodeModels) {

        If ($CheckNodeModel.SystemModel.contains("AFF")) {

            If ($VerboseOutputSelection) {

                Write-Host "Node" $CheckNodeModel.SystemName "is an AFF"

            }

            $AFFNodesArray += $CheckNodeModel.SystemName

        } else {

            If ($VerboseOutputSelection) {

                Write-Host "Node" $CheckNodeModel.SystemName "is a FAS"

            }

            $FASNodesArray += $CheckNodeModel.SystemName

        }

    }

    #Get all the data aggregates of the AFF nodes
    $AFFAggregates = Get-NcAggr | Where-Object {$_.AggrRaidAttributes.HasLocalRoot -eq $false -and $AFFNodesArray -contains $_.Nodes}

    #Find all read/write, non-SVM root volumes within the AFF aggregates
    $AFFVolumes = Get-NcVol | Where-Object {$AFFAggregates.AggregateName -contains $_.Aggregate -and $_.VolumeStateAttributes.IsVserverRoot -eq $False -and $_.VolumeIdAttributes.Type -eq "rw"}

    #Data to export to Excel
    $FullCollectedOutput = @()

    #Volume information
    ForEach ($AFFVolume in $AFFVolumes) {

        #Prepare data to send to Excel
        $PowerShellObject = New-Object PSCustomObject

        $Vserver = $AFFVolume.vserver

        $VolumeName = $AFFVolume.name

        If ($VerboseOutputSelection) {

            Write-Host "Working with volume" $VolumeName "on SVM" $Vserver

        }

        $PowerShellObject | Add-Member -type NoteProperty -name "Volume" -value $VolumeName

        $PowerShellObject | Add-Member -type NoteProperty -name "SVM" -value $Vserver

        $PowerShellObject | Add-Member -type NoteProperty -name "Aggregate" -value $AFFVolume.Aggregate

        If ($AFFVolume.VolumeSpaceAttributes.SpaceGuarantee -eq "none" -and $AFFVolume.VolumeSpaceAttributes.IsSpaceGuaranteeEnabled -eq $true) {

            $PowerShellObject | Add-Member -type NoteProperty -name "Thin Provisioned" -value $true

        } else {

            $PowerShellObject | Add-Member -type NoteProperty -name "Thin Provisioned" -value $false

        }

        $SSHCommand = "volume show -fields encryption-type -vserver $Vserver -volume $VolumeName"    
      
        $EncryptionType = Invoke-NcSsh $SSHCommand  
    
        $EncryptionType = $EncryptionType.ToString()

        $EncryptionType = $EncryptionType.Split("`n")

        $EncryptionType = $EncryptionType[4].ToString() #removes header rows
    
        If ($EncryptionType.Contains("aggregate") -or $EncryptionType.Contains("none")) {

            $PowerShellObject | Add-Member -type NoteProperty -name "Encryption at Aggregate Level or Off" -value $true

        } else {

            $PowerShellObject | Add-Member -type NoteProperty -name "Encryption at Aggregate Level or Off" -value $false

        }

        #Get efficiency information for all matching AFF volumes
        $AFFVolumeEfficiency = $AFFVolume | Get-NcSis

        If ($AFFVolumeEfficiency.State -eq "Enabled") {

            $PowerShellObject | Add-Member -type NoteProperty -name "Efficiency Enabled" -value $true

        } else {

            $PowerShellObject | Add-Member -type NoteProperty -name "Efficiency Enabled" -value $False

        }

        $PowerShellObject | Add-Member -type NoteProperty -name "Data Compaction Enabled" -value $AFFVolumeEfficiency.IsDataCompactionEnabled

        $PowerShellObject | Add-Member -type NoteProperty -name "Compression Enabled" -value $AFFVolumeEfficiency.IsCompressionEnabled
    
        $PowerShellObject | Add-Member -type NoteProperty -name "Inline Compression Enabled" -value $AFFVolumeEfficiency.IsInlineCompressionEnabled

        $PowerShellObject | Add-Member -type NoteProperty -name "Inline Dedupe Enabled" -value $AFFVolumeEfficiency.IsInlineDedupeEnabled

        $PowerShellObject | Add-Member -type NoteProperty -name "Cross Volume Background Dedupe Enabled" -value $AFFVolumeEfficiency.IsCrossVolumeBackgroundDedupeEnabled

        $PowerShellObject | Add-Member -type NoteProperty -name "Cross Volume Inline Dedupe Enabled" -value $AFFVolumeEfficiency.IsCrossVolumeInlineDedupeEnabled


        If ($AFFVolumeEfficiency.Schedule -eq "-") {

            $PowerShellObject | Add-Member -type NoteProperty -name "Schedule Blank" -value  $true

        } else {

            $PowerShellObject | Add-Member -type NoteProperty -name "Schedule Blank" -value  $False

        }


        If ($AFFVolumeEfficiency.Policy -eq "auto") {

            $PowerShellObject | Add-Member -type NoteProperty -name "Auto Policy" -value  $true

        } else {

            $PowerShellObject | Add-Member -type NoteProperty -name "Auto Policy" -value  $False

        }

        If ($AFFVolumeEfficiency.CompressionType -eq "adaptive") {

            $PowerShellObject | Add-Member -type NoteProperty -name "Adaptive Compression Type" -value $true

        } else {

            $PowerShellObject | Add-Member -type NoteProperty -name "Adaptive Compression Type" -value $False

        }

        $SSHCommand = "set -privilege diagnostic -confirmations off;volume efficiency show -fields auto-state -vserver $Vserver -volume $VolumeName;set -privilege admin -confirmations on"    
    
        $DeprioritizedVolume = Invoke-NcSsh $SSHCommand  

        $DeprioritizedVolume = $DeprioritizedVolume.ToString()

        $DeprioritizedVolume = $DeprioritizedVolume.Split("`n")

        $DeprioritizedVolume = $DeprioritizedVolume[4].ToString() #removes header rows

        If ($DeprioritizedVolume.Contains("Deprioritized")) {

            $PowerShellObject | Add-Member -type NoteProperty -name "Is Prioritized" -value $False

        } else {

            $PowerShellObject | Add-Member -type NoteProperty -name "Is Prioritized" -value $true

        }

        #TODO - this is for the corresponding support bulletin which has been fixed in all recommended releases: https://kb.netapp.com/Support_Bulletins/Customer_Bulletins/SU443
        $PowerShellObject | Add-Member -type NoteProperty -name "Compression Algorithm" -value $AFFVolumeEfficiency.CompressionAlgorithm

        $FullCollectedOutput += $PowerShellObject

        If ($VerboseOutputSelection) {

            Write-Host ($PowerShellObject | Format-List | Out-String)

            Write-Host

        }

    }

    $WorksheetName = "AFF Volume Efficiency Settings"

    $WorksheetStyle = New-ExcelStyle -BorderAround Thin -BorderBottom Thin -BorderTop Thin -BorderLeft Thin -BorderRight Thin

    $RangeTrueFalse = "D:P"

    #Highlight anything that is false
    $FullCollectedOutput | Export-Excel -Path $Global:OutputFile -WorkSheetname $WorksheetName -BoldTopRow -AutoSize -FreezeTopRow -FreezeFirstColumn -AutoFilter -Numberformat "#,##0" -Style $WorksheetStyle -ConditionalText $(
            New-ConditionalText -Range $RangeTrueFalse -ConditionalType ContainsText FALSE -ConditionalTextColor black -BackgroundColor red) 

    $FullCollectedOutput = @()

    #Aggregate information
    foreach ($AFFAggregate in $AFFAggregates) {

        #Prepare data to send to Excel
        $PowerShellObject = New-Object PSCustomObject

        $PowerShellObject | Add-Member -type NoteProperty -name "Name" -value $AFFAggregate.AggregateName

        $PowerShellObject | Add-Member -type NoteProperty -name "Node" -value $AFFAggregate.AggrOwnershipAttributes.HomeName

        $AFFAggregatesEfficiency =  $AFFAggregate | Get-NcAggrEfficiency

        $PowerShellObject | Add-Member -type NoteProperty -name "Aggregate Data Reduction Storage Efficiency Ratio" -value $AFFAggregatesEfficiency.AggrEfficiencyAggrInfo.AggrDataReductionStorageEfficiencyRatio

        $PowerShellObject | Add-Member -type NoteProperty -name "Volume Data Reduction Storage Efficiency Ratio" -value $AFFAggregatesEfficiency.AggrEfficiencyVolumeInfo.VolumeDataReductionStorageEfficiencyRatio

        $PowerShellObject | Add-Member -type NoteProperty -name "Total Data Reduction Storage Efficiency Ratio" -value $AFFAggregatesEfficiency.AggrEfficiencyCumulativeInfo.TotalStorageEfficiencyRatio

        $PowerShellObject | Add-Member -type NoteProperty -name "Used Space in TiB" -value (($AFFAggregate.AggrSpaceAttributes.SizeUsed)/1024/1024/1024/1024)

        $PowerShellObject | Add-Member -type NoteProperty -name "Total Space in TiB" -value (($AFFAggregate.AggrSpaceAttributes.SizeTotal)/1024/1024/1024/1024)

        $TotalRatioMultiplier = $AFFAggregatesEfficiency.AggrEfficiencyCumulativeInfo.TotalStorageEfficiencyRatio.Substring(0,$AFFAggregatesEfficiency.AggrEfficiencyCumulativeInfo.TotalStorageEfficiencyRatio.IndexOf(":"))

        $TotalRatioMultiplier = [decimal]$TotalRatioMultiplier

        $PowerShellObject | Add-Member -type NoteProperty -name "Effective Capacity of Used Space Multiplied by Total Data Reduction SE Ration in TiB" -value ((($AFFAggregate.AggrSpaceAttributes.SizeTotal)/1024/1024/1024/1024)*$TotalRatioMultiplier)

        $FullCollectedOutput += $PowerShellObject
        
    }

    $WorksheetName = "AFF Aggregate Efficiency Info"

    $WorksheetStyle = New-ExcelStyle -BorderAround Thin -BorderBottom Thin -BorderTop Thin -BorderLeft Thin -BorderRight Thin

    $FullCollectedOutput | Export-Excel -Path $Global:OutputFile -WorkSheetname $WorksheetName -BoldTopRow -AutoSize -FreezeTopRow -FreezeFirstColumn -AutoFilter -Numberformat "#,##0.0000" -Style $WorksheetStyle

    Write-Host

    Write-Host "See output in file:" $Global:OutputFile

} else {

       Write-Host "This script is only available for ONTAP 9.5+ systems." -ForegroundColor Red

}