Scripts/DisableIPProvider.ps1 (247 lines of code) (raw):

#Requires -Version 3.0 # ------------------------------------------------------------ # Copyright (c) Microsoft Corporation. All rights reserved. # Licensed under the MIT License (MIT). See License.txt in the repo root for license information. # Feedback : sedwards@microsoft.com # ------------------------------------------------------------ <# .SYNOPSIS Update the Service Fabric Cluster to disable IPProviderEnabled settings Automating steps found in https://github.com/Azure/Service-Fabric-Troubleshooting-Guides/blob/master/Cluster/unsupported-ipprovider.md Also covers edge case for clusters running 7.0 =>7.0CU3 (7.0.455 => 7.0.469) using Windows Container feature (irrespective of whether open network feature is enabled or disabled) .DESCRIPTION Script can be used to recover an unpatched Service Fabric cluster running > 6.3.63 with open networking enabled. .PARAMETER nodeIpArray By default script will automatically grab the IP address for all the nodes .PARAMETER clusterDataRootPath Cluster data root path. default 'd:\svcfab' .PARAMETER tempPath Temporary path to store backup files. default 'd:\temp' .PARAMETER removeContainerFeature switch to optionally remove the container feature if the 7.0 edge case is detected. .PARAMETER cacheCredentials switch to optionally enable storing credentials in $global:creds variable. to clear, execute: $global:creds=$null .PARAMETER localOnly switch to optionally run script only on local node. use when there are connectivity issues between nodes by rdp'ing to each node and running with this switch. .LINK iwr https://raw.githubusercontent.com/Azure/Service-Fabric-Troubleshooting-Guides/master/Scripts/DisableIPProvider.ps1 -out $pwd/DisableIPProvider.ps1 #> Param( [ValidateNotNullOrEmpty()] [string[]] $nodeIpArray = @("0"), [string]$clusterDataRootPath = 'd:\svcfab', [ValidateNotNullOrEmpty()] [string]$tempPath = 'd:\temp', [switch]$removeContainerFeature, [switch]$cacheCredentials, [switch]$localOnly ) $ErrorActionPreference = 'continue' $supportedVersion = [version]"6.3.119.0" $currentVersion = [version]"0.0" $startTime = get-date $global:failNodes = @() $global:successNodes = @() $SFEnv = Get-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\Service Fabric" $defaultBinary = 'C:\Program Files\Microsoft Service Fabric\bin\FabricHost.exe' $creds = $null if ($SFEnv.FabricDataRoot) { $clusterDataRootPath = $SFEnv.FabricDataRoot } if ($SFEnv.FabricVersion) { $currentVersion = $SFEnv.FabricVersion } else { $currentVersion = [io.fileinfo]::new($defaultBinary).VersionInfo.FileVersion } #Verifying whether SF Runtime is at or above $supportedVersion if ($currentVersion -lt $supportedVersion ) { write-warning "This Script is supported for the Service Fabric runtime version greater than $supportedVersion. Please review external communication 'https://gist.github.com/athinanthny/f2191b93a3caea87446a73feacc66c79' for clusters running version : $($SFEnv.FabricVersion )" return } If (!(Test-Path $clusterDataRootPath)) { write-warning $clusterDataRootPath " not found, exiting." return } #Saving current list of Trusted Hosts $curValue = (get-item wsman:\localhost\Client\TrustedHosts).value $scriptBlock = { param($clusterDataRootPath, $tempPath) <# .SYNOPSIS . Updating Cluster Setting file with IPPE Parameter #> function updateSettings { param ( [ValidateNotNullOrEmpty()] [parameter (Mandatory=$true, Position=0, ParameterSetName='settingsFileInfo')] [string]$SettingsFile ) Write-Host "$env:computername : Begin updating $SettingsFile" $xmlDoc = [System.Xml.XmlDocument](Get-Content $SettingsFile); $hosting = ($xmlDoc.Settings.Section | Where-Object -Property Name -EQ "Hosting").ChildNodes $ippeSetting = $hosting | Where-Object -Property Name -EQ "IPProviderEnabled" $ippeSetting.value = "$false" $xmlDoc.Save($settingsFile) Write-Host "$env:computername : Updated $SettingsFile sucessfully" } <# .SYNOPSIS . Add's a newline to end of file to trigger update of modified package config #> function addNewLine { param ( [ValidateNotNullOrEmpty()] [parameter (Mandatory=$true, Position=0, ParameterSetName='settingsFileInfo')] [string]$fileToModify ) Write-Host "$env:computername : Begin dummy update to $fileToModify" Add-Content $fileToModify "`n" Write-Host "$env:computername : Updated $fileToModify sucessfully" } # # Begin Script # $result = Get-ChildItem -Path $clusterDataRootPath -Filter "Fabric.Data" -Directory -Recurse $hostPath = $result.Parent.Parent.Name Write-Host "---- Node Name : " $hostPath Write-Host "---------------------------------------------------------------------------------------------------------" $settingsPath = ($clusterDataRootPath) Write-Host "$env:computername : Settings path: " $settingsPath # if cluster is running 7.0 =>7.0CU3 (7.0.455 => 7.0.469) using Windows Container feature (irrespective of whether open network feature is enabled or disabled) # it should be disabled to allow the Cluster Upgrade if( ($clusterVersion.Major -eq 7) -and ($clusterVersion.Minor -eq 0) -and ($clusterVersion.Build -lt 470)) { $isContainerFeatureInstalled = Get-WindowsFeature -Name "containers" if($isContainerFeatureInstalled.Installed) { Write-Host "Cluster is running 7.0 =>7.0CU3 (7.0.455 => 7.0.469) and is using Windows Container feature. To mitigate the security issue the cluster must be upgraded. If the cluster is already in a failed state the Windows Container Feature must be removed to allow a cluster upgrade to complete successfully." -ForegroundColor Yellow if($removeContainerFeature) { Write-Host "Removing Windows Container feature" $isContainerFeatureInstalled | Remove-WindowsFeature } else { Write-Host "WARNING: to remove the Windows Container feature please rerun this script and use the -removeContainerFeature switch" -ForegroundColor Yellow } } } # Validating whether Manifest file already contain IPProviderEnabled parameter with true [object]$tempIPPE = get-content ($settingsPath + '\FabricHostSettings.xml') | select-string -pattern '<Parameter Name="IPProviderEnabled" Value="true" />' -AllMatches If ($tempIPPE) { # create a temp folder $backupFolder = $tempPath + '\' + "backup" + $(get-date -f yyyy-MM-dd-HHmmss) $tempFolder = New-Item -ItemType Directory -Force -Path $backupFolder Write-Host "$env:computername : Created the backup folder :" $tempFolder # backup current config to the temp folder, append meta from where it came and when we made the backup $settingsFile = $settingsPath + '\FabricHostSettings.xml' Copy-Item -Path $settingsFile -Destination $backupFolder -Force -Verbose # appending cluster Settings File with IPProviderEnabled with value false updateSettings ($settingsPath + "\FabricHostSettings.xml") # add newline to Fabric.Package.current.xml to trigger update of modified settings $triggerUpdateFile = $clusterDataRootPath + "\" + $hostPath + "\Fabric\Fabric.Package.current.xml" addNewLine $triggerUpdateFile } else { Write-Host "No action required, the IPProviderEnabled parameter is not in use or has already been set = 'false' on: $env:computername" } } if ($localOnly) { write-host "executing on local node only" invoke-command -ScriptBlock $scriptBlock -ArgumentList $clusterDataRootPath, $tempPath return } if (!$global:creds) { Write-Host "Enter your RDP Credentials" $creds = Get-Credential if ($cacheCredentials) { $global:creds = $creds } } function fixNodes($title, $scriptBlock, $nodeIpArray) { $count = 0 ForEach ($nodeIpAddress in $nodeIpArray) { $count++ #Verifying whether corresponding VM is up and running if (Test-Connection -ComputerName $nodeIpAddress -Quiet) { $activity = "$title : total minutes: $(((get-date) - $startTime).TotalMinutes.tostring("0.0")). connecting to: $nodeIpAddress ($count of $($nodeIpArray.Count))" $status = "success: $($global:successNodes | Sort-Object -Unique) fail: $($global:failNodes | sort -Unique)" Write-Progress -Activity $activity ` -Status $status ` -PercentComplete (($count / $nodeIpArray.Count) * 100) write-host "updating trustedhosts list" -foregroundcolor green set-item wsman:\localhost\Client\TrustedHosts -value $nodeIpAddress -Force Write-Host "---------------------------------------------------------------------------------------------------------" Write-Host "---- Node IP :" $nodeIpAddress Start-Sleep(1) $error.clear() Invoke-Command -Authentication Negotiate -ComputerName $nodeIpAddress { $temp = Set-NetFirewallRule -DisplayGroup 'File and Printer Sharing' -Enabled True -PassThru | Select-Object DisplayName, Enabled } -Credential ($creds) if ($error) { $global:failNodes += $nodeIpAddress continue } #****************************************************************************** # Script body # Execution begins here #****************************************************************************** $error.clear() Invoke-Command -Authentication Negotiate -Computername $nodeIpAddress -Scriptblock $scriptBlock -ArgumentList $clusterDataRootPath, $tempPath if ($error) { $global:failNodes += $nodeIpAddress } else { $global:successNodes += $nodeIpAddress } } else { Write-Warning "$env:computername : unable to connect to node: $nodeIpAddress" $global:failNodes += $nodeIpAddress } } Write-Progress -Completed -Activity "complete" } <# .SYNOPSIS Get the list of seed nodes #> if ($nodeIpArray[0] -eq 0 ) { Write-Host "Getting Seed node details" -foregroundcolor green $result = Get-ChildItem -Path $clusterDataRootPath -Filter "Fabric.Data" -Directory -Recurse $hostPath = $result.Parent.Parent.Name $manifestPath = $clusterDataRootPath + "\" + $hostPath + "\Fabric\ClusterManifest.current.xml" $manConfig = [System.Xml.XmlDocument](Get-Content $manifestPath) $seedNode = $manConfig.ClusterManifest.Infrastructure.PaaS.Votes.Vote $nodeIpArray = $seedNode.IPAddressOrFQDN fixNodes -title "Seed Nodes" -scriptBlock $scriptBlock -nodeIpArray $nodeIpArray } else { fixNodes -title "Custom Nodes" -scriptBlock $scriptBlock -nodeIpArray $nodeIpArray } Write-host "Waiting 120 seconds and then connecting to the cluster" -foregroundcolor green Write-host "getting the list of all nodes" -foregroundcolor green start-sleep -Seconds 120 Connect-ServiceFabricCluster $node = Get-ServiceFabricNode $nodeIpArray = $node.IpAddressOrFQDN Write-host "Fixing All Nodes" -foregroundcolor green fixNodes -title "All Nodes" -scriptBlock $scriptBlock -nodeIpArray $nodeIpArray write-host "reset trusted hosts to original values" -foregroundcolor green set-item wsman:\localhost\Client\TrustedHosts -value $curValue -Force Write-Progress -Completed -Activity "complete" if ($global:successNodes) { $successUnique = $global:successNodes | sort-object -Unique write-host "total node success: $(@($successUnique).Count)" -ForegroundColor green write-host ($successUnique | Format-List * | out-string) } if ($global:failNodes) { $failUnique = $global:failNodes | sort-object -Unique write-warning "`r`ntotal node failed: $(@($failUnique).Count). review output" write-host ($failUnique | Format-List * | out-string) write-warning "for any failed nodes, rdp to node and run this script with '-localOnly' switch" } write-host "finished. total minutes: $(((get-date) - $startTime).TotalMinutes.ToString("0.0"))" -foregroundcolor green