Thursday, April 30, 2015

Copy SharePoint views to other libraries with PowerShell

[CmdletBinding()]

Param
(
  [Parameter(Mandatory=$true,ValueFromPipeline=$true)][string]$WebURL,
  [Parameter(Mandatory=$true)][string]$SourceList,
  [Parameter(Mandatory=$true)][string]$SourceView,
  [Parameter(Mandatory=$false)][string]$NewViewName,
  [Parameter(Mandatory=$false)][string]$TargetURL,
  [Parameter(Mandatory=$false)][string]$IgnoreLibs,
  [Parameter(Mandatory=$false)][string]$AsDefault,
  [Parameter(Mandatory=$false)][string]$OutputPath,
  [Parameter(Mandatory=$false)][string]$SmtpServer,
  [Parameter(Mandatory=$false)][string]$EmailFrom,
  [Parameter(Mandatory=$false)][string]$EmailTo
)

Function Copy-SPView
{

  Write-Host "Loading Sytem Modules "
  Get-Module -listAvailable | import-module

  if ( (Get-PSSnapin -Name Microsoft.SharePoint.PowerShell -ErrorAction SilentlyContinue) -eq $null )
  {
    Write-Host "Loading Sharepoint Module "
    [System.Reflection.Assembly]::LoadWithPartialName("Microsoft.SharePoint")
    Add-PSSnapin -Name Microsoft.SharePoint.PowerShell

    if ( (Get-PSSnapin -Name Microsoft.SharePoint.PowerShell) -eq $null )
    {
      Write-Host "Failed to load sharepoint snap-in. Could not proceed further, Aborting ..."
      Exit
    }
  }

  Start-SPAssignment -Global

  $SPWeb  = Get-SPWeb -Identity $WebURL -ErrorAction SilentlyContinue
  $SPWebT = Get-SPWeb -Identity $TargetURL -ErrorAction SilentlyContinue

  $ignoreList = "Customized Reports","Form Templates","Shared Documents","Site Assets","Site Pages","Style Library","Master Page Gallery","Picture" + $SourceList + $IgnoreLibs

  if($SPWeb -eq $null){ Write-Host "Unable to reach the provided URL, Aborting ..." ;Exit }
  if( ($SPWeb.Lists.TryGetList($SourceList) ) -eq $Null){ Write-Host "The list $SourceList is not availible, Aborting ..."; Exit }
  if($AsDefault -ne $True){$AsDefault = $False}

  $SourceLists=$SPweb.lists["$SourceList"]

  if( ($SourceLists.Views[$SourceView]) -eq $Null ){ Write-Host "The view $SourceView does not exist, Aborting ..."; Exit  }
  if($NewViewName -lt 1){ $NewViewName = $SourceView }

  # Go through each document library in the target site
  $listIds = @();
  $i = 0;

  if($SPWebT -ne $null)
  {
    $lists=$SPWebT.lists
  }
  else
  {
    $lists=$SPWeb.lists
  }

  while ($i -lt $lists.Count)
  {
    $list = $lists[$i]

    if($list.BaseType -eq "DocumentLibrary")
    {
      if ($Ignorelist -contains $list.Title)
      {
        write-host $list "is Ignored" -foregroundcolor Yellow -backgroundcolor Black
      }
      else
      {
        $view = $list.Views[$NewViewName]
        if ($view -ne $null)
        {
          Write-Host "Updating existing view" -foregroundcolor Yellow -backgroundcolor Black

          $list.views.delete($view.ID)
          $list.update()
        }

        $Viewfields = $Sourcelists.Views[$SourceView].ViewFields.ToStringCollection()
        $viewRowLimit="100"
        $viewPaged=$true
        $viewDefaultView=$AsDefault

        # Setting the Query for the View
        $viewQuery = $Sourcelists.Views[$SourceView].Query
        $viewName = $NewViewName

        # Finally – Provisioning the View

        try
        {
          $myListView = $list.Views.Add($viewName, $viewFields, $viewQuery, 100, $True, $False, "HTML", $False)
        }
        catch
        {
          Write-Host "Not all columns are availible in the target library" -foregroundcolor Yellow
        }

        # You need to Update the View for changes made to the view
        # Updating the List is not enough
        $myListView.DefaultView = $AsDefault
        $myListView.Update()
        $list.Update()

        Write-Host "$viewName added to Library $list"
      }
    }
  $i = $i + 1
  }
$SPWeb.Dispose()
}

Copy-SPView ($WebURL,$SourceList,$SourceView,$NewViewName)

Using this script it’s possible to copy views from a source library to any target library.
This includes copying views to libraries in other site collections / web applications or even other SharePoint servers!

Summary of possible variables:

– WebURL
  URL of the source library
– SourceList
  Displayname of the source library what contains the view
– SourceView
  Name of the view that needs to be copied
– NewViewName
  Name of the view in the target libraries. (if left empty the source view name will be used.)
– TargetURL
URL of the target site / site collection of web application (If left empty the libraries in the WebURL are being
   updated)
– IgnoreLibs
  Name of the libraries that need to be ignored.
(The script contains a list of SharePoint Household Libraries
  that are ignored by default including the Source Library.)
  “Customized Reports”,”Form Templates”,”Shared Documents”,”Site Assets”,”Site Pages”,
  “Style Library”,”Master Page Gallery”,”Picture”

.Example 1
# This example copies the view to all document libraries within the source URL
# PS C:\> .\Copy-SPView.ps1 -WebURL <source URL> -SourceList <Your Source Library> -SourceView <Name of View>

.Example 2
# This example copies the view to all document libraries within the target URL.
# PS C:> .Copy-SPView.ps1 -WebURL <source URL> -SourceList <Your Source Library> -SourceView <Name of the View> -TargetURL “<Your target URL>”

.Example 3
# This example shows all possible variables that are currently working.
# PS C:> .Copy-SPView.ps1 -WebURL <source URL> -SourceList <Your Source Library> -SourceView <Name of the View> -TargetURL “<Your target URL>” -NewViewName “Rogier’s View” -IgnoreLibs “Shared Documents”

Please let me know if this was helpful

PowerShell Script to get all the Active Directory groups in your SharePoint Farm

At a client recently, I was tasked to create an inventory of all the Active Directory Groups that give access to a SharePoint site! I built it mostly from scratch, so here it is as well as some explanations to help you use it:

function WriteLogs ($message) {
    $message | Out-File $logfile -append
}

$logfile = "C:\ADGroupInventory\grouplist.txt"
Write-Host "Starting Group Script inventory"
$was = Get-SPWebApplication

foreach ($wa in $was)
 {       
  $webappUrl = $wa.url
  Write-Host "Starting to look in $webappUrl"
  $spWebApp = Get-SPWebApplication $wa.url       
  foreach($site in $spWebApp.Sites)
  {
    $siteurl = $site.url
    Write-Host "Going into SiteCollection $siteurl"
    $group = $site.RootWeb.SiteUsers
    foreach ($grp in $group)
    {
     # Ensure the item is a domain group
     if($grp.IsDomainGroup -eq "True")
     {
      $groupname = $grp.name
      WriteLogs "$groupname"
     }
    }
  }   
 }

* First of all, change the $logfile variable to a folder that exists to make sure the logs work.

* Second, in the Central Administration, give yourself "Full Control" in the Web Application User Policy. This will make sure that you won't have any access denied when you go through each and every site collection in your farm.

* Afterwards, open SharePoint Management Shell as an Administrator, and run the script. Depending of the size of you farm, it shouldn't take too long, and you should see progress of every site being scanned on the screen. At the end, you will have a text file looking like this:


You will notice in the screenshot that some group names are repeated, as well as some of them are in capital and some of them are lowercase.

* So, I used NotePad++ to get all the unique group names!
First of all, go in Edit > Convert Case to > Upercase!
You will notice in the screenshot that some group names are repeated, as well as some of them are in capital and some of them are lowercase.

To get unique lines, you will need the TextFX plugin. This used to be included in older versions of Notepad++, but if you have a newer version, you can add it from the menu by going to Plugins -> Plugin Manager -> Show Plugin Manager -> Available tab -> TextFX -> Install. In some cases it may also be called TextFX Characters, but this is the same thing.

After the plugin is installed, go in TestFX Tools and check the "sort ascending" and "sort outputs only UNIQUE" lines. Afterwards, click the "Sort lines case insensitive at column". (make sure that you do Ctrl+a in the file to select all the lines before clicking).

Now, your Notepad++ will only show the unique group names in your SharePoint Farm!

Drop a comment if this helped!

Move SharePoint 2013 Search Index Location

#-----------------------------------------------------------------------------
# Name:               Move-SPEnterpriseSearchIndex.ps1 
# Description:      This script will move the SharePoint 2013 Search Index               
# Usage:               Run the function with the 3 required Parameters 
#-----------------------------------------------------------------------------

function Move-SPEnterpriseSearchIndex($SearchServiceName,$Server,$IndexLocation){
    Add-PSSnapin Microsoft.SharePoint.PowerShell -ea 0;
    #Gets the Search Service Application
    $SSA = Get-SPServiceApplication -Name $SearchServiceName;
    if (!$?){throw "Cant find a Search Service Application: `"$SearchServiceName`"";}
    #Gets the Search Service Instance on the Specified Server
    $Instance = Get-SPEnterpriseSearchServiceInstance -Identity $Server;
    if (!$?){throw "Cant find a Search Service Instance on Server: `"$Server`"";}
    #Gets the current Search Topology
    $Current = Get-SPEnterpriseSearchTopology -SearchApplication $SSA -Active;
    if (!$?){throw "There is no Active Topology, you can try removing the `"-Active`" from the line above in the script";}
    #Creates a Copy of the current Search Topology
    $Clone = New-SPEnterpriseSearchTopology -Clone -SearchApplication $SSA -SearchTopology $Current;
    #Adds a new Index Component with the new Index Location
    New-SPEnterpriseSearchIndexComponent -SearchTopology $Clone -IndexPartition 0 -SearchServiceInstance $Instance -RootDirectory $IndexLocation | Out-Null;
    if (!$?){throw "Make sure that Index Location `"$IndexLocation`" exists on Server: `"$Server`"";}
    #Sets our new Search Topology as Active
    Set-SPEnterpriseSearchTopology -Identity $Clone;
    #Removes the old Search Topology
    Remove-SPEnterpriseSearchTopology -Identity $Current -Confirm:$false;
    #Now we need to remove the extra Index Component
    #Gets the Search Topology
    $Current = Get-SPEnterpriseSearchTopology -SearchApplication $SSA -Active;
    #Creates a copy of the current Search Topology
    $Clone=New-SPEnterpriseSearchTopology -Clone -SearchApplication $SSA -SearchTopology $Current;
    #Removes the old Index Component from the Search Topology
    Get-SPEnterpriseSearchComponent -SearchTopology $Clone | ? {($_.GetType().Name -eq "IndexComponent") -and ($_.ServerName -eq $($Instance.Server.Address)) -and ($_.RootDirectory -ne $IndexLocation)} | Remove-SPEnterpriseSearchComponent -SearchTopology $Clone -Confirm:$false;
    #Sets our new Search Topology as Active
    Set-SPEnterpriseSearchTopology -Identity $Clone;
    #Removes the old Search Topology
    Remove-SPEnterpriseSearchTopology -Identity $Current -Confirm:$False;
    Write-Host "The Index has been moved to $IndexLocation on $Server"
    Write-Host "This will not remove the data from the old index location. You will have to do that manually :)"
}


Move-SPEnterpriseSearchIndex -SearchServiceName "Search Service Application" -Server "SP2013-WFE" -IndexLocation "C:\Index"

 SharePoint 2013 places the Search index in the C: by default. There are many reasons why you would want to move the index to a different places.
This script will take three parameters, the Search Service Name, the Server Name and Index Location.  There is an example on the bottom of the script.

Wednesday, April 29, 2015

Repair corrupted SharePoint 2013 Distributed Cache cluster

Before you begin, please check if your SharePoint farm SQL servers are using Dynamic memory on ESX or Hyper-V. If yes - Turn it OFF! And check if it helped.

Launch AppFabric PowerShell console and execute:
Use-CacheCluster
Get-CacheHost
Check if Cluster is 'UP'
If not then:
Start-CacheHost –ComputerName [yourServerName] –CachePort 22233
then get your SharePoint farm GUID
$spFarm=[Microsoft.SharePoint.Administration.SPfarm]::Local
$spFarm.Id
Export your current cluster configuration as backup. For reference and just in case..
Export-CacheClusterConfig c:\Backup.xml
Replace text in XML bellow:
YOUR-FARM-ID to your farm GUID,
CONTOSO\ClusterServiceAccount to your cluster service account,
SERVER1.COM to your cluster host.
Copy host section if you have multiple cluster hosts and change server name.
Change hostid like in Backup.xml and refer to other configuration details in this file.
<?xml version="1.0" encoding="utf-8"?>
<configuration>
    <configSections>
        <section name="dataCache" type="Microsoft.ApplicationServer.Caching.DataCacheSection, Microsoft.ApplicationServer.Caching.Core, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" />
    </configSections>
    <dataCache size="Medium">
        <caches partitionCount="256">
            <cache consistency="StrongConsistency" name="DistributedAccessCache_YOUR-FARM-ID"
                minSecondaries="0">
                <policy>
                    <eviction type="Lru" />
                    <expiration defaultTTL="10" isExpirable="true" />
                </policy>
            </cache>
            <cache consistency="StrongConsistency" name="DistributedActivityFeedCache_YOUR-FARM-ID"
                minSecondaries="0">
                <policy>
                    <eviction type="Lru" />
                    <expiration defaultTTL="10" isExpirable="true" />
                </policy>
            </cache>
            <cache consistency="StrongConsistency" name="DistributedActivityFeedLMTCache_YOUR-FARM-ID"
                minSecondaries="0">
                <policy>
                    <eviction type="None" />
                    <expiration defaultTTL="10" isExpirable="true" />
                </policy>
            </cache>
            <cache consistency="StrongConsistency" name="DistributedBouncerCache_YOUR-FARM-ID"
                minSecondaries="0">
                <policy>
                    <eviction type="Lru" />
                    <expiration defaultTTL="10" isExpirable="true" />
                </policy>
            </cache>
            <cache consistency="StrongConsistency" name="DistributedDefaultCache_YOUR-FARM-ID"
                minSecondaries="0">
                <policy>
                    <eviction type="Lru" />
                    <expiration defaultTTL="10" isExpirable="true" />
                </policy>
            </cache>
            <cache consistency="StrongConsistency" name="DistributedLogonTokenCache_YOUR-FARM-ID"
                minSecondaries="0">
                <policy>
                    <eviction type="Lru" />
                    <expiration defaultTTL="10" isExpirable="true" />
                </policy>
            </cache>
            <cache consistency="StrongConsistency" name="DistributedSearchCache_YOUR-FARM-ID"
                minSecondaries="0">
                <policy>
                    <eviction type="Lru" />
                    <expiration defaultTTL="10" isExpirable="true" />
                </policy>
            </cache>
            <cache consistency="StrongConsistency" name="DistributedSecurityTrimmingCache_YOUR-FARM-ID"
                minSecondaries="0">
                <policy>
                    <eviction type="Lru" />
                    <expiration defaultTTL="10" isExpirable="true" />
                </policy>
            </cache>
            <cache consistency="StrongConsistency" name="DistributedServerToAppServerAccessTokenCache_YOUR-FARM-ID"
                minSecondaries="0">
                <policy>
                    <eviction type="Lru" />
                    <expiration defaultTTL="10" isExpirable="true" />
                </policy>
            </cache>
            <cache consistency="StrongConsistency" name="DistributedViewStateCache_YOUR-FARM-ID"
                minSecondaries="0">
                <policy>
                    <eviction type="Lru" />
                    <expiration defaultTTL="10" isExpirable="true" />
                </policy>
            </cache>
        </caches>
        <hosts>
            <host replicationPort="22236" arbitrationPort="22235" clusterPort="22234"
                hostId="280822625" size="1229" leadHost="true" account="CONTOSO\ClusterServiceAccount"
                cacheHostName="AppFabricCachingService" name="SERVER1.COM"
                cachePort="22233" />
        </hosts>
        <advancedProperties>
            <partitionStoreConnectionSettings leadHostManagement="false" />
            <securityProperties>
                <authorization>
                    <allow users="WSS_ADMIN_WPG" />
                    <allow users="WSS_WPG" />
                </authorization>
            </securityProperties>
        </advancedProperties>
        <deploymentSettings>
            <deploymentMode value="RoutingClient" />
        </deploymentSettings>
    </dataCache>
</configuration>

Save it to file c:\NewConfig.xml
Then stop cluster, import edited config file to cluster configuration and start it again:
Stop-CacheCluster
Import-CacheClusterConfig C:\NewConfig.xml
Start-CacheCluster
Then after several minutes try
Get-Cache
You should get:
CacheName            [Host]                      Regions
———————–
 default
 DistributedAccessCache_
 DistributedActivityFeedCache_
 DistributedActivityFeedLMTCache_
 .....

Retreive ALL Service Accounts and Passwords via PowerShell

I wanted to share a script I came across that will hopefully help many others out there in the future. I recently inherited a SharePoint/Project Server environment that no one in the organization had the credentials for the Farm or any service accounts.
Not only did I find out no one had any credentials but I also found out they used the same credentials for multiple environments. This left me with the task of having to reset the password on all of the servers, services, AD, etc. but would also cause a larger outage due to cross environment use.
So through some research I found this cool little script to help me out. This will go to the secure store databases and retrieve the Farm account information and then use it to retrieve the others.

#------------------------------------------------------------------------------------------
# Name: Recover-SPManagedAccounts
# Description: This script will retrieve the Farm Account credentials and show the
# passwords for all of the SharePoint Managed Accounts
# Usage: Run the script on a SP Server with an account that has Local Admin Rights
#------------------------------------------------------------------------------------------

#Checks if the Current PowerShell Session is running as the Administrator

if(([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole] "Administrator") -eq $false){
Throw "This Script must be ran as Administrator"
}

#This section retrives the Farm Account UserName/Password from the Security Token Service Application Pool

$Farm_user = C:\Windows\System32\cmd.exe /q /c $env:windir\system32\inetsrv\appcmd.exe list apppool "SecurityTokenServiceApplicationPool" /text:ProcessModel.UserName;
$Farm_pass = C:\Windows\System32\cmd.exe /q /c $env:windir\system32\inetsrv\appcmd.exe list apppool "SecurityTokenServiceApplicationPool" /text:ProcessModel.Password;
$Credential = New-Object System.Management.Automation.PsCredential($Farm_user, (ConvertTo-SecureString $Farm_pass -AsPlainText -Force));

# This line contains the script which returns the account passwords 

$GetManagedAccountPasswords = "
Add-PSSnapin Microsoft.SharePoint.PowerShell -EA 0;
function Bindings(){
return [System.Reflection.BindingFlags]::CreateInstance -bor
[System.Reflection.BindingFlags]::GetField -bor
[System.Reflection.BindingFlags]::Instance -bor
[System.Reflection.BindingFlags]::NonPublic;
}
function GetFieldValue([object]`$o, [string]`$fieldName){
`$bindings = Bindings;
return `$o.GetType().GetField(`$fieldName, `$bindings).GetValue(`$o);
}
function ConvertTo-UnsecureString([System.Security.SecureString]`$string){
`$intptr = [System.IntPtr]::Zero;
`$unmanagedString = [System.Runtime.InteropServices.Marshal]::SecureStringToGlobalAllocUnicode(`$string);
`$unsecureString = [System.Runtime.InteropServices.Marshal]::PtrToStringUni(`$unmanagedString);
[System.Runtime.InteropServices.Marshal]::ZeroFreeGlobalAllocUnicode(`$unmanagedString);
return `$unsecureString;
}

Get-SPManagedAccount | select UserName, @{Name='Password'; Expression={ConvertTo-UnsecureString (GetFieldValue `$_ 'm_Password').SecureStringValue}}";

#Writes the Script to the Public Folder (C:\Users\Public), this is required as we cant run the script inline as its too long.

Set-Content -Path "$($env:public.TrimEnd("\"))\GetManagedAccountPasswords" -Value $GetManagedAccountPasswords;

#The Script which will be ran in the new PowerShell Window running as the Farm Account, it also removes the script above which we wrote to the file system

$Script = "
`$Script = Get-Content `"$($env:public.TrimEnd("\"))\GetManagedAccountPasswords`";

PowerShell.exe -Command `$Script;
Remove-Item `"$($env:public.TrimEnd("\"))\GetManagedAccountPasswords`";
Add-PSSnapin Microsoft.SharePoint.PowerShell -EA 0;"

#Runs PowerShell as the Farm Account and loads the Script above

Start-Process -FilePath powershell.exe -Credential $Credential -ArgumentList "-noexit -command $Script" -WorkingDirectory C:\

Distributed Cache (repairing it with PowerShell)

* Recently we had issues with our distributed cache system that was set up on are farm quite some time ago when I built it with SPAuto-Installer.  This could have been from rolling out cumulative updates or what have you.  There is very little documentation on the web for this.

*  In our case we had 4 servers (2 web front-ends and 2 application servers)  all with the distributed cache enabled.  Only one server was running the distributed cache.

*  The correct topology for distributed cache is for it to exist on the web front-ends.  So we made some changes to the farm. 

Clean up all 4 Servers using the following commands:

#Stopping the service on local host
Stop-SPDistributedCacheServiceInstance -Graceful

#Removing the service from SharePoint on local host.
Remove-SPDistributedCacheServiceInstance

#Cleanup left over pieces from SharePoint
$instanceName =”SPDistributedCacheService Name=AppFabricCachingService”
$serviceInstance = Get-SPServiceInstance | ? {($_.service.tostring()) -eq $instanceName -and ($_.server.name) -eq $env:computername}
$serviceInstance.delete()


Then we added the cache host back to WEB01:

#Re-add the server back to the cluster
Add-SPDistributedCacheServiceInstance

We then checked the SPDistributedCacheClientSettings and found that "MaxConnectionsToServer" was set to 16 for all containers.

$DLTC = Get-SPDistributedCacheClientSetting -ContainerType DistributedLogonTokenCache
$DLTC

We used the following script to change  "MaxConnectionsToServer" back to 1 and increase the timeout for each container.

Add-PSSnapin Microsoft.Sharepoint.Powershell

#DistributedLogonTokenCache
$DLTC = Get-SPDistributedCacheClientSetting -ContainerType DistributedLogonTokenCache
$DLTC.MaxConnectionsToServer = 1
$DLTC.requestTimeout = "3000"
$DLTC.channelOpenTimeOut = "3000"
Set-SPDistributedCacheClientSetting -ContainerType DistributedLogonTokenCache -DistributedCacheClientSettings $DLTC

#DistributedViewStateCache
$DVSC = Get-SPDistributedCacheClientSetting -ContainerType DistributedViewStateCache
$DVSC.MaxConnectionsToServer = 1
$DVSC.requestTimeout = "3000"
$DLTC.channelOpenTimeOut = "3000"
Set-SPDistributedCacheClientSetting -ContainerType DistributedViewStateCache $DVSC

#DistributedAccessCache
$DAC = Get-SPDistributedCacheClientSetting -ContainerType DistributedAccessCache
$DAC.MaxConnectionsToServer = 1
$DAC.requestTimeout = "3000"
$DAC.channelOpenTimeOut = "3000"
Set-SPDistributedCacheClientSetting -ContainerType DistributedAccessCache $DAC

#DistributedAccessCache
$DAF = Get-SPDistributedCacheClientSetting -ContainerType DistributedAccessCache
$DAF.MaxConnectionsToServer = 1
$DAF.requestTimeout = "3000"
$DAF.channelOpenTimeOut = "3000"
Set-SPDistributedCacheClientSetting -ContainerType DistributedActivityFeedCache $DAF

#DistributedActivityFeedLMTCache
$DAFC = Get-SPDistributedCacheClientSetting -ContainerType DistributedActivityFeedLMTCache
$DAFC.MaxConnectionsToServer = 1
$DAFC.requestTimeout = "3000"
$DAFC.channelOpenTimeOut = "3000"
Set-SPDistributedCacheClientSetting -ContainerType DistributedActivityFeedLMTCache $DAFC

#DistributedBouncerCache
$DBC = Get-SPDistributedCacheClientSetting -ContainerType DistributedBouncerCache
$DBC.MaxConnectionsToServer = 1
$DBC.requestTimeout = "3000"
$DBC.channelOpenTimeOut = "3000"
Set-SPDistributedCacheClientSetting -ContainerType DistributedBouncerCache $DBC

#DistributedDefaultCache
$DDC = Get-SPDistributedCacheClientSetting -ContainerType DistributedDefaultCache
$DDC.MaxConnectionsToServer = 1
$DDC.requestTimeout = "3000"
$DDC.channelOpenTimeOut = "3000"
Set-SPDistributedCacheClientSetting -ContainerType DistributedDefaultCache $DDC

#DistributedSearchCache
$DSC = Get-SPDistributedCacheClientSetting -ContainerType DistributedSearchCache
$DSC.MaxConnectionsToServer = 1
$DSC.requestTimeout = "3000"
$DSC.channelOpenTimeOut = "3000"
Set-SPDistributedCacheClientSetting -ContainerType DistributedSearchCache $DSC

#DistributedSecurityTrimmingCache
$DTC = Get-SPDistributedCacheClientSetting -ContainerType DistributedSecurityTrimmingCache
$DTC.MaxConnectionsToServer = 1
$DTC.requestTimeout = "3000"
$DTC.channelOpenTimeOut = "3000"
Set-SPDistributedCacheClientSetting -ContainerType DistributedSecurityTrimmingCache $DTC

#DistributedServerToAppServerAccessTokenCache
$DSTAC = Get-SPDistributedCacheClientSetting -ContainerType DistributedServerToAppServerAccessTokenCache
$DSTAC.MaxConnectionsToServer = 1
$DSTAC.requestTimeout = "3000"
$DSTAC.channelOpenTimeOut = "3000"
Set-SPDistributedCacheClientSetting -ContainerType DistributedServerToAppServerAccessTokenCache $DSTAC 

- We then stopped and restarted Distributed Cache from Central Admin on WEB01

- We then attempted to start "Distributed Cache" on WEB02 and received error "failed to connect to hosts in the cluster"

- Performing a TRACERT from WEB01 to WEB02, we can see a device is in the middle (10.21.1.5).

- Installed Telnet

Import-Module servermanager
Add-WindowsFeature telnet-client


- Telnet from WEB01 to WEB02 on port 22233 and the connection was established.

- We then stopped, cleaned and added WEB02 back to the cache farm

#Stopping the service on local host
Stop-SPDistributedCacheServiceInstance -Graceful

#Removing the service from SharePoint on local host.
Remove-SPDistributedCacheServiceInstance

#Cleanup left over pieces from SharePoint
$instanceName =”SPDistributedCacheService Name=AppFabricCachingService”
$serviceInstance = Get-SPServiceInstance | ? {($_.service.tostring()) -eq $instanceName -and ($_.server.name) -eq $env:computername}
$serviceInstance.delete()

Then we added the cache host back to WEB02:

#Re-add the server back to the cluster
Add-SPDistributedCacheServiceInstance

This time it started!

- Now we have WEB01 and WEB02 servicing distributed Cache

- We checked the ULS Logs with ULSViewer and found all successful events for Distributed Cache.
Status
=======
Distributed cache is now healthy and in a working state on both WFE Servers.