Last active
May 7, 2021 06:44
-
-
Save ShauneDonohue/3d4f8cc78771c2bd35e5daa0c17ba41c to your computer and use it in GitHub Desktop.
SharePoint 2016 on-Prem ZDP script
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
[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