Skip to content

Instantly share code, notes, and snippets.

@ShauneDonohue
Last active May 7, 2021 06:44
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save ShauneDonohue/3d4f8cc78771c2bd35e5daa0c17ba41c to your computer and use it in GitHub Desktop.
Save ShauneDonohue/3d4f8cc78771c2bd35e5daa0c17ba41c to your computer and use it in GitHub Desktop.
SharePoint 2016 on-Prem ZDP script
[CmdletBinding()]
Param(
[Parameter(Mandatory=$false, HelpMessage="The Farm account to connect to SharePoint"))] [string] $farmaccount = "test\test-farm",
[Parameter(Mandatory=$false, HelpMessage="Web application URL to the use for upgrading")][string] $weburl = "https://test-partner.example.com/",
[Parameter(HelpMessage="Name of the first Web Front End")][string] $wfeSvr01 = "TESTWFE01",
[Parameter(HelpMessage="Name of the Second Web Front End")][string] $wfeSvr02 = "TESTWFE02",
[Parameter(HelpMessage="Other servers in your HA farm all 01")]$otherSvrs01 = @( "TESTAPP01", "TESTSRCH01", "TESTDCS01"),
[Parameter(HelpMessage="Other servers in your HA farm all 02")]$otherSvrs02 = @( "TESTAPP02", "TESTSRCH02", "TESTDCS02"),
[parameter(HelpMessage="UNC to copy the upgrade files from")][string] $patchFolderUNC = "\\$($env:computername)\d$\installs\Patches\SharePoint\Sept12_PU",
[Parameter(HelpMessage="The folder to place the upgrade files in on remote servers")][string] $patchInstallFolder = "D:\installs\Patches\SharePoint\Sept12_PU",
[Parameter(HelpMessage="run through script connecting to servers but not actually performing actions")][bool]$test = $false
)
$creds = (Get-Credential $farmaccount )
Add-PSSnapin Microsoft.SharePoint.PowerShell -ErrorAction SilentlyContinue | Out-Null
# Number of current threads to upgrade content databases in parallel.
# This is limited to a max of logical processors on the box, but the really stress will be on the SQL server
$maxUpgradeThreads = 4
function RunPSConfig($server){
if($server -ne $env:computername) {
$session = New-PSSession -ComputerName $server -Authentication CredSSP -credential $creds
#this need to be done on both WFE
#Enter-PSSession $server -Authentication CredSSP -Credential $creds
Invoke-command -Session $session -ScriptBlock{ add-PSSnapIn Microsoft.SharePoint.PowerShell }
Write-Host "`n`nRunning PSConfig on server $server`n" -ForegroundColor Green
if(-not $test){
$results = Invoke-command -Session $session -ScriptBlock{ PSConfig.exe -cmd upgrade -inplace b2b -wait -cmd applicationcontent -install -cmd installfeatures -cmd secureresources -cmd services -install }
Remove-PSsession $session
}
}
else{
$str = "PSConfig.exe -cmd upgrade -inplace b2b -wait -cmd applicationcontent -install -cmd installfeatures -cmd secureresources -cmd services -install"
if(-not $test){
$results = Invoke-Expression $str
}
}
$results
Write-host "`n`nPSConfig completed.`n" -ForegroundColor Green
}
function EnableSideBySide([string]$server, [bool]$enable, [string]$buildversion, $weburl ){
$session = New-PSSession -ComputerName $server -Authentication CredSSP -credential $creds
try{
#this need to be done on both WFE
Invoke-command -Session $session -ScriptBlock{ add-PSSnapIn Microsoft.SharePoint.PowerShell }
$command = {
$weburl = $args[0]
$webapp = Get-SPWebApplication -Identity $weburl
}
Invoke-command -Session $session -ScriptBlock $command -ArgumentList $weburl
if(-not $test) {
Invoke-command -Session $session -ScriptBlock { $webapp.WebService.EnableSideBySide = $args[0]} -ArgumentList $enable
Invoke-command -Session $session -ScriptBlock { $webapp.WebService.Update()}
}
if($enable){
write-Host "`nSide By Side has been enabled on $server`n" -ForegroundColor Green
if(-not $test) {
Invoke-command -Session $session -ScriptBlock {Copy-SPSideBySideFiles }
}
write-Host "`nSide By Side file for build $buildversion have been copied on $server`n" -ForegroundColor Green
}
else {
Write-Host "`nSide By Side has been disabled on $server`n" -ForegroundColor Green
}
Write-Host "`nSideBySideToken set to $buildversion`n"
if(-not $test) {
Invoke-command -Session $session -ScriptBlock {$webapp.WebService.SideBySideToken = $args[0] } -ArgumentList $buildversion
Invoke-command -Session $session -ScriptBlock {$webapp.WebService.Update() }
}
}
finally{
Remove-PSsession $session
}
}
function DistributedCache($server, [bool]$enable ){
#Enter-PSSession $server -Authentication CredSSP -Credential $creds
$session = New-PSSession -ComputerName $server -Authentication CredSSP -credential $creds
Invoke-command -Session $session -ScriptBlock{ Add-PSSnapIn Microsoft.SharePoint.PowerShell }
Invoke-command -Session $session -ScriptBlock{ $instanceName ="SPDistributedCacheService Name=AppFabricCachingService" }
Invoke-command -Session $session -ScriptBlock{ $serviceInstance = Get-SPServiceInstance | ? {
($_.service.tostring()) -eq $instanceName -and ($_.server.name) -eq $env:computername }}
if($enable){
Write-Host "`n`nProvisioning Distributed Cache on $server`n" -ForegroundColor Green
if(-not $test) {
Invoke-command -Session $session -ScriptBlock{ $serviceInstance.Provision() }
}
}else{
Write-Host "`n`nUnprovisioning Distributed Cache on $server`n" -ForegroundColor Green
if(-not $test) {
Invoke-command -Session $session -ScriptBlock{ $serviceInstance.Unprovision() }
}
}
Exit-PSSession
}
function Get-ProcessorCount() {
return [int](@(Get-WmiObject -Class Win32_Processor) | Measure-Object -Property NumberOfLogicalProcessors -Sum).Sum
}
function New-ContentDatabaseUpgradeJob() {
param(
[Parameter(Mandatory=$true)][string]$ContentDatabaseName,
[Parameter(Mandatory=$true)][System.Management.Automation.Runspaces.RunspacePool]$RunspacePool
)
$ErrorActionPreference = "Stop"
try {
# create an upgrade job
$upgradeJob = [System.Management.Automation.PowerShell]::Create()
# set the runspace pool
$upgradeJob.RunspacePool = $RunspacePool
# add the script block and the parameters to execute
$powershell = $upgradeJob.AddScript( $UpgradeContentDatabaseScriptBlock )
$powershell = $upgradeJob.AddParameter( "ContentDatabaseName", $ContentDatabaseName )
return $upgradeJob
}
catch {
throw $_.Exception
}
finally {
$ErrorActionPreference = "Continue"
}
}
function New-RunspacePool() {
param([Parameter(Mandatory=$true)][int]$MaxRunspaces)
$ErrorActionPreference = "Stop"
try
{
$warning = ""
# create a shared session state that imports the SharePoint Snap-In
$defaultSessionState = [System.Management.Automation.Runspaces.InitialSessionState]::CreateDefault()
$snapInInfo = $defaultSessionState.ImportPSSnapIn( "Microsoft.SharePoint.PowerShell", [ref]$warning )
$defaultSessionState.ThreadOptions = [System.Management.Automation.Runspaces.PSThreadOptions]::UseNewThread
# create the runspace pool that will be unique for all of the upgrade jobs
$runspacePool = [System.Management.Automation.Runspaces.RunspaceFactory]::CreateRunspacePool($defaultSessionState)
$added = $runspacePool.SetMinRunspaces(1)
$added = $runspacePool.SetMaxRunspaces($MaxRunspaces)
$runspacePool.Open()
return $runspacePool
}
catch
{
throw $_.Exception
}
finally
{
$ErrorActionPreference = "Continue"
}
}
filter Write-ColorCodedTable {
param
(
[HashTable]$ColorMappings
)
$ForegroundColor = "White" # default color for table headers
if( $Host.UI.RawUI.ForegroundColor -eq -1 )
{
$ForegroundColor = $Host.UI.RawUI.ForegroundColor
}
$lines = $_ -split '\r\n'
foreach( $line in $lines )
{
$ColorMappings.GetEnumerator() | % {
if($line -match $_.Key)
{
$ForegroundColor = $_.Value
}
}
Write-Host $line -ForegroundColor $ForegroundColor
}
}
function updateContentDatabases($contentDatabaseNamesToUpgrade){
$resultColorHash = @{
# message regex = display color
"No Upgrade Required|Completed|DEBUG" = "Green"
"Error|Invalid|Failed|Unknown" = "Red"
}
$UpgradeContentDatabaseScriptBlock = {
param([string]$ContentDatabaseName)
$upgradeResultMessage = "Completed"
if($test) # USED FOR TESTING AND DEBUGGING ONLY
{
$randomInt = Get-Random -Minimum 5.0 -Maximum 60.0
Start-Sleep -Seconds $randomInt
return New-Object PSObject -Property @{
ContentDatabaseName = $ContentDatabaseName;
ExecutionTime = $randomInt;
Result = "DEBUG/TEST EXECUTION, NO UPGRADE PERFORMED";
}
}
try
{
$contentDatabase = Get-SPContentDatabase | ? { $_.Name -eq $ContentDatabaseName }
if($contentDatabase -and $contentDatabase.NeedsUpgrade) {
$upgradeErrorMessage = $upgradeWarningMessage = ""
# we need to stagger start time to prevent upgrades from starting at the same time
# because they will have a session naming conflict and cause the upgrade to fail
Start-Sleep -Seconds $( Get-Random -Minimum 5.0 -Maximum 60.0 )
$databaseUpgradeStopWatch = Measure-Command {
$contentDatabase | Upgrade-SPContentDatabase -Confirm:$false -ErrorVariable upgradeErrorMessage -WarningVariable upgradeWarningMessage
}
}
elseif($contentDatabase -and !$contentDatabase.NeedsUpgrade) {
# return the object showing the db doesn't need to be upgraded
return New-Object PSObject -Property @{
ContentDatabaseName = $ContentDatabaseName;
ExecutionTime = "0";
Result = "No Upgrade Required";
}
}
}
catch {
$upgradeResultMessage = $_.Exception.ToString()
}
if($upgradeWarningMessage) {
$upgradeResultMessage = $upgradeWarningMessage
}
if($upgradeErrorMessage) {
$upgradeResultMessage = $upgradeErrorMessage
}
return New-Object PSObject -Property @{
ContentDatabaseName = $ContentDatabaseName;
ExecutionTime = $databaseUpgradeStopWatch.TotalSeconds.ToString("N1")
Result = $upgradeResultMessage;
}
}
# limit the number of threads to a max of the number of logical processsors on the box
if($maxUpgradeThreads -gt $(Get-ProcessorCount)) {
$maxUpgradeThreads = $logicalProcessorCount
Write-Host "`n`tLimiting max threads to the number of logical processsors ($maxUpgradeThreads).`n" -ForegroundColor Yellow
}
# start the upgrades
$databaseUpgradeJobs = $lastCheckedCompletedUpgradeJobs = $databaseUpgradeResults = @()
$databaseUpgradeProgressCounter = 0
$warning = $null
# create the run space
$runspacePool = New-RunspacePool -MaxRunspaces $maxUpgradeThreads
Write-Host "`n`nCreating Content Database Upgrade Jobs`n" -ForegroundColor Green
# enumerate all the content databases to upgrade
$contentDatabaseNamesToUpgrade | % {
$databaseName = $_
try {
# Make sure the database exists
$contentDatabase = Get-SPContentDatabase -Identity $databaseName -ErrorAction SilentlyContinue
if($contentDatabase) {
Write-Host "`tQueuing upgrade job for database: $databaseName"
# don't crush the machine with starting jobs.
Start-Sleep -Seconds 5
# create the upgrade job
$contentDatabaseUpgradeJob = New-ContentDatabaseUpgradeJob -ContentDatabaseName $databaseName -RunspacePool $runspacePool
# add the job to our upgrade job list and start the job
$databaseUpgradeJobs += New-Object PSObject -Property @{
PowerShell = $contentDatabaseUpgradeJob;
Runspace = $contentDatabaseUpgradeJob.BeginInvoke();
ContentDatabase = $databaseName;
UpgradeJobResult = $null;
NeedsUpgrade = $null;
}
}
}
catch {
Write-Host "`t`tError: Error creating or starting upgrade job for database: $databaseName." -ForegroundColor Red
Write-host "`t`tDetails: $($_.Exception.Message)" -ForegroundColor Red
}
}
if( $databaseUpgradeJobs.Count -gt 0 ) {
Write-Host "`nDatabase Upgrade Progress`n" -ForegroundColor Green
# keep looping until all the installation jobs are complete
do {
try {
# pause for a few seconds between checks for completed jobs
Start-Sleep -Seconds 5
# get a list of all the completed database jobs
$completedDatabaseUpgradeJobs = @($databaseUpgradeJobs | ? { $_.Runspace.IsCompleted })
# check if any more jobs completed since last check
if($lastCheckedCompletedUpgradeJobs.Count -ne $completedDatabaseUpgradeJobs.Count) {
# print out the computer names for each job that completed since the last check
$completedDatabaseUpgradeJobs | ? { $lastCheckedCompletedUpgradeJobs -notcontains $_ } | % {
Write-Output "`t$(Get-Date) - $($_.ComputerName): Upgrade of $($_.ContentDatabase) has completed."
}
# update the list of completed jobs
$lastCheckedCompletedUpgradeJobs = $completedDatabaseUpgradeJobs
# update the progress bar
Write-Progress `
-Activity "Content Database Upgrade Progress" `
-Status "Progress: $($completedDatabaseUpgradeJobs.Count) of $($databaseUpgradeJobs.Count) Upgrades Completed" `
-PercentComplete $(($($Progress.Count)/$($databaseUpgradeJobs.Count))*100)
}
}
catch {
Write-Host "`t`tError: Error checking for job completion status" -ForegroundColor Red
Write-host "`t`tDetails: $($_.Exception.Message)" -ForegroundColor Red
}
}
while( $databaseUpgradeJobs | ? { !$_.Runspace.IsCompleted } )
try {
# collect the job results
$databaseUpgradeJobs | % {
$_.UpgradeJobResult = $_.PowerShell.EndInvoke($_.Runspace)
}
# pull the NeedsUpgrade property from the attached database
$databaseUpgradeJobs | % {
$_.NeedsUpgrade = $(Get-SPContentDatabase -Identity $_.ContentDatabase).NeedsUpgrade
}
# build an output object collection we can display and write to csv
$databaseUpgradeJobs | % {
$databaseUpgradeResults += New-Object PSObject -Property @{
"Database" = $_.ContentDatabase;
"Execution Time" = $_.UpgradeJobResult | % { $_.ExecutionTime }; # hack for PowerShell 2.0
"Upgrade Result" = $_.UpgradeJobResult | % { $_.Result }; # hack for PowerShell 2.0
"Needs Upgrade" = $_.NeedsUpgrade;
}
}
Write-Host "`nContent Database Upgrade Results`n" -ForegroundColor Green
# write out the results to the screen and .csv file
$databaseUpgradeResults | Sort Database | FT Database, "Upgrade Result", "Execution Time", "Needs Upgrade" -AutoSize | Out-String | Write-ColorCodedTable -ColorMappings $resultColorHash
$databaseUpgradeResults | Export-Csv -Path $outputFile -NoTypeInformation
if( $outputFile.IndexOfAny("\") -gt 0 ) {
Write-Output "`nUpgrade results written to $outputFile`n"
}
else {
Write-Output "`nUpgrade results written to $((Get-Item -Path ".\" -Verbose).FullName)\$outputFile`n"
}
}
catch {
Write-Host "`t`tError: Error upgrading content database: $databaseName." -ForegroundColor Red
Write-host "`t`tDetails: $($_.Exception.Message)" -ForegroundColor Red
return
}
finally {
if( $runspacePool -and $runspacePool.RunspacePoolStateInfo -and $runspacePool.RunspacePoolStateInfo.State -eq [System.Management.Automation.Runspaces.RunspaceState]::Opened) {
$runspacePool.Close()
$runspacePool.Dispose()
}
}
}
}
function CopySharepointUpdatesToServer ($foldersToCopy){
$roboCopyThreads = 50
$averageCopyTime = 0
$serverCount = 0
Write-Output "`n"
Get-SPServer | ? { $_.Role -ne "Invalid" } | Sort $_.Name | % {
$serverCount++
$serverName = $_.Name
$foldersToCopy.GetEnumerator() | % {
$sourceFilePath = $_.Name
$targetDirectory = $_.Value
# turn the local path into a UNC path
$targetDirectory = $targetDirectory.Replace(":", "$")
$targetDirectory = "\\$serverName\$targetDirectory"
# create the folder structure
New-Item -Path $targetDirectory -ItemType Directory -ErrorAction SilentlyContinue | Out-Null
# start the copy
Write-host "$serverCount`t$(Get-Date) - $($serverName): Starting RoboCopy. " -NoNewline
$copyTime = Measure-Command { Invoke-Expression "$env:windir\system32\Robocopy.exe '$sourceFilePath' '$targetDirectory' /MT:$roboCopyThreads" }
Write-host "Completed in $($copyTime.TotalSeconds.ToString('0.00')) seconds"
$averageCopyTime += $copyTime.TotalSeconds
}
}
Write-Output "`nAverage File Copy Time: $($($averageCopyTime/$serverCount).ToString('0.00')) seconds`n`n"
}
$foldersToCopy = @{
"$patchFolderUNC" = "$patchInstallFolder";
}
if(-not $test) {
CopySharepointUpdatesToServer $foldersToCopy
}
$farm = get-spfarm
$buildversion = "$($farm.buildversion.Major).$($farm.buildversion.Minor).$($farm.buildversion.Build).$($farm.buildversion.Revision+1)"
Read-host -Prompt "Remove $wfeSvr01 from NLB and press enter when done"
EnableSideBySide $wfeSvr01 $true $buildversion $weburl
Write-host "Install all patches on $wfeSvr01 and reboot server." -foregroundcolor Yellow
Write-host 'DO NOT RUN PSCONFIG ON SERVER' -foregroundcolor red -backgroundcolor yellow
Read-host -Prompt 'Press enter when done.'
Read-host -Prompt "Add $wfeSvr01 back to NLB and remove $wfeSvr02 from NLB then press enter when done"
EnableSideBySide $wfeSvr02 $true $buildversion $weburl
Write-host "Install all patches on $wfeSvr02 and reboot server." -foregroundcolor Yellow
Write-host 'DO NOT RUN PSCONFIG ON SERVER' -foregroundcolor red -backgroundcolor yellow
Read-host -Prompt 'Press enter when done. '
#SMD THIS IS ERRORING
$cacheSvrs = Get-SPServer | ? { $_.Role -eq "DistributedCache" } | Sort $_.Name | % {$_.Name}
DistributedCache $cacheSvrs[0] $false
write-host "Install all patches on $($otherSvrs01 -join ', ') and reboot servers." -foregroundcolor Yellow
Write-host 'DO NOT RUN PSCONFIG ON SERVER' -foregroundcolor red -backgroundcolor yellow
Read-host -Prompt 'Press enter when done. '
DistributedCache $cacheSvrs[0] $true
DistributedCache $cacheSvrs[1] $false
Write-host "Install all patches on $($otherSvrs02 -join ', ') and reboot servers." -foregroundcolor Yellow
Write-host 'DO NOT RUN PSCONFIG ON SERVER' -foregroundcolor red -backgroundcolor yellow
Read-host -Prompt 'Press enter when done. '
DistributedCache $cacheSvrs[1] $true
# Example: upgrade specific databases
#$contentDatabaseNamesToUpgrade = @("SP2010_CONTENT_001", "SP2010_CONTENT_002", "SP2010_CONTENT_003" )
# Example: upgrade all the content databases hosted on SQL01
#$contentDatabaseNamesToUpgrade = Get-SPContentDatabase | ? { $_.Server -eq "SQL01" } | % { $_.Name }
# Example: upgrade all the content databases on the finance web application
#$contentDatabaseNamesToUpgrade = Get-SPContentDatabase | ? { $_.WebApplication.Url -match "finance.contoso.com" } | % { $_.Name }
# Example: upgrade all the content databases attached to the SharePoint farm
$contentDatabaseNamesToUpgrade = Get-SPContentDatabase | % { $_.Name }
# log file to write the results to
$outputFile = "Upgrade-ContentDatabaseB2B.results.{0}.csv" -f [DateTime]::Now.ToString("yyyy-MM-dd_hh-mm-ss")
$startTime = Get-Date
# hack for powershell 2.0, so we always have an array of database names
if($contentDatabaseNamesToUpgrade.GetType().Name -eq "String") {
$contentDatabaseNamesToUpgrade = @($contentDatabaseNamesToUpgrade)
}
updateContentDatabases $contentDatabaseNamesToUpgrade
$TimeSpan = New-TimeSpan -Start $startTime -End (Get-Date)
Write-Output "`n`nExection Time: $($TimeSpan.Hours):$($TimeSpan.Minutes):$($TimeSpan.Seconds)`n"
RunPSConfig $wfeSvr02
Write-host "`n`nREBOOT and add $wfeSvr02 back to NLB and then remove $wfeSvr01 `n" -foregroundcolor Yellow
Read-host -Prompt "`n`nPress enter when done"
RunPSConfig $wfeSvr01
Write-host "`n`nAdd $wfeSvr01 back to NLB`n" -foregroundcolor Yellow
Read-host -Prompt "`n`nPress enter when done"
$otherSvrs01 | %{
if( $cacheSvrs -contains $_){
DistributedCache $_ $false
}
RunPSConfig $_
if( $cacheSvrs -contains $_){
DistributedCache $_ $true
}
}
Write-host "Reboot these servers $($otherSvrs01 -join ', ')" -foregroundcolor Yellow
Read-host -Prompt "`n`nPress enter when done"
$otherSvrs02 | %{
if( $cacheSvrs -contains $_){
DistributedCache $_ $false
}
RunPSConfig $_
if( $cacheSvrs -contains $_){
DistributedCache $_ $true
}
}
Write-host "Reboot these servers $($otherSvrs02 -join ', ')" -foregroundcolor Yellow
Read-host -Prompt "`n`nPress enter when done"
$farm = get-spfarm
$buildversion = "$($farm.buildversion.Major).$($farm.buildversion.Minor).$($farm.buildversion.Build).$($farm.buildversion.Revision+1)"
#$buildversion = "16.0.4588.1001"
EnableSideBySide $wfeSvr01 $False $buildversion $weburl
EnableSideBySide $wfeSvr02 $False $buildversion $weburl
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment