NetAppSSLCertificateRenew.ps1

<#
 
.NOTES
 
File Name: NetAppSSLCertificateRenew.ps1
 
.COMPONENT
 
-NetApp PowerShell Toolkit: https://www.powershellgallery.com/packages/NetApp.ONTAP
 
 
.SYNOPSIS
 
Version:
 
1.6 - Changed the script file name for publishing
      Moved to using current NetApp.ONTAP module
      Simplified connection to cluster
      Execute only on ONTAP 9.x clusters
      Moved deletion of previous certificate to end of creation of new one and configuring of SSL
 
1.5 - Set toolkit import just to import and not check for version (due to new versioning of current releases)
      Added option to check certificates that expire X number of days in the future
      Added a check if there are multiple certificates in the same SVM not to continue if they are of the same common name
      Added more inline comments
     
1.4 - Changed self-signing check and for type of server only to be returned.
 
1.3 - Changed to handle single node clusters.
 
1.2 - Changed to 8.3+ only to better verify that the certificate is self signed and implemented a workaround
      with how the toolkit interacts with 9.x for setting a certificate
 
1.1 - Added prompt for confirming each action
 
1.0 - Original release
 
Known Issues:
 
-A destination SVM DR instance with identity preserve parameter enabled copies the certificate from the source SVM.
 An attempt to recreate the certificate of such a destination SVM will produce a warning that is not possible:
 "This operation is not permitted on a Vserver that is configured as the destination for identity preserve Vserver DR."
 No actual certificate changes are made for this SVM DR destination.
 
-Some single node clusters that have pre-8.3 ONTAP node certificates might not properly cleanup those certificates.
 Those certificates are no longer used by ONTAP and this is a very rare scenario.
 
.DESCRIPTION
 
ONTAP uses self-signed certificates by default for management
of the environment. These certificates have a typical expiration date of 1 year (365 days).
This KB describes the process to recreate the certificates:
https://kb.netapp.com/Advice_and_Troubleshooting/Data_Storage_Software/ONTAP_OS/How_to_renew_a_Self-Signed_SSL_certificate_in_ONTAP_9
 
This script handles the steps outlined in the article by doing the following:
    1) Connecting to a cluster
    2) Collecting all existing certificates
    3) Ensuring the certificate is self-signed
    4) Creates a new certificate with the same properties as the previous one with a 10 year expiration
    5) Configures SSL on the SVM to use the new certificate
    6) Deletes the previous self-signed certificate
 
Special thanks for inspiration from N.E. at:
http://community.netapp.com/t5/OnCommand-Storage-Management-Software-Discussions/Can-t-add-a-cluster/m-p/62376
 
.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. If not provided you will be prompted for the password during the script and it will be obfuscated.
 
.EXAMPLE
 
.\NetAppSSLCertificateRenew.ps1
 
Running without any parameters will prompt for all necessary values
 
.EXAMPLE
 
.\NetAppSSLCertificateRenew.ps11 -Cluster NetApp1 -Username admin -Password MyPassword
 
Connects to the cluster named NetApp1 with the provided credentials.
 
 
#>


<#PSScriptInfo
 
.VERSION 1.6
 
.GUID f1e1fe83-68bb-4c90-88c1-5f671104105e
 
.AUTHOR mcgue
 
.COMPANYNAME
 
.COPYRIGHT
 
.TAGS
 
.LICENSEURI
 
.PROJECTURI
 
.ICONURI
 
.EXTERNALMODULEDEPENDENCIES NetApp.ONTAP
 
.REQUIREDSCRIPTS
 
.EXTERNALSCRIPTDEPENDENCIES
 
.RELEASENOTES
 
.PRIVATEDATA
 
#>


<#
 
.DESCRIPTION
 
ONTAP uses self-signed certificates by default for management
of the environment. These certificates have a typical expiration date of 1 year (365 days).
This KB describes the process to recreate the certificates:
https://kb.netapp.com/Advice_and_Troubleshooting/Data_Storage_Software/ONTAP_OS/How_to_renew_a_Self-Signed_SSL_certificate_in_ONTAP_9
 
This script handles the steps outlined in the article by doing the following:
    1) Connecting to a cluster
    2) Collecting all existing certificates
    3) Ensuring the certificate is self-signed
    4) Creates a new certificate with the same properties as the previous one with a 10 year expiration
    5) Configures SSL on the SVM to use the new certificate
    6) Deletes the previous self-signed certificate
 
#>
 


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

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

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

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

)

#Get today's date
$CurrentDate = Get-Date

#Using 10 years which is the max, change if different value is desired
$NewCertificateExpireDays = 3650

$ProceedCertificateRenewal = $true

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

}

#endregion

#region Main Body

#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"

# 1) Connecting to a 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 cluster information
$ClusterInformation = Get-NcCluster

$Nodes = Get-NcNode

$NumberOfNodes = $Nodes.Length

$NodeInformation = Get-NcNode

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

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

$Found9Plus = $False

$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 in ONTAP 9 since that is the only supported release
If ($FamilyRelease -eq 9) {

    $Found9Plus = $true

}

#Only continue of version of ONTAP is at least 9.0
If ($Found9Plus) {

    #Ask how to proceed
    $RenewNonExpiredCertificatesChoice = $host.ui.PromptForChoice("Renew non-expired certificates?","Please select yes or no",[System.Management.Automation.Host.ChoiceDescription[]]("&Yes","&No"),1)

    Switch ($RenewNonExpiredCertificatesChoice) {

        0 {$RenewNonExpiredCertificates = $true}

        1 {$RenewNonExpiredCertificates = $false}

    }

    #Ask for checking number of days in future for expiration
    If (!$RenewNonExpiredCertificates) {

        $DaysOffset = Read-Host "Enter number of days in the future to check expiration dates (press enter to renew certificates expired through today)"
    
        If ([string]::IsNullOrWhiteSpace($DaysOffset)) {

            $DaysOffset = 0

        }
    
    } else {

        $DaysOffset = 0

    }

    #Additional confirmations along the way
    $ConfirmEachCertificateChoice = $host.ui.PromptForChoice("Confirm each certificate before proceeding?","Please select yes or no",[System.Management.Automation.Host.ChoiceDescription[]]("&Yes","&No"),0)

    Switch ($ConfirmEachCertificateChoice) {

        0 {$ConfirmEachCertificate = $true}

        1 {$ConfirmEachCertificate = $false}

    } 

    # 2) Collecting all existing certificates
    $Certificates = Get-NcSecurityCertificate

    #Process each certificate one by one
    ForEach ($Certificate in $Certificates) {

        Write-Host ""

        Write-Host "*************************************************************************************"

        #Get values
        $CertificateAuthority = $Certificate.CertificateAuthority

        $CommonName = $Certificate.CommonName

        $Country = $Certificate.Country

        $EmailAddress = $Certificate.EmailAddress

        $ExpirationDate = $Certificate.ExpirationDate

        $ExpirationDateDT = $Certificate.ExpirationDateDT

        $ExpirationDateSpecified = $Certificate.ExpirationDateSpecified

        $ExpireDays = $Certificate.ExpireDays

        $ExpireDaysSpecified = $Certificate.ExpireDaysSpecified

        $HashFunction = $Certificate.HashFunction

        $Locality = $Certificate.Locality

        $NcController = $Certificate.NcController

        $Organization = $Certificate.Organization

        $OrganizationUnit = $Certificate.OrganizationUnit

        $Protocol = $Certificate.Protocol

        $PublicCertificate = $Certificate.PublicCertificate

        $SerialNumber = $Certificate.SerialNumber

        $Size = $Certificate.Size

        $StartDate = $Certificate.StartDate

        $StartDateDT = $Certificate.StartDateDT

        $StartDateSpecified = $Certificate.StartDateSpecified

        $State = $Certificate.State

        $Type = $Certificate.Type

        $Vserver = $Certificate.Vserver

        $VserverPlusCertExtension = $Vserver+".cert"

        Write-Host "Working with certificate" $CommonName "on SVM" $Vserver "with serial number" $SerialNumber
    
        # 3) Ensuring the certificate is self-signed
        #Make sure certificate is self-signed by checking the certificate authority is equal to the vserver
        If ($CommonName -eq $CertificateAuthority) {

            $IsSelfSignedCertificate = $true

        } else {

            $IsSelfSignedCertificate = $False

        }

        #Only work with server type and ignore other types
        If ($Type -eq "server") {

            #Must be self signed
            If ($IsSelfSignedCertificate) {

                #Check if this is 8.3+ and if this is a node specific certificate
                If (($NumberOfNodes -ne 1 -and $Nodes -contains $Vserver) -or ($NumberOfNodes -eq 1 -and $Nodes.Node -eq $Vserver)) {

                    Write-Host "In ONTAP" $FullONTAPVersion "node certificates are no longer needed. Reference https://kb.netapp.com/support/index?page=content&id=2024831&locale=en_US&access=s" -ForegroundColor Yellow

                    $ProceedWithNodeCertificateRemovalChoice = $host.ui.PromptForChoice("Proceed with removing of no longer needed node certificate?","Please select yes or no",[System.Management.Automation.Host.ChoiceDescription[]]("&Yes","&No"),0)

                    Switch ($ProceedWithNodeCertificateRemovalChoice) {

                        0 {$ProceedWithNodeCertificateRemoval = $true}

                        1 {$ProceedWithNodeCertificateRemoval = $false}

                    } 

                    If ($ProceedWithNodeCertificateRemoval) {
            
                        #Remove node certificate that is no longer needed
                        $QueryCertificate = Get-NcSecurityCertificate -Template

                        $QueryCertificate = $Certificate

                        $error.clear()
            
                        $RemoveCertificate = Remove-NcSecurityCertificate -Query $QueryCertificate -Confirm:$False -ErrorAction SilentlyContinue

                        If ($RemoveCertificate.SuccessCount -eq 1) {

                            Write-Host "Removed certificate" $CommonName "on SVM" $Vserver -ForegroundColor Green

                        } else {
            
                            Write-Host "Removing certificate" $CommonName "on SVM" $Vserver "has failed. See error below." -ForegroundColor Red

                            write-host $error -ForegroundColor Red

                            Continue

                        }

                    }

                    Continue
                }

                #Check date on certificate for expiration
                If ((($ExpirationDateDT) -le $CurrentDate.AddDays($DaysOffset)) -or $RenewNonExpiredCertificates) {

                    #Ask if required
                    If ($ConfirmEachCertificate) {            

                        $ProceedCertificateRenewalChoice = $host.ui.PromptForChoice("Proceed with recreating certificate $CommonName on SVM $Vserver now?","Please select yes or no",[System.Management.Automation.Host.ChoiceDescription[]]("&Yes","&No"),0)

                        Switch ($ProceedCertificateRenewalChoice) {

                            0 {$ProceedCertificateRenewal = $true}

                            1 {$ProceedCertificateRenewal = $false}

                        }

                    }

                    If ($ProceedCertificateRenewal) {
        
                        #Get SSL configuration for the SVM
                        $CurrentSSLConfiguration = Get-NcSecuritySsl -VserverContext $Vserver

                        $SSLConfigurationCertificateAuthority = $CurrentSSLConfiguration.CertificateAuthority

                        $SSLConfigurationCertificateSerialNumber = $CurrentSSLConfiguration.CertificateSerialNumber

                        $SSLConfigurationClientAuthenticationEnabled = $CurrentSSLConfiguration.ClientAuthenticationEnabled

                        $SSLConfigurationCommonName = $CurrentSSLConfiguration.CommonName

                        $SSLConfigurationNcController = $CurrentSSLConfiguration.NcController

                        $SSLConfigurationServerAuthenticationEnabled = $CurrentSSLConfiguration.ServerAuthenticationEnabled

                        $SSLConfigurationVserver = $CurrentSSLConfiguration.Vserver

                        $SSLConfigurationClientAuthenticationEnabled = $CurrentSSLConfiguration.ClientAuthenticationEnabled

                        $SSLConfigurationServerAuthenticationEnabled = $CurrentSSLConfiguration.ServerAuthenticationEnabled

                        $CommonNameCount = Get-NcSecurityCertificate | Where-Object {$_.CommonName -eq $CommonName}
           
                        If ($CommonNameCount.length -gt 1) {

                            Write-Host "More than one certificate exists with the common name of" $CommonName "on SVM" $Vserver "therefore not proceeding with SSL configuration until only a single certificate exists with that common name." -ForegroundColor Red

                            Continue

                        } 
                        
                        #Unique serial numbers must match between the SVM SSL configuration and the certificate
                        If ($SSLConfigurationCertificateSerialNumber -eq $Certificate.SerialNumber) {  
                        
                            $error.clear()
                            
                            # 4) Creates a new certificate with the same properties as the previous one with a 10 year expiration
                            $CreateCertificate = New-NcSecurityCertificate -CommonName $CommonName -Type $Type -Size $Size `
                                -Country $Country -State $State -Locality $Locality -Organization $Organization `
                                -OrganizationUnit $OrganizationUnit -EmailAddress $EmailAddress -HashFunction $HashFunction `
                                -Vserver $Vserver -ExpireDays $NewCertificateExpireDays -Confirm:$False -ErrorAction SilentlyContinue -ErrorVariable CreateCertificateFailed
                                                        
                            If (!$CreateCertificateFailed) {

                                #When New-NcSecurityCertificate it returns an object containing all certificates that match the common name, and since the previous certificate is not deleted we have to filter out the previous serial number
                                $NewCreateCertificate = $CreateCertificate | Where-Object {$_.SerialNumber -ne $SerialNumber}

                                If ($NewCreateCertificate.count -eq 1) {
                                
                                    #Get variables to configure SVM SSL later
                                    $RecreatedCertificateSerialNumber = $NewCreateCertificate.SerialNumber.ToString()
                                    
                                    $RecreatedCertificateCommonName =  $NewCreateCertificate.CommonName.ToString()
                                    
                                    $RecreatedCertificateCertificateAuthority = $NewCreateCertificate.CertificateAuthority.ToString()
                                    
                                    Write-Host "Recreated certificate" $CommonName "on SVM" $Vserver "with new expiration date of" $CreateCertificate.ExpirationDateDT -ForegroundColor Green
                                
                                    } else {
            
                                        Write-Host "More than one certificate exists with the common name of" $CommonName "on SVM" $Vserver "therefore not proceeding with SSL configuration until only a single certificate exists with that common name." -ForegroundColor Red

                                        Continue

                                    }
                                    
                            } else {
            
                                Write-Host "Recreating certificate" $CommonName "on SVM" $Vserver "has failed. See error below." -ForegroundColor Red

                                write-host $error -ForegroundColor Red

                                Continue

                            }

                            $error.clear()

                            # 5) Configures SSL on the SVM to use the new certificate

                            #workaround for bug in PowerShell toolkit specific to the Set-NcSecuritySsl cmdlet
                            $APIRequest = "<security-ssl-modify> `
                                        <vserver>$SSLConfigurationVserver</vserver> `
                                        <certificate-authority>$RecreatedCertificateCertificateAuthority</certificate-authority> `
                                        <common-name>$RecreatedCertificateCommonName</common-name> `
                                        <server-authentication-enabled>$SSLConfigurationServerAuthenticationEnabled</server-authentication-enabled> `
                                        <client-authentication-enabled>$SSLConfigurationClientAuthenticationEnabled</client-authentication-enabled> `
                                        <certificate-serial-number>$RecreatedCertificateSerialNumber</certificate-serial-number> `
                                        </security-ssl-modify>"


                            $ConfiguredSSL = Invoke-NcSystemApi -VserverContext $SSLConfigurationVserver `
                                    -Request $APIRequest -ErrorAction SilentlyContinue -ErrorVariable ConfiguredSSLFailed
                        
                            If (!$ConfiguredSSLFailed) {

                                Write-Host "Reconfigured SSL on SVM" $Vserver "to work with recreated certificate" $RecreatedCertificate.CommonName -ForegroundColor Green

                            } else {
            
                                Write-Host "Reconfiguring SSL on SVM" $Vserver "to work with recreated certificate" $RecreatedCertificate.CommonName  "has failed. See error below." -ForegroundColor Red

                                write-host $error -ForegroundColor Red

                                Continue

                            }  

                            # 6) Deletes the previous self-signed certificate

                            $error.clear()

                            $RemoveCertificate = Remove-NcSecurityCertificate -Query $Certificate -Confirm:$False -ErrorAction SilentlyContinue 

                            If ($RemoveCertificate.SuccessCount -eq 1) {

                                Write-Host "Removed certificate" $CommonName "on SVM" $Vserver -ForegroundColor Green

                            } else {
            
                                Write-Host "Removing certificate" $CommonName "on SVM" $Vserver "has failed. See error below." -ForegroundColor Red

                                write-host $error -ForegroundColor Red

                                #Continue

                            }

                        } else {

                            Write-Host "This certificate is currently not in use for SSL configuration. Not recreating." -ForegroundColor Yellow

                        }
                
                    } else {
            
                        Write-Host "Not processing certificate" $CommonName "on SVM" $Vserver "based upon choice." -ForegroundColor Yellow

                    }

                } else {

                    Write-Host "Certificate" $CommonName "has not expired and will not be renewed." 

                }

            } else {

                Write-Host "Certificate" $CommonName "is not a self-signed certificate and must be renewed through your external certificate authority." -ForegroundColor Yellow
                
            }

        } else {

            Write-Host "Certificate" $CommonName "is not a server type certificate and does not need to be processed."            

        }

    }

} else {

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

}

#endregion