Azs.Deployment.Worksheet.psm1

function Initialize-Environment
{
    try
    {
        Invoke-LoadLocalizedData

        # Non-localized strings
        $script:arrIdentityStore = @('Azure Active Directory','Active Directory Federation Services')
        $script:arrAzureEnvironment = @('AzureCloud','AzureChinaCloud','AzureGermanCloud','AzureUSGovernment','CustomCloud')
        $script:arrRoutingMethod = @('BGP Routing','Static Routing')
        $script:objCiscoUCS = @('lblTORASN','txtTORASN','lblSwitchInfraSubnet','txtSwitchInfraSubnet')
        $script:strMinWinVer = '6.1.7601.65536'
        $script:strMinWMFVer = '5.1.0.0'
        $script:strMaxWMFVer = '7.9999.9999.9'
        $script:strSwitchFilePath = Join-Path $PSScriptRoot 'JSON\switch.json'
        $script:strSwitchFilterPath = Join-Path $PSScriptRoot 'JSON\filter.json'
        $script:strPartnerJsonFile = Join-Path $PSScriptRoot 'JSON\partner.json'
        $script:switchTypes = @('TOR','BMC','SPINE')
        $script:spineless = @('CiscoUCS','Mellanox')
        $script:logPath = Join-Path $env:TEMP 'Microsoft_AzureStack\DeploymentWorksheet_Log.txt'
        $script:dllIPCalculator = 'Microsoft.AzureStack.PartnerToolkit.IPCalculator.dll'
        $script:dllJsonParser = 'Newtonsoft.Json.dll'
        $script:dllIPNetwork = 'System.Net.IPNetwork.dll'
        $script:arrDeployProperties = @('BorderConnectivity','PermitNetworks','UplinkSpeed')
        $script:arrCloudProperties = @('NodeCount','ConnectToAzure','NamingPrefix','PhysicalNamingPrefix','CompanyName','RegionName','DomainFQDN','ExternalDomainFQDN','DNSForwarder','InfraAzureEnvironment','InfraAzureDirectoryTenantName','TimeServer','SyslogServerIPv4Address','ADFSForestFQDN','ADFSProviderName','ADFSMetadataUri')
        $script:strPartnerExportFile = 'PartnerData.json'
        $script:isExtStor = $false
        $script:isStaticRouting = $false
        $script:arrUplinkSpeed = Get-UplinkSpeedList
        $script:arrExtStorageSwitch = @('msftcisco','DellEMC')
        $script:ignoreSwitch = @('msftfirepower','msftjuniper','msftcisco','msftdellemc')
        $script:ignoreSwitchAlways = @('msftfirepower','msftjuniper','msftdellemc')

        # RegEx validators
        $script:validNumber = '^[0-9]+$'
        $script:validIPAddress = "^([1-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])(\.([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])){3}$"
        $script:validAlphaNumeric = '^[a-zA-Z0-9]+$'
        $script:validDomain = "(?=^.{4,253}$)(^((?!-)[a-zA-Z0-9-]{0,62}[a-zA-Z0-9]\.)+[a-zA-Z]{2,63}$)"

        $script:validNodeMin = 4
        $script:validNodeMax = 16
        $script:validCloudMin = 1
        $script:validCloudMax = 16
        $script:validSubnetExternalMax = 26
        $script:validSubnetExternalMin = 22
        $script:maxSU = 2
        $script:validASNMin = 1
        $script:validASNMax = 4294967295
        $script:maxDNSItems = 2
        $script:validPermitNetworksMax = 32
        $script:validPermitNetworksMin = 8
        $script:defaultNodeCount = 4
        $script:defaultNodeCountExtStore = 8
        $script:defaultSUCount = 1
        $script:defaultCloudCount = 1
        $script:defaultBorderCount = 2
        $script:maxBorder = 4
        $script:validBorder = 2
        $script:defaultTORCount = 2
        $script:defaultExternalSubnetSize = 24
        $script:defaultBMCSubnetSize = 26
        $script:defaultSupernetSize = 24
        $script:defaultSpineCount = 2
        $script:defaultSpineSubnetSize = 25
        $script:defaultPrivateSubnetSize = 20
        $script:defaultInfraSubnetSize = 24
        $script:defaultSwInfraSubnetSize = 26

        $script:defaultMainFormSize = @(980,640)
        $script:defaultIPCellWidth = '110'
        $script:lenIPAddress = '15'
        $script:lenText = '50'
        $script:lenDomain = '120'
        $script:lenURI = '180'
        $script:lenPrefix = '8'
        $script:txtPrefixSize = @(100,20)
        $script:lblMaxSize = @(120,40)
        $script:lblLeft = '10'
        $script:lblLeftNet2 = '500'
        $script:lblLeftP2P2 = '400'
        $script:txtLeftP2P2 = '480'
        $script:lblLeftP2P3 = '600'
        $script:txtLeftP2P3 = '680'
        $script:lblLeftP2P4 = '800'
        $script:txtLeftP2P4 = '840'
        $script:txtLeft = '130'
        [int]$custColumn2 = 460
        $script:lblLeft2 = [int]$lblLeft + $custColumn2
        $script:txtLeft2 = [int]$txtLeft + $custColumn2
        $script:txtLeftNet = '150'
        $script:txtLeftNet2 = '620'
        $script:txtSize = @(280,20)
        $script:txtSizeNet = @(200,20)
        $script:lstRoutingMethodSize = @(150,20)
        $script:txtCountSize = @(40,20)
        $script:lblLeftNetASN = '555'
        $script:txtLeftNetASN = '666'
        $script:waitSize = @(500,200)
        $script:txtSubnetLeft = '240'
        $script:txtSubnetSizeLeft = '280'
        $script:txtSubnetLeft2 = '780'
        $script:txtSubnetSizeLeft2 = '820'
        [int]$script:maxComment = 680

        # Tab tracking variables
        $script:scenario = "Customer"
        $script:savedTabs = @()
        $script:scaleUnitTabs = @{}
        $script:borderTab = @{}
        [System.Collections.ArrayList]$script:tabBadInput = @()

        if($host.Runspace.ApartmentState -ne 'STA')
        {
            [System.Threading.Thread]::CurrentThread.ApartmentState = [System.Threading.ApartmentState]::STA
        }
        $script:scriptPath = $PSScriptRoot

        Test-ScriptRequirements

        $script:defaultForeColor = [System.Drawing.Color]::Black
        $script:defaultBackColor = [System.Drawing.Color]::White
        $script:summaryForeColor = [System.Drawing.Color]::Black
        $script:summaryBackColor = [System.Drawing.Color]::MistyRose
        $script:tableForeColor = [System.Drawing.Color]::Black
        if((Get-ItemProperty 'HKCU:\Control Panel\Accessibility\HighContrast' -Name "Flags").Flags -eq 127)
        {
            $color = (Get-ItemProperty 'HKCU:\Control Panel\Colors' -Name 'WindowText').WindowText.Split(' ')
            $script:defaultForeColor = [System.Drawing.Color]::FromArgb(255,($color -join ','))
            $color = (Get-ItemProperty 'HKCU:\Control Panel\Colors' -Name 'Background').Background.Split(' ')
            $script:defaultBackColor = [System.Drawing.Color]::FromArgb(255,($color -join ','))
            if((Get-ItemProperty 'HKCU:\Control Panel\Accessibility\HighContrast' -Name 'High Contrast Scheme').'High Contrast Scheme' -match 'White')
            {
                $script:summaryBackColor = [System.Drawing.Color]::FromArgb(255,40,0,80)
                $script:summaryForeColor = [System.Drawing.Color]::White
                $script:tableForeColor = [System.Drawing.Color]::Black
            }
            else
            {
                $script:summaryBackColor = [System.Drawing.Color]::FromArgb(255,40,0,80)
                $script:summaryForeColor = [System.Drawing.Color]::White
                $script:tableForeColor = [System.Drawing.Color]::White
            }
        }
        $script:defaultFailColor = [System.Drawing.Color]::Red
        $script:defaultDupeColor = [System.Drawing.Color]::Blue
        $script:disabledBackColor = [System.Drawing.Color]::White
        $script:defaultLabelFont = [System.Drawing.Font]::New("Microsoft Sans Serif", 8.5)
        $script:defaultAutoScaleMode = [System.Windows.Forms.AutoScaleMode]::Font
        $script:defaultAutoScaleDimensions = New-Object System.Drawing.SizeF @([double]6, [double]13)
        $script:defaultAutoSizeMode = [System.Windows.Forms.AutoSizeMode]::GrowAndShrink
        $script:topMost = New-Object System.Windows.Forms.Form -Property @{Topmost=$true}

        if($locString.lblFontTypeFace -and $locString.lblFontSize)
        {
            $script:defaultLabelFont = [System.Drawing.Font]::New($locString.lblFontTypeFace, $locString.lblFontSize)
        }
    }
    catch
    {
        throw $PSItem
    }
}

function Test-ScriptRequirements
{
    [string[]]$reqError = @()
    Write-Verbose -Message "Powershell Version : $($host.Version)" -Verbose
    $clrVer = [string][environment]::Version
    Write-Verbose -Message "Powershell .NET CLR Version : $($clrVer)" -Verbose
    if(($host.Version -lt $strMinWMFVer) -or ($host.Version -ge $strMaxWMFVer))
    {
        $reqError += ($locString.errScriptHostVer -f $strMinWMFVer,$strMaxWMFVer)
    }
    if([System.Environment]::OSVersion.Version -lt $strMinWinVer)
    {
        $reqError += ($locString.errWinVer -f $strMinWinVer)
    }
    if($reqError.Count -eq 0)
    {
        try
        {
            Add-Assembly System.Windows.Forms
            Add-Assembly System.Drawing
            Add-Assembly PresentationFramework
            try
            {
                Add-Assembly (Join-Path $PSScriptRoot $dllIPCalculator)
                Add-Assembly (Join-Path $PSScriptRoot $dllJsonParser)
                Add-Assembly (Join-Path $PSScriptRoot $dllIPNetwork)
            }
            catch
            {
                if($PSItem.Exception.Message -notmatch "already loaded")
                {
                    throw $PSItem
                }
            }
        }
        catch
        {
            $errMsg = $PSItem.Exception.Message
            $reqError += ($locString.errAssemblyLoad -f $errMsg)
        }
    }
    if($reqError.Count -ne 0)
    {
        $reqError | ForEach-Object `
        {
            Write-Error $PSItem
        }
        throw $locString.errScriptRequirements
    }
}

function Invoke-LoadLocalizedData
{
    $loc = (Get-UICulture).Name
    $locStringFile = "Azs.Deployment.Worksheet.Strings.$($loc).psd1"
    $locStringPath = Join-Path $PSScriptRoot $locStringFile
    if(-not(Test-Path $locStringPath))
    {
        Write-Warning -Message "Localized strings file for '$($loc)' could not be found - defaulting to 'en-US'"
        $locStringFile = "Azs.Deployment.Worksheet.Strings.en-US.psd1"
        $locStringPath = Join-Path $PSScriptRoot $locStringFile
    }
    if(Test-Path $locStringPath)
    {
        if((Get-AuthenticodeSignature $locStringPath).Status -ne 'Valid')
        {
            $message = "'$($locStringFile)' does not have a valid digital signature."
            $bar = '-' * [int]($message).Length
            Write-Warning $bar
            Write-Warning $message
            Write-Warning $bar
            Start-Sleep -Seconds 3
        }
        Write-Verbose -Message "Import-LocalizedData from '$($locStringPath)'" -Verbose
        $script:locString = Import-LocalizedData -BaseDirectory $PSScriptRoot -Filename $locStringFile
    }
    else
    {
        throw "$($locStringFile) could not be found."
    }
}

function Keystroke_Enter
{
    [System.Windows.Forms.SendKeys]::SendWait("{TAB}")
}

function Add-Assembly
{
    param
    (
        [string]$AssemblyName
    )
    if(-not([AppDomain]::CurrentDomain.GetAssemblies().Location -like "*$AssemblyName*"))
    {
        if($AssemblyName -like "*.dll")
        {
            Add-Type -Path $AssemblyName
        }
        else
        {
            Add-Type -AssemblyName $AssemblyName
        }
    }
}

function Invoke-TrimJson
{
    param
    (
        [Parameter(Mandatory=$true, ValueFromPipeline)][string]$Json
    )
    $indent = 0
    $formatted = `
    foreach($line in $Json -split '\n')
    {
        if($line -match '[\}\]]')
        {
            $indent--
        }
        $newline = (' ' * $indent * 2) + $line.Trim().Replace(': ', ': ')
        if($line -match '[\{\[]')
        {
            $indent++
        }
        $newline
    }
    $formatted -join "`n"
}

function Get-FolderName
{
    param
    (
        [string]$InitialDirectory,
        [string]$Description
    )
    $openFolderDialog = New-Object System.Windows.Forms.FolderBrowserDialog
    $openFolderDialog.Description = $Description
    if([string]::IsNullOrEmpty($InitialDirectory))
    {
        if($host.Version -gt 5.2)
        {
            $openFolderDialog.SelectedPath = $null
        }
        else
        {
            $openFolderDialog.SelectedPath = $scriptPath
        }
    }
    else
    {
        $openFolderDialog.SelectedPath = $InitialDirectory
    }
    $openFolderDialog.ShowNewFolderButton = $true
    $openFolderDialog.RootFolder = [System.Environment+SpecialFolder]::MyComputer
    $res = $openFolderDialog.ShowDialog($frmMain)
    $openFolderDialog.Dispose()
    if($res -eq [System.Windows.Forms.DialogResult]::OK)
    {
        return $openFolderDialog.SelectedPath
    }
}

function Get-SaveFileName
{
    param
    (
        [string]$InitialDirectory
    )
    Write-Log -Message "$($MyInvocation.MyCommand.Name) - called"
    $saveFileDialog = New-Object System.Windows.Forms.SaveFileDialog
    $saveFileDialog.DefaultExt = 'json'
    $saveFileDialog.Filter = 'JSON files | *.json'
    if([string]::IsNullOrEmpty($InitialDirectory))
    {
        $saveFileDialog.InitialDirectory = $null
    }
    else
    {
        $saveFileDialog.InitialDirectory = $InitialDirectory
    }
    $saveFileDialog.FileName = "$($locString.strDefaultSaveFileName).json"
    $res = $saveFileDialog.ShowDialog($frmMain)
    $saveFileDialog.Dispose()
    if($res -eq [System.Windows.Forms.DialogResult]::OK)
    {
        return $saveFileDialog.FileName
    }
}

function Get-FileName
{
    param
    (
        [string]$InitialDirectory,
        [string]$Filter = "ConfigurationData files|ConfigurationData.json|JSON files (*.json)|*.json|All files (*.*)|*.*"
    )
    Write-Log -Message "$($MyInvocation.MyCommand.Name) - called"
    $openFileDialog = New-Object System.Windows.Forms.OpenFileDialog
    $openFileDialog.Filter = $Filter
    if([string]::IsNullOrEmpty($InitialDirectory))
    {
        $openFileDialog.InitialDirectory = $null
    }
    else
    {
        $openFileDialog.InitialDirectory = $InitialDirectory
    }
    $res = $openFileDialog.ShowDialog($frmMain)
    $openFileDialog.Dispose()
    if($res -eq [System.Windows.Forms.DialogResult]::OK)
    {
        return $openFileDialog.FileName
    }
}

function Write-Log
{
    param
    (
        [string]$Message,
        [string]$LogFile = $logPath,
        [switch]$Quiet,
        [switch]$Force
    )
    if((-not $logging) -and (-not $Force)) { return }
    $output = "$(Get-Date -Format yyyyMMdd-HHmm) : $($Message)"
    if(-not $Quiet)
    {
        Write-Verbose -Message $output -Verbose
    }
    if($logging)
    {
        if(-not(Test-Path (Split-Path $LogFile -Parent)))
        {
            $null = New-Item (Split-Path $LogFile -Parent) -ItemType Directory
        }
        $output | Add-Content $LogFile
    }
}

function Invoke-InitializeLogging
{
    Write-Verbose -Message "$($MyInvocation.MyCommand.Name)" -Verbose
    if(Test-Path $logPath)
    {
        Remove-Item $logPath | Out-Null
    }
    Write-Log -Message "Log will be written to: '$($logPath)'"
}

function Get-FileVersionInfo
{
    try
    {
        [string]$thisVersion = ((Get-Module Azs.Deployment.Worksheet) | Select-Object -Last 1).Version
        return $thisVersion
    }
    catch
    {
        throw $PSItem
    }
}

function Show-WaitForm
{
    param
    (
        [string]$Message
    )
    Close-WaitForm
    $script:waitForm = New-Object System.Windows.Forms.Form
    $script:waitLabel = New-Object System.Windows.Forms.Label
    $waitForm.StartPosition = [System.Windows.Forms.FormStartPosition]::CenterScreen
    $waitForm.TopMost = $true
    $waitForm.ShowIcon = $false
    $waitForm.Size = New-Object System.Drawing.Size($waitSize)
    $waitForm.Text = $locString.msgPleaseWait
    $waitForm.Controls.Add($waitLabel)
    $waitLabel.AutoSize = $true
    $waitForm.Visible = $true
    $waitLabel.Text = "`n$($Message)`n`n $($locString.msgPleaseWait)`n"
    $waitForm.Update()
}

function Close-WaitForm
{
    if(Get-Variable -Name waitForm -ea SilentlyContinue)
    {
        $waitForm.Close()
        $waitForm.Dispose()
        $waitForm = $null
        if(Get-Variable -Name waitLabel -ea SilentlyContinue)
        {
            $waitLabel = $null
        }
    }
}

function Update-SwitchVendor
{
    # Clear and repopulate the switch vendors list to insure all required switch types are available
    $script:currentSwitchVendor = $lstSwitchVendor.SelectedItem
    $script:currentTORModel = $lstTORModel.SelectedItem
    $script:currentBMCModel = $lstBMCModel.SelectedItem
    $script:currentSpineModel = $lstSpineModel.SelectedItem
    $lstTORModel.SelectedIndex = -1
    $lstBMCModel.SelectedIndex = -1
    $lstSpineModel.SelectedIndex = -1
    [void]$lstSwitchVendor.Items.Clear()
    $skip = @()
    if($isExtStor)
    {
        $skip += $ignoreSwitchAlways
        $script:edgeSwitch = Get-SwitchInfo -SwitchType EDGE
        $script:edgeMake = $edgeSwitch.Make
        $script:edgeModel = $edgeSwitch.Model
        $script:edgeFirmware = $edgeSwitch.Firmware
        $script:backSwitch = Get-SwitchInfo -SwitchType BACKEND
        $script:backMake = $backSwitch.Make
        $script:backModel = $backSwitch.Model
        $script:backFirmware = $backSwitch.Firmware
    }
    else
    {
        $skip += $ignoreSwitch
    }
    $requireSpine = $false
    if([int]$txtScaleUnitCount.Text -gt 1)
    {
        $requireSpine = $true
    }
    Get-SwitchVendors -IgnoreMake $skip | Foreach-Object `
    {
        $make = $PSItem
        if($isExtStor -and ($script:arrExtStorageSwitch -notcontains $make))
        {
            return
        }
        $add = $true
        foreach($type in $switchTypes)
        {
            if(-not(($type -eq 'SPINE') -and (-not $requireSpine)))
            {
                if(-not(($type -eq 'SPINE') -and ($spineless -contains $make)))
                {
                    [string[]]$result = Get-SwitchModels -Make $make -Type $type
                    if($result.Count -eq 0)
                    {
                        $add = $false
                        Write-Log -Message "WARNING: switch make '$($make)' does not contain any models for the required type '$($type)'" -Force
                    }
                }
            }
        }
        if($add)
        {
            [void]$lstSwitchVendor.Items.Add($make)
        }
    }
}

function Restore-SelectedSwitch
{
    if((-not [string]::IsNullOrEmpty($currentSwitchVendor)) -and ($lstSwitchVendor.Items -contains $currentSwitchVendor))
    {
        $lstSwitchVendor.SelectedItem = $currentSwitchVendor
        if((-not [string]::IsNullOrEmpty($currentTORModel)) -and ($lstTORModel.Items -contains $currentTORModel))
        {
            $lstTORModel.SelectedItem = $currentTORModel
            Invoke-Command -ScriptBlock $lstTORModel_Changed
        }
        else
        {
            $lstTORFirmware.SelectedIndex = -1
            $lstTORModel.SelectedIndex = -1
        }
        if((-not [string]::IsNullOrEmpty($currentBMCModel)) -and ($lstBMCModel.Items -contains $currentBMCModel))
        {
            $lstBMCModel.SelectedItem = $currentBMCModel
            Invoke-Command -ScriptBlock $lstBMCModel_Changed
        }
        else
        {
            $lstBMCFirmware.SelectedIndex = -1
            $lstBMCModel.SelectedIndex = -1
        }
        if((-not [string]::IsNullOrEmpty($currentSpineModel)) -and ($lstSpineModel.Items -contains $currentSpineModel))
        {
            $lstSpineModel.SelectedItem = $currentSpineModel
            Invoke-Command -ScriptBlock $lstSpineModel_Changed
        }
        else
        {
            $lstSpineFirmware.SelectedIndex = -1
            $lstSpineModel.SelectedIndex = -1
        }
    }
    else
    {
        $lstTORFirmware.SelectedIndex = -1
        $lstBMCFirmware.SelectedIndex = -1
        $lstSpineFirmware.SelectedIndex = -1
        $lstTORModel.SelectedIndex = -1
        $lstBMCModel.SelectedIndex = -1
        $lstSpineModel.SelectedIndex = -1
    }
}

function Get-SwitchInfo
{
    param
    (
        [string]$SwitchType
    )
    if(-not $switches)
    {
        $script:switches = Get-Content -Raw $strSwitchFilePath | ConvertFrom-Json
    }
    $portmap = $switches.portmap
    $names = @(($portmap | Get-Member -Type NoteProperty).Name)

    $foundSwitch = @()
    $thisMake = [string]::Empty
    $thisModel = [string]::Empty
    $thisFirmware = [string]::Empty

    foreach($make in $names)
    {
        if($portmap.$make.$SwitchType)
        {
            $foundSwitch += $portmap.$make
            if(-not $thisMake)
            {
                $thisMake = $make
            }
        }
    }
    if($foundSwitch.Count -eq 0)
    {
        return
    }
    elseif($foundSwitch.Count -gt 1)
    {
        Write-Log -Message "WARNING: more than one $($SwitchType) switch makes found in switch.json" -Force
    }
    $thisModel = (@($foundSwitch.$SwitchType)[0] | Get-Member -Type NoteProperty).Name
    if($thisModel)
    {
        $thisFirmware = @($foundSwitch.$SwitchType.$thisModel.FIRMWAREVERSION.version)[0]
    }
    $returnVal = @{Make=$thisMake;Model=$thisModel;Firmware=$thisFirmware}
    return $returnVal
}

function Test-EdgeSwitchInfo
{
    if([string]::IsNullOrEmpty($edgeMake))
    {
        Write-Log -Message "WARNING: no valid Edge switch makes found switch.json" -Force
    }
    elseif([string]::IsNullOrEmpty($edgeModel))
    {
        Write-Log -Message "WARNING: no valid Edge switch models found switch.json" -Force
    }
    elseif([string]::IsNullOrEmpty($edgeFirmware))
    {
        Write-Log -Message "WARNING: no valid Edge switch firmware versions found switch.json" -Force
    }
}

function Test-BackendSwitchInfo
{
    if([string]::IsNullOrEmpty($backMake))
    {
        Write-Log -Message "WARNING: no valid Backend switch makes found switch.json" -Force
    }
    elseif([string]::IsNullOrEmpty($backModel))
    {
        Write-Log -Message "WARNING: no valid Backend switch models found switch.json" -Force
    }
    elseif([string]::IsNullOrEmpty($backFirmware))
    {
        Write-Log -Message "WARNING: no valid Backend switch firmware versions found switch.json" -Force
    }
}

function Get-SwitchVendors
{
    param
    (
        [string[]]$IgnoreMake
    )
    try
    {
        if(-not $switches)
        {
            $script:switches = Get-Content -Raw $strSwitchFilePath | ConvertFrom-Json
        }
        if(Test-Path $strSwitchFilterPath)
        {
            $filter = (Get-Content $strSwitchFilterPath -Raw | ConvertFrom-Json).filter
            if($filter.Count -ne 0)
            {
                Write-Log -Message "$($MyInvocation.MyCommand.Name) - Filtering switch vendors based on entries in '$($strSwitchFilterPath)'"
            }
            else
            {
                # filter.json is empty so use all vendors in switch.json
                $filter = @(($switches.portmap | Get-Member -Type NoteProperty).Name)
            }
        }
        else
        {
            # filter.json was not found so use all vendors in switch.json
            $filter = @(($switches.portmap | Get-Member -Type NoteProperty).Name)
        }
        if($filter.Count -ne 0)
        {
            $switchInfo = @()
            $portmap = $switches.portmap
            $filter | ForEach-Object `
            {
                if($portmap.$PSItem)
                {
                    if($IgnoreMake -notcontains $PSItem)
                    {
                        if(($portmap.$PSItem.TOR) -and ((($portmap.$PSItem.TOR | Get-Member -Type NoteProperty).Name).Count -gt 0))
                        {
                            if(($PSItem -eq "CiscoUCS") -or ($PSItem -eq "Mellanox"))
                            {
                                $switchInfo += $PSItem
                            }
                            elseif(($portmap.$PSItem.BMC) -and ((($portmap.$PSItem.BMC | Get-Member -Type NoteProperty).Name).Count -gt 0))
                            {
                                $switchInfo += $PSItem
                            }
                        }
                        else
                        {
                            Write-Log -Message "$($MyInvocation.MyCommand.Name) - WARNING: switch make '$($PSItem)' does not contain required models for TOR and/or BMC" -Force
                        }
                    }
                }
                else
                {
                    Write-Log -Message "$($MyInvocation.MyCommand.Name) - WARNING: switch make '$($PSItem)' could not be found in switch.json" -Force
                }
            }
            if($switchInfo.Count -gt 0)
            {
                return $switchInfo
            }
        }
    }
    catch
    {
        throw $PSItem
    }
}

function Invoke-RefreshTotalNodeCount
{
    $thisName = 'txtNodeCount'
    $total = 0
    (1..$maxSU) | Foreach-Object `
    {
        $countTab = "tabSU$($PSItem)"
        if($scaleUnitTabs.$countTab)
        {
            $total += [int]$scaleUnitTabs.$countTab.Objects.$thisName.Text
        }
    }
    $txtTotalNodeCount.Text = $total
}

function Set-ScaleUnitsCiscoUCS
{
    param
    (
        [Parameter(Mandatory=$true)]
        [bool]$Enabled
    )
    if($isStaticRouting)
    {
        $scaleUnitTabs.tabSU1.Objects.lblTORASN.Visible = $false
        $scaleUnitTabs.tabSU1.Objects.txtTORASN.Visible = $false
        $scaleUnitTabs.tabSU1.Objects.txtTORASN.Text = [string]::Empty
    }
    else
    {
        $scaleUnitTabs.tabSU1.Objects.lblTORASN.Visible = $true
        $scaleUnitTabs.tabSU1.Objects.txtTORASN.Visible = $true
    }
    (2..$maxSU) | Foreach-Object `
    {
        $setTab = "tabSU$($PSItem)"
        if($scaleUnitTabs.$setTab)
        {
            foreach($item in $objCiscoUCS)
            {
                if($isStaticRouting -and ($item -match "TORASN"))
                {
                    $scaleUnitTabs.$setTab.Objects.$item.Visible = $false
                    if(($scaleUnitTabs.$setTab.Objects.$item.GetType().Name -ne "Label"))
                    {
                        $scaleUnitTabs.$setTab.Objects.$item.Text = [string]::Empty
                    }
                }
                else
                {
                    $scaleUnitTabs.$setTab.Objects.$item.Enabled = $Enabled
                    $scaleUnitTabs.$setTab.Objects.$item.Visible = $Enabled
                    if((-not $Enabled) -and ($scaleUnitTabs.$setTab.Objects.$item.GetType().Name -ne "Label"))
                    {
                        $scaleUnitTabs.$setTab.Objects.$item.Text = [string]::Empty
                    }
                }
            }
            $scaleUnitTabs.$setTab.Objects.lblSwitchInfraSubnetSize.Visible = $Enabled
            $scaleUnitTabs.$setTab.Objects.txtSwitchInfraSubnetSize.Visible = $Enabled
        }
    }
}

function Remove-GeneratedTabs
{
    if($tabControl.TabPages["tabSubnetSummary"])
    {
        $tabControl.TabPages.Remove($tabControl.TabPages["tabSubnetSummary"])
        $tabSubnetSummary.Dispose()
    }
    if($tabControl.TabPages["tabIPAssignments"])
    {
        $tabControl.TabPages.Remove($tabControl.TabPages["tabIPAssignments"])
        $tabIPAssignments.Dispose()
    }
}

function Get-DataGridViewSize
{
    param
    (
        [Parameter(Mandatory=$true)]
        [System.Windows.Forms.DataGridView]$Table
    )
    [int]$h = 3
    $h += $Table.ColumnHeadersHeight
    $Table.Rows | ForEach-Object `
    {
        $h += $PSitem.Height
    }
    [int]$w = 3
    $Table.Columns | ForEach-Object { $w += $PSItem.Width }

    $size = New-Object System.Drawing.Size($w,$h)

    return $size
}

function Add-ScaleUnitTab
{
    param
    (
        [Parameter(Mandatory=$true)]
        [int]$ID
    )
    try
    {
        $tabName = "tabSU$($ID)"
        if(-not($scaleUnitTabs.$tabName))
        {
            $objTab = New-Object System.Windows.Forms.TabPage
            $typTab = $objTab.GetType()
            $prpTab = $typTab.GetProperty('DoubleBuffered',('Instance','NonPublic'))
            $prpTab.SetValue($objTab, $true, $null)
            $scaleUnitTabs.Add($tabName,@{Tab=$objTab;Objects=@{}})
            $scaleUnitTabs.$tabName.Tab.Name = $tabName
            $scaleUnitTabs.$tabName.Tab.Text = "$($locString.lblScaleUnit) $($ID)"
            $tabControl.Controls.Add($scaleUnitTabs.$tabName.Tab)

            $tabRow = 20

            # ---------------------------- SCALE UNIT ID ------------------------------
            $thisName = 'lblScaleUnit'
            $thisObj = New-Object System.Windows.Forms.Label
            $scaleUnitTabs.$tabName.Objects.Add($thisName,$thisObj)
            $scaleUnitTabs.$tabName.Objects.$thisName.Location = New-Object System.Drawing.Size($lblLeft,($tabRow))
            $scaleUnitTabs.$tabName.Objects.$thisName.MaximumSize = New-Object System.Drawing.Size($lblMaxSize)
            $scaleUnitTabs.$tabName.Objects.$thisName.AutoSize = $true
            $scaleUnitTabs.$tabName.Objects.$thisName.Text = "$($locString.lblScaleUnit) : $($ID)"
            $scaleUnitTabs.$tabName.Objects.$thisName.Font = [System.Drawing.Font]::New("Microsoft Sans Serif", 12, [System.Drawing.FontStyle]::Bold)
            $scaleUnitTabs.$tabName.Tab.Controls.Add($scaleUnitTabs.$tabName.Objects.$thisName)

            $tabRow += 60

            # ------------------------------ NODE COUNT -------------------------------
            $thisName = 'lblNodeCount'
            $thisObj = New-Object System.Windows.Forms.Label
            $scaleUnitTabs.$tabName.Objects.Add($thisName,$thisObj)
            $scaleUnitTabs.$tabName.Objects.$thisName.Location = New-Object System.Drawing.Size($lblLeft,($tabRow+2))
            $scaleUnitTabs.$tabName.Objects.$thisName.MaximumSize = New-Object System.Drawing.Size($lblMaxSize)
            $scaleUnitTabs.$tabName.Objects.$thisName.AutoSize = $true
            $scaleUnitTabs.$tabName.Objects.$thisName.Text = $locString.lblNodeCount
            $scaleUnitTabs.$tabName.Objects.$thisName.Font = $defaultLabelFont
            $scaleUnitTabs.$tabName.Tab.Controls.Add($scaleUnitTabs.$tabName.Objects.$thisName)

            $thisName = 'txtNodeCount'
            $thisObj = New-Object System.Windows.Forms.TextBox
            $scaleUnitTabs.$tabName.Objects.Add($thisName,$thisObj)
            $scaleUnitTabs.$tabName.Objects.$thisName.Location = New-Object System.Drawing.Size($txtLeft,$tabRow)
            $scaleUnitTabs.$tabName.Objects.$thisName.Size = New-Object System.Drawing.Size($txtCountSize)
            $scaleUnitTabs.$tabName.Objects.$thisName.TextAlign = [System.Windows.Forms.HorizontalAlignment]::Center
            $scaleUnitTabs.$tabName.Objects.$thisName.Font = $defaultLabelFont
            $scaleUnitTabs.$tabName.Objects.$thisName.MaxLength = 2
            #$scaleUnitTabs.$tabName.Objects.$thisName.Text = $defaultNodeCount
            $scaleUnitTabs.$tabName.Objects.$thisName.Name = $thisName
            $scaleUnitTabs.$tabName.Tab.Controls.Add($scaleUnitTabs.$tabName.Objects.$thisName)
            $script:txtNodeCount_Changed = `
            {
                $thisName = 'txtNodeCount'
                $thisTab = $tabControl.SelectedTab
                $tabName = $thisTab.Name
                if(-not [string]::IsNullOrWhiteSpace($scaleUnitTabs.$tabName.Objects.$thisName.Text))
                {
                    $valid = $true
                    $scaleUnitTabs.$tabName.Objects.$thisName.Text = ($scaleUnitTabs.$tabName.Objects.$thisName.Text).Trim()
                    if($scaleUnitTabs.$tabName.Objects.$thisName.Text -notmatch '^[0-9]+$')
                    {
                        $valid = $false
                    }
                    elseif(([int]$scaleUnitTabs.$tabName.Objects.$thisName.Text -lt $validNodeMin) -or ([int]$scaleUnitTabs.$tabName.Objects.$thisName.Text -gt $validNodeMax))
                    {
                        $valid = $false
                    }
                    if($valid)
                    {
                        $scaleUnitTabs.$tabName.Objects.$thisName.Text = ($scaleUnitTabs.$tabName.Objects.$thisName.Text).TrimStart('0')
                        $scaleUnitTabs.$tabName.Objects.$thisName.ForeColor = $defaultForeColor
                        Invoke-RefreshTotalNodeCount
                    }
                    else
                    {
                        $scaleUnitTabs.$tabName.Objects.$thisName.ForeColor = $defaultFailColor
                        Close-WaitForm
                        $itemName = ($locString.lblNodeCount).Replace(':','')
                        [string]$errMessage = "'$($itemName)' " + ($locString.errNumberNotInRange -f $validNodeMin,$validNodeMax)
                        [System.Windows.Forms.MessageBox]::Show($errMessage, $locString.capValidationError, [System.Windows.Forms.MessageBoxButtons]::OK)
                        $scaleUnitTabs.$tabName.Objects.$thisName.Text = [string]::Empty
                        $scaleUnitTabs.$tabName.Objects.$thisName.ForeColor = $defaultForeColor
                    }
                    Update-TextBox $scaleUnitTabs.$tabName.Objects.$thisName
                }
            }
            $scaleUnitTabs.$tabName.Objects.$thisName.Add_Leave($txtNodeCount_Changed)

            $tabRow += 60

            # -------------------------- SCALE-UNIT SUBNETS ---------------------------
            $thisName = 'lblScaleUnitSubnets'
            $thisObj = New-Object System.Windows.Forms.Label
            $scaleUnitTabs.$tabName.Objects.Add($thisName,$thisObj)
            $scaleUnitTabs.$tabName.Objects.$thisName.Location = New-Object System.Drawing.Size($lblLeft,($tabRow))
            $scaleUnitTabs.$tabName.Objects.$thisName.AutoSize = $true
            $scaleUnitTabs.$tabName.Objects.$thisName.Text = $locString.lblScaleUnitSubnets
            $scaleUnitTabs.$tabName.Objects.$thisName.Font = [System.Drawing.Font]::New("Microsoft Sans Serif", 10, [System.Drawing.FontStyle]::Bold)
            $scaleUnitTabs.$tabName.Tab.Controls.Add($scaleUnitTabs.$tabName.Objects.$thisName)

            $tabRow += 40

            # ------------------------------ BMC SUBNET -------------------------------
            $thisName = 'lblBMCSubnet'
            $thisObj = New-Object System.Windows.Forms.Label
            $scaleUnitTabs.$tabName.Objects.Add($thisName,$thisObj)
            $scaleUnitTabs.$tabName.Objects.$thisName.Location = New-Object System.Drawing.Size($lblLeft,($tabRow+2))
            $scaleUnitTabs.$tabName.Objects.$thisName.MaximumSize = New-Object System.Drawing.Size($lblMaxSize)
            $scaleUnitTabs.$tabName.Objects.$thisName.AutoSize = $true
            $scaleUnitTabs.$tabName.Objects.$thisName.Text = $locString.lblBMCSubnet
            $scaleUnitTabs.$tabName.Objects.$thisName.Font = $defaultLabelFont
            $scaleUnitTabs.$tabName.Objects.$thisName.Name = $thisName
            $scaleUnitTabs.$tabName.Tab.Controls.Add($scaleUnitTabs.$tabName.Objects.$thisName)

            $thisName = 'txtBMCSubnet'
            $thisObj = New-Object System.Windows.Forms.TextBox
            $scaleUnitTabs.$tabName.Objects.Add($thisName,$thisObj)
            $scaleUnitTabs.$tabName.Objects.$thisName.Location = New-Object System.Drawing.Size($txtLeft,$tabRow)
            $scaleUnitTabs.$tabName.Objects.$thisName.Size = New-Object System.Drawing.Size($txtPrefixSize)
            $scaleUnitTabs.$tabName.Objects.$thisName.MaxLength = $lenIPAddress
            $scaleUnitTabs.$tabName.Objects.$thisName.Font = $defaultLabelFont
            $scaleUnitTabs.$tabName.Objects.$thisName.Name = $thisName
            $scaleUnitTabs.$tabName.Tab.Controls.Add($scaleUnitTabs.$tabName.Objects.$thisName)
            $script:txtBMCSubnet_Changed = `
            {
                $thisName = 'txtBMCSubnet'
                $thisTab = $tabControl.SelectedTab
                $tabName = $thisTab.Name
                if(-not [string]::IsNullOrEmpty($scaleUnitTabs.$tabName.Objects.$thisName.Text))
                {
                    $itemName = ($locString.lblBMCSubnet).Replace(':','')
                    $scaleUnitTabs.$tabName.Objects.$thisName.Text = ($scaleUnitTabs.$tabName.Objects.$thisName.Text).Trim()
                    if($scaleUnitTabs.$tabName.Objects.$thisName.Text -notmatch $validIPAddress)
                    {
                        $scaleUnitTabs.$tabName.Objects.$thisName.ForeColor = $defaultFailColor
                        Close-WaitForm
                        $message = "'$($itemName)' " + $locString.errIPAddressInvalid
                        [System.Windows.Forms.MessageBox]::Show($message, $locString.capValidationError, [System.Windows.Forms.MessageBoxButtons]::OK)
                        if(-not $tabBadInput.Contains($scaleUnitTabs.$tabName.Objects.$thisName))
                        {
                            $null = $tabBadInput.Add($scaleUnitTabs.$tabName.Objects.$thisName)
                        }
                    }
                    elseif(-not(Get-IsValidSubnet -IPAddress $scaleUnitTabs.$tabName.Objects.$thisName.Text -CIDR $scaleUnitTabs.$tabName.Objects.'txtBMCSubnetSize'.Text))
                    {
                        $scaleUnitTabs.$tabName.Objects.$thisName.ForeColor = $defaultFailColor
                        Close-WaitForm
                        $message = "'$($itemName)' " + ($locString.errSubnetInvalid -f $scaleUnitTabs.$tabName.Objects.$thisName.Text,$scaleUnitTabs.$tabName.Objects.'txtBMCSubnetSize'.Text)
                        [System.Windows.Forms.MessageBox]::Show($message, $locString.capValidationError, [System.Windows.Forms.MessageBoxButtons]::OK)
                        if(-not $tabBadInput.Contains($scaleUnitTabs.$tabName.Objects.$thisName))
                        {
                            $null = $tabBadInput.Add($scaleUnitTabs.$tabName.Objects.$thisName)
                        }
                    }
                    else
                    {
                        $null = $tabBadInput.Remove($scaleUnitTabs.$tabName.Objects.$thisName)
                        $scaleUnitTabs.$tabName.Objects.$thisName.ForeColor = $defaultForeColor
                    }
                    Update-TextBox $scaleUnitTabs.$tabName.Objects.$thisName
                }
                else
                {
                    $null = $tabBadInput.Remove($scaleUnitTabs.$tabName.Objects.$thisName)
                }
            }
            $scaleUnitTabs.$tabName.Objects.$thisName.Add_Leave($txtBMCSubnet_Changed)

            $thisName = 'lblBMCSubnetSize'
            $thisObj = New-Object System.Windows.Forms.Label
            $scaleUnitTabs.$tabName.Objects.Add($thisName,$thisObj)
            $scaleUnitTabs.$tabName.Objects.$thisName.Location = New-Object System.Drawing.Size($txtSubnetLeft,($tabRow+2))
            $scaleUnitTabs.$tabName.Objects.$thisName.Size = New-Object System.Drawing.Size($txtCountSize)
            $scaleUnitTabs.$tabName.Objects.$thisName.Text = $locString.lblSubnetSize
            $scaleUnitTabs.$tabName.Objects.$thisName.Font = $defaultLabelFont
            $scaleUnitTabs.$tabName.Objects.$thisName.Name = $thisName
            $scaleUnitTabs.$tabName.Tab.Controls.Add($scaleUnitTabs.$tabName.Objects.$thisName)

            $thisName = 'txtBMCSubnetSize'
            $thisObj = New-Object System.Windows.Forms.TextBox
            $scaleUnitTabs.$tabName.Objects.Add($thisName,$thisObj)
            $scaleUnitTabs.$tabName.Objects.$thisName.Location = New-Object System.Drawing.Size($txtSubnetSizeLeft,$tabRow)
            $scaleUnitTabs.$tabName.Objects.$thisName.Size = New-Object System.Drawing.Size($txtCountSize)
            $scaleUnitTabs.$tabName.Objects.$thisName.MaxLength = 2
            $scaleUnitTabs.$tabName.Objects.$thisName.Text = $defaultBMCSubnetSize
            $scaleUnitTabs.$tabName.Objects.$thisName.Font = $defaultLabelFont
            $scaleUnitTabs.$tabName.Objects.$thisName.Name = $thisName
            $scaleUnitTabs.$tabName.Objects.$thisName.AccessibleName = $thisName
            $scaleUnitTabs.$tabName.Objects.$thisName.AccessibleDescription = $locString.accBMCSubnetSize
            $scaleUnitTabs.$tabName.Objects.$thisName.Enabled = $false
            $scaleUnitTabs.$tabName.Objects.$thisName.BackColor = $defaultBackColor
            $scaleUnitTabs.$tabName.Objects.$thisName.ForeColor = $defaultForeColor
            $scaleUnitTabs.$tabName.Objects.$thisName.ReadOnly = $true
            $scaleUnitTabs.$tabName.Tab.Controls.Add($scaleUnitTabs.$tabName.Objects.$thisName)

            $tabRow += 40

            # ----------------------------- INFRA SUBNET ------------------------------
            $thisName = 'lblInfraSubnet'
            $thisObj = New-Object System.Windows.Forms.Label
            $scaleUnitTabs.$tabName.Objects.Add($thisName,$thisObj)
            $scaleUnitTabs.$tabName.Objects.$thisName.Location = New-Object System.Drawing.Size($lblLeft,($tabRow+2))
            $scaleUnitTabs.$tabName.Objects.$thisName.MaximumSize = New-Object System.Drawing.Size($lblMaxSize)
            $scaleUnitTabs.$tabName.Objects.$thisName.AutoSize = $true
            $scaleUnitTabs.$tabName.Objects.$thisName.Text = $locString.lblInfraSubnet
            $scaleUnitTabs.$tabName.Objects.$thisName.Font = $defaultLabelFont
            $scaleUnitTabs.$tabName.Objects.$thisName.Name = $thisName
            $scaleUnitTabs.$tabName.Tab.Controls.Add($scaleUnitTabs.$tabName.Objects.$thisName)

            $thisName = 'txtInfraSubnet'
            $thisObj = New-Object System.Windows.Forms.TextBox
            $scaleUnitTabs.$tabName.Objects.Add($thisName,$thisObj)
            $scaleUnitTabs.$tabName.Objects.$thisName.Location = New-Object System.Drawing.Size($txtLeft,$tabRow)
            $scaleUnitTabs.$tabName.Objects.$thisName.Size = New-Object System.Drawing.Size($txtPrefixSize)
            $scaleUnitTabs.$tabName.Objects.$thisName.MaxLength = $lenIPAddress
            $scaleUnitTabs.$tabName.Objects.$thisName.Font = $defaultLabelFont
            $scaleUnitTabs.$tabName.Objects.$thisName.Name = $thisName
            $scaleUnitTabs.$tabName.Tab.Controls.Add($scaleUnitTabs.$tabName.Objects.$thisName)
            $script:txtInfraSubnet_Changed = `
            {
                $thisName = 'txtInfraSubnet'
                $thisTab = $tabControl.SelectedTab
                $tabName = $thisTab.Name
                if(-not([string]::IsNullOrEmpty($scaleUnitTabs.$tabName.Objects.$thisName.Text)))
                {
                    $itemName = ($locString.lblInfraSubnet).Replace(':','')
                    $scaleUnitTabs.$tabName.Objects.$thisName.Text = ($scaleUnitTabs.$tabName.Objects.$thisName.Text).Trim()
                    if($scaleUnitTabs.$tabName.Objects.$thisName.Text -notmatch $validIPAddress)
                    {
                        $scaleUnitTabs.$tabName.Objects.$thisName.ForeColor = $defaultFailColor
                        Close-WaitForm
                        $message = "'$($itemName)' " + $locString.errIPAddressInvalid
                        [System.Windows.Forms.MessageBox]::Show($message, $locString.capValidationError, [System.Windows.Forms.MessageBoxButtons]::OK)
                        if(-not $tabBadInput.Contains($scaleUnitTabs.$tabName.Objects.$thisName))
                        {
                            $null = $tabBadInput.Add($scaleUnitTabs.$tabName.Objects.$thisName)
                        }
                    }
                    elseif(-not(Get-IsValidSubnet -IPAddress $scaleUnitTabs.$tabName.Objects.$thisName.Text -CIDR $scaleUnitTabs.$tabName.Objects.'txtInfraSubnetSize'.Text))
                    {
                        $scaleUnitTabs.$tabName.Objects.$thisName.ForeColor = $defaultFailColor
                        Close-WaitForm
                        $message = "'$($itemName)' " + ($locString.errSubnetInvalid -f $scaleUnitTabs.$tabName.Objects.$thisName.Text,$scaleUnitTabs.$tabName.Objects.'txtInfraSubnetSize'.Text)
                        [System.Windows.Forms.MessageBox]::Show($message, $locString.capValidationError, [System.Windows.Forms.MessageBoxButtons]::OK)
                        if(-not $tabBadInput.Contains($scaleUnitTabs.$tabName.Objects.$thisName))
                        {
                            $null = $tabBadInput.Add($scaleUnitTabs.$tabName.Objects.$thisName)
                        }
                    }
                    else
                    {
                        $scaleUnitTabs.$tabName.Objects.$thisName.ForeColor = $defaultForeColor
                        $null = $tabBadInput.Remove($scaleUnitTabs.$tabName.Objects.$thisName)
                    }
                    Update-TextBox $scaleUnitTabs.$tabName.Objects.$thisName
                }
                else
                {
                    $null = $tabBadInput.Remove($scaleUnitTabs.$tabName.Objects.$thisName)
                }
            }
            $scaleUnitTabs.$tabName.Objects.$thisName.Add_Leave($txtInfraSubnet_Changed)

            $thisName = 'lblInfraSubnetSize'
            $thisObj = New-Object System.Windows.Forms.Label
            $scaleUnitTabs.$tabName.Objects.Add($thisName,$thisObj)
            $scaleUnitTabs.$tabName.Objects.$thisName.Location = New-Object System.Drawing.Size($txtSubnetLeft,($tabRow+2))
            $scaleUnitTabs.$tabName.Objects.$thisName.Size = New-Object System.Drawing.Size($txtCountSize)
            $scaleUnitTabs.$tabName.Objects.$thisName.Text = $locString.lblSubnetSize
            $scaleUnitTabs.$tabName.Objects.$thisName.Font = $defaultLabelFont
            $scaleUnitTabs.$tabName.Objects.$thisName.Name = $thisName
            $scaleUnitTabs.$tabName.Tab.Controls.Add($scaleUnitTabs.$tabName.Objects.$thisName)

            $thisName = 'txtInfraSubnetSize'
            $thisObj = New-Object System.Windows.Forms.TextBox
            $scaleUnitTabs.$tabName.Objects.Add($thisName,$thisObj)
            $scaleUnitTabs.$tabName.Objects.$thisName.Location = New-Object System.Drawing.Size($txtSubnetSizeLeft,$tabRow)
            $scaleUnitTabs.$tabName.Objects.$thisName.Size = New-Object System.Drawing.Size($txtCountSize)
            $scaleUnitTabs.$tabName.Objects.$thisName.MaxLength = 2
            $scaleUnitTabs.$tabName.Objects.$thisName.Text = $defaultInfraSubnetSize
            $scaleUnitTabs.$tabName.Objects.$thisName.Font = $defaultLabelFont
            $scaleUnitTabs.$tabName.Objects.$thisName.Name = $thisName
            $scaleUnitTabs.$tabName.Objects.$thisName.AccessibleName = $thisName
            $scaleUnitTabs.$tabName.Objects.$thisName.AccessibleDescription = $locString.accInfraSubnetSize
            $scaleUnitTabs.$tabName.Objects.$thisName.Enabled = $false
            $scaleUnitTabs.$tabName.Objects.$thisName.BackColor = $defaultBackColor
            $scaleUnitTabs.$tabName.Objects.$thisName.ForeColor = $defaultForeColor
            $scaleUnitTabs.$tabName.Objects.$thisName.ReadOnly = $true
            $scaleUnitTabs.$tabName.Tab.Controls.Add($scaleUnitTabs.$tabName.Objects.$thisName)

            $tabRow += 40

            # -------------------------- SWITCH INFRA SUBNET --------------------------
            $thisName = 'lblSwitchInfraSubnet'
            $thisObj = New-Object System.Windows.Forms.Label
            $scaleUnitTabs.$tabName.Objects.Add($thisName,$thisObj)
            $scaleUnitTabs.$tabName.Objects.$thisName.Location = New-Object System.Drawing.Size($lblLeft,($tabRow+2))
            $scaleUnitTabs.$tabName.Objects.$thisName.MaximumSize = New-Object System.Drawing.Size($lblMaxSize)
            $scaleUnitTabs.$tabName.Objects.$thisName.AutoSize = $true
            $scaleUnitTabs.$tabName.Objects.$thisName.Text = $locString.lblSwitchInfraSubnet
            $scaleUnitTabs.$tabName.Objects.$thisName.Font = $defaultLabelFont
            $scaleUnitTabs.$tabName.Objects.$thisName.Name = $thisName
            $scaleUnitTabs.$tabName.Tab.Controls.Add($scaleUnitTabs.$tabName.Objects.$thisName)

            $thisName = 'txtSwitchInfraSubnet'
            $thisObj = New-Object System.Windows.Forms.TextBox
            $scaleUnitTabs.$tabName.Objects.Add($thisName,$thisObj)
            $scaleUnitTabs.$tabName.Objects.$thisName.Location = New-Object System.Drawing.Size($txtLeft,$tabRow)
            $scaleUnitTabs.$tabName.Objects.$thisName.Size = New-Object System.Drawing.Size($txtPrefixSize)
            $scaleUnitTabs.$tabName.Objects.$thisName.MaxLength = $lenIPAddress
            $scaleUnitTabs.$tabName.Objects.$thisName.Font = $defaultLabelFont
            $scaleUnitTabs.$tabName.Objects.$thisName.Name = $thisName
            $scaleUnitTabs.$tabName.Tab.Controls.Add($scaleUnitTabs.$tabName.Objects.$thisName)
            $script:txtSwitchInfraSubnet_Changed = `
            {
                $thisName = 'txtSwitchInfraSubnet'
                $thisTab = $tabControl.SelectedTab
                $tabName = $thisTab.Name
                if(-not([string]::IsNullOrEmpty($scaleUnitTabs.$tabName.Objects.$thisName.Text)))
                {
                    $itemName = ($locString.lblSwitchInfraSubnet).Replace(':','')
                    $scaleUnitTabs.$tabName.Objects.$thisName.Text = ($scaleUnitTabs.$tabName.Objects.$thisName.Text).Trim()
                    if($scaleUnitTabs.$tabName.Objects.$thisName.Text -notmatch $validIPAddress)
                    {
                        $scaleUnitTabs.$tabName.Objects.$thisName.ForeColor = $defaultFailColor
                        Close-WaitForm
                        $message = "'$($itemName)' " + $locString.errIPAddressInvalid
                        [System.Windows.Forms.MessageBox]::Show($message, $locString.capValidationError, [System.Windows.Forms.MessageBoxButtons]::OK)
                        if(-not $tabBadInput.Contains($scaleUnitTabs.$tabName.Objects.$thisName))
                        {
                            $null = $tabBadInput.Add($scaleUnitTabs.$tabName.Objects.$thisName)
                        }
                    }
                    elseif(-not(Get-IsValidSubnet -IPAddress $scaleUnitTabs.$tabName.Objects.$thisName.Text -CIDR $scaleUnitTabs.$tabName.Objects.'txtSwitchInfraSubnetSize'.Text))
                    {
                        $scaleUnitTabs.$tabName.Objects.$thisName.ForeColor = $defaultFailColor
                        Close-WaitForm
                        $message = "'$($itemName)' " + ($locString.errSubnetInvalid -f $scaleUnitTabs.$tabName.Objects.$thisName.Text,$scaleUnitTabs.$tabName.Objects.'txtSwitchInfraSubnetSize'.Text)
                        [System.Windows.Forms.MessageBox]::Show($message, $locString.capValidationError, [System.Windows.Forms.MessageBoxButtons]::OK)
                        if(-not $tabBadInput.Contains($scaleUnitTabs.$tabName.Objects.$thisName))
                        {
                            $null = $tabBadInput.Add($scaleUnitTabs.$tabName.Objects.$thisName)
                        }
                    }
                    else
                    {
                        $scaleUnitTabs.$tabName.Objects.$thisName.ForeColor = $defaultForeColor
                        $null = $tabBadInput.Remove($scaleUnitTabs.$tabName.Objects.$thisName)
                    }
                    Update-TextBox $scaleUnitTabs.$tabName.Objects.$thisName
                }
                else
                {
                    $null = $tabBadInput.Remove($scaleUnitTabs.$tabName.Objects.$thisName)
                }
            }
            $scaleUnitTabs.$tabName.Objects.$thisName.Add_Leave($txtSwitchInfraSubnet_Changed)

            $thisName = 'lblSwitchInfraSubnetSize'
            $thisObj = New-Object System.Windows.Forms.Label
            $scaleUnitTabs.$tabName.Objects.Add($thisName,$thisObj)
            $scaleUnitTabs.$tabName.Objects.$thisName.Location = New-Object System.Drawing.Size($txtSubnetLeft,($tabRow+2))
            $scaleUnitTabs.$tabName.Objects.$thisName.Size = New-Object System.Drawing.Size($txtCountSize)
            $scaleUnitTabs.$tabName.Objects.$thisName.Text = $locString.lblSubnetSize
            $scaleUnitTabs.$tabName.Objects.$thisName.Font = $defaultLabelFont
            $scaleUnitTabs.$tabName.Objects.$thisName.Name = $thisName
            $scaleUnitTabs.$tabName.Tab.Controls.Add($scaleUnitTabs.$tabName.Objects.$thisName)

            $thisName = 'txtSwitchInfraSubnetSize'
            $thisObj = New-Object System.Windows.Forms.TextBox
            $scaleUnitTabs.$tabName.Objects.Add($thisName,$thisObj)
            $scaleUnitTabs.$tabName.Objects.$thisName.Location = New-Object System.Drawing.Size($txtSubnetSizeLeft,$tabRow)
            $scaleUnitTabs.$tabName.Objects.$thisName.Size = New-Object System.Drawing.Size($txtCountSize)
            $scaleUnitTabs.$tabName.Objects.$thisName.MaxLength = 2
            $scaleUnitTabs.$tabName.Objects.$thisName.Text = $defaultSwInfraSubnetSize
            $scaleUnitTabs.$tabName.Objects.$thisName.Font = $defaultLabelFont
            $scaleUnitTabs.$tabName.Objects.$thisName.Name = $thisName
            $scaleUnitTabs.$tabName.Objects.$thisName.AccessibleName = $thisName
            $scaleUnitTabs.$tabName.Objects.$thisName.AccessibleDescription = $locString.accSwitchInfraSubnetSize
            $scaleUnitTabs.$tabName.Objects.$thisName.Enabled = $false
            $scaleUnitTabs.$tabName.Objects.$thisName.BackColor = $defaultBackColor
            $scaleUnitTabs.$tabName.Objects.$thisName.ForeColor = $defaultForeColor
            $scaleUnitTabs.$tabName.Objects.$thisName.ReadOnly = $true
            $scaleUnitTabs.$tabName.Tab.Controls.Add($scaleUnitTabs.$tabName.Objects.$thisName)

            $tabRow += 60

            # ------------------------------ TOR BGP ASN ------------------------------
            $thisName = 'lblTORASN'
            $thisObj = New-Object System.Windows.Forms.Label
            $scaleUnitTabs.$tabName.Objects.Add($thisName,$thisObj)
            $scaleUnitTabs.$tabName.Objects.$thisName.Location = New-Object System.Drawing.Size($lblLeft,($tabRow+2))
            $scaleUnitTabs.$tabName.Objects.$thisName.MaximumSize = New-Object System.Drawing.Size($lblMaxSize)
            $scaleUnitTabs.$tabName.Objects.$thisName.AutoSize = $true
            $scaleUnitTabs.$tabName.Objects.$thisName.Font = $defaultLabelFont
            $scaleUnitTabs.$tabName.Objects.$thisName.Text = $locString.lblTORASN
            $scaleUnitTabs.$tabName.Tab.Controls.Add($scaleUnitTabs.$tabName.Objects.$thisName)

            $thisName = 'txtTORASN'
            $thisObj = New-Object System.Windows.Forms.TextBox
            $scaleUnitTabs.$tabName.Objects.Add($thisName,$thisObj)
            $scaleUnitTabs.$tabName.Objects.$thisName.Location = New-Object System.Drawing.Size($txtLeft,$tabRow)
            $scaleUnitTabs.$tabName.Objects.$thisName.Size = New-Object System.Drawing.Size($txtPrefixSize)
            $scaleUnitTabs.$tabName.Objects.$thisName.TextAlign = [System.Windows.Forms.HorizontalAlignment]::Center
            $scaleUnitTabs.$tabName.Objects.$thisName.MaxLength = $lenIPAddress
            $scaleUnitTabs.$tabName.Objects.$thisName.Font = $defaultLabelFont
            $scaleUnitTabs.$tabName.Objects.$thisName.Name = $thisName
            $scaleUnitTabs.$tabName.Tab.Controls.Add($scaleUnitTabs.$tabName.Objects.$thisName)
            $script:txtTORASN_Changed = `
            {
                $thisName = 'txtTORASN'
                $thisTab = $tabControl.SelectedTab
                $tabName = $thisTab.Name
                if(-not [string]::IsNullOrEmpty($scaleUnitTabs.$tabName.Objects.$thisName.Text))
                {
                    $scaleUnitTabs.$tabName.Objects.$thisName.Text = ($scaleUnitTabs.$tabName.Objects.$thisName.Text).Trim()
                    $valid = $true
                    if($scaleUnitTabs.$tabName.Objects.$thisName.Text -notmatch $validNumber)
                    {
                        $valid = $false
                    }
                    else
                    {
                        [uint64]$thisASN = $scaleUnitTabs.$tabName.Objects.$thisName.Text
                        if(-not(Get-IsValidASN -ASN $thisASN))
                        {
                            $valid = $false
                        }
                    }
                    if($valid)
                    {
                        $scaleUnitTabs.$tabName.Objects.$thisName.Text = $scaleUnitTabs.$tabName.Objects.$thisName.Text -replace '\b0+\B'
                        $scaleUnitTabs.$tabName.Objects.$thisName.ForeColor = $defaultForeColor
                        $null = $tabBadInput.Remove($scaleUnitTabs.$tabName.Objects.$thisName)
                    }
                    else
                    {
                        $scaleUnitTabs.$tabName.Objects.$thisName.ForeColor = $defaultFailColor
                        Close-WaitForm
                        $itemName = ($locString.lblTORASN).Replace(':','')
                        [string]$errMessage = "'$($itemName)' " + ($locString.errNumberNotInRange -f $validASNMin,$validASNMax)
                        [System.Windows.Forms.MessageBox]::Show($errMessage, $locString.capValidationError, [System.Windows.Forms.MessageBoxButtons]::OK)
                        if(-not $tabBadInput.Contains($scaleUnitTabs.$tabName.Objects.$thisName))
                        {
                            $null = $tabBadInput.Add($scaleUnitTabs.$tabName.Objects.$thisName)
                        }
                    }
                    Update-TextBox $scaleUnitTabs.$tabName.Objects.$thisName
                }
                else
                {
                    $null = $tabBadInput.Remove($scaleUnitTabs.$tabName.Objects.$thisName)
                }
            }
            $scaleUnitTabs.$tabName.Objects.$thisName.Add_Leave($txtTORASN_Changed)

            $tabRow += 60
        }
    }
    catch
    {
        throw $PSItem
    }
}

function Remove-ScaleUnit
{
    param
    (
        [Parameter(Mandatory=$true)]
        [int]$ID
    )
    try
    {
        $tabName = "tabSU$($ID)"
        if($scaleUnitTabs.$tabName)
        {
            $tabRemove = $tabControl.TabPages[$tabName]
            $tabControl.Controls.Remove($tabRemove)
            $scaleUnitTabs.Remove($tabName)
            Invoke-RefreshTotalNodeCount
        }
    }
    catch
    {
        throw $PSItem
    }
}

function Clear-ListSelection
{
    param
    (
        [Parameter(Mandatory=$true)]
        $Control
    )
    try
    {
        if($Control.Items.Count -ge 1)
        {
            $Control.SelectedIndex = -1
        }
        else
        {
            $Control.Text = [string]::Empty
        }
    }
    catch
    {
        throw $PSItem.Exception.Message
    }
}

function Get-SwitchModels
{
    param
    (
        [Parameter(Mandatory=$true)]
        [string]$Make,
        [Parameter(Mandatory=$true)]
        [ValidateSet("TOR","BMC","SPINE")]
        [string]$Type
    )
    try
    {
        if(-not $switches)
        {
            $script:switches = Get-Content -Raw $strSwitchFilePath | ConvertFrom-Json
        }
        $switchInfo = $switches.portmap
        if($null -ne $switchInfo.$Make.$Type)
        {
            return @(($switchInfo.$Make.$Type | Get-Member -Type NoteProperty).Name)
        }
    }
    catch
    {
        throw $PSItem
    }
}

function Get-SwitchFirmware
{
    param
    (
        [Parameter(Mandatory=$true)]
        [string]$Make,
        [Parameter(Mandatory=$true)]
        [string]$Model,
        [Parameter(Mandatory=$true)]
        [ValidateSet("TOR","BMC","SPINE")]
        [string]$Type
    )
    try
    {
        if(-not $switches)
        {
            $script:switches = Get-Content -Raw $strSwitchFilePath | ConvertFrom-Json
        }
        $switchInfo = $switches.portmap
        if($null -ne $switchInfo.$Make.$Type.$Model.FIRMWAREVERSION)
        {
            return @(($switchInfo.$Make.$Type.$Model.FIRMWAREVERSION).Version)
        }
    }
    catch
    {
        throw $PSItem
    }
}

function Get-SwitchMaxBorder
{
    param
    (
        [Parameter(Mandatory=$true)]
        [string]$Make,
        [Parameter(Mandatory=$true)]
        [string]$Model
    )
    [string]$Type = "TOR"
    try
    {
        $ret = 0
        if(-not([string]::IsNullOrEmpty($Make) -or [string]::IsNullOrEmpty($Model)))
        {
            if(-not $switches)
            {
                $script:switches = Get-Content -Raw $strSwitchFilePath | ConvertFrom-Json
            }
            $switchInfo = $switches.portmap
            if($null -ne $switchInfo.$Make.$Type.$Model.MAXBORDER)
            {
                $ret = $switchInfo.$Make.$Type.$Model.MAXBORDER
            }
        }
        return $ret
    }
    catch
    {
        throw $PSItem
    }
}

function Update-MaxBorderFromSwitch
{
    if(($lstSwitchVendor.SelectedIndex -ne -1) -and  ($lstTORModel.SelectedIndex -ne -1))
    {
        $thisMaxBorder = Get-SwitchMaxBorder -Make $lstSwitchVendor.SelectedItem -Model $lstTORModel.SelectedItem
        if($thisMaxBorder -ne $validBorder)
        {
            if($thisMaxBorder -eq 0)
            {
                if([int]$txtScaleUnitCount.Text -gt 1)
                {
                    $script:validBorder = 4
                }
                else
                {
                    $script:validBorder = 2
                }
            }
            else
            {
                $script:validBorder = $thisMaxBorder
            }
        }
        if(($lstSwitchVendor.SelectedItem -eq "CiscoUCS") -or ($lstSwitchVendor.SelectedItem -eq "Mellanox"))
        {
            $script:validBorder = 4
        }
        if([int]$txtBorderCount.Text -gt [int]$validBorder)
        {
            $txtBorderCount.Text = $validBorder
            Invoke-Command -ScriptBlock $txtBorderCount_Changed
        }
    }
}

function Get-IsValidURI
{
    param
    (
        [Parameter(Mandatory=$true)]
        [string]$URI
    )
    $sysURI = $URI -as [System.URI]
    (($null -ne $sysURI.AbsoluteURI) -and ($sysURI.Scheme -match 'http|https'))
}

function Get-IsValidSubnet
{
    param
    (
        [Parameter(Mandatory=$true)]
        [string]$IPAddress,
        [Parameter(Mandatory=$true)]
        [string]$CIDR
    )
    [System.Net.IPNetwork]$net = $IPAddress + '/' + $CIDR
    if($IPAddress -ne $net.Network.IPAddressToString)
    {
        return $false
    }
    else
    {
        return $true
    }
}

function Get-IsValidASN
{
    param
    (
        [uint64]$ASN
    )
    if($ASN -lt $validASNMin)
    {
        return $false
    }
    if($ASN -gt $validASNMax)
    {
        return $false
    }
    return $true
}

function Disable-BorderBGP
{
    param
    (
        [Parameter(Mandatory=$true)]
        [int]$Num
    )
    $thisScenario = $scenario
    if($scenario -eq "AddRack")
    {
        $thisScenario = "Customer"
    }
    switch ($thisScenario)
    {
        "Customer"
        {
            $varLbl = $tabBorder.Controls | Where-Object Name -eq "lblBorderBGP$($Num)"
            $varTxt = $tabBorder.Controls | Where-Object Name -eq "txtBorderBGP$($Num)"
            if($varLbl -and $varTxt)
            {
                $varLbl.Enabled = $false
                $varLbl.Visible = $false
                $varTxt.Enabled = $false
                $varTxt.Visible = $false
                $varTxt.Text = [string]::Empty
                $varLbl.Refresh()
                $varTxt.Refresh()
            }
            else
            {
                Write-Log -Message "$($MyInvocation.MyCommand.Name) - WARNING: unable to find Border BGP ASN controls" -Force
            }
        }
        default
        {
        }
    }
}

function Enable-BorderBGP
{
    param
    (
        [Parameter(Mandatory=$true)]
        [int]$Num
    )
    $thisScenario = $scenario
    if($scenario -eq "AddRack")
    {
        $thisScenario = "Customer"
    }
    switch ($thisScenario)
    {
        "Customer"
        {
            $varLbl = $tabBorder.Controls | Where-Object Name -eq "lblBorderBGP$($Num)"
            $varTxt = $tabBorder.Controls | Where-Object Name -eq "txtBorderBGP$($Num)"
            if($varLbl -and $varTxt)
            {
                $varLbl.Enabled = $true
                $varLbl.Visible = $true
                $varTxt.Enabled = $true
                $varTxt.Visible = $true
                $varLbl.Refresh()
                $varTxt.Refresh()
            }
            else
            {
                Write-Log -Message "$($MyInvocation.MyCommand.Name) - WARNING: unable to find Border BGP ASN controls" -Force
            }
        }
        default
        {
        }
    }
}

function Disable-SpineBGP
{
    $thisScenario = $scenario
    if($scenario -eq "AddRack")
    {
        $thisScenario = "Customer"
    }
    switch ($thisScenario)
    {
        "Customer"
        {
            $lblSpineBGP.Enabled = $false
            $lblSpineBGP.Visible = $false
            $txtSpineBGP.Enabled = $false
            $txtSpineBGP.Visible = $false
            $txtSpineBGP.Text = [string]::Empty
            $lblSpineBGP.Refresh()
            $txtSpineBGP.Refresh()
        }
        default
        {
        }
    }
}

function Enable-SpineBGP
{
    $thisScenario = $scenario
    if($scenario -eq "AddRack")
    {
        $thisScenario = "Customer"
    }
    switch ($thisScenario)
    {
        "Customer"
        {
            $lblSpineBGP.Enabled = $true
            $lblSpineBGP.Visible = $true
            $txtSpineBGP.Enabled = $true
            $txtSpineBGP.Visible = $true
            $lblSpineBGP.Refresh()
            $txtSpineBGP.Refresh()
        }
        default
        {
        }
    }
}

function Enable-SpineCount
{
    $thisScenario = $scenario
    if($scenario -eq "AddRack")
    {
        $thisScenario = "Customer"
    }
    switch ($thisScenario)
    {
        "Customer"
        {
            $lblSpineCount.Enabled = $true
            $lblSpineCount.Visible = $true
            $txtSpineCount.Enabled = $true
            $txtSpineCount.Visible = $true
            $txtSpineCount.Text = $defaultSpineCount
            $lblSpineCount.Refresh()
            $txtSpineCount.Refresh()
        }
        default
        {
        }
    }
}

function Disable-SpineCount
{
    $thisScenario = $scenario
    if($scenario -eq "AddRack")
    {
        $thisScenario = "Customer"
    }
    switch ($thisScenario)
    {
        "Customer"
        {
            $lblSpineCount.Enabled = $false
            $lblSpineCount.Visible = $false
            $txtSpineCount.Enabled = $false
            $txtSpineCount.Visible = $false
            $txtSpineCount.Text = [string]::Empty
            $lblSpineCount.Refresh()
            $txtSpineCount.Refresh()
        }
        default
        {
        }
    }
}

function Enable-SpineSubnet
{
    $thisScenario = $scenario
    if($scenario -eq "AddRack")
    {
        $thisScenario = "Customer"
    }
    switch ($thisScenario)
    {
        "Customer"
        {
            $lblSpineSubnet.Enabled = $true
            $lblSpineSubnet.Visible = $true
            $txtSpineSubnet.Enabled = $true
            $txtSpineSubnet.Visible = $true
            $lblSpineSubnetSize.Enabled = $true
            $lblSpineSubnetSize.Visible = $true
            $txtSpineSubnetSize.Enabled = $false
            $txtSpineSubnetSize.Visible = $true
        }
        default
        {
        }
    }
}

function Disable-SpineSubnet
{
    $thisScenario = $scenario
    if($scenario -eq "AddRack")
    {
        $thisScenario = "Customer"
    }
    switch ($thisScenario)
    {
        "Customer"
        {
            $lblSpineSubnet.Enabled = $false
            $lblSpineSubnet.Visible = $false
            $txtSpineSubnet.Enabled = $false
            $txtSpineSubnet.Visible = $false
            $txtSpineSubnet.Text = [string]::Empty
            $lblSpineSubnetSize.Enabled = $false
            $lblSpineSubnetSize.Visible = $false
            $txtSpineSubnetSize.Enabled = $false
            $txtSpineSubnetSize.Visible = $false
        }
        default
        {
        }
    }
}

function Invoke-RefreshBorderBGP
{
    param
    (
        [string]$BorderCount
    )
    (1..$maxBorder) | ForEach-Object `
    {
        if($PSItem -le $BorderCount)
        {
            if(-not $isStaticRouting)
            {
                Enable-BorderBGP $PSItem
            }
            else
            {
                Disable-BorderBGP $PSItem
            }
        }
        else
        {
            Disable-BorderBGP $PSItem
        }
    }
}

function Clear-ExistingData
{
    # Set textbox items
    $objTextBox = `
    @(
        $txtBorderBGP1
        $txtBorderBGP2
        $txtBorderBGP3
        $txtBorderBGP4
        $txtEdgeBGP
        $txtSpineBGP
        $txtSoftwareBGP
        $txtExternalSubnet
        $txtCompanyName
        $txtRegionName
        $txtExternalDomain
        $txtPrivateDomain
        $txtDeployPrefix
        $txtPhysicalPrefix
        $txtTimeServer
        $txtDNSForwarder
        $txtSyslogServer
        $txtSpineSubnet
        $txtAADTenantName
        $txtADFSForestFQDN
        $txtADFSProviderName
        $txtADFSMetadataUri
        $txtPermitNetworks
        $txtPrivateSubnet
    )
    # Set values on the Scale-Unit tab
    $objScaleUnit = `
    @(
        'txtNodeCount'
        'txtBMCSubnet'
        'txtInfraSubnet'
        'txtSwitchInfraSubnet'
        'txtTORASN'
    )
    # Set combobox items
    $objComboBox = `
    @(
        $lstSwitchVendor
        $lstTORModel
        $lstTORFirmware
        $lstBMCModel
        $lstBMCFirmware
        $lstSpineModel
        $lstSpineFirmware
        $lstAzureEnvironment
        $lstUplinkSpeed
    )
    # Set listbox items
    $objListBox = `
    @(
        $lstPermitNetworks
    )
    # Clear Scale-Unit objects
    $txtScaleUnitCount.Text = '1'
    Invoke-Command -ScriptBlock $txtScaleUnitCount_Changed
    $objScaleUnit | ForEach-Object `
    {
        if(-not($isExtStor -and $PSItem -eq 'txtNodeCount'))
        {
            $scaleUnitTabs.tabSU1.Objects.$PSItem.Text = [string]::Empty
            $scaleUnitTabs.tabSU1.Objects.$PSItem.ForeColor = $defaultForeColor
        }
    }
    # Reset Customer items to default settings
    $lstIdentityStore.SelectedIndex = -1
    $lstAzureEnvironment.SelectedIndex = -1
    $lblAADTenantName.Enabled = $false
    $lblAADTenantName.Visible = $false
    $lblAzureEnvironment.Enabled = $false
    $lblAzureEnvironment.Visible = $false
    $txtAADTenantName.Text = [string]::Empty
    $txtAADTenantName.Enabled = $false
    $txtAADTenantName.Visible = $false
    $lstAzureEnvironment.Enabled = $false
    $lstAzureEnvironment.Visible = $false
    $txtADFSForestFQDN.Text = [string]::Empty
    $txtADFSForestFQDN.Enabled = $false
    $txtADFSForestFQDN.Visible = $false
    $txtADFSProviderName.Text = [string]::Empty
    $txtADFSProviderName.Enabled = $false
    $txtADFSProviderName.Visible = $false
    $txtADFSMetadataUri.Text = [string]::Empty
    $txtADFSMetadataUri.Enabled = $false
    $txtADFSMetadataUri.Visible = $false
    $lblADFSForestFQDN.Enabled = $false
    $lblADFSProviderName.Enabled = $false
    $lblADFSMetadataUri.Enabled = $false
    $lblADFSForestFQDN.Visible = $false
    $lblADFSProviderName.Visible = $false
    $lblADFSMetadataUri.Visible = $false
    $lstRoutingMethod.SelectedIndex = 0
    $txtBorderCount.Text = $defaultBorderCount
    $txtExternalSubnetSize.Text = $defaultExternalSubnetSize
    Invoke-RefreshTotalNodeCount

    # Clear textbox objects
    $objTextBox | Foreach-Object `
    {
        $PSItem.Text = [string]::Empty
        $PSItem.ForeColor = $defaultForeColor
    }
    # Clear combobox selections
    foreach($obj in $objComboBox)
    {
        if($isExtStor -and (($obj.Name -eq 'lstSwitchVendor') -or ($obj.Name -like "lstTOR*") -or ($obj.Name -like "lstBMC*")))
        {
            if($obj.Name -eq 'lstSwitchVendor')
            {
                $lstTORModel.SelectedIndex = 0
                $lstTORFirmware.SelectedIndex = 0
                $lstBMCModel.SelectedIndex = 0
                $lstBMCFirmware.SelectedIndex = 0
            }
        }
        else
        {
            if($obj.Items.Count -ge 1)
            {
                $obj.SelectedIndex = -1
            }
            else
            {
                $obj.Text = [string]::Empty
            }
        }
    }
    # Clear listbox objects
    foreach($obj in $objListBox)
    {
        [void]$obj.Items.Clear()
    }
    # Reset Network switch items to visible
    $lblTORModel.Enabled = $true
    $lblTORModel.Visible = $true
    $lstTORModel.Enabled = $true
    $lstTORModel.Visible = $true
    $lblTORFirmware.Enabled = $true
    $lblTORFirmware.Visible = $true
    $lstTORFirmware.Enabled = $true
    $lstTORFirmware.Visible = $true
    $lblBMCModel.Enabled = $true
    $lblBMCModel.Visible = $true
    $lstBMCModel.Enabled = $true
    $lstBMCModel.Visible = $true
    $lblBMCFirmware.Enabled = $true
    $lblBMCFirmware.Visible = $true
    $lstBMCFirmware.Enabled = $true
    $lstBMCFirmware.Visible = $true
    # Clear Partner tab if present
    if($tabControl.TabPages["tabPartner"])
    {
        $tabPartner.Controls | ForEach-Object `
        {
            if($PSItem -is [System.Windows.Forms.TextBox])
            {
                $PSItem.Text = [string]::Empty
            }
            elseif($PSItem -is [System.Windows.Forms.ComboBox])
            {
                $PSItem.SelectedIndex = -1
            }
        }
    }
    Remove-GeneratedTabs
    $script:validBorder = $defaultBorderCount
    Invoke-Command -ScriptBlock $txtBorderCount_Changed
    if(-not $isExtStor)
    {
        Disable-UplinkSpeed
    }
}

function Get-SubnetSize
{
    param
    (
        [Parameter(Mandatory=$true)]
        [int]$CloudCount,
        [Parameter(Mandatory=$true)]
        [int]$SupernetSize
    )
    $ipsInSupernet = [System.Math]::Pow(2, 32 - $SupernetSize)
    $ipsInCluster = $ipsInSupernet/$CloudCount
    $roundDownLogVal = [System.Math]::Floor([System.Math]::Log($ipsInCluster, 2))
    [double]$subnetsizePerCluster = 32 - $roundDownLogVal
    return [int]$subnetsizePerCluster
}

function Get-SupernetSize
{
    param
    (
        [Parameter(Mandatory=$true)]
        [int]$SubnetCount,
        [Parameter(Mandatory=$false)]
        [int]$SubnetSize = 30
    )
    if($SubnetCount -eq 1)
    {
        return $SubnetSize
    }
    [int]$supernetSize = 0
    for($i = $SubNetSize-1; $i -ge 1; $i--)
    {
        $numSubnets = [System.Math]::Pow(2,(32-$i)) / [System.Math]::Pow(2,(32-$SubnetSize))
        if($numSubnets -ge $SubnetCount)
        {
            $supernetSize = $i
            break
        }
    }
    return $supernetSize
}

function New-Deployment
{
    $thisDeployment = New-Object Microsoft.AzureStack.PartnerToolkit.IPCalculator.Deployment
    return $thisDeployment
}

function Get-PartnerData
{
    $script:partnerData = @{}
    $tabPartner.Controls | ForEach-Object `
    {
        if($PSItem -is [System.Windows.Forms.TextBox])
        {
            $partnerData.Add($PSItem.Name,$PSItem.Text)
        }
        elseif($PSItem -is [System.Windows.Forms.ComboBox])
        {
            $partnerData.Add($PSItem.Name,$PSItem.SelectedItem)
        }
    }
}

function Add-PartnerTab
{
    $script:tabPartner = New-Object System.Windows.Forms.TabPage
    $typTab = $tabPartner.GetType()
    $prpTab = $typTab.GetProperty('DoubleBuffered',('Instance','NonPublic'))
    $prpTab.SetValue($tabPartner, $true, $null)
    $tabPartner.BackColor = [System.Drawing.Color]::Lavender
    $tabPartner.Name = "tabPartner"
    $tabPartner.Text = $locString.tabPartner
    $tabPartner.AutoScroll = $true
    $tabControl.Controls.Add($tabPartner)
}

function Move-PartnerTab
{
    if($tabControl.TabPages.ContainsKey('tabPartner'))
    {
        $tabMove += $tabControl.TabPages['tabPartner']
        $tabControl.TabPages.Remove($tabMove)
        $tabControl.TabPages.Add($tabMove)
    }
}

function Import-PartnerExtension
{
    param
    (
        [Parameter(Mandatory=$true)]
        [string]$FileName
    )
    Write-Log -Message "$($MyInvocation.MyCommand.Name) - called with a FileName of '$($FileName)'"
    [int]$script:oemrow = 20
    [int]$lblSubSection = [int]$lblLeft + 40
    [int]$lblSection = $lblLeft
    $partnerExtension = Get-Content $FileName -Raw | ConvertFrom-Json
    Add-PartnerTab

    function Add-PartnerDataSectionToTab
    {
        param
        (
            $Section,
            $Left
        )
        [int]$lblPartner = $Left + 40
        [int]$txtPartner = $Left + 80
        $sectionTitle = $Section.title
        # Get-Member automatically sorts the list of names, so don't us it
        # $valueNames = ($Section.Properties | Get-Member -Type NoteProperty).Name
        $valueNames = ($Section.Properties.PSObject.Properties).Name
        if($valueNames.Count -gt 0)
        {
            foreach($item in $valueNames)
            {
                $thisLabel = New-Object System.Windows.Forms.Label
                $thisLabel.Location = New-Object System.Drawing.Size($lblPartner,($script:oemrow))
                $thisLabel.AutoSize = $true
                $thisLabel.Font = $defaultLabelFont
                $thisLabel.Text = "$($item) :"
                $tabPartner.Controls.Add($thisLabel)
                $thisType = $Section.Properties.$($item).type
                switch ($thisType)
                {
                    "string"
                    {
                        $script:oemrow += 20
                        $thisText = New-Object System.Windows.Forms.TextBox
                        $thisText.Location = New-Object System.Drawing.Size($txtPartner,$script:oemrow)
                        $thisText.Size = New-Object System.Drawing.Size($txtSize)
                        $thisText.Font = $defaultLabelFont
                        $thisText.Name = $item
                        if($Section.Properties.$item.maxlength)
                        {
                            $thisText.MaxLength = $Section.Properties.$item.maxlength
                        }
                        $tabPartner.Controls.Add($thisText)
                    }
                    "list"
                    {
                        $script:oemrow += 20
                        $thisList = New-Object System.Windows.Forms.ComboBox
                        $thisList.DropDownStyle = [System.Windows.Forms.ComboBoxStyle]::DropDownList
                        $thisList.Location = New-Object System.Drawing.Size($txtPartner,$script:oemrow)
                        $thisList.Size = New-Object System.Drawing.Size($txtSize)
                        $thisList.Font = $defaultLabelFont
                        $thisList.Name = $item
                        [void]$thisList.Items.Clear()
                        $Section.Properties.$item.data | Foreach-Object `
                        {
                            [void]$thisList.Items.Add($PSItem)
                        }
                        $tabPartner.Controls.Add($thisList)
                    }
                    default
                    {
                        Write-Warning "An unknown partner extension Type of '$($thisType)' was encountered."
                    }
                }
                $script:oemrow += 40
            }
        }
    }

    foreach($section in $partnerExtension.Sections)
    {
        if($section.title)
        {
            $sectionTitle = $section.title
            $lblPartnerSection = New-Object System.Windows.Forms.Label
            $lblPartnerSection.Location = New-Object System.Drawing.Size($lblSection,($script:oemrow))
            $lblPartnerSection.AutoSize = $true
            $lblPartnerSection.Text = $sectionTitle
            $lblPartnerSection.Font = [System.Drawing.Font]::New("Microsoft Sans Serif", 12, [System.Drawing.FontStyle]::Bold)
            $tabPartner.Controls.Add($lblPartnerSection)
            $script:oemrow += $lblPartnerSection.Size.Height + 8
        }
        if($section.comment)
        {
            $sectionComment = $section.comment
            $lblPartnerSectionComment = New-Object System.Windows.Forms.Label
            $lblPartnerSectionComment.Location = New-Object System.Drawing.Size($lblSection,($script:oemrow))
            $lblPartnerSectionComment.MaximumSize = New-Object System.Drawing.Size($maxComment, 0)
            $lblPartnerSectionComment.AutoSize = $true
            $lblPartnerSectionComment.Text = $sectionComment
            $lblPartnerSectionComment.Font = [System.Drawing.Font]::New("Microsoft Sans Serif", 8.5, [System.Drawing.FontStyle]::Italic)
            $tabPartner.Controls.Add($lblPartnerSectionComment)
            $script:oemrow += $lblPartnerSectionComment.Size.Height + 12
        }
        else
        {
            $script:oemrow += 10
        }
        if($section.SubSections)
        {
            foreach($subsection in $section.SubSections)
            {
                if($subsection.title)
                {
                    $sectionTitle = $subsection.title
                    $lblPartnerSubSection = New-Object System.Windows.Forms.Label
                    $lblPartnerSubSection.Location = New-Object System.Drawing.Size($lblSubSection,($script:oemrow))
                    $lblPartnerSubSection.AutoSize = $true
                    $lblPartnerSubSection.Text = $sectionTitle
                    $lblPartnerSubSection.Font = [System.Drawing.Font]::New("Microsoft Sans Serif", 10, [System.Drawing.FontStyle]::Bold)
                    $tabPartner.Controls.Add($lblPartnerSubSection)
                    $script:oemrow += $lblPartnerSubSection.Size.Height + 8
                }
                if($subsection.comment)
                {
                    $sectionComment = $subsection.comment
                    $lblPartnerSubSectionComment = New-Object System.Windows.Forms.Label
                    $lblPartnerSubSectionComment.Location = New-Object System.Drawing.Size($lblSubSection,($script:oemrow))
                    $lblPartnerSubSectionComment.MaximumSize = New-Object System.Drawing.Size(($maxComment-$lblSubSection), 0)
                    $lblPartnerSubSectionComment.AutoSize = $true
                    $lblPartnerSubSectionComment.Text = $sectionComment
                    $lblPartnerSubSectionComment.Font = [System.Drawing.Font]::New("Microsoft Sans Serif", 8.5, [System.Drawing.FontStyle]::Italic)
                    $tabPartner.Controls.Add($lblPartnerSubSectionComment)
                    $script:oemrow += $lblPartnerSubSectionComment.Size.Height + 12
                }
                else
                {
                    $script:oemrow += 10
                }
                Add-PartnerDataSectionToTab -Section $subsection -Left $lblSubSection
            }
        }
        else
        {
            Add-PartnerDataSectionToTab -Section $section -Left $lblSection
        }
        $script:oemrow += 20
    }
    $script:oemrow += 20
    $lblPadding = New-Object System.Windows.Forms.Label
    $lblPadding.Location = New-Object System.Drawing.Size($lblSection,($script:oemrow))
    $lblPadding.AutoSize = $true
    $lblPadding.Text = ""
    $lblPadding.Font = [System.Drawing.Font]::New("Microsoft Sans Serif", 12, [System.Drawing.FontStyle]::Bold)
    $tabPartner.Controls.Add($lblPadding)
}

function Get-ValidationError
{
    param
    (
        [Parameter(Mandatory=$true)]$ErrorList
    )
    $retMessage = @()
    foreach($item in $ErrorList)
    {
        # Weed out some of the duplicate errors to make it easier on the user
        $item = $item.Trim()
        if((-not [string]::IsNullOrEmpty($item)) -and ($retMessage -notcontains $item))
        {
            $add = $true
            if($item -match "correct all validation issues")
            {
                $add = $false
            }
            elseif($item -match "overlap 0.0.0.0")
            {
                $add = $false
            }
            elseif(($item -match "BMC-1 ASN") -or ($item -like "*prefix-Rack0?-BMC-1*"))
            {
                $add = $false
            }
            elseif(($item -match "TOR2") -or ($item -match "TOR-2"))
            {
                $add = $false
            }
            elseif(($item -like "*Model is empty for (prefix-Rack0?-Spine*") -and ($retMessage -like "*Model is empty for (prefix-Rack0?-Spine*"))
            {
                $add = $false
            }
            elseif(($item -like "*Spine BGP ASN value is empty*") -and ($retMessage -like "*Spine BGP ASN value is empty*"))
            {
                $add = $false
            }
            elseif(($item -like "*firmware version is empty for (prefix-Rack0?-Spine*") -and ($retMessage -like "*firmware version is empty for (prefix-Rack0?-Spine*"))
            {
                $add = $false
            }
            elseif(($item -like "*TOR? and Border*") -or ($item -like "*Border and TOR?*"))
            {
                if($retMessage -like "*TOR? and Border*") { $add = $false }
                if($retMessage -like "*Border and TOR?*") { $add = $false }
            }
            elseif(($item -like "*MUX and TOR?*") -or ($item -like "*TOR? and MUX*"))
            {
                if($retMessage -like "*MUX and TOR?*") { $add = $false }
                if($retMessage -like "*TOR? and MUX*") { $add = $false }
            }
            elseif(($item -like "*MUX and Border*") -or ($item -like "*Border and MUX*"))
            {
                if($retMessage -like "*MUX and Border*") { $add = $false }
                if($retMessage -like "*Border and MUX*") { $add = $false }
            }
            elseif($item -like "*Edge Switch BGP ASN value is empty*")
            {
                if($retMessage -like "*Edge Switch BGP ASN value is empty*") { $add = $false }
            }
            elseif($item -like "*Edge BGP ASN value is empty*")
            {
                if($retMessage -like "*Edge BGP ASN value is empty*") { $add = $false }
            }
            elseif($item -like "*Border Switch BGP ASN value is empty*")
            {
                if($retMessage -like "*Border Switch BGP ASN value is empty*") { $add = $false }
            }
            elseif($item -like "*TOR2 BGP ASN value is empty*")
            {
                $add = $false
            }
            elseif(($item -match "at System") -or ($item -match "at Microsoft") -or ($item -match "at CallSite"))
            {
                $add = $false
            }
            if($add)
            {
                $retMessage += "`n$($item)`n"
            }
        }
    }
    return $retMessage
}

function Get-DomainValidationError
{
    param
    (
        [Parameter(Mandatory=$true)][System.Windows.Forms.TextBox]$TextControl
    )
    [string[]]$errDetails = @()
    if($TextControl.Text.Split('.').Count -le 1)
    {
        [string[]]$errDetails += $locString.msgFQDNTwoLevels
    }
    else
    {
        [string]$tld = $TextControl.Text.Split('.') | Select-Object -Last 1
        if($tld.Length -lt 2)
        {
            [string[]]$errDetails += $locString.msgFQDNTLDTwoChars
        }
        if($tld -match '[0-9]')
        {
            [string[]]$errDetails += $locString.msgFQDNTLDNoNum
        }
        if($tld -match '_|\W+')
        {
            [string[]]$errDetails += $locString.msgFQDNTLDNoDash
        }
    }
    return $errDetails
}

function Update-TextBox
{
    param
    (
        [Parameter(Mandatory=$true)][System.Windows.Forms.TextBox]$TextControl
    )
    $value = $TextControl.Text
    $TextControl.ResetText()
    $TextControl.Text = $value
    $TextControl.Refresh()
}

function Enable-PermitNetworks
{
    $lblPermitNetworksHeader.Enabled = $true
    $lblPermitNetworksOptional.Enabled = $true
    $lblPermitNetworks.Enabled = $true
    $txtPermitNetworks.Enabled = $true
    $btnPermitNetworksAdd.Enabled = $true
    $btnPermitNetworksDel.Enabled = $true
    $lstPermitNetworks.Enabled = $true
    $lblPermitNetworksHeader.Visible = $true
    $lblPermitNetworksOptional.Visible = $true
    $lblPermitNetworks.Visible = $true
    $txtPermitNetworks.Visible = $true
    $btnPermitNetworksAdd.Visible = $true
    $btnPermitNetworksDel.Visible = $true
    $lstPermitNetworks.Visible = $true
}

function Disable-PermitNetworks
{
    $lblPermitNetworksHeader.Enabled = $false
    $lblPermitNetworksOptional.Enabled = $false
    $lblPermitNetworks.Enabled = $false
    $txtPermitNetworks.Enabled = $false
    $btnPermitNetworksAdd.Enabled = $false
    $btnPermitNetworksDel.Enabled = $false
    $lstPermitNetworks.Enabled = $false
    $lblPermitNetworksHeader.Visible = $false
    $lblPermitNetworksOptional.Visible = $false
    $lblPermitNetworks.Visible = $false
    $txtPermitNetworks.Visible = $false
    $btnPermitNetworksAdd.Visible = $false
    $btnPermitNetworksDel.Visible = $false
    $lstPermitNetworks.Visible = $false
    $lstPermitNetworks.Items.Clear()
}

function Enable-UplinkSpeed
{
    if($arrUplinkSpeed.Count -eq 0)
    {
        Write-Log -Message "WARNING: switch.json does not contain any required UplinkSpeed values" -Force
    }
    $lblUplinks.Enabled = $true
    $lblUplinks.Visible = $true
    $lblUplinkSpeed.Enabled = $true
    $lblUplinkSpeed.Visible = $true
    $lstUplinkSpeed.Enabled = $true
    $lstUplinkSpeed.Visible = $true
}

function Disable-UplinkSpeed
{
    $lblUplinks.Enabled = $false
    $lblUplinks.Visible = $false
    $lblUplinkSpeed.Enabled = $false
    $lblUplinkSpeed.Visible = $false
    $lstUplinkSpeed.Enabled = $false
    $lstUplinkSpeed.Visible = $false
}

function Enable-ExtendedStorage
{
    $script:isExtStor = $true
    Update-SwitchVendor
    if($lstSwitchVendor.Items -contains 'msftcisco')
    {
        $lstSwitchVendor.SelectedItem = 'msftcisco'
        Invoke-Command -ScriptBlock $lstSwitchVendor_Changed
        $lstSwitchVendor.Enabled = $true
        $txtScaleUnitCount.Text = '1'
        $txtScaleUnitCount.Enabled = $false
        $txtScaleUnitCount.BackColor = $defaultBackColor
        $txtScaleUnitCount.ForeColor = $defaultForeColor
        $txtScaleUnitCount.ReadOnly = $true
        $scaleUnitTabs.tabSU1.Objects.txtNodeCount.Text = $script:defaultNodeCountExtStore
        $scaleUnitTabs.tabSU1.Objects.txtNodeCount.Enabled = $true
        $scaleUnitTabs.tabSU1.Objects.txtNodeCount.BackColor = $defaultBackColor
        $scaleUnitTabs.tabSU1.Objects.txtNodeCount.ForeColor = $defaultForeColor
        $scaleUnitTabs.tabSU1.Objects.txtNodeCount.ReadOnly = $false
        Invoke-RefreshTotalNodeCount
        Invoke-Command -ScriptBlock $txtScaleUnitCount_Changed
        if($isStaticRouting)
        {
            $lblEdgeBGP.Visible = $false
            $txtEdgeBGP.Visible = $false
            $txtEdgeBGP.Text = [string]::Empty
            $scaleUnitTabs.tabSU1.Objects.lblTORASN.Visible = $false
            $scaleUnitTabs.tabSU1.Objects.txtTORASN.Visible = $false
            $scaleUnitTabs.tabSU1.Objects.txtTORASN.Text = [string]::Empty
        }
        elseif($lstSwitchVendor.SelectedItem -eq 'msftcisco')
        {
            $lblEdgeBGP.Visible = $true
            $txtEdgeBGP.Visible = $true
            $scaleUnitTabs.tabSU1.Objects.lblTORASN.Visible = $false
            $scaleUnitTabs.tabSU1.Objects.txtTORASN.Visible = $false
        }
        else
        {
            $lblEdgeBGP.Visible = $false
            $txtEdgeBGP.Visible = $false
            $txtEdgeBGP.Text = [string]::Empty
            $scaleUnitTabs.tabSU1.Objects.lblTORASN.Visible = $true
            $scaleUnitTabs.tabSU1.Objects.txtTORASN.Visible = $true
        }
        $script:arrUplinkSpeed = @()
        $script:arrUplinkSpeed = Get-UplinkSpeedList
        Invoke-Command -ScriptBlock $UpdateUplinkSpeedItems
        Enable-UplinkSpeed
    }
    else
    {
        $script:isExtStor = $false
        Write-Log "WARNING: 'msftcisco' switch make is not present in switch.json - unable to select scenario" -Force
        [System.Windows.Forms.MessageBox]::Show($locString.errMSFTCisco, $locString.capError, [System.Windows.Forms.MessageBoxButtons]::OK)
        Update-SwitchVendor
        Disable-UplinkSpeed
    }
}

function Disable-ExtendedStorage
{
    $script:isExtStor = $false
    Disable-UplinkSpeed
    $scaleUnitTabs.tabSU1.Objects.txtNodeCount.Enabled = $true
    $scaleUnitTabs.tabSU1.Objects.txtNodeCount.ReadOnly = $false
    $scaleUnitTabs.tabSU1.Objects.txtNodeCount.Text = [string]::Empty
    Invoke-RefreshTotalNodeCount
    $scaleUnitTabs.tabSU1.Objects.lblTORASN.Visible = $true
    $scaleUnitTabs.tabSU1.Objects.txtTORASN.Visible = $true
    $scaleUnitTabs.tabSU1.Objects.txtTORASN.Text = [string]::Empty
    Update-SwitchVendor
    $lstSwitchVendor.Enabled = $true
    $lstSwitchVendor.SelectedIndex = -1
    Invoke-Command -ScriptBlock $lstSwitchVendor_Changed
    $lblEdgeBGP.Visible = $false
    $txtEdgeBGP.Visible = $false
    $txtEdgeBGP.Text = [string]::Empty
}

function Get-UplinkSpeedList
{
    param
    (
        [string]$SpecificType,
        [string]$SpecificMake,
        [string]$SpecificModel
    )
    $retArray = @()
    if(-not $switches)
    {
        $script:switches = Get-Content -Raw $strSwitchFilePath | ConvertFrom-Json
    }
    if([string]::IsNullOrEmpty($SpecificModel) -and [string]::IsNullOrEmpty($SpecificType))
    {
        $SpecificType = 'EDGE'
    }
    $switchInfo = $switches.portmap
    if(-not [string]::IsNullOrEmpty($SpecificType))
    {
        foreach($make in ($switchInfo | Get-Member -Type NoteProperty).Name)
        {
            if(-not [string]::IsNullOrEmpty($make))
            {
                foreach($type in ($switchInfo.$make | Get-Member -Type NoteProperty).Name)
                {
                    if(-not [string]::IsNullOrEmpty($type) -and ($type -eq $SpecificType))
                    {
                        foreach($model in ($switchInfo.$make.$type | Get-Member -Type NoteProperty).Name)
                        {
                            if($switchInfo.$make.$type.$model.UplinkOption)
                            {
                                foreach($link in $switchInfo.$make.$type.$model.UplinkOption)
                                {
                                    if(-not [string]::IsNullOrEmpty($link.FriendlyName))
                                    {
                                        if($retArray -notcontains $link.FriendlyName)
                                        {
                                            $retArray += $link.FriendlyName
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
    }
    elseif((-not [string]::IsNullOrEmpty($SpecificMake)) -and (-not [string]::IsNullOrEmpty($SpecificModel)))
    {
        foreach($make in ($switchInfo | Get-Member -Type NoteProperty).Name)
        {
            if((-not [string]::IsNullOrEmpty($make)) -and ($make -eq $SpecificMake))
            {
                foreach($type in ($switchInfo.$make | Get-Member -Type NoteProperty).Name)
                {
                    if((-not [string]::IsNullOrEmpty($type)) -and ($type -match "TOR"))
                    {
                        foreach($model in ($switchInfo.$make.$type | Get-Member -Type NoteProperty).Name)
                        {
                            if(($model -eq $SpecificModel) -and ($switchInfo.$make.$type.$model.UplinkOption))
                            {
                                foreach($link in $switchInfo.$make.$type.$model.UplinkOption)
                                {
                                    if(-not [string]::IsNullOrEmpty($link.FriendlyName))
                                    {
                                        if($retArray -notcontains $link.FriendlyName)
                                        {
                                            $retArray += $link.FriendlyName
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
    }
    return $retArray
}

function Set-Experimental
{
    $menuScenario.DropDownItems.Clear()
    $menuScenario.DropDownItems.AddRange(@($menuItemCustomer,(New-Object System.Windows.Forms.ToolStripSeparator),$menuItemPreviewMultiSU,$menuItemPreviewAddSU,$menuItemPreviewExtStor))
    $script:EnableExperimental = $true
}

function Start-DeploymentWorksheet
{
    param
    (
        [switch]$EnableLogging,
        [switch]$EnableExperimental
    )
    try
    {
        $script:logging = $false
        Initialize-Environment
        if($EnableLogging)
        {
            $script:logging = $true
            Invoke-InitializeLogging
            Write-Log -Message "$($locString.titleMain) - v$(Get-FileVersionInfo)"
        }
        if($psISE)
        {
            Write-Log -Message "$($locString.powershellISE)" -Quiet
            $bar = '-' * [int]($locString.powershellISE).Length
            Write-Warning $bar
            Write-Warning $locString.powershellISE
            Write-Warning $bar
        }
        if(([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole] "Administrator"))
        {
            Write-Log -Message "$($locString.powershellAdmin)" -Quiet
            $bar = '-' * [int]($locString.powershellAdmin).Length
            Write-Warning $bar
            Write-Warning $locString.powershellAdmin
            Write-Warning $bar
        }
    }
    catch
    {
        throw $PSItem
    }

    # =============================== MAIN FORM ===============================
    $script:frmMain = New-Object System.Windows.Forms.Form
    $frmMain.AutoScaleDimensions = $defaultAutoScaleDimensions
    $frmMain.AutoScaleMode = $defaultAutoScaleMode
    $frmMain.AutoSizeMode = $defaultAutoSizeMode
    $frmMain.SuspendLayout()
    $typForm = $frmMain.GetType()
    $prpForm = $typForm.GetProperty('DoubleBuffered',('Instance','NonPublic'))
    $prpForm.SetValue($frmMain, $true, $null)
    $frmMain.Size = New-Object System.Drawing.Size($defaultMainFormSize)
    $frmMain.ShowIcon = $false
    $frmMain.MaximizeBox = $true
    $frmMain.FormBorderStyle = [System.Windows.Forms.FormBorderStyle]::Sizable
    $frmMain.MinimumSize = New-Object System.Drawing.Size($defaultMainFormSize)
    $frmMain.StartPosition = "CenterScreen"
    $frmMain.Text = "$($locString.titleMain) - v$(Get-FileVersionInfo)"
    $frmMain.Name = 'DeploymentWorksheet'
    $frmMain.BackColor = [System.Drawing.Color]::LightSlateGray
    $frmMain.KeyPreview = $true
    $script:btnEnter = New-Object System.Windows.Forms.Button
    $btnEnter.Add_Click({Keystroke_Enter})
    $frmMain.AcceptButton = $btnEnter
    #$frmMain.Add_KeyDown({if($PSItem.KeyCode -eq "Enter"){Keystroke_Enter}})
    #$frmMain.Add_KeyDown({if($PSItem.KeyCode -eq "Escape"){$frmMain.Close()}})
    $frmMain.Add_Resize( { OnResize_frmMain $frmMain $EventArgs } )
    function OnResize_frmMain
    {
        $w = [int]$frmMain.Size.Width - 22
        $h = [int]$frmMain.Size.Height - 70
        $tabControl.Size = New-Object System.Drawing.Size($w,$h)
        $frmMenu.Width = $frmMain.Width
    }

    # ========================= ADD MENUS TO THE FORM =========================
    $frmMenu = New-Object System.Windows.Forms.MenuStrip
    $menuFile = New-Object System.Windows.Forms.ToolStripMenuItem
    $menuItemOpen = New-Object System.Windows.Forms.ToolStripMenuItem
    $menuItemSave = New-Object System.Windows.Forms.ToolStripMenuItem
    $menuItemExit = New-Object System.Windows.Forms.ToolStripMenuItem

    $menuAction = New-Object System.Windows.Forms.ToolStripMenuItem
    $menuItemGenerate = New-Object System.Windows.Forms.ToolStripMenuItem
    $menuItemImport = New-Object System.Windows.Forms.ToolStripMenuItem
    $menuItemExport = New-Object System.Windows.Forms.ToolStripMenuItem
    $menuItemClear = New-Object System.Windows.Forms.ToolStripMenuItem

    $menuScenario = New-Object System.Windows.Forms.ToolStripMenuItem
    $menuItemCustomer = New-Object System.Windows.Forms.ToolStripMenuItem
    $menuItemPreviewMultiSU = New-Object System.Windows.Forms.ToolStripMenuItem
    $menuItemPreviewAddSU = New-Object System.Windows.Forms.ToolStripMenuItem
    $menuItemPreviewExtStor = New-Object System.Windows.Forms.ToolStripMenuItem

    $frmMenu.Items.AddRange(@($menuFile,$menuAction,$menuScenario))
    $frmMenu.Location = New-Object System.Drawing.Point(0,0)
    $frmMenu.Name = "frmMenu"
    $frmMenu.AccessibleName = "MenuStrip"
    $frmMenu.AutoSize = $true
    $frmMenu.MinimumSize = New-Object System.Drawing.Size ($frmMain.Width,24)

    $menuFile.DropDownItems.AddRange(@($menuItemOpen,$menuItemSave,(New-Object System.Windows.Forms.ToolStripSeparator),$menuItemExit))
    $menuFile.Name = "menuFile"
    $menuFile.AutoSize = $true
    $menuFile.Text = $locString.menuFile

    $menuAction.DropDownItems.AddRange(@($menuItemImport,$menuItemExport,$menuItemGenerate,(New-Object System.Windows.Forms.ToolStripSeparator),$menuItemClear))
    $menuAction.Name = "menuAction"
    $menuAction.AutoSize = $true
    $menuAction.Text = $locString.menuAction

    $menuScenario.Name = "menuScenario"
    $menuScenario.AutoSize = $true
    $menuScenario.Text = $locString.menuScenario

    # ---------------------------- FILE MENU ITEMS ----------------------------
    $menuItemOpen.Name = "menuItemOpen"
    $menuItemOpen.AutoSize = $true
    $menuItemOpen.Text = $locString.menuOpen
    function OnClick_menuItemOpen($Sender,$e)
    {
        Write-Log -Message "$($MyInvocation.MyCommand.Name) - called"
        $script:openFile = Get-FileName -Filter 'JSON files | *.json'
        if($openFile)
        {
            Show-WaitForm "`n $($locString.msgPleaseWait)`n`n $($locString.msgWait)`n`n"
            Clear-ExistingData
            Remove-GeneratedTabs
            $script:invalidOpenData = @()
            $script:openData = Get-Content $openFile | ConvertFrom-Json
            $openScenario = $openData.scenario
            if(-not $openScenario)
            {
                Close-WaitForm
                $caption = $locString.capError
                $message = $locString.msgInvalidOpenFile
                [System.Windows.Forms.MessageBox]::Show($message, $caption, [System.Windows.Forms.MessageBoxButtons]::OK)
                return
            }
            if($openScenario -eq "Lab")
            {
                Close-WaitForm
                $caption = $locString.capError
                $message = $locString.msgLabNotSupported
                [System.Windows.Forms.MessageBox]::Show($message, $caption, [System.Windows.Forms.MessageBoxButtons]::OK)
                return
            }
            $openVersion = $openData.Version
            if([version](Get-FileVersionInfo) -lt [version]$openVersion)
            {
                Write-Log -Message "$($MyInvocation.MyCommand.Name) - WARNING: the file opened is from a newer version of this application" -Force
            }
            switch ($openScenario)
            {
                "Customer"
                {
                    OnClick_menuItemCustomer
                    if([int]$openData.tabNetwork.txtScaleUnitCount -gt 1)
                    {
                        if(-not($EnableExperimental))
                        {
                            Set-Experimental
                            Enable-PermitNetworks
                        }
                        $txtScaleUnitCount.Enabled = $true
                        $txtScaleUnitCount.ReadOnly = $false
                        OnClick_menuItemPreviewMultiSU
                    }
                    if($openData.extendedstorage -eq "true")
                    {
                        if(-not($EnableExperimental))
                        {
                            Set-Experimental
                            Enable-PermitNetworks
                        }
                        OnClick_menuItemPreviewExtStor
                    }
                }
                "AddRack"
                {
                    if(-not($EnableExperimental))
                    {
                        Set-Experimental
                        Enable-PermitNetworks
                    }
                    Close-WaitForm
                    [System.Windows.Forms.MessageBox]::Show($locString.msgAddSUPreview, $locString.capAttention, [System.Windows.Forms.MessageBoxButtons]::OK)
                    Set-AddRack
                }
            }
            $script:openUplinkSpeed = $null
            Update-SwitchVendor
            Write-OpenDataToTab -TabName 'tabCustomer'
            Write-OpenDataToTab -TabName 'tabNetwork'
            Write-OpenDataToTab -TabName 'tabBorder'
            Invoke-RefreshBorderBGP -BorderCount $txtBorderCount.Text
            Invoke-Command -ScriptBlock $lstRoutingMethod_Changed

            $script:retainSpineSettings = $true
            $currentTORModel = $lstTORModel.SelectedItem
            $currentTORFirmware = $lstTORFirmware.SelectedItem
            $currentBMCModel = $lstBMCModel.SelectedItem
            $currentBMCFirmware = $lstBMCFirmware.SelectedItem
            $currentSpineModel = $lstSpineModel.SelectedItem
            $currentSpineFirmware = $lstSpineFirmware.SelectedItem
            Invoke-Command -ScriptBlock $txtScaleUnitCount_Changed
            $script:retainSpineSettings = $false
            if($lstTORModel.Items -contains $currentTORModel)
            {
                $lstTORModel.SelectedItem = $currentTORModel
            }
            else
            {
                $lstTORModel.SelectedIndex = -1
            }
            if($lstBMCModel.Items -contains $currentBMCModel)
            {
                $lstBMCModel.SelectedItem = $currentBMCModel
            }
            else
            {
                $lstBMCModel.SelectedIndex = -1
            }
            if($lstSpineModel.Items -contains $currentSpineModel)
            {
                $lstSpineModel.SelectedItem = $currentSpineModel
            }
            else
            {
                $lstSpineModel.SelectedIndex = -1
            }
            if($lstTORFirmware.Items -contains $currentTORFirmware)
            {
                $lstTORFirmware.SelectedItem = $currentTORFirmware
            }
            else
            {
                $lstTORFirmware.SelectedIndex = -1
            }
            if($lstBMCFirmware.Items -contains $currentBMCFirmware)
            {
                $lstBMCFirmware.SelectedItem = $currentBMCFirmware
            }
            else
            {
                $lstBMCFirmware.SelectedIndex = -1
            }
            if($lstSpineFirmware.Items -contains $currentSpineFirmware)
            {
                $lstSpineFirmware.SelectedItem = $currentSpineFirmware
            }
            else
            {
                $lstSpineFirmware.SelectedIndex = -1
            }

            [int]$suCount = $txtScaleUnitCount.Text
            for($i = 1; $i -le $suCount; $i++)
            {
                $tabName = "tabSU$i"
                Write-Log -Message "$($MyInvocation.MyCommand.Name) - process TabName '$($tabName)'"
                $tabControl.SelectedTab = $tabControl.TabPages[$tabName]
                ($openData.$tabName | Get-Member -Type NoteProperty).Name | ForEach-Object `
                {
                    if($openData.$tabName.$PSItem)
                    {
                        if(($scaleUnitTabs.$tabName.Objects.$PSItem -is [System.Windows.Forms.TextBox]) -and ($scaleUnitTabs.$tabName.Objects.$PSItem.Name -notlike "*Size"))
                        {
                            if($PSItem -match "TORASN")
                            {
                                if($lstSwitchVendor -ne 'msftcisco')
                                {
                                    $scaleUnitTabs.$tabName.Objects.$PSItem.Text = $openData.$tabName.$PSItem
                                    Write-Log -Message "$($MyInvocation.MyCommand.Name) - set control named '$($PSItem)' to '$($openData.$TabName.$PSItem)'"
                                    $validationEvent = Get-Variable -Name ($scaleUnitTabs.$tabName.Objects.$PSItem.Name + '_Changed') -ErrorAction SilentlyContinue
                                    if($validationEvent)
                                    {
                                        Invoke-Command -ScriptBlock $validationEvent.Value
                                    }
                                }
                            }
                            else
                            {
                                $scaleUnitTabs.$tabName.Objects.$PSItem.Text = $openData.$tabName.$PSItem
                                Write-Log -Message "$($MyInvocation.MyCommand.Name) - set control named '$($PSItem)' to '$($openData.$TabName.$PSItem)'"
                                $validationEvent = Get-Variable -Name ($scaleUnitTabs.$tabName.Objects.$PSItem.Name + '_Changed') -ErrorAction SilentlyContinue
                                if($validationEvent)
                                {
                                    Invoke-Command -ScriptBlock $validationEvent.Value
                                }
                            }
                        }
                        elseif($scaleUnitTabs.$tabName.Objects.$PSItem -is [System.Windows.Forms.ComboBox])
                        {
                            $scaleUnitTabs.$tabName.Objects.$PSItem.SelectedItem = $openData.$tabName.$PSItem
                        }
                    }
                }
            }
            Invoke-RefreshTotalNodeCount
            Invoke-Command -ScriptBlock $txtBorderCount_Changed
            if([int]$txtScaleUnitCount.Text -gt 1)
            {
                $txtScaleUnitCount.Enabled = $true
                $txtScaleUnitCount.ReadOnly = $false
            }
            if((-not [string]::IsNullOrEmpty($script:openUplinkSpeed)) -and ($true -eq $lstUplinkSpeed.Enabled))
            {
                if($lstUplinkSpeed.Items -contains $script:openUplinkSpeed)
                {
                    $lstUplinkSpeed.SelectedItem = $script:openUplinkSpeed
                }
            }
            if(($tabControl.TabPages["tabPartner"]) -and ($openData.tabPartner))
            {
                Write-OpenDataToTab -TabName 'tabPartner'
            }
            Move-PartnerTab
            Close-WaitForm
            if($scenario -eq "AddRack")
            {
                $tabControl.SelectedTab = $tabControl.TabPages["tabNetwork"]
            }
            else
            {
                $tabControl.SelectedTab = $tabControl.TabPages["tabCustomer"]
            }
            if($invalidOpenData.Count -ne 0)
            {
                $caption = $locString.capWarning
                $message = $locString.msgInvalidOpenData
                [System.Windows.Forms.MessageBox]::Show($message, $caption, [System.Windows.Forms.MessageBoxButtons]::OK)
            }
        }
        else
        {
            Write-Log -Message "$($MyInvocation.MyCommand.Name) - aborted as no file name was selected"
        }
    }
    $menuItemOpen.Add_Click( { OnClick_menuItemOpen $menuItemOpen $EventArgs } )

    $menuItemSave.Name = "menuItemSave"
    $menuItemSave.AutoSize = $true
    $menuItemSave.Text = $locString.menuSave
    function OnClick_menuItemSave($Sender,$e)
    {
        Write-Log -Message "$($MyInvocation.MyCommand.Name) - called"
        $saveFile = Get-SaveFileName
        if($saveFile)
        {
            Show-WaitForm "`n $($locString.msgPleaseWait)`n`n $($locString.msgWait)`n`n"
            $script:saveData = @{}
            $saveData.Add('scenario',$scenario)
            $saveData.Add('version',(Get-FileVersionInfo))
            $saveData.Add('extendedstorage',$isExtStor)
            foreach($tabName in @('tabCustomer','tabNetwork','tabBorder'))
            {
                $thisObj = @{}
                $thisTab = $tabControl.TabPages[$tabName]
                foreach($ctrl in $thisTab.Controls)
                {
                    if((-not $ctrl.Name) -or ($ctrl.Name -like "txtPermitNetworks*"))
                    {
                        continue
                    }
                    if($ctrl.Name -like "lstPermitNetworks*")
                    {
                        [string]$listString = $ctrl.Items -join ','
                        $thisObj.Add($ctrl.Name,$listString)
                    }
                    else
                    {
                        if(($ctrl -is [System.Windows.Forms.TextBox]) -or ($ctrl -is [System.Windows.Forms.RichTextBox]))
                        {
                            $thisObj.Add($ctrl.Name,$ctrl.Text)
                        }
                        elseif($ctrl -is [System.Windows.Forms.ComboBox])
                        {
                            $thisObj.Add($ctrl.Name,$ctrl.SelectedItem)
                        }
                    }
                }
                $saveData.Add($tabName,$thisObj)
            }
            for($i=1; $i -le 4; $i++)
            {
                $thisTab = "tabSU$i"
                if($tabControl.TabPages[$thisTab])
                {
                    $thisObj = @{}
                    $scaleUnitTabs.$thisTab.Objects.Keys | ForEach-Object `
                    {
                        if($scaleUnitTabs.$thisTab.Objects.$PSItem -is [System.Windows.Forms.TextBox])
                        {
                            $thisObj.Add($PSItem,$scaleUnitTabs.$thisTab.Objects.$PSItem.Text)
                        }
                        elseif($scaleUnitTabs.$thisTab.Objects.$PSItem -is [System.Windows.Forms.ComboBox])
                        {
                            $thisObj.Add($PSItem,$scaleUnitTabs.$thisTab.Objects.$PSItem.SelectedItem)
                        }
                    }
                    $saveData.Add($thisTab,$thisObj)
                }
            }
            if($tabControl.TabPages["tabPartner"])
            {
                $thisObj = @{}
                $tabPartner.Controls | ForEach-Object `
                {
                    if($PSItem -is [System.Windows.Forms.TextBox])
                    {
                        $thisObj.Add($PSItem.Name,$PSItem.Text)
                    }
                    elseif($PSItem -is [System.Windows.Forms.ComboBox])
                    {
                        $thisObj.Add($PSItem.Name,$PSItem.SelectedItem)
                    }
                }
                $saveData.Add("tabPartner",$thisObj)
            }
            $saveData | ConvertTo-Json -Depth 4 | Set-Content $saveFile
            $message = ($locString.msgSaveSucceeded -f $saveFile)
            $caption = $locString.capSuccess
            Close-WaitForm
            [System.Windows.Forms.MessageBox]::Show($message, $caption, [System.Windows.Forms.MessageBoxButtons]::OK)
        }
        else
        {
            Write-Log -Message "$($MyInvocation.MyCommand.Name) - aborted as no save file name was given"
        }
    }
    $menuItemSave.Add_Click( { OnClick_menuItemSave $menuItemSave $EventArgs } )

    $menuItemExit.Name = $locString.menuExit
    $menuItemExit.AutoSize = $true
    $menuItemExit.Text = $locString.menuExit
    function OnClick_menuItemExit($Sender,$e)
    {
        Write-Log -Message "Exit"
        $frmMain.Close()
        [void]$frmMain.Dispose()
        [System.GC]::Collect()
    }
    $menuItemExit.Add_Click( { OnClick_menuItemExit $menuItemExit $EventArgs } )

    # --------------------------- ACTION MENU ITEMS ---------------------------
    $menuItemImport.Name = "menuItemImport"
    $menuItemImport.AutoSize = $true
    $menuItemImport.Text = $locString.menuImport
    function OnClick_menuItemImport($Sender,$e)
    {
        try
        {
            Invoke-ImportDeploymentData
        }
        catch
        {
            throw $PSItem
        }
    }
    $menuItemImport.Add_Click( { OnClick_menuItemImport $menuItemExport $EventArgs } )

    $menuItemExport.Name = "menuItemExport"
    $menuItemExport.AutoSize = $true
    $menuItemExport.Text = $locString.menuExport
    function OnClick_menuItemExport($Sender,$e)
    {
        try
        {
            Invoke-ExportDeploymentData
        }
        catch
        {
            throw $PSItem
        }
    }
    $menuItemExport.Add_Click( { OnClick_menuItemExport $menuItemExport $EventArgs } )

    $menuItemGenerate.Name = "menuItemGenerate"
    $menuItemGenerate.AutoSize = $true
    $menuItemGenerate.Text = $locString.menuGenerate
    function OnClick_menuItemGenerate($Sender,$e)
    {
        try
        {
            Invoke-GenerateDeploymentData
        }
        catch
        {
            throw $PSItem
        }
    }
    $menuItemGenerate.Add_Click( { OnClick_menuItemGenerate $menuItemGenerate $EventArgs } )

    $menuItemClear.Name = "menuItemClear"
    $menuItemClear.AutoSize = $true
    $menuItemClear.Text = $locString.menuClear
    function OnClick_menuItemClear($Sender,$e)
    {
        Clear-ExistingData
    }
    $menuItemClear.Add_Click( { OnClick_menuItemClear $menuItemClear $EventArgs } )

    # -------------------------- SCENARIO MENU ITEMS --------------------------
    $menuItemCustomer.Name = "menuItemCustomer"
    $menuItemCustomer.AutoSize = $true
    $menuItemCustomer.Text = $locString.menuCustomer
    $menuItemCustomer.Checked = $true
    function OnClick_menuItemCustomer($Sender,$e)
    {
        Write-Log -Message "$($MyInvocation.MyCommand.Name) - current scenario: $($scenario)"
        Disable-ExtendedStorage
        $menuScenario.DropDownItems | Foreach-Object `
        {
            if($PSItem.Name -eq $menuItemCustomer.Name)
            {
                $PSItem.Checked = $true
            }
            else
            {
                if($PSItem -is [System.Windows.Forms.ToolStripMenuItem])
                {
                    $PSItem.Checked = $false
                }
            }
        }
        if($scenario -ne "Customer")
        {
            Clear-ExistingData
            foreach($tab in $script:savedTabs)
            {
                $tabControl.TabPages.Add($tab)
            }
            $script:savedTabs = @()
            foreach($tab in $script:savedTabs)
            {
                $tabControl.TabPages.Remove($tab)
            }
            Remove-GeneratedTabs
            Move-PartnerTab
        }
        $tabControl.TabPages['tabCustomer'].Enabled = $true
        $script:scenario = "Customer"
        $txtScaleUnitCount.Enabled = $false
        $txtScaleUnitCount.BackColor = $defaultBackColor
        $txtScaleUnitCount.ForeColor = $defaultForeColor
        $txtScaleUnitCount.ReadOnly = $true
        $txtScaleUnitCount.Text = $defaultSUCount
        Invoke-Command -ScriptBlock $txtScaleUnitCount_Changed
    }
    $menuItemCustomer.Add_Click( { OnClick_menuItemCustomer $menuItemCustomer $EventArgs } )

    $menuItemPreviewMultiSU.Name = "menuItemPreviewMultiSU"
    $menuItemPreviewMultiSU.AutoSize = $true
    $menuItemPreviewMultiSU.Text = $locString.menuMultiSU
    function OnClick_menuItemPreviewMultiSU($Sender,$e)
    {
        Write-Log -Message "$($MyInvocation.MyCommand.Name) - current scenario: $($scenario)"
        Remove-GeneratedTabs
        Disable-ExtendedStorage
        Restore-SelectedSwitch
        $menuScenario.DropDownItems | Foreach-Object `
        {
            if($PSItem.Name -eq $menuItemPreviewMultiSU.Name)
            {
                $PSItem.Checked = $true
            }
            else
            {
                if($PSItem -is [System.Windows.Forms.ToolStripMenuItem])
                {
                    $PSItem.Checked = $false
                }
            }
        }
        if($scenario -ne "Customer")
        {
            Clear-ExistingData
            foreach($tab in $script:savedTabs)
            {
                $tabControl.TabPages.Add($tab)
            }
            $script:savedTabs = @()
            foreach($tab in $script:savedTabs)
            {
                $tabControl.TabPages.Remove($tab)
            }
        }
        $tabControl.TabPages['tabCustomer'].Enabled = $true
        $script:scenario = "Customer"
        $txtScaleUnitCount.Enabled = $true
        $txtScaleUnitCount.ReadOnly = $false
        Close-WaitForm
        [System.Windows.Forms.MessageBox]::Show($locString.msgMultiSUPreview, $locString.capAttention, [System.Windows.Forms.MessageBoxButtons]::OK)
        Move-PartnerTab
    }
    $menuItemPreviewMultiSU.Add_Click( { OnClick_menuItemPreviewMultiSU $menuItemPreviewMultiSU $EventArgs } )

    $menuItemPreviewAddSU.Name = "menuItemPreviewAddSU"
    $menuItemPreviewAddSU.AutoSize = $true
    $menuItemPreviewAddSU.Text = $locString.menuAddSU
    function Set-AddRack
    {
        Write-Log -Message "$($MyInvocation.MyCommand.Name) - called"
        Disable-ExtendedStorage
        $menuScenario.DropDownItems | Foreach-Object `
        {
            if($PSItem.Name -eq $menuItemPreviewAddSU.Name)
            {
                $PSItem.Checked = $true
            }
            else
            {
                if($PSItem -is [System.Windows.Forms.ToolStripMenuItem])
                {
                    $PSItem.Checked = $false
                }
            }
        }
        Remove-GeneratedTabs
        if($scenario -ne "Customer")
        {
            Clear-ExistingData
            foreach($tab in $script:savedTabs)
            {
                $tabControl.TabPages.Add($tab)
            }
            $script:savedTabs = @()
            foreach($tab in $script:savedTabs)
            {
                $tabControl.TabPages.Remove($tab)
            }
        }
        $script:scenario = "AddRack"
        $tabControl.TabPages['tabCustomer'].Enabled = $false
        $txtScaleUnitCount.Enabled = $true
        $txtScaleUnitCount.ReadOnly = $false
        Clear-ExistingData
        Write-Log -Message "$($MyInvocation.MyCommand.Name) - completed"
    }
    function OnClick_menuItemPreviewAddSU($Sender,$e)
    {
        Write-Log -Message "$($MyInvocation.MyCommand.Name) - current scenario: $($scenario)"
        if($scenario -ne "AddRack")
        {
            Disable-ExtendedStorage
            Set-AddRack
            Close-WaitForm
            [System.Windows.Forms.MessageBox]::Show($locString.msgAddSUPreview, $locString.capAttention, [System.Windows.Forms.MessageBoxButtons]::OK)
            [string]$importJsonFile = Get-FileName -Filter "AzureStackStampInformation (*.json)| *.json| All files (*.*) | *.*"
            if($importJsonFile)
            {
                Write-Log -Message "$($MyInvocation.MyCommand.Name) - file selected: '$($importJsonFile)'"
                $script:importData = Get-Content $importJsonFile -Raw | ConvertFrom-Json
                Show-WaitForm "`n AddRack : $($locString.msgImportExisting)`n`n$($locString.msgWait)`n`n"
                $txtScaleUnitCount.Text = '2'
                Invoke-Command -ScriptBlock $txtScaleUnitCount_Changed
                if($importData.Prefix)
                {
                    Write-Log -Message "$($MyInvocation.MyCommand.Name) - importing 'Prefix' setting"
                    $txtPhysicalPrefix.Text = $importData.Prefix
                }
                if($importData.CompanyName)
                {
                    Write-Log -Message "$($MyInvocation.MyCommand.Name) - importing 'CompanyName' setting"
                    $txtCompanyName.Text = $importData.CompanyName
                }
                if($importData.RegionName)
                {
                    Write-Log -Message "$($MyInvocation.MyCommand.Name) - importing 'RegionName' setting"
                    $txtRegionName.Text = $importData.RegionName
                }
                if($importData.ExternalDNSIPAddress01 -or $importData.ExternalDNSIPAddress02)
                {
                    Write-Log -Message "$($MyInvocation.MyCommand.Name) - importing 'DNSForwarder' setting"
                    [string]$addList = $importData.ExternalDNSIPAddress01
                    if($importData.ExternalDNSIPAddress02)
                    {
                        $addList += ',' + $importData.ExternalDNSIPAddress02
                    }
                    $txtDNSForwarder.Text = $addList
                }
                if($importData.NumberOfNodes)
                {
                    Write-Log -Message "$($MyInvocation.MyCommand.Name) - importing 'SU01-NodeCount' setting"
                    $scaleUnitTabs.tabSU1.Objects.txtNodeCount.Text = $importData.NumberOfNodes
                    Invoke-RefreshTotalNodeCount
                }
            }
            else
            {
                $message = $locString.msgNoFileSelected
                $caption = $locString.capWarning
                Close-WaitForm
                [System.Windows.Forms.MessageBox]::Show($message, $caption, [System.Windows.Forms.MessageBoxButtons]::OK)
            }
            Move-PartnerTab
        }
        Close-WaitForm
    }
    $menuItemPreviewAddSU.Add_Click( { OnClick_menuItemPreviewAddSU $menuItemPreviewAddSU $EventArgs } )

    $menuItemPreviewExtStor.Name = "menuItemPreviewExtStor"
    $menuItemPreviewExtStor.AutoSize = $true
    $menuItemPreviewExtStor.Text = $locString.menuExtStor
    function Set-ExtStor
    {
        Write-Log -Message "$($MyInvocation.MyCommand.Name) - called"
        Enable-ExtendedStorage
        $menuScenario.DropDownItems | Foreach-Object `
        {
            if($PSItem.Name -eq $menuItemPreviewExtStor.Name)
            {
                $PSItem.Checked = $true
            }
            else
            {
                if($PSItem -is [System.Windows.Forms.ToolStripMenuItem])
                {
                    $PSItem.Checked = $false
                }
            }
        }
        Remove-GeneratedTabs
        if($scenario -ne "Customer")
        {
            Clear-ExistingData
            foreach($tab in $script:savedTabs)
            {
                $tabControl.TabPages.Add($tab)
            }
            $script:savedTabs = @()
            foreach($tab in $script:savedTabs)
            {
                $tabControl.TabPages.Remove($tab)
            }
            Remove-GeneratedTabs
            Move-PartnerTab
        }
        $tabControl.TabPages['tabCustomer'].Enabled = $true
        $script:scenario = "Customer"
        Write-Log -Message "$($MyInvocation.MyCommand.Name) - completed"
    }
    function OnClick_menuItemPreviewExtStor($Sender,$e)
    {
        Write-Log -Message "$($MyInvocation.MyCommand.Name) - current scenario: $($scenario)"
        Close-WaitForm
        [System.Windows.Forms.MessageBox]::Show($locString.msgExtendStoragePreview, $locString.capAttention, [System.Windows.Forms.MessageBoxButtons]::OK)
        Set-ExtStor
        if(-not $isExtStor)
        {
            OnClick_menuItemCustomer
        }
    }
    $menuItemPreviewExtStor.Add_Click( { OnClick_menuItemPreviewExtStor $menuItemPreviewExtStor $EventArgs } )

    # ---------------------------- MAIN FORM TABS -----------------------------
    $script:tabControl = New-Object System.Windows.Forms.TabControl
    $tabControl.BackColor = [System.Drawing.Color]::SlateGray
    $tabControl.Alignment = 'Top'
    $tabControl.Location = New-Object System.Drawing.Point(2,28)
    $tabControl.Multiline = $true
    $tabControl.Name = 'TabControl'
    $tabControl.SelectedIndex = 0
    $tabControl.AutoSize = $true
    $tabControl.MinimumSize = New-Object System.Drawing.Size(([int]$frmMain.Size.Width - 22),([int]$frmMain.Size.Height - 70))
    $tabControl.Add_SelectedIndexChanged( { OnChange_tabControl $tabControl $EventArgs } )
    function OnChange_tabControl
    {
        if($tabBadInput.Count -ne 0)
        {
            foreach($ctrl in $tabBadInput)
            {
                $ctrl.Text = [string]::Empty
                $ctrl.ForeColor = $defaultForeColor
                $ctrl.BackColor = $defaultBackColor
            }
            [System.Collections.ArrayList]$script:tabBadInput = @()
        }
    }

    $script:tabCustomer = New-Object System.Windows.Forms.TabPage
    $typTab = $tabCustomer.GetType()
    $prpTab = $typTab.GetProperty('DoubleBuffered',('Instance','NonPublic'))
    $prpTab.SetValue($tabCustomer, $true, $null)
    $tabCustomer.Name = "tabCustomer"
    $tabCustomer.Text = $locString.tabCustomer

    $script:tabNetwork = New-Object System.Windows.Forms.TabPage
    $typTab = $tabNetwork.GetType()
    $prpTab = $typTab.GetProperty('DoubleBuffered',('Instance','NonPublic'))
    $prpTab.SetValue($tabNetwork, $true, $null)
    $tabNetwork.Name = "tabNetwork"
    $tabNetwork.Text = $locString.tabNetwork

    $script:tabBorder = New-Object System.Windows.Forms.TabPage
    $typTab = $tabBorder.GetType()
    $prpTab = $typTab.GetProperty('DoubleBuffered',('Instance','NonPublic'))
    $prpTab.SetValue($tabBorder, $true, $null)
    $tabBorder.Name = "tabBorder"
    $tabBorder.Text = $locString.tabBorder

    $tabControl.Controls.Add($tabCustomer)
    $tabControl.Controls.Add($tabNetwork)
    $tabControl.Controls.Add($tabBorder)
    Add-ScaleUnitTab -ID 1

    # ============================= CUSTOMER TAB ==============================
    $row = 20

    # ----------------------------- DOCUMENT LINK -----------------------------
    $script:lnkCustomerDoc = New-Object System.Windows.Forms.LinkLabel
    $lnkCustomerDoc.Location = New-Object System.Drawing.Size(680,($row+2))
    $lnkCustomerDoc.Size = New-Object System.Drawing.Size(300,20)
    $lnkCustomerDoc.Text = $locString.customerLinkText
    $lnkCustomerDoc.Font = $defaultLabelFont
    $lnkCustomerDoc.Add_Click({Start-Process -FilePath $locString.customerLink})
    $tabCustomer.Controls.Add($lnkCustomerDoc)

    # --------------------------- ON-HOVER TOOLTIPS ---------------------------
    $tooltip1 = New-Object System.Windows.Forms.ToolTip
    $tooltip1.ShowAlways = $true
    $tooltip1.ReshowDelay = 500
    $tooltip1.AutoPopDelay = 10000
    $tooltip1.IsBalloon = $true
    $ShowHelp = `
    {
        # Display popup help - Each value is the name of a control on the form.
        switch -regex ($this.Name)
        {
            "txtTimeServer"
            {
                $tip = $locString.tipTimeServer
                break
            }
            "txtDNSForwarder"
            {
                $tip = $locString.tipDNSForwarder
                break
            }
            "txtSyslogServer"
            {
                $tip = $locString.tipSyslogServer
                break
            }
            "txtPermitNetworks"
            {
                $tip = $locString.tipPermitNetworks
                break
            }
            "btnDNSAdd|btnPermitNetworksAdd"
            {
                $tip = $locString.tipAddBtn
                break
            }
            "btnDNSDel|btnPermitNetworksDel"
            {
                $tip = $locString.tipDelBtn
                break
            }
            "txtADFSForestFQDN"
            {
                $tip = $locString.tipADFSForestFQDN
                break
            }
            "txtADFSProviderName"
            {
                $tip = $locString.tipADFSProviderName
                break
            }
            "txtADFSMetadataUri"
            {
                $tip = $locString.tipADFSMetadataUri
                break
            }
            default
            {
                Write-Log -Message "ShowHelp encountered an unhandled property named '$($this.Name)'"
                break
            }
        }
        $tooltip1.SetToolTip($this,$tip)
    }

    # ---------------------------- AZURE IDENTITY -----------------------------
    $script:lblAzureIdentity = New-Object System.Windows.Forms.Label
    $lblAzureIdentity.Location = New-Object System.Drawing.Size($lblLeft,($row))
    $lblAzureIdentity.AutoSize = $true
    $lblAzureIdentity.Text = $locString.lblAzureIdentity
    $lblAzureIdentity.Font = [System.Drawing.Font]::New("Microsoft Sans Serif", 12, [System.Drawing.FontStyle]::Bold)
    $tabCustomer.Controls.Add($lblAzureIdentity)

    $row += 40

    # --------------------------- IDENTITY STORE ------------------------------
    $script:lblIdentityStore = New-Object System.Windows.Forms.Label
    $lblIdentityStore.Location = New-Object System.Drawing.Size($lblLeft,($row+2))
    $lblIdentityStore.MaximumSize = New-Object System.Drawing.Size($lblMaxSize)
    $lblIdentityStore.AutoSize = $true
    $lblIdentityStore.Font = $defaultLabelFont
    $lblIdentityStore.Text = $locString.lblIdentityStore
    $tabCustomer.Controls.Add($lblIdentityStore)

    $script:lstIdentityStore = New-Object System.Windows.Forms.ComboBox
    $lstIdentityStore.DropDownStyle = [System.Windows.Forms.ComboBoxStyle]::DropDownList
    $lstIdentityStore.Location = New-Object System.Drawing.Size($txtLeft,$row)
    $lstIdentityStore.Size = New-Object System.Drawing.Size($txtSize)
    $lstIdentityStore.Font = $defaultLabelFont
    $lstIdentityStore.Name = 'lstIdentityStore'
    [void]$lstIdentityStore.Items.Clear()
    $arrIdentityStore | Foreach-Object `
    {
        [void]$lstIdentityStore.Items.Add($PSItem)
    }
    $lstIdentityStore.SelectedIndex = -1
    $tabCustomer.Controls.Add($lstIdentityStore)
    $lstIdentityStore_Changed = `
    {
        switch ($lstIdentityStore.SelectedItem)
        {
            "Azure Active Directory"
            {
                $lblAADTenantName.Enabled = $true
                $lblAADTenantName.Visible = $true
                $txtAADTenantName.Enabled = $true
                $txtAADTenantName.Visible = $true
                $lblAzureEnvironment.Enabled = $true
                $lblAzureEnvironment.Visible = $true
                $lstAzureEnvironment.Enabled = $true
                $lstAzureEnvironment.Visible = $true
                # Disable ADFS objects
                $lblADFSForestFQDN.Enabled = $false
                $lblADFSForestFQDN.Visible = $false
                $txtADFSForestFQDN.Text = [string]::Empty
                $txtADFSForestFQDN.Enabled = $false
                $txtADFSForestFQDN.Visible = $false
                $lblADFSProviderName.Enabled = $false
                $lblADFSProviderName.Visible = $false
                $txtADFSProviderName.Text = [string]::Empty
                $txtADFSProviderName.Enabled = $false
                $txtADFSProviderName.Visible = $false
                $lblADFSMetadataUri.Enabled = $false
                $lblADFSMetadataUri.Visible = $false
                $txtADFSMetadataUri.Text = [string]::Empty
                $txtADFSMetadataUri.Enabled = $false
                $txtADFSMetadataUri.Visible = $false
            }
            "Active Directory Federation Services"
            {
                $lblADFSForestFQDN.Enabled = $true
                $lblADFSForestFQDN.Visible = $true
                $txtADFSForestFQDN.Enabled = $true
                $txtADFSForestFQDN.Visible = $true
                $lblADFSProviderName.Enabled = $true
                $lblADFSProviderName.Visible = $true
                $txtADFSProviderName.Enabled = $true
                $txtADFSProviderName.Visible = $true
                $lblADFSMetadataUri.Enabled = $true
                $lblADFSMetadataUri.Visible = $true
                $txtADFSMetadataUri.Enabled = $true
                $txtADFSMetadataUri.Visible = $true
                # Disable AAD objects
                $lblAADTenantName.Enabled = $false
                $lblAADTenantName.Visible = $false
                $txtAADTenantName.Enabled = $false
                $txtAADTenantName.Visible = $false
                $txtAADTenantName.Text = [string]::Empty
                $lblAzureEnvironment.Enabled = $false
                $lblAzureEnvironment.Visible = $false
                $lstAzureEnvironment.Enabled = $false
                $lstAzureEnvironment.Visible = $false
                $lstAzureEnvironment.SelectedIndex = -1
            }
            default
            {
            }
        }
    }
    $lstIdentityStore.Add_SelectedIndexChanged($lstIdentityStore_Changed)

    $row += 25

    # ---------------------------- TENANT NAME --------------------------------
    $script:lblAADTenantName = New-Object System.Windows.Forms.Label
    $lblAADTenantName.Location = New-Object System.Drawing.Size($lblLeft,($row+2))
    $lblAADTenantName.MaximumSize = New-Object System.Drawing.Size($lblMaxSize)
    $lblAADTenantName.AutoSize = $true
    $lblAADTenantName.Font = $defaultLabelFont
    $lblAADTenantName.Text = $locString.lblAADTenantName
    $lblAADTenantName.Enabled = $false
    $lblAADTenantName.Visible = $false
    $tabCustomer.Controls.Add($lblAADTenantName)

    $script:txtAADTenantName = New-Object System.Windows.Forms.TextBox
    $txtAADTenantName.Location = New-Object System.Drawing.Size($txtLeft,$row)
    $txtAADTenantName.Size = New-Object System.Drawing.Size($txtSize)
    $txtAADTenantName.Font = $defaultLabelFont
    $txtAADTenantName.Name = 'txtAADTenantName'
    $txtAADTenantName.MaxLength = $lenText
    $txtAADTenantName.Enabled = $false
    $txtAADTenantName.Visible = $false
    $tabCustomer.Controls.Add($txtAADTenantName)
    $txtAADTenantName_Changed = `
    {
        if(-not [string]::IsNullOrEmpty($txtAADTenantName.Text))
        {
            $txtAADTenantName.Text = ($txtAADTenantName.Text).Trim()
            if($txtAADTenantName.Text -notmatch $validDomain)
            {
                $txtAADTenantName.ForeColor = $defaultFailColor
                Close-WaitForm
                [System.Windows.Forms.MessageBox]::Show($locString.errDomainInvalid, $locString.capValidationError, [System.Windows.Forms.MessageBoxButtons]::OK)
            }
            else
            {
                $txtAADTenantName.ForeColor = $defaultForeColor
            }
            Update-TextBox $txtAADTenantName
        }
        else
        {
            $txtAADTenantName.ForeColor = $defaultForeColor
        }
    }
    $txtAADTenantName.Add_Leave($txtAADTenantName_Changed)

    # ----------------------------- ADFS FQDN ---------------------------------
    $script:lblADFSForestFQDN = New-Object System.Windows.Forms.Label
    $lblADFSForestFQDN.Location = New-Object System.Drawing.Size($lblLeft,($row+2))
    $lblADFSForestFQDN.MaximumSize = New-Object System.Drawing.Size($lblMaxSize)
    $lblADFSForestFQDN.AutoSize = $true
    $lblADFSForestFQDN.Font = $defaultLabelFont
    $lblADFSForestFQDN.Text = $locString.lblADFSForestFQDN
    $lblADFSForestFQDN.Enabled = $false
    $lblADFSForestFQDN.Visible = $false
    $tabCustomer.Controls.Add($lblADFSForestFQDN)

    $script:txtADFSForestFQDN = New-Object System.Windows.Forms.TextBox
    $txtADFSForestFQDN.Location = New-Object System.Drawing.Size($txtLeft,$row)
    $txtADFSForestFQDN.Size = New-Object System.Drawing.Size($txtSize)
    $txtADFSForestFQDN.Font = $defaultLabelFont
    $txtADFSForestFQDN.Name = 'txtADFSForestFQDN'
    $txtADFSForestFQDN.MaxLength = $lenDomain
    $txtADFSForestFQDN.Enabled = $false
    $txtADFSForestFQDN.Visible = $false
    $txtADFSForestFQDN.Add_MouseHover($ShowHelp)
    $tabCustomer.Controls.Add($txtADFSForestFQDN)
    $txtADFSForestFQDN_Changed = `
    {
        if(-not [string]::IsNullOrEmpty($txtADFSForestFQDN.Text))
        {
            $txtADFSForestFQDN.Text = ($txtADFSForestFQDN.Text).Trim()
            if($txtADFSForestFQDN.Text -notmatch $validDomain)
            {
                $txtADFSForestFQDN.ForeColor = $defaultFailColor
                Close-WaitForm
                [string[]]$detailErr = Get-DomainValidationError $txtADFSForestFQDN
                [string]$message = $locString.errDomainInvalid
                if($detailErr.Count -gt 0)
                {
                    $message += "`n`n$($locString.strDetails)`n"
                }
                foreach($item in $detailErr)
                {
                    $message += "`n$($item)"
                }
                [System.Windows.Forms.MessageBox]::Show($message, $locString.capWarning, [System.Windows.Forms.MessageBoxButtons]::OK)
            }
            else
            {
                $txtADFSForestFQDN.ForeColor = $defaultForeColor
            }
            Update-TextBox $txtADFSForestFQDN
        }
        else
        {
            $txtADFSForestFQDN.ForeColor = $defaultForeColor
        }
    }
    $txtADFSForestFQDN.Add_Leave($txtADFSForestFQDN_Changed)

    $row += 25

    # --------------------------- AZURE DIRECTORY -----------------------------
    $script:lblAzureEnvironment = New-Object System.Windows.Forms.Label
    $lblAzureEnvironment.Location = New-Object System.Drawing.Size($lblLeft,($row+2))
    $lblAzureEnvironment.MaximumSize = New-Object System.Drawing.Size($lblMaxSize)
    $lblAzureEnvironment.AutoSize = $true
    $lblAzureEnvironment.Font = $defaultLabelFont
    $lblAzureEnvironment.Text = $locString.lblAzureEnvironment
    $lblAzureEnvironment.Enabled = $false
    $lblAzureEnvironment.Visible = $false
    $tabCustomer.Controls.Add($lblAzureEnvironment)

    $script:lstAzureEnvironment = New-Object System.Windows.Forms.ComboBox
    $lstAzureEnvironment.DropDownStyle = [System.Windows.Forms.ComboBoxStyle]::DropDownList
    $lstAzureEnvironment.Location = New-Object System.Drawing.Size($txtLeft,$row)
    $lstAzureEnvironment.Size = New-Object System.Drawing.Size($txtSize)
    $lstAzureEnvironment.Font = $defaultLabelFont
    $lstAzureEnvironment.Name = 'lstAzureEnvironment'
    $lstAzureEnvironment.Enabled = $false
    $lstAzureEnvironment.Visible = $false
    [void]$lstAzureEnvironment.Items.Clear()
    $arrAzureEnvironment | Foreach-Object `
    {
        [void]$lstAzureEnvironment.Items.Add($PSItem)
    }
    $tabCustomer.Controls.Add($lstAzureEnvironment)

    # ----------------------------- ADFS NAME ---------------------------------
    $script:lblADFSProviderName = New-Object System.Windows.Forms.Label
    $lblADFSProviderName.Location = New-Object System.Drawing.Size($lblLeft,($row+2))
    $lblADFSProviderName.MaximumSize = New-Object System.Drawing.Size($lblMaxSize)
    $lblADFSProviderName.AutoSize = $true
    $lblADFSProviderName.Font = $defaultLabelFont
    $lblADFSProviderName.Text = $locString.lblADFSProviderName
    $lblADFSProviderName.Enabled = $false
    $lblADFSProviderName.Visible = $false
    $tabCustomer.Controls.Add($lblADFSProviderName)

    $script:txtADFSProviderName = New-Object System.Windows.Forms.TextBox
    $txtADFSProviderName.Location = New-Object System.Drawing.Size($txtLeft,$row)
    $txtADFSProviderName.Size = New-Object System.Drawing.Size($txtSize)
    $txtADFSProviderName.Font = $defaultLabelFont
    $txtADFSProviderName.Name = 'txtADFSProviderName'
    $txtADFSProviderName.MaxLength = $lenText
    $txtADFSProviderName.Enabled = $false
    $txtADFSProviderName.Visible = $false
    $txtADFSProviderName.Add_MouseHover($ShowHelp)
    $tabCustomer.Controls.Add($txtADFSProviderName)
    $txtADFSProviderName_Changed = `
    {
        if(-not [string]::IsNullOrEmpty($txtADFSProviderName.Text))
        {
            $txtADFSProviderName.Text = ($txtADFSProviderName.Text).Trim()
            # We are not doing any validation on this entered data at this time
            # $txtADFSProviderName.ForeColor = $defaultFailColor
            $txtADFSProviderName.ForeColor = $defaultForeColor
            Update-TextBox $txtADFSProviderName
        }
    }
    $txtADFSProviderName.Add_Leave($txtADFSProviderName_Changed)

    $row += 25

    # ------------------------- ADFS METADATA URI -----------------------------
    $script:lblADFSMetadataUri = New-Object System.Windows.Forms.Label
    $lblADFSMetadataUri.Location = New-Object System.Drawing.Size($lblLeft,($row+2))
    $lblADFSMetadataUri.MaximumSize = New-Object System.Drawing.Size($lblMaxSize)
    $lblADFSMetadataUri.AutoSize = $true
    $lblADFSMetadataUri.Font = $defaultLabelFont
    $lblADFSMetadataUri.Text = $locString.lblADFSMetadataUri
    $lblADFSMetadataUri.Enabled = $false
    $lblADFSMetadataUri.Visible = $false
    $tabCustomer.Controls.Add($lblADFSMetadataUri)

    $script:txtADFSMetadataUri = New-Object System.Windows.Forms.TextBox
    $txtADFSMetadataUri.Location = New-Object System.Drawing.Size($txtLeft,$row)
    $txtADFSMetadataUri.Size = New-Object System.Drawing.Size($txtSize)
    $txtADFSMetadataUri.Font = $defaultLabelFont
    $txtADFSMetadataUri.Name = 'txtADFSMetadataUri'
    $txtADFSMetadataUri.MaxLength = $lenURI
    $txtADFSMetadataUri.Enabled = $false
    $txtADFSMetadataUri.Visible = $false
    $txtADFSMetadataUri.Add_MouseHover($ShowHelp)
    $tabCustomer.Controls.Add($txtADFSMetadataUri)
    $txtADFSMetadataUri_Changed = `
    {
        if(-not [string]::IsNullOrEmpty($txtADFSMetadataUri.Text))
        {
            $txtADFSMetadataUri.Text = ($txtADFSMetadataUri.Text).Trim()
            if(-not(Get-IsValidURI -URI $txtADFSMetadataUri.Text))
            {
                $txtADFSMetadataUri.ForeColor = $defaultFailColor
                Close-WaitForm
                [System.Windows.Forms.MessageBox]::Show($locString.errURIInvalid, $locString.capValidationError, [System.Windows.Forms.MessageBoxButtons]::OK)
            }
            else
            {
                $txtADFSMetadataUri.ForeColor = $defaultForeColor
                Update-TextBox $txtADFSMetadataUri
            }
        }
        else
        {
            $txtADFSMetadataUri.ForeColor = $defaultForeColor
        }
    }
    $txtADFSMetadataUri.Add_Leave($txtADFSMetadataUri_Changed)

    $row += 40

    # --------------------------- CUSTOMER INFO -------------------------------
    $script:lblCustomerInfo = New-Object System.Windows.Forms.Label
    $lblCustomerInfo.Location = New-Object System.Drawing.Size($lblLeft,($row))
    $lblCustomerInfo.AutoSize = $true
    $lblCustomerInfo.Text = $locString.lblCustomerInfo
    $lblCustomerInfo.Font = [System.Drawing.Font]::New("Microsoft Sans Serif", 12, [System.Drawing.FontStyle]::Bold)
    $tabCustomer.Controls.Add($lblCustomerInfo)

    $row += 40

    # ---------------------------- COMPANY NAME -------------------------------
    $script:lblCompanyName = New-Object System.Windows.Forms.Label
    $lblCompanyName.Location = New-Object System.Drawing.Size($lblLeft,($row+2))
    $lblCompanyName.MaximumSize = New-Object System.Drawing.Size($lblMaxSize)
    $lblCompanyName.AutoSize = $true
    $lblCompanyName.Font = $defaultLabelFont
    $lblCompanyName.Text = $locString.lblCompanyName
    $tabCustomer.Controls.Add($lblCompanyName)

    $script:txtCompanyName = New-Object System.Windows.Forms.TextBox
    $txtCompanyName.Location = New-Object System.Drawing.Size($txtLeft,$row)
    $txtCompanyName.Size = New-Object System.Drawing.Size($txtSize)
    $txtCompanyName.Font = $defaultLabelFont
    $txtCompanyName.Name = 'txtCompanyName'
    $txtCompanyName.MaxLength = $lenText
    $tabCustomer.Controls.Add($txtCompanyName)

    $row += 30

    # --------------------------- EXTERNAL DOMAIN -----------------------------
    $script:lblExternalDomain = New-Object System.Windows.Forms.Label
    $lblExternalDomain.Location = New-Object System.Drawing.Size($lblLeft,($row+2))
    $lblExternalDomain.MaximumSize = New-Object System.Drawing.Size($lblMaxSize)
    $lblExternalDomain.AutoSize = $true
    $lblExternalDomain.Font = $defaultLabelFont
    $lblExternalDomain.Text = $locString.lblExternalDomain
    $tabCustomer.Controls.Add($lblExternalDomain)

    $script:txtExternalDomain = New-Object System.Windows.Forms.TextBox
    $txtExternalDomain.Location = New-Object System.Drawing.Size($txtLeft,$row)
    $txtExternalDomain.Size = New-Object System.Drawing.Size($txtSize)
    $txtExternalDomain.Font = $defaultLabelFont
    $txtExternalDomain.Name = 'txtExternalDomain'
    $txtExternalDomain.MaxLength = $lenDomain
    $tabCustomer.Controls.Add($txtExternalDomain)
    $txtExternalDomain_Changed = `
    {
        if(-not [string]::IsNullOrEmpty($txtExternalDomain.Text))
        {
            $txtExternalDomain.Text = ($txtExternalDomain.Text).Trim()
            if($txtExternalDomain.Text -notmatch $validDomain)
            {
                $txtExternalDomain.ForeColor = $defaultFailColor
                Close-WaitForm
                $itemName = ($locString.lblExternalDomain).Replace(':','')
                $message = "'$($itemName)' " + $locString.errDomainInvalid
                [System.Windows.Forms.MessageBox]::Show($message, $locString.capValidationError, [System.Windows.Forms.MessageBoxButtons]::OK)
            }
            else
            {
                $txtExternalDomain.ForeColor = $defaultForeColor
            }
            Update-TextBox $txtExternalDomain
        }
        else
        {
            $txtExternalDomain.ForeColor = $defaultForeColor
        }
    }
    $txtExternalDomain.Add_Leave($txtExternalDomain_Changed)

    $row += 30

    # --------------------------- PRIVATE DOMAIN ------------------------------
    $script:lblPrivateDomain = New-Object System.Windows.Forms.Label
    $lblPrivateDomain.Location = New-Object System.Drawing.Size($lblLeft,($row+2))
    $lblPrivateDomain.MaximumSize = New-Object System.Drawing.Size($lblMaxSize)
    $lblPrivateDomain.AutoSize = $true
    $lblPrivateDomain.Font = $defaultLabelFont
    $lblPrivateDomain.Text = $locString.lblPrivateDomain
    $tabCustomer.Controls.Add($lblPrivateDomain)

    $script:txtPrivateDomain = New-Object System.Windows.Forms.TextBox
    $txtPrivateDomain.Location = New-Object System.Drawing.Size($txtLeft,$row)
    $txtPrivateDomain.Size = New-Object System.Drawing.Size($txtSize)
    $txtPrivateDomain.Font = $defaultLabelFont
    $txtPrivateDomain.Name = 'txtPrivateDomain'
    $txtPrivateDomain.MaxLength = $lenDomain
    $tabCustomer.Controls.Add($txtPrivateDomain)
    $txtPrivateDomain_Changed = `
    {
        if(-not [string]::IsNullOrEmpty($txtPrivateDomain.Text))
        {
            $txtPrivateDomain.Text = ($txtPrivateDomain.Text).Trim()
            if($txtPrivateDomain.Text -notmatch $validDomain)
            {
                $txtPrivateDomain.ForeColor = $defaultFailColor
                Close-WaitForm
                $itemName = ($locString.lblPrivateDomain).Replace(':','')
                $message = "'$($itemName)' " + $locString.errDomainInvalid
                [System.Windows.Forms.MessageBox]::Show($message, $locString.capValidationError, [System.Windows.Forms.MessageBoxButtons]::OK)
            }
            else
            {
                $txtPrivateDomain.ForeColor = $defaultForeColor
            }
            Update-TextBox $txtPrivateDomain
        }
        else
        {
            $txtPrivateDomain.ForeColor = $defaultForeColor
        }
    }
    $txtPrivateDomain.Add_Leave($txtPrivateDomain_Changed)

    $row += 30

    # ---------------------------- REGION NAME --------------------------------
    $script:lblRegionName = New-Object System.Windows.Forms.Label
    $lblRegionName.Location = New-Object System.Drawing.Size($lblLeft,($row+2))
    $lblRegionName.MaximumSize = New-Object System.Drawing.Size($lblMaxSize)
    $lblRegionName.AutoSize = $true
    $lblRegionName.Font = $defaultLabelFont
    $lblRegionName.Text = $locString.lblRegionName
    $tabCustomer.Controls.Add($lblRegionName)

    $script:txtRegionName = New-Object System.Windows.Forms.TextBox
    $txtRegionName.Location = New-Object System.Drawing.Size($txtLeft,$row)
    $txtRegionName.Size = New-Object System.Drawing.Size($txtPrefixSize)
    $txtRegionName.Font = $defaultLabelFont
    $txtRegionName.Name = 'txtRegionName'
    $txtRegionName.MaxLength = $lenText
    $tabCustomer.Controls.Add($txtRegionName)
    $txtRegionName_Changed = `
    {
        if(-not [string]::IsNullOrEmpty($txtRegionName.Text))
        {
            $txtRegionName.Text = ($txtRegionName.Text).Trim()
            if($txtRegionName.Text -notmatch $validAlphaNumeric)
            {
                $txtRegionName.ForeColor = $defaultFailColor
                Close-WaitForm
                $itemName = ($locString.lblRegionName).Replace(':','')
                $message = "'$($itemName)' " + $locString.errAlphaNumericInvalid
                [System.Windows.Forms.MessageBox]::Show($message, $locString.capValidationError, [System.Windows.Forms.MessageBoxButtons]::OK)
            }
            else
            {
                $txtRegionName.ForeColor = $defaultForeColor
            }
            Update-TextBox $txtRegionName
        }
        else
        {
            $txtRegionName.ForeColor = $defaultForeColor
        }
    }
    $txtRegionName.Add_Leave($txtRegionName_Changed)

    $row += 30

    # --------------------------- DEPLOY PREFIX -------------------------------
    $script:lblDeployPrefix = New-Object System.Windows.Forms.Label
    $lblDeployPrefix.Location = New-Object System.Drawing.Size($lblLeft,($row+2))
    $lblDeployPrefix.MaximumSize = New-Object System.Drawing.Size($lblMaxSize)
    $lblDeployPrefix.AutoSize = $true
    $lblDeployPrefix.Font = $defaultLabelFont
    $lblDeployPrefix.Text = $locString.lblDeployPrefix
    $tabCustomer.Controls.Add($lblDeployPrefix)

    $script:txtDeployPrefix = New-Object System.Windows.Forms.TextBox
    $txtDeployPrefix.Location = New-Object System.Drawing.Size($txtLeft,$row)
    $txtDeployPrefix.Size = New-Object System.Drawing.Size($txtPrefixSize)
    $txtDeployPrefix.Font = $defaultLabelFont
    $txtDeployPrefix.Name = 'txtDeployPrefix'
    $txtDeployPrefix.MaxLength = $lenPrefix
    $tabCustomer.Controls.Add($txtDeployPrefix)
    $txtDeployPrefix_Changed = `
    {
        if(-not [string]::IsNullOrEmpty($txtDeployPrefix.Text))
        {
            $txtDeployPrefix.Text = ($txtDeployPrefix.Text).Trim()
            $itemName = ($locString.lblDeployPrefix).Replace(':','')
            if($txtDeployPrefix.Text -notmatch $validAlphaNumeric)
            {
                $txtDeployPrefix.ForeColor = $defaultFailColor
                Close-WaitForm
                $message = "'$($itemName)' " + $locString.errAlphaNumericInvalid
                [System.Windows.Forms.MessageBox]::Show($message, $locString.capValidationError, [System.Windows.Forms.MessageBoxButtons]::OK)
            }
            elseif($txtDeployPrefix.Text.Length -gt [int]$lenPrefix)
            {
                $txtDeployPrefix.ForeColor = $defaultFailColor
                Close-WaitForm
                $message = "'$($itemName)' " + ($locString.errCharacterLength -f $lenPrefix)
                [System.Windows.Forms.MessageBox]::Show($message, $locString.capValidationError, [System.Windows.Forms.MessageBoxButtons]::OK)
            }
            else
            {
                $txtDeployPrefix.ForeColor = $defaultForeColor
            }
            Update-TextBox $txtDeployPrefix
        }
        else
        {
            $txtDeployPrefix.ForeColor = $defaultForeColor
        }
    }
    $txtDeployPrefix.Add_Leave($txtDeployPrefix_Changed)

    $row += 30

    # -------------------------- PHYSICAL PREFIX ------------------------------
    $script:lblPhysicalPrefix = New-Object System.Windows.Forms.Label
    $lblPhysicalPrefix.Location = New-Object System.Drawing.Size($lblLeft,($row+2))
    $lblPhysicalPrefix.MaximumSize = New-Object System.Drawing.Size($lblMaxSize)
    $lblPhysicalPrefix.AutoSize = $true
    $lblPhysicalPrefix.Font = $defaultLabelFont
    $lblPhysicalPrefix.Text = $locString.lblPhysicalPrefix
    $tabCustomer.Controls.Add($lblPhysicalPrefix)

    $script:txtPhysicalPrefix = New-Object System.Windows.Forms.TextBox
    $txtPhysicalPrefix.Location = New-Object System.Drawing.Size($txtLeft,$row)
    $txtPhysicalPrefix.Size = New-Object System.Drawing.Size($txtPrefixSize)
    $txtPhysicalPrefix.Font = $defaultLabelFont
    $txtPhysicalPrefix.Name = 'txtPhysicalPrefix'
    $txtPhysicalPrefix.MaxLength = $lenPrefix
    $tabCustomer.Controls.Add($txtPhysicalPrefix)
    $txtPhysicalPrefix_Changed = `
    {
        if(-not [string]::IsNullOrEmpty($txtPhysicalPrefix.Text))
        {
            $txtPhysicalPrefix.Text = ($txtPhysicalPrefix.Text).Trim()
            $itemName = ($locString.lblPhysicalPrefix).Replace(':','')
            if($txtPhysicalPrefix.Text -notmatch $validAlphaNumeric)
            {
                $txtPhysicalPrefix.ForeColor = $defaultFailColor
                Close-WaitForm
                $message = "'$($itemName)' " + $locString.errAlphaNumericInvalid
                [System.Windows.Forms.MessageBox]::Show($message, $locString.capValidationError, [System.Windows.Forms.MessageBoxButtons]::OK)
            }
            elseif($txtPhysicalPrefix.Text.Length -gt [int]$lenPrefix)
            {
                $txtPhysicalPrefix.ForeColor = $defaultFailColor
                Close-WaitForm
                $message = "'$($itemName)' " + ($locString.errCharacterLength -f $lenPrefix)
                [System.Windows.Forms.MessageBox]::Show($message, $locString.capValidationError, [System.Windows.Forms.MessageBoxButtons]::OK)
            }
            else
            {
                $txtPhysicalPrefix.ForeColor = $defaultForeColor
            }
            Update-TextBox $txtPhysicalPrefix
        }
        else
        {
            $txtPhysicalPrefix.ForeColor = $defaultForeColor
        }
    }
    $txtPhysicalPrefix.Add_Leave($txtPhysicalPrefix_Changed)

    $row += 40

    # -------------------------- ENVIRONMENT INFO -----------------------------
    $script:lblEnvironmentInfo = New-Object System.Windows.Forms.Label
    $lblEnvironmentInfo.Location = New-Object System.Drawing.Size($lblLeft,($row))
    $lblEnvironmentInfo.AutoSize = $true
    $lblEnvironmentInfo.Text = $locString.lblEnvironmentInfo
    $lblEnvironmentInfo.Font = [System.Drawing.Font]::New("Microsoft Sans Serif", 12, [System.Drawing.FontStyle]::Bold)
    $tabCustomer.Controls.Add($lblEnvironmentInfo)

    $row += 40

    # ---------------------------- TIME SERVER --------------------------------
    $script:lblTimeServer = New-Object System.Windows.Forms.Label
    $lblTimeServer.Location = New-Object System.Drawing.Size($lblLeft,($row+2))
    $lblTimeServer.MaximumSize = New-Object System.Drawing.Size($lblMaxSize)
    $lblTimeServer.AutoSize = $true
    $lblTimeServer.Font = $defaultLabelFont
    $lblTimeServer.Text = $locString.lblTimeServer
    $tabCustomer.Controls.Add($lblTimeServer)

    $script:txtTimeServer = New-Object System.Windows.Forms.TextBox
    $txtTimeServer.Location = New-Object System.Drawing.Size($txtLeft,$row)
    $txtTimeServer.Size = New-Object System.Drawing.Size($txtSize)
    $txtTimeServer.Font = $defaultLabelFont
    $txtTimeServer.Name = 'txtTimeServer'
    $txtTimeServer.MaxLength = $lenDomain
    $txtTimeServer.Add_MouseHover($ShowHelp)
    $tabCustomer.Controls.Add($txtTimeServer)
    $txtTimeServer_Changed = `
    {
        if(-not [string]::IsNullOrEmpty($txtTimeServer.Text))
        {
            $txtTimeServer.Text = ($txtTimeServer.Text).Trim()
            $valid = $false
            if($txtTimeServer.Text -match $validIPAddress)
            {
                $valid = $true
            }
            elseif($txtTimeServer.Text -match $validDomain)
            {
                $valid = $true
            }
            if(-not $valid)
            {
                $txtTimeServer.ForeColor = $defaultFailColor
                Close-WaitForm
                $itemName = ($locString.lblTimeServer).Replace(':','')
                $message = "'$($itemName)' " + $locString.errIPorFQDNInvalid
                [System.Windows.Forms.MessageBox]::Show($message, $locString.capValidationError, [System.Windows.Forms.MessageBoxButtons]::OK)
            }
            else
            {
                $txtTimeServer.ForeColor = $defaultForeColor
            }
        }
        else
        {
            $txtTimeServer.ForeColor = $defaultForeColor
        }
        Update-TextBox $txtTimeServer
    }
    $txtTimeServer.Add_Leave($txtTimeServer_Changed)

    $row += 30

    # ----------------------------- DNS SERVER --------------------------------
    $script:lblDNSForwarder = New-Object System.Windows.Forms.Label
    $lblDNSForwarder.Location = New-Object System.Drawing.Size($lblLeft,($row+2))
    $lblDNSForwarder.MaximumSize = New-Object System.Drawing.Size($lblMaxSize)
    $lblDNSForwarder.AutoSize = $true
    $lblDNSForwarder.Font = $defaultLabelFont
    $lblDNSForwarder.Text = $locString.lblDNSForwarder
    $tabCustomer.Controls.Add($lblDNSForwarder)

    $script:txtDNSForwarder = New-Object System.Windows.Forms.RichTextBox
    $txtDNSForwarder.Location = New-Object System.Drawing.Size($txtLeft,$row)
    $txtDNSForwarder.Size = New-Object System.Drawing.Size($txtSize)
    $txtDNSForwarder.Font = $defaultLabelFont
    $txtDNSForwarder.Name = 'txtDNSForwarder'
    $txtDNSForwarder.MaxLength = 32
    $txtDNSForwarder.Multiline = $false
    $tabCustomer.Controls.Add($txtDNSForwarder)
    $txtDNSForwarder.Add_MouseHover($ShowHelp)
    $txtDNSForwarder_Changed = `
    {
        if(-not [string]::IsNullOrEmpty($txtDNSForwarder.Text))
        {
            $txtDNSForwarder.Text = [string]($txtDNSForwarder.Text).Trim()
            $txtDNSForwarder.Text = [string]($txtDNSForwarder.Text).Replace(' ','')
            $txtDNSForwarder.Text = [string]($txtDNSForwarder.Text).Replace(';',',')
            $errItem = @()
            $dnsList = @()
            $itemName = ($locString.lblDNSForwarder).Replace(':','').Replace('(s)','')
            if($txtDNSForwarder.Text -match ',')
            {
                ($txtDNSForwarder.Text).Split(',').Trim() | Foreach-Object `
                {
                    $dnsList += $PSItem
                    $newList = @()
                    foreach($dns in $dnsList)
                    {
                        if(($newList -notcontains $dns) -and ($newList.Count -lt $maxDNSItems))
                        {
                            $newList += $dns
                        }
                    }
                    $dnsList = $newList
                    $txtDNSForwarder.Text = ($dnsList -join ',')
                }
            }
            else
            {
                $dnsList += $txtDNSForwarder.Text
            }
            foreach($dns in $dnsList)
            {
                if($dns -notmatch $validIPAddress)
                {
                    $errItem += $dns
                    $txtDNSForwarder.SelectionStart = $txtDNSForwarder.Text.IndexOf($dns)
                    $txtDNSForwarder.SelectionLength = $dns.Length
                    $txtDNSForwarder.SelectionColor = $defaultFailColor
                }
                else
                {
                    $txtDNSForwarder.SelectionStart = $txtDNSForwarder.Text.IndexOf($dns)
                    $txtDNSForwarder.SelectionLength = $dns.Length
                    $txtDNSForwarder.SelectionColor = $defaultForeColor
                    $txtDNSForwarder.SelectionStart = 0
                    $txtDNSForwarder.SelectionLength = 0
                }
            }
            if($errItem.Count -gt 0)
            {
                $errMessage = $null
                $errItem | ForEach-Object `
                {
                    [string]$errMessage += "'$($itemName)' " + "$($locString.errInvalidIPList) $($PSItem)`n"
                }
                Close-WaitForm
                [System.Windows.Forms.MessageBox]::Show($errMessage, $locString.capValidationError, [System.Windows.Forms.MessageBoxButtons]::OK)
            }
            else
            {
                $txtDNSForwarder.SelectionStart = 0
                $txtDNSForwarder.SelectionLength = $txtDNSForwarder.Text.Length
                $txtDNSForwarder.ForeColor = $defaultForeColor
            }
        }
        else
        {
            $txtDNSForwarder.ForeColor = $defaultForeColor
        }
        $txtDNSForwarder.Refresh()
    }
    $txtDNSForwarder.Add_Leave($txtDNSForwarder_Changed)

    $row += 30

    # ---------------------------- SYSLOG SERVER ------------------------------
    $script:lblSyslogServer = New-Object System.Windows.Forms.Label
    $lblSyslogServer.Location = New-Object System.Drawing.Size($lblLeft,($row+2))
    $lblSyslogServer.MaximumSize = New-Object System.Drawing.Size($lblMaxSize)
    $lblSyslogServer.AutoSize = $true
    $lblSyslogServer.Font = $defaultLabelFont
    $lblSyslogServer.Text = $locString.lblSyslogServer
    $tabCustomer.Controls.Add($lblSyslogServer)

    $script:txtSyslogServer = New-Object System.Windows.Forms.TextBox
    $txtSyslogServer.Location = New-Object System.Drawing.Size($txtLeft,$row)
    $txtSyslogServer.Size = New-Object System.Drawing.Size($txtPrefixSize)
    $txtSyslogServer.Font = $defaultLabelFont
    $txtSyslogServer.Name = 'txtSyslogServer'
    $txtSyslogServer.MaxLength = $lenIPAddress
    $txtSyslogServer.Add_MouseHover($ShowHelp)
    $tabCustomer.Controls.Add($txtSyslogServer)
    $txtSyslogServer_Changed = `
    {
        if(-not [string]::IsNullOrEmpty($txtSyslogServer.Text))
        {
            $txtSyslogServer.Text = ($txtSyslogServer.Text).Trim()
            if($txtSyslogServer.Text -notmatch $validIPAddress)
            {
                $txtSyslogServer.ForeColor = $defaultFailColor
                Close-WaitForm
                $itemName = ($locString.lblSyslogServer).Replace(':','')
                $message = "'$($itemName)' " + $locString.errIPAddressInvalid
                [System.Windows.Forms.MessageBox]::Show($message, $locString.capValidationError, [System.Windows.Forms.MessageBoxButtons]::OK)
            }
            else
            {
                $txtSyslogServer.ForeColor = $defaultForeColor
            }
            Update-TextBox $txtSyslogServer
        }
        else
        {
            $txtSyslogServer.ForeColor = $defaultForeColor
        }
    }
    $txtSyslogServer.Add_Leave($txtSyslogServer_Changed)

    $script:lblSyslogServerOptional = New-Object System.Windows.Forms.Label
    $lblSyslogServerOptional.Location = New-Object System.Drawing.Size(($txtSyslogServer.Right+8),($txtSyslogServer.Location.Y+2))
    $lblSyslogServerOptional.AutoSize = $true
    $lblSyslogServerOptional.Font = $defaultLabelFont
    $lblSyslogServerOptional.Text = $locString.strOptionalNote
    $tabCustomer.Controls.Add($lblSyslogServerOptional)

    # ============================== NETWORK TAB ==============================
    $row = 20

    # ----------------------------- DOCUMENT LINK -----------------------------
    $script:lnkNetworkDoc = New-Object System.Windows.Forms.LinkLabel
    $lnkNetworkDoc.Location = New-Object System.Drawing.Size(680,($row+2))
    $lnkNetworkDoc.Size = New-Object System.Drawing.Size(300,20)
    $lnkNetworkDoc.Font = $defaultLabelFont
    $lnkNetworkDoc.AutoSize = $true
    $lnkNetworkDoc.Text = $locString.networkLinkText
    $lnkNetworkDoc.Add_Click({Start-Process -FilePath $locString.networkLink})
    $tabNetwork.Controls.Add($lnkNetworkDoc)

    # ---------------------------- NETWORK SETTINGS ---------------------------
    $script:lblNetworkSettings = New-Object System.Windows.Forms.Label
    $lblNetworkSettings.Location = New-Object System.Drawing.Size($lblLeft,($row))
    $lblNetworkSettings.AutoSize = $true
    $lblNetworkSettings.Text = $locString.lblNetworkSettings
    $lblNetworkSettings.Font = [System.Drawing.Font]::New("Microsoft Sans Serif", 12, [System.Drawing.FontStyle]::Bold)
    $tabNetwork.Controls.Add($lblNetworkSettings)

    $row += 40

    # ------------------------------- TOPOLOGY --------------------------------
    $script:lblTopology = New-Object System.Windows.Forms.Label
    $lblTopology.Location = New-Object System.Drawing.Size($lblLeft,($row))
    $lblTopology.AutoSize = $true
    $lblTopology.Text = $locString.lblTopology
    $lblTopology.Font = [System.Drawing.Font]::New("Microsoft Sans Serif", 10, [System.Drawing.FontStyle]::Bold)
    $tabNetwork.Controls.Add($lblTopology)

    $row2 = $row
    $row += 30

    # ----------------------------- SCALE UNITS -------------------------------
    $script:lblScaleUnitCount = New-Object System.Windows.Forms.Label
    $lblScaleUnitCount.Location = New-Object System.Drawing.Size($lblLeft,($row+2))
    $lblScaleUnitCount.MaximumSize = New-Object System.Drawing.Size($lblMaxSize)
    $lblScaleUnitCount.AutoSize = $true
    $lblScaleUnitCount.Font = $defaultLabelFont
    $lblScaleUnitCount.Text = $locString.lblScaleUnitCount
    $tabNetwork.Controls.Add($lblScaleUnitCount)

    $script:txtScaleUnitCount = New-Object System.Windows.Forms.TextBox
    $txtScaleUnitCount.Location = New-Object System.Drawing.Size($txtLeftNet,$row)
    $txtScaleUnitCount.Size = New-Object System.Drawing.Size($txtCountSize)
    $txtScaleUnitCount.Font = $defaultLabelFont
    $txtScaleUnitCount.TextAlign = [System.Windows.Forms.HorizontalAlignment]::Center
    $txtScaleUnitCount.Name = 'txtScaleUnitCount'
    $txtScaleUnitCount.MaxLength = 1
    $txtScaleUnitCount.Text = $defaultSUCount
    $txtScaleUnitCount.Enabled = $false
    $txtScaleUnitCount.BackColor = $defaultBackColor
    $txtScaleUnitCount.ForeColor = $defaultForeColor
    $txtScaleUnitCount.ReadOnly = $true
    $tabNetwork.Controls.Add($txtScaleUnitCount)
    $script:prevScaleUnitCount = $txtScaleUnitCount.Text
    $txtScaleUnitCount_Changed = `
    {
        if(-not [string]::IsNullOrWhiteSpace($txtScaleUnitCount.Text))
        {
            $txtScaleUnitCount.Text = ($txtScaleUnitCount.Text).Trim()
            if($txtScaleUnitCount.Text -notmatch "^[1-$($maxSU)]+$")
            {
                Close-WaitForm
                $itemName = ($locString.lblScaleUnitCount).Replace(':','')
                [string]$errMessage = "'$($itemName)' " + ($locString.errNumberNotInRange -f '1',$maxSU)
                [System.Windows.Forms.MessageBox]::Show($errMessage, $locString.capValidationError, [System.Windows.Forms.MessageBoxButtons]::OK)
                $txtScaleUnitCount.Text = $script:prevScaleUnitCount
            }
            else
            {
                $null = $tabBadInput.Remove($txtScaleUnitCount)
                $script:prevScaleUnitCount = $txtScaleUnitCount.Text
                Remove-GeneratedTabs
                $txtScaleUnitCount.ForeColor = $defaultForeColor
                (1..$maxSU) | Foreach-Object `
                {
                    if($PSItem -le [int]$txtScaleUnitCount.Text)
                    {
                        Add-ScaleUnitTab -ID $PSItem
                    }
                    else
                    {
                        Remove-ScaleUnit -ID $PSItem
                    }
                }
                Update-SwitchVendor
                Restore-SelectedSwitch
                if([int]$txtScaleUnitCount.Text -gt 1)
                {
                    # Multi Scale-Unit
                    $script:validBorder = 4
                    if(($lstSwitchVendor.SelectedItem -eq "CiscoUCS") -or ($lstSwitchVendor.SelectedItem -eq "Mellanox"))
                    {
                        Disable-SpineSubnet
                        [void]$lstSpineModel.Items.Add('-none-')
                        $lblSpineModel.Enabled = $false
                        $lblSpineModel.Visible = $false
                        $lstSpineModel.Enabled = $false
                        $lstSpineModel.Visible = $false
                        $lstSpineModel.SelectedIndex = 0
                        Clear-ListSelection $lstSpineFirmware
                        $lblSpineFirmware.Enabled = $false
                        $lblSpineFirmware.Visible = $false
                        $lstSpineFirmware.Enabled = $false
                        $lstSpineFirmware.Visible = $false
                        Set-ScaleUnitsCiscoUCS -Enabled $false
                    }
                    else
                    {
                        Enable-SpineSubnet
                        $lblSpineFirmware.Enabled = $true
                        $lblSpineFirmware.Visible = $true
                        $lstSpineFirmware.Enabled = $true
                        $lstSpineFirmware.Visible = $true
                        $lblSpineModel.Enabled = $true
                        $lblSpineModel.Visible = $true
                        $lstSpineModel.Enabled = $true
                        $lstSpineModel.Visible = $true
                        if(-not $retainSpineSettings)
                        {
                            Clear-ListSelection $lstSpineFirmware
                            if($lstSpineModel.Items.Count -gt 0)
                            {
                                $lstSpineModel.SelectedIndex = 0
                                if($lstSpineFirmware.Items.Count -gt 0)
                                {
                                    $lstSpineFirmware.SelectedIndex = 0
                                }
                            }
                            else
                            {
                                Clear-ListSelection $lstSpineModel
                            }
                        }
                        Enable-SpineCount
                        Set-ScaleUnitsCiscoUCS -Enabled $true
                    }
                    if($isStaticRouting)
                    {
                        Disable-SpineBGP
                    }
                    else
                    {
                        if(($lstSwitchVendor.SelectedItem -eq "CiscoUCS") -or ($lstSwitchVendor.SelectedItem -eq "Mellanox"))
                        {
                            Disable-SpineBGP
                        }
                        else
                        {
                            Enable-SpineBGP
                        }
                    }
                }
                else
                {
                    # Single Scale-Unit
                    Disable-SpineCount
                    Disable-SpineBGP
                    Disable-SpineSubnet
                    Clear-ListSelection $lstSpineModel
                    $lblSpineModel.Enabled = $false
                    $lblSpineModel.Visible = $false
                    $lstSpineModel.Enabled = $false
                    $lstSpineModel.Visible = $false
                    Clear-ListSelection $lstSpineFirmware
                    $lblSpineFirmware.Enabled = $false
                    $lblSpineFirmware.Visible = $false
                    $lstSpineFirmware.Enabled = $false
                    $lstSpineFirmware.Visible = $false
                    Update-MaxBorderFromSwitch
                    if($lstSwitchVendor.SelectedItem -eq "CiscoUCS")
                    {
                        $script:validBorder = 4
                    }
                    if($isExtStor)
                    {
                        if($isStaticRouting)
                        {
                            $lblEdgeBGP.Visible = $false
                            $txtEdgeBGP.Visible = $false
                            $txtEdgeBGP.Text = [string]::Empty
                        }
                        elseif($lstSwitchVendor.SelectedItem -eq 'msftcisco')
                        {
                            $lblEdgeBGP.Visible = $true
                            $txtEdgeBGP.Visible = $true
                        }
                        else
                        {
                            $txtEdgeBGP.Text = [string]::Empty
                            $txtEdgeBGP.Visible = $false
                            $lblEdgeBGP.Visible = $false
                        }
                    }
                    else
                    {
                        $lblEdgeBGP.Visible = $false
                        $txtEdgeBGP.Visible = $false
                        $txtEdgeBGP.Text = [string]::Empty
                    }
                }
            }
            if([int]$txtBorderCount.Text -gt $validBorder)
            {
                $txtBorderCount.Text = $validBorder
            }
            Move-PartnerTab
            Invoke-Command -ScriptBlock $txtBorderCount_Changed
            Update-TextBox $txtScaleUnitCount
        }
        else
        {
            $txtScaleUnitCount.Text = $script:prevScaleUnitCount
            $txtScaleUnitCount.ForeColor = $defaultForeColor
        }
    }
    $txtScaleUnitCount.Add_Leave($txtScaleUnitCount_Changed)

    $row += 25

    # -------------------------- TOTAL NODE COUNT -----------------------------
    $script:lblTotalNodeCount = New-Object System.Windows.Forms.Label
    $lblTotalNodeCount.Location = New-Object System.Drawing.Size($lblLeft,($row+2))
    $lblTotalNodeCount.MaximumSize = New-Object System.Drawing.Size($lblMaxSize)
    $lblTotalNodeCount.AutoSize = $true
    $lblTotalNodeCount.Font = $defaultLabelFont
    $lblTotalNodeCount.Text = $locString.lblTotalNodeCount
    $tabNetwork.Controls.Add($lblTotalNodeCount)

    $script:txtTotalNodeCount = New-Object System.Windows.Forms.TextBox
    $txtTotalNodeCount.Location = New-Object System.Drawing.Size($txtLeftNet,$row)
    $txtTotalNodeCount.Size = New-Object System.Drawing.Size($txtCountSize)
    $txtTotalNodeCount.Font = $defaultLabelFont
    $txtTotalNodeCount.Name = 'txtTotalNodeCount'
    $txtTotalNodeCount.TextAlign = [System.Windows.Forms.HorizontalAlignment]::Center
    $txtTotalNodeCount.Text = "0"
    $txtTotalNodeCount.Enabled = $false
    $txtTotalNodeCount.BackColor = $defaultBackColor
    $txtTotalNodeCount.ForeColor = $defaultForeColor
    $txtTotalNodeCount.ReadOnly = $true
    $tabNetwork.Controls.Add($txtTotalNodeCount)

    $row += 40

    # ----------------------------- SWITCH INFO -------------------------------
    $script:lblSwitchInfo = New-Object System.Windows.Forms.Label
    $lblSwitchInfo.Location = New-Object System.Drawing.Size($lblLeft,($row))
    $lblSwitchInfo.AutoSize = $true
    $lblSwitchInfo.Text = $locString.lblSwitchInfo
    $lblSwitchInfo.Font = [System.Drawing.Font]::New("Microsoft Sans Serif", 10, [System.Drawing.FontStyle]::Bold)
    $tabNetwork.Controls.Add($lblSwitchInfo)

    $rowSwInfo = $row
    $row += 30

    # ---------------------------- SWITCH VENDOR ------------------------------
    $script:lblSwitchVendor = New-Object System.Windows.Forms.Label
    $lblSwitchVendor.Location = New-Object System.Drawing.Size($lblLeft,($row+2))
    $lblSwitchVendor.MaximumSize = New-Object System.Drawing.Size($lblMaxSize)
    $lblSwitchVendor.AutoSize = $true
    $lblSwitchVendor.Font = $defaultLabelFont
    $lblSwitchVendor.Text = $locString.lblSwitchVendor
    $lblSwitchVendor.Visible = $true
    $tabNetwork.Controls.Add($lblSwitchVendor)

    $script:lstSwitchVendor = New-Object System.Windows.Forms.ComboBox
    $lstSwitchVendor.DropDownStyle = [System.Windows.Forms.ComboBoxStyle]::DropDownList
    $lstSwitchVendor.Location = New-Object System.Drawing.Size($txtLeft,$row)
    $lstSwitchVendor.Size = New-Object System.Drawing.Size($txtSizeNet)
    $lstSwitchVendor.Font = $defaultLabelFont
    $lstSwitchVendor.Name = 'lstSwitchVendor'
    $lstSwitchVendor.Visible = $true
    [void]$lstSwitchVendor.Items.Clear()
    Get-SwitchVendors -IgnoreMake $ignoreSwitch | Foreach-Object `
    {
        [void]$lstSwitchVendor.Items.Add($PSItem)
    }
    $lstSwitchVendor.SelectedIndex = -1
    $lstSwitchVendor_Changed = `
    {
        if($lstSwitchVendor.SelectedIndex -ge 0)
        {
            if($prevSwitchVendor -ne $lstSwitchVendor.SelectedItem)
            {
                $script:prevSwitchVendor = $lstSwitchVendor.SelectedItem
                $currentBorderCount = $txtBorderCount.Text
                [void]$lstTORFirmware.Items.Clear()
                [void]$lstTORModel.Items.Clear()
                [void]$lstBMCFirmware.Items.Clear()
                [void]$lstBMCModel.Items.Clear()
                [void]$lstSpineFirmware.Items.Clear()
                [void]$lstSpineModel.Items.Clear()
                if($isStaticRouting)
                {
                    $lblEdgeBGP.Visible = $false
                    $txtEdgeBGP.Visible = $false
                    $txtEdgeBGP.Text = [string]::Empty
                }
                elseif($lstSwitchVendor.SelectedItem -eq 'msftcisco')
                {
                    $lblEdgeBGP.Visible = $true
                    $txtEdgeBGP.Visible = $true
                }
                else
                {
                    $lblEdgeBGP.Visible = $false
                    $txtEdgeBGP.Visible = $false
                    $txtEdgeBGP.Text = [string]::Empty
                }
                Get-SwitchModels -Make $lstSwitchVendor.SelectedItem -Type 'TOR' | Foreach-Object `
                {
                    [void]$lstTORModel.Items.Add($PSItem)
                }
                if($lstTORModel.Items.Count -gt 0)
                {
                    $lstTORModel.SelectedIndex = 0
                    $lstTORModel.Refresh()
                }
                else
                {
                    $lstTORModel.SelectedIndex = -1
                }
                Invoke-Command -ScriptBlock $lstRoutingMethod_Changed
                if(($lstSwitchVendor.SelectedItem -eq "CiscoUCS") -or ($lstSwitchVendor.SelectedItem -eq "Mellanox"))
                {
                    $script:validBorder = 4
                    [void]$lstBMCModel.Items.Add('-none-')
                    $lblBMCModel.Enabled = $false
                    $lblBMCModel.Visible = $false
                    $lstBMCModel.Enabled = $false
                    $lstBMCModel.Visible = $false

                    [void]$lstSpineModel.Items.Add('-none-')
                    $lblSpineModel.Enabled = $false
                    $lblSpineModel.Visible = $false
                    $lstSpineModel.Enabled = $false
                    $lstSpineModel.Visible = $false
                    $lstSpineModel.SelectedIndex = 0

                    Disable-SpineBGP
                    Disable-SpineCount
                    Disable-SpineSubnet
                    Set-ScaleUnitsCiscoUCS -Enabled $false
                }
                else
                {
                    $script:validBorder = 2
                    if([int]$txtScaleUnitCount.Text -gt 1)
                    {
                        $script:validBorder = 4
                        Enable-SpineSubnet
                        Enable-SpineBGP
                        Enable-SpineCount
                        Set-ScaleUnitsCiscoUCS -Enabled $true
                        if($isStaticRouting)
                        {
                            Disable-SpineBGP
                        }
                        else
                        {
                            Enable-SpineBGP
                        }
                    }
                    else
                    {
                        Disable-SpineBGP
                        Disable-SpineCount
                        Disable-SpineSubnet
                    }
                    Get-SwitchModels -Make $lstSwitchVendor.SelectedItem -Type 'BMC' | Foreach-Object `
                    {
                        [void]$lstBMCModel.Items.Add($PSItem)
                    }
                    $lblBMCModel.Enabled = $true
                    $lblBMCModel.Visible = $true
                    $lstBMCModel.Enabled = $true
                    $lstBMCModel.Visible = $true

                    $models = Get-SwitchModels -Make $lstSwitchVendor.SelectedItem -Type 'SPINE'
                    if($models.Count -gt 0)
                    {
                        $models | Foreach-Object `
                        {
                            [void]$lstSpineModel.Items.Add($PSItem)
                        }
                        if($txtScaleUnitCount.Text -eq '1')
                        {
                            Clear-ListSelection $lstSpineModel
                            $lblSpineModel.Enabled = $false
                            $lblSpineModel.Visible = $false
                            $lstSpineModel.Enabled = $false
                            $lstSpineModel.Visible = $false
                        }
                        else
                        {
                            $lblSpineModel.Enabled = $true
                            $lblSpineModel.Visible = $true
                            $lstSpineModel.Enabled = $true
                            $lstSpineModel.Visible = $true
                            $lstSpineModel.SelectedIndex = 0
                        }
                    }
                    else
                    {
                        Clear-ListSelection $lstSpineModel
                        $lblSpineModel.Enabled = $false
                        $lblSpineModel.Visible = $false
                        $lstSpineModel.Enabled = $false
                        $lstSpineModel.Visible = $false

                        Clear-ListSelection $lstSpineFirmware
                        $lblSpineFirmware.Enabled = $false
                        $lblSpineFirmware.Visible = $false
                        $lstSpineFirmware.Enabled = $false
                        $lstSpineFirmware.Visible = $false
                    }
                }
                Update-MaxBorderFromSwitch
                if([string]::IsNullOrEmpty($currentBorderCount))
                {
                    $txtBorderCount.Text = $defaultBorderCount
                }
                elseif([int]$currentBorderCount -le [int]$validBorder)
                {
                    $txtBorderCount.Text = $currentBorderCount
                }
                else
                {
                    $txtBorderCount.Text = $defaultBorderCount
                }
                Invoke-Command -ScriptBlock $txtBorderCount_Changed
                if($lstBMCModel.Items.Count -gt 0)
                {
                    $lstBMCModel.SelectedIndex = 0
                    $lstBMCModel.Refresh()
                    $lstSpineModel.Refresh()
                }
                else
                {
                    $lstBMCModel.SelectedIndex = -1
                }
            }
        }
    }
    $lstSwitchVendor.Add_SelectedIndexChanged($lstSwitchVendor_Changed)
    $tabNetwork.Controls.Add($lstSwitchVendor)

    $row += 25

    # ------------------------------ TOR MODEL --------------------------------
    $script:lblTORModel = New-Object System.Windows.Forms.Label
    $lblTORModel.Location = New-Object System.Drawing.Size($lblLeft,($row+2))
    $lblTORModel.MaximumSize = New-Object System.Drawing.Size($lblMaxSize)
    $lblTORModel.AutoSize = $true
    $lblTORModel.Font = $defaultLabelFont
    $lblTORModel.Text = $locString.lblTORModel
    $lblTORModel.Visible = $true
    $tabNetwork.Controls.Add($lblTORModel)

    $script:lstTORModel = New-Object System.Windows.Forms.ComboBox
    $lstTORModel.DropDownStyle = [System.Windows.Forms.ComboBoxStyle]::DropDownList
    $lstTORModel.Location = New-Object System.Drawing.Size($txtLeft,$row)
    $lstTORModel.Size = New-Object System.Drawing.Size($txtSizeNet)
    $lstTORModel.Font = $defaultLabelFont
    $lstTORModel.Name = 'lstTORModel'
    $lstTORModel.Visible = $true
    [void]$lstTORModel.Items.Clear()
    $lstTORModel.SelectedIndex = -1
    $tabNetwork.Controls.Add($lstTORModel)
    $lstTORModel_Changed = `
    {
        [void]$lstTORFirmware.Items.Clear()
        if($lstTORModel.SelectedItem)
        {
            $currentBorderCount = $txtBorderCount.Text
            $firmware = @(Get-SwitchFirmware -Make $lstSwitchVendor.SelectedItem -Type 'TOR' -Model $lstTORModel.SelectedItem)
            if($firmware.Count -gt 0)
            {
                $firmware | Foreach-Object `
                {
                    [void]$lstTORFirmware.Items.Add($PSItem)
                }
                $lstTORFirmware.SelectedIndex = 0
            }
            else
            {
                Clear-ListSelection $lstTORFirmware
            }
            Update-MaxBorderFromSwitch
            if([string]::IsNullOrEmpty($currentBorderCount))
            {
                $txtBorderCount.Text = $defaultBorderCount
            }
            elseif([int]$currentBorderCount -le [int]$validBorder)
            {
                $txtBorderCount.Text = $currentBorderCount
            }
            else
            {
                $txtBorderCount.Text = $defaultBorderCount
            }
            Invoke-Command -ScriptBlock $txtBorderCount_Changed
            if($lstSwitchVendor.SelectedItem -eq "msftcisco")
            {
                $script:arrUplinkSpeed = @()
                $script:arrUplinkSpeed = Get-UplinkSpeedList
                Invoke-Command -ScriptBlock $UpdateUplinkSpeedItems
                Enable-UplinkSpeed
            }
            else
            {
                $thisUplink = @(Get-UplinkSpeedList -SpecificMake $lstSwitchVendor.SelectedItem -SpecificModel $lstTORModel.SelectedItem)
                if($thisUplink.Count -gt 0)
                {
                    $script:arrUplinkSpeed = @()
                    $thisUplink | Foreach-Object `
                    {
                        if($script:arrUplinkSpeed -notcontains $PSItem) { $script:arrUplinkSpeed += $PSItem }
                    }
                    Invoke-Command -ScriptBlock $UpdateUplinkSpeedItems
                    Enable-UplinkSpeed
                    $lstUplinkSpeed.SelectedIndex = 0
                }
                else
                {
                    $lstUplinkSpeed.SelectedIndex = -1
                    Disable-UplinkSpeed
                }
            }
        }
        else
        {
            Clear-ListSelection $lstTORFirmware
        }
        $lstTORFirmware.Refresh()
    }
    $lstTORModel.Add_SelectedIndexChanged($lstTORModel_Changed)

    $row += 25

    # ---------------------------- TOR FIRMWARE -------------------------------
    $script:lblTORFirmware = New-Object System.Windows.Forms.Label
    $lblTORFirmware.Location = New-Object System.Drawing.Size($lblLeft,($row+2))
    $lblTORFirmware.MaximumSize = New-Object System.Drawing.Size($lblMaxSize)
    $lblTORFirmware.AutoSize = $true
    $lblTORFirmware.Font = $defaultLabelFont
    $lblTORFirmware.Text = $locString.lblTORFirmware
    $lblTORFirmware.Visible = $true
    $tabNetwork.Controls.Add($lblTORFirmware)

    $script:lstTORFirmware = New-Object System.Windows.Forms.ComboBox
    $lstTORFirmware.DropDownStyle = [System.Windows.Forms.ComboBoxStyle]::DropDownList
    $lstTORFirmware.Location = New-Object System.Drawing.Size($txtLeft,$row)
    $lstTORFirmware.Size = New-Object System.Drawing.Size($txtSizeNet)
    $lstTORFirmware.Font = $defaultLabelFont
    $lstTORFirmware.Name = 'lstTORFirmware'
    $lstTORFirmware.Visible = $true
    [void]$lstTORFirmware.Items.Clear()
    $lstTORFirmware.SelectedIndex = -1
    $tabNetwork.Controls.Add($lstTORFirmware)

    $row += 25

    # ------------------------------ BMC MODEL --------------------------------
    $script:lblBMCModel = New-Object System.Windows.Forms.Label
    $lblBMCModel.Location = New-Object System.Drawing.Size($lblLeft,($row+2))
    $lblBMCModel.MaximumSize = New-Object System.Drawing.Size($lblMaxSize)
    $lblBMCModel.AutoSize = $true
    $lblBMCModel.Font = $defaultLabelFont
    $lblBMCModel.Text = $locString.lblBMCModel
    $lblBMCModel.Visible = $true
    $tabNetwork.Controls.Add($lblBMCModel)

    $script:lstBMCModel = New-Object System.Windows.Forms.ComboBox
    $lstBMCModel.DropDownStyle = [System.Windows.Forms.ComboBoxStyle]::DropDownList
    $lstBMCModel.Location = New-Object System.Drawing.Size($txtLeft,$row)
    $lstBMCModel.Size = New-Object System.Drawing.Size($txtSizeNet)
    $lstBMCModel.Font = $defaultLabelFont
    $lstBMCModel.Name = 'lstBMCModel'
    $lstBMCModel.Visible = $true
    [void]$lstBMCModel.Items.Clear()
    $lstBMCModel.SelectedIndex = -1
    $lstBMCModel_Changed = `
    {
        [void]$lstBMCFirmware.Items.Clear()
        if(($lstSwitchVendor.SelectedItem -eq "CiscoUCS") -or ($lstSwitchVendor.SelectedItem -eq "Mellanox"))
        {
            Clear-ListSelection $lstBMCFirmware
            $lblBMCFirmware.Enabled = $false
            $lblBMCFirmware.Visible = $false
            $lstBMCFirmware.Enabled = $false
            $lstBMCFirmware.Visible = $false
        }
        else
        {
            $lblBMCFirmware.Enabled = $true
            $lblBMCFirmware.Visible = $true
            $lstBMCFirmware.Enabled = $true
            $lstBMCFirmware.Visible = $true
            if($lstBMCModel.SelectedItem)
            {
                $firmware = @(Get-SwitchFirmware -Make $lstSwitchVendor.SelectedItem -Type 'BMC' -Model $lstBMCModel.SelectedItem)
                if($firmware.Count -gt 0)
                {
                    $firmware | Foreach-Object `
                    {
                        [void]$lstBMCFirmware.Items.Add($PSItem)
                    }
                    $lstBMCFirmware.SelectedIndex = 0
                }
                else
                {
                    Clear-ListSelection $lstBMCFirmware
                }
            }
            else
            {
                Clear-ListSelection $lstBMCFirmware
            }
        }
        $lstBMCFirmware.Refresh()
    }
    $lstBMCModel.Add_SelectedIndexChanged($lstBMCModel_Changed)
    $tabNetwork.Controls.Add($lstBMCModel)

    $row += 25

    # ---------------------------- BMC FIRMWARE -------------------------------
    $script:lblBMCFirmware = New-Object System.Windows.Forms.Label
    $lblBMCFirmware.Location = New-Object System.Drawing.Size($lblLeft,($row+2))
    $lblBMCFirmware.MaximumSize = New-Object System.Drawing.Size($lblMaxSize)
    $lblBMCFirmware.AutoSize = $true
    $lblBMCFirmware.Font = $defaultLabelFont
    $lblBMCFirmware.Text = $locString.lblBMCFirmware
    $lblBMCFirmware.Visible = $true
    $tabNetwork.Controls.Add($lblBMCFirmware)

    $script:lstBMCFirmware = New-Object System.Windows.Forms.ComboBox
    $lstBMCFirmware.DropDownStyle = [System.Windows.Forms.ComboBoxStyle]::DropDownList
    $lstBMCFirmware.Location = New-Object System.Drawing.Size($txtLeft,$row)
    $lstBMCFirmware.Size = New-Object System.Drawing.Size($txtSizeNet)
    $lstBMCFirmware.Font = $defaultLabelFont
    $lstBMCFirmware.Name = 'lstBMCFirmware'
    $lstBMCFirmware.Visible = $true
    [void]$lstBMCFirmware.Items.Clear()
    $lstBMCFirmware.SelectedIndex = -1
    $tabNetwork.Controls.Add($lstBMCFirmware)

    $row += 25

    # ----------------------------- SPINE MODEL -------------------------------
    $script:lblSpineModel = New-Object System.Windows.Forms.Label
    $lblSpineModel.Location = New-Object System.Drawing.Size($lblLeft,($row+2))
    $lblSpineModel.MaximumSize = New-Object System.Drawing.Size($lblMaxSize)
    $lblSpineModel.AutoSize = $true
    $lblSpineModel.Font = $defaultLabelFont
    $lblSpineModel.Text = $locString.lblSpineModel
    $lblSpineModel.Enabled = $true
    $lblSpineModel.Visible = $true
    $tabNetwork.Controls.Add($lblSpineModel)

    $script:lstSpineModel = New-Object System.Windows.Forms.ComboBox
    $lstSpineModel.DropDownStyle = [System.Windows.Forms.ComboBoxStyle]::DropDownList
    $lstSpineModel.Location = New-Object System.Drawing.Size($txtLeft,$row)
    $lstSpineModel.Size = New-Object System.Drawing.Size($txtSizeNet)
    $lstSpineModel.Font = $defaultLabelFont
    $lstSpineModel.Name = 'lstSpineModel'
    $lstSpineModel.Enabled = $true
    $lstSpineModel.Visible = $true
    [void]$lstSpineModel.Items.Clear()
    $lstSpineModel.SelectedIndex = -1
    $tabNetwork.Controls.Add($lstSpineModel)

    $row += 25

    # ---------------------------- SPINE FIRMWARE -------------------------------
    $script:lblSpineFirmware = New-Object System.Windows.Forms.Label
    $lblSpineFirmware.Location = New-Object System.Drawing.Size($lblLeft,($row+2))
    $lblSpineFirmware.MaximumSize = New-Object System.Drawing.Size($lblMaxSize)
    $lblSpineFirmware.AutoSize = $true
    $lblSpineFirmware.Font = $defaultLabelFont
    $lblSpineFirmware.Text = $locString.lblSpineFirmware
    $lblSpineFirmware.Enabled = $true
    $lblSpineFirmware.Visible = $true
    $tabNetwork.Controls.Add($lblSpineFirmware)

    $script:lstSpineFirmware = New-Object System.Windows.Forms.ComboBox
    $lstSpineFirmware.DropDownStyle = [System.Windows.Forms.ComboBoxStyle]::DropDownList
    $lstSpineFirmware.Location = New-Object System.Drawing.Size($txtLeft,$row)
    $lstSpineFirmware.Size = New-Object System.Drawing.Size($txtSizeNet)
    $lstSpineFirmware.Font = $defaultLabelFont
    $lstSpineFirmware.Name = 'lstSpineFirmware'
    $lstSpineFirmware.Enabled = $true
    $lstSpineFirmware.Visible = $true
    [void]$lstSpineFirmware.Items.Clear()
    $lstSpineFirmware.SelectedIndex = -1
    $tabNetwork.Controls.Add($lstSpineFirmware)
    $lstSpineModel_Changed = `
    {
        [void]$lstSpineFirmware.Items.Clear()
        if(($lstSwitchVendor.SelectedItem -eq "CiscoUCS") -or ($lstSwitchVendor.SelectedItem -eq "Mellanox"))
        {
            Clear-ListSelection $lstSpineFirmware
            $lblSpineFirmware.Enabled = $false
            $lblSpineFirmware.Visible = $false
            $lstSpineFirmware.Enabled = $false
            $lstSpineFirmware.Visible = $false
        }
        else
        {
            if(-not [string]::IsNullOrEmpty($lstSpineModel.SelectedItem))
            {
                $lblSpineFirmware.Enabled = $true
                $lblSpineFirmware.Visible = $true
                $lstSpineFirmware.Enabled = $true
                $lstSpineFirmware.Visible = $true
                $firmware = @()
                if(-not [string]::IsNullOrEmpty($lstSwitchVendor.SelectedItem))
                {
                    $firmware = @(Get-SwitchFirmware -Make $lstSwitchVendor.SelectedItem -Type 'SPINE' -Model $lstSpineModel.SelectedItem)
                }
                if($firmware.Count -gt 0)
                {
                    $firmware | Foreach-Object `
                    {
                        [void]$lstSpineFirmware.Items.Add($PSItem)
                    }
                    $lblSpineFirmware.Enabled = $true
                    $lblSpineFirmware.Visible = $true
                    $lstSpineFirmware.Enabled = $true
                    $lstSpineFirmware.Visible = $true
                    $lstSpineFirmware.SelectedIndex = 0
                }
                else
                {
                    Clear-ListSelection $lstSpineFirmware
                    $lblSpineFirmware.Enabled = $false
                    $lblSpineFirmware.Visible = $false
                    $lstSpineFirmware.Enabled = $false
                    $lstSpineFirmware.Visible = $false
                }
                if($txtScaleUnitCount.Text -eq '1')
                {
                    Clear-ListSelection $lstSpineModel
                    $lblSpineModel.Enabled = $false
                    $lblSpineModel.Visible = $false
                    $lstSpineModel.Enabled = $false
                    $lstSpineModel.Visible = $false
                    Clear-ListSelection $lstSpineFirmware
                    $lblSpineFirmware.Enabled = $false
                    $lblSpineFirmware.Visible = $false
                    $lstSpineFirmware.Enabled = $false
                    $lstSpineFirmware.Visible = $false
                }
            }
        }
    }
    $lstSpineModel.Add_SelectedIndexChanged($lstSpineModel_Changed)

    $row += 40

    # --------------------------- SOFTWARE BGP ASN ----------------------------
    $script:lblSoftwareBGP = New-Object System.Windows.Forms.Label
    $lblSoftwareBGP.Location = New-Object System.Drawing.Size($lblLeft,($row+2))
    $lblSoftwareBGP.MaximumSize = New-Object System.Drawing.Size($lblMaxSize)
    $lblSoftwareBGP.AutoSize = $true
    $lblSoftwareBGP.Font = $defaultLabelFont
    $lblSoftwareBGP.Text = $locString.lblSoftwareBGP
    $tabNetwork.Controls.Add($lblSoftwareBGP)

    $script:txtSoftwareBGP = New-Object System.Windows.Forms.TextBox
    $txtSoftwareBGP.Location = New-Object System.Drawing.Size($txtLeft,$row)
    $txtSoftwareBGP.Size = New-Object System.Drawing.Size($txtPrefixSize)
    $txtSoftwareBGP.Font = $defaultLabelFont
    $txtSoftwareBGP.Name = 'txtSoftwareBGP'
    $txtSoftwareBGP.TextAlign = [System.Windows.Forms.HorizontalAlignment]::Center
    $txtSoftwareBGP.MaxLength = 10
    $tabNetwork.Controls.Add($txtSoftwareBGP)
    $txtSoftwareBGP_Changed = `
    {
        if(-not [string]::IsNullOrEmpty($txtSoftwareBGP.Text))
        {
            $txtSoftwareBGP.Text = ($txtSoftwareBGP.Text).Trim()
            $valid = $true
            if($txtSoftwareBGP.Text -notmatch $validNumber)
            {
                $valid = $false
            }
            else
            {
                [uint64]$thisASN = $txtSoftwareBGP.Text
                if(-not(Get-IsValidASN -ASN $thisASN))
                {
                    $valid = $false
                }
            }
            if(-not $valid)
            {
                $itemName = ($locString.lblSoftwareBGP).Replace(':','')
                [string]$errMessage = "'$($itemName)' " + ($locString.errNumberNotInRange -f $validASNMin,$validASNMax)
                $txtSoftwareBGP.ForeColor = $defaultFailColor
                Close-WaitForm
                [System.Windows.Forms.MessageBox]::Show($errMessage, $locString.capValidationError, [System.Windows.Forms.MessageBoxButtons]::OK)
                if(-not $tabBadInput.Contains($txtSoftwareBGP))
                {
                    $null = $tabBadInput.Add($txtSoftwareBGP)
                }
            }
            else
            {
                $txtSoftwareBGP.ForeColor = $defaultForeColor
                $txtSoftwareBGP.Text = $txtSoftwareBGP.Text -replace '\b0+\B'
                $null = $tabBadInput.Remove($txtSoftwareBGP)
            }
            Update-TextBox $txtSoftwareBGP
        }
    }
    $txtSoftwareBGP.Add_Leave($txtSoftwareBGP_Changed)

    $row += 40

    # ----------------------------- SPINE COUNT -------------------------------
    $script:lblSpineCount = New-Object System.Windows.Forms.Label
    $lblSpineCount.Location = New-Object System.Drawing.Size($lblLeft,($row+2))
    $lblSpineCount.MaximumSize = New-Object System.Drawing.Size($lblMaxSize)
    $lblSpineCount.AutoSize = $true
    $lblSpineCount.Font = $defaultLabelFont
    $lblSpineCount.Text = $locString.lblSpineCount
    $tabNetwork.Controls.Add($lblSpineCount)

    $script:txtSpineCount = New-Object System.Windows.Forms.TextBox
    $txtSpineCount.Location = New-Object System.Drawing.Size($txtLeft,$row)
    $txtSpineCount.Size = New-Object System.Drawing.Size($txtCountSize)
    $txtSpineCount.Font = $defaultLabelFont
    $txtSpineCount.Name = 'txtSpineCount'
    $txtSpineCount.TextAlign = [System.Windows.Forms.HorizontalAlignment]::Center
    $txtSpineCount.MaxLength = 1
    $txtSpineCount.Text = $defaultSpineCount
    $tabNetwork.Controls.Add($txtSpineCount)
    $txtSpineCount_Changed = `
    {
        if(-not([string]::IsNullOrEmpty($txtSpineCount.Text)) -and ($txtSpineCount.Text -notmatch '^[2-4]+$'))
        {
            $itemName = ($locString.lblSpineCount).Replace(':','')
            [string]$errMessage = "'$($itemName)' " + ($locString.errNumberNotInRange -f '2','4')
            Close-WaitForm
            [System.Windows.Forms.MessageBox]::Show($errMessage, $locString.capValidationError, [System.Windows.Forms.MessageBoxButtons]::OK)
            $txtSpineCount.SelectionStart = 0
            $txtSpineCount.SelectionLength = 1
            $txtSpineCount.Text = [string]::Empty
        }
        else
        {
            $txtSpineCount.ForeColor = $defaultForeColor
        }
        Update-TextBox $txtSpineCount
    }
    $txtSpineCount.Add_Leave($txtSpineCount_Changed)

    $row += 30

    # ---------------------------- SPINE BGP ASN ------------------------------
    $script:lblSpineBGP = New-Object System.Windows.Forms.Label
    $lblSpineBGP.Location = New-Object System.Drawing.Size($lblLeft,($row+2))
    $lblSpineBGP.MaximumSize = New-Object System.Drawing.Size($lblMaxSize)
    $lblSpineBGP.AutoSize = $true
    $lblSpineBGP.Font = $defaultLabelFont
    $lblSpineBGP.Text = $locString.lblSpineBGP
    $tabNetwork.Controls.Add($lblSpineBGP)

    $script:txtSpineBGP = New-Object System.Windows.Forms.TextBox
    $txtSpineBGP.Location = New-Object System.Drawing.Size($txtLeft,$row)
    $txtSpineBGP.Size = New-Object System.Drawing.Size($txtPrefixSize)
    $txtSpineBGP.Font = $defaultLabelFont
    $txtSpineBGP.Name = 'txtSpineBGP'
    $txtSpineBGP.TextAlign = [System.Windows.Forms.HorizontalAlignment]::Center
    $txtSpineBGP.MaxLength = 10
    $tabNetwork.Controls.Add($txtSpineBGP)
    $txtSpineBGP_Changed = `
    {
        if(-not([string]::IsNullOrEmpty($txtSpineBGP.Text)))
        {
            $valid = $true
            if($txtSpineBGP.Text -notmatch $validNumber)
            {
                $valid = $false
            }
            else
            {
                [uint64]$thisASN = $txtSpineBGP.Text
                if(-not(Get-IsValidASN -ASN $thisASN))
                {
                    $valid = $false
                }
            }
            if(-not $valid)
            {
                $itemName = ($locString.lblSpineBGP).Replace(':','')
                [string]$errMessage = "'$($itemName)' " + ($locString.errNumberNotInRange -f $validASNMin,$validASNMax)
                $txtSpineBGP.ForeColor = $defaultFailColor
                Close-WaitForm
                [System.Windows.Forms.MessageBox]::Show($errMessage, $locString.capValidationError, [System.Windows.Forms.MessageBoxButtons]::OK)
                if(-not $tabBadInput.Contains($txtSpineBGP))
                {
                    $null = $tabBadInput.Add($txtSpineBGP)
                }
            }
            else
            {
                $txtSpineBGP.ForeColor = $defaultForeColor
                $txtSpineBGP.Text = $txtSpineBGP.Text -replace '\b0+\B'
                $null = $tabBadInput.Remove($txtSpineBGP)
            }
        }
        Update-TextBox $txtSpineBGP
    }
    $txtSpineBGP.Add_Leave($txtSpineBGP_Changed)

    $row += 30

    # ----------------------------- SPINE SUBNET ------------------------------
    $script:lblSpineSubnet = New-Object System.Windows.Forms.Label
    $lblSpineSubnet.Location = New-Object System.Drawing.Size($lblLeft,($row+2))
    $lblSpineSubnet.MaximumSize = New-Object System.Drawing.Size($lblMaxSize)
    $lblSpineSubnet.AutoSize = $true
    $lblSpineSubnet.Font = $defaultLabelFont
    $lblSpineSubnet.Text = $locString.lblSpineSubnet
    $lblSpineSubnet.Enabled = $false
    $lblSpineSubnet.Visible = $false
    $tabNetwork.Controls.Add($lblSpineSubnet)

    $script:txtSpineSubnet = New-Object System.Windows.Forms.TextBox
    $txtSpineSubnet.Location = New-Object System.Drawing.Size($txtLeft,$row)
    $txtSpineSubnet.Size = New-Object System.Drawing.Size($txtPrefixSize)
    $txtSpineSubnet.Font = $defaultLabelFont
    $txtSpineSubnet.Name = 'txtSpineSubnet'
    $txtSpineSubnet.MaxLength = $lenIPAddress
    $txtSpineSubnet.Enabled = $false
    $txtSpineSubnet.Visible = $false
    $tabNetwork.Controls.Add($txtSpineSubnet)
    $txtSpineSubnet_Changed = `
    {
        if(-not([string]::IsNullOrEmpty($txtSpineSubnet.Text)))
        {
            $itemName = ($locString.lblSpineSubnet).Replace(':','')
            if($txtSpineSubnet.Text -notmatch $validIPAddress)
            {
                $txtSpineSubnet.ForeColor = $defaultFailColor
                Close-WaitForm
                $message = "'$($itemName)' " + $locString.errIPAddressInvalid
                [System.Windows.Forms.MessageBox]::Show($message, $locString.capValidationError, [System.Windows.Forms.MessageBoxButtons]::OK)
                if(-not $tabBadInput.Contains($txtSpineSubnet))
                {
                    $null = $tabBadInput.Add($txtSpineSubnet)
                }
            }
            elseif(-not(Get-IsValidSubnet -IPAddress $txtSpineSubnet.Text -CIDR $txtSpineSubnetSize.Text))
            {
                $txtSpineSubnet.ForeColor = $defaultFailColor
                Close-WaitForm
                $message = "'$($itemName)' " + ($locString.errSubnetInvalid -f $txtSpineSubnet.Text,$txtSpineSubnetSize.Text)
                [System.Windows.Forms.MessageBox]::Show($message, $locString.capValidationError, [System.Windows.Forms.MessageBoxButtons]::OK)
                if(-not $tabBadInput.Contains($txtSpineSubnet))
                {
                    $null = $tabBadInput.Add($txtSpineSubnet)
                }
            }
            else
            {
                $txtSpineSubnet.ForeColor = $defaultForeColor
                $null = $tabBadInput.Remove($txtSpineSubnet)
            }
            Update-TextBox $txtSpineSubnet
        }
    }
    $txtSpineSubnet.Add_Leave($txtSpineSubnet_Changed)

    $script:lblSpineSubnetSize = New-Object System.Windows.Forms.Label
    $lblSpineSubnetSize.Location = New-Object System.Drawing.Size(($txtSpineSubnet.Right+10),($txtSpineSubnet.Location.Y+2))
    $lblSpineSubnetSize.AutoSize = $true
    $lblSpineSubnetSize.Font = $defaultLabelFont
    $lblSpineSubnetSize.Text = $locString.lblSubnetSize
    $lblSpineSubnetSize.Enabled = $false
    $lblSpineSubnetSize.Visible = $false
    $tabNetwork.Controls.Add($lblSpineSubnetSize)

    $script:txtSpineSubnetSize = New-Object System.Windows.Forms.TextBox
    $txtSpineSubnetSize.Location = New-Object System.Drawing.Size(($lblSpineSubnetSize.Right+10),($txtSpineSubnet.Location.Y))
    $txtSpineSubnetSize.Size = New-Object System.Drawing.Size($txtCountSize)
    $txtSpineSubnetSize.Font = $defaultLabelFont
    $txtSpineSubnetSize.Name = 'txtSpineSubnetSize'
    $txtSpineSubnetSize.MaxLength = 2
    $txtSpineSubnetSize.Text = $defaultSpineSubnetSize
    $txtSpineSubnetSize.Enabled = $false
    $txtSpineSubnetSize.Visible = $false
    $txtSpineSubnetSize.BackColor = $defaultBackColor
    $txtSpineSubnetSize.ForeColor = $defaultForeColor
    $txtSpineSubnetSize.ReadOnly = $true
    $tabNetwork.Controls.Add($txtSpineSubnetSize)
    $txtSpineSubnetSize_Changed = `
    {
        $txtSpineSubnetSize.ForeColor = $defaultForeColor
    }
    $txtSpineSubnetSize.Add_Leave($txtSpineSubnetSize_Changed)

    if($txtScaleUnitCount.Text -eq '1')
    {
        $lblSpineModel.Enabled = $false
        $lblSpineModel.Visible = $false
        $lstSpineModel.Enabled = $false
        $lstSpineModel.Visible = $false
        $lblSpineModel.Refresh()
        $lstSpineModel.Refresh()
        $lblSpineFirmware.Enabled = $false
        $lblSpineFirmware.Visible = $false
        $lstSpineFirmware.Enabled = $false
        $lstSpineFirmware.Visible = $false
        $lblSpineFirmware.Refresh()
        $lstSpineFirmware.Refresh()
        Disable-SpineBGP
        Disable-SpineCount
    }
    else
    {
        $lblSpineModel.Enabled = $true
        $lblSpineModel.Visible = $true
        $lstSpineModel.Enabled = $true
        $lstSpineModel.Visible = $true
        $lblSpineModel.Refresh()
        $lstSpineModel.Refresh()
        $lblSpineFirmware.Enabled = $true
        $lblSpineFirmware.Visible = $true
        $lstSpineFirmware.Enabled = $true
        $lstSpineFirmware.Visible = $true
        $lblSpineFirmware.Refresh()
        $lstSpineFirmware.Refresh()
        if($isStaticRouting)
        {
            Disable-SpineBGP
        }
        else
        {
            Enable-SpineBGP
        }
        Enable-SpineCount
    }

    $row2 = $rowSwInfo

    # ---------------------------- CLOUD SUBNETS ------------------------------
    $script:lblCloudSubnets = New-Object System.Windows.Forms.Label
    $lblCloudSubnets.Location = New-Object System.Drawing.Size($lblLeft2,($row2))
    $lblCloudSubnets.AutoSize = $true
    $lblCloudSubnets.Text = $locString.lblCloudSubnets
    $lblCloudSubnets.Font = [System.Drawing.Font]::New("Microsoft Sans Serif", 10, [System.Drawing.FontStyle]::Bold)
    $tabNetwork.Controls.Add($lblCloudSubnets)

    $row2 += 30

    # --------------------------- EXTERNAL SUBNET -----------------------------
    $script:lblExternalSubnet = New-Object System.Windows.Forms.Label
    $lblExternalSubnet.Location = New-Object System.Drawing.Size($lblLeft2,($row2+2))
    $lblExternalSubnet.MaximumSize = New-Object System.Drawing.Size($lblMaxSize)
    $lblExternalSubnet.AutoSize = $true
    $lblExternalSubnet.Font = $defaultLabelFont
    $lblExternalSubnet.Text = $locString.lblExternalSubnet
    $tabNetwork.Controls.Add($lblExternalSubnet)

    $script:txtExternalSubnet = New-Object System.Windows.Forms.TextBox
    $txtExternalSubnet.Location = New-Object System.Drawing.Size($txtLeft2,$row2)
    $txtExternalSubnet.Size = New-Object System.Drawing.Size($txtPrefixSize)
    $txtExternalSubnet.Font = $defaultLabelFont
    $txtExternalSubnet.Name = 'txtExternalSubnet'
    $txtExternalSubnet.MaxLength = $lenIPAddress
    $tabNetwork.Controls.Add($txtExternalSubnet)
    $txtExternalSubnet_Changed = `
    {
        if(-not [string]::IsNullOrEmpty($txtExternalSubnet.Text))
        {
            $itemName = ($locString.lblExternalSubnet).Replace(':','')
            if($txtExternalSubnet.Text -notmatch $validIPAddress)
            {
                $txtExternalSubnet.ForeColor = $defaultFailColor
                Close-WaitForm
                $message = "'$($itemName)' " + $locString.errIPAddressInvalid
                [System.Windows.Forms.MessageBox]::Show($message, $locString.capValidationError, [System.Windows.Forms.MessageBoxButtons]::OK)
                if(-not $tabBadInput.Contains($txtExternalSubnet))
                {
                    $null = $tabBadInput.Add($txtExternalSubnet)
                }
            }
            elseif(-not(Get-IsValidSubnet -IPAddress $txtExternalSubnet.Text -CIDR $txtExternalSubnetSize.Text))
            {
                $txtExternalSubnet.ForeColor = $defaultFailColor
                Close-WaitForm
                $message = "'$($itemName)' " + ($locString.errSubnetInvalid -f $txtExternalSubnet.Text,$txtExternalSubnetSize.Text)
                [System.Windows.Forms.MessageBox]::Show($message, $locString.capValidationError, [System.Windows.Forms.MessageBoxButtons]::OK)
                if(-not $tabBadInput.Contains($txtExternalSubnet))
                {
                    $null = $tabBadInput.Add($txtExternalSubnet)
                }
            }
            else
            {
                $txtExternalSubnet.ForeColor = $defaultForeColor
                $null = $tabBadInput.Remove($txtExternalSubnet)
            }
            Update-TextBox $txtExternalSubnet
        }
    }
    $txtExternalSubnet.Add_Leave($txtExternalSubnet_Changed)

    $script:lblExternalSubnetSize = New-Object System.Windows.Forms.Label
    $lblExternalSubnetSize.Location = New-Object System.Drawing.Size(($txtExternalSubnet.Right+10),($txtExternalSubnet.Location.Y+2))
    $lblExternalSubnetSize.AutoSize = $true
    $lblExternalSubnetSize.Font = $defaultLabelFont
    $lblExternalSubnetSize.Text = $locString.lblSubnetSize
    $lblExternalSubnetSize.Name = 'lblExternalSubnetSize'
    $tabNetwork.Controls.Add($lblExternalSubnetSize)

    $script:txtExternalSubnetSize = New-Object System.Windows.Forms.TextBox
    $txtExternalSubnetSize.Location = New-Object System.Drawing.Size(($lblExternalSubnetSize.Right+10),($txtExternalSubnet.Location.Y))
    $txtExternalSubnetSize.Size = New-Object System.Drawing.Size($txtCountSize)
    $txtExternalSubnetSize.Font = $defaultLabelFont
    $txtExternalSubnetSize.Name = 'txtExternalSubnetSize'
    $txtExternalSubnetSize.AccessibleName = 'txtExternalSubnetSize'
    $txtExternalSubnetSize.AccessibleDescription = $locString.accExternalSubnetSize
    $txtExternalSubnetSize.MaxLength = 2
    $txtExternalSubnetSize.Text = $defaultExternalSubnetSize
    $tabNetwork.Controls.Add($txtExternalSubnetSize)
    $txtExternalSubnetSize_Changed = `
    {
        [string]$message = ''
        $itemName = ($locString.lblExternalSubnet).Replace(':','') + " " + ($locString.lblSubnetSize).Replace(' /','')
        $valid = $true
        if(-not([string]::IsNullOrEmpty($txtExternalSubnetSize.Text)) -and ($txtExternalSubnetSize.Text -notmatch $validNumber))
        {
            $valid = $false
        }
        elseif(([int]$txtExternalSubnetSize.Text -gt $validSubnetExternalMax) -or ([int]$txtExternalSubnetSize.Text -lt $validSubnetExternalMin))
        {
            $valid = $false
        }
        elseif((-not([string]::IsNullOrEmpty($txtExternalSubnet.Text))) -and ($txtExternalSubnet.Text -match $validIPAddress))
        {
            if(-not(Get-IsValidSubnet -IPAddress $txtExternalSubnet.Text -CIDR $txtExternalSubnetSize.Text))
            {
                $message = "'$($itemName)' " + ($locString.errSubnetInvalid -f $txtExternalSubnet.Text,$txtExternalSubnetSize.Text)
                $valid = $false
            }
        }
        if(-not $valid)
        {
            $txtExternalSubnetSize.ForeColor = $defaultFailColor
            Close-WaitForm
            if(-not $message)
            {
                [string]$message = "'$($itemName)' " + ($locString.errNumberNotInRange -f $validSubnetExternalMin,$validSubnetExternalMax)
            }
            [System.Windows.Forms.MessageBox]::Show($message, $locString.capValidationError, [System.Windows.Forms.MessageBoxButtons]::OK)
            $txtExternalSubnetSize.Text = $defaultExternalSubnetSize
        }
        else
        {
            $txtExternalSubnetSize.ForeColor = $defaultForeColor
        }
        Update-TextBox $txtExternalSubnetSize
    }
    $txtExternalSubnetSize.Add_Leave($txtExternalSubnetSize_Changed)

    $row2 += 30

    # ---------------------------- PRIVATE SUBNET -----------------------------
    $script:lblPrivateSubnet = New-Object System.Windows.Forms.Label
    $lblPrivateSubnet.Location = New-Object System.Drawing.Size($lblLeft2,($row2+2))
    $lblPrivateSubnet.MaximumSize = New-Object System.Drawing.Size($lblMaxSize)
    $lblPrivateSubnet.AutoSize = $true
    $lblPrivateSubnet.Text = $locString.lblPrivateSubnet
    $lblPrivateSubnet.Font = $defaultLabelFont
    $tabNetwork.Controls.Add($lblPrivateSubnet)

    $script:txtPrivateSubnet = New-Object System.Windows.Forms.TextBox
    $txtPrivateSubnet.Location = New-Object System.Drawing.Size($txtLeft2,$row2)
    $txtPrivateSubnet.Size = New-Object System.Drawing.Size($txtPrefixSize)
    $txtPrivateSubnet.MaxLength = $lenIPAddress
    $txtPrivateSubnet.Font = $defaultLabelFont
    $txtPrivateSubnet.Name = 'txtPrivateSubnet'
    $tabNetwork.Controls.Add($txtPrivateSubnet)
    $script:txtPrivateSubnet_Changed = `
    {
        if(-not([string]::IsNullOrEmpty($txtPrivateSubnet.Text)))
        {
            $itemName = ($locString.lblPrivateSubnet).Replace(':','')
            $txtPrivateSubnet.Text = ($txtPrivateSubnet.Text).Trim()
            if($txtPrivateSubnet.Text -notmatch $validIPAddress)
            {
                $txtPrivateSubnet.ForeColor = $defaultFailColor
                Close-WaitForm
                $message = "'$($itemName)' " + $locString.errIPAddressInvalid
                [System.Windows.Forms.MessageBox]::Show($message, $locString.capValidationError, [System.Windows.Forms.MessageBoxButtons]::OK)
                if(-not $tabBadInput.Contains($txtPrivateSubnet))
                {
                    $null = $tabBadInput.Add($txtPrivateSubnet)
                }
            }
            elseif(-not(Get-IsValidSubnet -IPAddress $txtPrivateSubnet.Text -CIDR $txtPrivateSubnetSize.Text))
            {
                $txtPrivateSubnet.ForeColor = $defaultFailColor
                Close-WaitForm
                $message = "'$($itemName)' " + ($locString.errSubnetInvalid -f $txtPrivateSubnet.Text,$txtPrivateSubnetSize.Text)
                [System.Windows.Forms.MessageBox]::Show($message, $locString.capValidationError, [System.Windows.Forms.MessageBoxButtons]::OK)
                if(-not $tabBadInput.Contains($txtPrivateSubnet))
                {
                    $null = $tabBadInput.Add($txtPrivateSubnet)
                }
            }
            else
            {
                $txtPrivateSubnet.ForeColor = $defaultForeColor
                $null = $tabBadInput.Remove($txtPrivateSubnet)
            }
            Update-TextBox $txtPrivateSubnet
        }
        else
        {
            $null = $tabBadInput.Remove($txtPrivateSubnet)
        }
    }
    $txtPrivateSubnet.Add_Leave($txtPrivateSubnet_Changed)

    $lblPrivateSubnetSize = New-Object System.Windows.Forms.Label
    $lblPrivateSubnetSize.Location = New-Object System.Drawing.Size(($txtPrivateSubnet.Right+10),($txtPrivateSubnet.Location.Y+2))
    $lblPrivateSubnetSize.AutoSize = $true
    $lblPrivateSubnetSize.Text = $locString.lblSubnetSize
    $lblPrivateSubnetSize.Name = 'lblPrivateSubnetSize'
    $lblPrivateSubnetSize.Font = $defaultLabelFont
    $tabNetwork.Controls.Add($lblPrivateSubnetSize)

    $txtPrivateSubnetSize = New-Object System.Windows.Forms.TextBox
    $txtPrivateSubnetSize.Location = New-Object System.Drawing.Size(($lblPrivateSubnetSize.Right+10),($txtPrivateSubnet.Location.Y))
    $txtPrivateSubnetSize.Size = New-Object System.Drawing.Size($txtCountSize)
    $txtPrivateSubnetSize.MaxLength = 2
    $txtPrivateSubnetSize.Font = $defaultLabelFont
    $txtPrivateSubnetSize.Text = $defaultPrivateSubnetSize
    $txtPrivateSubnetSize.Name = 'txtPrivateSubnetSize'
    $txtPrivateSubnetSize.AccessibleName = 'txtPrivateSubnetSize'
    $txtPrivateSubnetSize.AccessibleDescription = $locString.accPrivateSubnetSize
    $txtPrivateSubnetSize.Enabled = $false
    $txtPrivateSubnetSize.BackColor = $defaultBackColor
    $txtPrivateSubnetSize.ForeColor = $defaultForeColor
    $txtPrivateSubnetSize.ReadOnly = $true
    $tabNetwork.Controls.Add($txtPrivateSubnetSize)

    $row2 += 50

    # ----------------------------- PERMIT SUBNETS ----------------------------
    $script:lblPermitNetworksHeader = New-Object System.Windows.Forms.Label
    $lblPermitNetworksHeader.Location = New-Object System.Drawing.Size($lblLeft2,($row2))
    $lblPermitNetworksHeader.AutoSize = $true
    $lblPermitNetworksHeader.Text = $locString.lblPermitNetworksHeader
    $lblPermitNetworksHeader.Font = [System.Drawing.Font]::New("Microsoft Sans Serif", 10, [System.Drawing.FontStyle]::Bold)
    $lblPermitNetworksHeader.Visible = $false
    $lblPermitNetworksHeader.Enabled = $false
    $tabNetwork.Controls.Add($lblPermitNetworksHeader)

    $lblPermitNetworksOptional = New-Object System.Windows.Forms.Label
    $lblPermitNetworksOptional.Location = New-Object System.Drawing.Size(($lblPermitNetworksHeader.Right+8),($lblPermitNetworksHeader.Location.Y+2))
    $lblPermitNetworksOptional.MaximumSize = New-Object System.Drawing.Size($lblMaxSize)
    $lblPermitNetworksOptional.AutoSize = $true
    $lblPermitNetworksOptional.Font = $defaultLabelFont
    $lblPermitNetworksOptional.Text = $locString.strOptionalNote
    $lblPermitNetworksOptional.Visible = $false
    $lblPermitNetworksOptional.Enabled = $false
    $tabNetwork.Controls.Add($lblPermitNetworksOptional)

    $row2 += 30

    $script:lblPermitNetworks = New-Object System.Windows.Forms.Label
    $lblPermitNetworks.Location = New-Object System.Drawing.Size($lblLeft2,($row2+2))
    $lblPermitNetworks.MaximumSize = New-Object System.Drawing.Size($lblMaxSize)
    $lblPermitNetworks.AutoSize = $true
    $lblPermitNetworks.Font = $defaultLabelFont
    $lblPermitNetworks.Text = $locString.lblPermitNetworks
    $lblPermitNetworks.Visible = $false
    $lblPermitNetworks.Enabled = $false
    $tabNetwork.Controls.Add($lblPermitNetworks)

    $script:txtPermitNetworks = New-Object System.Windows.Forms.RichTextBox
    $txtPermitNetworks.Location = New-Object System.Drawing.Size($txtLeft2,$row2)
    $txtPermitNetworks.Size = New-Object System.Drawing.Size($txtSizeNet)
    $txtPermitNetworks.Font = $defaultLabelFont
    $txtPermitNetworks.Name = 'txtPermitNetworks'
    $txtPermitNetworks.AccessibleName = $locString.accPermitText
    $txtPermitNetworks.Multiline = $false
    $txtPermitNetworks.Add_MouseHover($ShowHelp)
    $txtPermitNetworks.Visible = $false
    $txtPermitNetworks.Enabled = $false
    $tabNetwork.Controls.Add($txtPermitNetworks)

    $btnPermitNetworksAdd = New-Object System.Windows.Forms.Button
    $btnPermitNetworksAdd.Location = New-Object System.Drawing.Size(($txtPermitNetworks.Right+4),($txtPermitNetworks.Location.Y))
    $btnPermitNetworksAdd.Size = New-Object System.Drawing.Size(20,20)
    $btnPermitNetworksAdd.Font = [System.Drawing.Font]::New("Microsoft Sans Serif", 10, [System.Drawing.FontStyle]::Bold)
    $btnPermitNetworksAdd.Text = "+"
    $btnPermitNetworksAdd.Name = "btnPermitNetworksAdd"
    $btnPermitNetworksAdd.AccessibleName = $locString.accPermitBtnAdd
    $btnPermitNetworksAdd.AccessibleDescription = $locString.accPermitBtnAddDesc
    $btnPermitNetworksAdd.Add_MouseHover($ShowHelp)
    $btnPermitNetworksAdd.Visible = $false
    $btnPermitNetworksAdd.Enabled = $false
    $btnPermitNetworksAdd.Add_Click({Add-PermitNetworks -InputTextBox $txtPermitNetworks -OutputListBox $lstPermitNetworks})
    $tabNetwork.Controls.Add($btnPermitNetworksAdd)

    $row2 += 20

    $script:lstPermitNetworks = New-Object System.Windows.Forms.ListBox
    $lstPermitNetworks.Location = New-Object System.Drawing.Size($txtLeft2,$row2)
    $lstPermitNetworks.ItemHeight = $txtSize[1]
    $lstPermitNetworks.ScrollAlwaysVisible = $true
    $lstPermitNetworks.SelectionMode = 'MultiExtended'
    $lstPermitNetworks.Size = New-Object System.Drawing.Size($txtPermitNetworks.Size.Width,($txtPermitNetworks.Size.Height*8))
    $lstPermitNetworks.MaximumSize = New-Object System.Drawing.Size($txtPermitNetworks.Size.Width,($txtPermitNetworks.Size.Height*40))
    $lstPermitNetworks.Font = $defaultLabelFont
    $lstPermitNetworks.Name = 'lstPermitNetworks'
    $lstPermitNetworks.AccessibleName = $locString.accPermitList
    $lstPermitNetworks.Visible = $false
    $lstPermitNetworks.Enabled = $false
    $tabNetwork.Controls.Add($lstPermitNetworks)

    $btnPermitNetworksDel = New-Object System.Windows.Forms.Button
    $btnPermitNetworksDel.Location = New-Object System.Drawing.Size(($lstPermitNetworks.Right+4),($lstPermitNetworks.Location.Y))
    $btnPermitNetworksDel.Size = New-Object System.Drawing.Size(20,20)
    $btnPermitNetworksDel.Font = [System.Drawing.Font]::New("Microsoft Sans Serif", 10, [System.Drawing.FontStyle]::Bold)
    $btnPermitNetworksDel.Text = "-"
    $btnPermitNetworksDel.Name = "btnPermitNetworksDel"
    $btnPermitNetworksDel.AccessibleName = $locString.accPermitBtnDel
    $btnPermitNetworksDel.AccessibleDescription = $locString.accPermitBtnDelDesc
    $btnPermitNetworksDel.Add_MouseHover($ShowHelp)
    $btnPermitNetworksDel.Visible = $false
    $btnPermitNetworksDel.Enabled = $false
    $btnPermitNetworksDel.Add_Click({Remove-PermitNetworks -OutputListBox $lstPermitNetworks})
    $tabNetwork.Controls.Add($btnPermitNetworksDel)
    Enable-PermitNetworks

    $row2 += $lstPermitNetworks.Size.Height + 10

    function Add-PermitNetworks
    {
        param
        (
            $InputTextBox,
            $OutputListBox
        )
        $InputTextBox.Text = ($InputTextBox.Text).Trim()
        if(-not([string]::IsNullOrEmpty($InputTextBox.Text)))
        {
            $InputTextBox.Text = [string]($InputTextBox.Text).Replace(' ','')
            $InputTextBox.Text = [string]($InputTextBox.Text).Replace(';',',')
            $errList = @()
            $dupeList = @()
            $addList = @()
            $msgList = @()
            if($InputTextBox.Text -match ',')
            {
                ($InputTextBox.Text).Split(',').Trim() | Foreach-Object `
                {
                    $addList += $PSItem
                }
            }
            else
            {
                $addList += $InputTextBox.Text
            }
            foreach($item in $addList)
            {
                if(($item.ToCharArray() | Where-Object {$PSItem -eq '/'}).Count -ne 1)
                {
                    Write-Log -Message "$($MyInvocation.MyCommand.Name) - $($locString.msgSubnetMissingSlash)" -Force
                    $errList += $item
                }
                else
                {
                    if($item -eq '0.0.0.0/0')
                    {
                        if($OutputListBox.Items -match $item)
                        {
                            $dupeList += $item
                        }
                        else
                        {
                            Write-Log -Message "$($MyInvocation.MyCommand.Name) - WARNING: $($locString.msgSubnetWildcard)" -Force
                            Close-WaitForm
                            [System.Windows.Forms.MessageBox]::Show($locString.msgSubnetWildcard, $locString.capWarning, [System.Windows.Forms.MessageBoxButtons]::OK)
                            $InputTextBox.SelectionStart = $InputTextBox.Text.IndexOf($item)
                            $InputTextBox.SelectionLength = $item.Length
                            [void]$OutputListBox.Items.Add($InputTextBox.SelectedText)
                            $InputTextBox.SelectedText = ''
                            $InputTextBox.SelectionStart = 0
                            $InputTextBox.SelectionLength = 0
                            $InputTextBox.SelectionColor = $defaultForeColor
                        }
                    }
                    else
                    {
                        $ip = $item.Split('/')[0]
                        $sz = $item.Split('/')[1]
                        if($ip -notmatch $validIPAddress)
                        {
                            Write-Log -Message "$($MyInvocation.MyCommand.Name) - $($locString.msgSubnetIPv4Invalid -f $ip)" -Force
                            $errList += $item
                        }
                        elseif(($sz -notmatch $validNumber) -or ([int]$sz -gt $validPermitNetworksMax) -or ([int]$sz -lt $validPermitNetworksMin))
                        {
                            Write-Log -Message "$($MyInvocation.MyCommand.Name) - $($locString.msgSubnetCIDRInvalid -f $sz)" -Force
                            $errList += $item
                        }
                        elseif(-not(Get-IsValidSubnet -IPAddress $ip -CIDR $sz))
                        {
                            Write-Log -Message "$($MyInvocation.MyCommand.Name) - $($locString.msgSubnetIPv4Invalid -f $item)" -Force
                            $errList += $item
                        }
                        else
                        {
                            if($OutputListBox.Items -match $item)
                            {
                                $dupeList += $item
                            }
                            else
                            {
                                $InputTextBox.SelectionStart = $InputTextBox.Text.IndexOf($item)
                                $InputTextBox.SelectionLength = $item.Length
                                [void]$OutputListBox.Items.Add($InputTextBox.SelectedText)
                                $InputTextBox.SelectedText = ''
                                $InputTextBox.SelectionStart = 0
                                $InputTextBox.SelectionLength = 0
                                $InputTextBox.SelectionColor = $defaultForeColor
                            }
                        }
                    }
                }
            }
            $InputTextBox.Text = ($InputTextBox.Text).Trim()
            $InputTextBox.Text = ($InputTextBox.Text).Replace(',,','')
            $commas = ($InputTextBox.Text | Select-String ',' -AllMatches).Matches.Index
            $commas | ForEach-Object `
            {
                if($PSItem -eq 0)
                {
                    $InputTextBox.SelectionStart = $PSItem
                    $InputTextBox.SelectionLength = 1
                    $InputTextBox.SelectedText = ''
                }
                elseif($PSItem -eq ($InputTextBox.Text).Length-1)
                {
                    $InputTextBox.SelectionStart = $PSItem
                    $InputTextBox.SelectionLength = 1
                    $InputTextBox.SelectedText = ''
                }
            }
            if($dupeList.Count -gt 0)
            {
                $dupeList | ForEach-Object `
                {
                    [string]$msgList += "$($locString.msgDuplicate) $($PSItem)`n"
                    $InputTextBox.SelectionStart = $InputTextBox.Text.IndexOf($PSItem)
                    $InputTextBox.SelectionLength = $PSItem.Length
                    $InputTextBox.SelectionColor = $defaultDupeColor
                }
            }
            if($errList.Count -gt 0)
            {
                $errList | ForEach-Object `
                {
                    [string]$msgList += "$($locString.errInvalidIPNetwork) $($PSItem)`n"
                    $InputTextBox.SelectionStart = $InputTextBox.Text.IndexOf($PSItem)
                    $InputTextBox.SelectionLength = $PSItem.Length
                    $InputTextBox.SelectionColor = $defaultFailColor
                }
            }
            if($msgList.Count -gt 0)
            {
                Close-WaitForm
                [System.Windows.Forms.MessageBox]::Show($msgList, $locString.capValidationError, [System.Windows.Forms.MessageBoxButtons]::OK)
            }
            $InputTextBox.SelectionStart = 0
            $InputTextBox.SelectionLength = 0
            $InputTextBox.SelectionColor = $defaultForeColor
        }
    }

    function Remove-PermitNetworks
    {
        param
        (
            $OutputListBox
        )
        if($OutputListBox.SelectedItems.Count -ge 1)
        {
            while($OutputListBox.SelectedItems)
            {
                $OutputListBox.Items.Remove($OutputListBox.SelectedItems[0])
            }
        }
    }

    # ============================== BORDER TAB ===============================
    $tabRow = 20

    # ---------------------------- BORDER SETTINGS ----------------------------
    $script:lblBorderSettings = New-Object System.Windows.Forms.Label
    $lblBorderSettings.Location = New-Object System.Drawing.Size($lblLeft,($tabRow))
    $lblBorderSettings.AutoSize = $true
    $lblBorderSettings.Text = $locString.tabBorder
    $lblBorderSettings.Font = [System.Drawing.Font]::New("Microsoft Sans Serif", 12, [System.Drawing.FontStyle]::Bold)
    $tabBorder.Controls.Add($lblBorderSettings)

    $tabRow += 40
    $tabRow2 = $tabRow

    # -------------------------- BORDER CONNECTIVITY --------------------------
    $script:lblBorderConnect = New-Object System.Windows.Forms.Label
    $lblBorderConnect.Location = New-Object System.Drawing.Size($lblLeft,($tabRow))
    $lblBorderConnect.AutoSize = $true
    $lblBorderConnect.Text = $locString.lblBorderConnect
    $lblBorderConnect.Font = [System.Drawing.Font]::New("Microsoft Sans Serif", 10, [System.Drawing.FontStyle]::Bold)
    $tabBorder.Controls.Add($lblBorderConnect)

    $tabRow += 30

    # --------------------------- ROUTING METHOD ------------------------------
    $script:lblRoutingMethod = New-Object System.Windows.Forms.Label
    $lblRoutingMethod.Location = New-Object System.Drawing.Size($lblLeft,($tabRow+2))
    $lblRoutingMethod.MaximumSize = New-Object System.Drawing.Size($lblMaxSize)
    $lblRoutingMethod.AutoSize = $true
    $lblRoutingMethod.Font = $defaultLabelFont
    $lblRoutingMethod.Text = $locString.lblRoutingMethod
    $tabBorder.Controls.Add($lblRoutingMethod)

    $script:lstRoutingMethod = New-Object System.Windows.Forms.ComboBox
    $lstRoutingMethod.DropDownStyle = [System.Windows.Forms.ComboBoxStyle]::DropDownList
    $lstRoutingMethod.Location = New-Object System.Drawing.Size($txtLeft,$tabRow)
    $lstRoutingMethod.Size = New-Object System.Drawing.Size($lstRoutingMethodSize)
    $lstRoutingMethod.Font = $defaultLabelFont
    $lstRoutingMethod.Name = 'lstRoutingMethod'
    [void]$lstRoutingMethod.Items.Clear()
    $arrRoutingMethod | Foreach-Object `
    {
        [void]$lstRoutingMethod.Items.Add($PSItem)
    }
    $lstRoutingMethod.SelectedIndex = 0
    $tabBorder.Controls.Add($lstRoutingMethod)
    $lstRoutingMethod_Changed = `
    {
        switch ($lstRoutingMethod.SelectedItem)
        {
            "Static Routing"
            {
                $script:isStaticRouting = $true
                (1..$maxBorder) | ForEach-Object `
                {
                    Disable-BorderBGP $PSItem
                }
                $lblSoftwareBGP.Visible = $false
                $txtSoftwareBGP.Visible = $false
                $txtSoftwareBGP.Text = [string]::Empty
                $lblEdgeBGP.Visible = $false
                $txtEdgeBGP.Visible = $false
                $txtEdgeBGP.Text = [string]::Empty
                (1..$maxSU) | Foreach-Object `
                {
                    $suTab = "tabSU$($PSItem)"
                    if($scaleUnitTabs.$suTab)
                    {
                        $scaleUnitTabs.$suTab.Objects.lblTORASN.Visible = $false
                        $scaleUnitTabs.$suTab.Objects.txtTORASN.Visible = $false
                        $scaleUnitTabs.$suTab.Objects.txtTORASN.Text = [string]::Empty
                    }
                }
                Disable-SpineBGP
            }
            "BGP Routing"
            {
                $script:isStaticRouting = $false
                Invoke-RefreshBorderBGP -BorderCount $txtBorderCount.Text
                $lblSoftwareBGP.Visible = $true
                $txtSoftwareBGP.Visible = $true
                if($isExtStor -and ($lstSwitchVendor.SelectedItem -eq 'msftcisco'))
                {
                    $lblEdgeBGP.Visible = $true
                    $txtEdgeBGP.Visible = $true
                }
                else
                {
                    $lblEdgeBGP.Visible = $false
                    $txtEdgeBGP.Visible = $false
                    $txtEdgeBGP.Text = [string]::Empty
                }
                (1..$maxSU) | Foreach-Object `
                {
                    $suTab = "tabSU$($PSItem)"
                    if($scaleUnitTabs.$suTab)
                    {
                        if(($lstSwitchVendor.SelectedItem -eq "CiscoUCS") -or ($lstSwitchVendor.SelectedItem -eq "Mellanox"))
                        {
                            if($PSItem -eq 1)
                            {
                                $scaleUnitTabs.$suTab.Objects.lblTORASN.Visible = $true
                                $scaleUnitTabs.$suTab.Objects.txtTORASN.Visible = $true
                            }
                            else
                            {
                                $scaleUnitTabs.$suTab.Objects.lblTORASN.Visible = $false
                                $scaleUnitTabs.$suTab.Objects.txtTORASN.Visible = $false
                            }
                        }
                        else
                        {
                            if($lstSwitchVendor.SelectedItem -eq 'msftcisco')
                            {
                                $scaleUnitTabs.$suTab.Objects.lblTORASN.Visible = $false
                                $scaleUnitTabs.$suTab.Objects.txtTORASN.Visible = $false
                            }
                            else
                            {
                                $scaleUnitTabs.$suTab.Objects.lblTORASN.Visible = $true
                                $scaleUnitTabs.$suTab.Objects.txtTORASN.Visible = $true
                            }
                        }
                    }
                }
                if([int]$txtScaleUnitCount.Text -gt 1)
                {
                    if(($lstSwitchVendor.SelectedItem -eq "CiscoUCS") -or ($lstSwitchVendor.SelectedItem -eq "Mellanox"))
                    {
                        Disable-SpineBGP
                    }
                    else
                    {
                        Enable-SpineBGP
                    }
                }
            }
            default
            {
            }
        }
    }
    $lstRoutingMethod.Add_SelectedIndexChanged($lstRoutingMethod_Changed)

    $tabRow += 40

    # ----------------------------- BORDER COUNT ------------------------------
    $script:lblBorderCount = New-Object System.Windows.Forms.Label
    $lblBorderCount.Location = New-Object System.Drawing.Size($lblLeft,($tabRow+2))
    $lblBorderCount.MaximumSize = New-Object System.Drawing.Size($lblMaxSize)
    $lblBorderCount.AutoSize = $true
    $lblBorderCount.Font = $defaultLabelFont
    $lblBorderCount.Text = $locString.lblBorderCount
    $tabBorder.Controls.Add($lblBorderCount)

    $script:txtBorderCount = New-Object System.Windows.Forms.TextBox
    $txtBorderCount.Location = New-Object System.Drawing.Size($txtLeft,$tabRow)
    $txtBorderCount.Size = New-Object System.Drawing.Size($txtCountSize)
    $txtBorderCount.Font = $defaultLabelFont
    $txtBorderCount.Name = 'txtBorderCount'
    $txtBorderCount.TextAlign = [System.Windows.Forms.HorizontalAlignment]::Center
    $txtBorderCount.MaxLength = 1
    $txtBorderCount.Text = $defaultBorderCount
    $script:prevBorderCount = $txtBorderCount.Text
    $tabBorder.Controls.Add($txtBorderCount)
    $txtBorderCount_Changed = `
    {
        if(-not [string]::IsNullOrEmpty($txtBorderCount.Text))
        {
            $txtBorderCount.Text = ($txtBorderCount.Text).Trim()
            if($txtBorderCount.Text -notmatch "^[1-$($validBorder)]+$")
            {
                $itemName = ($locString.lblBorderCount).Replace(':','')
                [string]$errMessage = "'$($itemName)' " + ($locString.errNumberNotInRange -f '1',$validBorder)
                Close-WaitForm
                [System.Windows.Forms.MessageBox]::Show($errMessage, $locString.capValidationError, [System.Windows.Forms.MessageBoxButtons]::OK)
                $txtBorderCount.Text = $prevBorderCount
                if(-not $isStaticRouting)
                {
                    Invoke-RefreshBorderBGP -BorderCount $txtBorderCount.Text
                }
            }
            else
            {
                $txtBorderCount.ForeColor = $defaultForeColor
                if(-not $isStaticRouting)
                {
                    Invoke-RefreshBorderBGP -BorderCount $txtBorderCount.Text
                    $script:prevBorderCount = $txtBorderCount.Text
                }
            }
        }
        else
        {
            $txtBorderCount.ForeColor = $defaultForeColor
        }
    }
    $txtBorderCount.Add_Leave($txtBorderCount_Changed)

    $tabRow += 30

    # --------------------------- BORDER 1 BGP ASN ----------------------------
    $script:lblBorderBGP1 = New-Object System.Windows.Forms.Label
    $lblBorderBGP1.Name = 'lblBorderBGP1'
    $lblBorderBGP1.Location = New-Object System.Drawing.Size($lblLeft,($tabRow+2))
    $lblBorderBGP1.MaximumSize = New-Object System.Drawing.Size($lblMaxSize)
    $lblBorderBGP1.AutoSize = $true
    $lblBorderBGP1.Font = $defaultLabelFont
    $lblBorderBGP1.Text = $locString.lblBorderBGP1
    $tabBorder.Controls.Add($lblBorderBGP1)

    $script:txtBorderBGP1 = New-Object System.Windows.Forms.TextBox
    $txtBorderBGP1.Location = New-Object System.Drawing.Size($txtLeft,$tabRow)
    $txtBorderBGP1.Size = New-Object System.Drawing.Size($txtPrefixSize)
    $txtBorderBGP1.Font = $defaultLabelFont
    $txtBorderBGP1.Name = 'txtBorderBGP1'
    $txtBorderBGP1.TextAlign = [System.Windows.Forms.HorizontalAlignment]::Center
    $txtBorderBGP1.MaxLength = 10
    $tabBorder.Controls.Add($txtBorderBGP1)
    $txtBorderBGP1_Changed = `
    {
        if(-not [string]::IsNullOrEmpty($txtBorderBGP1.Text))
        {
            $txtBorderBGP1.Text = ($txtBorderBGP1.Text).Trim()
            $valid = $true
            if($txtBorderBGP1.Text -notmatch $validNumber)
            {
                $valid = $false
            }
            else
            {
                [uint64]$thisASN = $txtBorderBGP1.Text
                if(-not(Get-IsValidASN -ASN $thisASN))
                {
                    $valid = $false
                }
            }
            if(-not $valid)
            {
                $itemName = ($locString.lblBorderBGP1).Replace(':','')
                [string]$errMessage = "'$($itemName)' " + ($locString.errNumberNotInRange -f $validASNMin,$validASNMax)
                $txtBorderBGP1.ForeColor = $defaultFailColor
                Close-WaitForm
                [System.Windows.Forms.MessageBox]::Show($errMessage, $locString.capValidationError, [System.Windows.Forms.MessageBoxButtons]::OK)
                if(-not $tabBadInput.Contains($txtBorderBGP1))
                {
                    $null = $tabBadInput.Add($txtBorderBGP1)
                }
            }
            else
            {
                $txtBorderBGP1.ForeColor = $defaultForeColor
                $txtBorderBGP1.Text = $txtBorderBGP1.Text -replace '\b0+\B'
                $null = $tabBadInput.Remove($txtBorderBGP1)
            }
        }
        Update-TextBox $txtBorderBGP1
    }
    $txtBorderBGP1.Add_Leave($txtBorderBGP1_Changed)

    $tabRow += 25

    # --------------------------- BORDER 2 BGP ASN ----------------------------
    $script:lblBorderBGP2 = New-Object System.Windows.Forms.Label
    $lblBorderBGP2.Name = 'lblBorderBGP2'
    $lblBorderBGP2.Location = New-Object System.Drawing.Size($lblLeft,($tabRow+2))
    $lblBorderBGP2.MaximumSize = New-Object System.Drawing.Size($lblMaxSize)
    $lblBorderBGP2.AutoSize = $true
    $lblBorderBGP2.Font = $defaultLabelFont
    $lblBorderBGP2.Text = $locString.lblBorderBGP2
    $tabBorder.Controls.Add($lblBorderBGP2)

    $script:txtBorderBGP2 = New-Object System.Windows.Forms.TextBox
    $txtBorderBGP2.Location = New-Object System.Drawing.Size($txtLeft,$tabRow)
    $txtBorderBGP2.Size = New-Object System.Drawing.Size($txtPrefixSize)
    $txtBorderBGP2.Font = $defaultLabelFont
    $txtBorderBGP2.Name = 'txtBorderBGP2'
    $txtBorderBGP2.TextAlign = [System.Windows.Forms.HorizontalAlignment]::Center
    $txtBorderBGP2.MaxLength = 10
    $tabBorder.Controls.Add($txtBorderBGP2)
    $txtBorderBGP2_Changed = `
    {
        if(-not [string]::IsNullOrEmpty($txtBorderBGP2.Text))
        {
            $txtBorderBGP2.Text = ($txtBorderBGP2.Text).Trim()
            $valid = $true
            if($txtBorderBGP2.Text -notmatch $validNumber)
            {
                $valid = $false
            }
            else
            {
                [uint64]$thisASN = $txtBorderBGP2.Text
                if(-not(Get-IsValidASN -ASN $thisASN))
                {
                    $valid = $false
                }
            }
            if(-not $valid)
            {
                $itemName = ($locString.lblBorderBGP2).Replace(':','')
                [string]$errMessage = "'$($itemName)' " + ($locString.errNumberNotInRange -f $validASNMin,$validASNMax)
                $txtBorderBGP2.ForeColor = $defaultFailColor
                Close-WaitForm
                [System.Windows.Forms.MessageBox]::Show($errMessage, $locString.capValidationError, [System.Windows.Forms.MessageBoxButtons]::OK)
                if(-not $tabBadInput.Contains($txtBorderBGP2))
                {
                    $null = $tabBadInput.Add($txtBorderBGP2)
                }
            }
            else
            {
                $txtBorderBGP2.ForeColor = $defaultForeColor
                $txtBorderBGP2.Text = $txtBorderBGP2.Text -replace '\b0+\B'
                $null = $tabBadInput.Remove($txtBorderBGP2)
            }
        }
        Update-TextBox $txtBorderBGP2
    }
    $txtBorderBGP2.Add_Leave($txtBorderBGP2_Changed)

    $tabRow += 25

    # --------------------------- BORDER 3 BGP ASN ----------------------------
    $script:lblBorderBGP3 = New-Object System.Windows.Forms.Label
    $lblBorderBGP3.Name = 'lblBorderBGP3'
    $lblBorderBGP3.Location = New-Object System.Drawing.Size($lblLeft,($tabRow+2))
    $lblBorderBGP3.MaximumSize = New-Object System.Drawing.Size($lblMaxSize)
    $lblBorderBGP3.AutoSize = $true
    $lblBorderBGP3.Font = $defaultLabelFont
    $lblBorderBGP3.Text = $locString.lblBorderBGP3
    $lblBorderBGP3.Enabled = $false
    $lblBorderBGP3.Visible = $false
    $tabBorder.Controls.Add($lblBorderBGP3)

    $script:txtBorderBGP3 = New-Object System.Windows.Forms.TextBox
    $txtBorderBGP3.Location = New-Object System.Drawing.Size($txtLeft,$tabRow)
    $txtBorderBGP3.Size = New-Object System.Drawing.Size($txtPrefixSize)
    $txtBorderBGP3.Name = 'txtBorderBGP3'
    $txtBorderBGP3.TextAlign = [System.Windows.Forms.HorizontalAlignment]::Center
    $txtBorderBGP3.Font = $defaultLabelFont
    $txtBorderBGP3.MaxLength = 10
    $txtBorderBGP3.Enabled = $false
    $txtBorderBGP3.Visible = $false
    $tabBorder.Controls.Add($txtBorderBGP3)
    $txtBorderBGP3_Changed = `
    {
        if(-not [string]::IsNullOrEmpty($txtBorderBGP3.Text))
        {
            $txtBorderBGP3.Text = ($txtBorderBGP3.Text).Trim()
            $valid = $true
            if($txtBorderBGP3.Text -notmatch $validNumber)
            {
                $valid = $false
            }
            else
            {
                [uint64]$thisASN = $txtBorderBGP3.Text
                if(-not(Get-IsValidASN -ASN $thisASN))
                {
                    $valid = $false
                }
            }
            if(-not $valid)
            {
                $itemName = ($locString.lblBorderBGP3).Replace(':','')
                [string]$errMessage = "'$($itemName)' " + ($locString.errNumberNotInRange -f $validASNMin,$validASNMax)
                $txtBorderBGP3.ForeColor = $defaultFailColor
                Close-WaitForm
                [System.Windows.Forms.MessageBox]::Show($errMessage, $locString.capValidationError, [System.Windows.Forms.MessageBoxButtons]::OK)
                if(-not $tabBadInput.Contains($txtBorderBGP3))
                {
                    $null = $tabBadInput.Add($txtBorderBGP3)
                }
            }
            else
            {
                $txtBorderBGP3.ForeColor = $defaultForeColor
                $txtBorderBGP3.Text = $txtBorderBGP3.Text -replace '\b0+\B'
                $null = $tabBadInput.Remove($txtBorderBGP3)
            }
        }
        Update-TextBox $txtBorderBGP3
    }
    $txtBorderBGP3.Add_Leave($txtBorderBGP3_Changed)

    $tabRow += 25

    # --------------------------- BORDER 4 BGP ASN ----------------------------
    $script:lblBorderBGP4 = New-Object System.Windows.Forms.Label
    $lblBorderBGP4.Name = 'lblBorderBGP4'
    $lblBorderBGP4.Location = New-Object System.Drawing.Size($lblLeft,($tabRow+2))
    $lblBorderBGP4.MaximumSize = New-Object System.Drawing.Size($lblMaxSize)
    $lblBorderBGP4.AutoSize = $true
    $lblBorderBGP4.Font = $defaultLabelFont
    $lblBorderBGP4.Text = $locString.lblBorderBGP4
    $lblBorderBGP4.Enabled = $false
    $lblBorderBGP4.Visible = $false
    $tabBorder.Controls.Add($lblBorderBGP4)

    $script:txtBorderBGP4 = New-Object System.Windows.Forms.TextBox
    $txtBorderBGP4.Location = New-Object System.Drawing.Size($txtLeft,$tabRow)
    $txtBorderBGP4.Size = New-Object System.Drawing.Size($txtPrefixSize)
    $txtBorderBGP4.Font = $defaultLabelFont
    $txtBorderBGP4.Name = 'txtBorderBGP4'
    $txtBorderBGP4.TextAlign = [System.Windows.Forms.HorizontalAlignment]::Center
    $txtBorderBGP4.MaxLength = 10
    $txtBorderBGP4.Enabled = $false
    $txtBorderBGP4.Visible = $false
    $tabBorder.Controls.Add($txtBorderBGP4)
    $txtBorderBGP4_Changed = `
    {
        if(-not [string]::IsNullOrEmpty($txtBorderBGP4.Text))
        {
            $txtBorderBGP4.Text = ($txtBorderBGP4.Text).Trim()
            $valid = $true
            if($txtBorderBGP4.Text -notmatch $validNumber)
            {
                $valid = $false
            }
            else
            {
                [uint64]$thisASN = $txtBorderBGP4.Text
                if(-not(Get-IsValidASN -ASN $thisASN))
                {
                    $valid = $false
                }
            }
            if(-not $valid)
            {
                $itemName = ($locString.lblBorderBGP4).Replace(':','')
                [string]$errMessage = "'$($itemName)' " + ($locString.errNumberNotInRange -f $validASNMin,$validASNMax)
                $txtBorderBGP4.ForeColor = $defaultFailColor
                Close-WaitForm
                [System.Windows.Forms.MessageBox]::Show($errMessage, $locString.capValidationError, [System.Windows.Forms.MessageBoxButtons]::OK)
                if(-not $tabBadInput.Contains($txtBorderBGP4))
                {
                    $null = $tabBadInput.Add($txtBorderBGP4)
                }
            }
            else
            {
                $txtBorderBGP4.ForeColor = $defaultForeColor
                $txtBorderBGP4.Text = $txtBorderBGP4.Text -replace '\b0+\B'
                $null = $tabBadInput.Remove($txtBorderBGP4)
            }
        }
        Update-TextBox $txtBorderBGP4
    }
    $txtBorderBGP4.Add_Leave($txtBorderBGP4_Changed)

    $tabRow += 40

    # ----------------------------- EDGE BGP ASN ------------------------------
    $script:lblEdgeBGP = New-Object System.Windows.Forms.Label
    $lblEdgeBGP.Name = 'lblEdgeBGP'
    $lblEdgeBGP.Location = New-Object System.Drawing.Size($lblLeft,($tabRow+2))
    $lblEdgeBGP.MaximumSize = New-Object System.Drawing.Size($lblMaxSize)
    $lblEdgeBGP.AutoSize = $true
    $lblEdgeBGP.Font = $defaultLabelFont
    $lblEdgeBGP.Text = $locString.lblEdgeBGP
    $lblEdgeBGP.Visible = $false
    $tabBorder.Controls.Add($lblEdgeBGP)

    $script:txtEdgeBGP = New-Object System.Windows.Forms.TextBox
    $txtEdgeBGP.Location = New-Object System.Drawing.Size($txtLeft,$tabRow)
    $txtEdgeBGP.Size = New-Object System.Drawing.Size($txtPrefixSize)
    $txtEdgeBGP.Font = $defaultLabelFont
    $txtEdgeBGP.Name = 'txtEdgeBGP'
    $txtEdgeBGP.TextAlign = [System.Windows.Forms.HorizontalAlignment]::Center
    $txtEdgeBGP.MaxLength = 10
    $txtEdgeBGP.Visible = $false
    $tabBorder.Controls.Add($txtEdgeBGP)
    $txtEdgeBGP_Changed = `
    {
        if(-not [string]::IsNullOrEmpty($txtEdgeBGP.Text))
        {
            $txtEdgeBGP.Text = ($txtEdgeBGP.Text).Trim()
            $valid = $true
            if($txtEdgeBGP.Text -notmatch $validNumber)
            {
                $valid = $false
            }
            else
            {
                [uint64]$thisASN = $txtEdgeBGP.Text
                if(-not(Get-IsValidASN -ASN $thisASN))
                {
                    $valid = $false
                }
            }
            if(-not $valid)
            {
                $itemName = ($locString.lblEdgeBGP).Replace(':','')
                [string]$errMessage = "'$($itemName)' " + ($locString.errNumberNotInRange -f $validASNMin,$validASNMax)
                $txtEdgeBGP.ForeColor = $defaultFailColor
                Close-WaitForm
                [System.Windows.Forms.MessageBox]::Show($errMessage, $locString.capValidationError, [System.Windows.Forms.MessageBoxButtons]::OK)
                if(-not $tabBadInput.Contains($txtEdgeBGP))
                {
                    $null = $tabBadInput.Add($txtEdgeBGP)
                }
            }
            else
            {
                $txtEdgeBGP.ForeColor = $defaultForeColor
                $txtEdgeBGP.Text = $txtEdgeBGP.Text -replace '\b0+\B'
                $null = $tabBadInput.Remove($txtEdgeBGP)
            }
        }
        Update-TextBox $txtEdgeBGP
    }
    $txtEdgeBGP.Add_Leave($txtEdgeBGP_Changed)

    $tabRow += 40

    # -------------------------------- UPLINKS --------------------------------
    $script:lblUplinks = New-Object System.Windows.Forms.Label
    $lblUplinks.Location = New-Object System.Drawing.Size($lblLeft,($tabRow))
    $lblUplinks.AutoSize = $true
    $lblUplinks.Text = $locString.lblUplinks
    $lblUplinks.Font = [System.Drawing.Font]::New("Microsoft Sans Serif", 10, [System.Drawing.FontStyle]::Bold)
    $lblUplinks.Visible = $false
    $lblUplinks.Enabled = $false
    $tabBorder.Controls.Add($lblUplinks)

    $tabRow += 30

    # ----------------------------- UPLINK SPEED ------------------------------
    $script:lblUplinkSpeed = New-Object System.Windows.Forms.Label
    $lblUplinkSpeed.Location = New-Object System.Drawing.Size($lblLeft,($tabRow+2))
    $lblUplinkSpeed.MaximumSize = New-Object System.Drawing.Size($lblMaxSize)
    $lblUplinkSpeed.AutoSize = $true
    $lblUplinkSpeed.Font = $defaultLabelFont
    $lblUplinkSpeed.Text = $locString.lblUplinkSpeed
    $lblUplinkSpeed.Visible = $false
    $lblUplinkSpeed.Enabled = $false
    $tabBorder.Controls.Add($lblUplinkSpeed)

    $script:lstUplinkSpeed = New-Object System.Windows.Forms.ComboBox
    $lstUplinkSpeed.DropDownStyle = [System.Windows.Forms.ComboBoxStyle]::DropDownList
    $lstUplinkSpeed.Location = New-Object System.Drawing.Size($txtLeft,$tabRow)
    $lstUplinkSpeed.Size = New-Object System.Drawing.Size($lstRoutingMethodSize)
    $lstUplinkSpeed.Font = $defaultLabelFont
    $lstUplinkSpeed.Name = 'lstUplinkSpeed'
    $lstUplinkSpeed.Visible = $false
    $lstUplinkSpeed.Enabled = $false
    $UpdateUplinkSpeedItems = `
    {
        [void]$lstUplinkSpeed.Items.Clear()
        if($arrUplinkSpeed.Count -gt 0)
        {
            $arrUplinkSpeed | Foreach-Object `
            {
                [void]$lstUplinkSpeed.Items.Add($PSItem)
            }
            $lstUplinkSpeed.SelectedIndex = -1
        }
    }
    Invoke-Command -ScriptBlock $UpdateUplinkSpeedItems
    Disable-UplinkSpeed
    $tabBorder.Controls.Add($lstUplinkSpeed)

    # ============================= SHOW THE FORM =============================
    if($EnableExperimental)
    {
        $menuScenario.DropDownItems.AddRange(@($menuItemCustomer,(New-Object System.Windows.Forms.ToolStripSeparator),$menuItemPreviewMultiSU,$menuItemPreviewAddSU,$menuItemPreviewExtStor))
        Enable-PermitNetworks
    }
    else
    {
        $menuScenario.DropDownItems.AddRange(@($menuItemCustomer))
    }
    $frmMain.Controls.Add($frmMenu)
    $frmMain.MainMenuStrip = $frmMenu
    $frmMain.Controls.Add($tabControl)
    $frmMain.Topmost = $false
    $frmMain.Add_Shown({$frmMain.Activate()})
    if(Test-Path $strPartnerJsonFile)
    {
        Import-PartnerExtension -FileName $strPartnerJsonFile
    }
    $frmMain.ResumeLayout()
    $lnkCustomerDoc.Select()
    [void]$frmMain.ShowDialog()
    [void]$frmMain.Dispose()
    [System.GC]::Collect()
}

function New-SubnetSummary
{
    param
    (
        [bool]$ReadOnly = $true
    )
    $script:tabSubnetSummary = New-Object System.Windows.Forms.TabPage
    $typTab = $tabSubnetSummary.GetType()
    $prpTab = $typTab.GetProperty('DoubleBuffered',('Instance','NonPublic'))
    $prpTab.SetValue($tabSubnetSummary, $true, $null)
    $tabSubnetSummary.Name = 'tabSubnetSummary'
    $tabSubnetSummary.Text = $locString.tabSubnetSummary
    $tabSubnetSummary.AutoScroll = $true
    $tabSubnetSummary.BackColor = $summaryBackColor
    $tabSubnetSummary.ForeColor = $summaryForeColor

    $lblSubnetSummary = New-Object System.Windows.Forms.Label
    $lblSubnetSummary.Location = New-Object System.Drawing.Size(10,10)
    $lblSubnetSummary.AutoSize = $true
    $lblSubnetSummary.Text = $locString.tabSubnetSummary
    $lblSubnetSummary.Font = [System.Drawing.Font]::New("Microsoft Sans Serif", 10)
    $lblSubnetSummary.ForeColor = $summaryForeColor
    $tabSubnetSummary.Controls.Add($lblSubnetSummary)

    $script:dgvSubnetSummary = New-Object System.Windows.Forms.DataGridView
    $typDgv = $dgvSubnetSummary.GetType()
    $prpDgv = $typDgv.GetProperty('DoubleBuffered',('Instance','NonPublic'))
    $prpDgv.SetValue($dgvSubnetSummary, $true, $null)
    $dgvSubnetSummary.RowHeadersVisible = $false
    $dgvSubnetSummary.AllowUserToDeleteRows = $false
    $dgvSubnetSummary.AllowUserToAddRows = $false
    $dgvSubnetSummary.AllowUserToOrderColumns = $true
    $dgvSubnetSummary.AllowUserToResizeColumns = $true
    $dgvSubnetSummary.AllowUserToResizeRows = $false
    $dgvSubnetSummary.SelectionMode = [System.Windows.Forms.DataGridViewSelectionMode]::CellSelect
    $dgvSubnetSummary.MultiSelect = $true
    $dgvSubnetSummary.EditMode = [System.Windows.Forms.DataGridViewEditMode]::EditProgrammatically
    $dgvSubnetSummary.ColumnHeadersHeightSizeMode = [System.Windows.Forms.DataGridViewColumnHeadersHeightSizeMode]::DisableResizing
    $dgvSubnetSummary.RowHeadersWidthSizeMode = [System.Windows.Forms.DataGridViewRowHeadersWidthSizeMode]::DisableResizing
    $dgvSubnetSummary.AutoSizeColumnsMode = [System.Windows.Forms.DataGridViewAutoSizeColumnMode]::AllCells
    $dgvSubnetSummary.DefaultCellStyle.Font = $defaultLabelFont
    $dgvSubnetSummary.DefaultCellStyle.ForeColor = $tableForeColor
    $dgvSubnetSummary.Location = New-Object System.Drawing.Size(10,40)
    $dgvSubnetSummary.ReadOnly = $ReadOnly
    $tabSubnetSummary.Controls.Add($dgvSubnetSummary)

    $col01 = New-Object -TypeName System.Windows.Forms.DataGridViewTextBoxColumn
    $col01.Name = "Association"
    $col01.HeaderText = "Association"
    $col01.ReadOnly = $ReadOnly
    $col01.MinimumWidth = 120
    $col01.DefaultCellStyle.Alignment = [System.Windows.Forms.DataGridViewContentAlignment]::MiddleLeft
    $col01.HeaderCell.Style.Alignment = [System.Windows.Forms.DataGridViewContentAlignment]::MiddleLeft
    #$col01.SortMode = [System.Windows.Forms.DataGridViewColumnSortMode]::NotSortable

    $col02 = New-Object -TypeName System.Windows.Forms.DataGridViewTextBoxColumn
    $col02.Name = "TORGroup"
    $col02.HeaderText = "TOR Group"
    $col02.ReadOnly = $ReadOnly
    $col02.MinimumWidth = 60
    $col02.DefaultCellStyle.Alignment = [System.Windows.Forms.DataGridViewContentAlignment]::MiddleCenter
    $col02.HeaderCell.Style.Alignment = [System.Windows.Forms.DataGridViewContentAlignment]::MiddleCenter
    #$col02.SortMode = [System.Windows.Forms.DataGridViewColumnSortMode]::NotSortable

    $col03 = New-Object -TypeName System.Windows.Forms.DataGridViewTextBoxColumn
    $col03.Name = "SubnetType"
    $col03.HeaderText = "Subnet Type"
    $col03.ReadOnly = $ReadOnly
    $col03.MinimumWidth = 80
    $col03.DefaultCellStyle.Alignment = [System.Windows.Forms.DataGridViewContentAlignment]::MiddleLeft
    $col03.HeaderCell.Style.Alignment = [System.Windows.Forms.DataGridViewContentAlignment]::MiddleLeft
    #$col03.SortMode = [System.Windows.Forms.DataGridViewColumnSortMode]::NotSortable

    $col04 = New-Object -TypeName System.Windows.Forms.DataGridViewTextBoxColumn
    $col04.Name = "VLANID"
    $col04.HeaderText = "VLAN ID"
    $col04.ReadOnly = $ReadOnly
    $col04.MinimumWidth = 50
    $col04.DefaultCellStyle.Alignment = [System.Windows.Forms.DataGridViewContentAlignment]::MiddleCenter
    $col04.HeaderCell.Style.Alignment = [System.Windows.Forms.DataGridViewContentAlignment]::MiddleCenter
    #$col04.SortMode = [System.Windows.Forms.DataGridViewColumnSortMode]::NotSortable

    $col05 = New-Object -TypeName System.Windows.Forms.DataGridViewTextBoxColumn
    $col05.Name = "Supernet"
    $col05.HeaderText = "Supernet"
    $col05.ReadOnly = $ReadOnly
    $col05.MinimumWidth = 180
    $col05.DefaultCellStyle.Alignment = [System.Windows.Forms.DataGridViewContentAlignment]::MiddleLeft
    $col05.HeaderCell.Style.Alignment = [System.Windows.Forms.DataGridViewContentAlignment]::MiddleLeft
    #$col05.SortMode = [System.Windows.Forms.DataGridViewColumnSortMode]::NotSortable

    $col06 = New-Object -TypeName System.Windows.Forms.DataGridViewTextBoxColumn
    $col06.Name = "Name"
    $col06.HeaderText = "Name"
    $col06.ReadOnly = $ReadOnly
    $col06.MinimumWidth = 240
    $col06.DefaultCellStyle.Alignment = [System.Windows.Forms.DataGridViewContentAlignment]::MiddleLeft
    $col06.HeaderCell.Style.Alignment = [System.Windows.Forms.DataGridViewContentAlignment]::MiddleLeft
    #$col06.SortMode = [System.Windows.Forms.DataGridViewColumnSortMode]::NotSortable

    $col07 = New-Object -TypeName System.Windows.Forms.DataGridViewTextBoxColumn
    $col07.Name = "IPv4Subnet"
    $col07.HeaderText = "IPv4 Subnet"
    $col07.ReadOnly = $ReadOnly
    $col07.MinimumWidth = 120
    $col07.DefaultCellStyle.Alignment = [System.Windows.Forms.DataGridViewContentAlignment]::MiddleRight
    $col07.HeaderCell.Style.Alignment = [System.Windows.Forms.DataGridViewContentAlignment]::MiddleRight
    #$col07.SortMode = [System.Windows.Forms.DataGridViewColumnSortMode]::NotSortable

    $col08 = New-Object -TypeName System.Windows.Forms.DataGridViewTextBoxColumn
    $col08.Name = "Addresses"
    $col08.HeaderText = "Addresses"
    $col08.ReadOnly = $ReadOnly
    $col08.MinimumWidth = 60
    $col08.DefaultCellStyle.Alignment = [System.Windows.Forms.DataGridViewContentAlignment]::MiddleCenter
    $col08.HeaderCell.Style.Alignment = [System.Windows.Forms.DataGridViewContentAlignment]::MiddleCenter
    #$col08.SortMode = [System.Windows.Forms.DataGridViewColumnSortMode]::NotSortable

    $col09 = New-Object -TypeName System.Windows.Forms.DataGridViewTextBoxColumn
    $col09.Name = "MaskBits"
    $col09.HeaderText = "Mask Bits"
    $col09.ReadOnly = $ReadOnly
    $col09.MinimumWidth = 60
    $col09.DefaultCellStyle.Alignment = [System.Windows.Forms.DataGridViewContentAlignment]::MiddleCenter
    $col09.HeaderCell.Style.Alignment = [System.Windows.Forms.DataGridViewContentAlignment]::MiddleCenter
    #$col09.SortMode = [System.Windows.Forms.DataGridViewColumnSortMode]::NotSortable

    $col10 = New-Object -TypeName System.Windows.Forms.DataGridViewTextBoxColumn
    $col10.Name = "Mask"
    $col10.HeaderText = "Mask"
    $col10.ReadOnly = $ReadOnly
    $col10.MinimumWidth = $defaultIPCellWidth
    $col10.DefaultCellStyle.Alignment = [System.Windows.Forms.DataGridViewContentAlignment]::MiddleRight
    $col10.HeaderCell.Style.Alignment = [System.Windows.Forms.DataGridViewContentAlignment]::MiddleRight
    #$col10.SortMode = [System.Windows.Forms.DataGridViewColumnSortMode]::NotSortable

    $col11 = New-Object -TypeName System.Windows.Forms.DataGridViewTextBoxColumn
    $col11.Name = "Address"
    $col11.HeaderText = "Address"
    $col11.ReadOnly = $ReadOnly
    $col11.MinimumWidth = $defaultIPCellWidth
    $col11.DefaultCellStyle.Alignment = [System.Windows.Forms.DataGridViewContentAlignment]::MiddleRight
    $col11.HeaderCell.Style.Alignment = [System.Windows.Forms.DataGridViewContentAlignment]::MiddleRight
    #$col11.SortMode = [System.Windows.Forms.DataGridViewColumnSortMode]::NotSortable

    $col12 = New-Object -TypeName System.Windows.Forms.DataGridViewTextBoxColumn
    $col12.Name = "Gateway"
    $col12.HeaderText = "Gateway"
    $col12.ReadOnly = $ReadOnly
    $col12.MinimumWidth = $defaultIPCellWidth
    $col12.DefaultCellStyle.Alignment = [System.Windows.Forms.DataGridViewContentAlignment]::MiddleRight
    $col12.HeaderCell.Style.Alignment = [System.Windows.Forms.DataGridViewContentAlignment]::MiddleRight
    #$col12.SortMode = [System.Windows.Forms.DataGridViewColumnSortMode]::NotSortable

    $col13 = New-Object -TypeName System.Windows.Forms.DataGridViewTextBoxColumn
    $col13.Name = "First"
    $col13.HeaderText = "First"
    $col13.ReadOnly = $ReadOnly
    $col13.MinimumWidth = $defaultIPCellWidth
    $col13.DefaultCellStyle.Alignment = [System.Windows.Forms.DataGridViewContentAlignment]::MiddleRight
    $col13.HeaderCell.Style.Alignment = [System.Windows.Forms.DataGridViewContentAlignment]::MiddleRight
    #$col13.SortMode = [System.Windows.Forms.DataGridViewColumnSortMode]::NotSortable

    $col14 = New-Object -TypeName System.Windows.Forms.DataGridViewTextBoxColumn
    $col14.Name = "Last"
    $col14.HeaderText = "Last"
    $col14.ReadOnly = $ReadOnly
    $col14.MinimumWidth = $defaultIPCellWidth
    $col14.DefaultCellStyle.Alignment = [System.Windows.Forms.DataGridViewContentAlignment]::MiddleRight
    $col14.HeaderCell.Style.Alignment = [System.Windows.Forms.DataGridViewContentAlignment]::MiddleRight
    #$col14.SortMode = [System.Windows.Forms.DataGridViewColumnSortMode]::NotSortable

    $col15 = New-Object -TypeName System.Windows.Forms.DataGridViewTextBoxColumn
    $col15.Name = "Broadcast"
    $col15.HeaderText = "Broadcast"
    $col15.ReadOnly = $ReadOnly
    $col15.MinimumWidth = $defaultIPCellWidth
    $col15.DefaultCellStyle.Alignment = [System.Windows.Forms.DataGridViewContentAlignment]::MiddleRight
    $col15.HeaderCell.Style.Alignment = [System.Windows.Forms.DataGridViewContentAlignment]::MiddleRight
    #$col15.SortMode = [System.Windows.Forms.DataGridViewColumnSortMode]::NotSortable

    [void]$dgvSubnetSummary.Columns.Add($col01)
    [void]$dgvSubnetSummary.Columns.Add($col02)
    [void]$dgvSubnetSummary.Columns.Add($col03)
    [void]$dgvSubnetSummary.Columns.Add($col04)
    [void]$dgvSubnetSummary.Columns.Add($col05)
    [void]$dgvSubnetSummary.Columns.Add($col06)
    [void]$dgvSubnetSummary.Columns.Add($col07)
    [void]$dgvSubnetSummary.Columns.Add($col08)
    [void]$dgvSubnetSummary.Columns.Add($col09)
    [void]$dgvSubnetSummary.Columns.Add($col10)
    [void]$dgvSubnetSummary.Columns.Add($col11)
    [void]$dgvSubnetSummary.Columns.Add($col12)
    [void]$dgvSubnetSummary.Columns.Add($col13)
    [void]$dgvSubnetSummary.Columns.Add($col14)
    [void]$dgvSubnetSummary.Columns.Add($col15)

    $tabControl.Controls.Add($tabSubnetSummary)
}

function New-IPAssignmentsTab
{
    param
    (
        [Parameter(Mandatory=$false)]
        [bool]$ReadOnly = $true
    )
    $script:tabIPAssignments = New-Object System.Windows.Forms.TabPage
    $typTab = $tabIPAssignments.GetType()
    $prpTab = $typTab.GetProperty('DoubleBuffered',('Instance','NonPublic'))
    $prpTab.SetValue($tabIPAssignments, $true, $null)
    $tabIPAssignments.Name = 'tabIPAssignments'
    $tabIPAssignments.Text = $locString.tabIPAssignments
    $tabIPAssignments.BackColor = $summaryBackColor
    $tabIPAssignments.ForeColor = $summaryForeColor
    $tabIPAssignments.AutoScroll = $true
    $tabControl.Controls.Add($tabIPAssignments)
}

function New-IPAssignmentTable
{
    param
    (
        [Parameter(Mandatory=$true)]
        [string]$Name,
        [Parameter(Mandatory=$false)]
        [bool]$ReadOnly = $true
    )
    $script:dgvIPAssignments = New-Object System.Windows.Forms.DataGridView
    $typDgv = $dgvIPAssignments.GetType()
    $prpDgv = $typDgv.GetProperty('DoubleBuffered',('Instance','NonPublic'))
    $prpDgv.SetValue($dgvIPAssignments, $true, $null)
    $dgvIPAssignments.Name = $Name
    $dgvIPAssignments.RowHeadersVisible = $false
    $dgvIPAssignments.AllowUserToDeleteRows = $false
    $dgvIPAssignments.AllowUserToAddRows = $false
    $dgvIPAssignments.AllowUserToOrderColumns = $false
    $dgvIPAssignments.AllowUserToResizeColumns = $true
    $dgvIPAssignments.AllowUserToResizeRows = $false
    $dgvIPAssignments.SelectionMode = [System.Windows.Forms.DataGridViewSelectionMode]::CellSelect
    $dgvIPAssignments.MultiSelect = $true
    $dgvIPAssignments.EditMode = [System.Windows.Forms.DataGridViewEditMode]::EditProgrammatically
    $dgvIPAssignments.ColumnHeadersHeightSizeMode = [System.Windows.Forms.DataGridViewColumnHeadersHeightSizeMode]::DisableResizing
    $dgvIPAssignments.RowHeadersWidthSizeMode = [System.Windows.Forms.DataGridViewRowHeadersWidthSizeMode]::DisableResizing
    $dgvIPAssignments.AutoSizeColumnsMode = [System.Windows.Forms.DataGridViewAutoSizeColumnMode]::AllCells
    $dgvIPAssignments.DefaultCellStyle.Font = $defaultLabelFont
    $dgvIPAssignments.DefaultCellStyle.ForeColor = $tableForeColor
    $dgvIPAssignments.ReadOnly = $ReadOnly
    $dgvIPAssignments.Location = New-Object System.Drawing.Size(10,10)

    $col01 = New-Object -TypeName System.Windows.Forms.DataGridViewTextBoxColumn
    $col01.Name = "AssignedTo"
    $col01.HeaderText = "AssignedTo"
    $col01.ReadOnly = $ReadOnly
    $col01.MinimumWidth = 240
    $col01.DefaultCellStyle.Alignment = [System.Windows.Forms.DataGridViewContentAlignment]::MiddleLeft
    $col01.HeaderCell.Style.Alignment = [System.Windows.Forms.DataGridViewContentAlignment]::MiddleLeft
    #$col01.SortMode = [System.Windows.Forms.DataGridViewColumnSortMode]::NotSortable

    $col02 = New-Object -TypeName System.Windows.Forms.DataGridViewTextBoxColumn
    $col02.Name = "IPv4Address"
    $col02.HeaderText = "IPv4 Address"
    $col02.ReadOnly = $ReadOnly
    $col02.MinimumWidth = 110
    $col02.DefaultCellStyle.Alignment = [System.Windows.Forms.DataGridViewContentAlignment]::MiddleRight
    $col02.HeaderCell.Style.Alignment = [System.Windows.Forms.DataGridViewContentAlignment]::MiddleRight
    #$col02.SortMode = [System.Windows.Forms.DataGridViewColumnSortMode]::NotSortable

    [void]$dgvIPAssignments.Columns.Add($col01)
    [void]$dgvIPAssignments.Columns.Add($col02)

    return $dgvIPAssignments
}

function Write-SubnetSummary
{
    Write-Log -Message "$($MyInvocation.MyCommand.Name) - called"
    try
    {
        $name = $locString.tabSubnetSummary
        if(-not $activeStamp)
        {
            $msg = '$activeStamp variable could not be found'
            Write-Log -Message "$($MyInvocation.MyCommand.Name) - ERROR: $msg" -Force
            throw $msg
        }
        if($tabControl.TabPages["tabSubnetSummary"])
        {
            $tabControl.TabPages.Remove($tabControl.TabPages["tabSubnetSummary"])
        }
        if($tabSubnetSummary)
        {
            $tabSubnetSummary.Dispose()
        }
        Write-Log -Message "$($MyInvocation.MyCommand.Name) - create a new tab named '$($name)'"
        New-SubnetSummary

        Write-Log -Message "$($MyInvocation.MyCommand.Name) - write values to '$($name)'"
        foreach($network in $activeStamp.Supernets.Networks)
        {
            [int]$currentRow = $dgvSubnetSummary.Rows.Add()
            [int]$currentCell = 0
            [string]$gateway = $network.IPv4.Network.ToString()
            [string]$first = $network.IPv4.Network.ToString()
            [string]$last = $network.IPv4.Network.ToString()
            if($network.IPv4.Cidr -eq [byte]30)
            {
                $gateway = [string]::Empty
                $first = $network.IPv4.FirstUsable.ToString()
                $last = $network.IPv4.LastUsable.ToString()
            }
            else
            {
                $gateway = $network.IPv4.FirstUsable.ToString()
                [string]$first = [System.Net.IPNetwork]::ListIPAddress($network.IPv4)[2]
                [string]$last = [System.Net.IPNetwork]::ListIPAddress($network.IPv4)[$network.IPv4.Usable]
            }
            [string]$rkName = $network.Parent.Name.Split('-') | Select-String -Pattern "Rack(\d{2}$)"
            [string]$clName = $network.Parent.Name.Split('-') | Select-String -Pattern "CL(\d{2}$)"
            [string]$suName = $network.Parent.Name.Split('-') | Select-String -Pattern "SU(\d{2}$)"
            [string]$assoc = ''
            if($rkName)
            {
                $assoc += $rkName
            }
            if($clName)
            {
                if($assoc -eq '')
                {
                    $assoc += $clName
                }
                else
                {
                    $assoc += "-$($clName)"
                }
            }
            if($suName)
            {
                if($assoc -eq '')
                {
                    $assoc += $suName
                }
                else
                {
                    $assoc += "-$($suName)"
                }
            }
            if($assoc -eq '')
            {
                $assoc = "N/A"
            }
            $dgvSubnetSummary.Rows[$currentRow].Cells[$currentCell].Value = $assoc
            $currentCell++
            $dgvSubnetSummary.Rows[$currentRow].Cells[$currentCell].Value = $network.TorGroup
            $currentCell++
            $dgvSubnetSummary.Rows[$currentRow].Cells[$currentCell].Value = $network.SubnetType
            $currentCell++
            $dgvSubnetSummary.Rows[$currentRow].Cells[$currentCell].Value = $network.VlanId
            $currentCell++
            $dgvSubnetSummary.Rows[$currentRow].Cells[$currentCell].Value = $network.Parent.Name
            $currentCell++
            $dgvSubnetSummary.Rows[$currentRow].Cells[$currentCell].Value = $network.Name
            $currentCell++
            $dgvSubnetSummary.Rows[$currentRow].Cells[$currentCell].Value = $network.IPv4.ToString()
            $currentCell++
            $dgvSubnetSummary.Rows[$currentRow].Cells[$currentCell].Value = $network.IPv4.Total
            $currentCell++
            $dgvSubnetSummary.Rows[$currentRow].Cells[$currentCell].Value = $network.IPv4.Cidr
            $currentCell++
            $dgvSubnetSummary.Rows[$currentRow].Cells[$currentCell].Value = $network.IPv4.Netmask
            $currentCell++
            $dgvSubnetSummary.Rows[$currentRow].Cells[$currentCell].Value = $network.IPv4.Network
            $currentCell++
            $dgvSubnetSummary.Rows[$currentRow].Cells[$currentCell].Value = $gateway
            $currentCell++
            $dgvSubnetSummary.Rows[$currentRow].Cells[$currentCell].Value = $first
            $currentCell++
            $dgvSubnetSummary.Rows[$currentRow].Cells[$currentCell].Value = $last
            $currentCell++
            $dgvSubnetSummary.Rows[$currentRow].Cells[$currentCell].Value = $network.IPv4.Broadcast
        }
        $dgvSubnetSummary.AutoResizeColumns()
        $dgvSubnetSummary.AutoResizeRows()
        $dgvSubnetSummary.Location = New-Object System.Drawing.Size(10,40)
        $dgvSubnetSummary.Size = (Get-DataGridViewSize $dgvSubnetSummary)
    }
    catch
    {
        Write-Log -Message "$($MyInvocation.MyCommand.Name) - ERROR: $($PSItem.Exception.Message)" -Force
        throw $PSItem
    }
    finally
    {
        Write-Log -Message "$($MyInvocation.MyCommand.Name) - completed"
    }
}

function Write-IPAssignments
{
    Write-Log -Message "$($MyInvocation.MyCommand.Name) - called"
    try
    {
        $name = $locString.tabIPAssignments
        if(-not $activeStamp)
        {
            $msg = '$activeStamp variable could not be found'
            Write-Log -Message "$($MyInvocation.MyCommand.Name) - ERROR: $msg" -Force
            throw $msg
        }
        if($tabControl.TabPages["tabIPAssignments"])
        {
            $tabControl.TabPages.Remove($tabControl.TabPages["tabIPAssignments"])
        }
        if($tabIPAssignments)
        {
            $tabIPAssignments.Dispose()
        }
        Write-Log -Message "$($MyInvocation.MyCommand.Name) - create a new tab named '$($name)'"
        New-IPAssignmentsTab

        Write-Log -Message "$($MyInvocation.MyCommand.Name) - write values to '$($name)'"
        $thisRow = 10
        $script:ipAssignmentObjects = @{}
        foreach($network in $activeStamp.Supernets.Networks)
        {
            Write-Log -Message "$($MyInvocation.MyCommand.Name) - process network: '$($network.Name)'"
            if(($network.Assignments.Count -gt 0) -OR ($network.Name -match "Loopback"))
            {
                $netName = $network.Name
                if($network.Name -match "Loopback")
                {
                    $netName = $network.Name.Replace("Loopback", "BGPPeer")
                }
                $thisLBL = New-Object System.Windows.Forms.Label
                $thisDGV = New-IPAssignmentTable -Name $netName
                $ipAssignmentObjects.Add($netName,@{Table=$thisDGV;Label=$thisLBL})
                $ipAssignmentObjects.$netName.Label.Location = New-Object System.Drawing.Size($lblLeft,$thisRow)
                $ipAssignmentObjects.$netName.Label.AutoSize = $true
                $ipAssignmentObjects.$netName.Label.Text = "$($locString.name) $($netName)"
                $ipAssignmentObjects.$netName.Label.Font = [System.Drawing.Font]::New("Microsoft Sans Serif", 10)
                $tabIPAssignments.Controls.Add($ipAssignmentObjects.$netName.Label)
                $thisRow += 20
                $tabIPAssignments.Controls.Add($ipAssignmentObjects.$netName.Table)
                foreach($ip in [System.Net.IPNetwork]::ListIPAddress($network.IPv4))
                {
                    # Write-Log -Message "$($MyInvocation.MyCommand.Name) - processing IP Address '$($ip.ToString())'"
                    $currentCell = 0
                    $currentRow = $ipAssignmentObjects.$netName.Table.Rows.Add()
                    $activeAssignment = $network.Assignments | Where-Object { $PSItem.Address.ToString() -eq $ip.ToString() }
                    if($activeAssignment.Count -eq 1)
                    {
                        $ipAssignmentObjects.$netName.Table.Rows[$currentRow].Cells[$currentCell].Value = $activeAssignment.Name
                        $ipAssignmentObjects.$netName.Table.Rows[$currentRow].Cells[$currentCell+1].Value = $activeAssignment.Address.ToString()
                    }
                    else
                    {
                        # Write-Log -Message "$($MyInvocation.MyCommand.Name) - found '$($activeAssignment.Count)' assignments for '$($ip.ToString())'"
                        if($ip.ToString() -eq $network.IPv4.Broadcast.ToString())
                        {
                            $ipAssignmentObjects.$netName.Table.Rows[$currentRow].Cells[$currentCell].Value = "Broadcast"
                            if($network.Name -like "*Loopback*")
                            {
                                $ipAssignmentObjects.$netName.Table.Rows[$currentRow].Cells[$currentCell].Value = "BGP Peer"
                            }
                            if($network.Name -like "*BMCMgmt*")
                            {
                                $currentCell++
                                $ipAssignmentObjects.$netName.Table.Rows[$currentRow].Cells[$currentCell].Value = [string]::Empty
                            }
                            $currentCell++
                            $ipAssignmentObjects.$netName.Table.Rows[$currentRow].Cells[$currentCell].Value = $ip.ToString()
                        }
                        elseif($ip.ToString() -eq $network.IPv4.Network.ToString())
                        {
                            $ipAssignmentObjects.$netName.Table.Rows[$currentRow].Cells[$currentCell].Value = "Network"
                            if($network.Name -like "*BMCMgmt*")
                            {
                                $currentCell++
                                $ipAssignmentObjects.$netName.Table.Rows[$currentRow].Cells[$currentCell].Value = [string]::Empty
                            }
                            $currentCell++
                            $ipAssignmentObjects.$netName.Table.Rows[$currentRow].Cells[$currentCell].Value = $ip.ToString()
                        }
                        else
                        {
                            $ipAssignmentObjects.$netName.Table.Rows.RemoveAt($ipAssignmentObjects.$netName.Table.Rows.Count-1)
                        }
                    }
                }
                $ipAssignmentObjects.$netName.Table.AutoResizeColumns()
                $ipAssignmentObjects.$netName.Table.AutoResizeRows()
                $ipAssignmentObjects.$netName.Table.Location = New-Object System.Drawing.Size($lblLeft,$thisRow)
                $ipAssignmentObjects.$netName.Table.Size = (Get-DataGridViewSize $ipAssignmentObjects.$netName.Table)
                [int]$h = (Get-DataGridViewSize $ipAssignmentObjects.$netName.Table).Height
                $thisRow += ($h+20)
            }
        }
    }
    catch
    {
        Write-Log -Message "$($MyInvocation.MyCommand.Name) - ERROR: $($PSItem.Exception.Message)" -Force
        throw $PSItem
    }
    finally
    {
        Write-Log -Message "$($MyInvocation.MyCommand.Name) - completed"
    }
}

function Invoke-GenerateDeploymentData
{
    Write-Log -Message "$($MyInvocation.MyCommand.Name) - called"
    try
    {
        Show-WaitForm ("`n $($MyInvocation.MyCommand.Name) : {0}.`n`n {1}" -f $locString.msgGeneratingData,$locString.msgWait)
        $global:activeStamp = $null
        $global:activeStamp = New-Deployment
        [int]$cloudCount = 1
        [string]$switchMake = $lstSwitchVendor.SelectedItem
        if($scenario -eq "AddRack")
        {
            $activeStamp.IsAddRack = $true
        }
        if($isExtStor)
        {
            $activeStamp.IsExtendedStorage = $true
            Test-EdgeSwitchInfo
            Test-BackendSwitchInfo
        }
        # Set per-Deployment properties
        Write-Log -Message "$($MyInvocation.MyCommand.Name) - set per-Deployment properties"
        foreach($property in $arrDeployProperties)
        {
            switch ($property)
            {
                "BorderConnectivity"
                {
                    Write-Log -Message "$($MyInvocation.MyCommand.Name) - set '$($property)' value"
                    $activeStamp."$property" = $lstRoutingMethod.SelectedItem
                    break
                }
                "PermitNetworks"
                {
                    if($lstPermitNetworks.Items.Count -gt 0)
                    {
                        Write-Log -Message "$($MyInvocation.MyCommand.Name) - set '$($property)' value"
                        foreach($item in $lstPermitNetworks.Items)
                        {
                            if(-not [string]::IsNullOrEmpty($item))
                            {
                                $activeStamp.PermitNetworks.Add($item)
                            }
                        }
                    }
                    break
                }
                "UplinkSpeed"
                {
                    if($arrUplinkSpeed.Count -gt 0)
                    {
                        Write-Log -Message "$($MyInvocation.MyCommand.Name) - set '$($property)' value"
                        $activeStamp."$property" = $lstUplinkSpeed.SelectedItem
                    }
                    break
                }
                default
                {
                    Write-Log "$($MyInvocation.MyCommand.Name) - WARNING: Unhandled property named '$($property)'" -Force
                }
            }
        }
        # Set per-Cloud properties
        for($cl = 1; $cl -le $cloudCount; $cl++)
        {
            [string]$clID = "{0:d2}" -f $cl
            [string]$clName = "CL$($clID)"
            Write-Log -Message "$($MyInvocation.MyCommand.Name) - set per-Cloud properties for $($clName)"
            $cloud = $activeStamp.AddCloud()
            foreach($property in $arrCloudProperties)
            {
                Write-Log -Message "$($MyInvocation.MyCommand.Name) - process: '$($property)'"
                $val = $null
                switch ($property)
                {
                    "NodeCount"
                    {
                        $property = $null
                        break
                    }
                    "TimeServer"
                    {
                        $val = $txtTimeServer.Text
                        break
                    }
                    "SyslogServerIPv4Address"
                    {
                        $val = $txtSyslogServer.Text
                        if(-not $val)
                        {
                            $property = $null
                        }
                        break
                    }
                    "DNSForwarder"
                    {
                        # Populate DNSForwarder differently as it could be a comma-separated array
                        [string[]]$dnsFwd = ($txtDNSForwarder.Text).Split(',')
                        if($dnsFwd.Count -gt 1)
                        {
                            $val = $dnsFwd
                        }
                        else
                        {
                            $val = $dnsFwd[0]
                        }
                        if(-not $val)
                        {
                            $property = $null
                        }
                        break
                    }
                    "ConnectToAzure"
                    {
                        $val = $lstIdentityStore.SelectedItem
                        break
                    }
                    "InfraAzureEnvironment"
                    {
                        if($lstAzureEnvironment.Enabled)
                        {
                            $val = $lstAzureEnvironment.SelectedItem
                        }
                        else
                        {
                            $property = $null
                        }
                        break
                    }
                    "InfraAzureDirectoryTenantName"
                    {
                        if($txtAADTenantName.Enabled)
                        {
                            $val = $txtAADTenantName.Text
                        }
                        else
                        {
                            $property = $null
                        }
                        break
                    }
                    "ADFSForestFQDN"
                    {
                        if($txtADFSForestFQDN.Enabled)
                        {
                            $val = $txtADFSForestFQDN.Text
                        }
                        else
                        {
                            $property = $null
                        }
                        break
                    }
                    "ADFSProviderName"
                    {
                        if($txtADFSProviderName.Enabled)
                        {
                            $val = $txtADFSProviderName.Text
                        }
                        else
                        {
                            $property = $null
                        }
                        break
                    }
                    "ADFSMetadataUri"
                    {
                        if($txtADFSMetadataUri.Enabled)
                        {
                            $val = $txtADFSMetadataUri.Text
                        }
                        else
                        {
                            $property = $null
                        }
                        break
                    }
                    "NamingPrefix"
                    {
                        $val = $txtDeployPrefix.Text
                        break
                    }
                    "PhysicalNamingPrefix"
                    {
                        $val = $txtPhysicalPrefix.Text
                        break
                    }
                    "ExternalDomainFQDN"
                    {
                        $val = $txtExternalDomain.Text
                        break
                    }
                    "DomainFQDN"
                    {
                        $val = $txtPrivateDomain.Text
                        break
                    }
                    "RegionName"
                    {
                        $val = $txtRegionName.Text
                        break
                    }
                    "CompanyName"
                    {
                        $val = $txtCompanyName.Text
                        break
                    }
                    default
                    {
                        Write-Log  -Message "$($MyInvocation.MyCommand.Name) - WARNING: Unhandled property named '$($property)'" -Force
                        $property = $null
                        break
                    }
                }
                if($property)
                {
                    $cloud.$property = $val
                }
            }
            # Add Racks and Scale-Unit(s) first
            Write-Log -Message "$($MyInvocation.MyCommand.Name) - set $($clName) per-Scale-Unit properties"
            [int]$suCount = $txtScaleUnitCount.Text
            for($su = 1; $su -le $suCount; $su++)
            {
                $suID = "SU{0:d2}" -f $su
                $needNewRack = $false
                if($clName -eq 'CL01')
                {
                    $needNewRack = $true
                }
                if($needNewRack)
                {
                    Write-Log -Message "$($MyInvocation.MyCommand.Name) - $($clName) add a new Rack to deployment"
                    $newRack = $activeStamp.AddRack()
                    if($newRack)
                    {
                        Write-Log -Message "$($MyInvocation.MyCommand.Name) - $($clName) successfully added Rack '$($newRack.Name)'"
                        $thisRack = $newRack
                    }
                    else
                    {
                        throw "Failed to add Rack for '$($clId)' Scale-Unit '$($suID)'"
                    }
                }
                else
                {
                    $thisRack = $activeStamp.Racks | Where-Object Name -eq "Rack01"
                }
                if(-not $thisRack)
                {
                    throw "Unable to find Rack for '$($clName)' Scale-Unit '$($suId)'"
                }
                Write-Log -Message "$($MyInvocation.MyCommand.Name) - $($clName) add Scale-Unit '$($suID)' assigned to Rack '$($thisRack.Name)'"
                $newScaleUnit = $cloud.AddScaleUnit($thisRack.Name)
                if($newScaleUnit)
                {
                    Write-Log -Message "$($MyInvocation.MyCommand.Name) - successfully added $($clName) Scale-Unit '$($newScaleUnit.Name)'"
                }
                else
                {
                    throw "Failed to add $($clName) Scale-Unit ''$($suID)'"
                }
            }
            # Populate per-Scale-Unit properties
            for($su = 1; $su -le $suCount; $su++)
            {
                $suID = "{0:d2}" -f $su
                $suName = "SU$($suID)"
                $suTab = "tabSU$su"
                Write-Log -Message "$($MyInvocation.MyCommand.Name) - populate values for Scale-Unit '$($suName)'"
                $thisScaleUnit = $cloud.ScaleUnits | Where-Object Name -eq $suName
                if(-not $thisScaleUnit)
                {
                    throw "Unable to find $($clName) Scale-Unit named '$($suName)'"
                }
                $rack = $activeStamp.Racks | Where-Object Name -eq $thisScaleUnit.RackId
                if(-not $rack)
                {
                    throw "Unable to find Rack object named '$($thisScaleUnit.RackId)'"
                }
                $thisScaleUnit.NodeCount = $scaleUnitTabs.$suTab.Objects.txtNodeCount.Text
                # Set switch values
                Write-Log -Message "$($MyInvocation.MyCommand.Name) - $($clName)-$($suName) set network switch properties"
                if($cloud.Name -eq "CL01")
                {
                    $skipThis = $false
                    if((($switchMake -eq "CiscoUCS") -or ($switchMake -eq "Mellanox")) -and ($rack.Index -gt 1))
                    {
                        $skipThis = $true
                    }
                    if(-not $skipThis)
                    {
                        Write-Log -Message "$($MyInvocation.MyCommand.Name) - $($rack.Name)-BMC switch"
                        $bmc = $activeStamp.Switches | Where-Object Hostname -like "*-$($rack.Name)-BMC-1"
                        if(-not $bmc)
                        {
                            throw "Unable to find Switch named '*-$($rack.Name)-BMC-1'"
                        }
                        $bmc.Make = $switchMake
                        Write-Log -Message "$($MyInvocation.MyCommand.Name) - $($rack.Name)-TOR switches"
                        $tor1 = $activeStamp.Switches | Where-Object Hostname -like "*-$($rack.Name)-TOR-1"
                        if(-not $tor1)
                        {
                            throw "Unable to find Switch named '*-$($rack.Name)-TOR-1'"
                        }
                        $tor2 = $activeStamp.Switches | Where-Object Hostname -like "*-$($rack.Name)-TOR-2"
                        if(-not $tor2)
                        {
                            throw "Unable to find Switch named '*-$($rack.Name)-TOR-2'"
                        }
                        $tor1.Make = $switchMake
                        $tor2.Make = $switchMake
                        $bmc.Model = $lstBMCModel.SelectedItem
                        $bmc.FIRMWAREVERSION = $lstBMCFirmware.SelectedItem
                        $bmc.Asn = $scaleUnitTabs.$suTab.Objects.txtTORASN.Text
                        $tor1.Model = $lstTORModel.SelectedItem
                        $tor2.Model = $lstTORModel.SelectedItem
                        $tor1.FIRMWAREVERSION = $lstTORFirmware.SelectedItem
                        $tor2.FIRMWAREVERSION = $lstTORFirmware.SelectedItem
                        $tor1.Asn = $scaleUnitTabs.$suTab.Objects.txtTORASN.Text
                        $tor2.Asn = $scaleUnitTabs.$suTab.Objects.txtTORASN.Text
                    }
                }
                Write-Log -Message "$($MyInvocation.MyCommand.Name) - $($clName)-MUX switch"
                $mux = $activeStamp.Switches | Where-Object Hostname -match "-$($clName)-MUX-1"
                if(-not $mux)
                {
                    throw ($locString.errSwitchNotFound -f "-$($clName)-MUX-1")
                }
                $mux.Make = 'Microsoft'
                $mux.Model = 'VirtualSwitch'
                $mux.Asn = $txtSoftwareBGP.Text
                # Spine switches are only present if the SU count is greater than 1 and we only set Spine properties for SU01
                if(($switchMake -ne "CiscoUCS") -and ($switchMake -ne "Mellanox"))
                {
                    if($suCount -gt 1 -and $su -eq 1)
                    {
                        Write-Log -Message "$($MyInvocation.MyCommand.Name) - $($rack.Name)-SpineInfrastructure switches"
                        $spineNet = $activeStamp.Supernets | Where-Object Name -eq ("$($rack.Name)-SpineInfrastructure")
                        if(-not $spineNet)
                        {
                            throw ("$locString.errSupernetNotFound" -f "$($rack.Name)-SpineInfrastructure")
                        }
                        [string]$ip = $txtSpineSubnet.Text
                        [string]$sz = $txtSpineSubnetSize.Text
                        if(-not $ip)
                        {
                            $ip = '0.0.0.0'
                            $sz = $defaultSpineSubnetSize
                        }
                        $spineNet.IPv4 = $ip + '/' + $sz
                    }
                }
                # Set Supernet values
                Write-Log -Message "$($MyInvocation.MyCommand.Name) - $($clName)-$($suName) set Supernet values"
                if($cloud.Name -eq "CL01")
                {
                    Write-Log -Message "$($MyInvocation.MyCommand.Name) - $($rack.Name)-BMC set Supernet values"
                    $bmcNet = $activeStamp.Supernets | Where-Object Name -eq ("$($rack.Name)-BMC")
                    if(-not $bmcNet)
                    {
                        throw ("$locString.errSupernetNotFound" -f "$($rack.Name)-BMC")
                    }
                    $ip = $scaleUnitTabs.$suTab.Objects.txtBMCSubnet.Text
                    $sz = $scaleUnitTabs.$suTab.Objects.txtBMCSubnetSize.Text
                    if(-not $ip)
                    {
                        $ip = '0.0.0.0'
                        $sz = $defaultBMCSubnetSize
                    }
                    $bmcNet.IPv4 = $ip + '/' + $sz

                    $skipThis = $false
                    if((($switchMake -eq "CiscoUCS") -or ($switchMake -eq "Mellanox")) -and ($rack.Index -gt 1))
                    {
                        $skipThis = $true
                    }
                    if(-not $skipThis)
                    {
                        Write-Log -Message "$($MyInvocation.MyCommand.Name) - $($rack.Name)-SwitchInfrastructure set Supernet values"
                        $switchNet = $activeStamp.Supernets | Where-Object Name -eq ("$($rack.Name)-SwitchInfrastructure")
                        if(-not $switchNet)
                        {
                            throw ("$locString.errSupernetNotFound" -f "$($rack.Name)-SwitchInfrastructure")
                        }
                        $ip = $scaleUnitTabs.$suTab.Objects.txtSwitchInfraSubnet.Text
                        $sz = $scaleUnitTabs.$suTab.Objects.txtSwitchInfraSubnetSize.Text
                        if(-not $ip)
                        {
                            $ip = '0.0.0.0'
                            $sz = $defaultSwInfraSubnetSize
                        }
                        $switchNet.IPv4 = $ip + '/' + $sz
                    }
                }

                Write-Log -Message "$($MyInvocation.MyCommand.Name) - $($Rack.Name)-$($clName)-$($suName)-Infrastructure set Supernet values"
                $infraNet = $activeStamp.Supernets | Where-Object Name -eq ("$($Rack.Name)-$($clName)-$($suName)-Infrastructure")
                if(-not $infraNet)
                {
                    throw ("$locString.errSupernetNotFound" -f "$($Rack.Name)-$($clName)-$($suName)-Infrastructure")
                }
                $ip = $scaleUnitTabs.$suTab.Objects.txtInfraSubnet.Text
                $sz = $scaleUnitTabs.$suTab.Objects.txtInfraSubnetSize.Text
                if(-not $ip)
                {
                    $ip = '0.0.0.0'
                    $sz = $defaultInfraSubnetSize
                }
                $infraNet.IPv4 = $ip + '/' + $sz

                Write-Log -Message "$($MyInvocation.MyCommand.Name) - $($clName)-Private set Supernet values"
                $pvtNet = $activeStamp.Supernets | Where-Object Name -eq ("$($clName)-Private")
                if(-not $pvtNet)
                {
                    throw ("$locString.errSupernetNotFound" -f "$($clName)-Private")
                }
                $ip = $txtPrivateSubnet.Text
                $sz = $txtPrivateSubnetSize.Text
                if(-not $ip)
                {
                    $ip = '0.0.0.0'
                    $sz = $defaultPrivateSubnetSize
                }
                $pvtNet.IPv4 = $ip + '/' + $sz

                Write-Log -Message "$($MyInvocation.MyCommand.Name) - $($clName)-External set Supernet values"
                $ext = $activeStamp.Supernets | Where-Object Name -eq "$($clName)-External"
                if(-not $ext)
                {
                    throw ("$locString.errSupernetNotFound" -f "$($clName)-External")
                }
                $ip = $txtExternalSubnet.Text
                $sz = $txtExternalSubnetSize.Text
                if(-not $ip)
                {
                    $ip = '0.0.0.0'
                    $sz = $defaultExternalSubnetSize
                }
                $ext.IPv4 = $ip + '/' + $sz
            }
        }
        Write-Log -Message "$($MyInvocation.MyCommand.Name) - set BORDER switch count"
        $borderCount = $txtBorderCount.Text
        $activeStamp.SetBorderCount($borderCount)
        for($b = 1; $b -le $borderCount; $b++)
        {
            Write-Log -Message "$($MyInvocation.MyCommand.Name) - set BORDER switch $($b) values"
            $border = $activeStamp.Switches | Where-Object Hostname -like ("*-Border-Border-" + $b)
            if(-not $border)
            {
                throw ($locString.errSwitchNotFound -f "-Border-Border-$($b)")
            }
            $border.Make = "border"
            if(-not $isStaticRouting)
            {
                switch ($b)
                {
                    '1'
                    {
                        $thisAsn = $txtBorderBGP1.Text
                    }
                    '2'
                    {
                        $thisAsn = $txtBorderBGP2.Text
                    }
                    '3'
                    {
                        $thisAsn = $txtBorderBGP3.Text
                    }
                    '4'
                    {
                        $thisAsn = $txtBorderBGP4.Text
                    }
                    default
                    {
                        $thisAsn = $null
                    }
                }
                $border.Asn = $thisAsn
            }
        }
        if(($suCount -gt 1) -and (($switchMake -ne "CiscoUCS") -or ($switchMake -ne "Mellanox")))
        {
            Write-Log -Message "$($MyInvocation.MyCommand.Name) - set SPINE switch count"
            $spineCount = $txtSpineCount.Text
            $activeStamp.SetSpineCount($spineCount)
            for($s = 1; $s -le $spineCount; $s++)
            {
                Write-Log -Message "$($MyInvocation.MyCommand.Name) - set SPINE switch $($s) properties"
                [string]$match = "*-Spine-Spine-" + $s
                $spine = @($activeStamp.Switches | Where-Object Hostname -like $match)
                if(-not $spine)
                {
                    throw ($locString.errSwitchNotFound -f "$($match)")
                }
                if($spine.Count -gt 1)
                {
                    throw "Found more than one Spine switch matching the name '$($match)'"
                }
                $spine[0].Asn = $txtSpineBGP.Text
                $spine[0].Make = $switchMake
                $spine[0].Model = $lstSpineModel.SelectedItem
                $spine[0].FIRMWAREVERSION = $lstSpineFirmware.SelectedItem
            }
        }
        if($switchMake -eq 'msftcisco')
        {
            $edge = @($activeStamp.Switches | Where-Object Hostname -like ("*-Edge-Edge-*"))
            foreach($sw in $edge)
            {
                Write-Log -Message "$($MyInvocation.MyCommand.Name) - set EDGE switch values"
                $sw.Make = $edgeMake
                $sw.Model = $edgeModel
                $sw.FIRMWAREVERSION = $edgeFirmware
                if(-not $isStaticRouting)
                {
                    Write-Log -Message "$($MyInvocation.MyCommand.Name) - set EDGE switch BGP ASN"
                    $sw.Asn = $txtEdgeBGP.Text
                }
            }
            $back = @($activeStamp.Switches | Where-Object Hostname -like ("*-BACKEND-*"))
            foreach($sw in $back)
            {
                Write-Log -Message "$($MyInvocation.MyCommand.Name) - set BACKEND switch values"
                $sw.Make = $backMake
                $sw.Model = $backModel
                $sw.FIRMWAREVERSION = $backFirmware
            }
        }
        # Validate the input data
        $activeStamp.SetCurrentPath($PSScriptRoot)
        Write-Log -Message "$($MyInvocation.MyCommand.Name) - call the Validate() method"
        $caption = $locString.capValidationError
        $ref = New-Object -TypeName "System.Text.StringBuilder"
        $script:errorText = [ref]$ref
        [bool]$isValid = $activeStamp.Validate($errorText)
        if($lstIdentityStore.SelectedIndex -eq -1)
        {
            $isValid = $false
            [string]$thisErr = $locString.msgValidationEmpty -f $locString.lblIdentityStore.Replace(':','')
            $ref.Insert(0,"$($thisErr)`n")
        }
        if($isValid)
        {
            Write-Log -Message "$($MyInvocation.MyCommand.Name) - deployment data has passed validation"

            # Generate IP Usage and assignments
            Write-Log -Message "$($MyInvocation.MyCommand.Name) - call the Generate() method"
            $activeStamp.Generate()

            # Write values to spreadsheet
            Write-Log -Message "$($MyInvocation.MyCommand.Name) - write subnet values to the '$($locString.tabSubnetSummary)' tab"
            Show-WaitForm ("`n $($MyInvocation.MyCommand.Name) : {0} '{1}'.`n`n {2}" -f $locString.msgWritingValuesTo,$locString.tabSubnetSummary,$locString.msgWait)
            Write-SubnetSummary

            Write-Log -Message "$($MyInvocation.MyCommand.Name) - write IP values to the '$($locString.tabIPAssignments)' tab"
            Show-WaitForm ("`n $($MyInvocation.MyCommand.Name) : {0} '{1}'.`n`n {2}" -f $locString.msgWritingValuesTo,$locString.tabIPAssignments,$locString.msgWait)
            Write-IPAssignments

            $tabControl.SelectedTab = $tabControl.TabPages["tabSubnetSummary"]
            $script:genRes = $true
        }
        else
        {
            Write-Log -Message "$($MyInvocation.MyCommand.Name) - WARNING: deployment data failed validation" -Force
            $errList = @($errorText.Value.ToString().Split("`n"))
            [string[]]$message = @(Get-ValidationError -ErrorList $errList)
            $message += "`n`n$($locString.msgFixValidationErrors)"
            Close-WaitForm
            [System.Windows.Forms.MessageBox]::Show($message, $caption, [System.Windows.Forms.MessageBoxButtons]::OK)
            $script:genRes = $false
        }
    }
    catch
    {
        Write-Log -Message "$($MyInvocation.MyCommand.Name) - ERROR: $($PSItem.Exception.Message)" -Force
        $caption = $locString.capError
        [string]$message = $PSItem.Exception.Message
        $message += "`n`n$($locString.msgFixValidationErrors)"
        Close-WaitForm
        [System.Windows.Forms.MessageBox]::Show($message, $caption, [System.Windows.Forms.MessageBoxButtons]::OK)
        $script:genRes = $false
    }
    finally
    {
        Write-Log -Message "$($MyInvocation.MyCommand.Name) - completed"
        Close-WaitForm
    }
}

function Add-PartnerToolkitVersion
{
    param
    (
        [string]$ConfigFile,
        [string]$Version
    )
    try
    {
        if(Test-Path $ConfigFile)
        {
            if(-not $Version)
            {
                $Version = Get-FileVersionInfo
            }
            $cfgData = Get-Content $ConfigFile -Raw | ConvertFrom-Json
            $cfgData | Add-Member -Name "PartnerToolkitVersion" -Value $Version -MemberType NoteProperty
            $cfgData | ConvertTo-Json -Depth 100 | Invoke-TrimJson | Out-File $ConfigFile -Encoding utf8 -Force
            return $true
        }
        else
        {
            throw "File not found"
        }
    }
    catch
    {
        return $false
    }
}

function Invoke-ExportDeploymentData
{
    Write-Log -Message "$($MyInvocation.MyCommand.Name) - called"
    try
    {
        [bool]$script:genRes = $false
        Invoke-GenerateDeploymentData
        if($genRes)
        {
            $subnets = $activeStamp.Supernets.Networks
            if($subnets.Count -eq 0)
            {
                $message = $locString.errMissingData
                $caption = $locString.capMissingData
                Close-WaitForm
                [System.Windows.Forms.MessageBox]::Show($message, $caption, [System.Windows.Forms.MessageBoxButtons]::OK)
            }
            else
            {
                [string]$folder = Get-FolderName
                if($folder)
                {
                    Write-Log -Message "$($MyInvocation.MyCommand.Name) - files will be created in '$($folder)'"
                    # Read values from the tabs and convert to JSON
                    $combinedSerializer = New-Object Microsoft.AzureStack.PartnerToolkit.IPCalculator.Serializer.CombinedJsonDataSerializer
                    Write-Log -Message "$($MyInvocation.MyCommand.Name) - calling CombinedJsonDataSerializer.ProcessExport with a CurrentDirectory of '$($PSScriptRoot)'"
                    $combinedSerializer.CurrentDirectory = $PSScriptRoot
                    [bool]$success = $combinedSerializer.ProcessExport($activeStamp, $folder)
                    if($success)
                    {
                        # Write OEM Partner data to file
                        if($tabControl.TabPages["tabPartner"])
                        {
                            Write-Log -Message "$($MyInvocation.MyCommand.Name) - write PartnerData to $($strPartnerExportFile) file"
                            Get-PartnerData
                            $partnerData | ConvertTo-Json -Depth 10 | Set-Content (Join-Path $folder $strPartnerExportFile)
                        }
                        $message = $locString.msgExportSucceeded
                        $caption = $locString.capSuccess
                        Close-WaitForm
                        [System.Windows.Forms.MessageBox]::Show($message, $caption, [System.Windows.Forms.MessageBoxButtons]::OK)
                    }
                    else
                    {
                        $message = $locString.errExportFailed
                        $caption = $locString.capError
                        Close-WaitForm
                        [System.Windows.Forms.MessageBox]::Show($message, $caption, [System.Windows.Forms.MessageBoxButtons]::OK)
                    }
                }
                else
                {
                    $message = $locString.msgNoFolderSelected
                    $caption = $locString.capWarning
                    Close-WaitForm
                    [System.Windows.Forms.MessageBox]::Show($message, $caption, [System.Windows.Forms.MessageBoxButtons]::OK)
                }
            }
        }
    }
    catch
    {
        Write-Log -Message "$($MyInvocation.MyCommand.Name) - ERROR: $($PSItem.Exception.Message)" -Force
        $errorText = New-Object -TypeName "System.Text.StringBuilder"
        $null = $errorText.Append("An exception occured while generating Json files.")
        $null = $errorText.AppendLine()
        $null = $errorText.AppendFormat("Exception: {0}" -f $PSItem.Exception.Message)
        $null = $errorText.AppendLine()
        $null = $errorText.AppendFormat("Source: {0}" -f $PSItem.Exception.Source)
        $caption = $locString.capError
        $message = $errorText.ToString()
        Close-WaitForm
        [System.Windows.Forms.MessageBox]::Show($message, $caption, [System.Windows.Forms.MessageBoxButtons]::OK)
    }
    finally
    {
        Write-Log -Message "$($MyInvocation.MyCommand.Name) - completed"
        Close-WaitForm
    }
}

function Invoke-ImportDeploymentData
{
    Write-Log -Message "$($MyInvocation.MyCommand.Name) - called"
    try
    {
        [string]$message = $locString.msgImport
        [string]$caption = $locString.capImport
        Close-WaitForm
        $response = [System.Windows.Forms.MessageBox]::Show($message, $caption, [System.Windows.Forms.MessageBoxButtons]::YesNoCancel)
        if($response -eq [System.Windows.Forms.DialogResult]::Yes)
        {
            Remove-GeneratedTabs
            Clear-ExistingData
            if([string]($txtBorderCount.Text) -ne $defaultBorderCount)
            {
                Write-Log -Message "$($MyInvocation.MyCommand.Name) - set the BorderCount to $($defaultBorderCount)"
                $lstRoutingMethod.SelectedIndex = 0
                $txtBorderCount.Text = $defaultBorderCount
                Invoke-RefreshBorderBGP -BorderCount $txtBorderCount.Text
            }
            if([string]($txtSpineCount.Text) -ne $defaultSpineCount)
            {
                Write-Log -Message "$($MyInvocation.MyCommand.Name) - set the SpineCount to $($defaultSpineCount)"
                $txtSpineCount.Text = $defaultSpineCount
            }
            if([string]($txtScaleUnitCount.Text) -ne '1')
            {
                Write-Log -Message "$($MyInvocation.MyCommand.Name) - set the ScaleUnitCount to $($defaultSUCount)"
                $txtScaleUnitCount.Text = $defaultSUCount
                Invoke-Command -ScriptBlock $txtScaleUnitCount_Changed
            }
            [string]$importJsonFile = Get-FileName
            if($importJsonFile)
            {
                Show-WaitForm ("`n $($MyInvocation.MyCommand.Name) : {0}.`n`n {1}" -f $locString.msgImportingData,$locString.msgWait)
                $jsonData = Get-Content $importJsonFile -Raw | ConvertFrom-Json
                # Warn but continue if the ConfigData file was created with a newer version of this module
                if($jsonData.PartnerToolkitVersion)
                {
                    [version]$importVersion = $jsonData.PartnerToolkitVersion
                }
                elseif($jsonData.Version)
                {
                    [version]$importVersion = $jsonData.Version
                }
                if($importVersion -and $importVersion -gt [version](Get-FileVersionInfo))
                {
                    Write-Log -Message "$($MyInvocation.MyCommand.Name) - WARNING: the import file is from a newer version - unexpected values and/or errors may occur as a result" -Force
                }
                $jsonData = $null
                $global:activeStamp = New-Deployment
                Invoke-DeserializeJson $importJsonFile
                if($activeStamp.IsLabScenario -eq $true)
                {
                    Close-WaitForm
                    return
                }
                elseif($activeStamp.IsAddRack -eq $true)
                {
                    $tabControl.SelectedTab = $tabControl.TabPages["tabNetwork"]
                    $activeStamp.IsAddRack = $true
                }
                else
                {
                    $tabControl.SelectedTab = $tabControl.TabPages["tabCustomer"]
                }
                if($tabControl.TabPages.ContainsKey('tabPartner'))
                {
                    $missingNames = @()
                    $partnerDataJsonFile = Join-Path ($importJsonFile | Split-Path -Parent) $strPartnerExportFile
                    if(Test-Path $partnerDataJsonFile)
                    {
                        $importedPartnerData = Get-Content $partnerDataJsonFile | ConvertFrom-Json
                        foreach($name in ($importedPartnerData | Get-Member -Type NoteProperty).Name)
                        {
                            $thisControl = $tabPartner.Controls | Where-Object Name -eq $name
                            if($thisControl)
                            {
                                if($thisControl -is [System.Windows.Forms.TextBox])
                                {
                                    $thisControl.Text = $importedPartnerData.$name
                                }
                                elseif($thisControl -is [System.Windows.Forms.ComboBox])
                                {
                                    $thisControl.SelectedItem = $importedPartnerData.$name
                                }
                                else
                                {
                                    Write-Log -Message "$($MyInvocation.MyCommand.Name) - WARNING: Unknown control type for object named '$($name)'" -Force
                                }
                            }
                            else
                            {
                                Write-Log -Message "$($MyInvocation.MyCommand.Name) - WARNING: The item named '$($name)' no longer exists on the 'OEM Settings' tab" -Force
                                $missingNames += $name
                            }
                        }
                        if($missingNames.Count -ne 0)
                        {
                            $caption = $locString.capWarning
                            $message = "$($locString.msgMissingOEMItems):`n`n"
                            foreach($name in $missingNames)
                            {
                                $message += "$($name)`n"
                            }
                            Close-WaitForm
                            [System.Windows.Forms.MessageBox]::Show($message, $caption, [System.Windows.Forms.MessageBoxButtons]::OK)
                        }
                    }
                    else
                    {
                        Write-Log -Message "$($MyInvocation.MyCommand.Name) - WARNING: $($strPartnerExportFile) was not found with the exported files" -Force
                    }
                }
            }
            else
            {
                Close-WaitForm
                Write-Log -Message "$($MyInvocation.MyCommand.Name) - the user did not select a file to import"
            }
        }
        else
        {
            Write-Log -Message "$($MyInvocation.MyCommand.Name) - the user chose to abort the import"
        }
    }
    catch
    {
        Write-Log -Message "$($MyInvocation.MyCommand.Name) - ERROR: $($PSItem)" -Force
        throw $PSItem
    }
    finally
    {
        Write-Log -Message "$($MyInvocation.MyCommand.Name) - IsLabScenario : $($activeStamp.IsLabScenario)"
        Write-Log -Message "$($MyInvocation.MyCommand.Name) - IsAddRack : $($activeStamp.IsAddRack)"
        Write-Log -Message "$($MyInvocation.MyCommand.Name) - IsExtendedStorage : $($activeStamp.IsExtendedStorage)"
        Write-Log -Message "$($MyInvocation.MyCommand.Name) - completed"
        Close-WaitForm
    }
}

function Invoke-DeserializeJson
{
    param
    (
        [string]$JsonFileName
    )
    Write-Log -Message "$($MyInvocation.MyCommand.Name) - called with a JsonFileName of '$($JsonFileName)'"
    try
    {
        $jsonDeserializer = New-Object Microsoft.AzureStack.PartnerToolkit.IPCalculator.Deserializer.JsonDeserializer
        $global:activeStamp = $jsonDeserializer::DeserializeJson($JsonFileName)
        if(($activeStamp.Clouds).Count -eq 0)
        {
            throw "Imported cloud count is '0'"
        }

        Write-Log -Message "$($MyInvocation.MyCommand.Name) - IsLabScenario : $($activeStamp.IsLabScenario)"
        Write-Log -Message "$($MyInvocation.MyCommand.Name) - IsAddRack : $($activeStamp.IsAddRack)"
        Write-Log -Message "$($MyInvocation.MyCommand.Name) - IsExtendedStorage : $($activeStamp.IsExtendedStorage)"

        if($activeStamp.IsLabScenario -eq $true)
        {
            # Lab Scenario for sure
            Close-WaitForm
            $message = $locString.msgLabNotSupported
            $caption = $locString.capError
            [System.Windows.Forms.MessageBox]::Show($message, $caption, [System.Windows.Forms.MessageBoxButtons]::OK)
            $activeStamp = $null
            return
        }
        elseif($activeStamp.IsAddRack -eq $true)
        {
            # AddRack scenario for sure
            Write-Log -Message "$($MyInvocation.MyCommand.Name) - imported data is for an AddRack scenario" -Force
            if(-not($EnableExperimental))
            {
                Set-Experimental
                Enable-PermitNetworks
            }
            Close-WaitForm
            [System.Windows.Forms.MessageBox]::Show($locString.msgAddSUPreview, $locString.capAttention, [System.Windows.Forms.MessageBoxButtons]::OK)
            Set-AddRack
        }
        elseif((($activeStamp.Clouds).Count -eq 1) -and ($activeStamp.Clouds[0].ScaleUnits).Count -gt 1)
        {
            # Multi-SU, so assume this is a Customer scenario
            Write-Log -Message "$($MyInvocation.MyCommand.Name) - imported data is for a Multi-ScaleUnit scenario" -Force
            if(-not($EnableExperimental))
            {
                Set-Experimental
                Enable-PermitNetworks
            }
            if(($txtScaleUnitCount.Enabled -eq $false) -or ($scenario -eq "AddRack"))
            {
                OnClick_menuItemPreviewMultiSU
            }
        }
        elseif(($activeStamp.Clouds).Count -gt 1)
        {
            # Lab scenario is not supported in the UI at this time
            Close-WaitForm
            $message = $locString.msgLabNotSupported
            $caption = $locString.capError
            [System.Windows.Forms.MessageBox]::Show($message, $caption, [System.Windows.Forms.MessageBoxButtons]::OK)
            return
        }
        elseif($activeStamp.IsExtendedStorage -eq $true)
        {
            Write-Log -Message "$($MyInvocation.MyCommand.Name) - imported data is for an ExtendedStorage scenario"
            if(-not($EnableExperimental))
            {
                Set-Experimental
                Enable-PermitNetworks
            }
            OnClick_menuItemPreviewExtStor
        }
        else
        {
            OnClick_menuItemCustomer
        }
        # Write the imported data to the forms
        Write-DeploymentData
    }
    catch
    {
        Write-Log -Message "$($MyInvocation.MyCommand.Name) - $(($locString.capError).ToUpper([System.Globalization.CultureInfo]::InvariantCulture)): $($PSItem.Exception.Message)"
        $errorText = New-Object -TypeName "System.Text.StringBuilder"
        $null = $errorText.Append($locString.errImportException)
        $null = $errorText.AppendLine()
        $null = $errorText.AppendFormat("{0}: {1}" -f $locString.strException,$PSItem.Exception.Message)
        $null = $errorText.AppendLine()
        $null = $errorText.AppendFormat("{0}: {1}" -f $locString.strSource,$PSItem.Exception.Source)
        $message = $errorText.ToString()
        $caption = $locString.capError
        Close-WaitForm
        [System.Windows.Forms.MessageBox]::Show($message, $caption, [System.Windows.Forms.MessageBoxButtons]::OK)
        throw $PSItem
    }
    finally
    {
        Write-Log -Message "$($MyInvocation.MyCommand.Name) - completed"
    }
}

function Write-DeploymentData
{
    Write-Log -Message "$($MyInvocation.MyCommand.Name) - called"
    try
    {
        # Clouds - there should only be one Cloud for Customer scenarios
        $cloudObj = @($activeStamp.Clouds)[0]
        foreach($property in $arrDeployProperties)
        {
            Write-Log -Message "$($MyInvocation.MyCommand.Name) - process: '$($property)'"
            switch ($property)
            {
                "BorderConnectivity"
                {
                    Write-Log -Message "$($MyInvocation.MyCommand.Name) - set '$($property)' to '$($activeStamp.$property)'"
                    $index = 1
                    if($activeStamp."$property" -match "BGP")
                    {
                        $index = 0
                    }
                    $lstRoutingMethod.SelectedIndex = $index
                    Invoke-Command -ScriptBlock $lstRoutingMethod_Changed
                    break
                }
                "PermitNetworks"
                {
                    $list = @($activeStamp.PermitNetworks)
                    if($list.Count -gt 0)
                    {
                        [string]$listStr = $list -Join ','
                        Write-Log -Message "$($MyInvocation.MyCommand.Name) - set '$($property)' to '$($listStr)'"
                        $list | ForEach-Object `
                        {
                            if(-not [string]::IsNullOrEmpty($PSItem))
                            {
                                [void]$lstPermitNetworks.Items.Add($PSItem)
                            }
                        }
                    }
                    break
                }
                "UplinkSpeed"
                {
                    if($arrUplinkSpeed.Count -gt 0)
                    {
                        $linkSpeed = $activeStamp."$property"
                        $script:openUplinkSpeed = $null
                        if(-not [string]::IsNullOrEmpty($linkSpeed))
                        {
                            Write-Log -Message "$($MyInvocation.MyCommand.Name) - set '$($property)' to '$($linkSpeed)'"
                            if($lstUplinkSpeed.Items.Contains($linkSpeed))
                            {
                                $lstUplinkSpeed.SelectedItem = $linkSpeed
                                $script:openUplinkSpeed = $linkSpeed
                            }
                            else
                            {
                                Write-Log -Message "$($MyInvocation.MyCommand.Name) - WARNING: '$($linkSpeed)' is not a valid '$($property)'" -Force
                                $lstUplinkSpeed.SelectedIndex = -1
                            }
                        }
                        else
                        {
                            Write-Log -Message "$($MyInvocation.MyCommand.Name) - WARNING: '$($property)' value is not present in this ConfigurationData file" -Force
                            $lstUplinkSpeed.SelectedIndex = -1
                        }
                    }
                    break
                }
                default
                {
                }
            }
        }
        foreach($cloud in $cloudObj)
        {
            [string]$strID = $cloud.Name.Substring($cloud.Name.Length -2)
            [int]$numID = $cloud.Name.Substring($cloud.Name.Length -2)
            foreach($property in $arrCloudProperties)
            {
                Write-Log -Message "$($MyInvocation.MyCommand.Name) - process: '$($property)'"
                switch ($property)
                {
                    "NodeCount"
                    {
                        # Do nothing as this will be calculated based on SU node counts
                    }
                    "DNSForwarder"
                    {
                        # Handle DNSForwarder differently as it could be an array that needs to be passed as a comma-separated string
                        $list = @($cloud."$property")
                        [string]$listStr = $list -Join ','
                        $txtDNSForwarder.Text = $listStr
                        Invoke-Command -ScriptBlock $txtDNSForwarder_Changed
                    }
                    "TimeServer"
                    {
                        $txtTimeServer.Text = $cloud."$property"
                        Invoke-Command -ScriptBlock $txtTimeServer_Changed
                    }
                    "SyslogServerIPv4Address"
                    {
                        $list = @($cloud."$property")
                        [string]$listStr = $list -Join ','
                        if([string]::IsNullOrEmpty($listStr))
                        {
                                $txtSyslogServer.Text = [string]::Empty
                        }
                        else
                        {
                            $txtSyslogServer.Text = $listStr
                        }
                        Invoke-Command -ScriptBlock $txtSyslogServer_Changed
                    }
                    "ConnectToAzure"
                    {
                        if($cloud.InfraAzureDirectoryTenantName)
                        {
                            $index = 0
                        }
                        elseif($cloud."$property" -eq "Azure Active Directory")
                        {
                            $index = 0
                        }
                        else
                        {
                            $index = 1
                        }
                        $lstIdentityStore.SelectedIndex = $index
                        Invoke-Command -ScriptBlock $lstIdentityStore_Changed
                    }
                    "InfraAzureEnvironment"
                    {
                        [string]$val = $cloud."$property"
                        if($val)
                        {
                            if($arrAzureEnvironment -contains $val)
                            {
                                $lstAzureEnvironment.SelectedItem = $val
                            }
                            else
                            {
                                Write-Log -Message "$($MyInvocation.MyCommand.Name) - WARNING: Unknown InfraAzureEnvironment value of '$($val)'" -Force
                            }
                        }
                    }
                    "InfraAzureDirectoryTenantName"
                    {
                        [string]$val = $cloud."$property"
                        if($val)
                        {
                            $txtAADTenantName.Text = $val
                        }
                        Invoke-Command -ScriptBlock $txtAADTenantName_Changed
                    }
                    "ADFSForestFQDN"
                    {
                        [string]$val = $cloud."$property"
                        if($val)
                        {
                            $txtADFSForestFQDN.Text = $val
                        }
                        Invoke-Command -ScriptBlock $txtADFSForestFQDN_Changed
                    }
                    "ADFSProviderName"
                    {
                        [string]$val = $cloud."$property"
                        if($val)
                        {
                            $txtADFSProviderName.Text = $val
                        }
                        Invoke-Command -ScriptBlock $txtADFSProviderName_Changed
                    }
                    "ADFSMetadataUri"
                    {
                        [string]$val = $cloud."$property"
                        if($val)
                        {
                            $txtADFSMetadataUri.Text = $val
                        }
                        Invoke-Command -ScriptBlock $txtADFSMetadataUri_Changed
                    }
                    "PhysicalNamingPrefix"
                    {
                        [string]$val = $cloud."$property"
                        $txtPhysicalPrefix.Text = $val
                        Invoke-Command -ScriptBlock $txtPhysicalPrefix_Changed
                    }
                    "NamingPrefix"
                    {
                        [string]$val = $cloud."$property"
                        $txtDeployPrefix.Text = $val
                        Invoke-Command -ScriptBlock $txtDeployPrefix_Changed
                    }
                    "CompanyName"
                    {
                        [string]$val = $cloud."$property"
                        $txtCompanyName.Text = $val
                    }
                    "RegionName"
                    {
                        [string]$val = $cloud."$property"
                        $txtRegionName.Text = $val
                        Invoke-Command -ScriptBlock $txtRegionName_Changed
                    }
                    "DomainFQDN"
                    {
                        [string]$val = $cloud."$property"
                        $txtPrivateDomain.Text = $val
                        Invoke-Command -ScriptBlock $txtPrivateDomain_Changed
                    }
                    "ExternalDomainFQDN"
                    {
                        [string]$val = $cloud."$property"
                        $txtExternalDomain.Text = $val
                        Invoke-Command -ScriptBlock $txtExternalDomain_Changed
                    }
                    default
                    {
                        [string]$val = $cloud."$property"
                        Write-Log  -Message "$($MyInvocation.MyCommand.Name) - WARNING: Unhandled property '$($property)' with a value of '$($val)'" -Force
                    }
                }
            }
            # ScaleUnits - there could be multiple Scale-Units for Customer scenarios
            $scaleUnits = @($cloud.ScaleUnits)
            [string]$suCount = $scaleUnits.Count
            $txtScaleUnitCount.Text = $suCount
            Invoke-Command -ScriptBlock $txtScaleUnitCount_Changed
            if([int]$suCount -gt 1)
            {
                $txtScaleUnitCount.Enabled = $true
                $txtScaleUnitCount.ReadOnly = $false
            }
        }
        Write-Log -Message "$($MyInvocation.MyCommand.Name) - process: Border Count"
        $switchObj = $activeStamp.Switches
        $borderObj = @($switchObj | Where-Object SwitchType -eq "Border")
        $torObj = @($switchObj | Where-Object SwitchType -eq "TOR1")
        [string]$val = $borderObj.Count
        $invalidSwitchImportData = @()
        Write-Log -Message "$($MyInvocation.MyCommand.Name) - process: Switch Vendor"
        $validSwitchMakes = Get-SwitchVendors
        $switchMake = $torObj[0].Make
        $validMake = $false
        Clear-ListSelection $lstSwitchVendor
        if($validSwitchMakes -contains $switchMake)
        {
            $lstSwitchVendor.SelectedItem = $switchMake
            $validMake = $true
            Invoke-Command -ScriptBlock $lstSwitchVendor_Changed
        }
        else
        {
            Write-Log -Message "$($MyInvocation.MyCommand.Name) - WARNING: The imported Switch Vendor '$($switchMake)' is no longer valid" -Force
            $invalidSwitchImportData += "SwitchMake: $($switchMake)"
            Clear-ListSelection $lstSwitchVendor
        }
        Clear-ListSelection $lstTORModel
        Clear-ListSelection $lstTORFirmware
        if(($switchMake -ne "CiscoUCS") -and ($switchMake -ne "Mellanox"))
        {
            Clear-ListSelection $lstBMCModel
            Clear-ListSelection $lstBMCFirmware
            Clear-ListSelection $lstSpineModel
            Clear-ListSelection $lstSpineFirmware
        }
        $script:validBorder = $maxBorder
        $txtBorderCount.Text = $val
        Invoke-Command -ScriptBlock $txtBorderCount_Changed
        Write-Log -Message "$($MyInvocation.MyCommand.Name) - process: Border BGP ASN values"
        $goodAsn = 0
        foreach($sw in $borderObj)
        {
            $i = $sw.Hostname.Split('-') | Select-Object -Last 1
            if([int]$i -le [int]$maxBorder)
            {
                switch ($i)
                {
                    '1'
                    {
                        $txtBorderBGP1.Text = $sw.Asn
                        Invoke-Command -ScriptBlock $txtBorderBGP1_Changed
                        break
                    }
                    '2'
                    {
                        $txtBorderBGP2.Text = $sw.Asn
                        Invoke-Command -ScriptBlock $txtBorderBGP2_Changed
                        break
                    }
                    '3'
                    {
                        $txtBorderBGP3.Text = $sw.Asn
                        Invoke-Command -ScriptBlock $txtBorderBGP3_Changed
                        break
                    }
                    '4'
                    {
                        $txtBorderBGP4.Text = $sw.Asn
                        Invoke-Command -ScriptBlock $txtBorderBGP4_Changed
                        break
                    }
                    default
                    {
                    }
                }
            }
            # Special case for old exports that might have duplicate Border names (find a good ASN)
            if(($goodAsn -eq 0) -and ($sw.Asn) -and ($i -le 2))
            {
                $goodAsn = $sw.Asn
            }
        }
        # Special case for old exports that might have duplicate Border names (set ASN of Border devices that did not get set correctly)
        for($b = 1; $b -le $borderObj.Count; $b++)
        {
            if([int]$b -le [int]$validBorder)
            {
                switch ($b)
                {
                    '1'
                    {
                        $val = $txtBorderBGP1.Text
                        if((-not $val) -and ($goodAsn -ne 0))
                        {
                            $txtBorderBGP1.Text = $goodAsn
                            Invoke-Command -ScriptBlock $txtBorderBGP1_Changed
                        }
                        break
                    }
                    '2'
                    {
                        $val = $txtBorderBGP2.Text
                        if((-not $val) -and ($goodAsn -ne 0))
                        {
                            $txtBorderBGP2.Text = $goodAsn
                            Invoke-Command -ScriptBlock $txtBorderBGP2_Changed
                        }
                        break
                    }
                    '3'
                    {
                        $val = $txtBorderBGP3.Text
                        if((-not $val) -and ($goodAsn -ne 0))
                        {
                            $txtBorderBGP3.Text = $goodAsn
                            Invoke-Command -ScriptBlock $txtBorderBGP3_Changed
                        }
                        break
                    }
                    '4'
                    {
                        $val = $txtBorderBGP4.Text
                        if((-not $val) -and ($goodAsn -ne 0))
                        {
                            $txtBorderBGP4.Text = $goodAsn
                            Invoke-Command -ScriptBlock $txtBorderBGP4_Changed
                        }
                        break
                    }
                    default
                    {
                    }
                }
            }
        }
        if(($switchMake -eq 'msftcisco') -and (-not $isStaticRouting))
        {
            Write-Log -Message "$($MyInvocation.MyCommand.Name) - process: Edge BGP ASN value"
            $edgeObj = @($switchObj | Where-Object SwitchType -eq "Edge")
            if($edgeObj.Count -eq 0)
            {
                # This may be an old Export that did not have Edge Asn, so warn but continue
                Write-Log -Message "$($MyInvocation.MyCommand.Name) - WARNING: The required Edge ASN value is not present" -Force
            }
            else
            {
                $txtEdgeBGP.Text = $edgeObj[0].Asn
                Invoke-Command -ScriptBlock $txtEdgeBGP_Changed
            }
        }

        Write-Log -Message "$($MyInvocation.MyCommand.Name) - process: Software MUX BGP ASN value"
        $muxObj = @($switchObj | Where-Object SwitchType -eq "MUX")
        $txtSoftwareBGP.Text = $muxObj[0].Asn
        Invoke-Command -ScriptBlock $txtSoftwareBGP_Changed
        $netObj = @($activeStamp.Supernets)

        Write-Log -Message "$($MyInvocation.MyCommand.Name) - process: External Network"
        $extNet = $netObj | Where-Object Name -eq "CL01-External"
        $txtExternalSubnet.Text = $extNet.IPv4.Network.IPAddressToString
        $txtExternalSubnetSize.Text = $extNet.IPv4.Cidr
        Invoke-Command -ScriptBlock $txtExternalSubnet_Changed
        Invoke-Command -ScriptBlock $txtExternalSubnetSize_Changed

        Write-Log -Message "$($MyInvocation.MyCommand.Name) - process: Private Network"
        $pvtNet = $netObj | Where-Object Name -eq "CL01-Private"
        if([int]$pvtNet.IPv4.Cidr -le 20)
        {
            $txtPrivateSubnet.Text = $pvtNet.IPv4.Network.IPAddressToString
            Invoke-Command -ScriptBlock $txtPrivateSubnet_Changed
        }
        else
        {
            Write-Log -Message "$($MyInvocation.MyCommand.Name) - WARNING: The imported Private Network is not valid" -Force
        }
        $torObj = @($switchObj | Where-Object SwitchType -like "TOR*")
        $bmcObj = @($switchObj | Where-Object SwitchType -like "BMC*")
        if($validMake)
        {
            Write-Log -Message "$($MyInvocation.MyCommand.Name) - process: TOR Switch settings"
            $validTOR = Get-SwitchModels -Make $switchMake -Type 'TOR'
            if(($torObj.Count -gt 0) -and ($null -ne $torObj[0].Model))
            {
                Write-Log -Message "$($MyInvocation.MyCommand.Name) - TOR Model: $($torObj[0].Model)"
                if($validTOR -contains $torObj[0].Model)
                {
                    $firmwareTOR = Get-SwitchFirmware -Make $switchMake -Type 'TOR' -Model $torObj[0].Model
                    $lstTORModel.SelectedIndex = $lstTORModel.FindStringExact($torObj[0].Model)
                    if($null -ne $torObj[0].FIRMWAREVERSION)
                    {
                        Write-Log -Message "$($MyInvocation.MyCommand.Name) - TOR Firmware: $($torObj[0].FIRMWAREVERSION)"
                        if($firmwareTOR -contains $torObj[0].FIRMWAREVERSION)
                        {
                            $lstTORFirmware.SelectedIndex = $lstTORFirmware.FindStringExact($torObj[0].FIRMWAREVERSION)
                        }
                        else
                        {
                            Write-Log -Message "$($MyInvocation.MyCommand.Name) - WARNING: The imported TOR firmware version '$($torObj[0].FIRMWAREVERSION)' is no longer valid" -Force
                            $invalidSwitchImportData += "TOR Firmware: $($torObj[0].FIRMWAREVERSION)"
                            Clear-ListSelection $lstTORFirmware
                        }
                    }
                    else
                    {
                        Write-Log -Message "$($MyInvocation.MyCommand.Name) - WARNING: The imported TOR firmware version was null" -Force
                        $invalidSwitchImportData += "TOR Firmware: null"
                        Clear-ListSelection $lstTORFirmware
                    }
                    Update-MaxBorderFromSwitch
                }
                else
                {
                    Write-Log -Message "$($MyInvocation.MyCommand.Name) - WARNING: The imported TOR switch model '$($torObj[0].Model)' is no longer valid" -Force
                    $invalidSwitchImportData += "TOR Model: $($torObj[0].Model)"
                    Clear-ListSelection $lstTORModel
                }
            }
            else
            {
                Write-Log -Message "$($MyInvocation.MyCommand.Name) - WARNING: The imported TOR switch model was null" -Force
                $invalidSwitchImportData += "TOR Model: null"
                Clear-ListSelection $lstTORModel
            }
            if(($switchMake -ne "CiscoUCS") -and ($switchMake -ne "Mellanox"))
            {
                Write-Log -Message "$($MyInvocation.MyCommand.Name) - process: BMC Switch settings"
                $validBMC = Get-SwitchModels -Make $switchMake -Type 'BMC'
                if(($bmcObj.Count -gt 0) -and ($null -ne $bmcObj[0].Model))
                {
                    Write-Log -Message "$($MyInvocation.MyCommand.Name) - BMC Model: $($bmcObj[0].Model)"
                    if($validBMC -contains $bmcObj[0].Model)
                    {
                        $firmwareBMC = Get-SwitchFirmware -Make $switchMake -Type 'BMC' -Model $bmcObj[0].Model
                        $lstBMCModel.SelectedIndex = $lstBMCModel.FindStringExact($bmcObj[0].Model)
                        if($null -ne $bmcObj[0].FIRMWAREVERSION)
                        {
                            Write-Log -Message "$($MyInvocation.MyCommand.Name) - BMC Firmware: $($bmcObj[0].FIRMWAREVERSION)"
                            if($firmwareBMC -contains $bmcObj[0].FIRMWAREVERSION)
                            {
                                $lstBMCFirmware.SelectedIndex = $lstBMCFirmware.FindStringExact($bmcObj[0].FIRMWAREVERSION)
                            }
                            else
                            {
                                Write-Log -Message "$($MyInvocation.MyCommand.Name) - WARNING: The imported BMC switch firmware version '$($bmcObj[0].FIRMWAREVERSION)' is no longer valid" -Force
                                $invalidSwitchImportData += "BMC Firmware: $($bmcObj[0].FIRMWAREVERSION)"
                                Clear-ListSelection $lstBMCFirmware
                            }
                        }
                        else
                        {
                            Write-Log -Message "$($MyInvocation.MyCommand.Name) - WARNING: The imported BMC firmware version was null" -Force
                            $invalidSwitchImportData += "BMC Firmware: null"
                            Clear-ListSelection $lstBMCFirmware
                        }
                    }
                    else
                    {
                        Write-Log -Message "$($MyInvocation.MyCommand.Name) - WARNING: The imported BMC switch model '$($bmcObj[0].Model)' is no longer valid" -Force
                        $invalidSwitchImportData += "BMC Model: $($bmcObj[0].Model)"
                        Clear-ListSelection $lstBMCModel
                    }
                }
                else
                {
                    Write-Log -Message "$($MyInvocation.MyCommand.Name) - WARNING: The imported BMC switch model was null" -Force
                    $invalidSwitchImportData += "BMC Model: null"
                    Clear-ListSelection $lstBMCModel
                }
            }
        }
        if($scaleUnits.Count -gt 1)
        {
            if(($validMake) -and ($switchMake -ne "CiscoUCS") -and ($switchMake -ne "Mellanox"))
            {
                Write-Log -Message "$($MyInvocation.MyCommand.Name) - process: SPINE Switch settings"
                $spineObj = @($switchObj | Where-Object SwitchType -eq "Spine")
                $txtSpineCount.Text = $spineObj.Count
                Invoke-Command -ScriptBlock $txtSpineCount_Changed
                $txtSpineBGP.Text = $spineObj[0].Asn
                Invoke-Command -ScriptBlock $txtSpineBGP_Changed
                $modelsSpine = Get-SwitchModels -Make $switchMake -Type 'SPINE'
                if(($spineObj.Count -gt 0) -and ($null -ne $spineObj[0].Model))
                {
                    if($modelsSpine -contains $spineObj[0].Model)
                    {
                        $lstSpineModel.SelectedIndex = $lstSpineModel.FindStringExact($spineObj[0].Model)
                        $firmwareSpine = Get-SwitchFirmware -Make $switchMake -Type 'SPINE' -Model $spineObj[0].Model
                        if($null -ne $spineObj[0].FIRMWAREVERSION)
                        {
                            if($firmwareSpine -contains $spineObj[0].FIRMWAREVERSION)
                            {
                                $lstSpineFirmware.SelectedIndex = $lstSpineFirmware.FindStringExact($spineObj[0].FIRMWAREVERSION)
                            }
                            else
                            {
                                Write-Log -Message "$($MyInvocation.MyCommand.Name) - WARNING: The imported SPINE switch firmware version '$($spineObj[0].FIRMWAREVERSION)' is no longer valid" -Force
                                $invalidSwitchImportData += "SPINE Firmware: $($spineObj[0].FIRMWAREVERSION)"
                                Clear-ListSelection $lstSpineFirmware
                            }
                        }
                        else
                        {
                            Write-Log -Message "$($MyInvocation.MyCommand.Name) - WARNING: The imported SPINE firmware version was null" -Force
                            $invalidSwitchImportData += "SPINE Firmware: null"
                            Clear-ListSelection $lstSpineFirmware
                        }
                    }
                    else
                    {
                        Write-Log -Message "$($MyInvocation.MyCommand.Name) - WARNING: The imported SPINE switch model '$($spineObj[0].Model)' is no longer valid" -Force
                        $invalidSwitchImportData += "SPINE Model: $($spineObj[0].Model)"
                        Clear-ListSelection $lstSpineModel
                    }
                }
                else
                {
                    Write-Log -Message "$($MyInvocation.MyCommand.Name) - WARNING: The imported SPINE switch model was null" -Force
                    $invalidSwitchImportData += "SPINE Model: null"
                    Clear-ListSelection $lstSpineModel
                }
                $spineNet = $netObj | Where-Object Name -eq "Rack01-SpineInfrastructure"
                $txtSpineSubnet.Text = $spineNet.IPv4.Network.IPAddressToString
                Invoke-Command -ScriptBlock $txtSpineSubnet_Changed
            }
        }
        foreach($su in $scaleUnits)
        {
            [string]$suID = "SU{0:d2}" -f $su.Index
            [string]$rackId = $su.RackId
            [string]$rack = $activeStamp.Racks | Where-Object Name -eq $rackId
            [string]$suTab = "tabSU$($su.Index)"
            Write-Log -Message "$($MyInvocation.MyCommand.Name) - process: Scale-Unit '$($suID)' settings on tab '$($suTab)'"

            Write-Log -Message "$($MyInvocation.MyCommand.Name) - set '$($suID)' NodeCount"
            $scaleUnitTabs.$suTab.Objects.txtNodeCount.Text = $su.NodeCount

            Write-Log -Message "$($MyInvocation.MyCommand.Name) - set '$($suID)' Infrastructure Network"
            $swInf = $netObj | Where-Object Name -match "$($suID)-Infrastructure"
            $scaleUnitTabs.$suTab.Objects.txtInfraSubnet.Text = $swInf.IPv4.Network.IPAddressToString

            $suSwitch = @($switchObj | Where-Object Hostname -like "*-$($suID)-*")
            if($suSwitch.Count -eq 0)
            {
                $suSwitch = @($switchObj | Where-Object SwitchType -like "*TOR*")
            }

            Write-Log -Message "$($MyInvocation.MyCommand.Name) - set '$($rackId)' BMC Network"
            $swBmc = $netObj | Where-Object Name -match "$($rackId)-BMC"
            $scaleUnitTabs.$suTab.Objects.txtBMCSubnet.Text = $swBmc.IPv4.Network.IPAddressToString

            $skipThis = $false
            if(($su.Index -gt 1) -and (($switchMake -eq "CiscoUCS") -or ($switchMake -eq "Mellanox")))
            {
                $skipThis = $true
            }
            if(-not $skipThis)
            {
                if($switchMake -ne 'msftcisco')
                {
                    Write-Log -Message "$($MyInvocation.MyCommand.Name) - set '$($rackId)' TOR BGP ASN value"
                    $tor1 = $suSwitch | Where-Object Hostname -like "*$($rackId)-TOR-1"
                    $scaleUnitTabs.$suTab.Objects.txtTORASN.Text = $tor1.Asn
                }
                Write-Log -Message "$($MyInvocation.MyCommand.Name) - set '$($rackId)' SwitchInfra Network"
                $swSwi = $netObj | Where-Object Name -match "$($rackId)-SwitchInfrastructure"
                $scaleUnitTabs.$suTab.Objects.txtSwitchInfraSubnet.Text = $swSwi.IPv4.Network.IPAddressToString
            }
        }
        if((-not [string]::IsNullOrEmpty($script:openUplinkSpeed)) -and ($true -eq $lstUplinkSpeed.Enabled))
        {
            if($lstUplinkSpeed.Items -contains $script:openUplinkSpeed)
            {
                $lstUplinkSpeed.SelectedItem = $script:openUplinkSpeed
            }
        }
        Invoke-RefreshTotalNodeCount
        [string]$caption = $locString.capImportComplete
        [string]$message = $locString.msgImportComplete
        Close-WaitForm
        [System.Windows.Forms.MessageBox]::Show($message, $caption, [System.Windows.Forms.MessageBoxButtons]::OK)
    }
    catch
    {
        Close-WaitForm
        Write-Log -Message "$($MyInvocation.MyCommand.Name) - ERROR: $($PSItem.Exception.Message)" -Force
        throw $PSItem
    }
    finally
    {
        if($invalidSwitchImportData.Count -ne 0)
        {
            $caption = $locString.capWarning
            $message = "$($locString.msgInvalidSwitchData):`n`n"
            foreach($item in $invalidSwitchImportData)
            {
                $message += "$($item)`n"
            }
            Close-WaitForm
            [System.Windows.Forms.MessageBox]::Show($message, $caption, [System.Windows.Forms.MessageBoxButtons]::OK)
        }
        Write-Log -Message "$($MyInvocation.MyCommand.Name) - completed"
    }
}

function Write-OpenDataToTab
{
    param
    (
        [Parameter(Mandatory=$true)]
        [string]$TabName
    )
    Write-Log -Message "$($MyInvocation.MyCommand.Name) - called for TabName '$($TabName)'"
    $thisTab = $tabControl.TabPages[$TabName]
    $orderList = @()
    if($TabName -eq "tabPartner")
    {
        ($openData.$TabName | Get-Member -Type NoteProperty).Name | ForEach-Object { $orderList += $PSItem }
    }
    else
    {
        $doFirst = @()
        $doSecond = @()
        $doLast = @()
        if($openData.$TabName)
        {
            ($openData.$TabName | Get-Member -Type NoteProperty).Name | ForEach-Object `
            {
                if(($PSItem -like "*Model") -or ($PSItem -like "txtBorderBGP*"))
                {
                    $doSecond += $PSItem
                }
                elseif($PSItem -like "*Firmware")
                {
                    $doLast += $PSItem
                }
                else
                {
                    $doFirst += $PSItem
                }
            }
            $orderList += $doFirst
            $orderList += $doSecond
            $orderList += $doLast
        }
    }
    foreach($item in $orderList)
    {
        if($openData.$TabName.$item)
        {
            Write-Log -Message "$($MyInvocation.MyCommand.Name) - set control named '$($item)' to '$($openData.$TabName.$item)'"
            # Special case ListBox items
            if(($item -like "lstPermitNetworks*") -and ($thisTab.Controls[$item]))
            {
                ($openData.$TabName.$item).Split(',') | ForEach-Object `
                {
                    [void]$thisTab.Controls[$item].Items.Add($PSItem)
                }
                continue
            }
            # Special case for Border settings saved on Network tab instead of Border tab
            if(($item -eq "lstRoutingMethod" -or $item -eq "txtBorderCount" -or $item -match "txtBorderBGP") -and $TabName -eq 'tabNetwork')
            {
                if($tabBorder.Controls[$item] -is [System.Windows.Forms.TextBox])
                {
                    $tabBorder.Controls[$item].Text = $openData.$TabName.$item
                    $validationEvent = Get-Variable -Name ($item + '_Changed') -ErrorAction SilentlyContinue
                    if($validationEvent)
                    {
                        Invoke-Command -ScriptBlock $validationEvent.Value
                    }
                }
                elseif($tabBorder.Controls[$item] -is [System.Windows.Forms.ComboBox])
                {
                    $tabBorder.Controls[$item].SelectedItem = $openData.$TabName.$item
                }
                continue
            }
            elseif(($thisTab.Controls[$item] -is [System.Windows.Forms.TextBox]) -or ($thisTab.Controls[$item] -is [System.Windows.Forms.RichTextBox]))
            {
                $thisTab.Controls[$item].Text = $openData.$TabName.$item
                $validationEvent = Get-Variable -Name ($item + '_Changed') -ErrorAction SilentlyContinue
                if($validationEvent)
                {
                    Invoke-Command -ScriptBlock $validationEvent.Value
                }
            }
            elseif($thisTab.Controls[$item] -is [System.Windows.Forms.ComboBox])
            {
                if($thisTab.Controls[$item].Items -contains $openData.$TabName.$item)
                {
                    $thisTab.Controls[$item].SelectedIndex = $thisTab.Controls[$item].FindStringExact($openData.$TabName.$item)
                    if($item -like "*SwitchVendor")
                    {
                        Clear-ListSelection $lstTORFirmware
                        Clear-ListSelection $lstBMCFirmware
                        Clear-ListSelection $lstSpineFirmware
                        Clear-ListSelection $lstTORModel
                        Clear-ListSelection $lstBMCModel
                        Clear-ListSelection $lstSpineModel
                    }
                    elseif($item -like "*TORModel")
                    {
                        Clear-ListSelection $lstTORFirmware
                        Update-MaxBorderFromSwitch
                    }
                    elseif($item -like "*BMCModel")
                    {
                        Clear-ListSelection $lstBMCFirmware
                    }
                    elseif($item -like "*SpineModel")
                    {
                        Clear-ListSelection $lstSpineFirmware
                    }
                    elseif($item -match "UplinkSpeed")
                    {
                        $script:openUplinkSpeed = $openData.$TabName.$item
                    }
                }
                else
                {
                    Write-Log -Message "$($MyInvocation.MyCommand.Name) - WARNING: '$($item)' does not contain '$($openData.$TabName.$item)'" -Force
                    Clear-ListSelection $thisTab.Controls[$item]
                    $script:invalidOpenData += $item
                }
            }
            else
            {
                Write-Log -Message "$($MyInvocation.MyCommand.Name) - WARNING: '$($item)' does not exist on the tab named '$($TabName)'" -Force
                $script:invalidOpenData += $item
            }
        }
    }
    Write-Log -Message "$($MyInvocation.MyCommand.Name) - completed"
}

function Export-NetMap
{
    param
    (
        [Parameter(Mandatory=$true)]
        [string]$InputFile,
        [Parameter(Mandatory=$true)]
        [string]$OutputDirectory,
        [switch]$Force
    )
    try
    {
        Invoke-LoadLocalizedData
        if(-not(Test-Path $InputFile))
        {
            $message = ($locString.msgFileNotFound -f $InputFile)
            throw $message
        }
        if(-not($InputFile.EndsWith('.json')))
        {
            $message = ($locString.msgFileNotValid -f $InputFile)
            throw $message
        }
        if(-not(Test-Path $OutputDirectory))
        {
            try
            {
                New-Item -ItemType Directory -Path $OutputDirectory | Out-Null
            }
            catch
            {
                throw $PSItem
            }
        }
        if((Test-Path (Join-Path $OutputDirectory 'ConfigurationData.json')) -or (Test-Path (Join-Path $OutputDirectory 'DeploymentData.json')))
        {
            if($Force)
            {
                if(Test-Path (Join-Path $OutputDirectory 'ConfigurationData.json'))
                {
                    Remove-Item (Join-Path $OutputDirectory 'ConfigurationData.json') -Force | Out-Null
                }
                if((Get-ChildItem (Join-Path $OutputDirectory 'DeploymentData*.json')).Count -gt 0)
                {
                    Remove-Item (Join-Path $OutputDirectory 'DeploymentData*.json') -Force | Out-Null
                }
                if(Test-Path (Join-Path $OutputDirectory 'onenode'))
                {
                    Remove-Item (Join-Path $OutputDirectory 'onenode') -Recurse -Force | Out-Null
                }
            }
            else
            {
                $message = ($locString.msgOverwriteConfigData -f $InputFile)
                Write-Error $message
                return
            }
        }
        $script:dllIPCalculator = 'Microsoft.AzureStack.PartnerToolkit.IPCalculator.dll'
        $script:dllJsonParser = 'Newtonsoft.Json.dll'
        $script:dllIPNetwork = 'System.Net.IPNetwork.dll'
        try
        {
            Add-Assembly (Join-Path $PSScriptRoot $dllIPCalculator)
            Add-Assembly (Join-Path $PSScriptRoot $dllJsonParser)
            Add-Assembly (Join-Path $PSScriptRoot $dllIPNetwork)
        }
        catch
        {
            if($PSItem.Exception.Message -notmatch "already loaded")
            {
                throw $PSItem
            }
        }
        $jsonDeserializer = New-Object Microsoft.AzureStack.PartnerToolkit.IPCalculator.Deserializer.JsonDeserializer
        $activeStamp = $jsonDeserializer::DeserializeJson((Resolve-Path $InputFile))
        $activeStamp.SetCurrentPath($PSScriptRoot)
        $ref = New-Object -TypeName "System.Text.StringBuilder"
        $script:errorText = [ref]$ref
        [bool]$isValid = $activeStamp.Validate($errorText)
        if($isValid)
        {
            $activeStamp.Generate()
            $combinedSerializer = New-Object Microsoft.AzureStack.PartnerToolkit.IPCalculator.Serializer.CombinedJsonDataSerializer
            $combinedSerializer.CurrentDirectory = $PSScriptRoot
            [bool]$success = $combinedSerializer.ProcessExport($activeStamp, (Resolve-Path $OutputDirectory))
            if(-not $success)
            {
                $message = $locString.errExportFailed
                throw $message
            }
        }
        else
        {
            $errList = @($errorText.Value.ToString().Split("`n"))
            [string[]]$message = @(Get-ValidationError -ErrorList $errList)
            [string]$errStr = ($locString.capError).ToUpper([System.Globalization.CultureInfo]::InvariantCulture).Trim()
            foreach($msg in $message)
            {
                $msg = $msg.Trim()
                Write-Verbose -Message "$($errStr): $($msg)" -Verbose
            }
            throw $locString.msgValidationFailed
        }
    }
    catch
    {
        throw $PSItem
    }
}
# SIG # Begin signature block
# MIIjhgYJKoZIhvcNAQcCoIIjdzCCI3MCAQExDzANBglghkgBZQMEAgEFADB5Bgor
# BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG
# KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCAHPsDcmdD1QBHa
# zoxHGR34lfKChDd1Y/jKfWgPVsZw3qCCDYEwggX/MIID56ADAgECAhMzAAABh3IX
# chVZQMcJAAAAAAGHMA0GCSqGSIb3DQEBCwUAMH4xCzAJBgNVBAYTAlVTMRMwEQYD
# VQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNy
# b3NvZnQgQ29ycG9yYXRpb24xKDAmBgNVBAMTH01pY3Jvc29mdCBDb2RlIFNpZ25p
# bmcgUENBIDIwMTEwHhcNMjAwMzA0MTgzOTQ3WhcNMjEwMzAzMTgzOTQ3WjB0MQsw
# CQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9u
# ZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMR4wHAYDVQQDExVNaWNy
# b3NvZnQgQ29ycG9yYXRpb24wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB
# AQDOt8kLc7P3T7MKIhouYHewMFmnq8Ayu7FOhZCQabVwBp2VS4WyB2Qe4TQBT8aB
# znANDEPjHKNdPT8Xz5cNali6XHefS8i/WXtF0vSsP8NEv6mBHuA2p1fw2wB/F0dH
# sJ3GfZ5c0sPJjklsiYqPw59xJ54kM91IOgiO2OUzjNAljPibjCWfH7UzQ1TPHc4d
# weils8GEIrbBRb7IWwiObL12jWT4Yh71NQgvJ9Fn6+UhD9x2uk3dLj84vwt1NuFQ
# itKJxIV0fVsRNR3abQVOLqpDugbr0SzNL6o8xzOHL5OXiGGwg6ekiXA1/2XXY7yV
# Fc39tledDtZjSjNbex1zzwSXAgMBAAGjggF+MIIBejAfBgNVHSUEGDAWBgorBgEE
# AYI3TAgBBggrBgEFBQcDAzAdBgNVHQ4EFgQUhov4ZyO96axkJdMjpzu2zVXOJcsw
# UAYDVR0RBEkwR6RFMEMxKTAnBgNVBAsTIE1pY3Jvc29mdCBPcGVyYXRpb25zIFB1
# ZXJ0byBSaWNvMRYwFAYDVQQFEw0yMzAwMTIrNDU4Mzg1MB8GA1UdIwQYMBaAFEhu
# ZOVQBdOCqhc3NyK1bajKdQKVMFQGA1UdHwRNMEswSaBHoEWGQ2h0dHA6Ly93d3cu
# bWljcm9zb2Z0LmNvbS9wa2lvcHMvY3JsL01pY0NvZFNpZ1BDQTIwMTFfMjAxMS0w
# Ny0wOC5jcmwwYQYIKwYBBQUHAQEEVTBTMFEGCCsGAQUFBzAChkVodHRwOi8vd3d3
# Lm1pY3Jvc29mdC5jb20vcGtpb3BzL2NlcnRzL01pY0NvZFNpZ1BDQTIwMTFfMjAx
# MS0wNy0wOC5jcnQwDAYDVR0TAQH/BAIwADANBgkqhkiG9w0BAQsFAAOCAgEAixmy
# S6E6vprWD9KFNIB9G5zyMuIjZAOuUJ1EK/Vlg6Fb3ZHXjjUwATKIcXbFuFC6Wr4K
# NrU4DY/sBVqmab5AC/je3bpUpjtxpEyqUqtPc30wEg/rO9vmKmqKoLPT37svc2NV
# BmGNl+85qO4fV/w7Cx7J0Bbqk19KcRNdjt6eKoTnTPHBHlVHQIHZpMxacbFOAkJr
# qAVkYZdz7ikNXTxV+GRb36tC4ByMNxE2DF7vFdvaiZP0CVZ5ByJ2gAhXMdK9+usx
# zVk913qKde1OAuWdv+rndqkAIm8fUlRnr4saSCg7cIbUwCCf116wUJ7EuJDg0vHe
# yhnCeHnBbyH3RZkHEi2ofmfgnFISJZDdMAeVZGVOh20Jp50XBzqokpPzeZ6zc1/g
# yILNyiVgE+RPkjnUQshd1f1PMgn3tns2Cz7bJiVUaqEO3n9qRFgy5JuLae6UweGf
# AeOo3dgLZxikKzYs3hDMaEtJq8IP71cX7QXe6lnMmXU/Hdfz2p897Zd+kU+vZvKI
# 3cwLfuVQgK2RZ2z+Kc3K3dRPz2rXycK5XCuRZmvGab/WbrZiC7wJQapgBodltMI5
# GMdFrBg9IeF7/rP4EqVQXeKtevTlZXjpuNhhjuR+2DMt/dWufjXpiW91bo3aH6Ea
# jOALXmoxgltCp1K7hrS6gmsvj94cLRf50QQ4U8Qwggd6MIIFYqADAgECAgphDpDS
# AAAAAAADMA0GCSqGSIb3DQEBCwUAMIGIMQswCQYDVQQGEwJVUzETMBEGA1UECBMK
# V2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0
# IENvcnBvcmF0aW9uMTIwMAYDVQQDEylNaWNyb3NvZnQgUm9vdCBDZXJ0aWZpY2F0
# ZSBBdXRob3JpdHkgMjAxMTAeFw0xMTA3MDgyMDU5MDlaFw0yNjA3MDgyMTA5MDla
# MH4xCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdS
# ZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xKDAmBgNVBAMT
# H01pY3Jvc29mdCBDb2RlIFNpZ25pbmcgUENBIDIwMTEwggIiMA0GCSqGSIb3DQEB
# AQUAA4ICDwAwggIKAoICAQCr8PpyEBwurdhuqoIQTTS68rZYIZ9CGypr6VpQqrgG
# OBoESbp/wwwe3TdrxhLYC/A4wpkGsMg51QEUMULTiQ15ZId+lGAkbK+eSZzpaF7S
# 35tTsgosw6/ZqSuuegmv15ZZymAaBelmdugyUiYSL+erCFDPs0S3XdjELgN1q2jz
# y23zOlyhFvRGuuA4ZKxuZDV4pqBjDy3TQJP4494HDdVceaVJKecNvqATd76UPe/7
# 4ytaEB9NViiienLgEjq3SV7Y7e1DkYPZe7J7hhvZPrGMXeiJT4Qa8qEvWeSQOy2u
# M1jFtz7+MtOzAz2xsq+SOH7SnYAs9U5WkSE1JcM5bmR/U7qcD60ZI4TL9LoDho33
# X/DQUr+MlIe8wCF0JV8YKLbMJyg4JZg5SjbPfLGSrhwjp6lm7GEfauEoSZ1fiOIl
# XdMhSz5SxLVXPyQD8NF6Wy/VI+NwXQ9RRnez+ADhvKwCgl/bwBWzvRvUVUvnOaEP
# 6SNJvBi4RHxF5MHDcnrgcuck379GmcXvwhxX24ON7E1JMKerjt/sW5+v/N2wZuLB
# l4F77dbtS+dJKacTKKanfWeA5opieF+yL4TXV5xcv3coKPHtbcMojyyPQDdPweGF
# RInECUzF1KVDL3SV9274eCBYLBNdYJWaPk8zhNqwiBfenk70lrC8RqBsmNLg1oiM
# CwIDAQABo4IB7TCCAekwEAYJKwYBBAGCNxUBBAMCAQAwHQYDVR0OBBYEFEhuZOVQ
# BdOCqhc3NyK1bajKdQKVMBkGCSsGAQQBgjcUAgQMHgoAUwB1AGIAQwBBMAsGA1Ud
# DwQEAwIBhjAPBgNVHRMBAf8EBTADAQH/MB8GA1UdIwQYMBaAFHItOgIxkEO5FAVO
# 4eqnxzHRI4k0MFoGA1UdHwRTMFEwT6BNoEuGSWh0dHA6Ly9jcmwubWljcm9zb2Z0
# LmNvbS9wa2kvY3JsL3Byb2R1Y3RzL01pY1Jvb0NlckF1dDIwMTFfMjAxMV8wM18y
# Mi5jcmwwXgYIKwYBBQUHAQEEUjBQME4GCCsGAQUFBzAChkJodHRwOi8vd3d3Lm1p
# Y3Jvc29mdC5jb20vcGtpL2NlcnRzL01pY1Jvb0NlckF1dDIwMTFfMjAxMV8wM18y
# Mi5jcnQwgZ8GA1UdIASBlzCBlDCBkQYJKwYBBAGCNy4DMIGDMD8GCCsGAQUFBwIB
# FjNodHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20vcGtpb3BzL2RvY3MvcHJpbWFyeWNw
# cy5odG0wQAYIKwYBBQUHAgIwNB4yIB0ATABlAGcAYQBsAF8AcABvAGwAaQBjAHkA
# XwBzAHQAYQB0AGUAbQBlAG4AdAAuIB0wDQYJKoZIhvcNAQELBQADggIBAGfyhqWY
# 4FR5Gi7T2HRnIpsLlhHhY5KZQpZ90nkMkMFlXy4sPvjDctFtg/6+P+gKyju/R6mj
# 82nbY78iNaWXXWWEkH2LRlBV2AySfNIaSxzzPEKLUtCw/WvjPgcuKZvmPRul1LUd
# d5Q54ulkyUQ9eHoj8xN9ppB0g430yyYCRirCihC7pKkFDJvtaPpoLpWgKj8qa1hJ
# Yx8JaW5amJbkg/TAj/NGK978O9C9Ne9uJa7lryft0N3zDq+ZKJeYTQ49C/IIidYf
# wzIY4vDFLc5bnrRJOQrGCsLGra7lstnbFYhRRVg4MnEnGn+x9Cf43iw6IGmYslmJ
# aG5vp7d0w0AFBqYBKig+gj8TTWYLwLNN9eGPfxxvFX1Fp3blQCplo8NdUmKGwx1j
# NpeG39rz+PIWoZon4c2ll9DuXWNB41sHnIc+BncG0QaxdR8UvmFhtfDcxhsEvt9B
# xw4o7t5lL+yX9qFcltgA1qFGvVnzl6UJS0gQmYAf0AApxbGbpT9Fdx41xtKiop96
# eiL6SJUfq/tHI4D1nvi/a7dLl+LrdXga7Oo3mXkYS//WsyNodeav+vyL6wuA6mk7
# r/ww7QRMjt/fdW1jkT3RnVZOT7+AVyKheBEyIXrvQQqxP/uozKRdwaGIm1dxVk5I
# RcBCyZt2WwqASGv9eZ/BvW1taslScxMNelDNMYIVWzCCFVcCAQEwgZUwfjELMAkG
# A1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQx
# HjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEoMCYGA1UEAxMfTWljcm9z
# b2Z0IENvZGUgU2lnbmluZyBQQ0EgMjAxMQITMwAAAYdyF3IVWUDHCQAAAAABhzAN
# BglghkgBZQMEAgEFAKCBrjAZBgkqhkiG9w0BCQMxDAYKKwYBBAGCNwIBBDAcBgor
# BgEEAYI3AgELMQ4wDAYKKwYBBAGCNwIBFTAvBgkqhkiG9w0BCQQxIgQgVuCCCg4G
# d7i27JkmrXroN9QWua0eB4OJ2b8rCbm2HxMwQgYKKwYBBAGCNwIBDDE0MDKgFIAS
# AE0AaQBjAHIAbwBzAG8AZgB0oRqAGGh0dHA6Ly93d3cubWljcm9zb2Z0LmNvbTAN
# BgkqhkiG9w0BAQEFAASCAQCgR0BK1viQCrwT8HKiyfgKw6IbU1Du1YPLO0iFLsT7
# rgjNzXiiL2DuQ/daVDBBapYnaITFcacVr0fNKhN5+KriJtqHPwcVsCX7TXknwwQz
# BL3mmih69daKMB7OniMw+wWOZ/t4rAemjZ/GGwiRROmM5UEFbW/DgAz0W88Db7hf
# av5DZIaIzoyP/iozDEhIjd+EfFJerjyvcqJTwHlsS0mSvBYRri9ZILpb7aY+zxQ4
# uOabPIQzRKihavu3zLoRTe8RSw2mwiiPdw0/cExyNgqu2Z/b04/gNsvd5S82Pxuk
# SgYfSx4Nl5Jekkqlx7vWfwSfpQO57qEqE8+oEFlZvOoqoYIS5TCCEuEGCisGAQQB
# gjcDAwExghLRMIISzQYJKoZIhvcNAQcCoIISvjCCEroCAQMxDzANBglghkgBZQME
# AgEFADCCAVEGCyqGSIb3DQEJEAEEoIIBQASCATwwggE4AgEBBgorBgEEAYRZCgMB
# MDEwDQYJYIZIAWUDBAIBBQAEIEbMa+qcA1GoQm/9hGCaPFtPOE1+BXSSJURTPoWU
# kKMvAgZfFf3xzwcYEzIwMjAwODAxMDQxNzQ5LjY4MVowBIACAfSggdCkgc0wgcox
# CzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRt
# b25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xJTAjBgNVBAsTHE1p
# Y3Jvc29mdCBBbWVyaWNhIE9wZXJhdGlvbnMxJjAkBgNVBAsTHVRoYWxlcyBUU1Mg
# RVNOOjNFN0EtRTM1OS1BMjVEMSUwIwYDVQQDExxNaWNyb3NvZnQgVGltZS1TdGFt
# cCBTZXJ2aWNloIIOPDCCBPEwggPZoAMCAQICEzMAAAEgGjnb2WufrfIAAAAAASAw
# DQYJKoZIhvcNAQELBQAwfDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0
# b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3Jh
# dGlvbjEmMCQGA1UEAxMdTWljcm9zb2Z0IFRpbWUtU3RhbXAgUENBIDIwMTAwHhcN
# MTkxMTEzMjE0MDQyWhcNMjEwMjExMjE0MDQyWjCByjELMAkGA1UEBhMCVVMxEzAR
# BgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1p
# Y3Jvc29mdCBDb3Jwb3JhdGlvbjElMCMGA1UECxMcTWljcm9zb2Z0IEFtZXJpY2Eg
# T3BlcmF0aW9uczEmMCQGA1UECxMdVGhhbGVzIFRTUyBFU046M0U3QS1FMzU5LUEy
# NUQxJTAjBgNVBAMTHE1pY3Jvc29mdCBUaW1lLVN0YW1wIFNlcnZpY2UwggEiMA0G
# CSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC1BOuDmpgS5mLJDPUQaEcVyH5bHBLc
# 4Lvq5gGHF5J1yPhhbg8NSErV0fP+N5CQwe4KTLU/BROOpHlO/BV1UGOs+BavEPGR
# iVYdhAqKmzWU+TWbAOVxkrnpa/FJ6A5Q5cvINSiOabyOfHe2dMFCQPlggBzqC3x9
# lnnJkUxSpetfTcj6FVCwxYVPk55zz4O4UpmeiHDiWEBpC9r64VHZZOSz89Z7ML1l
# penM2r8+WnabZLPoU2Y7p9QrPzQH7gvQiu/zgcYYMqmVIlfFTPS1iOvZbAqk1U6B
# NRgnOKirD+0+8yujM4CY/0DlZDDlLfU7Vg8ojsGg7peVh+MnYDwlyKGxAgMBAAGj
# ggEbMIIBFzAdBgNVHQ4EFgQUmdk2BLDfR8UfnN3pLGUCcaciVc8wHwYDVR0jBBgw
# FoAU1WM6XIoxkPNDe3xGG8UzaFqFbVUwVgYDVR0fBE8wTTBLoEmgR4ZFaHR0cDov
# L2NybC5taWNyb3NvZnQuY29tL3BraS9jcmwvcHJvZHVjdHMvTWljVGltU3RhUENB
# XzIwMTAtMDctMDEuY3JsMFoGCCsGAQUFBwEBBE4wTDBKBggrBgEFBQcwAoY+aHR0
# cDovL3d3dy5taWNyb3NvZnQuY29tL3BraS9jZXJ0cy9NaWNUaW1TdGFQQ0FfMjAx
# MC0wNy0wMS5jcnQwDAYDVR0TAQH/BAIwADATBgNVHSUEDDAKBggrBgEFBQcDCDAN
# BgkqhkiG9w0BAQsFAAOCAQEAYulQdnFLE5FCLMEUPpBNSy+OCJwmY7ihAF4Jye0S
# Q8fDJ0CYhsYU/uHUl8oTTuX5JQONX6QnKSU/+/0XuezSFtuYsN+uVrvGIciGXCKP
# uPtgFjFJBSW5y4xxurfzAuPO12wGJdVD4J+0ihF5Wr+RNUbX9MPiU+0qsIKDPs2p
# A3KNdXCP9m4WLi/+Fb0KMGwoX6XtDCvBHVw2qXbqcItlh4JfM9vLfjCgwS6zbIt7
# igBKOLXRWaOqpeIfqsBHVitPN5RsneeXPqHpBOT+wCsm7lkqDAOS98RDWyJe8hZA
# geeXLHF7gL/lBgQbzScWYx0tg/4+hlPijywAAe0SBMDkmDCCBnEwggRZoAMCAQIC
# CmEJgSoAAAAAAAIwDQYJKoZIhvcNAQELBQAwgYgxCzAJBgNVBAYTAlVTMRMwEQYD
# VQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNy
# b3NvZnQgQ29ycG9yYXRpb24xMjAwBgNVBAMTKU1pY3Jvc29mdCBSb290IENlcnRp
# ZmljYXRlIEF1dGhvcml0eSAyMDEwMB4XDTEwMDcwMTIxMzY1NVoXDTI1MDcwMTIx
# NDY1NVowfDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNV
# BAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEmMCQG
# A1UEAxMdTWljcm9zb2Z0IFRpbWUtU3RhbXAgUENBIDIwMTAwggEiMA0GCSqGSIb3
# DQEBAQUAA4IBDwAwggEKAoIBAQCpHQ28dxGKOiDs/BOX9fp/aZRrdFQQ1aUKAIKF
# ++18aEssX8XD5WHCdrc+Zitb8BVTJwQxH0EbGpUdzgkTjnxhMFmxMEQP8WCIhFRD
# DNdNuDgIs0Ldk6zWczBXJoKjRQ3Q6vVHgc2/JGAyWGBG8lhHhjKEHnRhZ5FfgVSx
# z5NMksHEpl3RYRNuKMYa+YaAu99h/EbBJx0kZxJyGiGKr0tkiVBisV39dx898Fd1
# rL2KQk1AUdEPnAY+Z3/1ZsADlkR+79BL/W7lmsqxqPJ6Kgox8NpOBpG2iAg16Hgc
# sOmZzTznL0S6p/TcZL2kAcEgCZN4zfy8wMlEXV4WnAEFTyJNAgMBAAGjggHmMIIB
# 4jAQBgkrBgEEAYI3FQEEAwIBADAdBgNVHQ4EFgQU1WM6XIoxkPNDe3xGG8UzaFqF
# bVUwGQYJKwYBBAGCNxQCBAweCgBTAHUAYgBDAEEwCwYDVR0PBAQDAgGGMA8GA1Ud
# EwEB/wQFMAMBAf8wHwYDVR0jBBgwFoAU1fZWy4/oolxiaNE9lJBb186aGMQwVgYD
# VR0fBE8wTTBLoEmgR4ZFaHR0cDovL2NybC5taWNyb3NvZnQuY29tL3BraS9jcmwv
# cHJvZHVjdHMvTWljUm9vQ2VyQXV0XzIwMTAtMDYtMjMuY3JsMFoGCCsGAQUFBwEB
# BE4wTDBKBggrBgEFBQcwAoY+aHR0cDovL3d3dy5taWNyb3NvZnQuY29tL3BraS9j
# ZXJ0cy9NaWNSb29DZXJBdXRfMjAxMC0wNi0yMy5jcnQwgaAGA1UdIAEB/wSBlTCB
# kjCBjwYJKwYBBAGCNy4DMIGBMD0GCCsGAQUFBwIBFjFodHRwOi8vd3d3Lm1pY3Jv
# c29mdC5jb20vUEtJL2RvY3MvQ1BTL2RlZmF1bHQuaHRtMEAGCCsGAQUFBwICMDQe
# MiAdAEwAZQBnAGEAbABfAFAAbwBsAGkAYwB5AF8AUwB0AGEAdABlAG0AZQBuAHQA
# LiAdMA0GCSqGSIb3DQEBCwUAA4ICAQAH5ohRDeLG4Jg/gXEDPZ2joSFvs+umzPUx
# vs8F4qn++ldtGTCzwsVmyWrf9efweL3HqJ4l4/m87WtUVwgrUYJEEvu5U4zM9GAS
# inbMQEBBm9xcF/9c+V4XNZgkVkt070IQyK+/f8Z/8jd9Wj8c8pl5SpFSAK84Dxf1
# L3mBZdmptWvkx872ynoAb0swRCQiPM/tA6WWj1kpvLb9BOFwnzJKJ/1Vry/+tuWO
# M7tiX5rbV0Dp8c6ZZpCM/2pif93FSguRJuI57BlKcWOdeyFtw5yjojz6f32WapB4
# pm3S4Zz5Hfw42JT0xqUKloakvZ4argRCg7i1gJsiOCC1JeVk7Pf0v35jWSUPei45
# V3aicaoGig+JFrphpxHLmtgOR5qAxdDNp9DvfYPw4TtxCd9ddJgiCGHasFAeb73x
# 4QDf5zEHpJM692VHeOj4qEir995yfmFrb3epgcunCaw5u+zGy9iCtHLNHfS4hQEe
# gPsbiSpUObJb2sgNVZl6h3M7COaYLeqN4DMuEin1wC9UJyH3yKxO2ii4sanblrKn
# QqLJzxlBTeCG+SqaoxFmMNO7dDJL32N79ZmKLxvHIa9Zta7cRDyXUHHXodLFVeNp
# 3lfB0d4wwP3M5k37Db9dT+mdHhk4L7zPWAUu7w2gUDXa7wknHNWzfjUeCLraNtvT
# X4/edIhJEqGCAs4wggI3AgEBMIH4oYHQpIHNMIHKMQswCQYDVQQGEwJVUzETMBEG
# A1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWlj
# cm9zb2Z0IENvcnBvcmF0aW9uMSUwIwYDVQQLExxNaWNyb3NvZnQgQW1lcmljYSBP
# cGVyYXRpb25zMSYwJAYDVQQLEx1UaGFsZXMgVFNTIEVTTjozRTdBLUUzNTktQTI1
# RDElMCMGA1UEAxMcTWljcm9zb2Z0IFRpbWUtU3RhbXAgU2VydmljZaIjCgEBMAcG
# BSsOAwIaAxUAv1r8uFykxw2Ng0e88GallxNnXkyggYMwgYCkfjB8MQswCQYDVQQG
# EwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwG
# A1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSYwJAYDVQQDEx1NaWNyb3NvZnQg
# VGltZS1TdGFtcCBQQ0EgMjAxMDANBgkqhkiG9w0BAQUFAAIFAOLO/KgwIhgPMjAy
# MDA4MDEwNDI1MTJaGA8yMDIwMDgwMjA0MjUxMlowdzA9BgorBgEEAYRZCgQBMS8w
# LTAKAgUA4s78qAIBADAKAgEAAgIREwIB/zAHAgEAAgIRlTAKAgUA4tBOKAIBADA2
# BgorBgEEAYRZCgQCMSgwJjAMBgorBgEEAYRZCgMCoAowCAIBAAIDB6EgoQowCAIB
# AAIDAYagMA0GCSqGSIb3DQEBBQUAA4GBAKd1l6byrGnQbPBMYZXuQhP4jd8GGEng
# TDO8njrGmLGuEW9roYvKf/mEm+iWy/Dr+VpRnf3q84bCHfyG5kD7sx+XjIgPxQqj
# gVZRjopK3xVE+Aq6pNIUW5HxZR6Hc9PTCx+r1hX2skmbljKFZ2wV175SOARYllBI
# +Wm2DX1wHk77MYIDDTCCAwkCAQEwgZMwfDELMAkGA1UEBhMCVVMxEzARBgNVBAgT
# Cldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29m
# dCBDb3Jwb3JhdGlvbjEmMCQGA1UEAxMdTWljcm9zb2Z0IFRpbWUtU3RhbXAgUENB
# IDIwMTACEzMAAAEgGjnb2WufrfIAAAAAASAwDQYJYIZIAWUDBAIBBQCgggFKMBoG
# CSqGSIb3DQEJAzENBgsqhkiG9w0BCRABBDAvBgkqhkiG9w0BCQQxIgQgM7Zhm8iB
# /uWL6S6/gqD87Bh/NXSycukOungf53XURycwgfoGCyqGSIb3DQEJEAIvMYHqMIHn
# MIHkMIG9BCAcDFBrFJx21/VR4TsFktJBSYQgHP7GymOt92ieZ8AaVTCBmDCBgKR+
# MHwxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdS
# ZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xJjAkBgNVBAMT
# HU1pY3Jvc29mdCBUaW1lLVN0YW1wIFBDQSAyMDEwAhMzAAABIBo529lrn63yAAAA
# AAEgMCIEIO6ie1zwR+RPIGSbLvPdqJ5ckL6oYAaryqGzk2PeIKvuMA0GCSqGSIb3
# DQEBCwUABIIBAJ6GsRC8rKoigt+QpWsmJH/7Qmcbps5cPruqX/KQAW55h67v9avp
# rLNjKAAi1xJuXgtHJswXm488da89/gZXs2I0jabgC0ST8B+hX6E351gW7s7GRfxH
# rl21o4ggCuv7/4uBaj8DMoCUUactuo9F0lztOFWs4ibc2dKUnZQ0uxiyBJfAKW5S
# VpCotDomb8TIImE0dsyaoSdP7+1eGSey0+riojtONP5I8f7MBEBz92Unc2qbFZiV
# 39r58eJSzMzMX96UnLuERRh8QPInCbEGcwD2BKGg0Q4Nbg5acRNIK17nGHXbxZbV
# E8HND6eRxrDPW2qVOfpqcBn3BYFyAyLb4j0=
# SIG # End signature block