From 72dd6619017059dde9487e677a4c9b57a9188eac Mon Sep 17 00:00:00 2001 From: Adam Rudell Date: Thu, 4 Apr 2024 11:47:56 -0500 Subject: [PATCH 1/9] update cert functions and rotate node certs --- .../public/Get-SdnCertificate.ps1 | 29 ++++- .../public/Import-SdnCertificate.ps1 | 112 +++++++++--------- ...ew-SdnNetworkControllerNodeCertificate.ps1 | 12 +- ...ew-SdnNetworkControllerRestCertificate.ps1 | 12 +- .../public/New-SdnServerCertificate.ps1 | 14 ++- 5 files changed, 104 insertions(+), 75 deletions(-) diff --git a/src/modules/SdnDiag.Common/public/Get-SdnCertificate.ps1 b/src/modules/SdnDiag.Common/public/Get-SdnCertificate.ps1 index e95e9c2c..946cc030 100644 --- a/src/modules/SdnDiag.Common/public/Get-SdnCertificate.ps1 +++ b/src/modules/SdnDiag.Common/public/Get-SdnCertificate.ps1 @@ -29,26 +29,43 @@ function Get-SdnCertificate { [Parameter(Mandatory = $false, ParameterSetName = 'Thumbprint')] [ValidateNotNullorEmpty()] - [System.String]$Thumbprint + [System.String]$Thumbprint, + + [Parameter(Mandatory = $false, ParameterSetName = 'Default')] + [Parameter(Mandatory = $false, ParameterSetName = 'Subject')] + [Parameter(Mandatory = $false, ParameterSetName = 'Thumbprint')] + [switch]$NetworkControllerOid ) + [string]$ObjectIdentifier = @('1.3.6.1.4.1.311.95.1.1.1') # this is a custom OID used for Network Controller + $array = @() + try { - $certificateList = Get-ChildItem -Path $Path -Recurse | Where-Object {$_.PSISContainer -eq $false} -ErrorAction Stop + $certificateList = Get-ChildItem -Path $Path | Where-Object {$_.PSISContainer -eq $false} -ErrorAction Ignore + if ($NetworkControllerOid) { + $certificateList | ForEach-Object { + if ($ObjectIdentifier -iin $_.EnhancedKeyUsageList.ObjectId) { + $array += $_ + } + } + } + else { + $array = $certificateList + } switch ($PSCmdlet.ParameterSetName) { 'Subject' { - $filteredCert = $certificateList | Where-Object {$_.Subject -ieq $Subject} + $filteredCert = $array | Where-Object {$_.Subject -ieq $Subject} } 'Thumbprint' { - $filteredCert = $certificateList | Where-Object {$_.Thumbprint -ieq $Thumbprint} + $filteredCert = $array | Where-Object {$_.Thumbprint -ieq $Thumbprint} } default { - return $certificateList + return $array } } if ($null -eq $filteredCert) { - "Unable to locate certificate using {0}" -f $PSCmdlet.ParameterSetName | Trace-Output -Level:Warning return $null } diff --git a/src/modules/SdnDiag.Common/public/Import-SdnCertificate.ps1 b/src/modules/SdnDiag.Common/public/Import-SdnCertificate.ps1 index 9c98e94c..e7ce7e67 100644 --- a/src/modules/SdnDiag.Common/public/Import-SdnCertificate.ps1 +++ b/src/modules/SdnDiag.Common/public/Import-SdnCertificate.ps1 @@ -14,7 +14,7 @@ function Import-SdnCertificate { PS> Import-SdnCertificate -FilePath c:\certs\cert.pfx -CertStore Cert:\LocalMachine\Root -Password $secureString #> - [CmdletBinding()] + [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = 'Medium')] param ( [Parameter(Mandatory = $true)] [System.String]$FilePath, @@ -27,76 +27,82 @@ function Import-SdnCertificate { ) $trustedRootStore = 'Cert:\LocalMachine\Root' - $fileInfo = Get-Item -Path $FilePath - - $certObject = @{ + $certObject = [PSCustomObject]@{ SelfSigned = $false CertInfo = $null CerFileInfo = $null } - switch ($fileInfo.Extension) { - '.pfx' { - if ($CertPassword) { - $certData = (Get-PfxData -FilePath $fileInfo.FullName -Password $CertPassword).EndEntityCertificates - } - else { - $certData = Get-PfxCertificate -FilePath $fileInfo.FullName + try { + $fileInfo = Get-Item -Path $FilePath -ErrorAction Stop + switch ($fileInfo.Extension) { + '.pfx' { + if ($CertPassword) { + $certData = (Get-PfxData -FilePath $fileInfo.FullName -Password $CertPassword).EndEntityCertificates + } + else { + $certData = Get-PfxCertificate -FilePath $fileInfo.FullName + } } - } - '.cer' { - $certData = New-Object System.Security.Cryptography.X509Certificates.X509Certificate2 - $certData.Import($fileInfo) - } + '.cer' { + $certData = New-Object System.Security.Cryptography.X509Certificates.X509Certificate2 + $certData.Import($fileInfo) + } - default { - throw New-Object System.NotSupportedException("Unsupported certificate extension") + default { + throw New-Object System.NotSupportedException("Unsupported certificate extension") + } } - } - $certExists = Get-ChildItem -Path $CertStore | Where-Object {$_.Thumbprint -ieq $certData.Thumbprint} - if ($certExists) { - "{0} already exists under {1}" -f $certExists.Thumbprint, $CertStore | Trace-Output -Level:Verbose - $certObject.CertInfo = $certExists - } - else { - "Importing {0} to {1}" -f $certData.Thumbprint, $CertStore | Trace-Output - if ($certData.HasPrivateKey) { - $importCert = Import-PfxCertificate -FilePath $fileInfo.FullName -CertStoreLocation $CertStore -Password $CertPassword -Exportable -ErrorAction Stop - Set-SdnCertificateAcl -Path $CertStore -Thumbprint $importCert.Thumbprint + $certExists = Get-SdnCertificate -Path $CertStore -Thumbprint $certData.Thumbprint + if ($certExists) { + $certObject.CertInfo = $certExists } else { - $importCert = Import-Certificate -FilePath $fileInfo.FullName -CertStoreLocation $CertStore -ErrorAction Stop - } + "Importing [Subject: $($_.Subject), Thumbprint: $($_.Thumbprint)] to $CertStore" | Trace-Output + if ($certData.HasPrivateKey) { + $importCert = Import-PfxCertificate -FilePath $fileInfo.FullName -CertStoreLocation $CertStore -Password $CertPassword -Exportable -ErrorAction Stop + Set-SdnCertificateAcl -Path $CertStore -Thumbprint $importCert.Thumbprint + } + else { + $importCert = Import-Certificate -FilePath $fileInfo.FullName -CertStoreLocation $CertStore -ErrorAction Stop + } - $certObject.CertInfo = $importCert - } + $certObject.CertInfo = $importCert + } - # determine if the certificates being used are self signed - if ($certObject.CertInfo.Subject -ieq $certObject.CertInfo.Issuer) { - "Detected the certificate subject and issuer are the same. Setting SelfSigned to true" | Trace-Output -Level:Verbose - $certObject.SelfSigned = $true + # determine if the certificates being used are self signed + if ($certObject.CertInfo.Subject -ieq $certObject.CertInfo.Issuer) { + "Detected the certificate subject and issuer are the same. Setting SelfSigned to true" | Trace-Output -Level:Verbose + $certObject.SelfSigned = $true - # check to see if we installed to root store with above operation - # if it is not, then we want to check the root store to see if this certificate has already been installed - # and finally if does not exist, then export the certificate from current store and import into trusted root store - if ($CertStore -ine $trustedRootStore) { - $selfSignedCerExists = Get-ChildItem -Path $trustedRootStore | Where-Object {$_.Thumbprint -ieq $certObject.CertInfo.Thumbprint} - [System.String]$selfSignedCerPath = "{0}\{1}.cer" -f (Split-Path $fileInfo.FullName -Parent), ($certObject.CertInfo.Subject).Replace('=','_') - $selfSignedCer = Export-Certificate -Cert $certObject.CertInfo -FilePath $selfSignedCerPath -ErrorAction Stop - $certObject.CerFileInfo = $selfSignedCer + # check to see if we installed to root store with above operation + # if it is not, then we want to check the root store to see if this certificate has already been installed + # and finally if does not exist, then export the certificate from current store and import into trusted root store + if ($CertStore -ine $trustedRootStore) { + $selfSignedCerExists = Get-SdnCertificate -Path $trustedRootStore -Thumbprint $certObject.CertInfo.Thumbprint + [System.String]$selfSignedCerPath = "{0}\{1}.cer" -f (Split-Path $fileInfo.FullName -Parent), ($certObject.CertInfo.Subject).Replace('=','_') + $selfSignedCer = Export-Certificate -Cert $certObject.CertInfo -FilePath $selfSignedCerPath -ErrorAction Stop + $certObject.CerFileInfo = $selfSignedCer - if (-NOT ($selfSignedCerExists)) { - # import the certificate to the trusted root store - "Importing public key to {0}" -f $trustedRootStore | Trace-Output - $null = Import-Certificate -FilePath $selfSignedCer.FullName -CertStoreLocation $trustedRootStore -ErrorAction Stop - } - else { - "{0} already exists under {1}" -f $certObject.CertInfo.Thumbprint, $trustedRootStore | Trace-Output -Level:Verbose + if (-NOT ($selfSignedCerExists)) { + # import the certificate to the trusted root store + "Importing public key to {0}" -f $trustedRootStore | Trace-Output + $null = Import-Certificate -FilePath $selfSignedCer.FullName -CertStoreLocation $trustedRootStore -ErrorAction Stop + } + else { + "{0} already exists under {1}" -f $certObject.CertInfo.Thumbprint, $trustedRootStore | Trace-Output -Level:Verbose + } } } + + return $certObject + } + catch { + $_ | Trace-Exception + $_ | Write-Error } - return $certObject + return $null } diff --git a/src/modules/SdnDiag.NetworkController/public/New-SdnNetworkControllerNodeCertificate.ps1 b/src/modules/SdnDiag.NetworkController/public/New-SdnNetworkControllerNodeCertificate.ps1 index aebebc10..bf37e2bf 100644 --- a/src/modules/SdnDiag.NetworkController/public/New-SdnNetworkControllerNodeCertificate.ps1 +++ b/src/modules/SdnDiag.NetworkController/public/New-SdnNetworkControllerNodeCertificate.ps1 @@ -19,7 +19,7 @@ function New-SdnNetworkControllerNodeCertificate { [System.Security.SecureString]$CertPassword, [Parameter(Mandatory = $false)] - [System.String]$Path = "$(Get-WorkingDirectory)\Cert_{0}" -f (Get-FormattedDateTimeUTC), + [System.String]$Path = "$(Get-WorkingDirectory)\NcCert_{0}" -f (Get-FormattedDateTimeUTC), [Parameter(Mandatory = $false)] [System.Object]$FabricDetails, @@ -50,18 +50,18 @@ function New-SdnNetworkControllerNodeCertificate { if (-NOT (Test-Path -Path $Path -PathType Container)) { "Creating directory {0}" -f $Path | Trace-Output - $CertPath = New-Item -Path $Path -ItemType Directory -Force + $certPath = New-Item -Path $Path -ItemType Directory -Force } else { - $CertPath = Get-Item -Path $Path + $certPath = Get-Item -Path $Path } $nodeCertSubject = (Get-SdnNetworkControllerNodeCertificate).Subject - $certificate = New-SdnCertificate -Subject $nodeCertSubject -NotAfter $NotAfter + $certificate = New-SdnSelfSignedCertificate -Subject $nodeCertSubject -NotAfter $NotAfter # after the certificate has been generated, we want to export the certificate using the $CertPassword provided by the operator # and save the file to directory. This allows the rest of the function to pick up these files and perform the steps as normal - [System.String]$pfxFilePath = "$(Join-Path -Path $CertPath.FullName -ChildPath $nodeCertSubject.ToString().ToLower().Replace('.','_').Replace("=",'_').Trim()).pfx" + [System.String]$pfxFilePath = "$(Join-Path -Path $certPath.FullName -ChildPath $nodeCertSubject.ToString().ToLower().Replace('.','_').Replace("=",'_').Trim()).pfx" "Exporting pfx certificate to {0}" -f $pfxFilePath | Trace-Output $exportedCertificate = Export-PfxCertificate -Cert $certificate -FilePath $pfxFilePath -Password $CertPassword -CryptoAlgorithmOption AES256_SHA256 $null = Import-SdnCertificate -FilePath $exportedCertificate.FullName -CertStore 'Cert:\LocalMachine\Root' -CertPassword $CertPassword @@ -78,4 +78,6 @@ function New-SdnNetworkControllerNodeCertificate { $_ | Trace-Exception $_ | Write-Error } + + return $null } diff --git a/src/modules/SdnDiag.NetworkController/public/New-SdnNetworkControllerRestCertificate.ps1 b/src/modules/SdnDiag.NetworkController/public/New-SdnNetworkControllerRestCertificate.ps1 index 974e894c..7178b163 100644 --- a/src/modules/SdnDiag.NetworkController/public/New-SdnNetworkControllerRestCertificate.ps1 +++ b/src/modules/SdnDiag.NetworkController/public/New-SdnNetworkControllerRestCertificate.ps1 @@ -20,7 +20,7 @@ function New-SdnNetworkControllerRestCertificate { [System.Security.SecureString]$CertPassword, [Parameter(Mandatory = $false)] - [System.String]$Path = "$(Get-WorkingDirectory)\Cert_{0}" -f (Get-FormattedDateTimeUTC), + [System.String]$Path = "$(Get-WorkingDirectory)\NcRest_{0}" -f (Get-FormattedDateTimeUTC), [Parameter(Mandatory = $false)] [System.Object]$FabricDetails, @@ -61,18 +61,18 @@ function New-SdnNetworkControllerRestCertificate { if (-NOT (Test-Path -Path $Path -PathType Container)) { "Creating directory {0}" -f $Path | Trace-Output - $CertPath = New-Item -Path $Path -ItemType Directory -Force + $certPath = New-Item -Path $Path -ItemType Directory -Force } else { - $CertPath = Get-Item -Path $Path + $certPath = Get-Item -Path $Path } [System.String]$formattedSubject = "CN={0}" -f $RestName.Trim() - $certificate = New-SdnCertificate -Subject $formattedSubject -NotAfter $NotAfter + $certificate = New-SdnSelfSignedCertificate -Subject $formattedSubject -NotAfter $NotAfter # after the certificate has been generated, we want to export the certificate using the $CertPassword provided by the operator # and save the file to directory. This allows the rest of the function to pick up these files and perform the steps as normal - [System.String]$pfxFilePath = "$(Join-Path -Path $CertPath.FullName -ChildPath $RestName.ToLower().Replace('.','_').Replace('=','_').Trim()).pfx" + [System.String]$pfxFilePath = "$(Join-Path -Path $certPath.FullName -ChildPath $RestName.ToLower().Replace('.','_').Replace('=','_').Trim()).pfx" "Exporting pfx certificate to {0}" -f $pfxFilePath | Trace-Output $exportedCertificate = Export-PfxCertificate -Cert $certificate -FilePath $pfxFilePath -Password $CertPassword -CryptoAlgorithmOption AES256_SHA256 $null = Import-SdnCertificate -FilePath $exportedCertificate.FullName -CertStore 'Cert:\LocalMachine\Root' -CertPassword $CertPassword @@ -89,4 +89,6 @@ function New-SdnNetworkControllerRestCertificate { $_ | Trace-Exception $_ | Write-Error } + + return $null } diff --git a/src/modules/SdnDiag.Server/public/New-SdnServerCertificate.ps1 b/src/modules/SdnDiag.Server/public/New-SdnServerCertificate.ps1 index 37ecff51..94f0054c 100644 --- a/src/modules/SdnDiag.Server/public/New-SdnServerCertificate.ps1 +++ b/src/modules/SdnDiag.Server/public/New-SdnServerCertificate.ps1 @@ -20,7 +20,7 @@ function New-SdnServerCertificate { [datetime]$NotAfter = (Get-Date).AddYears(3), [Parameter(Mandatory = $false)] - [System.String]$Path = "$(Get-WorkingDirectory)\MuxCert_{0}" -f (Get-FormattedDateTimeUTC), + [System.String]$Path = "$(Get-WorkingDirectory)\ServerCert_{0}" -f (Get-FormattedDateTimeUTC), [Parameter(Mandatory = $false)] [System.Object]$FabricDetails, @@ -45,19 +45,19 @@ function New-SdnServerCertificate { try { if (-NOT (Test-Path -Path $Path -PathType Container)) { "Creating directory {0}" -f $Path | Trace-Output - $CertPath = New-Item -Path $Path -ItemType Directory -Force + $certPath = New-Item -Path $Path -ItemType Directory -Force } else { - $CertPath = Get-Item -Path $Path + $certPath = Get-Item -Path $Path } - $serverCert = Get-ItemPropertyValue -Path 'HKLM:\SYSTEM\CurrentControlSet\Services\NcHostAgent\Parameters' -Name 'HostAgentCertificateCName' + $serverCert = Get-ItemPropertyValue -Path 'HKLM:\SYSTEM\CurrentControlSet\Services\NcHostAgent\Parameters' -Name 'HostAgentCertificateCName' -ErrorAction Stop $subjectName = "CN={0}" -f $serverCert - $certificate = New-SdnCertificate -Subject $subjectName -NotAfter $NotAfter + $certificate = New-SdnSelfSignedCertificate -Subject $subjectName -NotAfter $NotAfter # after the certificate has been generated, we want to export the certificate and save the file to directory # This allows the rest of the function to pick up these files and perform the steps as normal - [System.String]$cerFilePath = "$(Join-Path -Path $CertPath.FullName -ChildPath $subjectName.ToString().ToLower().Replace('.','_').Replace("=",'_').Trim()).cer" + [System.String]$cerFilePath = "$(Join-Path -Path $certPath.FullName -ChildPath $subjectName.ToString().ToLower().Replace('.','_').Replace("=",'_').Trim()).cer" "Exporting certificate to {0}" -f $cerFilePath | Trace-Output $exportedCertificate = Export-Certificate -Cert $certificate -FilePath $cerFilePath -Type CERT Copy-CertificateToFabric -CertFile $exportedCertificate.FullName -FabricDetails $FabricDetails -ServerNodeCert -Credential $Credential @@ -73,4 +73,6 @@ function New-SdnServerCertificate { $_ | Trace-Exception $_ | Write-Error } + + return $null } From b43beb53636ae183bcaf694acb4e71aeb400b597 Mon Sep 17 00:00:00 2001 From: Adam Rudell Date: Thu, 4 Apr 2024 11:48:55 -0500 Subject: [PATCH 2/9] rename function --- .../public/New-SdnSelfSignedCertificate.ps1 | 59 +++++++++++++++++++ 1 file changed, 59 insertions(+) create mode 100644 src/modules/SdnDiag.Common/public/New-SdnSelfSignedCertificate.ps1 diff --git a/src/modules/SdnDiag.Common/public/New-SdnSelfSignedCertificate.ps1 b/src/modules/SdnDiag.Common/public/New-SdnSelfSignedCertificate.ps1 new file mode 100644 index 00000000..29686b46 --- /dev/null +++ b/src/modules/SdnDiag.Common/public/New-SdnSelfSignedCertificate.ps1 @@ -0,0 +1,59 @@ +function New-SdnSelfSignedCertificate { + <# + .SYNOPSIS + Creates a new self-signed certificate for use with SDN fabric. + .PARAMETER Subject + Specifies the string that appears in the subject of the new certificate. This cmdlet prefixes CN= to any value that does not contain an equal sign. + .PARAMETER CertStoreLocation + Specifies the certificate store in which to store the new certificate. If paramater is not specified, defaults to Cert:\LocalMachine\My. + .PARAMETER NotAfter + Specifies the date and time, as a DateTime object, that the certificate expires. To obtain a DateTime object, use the Get-Date cmdlet. The default value for this parameter is one year after the certificate was created. + .EXAMPLE + PS> New-SdnSelfSignedCertificate -Subject rest.sdn.contoso -CertStoreLocation Cert:\LocalMachine\My + #> + + [CmdletBinding()] + param( + [Parameter(Mandatory = $true)] + [System.String]$Subject, + + [Parameter(Mandatory = $false)] + [ValidateScript({ + if ($_ -notlike "cert:\*") { + throw New-Object System.FormatException("Invalid path") + } + + return $true + })] + [System.String]$CertStoreLocation = 'Cert:\LocalMachine\My', + + [Parameter(Mandatory = $true)] + [System.DateTime]$NotAfter + ) + + try { + "Generating certificate with subject {0} under {1}" -f $Subject, $CertStoreLocation | Trace-Output + + # create new self signed certificate with the following EnhancedKeyUsageList + # 1.3.6.1.5.5.7.3.1 - Server Authentication OID + # 1.3.6.1.5.5.7.3.2 - Client Authentication OID + # 1.3.6.1.4.1.311.95.1.1.1 - Network Controller OID + $selfSignedCert = New-SelfSignedCertificate -Type Custom -KeySpec KeyExchange -Subject $Subject ` + -KeyExportPolicy Exportable -HashAlgorithm sha256 -KeyLength 2048 ` + -CertStoreLocation $CertStoreLocation -TextExtension @("2.5.29.37={text}1.3.6.1.5.5.7.3.1,1.3.6.1.5.5.7.3.2,1.3.6.1.4.1.311.95.1.1.1") ` + -NotAfter $NotAfter + + if ($selfSignedCert) { + "Successfully generated self signed certificate`n`tSubject: {0}`n`tThumbprint: {1}`n`tNotAfter: {2}" ` + -f $selfSignedCert.Subject, $selfSignedCert.Thumbprint, $selfSignedCert.NotAfter | Trace-Output + + Set-SdnCertificateAcl -Path $CertStoreLocation -Thumbprint $selfSignedCert.Thumbprint + } + + return $selfSignedCert + } + catch { + $_ | Trace-Exception + $_ | Write-Error + } +} From 616e1905e6fb18120ce29c1b05d273fd6bf4d43c Mon Sep 17 00:00:00 2001 From: Adam Rudell Date: Thu, 4 Apr 2024 11:52:10 -0500 Subject: [PATCH 3/9] update cert rotate functions --- .../public/New-SdnCertificate.ps1 | 55 --------- .../public/Remove-SdnCertificate.ps1 | 71 +++++++++++ .../public/New-SdnMuxCertificate.ps1 | 14 ++- ...etworkController.ServiceFabric.Config.psd1 | 24 ++++ .../Start-SdnMuxCertificateRotation.ps1 | 45 ++----- ...dnNetworkControllerCertificateRotation.ps1 | 116 ++++++++++++++++++ .../Start-SdnServerCertificateRotation.ps1 | 45 ++----- 7 files changed, 233 insertions(+), 137 deletions(-) delete mode 100644 src/modules/SdnDiag.Common/public/New-SdnCertificate.ps1 create mode 100644 src/modules/SdnDiag.Common/public/Remove-SdnCertificate.ps1 create mode 100644 src/modules/SdnDiag.NetworkController.ServiceFabric/SdnDiag.NetworkController.ServiceFabric.Config.psd1 rename src/modules/{SdnDiag.LoadBalancerMux => SdnDiag.NetworkController}/public/Start-SdnMuxCertificateRotation.ps1 (78%) create mode 100644 src/modules/SdnDiag.NetworkController/public/Start-SdnNetworkControllerCertificateRotation.ps1 rename src/modules/{SdnDiag.Server => SdnDiag.NetworkController}/public/Start-SdnServerCertificateRotation.ps1 (78%) diff --git a/src/modules/SdnDiag.Common/public/New-SdnCertificate.ps1 b/src/modules/SdnDiag.Common/public/New-SdnCertificate.ps1 deleted file mode 100644 index d2899879..00000000 --- a/src/modules/SdnDiag.Common/public/New-SdnCertificate.ps1 +++ /dev/null @@ -1,55 +0,0 @@ -function New-SdnCertificate { - <# - .SYNOPSIS - Creates a new self-signed certificate for use with SDN fabric. - .PARAMETER Subject - Specifies the string that appears in the subject of the new certificate. This cmdlet prefixes CN= to any value that does not contain an equal sign. - .PARAMETER CertStoreLocation - Specifies the certificate store in which to store the new certificate. If paramater is not specified, defaults to Cert:\LocalMachine\My. - .PARAMETER NotAfter - Specifies the date and time, as a DateTime object, that the certificate expires. To obtain a DateTime object, use the Get-Date cmdlet. The default value for this parameter is one year after the certificate was created. - .EXAMPLE - PS> New-SdnCertificate -Subject rest.sdn.contoso -CertStoreLocation Cert:\LocalMachine\My - #> - - [CmdletBinding()] - param( - [Parameter(Mandatory = $true)] - [System.String]$Subject, - - [Parameter(Mandatory = $false)] - [ValidateScript({ - if ($_ -notlike "cert:\*") { - throw New-Object System.FormatException("Invalid path") - } - - return $true - })] - [System.String]$CertStoreLocation = 'Cert:\LocalMachine\My', - - [Parameter(Mandatory = $true)] - [System.DateTime]$NotAfter - ) - - try { - "Generating certificate with subject {0} under {1}" -f $Subject, $CertStoreLocation | Trace-Output - - $selfSignedCert = New-SelfSignedCertificate -Type Custom -KeySpec KeyExchange -Subject $Subject ` - -KeyExportPolicy Exportable -HashAlgorithm sha256 -KeyLength 2048 ` - -CertStoreLocation $CertStoreLocation -TextExtension @("2.5.29.37={text}1.3.6.1.5.5.7.3.1,1.3.6.1.5.5.7.3.2,1.3.6.1.4.1.311.95.1.1.1") ` - -NotAfter $NotAfter - - if ($selfSignedCert) { - "Successfully generated self signed certificate`n`tSubject: {0}`n`tThumbprint: {1}`n`tNotAfter: {2}" ` - -f $selfSignedCert.Subject, $selfSignedCert.Thumbprint, $selfSignedCert.NotAfter | Trace-Output - - Set-SdnCertificateAcl -Path $CertStoreLocation -Thumbprint $selfSignedCert.Thumbprint - } - - return $selfSignedCert - } - catch { - $_ | Trace-Exception - $_ | Write-Error - } -} diff --git a/src/modules/SdnDiag.Common/public/Remove-SdnCertificate.ps1 b/src/modules/SdnDiag.Common/public/Remove-SdnCertificate.ps1 new file mode 100644 index 00000000..3430ce92 --- /dev/null +++ b/src/modules/SdnDiag.Common/public/Remove-SdnCertificate.ps1 @@ -0,0 +1,71 @@ +function Remove-SdnCertificate { + <# + .SYNOPSIS + Removes a certificate from the certificate store that contains a custom Network Controller OID. + .PARAMETER Path + Defines the path within the certificate store. Path is expected to start with cert:\. + .PARAMETER Thumbprint + Specifies the thumbprint of the certificate to remove. + .PARAMETER Subject + Specifies the subject of the certificate to remove. + .EXAMPLE + PS> Remove-SdnCertificate -Path "Cert:\LocalMachine\My" -Thumbprint "1234567890ABCDEF1234567890ABCDEF12345678" + .EXAMPLE + PS> Remove-SdnCertificate -Path "Cert:\LocalMachine\My" -Subject "rest.sdn.contoso" + #> + + [CmdletBinding(DefaultParameterSetName = 'Thumbprint', SupportsShouldProcess = $true, ConfirmImpact = 'High')] + param ( + [Parameter(Mandatory = $true, ParameterSetName = 'Thumbprint')] + [Parameter(Mandatory = $true, ParameterSetName = 'Subject')] + [ValidateScript({ + if ($_ -notlike "cert:\*") { + throw New-Object System.FormatException("Invalid path") + } + + return $true + })] + [System.String]$Path, + + [Parameter(Mandatory = $true, ParameterSetName = 'Thumbprint')] + [ValidateNotNullorEmpty()] + [System.String]$Thumbprint, + + [Parameter(Mandatory = $true, ParameterSetName = 'Subject')] + [ValidateNotNullorEmpty()] + [System.String]$Subject + ) + + $params = @{ + Path = $Path + NetworkControllerOid = $true + } + switch ($PSCmdlet.ParameterSetName) { + 'Thumbprint' { + $params.Add('Thumbprint', $Thumbprint) + } + 'Subject' { + $params.Add('Subject', $Subject) + } + } + + try { + $cert = Get-SdnCertificate @params + if ($cert) { + $cert | ForEach-Object { + $message = "Certificate [Subject: $($_.Subject), Thumbprint: $($_.Thumbprint)]" + if ($PSCmdlet.ShouldProcess($message)) { + "Removing certificate [Subject: $($_.Subject), Thumbprint: $($_.Thumbprint)] from $Path" | Trace-Output -Level:Verbose + $_ | Remove-Item + } + } + } + else { + "No certificate found with with the specified $($PSCmdlet.ParameterSetName)" | Trace-Output -Level:Warning + } + } + catch { + $_ | Trace-Exception + $_ | Write-Error + } +} diff --git a/src/modules/SdnDiag.LoadBalancerMux/public/New-SdnMuxCertificate.ps1 b/src/modules/SdnDiag.LoadBalancerMux/public/New-SdnMuxCertificate.ps1 index 1dd66009..6a0db031 100644 --- a/src/modules/SdnDiag.LoadBalancerMux/public/New-SdnMuxCertificate.ps1 +++ b/src/modules/SdnDiag.LoadBalancerMux/public/New-SdnMuxCertificate.ps1 @@ -17,7 +17,7 @@ function New-SdnMuxCertificate { [CmdletBinding()] param ( [Parameter(Mandatory = $false)] - [datetime]$NotAfter = (Get-Date).AddYears(3), + [datetime]$NotAfter = (Get-Date).AddYears(1), [Parameter(Mandatory = $false)] [System.String]$Path = "$(Get-WorkingDirectory)\MuxCert_{0}" -f (Get-FormattedDateTimeUTC), @@ -45,19 +45,19 @@ function New-SdnMuxCertificate { try { if (-NOT (Test-Path -Path $Path -PathType Container)) { "Creating directory {0}" -f $Path | Trace-Output - $CertPath = New-Item -Path $Path -ItemType Directory -Force + $certPath = New-Item -Path $Path -ItemType Directory -Force } else { - $CertPath = Get-Item -Path $Path + $certPath = Get-Item -Path $Path } - $muxCert = Get-ItemPropertyValue -Path 'HKLM:\SYSTEM\CurrentControlSet\Services\SlbMux' -Name 'MuxCert' + $muxCert = Get-ItemPropertyValue -Path 'HKLM:\SYSTEM\CurrentControlSet\Services\SlbMux' -Name 'MuxCert' -ErrorAction Stop $subjectName = "CN={0}" -f $muxCert - $certificate = New-SdnCertificate -Subject $subjectName -NotAfter $NotAfter + $certificate = New-SdnSelfSignedCertificate -Subject $subjectName -NotAfter $NotAfter # after the certificate has been generated, we want to export the certificate and save the file to directory # This allows the rest of the function to pick up these files and perform the steps as normal - [System.String]$cerFilePath = "$(Join-Path -Path $CertPath.FullName -ChildPath $subjectName.ToString().ToLower().Replace('.','_').Replace("=",'_').Trim()).cer" + [System.String]$cerFilePath = "$(Join-Path -Path $certPath.FullName -ChildPath $subjectName.ToString().ToLower().Replace('.','_').Replace("=",'_').Trim()).cer" "Exporting certificate to {0}" -f $cerFilePath | Trace-Output $exportedCertificate = Export-Certificate -Cert $certificate -FilePath $cerFilePath -Type CERT Copy-CertificateToFabric -CertFile $exportedCertificate.FullName -FabricDetails $FabricDetails -LoadBalancerMuxNodeCert -Credential $Credential @@ -73,4 +73,6 @@ function New-SdnMuxCertificate { $_ | Trace-Exception $_ | Write-Error } + + return $null } diff --git a/src/modules/SdnDiag.NetworkController.ServiceFabric/SdnDiag.NetworkController.ServiceFabric.Config.psd1 b/src/modules/SdnDiag.NetworkController.ServiceFabric/SdnDiag.NetworkController.ServiceFabric.Config.psd1 new file mode 100644 index 00000000..00f21289 --- /dev/null +++ b/src/modules/SdnDiag.NetworkController.ServiceFabric/SdnDiag.NetworkController.ServiceFabric.Config.psd1 @@ -0,0 +1,24 @@ +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT License. + +@{ + CommonPaths = @{ + serviceFabricLogDirectory = @( + "C:\ProgramData\Microsoft\Service Fabric\log\Traces" + "C:\ProgramData\Microsoft\Service Fabric\log\OperationalTraces" + "C:\ProgramData\Microsoft\Service Fabric\log\QueryTraces" + "C:\ProgramData\Microsoft\Service Fabric\log\CrashDumps" + "C:\ProgramData\Microsoft\Service Fabric\log\PerformanceCounters_WinFabPerfCtrFolder" + ) + } + EventLogProviders = @( + "Microsoft-ServiceFabric*" + ) + Services = @{ + FabricHostSvc = @{ + Properties = @{ + DisplayName = "Service Fabric Host Service" + } + } + } +} diff --git a/src/modules/SdnDiag.LoadBalancerMux/public/Start-SdnMuxCertificateRotation.ps1 b/src/modules/SdnDiag.NetworkController/public/Start-SdnMuxCertificateRotation.ps1 similarity index 78% rename from src/modules/SdnDiag.LoadBalancerMux/public/Start-SdnMuxCertificateRotation.ps1 rename to src/modules/SdnDiag.NetworkController/public/Start-SdnMuxCertificateRotation.ps1 index 39650b9e..482fa6f2 100644 --- a/src/modules/SdnDiag.LoadBalancerMux/public/Start-SdnMuxCertificateRotation.ps1 +++ b/src/modules/SdnDiag.NetworkController/public/Start-SdnMuxCertificateRotation.ps1 @@ -6,85 +6,54 @@ function Start-SdnMuxCertificateRotation { Specifies a user account that has permission to perform this action on the Load Balancer Mux and Network Controller nodes. The default is the current user. .PARAMETER NcRestCredential Specifies a user account that has permission to access the northbound NC API interface. The default is the current user. - .PARAMETER CertPath - Path directory where certificate(s) .pfx files are located for use with certificate rotation. .PARAMETER GenerateCertificate Switch to determine if certificate rotate function should generate self-signed certificates. .PARAMETER CertPassword SecureString password for accessing the .pfx files, or if using -GenerateCertificate, what the .pfx files will be encrypted with. .PARAMETER NotAfter Expiration date when using -GenerateCertificate. If ommited, defaults to 3 years. - .PARAMETER CertRotateConfig - The Config generated by New-SdnCertificateRotationConfig to include appropriate certificate thumbprints for mux nodes. .PARAMETER Force Switch to force the rotation without being prompted, when Service Fabric is unhealthy. #> [CmdletBinding(DefaultParameterSetName = 'GenerateCertificate')] param ( - [Parameter(Mandatory = $true, ParameterSetName = 'Pfx')] - [Parameter(Mandatory = $true, ParameterSetName = 'GenerateCertificate')] - [Parameter(Mandatory = $true, ParameterSetName = 'CertConfig')] - [System.String]$NetworkController, + [Parameter(Mandatory = $false, ParameterSetName = 'GenerateCertificate')] + [System.String]$NetworkController = $env:COMPUTERNAME, - [Parameter(Mandatory = $true, ParameterSetName = 'Pfx')] [Parameter(Mandatory = $true, ParameterSetName = 'GenerateCertificate')] - [Parameter(Mandatory = $true, ParameterSetName = 'CertConfig')] [System.Management.Automation.PSCredential] [System.Management.Automation.Credential()] $Credential, - [Parameter(Mandatory = $false, ParameterSetName = 'Pfx')] [Parameter(Mandatory = $false, ParameterSetName = 'GenerateCertificate')] - [Parameter(Mandatory = $false, ParameterSetName = 'CertConfig')] [System.Management.Automation.PSCredential] [System.Management.Automation.Credential()] $NcRestCredential = [System.Management.Automation.PSCredential]::Empty, - [Parameter(Mandatory = $true, ParameterSetName = 'Pfx')] - [System.String]$CertPath, - [Parameter(Mandatory = $true, ParameterSetName = 'GenerateCertificate')] [Switch]$GenerateCertificate, - [Parameter(Mandatory = $true, ParameterSetName = 'Pfx')] [Parameter(Mandatory = $true, ParameterSetName = 'GenerateCertificate')] [System.Security.SecureString]$CertPassword, [Parameter(Mandatory = $false, ParameterSetName = 'GenerateCertificate')] [datetime]$NotAfter = (Get-Date).AddYears(3), - [Parameter(Mandatory = $true, ParameterSetName = 'CertConfig')] - [hashtable]$CertRotateConfig, - - [Parameter(Mandatory = $false, ParameterSetName = 'Pfx')] [Parameter(Mandatory = $false, ParameterSetName = 'GenerateCertificate')] - [Parameter(Mandatory = $false, ParameterSetName = 'CertConfig')] [switch]$Force ) - # these are not yet supported and will take a bit more time to implement as it touches on core framework for rotate functionality - # however majority of the environments impacted are using sdnexpress which leverage self-signed certificates. - if ($CertRotateConfig -or $CertPath) { - "This feature is not yet supported and is under development. Please use -GenerateCertificate or reference {0} for manual steps." ` - -f 'https://learn.microsoft.com/en-us/azure-stack/hci/manage/update-network-controller-certificates?tabs=manual-renewal' | Trace-Output -Level:Warning - return - } - # ensure that the module is running as local administrator $elevated = ([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator) if (-NOT $elevated) { throw New-Object System.Exception("This function requires elevated permissions. Run PowerShell as an Administrator and import the module again.") } - # add disclaimer that this feature is currently under preview - if (!$Force) { - "This feature is currently under preview. Please report any issues to https://github.com/microsoft/SdnDiagnostics/issues so we can accurately track any issues and help unblock your cert rotation." | Trace-Output -Level:Warning - $confirm = Confirm-UserInput -Message "Do you want to proceed with certificate rotation? [Y/N]:" - if (-NOT $confirm) { - "User has opted to abort the operation. Terminating operation" | Trace-Output -Level:Warning - return - } + $config = Get-SdnModuleConfiguration -Role 'NetworkController' + $confirmFeatures = Confirm-RequiredFeaturesInstalled -Name $config.windowsFeature + if (-NOT ($confirmFeatures)) { + throw New-Object System.NotSupportedException("The current machine is not a NetworkController, run this on NetworkController.") } $array = @() @@ -160,7 +129,7 @@ function Start-SdnMuxCertificateRotation { else { # in instances where the certificate property does not exist, we will need to add it # this typically will occur if converting from CA issued certificate to self-signed certificate - $virtualServer.properties | Add-Member -MemberType NoteProperty -Name 'certificate' -Value $encoding + $virtualServer.properties | Add-Member -MemberType NoteProperty -Name 'certificate' -Value $encoding -Force } $jsonBody = $virtualServer | ConvertTo-Json -Depth 100 diff --git a/src/modules/SdnDiag.NetworkController/public/Start-SdnNetworkControllerCertificateRotation.ps1 b/src/modules/SdnDiag.NetworkController/public/Start-SdnNetworkControllerCertificateRotation.ps1 new file mode 100644 index 00000000..70ef581f --- /dev/null +++ b/src/modules/SdnDiag.NetworkController/public/Start-SdnNetworkControllerCertificateRotation.ps1 @@ -0,0 +1,116 @@ +function Start-SdnNetworkControllerCertificateRotation { + <# + .SYNOPSIS + Performs a rotate of the Network Controller node certificates if using X509. + .PARAMETER Credential + Specifies a user account that has permission to perform this action. The default is the current user. + .PARAMETER NcRestCredential + Specifies a user account that has permission to access the northbound NC API interface. The default is the current user. + .PARAMETER CertPath + Path directory where certificate(s) .pfx files are located for use with certificate rotation. + .PARAMETER GenerateCertificate + Switch to determine if certificate rotate function should generate self-signed certificates. + .PARAMETER CertPassword + SecureString password for accessing the .pfx files, or if using -GenerateCertificate, what the .pfx files will be encrypted with. + .PARAMETER NotAfter + Expiration date when using -GenerateCertificate. If ommited, defaults to 3 years. + .PARAMETER CertRotateConfig + The Config generated by New-SdnCertificateRotationConfig to include NC REST certificate thumbprint and node certificate thumbprint. + .PARAMETER Force + Switch to force the rotation without being prompted, when Service Fabric is unhealthy. + #> + + [CmdletBinding(DefaultParameterSetName = 'GenerateCertificate')] + param ( + [Parameter(Mandatory = $true, ParameterSetName = 'Pfx')] + [Parameter(Mandatory = $true, ParameterSetName = 'GenerateCertificate')] + [Parameter(Mandatory = $true, ParameterSetName = 'CertConfig')] + [System.Management.Automation.PSCredential] + [System.Management.Automation.Credential()] + $Credential, + + [Parameter(Mandatory = $false, ParameterSetName = 'Pfx')] + [Parameter(Mandatory = $false, ParameterSetName = 'GenerateCertificate')] + [Parameter(Mandatory = $false, ParameterSetName = 'CertConfig')] + [System.Management.Automation.PSCredential] + [System.Management.Automation.Credential()] + $NcRestCredential = [System.Management.Automation.PSCredential]::Empty, + + [Parameter(Mandatory = $true, ParameterSetName = 'Pfx')] + [System.String]$CertPath, + + [Parameter(Mandatory = $true, ParameterSetName = 'GenerateCertificate')] + [Switch]$GenerateCertificate, + + [Parameter(Mandatory = $true, ParameterSetName = 'Pfx')] + [Parameter(Mandatory = $true, ParameterSetName = 'GenerateCertificate')] + [System.Security.SecureString]$CertPassword, + + [Parameter(Mandatory = $false, ParameterSetName = 'GenerateCertificate')] + [datetime]$NotAfter = (Get-Date).AddYears(3), + + [Parameter(Mandatory = $true, ParameterSetName = 'CertConfig')] + [hashtable]$CertRotateConfig, + + [Parameter(Mandatory = $false, ParameterSetName = 'Pfx')] + [Parameter(Mandatory = $false, ParameterSetName = 'GenerateCertificate')] + [Parameter(Mandatory = $false, ParameterSetName = 'CertConfig')] + [switch]$Force + ) + + # ensure that the module is running as local administrator + $elevated = ([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator) + if (-NOT $elevated) { + throw New-Object System.Exception("This function requires elevated permissions. Run PowerShell as an Administrator and import the module again.") + } + + $config = Get-SdnModuleConfiguration -Role 'NetworkController' + $confirmFeatures = Confirm-RequiredFeaturesInstalled -Name $config.windowsFeature + if (-NOT ($confirmFeatures)) { + throw New-Object System.NotSupportedException("The current machine is not a NetworkController, run this on NetworkController.") + } + + # purge any existing remote sessions to prevent situation where + # we leverage a session without credentials + Remove-PSRemotingSession + + try { + "Starting Network Controller Node certificate rotation" | Trace-Output + + if ([String]::IsNullOrEmpty($CertPath)) { + [System.String]$CertPath = "$(Get-WorkingDirectory)\Cert_{0}" -f (Get-FormattedDateTimeUTC) + + if (-NOT (Test-Path -Path $CertPath -PathType Container)) { + $null = New-Item -Path $CertPath -ItemType Directory -Force + } + } + [System.IO.FileSystemInfo]$CertPath = Get-Item -Path $CertPath -ErrorAction Stop + + "Retrieving current SDN environment details" | Trace-Output + $ncInfrastructureInfo = Get-SdnNetworkControllerInfoOffline -Credential $Credential + if ($ncInfrastructureInfo.ClusterCredentialType -ine 'X509') { + "Network Controller nodes are not using X509 certificates and do not need to be rotated." | Trace-Output + return + } + + # before we proceed with anything else, we want to make sure that all the Network Controllers and MUXes within the SDN fabric are running the current version + Install-SdnDiagnostics -ComputerName $sdnFabricDetails.NetworkController -ErrorAction Stop + + $null = Invoke-PSRemoteCommand -ComputerName $sdnFabricDetails.NetworkController -Credential $Credential -ScriptBlock { + param( + [Parameter(Position = 0)][DateTime]$param1, + [Parameter(Position = 1)][SecureString]$param2, + [Parameter(Position = 2)][PSCredential]$param3, + [Parameter(Position = 3)][String]$param4, + [Parameter(Position = 4)][System.Object]$param5 + ) + + New-SdnNetworkControllerNodeCertificate -NotAfter $param1 -CertPassword $param2 -Credential $param3 -Path $param4 -FabricDetails $param5 + } -ArgumentList @($NotAfter, $CertPassword, $Credential, $CertPath.FullName, $sdnFabricDetails) + + } + catch { + $_ | Trace-Exception + $_ | Write-Error + } +} diff --git a/src/modules/SdnDiag.Server/public/Start-SdnServerCertificateRotation.ps1 b/src/modules/SdnDiag.NetworkController/public/Start-SdnServerCertificateRotation.ps1 similarity index 78% rename from src/modules/SdnDiag.Server/public/Start-SdnServerCertificateRotation.ps1 rename to src/modules/SdnDiag.NetworkController/public/Start-SdnServerCertificateRotation.ps1 index 38329dd4..e3f26279 100644 --- a/src/modules/SdnDiag.Server/public/Start-SdnServerCertificateRotation.ps1 +++ b/src/modules/SdnDiag.NetworkController/public/Start-SdnServerCertificateRotation.ps1 @@ -8,83 +8,52 @@ function Start-SdnServerCertificateRotation { Specifies a user account that has permission to access the northbound NC API interface. The default is the current user. .PARAMETER GenerateCertificate Switch to determine if certificate rotate function should generate self-signed certificates. - .PARAMETER CertPath - Path directory where certificate(s) .pfx files are located for use with certificate rotation. .PARAMETER CertPassword SecureString password for accessing the .pfx files, or if using -GenerateCertificate, what the .pfx files will be encrypted with. .PARAMETER NotAfter Expiration date when using -GenerateCertificate. If ommited, defaults to 3 years. - .PARAMETER CertRotateConfig - The Config generated by New-SdnCertificateRotationConfig to include appropriate certificate thumbprints for server nodes. .PARAMETER Force Switch to force the rotation without being prompted, when Service Fabric is unhealthy. #> [CmdletBinding(DefaultParameterSetName = 'GenerateCertificate')] param ( - [Parameter(Mandatory = $true, ParameterSetName = 'Pfx')] - [Parameter(Mandatory = $true, ParameterSetName = 'GenerateCertificate')] - [Parameter(Mandatory = $true, ParameterSetName = 'CertConfig')] - [System.String]$NetworkController, + [Parameter(Mandatory = $false, ParameterSetName = 'GenerateCertificate')] + [System.String]$NetworkController = $env:COMPUTERNAME, - [Parameter(Mandatory = $true, ParameterSetName = 'Pfx')] [Parameter(Mandatory = $true, ParameterSetName = 'GenerateCertificate')] - [Parameter(Mandatory = $true, ParameterSetName = 'CertConfig')] [System.Management.Automation.PSCredential] [System.Management.Automation.Credential()] $Credential, - [Parameter(Mandatory = $false, ParameterSetName = 'Pfx')] [Parameter(Mandatory = $false, ParameterSetName = 'GenerateCertificate')] - [Parameter(Mandatory = $false, ParameterSetName = 'CertConfig')] [System.Management.Automation.PSCredential] [System.Management.Automation.Credential()] $NcRestCredential = [System.Management.Automation.PSCredential]::Empty, - [Parameter(Mandatory = $true, ParameterSetName = 'Pfx')] - [System.String]$CertPath, - [Parameter(Mandatory = $true, ParameterSetName = 'GenerateCertificate')] [Switch]$GenerateCertificate, - [Parameter(Mandatory = $true, ParameterSetName = 'Pfx')] [Parameter(Mandatory = $true, ParameterSetName = 'GenerateCertificate')] [System.Security.SecureString]$CertPassword, [Parameter(Mandatory = $false, ParameterSetName = 'GenerateCertificate')] [datetime]$NotAfter = (Get-Date).AddYears(3), - [Parameter(Mandatory = $true, ParameterSetName = 'CertConfig')] - [hashtable]$CertRotateConfig, - - [Parameter(Mandatory = $false, ParameterSetName = 'Pfx')] [Parameter(Mandatory = $false, ParameterSetName = 'GenerateCertificate')] - [Parameter(Mandatory = $false, ParameterSetName = 'CertConfig')] [switch]$Force ) - # these are not yet supported and will take a bit more time to implement as it touches on core framework for rotate functionality - # however majority of the environments impacted are using sdnexpress which leverage self-signed certificates. - if ($CertRotateConfig -or $CertPath) { - "This feature is not yet supported and is under development. Please use -GenerateCertificate or reference {0} for manual steps." ` - -f 'https://learn.microsoft.com/en-us/azure-stack/hci/manage/update-network-controller-certificates?tabs=manual-renewal' | Trace-Output -Level:Warning - return - } - # ensure that the module is running as local administrator $elevated = ([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator) if (-NOT $elevated) { throw New-Object System.Exception("This function requires elevated permissions. Run PowerShell as an Administrator and import the module again.") } - # add disclaimer that this feature is currently under preview - if (!$Force) { - "This feature is currently under preview. Please report any issues to https://github.com/microsoft/SdnDiagnostics/issues so we can accurately track any issues and help unblock your cert rotation." | Trace-Output -Level:Warning - $confirm = Confirm-UserInput -Message "Do you want to proceed with certificate rotation? [Y/N]:" - if (-NOT $confirm) { - "User has opted to abort the operation. Terminating operation" | Trace-Output -Level:Warning - return - } + $config = Get-SdnModuleConfiguration -Role 'NetworkController' + $confirmFeatures = Confirm-RequiredFeaturesInstalled -Name $config.windowsFeature + if (-NOT ($confirmFeatures)) { + throw New-Object System.NotSupportedException("The current machine is not a NetworkController, run this on NetworkController.") } $array = @() @@ -159,7 +128,7 @@ function Start-SdnServerCertificateRotation { else { # in instances where the certificate property does not exist, we will need to add it # this typically will occur if converting from CA issued certificate to self-signed certificate - $server.properties | Add-Member -MemberType NoteProperty -Name 'certificate' -Value $encoding + $server.properties | Add-Member -MemberType NoteProperty -Name 'certificate' -Value $encoding -Force } $jsonBody = $server | ConvertTo-Json -Depth 100 From d100039930bcb5ec71f0f70aac0731311ef75e01 Mon Sep 17 00:00:00 2001 From: Adam Rudell Date: Thu, 4 Apr 2024 11:52:50 -0500 Subject: [PATCH 4/9] move NC SF cmdlets to new module --- src/SdnDiagnostics.psd1 | 1 + ...etworkController.ServiceFabric.Helper.psm1 | 62 +++++++++++++++++++ ...nDiag.NetworkController.ServiceFabric.psm1 | 14 +++++ ...iceFabricManifestFromNetworkController.ps1 | 0 ...rviceFabricManifestToNetworkController.ps1 | 0 ...kControllerNodeInfoFromClusterManifest.ps1 | 0 ...tworkControllerInfoFromClusterManifest.ps1 | 0 .../Get-SdnNetworkControllerInfoOffline.ps1 | 0 .../New-NetworkControllerClusterSecret.ps1 | 0 .../Start-SdnExpiredCertificateRotation.ps1 | 0 ...NetworkControllerCertificateInManifest.ps1 | 0 .../Update-NetworkControllerConfig.ps1 | 0 .../private/Update-ServiceFabricCluster.ps1 | 0 .../Wait-NetworkControllerAppHealthy.ps1 | 0 .../Wait-ServiceFabricClusterHealthy.ps1 | 0 .../Get-SdnNetworkControllerClusterInfo.ps1 | 0 .../public/Get-SdnNetworkControllerNode.ps1 | 0 .../Get-SdnServiceFabricApplicationHealth.ps1 | 0 .../Get-SdnServiceFabricClusterConfig.ps1 | 0 .../Get-SdnServiceFabricClusterHealth.ps1 | 0 .../Get-SdnServiceFabricClusterManifest.ps1 | 0 .../public/Get-SdnServiceFabricNode.ps1 | 0 .../public/Get-SdnServiceFabricPartition.ps1 | 0 .../public/Get-SdnServiceFabricReplica.ps1 | 0 .../public/Get-SdnServiceFabricService.ps1 | 0 .../public/Invoke-SdnServiceFabricCommand.ps1 | 0 .../public/Move-SdnServiceFabricReplica.ps1 | 0 .../public/Set-SdnNetworkController.ps1 | 0 .../Set-SdnServiceFabricClusterConfig.ps1 | 0 .../SdnDiag.NetworkController.Config.psd1 | 19 +----- .../SdnDiag.NetworkController.Helper.psm1 | 60 ------------------ .../SdnDiag.NetworkController.psm1 | 1 + 32 files changed, 80 insertions(+), 77 deletions(-) create mode 100644 src/modules/SdnDiag.NetworkController.ServiceFabric/SdnDiag.NetworkController.ServiceFabric.Helper.psm1 create mode 100644 src/modules/SdnDiag.NetworkController.ServiceFabric/SdnDiag.NetworkController.ServiceFabric.psm1 rename src/modules/{SdnDiag.NetworkController => SdnDiag.NetworkController.ServiceFabric}/private/Copy-ServiceFabricManifestFromNetworkController.ps1 (100%) rename src/modules/{SdnDiag.NetworkController => SdnDiag.NetworkController.ServiceFabric}/private/Copy-ServiceFabricManifestToNetworkController.ps1 (100%) rename src/modules/{SdnDiag.NetworkController => SdnDiag.NetworkController.ServiceFabric}/private/Get-NetworkControllerNodeInfoFromClusterManifest.ps1 (100%) rename src/modules/{SdnDiag.NetworkController => SdnDiag.NetworkController.ServiceFabric}/private/Get-SdnNetworkControllerInfoFromClusterManifest.ps1 (100%) rename src/modules/{SdnDiag.NetworkController => SdnDiag.NetworkController.ServiceFabric}/private/Get-SdnNetworkControllerInfoOffline.ps1 (100%) rename src/modules/{SdnDiag.NetworkController => SdnDiag.NetworkController.ServiceFabric}/private/New-NetworkControllerClusterSecret.ps1 (100%) rename src/modules/{SdnDiag.NetworkController => SdnDiag.NetworkController.ServiceFabric}/private/Start-SdnExpiredCertificateRotation.ps1 (100%) rename src/modules/{SdnDiag.NetworkController => SdnDiag.NetworkController.ServiceFabric}/private/Update-NetworkControllerCertificateInManifest.ps1 (100%) rename src/modules/{SdnDiag.NetworkController => SdnDiag.NetworkController.ServiceFabric}/private/Update-NetworkControllerConfig.ps1 (100%) rename src/modules/{SdnDiag.NetworkController => SdnDiag.NetworkController.ServiceFabric}/private/Update-ServiceFabricCluster.ps1 (100%) rename src/modules/{SdnDiag.NetworkController => SdnDiag.NetworkController.ServiceFabric}/private/Wait-NetworkControllerAppHealthy.ps1 (100%) rename src/modules/{SdnDiag.NetworkController => SdnDiag.NetworkController.ServiceFabric}/private/Wait-ServiceFabricClusterHealthy.ps1 (100%) rename src/modules/{SdnDiag.NetworkController => SdnDiag.NetworkController.ServiceFabric}/public/Get-SdnNetworkControllerClusterInfo.ps1 (100%) rename src/modules/{SdnDiag.NetworkController => SdnDiag.NetworkController.ServiceFabric}/public/Get-SdnNetworkControllerNode.ps1 (100%) rename src/modules/{SdnDiag.NetworkController => SdnDiag.NetworkController.ServiceFabric}/public/Get-SdnServiceFabricApplicationHealth.ps1 (100%) rename src/modules/{SdnDiag.NetworkController => SdnDiag.NetworkController.ServiceFabric}/public/Get-SdnServiceFabricClusterConfig.ps1 (100%) rename src/modules/{SdnDiag.NetworkController => SdnDiag.NetworkController.ServiceFabric}/public/Get-SdnServiceFabricClusterHealth.ps1 (100%) rename src/modules/{SdnDiag.NetworkController => SdnDiag.NetworkController.ServiceFabric}/public/Get-SdnServiceFabricClusterManifest.ps1 (100%) rename src/modules/{SdnDiag.NetworkController => SdnDiag.NetworkController.ServiceFabric}/public/Get-SdnServiceFabricNode.ps1 (100%) rename src/modules/{SdnDiag.NetworkController => SdnDiag.NetworkController.ServiceFabric}/public/Get-SdnServiceFabricPartition.ps1 (100%) rename src/modules/{SdnDiag.NetworkController => SdnDiag.NetworkController.ServiceFabric}/public/Get-SdnServiceFabricReplica.ps1 (100%) rename src/modules/{SdnDiag.NetworkController => SdnDiag.NetworkController.ServiceFabric}/public/Get-SdnServiceFabricService.ps1 (100%) rename src/modules/{SdnDiag.NetworkController => SdnDiag.NetworkController.ServiceFabric}/public/Invoke-SdnServiceFabricCommand.ps1 (100%) rename src/modules/{SdnDiag.NetworkController => SdnDiag.NetworkController.ServiceFabric}/public/Move-SdnServiceFabricReplica.ps1 (100%) rename src/modules/{SdnDiag.NetworkController => SdnDiag.NetworkController.ServiceFabric}/public/Set-SdnNetworkController.ps1 (100%) rename src/modules/{SdnDiag.NetworkController => SdnDiag.NetworkController.ServiceFabric}/public/Set-SdnServiceFabricClusterConfig.ps1 (100%) diff --git a/src/SdnDiagnostics.psd1 b/src/SdnDiagnostics.psd1 index 030b8d02..06f887dd 100644 --- a/src/SdnDiagnostics.psd1 +++ b/src/SdnDiagnostics.psd1 @@ -35,6 +35,7 @@ 'modules\SdnDiag.Health\SdnDiag.Health.psm1' 'modules\SdnDiag.LoadBalancerMux\SdnDiag.LoadBalancerMux.psm1' 'modules\SdnDiag.NetworkController\SdnDiag.NetworkController.psm1' + 'modules\SdnDiag.NetworkController.ServiceFabric\SdnDiag.NetworkController.ServiceFabric.psm1' 'modules\SdnDiag.Server\SdnDiag.Server.psm1' 'modules\SdnDiag.Utilities\SdnDiag.Utilities.psm1' 'modules\Test-SdnExpressBgp.psm1' diff --git a/src/modules/SdnDiag.NetworkController.ServiceFabric/SdnDiag.NetworkController.ServiceFabric.Helper.psm1 b/src/modules/SdnDiag.NetworkController.ServiceFabric/SdnDiag.NetworkController.ServiceFabric.Helper.psm1 new file mode 100644 index 00000000..9c6c0449 --- /dev/null +++ b/src/modules/SdnDiag.NetworkController.ServiceFabric/SdnDiag.NetworkController.ServiceFabric.Helper.psm1 @@ -0,0 +1,62 @@ +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT License. + +$scriptBlocks = @{ + ServiceFabricServiceName = { + param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameters) + $serviceName = @( + 'fabric:/NetworkController/ApiService' + 'fabric:/NetworkController/BackupRestore' + 'fabric:/NetworkController/ControllerService' + 'fabric:/NetworkController/FirewallService' + 'fabric:/NetworkController/FnmService' + 'fabric:/NetworkController/GatewayManager' + 'fabric:/NetworkController/HelperService' + 'fabric:/NetworkController/ServiceInsertion' + 'fabric:/NetworkController/SlbManagerService' + 'fabric:/NetworkController/UpdateService' + 'fabric:/NetworkController/VSwitchService' + ) + + if ([string]::IsNullOrEmpty($wordToComplete)) { + return ($serviceName | Sort-Object) + } + + return $serviceName | Where-Object {$_ -ilike "*$wordToComplete*"} | Sort-Object + } + + ServiceFabricServiceTypeName = { + param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameters) + $serviceTypeName = @( + 'ApiService' + 'BackupRestore' + 'ControllerService' + 'FirewallService' + 'FnmService' + 'GatewayManager' + 'HelperService' + 'ServiceInsertion' + 'SlbManagerService' + 'UpdateService' + 'VSwitchService' + ) + + if ([string]::IsNullOrEmpty($wordToComplete)) { + return ($serviceTypeName | Sort-Object) + } + + return $serviceTypeName | Where-Object {$_ -ilike "*$wordToComplete*"} | Sort-Object + } +} + +Register-ArgumentCompleter -CommandName 'Get-SdnServiceFabricReplica' -ParameterName 'ServiceName' -ScriptBlock $scriptBlocks.ServiceFabricServiceName +Register-ArgumentCompleter -CommandName 'Get-SdnServiceFabricReplica' -ParameterName 'ServiceTypeName' -ScriptBlock $scriptBlocks.ServiceFabricServiceTypeName + +Register-ArgumentCompleter -CommandName 'Get-SdnServiceFabricService' -ParameterName 'ServiceName' -ScriptBlock $scriptBlocks.ServiceFabricServiceName +Register-ArgumentCompleter -CommandName 'Get-SdnServiceFabricService' -ParameterName 'ServiceTypeName' -ScriptBlock $scriptBlocks.ServiceFabricServiceTypeName + +Register-ArgumentCompleter -CommandName 'Get-SdnServiceFabricPartition' -ParameterName 'ServiceName' -ScriptBlock $scriptBlocks.ServiceFabricServiceName +Register-ArgumentCompleter -CommandName 'Get-SdnServiceFabricPartition' -ParameterName 'ServiceTypeName' -ScriptBlock $scriptBlocks.ServiceFabricServiceTypeName + +Register-ArgumentCompleter -CommandName 'Move-SdnServiceFabricReplica' -ParameterName 'ServiceName' -ScriptBlock $scriptBlocks.ServiceFabricServiceName +Register-ArgumentCompleter -CommandName 'Move-SdnServiceFabricReplica' -ParameterName 'ServiceTypeName' -ScriptBlock $scriptBlocks.ServiceFabricServiceTypeName diff --git a/src/modules/SdnDiag.NetworkController.ServiceFabric/SdnDiag.NetworkController.ServiceFabric.psm1 b/src/modules/SdnDiag.NetworkController.ServiceFabric/SdnDiag.NetworkController.ServiceFabric.psm1 new file mode 100644 index 00000000..dfba3f58 --- /dev/null +++ b/src/modules/SdnDiag.NetworkController.ServiceFabric/SdnDiag.NetworkController.ServiceFabric.psm1 @@ -0,0 +1,14 @@ +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT License. + +Import-Module $PSScriptRoot\SdnDiag.NetworkController.ServiceFabric.Helper.psm1 +Import-Module $PSScriptRoot\..\SdnDiag.Common\SdnDiag.Common.psm1 +Import-Module $PSScriptRoot\..\SdnDiag.Utilities\SdnDiag.Utilities.psm1 + +# create local variable to store configuration data +$configurationData = Import-PowerShellDataFile -Path $PSScriptRoot\SdnDiag.NetworkController.ServiceFabric.Config.psd1 +New-Variable -Name 'SdnDiagnostics_NC_SF' -Scope 'Script' -Force -Value @{ + Config = $configurationData +} + +##### FUNCTIONS AUTO-POPULATED BELOW THIS LINE DURING BUILD ##### diff --git a/src/modules/SdnDiag.NetworkController/private/Copy-ServiceFabricManifestFromNetworkController.ps1 b/src/modules/SdnDiag.NetworkController.ServiceFabric/private/Copy-ServiceFabricManifestFromNetworkController.ps1 similarity index 100% rename from src/modules/SdnDiag.NetworkController/private/Copy-ServiceFabricManifestFromNetworkController.ps1 rename to src/modules/SdnDiag.NetworkController.ServiceFabric/private/Copy-ServiceFabricManifestFromNetworkController.ps1 diff --git a/src/modules/SdnDiag.NetworkController/private/Copy-ServiceFabricManifestToNetworkController.ps1 b/src/modules/SdnDiag.NetworkController.ServiceFabric/private/Copy-ServiceFabricManifestToNetworkController.ps1 similarity index 100% rename from src/modules/SdnDiag.NetworkController/private/Copy-ServiceFabricManifestToNetworkController.ps1 rename to src/modules/SdnDiag.NetworkController.ServiceFabric/private/Copy-ServiceFabricManifestToNetworkController.ps1 diff --git a/src/modules/SdnDiag.NetworkController/private/Get-NetworkControllerNodeInfoFromClusterManifest.ps1 b/src/modules/SdnDiag.NetworkController.ServiceFabric/private/Get-NetworkControllerNodeInfoFromClusterManifest.ps1 similarity index 100% rename from src/modules/SdnDiag.NetworkController/private/Get-NetworkControllerNodeInfoFromClusterManifest.ps1 rename to src/modules/SdnDiag.NetworkController.ServiceFabric/private/Get-NetworkControllerNodeInfoFromClusterManifest.ps1 diff --git a/src/modules/SdnDiag.NetworkController/private/Get-SdnNetworkControllerInfoFromClusterManifest.ps1 b/src/modules/SdnDiag.NetworkController.ServiceFabric/private/Get-SdnNetworkControllerInfoFromClusterManifest.ps1 similarity index 100% rename from src/modules/SdnDiag.NetworkController/private/Get-SdnNetworkControllerInfoFromClusterManifest.ps1 rename to src/modules/SdnDiag.NetworkController.ServiceFabric/private/Get-SdnNetworkControllerInfoFromClusterManifest.ps1 diff --git a/src/modules/SdnDiag.NetworkController/private/Get-SdnNetworkControllerInfoOffline.ps1 b/src/modules/SdnDiag.NetworkController.ServiceFabric/private/Get-SdnNetworkControllerInfoOffline.ps1 similarity index 100% rename from src/modules/SdnDiag.NetworkController/private/Get-SdnNetworkControllerInfoOffline.ps1 rename to src/modules/SdnDiag.NetworkController.ServiceFabric/private/Get-SdnNetworkControllerInfoOffline.ps1 diff --git a/src/modules/SdnDiag.NetworkController/private/New-NetworkControllerClusterSecret.ps1 b/src/modules/SdnDiag.NetworkController.ServiceFabric/private/New-NetworkControllerClusterSecret.ps1 similarity index 100% rename from src/modules/SdnDiag.NetworkController/private/New-NetworkControllerClusterSecret.ps1 rename to src/modules/SdnDiag.NetworkController.ServiceFabric/private/New-NetworkControllerClusterSecret.ps1 diff --git a/src/modules/SdnDiag.NetworkController/private/Start-SdnExpiredCertificateRotation.ps1 b/src/modules/SdnDiag.NetworkController.ServiceFabric/private/Start-SdnExpiredCertificateRotation.ps1 similarity index 100% rename from src/modules/SdnDiag.NetworkController/private/Start-SdnExpiredCertificateRotation.ps1 rename to src/modules/SdnDiag.NetworkController.ServiceFabric/private/Start-SdnExpiredCertificateRotation.ps1 diff --git a/src/modules/SdnDiag.NetworkController/private/Update-NetworkControllerCertificateInManifest.ps1 b/src/modules/SdnDiag.NetworkController.ServiceFabric/private/Update-NetworkControllerCertificateInManifest.ps1 similarity index 100% rename from src/modules/SdnDiag.NetworkController/private/Update-NetworkControllerCertificateInManifest.ps1 rename to src/modules/SdnDiag.NetworkController.ServiceFabric/private/Update-NetworkControllerCertificateInManifest.ps1 diff --git a/src/modules/SdnDiag.NetworkController/private/Update-NetworkControllerConfig.ps1 b/src/modules/SdnDiag.NetworkController.ServiceFabric/private/Update-NetworkControllerConfig.ps1 similarity index 100% rename from src/modules/SdnDiag.NetworkController/private/Update-NetworkControllerConfig.ps1 rename to src/modules/SdnDiag.NetworkController.ServiceFabric/private/Update-NetworkControllerConfig.ps1 diff --git a/src/modules/SdnDiag.NetworkController/private/Update-ServiceFabricCluster.ps1 b/src/modules/SdnDiag.NetworkController.ServiceFabric/private/Update-ServiceFabricCluster.ps1 similarity index 100% rename from src/modules/SdnDiag.NetworkController/private/Update-ServiceFabricCluster.ps1 rename to src/modules/SdnDiag.NetworkController.ServiceFabric/private/Update-ServiceFabricCluster.ps1 diff --git a/src/modules/SdnDiag.NetworkController/private/Wait-NetworkControllerAppHealthy.ps1 b/src/modules/SdnDiag.NetworkController.ServiceFabric/private/Wait-NetworkControllerAppHealthy.ps1 similarity index 100% rename from src/modules/SdnDiag.NetworkController/private/Wait-NetworkControllerAppHealthy.ps1 rename to src/modules/SdnDiag.NetworkController.ServiceFabric/private/Wait-NetworkControllerAppHealthy.ps1 diff --git a/src/modules/SdnDiag.NetworkController/private/Wait-ServiceFabricClusterHealthy.ps1 b/src/modules/SdnDiag.NetworkController.ServiceFabric/private/Wait-ServiceFabricClusterHealthy.ps1 similarity index 100% rename from src/modules/SdnDiag.NetworkController/private/Wait-ServiceFabricClusterHealthy.ps1 rename to src/modules/SdnDiag.NetworkController.ServiceFabric/private/Wait-ServiceFabricClusterHealthy.ps1 diff --git a/src/modules/SdnDiag.NetworkController/public/Get-SdnNetworkControllerClusterInfo.ps1 b/src/modules/SdnDiag.NetworkController.ServiceFabric/public/Get-SdnNetworkControllerClusterInfo.ps1 similarity index 100% rename from src/modules/SdnDiag.NetworkController/public/Get-SdnNetworkControllerClusterInfo.ps1 rename to src/modules/SdnDiag.NetworkController.ServiceFabric/public/Get-SdnNetworkControllerClusterInfo.ps1 diff --git a/src/modules/SdnDiag.NetworkController/public/Get-SdnNetworkControllerNode.ps1 b/src/modules/SdnDiag.NetworkController.ServiceFabric/public/Get-SdnNetworkControllerNode.ps1 similarity index 100% rename from src/modules/SdnDiag.NetworkController/public/Get-SdnNetworkControllerNode.ps1 rename to src/modules/SdnDiag.NetworkController.ServiceFabric/public/Get-SdnNetworkControllerNode.ps1 diff --git a/src/modules/SdnDiag.NetworkController/public/Get-SdnServiceFabricApplicationHealth.ps1 b/src/modules/SdnDiag.NetworkController.ServiceFabric/public/Get-SdnServiceFabricApplicationHealth.ps1 similarity index 100% rename from src/modules/SdnDiag.NetworkController/public/Get-SdnServiceFabricApplicationHealth.ps1 rename to src/modules/SdnDiag.NetworkController.ServiceFabric/public/Get-SdnServiceFabricApplicationHealth.ps1 diff --git a/src/modules/SdnDiag.NetworkController/public/Get-SdnServiceFabricClusterConfig.ps1 b/src/modules/SdnDiag.NetworkController.ServiceFabric/public/Get-SdnServiceFabricClusterConfig.ps1 similarity index 100% rename from src/modules/SdnDiag.NetworkController/public/Get-SdnServiceFabricClusterConfig.ps1 rename to src/modules/SdnDiag.NetworkController.ServiceFabric/public/Get-SdnServiceFabricClusterConfig.ps1 diff --git a/src/modules/SdnDiag.NetworkController/public/Get-SdnServiceFabricClusterHealth.ps1 b/src/modules/SdnDiag.NetworkController.ServiceFabric/public/Get-SdnServiceFabricClusterHealth.ps1 similarity index 100% rename from src/modules/SdnDiag.NetworkController/public/Get-SdnServiceFabricClusterHealth.ps1 rename to src/modules/SdnDiag.NetworkController.ServiceFabric/public/Get-SdnServiceFabricClusterHealth.ps1 diff --git a/src/modules/SdnDiag.NetworkController/public/Get-SdnServiceFabricClusterManifest.ps1 b/src/modules/SdnDiag.NetworkController.ServiceFabric/public/Get-SdnServiceFabricClusterManifest.ps1 similarity index 100% rename from src/modules/SdnDiag.NetworkController/public/Get-SdnServiceFabricClusterManifest.ps1 rename to src/modules/SdnDiag.NetworkController.ServiceFabric/public/Get-SdnServiceFabricClusterManifest.ps1 diff --git a/src/modules/SdnDiag.NetworkController/public/Get-SdnServiceFabricNode.ps1 b/src/modules/SdnDiag.NetworkController.ServiceFabric/public/Get-SdnServiceFabricNode.ps1 similarity index 100% rename from src/modules/SdnDiag.NetworkController/public/Get-SdnServiceFabricNode.ps1 rename to src/modules/SdnDiag.NetworkController.ServiceFabric/public/Get-SdnServiceFabricNode.ps1 diff --git a/src/modules/SdnDiag.NetworkController/public/Get-SdnServiceFabricPartition.ps1 b/src/modules/SdnDiag.NetworkController.ServiceFabric/public/Get-SdnServiceFabricPartition.ps1 similarity index 100% rename from src/modules/SdnDiag.NetworkController/public/Get-SdnServiceFabricPartition.ps1 rename to src/modules/SdnDiag.NetworkController.ServiceFabric/public/Get-SdnServiceFabricPartition.ps1 diff --git a/src/modules/SdnDiag.NetworkController/public/Get-SdnServiceFabricReplica.ps1 b/src/modules/SdnDiag.NetworkController.ServiceFabric/public/Get-SdnServiceFabricReplica.ps1 similarity index 100% rename from src/modules/SdnDiag.NetworkController/public/Get-SdnServiceFabricReplica.ps1 rename to src/modules/SdnDiag.NetworkController.ServiceFabric/public/Get-SdnServiceFabricReplica.ps1 diff --git a/src/modules/SdnDiag.NetworkController/public/Get-SdnServiceFabricService.ps1 b/src/modules/SdnDiag.NetworkController.ServiceFabric/public/Get-SdnServiceFabricService.ps1 similarity index 100% rename from src/modules/SdnDiag.NetworkController/public/Get-SdnServiceFabricService.ps1 rename to src/modules/SdnDiag.NetworkController.ServiceFabric/public/Get-SdnServiceFabricService.ps1 diff --git a/src/modules/SdnDiag.NetworkController/public/Invoke-SdnServiceFabricCommand.ps1 b/src/modules/SdnDiag.NetworkController.ServiceFabric/public/Invoke-SdnServiceFabricCommand.ps1 similarity index 100% rename from src/modules/SdnDiag.NetworkController/public/Invoke-SdnServiceFabricCommand.ps1 rename to src/modules/SdnDiag.NetworkController.ServiceFabric/public/Invoke-SdnServiceFabricCommand.ps1 diff --git a/src/modules/SdnDiag.NetworkController/public/Move-SdnServiceFabricReplica.ps1 b/src/modules/SdnDiag.NetworkController.ServiceFabric/public/Move-SdnServiceFabricReplica.ps1 similarity index 100% rename from src/modules/SdnDiag.NetworkController/public/Move-SdnServiceFabricReplica.ps1 rename to src/modules/SdnDiag.NetworkController.ServiceFabric/public/Move-SdnServiceFabricReplica.ps1 diff --git a/src/modules/SdnDiag.NetworkController/public/Set-SdnNetworkController.ps1 b/src/modules/SdnDiag.NetworkController.ServiceFabric/public/Set-SdnNetworkController.ps1 similarity index 100% rename from src/modules/SdnDiag.NetworkController/public/Set-SdnNetworkController.ps1 rename to src/modules/SdnDiag.NetworkController.ServiceFabric/public/Set-SdnNetworkController.ps1 diff --git a/src/modules/SdnDiag.NetworkController/public/Set-SdnServiceFabricClusterConfig.ps1 b/src/modules/SdnDiag.NetworkController.ServiceFabric/public/Set-SdnServiceFabricClusterConfig.ps1 similarity index 100% rename from src/modules/SdnDiag.NetworkController/public/Set-SdnServiceFabricClusterConfig.ps1 rename to src/modules/SdnDiag.NetworkController.ServiceFabric/public/Set-SdnServiceFabricClusterConfig.ps1 diff --git a/src/modules/SdnDiag.NetworkController/SdnDiag.NetworkController.Config.psd1 b/src/modules/SdnDiag.NetworkController/SdnDiag.NetworkController.Config.psd1 index 25f69fe2..257a7136 100644 --- a/src/modules/SdnDiag.NetworkController/SdnDiag.NetworkController.Config.psd1 +++ b/src/modules/SdnDiag.NetworkController/SdnDiag.NetworkController.Config.psd1 @@ -204,18 +204,9 @@ includeInResourceDump = $true } } - CommonPaths = @{ - serviceFabricLogDirectory = @( - "C:\ProgramData\Microsoft\Service Fabric\log\Traces" - "C:\ProgramData\Microsoft\Service Fabric\log\OperationalTraces" - "C:\ProgramData\Microsoft\Service Fabric\log\QueryTraces" - "C:\ProgramData\Microsoft\Service Fabric\log\CrashDumps" - "C:\ProgramData\Microsoft\Service Fabric\log\PerformanceCounters_WinFabPerfCtrFolder" - ) - } + CommonPaths = @{} EventLogProviders = @( "Microsoft-Windows-NetworkController*" - "Microsoft-ServiceFabric*" ) NetControllerStatePath = "C:\Windows\Tracing\SDNDiagnostics\NetworkControllerState" RegKeyPaths = @( @@ -223,12 +214,6 @@ "HKLM:\SYSTEM\CurrentControlSet\Services\NcHostAgent" "HKLM:\SYSTEM\CurrentControlSet\Services\Network Controller" ) - Services = @{ - FabricHostSvc = @{ - Properties = @{ - DisplayName = "Service Fabric Host Service" - } - } - } + Services = @{} } } diff --git a/src/modules/SdnDiag.NetworkController/SdnDiag.NetworkController.Helper.psm1 b/src/modules/SdnDiag.NetworkController/SdnDiag.NetworkController.Helper.psm1 index 24a9bceb..2a4efa50 100644 --- a/src/modules/SdnDiag.NetworkController/SdnDiag.NetworkController.Helper.psm1 +++ b/src/modules/SdnDiag.NetworkController/SdnDiag.NetworkController.Helper.psm1 @@ -47,63 +47,3 @@ enum SdnApiResource { VirtualServers VirtualSwitchManagerConfig } - -$scriptBlocks = @{ - ServiceFabricServiceName = { - param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameters) - $serviceName = @( - 'fabric:/NetworkController/ApiService' - 'fabric:/NetworkController/BackupRestore' - 'fabric:/NetworkController/ControllerService' - 'fabric:/NetworkController/FirewallService' - 'fabric:/NetworkController/FnmService' - 'fabric:/NetworkController/GatewayManager' - 'fabric:/NetworkController/HelperService' - 'fabric:/NetworkController/ServiceInsertion' - 'fabric:/NetworkController/SlbManagerService' - 'fabric:/NetworkController/UpdateService' - 'fabric:/NetworkController/VSwitchService' - ) - - if ([string]::IsNullOrEmpty($wordToComplete)) { - return ($serviceName | Sort-Object) - } - - return $serviceName | Where-Object {$_ -ilike "*$wordToComplete*"} | Sort-Object - } - - ServiceFabricServiceTypeName = { - param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameters) - $serviceTypeName = @( - 'ApiService' - 'BackupRestore' - 'ControllerService' - 'FirewallService' - 'FnmService' - 'GatewayManager' - 'HelperService' - 'ServiceInsertion' - 'SlbManagerService' - 'UpdateService' - 'VSwitchService' - ) - - if ([string]::IsNullOrEmpty($wordToComplete)) { - return ($serviceTypeName | Sort-Object) - } - - return $serviceTypeName | Where-Object {$_ -ilike "*$wordToComplete*"} | Sort-Object - } -} - -Register-ArgumentCompleter -CommandName 'Get-SdnServiceFabricReplica' -ParameterName 'ServiceName' -ScriptBlock $scriptBlocks.ServiceFabricServiceName -Register-ArgumentCompleter -CommandName 'Get-SdnServiceFabricReplica' -ParameterName 'ServiceTypeName' -ScriptBlock $scriptBlocks.ServiceFabricServiceTypeName - -Register-ArgumentCompleter -CommandName 'Get-SdnServiceFabricService' -ParameterName 'ServiceName' -ScriptBlock $scriptBlocks.ServiceFabricServiceName -Register-ArgumentCompleter -CommandName 'Get-SdnServiceFabricService' -ParameterName 'ServiceTypeName' -ScriptBlock $scriptBlocks.ServiceFabricServiceTypeName - -Register-ArgumentCompleter -CommandName 'Get-SdnServiceFabricPartition' -ParameterName 'ServiceName' -ScriptBlock $scriptBlocks.ServiceFabricServiceName -Register-ArgumentCompleter -CommandName 'Get-SdnServiceFabricPartition' -ParameterName 'ServiceTypeName' -ScriptBlock $scriptBlocks.ServiceFabricServiceTypeName - -Register-ArgumentCompleter -CommandName 'Move-SdnServiceFabricReplica' -ParameterName 'ServiceName' -ScriptBlock $scriptBlocks.ServiceFabricServiceName -Register-ArgumentCompleter -CommandName 'Move-SdnServiceFabricReplica' -ParameterName 'ServiceTypeName' -ScriptBlock $scriptBlocks.ServiceFabricServiceTypeName diff --git a/src/modules/SdnDiag.NetworkController/SdnDiag.NetworkController.psm1 b/src/modules/SdnDiag.NetworkController/SdnDiag.NetworkController.psm1 index fcaacfbf..e890aa5e 100644 --- a/src/modules/SdnDiag.NetworkController/SdnDiag.NetworkController.psm1 +++ b/src/modules/SdnDiag.NetworkController/SdnDiag.NetworkController.psm1 @@ -3,6 +3,7 @@ Using module .\SdnDiag.NetworkController.Helper.psm1 Import-Module $PSScriptRoot\SdnDiag.NetworkController.Helper.psm1 +Import-Module $PSScriptRoot\..\SdnDiag.NetworkController.ServiceFabric\SdnDiag.NetworkController.ServiceFabric.psm1 Import-Module $PSScriptRoot\..\SdnDiag.Common\SdnDiag.Common.psm1 Import-Module $PSScriptRoot\..\SdnDiag.Utilities\SdnDiag.Utilities.psm1 From fd486576f3f9121d06127f91a6b01458997aff37 Mon Sep 17 00:00:00 2001 From: Adam Rudell Date: Thu, 4 Apr 2024 22:28:52 -0500 Subject: [PATCH 5/9] save latest changes --- .../New-SdnCertificateRotationConfig.ps1 | 59 +++--- .../public/Start-SdnCertificateRotation.ps1 | 188 +++--------------- ...dnNetworkControllerCertificateRotation.ps1 | 23 +++ ...tworkControllerRestCertificateRotation.ps1 | 167 ++++++++++++++++ 4 files changed, 247 insertions(+), 190 deletions(-) create mode 100644 src/modules/SdnDiag.NetworkController/public/Start-SdnNetworkControllerRestCertificateRotation.ps1 diff --git a/src/modules/SdnDiag.NetworkController/public/New-SdnCertificateRotationConfig.ps1 b/src/modules/SdnDiag.NetworkController/public/New-SdnCertificateRotationConfig.ps1 index 8c6d1efa..fe54a45d 100644 --- a/src/modules/SdnDiag.NetworkController/public/New-SdnCertificateRotationConfig.ps1 +++ b/src/modules/SdnDiag.NetworkController/public/New-SdnCertificateRotationConfig.ps1 @@ -15,45 +15,52 @@ function New-SdnCertificateRotationConfig { [CmdletBinding()] param ( [Parameter(Mandatory = $false)] - [String]$NetworkController = $(HostName), + [String]$NetworkController = $env:COMPUTERNAME, [Parameter(Mandatory = $false)] [System.Management.Automation.PSCredential] [System.Management.Automation.Credential()] - $Credential = [System.Management.Automation.PSCredential]::Empty + $Credential = [System.Management.Automation.PSCredential]::Empty, + + [Parameter(Mandatory = $true)] + [ValidateSet('Rest','NetworkController','Server','LoadBalancerMux')] + [String]$CertificateType ) - try { - if (-NOT ($PSBoundParameters.ContainsKey('NetworkController'))) { - $config = Get-SdnModuleConfiguration -Role 'NetworkController' - $confirmFeatures = Confirm-RequiredFeaturesInstalled -Name $config.windowsFeature - if (-NOT ($confirmFeatures)) { - "The current machine is not a NetworkController, run this on NetworkController or use -NetworkController parameter to specify one" | Trace-Output -Level:Warning - return # don't throw exception, since this is a controlled scenario and we do not need stack exception tracing - } + $CertificateRotationConfig = @{} + $getNewestCertScript = { + param([string]$param1, [string]$param2, [string]$param3) + + if ([string]::IsNullOrWhiteSpace($param2)) { + $cert = Get-SdnCertificate -Path $param1 -Subject $param2 -NetworkControllerOid + } + else { + $cert = Get-SdnCertificate -Path $param1 -Subject $param3 -NetworkControllerOid + } + # get the certificate that has the expiration date furthest in the future + # we also want to ensure we filter for certificates that have the Network Controller OID + if ($cert) { + $cert = $cert | Sort-Object -Property NotAfter -Descending | Select-Object -First 1 + return $cert.Thumbprint } - $NcInfraInfo = Get-SdnNetworkControllerInfoOffline -NetworkController $NetworkController -Credential $Credential + return $null + } - $CertificateRotationConfig = @{} + try { + $NcInfraInfo = Get-SdnNetworkControllerInfoOffline -NetworkController $NetworkController -Credential $Credential $CertificateRotationConfig["ClusterCredentialType"] = $NcInfraInfo.ClusterCredentialType - $getNewestCertScript = { - param( - [String] - $certSubject - ) - # Default to return Node Certificate - if ([string]::IsNullOrEmpty($certSubject)) { - $NodeFQDN = (get-ciminstance win32_computersystem).DNSHostName + "." + (get-ciminstance win32_computersystem).Domain - $certSubject = "CN=$NodeFQDN" + switch ($CertificateType) { + 'Rest' { + $CertificateRotationConfig["NcRestCert"] = Invoke-PSRemoteCommand @{ + ComputerName = $NetworkController + ScriptBlock = $getNewestCertScript + Credential = $Credential + ArgumentList = @("Cert:\LocalMachine\My", "CN=$($NcInfraInfo.NcRestName)") + } } - - Write-Verbose "Looking for cert match $certSubject" - $cert = Get-ChildItem -Path Cert:\LocalMachine\My | ? { $_.Subject -ieq $certSubject } | Sort-Object -Property NotBefore -Descending | Select-Object -First 1 - return $cert.Thumbprint } - $CertificateRotationConfig["NcRestCert"] = Invoke-PSRemoteCommand -ComputerName $NetworkController -ScriptBlock $getNewestCertScript -ArgumentList "CN=$($NcInfraInfo.NcRestName)" -Credential $Credential if($NcInfraInfo.ClusterCredentialType -eq "X509"){ foreach ($ncNode in $($NcInfraInfo.NodeList)) { diff --git a/src/modules/SdnDiag.NetworkController/public/Start-SdnCertificateRotation.ps1 b/src/modules/SdnDiag.NetworkController/public/Start-SdnCertificateRotation.ps1 index 91272bbb..76f052a9 100644 --- a/src/modules/SdnDiag.NetworkController/public/Start-SdnCertificateRotation.ps1 +++ b/src/modules/SdnDiag.NetworkController/public/Start-SdnCertificateRotation.ps1 @@ -22,6 +22,12 @@ function Start-SdnCertificateRotation { [CmdletBinding(DefaultParameterSetName = 'GenerateCertificate')] param ( + [Parameter(Mandatory = $true, ParameterSetName = 'Pfx')] + [Parameter(Mandatory = $true, ParameterSetName = 'GenerateCertificate')] + [Parameter(Mandatory = $true, ParameterSetName = 'CertConfig')] + [ValidateSet('Rest','NetworkController','Server','LoadBalancerMux')] + [System.String]$CertificateType, + [Parameter(Mandatory = $true, ParameterSetName = 'Pfx')] [Parameter(Mandatory = $true, ParameterSetName = 'GenerateCertificate')] [Parameter(Mandatory = $true, ParameterSetName = 'CertConfig')] @@ -70,16 +76,6 @@ function Start-SdnCertificateRotation { throw New-Object System.NotSupportedException("The current machine is not a NetworkController, run this on NetworkController.") } - # add disclaimer that this feature is currently under preview - if (!$Force) { - "This feature is currently under preview. Please report any issues to https://github.com/microsoft/SdnDiagnostics/issues so we can accurately track any issues and help unblock your cert rotation." | Trace-Output -Level:Warning - $confirm = Confirm-UserInput -Message "Do you want to proceed with certificate rotation? [Y/N]:" - if (-NOT $confirm) { - "User has opted to abort the operation. Terminating operation" | Trace-Output -Level:Warning - return - } - } - try { "Starting certificate rotation" | Trace-Output @@ -177,26 +173,22 @@ function Start-SdnCertificateRotation { if ($PSCmdlet.ParameterSetName -ieq 'GenerateCertificate') { "== STAGE: CREATE SELF SIGNED CERTIFICATES ==" | Trace-Output - $newSelfSignedCert = New-SdnNetworkControllerRestCertificate -RestName $NcInfraInfo.NcRestName.ToString() -NotAfter $NotAfter -Path $CertPath.FullName ` - -CertPassword $CertPassword -Credential $Credential -FabricDetails $sdnFabricDetails - - $selfSignedRestCertFile = $newSelfSignedCert.FileInfo - - if ($rotateNCNodeCerts) { - $null = Invoke-PSRemoteCommand -ComputerName $sdnFabricDetails.NetworkController -Credential $Credential -ScriptBlock { - param( - [Parameter(Position = 0)][DateTime]$param1, - [Parameter(Position = 1)][SecureString]$param2, - [Parameter(Position = 2)][PSCredential]$param3, - [Parameter(Position = 3)][String]$param4, - [Parameter(Position = 4)][System.Object]$param5 - ) - - New-SdnNetworkControllerNodeCertificate -NotAfter $param1 -CertPassword $param2 -Credential $param3 -Path $param4 -FabricDetails $param5 - } -ArgumentList @($NotAfter, $CertPassword, $Credential, $CertPath.FullName, $sdnFabricDetails) + switch ($CertificateType) { + 'Rest' { + $newSelfSignedCert = New-SdnNetworkControllerRestCertificate -RestName $NcInfraInfo.NcRestName.ToString() -NotAfter $NotAfter -Path $CertPath.FullName ` + -CertPassword $CertPassword -Credential $Credential -FabricDetails $sdnFabricDetails + $selfSignedRestCertFile = $newSelfSignedCert.FileInfo + } + 'NetworkController' { + $newSelfSignedCert = New-SdnNetworkControllerCertificate -NotAfter $NotAfter -Path $CertPath.FullName -CertPassword $CertPassword -Credential $Credential + } + 'Server' { + $newSelfSignedCert = New-SdnServerCertificate -NotAfter $NotAfter -Path $CertPath.FullName -CertPassword $CertPassword -Credential $Credential + } + 'LoadBalancerMux' { + $newSelfSignedCert = New-SdnLoadBalancerMuxCertificate -NotAfter $NotAfter -Path $CertPath.FullName -CertPassword $CertPassword -Credential $Credential + } } - - $CertRotateConfig = New-SdnCertificateRotationConfig -Credential $Credential } ##################################### @@ -236,31 +228,14 @@ function Start-SdnCertificateRotation { throw New-Object System.NotSupportedException("Unable to validate certificate configuration") } - $updatedRestCertificate = Get-ChildItem -Path Cert:\LocalMachine\My | Where-Object { $_.Subject -ieq $currentRestCert.Subject } ` - | Sort-Object -Property NotBefore -Descending | Select-Object -First 1 + # grab the rest certificate with the latest expiration date + $updatedRestCertificate = Get-SdnCertificate -Path 'Cert:\LocalMachine\My' -Subject $currentRestCert.Subject -NetworkControllerOid ` + | Sort-Object -Property NotAfter -Descending | Select-Object -First 1 "Network Controller Rest Certificate {0} will be updated from [Thumbprint:{1} NotAfter:{2}] to [Thumbprint:{3} NotAfter:{4}]" ` -f $currentRestCert.Subject, $currentRestCert.Thumbprint, $currentRestCert.NotAfter, $CertRotateConfig["NcRestCert"], $updatedRestCertificate.NotAfter ` | Trace-Output -Level:Warning - if ($rotateNCNodeCerts) { - foreach ($node in $NcInfraInfo.NodeList) { - $nodeCertThumbprint = $certRotateConfig[$node.NodeName.ToLower()] - $currentNodeCert = Invoke-PSRemoteCommand -ComputerName $node.IpAddressOrFQDN -Credential $Credential -ScriptBlock { - Get-SdnNetworkControllerNodeCertificate - } - - $newNodeCert = Invoke-PSRemoteCommand -ComputerName $node.IpAddressOrFQDN -Credential $Credential -ScriptBlock { - param([Parameter(Position = 0)][String]$param1, [Parameter(Position = 1)][String]$param2) - Get-SdnCertificate -Path $param1 -Thumbprint $param2 - } -ArgumentList @('Cert:\LocalMachine\My', $nodeCertThumbprint) - - "Network Controller Node Certificate {0} will be updated from [Thumbprint:{1} NotAfter:{2}] to [Thumbprint:{3} NotAfter:{4}]" ` - -f $currentNodeCert.Subject, $currentNodeCert.Thumbprint, $currentNodeCert.NotAfter, ` - $newNodeCert.Thumbprint, $newNodeCert.NotAfter | Trace-Output -Level:Warning - } - } - if (!$Force) { $confirm = Confirm-UserInput if (-NOT $confirm) { @@ -280,121 +255,6 @@ function Start-SdnCertificateRotation { Start-SdnExpiredCertificateRotation -CertRotateConfig $CertRotateConfig -Credential $Credential -NcRestCredential $NcRestCredential } - ##################################### - # - # Rotate NC Northbound Certificate (REST) - # - ##################################### - - "== STAGE: ROTATE NC REST CERTIFICATE ==" | Trace-Output - - $null = Invoke-CertRotateCommand -Command 'Set-NetworkController' -Credential $Credential -Thumbprint $CertRotateConfig["NcRestCert"] - - "Waiting for 5 minutes before proceeding to the next step. Script will resume at {0}" -f (Get-Date).AddMinutes(5).ToUniversalTime().ToString() | Trace-Output - Start-Sleep -Seconds 300 - - ##################################### - # - # Rotate Cluster Certificate - # - ##################################### - - "== STAGE: ROTATE NC CLUSTER CERTIFICATE ==" | Trace-Output - - $null = Invoke-CertRotateCommand -Command 'Set-NetworkControllerCluster' -Credential $Credential -Thumbprint $CertRotateConfig["NcRestCert"] - - "Waiting for 5 minutes before proceeding to the next step. Script will resume at {0}" -f (Get-Date).AddMinutes(5).ToUniversalTime().ToString() | Trace-Output - Start-Sleep -Seconds 300 - - ##################################### - # - # Rotate NC Node Certificates - # - ##################################### - - if ($rotateNCNodeCerts) { - "== STAGE: ROTATE NC NODE CERTIFICATE ==" | Trace-Output - - foreach ($node in $NcInfraInfo.NodeList) { - $nodeCertThumbprint = $certRotateConfig[$node.NodeName.ToLower()] - $null = Invoke-CertRotateCommand -Command 'Set-NetworkControllerNode' -NetworkController $node.IpAddressOrFQDN -Credential $Credential -Thumbprint $nodeCertThumbprint - - "Waiting for 2 minutes before proceeding to the next step. Script will resume at {0}" -f (Get-Date).AddMinutes(5).ToUniversalTime().ToString() | Trace-Output - Start-Sleep -Seconds 120 - } - } - - ##################################### - # - # Rotate NC Southbound Certificates - # - ##################################### - - "== STAGE: ROTATE SOUTHBOUND CERTIFICATE CREDENTIALS ==" | Trace-Output - - $null = Update-NetworkControllerCredentialResource -NcUri "https://$($NcInfraInfo.NcRestName)" -Credential $NcRestCredential ` - -NewRestCertThumbprint $CertRotateConfig["NcRestCert"] -ErrorAction Stop - - "Southbound certificate rotation completed" | Trace-Output - - ##################################### - # - # Certificate Seeding (Southbound Nodes) - # - ##################################### - - # if nc was unhealthy and unable to determine southbound devices in the dataplane earlier - # we now want to check to see if nc is healthy and if we need to install the rest cert (for self-signed) to southbound devices - if ($postRotateSBRestCert) { - if ($selfSignedRestCertFile) { - $sdnFabricDetails = Get-SdnInfrastructureInfo -Credential $Credential -NcRestCredential $NcRestCredential -Force - $southBoundNodes = @() - if ($null -ne $sdnFabricDetails.LoadBalancerMux) { - $southBoundNodes += $sdnFabricDetails.LoadBalancerMux - } - if ($null -ne $sdnFabricDetails.Server) { - $southBoundNodes += $sdnFabricDetails.Server - } - - if ($southBoundNodes) { - "== STAGE: REST SELF-SIGNED CERTIFICATE SEEDING (Southbound Nodes) ==" | Trace-Output - - # ensure that we have the latest version of sdnDiagnostics module on the southbound devices - Install-SdnDiagnostics -ComputerName $southBoundNodes -Credential $Credential -ErrorAction Stop - - "[REST CERT] Installing self-signed certificate to {0}" -f ($southBoundNodes -join ', ') | Trace-Output - [System.String]$remoteFilePath = Join-Path -Path $CertPath.FullName -ChildPath $selfSignedRestCertFile.Name - Copy-FileToRemoteComputer -ComputerName $southBoundNodes -Credential $Credential -Path $selfSignedRestCertFile.FullName -Destination $remoteFilePath - $null = Invoke-PSRemoteCommand -ComputerName $southBoundNodes -Credential $Credential -ScriptBlock { - param([Parameter(Position = 0)][String]$param1, [Parameter(Position = 1)][String]$param2) - Import-SdnCertificate -FilePath $param1 -CertStore $param2 - } -ArgumentList @($remoteFilePath, 'Cert:\LocalMachine\Root') -ErrorAction Stop - } - } - } - - ##################################### - # - # Restart services - # - ##################################### - - "== STAGE: RESTART NETWORK CONTROLLER SERVICES ==" | Trace-Output - # restart the network controller services - # this will force new TLS connections to be established to southbound devices - # ensuring that the new certificates are used and we are able to push policies successfully - - # check to determine if we have a multi-node NC cluster and if so, leverage the SF cmdlets to move the replicas - # otherwise, we will just stop the processes and let SF restart them automatically - if ($sdnFabricDetails.NetworkController.Count -gt 1) { - Move-SdnServiceFabricReplica -ServiceTypeName 'SlbManagerService' - Move-SdnServiceFabricReplica -ServiceTypeName 'VSwitchService' - } - else { - Get-Process -Name 'SDNFW' | Stop-Process -Force -ErrorAction Continue - Get-Process -Name 'SDNSLBM' | Stop-Process -Force -ErrorAction Continue - } - "Certificate rotation has completed" | Trace-Output } catch { diff --git a/src/modules/SdnDiag.NetworkController/public/Start-SdnNetworkControllerCertificateRotation.ps1 b/src/modules/SdnDiag.NetworkController/public/Start-SdnNetworkControllerCertificateRotation.ps1 index 70ef581f..e0279960 100644 --- a/src/modules/SdnDiag.NetworkController/public/Start-SdnNetworkControllerCertificateRotation.ps1 +++ b/src/modules/SdnDiag.NetworkController/public/Start-SdnNetworkControllerCertificateRotation.ps1 @@ -108,6 +108,29 @@ function Start-SdnNetworkControllerCertificateRotation { New-SdnNetworkControllerNodeCertificate -NotAfter $param1 -CertPassword $param2 -Credential $param3 -Path $param4 -FabricDetails $param5 } -ArgumentList @($NotAfter, $CertPassword, $Credential, $CertPath.FullName, $sdnFabricDetails) + foreach ($node in $NcInfraInfo.NodeList) { + $nodeCertThumbprint = $certRotateConfig[$node.NodeName.ToLower()] + $currentNodeCert = Invoke-PSRemoteCommand -ComputerName $node.IpAddressOrFQDN -Credential $Credential -ScriptBlock { + Get-SdnNetworkControllerNodeCertificate + } + + $newNodeCert = Invoke-PSRemoteCommand -ComputerName $node.IpAddressOrFQDN -Credential $Credential -ScriptBlock { + param([Parameter(Position = 0)][String]$param1, [Parameter(Position = 1)][String]$param2) + Get-SdnCertificate -Path $param1 -Thumbprint $param2 + } -ArgumentList @('Cert:\LocalMachine\My', $nodeCertThumbprint) + + "Network Controller Node Certificate {0} will be updated from [Thumbprint:{1} NotAfter:{2}] to [Thumbprint:{3} NotAfter:{4}]" ` + -f $currentNodeCert.Subject, $currentNodeCert.Thumbprint, $currentNodeCert.NotAfter, ` + $newNodeCert.Thumbprint, $newNodeCert.NotAfter | Trace-Output -Level:Warning + } + + foreach ($node in $NcInfraInfo.NodeList) { + $nodeCertThumbprint = $certRotateConfig[$node.NodeName.ToLower()] + $null = Invoke-CertRotateCommand -Command 'Set-NetworkControllerNode' -NetworkController $node.IpAddressOrFQDN -Credential $Credential -Thumbprint $nodeCertThumbprint + + "Waiting for 2 minutes before proceeding to the next step. Script will resume at {0}" -f (Get-Date).AddMinutes(5).ToUniversalTime().ToString() | Trace-Output + Start-Sleep -Seconds 120 + } } catch { $_ | Trace-Exception diff --git a/src/modules/SdnDiag.NetworkController/public/Start-SdnNetworkControllerRestCertificateRotation.ps1 b/src/modules/SdnDiag.NetworkController/public/Start-SdnNetworkControllerRestCertificateRotation.ps1 new file mode 100644 index 00000000..2fa216fb --- /dev/null +++ b/src/modules/SdnDiag.NetworkController/public/Start-SdnNetworkControllerRestCertificateRotation.ps1 @@ -0,0 +1,167 @@ +function Start-SdnNetworkControllerRestCertificateRotation { + <# + .SYNOPSIS + Performs a rotate of the Network Controller Rest and Encryption certificate. + .PARAMETER Credential + Specifies a user account that has permission to perform this action. The default is the current user. + .PARAMETER NcRestCredential + Specifies a user account that has permission to access the northbound NC API interface. The default is the current user. + .PARAMETER CertPath + Path directory where certificate(s) .pfx files are located for use with certificate rotation. + .PARAMETER GenerateCertificate + Switch to determine if certificate rotate function should generate self-signed certificates. + .PARAMETER CertPassword + SecureString password for accessing the .pfx files, or if using -GenerateCertificate, what the .pfx files will be encrypted with. + .PARAMETER NotAfter + Expiration date when using -GenerateCertificate. If ommited, defaults to 3 years. + .PARAMETER CertRotateConfig + The Config generated by New-SdnCertificateRotationConfig to include NC REST certificate thumbprint and node certificate thumbprint. + .PARAMETER Force + Switch to force the rotation without being prompted, when Service Fabric is unhealthy. + #> + + [CmdletBinding(DefaultParameterSetName = 'GenerateCertificate')] + param ( + [Parameter(Mandatory = $true, ParameterSetName = 'Pfx')] + [Parameter(Mandatory = $true, ParameterSetName = 'GenerateCertificate')] + [Parameter(Mandatory = $true, ParameterSetName = 'CertConfig')] + [System.Management.Automation.PSCredential] + [System.Management.Automation.Credential()] + $Credential, + + [Parameter(Mandatory = $false, ParameterSetName = 'Pfx')] + [Parameter(Mandatory = $false, ParameterSetName = 'GenerateCertificate')] + [Parameter(Mandatory = $false, ParameterSetName = 'CertConfig')] + [System.Management.Automation.PSCredential] + [System.Management.Automation.Credential()] + $NcRestCredential = [System.Management.Automation.PSCredential]::Empty, + + [Parameter(Mandatory = $true, ParameterSetName = 'Pfx')] + [System.String]$CertPath, + + [Parameter(Mandatory = $true, ParameterSetName = 'GenerateCertificate')] + [Switch]$GenerateCertificate, + + [Parameter(Mandatory = $true, ParameterSetName = 'Pfx')] + [Parameter(Mandatory = $true, ParameterSetName = 'GenerateCertificate')] + [System.Security.SecureString]$CertPassword, + + [Parameter(Mandatory = $false, ParameterSetName = 'GenerateCertificate')] + [datetime]$NotAfter = (Get-Date).AddYears(3), + + [Parameter(Mandatory = $true, ParameterSetName = 'CertConfig')] + [hashtable]$CertRotateConfig, + + [Parameter(Mandatory = $false, ParameterSetName = 'Pfx')] + [Parameter(Mandatory = $false, ParameterSetName = 'GenerateCertificate')] + [Parameter(Mandatory = $false, ParameterSetName = 'CertConfig')] + [switch]$Force + ) + + # ensure that the module is running as local administrator + $elevated = ([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator) + if (-NOT $elevated) { + throw New-Object System.Exception("This function requires elevated permissions. Run PowerShell as an Administrator and import the module again.") + } + + $config = Get-SdnModuleConfiguration -Role 'NetworkController' + $confirmFeatures = Confirm-RequiredFeaturesInstalled -Name $config.windowsFeature + if (-NOT ($confirmFeatures)) { + throw New-Object System.NotSupportedException("The current machine is not a NetworkController, run this on NetworkController.") + } + + # purge any existing remote sessions to prevent situation where + # we leverage a session without credentials + Remove-PSRemotingSession + + try { + # grab the rest certificate with the latest expiration date + $updatedRestCertificate = Get-SdnCertificate -Path 'Cert:\LocalMachine\My' -Subject $currentRestCert.Subject -NetworkControllerOid ` + | Sort-Object -Property NotAfter -Descending | Select-Object -First 1 + + "Network Controller Rest Certificate {0} will be updated:`n`tCurrent: [Thumbprint:{1} NotAfter:{2}]`n`tUpdated: [Thumbprint:{3} NotAfter:{4}]" ` + -f $currentRestCert.Subject, $currentRestCert.Thumbprint, $currentRestCert.NotAfter, $CertRotateConfig["NcRestCert"], $updatedRestCertificate.NotAfter ` + | Trace-Output -Level:Warning + + $null = Invoke-CertRotateCommand -Command 'Set-NetworkController' -Credential $Credential -Thumbprint $CertRotateConfig["NcRestCert"] + + "Waiting for 5 minutes before proceeding to the next step. Script will resume at {0}" -f (Get-Date).AddMinutes(5).ToUniversalTime().ToString() | Trace-Output + Start-Sleep -Seconds 300 + + # Rotate Cluster Certificate + $null = Invoke-CertRotateCommand -Command 'Set-NetworkControllerCluster' -Credential $Credential -Thumbprint $CertRotateConfig["NcRestCert"] + + "Waiting for 5 minutes before proceeding to the next step. Script will resume at {0}" -f (Get-Date).AddMinutes(5).ToUniversalTime().ToString() | Trace-Output + Start-Sleep -Seconds 300 + + # Update Credential Resource + $null = Update-NetworkControllerCredentialResource @{ + NcUri = "https://$($NcInfraInfo.NcRestName)" + Credential = $NcRestCredential + NewRestCertThumbprint = $CertRotateConfig["NcRestCert"] + ErrorAction = Stop + } + + ##################################### + # + # Certificate Seeding (Southbound Nodes) + # + ##################################### + + # in situation where maybe the Network Controller was down and we could not previously determine the southbound devices (if they exist) + # we will want to get updated infrastructure information to determine if we need to seed the certificate to the southbound devices + # if we get an error with returning the infrastructure information, we will terminate the script + if ($selfSignedRestCertFile) { + $sdnFabricDetails = Get-SdnInfrastructureInfo -Credential $Credential -NcRestCredential $NcRestCredential -Force -ErrorAction Stop + $southBoundNodes = @() + if ($null -ne $sdnFabricDetails.LoadBalancerMux) { + $southBoundNodes += $sdnFabricDetails.LoadBalancerMux + } + if ($null -ne $sdnFabricDetails.Server) { + $southBoundNodes += $sdnFabricDetails.Server + } + + if ($southBoundNodes) { + "== STAGE: REST SELF-SIGNED CERTIFICATE SEEDING (Southbound Nodes) ==" | Trace-Output + + # ensure that we have the latest version of sdnDiagnostics module on the southbound devices + Install-SdnDiagnostics -ComputerName $southBoundNodes -Credential $Credential -ErrorAction Stop + + "[REST CERT] Installing self-signed certificate to {0}" -f ($southBoundNodes -join ', ') | Trace-Output + [System.String]$remoteFilePath = Join-Path -Path $CertPath.FullName -ChildPath $selfSignedRestCertFile.Name + Copy-FileToRemoteComputer -ComputerName $southBoundNodes -Credential $Credential -Path $selfSignedRestCertFile.FullName -Destination $remoteFilePath + $null = Invoke-PSRemoteCommand -ComputerName $southBoundNodes -Credential $Credential -ScriptBlock { + param([Parameter(Position = 0)][String]$param1, [Parameter(Position = 1)][String]$param2) + Import-SdnCertificate -FilePath $param1 -CertStore $param2 + } -ArgumentList @($remoteFilePath, 'Cert:\LocalMachine\Root') -ErrorAction Stop + } + } + + ##################################### + # + # Restart services + # + ##################################### + + "== STAGE: RESTART NETWORK CONTROLLER SERVICES ==" | Trace-Output + # restart the network controller services + # this will force new TLS connections to be established to southbound devices + # ensuring that the new certificates are used and we are able to push policies successfully + + # check to determine if we have a multi-node NC cluster and if so, leverage the SF cmdlets to move the replicas + # otherwise, we will just stop the processes and let SF restart them automatically + if ($sdnFabricDetails.NetworkController.Count -gt 1) { + Move-SdnServiceFabricReplica -ServiceTypeName 'SlbManagerService' + Move-SdnServiceFabricReplica -ServiceTypeName 'VSwitchService' + } + else { + Get-Process -Name 'SDNFW' | Stop-Process -Force -ErrorAction Continue + Get-Process -Name 'SDNSLBM' | Stop-Process -Force -ErrorAction Continue + } + + "Certificate rotation has completed" | Trace-Output + } + catch { + + } +} From ad52266d1602f7e19eae36a07eee6b9df6993385 Mon Sep 17 00:00:00 2001 From: Adam Rudell Date: Thu, 4 Apr 2024 22:43:08 -0500 Subject: [PATCH 6/9] eliminate changes for moving SF cmdlets to new module --- src/SdnDiagnostics.psd1 | 1 - ...etworkController.ServiceFabric.Config.psd1 | 24 ------- ...etworkController.ServiceFabric.Helper.psm1 | 62 ------------------- ...nDiag.NetworkController.ServiceFabric.psm1 | 14 ----- .../SdnDiag.NetworkController.Config.psd1 | 19 +++++- .../SdnDiag.NetworkController.Helper.psm1 | 60 ++++++++++++++++++ .../SdnDiag.NetworkController.psm1 | 1 - ...iceFabricManifestFromNetworkController.ps1 | 0 ...rviceFabricManifestToNetworkController.ps1 | 0 ...kControllerNodeInfoFromClusterManifest.ps1 | 0 ...tworkControllerInfoFromClusterManifest.ps1 | 0 .../Get-SdnNetworkControllerInfoOffline.ps1 | 0 .../New-NetworkControllerClusterSecret.ps1 | 0 .../Start-SdnExpiredCertificateRotation.ps1 | 0 ...NetworkControllerCertificateInManifest.ps1 | 0 .../Update-NetworkControllerConfig.ps1 | 0 .../private/Update-ServiceFabricCluster.ps1 | 0 .../Wait-NetworkControllerAppHealthy.ps1 | 0 .../Wait-ServiceFabricClusterHealthy.ps1 | 0 .../Get-SdnNetworkControllerClusterInfo.ps1 | 0 .../public/Get-SdnNetworkControllerNode.ps1 | 0 .../Get-SdnServiceFabricApplicationHealth.ps1 | 0 .../Get-SdnServiceFabricClusterConfig.ps1 | 0 .../Get-SdnServiceFabricClusterHealth.ps1 | 0 .../Get-SdnServiceFabricClusterManifest.ps1 | 0 .../public/Get-SdnServiceFabricNode.ps1 | 0 .../public/Get-SdnServiceFabricPartition.ps1 | 0 .../public/Get-SdnServiceFabricReplica.ps1 | 0 .../public/Get-SdnServiceFabricService.ps1 | 0 .../public/Invoke-SdnServiceFabricCommand.ps1 | 0 .../public/Move-SdnServiceFabricReplica.ps1 | 0 .../public/Set-SdnNetworkController.ps1 | 0 .../Set-SdnServiceFabricClusterConfig.ps1 | 0 33 files changed, 77 insertions(+), 104 deletions(-) delete mode 100644 src/modules/SdnDiag.NetworkController.ServiceFabric/SdnDiag.NetworkController.ServiceFabric.Config.psd1 delete mode 100644 src/modules/SdnDiag.NetworkController.ServiceFabric/SdnDiag.NetworkController.ServiceFabric.Helper.psm1 delete mode 100644 src/modules/SdnDiag.NetworkController.ServiceFabric/SdnDiag.NetworkController.ServiceFabric.psm1 rename src/modules/{SdnDiag.NetworkController.ServiceFabric => SdnDiag.NetworkController}/private/Copy-ServiceFabricManifestFromNetworkController.ps1 (100%) rename src/modules/{SdnDiag.NetworkController.ServiceFabric => SdnDiag.NetworkController}/private/Copy-ServiceFabricManifestToNetworkController.ps1 (100%) rename src/modules/{SdnDiag.NetworkController.ServiceFabric => SdnDiag.NetworkController}/private/Get-NetworkControllerNodeInfoFromClusterManifest.ps1 (100%) rename src/modules/{SdnDiag.NetworkController.ServiceFabric => SdnDiag.NetworkController}/private/Get-SdnNetworkControllerInfoFromClusterManifest.ps1 (100%) rename src/modules/{SdnDiag.NetworkController.ServiceFabric => SdnDiag.NetworkController}/private/Get-SdnNetworkControllerInfoOffline.ps1 (100%) rename src/modules/{SdnDiag.NetworkController.ServiceFabric => SdnDiag.NetworkController}/private/New-NetworkControllerClusterSecret.ps1 (100%) rename src/modules/{SdnDiag.NetworkController.ServiceFabric => SdnDiag.NetworkController}/private/Start-SdnExpiredCertificateRotation.ps1 (100%) rename src/modules/{SdnDiag.NetworkController.ServiceFabric => SdnDiag.NetworkController}/private/Update-NetworkControllerCertificateInManifest.ps1 (100%) rename src/modules/{SdnDiag.NetworkController.ServiceFabric => SdnDiag.NetworkController}/private/Update-NetworkControllerConfig.ps1 (100%) rename src/modules/{SdnDiag.NetworkController.ServiceFabric => SdnDiag.NetworkController}/private/Update-ServiceFabricCluster.ps1 (100%) rename src/modules/{SdnDiag.NetworkController.ServiceFabric => SdnDiag.NetworkController}/private/Wait-NetworkControllerAppHealthy.ps1 (100%) rename src/modules/{SdnDiag.NetworkController.ServiceFabric => SdnDiag.NetworkController}/private/Wait-ServiceFabricClusterHealthy.ps1 (100%) rename src/modules/{SdnDiag.NetworkController.ServiceFabric => SdnDiag.NetworkController}/public/Get-SdnNetworkControllerClusterInfo.ps1 (100%) rename src/modules/{SdnDiag.NetworkController.ServiceFabric => SdnDiag.NetworkController}/public/Get-SdnNetworkControllerNode.ps1 (100%) rename src/modules/{SdnDiag.NetworkController.ServiceFabric => SdnDiag.NetworkController}/public/Get-SdnServiceFabricApplicationHealth.ps1 (100%) rename src/modules/{SdnDiag.NetworkController.ServiceFabric => SdnDiag.NetworkController}/public/Get-SdnServiceFabricClusterConfig.ps1 (100%) rename src/modules/{SdnDiag.NetworkController.ServiceFabric => SdnDiag.NetworkController}/public/Get-SdnServiceFabricClusterHealth.ps1 (100%) rename src/modules/{SdnDiag.NetworkController.ServiceFabric => SdnDiag.NetworkController}/public/Get-SdnServiceFabricClusterManifest.ps1 (100%) rename src/modules/{SdnDiag.NetworkController.ServiceFabric => SdnDiag.NetworkController}/public/Get-SdnServiceFabricNode.ps1 (100%) rename src/modules/{SdnDiag.NetworkController.ServiceFabric => SdnDiag.NetworkController}/public/Get-SdnServiceFabricPartition.ps1 (100%) rename src/modules/{SdnDiag.NetworkController.ServiceFabric => SdnDiag.NetworkController}/public/Get-SdnServiceFabricReplica.ps1 (100%) rename src/modules/{SdnDiag.NetworkController.ServiceFabric => SdnDiag.NetworkController}/public/Get-SdnServiceFabricService.ps1 (100%) rename src/modules/{SdnDiag.NetworkController.ServiceFabric => SdnDiag.NetworkController}/public/Invoke-SdnServiceFabricCommand.ps1 (100%) rename src/modules/{SdnDiag.NetworkController.ServiceFabric => SdnDiag.NetworkController}/public/Move-SdnServiceFabricReplica.ps1 (100%) rename src/modules/{SdnDiag.NetworkController.ServiceFabric => SdnDiag.NetworkController}/public/Set-SdnNetworkController.ps1 (100%) rename src/modules/{SdnDiag.NetworkController.ServiceFabric => SdnDiag.NetworkController}/public/Set-SdnServiceFabricClusterConfig.ps1 (100%) diff --git a/src/SdnDiagnostics.psd1 b/src/SdnDiagnostics.psd1 index 06f887dd..030b8d02 100644 --- a/src/SdnDiagnostics.psd1 +++ b/src/SdnDiagnostics.psd1 @@ -35,7 +35,6 @@ 'modules\SdnDiag.Health\SdnDiag.Health.psm1' 'modules\SdnDiag.LoadBalancerMux\SdnDiag.LoadBalancerMux.psm1' 'modules\SdnDiag.NetworkController\SdnDiag.NetworkController.psm1' - 'modules\SdnDiag.NetworkController.ServiceFabric\SdnDiag.NetworkController.ServiceFabric.psm1' 'modules\SdnDiag.Server\SdnDiag.Server.psm1' 'modules\SdnDiag.Utilities\SdnDiag.Utilities.psm1' 'modules\Test-SdnExpressBgp.psm1' diff --git a/src/modules/SdnDiag.NetworkController.ServiceFabric/SdnDiag.NetworkController.ServiceFabric.Config.psd1 b/src/modules/SdnDiag.NetworkController.ServiceFabric/SdnDiag.NetworkController.ServiceFabric.Config.psd1 deleted file mode 100644 index 00f21289..00000000 --- a/src/modules/SdnDiag.NetworkController.ServiceFabric/SdnDiag.NetworkController.ServiceFabric.Config.psd1 +++ /dev/null @@ -1,24 +0,0 @@ -# Copyright (c) Microsoft Corporation. -# Licensed under the MIT License. - -@{ - CommonPaths = @{ - serviceFabricLogDirectory = @( - "C:\ProgramData\Microsoft\Service Fabric\log\Traces" - "C:\ProgramData\Microsoft\Service Fabric\log\OperationalTraces" - "C:\ProgramData\Microsoft\Service Fabric\log\QueryTraces" - "C:\ProgramData\Microsoft\Service Fabric\log\CrashDumps" - "C:\ProgramData\Microsoft\Service Fabric\log\PerformanceCounters_WinFabPerfCtrFolder" - ) - } - EventLogProviders = @( - "Microsoft-ServiceFabric*" - ) - Services = @{ - FabricHostSvc = @{ - Properties = @{ - DisplayName = "Service Fabric Host Service" - } - } - } -} diff --git a/src/modules/SdnDiag.NetworkController.ServiceFabric/SdnDiag.NetworkController.ServiceFabric.Helper.psm1 b/src/modules/SdnDiag.NetworkController.ServiceFabric/SdnDiag.NetworkController.ServiceFabric.Helper.psm1 deleted file mode 100644 index 9c6c0449..00000000 --- a/src/modules/SdnDiag.NetworkController.ServiceFabric/SdnDiag.NetworkController.ServiceFabric.Helper.psm1 +++ /dev/null @@ -1,62 +0,0 @@ -# Copyright (c) Microsoft Corporation. -# Licensed under the MIT License. - -$scriptBlocks = @{ - ServiceFabricServiceName = { - param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameters) - $serviceName = @( - 'fabric:/NetworkController/ApiService' - 'fabric:/NetworkController/BackupRestore' - 'fabric:/NetworkController/ControllerService' - 'fabric:/NetworkController/FirewallService' - 'fabric:/NetworkController/FnmService' - 'fabric:/NetworkController/GatewayManager' - 'fabric:/NetworkController/HelperService' - 'fabric:/NetworkController/ServiceInsertion' - 'fabric:/NetworkController/SlbManagerService' - 'fabric:/NetworkController/UpdateService' - 'fabric:/NetworkController/VSwitchService' - ) - - if ([string]::IsNullOrEmpty($wordToComplete)) { - return ($serviceName | Sort-Object) - } - - return $serviceName | Where-Object {$_ -ilike "*$wordToComplete*"} | Sort-Object - } - - ServiceFabricServiceTypeName = { - param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameters) - $serviceTypeName = @( - 'ApiService' - 'BackupRestore' - 'ControllerService' - 'FirewallService' - 'FnmService' - 'GatewayManager' - 'HelperService' - 'ServiceInsertion' - 'SlbManagerService' - 'UpdateService' - 'VSwitchService' - ) - - if ([string]::IsNullOrEmpty($wordToComplete)) { - return ($serviceTypeName | Sort-Object) - } - - return $serviceTypeName | Where-Object {$_ -ilike "*$wordToComplete*"} | Sort-Object - } -} - -Register-ArgumentCompleter -CommandName 'Get-SdnServiceFabricReplica' -ParameterName 'ServiceName' -ScriptBlock $scriptBlocks.ServiceFabricServiceName -Register-ArgumentCompleter -CommandName 'Get-SdnServiceFabricReplica' -ParameterName 'ServiceTypeName' -ScriptBlock $scriptBlocks.ServiceFabricServiceTypeName - -Register-ArgumentCompleter -CommandName 'Get-SdnServiceFabricService' -ParameterName 'ServiceName' -ScriptBlock $scriptBlocks.ServiceFabricServiceName -Register-ArgumentCompleter -CommandName 'Get-SdnServiceFabricService' -ParameterName 'ServiceTypeName' -ScriptBlock $scriptBlocks.ServiceFabricServiceTypeName - -Register-ArgumentCompleter -CommandName 'Get-SdnServiceFabricPartition' -ParameterName 'ServiceName' -ScriptBlock $scriptBlocks.ServiceFabricServiceName -Register-ArgumentCompleter -CommandName 'Get-SdnServiceFabricPartition' -ParameterName 'ServiceTypeName' -ScriptBlock $scriptBlocks.ServiceFabricServiceTypeName - -Register-ArgumentCompleter -CommandName 'Move-SdnServiceFabricReplica' -ParameterName 'ServiceName' -ScriptBlock $scriptBlocks.ServiceFabricServiceName -Register-ArgumentCompleter -CommandName 'Move-SdnServiceFabricReplica' -ParameterName 'ServiceTypeName' -ScriptBlock $scriptBlocks.ServiceFabricServiceTypeName diff --git a/src/modules/SdnDiag.NetworkController.ServiceFabric/SdnDiag.NetworkController.ServiceFabric.psm1 b/src/modules/SdnDiag.NetworkController.ServiceFabric/SdnDiag.NetworkController.ServiceFabric.psm1 deleted file mode 100644 index dfba3f58..00000000 --- a/src/modules/SdnDiag.NetworkController.ServiceFabric/SdnDiag.NetworkController.ServiceFabric.psm1 +++ /dev/null @@ -1,14 +0,0 @@ -# Copyright (c) Microsoft Corporation. -# Licensed under the MIT License. - -Import-Module $PSScriptRoot\SdnDiag.NetworkController.ServiceFabric.Helper.psm1 -Import-Module $PSScriptRoot\..\SdnDiag.Common\SdnDiag.Common.psm1 -Import-Module $PSScriptRoot\..\SdnDiag.Utilities\SdnDiag.Utilities.psm1 - -# create local variable to store configuration data -$configurationData = Import-PowerShellDataFile -Path $PSScriptRoot\SdnDiag.NetworkController.ServiceFabric.Config.psd1 -New-Variable -Name 'SdnDiagnostics_NC_SF' -Scope 'Script' -Force -Value @{ - Config = $configurationData -} - -##### FUNCTIONS AUTO-POPULATED BELOW THIS LINE DURING BUILD ##### diff --git a/src/modules/SdnDiag.NetworkController/SdnDiag.NetworkController.Config.psd1 b/src/modules/SdnDiag.NetworkController/SdnDiag.NetworkController.Config.psd1 index 257a7136..25f69fe2 100644 --- a/src/modules/SdnDiag.NetworkController/SdnDiag.NetworkController.Config.psd1 +++ b/src/modules/SdnDiag.NetworkController/SdnDiag.NetworkController.Config.psd1 @@ -204,9 +204,18 @@ includeInResourceDump = $true } } - CommonPaths = @{} + CommonPaths = @{ + serviceFabricLogDirectory = @( + "C:\ProgramData\Microsoft\Service Fabric\log\Traces" + "C:\ProgramData\Microsoft\Service Fabric\log\OperationalTraces" + "C:\ProgramData\Microsoft\Service Fabric\log\QueryTraces" + "C:\ProgramData\Microsoft\Service Fabric\log\CrashDumps" + "C:\ProgramData\Microsoft\Service Fabric\log\PerformanceCounters_WinFabPerfCtrFolder" + ) + } EventLogProviders = @( "Microsoft-Windows-NetworkController*" + "Microsoft-ServiceFabric*" ) NetControllerStatePath = "C:\Windows\Tracing\SDNDiagnostics\NetworkControllerState" RegKeyPaths = @( @@ -214,6 +223,12 @@ "HKLM:\SYSTEM\CurrentControlSet\Services\NcHostAgent" "HKLM:\SYSTEM\CurrentControlSet\Services\Network Controller" ) - Services = @{} + Services = @{ + FabricHostSvc = @{ + Properties = @{ + DisplayName = "Service Fabric Host Service" + } + } + } } } diff --git a/src/modules/SdnDiag.NetworkController/SdnDiag.NetworkController.Helper.psm1 b/src/modules/SdnDiag.NetworkController/SdnDiag.NetworkController.Helper.psm1 index 2a4efa50..24a9bceb 100644 --- a/src/modules/SdnDiag.NetworkController/SdnDiag.NetworkController.Helper.psm1 +++ b/src/modules/SdnDiag.NetworkController/SdnDiag.NetworkController.Helper.psm1 @@ -47,3 +47,63 @@ enum SdnApiResource { VirtualServers VirtualSwitchManagerConfig } + +$scriptBlocks = @{ + ServiceFabricServiceName = { + param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameters) + $serviceName = @( + 'fabric:/NetworkController/ApiService' + 'fabric:/NetworkController/BackupRestore' + 'fabric:/NetworkController/ControllerService' + 'fabric:/NetworkController/FirewallService' + 'fabric:/NetworkController/FnmService' + 'fabric:/NetworkController/GatewayManager' + 'fabric:/NetworkController/HelperService' + 'fabric:/NetworkController/ServiceInsertion' + 'fabric:/NetworkController/SlbManagerService' + 'fabric:/NetworkController/UpdateService' + 'fabric:/NetworkController/VSwitchService' + ) + + if ([string]::IsNullOrEmpty($wordToComplete)) { + return ($serviceName | Sort-Object) + } + + return $serviceName | Where-Object {$_ -ilike "*$wordToComplete*"} | Sort-Object + } + + ServiceFabricServiceTypeName = { + param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameters) + $serviceTypeName = @( + 'ApiService' + 'BackupRestore' + 'ControllerService' + 'FirewallService' + 'FnmService' + 'GatewayManager' + 'HelperService' + 'ServiceInsertion' + 'SlbManagerService' + 'UpdateService' + 'VSwitchService' + ) + + if ([string]::IsNullOrEmpty($wordToComplete)) { + return ($serviceTypeName | Sort-Object) + } + + return $serviceTypeName | Where-Object {$_ -ilike "*$wordToComplete*"} | Sort-Object + } +} + +Register-ArgumentCompleter -CommandName 'Get-SdnServiceFabricReplica' -ParameterName 'ServiceName' -ScriptBlock $scriptBlocks.ServiceFabricServiceName +Register-ArgumentCompleter -CommandName 'Get-SdnServiceFabricReplica' -ParameterName 'ServiceTypeName' -ScriptBlock $scriptBlocks.ServiceFabricServiceTypeName + +Register-ArgumentCompleter -CommandName 'Get-SdnServiceFabricService' -ParameterName 'ServiceName' -ScriptBlock $scriptBlocks.ServiceFabricServiceName +Register-ArgumentCompleter -CommandName 'Get-SdnServiceFabricService' -ParameterName 'ServiceTypeName' -ScriptBlock $scriptBlocks.ServiceFabricServiceTypeName + +Register-ArgumentCompleter -CommandName 'Get-SdnServiceFabricPartition' -ParameterName 'ServiceName' -ScriptBlock $scriptBlocks.ServiceFabricServiceName +Register-ArgumentCompleter -CommandName 'Get-SdnServiceFabricPartition' -ParameterName 'ServiceTypeName' -ScriptBlock $scriptBlocks.ServiceFabricServiceTypeName + +Register-ArgumentCompleter -CommandName 'Move-SdnServiceFabricReplica' -ParameterName 'ServiceName' -ScriptBlock $scriptBlocks.ServiceFabricServiceName +Register-ArgumentCompleter -CommandName 'Move-SdnServiceFabricReplica' -ParameterName 'ServiceTypeName' -ScriptBlock $scriptBlocks.ServiceFabricServiceTypeName diff --git a/src/modules/SdnDiag.NetworkController/SdnDiag.NetworkController.psm1 b/src/modules/SdnDiag.NetworkController/SdnDiag.NetworkController.psm1 index e890aa5e..fcaacfbf 100644 --- a/src/modules/SdnDiag.NetworkController/SdnDiag.NetworkController.psm1 +++ b/src/modules/SdnDiag.NetworkController/SdnDiag.NetworkController.psm1 @@ -3,7 +3,6 @@ Using module .\SdnDiag.NetworkController.Helper.psm1 Import-Module $PSScriptRoot\SdnDiag.NetworkController.Helper.psm1 -Import-Module $PSScriptRoot\..\SdnDiag.NetworkController.ServiceFabric\SdnDiag.NetworkController.ServiceFabric.psm1 Import-Module $PSScriptRoot\..\SdnDiag.Common\SdnDiag.Common.psm1 Import-Module $PSScriptRoot\..\SdnDiag.Utilities\SdnDiag.Utilities.psm1 diff --git a/src/modules/SdnDiag.NetworkController.ServiceFabric/private/Copy-ServiceFabricManifestFromNetworkController.ps1 b/src/modules/SdnDiag.NetworkController/private/Copy-ServiceFabricManifestFromNetworkController.ps1 similarity index 100% rename from src/modules/SdnDiag.NetworkController.ServiceFabric/private/Copy-ServiceFabricManifestFromNetworkController.ps1 rename to src/modules/SdnDiag.NetworkController/private/Copy-ServiceFabricManifestFromNetworkController.ps1 diff --git a/src/modules/SdnDiag.NetworkController.ServiceFabric/private/Copy-ServiceFabricManifestToNetworkController.ps1 b/src/modules/SdnDiag.NetworkController/private/Copy-ServiceFabricManifestToNetworkController.ps1 similarity index 100% rename from src/modules/SdnDiag.NetworkController.ServiceFabric/private/Copy-ServiceFabricManifestToNetworkController.ps1 rename to src/modules/SdnDiag.NetworkController/private/Copy-ServiceFabricManifestToNetworkController.ps1 diff --git a/src/modules/SdnDiag.NetworkController.ServiceFabric/private/Get-NetworkControllerNodeInfoFromClusterManifest.ps1 b/src/modules/SdnDiag.NetworkController/private/Get-NetworkControllerNodeInfoFromClusterManifest.ps1 similarity index 100% rename from src/modules/SdnDiag.NetworkController.ServiceFabric/private/Get-NetworkControllerNodeInfoFromClusterManifest.ps1 rename to src/modules/SdnDiag.NetworkController/private/Get-NetworkControllerNodeInfoFromClusterManifest.ps1 diff --git a/src/modules/SdnDiag.NetworkController.ServiceFabric/private/Get-SdnNetworkControllerInfoFromClusterManifest.ps1 b/src/modules/SdnDiag.NetworkController/private/Get-SdnNetworkControllerInfoFromClusterManifest.ps1 similarity index 100% rename from src/modules/SdnDiag.NetworkController.ServiceFabric/private/Get-SdnNetworkControllerInfoFromClusterManifest.ps1 rename to src/modules/SdnDiag.NetworkController/private/Get-SdnNetworkControllerInfoFromClusterManifest.ps1 diff --git a/src/modules/SdnDiag.NetworkController.ServiceFabric/private/Get-SdnNetworkControllerInfoOffline.ps1 b/src/modules/SdnDiag.NetworkController/private/Get-SdnNetworkControllerInfoOffline.ps1 similarity index 100% rename from src/modules/SdnDiag.NetworkController.ServiceFabric/private/Get-SdnNetworkControllerInfoOffline.ps1 rename to src/modules/SdnDiag.NetworkController/private/Get-SdnNetworkControllerInfoOffline.ps1 diff --git a/src/modules/SdnDiag.NetworkController.ServiceFabric/private/New-NetworkControllerClusterSecret.ps1 b/src/modules/SdnDiag.NetworkController/private/New-NetworkControllerClusterSecret.ps1 similarity index 100% rename from src/modules/SdnDiag.NetworkController.ServiceFabric/private/New-NetworkControllerClusterSecret.ps1 rename to src/modules/SdnDiag.NetworkController/private/New-NetworkControllerClusterSecret.ps1 diff --git a/src/modules/SdnDiag.NetworkController.ServiceFabric/private/Start-SdnExpiredCertificateRotation.ps1 b/src/modules/SdnDiag.NetworkController/private/Start-SdnExpiredCertificateRotation.ps1 similarity index 100% rename from src/modules/SdnDiag.NetworkController.ServiceFabric/private/Start-SdnExpiredCertificateRotation.ps1 rename to src/modules/SdnDiag.NetworkController/private/Start-SdnExpiredCertificateRotation.ps1 diff --git a/src/modules/SdnDiag.NetworkController.ServiceFabric/private/Update-NetworkControllerCertificateInManifest.ps1 b/src/modules/SdnDiag.NetworkController/private/Update-NetworkControllerCertificateInManifest.ps1 similarity index 100% rename from src/modules/SdnDiag.NetworkController.ServiceFabric/private/Update-NetworkControllerCertificateInManifest.ps1 rename to src/modules/SdnDiag.NetworkController/private/Update-NetworkControllerCertificateInManifest.ps1 diff --git a/src/modules/SdnDiag.NetworkController.ServiceFabric/private/Update-NetworkControllerConfig.ps1 b/src/modules/SdnDiag.NetworkController/private/Update-NetworkControllerConfig.ps1 similarity index 100% rename from src/modules/SdnDiag.NetworkController.ServiceFabric/private/Update-NetworkControllerConfig.ps1 rename to src/modules/SdnDiag.NetworkController/private/Update-NetworkControllerConfig.ps1 diff --git a/src/modules/SdnDiag.NetworkController.ServiceFabric/private/Update-ServiceFabricCluster.ps1 b/src/modules/SdnDiag.NetworkController/private/Update-ServiceFabricCluster.ps1 similarity index 100% rename from src/modules/SdnDiag.NetworkController.ServiceFabric/private/Update-ServiceFabricCluster.ps1 rename to src/modules/SdnDiag.NetworkController/private/Update-ServiceFabricCluster.ps1 diff --git a/src/modules/SdnDiag.NetworkController.ServiceFabric/private/Wait-NetworkControllerAppHealthy.ps1 b/src/modules/SdnDiag.NetworkController/private/Wait-NetworkControllerAppHealthy.ps1 similarity index 100% rename from src/modules/SdnDiag.NetworkController.ServiceFabric/private/Wait-NetworkControllerAppHealthy.ps1 rename to src/modules/SdnDiag.NetworkController/private/Wait-NetworkControllerAppHealthy.ps1 diff --git a/src/modules/SdnDiag.NetworkController.ServiceFabric/private/Wait-ServiceFabricClusterHealthy.ps1 b/src/modules/SdnDiag.NetworkController/private/Wait-ServiceFabricClusterHealthy.ps1 similarity index 100% rename from src/modules/SdnDiag.NetworkController.ServiceFabric/private/Wait-ServiceFabricClusterHealthy.ps1 rename to src/modules/SdnDiag.NetworkController/private/Wait-ServiceFabricClusterHealthy.ps1 diff --git a/src/modules/SdnDiag.NetworkController.ServiceFabric/public/Get-SdnNetworkControllerClusterInfo.ps1 b/src/modules/SdnDiag.NetworkController/public/Get-SdnNetworkControllerClusterInfo.ps1 similarity index 100% rename from src/modules/SdnDiag.NetworkController.ServiceFabric/public/Get-SdnNetworkControllerClusterInfo.ps1 rename to src/modules/SdnDiag.NetworkController/public/Get-SdnNetworkControllerClusterInfo.ps1 diff --git a/src/modules/SdnDiag.NetworkController.ServiceFabric/public/Get-SdnNetworkControllerNode.ps1 b/src/modules/SdnDiag.NetworkController/public/Get-SdnNetworkControllerNode.ps1 similarity index 100% rename from src/modules/SdnDiag.NetworkController.ServiceFabric/public/Get-SdnNetworkControllerNode.ps1 rename to src/modules/SdnDiag.NetworkController/public/Get-SdnNetworkControllerNode.ps1 diff --git a/src/modules/SdnDiag.NetworkController.ServiceFabric/public/Get-SdnServiceFabricApplicationHealth.ps1 b/src/modules/SdnDiag.NetworkController/public/Get-SdnServiceFabricApplicationHealth.ps1 similarity index 100% rename from src/modules/SdnDiag.NetworkController.ServiceFabric/public/Get-SdnServiceFabricApplicationHealth.ps1 rename to src/modules/SdnDiag.NetworkController/public/Get-SdnServiceFabricApplicationHealth.ps1 diff --git a/src/modules/SdnDiag.NetworkController.ServiceFabric/public/Get-SdnServiceFabricClusterConfig.ps1 b/src/modules/SdnDiag.NetworkController/public/Get-SdnServiceFabricClusterConfig.ps1 similarity index 100% rename from src/modules/SdnDiag.NetworkController.ServiceFabric/public/Get-SdnServiceFabricClusterConfig.ps1 rename to src/modules/SdnDiag.NetworkController/public/Get-SdnServiceFabricClusterConfig.ps1 diff --git a/src/modules/SdnDiag.NetworkController.ServiceFabric/public/Get-SdnServiceFabricClusterHealth.ps1 b/src/modules/SdnDiag.NetworkController/public/Get-SdnServiceFabricClusterHealth.ps1 similarity index 100% rename from src/modules/SdnDiag.NetworkController.ServiceFabric/public/Get-SdnServiceFabricClusterHealth.ps1 rename to src/modules/SdnDiag.NetworkController/public/Get-SdnServiceFabricClusterHealth.ps1 diff --git a/src/modules/SdnDiag.NetworkController.ServiceFabric/public/Get-SdnServiceFabricClusterManifest.ps1 b/src/modules/SdnDiag.NetworkController/public/Get-SdnServiceFabricClusterManifest.ps1 similarity index 100% rename from src/modules/SdnDiag.NetworkController.ServiceFabric/public/Get-SdnServiceFabricClusterManifest.ps1 rename to src/modules/SdnDiag.NetworkController/public/Get-SdnServiceFabricClusterManifest.ps1 diff --git a/src/modules/SdnDiag.NetworkController.ServiceFabric/public/Get-SdnServiceFabricNode.ps1 b/src/modules/SdnDiag.NetworkController/public/Get-SdnServiceFabricNode.ps1 similarity index 100% rename from src/modules/SdnDiag.NetworkController.ServiceFabric/public/Get-SdnServiceFabricNode.ps1 rename to src/modules/SdnDiag.NetworkController/public/Get-SdnServiceFabricNode.ps1 diff --git a/src/modules/SdnDiag.NetworkController.ServiceFabric/public/Get-SdnServiceFabricPartition.ps1 b/src/modules/SdnDiag.NetworkController/public/Get-SdnServiceFabricPartition.ps1 similarity index 100% rename from src/modules/SdnDiag.NetworkController.ServiceFabric/public/Get-SdnServiceFabricPartition.ps1 rename to src/modules/SdnDiag.NetworkController/public/Get-SdnServiceFabricPartition.ps1 diff --git a/src/modules/SdnDiag.NetworkController.ServiceFabric/public/Get-SdnServiceFabricReplica.ps1 b/src/modules/SdnDiag.NetworkController/public/Get-SdnServiceFabricReplica.ps1 similarity index 100% rename from src/modules/SdnDiag.NetworkController.ServiceFabric/public/Get-SdnServiceFabricReplica.ps1 rename to src/modules/SdnDiag.NetworkController/public/Get-SdnServiceFabricReplica.ps1 diff --git a/src/modules/SdnDiag.NetworkController.ServiceFabric/public/Get-SdnServiceFabricService.ps1 b/src/modules/SdnDiag.NetworkController/public/Get-SdnServiceFabricService.ps1 similarity index 100% rename from src/modules/SdnDiag.NetworkController.ServiceFabric/public/Get-SdnServiceFabricService.ps1 rename to src/modules/SdnDiag.NetworkController/public/Get-SdnServiceFabricService.ps1 diff --git a/src/modules/SdnDiag.NetworkController.ServiceFabric/public/Invoke-SdnServiceFabricCommand.ps1 b/src/modules/SdnDiag.NetworkController/public/Invoke-SdnServiceFabricCommand.ps1 similarity index 100% rename from src/modules/SdnDiag.NetworkController.ServiceFabric/public/Invoke-SdnServiceFabricCommand.ps1 rename to src/modules/SdnDiag.NetworkController/public/Invoke-SdnServiceFabricCommand.ps1 diff --git a/src/modules/SdnDiag.NetworkController.ServiceFabric/public/Move-SdnServiceFabricReplica.ps1 b/src/modules/SdnDiag.NetworkController/public/Move-SdnServiceFabricReplica.ps1 similarity index 100% rename from src/modules/SdnDiag.NetworkController.ServiceFabric/public/Move-SdnServiceFabricReplica.ps1 rename to src/modules/SdnDiag.NetworkController/public/Move-SdnServiceFabricReplica.ps1 diff --git a/src/modules/SdnDiag.NetworkController.ServiceFabric/public/Set-SdnNetworkController.ps1 b/src/modules/SdnDiag.NetworkController/public/Set-SdnNetworkController.ps1 similarity index 100% rename from src/modules/SdnDiag.NetworkController.ServiceFabric/public/Set-SdnNetworkController.ps1 rename to src/modules/SdnDiag.NetworkController/public/Set-SdnNetworkController.ps1 diff --git a/src/modules/SdnDiag.NetworkController.ServiceFabric/public/Set-SdnServiceFabricClusterConfig.ps1 b/src/modules/SdnDiag.NetworkController/public/Set-SdnServiceFabricClusterConfig.ps1 similarity index 100% rename from src/modules/SdnDiag.NetworkController.ServiceFabric/public/Set-SdnServiceFabricClusterConfig.ps1 rename to src/modules/SdnDiag.NetworkController/public/Set-SdnServiceFabricClusterConfig.ps1 From 15d93e43d5bcc3cc2ff4049fd218513231c978b9 Mon Sep 17 00:00:00 2001 From: Adam Rudell Date: Tue, 16 Apr 2024 17:37:29 -0500 Subject: [PATCH 7/9] save latest changes --- .../public/Get-SdnMuxCertificate.ps1 | 21 ++- .../SdnDiag.NetworkController.Helper.psm1 | 46 +++++++ .../Start-SdnExpiredCertificateRotation.ps1 | 25 +--- .../public/Get-SdnNetworkController.ps1 | 29 ++-- .../public/Get-SdnNetworkControllerNode.ps1 | 72 ++++++---- ...et-SdnNetworkControllerNodeCertificate.ps1 | 14 +- .../New-SdnCertificateRotationConfig.ps1 | 127 ++++++++++++------ .../public/Start-SdnCertificateRotation.ps1 | 38 ++++-- ...dnNetworkControllerCertificateRotation.ps1 | 5 +- ...tworkControllerRestCertificateRotation.ps1 | 5 +- .../public/Get-SdnServerCertificate.ps1 | 21 ++- .../private/Confirm-IsAdmin.ps1 | 11 ++ .../private/Confirm-IsCertSelfSigned.ps1 | 13 ++ 13 files changed, 301 insertions(+), 126 deletions(-) create mode 100644 src/modules/SdnDiag.Utilities/private/Confirm-IsAdmin.ps1 create mode 100644 src/modules/SdnDiag.Utilities/private/Confirm-IsCertSelfSigned.ps1 diff --git a/src/modules/SdnDiag.LoadBalancerMux/public/Get-SdnMuxCertificate.ps1 b/src/modules/SdnDiag.LoadBalancerMux/public/Get-SdnMuxCertificate.ps1 index 9fee1a88..5ee815b5 100644 --- a/src/modules/SdnDiag.LoadBalancerMux/public/Get-SdnMuxCertificate.ps1 +++ b/src/modules/SdnDiag.LoadBalancerMux/public/Get-SdnMuxCertificate.ps1 @@ -1,16 +1,31 @@ function Get-SdnMuxCertificate { <# .SYNOPSIS - Returns the certificate used by the SDN Load Balancer Mux. + Returns the certificate used by the SDN Load Balancer Mux. + .PARAMETER NetworkControllerOid + Specifies to return only the certificate that has the specified Network Controller OID. #> [CmdletBinding()] - param () + param ( + [Parameter(Mandatory = $false)] + [switch]$NetworkControllerOid + ) try { $muxCert = Get-ItemPropertyValue -Path 'HKLM:\SYSTEM\CurrentControlSet\Services\SlbMux' -Name 'MuxCert' $subjectName = "CN={0}" -f $muxCert - $certificate = Get-SdnCertificate -Subject $subjectName -Path 'Cert:\LocalMachine\My' + $certificate = Get-SdnCertificate -Subject $subjectName -Path 'Cert:\LocalMachine\My' -NetworkControllerOid:$NetworkControllerOid + + if ($null -eq $certificate) { + if ($NetworkControllerOid) { + throw New-Object System.NullReferenceException("Failed to locate certificate for Load Balancer Mux containing Network Controller OID") + } + else { + throw New-Object System.NullReferenceException("Failed to locate certificate for Load Balancer Mux") + } + } + return $certificate } catch { diff --git a/src/modules/SdnDiag.NetworkController/SdnDiag.NetworkController.Helper.psm1 b/src/modules/SdnDiag.NetworkController/SdnDiag.NetworkController.Helper.psm1 index 24a9bceb..e66a304d 100644 --- a/src/modules/SdnDiag.NetworkController/SdnDiag.NetworkController.Helper.psm1 +++ b/src/modules/SdnDiag.NetworkController/SdnDiag.NetworkController.Helper.psm1 @@ -10,6 +10,52 @@ class SdnFabricInfrastructure { [System.String[]]$FabricNodes } +class BaseCert { + [String]$Thumbprint + [String]$SubjectName + [bool]$IsSelfSigned +} + +class RestCert : BaseCert { + [CertType]$CertificateType = [CertType]::Rest +} + +class NodeCert : BaseCert { + [String]$ResourceRef + [String]$IpAddressOrFQDN + [String]$NodeName +} + +class NetworkControllerNodeCert : NodeCert { + [CertType]$CertificateType = [CertType]::NetworkController +} + +class LoadBalancerMuxNodeCert : NodeCert { + [CertType]$CertificateType = [CertType]::LoadBalancerMux +} + +class ServerNodeCert : NodeCert { + [CertType]$CertificateType = [CertType]::Server +} + +class CertRotateConfig { + [RestCert]$RestCert + [ClusterCredentialType]$ClusterCredentialType = [ClusterCredentialType]::Kerberos + [Object[]]$NodeCerts +} + +enum ClusterCredentialType { + Kerberos + X509 +} + +enum CertType { + Rest + NetworkController + Server + LoadBalancerMux +} + enum NcManagedRoles { Gateway Server diff --git a/src/modules/SdnDiag.NetworkController/private/Start-SdnExpiredCertificateRotation.ps1 b/src/modules/SdnDiag.NetworkController/private/Start-SdnExpiredCertificateRotation.ps1 index f9d5d1a7..ab9a0a81 100644 --- a/src/modules/SdnDiag.NetworkController/private/Start-SdnExpiredCertificateRotation.ps1 +++ b/src/modules/SdnDiag.NetworkController/private/Start-SdnExpiredCertificateRotation.ps1 @@ -24,9 +24,11 @@ function Start-SdnExpiredCertificateRotation { [Parameter(Mandatory = $true)] [hashtable] $CertRotateConfig, + [System.Management.Automation.PSCredential] [System.Management.Automation.Credential()] $Credential = [System.Management.Automation.PSCredential]::Empty, + [System.Management.Automation.PSCredential] [System.Management.Automation.Credential()] $NcRestCredential = [System.Management.Automation.PSCredential]::Empty @@ -44,26 +46,24 @@ function Start-SdnExpiredCertificateRotation { $NcNodeList = $NcInfraInfo.NodeList if ($null -eq $NcNodeList -or $NcNodeList.Count -eq 0) { - Trace-Output -Message "Failed to get NC Node List from NetworkController: $(HostName)" -Level:Error + throw "Failed to get NC Node List from NetworkController: $($env:COMPUTERNAME)" } Trace-Output -Message "NcNodeList: $($NcNodeList.IpAddressOrFQDN)" - Trace-Output -Message "Validate CertRotateConfig" if(!(Test-SdnCertificateRotationConfig -NcNodeList $NcNodeList -CertRotateConfig $CertRotateConfig -Credential $Credential)){ - Trace-Output -Message "Invalid CertRotateConfig, please correct the configuration and try again" -Level:Error - return + throw "Invalid CertRotateConfig, please correct the configuration and try again" } if ([String]::IsNullOrEmpty($NcInfraInfo.NcRestName)) { Trace-Output -Message "Failed to get NcRestName using current secret certificate thumbprint. This might indicate the certificate not found on $(HOSTNAME). We won't be able to recover." -Level:Error - throw New-Object System.NotSupportedException("Current NC Rest Cert not found, Certificate Rotation cannot be continue.") + throw New-Object System.NotSupportedException("Current Network Controller Rest certificate not found.") } $NcVms = $NcNodeList.IpAddressOrFQDN - if (Test-Path $NcUpdateFolder) { - $items = Get-ChildItem $NcUpdateFolder + if (Test-Path -Path $NcUpdateFolder) { + $items = Get-ChildItem -Path $NcUpdateFolder -ErrorAction Ignore if ($items.Count -gt 0) { $confirmCleanup = Read-Host "The Folder $NcUpdateFolder not empty. Need to be cleared. Enter Y to confirm" if ($confirmCleanup -eq "Y") { @@ -122,15 +122,4 @@ function Start-SdnExpiredCertificateRotation { # Step 7 Restart Trace-Output -Message "Step 7 Restarting Service Fabric Cluster after configuration change" $clusterHealthy = Wait-ServiceFabricClusterHealthy -NcNodeList $NcNodeList -CertRotateConfig $CertRotateConfig -Credential $Credential -Restart - -<# Trace-Output -Message "Step 7.2 Rotate Network Controller Certificate" - #$null = Invoke-CertRotateCommand -Command 'Set-NetworkController' -Credential $Credential -Thumbprint $NcRestCertThumbprint - - # Step 8 Update REST CERT credential - Trace-Output -Message "Step 8 Update REST CERT credential" - # Step 8.1 Wait for NC App Healthy - Trace-Output -Message "Step 8.1 Wiating for Network Controller App Ready" - #Wait-NetworkControllerAppHealthy -Interval 60 - Trace-Output -Message "Step 8.2 Updating REST CERT Credential object calling REST API" #> - #Update-NetworkControllerCredentialResource -NcUri "https://$($NcInfraInfo.NcRestName)" -NewRestCertThumbprint $NcRestCertThumbprint -Credential $NcRestCredential } diff --git a/src/modules/SdnDiag.NetworkController/public/Get-SdnNetworkController.ps1 b/src/modules/SdnDiag.NetworkController/public/Get-SdnNetworkController.ps1 index b4f91141..43508ba9 100644 --- a/src/modules/SdnDiag.NetworkController/public/Get-SdnNetworkController.ps1 +++ b/src/modules/SdnDiag.NetworkController/public/Get-SdnNetworkController.ps1 @@ -23,22 +23,31 @@ function Get-SdnNetworkController { $Credential = [System.Management.Automation.PSCredential]::Empty ) - try { - if (-NOT ($PSBoundParameters.ContainsKey('NetworkController'))) { - $config = Get-SdnModuleConfiguration -Role 'NetworkController' - $confirmFeatures = Confirm-RequiredFeaturesInstalled -Name $config.windowsFeature - if (-NOT ($confirmFeatures)) { - "The current machine is not a NetworkController, run this on NetworkController or use -NetworkController parameter to specify one" | Trace-Output -Level:Warning - return # don't throw exception, since this is a controlled scenario and we do not need stack exception tracing - } + if (-NOT ($PSBoundParameters.ContainsKey('NetworkController'))) { + $config = Get-SdnModuleConfiguration -Role 'NetworkController' + $confirmFeatures = Confirm-RequiredFeaturesInstalled -Name $config.windowsFeature + if (-NOT ($confirmFeatures)) { + "The current machine is not a NetworkController, run this on NetworkController or use -NetworkController parameter to specify one" | Trace-Output -Level:Warning + return # don't throw exception, since this is a controlled scenario and we do not need stack exception tracing + } + } + + $sb = { + # check if service fabric service is running otherwise this command will hang + if ((Get-Service -Name 'FabricHostSvc').Status -ine 'Running' ) { + throw "Service Fabric Service is not running on $NetworkController" } + return (Get-NetworkController) + } + + try { try { if (Test-ComputerNameIsLocal -ComputerName $NetworkController) { - $result = Get-NetworkController + $result = Invoke-Command -ScriptBlock $sb -ErrorAction Stop } else { - $result = Invoke-PSRemoteCommand -ComputerName $NetworkController -ScriptBlock { Get-NetworkController } -Credential $Credential + $result = Invoke-PSRemoteCommand -ComputerName $NetworkController -ScriptBlock $sb -Credential $Credential -ErrorAction Stop } } catch { diff --git a/src/modules/SdnDiag.NetworkController/public/Get-SdnNetworkControllerNode.ps1 b/src/modules/SdnDiag.NetworkController/public/Get-SdnNetworkControllerNode.ps1 index c28abf4c..163398c9 100644 --- a/src/modules/SdnDiag.NetworkController/public/Get-SdnNetworkControllerNode.ps1 +++ b/src/modules/SdnDiag.NetworkController/public/Get-SdnNetworkControllerNode.ps1 @@ -6,7 +6,7 @@ function Get-SdnNetworkControllerNode { Specifies the friendly name of the node for the network controller. If not provided, settings are retrieved for all nodes in the deployment. .PARAMETER NetworkController Specifies the name or IP address of the network controller node on which this cmdlet operates. The parameter is optional if running on network controller node. - .PARAMETER Credential + .PARAMETER Credential Specifies a user account that has permission to perform this action. The default is the current user. .EXAMPLE PS> Get-SdnNetworkControllerNode @@ -31,50 +31,65 @@ function Get-SdnNetworkControllerNode { [switch]$ServerNameOnly ) - try { + if (-NOT ($PSBoundParameters.ContainsKey('NetworkController'))) { + $config = Get-SdnModuleConfiguration -Role 'NetworkController' + $confirmFeatures = Confirm-RequiredFeaturesInstalled -Name $config.windowsFeature + if (-NOT ($confirmFeatures)) { + "The current machine is not a NetworkController, run this on NetworkController or use -NetworkController parameter to specify one" | Trace-Output -Level:Warning + return # don't throw exception, since this is a controlled scenario and we do not need stack exception tracing + } + } - if (-NOT ($PSBoundParameters.ContainsKey('NetworkController'))) { - $config = Get-SdnModuleConfiguration -Role 'NetworkController' - $confirmFeatures = Confirm-RequiredFeaturesInstalled -Name $config.windowsFeature - if (-NOT ($confirmFeatures)) { - "The current machine is not a NetworkController, run this on NetworkController or use -NetworkController parameter to specify one" | Trace-Output -Level:Warning - return # don't throw exception, since this is a controlled scenario and we do not need stack exception tracing - } + $params = @{ + NetworkController = $NetworkController + Credential = $Credential + ErrorAction = 'Stop' + } + if ($Name) { + $params.Add('Name', $Name) + } + + $sb = { + param([String]$arg0) + + # check if service fabric service is running otherwise this command will hang + if ((Get-Service -Name 'FabricHostSvc').Status -ine 'Running' ) { + throw "Service Fabric Service is not running on $NetworkController" } + if ([string]::IsNullOrEmpty($arg0)) { + Get-NetworkControllerNode -ErrorAction Stop + } + else { + Get-NetworkControllerNode -Name $arg0 -ErrorAction Stop + } + } + + try { try { + # Run the script block locally or remotely if (Test-ComputerNameIsLocal -ComputerName $NetworkController) { - $result = Get-NetworkControllerNode -ErrorAction Stop + $result = Invoke-Command -ScriptBlock $sb -ArgumentList @($Name) -ErrorAction Stop } else { - $result = Invoke-PSRemoteCommand -ComputerName $NetworkController -Credential $Credential -ScriptBlock { - Get-NetworkControllerNode -ErrorAction Stop - } -ErrorAction Stop + $result = Invoke-PSRemoteCommand -ComputerName $NetworkController -Credential $Credential -ScriptBlock $sb -ArgumentList @($Name) -ErrorAction Stop } # in this scenario if the results returned we will parse the objects returned and generate warning to user if node is not up # this property is only going to exist though if service fabric is healthy and underlying NC cmdlet can query node status - foreach($obj in $result){ - if($obj.Status -ine 'Up'){ - "{0} is reporting status {1}" -f $obj.Name, $obj.Status | Trace-Output -Level:Warning + $result | ForEach-Object { + if ($_.Status -ine 'Up') { + "{0} is reporting status {1}" -f $_.Name, $_.Status | Trace-Output -Level:Warning } - # if we returned the object, we want to add a new property called NodeCertificateThumbprint as this will ensure consistent - # output in scenarios where this operation fails due to NC unhealthy and we need to fallback to reading the cluster manifest - $result | ForEach-Object { - if (!($_.PSOBject.Properties.name -contains "NodeCertificateThumbprint")) { - $_ | Add-Member -MemberType NoteProperty -Name 'NodeCertificateThumbprint' -Value $_.NodeCertificate.Thumbprint - } + if (!($_.PSOBject.Properties.name -contains "NodeCertificateThumbprint")) { + $_ | Add-Member -MemberType NoteProperty -Name 'NodeCertificateThumbprint' -Value $_.NodeCertificate.Thumbprint } } } catch { - "Get-NetworkControllerNode failed with following exception: `n`t{0}`n" -f $_ | Trace-Output -Level:Error - $result = Get-NetworkControllerNodeInfoFromClusterManifest -NetworkController $NetworkController -Credential $Credential - } - - if ($Name) { - $result = $result | Where-Object { $_.Name.Split(".")[0] -ieq $Name.Split(".")[0] -or $_.Server -ieq $Name.Split(".")[0] } + "Get-NetworkControllerNode failed: {0}" -f $_.Exception.Message | Trace-Output -Level:Error + $result = Get-NetworkControllerNodeInfoFromClusterManifest @params } if($ServerNameOnly){ @@ -83,7 +98,6 @@ function Get-SdnNetworkControllerNode { else { return $result } - } catch { $_ | Trace-Exception diff --git a/src/modules/SdnDiag.NetworkController/public/Get-SdnNetworkControllerNodeCertificate.ps1 b/src/modules/SdnDiag.NetworkController/public/Get-SdnNetworkControllerNodeCertificate.ps1 index 8e39275d..2719f39b 100644 --- a/src/modules/SdnDiag.NetworkController/public/Get-SdnNetworkControllerNodeCertificate.ps1 +++ b/src/modules/SdnDiag.NetworkController/public/Get-SdnNetworkControllerNodeCertificate.ps1 @@ -2,8 +2,16 @@ function Get-SdnNetworkControllerNodeCertificate { <# .SYNOPSIS Returns the current Network Controller node certificate + .PARAMETER NetworkControllerOid + Specifies to return only the certificate that has the specified Network Controller OID. #> + [CmdletBinding()] + param ( + [Parameter(Mandatory = $false)] + [switch]$NetworkControllerOid + ) + try { $networkControllerNode = Get-SdnNetworkControllerNode -Name $env:COMPUTERNAME @@ -14,17 +22,17 @@ function Get-SdnNetworkControllerNodeCertificate { switch ($networkControllerNode.FindCertificateBy) { 'FindBySubjectName' { "`tFindBySubjectName: {0}" -f $networkControllerNode.NodeCertSubjectName | Trace-Output -Level:Verbose - $certificate = Get-SdnCertificate -Path 'Cert:\LocalMachine\My' -Subject $networkControllerNode.NodeCertSubjectName + $certificate = Get-SdnCertificate -Path 'Cert:\LocalMachine\My' -Subject $networkControllerNode.NodeCertSubjectName -NetworkControllerOid:$NetworkControllerOid } 'FindByThumbprint' { "`FindByThumbprint: {0}" -f $networkControllerNode.NodeCertificateThumbprint | Trace-Output -Level:Verbose - $certificate = Get-SdnCertificate -Path 'Cert:\LocalMachine\My' -Thumbprint $networkControllerNode.NodeCertificateThumbprint + $certificate = Get-SdnCertificate -Path 'Cert:\LocalMachine\My' -Thumbprint $networkControllerNode.NodeCertificateThumbprint -NetworkControllerOid:$NetworkControllerOid } } } else { - $certificate = Get-SdnCertificate -Path 'Cert:\LocalMachine\My' -Thumbprint $networkControllerNode.NodeCertificateThumbprint + $certificate = Get-SdnCertificate -Path 'Cert:\LocalMachine\My' -Thumbprint $networkControllerNode.NodeCertificateThumbprint -NetworkControllerOid:$NetworkControllerOid } if ($null -eq $certificate) { diff --git a/src/modules/SdnDiag.NetworkController/public/New-SdnCertificateRotationConfig.ps1 b/src/modules/SdnDiag.NetworkController/public/New-SdnCertificateRotationConfig.ps1 index fe54a45d..3c8ca441 100644 --- a/src/modules/SdnDiag.NetworkController/public/New-SdnCertificateRotationConfig.ps1 +++ b/src/modules/SdnDiag.NetworkController/public/New-SdnCertificateRotationConfig.ps1 @@ -1,76 +1,115 @@ function New-SdnCertificateRotationConfig { <# .SYNOPSIS - Prepare the Network Controller Ceritifcate Rotation Configuration to determine which certificates to be used. - .PARAMETER NetworkController - Specifies the name the network controller node on which this cmdlet operates. The parameter is optional if running on network controller node. + Prepare the Network Controller Certificate Rotation Configuration to determine which certificates to be used. .PARAMETER Credential Specifies a user account that has permission to perform this action. The default is the current user. .EXAMPLE - PS> New-SdnCertificateRotationConfig - .EXAMPLE - PS> New-SdnCertificateRotationConfig -NetworkController 'NC01' -Credential (Get-Credential) + PS> New-SdnCertificateRotationConfig -CertificateType 'Rest' #> [CmdletBinding()] param ( - [Parameter(Mandatory = $false)] - [String]$NetworkController = $env:COMPUTERNAME, + [Parameter(Mandatory = $true)] + [ValidateSet('Rest','NetworkController','Server','LoadBalancerMux')] + [String]$CertificateType, [Parameter(Mandatory = $false)] [System.Management.Automation.PSCredential] [System.Management.Automation.Credential()] $Credential = [System.Management.Automation.PSCredential]::Empty, - [Parameter(Mandatory = $true)] - [ValidateSet('Rest','NetworkController','Server','LoadBalancerMux')] - [String]$CertificateType - ) - - $CertificateRotationConfig = @{} - $getNewestCertScript = { - param([string]$param1, [string]$param2, [string]$param3) + [Parameter(Mandatory = $false)] + [System.Management.Automation.PSCredential] + [System.Management.Automation.Credential()] + $NcRestCredential = [System.Management.Automation.PSCredential]::Empty, - if ([string]::IsNullOrWhiteSpace($param2)) { - $cert = Get-SdnCertificate -Path $param1 -Subject $param2 -NetworkControllerOid - } - else { - $cert = Get-SdnCertificate -Path $param1 -Subject $param3 -NetworkControllerOid - } - # get the certificate that has the expiration date furthest in the future - # we also want to ensure we filter for certificates that have the Network Controller OID - if ($cert) { - $cert = $cert | Sort-Object -Property NotAfter -Descending | Select-Object -First 1 - return $cert.Thumbprint - } + [Parameter(Mandatory = $false)] + [switch]$NetworkControllerOid + ) - return $null - } + $certRotateConfig = [CertRotateConfig]::new() try { - $NcInfraInfo = Get-SdnNetworkControllerInfoOffline -NetworkController $NetworkController -Credential $Credential - $CertificateRotationConfig["ClusterCredentialType"] = $NcInfraInfo.ClusterCredentialType + $ncInfraInfo = Get-SdnNetworkControllerInfoOffline + $certRotateConfig.ClusterCredentialType = $ncInfraInfo.ClusterCredentialType + [string]$restSubjectName = "CN=$($NcInfraInfo.NcRestName)" + [uri]$ncUrl = "https://$($NcInfraInfo.NcRestName)" switch ($CertificateType) { + 'LoadBalancerMux' { + $servers = Get-SdnLoadBalancerMux -NcUri $ncUrl -Credential $NcRestCredential + $servers | ForEach-Object { + $virtualServer = Get-SdnResource -NcUri $ncUrl -ResourceRef $_.properties.virtualServer.resourceRef + $connection = $virtualServer.properties.connections | Where-Object { $_.credentialType -ieq "X509Certificate" -or $_.credentialType -ieq "X509CertificateSubjectName" } + $managementAddress = $connection.managementAddresses[0] + + "Retrieving latest certificate from $managementAddress" | Trace-Output + $cert = Invoke-PSRemoteCommand -ComputerName $managementAddress -ScriptBlock { + param([switch]$arg0) + return (Get-SdnMuxCertificate -NetworkControllerOid:$arg0) + } -ArgumentList @($NetworkControllerOid) -Credential $Credential -ErrorAction Stop + + $newestCert = $cert | Sort-Object -Property NotAfter -Descending | Select-Object -First 1 + $certRotateConfig.NodeCerts += [LoadBalancerMuxNodeCert]@{ + Thumbprint = $newestCert.Thumbprint + SubjectName = $newestCert.Subject + IpAddressOrFQDN = $managementAddress + NodeName = $newestCert.PSComputerName + ResourceRef = $_.ResourceRef + IsSelfSigned = (Confirm-IsCertSelfSigned -Certificate $newestCert) + } + } + } + + 'NetworkController' { + } + 'Rest' { - $CertificateRotationConfig["NcRestCert"] = Invoke-PSRemoteCommand @{ - ComputerName = $NetworkController - ScriptBlock = $getNewestCertScript - Credential = $Credential - ArgumentList = @("Cert:\LocalMachine\My", "CN=$($NcInfraInfo.NcRestName)") + # grab the rest certificate with the latest expiration date + $restCertificate = Get-SdnCertificate -Path 'Cert:\LocalMachine\My' -Subject $restSubjectName -NetworkControllerOid:$NetworkControllerOid ` + | Sort-Object -Property NotAfter -Descending | Select-Object -First 1 + + if ($null -eq $restCertificate) { + throw New-Object System.NullReferenceException("Failed to locate Rest certificate") + } + + $restCertObject = [RestCert]@{ + CertificateType = 'Rest' + Thumbprint = $restCertificate.Thumbprint + SubjectName = $restCertificate.Subject + IsSelfSigned = (Confirm-IsCertSelfSigned -Certificate $restCertificate) } + + $certRotateConfig.RestCert = $restCertObject } - } - if($NcInfraInfo.ClusterCredentialType -eq "X509"){ - foreach ($ncNode in $($NcInfraInfo.NodeList)) { - Trace-Output -Message "Looking for Node Cert for Node: $($ncNode.NodeName), IpAddressOrFQDN: $($ncNode.IpAddressOrFQDN)" -Level:Verbose - $ncNodeCert = Invoke-PSRemoteCommand -ComputerName $ncNode.IpAddressOrFQDN -ScriptBlock $getNewestCertScript -Credential $Credential - $CertificateRotationConfig[$ncNode.NodeName.ToLower()] = $ncNodeCert + 'Server' { + $servers = Get-SdnServer -NcUri $ncUrl -Credential $NcRestCredential + $servers | ForEach-Object { + $connection = $_.properties.connections | Where-Object { $_.credentialType -ieq "X509Certificate" -or $_.credentialType -ieq "X509CertificateSubjectName" } + $managementAddress = $connection.managementAddresses[0] + + "Retrieving latest certificate from $managementAddress" | Trace-Output + $cert = Invoke-PSRemoteCommand -ComputerName $managementAddress -ScriptBlock { + param([switch]$arg0) + return (Get-SdnServerCertificate -NetworkControllerOid:$arg0) + } -ArgumentList @($NetworkControllerOid) -Credential $Credential -ErrorAction Stop + + $newestCert = $cert | Sort-Object -Property NotAfter -Descending | Select-Object -First 1 + $certRotateConfig.NodeCerts += [ServerNodeCert]@{ + Thumbprint = $newestCert.Thumbprint + SubjectName = $newestCert.Subject + IpAddressOrFQDN = $managementAddress + NodeName = $newestCert.PSComputerName + ResourceRef = $_.ResourceRef + IsSelfSigned = (Confirm-IsCertSelfSigned -Certificate $newestCert) + } + } } } - return $CertificateRotationConfig + return $certRotateConfig } catch { $_ | Trace-Exception diff --git a/src/modules/SdnDiag.NetworkController/public/Start-SdnCertificateRotation.ps1 b/src/modules/SdnDiag.NetworkController/public/Start-SdnCertificateRotation.ps1 index 76f052a9..c086efbd 100644 --- a/src/modules/SdnDiag.NetworkController/public/Start-SdnCertificateRotation.ps1 +++ b/src/modules/SdnDiag.NetworkController/public/Start-SdnCertificateRotation.ps1 @@ -65,10 +65,7 @@ function Start-SdnCertificateRotation { ) # ensure that the module is running as local administrator - $elevated = ([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator) - if (-NOT $elevated) { - throw New-Object System.Exception("This function requires elevated permissions. Run PowerShell as an Administrator and import the module again.") - } + Confirm-IsAdmin $config = Get-SdnModuleConfiguration -Role 'NetworkController' $confirmFeatures = Confirm-RequiredFeaturesInstalled -Name $config.windowsFeature @@ -178,6 +175,7 @@ function Start-SdnCertificateRotation { $newSelfSignedCert = New-SdnNetworkControllerRestCertificate -RestName $NcInfraInfo.NcRestName.ToString() -NotAfter $NotAfter -Path $CertPath.FullName ` -CertPassword $CertPassword -Credential $Credential -FabricDetails $sdnFabricDetails $selfSignedRestCertFile = $newSelfSignedCert.FileInfo + $encryptionCertificate = $newSelfSignedCert.FileInfo } 'NetworkController' { $newSelfSignedCert = New-SdnNetworkControllerCertificate -NotAfter $NotAfter -Path $CertPath.FullName -CertPassword $CertPassword -Credential $Credential @@ -246,15 +244,39 @@ function Start-SdnCertificateRotation { ##################################### # - # Rotate NC Certificate Expired + # Rotate Certificates # ##################################### - if ($restCertExpired -or !$ncHealthy) { - # Use this for certificate if either rest cert expired or nc unhealthy, get-networkcontroller failed - Start-SdnExpiredCertificateRotation -CertRotateConfig $CertRotateConfig -Credential $Credential -NcRestCredential $NcRestCredential + switch ($CertificateType) { + 'Rest' { + # if the rest certificate / encryption certificate is expired, we need to rotate it + if ($restCertExpired -or !$ncHealthy) { + Start-SdnExpiredCertificateRotation -CertRotateConfig $CertRotateConfig -Credential $Credential -NcRestCredential $NcRestCredential + } + + # rotate the rest certificate + "== STAGE: ROTATE NC REST CERTIFICATE ==" | Trace-Output + $null = Invoke-CertRotateCommand -Command 'Set-NetworkController' -Credential $Credential -Thumbprint $CertRotateConfig["NcRestCert"] + "Waiting for 5 minutes before proceeding to the next step. Script will resume at {0}" -f (Get-Date).AddMinutes(5).ToUniversalTime().ToString() | Trace-Output + Start-Sleep -Seconds 300 + + # rotate the encryption certificate + "== STAGE: ROTATE NC CLUSTER CERTIFICATE ==" | Trace-Output + $null = Invoke-CertRotateCommand -Command 'Set-NetworkControllerCluster' -Credential $Credential -Thumbprint $CertRotateConfig["NcRestCert"] + "Waiting for 5 minutes before proceeding to the next step. Script will resume at {0}" -f (Get-Date).AddMinutes(5).ToUniversalTime().ToString() | Trace-Output + Start-Sleep -Seconds 300 + } } + ##################################### + # + # Certificate Seeding (Southbound Nodes) + # + ##################################### + + $rotate + "Certificate rotation has completed" | Trace-Output } catch { diff --git a/src/modules/SdnDiag.NetworkController/public/Start-SdnNetworkControllerCertificateRotation.ps1 b/src/modules/SdnDiag.NetworkController/public/Start-SdnNetworkControllerCertificateRotation.ps1 index e0279960..69125a95 100644 --- a/src/modules/SdnDiag.NetworkController/public/Start-SdnNetworkControllerCertificateRotation.ps1 +++ b/src/modules/SdnDiag.NetworkController/public/Start-SdnNetworkControllerCertificateRotation.ps1 @@ -59,10 +59,7 @@ function Start-SdnNetworkControllerCertificateRotation { ) # ensure that the module is running as local administrator - $elevated = ([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator) - if (-NOT $elevated) { - throw New-Object System.Exception("This function requires elevated permissions. Run PowerShell as an Administrator and import the module again.") - } + Confirm-IsAdmin $config = Get-SdnModuleConfiguration -Role 'NetworkController' $confirmFeatures = Confirm-RequiredFeaturesInstalled -Name $config.windowsFeature diff --git a/src/modules/SdnDiag.NetworkController/public/Start-SdnNetworkControllerRestCertificateRotation.ps1 b/src/modules/SdnDiag.NetworkController/public/Start-SdnNetworkControllerRestCertificateRotation.ps1 index 2fa216fb..0afa3a92 100644 --- a/src/modules/SdnDiag.NetworkController/public/Start-SdnNetworkControllerRestCertificateRotation.ps1 +++ b/src/modules/SdnDiag.NetworkController/public/Start-SdnNetworkControllerRestCertificateRotation.ps1 @@ -59,10 +59,7 @@ function Start-SdnNetworkControllerRestCertificateRotation { ) # ensure that the module is running as local administrator - $elevated = ([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator) - if (-NOT $elevated) { - throw New-Object System.Exception("This function requires elevated permissions. Run PowerShell as an Administrator and import the module again.") - } + Confirm-IsAdmin $config = Get-SdnModuleConfiguration -Role 'NetworkController' $confirmFeatures = Confirm-RequiredFeaturesInstalled -Name $config.windowsFeature diff --git a/src/modules/SdnDiag.Server/public/Get-SdnServerCertificate.ps1 b/src/modules/SdnDiag.Server/public/Get-SdnServerCertificate.ps1 index 66e8f89d..0f0ec2e7 100644 --- a/src/modules/SdnDiag.Server/public/Get-SdnServerCertificate.ps1 +++ b/src/modules/SdnDiag.Server/public/Get-SdnServerCertificate.ps1 @@ -1,16 +1,31 @@ function Get-SdnServerCertificate { <# .SYNOPSIS - Returns the certificate used by the SDN Host Agent. + Returns the certificate used by the Network Controller Host Agent. + .PARAMETER NetworkControllerOid + Specifies to return only the certificate that has the specified Network Controller OID. #> [CmdletBinding()] - param() + param ( + [Parameter(Mandatory = $false)] + [switch]$NetworkControllerOid + ) try { $serverCert = Get-ItemPropertyValue -Path 'HKLM:\SYSTEM\CurrentControlSet\Services\NcHostAgent\Parameters' -Name 'HostAgentCertificateCName' $subjectName = "CN={0}" -f $serverCert - $certificate = Get-SdnCertificate -Subject $subjectName -Path 'Cert:\LocalMachine\My' + $certificate = Get-SdnCertificate -Subject $subjectName -Path 'Cert:\LocalMachine\My' -NetworkControllerOid:$NetworkControllerOid + + if ($null -eq $certificate) { + if ($NetworkControllerOid) { + throw New-Object System.NullReferenceException("Failed to locate certificate for NCHostAgent containing Network Controller OID") + } + else { + throw New-Object System.NullReferenceException("Failed to locate certificate for NCHostAgent") + } + } + return $certificate } catch { diff --git a/src/modules/SdnDiag.Utilities/private/Confirm-IsAdmin.ps1 b/src/modules/SdnDiag.Utilities/private/Confirm-IsAdmin.ps1 new file mode 100644 index 00000000..c33bb8f7 --- /dev/null +++ b/src/modules/SdnDiag.Utilities/private/Confirm-IsAdmin.ps1 @@ -0,0 +1,11 @@ +function Confirm-IsAdmin { + <# + .SYNOPSIS + Confirms that the current user is an administrator. + #> + + $elevated = ([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator) + if (-NOT $elevated) { + throw New-Object System.Exception("This function requires elevated permissions. Run PowerShell as an Administrator and import the module again.") + } +} diff --git a/src/modules/SdnDiag.Utilities/private/Confirm-IsCertSelfSigned.ps1 b/src/modules/SdnDiag.Utilities/private/Confirm-IsCertSelfSigned.ps1 new file mode 100644 index 00000000..21cd059b --- /dev/null +++ b/src/modules/SdnDiag.Utilities/private/Confirm-IsCertSelfSigned.ps1 @@ -0,0 +1,13 @@ +function Confirm-IsCertSelfSigned { + [CmdletBinding()] + param ( + [Parameter(Mandatory = $true)] + [System.Security.Cryptography.X509Certificates.X509Certificate2]$Certificate + ) + + if ($Certificate.Issuer -eq $Certificate.Subject) { + return $true + } + + return $false +} From 46e8a5220822887d531182d205fdac87abe12a7e Mon Sep 17 00:00:00 2001 From: Adam Rudell Date: Thu, 18 Apr 2024 12:46:46 -0500 Subject: [PATCH 8/9] save changes --- src/modules/SdnDiag.Common/public/Get-SdnCertificate.ps1 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/modules/SdnDiag.Common/public/Get-SdnCertificate.ps1 b/src/modules/SdnDiag.Common/public/Get-SdnCertificate.ps1 index 946cc030..582716d9 100644 --- a/src/modules/SdnDiag.Common/public/Get-SdnCertificate.ps1 +++ b/src/modules/SdnDiag.Common/public/Get-SdnCertificate.ps1 @@ -37,14 +37,14 @@ function Get-SdnCertificate { [switch]$NetworkControllerOid ) - [string]$ObjectIdentifier = @('1.3.6.1.4.1.311.95.1.1.1') # this is a custom OID used for Network Controller + [string]$objectIdentifier = @('1.3.6.1.4.1.311.95.1.1.1') # this is a custom OID used for Network Controller $array = @() try { $certificateList = Get-ChildItem -Path $Path | Where-Object {$_.PSISContainer -eq $false} -ErrorAction Ignore if ($NetworkControllerOid) { $certificateList | ForEach-Object { - if ($ObjectIdentifier -iin $_.EnhancedKeyUsageList.ObjectId) { + if ($objectIdentifier -iin $_.EnhancedKeyUsageList.ObjectId) { $array += $_ } } From f233e66e0bda422b7ff25e2cb01261eee18d7432 Mon Sep 17 00:00:00 2001 From: Adam Rudell Date: Thu, 18 Apr 2024 21:59:35 -0500 Subject: [PATCH 9/9] save latest changes from testing --- .../private/Confirm-IsCertSelfSigned.ps1 | 1 + .../private/Copy-CertificateToFabric.ps1 | 188 +++++---------- .../public/Get-SdnCertificate.ps1 | 10 + .../public/Import-SdnCertificate.ps1 | 7 +- .../Start-SdnMuxCertificateRotation.ps1 | 15 +- .../SdnDiag.NetworkController.Helper.psm1 | 20 +- .../private/Confirm-IsNetworkController.ps1 | 7 + .../Start-SdnExpiredCertificateRotation.ps1 | 50 ++-- .../Test-NetworkControllerIsHealthy.ps1 | 9 + ...Update-NetworkControllerCertificateAcl.ps1 | 48 ---- .../Wait-ServiceFabricClusterHealthy.ps1 | 22 +- ...et-SdnNetworkControllerRestCertificate.ps1 | 26 +-- .../New-SdnCertificateRotationConfig.ps1 | 15 +- ...ew-SdnNetworkControllerRestCertificate.ps1 | 37 +-- ...tworkControllerRestCertificateRotation.ps1 | 215 ++++++++++++++---- .../Test-SdnCertificateRotationConfig.ps1 | 90 ++++---- .../public/New-SdnServerCertificate.ps1 | 5 +- .../Start-SdnServerCertificateRotation.ps1 | 9 +- 18 files changed, 374 insertions(+), 400 deletions(-) rename src/modules/{SdnDiag.Utilities => SdnDiag.Common}/private/Confirm-IsCertSelfSigned.ps1 (70%) rename src/modules/{SdnDiag.NetworkController => SdnDiag.LoadBalancerMux}/public/Start-SdnMuxCertificateRotation.ps1 (91%) create mode 100644 src/modules/SdnDiag.NetworkController/private/Confirm-IsNetworkController.ps1 create mode 100644 src/modules/SdnDiag.NetworkController/private/Test-NetworkControllerIsHealthy.ps1 delete mode 100644 src/modules/SdnDiag.NetworkController/private/Update-NetworkControllerCertificateAcl.ps1 rename src/modules/{SdnDiag.NetworkController => SdnDiag.Server}/public/Start-SdnServerCertificateRotation.ps1 (94%) diff --git a/src/modules/SdnDiag.Utilities/private/Confirm-IsCertSelfSigned.ps1 b/src/modules/SdnDiag.Common/private/Confirm-IsCertSelfSigned.ps1 similarity index 70% rename from src/modules/SdnDiag.Utilities/private/Confirm-IsCertSelfSigned.ps1 rename to src/modules/SdnDiag.Common/private/Confirm-IsCertSelfSigned.ps1 index 21cd059b..3327f9aa 100644 --- a/src/modules/SdnDiag.Utilities/private/Confirm-IsCertSelfSigned.ps1 +++ b/src/modules/SdnDiag.Common/private/Confirm-IsCertSelfSigned.ps1 @@ -6,6 +6,7 @@ function Confirm-IsCertSelfSigned { ) if ($Certificate.Issuer -eq $Certificate.Subject) { + "Detected the certificate subject and issuer are the same. Setting SelfSigned to true" | Trace-Output -Level:Verbose return $true } diff --git a/src/modules/SdnDiag.Common/private/Copy-CertificateToFabric.ps1 b/src/modules/SdnDiag.Common/private/Copy-CertificateToFabric.ps1 index 312def8d..2e12ee42 100644 --- a/src/modules/SdnDiag.Common/private/Copy-CertificateToFabric.ps1 +++ b/src/modules/SdnDiag.Common/private/Copy-CertificateToFabric.ps1 @@ -43,6 +43,21 @@ function Copy-CertificateToFabric { $Credential = [System.Management.Automation.PSCredential]::Empty ) + $createRemoteDirectorySB = { + param([Parameter(Position = 0)][String]$param1) + if (-NOT (Test-Path -Path $param1 -PathType Container)) { + New-Item -Path $param1 -ItemType Directory -Force + } + } + + # scriptblock to import the certificate + # this function will automatically install the certificate to the localmachine\root cert directory + # if the certificate passed to it is self-signed and it is being installed to localmachine\my cert directory + $importCertSB = { + param([Parameter(Position = 0)][String]$param1, [Parameter(Position = 1)][SecureString]$param2, [Parameter(Position = 2)][String]$param3) + Import-SdnCertificate -FilePath $param1 -CertPassword $param2 -CertStore $param3 + } + # if we are installing the rest certificate and need to seed certificate to southbound devices # then define the variables to know which nodes must be updated if ($PSCmdlet.ParameterSetName -ieq 'NetworkControllerRest' -and $InstallToSouthboundDevices) { @@ -57,6 +72,8 @@ function Copy-CertificateToFabric { } $certFileInfo = Get-Item -Path $CertFile -ErrorAction Stop + [System.String]$remoteFilePath = Join-Path -Path $certFileInfo.Directory.FullName -ChildPath $certFileInfo.Name + switch ($certFileInfo.Extension) { '.pfx' { if ($CertPassword) { @@ -79,151 +96,68 @@ function Copy-CertificateToFabric { switch ($PSCmdlet.ParameterSetName) { 'LoadBalancerMuxNode' { - foreach ($controller in $FabricDetails.NetworkController) { - # if the certificate being passed is self-signed, we will need to copy the certificate to the other controller nodes - # within the fabric and install under localmachine\root as appropriate - if ($certData.Subject -ieq $certData.Issuer) { - "Importing certificate [Subject: {0} Thumbprint:{1}] to {2}" -f ` - $certData.Subject, $certData.Thumbprint, $controller | Trace-Output - - [System.String]$remoteFilePath = Join-Path -Path $certFileInfo.Directory.FullName -ChildPath $certFileInfo.Name - $null = Invoke-PSRemoteCommand -ComputerName $controller -Credential $Credential -ScriptBlock { - param([Parameter(Position = 0)][String]$param1) - if (-NOT (Test-Path -Path $param1 -PathType Container)) { - New-Item -Path $param1 -ItemType Directory -Force - } - } -ArgumentList $certFileInfo.Directory.FullName - - Copy-FileToRemoteComputer -ComputerName $controller -Credential $Credential -Path $certFileInfo.FullName -Destination $remoteFilePath - - $null = Invoke-PSRemoteCommand -ComputerName $controller -Credential $Credential -ScriptBlock { - param([Parameter(Position = 0)][String]$param1, [Parameter(Position = 1)][SecureString]$param2, [Parameter(Position = 2)][String]$param3) - Import-SdnCertificate -FilePath $param1 -CertPassword $param2 -CertStore $param3 - } -ArgumentList @($remoteFilePath, $CertPassword, 'Cert:\LocalMachine\Root') -ErrorAction Stop - } - - else { - "No action required for {0}" -f $certData.Thumbprint | Trace-Output -Level:Verbose - } + if (Confirm-IsCertSelfSigned -Certificate $certData) { + $certStore = 'Cert:\LocalMachine\Root' + $computersToInstallCert = $FabricDetails.NetworkController } } 'NetworkControllerRest' { - # copy the pfx certificate for the rest certificate to all network controllers within the cluster - # and import to localmachine\my cert directory - foreach ($controller in $FabricDetails.NetworkController) { - "Processing {0}" -f $controller | Trace-Output -Level:Verbose - - "[REST CERT] Importing certificate [Subject: {0} Thumbprint:{1}] to {2}" -f ` - $certData.Subject, $certData.Thumbprint, $controller | Trace-Output - - if (Test-ComputerNameIsLocal -ComputerName $controller) { - $importCert = Import-SdnCertificate -FilePath $certFileInfo.FullName -CertPassword $CertPassword -CertStore 'Cert:\LocalMachine\My' - - # if the certificate was detected as self signed - # we will then copy the .cer file returned from the previous command to all the southbound nodes to install - if ($importCert.SelfSigned -and $InstallToSouthboundDevices) { - Install-SdnDiagnostics -ComputerName $southBoundNodes -Credential $Credential -ErrorAction Stop - - "[REST CERT] Installing self-signed certificate to southbound devices" | Trace-Output - Invoke-PSRemoteCommand -ComputerName $southBoundNodes -Credential $Credential -ScriptBlock { - param([Parameter(Position = 0)][String]$param1) - if (-NOT (Test-Path -Path $param1 -PathType Container)) { - $null = New-Item -Path $param1 -ItemType Directory -Force - } - } -ArgumentList $importCert.CerFileInfo.Directory.FullName - - foreach ($sbNode in $southBoundNodes) { - "[REST CERT] Installing self-signed certificate to {0}" -f $sbNode | Trace-Output - Copy-FileToRemoteComputer -ComputerName $sbNode -Credential $Credential -Path $importCert.CerFileInfo.FullName -Destination $importCert.CerFileInfo.FullName - $null = Invoke-PSRemoteCommand -ComputerName $sbNode -Credential $Credential -ScriptBlock { - param([Parameter(Position = 0)][String]$param1,[Parameter(Position = 1)][String]$param2) - Import-SdnCertificate -FilePath $param1 -CertStore $param2 - } -ArgumentList @($importCert.CerFileInfo.FullName, 'Cert:\LocalMachine\Root') -ErrorAction Stop - } - } + if (Confirm-IsCertSelfSigned -Certificate $certData) { + if ($InstallToSouthboundDevices) { + # for southbound devices, if the certificate is self-signed, we will install the certificate under the localmachine\root cert directory + $certStore = 'Cert:\LocalMachine\Root' + $computersToInstallCert = $southBoundNodes } else { - [System.String]$remoteFilePath = Join-Path -Path $certFileInfo.Directory.FullName -ChildPath $certFileInfo.Name - $null = Invoke-PSRemoteCommand -ComputerName $controller -Credential $Credential -ScriptBlock { - param([Parameter(Position = 0)][String]$param1) - if (-NOT (Test-Path -Path $param1 -PathType Container)) { - New-Item -Path $param1 -ItemType Directory -Force - } - } -ArgumentList $certFileInfo.Directory.FullName - - Copy-FileToRemoteComputer -ComputerName $controller -Credential $Credential -Path $certFileInfo.FullName -Destination $remoteFilePath - - $null = Invoke-PSRemoteCommand -ComputerName $controller -Credential $Credential -ScriptBlock { - param([Parameter(Position = 0)][String]$param1, [Parameter(Position = 1)][SecureString]$param2, [Parameter(Position = 2)][String]$param3) - Import-SdnCertificate -FilePath $param1 -CertPassword $param2 -CertStore $param3 - } -ArgumentList @($remoteFilePath, $CertPassword, 'Cert:\LocalMachine\My') + # for network controller, we will install the certificate under the localmachine\my cert directory + $certStore = 'Cert:\LocalMachine\My' + $computersToInstallCert = $FabricDetails.NetworkController } } } 'NetworkControllerNode' { - foreach ($controller in $FabricDetails.NetworkController) { - "Processing {0}" -f $controller | Trace-Output -Level:Verbose - - # if the certificate being passed is self-signed, we will need to copy the certificate to the other controller nodes - # within the fabric and install under localmachine\root as appropriate - if ($certData.Subject -ieq $certData.Issuer) { - "Importing certificate [Subject: {0} Thumbprint:{1}] to {2}" -f ` - $certData.Subject, $certData.Thumbprint, $controller | Trace-Output - - [System.String]$remoteFilePath = Join-Path -Path $certFileInfo.Directory.FullName -ChildPath $certFileInfo.Name - $null = Invoke-PSRemoteCommand -ComputerName $controller -Credential $Credential -ScriptBlock { - param([Parameter(Position = 0)][String]$param1) - if (-NOT (Test-Path -Path $param1 -PathType Container)) { - New-Item -Path $param1 -ItemType Directory -Force - } - } -ArgumentList $certFileInfo.Directory.FullName - - Copy-FileToRemoteComputer -ComputerName $controller -Credential $Credential -Path $certFileInfo.FullName -Destination $remoteFilePath - - $null = Invoke-PSRemoteCommand -ComputerName $controller -Credential $Credential -ScriptBlock { - param([Parameter(Position = 0)][String]$param1, [Parameter(Position = 1)][SecureString]$param2, [Parameter(Position = 2)][String]$param3) - Import-SdnCertificate -FilePath $param1 -CertPassword $param2 -CertStore $param3 - } -ArgumentList @($remoteFilePath, $CertPassword, 'Cert:\LocalMachine\Root') -ErrorAction Stop - } - - else { - "No action required for {0}" -f $certData.Thumbprint | Trace-Output -Level:Verbose - } + if (Confirm-IsCertSelfSigned -Certificate $certData) { + $certStore = 'Cert:\LocalMachine\Root' + $computersToInstallCert = $FabricDetails.NetworkController } } # for ServerNodes, we must distribute the server certificate and install to the cert:\localmachine\root directory on each of the # network controller nodes 'ServerNode' { - foreach ($controller in $FabricDetails.NetworkController) { - # if the certificate being passed is self-signed, we will need to copy the certificate to the other controller nodes - # within the fabric and install under localmachine\root as appropriate - if ($certData.Subject -ieq $certData.Issuer) { - "Importing certificate [Subject: {0} Thumbprint:{1}] to {2}" -f ` - $certData.Subject, $certData.Thumbprint, $controller | Trace-Output - - [System.String]$remoteFilePath = Join-Path -Path $certFileInfo.Directory.FullName -ChildPath $certFileInfo.Name - $null = Invoke-PSRemoteCommand -ComputerName $controller -Credential $Credential -ScriptBlock { - param([Parameter(Position = 0)][String]$param1) - if (-NOT (Test-Path -Path $param1 -PathType Container)) { - New-Item -Path $param1 -ItemType Directory -Force - } - } -ArgumentList $certFileInfo.Directory.FullName - - Copy-FileToRemoteComputer -ComputerName $controller -Credential $Credential -Path $certFileInfo.FullName -Destination $remoteFilePath - - $null = Invoke-PSRemoteCommand -ComputerName $controller -Credential $Credential -ScriptBlock { - param([Parameter(Position = 0)][String]$param1, [Parameter(Position = 1)][SecureString]$param2, [Parameter(Position = 2)][String]$param3) - Import-SdnCertificate -FilePath $param1 -CertPassword $param2 -CertStore $param3 - } -ArgumentList @($remoteFilePath, $CertPassword, 'Cert:\LocalMachine\Root') -ErrorAction Stop - } - - else { - "No action required for {0}" -f $certData.Thumbprint | Trace-Output -Level:Verbose - } + if (Confirm-IsCertSelfSigned -Certificate $certData) { + $certStore = 'Cert:\LocalMachine\Root' + $computersToInstallCert = $FabricDetails.NetworkController } } } + + # create the remote directory we need to copy certificate to + Invoke-PSRemoteCommand @{ + ComputerName = $computersToInstallCert + Credential = $Credential + ScriptBlock = $createRemoteDirectorySB + ArgumentList = @($certFileInfo.Directory.FullName) + ErrorAction = 'Stop' + } + + # copy the file + Copy-FileToRemoteComputer @{ + ComputerName = $computersToInstallCert + Credential = $Credential + Path = $certFileInfo.FullName + Destination = $remoteFilePath + ErrorAction = 'Stop' + } + + # import the certificate + Invoke-PSRemoteCommand @{ + ComputerName = $computersToInstallCert + Credential = $Credential + ScriptBlock = $importCertSB + ArgumentList = @($remoteFilePath, $CertPassword, $certStore) + ErrorAction = 'Stop' + } } diff --git a/src/modules/SdnDiag.Common/public/Get-SdnCertificate.ps1 b/src/modules/SdnDiag.Common/public/Get-SdnCertificate.ps1 index 582716d9..35f6c5f0 100644 --- a/src/modules/SdnDiag.Common/public/Get-SdnCertificate.ps1 +++ b/src/modules/SdnDiag.Common/public/Get-SdnCertificate.ps1 @@ -42,12 +42,22 @@ function Get-SdnCertificate { try { $certificateList = Get-ChildItem -Path $Path | Where-Object {$_.PSISContainer -eq $false} -ErrorAction Ignore + if ($null -eq $certificateList) { + return $null + } + if ($NetworkControllerOid) { $certificateList | ForEach-Object { if ($objectIdentifier -iin $_.EnhancedKeyUsageList.ObjectId) { $array += $_ } } + + # if no certificates are found based on the OID, search based on other criteria + if (!$array) { + "Unable to locate certificates that match Network Controller OID: {0}. Searching based on other criteria." -f $objectIdentifier | Trace-Output -Level:Warning + $array = $certificateList + } } else { $array = $certificateList diff --git a/src/modules/SdnDiag.Common/public/Import-SdnCertificate.ps1 b/src/modules/SdnDiag.Common/public/Import-SdnCertificate.ps1 index e7ce7e67..c28d7b38 100644 --- a/src/modules/SdnDiag.Common/public/Import-SdnCertificate.ps1 +++ b/src/modules/SdnDiag.Common/public/Import-SdnCertificate.ps1 @@ -30,7 +30,7 @@ function Import-SdnCertificate { $certObject = [PSCustomObject]@{ SelfSigned = $false CertInfo = $null - CerFileInfo = $null + SelfSignedCertFileInfo = $null } try { @@ -73,8 +73,7 @@ function Import-SdnCertificate { } # determine if the certificates being used are self signed - if ($certObject.CertInfo.Subject -ieq $certObject.CertInfo.Issuer) { - "Detected the certificate subject and issuer are the same. Setting SelfSigned to true" | Trace-Output -Level:Verbose + if (Confirm-IsCertSelfSigned -Certificate $certObject.CertInfo) { $certObject.SelfSigned = $true # check to see if we installed to root store with above operation @@ -84,7 +83,7 @@ function Import-SdnCertificate { $selfSignedCerExists = Get-SdnCertificate -Path $trustedRootStore -Thumbprint $certObject.CertInfo.Thumbprint [System.String]$selfSignedCerPath = "{0}\{1}.cer" -f (Split-Path $fileInfo.FullName -Parent), ($certObject.CertInfo.Subject).Replace('=','_') $selfSignedCer = Export-Certificate -Cert $certObject.CertInfo -FilePath $selfSignedCerPath -ErrorAction Stop - $certObject.CerFileInfo = $selfSignedCer + $certObject.SelfSignedCertFileInfo = $selfSignedCer if (-NOT ($selfSignedCerExists)) { # import the certificate to the trusted root store diff --git a/src/modules/SdnDiag.NetworkController/public/Start-SdnMuxCertificateRotation.ps1 b/src/modules/SdnDiag.LoadBalancerMux/public/Start-SdnMuxCertificateRotation.ps1 similarity index 91% rename from src/modules/SdnDiag.NetworkController/public/Start-SdnMuxCertificateRotation.ps1 rename to src/modules/SdnDiag.LoadBalancerMux/public/Start-SdnMuxCertificateRotation.ps1 index 482fa6f2..754d3a56 100644 --- a/src/modules/SdnDiag.NetworkController/public/Start-SdnMuxCertificateRotation.ps1 +++ b/src/modules/SdnDiag.LoadBalancerMux/public/Start-SdnMuxCertificateRotation.ps1 @@ -18,8 +18,8 @@ function Start-SdnMuxCertificateRotation { [CmdletBinding(DefaultParameterSetName = 'GenerateCertificate')] param ( - [Parameter(Mandatory = $false, ParameterSetName = 'GenerateCertificate')] - [System.String]$NetworkController = $env:COMPUTERNAME, + [Parameter(Mandatory = $true, ParameterSetName = 'GenerateCertificate')] + [System.String]$NetworkController, [Parameter(Mandatory = $true, ParameterSetName = 'GenerateCertificate')] [System.Management.Automation.PSCredential] @@ -45,16 +45,7 @@ function Start-SdnMuxCertificateRotation { ) # ensure that the module is running as local administrator - $elevated = ([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator) - if (-NOT $elevated) { - throw New-Object System.Exception("This function requires elevated permissions. Run PowerShell as an Administrator and import the module again.") - } - - $config = Get-SdnModuleConfiguration -Role 'NetworkController' - $confirmFeatures = Confirm-RequiredFeaturesInstalled -Name $config.windowsFeature - if (-NOT ($confirmFeatures)) { - throw New-Object System.NotSupportedException("The current machine is not a NetworkController, run this on NetworkController.") - } + Confirm-IsAdmin $array = @() $headers = @{"Accept"="application/json"} diff --git a/src/modules/SdnDiag.NetworkController/SdnDiag.NetworkController.Helper.psm1 b/src/modules/SdnDiag.NetworkController/SdnDiag.NetworkController.Helper.psm1 index e66a304d..8ec58cdf 100644 --- a/src/modules/SdnDiag.NetworkController/SdnDiag.NetworkController.Helper.psm1 +++ b/src/modules/SdnDiag.NetworkController/SdnDiag.NetworkController.Helper.psm1 @@ -16,8 +16,8 @@ class BaseCert { [bool]$IsSelfSigned } -class RestCert : BaseCert { - [CertType]$CertificateType = [CertType]::Rest +class RestCertificate : BaseCert { + [CertType]$CertificateType = [CertType]::RestCertificate } class NodeCert : BaseCert { @@ -27,19 +27,19 @@ class NodeCert : BaseCert { } class NetworkControllerNodeCert : NodeCert { - [CertType]$CertificateType = [CertType]::NetworkController + [CertType]$CertificateType = [CertType]::NetworkControllerNodeCert } class LoadBalancerMuxNodeCert : NodeCert { - [CertType]$CertificateType = [CertType]::LoadBalancerMux + [CertType]$CertificateType = [CertType]::LoadBalancerMuxNodeCert } class ServerNodeCert : NodeCert { - [CertType]$CertificateType = [CertType]::Server + [CertType]$CertificateType = [CertType]::ServerNodeCert } class CertRotateConfig { - [RestCert]$RestCert + [RestCertificate]$RestCertificate [ClusterCredentialType]$ClusterCredentialType = [ClusterCredentialType]::Kerberos [Object[]]$NodeCerts } @@ -50,10 +50,10 @@ enum ClusterCredentialType { } enum CertType { - Rest - NetworkController - Server - LoadBalancerMux + RestCertificate + NetworkControllerNodeCert + ServerNodeCert + LoadBalancerMuxNodeCert } enum NcManagedRoles { diff --git a/src/modules/SdnDiag.NetworkController/private/Confirm-IsNetworkController.ps1 b/src/modules/SdnDiag.NetworkController/private/Confirm-IsNetworkController.ps1 new file mode 100644 index 00000000..e67233d7 --- /dev/null +++ b/src/modules/SdnDiag.NetworkController/private/Confirm-IsNetworkController.ps1 @@ -0,0 +1,7 @@ +function Confirm-IsNetworkController { + $config = Get-SdnModuleConfiguration -Role 'NetworkController' + $confirmFeatures = Confirm-RequiredFeaturesInstalled -Name $config.windowsFeature + if (-NOT ($confirmFeatures)) { + throw New-Object System.NotSupportedException("The current machine is not a NetworkController, run this on NetworkController.") + } +} diff --git a/src/modules/SdnDiag.NetworkController/private/Start-SdnExpiredCertificateRotation.ps1 b/src/modules/SdnDiag.NetworkController/private/Start-SdnExpiredCertificateRotation.ps1 index ab9a0a81..d934165d 100644 --- a/src/modules/SdnDiag.NetworkController/private/Start-SdnExpiredCertificateRotation.ps1 +++ b/src/modules/SdnDiag.NetworkController/private/Start-SdnExpiredCertificateRotation.ps1 @@ -22,8 +22,7 @@ function Start-SdnExpiredCertificateRotation { param ( [Parameter(Mandatory = $true)] - [hashtable] - $CertRotateConfig, + [CertRotateConfig]$CertRotateConfig, [System.Management.Automation.PSCredential] [System.Management.Automation.Credential()] @@ -60,25 +59,40 @@ function Start-SdnExpiredCertificateRotation { throw New-Object System.NotSupportedException("Current Network Controller Rest certificate not found.") } - $NcVms = $NcNodeList.IpAddressOrFQDN - - if (Test-Path -Path $NcUpdateFolder) { - $items = Get-ChildItem -Path $NcUpdateFolder -ErrorAction Ignore - if ($items.Count -gt 0) { - $confirmCleanup = Read-Host "The Folder $NcUpdateFolder not empty. Need to be cleared. Enter Y to confirm" - if ($confirmCleanup -eq "Y") { - $items | Remove-Item -Force -Recurse + if (Test-Path -Path $NcUpdateFolder -ItemType Container) { + $items = Get-ChildItem -Path $NcUpdateFolder + if ($items) { + $confirm = Confirm-UserInput -Message "$NcUpdateFolder not empty. Proceed with cleanup? [Y/N]: " + if (-NOT $confirm) { + # throw terminating exception here + # this will stop the execution Start-SdnCertificateRotation which calls into this function + throw New-Object System.OperationCanceledException("User cancelled the operation") } else { - return + $items | Remove-Item -Force -Recurse } } } - foreach ($nc in $NcVms) { - Invoke-Command -ComputerName $nc -ScriptBlock { - Write-Host "[$(HostName)] Stopping Service Fabric Service" - Stop-Service FabricHostSvc -Force + # stop service fabric service + $stopSfService = Invoke-PSRemoteCommand -ComputerName $NcNodeList.IpAddressOrFQDN -Credential $Credential -ScriptBlock { + Stop-Service -Name 'FabricHostSvc' -Force -ErrorAction Ignore 3>$null # redirect warning to null + if ((Get-Service -Name 'FabricHostSvc' -ErrorAction Ignore).Status -eq 'Stopped') { + return $true + } + else { + return $false + } + } -AsJob -PassThru -Activity 'Stopping Service Fabric Service on Network Controller' -ExecutionTimeOut 900 + + # enumerate the results of stopping service fabric service + # if any of the service fabric service is not stopped, throw an exception as we do not want to proceed further + $stopSfService | ForEach-Object { + if ($_) { + "Service Fabric Service stopped on {0}" -f $_.PSComputerName | Trace-Output + } + else { + throw "Failed to stop Service Fabric Service on $($_.PSComputerName)" } } @@ -93,16 +107,14 @@ function Start-SdnExpiredCertificateRotation { Trace-Output -Message "Step 3 Copy the new files back to the NC vms" Copy-ServiceFabricManifestToNetworkController -NcNodeList $NcNodeList -ManifestFolder $ManifestFolderNew -Credential $Credential - # Step 5 Start FabricHostSvc and wait for SF system service to become healty + # Step 4 Start FabricHostSvc and wait for SF system service to become healty Trace-Output -Message "Step 4 Start FabricHostSvc and wait for SF system service to become healty" - Trace-Output -Message "Step 4.1 Update Network Controller Certificate ACL to allow 'Network Service' Access" - Update-NetworkControllerCertificateAcl -NcNodeList $NcNodeList -CertRotateConfig $CertRotateConfig -Credential $Credential - Trace-Output -Message "Step 4.2 Start Service Fabric Host Service and wait" $clusterHealthy = Wait-ServiceFabricClusterHealthy -NcNodeList $NcNodeList -CertRotateConfig $CertRotateConfig -Credential $Credential Trace-Output -Message "ClusterHealthy: $clusterHealthy" if($clusterHealthy -ne $true){ throw New-Object System.NotSupportedException("Cluster unheathy after manifest update, we cannot continue with current situation") } + # Step 6 Invoke SF Cluster Upgrade Trace-Output -Message "Step 5 Invoke SF Cluster Upgrade" Update-ServiceFabricCluster -NcNodeList $NcNodeList -CertRotateConfig $CertRotateConfig -ManifestFolderNew $ManifestFolderNew -Credential $Credential diff --git a/src/modules/SdnDiag.NetworkController/private/Test-NetworkControllerIsHealthy.ps1 b/src/modules/SdnDiag.NetworkController/private/Test-NetworkControllerIsHealthy.ps1 new file mode 100644 index 00000000..dc129d60 --- /dev/null +++ b/src/modules/SdnDiag.NetworkController/private/Test-NetworkControllerIsHealthy.ps1 @@ -0,0 +1,9 @@ +function Test-NetworkControllerIsHealthy { + try { + $null = Get-NetworkController -ErrorAction 'Stop' + return $true + } + catch { + return $false + } +} diff --git a/src/modules/SdnDiag.NetworkController/private/Update-NetworkControllerCertificateAcl.ps1 b/src/modules/SdnDiag.NetworkController/private/Update-NetworkControllerCertificateAcl.ps1 deleted file mode 100644 index 87f1d4ff..00000000 --- a/src/modules/SdnDiag.NetworkController/private/Update-NetworkControllerCertificateAcl.ps1 +++ /dev/null @@ -1,48 +0,0 @@ -function Update-NetworkControllerCertificateAcl { - <# - .SYNOPSIS - Update the Network Controller Certificate to grant Network Service account read access to the private key. - .PARAMETER NcNodeList - The NcNodeList that retrieved via Get-SdnNetworkControllerInfoOffline. - .PARAMETER CertRotateConfig - The Config generated by New-SdnCertificateRotationConfig to include NC REST certificate thumbprint and node certificate thumbprint. - .PARAMETER Credential - Specifies a user account that has permission to perform this action. The default is the current user. - #> - - param ( - [Parameter(Mandatory = $true)] - [PSCustomObject[]] - $NcNodeList, - [Parameter(Mandatory = $true)] - [hashtable] - $CertRotateConfig, - [Parameter(Mandatory = $false)] - [System.Management.Automation.PSCredential] - [System.Management.Automation.Credential()] - $Credential = [System.Management.Automation.PSCredential]::Empty - ) - - try { - $NcRestCertThumbprint = $CertRotateConfig["NcRestCert"] - - foreach ($ncNode in $NcNodeList) { - $ncNodeCertThumbprint = $CertRotateConfig[$ncNode.NodeName.ToLower()] - Invoke-PSRemoteCommand -ComputerName $ncNode.IpAddressOrFQDN -Credential $Credential -ScriptBlock { - param([Parameter(Position = 0)][String]$param1, [Parameter(Position = 1)][String]$param2) - Set-SdnCertificateAcl -Path $param1 -Thumbprint $param2 - } -ArgumentList @('Cert:\LocalMachine\My', $NcRestCertThumbprint) - - if ($CertRotateConfig["ClusterCredentialType"] -ieq "X509") { - Invoke-PSRemoteCommand -ComputerName $ncNode.IpAddressOrFQDN -Credential $Credential -ScriptBlock { - param([Parameter(Position = 0)][String]$param1, [Parameter(Position = 1)][String]$param2) - Set-SdnCertificateAcl -Path $param1 -Thumbprint $param2 - } -ArgumentList @('Cert:\LocalMachine\My', $ncNodeCertThumbprint) - } - } - } - catch { - $_ | Trace-Exception - $_ | Write-Error - } -} diff --git a/src/modules/SdnDiag.NetworkController/private/Wait-ServiceFabricClusterHealthy.ps1 b/src/modules/SdnDiag.NetworkController/private/Wait-ServiceFabricClusterHealthy.ps1 index 44cb6dbe..d83ae0ba 100644 --- a/src/modules/SdnDiag.NetworkController/private/Wait-ServiceFabricClusterHealthy.ps1 +++ b/src/modules/SdnDiag.NetworkController/private/Wait-ServiceFabricClusterHealthy.ps1 @@ -13,12 +13,10 @@ function Wait-ServiceFabricClusterHealthy { [CmdletBinding()] param ( [Parameter(Mandatory = $true)] - [PSCustomObject[]] - $NcNodeList, + [PSCustomObject[]]$NcNodeList, [Parameter(Mandatory = $true)] - [hashtable] - $CertRotateConfig, + [CertRotateConfig]$CertRotateConfig, [Parameter(Mandatory = $false)] [System.Management.Automation.PSCredential] @@ -26,8 +24,7 @@ function Wait-ServiceFabricClusterHealthy { $Credential = [System.Management.Automation.PSCredential]::Empty, [Parameter(Mandatory = $false)] - [switch] - $Restart + [switch]$Restart ) try { @@ -35,7 +32,7 @@ function Wait-ServiceFabricClusterHealthy { # Start Service Fabric Service for each NC foreach ($ncNode in $NcNodeList) { - if(Test-ComputerNameIsLocal -ComputerName $ncNode.IpAddressOrFQDN){ + if (Test-ComputerNameIsLocal -ComputerName $ncNode.IpAddressOrFQDN) { $currentNcNode = $ncNode } @@ -59,10 +56,10 @@ function Wait-ServiceFabricClusterHealthy { $maxRetry = 10 $clusterConnected = $false while ($maxRetry -gt 0) { - if(!$clusterConnected){ - try{ + if (!$clusterConnected) { + try { "Service fabric cluster connect attempt $(11 - $maxRetry)/10" | Trace-Output - if ($CertRotateConfig["ClusterCredentialType"] -ieq "X509") { + if ($CertRotateConfig.ClusterCredentialType -ieq "X509") { "Connecting to Service Fabric Cluster using cert with thumbprint: {0}" -f $certThumb | Trace-Output Connect-ServiceFabricCluster -X509Credential -FindType FindByThumbprint -FindValue $certThumb -ConnectionEndpoint "$($NodeFQDN):49006" | Out-Null } @@ -70,13 +67,14 @@ function Wait-ServiceFabricClusterHealthy { Connect-ServiceFabricCluster | Out-Null } $clusterConnected = $true - }catch{ + } + catch { $maxRetry -- continue } } - if($clusterConnected){ + if ($clusterConnected) { $services = @() $services = Get-ServiceFabricService -ApplicationName fabric:/System $allServiceHealth = $true diff --git a/src/modules/SdnDiag.NetworkController/public/Get-SdnNetworkControllerRestCertificate.ps1 b/src/modules/SdnDiag.NetworkController/public/Get-SdnNetworkControllerRestCertificate.ps1 index 6e8d2536..1c5dfe7d 100644 --- a/src/modules/SdnDiag.NetworkController/public/Get-SdnNetworkControllerRestCertificate.ps1 +++ b/src/modules/SdnDiag.NetworkController/public/Get-SdnNetworkControllerRestCertificate.ps1 @@ -4,27 +4,21 @@ function Get-SdnNetworkControllerRestCertificate { Returns the current Network Controller REST Certificate #> - try { - - $config = Get-SdnModuleConfiguration -Role 'NetworkController' - $confirmFeatures = Confirm-RequiredFeaturesInstalled -Name $config.windowsFeature - if (-NOT ($confirmFeatures)) { - "The current machine is not a NetworkController, run this on NetworkController or use -NetworkController parameter to specify one" | Trace-Output -Level:Warning - return # don't throw exception, since this is a controlled scenario and we do not need stack exception tracing - } + Confirm-IsNetworkController - $networkController = Get-SdnNetworkController + try { + $networkController = Get-SdnNetworkController -ErrorAction 'Stop' $ncRestCertThumprint = $($networkController.ServerCertificate.Thumbprint).ToString() - $certificate = Get-SdnCertificate -Path 'Cert:\LocalMachine\My' -Thumbprint $ncRestCertThumprint - - if ($null -eq $certificate) { - throw New-Object System.NullReferenceException("Unable to locate Network Controller Rest Certificate") - } - - return $certificate + $certificate = Get-SdnCertificate -Path 'Cert:\LocalMachine\My' -Thumbprint $ncRestCertThumprint -ErrorAction 'Stop' } catch { $_ | Trace-Exception $_ | Write-Error } + + if ($null -eq $certificate) { + throw New-Object System.NullReferenceException("Unable to locate Network Controller Rest Certificate") + } + + return $certificate } diff --git a/src/modules/SdnDiag.NetworkController/public/New-SdnCertificateRotationConfig.ps1 b/src/modules/SdnDiag.NetworkController/public/New-SdnCertificateRotationConfig.ps1 index 3c8ca441..62da82f1 100644 --- a/src/modules/SdnDiag.NetworkController/public/New-SdnCertificateRotationConfig.ps1 +++ b/src/modules/SdnDiag.NetworkController/public/New-SdnCertificateRotationConfig.ps1 @@ -11,8 +11,7 @@ function New-SdnCertificateRotationConfig { [CmdletBinding()] param ( [Parameter(Mandatory = $true)] - [ValidateSet('Rest','NetworkController','Server','LoadBalancerMux')] - [String]$CertificateType, + [CertType]$CertificateType, [Parameter(Mandatory = $false)] [System.Management.Automation.PSCredential] @@ -37,7 +36,7 @@ function New-SdnCertificateRotationConfig { [uri]$ncUrl = "https://$($NcInfraInfo.NcRestName)" switch ($CertificateType) { - 'LoadBalancerMux' { + 'LoadBalancerMuxNodeCert' { $servers = Get-SdnLoadBalancerMux -NcUri $ncUrl -Credential $NcRestCredential $servers | ForEach-Object { $virtualServer = Get-SdnResource -NcUri $ncUrl -ResourceRef $_.properties.virtualServer.resourceRef @@ -62,10 +61,10 @@ function New-SdnCertificateRotationConfig { } } - 'NetworkController' { + 'NetworkControllerNodeCert' { } - 'Rest' { + 'RestCertificate' { # grab the rest certificate with the latest expiration date $restCertificate = Get-SdnCertificate -Path 'Cert:\LocalMachine\My' -Subject $restSubjectName -NetworkControllerOid:$NetworkControllerOid ` | Sort-Object -Property NotAfter -Descending | Select-Object -First 1 @@ -74,17 +73,15 @@ function New-SdnCertificateRotationConfig { throw New-Object System.NullReferenceException("Failed to locate Rest certificate") } - $restCertObject = [RestCert]@{ + $CertRotateConfig.RestCertificate = [RestCertificate]@{ CertificateType = 'Rest' Thumbprint = $restCertificate.Thumbprint SubjectName = $restCertificate.Subject IsSelfSigned = (Confirm-IsCertSelfSigned -Certificate $restCertificate) } - - $certRotateConfig.RestCert = $restCertObject } - 'Server' { + 'ServerNodeCert' { $servers = Get-SdnServer -NcUri $ncUrl -Credential $NcRestCredential $servers | ForEach-Object { $connection = $_.properties.connections | Where-Object { $_.credentialType -ieq "X509Certificate" -or $_.credentialType -ieq "X509CertificateSubjectName" } diff --git a/src/modules/SdnDiag.NetworkController/public/New-SdnNetworkControllerRestCertificate.ps1 b/src/modules/SdnDiag.NetworkController/public/New-SdnNetworkControllerRestCertificate.ps1 index 7178b163..c10053f7 100644 --- a/src/modules/SdnDiag.NetworkController/public/New-SdnNetworkControllerRestCertificate.ps1 +++ b/src/modules/SdnDiag.NetworkController/public/New-SdnNetworkControllerRestCertificate.ps1 @@ -5,7 +5,7 @@ function New-SdnNetworkControllerRestCertificate { .PARAMETER NotAfter Specifies the date and time, as a DateTime object, that the certificate expires. To obtain a DateTime object, use the Get-Date cmdlet. The default value for this parameter is one year after the certificate was created. .PARAMETER CertPassword - Specifies the password for the imported PFX file in the form of a secure string. + Specifies the password for the PFX file in the form of a secure string. #> [CmdletBinding()] @@ -22,9 +22,6 @@ function New-SdnNetworkControllerRestCertificate { [Parameter(Mandatory = $false)] [System.String]$Path = "$(Get-WorkingDirectory)\NcRest_{0}" -f (Get-FormattedDateTimeUTC), - [Parameter(Mandatory = $false)] - [System.Object]$FabricDetails, - [System.Management.Automation.PSCredential] [System.Management.Automation.Credential()] $Credential = [System.Management.Automation.PSCredential]::Empty @@ -37,28 +34,9 @@ function New-SdnNetworkControllerRestCertificate { } # ensure that the module is running as local administrator - $elevated = ([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator) - if (-NOT $elevated) { - throw New-Object System.Exception("This function requires elevated permissions. Run PowerShell as an Administrator and import the module again.") - } + Confirm-IsAdmin try { - if ($FabricDetails) { - if ($FabricDetails.LoadBalancerMux -or $FabricDetails.Server) { - $installToSouthboundDevices = $true - } - else { - $installToSouthboundDevices = $false - } - } - else { - $installToSouthboundDevices = $false - - $FabricDetails = [SdnFabricInfrastructure]@{ - NetworkController = (Get-SdnNetworkControllerNode).Server - } - } - if (-NOT (Test-Path -Path $Path -PathType Container)) { "Creating directory {0}" -f $Path | Trace-Output $certPath = New-Item -Path $Path -ItemType Directory -Force @@ -71,24 +49,15 @@ function New-SdnNetworkControllerRestCertificate { $certificate = New-SdnSelfSignedCertificate -Subject $formattedSubject -NotAfter $NotAfter # after the certificate has been generated, we want to export the certificate using the $CertPassword provided by the operator - # and save the file to directory. This allows the rest of the function to pick up these files and perform the steps as normal [System.String]$pfxFilePath = "$(Join-Path -Path $certPath.FullName -ChildPath $RestName.ToLower().Replace('.','_').Replace('=','_').Trim()).pfx" "Exporting pfx certificate to {0}" -f $pfxFilePath | Trace-Output $exportedCertificate = Export-PfxCertificate -Cert $certificate -FilePath $pfxFilePath -Password $CertPassword -CryptoAlgorithmOption AES256_SHA256 $null = Import-SdnCertificate -FilePath $exportedCertificate.FullName -CertStore 'Cert:\LocalMachine\Root' -CertPassword $CertPassword - Copy-CertificateToFabric -CertFile $exportedCertificate.FullName -CertPassword $CertPassword -FabricDetails $FabricDetails ` - -NetworkControllerRestCertificate -InstallToSouthboundDevices:$installToSouthboundDevices -Credential $Credential - - return ([PSCustomObject]@{ - Certificate = $certificate - FileInfo = $exportedCertificate - }) + return $exportedCertificate } catch { $_ | Trace-Exception $_ | Write-Error } - - return $null } diff --git a/src/modules/SdnDiag.NetworkController/public/Start-SdnNetworkControllerRestCertificateRotation.ps1 b/src/modules/SdnDiag.NetworkController/public/Start-SdnNetworkControllerRestCertificateRotation.ps1 index 0afa3a92..43550cad 100644 --- a/src/modules/SdnDiag.NetworkController/public/Start-SdnNetworkControllerRestCertificateRotation.ps1 +++ b/src/modules/SdnDiag.NetworkController/public/Start-SdnNetworkControllerRestCertificateRotation.ps1 @@ -6,8 +6,8 @@ function Start-SdnNetworkControllerRestCertificateRotation { Specifies a user account that has permission to perform this action. The default is the current user. .PARAMETER NcRestCredential Specifies a user account that has permission to access the northbound NC API interface. The default is the current user. - .PARAMETER CertPath - Path directory where certificate(s) .pfx files are located for use with certificate rotation. + .PARAMETER Certificate + Specifies the certificate file path to be used for certificate rotation. .PARAMETER GenerateCertificate Switch to determine if certificate rotate function should generate self-signed certificates. .PARAMETER CertPassword @@ -22,6 +22,19 @@ function Start-SdnNetworkControllerRestCertificateRotation { [CmdletBinding(DefaultParameterSetName = 'GenerateCertificate')] param ( + [Parameter(Mandatory = $true, ParameterSetName = 'Pfx')] + [System.String]$Certificate, + + [Parameter(Mandatory = $true, ParameterSetName = 'GenerateCertificate')] + [Switch]$GenerateCertificate, + + [Parameter(Mandatory = $true, ParameterSetName = 'Pfx')] + [Parameter(Mandatory = $true, ParameterSetName = 'GenerateCertificate')] + [System.Security.SecureString]$CertPassword, + + [Parameter(Mandatory = $true, ParameterSetName = 'CertConfig')] + [CertRotateConfig]$CertRotateConfig, + [Parameter(Mandatory = $true, ParameterSetName = 'Pfx')] [Parameter(Mandatory = $true, ParameterSetName = 'GenerateCertificate')] [Parameter(Mandatory = $true, ParameterSetName = 'CertConfig')] @@ -36,22 +49,9 @@ function Start-SdnNetworkControllerRestCertificateRotation { [System.Management.Automation.Credential()] $NcRestCredential = [System.Management.Automation.PSCredential]::Empty, - [Parameter(Mandatory = $true, ParameterSetName = 'Pfx')] - [System.String]$CertPath, - - [Parameter(Mandatory = $true, ParameterSetName = 'GenerateCertificate')] - [Switch]$GenerateCertificate, - - [Parameter(Mandatory = $true, ParameterSetName = 'Pfx')] - [Parameter(Mandatory = $true, ParameterSetName = 'GenerateCertificate')] - [System.Security.SecureString]$CertPassword, - [Parameter(Mandatory = $false, ParameterSetName = 'GenerateCertificate')] [datetime]$NotAfter = (Get-Date).AddYears(3), - [Parameter(Mandatory = $true, ParameterSetName = 'CertConfig')] - [hashtable]$CertRotateConfig, - [Parameter(Mandatory = $false, ParameterSetName = 'Pfx')] [Parameter(Mandatory = $false, ParameterSetName = 'GenerateCertificate')] [Parameter(Mandatory = $false, ParameterSetName = 'CertConfig')] @@ -59,46 +59,159 @@ function Start-SdnNetworkControllerRestCertificateRotation { ) # ensure that the module is running as local administrator + # and that we are operating the cmdlet on a network controller Confirm-IsAdmin + Confirm-IsNetworkController - $config = Get-SdnModuleConfiguration -Role 'NetworkController' - $confirmFeatures = Confirm-RequiredFeaturesInstalled -Name $config.windowsFeature - if (-NOT ($confirmFeatures)) { - throw New-Object System.NotSupportedException("The current machine is not a NetworkController, run this on NetworkController.") + # if we are generating a certificate, we will want to create specify the certificate path + if ([String]::IsNullOrEmpty($CertPath)) { + [System.String]$CertPath = "$(Get-WorkingDirectory)\Cert_{0}" -f (Get-FormattedDateTimeUTC) } - # purge any existing remote sessions to prevent situation where - # we leverage a session without credentials - Remove-PSRemotingSession - try { - # grab the rest certificate with the latest expiration date - $updatedRestCertificate = Get-SdnCertificate -Path 'Cert:\LocalMachine\My' -Subject $currentRestCert.Subject -NetworkControllerOid ` - | Sort-Object -Property NotAfter -Descending | Select-Object -First 1 + # create the directory if it does not exist + if (-NOT (Test-Path -Path $CertPath -PathType Container)) { + $null = New-Item -Path $CertPath -ItemType Directory -Force + } + $CertPathDir = Get-Item -Path $CertPath -ErrorAction 'Stop' + + "Retrieving current SDN environment details" | Trace-Output + $NcInfraInfo = Get-SdnNetworkControllerInfoOffline -Credential $Credential + + # get the network controller infrastructure information + # and determine if the rest certificate is expired or if the network controller is healthy + $currentRestCert = Get-SdnNetworkControllerRestCertificate -ErrorAction 'Stop' + $restCertExpired = (Get-Date) -gt $($currentRestCert.NotAfter) + if ($restCertExpired) { + "Network Controller Rest Certificate {0} expired at {1}" -f $currentRestCert.Thumbprint, $currentRestCert.NotAfter | Trace-Output -Level:Warning + $isNetworkControllerHealthy = $false + } + else { + $isNetworkControllerHealthy = Test-NetworkControllerIsHealthy + } + + if ($isNetworkControllerHealthy) { + $sdnFabricDetails = Get-SdnInfrastructureInfo -NetworkController $env:COMPUTERNAME -Credential $Credential -NcRestCredential $NcRestCredential + } + else { + $sdnFabricDetails = [SdnFabricInfrastructure]@{ + NetworkController = $NcInfraInfo.NodeList.IpAddressOrFQDN + } + } - "Network Controller Rest Certificate {0} will be updated:`n`tCurrent: [Thumbprint:{1} NotAfter:{2}]`n`tUpdated: [Thumbprint:{3} NotAfter:{4}]" ` - -f $currentRestCert.Subject, $currentRestCert.Thumbprint, $currentRestCert.NotAfter, $CertRotateConfig["NcRestCert"], $updatedRestCertificate.NotAfter ` - | Trace-Output -Level:Warning + switch ($PSCmdlet.ParameterSetName) { + 'GenerateCertificate' { + "== STAGE: CREATE SELF SIGNED REST CERTIFICATE ==" | Trace-Output + $restCertFileParams = @{ + RestName = $NcInfraInfo.NcRestName + NotAfter = $NotAfter + Path = $CertPathDir.FullName + CertPassword = $CertPassword + Credential = $Credential + FabricDetails = $sdnFabricDetails + ErrorAction = 'Stop' + } - $null = Invoke-CertRotateCommand -Command 'Set-NetworkController' -Credential $Credential -Thumbprint $CertRotateConfig["NcRestCert"] + New-SdnNetworkControllerRestCertificate @restCertFileParams + } + 'Pfx' { + "== STAGE: PARSE PFX CERTIFICATE ==" | Trace-Output + $pfxData = Get-PfxData -FilePath $Certificate -Password $CertPassword -ErrorAction 'Stop' + if ($pfxdata.EndEntityCertificates.Subject -ieq $currentRestCert.Subject) { + "Matched {0} [Subject: {1}; Thumbprint: {2}] to NC Rest Certificate" -f ` + $Certificate, $pfxData.EndEntityCertificates.Subject, $pfxData.EndEntityCertificates.Thumbprint | Trace-Output + } + $restCertFile = Get-Item -Path $Certificate -ErrorAction 'Stop' + } + 'CertConfig' { + "== STAGE: DETERMINE CERTIFICATE CONFIG ==" | Trace-Output + $certValidated = Test-SdnCertificateRotationConfig -NcNodeList $NcInfraInfo.NodeList -CertRotateConfig $CertRotateConfig -Credential $Credential + + if ($certValidated -ne $true) { + throw New-Object System.NotSupportedException("Unable to validate certificate configuration") + } + } + } + + # we will import the certificate, even though it may already exist + # as we want to ensure we return the properties that contain self-signed information + $importRestCert = Import-SdnCertificate -FilePath $restCertFile.FullName -CertStore 'Cert:\LocalMachine\My' -CertPassword $CertPassword -ErrorAction 'Stop' + + # in this instance, we will want to copy the certificate to the fabric + # however we will not copy to southbound devices at this time as we will do that later + # to account for situations where the current certificate is expired or network controller is unhealthy + # resulting in us not being able to determine the southbound devices + + "== STAGE: COPY CERTIFICATE TO FABRIC ==" | Trace-Output + $copyCertToFabricParams = @{ + CertFile = $restCertFile.FullName + CertPassword = $CertPassword + FabricDetails = $sdnFabricDetails + NetworkControllerRestCertificate = $true + InstallToSouthboundDevices = $false + Credential = $Credential + ErrorAction = 'Stop' + } + + Copy-CertificateToFabric @copyCertToFabricParams + + # generate the certificate rotation configuration + if (!$CertRotateConfig) { + $certRotateConfigParams = @{ + CertificateType = 'Rest' + Credential = $Credential + NcRestCredential = $NcRestCredential + NetworkControllerOid = $true + ErrorAction = 'Stop' + } + + $CertRotateConfig = New-SdnCertificateRotationConfig @certRotateConfigParams + } + + "Network Controller Rest Certificate {0} will be updated:`n`tCurrent: [Thumbprint:{1}]`n`tUpdated: [Thumbprint:{2}]" ` + -f $currentRestCert.Subject, $currentRestCert.Thumbprint, $CertRotateConfig.RestCertificate.Thumbprint | Trace-Output -Level:Warning + + # if we are not forcing the rotation, we will want to prompt the user to confirm the operation + if (!$Force) { + $confirm = Confirm-UserInput + if (-NOT $confirm) { + "User has opted to abort the operation. Terminating operation" | Trace-Output -Level:Warning + return + } + } + + # in situations where the rest certificate may be already expired, or the network controller is unhealthy + # we will want to leverage the expired certificate rotation function to fix up things so we may proceed with certificate rotate + if (!$isNetworkControllerHealthy) { + Start-SdnExpiredCertificateRotation @{ + CertRotateConfig = $CertRotateConfig + Credential = $Credential + NcRestCredential = $NcRestCredential + } + } + + # Rotate NC Northbound Certificate (REST) + "== STAGE: ROTATE NC REST CERTIFICATE ==" | Trace-Output + $null = Invoke-CertRotateCommand @{ + Command = 'Set-NetworkController' + Credential = $Credential + Thumbprint = $CertRotateConfig.RestCertificate.Thumbprint + } "Waiting for 5 minutes before proceeding to the next step. Script will resume at {0}" -f (Get-Date).AddMinutes(5).ToUniversalTime().ToString() | Trace-Output Start-Sleep -Seconds 300 # Rotate Cluster Certificate - $null = Invoke-CertRotateCommand -Command 'Set-NetworkControllerCluster' -Credential $Credential -Thumbprint $CertRotateConfig["NcRestCert"] + "== STAGE: ROTATE NC CLUSTER CERTIFICATE ==" | Trace-Output + $null = Invoke-CertRotateCommand @{ + Command = 'Set-NetworkControllerCluster' + Credential = $Credential + Thumbprint = $CertRotateConfig.RestCertificate.Thumbprint + } "Waiting for 5 minutes before proceeding to the next step. Script will resume at {0}" -f (Get-Date).AddMinutes(5).ToUniversalTime().ToString() | Trace-Output Start-Sleep -Seconds 300 - # Update Credential Resource - $null = Update-NetworkControllerCredentialResource @{ - NcUri = "https://$($NcInfraInfo.NcRestName)" - Credential = $NcRestCredential - NewRestCertThumbprint = $CertRotateConfig["NcRestCert"] - ErrorAction = Stop - } - ##################################### # # Certificate Seeding (Southbound Nodes) @@ -108,7 +221,7 @@ function Start-SdnNetworkControllerRestCertificateRotation { # in situation where maybe the Network Controller was down and we could not previously determine the southbound devices (if they exist) # we will want to get updated infrastructure information to determine if we need to seed the certificate to the southbound devices # if we get an error with returning the infrastructure information, we will terminate the script - if ($selfSignedRestCertFile) { + if ($CertRotateConfig.RestCertificate.IsSelfSigned) { $sdnFabricDetails = Get-SdnInfrastructureInfo -Credential $Credential -NcRestCredential $NcRestCredential -Force -ErrorAction Stop $southBoundNodes = @() if ($null -ne $sdnFabricDetails.LoadBalancerMux) { @@ -124,16 +237,24 @@ function Start-SdnNetworkControllerRestCertificateRotation { # ensure that we have the latest version of sdnDiagnostics module on the southbound devices Install-SdnDiagnostics -ComputerName $southBoundNodes -Credential $Credential -ErrorAction Stop - "[REST CERT] Installing self-signed certificate to {0}" -f ($southBoundNodes -join ', ') | Trace-Output - [System.String]$remoteFilePath = Join-Path -Path $CertPath.FullName -ChildPath $selfSignedRestCertFile.Name - Copy-FileToRemoteComputer -ComputerName $southBoundNodes -Credential $Credential -Path $selfSignedRestCertFile.FullName -Destination $remoteFilePath - $null = Invoke-PSRemoteCommand -ComputerName $southBoundNodes -Credential $Credential -ScriptBlock { - param([Parameter(Position = 0)][String]$param1, [Parameter(Position = 1)][String]$param2) - Import-SdnCertificate -FilePath $param1 -CertStore $param2 - } -ArgumentList @($remoteFilePath, 'Cert:\LocalMachine\Root') -ErrorAction Stop + Copy-CertificateToFabric @{ + CertFile = $importRestCert.SelfSignedCertFileInfo.FullName + FabricDetails = $sdnFabricDetails + NetworkControllerRestCertificate = $true + InstallToSouthboundDevices = $true + Credential = $Credential + } } } + "== STAGE: UPDATE X509 CREDENTIALS ==" | Trace-Output + $null = Update-NetworkControllerCredentialResource @{ + NcUri = "https://$($NcInfraInfo.NcRestName)" + Credential = $NcRestCredential + NewRestCertThumbprint = $CertRotateConfig.RestCertificate.Thumbprint + ErrorAction = 'Stop' + } + ##################################### # # Restart services diff --git a/src/modules/SdnDiag.NetworkController/public/Test-SdnCertificateRotationConfig.ps1 b/src/modules/SdnDiag.NetworkController/public/Test-SdnCertificateRotationConfig.ps1 index 04afebe6..e74dab68 100644 --- a/src/modules/SdnDiag.NetworkController/public/Test-SdnCertificateRotationConfig.ps1 +++ b/src/modules/SdnDiag.NetworkController/public/Test-SdnCertificateRotationConfig.ps1 @@ -15,7 +15,7 @@ function Test-SdnCertificateRotationConfig { [PSCustomObject[]]$NcNodeList, [Parameter(Mandatory = $true)] - [hashtable]$CertRotateConfig, + [CertRotateConfig]$CertRotateConfig, [Parameter(Mandatory = $false)] [System.Management.Automation.PSCredential] @@ -23,66 +23,52 @@ function Test-SdnCertificateRotationConfig { $Credential = [System.Management.Automation.PSCredential]::Empty ) - try { + $sb1 = { + param([Parameter(Position = 0)][String]$param1, [Parameter(Position = 1)][String]$param2) + $nodeCertObj = Get-SdnCertificate -Path $param1 -Thumbprint $param2 - if ([string]::IsNullOrEmpty($CertRotateConfig["NcRestCert"])) { - Trace-Output -Message "NcRestCert not specified in CertRotateConfig" -Level:Error + # ensure that certificate is present + if ($null -eq $nodeCertObj) { return $false } - $ncRestCert = $CertRotateConfig["NcRestCert"] - foreach ($ncNode in $NcNodeList) { - if ($CertRotateConfig["ClusterCredentialType"] -ieq "X509") { - $nodeCert = $CertRotateConfig[$ncNode.NodeName.ToLower()] - if ([string]::IsNullOrEmpty($nodeCert)) { - Trace-Output -Message "The ClusterCredentialType is X509 but Node $($ncNode.NodeName) does not have certificate specified" -Level:Error - return $false - } - else { - $certValid = Invoke-PSRemoteCommand -ComputerName $ncNode.IpAddressOrFQDN -Credential $Credential -ScriptBlock { - param([Parameter(Position = 0)][String]$param1) - $nodeCertObj = Get-SdnCertificate -Path "Cert:\LocalMachine\My" -Thumbprint $param1 - if ($null -eq $nodeCertObj) { - return $false - } - else { - if ($nodeCertObj.NotAfter -le (Get-Date)) { - return $false - } - } - return $true - } -ArgumentList $nodeCert + # ensure that certificate is not expired + if ($nodeCertObj.NotAfter -le (Get-Date)) { + return $false + } - if (!$certValid) { - Trace-Output -Message "Node $($ncNode.NodeName) does not have validate Node certificate with thumbprint $nodeCert installed" -Level:Error - return $false - } - } - } + return $true + } + + # validate that the certificates are installed on the nodes + $CertRotateConfig.NodeCerts | ForEach-Object { + if (Test-ComputerNameIsLocal -ComputerName $_.IpAddressOrFQDN) { + $certValid = Invoke-Command -ScriptBlock $sb1 -ArgumentList @("Cert:\LocalMachine\My", $_.Thumbprint) + } + else { + $certValid = Invoke-PSRemoteCommand -ComputerName $_.IpAddressOrFQDN -Credential $Credential -ScriptBlock $sb1 -ArgumentList @("Cert:\LocalMachine\My", $_.Thumbprint) + } - $certValid = Invoke-PSRemoteCommand -ComputerName $ncNode.IpAddressOrFQDN -Credential $Credential -ScriptBlock { - param([Parameter(Position = 0)][String]$param1) - $ncRestCertObj = Get-SdnCertificate -Path "Cert:\LocalMachine\My" -Thumbprint $param1 - if ($null -eq $ncRestCertObj) { - return $false - } - else { - if ($ncRestCertObj.NotAfter -le (Get-Date)) { - return $false - } - } - return $true - } -ArgumentList $ncRestCert + if (!$certValid) { + throw "$($_.NodeName) does not have valid certificate with thumbprint $($_.Thumbprint) installed" + } + } + + # validate the rest certificate exists on each of the nodes + if ($CertRotateConfig.RestCertificate) { + $NcNodeList | ForEach-Object { + if (Test-ComputerNameIsLocal -ComputerName $_.IpAddressOrFQDN) { + $certValid = Invoke-Command -ScriptBlock $sb1 -ArgumentList @("Cert:\LocalMachine\My", $CertRotateConfig.RestCertificate.Thumbprint) + } + else { + $certValid = Invoke-PSRemoteCommand -ComputerName $_.IpAddressOrFQDN -Credential $Credential -ScriptBlock $sb1 -ArgumentList @("Cert:\LocalMachine\My", $CertRotateConfig.RestCertificate.Thumbprint) + } if (!$certValid) { - Trace-Output -Message "Node $($ncNode.NodeName) does not have validate NcRest certificate with thumbprint $ncRestCert installed" -Level:Error - return $false + throw "$($_.NodeName) does not have valid rest certificate with thumbprint $($CertRotateConfig.RestCertificate.Thumbprint) installed" } } - return $true - } - catch { - $_ | Trace-Exception - $_ | Write-Error } + + return $true } diff --git a/src/modules/SdnDiag.Server/public/New-SdnServerCertificate.ps1 b/src/modules/SdnDiag.Server/public/New-SdnServerCertificate.ps1 index 94f0054c..f4c53dda 100644 --- a/src/modules/SdnDiag.Server/public/New-SdnServerCertificate.ps1 +++ b/src/modules/SdnDiag.Server/public/New-SdnServerCertificate.ps1 @@ -37,10 +37,7 @@ function New-SdnServerCertificate { } # ensure that the module is running as local administrator - $elevated = ([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator) - if (-NOT $elevated) { - throw New-Object System.Exception("This function requires elevated permissions. Run PowerShell as an Administrator and import the module again.") - } + Confirm-IsAdmin try { if (-NOT (Test-Path -Path $Path -PathType Container)) { diff --git a/src/modules/SdnDiag.NetworkController/public/Start-SdnServerCertificateRotation.ps1 b/src/modules/SdnDiag.Server/public/Start-SdnServerCertificateRotation.ps1 similarity index 94% rename from src/modules/SdnDiag.NetworkController/public/Start-SdnServerCertificateRotation.ps1 rename to src/modules/SdnDiag.Server/public/Start-SdnServerCertificateRotation.ps1 index e3f26279..b5e77afd 100644 --- a/src/modules/SdnDiag.NetworkController/public/Start-SdnServerCertificateRotation.ps1 +++ b/src/modules/SdnDiag.Server/public/Start-SdnServerCertificateRotation.ps1 @@ -18,8 +18,8 @@ function Start-SdnServerCertificateRotation { [CmdletBinding(DefaultParameterSetName = 'GenerateCertificate')] param ( - [Parameter(Mandatory = $false, ParameterSetName = 'GenerateCertificate')] - [System.String]$NetworkController = $env:COMPUTERNAME, + [Parameter(Mandatory = $true, ParameterSetName = 'GenerateCertificate')] + [System.String]$NetworkController, [Parameter(Mandatory = $true, ParameterSetName = 'GenerateCertificate')] [System.Management.Automation.PSCredential] @@ -45,10 +45,7 @@ function Start-SdnServerCertificateRotation { ) # ensure that the module is running as local administrator - $elevated = ([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator) - if (-NOT $elevated) { - throw New-Object System.Exception("This function requires elevated permissions. Run PowerShell as an Administrator and import the module again.") - } + Confirm-IsAdmin $config = Get-SdnModuleConfiguration -Role 'NetworkController' $confirmFeatures = Confirm-RequiredFeaturesInstalled -Name $config.windowsFeature