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','msftgeist')

        # 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 = 1
        $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:lblHeaderSize = @(400,40)
        $script:lblHeaderSmallSize = @(140,20)
        $script:lblHeaderMediumSize = @(260,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.SystemColors]::WindowText
        $script:defaultBackColor = [System.Drawing.SystemColors]::Window
        $script:summaryForeColor = [System.Drawing.SystemColors]::WindowText
        $script:summaryBackColor = [System.Drawing.SystemColors]::Control
        $script:tableForeColor = [System.Drawing.SystemColors]::WindowText
        $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
{
    Write-Log -Message "$($MyInvocation.MyCommand.Name) - Started"
    # 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($true -eq $isExtStor)
    {
        Write-Log -Message "$($MyInvocation.MyCommand.Name) - ExtendedStorage scenario detected"
        $skip += $ignoreSwitchAlways
        $script:edgeSwitch = Get-SwitchInfo -SwitchType EDGE
        $script:edgeMake = $edgeSwitch.Make
        $script:edgeModel = $edgeSwitch.Model
        $script:edgeFirmware = $edgeSwitch.Firmware
        $tryMake = $script:currentSwitchVendor
        if($script:currentSwitchVendor -eq 'msftcisco')
        {
            $tryMake = 'msftdellemc'
        }
        if(-not [string]::IsNullOrEmpty($tryMake))
        {
            $script:backSwitch = Get-SwitchInfo -SwitchType BACKEND -SwitchMake $tryMake
        }
        $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(($true -eq $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)
        {
            if($false -eq $lstSwitchVendor.Items.Contains($make))
            {
                Write-Log -Message "$($MyInvocation.MyCommand.Name) - Add $($make)"
                [void]$lstSwitchVendor.Items.Add($make)
            }
        }
    }
    Write-Log -Message "$($MyInvocation.MyCommand.Name) - Completed"
}

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 Enable-ComboBoxList
{
    param
    (
        [Parameter(Mandatory=$true)]$Control,
        [Parameter(Mandatory=$false)]$Label
    )
    $Control.Enabled = $true
    $Control.Visible = $true
    if($Label)
    {
        $Label.Enabled = $true
        $Label.Visible = $true
    }
}

function Disable-ComboBoxList
{
    param
    (
        [Parameter(Mandatory=$true)]$Control,
        [Parameter(Mandatory=$false)]$Label
    )
    Clear-ListSelection $Control
    $Control.Enabled = $false
    $Control.Visible = $false
    if($Label)
    {
        $Label.Enabled = $false
        $Label.Visible = $false
    }
}

function Get-SwitchInfo
{
    param
    (
        [string]$SwitchType,
        [string]$SwitchMake
    )
    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)
        {
            if([string]::IsNullOrEmpty($SwitchMake))
            {
                $foundSwitch += $portmap.$make
                if(-not $thisMake)
                {
                    $thisMake = $make
                }
            }
            else
            {
                if($make -eq $SwitchMake)
                {
                    $foundSwitch += $portmap.$make
                    if(-not $thisMake)
                    {
                        $thisMake = $make
                    }
                }
            }
        }
    }
    if($foundSwitch.Count -eq 0)
    {
        if($SwitchMake)
        {
            Write-Log -Message "$($MyInvocation.MyCommand.Name) - ERROR: unable to find any '$($SwitchType)' devices in switch.json for '$($SwitchMake)'" -Force
        }
        else
        {
            Write-Log -Message "$($MyInvocation.MyCommand.Name) - ERROR: unable to find any '$($SwitchType)' devices in switch.json" -Force
        }
        return
    }
    elseif($foundSwitch.Count -gt 1)
    {
        Write-Log -Message "$($MyInvocation.MyCommand.Name) - WARNING: more than one $($SwitchType) switch makes found in switch.json" -Force
        Write-Log -Message "$($MyInvocation.MyCommand.Name) - WARNING: the first Make/Model found will be used unless a specific SwitchMake is specified" -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")
                            {
                                $switchInfo += $PSItem
                            }
                            elseif(($portmap.$PSItem.BMC) -and ((($portmap.$PSItem.BMC | Get-Member -Type NoteProperty).Name).Count -gt 0))
                            {
                                $switchInfo += $PSItem
                            }
                        }
                        elseif($script:ignoreSwitchAlways -notcontains $PSItem)
                        {
                            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..[int]$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
    {
        if($lstSwitchVendor.SelectedItem -eq 'msftcisco')
        {
            $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
        }
    }
    if([int]$maxSU -gt 1)
    {
        (2..[int]$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)"
            $scaleUnitTabs.$tabName.Tab.BackColor = [System.Drawing.SystemColors]::Control
            $scaleUnitTabs.$tabName.Tab.ForeColor = [System.Drawing.SystemColors]::ControlText
            $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.AccessibleName = "$($locString.lblScaleUnit) : $($ID)"
            $scaleUnitTabs.$tabName.Objects.$thisName.AccessibleDescription = "$($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
                        $scaleUnitTabs.$tabName.Objects.$thisName.Focus()
                    }
                    Update-TextBox $scaleUnitTabs.$tabName.Objects.$thisName
                }
            }
            $scaleUnitTabs.$tabName.Objects.$thisName.Add_Leave($txtNodeCount_Changed)

            $tabRow += 60

            # -------------------------- SCALE-UNIT SUBNETS ---------------------------
            $grpBoxWidth = $lblMaxSize[0] + $txtSize[0] + 20
            $grpRow = 40
            $thisName = 'boxScaleUnitSubnets'
            $thisObj = New-Object System.Windows.Forms.GroupBox
            $scaleUnitTabs.$tabName.Objects.Add($thisName,$thisObj)
            $scaleUnitTabs.$tabName.Objects.$thisName.Location = New-Object System.Drawing.Point($lblLeft,$tabRow)
            $scaleUnitTabs.$tabName.Objects.$thisName.Size = New-Object System.Drawing.Size($grpBoxWidth,180)
            $scaleUnitTabs.$tabName.Objects.$thisName.Text = $locString.lblScaleUnitSubnets
            $scaleUnitTabs.$tabName.Objects.$thisName.Name = $locString.lblScaleUnitSubnets
            $scaleUnitTabs.$tabName.Objects.$thisName.AccessibleName = ("$($locString.lblScaleUnit) $($ID) $($locString.lblScaleUnitSubnets)")
            $scaleUnitTabs.$tabName.Objects.$thisName.Visible = $true
            $scaleUnitTabs.$tabName.Tab.Controls.Add($scaleUnitTabs.$tabName.Objects.$thisName)

            # ------------------------------ 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,($grpRow+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.Objects.'boxScaleUnitSubnets'.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,$grpRow)
            $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.Objects.'boxScaleUnitSubnets'.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)
                        $scaleUnitTabs.$tabName.Objects.$thisName.Focus()
                        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)
                        $scaleUnitTabs.$tabName.Objects.$thisName.Focus()
                        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,($grpRow+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.Objects.'boxScaleUnitSubnets'.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,$grpRow)
            $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.Objects.'boxScaleUnitSubnets'.Controls.Add($scaleUnitTabs.$tabName.Objects.$thisName)

            $grpRow += 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,($grpRow+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.Objects.'boxScaleUnitSubnets'.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,$grpRow)
            $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.Objects.'boxScaleUnitSubnets'.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)
                        $scaleUnitTabs.$tabName.Objects.$thisName.Focus()
                        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)
                        $scaleUnitTabs.$tabName.Objects.$thisName.Focus()
                        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,($grpRow+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.Objects.'boxScaleUnitSubnets'.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,$grpRow)
            $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.Objects.'boxScaleUnitSubnets'.Controls.Add($scaleUnitTabs.$tabName.Objects.$thisName)

            $grpRow += 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,($grpRow+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.Objects.'boxScaleUnitSubnets'.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,$grpRow)
            $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.Objects.'boxScaleUnitSubnets'.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)
                        $scaleUnitTabs.$tabName.Objects.$thisName.Focus()
                        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)
                        $scaleUnitTabs.$tabName.Objects.$thisName.Focus()
                        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,($grpRow+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.Objects.'boxScaleUnitSubnets'.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,$grpRow)
            $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.Objects.'boxScaleUnitSubnets'.Controls.Add($scaleUnitTabs.$tabName.Objects.$thisName)

            $tabRow += 200

            # ------------------------------ 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)
                        $scaleUnitTabs.$tabName.Objects.$thisName.Focus()
                        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
    {
        Write-Log -Message "$($MyInvocation.MyCommand.Name) - started -Make: $Make -Type: $Type"
        if(-not $switches)
        {
            $script:switches = Get-Content -Raw $strSwitchFilePath | ConvertFrom-Json
            if(-not $switches)
            {
                Write-Log -Message "$($MyInvocation.MyCommand.Name) - [ERROR] Unable to read from switch.json" -Force
                return @()
            }
        }
        $switchInfo = $switches.portmap
        if($null -ne $switchInfo.$Make.$Type)
        {
            return @(($switchInfo.$Make.$Type | Get-Member -Type NoteProperty).Name)
        }
        else
        {
            Write-Log -Message "$($MyInvocation.MyCommand.Name) - [WARNING] Unable to find any matching models for this Make/Type ($($Make)/$($Type))" -Force
        }
    }
    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-SupportNoBMC
{
    param
    (
        [string]$Make,
        [string]$Model
    )
    [string]$Type = "TOR"
    if(-not [string]::IsNullOrEmpty($openTORModel))
    {
        Write-Log -Message "$($MyInvocation.MyCommand.Name) - Forcing TOR model from Opened file: '$($openTORModel)'"
        $Model = $openTORModel
    }
    Write-Log -Message "$($MyInvocation.MyCommand.Name) - started -Make: $Make -Model: $Model -Type: $Type"
    try
    {
        $retValue = $false
        if(-not([string]::IsNullOrEmpty($Make) -or [string]::IsNullOrEmpty($Model)))
        {
            if(-not $switches)
            {
                $script:switches = Get-Content -Raw $strSwitchFilePath | ConvertFrom-Json
            }
            $switchInfo = $switches.portmap
            if($Make -eq "CiscoUCS") {
                $retValue = $true
            }
            elseif('true' -eq $switchInfo.$Make.$Type.$Model.SupportNoBMC)
            {
                $retValue = $true
            }
            else
            {
                $retValue = $false
            }
        }
        Write-Log -Message "$($MyInvocation.MyCommand.Name) TOR supports IsNoBMC = $($retValue)"
        return $retValue
    }
    catch
    {
        throw $PSItem
    }
}

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 = $boxBorderConnect.Controls | Where-Object Name -eq "lblBorderBGP$($Num)"
            $varTxt = $boxBorderConnect.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 = $boxBorderConnect.Controls | Where-Object Name -eq "lblBorderBGP$($Num)"
            $varTxt = $boxBorderConnect.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..[int]$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
        $lstTORFirmware
        $lstTORModel
        $lstBMCFirmware
        $lstBMCModel
        $lstSpineFirmware
        $lstSpineModel
        $lstAzureEnvironment
        $lstUplinkSpeed
    )
    # Set listbox items
    $objListBox = `
    @(
        $lstPermitNetworks
    )
    # Clear Scale-Unit objects
    $txtScaleUnitCount.Text = $defaultSUCount
    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
    $lstBMCFirmware.Items.Clear()
    $lstBMCModel.Items.Clear()
    $lstTORFirmware.Items.Clear()
    $lstTORModel.Items.Clear()
    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
    Enable-ComboBoxList -Control $lstTORModel -Label $lblTORModel
    Enable-ComboBoxList -Control $lstTORFirmware -Label $lblTORFirmware
    Enable-ComboBoxList -Control $lstBMCModel -Label $lblBMCModel
    Enable-ComboBoxList -Control $lstBMCFirmware -Label $lblBMCFirmware
    # 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
    }
    $chkDataBoundary.Checked = $false
}

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.SelectAll()
    $TextControl.Refresh()
}

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

function Disable-PermitNetworks
{
    $lblPermitNetworks.Visible = $false
    $lblPermitNetworks.Enabled = $false
    $txtPermitNetworks.Enabled = $false
    $btnPermitNetworksAdd.Enabled = $false
    $btnPermitNetworksDel.Enabled = $false
    $lstPermitNetworks.Enabled = $false
    $boxPermitNetworks.Visible = $true
    $boxPermitNetworks.Enabled = $true
    $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
    }
    $boxBorderUplinks.Visible = $true
    $lblUplinkSpeed.Enabled = $true
    $lblUplinkSpeed.Visible = $true
    $lstUplinkSpeed.Enabled = $true
    $lstUplinkSpeed.Visible = $true
}

function Disable-UplinkSpeed
{
    $boxBorderUplinks.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,$menuItemExtStor))
    $script:EnableExperimental = $true
}

function Start-DeploymentWorksheet
{
    param
    (
        [switch]$EnableLogging
    )
    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 ===============================
    if($host.Version -ge [system.version]"7.0.0.0")
    {
        [System.Windows.Forms.Application]::EnableVisualStyles()
        $script:defaultBorderStyle = [System.Windows.Forms.BorderStyle]::FixedSingle
    }
    else
    {
        $script:defaultBorderStyle = [System.Windows.Forms.BorderStyle]::Fixed3D
    }
    $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.SystemColors]::Control
    $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
        if($null -ne $tabControl.TabPages["tabSubnetSummary"])
        {
            $rowHeight = $dgvSubnetSummary.Rows.GetRowsHeight([System.Windows.Forms.DataGridViewElementStates]::Visible)
            $colWidth = $dgvSubnetSummary.Columns.GetColumnsWidth([System.Windows.Forms.DataGridViewElementStates]::Visible)
            $height = $frmMain.Size.Height-150
            $width = $frmMain.Size.Width-60
            if($height -gt $rowHeight)
            {
                $height = $rowHeight + 50
            }
            if($width -gt $colWidth)
            {
                $width = $colWidth + 30
            }
            $dgvSubnetSummary.Size = New-Object System.Drawing.Size($width,$height)
        }
        if($null -ne $tabControl.TabPages["tabIPAssignments"])
        {
            $height = $frmMain.Size.Height-150
            $width = $ipSummaryWidth
            if($height -gt $ipSummaryHeight)
            {
                $height = $ipSummaryHeight + 40
            }
            $ipSummaryPanel.Size = New-Object System.Drawing.Size($width,$height)
        }
    }

    # ========================= 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
    $menuItemExtStor = 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
            }
            if((-not [string]::IsNullOrEmpty($openData.classic)) -and ($false -eq [System.Convert]::ToBoolean($openData.classic)))
            {
                Write-Log -Message "$($MyInvocation.MyCommand.Name) - WARNING: the file opened was NOT created using the classic network architecture" -Force
            }
            switch ($openScenario)
            {
                "Customer"
                {
                    OnClick_menuItemCustomer
                    [int]$openSUCount = 0
                    if($openData.tabNetwork.txtScaleUnitCount)
                    {
                        [int]$openSUCount = $openData.tabNetwork.txtScaleUnitCount
                    }
                    elseif($openData.tabNetwork.lstScaleUnitCount)
                    {
                        [int]$openSUCount = $openData.tabNetwork.lstScaleUnitCount
                    }
                    $script:minBorder = 1
                    $script:validBorder = 2
                    if($openSUCount -gt 1)
                    {
                        if($openSUCount -gt [int]$maxSU)
                        {
                            Write-Log -Message "$($MyInvocation.MyCommand.Name) - WARNING: Scale-Unit count of '$($openSUCount)' is larger than the maximum allowed '$($maxSU)'" -Force
                            [int]$openSUCount = $maxSU
                        }
                        elseif(-not($EnableExperimental))
                        {
                            if(($lstSwitchVendor.SelectedItem -ne 'CiscoUCS') -and ($lstSwitchVendor.SelectedItem -ne 'Mellanox'))
                            {
                                $script:minBorder = 2
                            }
                            $script:validBorder = 4
                            Set-Experimental
                            Enable-PermitNetworks
                            $txtScaleUnitCount.Enabled = $true
                            $txtScaleUnitCount.ReadOnly = $false
                            OnClick_menuItemPreviewMultiSU
                        }
                    }
                    $txtScaleUnitCount.Text = $openSUCount
                    if($openData.extendedstorage -eq "true")
                    {
                        Enable-PermitNetworks
                        OnClick_menuItemExtStor
                    }
                }
                "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
            }
            if($lstSwitchVendor.SelectedItem -eq "CiscoUCS")
            {
                Disable-ComboBoxList -Control $lstBMCModel -Label $lblBMCModel
                Disable-ComboBoxList -Control $lstBMCFirmware -Label $lblBMCFirmware
            }

            [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
            }
            $script:arrUplinkSpeed = @()
            $script:arrUplinkSpeed += Get-UplinkSpeedList -SpecificMake $lstSwitchVendor.SelectedItem -SpecificModel $lstTORModel.SelectedItem
            if ($arrUplinkSpeed.Count -gt 0)
            {
                Invoke-Command -ScriptBlock $UpdateUplinkSpeedItems
                Enable-UplinkSpeed
            }
            if((-not [string]::IsNullOrEmpty($script:openUplinkSpeed)) -and ($true -eq $lstUplinkSpeed.Enabled))
            {
                Write-Log -Message "$($MyInvocation.MyCommand.Name) - set 'UplinkSpeed' to '$($script:openUplinkSpeed)'"
                if($lstUplinkSpeed.Items -contains $script:openUplinkSpeed)
                {
                    $lstUplinkSpeed.SelectedItem = $script:openUplinkSpeed
                }
                else
                {
                    Write-Log -Message "$($MyInvocation.MyCommand.Name) - WARNING: '$($script:openUplinkSpeed)' is not a valid 'UplinkSpeed' for this device" -Force
                }
            }
            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('classic',$true)
            $saveData.Add('extendedstorage',$isExtStor)
            foreach($tabName in @('tabCustomer','tabNetwork','tabBorder'))
            {
                $thisObj = @{}
                $thisTab = $tabControl.TabPages[$tabName]
                foreach($ctrl in $thisTab.Controls)
                {
                    if(
                        ([string]::IsNullOrEmpty($ctrl.Name)) -or
                        ($ctrl -is [System.Windows.Forms.Label]) -or
                        ($ctrl.Name -like "txtPermitNetworks*") -or
                        ($ctrl.Name -match "\bbtn.+[Del|Add]\b")
                    )
                    {
                        continue
                    }
                    if($ctrl -is [System.Windows.Forms.GroupBox])
                    {
                        foreach($grpCtrl in $ctrl.Controls)
                        {
                            if($grpCtrl.Name -eq "pnlDNSForwarder")
                            {
                                $thisObj.Add('txtDNSForwarder',$txtDNSForwarder.Text)
                            }
                            elseif($grpCtrl.Name -like "lstPermitNetworks*")
                            {
                                [string]$listString = $grpCtrl.Items -join ','
                                $thisObj.Add('lstPermitNetworks',$listString)
                            }
                            elseif(($grpCtrl -is [System.Windows.Forms.TextBox]) -or ($grpCtrl -is [System.Windows.Forms.RichTextBox]))
                            {
                                $thisObj.Add($grpCtrl.Name,$grpCtrl.Text)
                            }
                            elseif($grpCtrl -is [System.Windows.Forms.ComboBox])
                            {
                                $thisObj.Add($grpCtrl.Name,$grpCtrl.SelectedItem)
                            }
                            elseif($grpCtrl -is [System.Windows.Forms.Checkbox])
                            {
                                $thisObj.Add($grpCtrl.Name,$grpCtrl.Checked)
                            }
                        }
                    }
                    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)
                        }
                        elseif($ctrl -is [System.Windows.Forms.Checkbox])
                        {
                            $thisObj.Add($ctrl.Name,$ctrl.Checked)
                        }
                    }
                }
                $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 })

    $menuItemExtStor.Name = "menuItemExtStor"
    $menuItemExtStor.AutoSize = $true
    $menuItemExtStor.Text = $locString.menuExtStor
    function Set-ExtStor
    {
        Write-Log -Message "$($MyInvocation.MyCommand.Name) - called"
        Enable-ExtendedStorage
        $menuScenario.DropDownItems | Foreach-Object `
        {
            if($PSItem.Name -eq $menuItemExtStor.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_menuItemExtStor($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
        }
    }
    $menuItemExtStor.Add_Click({ OnClick_menuItemExtStor $menuItemExtStor $EventArgs })

    # ---------------------------- MAIN FORM TABS -----------------------------
    $script:tabControl = New-Object System.Windows.Forms.TabControl
    $tabControl.BackColor = [System.Drawing.SystemColors]::Window
    $tabControl.Alignment = 'Top'
    $tabControl.BackColor = [System.Drawing.SystemColors]::Control
    $tabControl.ForeColor = [System.Drawing.SystemColors]::ControlText
    $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))
    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 = @()
        }
        if($tabControl.SelectedTab.Name -eq "tabIPAssignments")
        {
            Clear-IpAssignmentsSelectedCells
            $ipSummaryPanel.VerticalScroll.Value = 0
            $ipAssignmentObjects.$firstTable.Table.Select()
            $ipAssignmentObjects.$firstTable.Table.CurrentCell = $ipAssignmentObjects.$firstTable.Table[0,0]
            $ipAssignmentObjects.$firstTable.Table.Rows[0].Cells[0].Selected = $true
            $ipAssignmentObjects.$firstTable.Table.Refresh()
        }
        elseif($tabControl.SelectedTab.Name -eq "tabSubnetSummary")
        {
            Clear-SubnetSummarySelectedCells
            $dgvSubnetSummary.Select()
            $dgvSubnetSummary.CurrentCell = $dgvSubnetSummary[0,0]
            $dgvSubnetSummary.Rows[0].Cells[0].Selected = $true
            $dgvSubnetSummary.Refresh()
        }
    }
    $tabControl.Add_SelectedIndexChanged({ OnChange_tabControl $tabControl $EventArgs })

    $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
    $tabCustomer.BackColor = [System.Drawing.SystemColors]::Control
    $tabCustomer.ForeColor = [System.Drawing.SystemColors]::ControlText

    $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
    $tabNetwork.BackColor = [System.Drawing.SystemColors]::Control
    $tabNetwork.ForeColor = [System.Drawing.SystemColors]::ControlText

    $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
    $tabBorder.BackColor = [System.Drawing.SystemColors]::Control
    $tabBorder.ForeColor = [System.Drawing.SystemColors]::ControlText

    $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)

    # ---------------------------- AZURE IDENTITY -----------------------------
    $grpBoxWidth = $lblMaxSize[0] + $txtSize[0] + 20
    $grpRow = 30
    $script:boxAzureIdentity = New-Object System.Windows.Forms.GroupBox
    $boxAzureIdentity.Location = New-Object System.Drawing.Point($lblLeft,$row)
    $boxAzureIdentity.Size = New-Object System.Drawing.Size($grpBoxWidth,140)
    $boxAzureIdentity.Text = $locString.lblAzureIdentity
    $boxAzureIdentity.Name = $locString.lblAzureIdentity
    $boxAzureIdentity.Visible = $true
    $tabCustomer.Controls.Add($boxAzureIdentity)

    # --------------------------- IDENTITY STORE ------------------------------
    $script:lblIdentityStore = New-Object System.Windows.Forms.Label
    $lblIdentityStore.Location = New-Object System.Drawing.Size($lblLeft,($grpRow+2))
    $lblIdentityStore.MaximumSize = New-Object System.Drawing.Size($lblMaxSize)
    $lblIdentityStore.AutoSize = $true
    $lblIdentityStore.Font = $defaultLabelFont
    $lblIdentityStore.Text = $locString.lblIdentityStore
    $boxAzureIdentity.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,$grpRow)
    $lstIdentityStore.Size = New-Object System.Drawing.Size($txtSize)
    $lstIdentityStore.Font = $defaultLabelFont
    $lstIdentityStore.BackColor = [System.Drawing.SystemColors]::Window
    $lstIdentityStore.Name = 'lstIdentityStore'
    [void]$lstIdentityStore.Items.Clear()
    $arrIdentityStore | Foreach-Object `
    {
        [void]$lstIdentityStore.Items.Add($PSItem)
    }
    $lstIdentityStore.SelectedIndex = -1
    $boxAzureIdentity.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 += 50

    # -------------------------- EU DATA BOUNDARY -----------------------------
    $script:chkDataBoundary = New-Object System.Windows.Forms.Checkbox
    $chkDataBoundary.Location = New-Object System.Drawing.Size($lblLeft2,($row+2))
    $chkDataBoundary.MaximumSize = New-Object System.Drawing.Size($lblMaxSize)
    $chkDataBoundary.AutoSize = $true
    $chkDataBoundary.Font = $defaultLabelFont
    $chkDataBoundary.Text = $locString.chkDataBoundary
    $chkDataBoundary.Name = 'chkDataBoundary'
    $tabCustomer.Controls.Add($chkDataBoundary)

    $grpRow += 25

    # ---------------------------- TENANT NAME --------------------------------
    $script:lblAADTenantName = New-Object System.Windows.Forms.Label
    $lblAADTenantName.Location = New-Object System.Drawing.Size($lblLeft,($grpRow+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
    $boxAzureIdentity.Controls.Add($lblAADTenantName)

    $script:txtAADTenantName = New-Object System.Windows.Forms.TextBox
    $txtAADTenantName.Location = New-Object System.Drawing.Size($txtLeft,$grpRow)
    $txtAADTenantName.Size = New-Object System.Drawing.Size($txtSize)
    $txtAADTenantName.Font = $defaultLabelFont
    $txtAADTenantName.Name = 'txtAADTenantName'
    $txtAADTenantName.MaxLength = $lenText
    $txtAADTenantName.Enabled = $false
    $txtAADTenantName.Visible = $false
    $boxAzureIdentity.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)
                $txtAADTenantName.Focus()
            }
            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,($grpRow+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
    $boxAzureIdentity.Controls.Add($lblADFSForestFQDN)

    $script:txtADFSForestFQDN = New-Object System.Windows.Forms.TextBox
    $txtADFSForestFQDN.Location = New-Object System.Drawing.Size($txtLeft,$grpRow)
    $txtADFSForestFQDN.Size = New-Object System.Drawing.Size($txtSize)
    $txtADFSForestFQDN.Font = $defaultLabelFont
    $txtADFSForestFQDN.Name = 'txtADFSForestFQDN'
    $txtADFSForestFQDN.MaxLength = $lenDomain
    $txtADFSForestFQDN.Enabled = $false
    $txtADFSForestFQDN.Visible = $false
    $script:tipADFSForestFQDN = New-Object System.Windows.Forms.ToolTip
    $tipADFSForestFQDN.ShowAlways = $false
    $tipADFSForestFQDN.ReshowDelay = 500
    $tipADFSForestFQDN.AutoPopDelay = 10000
    $tipADFSForestFQDN.IsBalloon = $true
    $tipADFSForestFQDN_Show = `
    {
        $tipADFSForestFQDN.RemoveAll()
        $tipADFSForestFQDN.Show($locString.tipADFSForestFQDN,$txtADFSForestFQDN,40,-65)
    }
    $tipADFSForestFQDN_Hide = `
    {
        $tipADFSForestFQDN.Hide($txtADFSForestFQDN)
        $tipADFSForestFQDN.RemoveAll()
    }
    $txtADFSForestFQDN.Add_GotFocus($tipADFSForestFQDN_Show)
    $txtADFSForestFQDN.Add_LostFocus($tipADFSForestFQDN_Hide)
    $boxAzureIdentity.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)
                $txtADFSForestFQDN.Focus()
            }
            else
            {
                $txtADFSForestFQDN.ForeColor = $defaultForeColor
            }
            Update-TextBox $txtADFSForestFQDN
        }
        else
        {
            $txtADFSForestFQDN.ForeColor = $defaultForeColor
        }
    }
    $txtADFSForestFQDN.Add_Leave($txtADFSForestFQDN_Changed)

    $grpRow += 25

    # --------------------------- AZURE DIRECTORY -----------------------------
    $script:lblAzureEnvironment = New-Object System.Windows.Forms.Label
    $lblAzureEnvironment.Location = New-Object System.Drawing.Size($lblLeft,($grpRow+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
    $boxAzureIdentity.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,$grpRow)
    $lstAzureEnvironment.Size = New-Object System.Drawing.Size($txtSize)
    $lstAzureEnvironment.Font = $defaultLabelFont
    $lstAzureEnvironment.Name = 'lstAzureEnvironment'
    $lstAzureEnvironment.Enabled = $false
    $lstAzureEnvironment.Visible = $false
    $lstAzureEnvironment.BackColor = [System.Drawing.SystemColors]::Control
    [void]$lstAzureEnvironment.Items.Clear()
    $arrAzureEnvironment | Foreach-Object `
    {
        [void]$lstAzureEnvironment.Items.Add($PSItem)
    }
    $boxAzureIdentity.Controls.Add($lstAzureEnvironment)

    # ----------------------------- ADFS NAME ---------------------------------
    $script:lblADFSProviderName = New-Object System.Windows.Forms.Label
    $lblADFSProviderName.Location = New-Object System.Drawing.Size($lblLeft,($grpRow+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
    $boxAzureIdentity.Controls.Add($lblADFSProviderName)

    $script:txtADFSProviderName = New-Object System.Windows.Forms.TextBox
    $txtADFSProviderName.Location = New-Object System.Drawing.Size($txtLeft,$grpRow)
    $txtADFSProviderName.Size = New-Object System.Drawing.Size($txtSize)
    $txtADFSProviderName.Font = $defaultLabelFont
    $txtADFSProviderName.Name = 'txtADFSProviderName'
    $txtADFSProviderName.MaxLength = $lenText
    $txtADFSProviderName.Enabled = $false
    $txtADFSProviderName.Visible = $false
    $script:tipADFSProviderName = New-Object System.Windows.Forms.ToolTip
    $tipADFSProviderName.ShowAlways = $false
    $tipADFSProviderName.ReshowDelay = 500
    $tipADFSProviderName.AutoPopDelay = 10000
    $tipADFSProviderName.IsBalloon = $true
    $tipADFSProviderName_Show = `
    {
        $tipADFSProviderName.RemoveAll()
        $tipADFSProviderName.Show($locString.tipADFSProviderName,$txtADFSProviderName,40,-65)
    }
    $tipADFSProviderName_Hide = `
    {
        $tipADFSProviderName.Hide($txtADFSProviderName)
        $tipADFSProviderName.RemoveAll()
    }
    $txtADFSProviderName.Add_GotFocus($tipADFSProviderName_Show)
    $txtADFSProviderName.Add_LostFocus($tipADFSProviderName_Hide)
    $boxAzureIdentity.Controls.Add($txtADFSProviderName)
    $txtADFSProviderName_Changed = `
    {
        if(-not [string]::IsNullOrEmpty($txtADFSProviderName.Text))
        {
            $txtADFSProviderName.Text = ($txtADFSProviderName.Text).Trim()
            $txtADFSProviderName.ForeColor = $defaultForeColor
            Update-TextBox $txtADFSProviderName
        }
    }
    $txtADFSProviderName.Add_Leave($txtADFSProviderName_Changed)

    $grpRow += 25

    # ------------------------- ADFS METADATA URI -----------------------------
    $script:lblADFSMetadataUri = New-Object System.Windows.Forms.Label
    $lblADFSMetadataUri.Location = New-Object System.Drawing.Size($lblLeft,($grpRow+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
    $boxAzureIdentity.Controls.Add($lblADFSMetadataUri)

    $script:txtADFSMetadataUri = New-Object System.Windows.Forms.TextBox
    $txtADFSMetadataUri.Location = New-Object System.Drawing.Size($txtLeft,$grpRow)
    $txtADFSMetadataUri.Size = New-Object System.Drawing.Size($txtSize)
    $txtADFSMetadataUri.Font = $defaultLabelFont
    $txtADFSMetadataUri.Name = 'txtADFSMetadataUri'
    $txtADFSMetadataUri.MaxLength = $lenURI
    $txtADFSMetadataUri.Enabled = $false
    $txtADFSMetadataUri.Visible = $false
    $script:tipADFSMetadataUri = New-Object System.Windows.Forms.ToolTip
    $tipADFSMetadataUri.ShowAlways = $false
    $tipADFSMetadataUri.ReshowDelay = 500
    $tipADFSMetadataUri.AutoPopDelay = 10000
    $tipADFSMetadataUri.IsBalloon = $true
    $tipADFSMetadataUri_Show = `
    {
        $tipADFSMetadataUri.RemoveAll()
        $tipADFSMetadataUri.Show($locString.tipADFSMetadataUri,$txtADFSMetadataUri,40,-65)
    }
    $tipADFSMetadataUri_Hide = `
    {
        $tipADFSMetadataUri.Hide($txtADFSMetadataUri)
        $tipADFSMetadataUri.RemoveAll()
    }
    $txtADFSMetadataUri.Add_GotFocus($tipADFSMetadataUri_Show)
    $txtADFSMetadataUri.Add_LostFocus($tipADFSMetadataUri_Hide)
    $boxAzureIdentity.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)
                $txtADFSMetadataUri.Focus()
            }
            else
            {
                $txtADFSMetadataUri.ForeColor = $defaultForeColor
            }
            Update-TextBox $txtADFSMetadataUri
        }
        else
        {
            $txtADFSMetadataUri.ForeColor = $defaultForeColor
        }
    }
    $txtADFSMetadataUri.Add_Leave($txtADFSMetadataUri_Changed)

    $row += 110

    # --------------------------- CUSTOMER INFO -------------------------------
    $grpRow = 30
    $script:boxCustomerInfo = New-Object System.Windows.Forms.GroupBox
    $boxCustomerInfo.Location = New-Object System.Drawing.Point($lblLeft,$row)
    $boxCustomerInfo.Size = New-Object System.Drawing.Size($grpBoxWidth,220)
    $boxCustomerInfo.Text = $locString.lblCustomerInfo
    $boxCustomerInfo.Name = $locString.lblCustomerInfo
    $boxCustomerInfo.Visible = $true
    $tabCustomer.Controls.Add($boxCustomerInfo)

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

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

    $grpRow += 30

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

    $script:txtExternalDomain = New-Object System.Windows.Forms.TextBox
    $txtExternalDomain.Location = New-Object System.Drawing.Size($txtLeft,$grpRow)
    $txtExternalDomain.Size = New-Object System.Drawing.Size($txtSize)
    $txtExternalDomain.Font = $defaultLabelFont
    $txtExternalDomain.Name = 'txtExternalDomain'
    $txtExternalDomain.MaxLength = $lenDomain
    $boxCustomerInfo.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)
                $txtExternalDomain.Focus()
            }
            else
            {
                $txtExternalDomain.ForeColor = $defaultForeColor
            }
            Update-TextBox $txtExternalDomain
        }
        else
        {
            $txtExternalDomain.ForeColor = $defaultForeColor
        }
    }
    $txtExternalDomain.Add_Leave($txtExternalDomain_Changed)

    $grpRow += 30

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

    $script:txtPrivateDomain = New-Object System.Windows.Forms.TextBox
    $txtPrivateDomain.Location = New-Object System.Drawing.Size($txtLeft,$grpRow)
    $txtPrivateDomain.Size = New-Object System.Drawing.Size($txtSize)
    $txtPrivateDomain.Font = $defaultLabelFont
    $txtPrivateDomain.Name = 'txtPrivateDomain'
    $txtPrivateDomain.MaxLength = $lenDomain
    $boxCustomerInfo.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)
                $txtPrivateDomain.Focus()
            }
            else
            {
                $txtPrivateDomain.ForeColor = $defaultForeColor
            }
            Update-TextBox $txtPrivateDomain
        }
        else
        {
            $txtPrivateDomain.ForeColor = $defaultForeColor
        }
    }
    $txtPrivateDomain.Add_Leave($txtPrivateDomain_Changed)

    $grpRow += 30

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

    $script:txtRegionName = New-Object System.Windows.Forms.TextBox
    $txtRegionName.Location = New-Object System.Drawing.Size($txtLeft,$grpRow)
    $txtRegionName.Size = New-Object System.Drawing.Size($txtPrefixSize)
    $txtRegionName.Font = $defaultLabelFont
    $txtRegionName.Name = 'txtRegionName'
    $txtRegionName.MaxLength = $lenText
    $boxCustomerInfo.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)
                $txtRegionName.Focus()
            }
            else
            {
                $txtRegionName.ForeColor = $defaultForeColor
            }
            Update-TextBox $txtRegionName
        }
        else
        {
            $txtRegionName.ForeColor = $defaultForeColor
        }
    }
    $txtRegionName.Add_Leave($txtRegionName_Changed)

    $grpRow += 30

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

    $script:txtDeployPrefix = New-Object System.Windows.Forms.TextBox
    $txtDeployPrefix.Location = New-Object System.Drawing.Size($txtLeft,$grpRow)
    $txtDeployPrefix.Size = New-Object System.Drawing.Size($txtPrefixSize)
    $txtDeployPrefix.Font = $defaultLabelFont
    $txtDeployPrefix.Name = 'txtDeployPrefix'
    $txtDeployPrefix.MaxLength = $lenPrefix
    $boxCustomerInfo.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)
                $txtDeployPrefix.Focus()
            }
            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)
                $txtDeployPrefix.Focus()
            }
            else
            {
                $txtDeployPrefix.ForeColor = $defaultForeColor
            }
            Update-TextBox $txtDeployPrefix
        }
        else
        {
            $txtDeployPrefix.ForeColor = $defaultForeColor
        }
    }
    $txtDeployPrefix.Add_Leave($txtDeployPrefix_Changed)

    $grpRow += 30

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

    $script:txtPhysicalPrefix = New-Object System.Windows.Forms.TextBox
    $txtPhysicalPrefix.Location = New-Object System.Drawing.Size($txtLeft,$grpRow)
    $txtPhysicalPrefix.Size = New-Object System.Drawing.Size($txtPrefixSize)
    $txtPhysicalPrefix.Font = $defaultLabelFont
    $txtPhysicalPrefix.Name = 'txtPhysicalPrefix'
    $txtPhysicalPrefix.MaxLength = $lenPrefix
    $boxCustomerInfo.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)
                $txtPhysicalPrefix.Focus()
            }
            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)
                $txtPhysicalPrefix.Focus()
            }
            else
            {
                $txtPhysicalPrefix.ForeColor = $defaultForeColor
            }
            Update-TextBox $txtPhysicalPrefix
        }
        else
        {
            $txtPhysicalPrefix.ForeColor = $defaultForeColor
        }
    }
    $txtPhysicalPrefix.Add_Leave($txtPhysicalPrefix_Changed)

    $row += 240

    # -------------------------- ENVIRONMENT INFO -----------------------------
    $grpRow = 30
    $script:boxEnvironmentInfo = New-Object System.Windows.Forms.GroupBox
    $boxEnvironmentInfo.Location = New-Object System.Drawing.Point($lblLeft,$row)
    $boxEnvironmentInfo.Size = New-Object System.Drawing.Size($grpBoxWidth,120)
    $boxEnvironmentInfo.Text = $locString.lblEnvironmentInfo
    $boxEnvironmentInfo.Name = $locString.lblEnvironmentInfo
    $boxEnvironmentInfo.Visible = $true
    $tabCustomer.Controls.Add($boxEnvironmentInfo)

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

    $script:txtTimeServer = New-Object System.Windows.Forms.TextBox
    $txtTimeServer.Location = New-Object System.Drawing.Size($txtLeft,$grpRow)
    $txtTimeServer.Size = New-Object System.Drawing.Size($txtSize)
    $txtTimeServer.Font = $defaultLabelFont
    $txtTimeServer.Name = 'txtTimeServer'
    $txtTimeServer.MaxLength = $lenDomain
    $script:tipTimeServer = New-Object System.Windows.Forms.ToolTip
    $tipTimeServer.ShowAlways = $false
    $tipTimeServer.ReshowDelay = 500
    $tipTimeServer.AutoPopDelay = 10000
    $tipTimeServer.IsBalloon = $true
    $tipTimeServer_Show = `
    {
        $tipTimeServer.RemoveAll()
        $tipTimeServer.Show($locString.tipTimeServer,$txtTimeServer,40,-80)
    }
    $tipTimeServer_Hide = `
    {
        $tipTimeServer.Hide($txtTimeServer)
        $tipTimeServer.RemoveAll()
    }
    $txtTimeServer.Add_GotFocus($tipTimeServer_Show)
    $txtTimeServer.Add_LostFocus($tipTimeServer_Hide)
    $boxEnvironmentInfo.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)
                $txtTimeServer.Focus()
            }
            else
            {
                $txtTimeServer.ForeColor = $defaultForeColor
            }
        }
        else
        {
            $txtTimeServer.ForeColor = $defaultForeColor
        }
        Update-TextBox $txtTimeServer
    }
    $txtTimeServer.Add_Leave($txtTimeServer_Changed)

    $grpRow += 30

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

    $pnlDNSForwarder = New-Object System.Windows.Forms.Panel
    $pnlDNSForwarder.BorderStyle = $defaultBorderStyle
    $pnlDNSForwarder.Location = New-Object System.Drawing.Size($txtLeft,$grpRow)
    $pnlDNSForwarder.Name = 'pnlDNSForwarder'
    $pnlDNSForwarder.Size = New-Object System.Drawing.Size($txtSize)
    $script:txtDNSForwarder = New-Object System.Windows.Forms.RichTextBox
    $txtDNSForwarder.Location = New-Object System.Drawing.Size(2,2)
    $txtDNSForwarder.Size = New-Object System.Drawing.Size($txtSize)
    $txtDNSForwarder.BorderStyle = [System.Windows.Forms.BorderStyle]::None
    $txtDNSForwarder.Font = $defaultLabelFont
    $txtDNSForwarder.Name = 'txtDNSForwarder'
    $txtDNSForwarder.MaxLength = 32
    $txtDNSForwarder.Multiline = $false
    $boxEnvironmentInfo.Controls.Add($pnlDNSForwarder)
    $pnlDNSForwarder.Controls.Add($txtDNSForwarder)
    $script:tipDNSForwarder = New-Object System.Windows.Forms.ToolTip
    $tipDNSForwarder.ShowAlways = $false
    $tipDNSForwarder.ReshowDelay = 500
    $tipDNSForwarder.AutoPopDelay = 10000
    $tipDNSForwarder.IsBalloon = $true
    $tipDNSForwarder_Show = `
    {
        $tipDNSForwarder.RemoveAll()
        $tipDNSForwarder.Show($locString.tipDNSForwarder,$txtDNSForwarder,40,-65)
    }
    $tipDNSForwarder_Hide = `
    {
        $tipDNSForwarder.Hide($txtDNSForwarder)
        $tipDNSForwarder.RemoveAll()
    }
    $txtDNSForwarder.Add_GotFocus($tipDNSForwarder_Show)
    $txtDNSForwarder.Add_LostFocus($tipDNSForwarder_Hide)
    $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
            }
            $selectStart = -1
            $selectLength = 0
            foreach($dns in $dnsList)
            {
                if($dns -notmatch $validIPAddress)
                {
                    $errItem += $dns
                    if($selectStart -eq -1)
                    {
                        $selectStart = $txtDNSForwarder.Text.IndexOf($dns)
                        $selectLength = $dns.Length
                    }
                    else
                    {
                        $selectLength += $dns.Length + 1
                    }
                }
                else
                {
                    $txtDNSForwarder.SelectionStart = $txtDNSForwarder.Text.IndexOf($dns)
                    $txtDNSForwarder.SelectionLength = $dns.Length
                    $txtDNSForwarder.SelectionColor = $defaultForeColor
                }
            }
            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)
                $txtDNSForwarder.SelectionStart = $selectStart
                $txtDNSForwarder.SelectionLength = $selectLength
                $txtDNSForwarder.SelectionColor = $defaultFailColor
                $txtDNSForwarder.Focus()
                $txtDNSForwarder.ForeColor = $defaultForeColor
            }
            else
            {
                $txtDNSForwarder.SelectionStart = 0
                $txtDNSForwarder.SelectionLength = $txtDNSForwarder.Text.Length
                $txtDNSForwarder.ForeColor = $defaultForeColor
            }
        }
        else
        {
            $txtDNSForwarder.ForeColor = $defaultForeColor
        }
        $txtDNSForwarder.Refresh()
    }
    $txtDNSForwarder.Add_Leave($txtDNSForwarder_Changed)

    $grpRow += 30

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

    $script:txtSyslogServer = New-Object System.Windows.Forms.TextBox
    $txtSyslogServer.Location = New-Object System.Drawing.Size($txtLeft,$grpRow)
    $txtSyslogServer.Size = New-Object System.Drawing.Size($txtPrefixSize)
    $txtSyslogServer.Font = $defaultLabelFont
    $txtSyslogServer.Name = 'txtSyslogServer'
    $accName = $locString.lblSyslogServer + " " + $locString.strOptionalNote
    $txtSyslogServer.AccessibleName = $accName
    $txtSyslogServer.AccessibleDescription = $accName
    $txtSyslogServer.MaxLength = $lenIPAddress
    $script:tipSyslogServer = New-Object System.Windows.Forms.ToolTip
    $tipSyslogServer.ShowAlways = $false
    $tipSyslogServer.ReshowDelay = 500
    $tipSyslogServer.AutoPopDelay = 10000
    $tipSyslogServer.IsBalloon = $true
    $tipSyslogServer_Show = `
    {
        $tipSyslogServer.RemoveAll()
        $tipSyslogServer.Show($locString.tipSyslogServer,$txtSyslogServer,60,-40)
    }
    $tipSyslogServer_Hide = `
    {
        $tipSyslogServer.Hide($txtSyslogServer)
        $tipSyslogServer.RemoveAll()
    }
    $txtSyslogServer.Add_GotFocus($tipSyslogServer_Show)
    $txtSyslogServer.Add_LostFocus($tipSyslogServer_Hide)
    $boxEnvironmentInfo.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)
                $txtSyslogServer.Focus()
            }
            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
    $boxEnvironmentInfo.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.AccessibleName = $locString.lblNetworkSettings
    $lblNetworkSettings.AccessibleDescription = $locString.lblNetworkSettings
    $lblNetworkSettings.Font = [System.Drawing.Font]::New("Microsoft Sans Serif", 12, [System.Drawing.FontStyle]::Bold)
    $tabNetwork.Controls.Add($lblNetworkSettings)

    $row2 = $row
    $row += 40

    # ----------------------------- 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.BorderStyle = $defaultBorderStyle
    $txtScaleUnitCount.TextAlign = [System.Windows.Forms.HorizontalAlignment]::Center
    $txtScaleUnitCount.Name = 'txtScaleUnitCount'
    $txtScaleUnitCount.MaxLength = 1
    $txtScaleUnitCount.Text = $defaultSUCount
    $txtScaleUnitCount.Enabled = $false
    $txtScaleUnitCount.BackColor = [System.Drawing.SystemColors]::Window
    $txtScaleUnitCount.ForeColor = [System.Drawing.SystemColors]::WindowText
    $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..[int]$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
                        if($false -eq $lstSpineModel.Items.Contains('-none-'))
                        {
                            [void]$lstSpineModel.Items.Add('-none-')
                        }
                        Disable-ComboBoxList -Control $lstSpineModel -Label $lblSpineModel
                        $lstSpineModel.SelectedIndex = 0
                        Disable-ComboBoxList -Control $lstSpineFirmware -Label $lblSpineFirmware
                        Set-ScaleUnitsCiscoUCS -Enabled $false
                    }
                    else
                    {
                        Enable-SpineSubnet
                        Enable-ComboBoxList -Control $lstSpineModel -Label $lblSpineModel
                        Enable-ComboBoxList -Control $lstSpineFirmware -Label $lblSpineFirmware
                        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
                    Disable-ComboBoxList -Control $lstSpineModel -Label $lblSpineModel
                    Disable-ComboBoxList -Control $lstSpineFirmware -Label $lblSpineFirmware
                    Update-MaxBorderFromSwitch
                    if($lstSwitchVendor.SelectedItem -eq "CiscoUCS")
                    {
                        $script:validBorder = 4
                    }
                    if($true -eq $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 -------------------------------
    $grpRow = 30
    $script:boxSwitchInfo = New-Object System.Windows.Forms.GroupBox
    $boxSwitchInfo.Location = New-Object System.Drawing.Point($lblLeft,$row)
    $boxSwitchInfo.Size = New-Object System.Drawing.Size($grpBoxWidth,220)
    $boxSwitchInfo.Text = $locString.lblSwitchInfo
    $boxSwitchInfo.Name = $locString.lblSwitchInfo
    $boxSwitchInfo.AccessibleName = ($locString.lblNetworkSettings + " " + $locString.lblSwitchInfo)
    $boxSwitchInfo.Visible = $true
    $tabNetwork.Controls.Add($boxSwitchInfo)

    $rowSwInfo = $row

    # ---------------------------- SWITCH VENDOR ------------------------------
    $script:lblSwitchVendor = New-Object System.Windows.Forms.Label
    $lblSwitchVendor.Location = New-Object System.Drawing.Size($lblLeft,($grpRow+2))
    $lblSwitchVendor.MaximumSize = New-Object System.Drawing.Size($lblMaxSize)
    $lblSwitchVendor.AutoSize = $true
    $lblSwitchVendor.Font = $defaultLabelFont
    $lblSwitchVendor.Text = $locString.lblSwitchVendor
    $lblSwitchVendor.Visible = $true
    $boxSwitchInfo.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,$grpRow)
    $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 `
                {
                    if(-not $lstTORModel.Items.Contains($PSItem))
                    {
                        [void]$lstTORModel.Items.Add($PSItem)
                    }
                }
                if($lstTORModel.Items.Count -gt 0)
                {
                    $lstTORModel.SelectedIndex = 0
                    $lstTORModel.Refresh()
                    if(Get-SupportNoBMC -Make $lstSwitchVendor.SelectedItem -Model $lstTORModel.SelectedItem)
                    {
                        if($false -eq $lstBMCModel.Items.Contains('-none-'))
                        {
                            $lstBMCModel.Items.Add('-none-')
                        }
                    }
                    else
                    {
                        while($lstBMCModel.Items.Contains('-none-'))
                        {
                            $lstBMCModel.Items.Remove('-none-')
                        }
                    }
                }
                else
                {
                    $lstTORModel.SelectedIndex = -1
                }
                Invoke-Command -ScriptBlock $lstRoutingMethod_Changed
                if(($lstSwitchVendor.SelectedItem -eq "CiscoUCS") -or ($lstSwitchVendor.SelectedItem -eq "Mellanox"))
                {
                    $script:validBorder = 4
                    if(($lstSwitchVendor.SelectedItem -eq "CiscoUCS") -or (($lstSwitchVendor.SelectedItem -eq "Mellanox") -and ($lstBMCModel.Item.Count -eq 0)))
                    {
                        Write-Log -Message "SwitchMakeChange - CiscoUCS or Mellanox with no BMC devices found, so force IsNoBMC"
                        if($false -eq $lstBMCModel.Items.Contains('-none-'))
                        {
                            [void]$lstBMCModel.Items.Add('-none-')
                        }
                        Disable-ComboBoxList -Control $lstBMCModel -Label $lblBMCModel
                        Disable-ComboBoxList -Control $lstBMCFirmware -Label $lblBMCFirmware
                    }
                    if($false -eq $lstSpineModel.Items.Contains('-none-'))
                    {
                        [void]$lstSpineModel.Items.Add('-none-')
                    }
                    Disable-ComboBoxList -Control $lstSpineModel -Label $lblSpineModel
                    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 `
                    {
                        if(-not $lstBMCModel.Items.Contains($PSItem))
                        {
                            [void]$lstBMCModel.Items.Add($PSItem)
                        }
                    }
                    Enable-ComboBoxList -Control $lstBMCModel -Label $lblBMCModel

                    $models = @()
                    #$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')
                        {
                            Disable-ComboBoxList -Control $lstSpineModel -Label $lblSpineModel
                        }
                        else
                        {
                            Enable-ComboBoxList -Control $lstSpineModel -Label $lblSpineModel
                            $lstSpineModel.SelectedIndex = 0
                        }
                    }
                    else
                    {
                        Disable-ComboBoxList -Control $lstSpineModel -Label $lblSpineModel
                        Disable-ComboBoxList -Control $lstSpineFirmware -Label $lblSpineFirmware
                    }
                }
                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
                }
                if($true -eq $isExtStor)
                {
                    $tryMake = $lstSwitchVendor.SelectedItem
                    if($tryMake -eq 'msftcisco')
                    {
                        $tryMake = 'msftdellemc'
                    }
                    if(-not [string]::IsNullOrEmpty($tryMake))
                    {
                        $script:backSwitch = Get-SwitchInfo -SwitchType BACKEND -SwitchMake $tryMake
                    }
                    $script:backMake = $backSwitch.Make
                    $script:backModel = $backSwitch.Model
                    $script:backFirmware = $backSwitch.Firmware
                }
            }
        }
    }
    $lstSwitchVendor.Add_SelectedIndexChanged($lstSwitchVendor_Changed)
    $boxSwitchInfo.Controls.Add($lstSwitchVendor)

    $grpRow += 25

    # ------------------------------ TOR MODEL --------------------------------
    $script:lblTORModel = New-Object System.Windows.Forms.Label
    $lblTORModel.Location = New-Object System.Drawing.Size($lblLeft,($grpRow+2))
    $lblTORModel.MaximumSize = New-Object System.Drawing.Size($lblMaxSize)
    $lblTORModel.AutoSize = $true
    $lblTORModel.Font = $defaultLabelFont
    $lblTORModel.Text = $locString.lblTORModel
    $lblTORModel.Visible = $true
    $boxSwitchInfo.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,$grpRow)
    $lstTORModel.Size = New-Object System.Drawing.Size($txtSizeNet)
    $lstTORModel.Font = $defaultLabelFont
    $lstTORModel.Name = 'lstTORModel'
    $lstTORModel.Visible = $true
    [void]$lstTORModel.Items.Clear()
    $lstTORModel.SelectedIndex = -1
    $boxSwitchInfo.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
                }
            }
            if(Get-SupportNoBMC -Make $lstSwitchVendor.SelectedItem -Model $lstTORModel.SelectedItem)
            {
                if($false -eq $lstBMCModel.Items.Contains('-none-'))
                {
                    $lstBMCModel.Items.Add('-none-')
                }
                if([int]$lstBMCModel.Items.Count -eq 1)
                {
                    $lstBMCModel.SelectedIndex = 0
                }
            }
            else
            {
                if($true -eq $lstBMCModel.Items.Contains('-none-'))
                {
                    while($lstBMCModel.Items.Contains('-none-'))
                    {
                        $lstBMCModel.Items.Remove('-none-')
                    }
                }
                if([int]$lstBMCModel.Items.Count -gt 0)
                {
                    $lstBMCModel.SelectedIndex = 0
                }
            }
        }
        else
        {
            Clear-ListSelection $lstTORFirmware
        }
        $lstTORFirmware.Refresh()
    }
    $lstTORModel.Add_SelectedIndexChanged($lstTORModel_Changed)

    $grpRow += 25

    # ---------------------------- TOR FIRMWARE -------------------------------
    $script:lblTORFirmware = New-Object System.Windows.Forms.Label
    $lblTORFirmware.Location = New-Object System.Drawing.Size($lblLeft,($grpRow+2))
    $lblTORFirmware.MaximumSize = New-Object System.Drawing.Size($lblMaxSize)
    $lblTORFirmware.AutoSize = $true
    $lblTORFirmware.Font = $defaultLabelFont
    $lblTORFirmware.Text = $locString.lblTORFirmware
    $lblTORFirmware.Visible = $true
    $boxSwitchInfo.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,$grpRow)
    $lstTORFirmware.Size = New-Object System.Drawing.Size($txtSizeNet)
    $lstTORFirmware.Font = $defaultLabelFont
    $lstTORFirmware.Name = 'lstTORFirmware'
    $lstTORFirmware.Visible = $true
    [void]$lstTORFirmware.Items.Clear()
    $lstTORFirmware.SelectedIndex = -1
    $boxSwitchInfo.Controls.Add($lstTORFirmware)

    $grpRow += 25

    # ------------------------------ BMC MODEL --------------------------------
    $script:lblBMCModel = New-Object System.Windows.Forms.Label
    $lblBMCModel.Location = New-Object System.Drawing.Size($lblLeft,($grpRow+2))
    $lblBMCModel.MaximumSize = New-Object System.Drawing.Size($lblMaxSize)
    $lblBMCModel.AutoSize = $true
    $lblBMCModel.Font = $defaultLabelFont
    $lblBMCModel.Text = $locString.lblBMCModel
    $lblBMCModel.Visible = $true
    $boxSwitchInfo.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,$grpRow)
    $lstBMCModel.Size = New-Object System.Drawing.Size($txtSizeNet)
    $lstBMCModel.Font = $defaultLabelFont
    $lstBMCModel.Name = 'lstBMCModel'
    $lstBMCModel.Visible = $true
    [void]$lstBMCModel.Items.Clear()
    $lstBMCModel.SelectedIndex = -1
    $script:isNoBMC = $false
    $lstBMCModel_Changed = `
    {
        [void]$lstBMCFirmware.Items.Clear()
        if($lstBMCModel.SelectedItem)
        {
            if($lstBMCModel.SelectedItem -eq '-none-')
            {
                $script:isNoBMC = $true
            }
            else
            {
                $script:isNoBMC = $false
            }
        }
        if($lstSwitchVendor.SelectedItem -eq "CiscoUCS")
        {
            Disable-ComboBoxList -Control $lstBMCFirmware -Label $lblBMCFirmware
            $script:isNoBMC = $true
        }
        else
        {
            if($true -eq $isNoBMC)
            {
                Disable-ComboBoxList -Control $lstBMCFirmware -Label $lblBMCFirmware
            }
            else
            {
                Enable-ComboBoxList -Control $lstBMCFirmware -Label $lblBMCFirmware
            }
            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)
    $boxSwitchInfo.Controls.Add($lstBMCModel)

    $grpRow += 25

    # ---------------------------- BMC FIRMWARE -------------------------------
    $script:lblBMCFirmware = New-Object System.Windows.Forms.Label
    $lblBMCFirmware.Location = New-Object System.Drawing.Size($lblLeft,($grpRow+2))
    $lblBMCFirmware.MaximumSize = New-Object System.Drawing.Size($lblMaxSize)
    $lblBMCFirmware.AutoSize = $true
    $lblBMCFirmware.Font = $defaultLabelFont
    $lblBMCFirmware.Text = $locString.lblBMCFirmware
    $lblBMCFirmware.Visible = $true
    $boxSwitchInfo.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,$grpRow)
    $lstBMCFirmware.Size = New-Object System.Drawing.Size($txtSizeNet)
    $lstBMCFirmware.Font = $defaultLabelFont
    $lstBMCFirmware.Name = 'lstBMCFirmware'
    $lstBMCFirmware.Visible = $true
    [void]$lstBMCFirmware.Items.Clear()
    $lstBMCFirmware.SelectedIndex = -1
    $boxSwitchInfo.Controls.Add($lstBMCFirmware)

    $grpRow += 25

    # ----------------------------- SPINE MODEL -------------------------------
    $script:lblSpineModel = New-Object System.Windows.Forms.Label
    $lblSpineModel.Location = New-Object System.Drawing.Size($lblLeft,($grpRow+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
    $boxSwitchInfo.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,$grpRow)
    $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
    $boxSwitchInfo.Controls.Add($lstSpineModel)

    $grpRow += 25

    # ---------------------------- SPINE FIRMWARE -------------------------------
    $script:lblSpineFirmware = New-Object System.Windows.Forms.Label
    $lblSpineFirmware.Location = New-Object System.Drawing.Size($lblLeft,($grpRow+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
    $boxSwitchInfo.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,$grpRow)
    $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
    $boxSwitchInfo.Controls.Add($lstSpineFirmware)
    $lstSpineModel_Changed = `
    {
        [void]$lstSpineFirmware.Items.Clear()
        if(($lstSwitchVendor.SelectedItem -eq "CiscoUCS") -or ($lstSwitchVendor.SelectedItem -eq "Mellanox"))
        {
            Disable-ComboBoxList -Control $lstSpineFirmware -Label $lblSpineFirmware
        }
        else
        {
            if(-not [string]::IsNullOrEmpty($lstSpineModel.SelectedItem))
            {
                Disable-ComboBoxList -Control $lstSpineFirmware -Label $lblSpineFirmware
                $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)
                    }
                    Enable-ComboBoxList -Control $lstSpineFirmware -Label $lblSpineFirmware
                    $lstSpineFirmware.SelectedIndex = 0
                }
                else
                {
                    Disable-ComboBoxList -Control $lstSpineFirmware -Label $lblSpineFirmware
                }
                if($txtScaleUnitCount.Text -eq '1')
                {
                    Disable-ComboBoxList -Control $lstSpineModel -Label $lblSpineModel
                    Disable-ComboBoxList -Control $lstSpineFirmware -Label $lblSpineFirmware
                }
            }
        }
    }
    $lstSpineModel.Add_SelectedIndexChanged($lstSpineModel_Changed)

    $row += 240

    # --------------------------- 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)
                $txtSoftwareBGP.Focus()
                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.Focus()
            $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)
                $txtSpineBGP.Focus()
                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)
                $txtSpineSubnet.Focus()
                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)
                $txtSpineSubnet.Focus()
                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')
    {
        Disable-ComboBoxList -Control $lstSpineModel -Label $lblSpineModel
        $lblSpineModel.Refresh()
        $lstSpineModel.Refresh()
        Disable-ComboBoxList -Control $lstSpineFirmware -Label $lblSpineFirmware
        $lblSpineFirmware.Refresh()
        $lstSpineFirmware.Refresh()
        Disable-SpineBGP
        Disable-SpineCount
    }
    else
    {
        Enable-ComboBoxList -Control $lstSpineModel -Label $lblSpineModel
        $lblSpineModel.Refresh()
        $lstSpineModel.Refresh()
        Enable-ComboBoxList -Control $lstSpineFirmware -Label $lblSpineFirmware
        $lblSpineFirmware.Refresh()
        $lstSpineFirmware.Refresh()
        if($isStaticRouting)
        {
            Disable-SpineBGP
        }
        else
        {
            Enable-SpineBGP
        }
        Enable-SpineCount
    }

    $row2 = $rowSwInfo

    # ---------------------------- CLOUD SUBNETS ------------------------------
    $grpLblLeft = 20
    $grpTxtLeft = 120
    $grpRow = 30
    $script:boxCloudSubnets = New-Object System.Windows.Forms.GroupBox
    $boxCloudSubnets.Location = New-Object System.Drawing.Point($lblLeft2,$row2)
    $boxCloudSubnets.Size = New-Object System.Drawing.Size($grpBoxWidth,100)
    $boxCloudSubnets.Text = $locString.lblCloudSubnets
    $boxCloudSubnets.Name = $locString.lblCloudSubnets
    $boxCloudSubnets.AccessibleName = ($locString.lblNetworkSettings + " " + $locString.lblCloudSubnets)
    $boxCloudSubnets.Visible = $true
    $tabNetwork.Controls.Add($boxCloudSubnets)

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

    $script:txtExternalSubnet = New-Object System.Windows.Forms.TextBox
    $txtExternalSubnet.Location = New-Object System.Drawing.Size($grpTxtLeft,$grpRow)
    $txtExternalSubnet.Size = New-Object System.Drawing.Size($txtPrefixSize)
    $txtExternalSubnet.Font = $defaultLabelFont
    $txtExternalSubnet.Name = 'txtExternalSubnet'
    $txtExternalSubnet.MaxLength = $lenIPAddress
    $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)
                }
                $txtExternalSubnet.Focus()
            }
            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)
                }
                $txtExternalSubnet.Focus()
            }
            else
            {
                $txtExternalSubnet.ForeColor = $defaultForeColor
                $null = $tabBadInput.Remove($txtExternalSubnet)
            }
            Update-TextBox $txtExternalSubnet
        }
    }
    $txtExternalSubnet.Add_Leave($txtExternalSubnet_Changed)
    $boxCloudSubnets.Controls.Add($txtExternalSubnet)

    $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'
    $boxCloudSubnets.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
    $boxCloudSubnets.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.Focus()
            $txtExternalSubnetSize.Text = $defaultExternalSubnetSize
        }
        else
        {
            $txtExternalSubnetSize.ForeColor = $defaultForeColor
        }
        Update-TextBox $txtExternalSubnetSize
    }
    $txtExternalSubnetSize.Add_Leave($txtExternalSubnetSize_Changed)

    $grpRow += 30

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

    $script:txtPrivateSubnet = New-Object System.Windows.Forms.TextBox
    $txtPrivateSubnet.Location = New-Object System.Drawing.Size($grpTxtLeft,$grpRow)
    $txtPrivateSubnet.Size = New-Object System.Drawing.Size($txtPrefixSize)
    $txtPrivateSubnet.MaxLength = $lenIPAddress
    $txtPrivateSubnet.Font = $defaultLabelFont
    $txtPrivateSubnet.Name = 'txtPrivateSubnet'
    $boxCloudSubnets.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)
                $txtPrivateSubnet.Focus()
                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)
                $txtPrivateSubnet.Focus()
                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
    $boxCloudSubnets.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
    $boxCloudSubnets.Controls.Add($txtPrivateSubnetSize)

    $row2 += 120

    # ----------------------------- PERMIT SUBNETS ----------------------------
    $grpRow = 30
    $script:boxPermitNetworks = New-Object System.Windows.Forms.GroupBox
    $boxPermitNetworks.Location = New-Object System.Drawing.Point($lblLeft2,$row2)
    $boxPermitNetworks.Size = New-Object System.Drawing.Size($grpBoxWidth,230)
    $boxPermitNetworks.Text = ($locString.lblPermitNetworksHeader + " " + $locString.strOptionalNote)
    $boxPermitNetworks.Name = $locString.lblPermitNetworksHeader
    $accName = ($locString.lblPermitNetworksHeader + " " + $locString.strOptionalNote)
    $boxPermitNetworks.AccessibleName = $accName
    $boxPermitNetworks.Visible = $true
    $boxPermitNetworks.Enabled = $true
    $tabNetwork.Controls.Add($boxPermitNetworks)

    $script:lblPermitNetworks = New-Object System.Windows.Forms.Label
    $lblPermitNetworks.Location = New-Object System.Drawing.Size($grpLblLeft,($grpRow+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
    $boxPermitNetworks.Controls.Add($lblPermitNetworks)

    $pnlPermitNetworks = New-Object System.Windows.Forms.Panel
    $pnlPermitNetworks.BorderStyle = $defaultBorderStyle
    $pnlPermitNetworks.Location = New-Object System.Drawing.Size($grpTxtLeft,$grpRow)
    $pnlPermitNetworks.Size = New-Object System.Drawing.Size($txtSizeNet)
    $script:txtPermitNetworks = New-Object System.Windows.Forms.RichTextBox
    $txtPermitNetworks.Location = New-Object System.Drawing.Size(2,2)
    $txtPermitNetworks.Size = New-Object System.Drawing.Size($txtSizeNet)
    $txtPermitNetworks.BorderStyle = [System.Windows.Forms.BorderStyle]::None
    $txtPermitNetworks.Font = $defaultLabelFont
    $txtPermitNetworks.Name = 'txtPermitNetworks'
    $accName = ($locString.lblPermitNetworksHeader + " " + $locString.strOptionalNote + " " + $locString.lblPermitNetworks)
    $pnlPermitNetworks.AccessibleName = $accName
    $txtPermitNetworks.Multiline = $false
    $script:tipPermitNetworks = New-Object System.Windows.Forms.ToolTip
    $tipPermitNetworks.ShowAlways = $false
    $tipPermitNetworks.ReshowDelay = 500
    $tipPermitNetworks.AutoPopDelay = 10000
    $tipPermitNetworks.IsBalloon = $true
    $tipPermitNetworks_Show = `
    {
        $tipPermitNetworks.RemoveAll()
        $tipPermitNetworks.Show($locString.tipPermitNetworks,$txtPermitNetworks,20,-40)
    }
    $tipPermitNetworks_Hide = `
    {
        $tipPermitNetworks.Hide($txtPermitNetworks)
        $tipPermitNetworks.RemoveAll()
    }
    $txtPermitNetworks.Add_GotFocus($tipPermitNetworks_Show)
    $txtPermitNetworks.Add_LostFocus($tipPermitNetworks_Hide)
    $txtPermitNetworks.Visible = $false
    $txtPermitNetworks.Enabled = $false
    $boxPermitNetworks.Controls.Add($pnlPermitNetworks)
    $pnlPermitNetworks.Controls.Add($txtPermitNetworks)

    $script:btnPermitNetworksAdd = New-Object System.Windows.Forms.Button
    $btnPermitNetworksAdd.Location = New-Object System.Drawing.Size(($pnlPermitNetworks.Right+4),($pnlPermitNetworks.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.Visible = $false
    $btnPermitNetworksAdd.Enabled = $false
    $btnPermitNetworksAdd.Add_Click({Add-PermitNetworks -InputTextBox $txtPermitNetworks -OutputListBox $lstPermitNetworks})
    $boxPermitNetworks.Controls.Add($btnPermitNetworksAdd)
    $script:tipPermitAdd = New-Object System.Windows.Forms.ToolTip
    $tipPermitAdd.ShowAlways = $false
    $tipPermitAdd.ReshowDelay = 500
    $tipPermitAdd.AutoPopDelay = 10000
    $tipPermitAdd.IsBalloon = $true
    $btnPermitAddTip_Show = `
    {
        $tipPermitAdd.RemoveAll()
        $tipPermitAdd.Show($locString.tipAddBtn,$btnPermitNetworksAdd,0,-40)
    }
    $btnPermitAddTip_Hide = `
    {
        $tipPermitAdd.Hide($btnPermitNetworksAdd)
        $tipPermitAdd.RemoveAll()
    }
    $btnPermitNetworksAdd.Add_GotFocus($btnPermitAddTip_Show)
    $btnPermitNetworksAdd.Add_MouseHover($btnPermitAddTip_Show)
    $btnPermitNetworksAdd.Add_LostFocus($btnPermitAddTip_Hide)
    $btnPermitNetworksAdd.Add_MouseLeave($btnPermitAddTip_Hide)

    $grpRow += 20

    $script:lstPermitNetworks = New-Object System.Windows.Forms.ListBox
    $lstPermitNetworks.Location = New-Object System.Drawing.Size($grpTxtLeft,$grpRow)
    $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 + " " + $locString.strOptionalNote)
    $lstPermitNetworks.Visible = $false
    $lstPermitNetworks.Enabled = $false
    $boxPermitNetworks.Controls.Add($lstPermitNetworks)

    $script: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.Visible = $false
    $btnPermitNetworksDel.Enabled = $false
    $btnPermitNetworksDel.Add_Click({Remove-PermitNetworks -OutputListBox $lstPermitNetworks})
    $boxPermitNetworks.Controls.Add($btnPermitNetworksDel)
    $script:tipPermitDel = New-Object System.Windows.Forms.ToolTip
    $tipPermitDel.ShowAlways = $false
    $tipPermitDel.ReshowDelay = 500
    $tipPermitDel.AutoPopDelay = 10000
    $tipPermitDel.IsBalloon = $true
    $btnPermitDelTip_Show = `
    {
        $tipPermitDel.RemoveAll()
        $tipPermitDel.Show($locString.tipDelBtn,$btnPermitNetworksDel,0,-40)
    }
    $btnPermitDelTip_Hide = `
    {
        $tipPermitDel.Hide($btnPermitNetworksDel)
        $tipPermitDel.RemoveAll()
    }
    $btnPermitNetworksDel.Add_MouseHover($btnPermitDelTip_Show)
    $btnPermitNetworksDel.Add_MouseLeave($btnPermitDelTip_Hide)
    $btnPermitNetworksDel.Add_GotFocus($btnPermitDelTip_Show)
    $btnPermitNetworksDel.Add_LostFocus($btnPermitDelTip_Hide)

    Enable-PermitNetworks

    $grpRow += $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(',,','')
            $InputTextBox.Text = $InputTextBox.Text.TrimEnd(',')
            $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($errList.Count + $dupeList.Count -eq 0)
            {
                $InputTextBox.SelectionStart = 0
                $InputTextBox.SelectionLength = 0
            }
            if($msgList.Count -gt 0)
            {
                #Close-WaitForm
                [System.Windows.Forms.MessageBox]::Show($msgList, $locString.capValidationError, [System.Windows.Forms.MessageBoxButtons]::OK)
                $InputTextBox.SelectAll()
                $InputTextBox.Focus()
            }
            $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.AccessibleName = $locString.tabBorder
    $lblBorderSettings.AccessibleDescription = $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 --------------------------
    $grpRow = 30
    $script:boxBorderConnect = New-Object System.Windows.Forms.GroupBox
    $boxBorderConnect.Location = New-Object System.Drawing.Point($lblLeft,$tabRow)
    $boxBorderConnect.Size = New-Object System.Drawing.Size($grpBoxWidth,220)
    $boxBorderConnect.Text = $locString.lblBorderConnect
    $boxBorderConnect.Name = $locString.lblBorderConnect
    $boxBorderConnect.AccessibleName = ($locString.tabBorder + " " + $locString.lblBorderConnect)
    $boxBorderConnect.Visible = $true
    $tabBorder.Controls.Add($boxBorderConnect)

    # --------------------------- ROUTING METHOD ------------------------------
    $script:lblRoutingMethod = New-Object System.Windows.Forms.Label
    $lblRoutingMethod.Location = New-Object System.Drawing.Size($lblLeft,($grpRow+2))
    $lblRoutingMethod.MaximumSize = New-Object System.Drawing.Size($lblMaxSize)
    $lblRoutingMethod.AutoSize = $true
    $lblRoutingMethod.Font = $defaultLabelFont
    $lblRoutingMethod.Text = $locString.lblRoutingMethod
    $boxBorderConnect.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,$grpRow)
    $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
    $boxBorderConnect.Controls.Add($lstRoutingMethod)
    $lstRoutingMethod_Changed = `
    {
        switch ($lstRoutingMethod.SelectedItem)
        {
            "Static Routing"
            {
                $script:isStaticRouting = $true
                (1..[int]$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..[int]$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..[int]$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)

    $grpRow += 40

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

    $script:txtBorderCount = New-Object System.Windows.Forms.TextBox
    $txtBorderCount.Location = New-Object System.Drawing.Size($txtLeft,$grpRow)
    $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
    $boxBorderConnect.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
                $txtBorderCount.Focus()
                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)

    $grpRow += 30

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

    $script:txtBorderBGP1 = New-Object System.Windows.Forms.TextBox
    $txtBorderBGP1.Location = New-Object System.Drawing.Size($txtLeft,$grpRow)
    $txtBorderBGP1.Size = New-Object System.Drawing.Size($txtPrefixSize)
    $txtBorderBGP1.Font = $defaultLabelFont
    $txtBorderBGP1.Name = 'txtBorderBGP1'
    $txtBorderBGP1.TextAlign = [System.Windows.Forms.HorizontalAlignment]::Center
    $txtBorderBGP1.MaxLength = 10
    $boxBorderConnect.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)
                $txtBorderBGP1.Focus()
                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)

    $grpRow += 25

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

    $script:txtBorderBGP2 = New-Object System.Windows.Forms.TextBox
    $txtBorderBGP2.Location = New-Object System.Drawing.Size($txtLeft,$grpRow)
    $txtBorderBGP2.Size = New-Object System.Drawing.Size($txtPrefixSize)
    $txtBorderBGP2.Font = $defaultLabelFont
    $txtBorderBGP2.Name = 'txtBorderBGP2'
    $txtBorderBGP2.TextAlign = [System.Windows.Forms.HorizontalAlignment]::Center
    $txtBorderBGP2.MaxLength = 10
    $boxBorderConnect.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)
                $txtBorderBGP2.Focus()
                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)

    $grpRow += 25

    # --------------------------- BORDER 3 BGP ASN ----------------------------
    $script:lblBorderBGP3 = New-Object System.Windows.Forms.Label
    $lblBorderBGP3.Name = 'lblBorderBGP3'
    $lblBorderBGP3.Location = New-Object System.Drawing.Size($lblLeft,($grpRow+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
    $boxBorderConnect.Controls.Add($lblBorderBGP3)

    $script:txtBorderBGP3 = New-Object System.Windows.Forms.TextBox
    $txtBorderBGP3.Location = New-Object System.Drawing.Size($txtLeft,$grpRow)
    $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
    $boxBorderConnect.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)
                $txtBorderBGP3.Focus()
                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)

    $grpRow += 25

    # --------------------------- BORDER 4 BGP ASN ----------------------------
    $script:lblBorderBGP4 = New-Object System.Windows.Forms.Label
    $lblBorderBGP4.Name = 'lblBorderBGP4'
    $lblBorderBGP4.Location = New-Object System.Drawing.Size($lblLeft,($grpRow+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
    $boxBorderConnect.Controls.Add($lblBorderBGP4)

    $script:txtBorderBGP4 = New-Object System.Windows.Forms.TextBox
    $txtBorderBGP4.Location = New-Object System.Drawing.Size($txtLeft,$grpRow)
    $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
    $boxBorderConnect.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)
                $txtBorderBGP4.Focus()
                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 += 240

    # ----------------------------- 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)
                $txtEdgeBGP.Focus()
                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 --------------------------------
    $grpRow = 30
    $script:boxBorderUplinks = New-Object System.Windows.Forms.GroupBox
    $boxBorderUplinks.Location = New-Object System.Drawing.Point($lblLeft,$tabRow)
    $boxBorderUplinks.Size = New-Object System.Drawing.Size($grpBoxWidth,80)
    $boxBorderUplinks.Text = $locString.lblUplinks
    $boxBorderUplinks.Name = $locString.lblUplinks
    $boxBorderUplinks.AccessibleName = ($locString.tabBorder + " " + $locString.lblUplinks)
    $boxBorderUplinks.Visible = $false
    $tabBorder.Controls.Add($boxBorderUplinks)

    $tabRow += 30

    # ----------------------------- UPLINK SPEED ------------------------------
    $script:lblUplinkSpeed = New-Object System.Windows.Forms.Label
    $lblUplinkSpeed.Location = New-Object System.Drawing.Size($lblLeft,($grpRow+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
    $boxBorderUplinks.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,$grpRow)
    $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
    $boxBorderUplinks.Controls.Add($lstUplinkSpeed)

    $lstSwitchVendor.SelectedIndex = 0
    Invoke-Command -ScriptBlock $lstSwitchVendor_Changed
    $script:prevSwitchVendor = ""
    $lstSwitchVendor.SelectedIndex = -1
    $lstTORFirmware.SelectedIndex = -1
    $lstTORModel.SelectedIndex = -1
    $lstBMCFirmware.SelectedIndex = -1
    $lstBMCModel.SelectedIndex = -1
    $lstSpineFirmware.SelectedIndex = -1
    $lstSpineModel.SelectedIndex = -1

    $script:groupBoxList = @(
        $boxAzureIdentity,
        $boxCustomerInfo,
        $boxEnvironmentInfo,
        $boxSwitchInfo,
        $boxCloudSubnets,
        $boxPermitNetworks,
        $boxBorderConnect,
        $boxBorderUplinks
    )

    # ============================= SHOW THE FORM =============================
    if($EnableExperimental)
    {
        #$menuScenario.DropDownItems.AddRange(@($menuItemCustomer,(New-Object System.Windows.Forms.ToolStripSeparator),$menuItemPreviewMultiSU,$menuItemPreviewAddSU,$menuItemExtStor))
        #Enable-PermitNetworks
    }
    else
    {
        $menuScenario.DropDownItems.AddRange(@($menuItemCustomer,$menuItemExtStor))
    }
    $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.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.Size = New-Object System.Drawing.Size(($frmMain.Size.Width-60),($frmMain.Size.Height-150))
    $dgvSubnetSummary.ReadOnly = $ReadOnly
    $dgvSubnetSummary.Text = $locString.tabSubnetSummary
    $dgvSubnetSummary.ScrollBars = 'Both'
    $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.Frozen = $false
    #$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.Frozen = $false
    #$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.Frozen = $false
    #$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.Frozen = $false
    #$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.Frozen = $false
    #$col05.SortMode = [System.Windows.Forms.DataGridViewColumnSortMode]::NotSortable

    $col06 = New-Object -TypeName System.Windows.Forms.DataGridViewTextBoxColumn
    $col06.Name = "SubnetName"
    $col06.HeaderText = "Subnet 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.Frozen = $false
    #$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.Frozen = $false
    #$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.Frozen = $false
    #$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.Frozen = $false
    #$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.Frozen = $false
    #$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.Frozen = $false
    #$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.Frozen = $false
    #$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.Frozen = $false
    #$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.Frozen = $false
    #$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.Frozen = $false
    #$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)

    $dgvSubnetSummary.Add_GotFocus({
        param($sender, $EventArgs)
        $sender.ClearSelection()
        $sender.Rows[$sender.CurrentCell.RowIndex].Cells[$sender.CurrentCell.ColumnIndex].Selected = $true
    })

    $tabControl.Controls.Add($tabSubnetSummary)
}

function Clear-SubnetSummarySelectedCells
{
    $dgvSubnetSummary.ClearSelection()
}

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.AccessibleName = 'tabIPAssignments'
    $tabIPAssignments.Text = $locString.tabIPAssignments
    $tabIPAssignments.BackColor = $summaryBackColor
    $tabIPAssignments.ForeColor = $summaryForeColor
    $tabIPAssignments.AutoScroll = $false
    $tabControl.Controls.Add($tabIPAssignments)
}

function New-IPAssignmentTable
{
    param
    (
        [Parameter(Mandatory=$true)]
        [string]$Name,
        [Parameter(Mandatory=$true)]
        [int]$Number,
        [Parameter(Mandatory=$false)]
        [bool]$ReadOnly = $true
    )
    Write-Log "New-IPAssignmentTable - NAME : $($Name) [$($Number)]"
    $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.AccessibleName = $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.ScrollBars = [System.Windows.Forms.ScrollBars]::None
    $dgvIPAssignments.Location = New-Object System.Drawing.Size(10,10)

    $col01 = New-Object -TypeName System.Windows.Forms.DataGridViewTextBoxColumn
    $col01.Name = "Table$($Number)_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.Frozen = $false
    #$col01.SortMode = [System.Windows.Forms.DataGridViewColumnSortMode]::NotSortable

    $col02 = New-Object -TypeName System.Windows.Forms.DataGridViewTextBoxColumn
    $col02.Name = "Table$($Number)_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.Frozen = $false
    #$col02.SortMode = [System.Windows.Forms.DataGridViewColumnSortMode]::NotSortable

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

    $dgvIPAssignments.Add_GotFocus({
        param($sender, $EventArgs)
        $sender.ClearSelection()
        $sender.Rows[$sender.CurrentCell.RowIndex].Cells[$sender.CurrentCell.ColumnIndex].Selected = $true
    })
    $dgvIPAssignments.Add_LostFocus({
        param($sender, $EventArgs)
        $sender.ClearSelection()
    })

    return $dgvIPAssignments
}

function Clear-IpAssignmentsSelectedCells
{
    foreach ($netName in $ipAssignmentObjects.Keys)
    {
        $ipAssignmentObjects.$netName.Table.ClearSelection()
    }
}

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.CurrentCell = $dgvSubnetSummary.Rows[0].Cells[0]
        $dgvSubnetSummary.Rows[0].Cells[0].Selected = $true
    }
    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

        $script:ipSummaryPanel = New-Object System.Windows.Forms.Panel
        $ipSummaryPanel.BorderStyle = 'None'
        $ipSummaryPanel.Location = New-Object System.Drawing.Size(10,10)
        $ipSummaryPanel.Name = "IPTable$($count)"
        $script:ipSummaryWidth = ($frmMain.Size.Width-60)
        $ipSummaryPanel.Size = New-Object System.Drawing.Size($ipSummaryWidth,($frmMain.Size.Height-150))
        $ipSummaryPanel.AutoScroll = $true
        $tabIPAssignments.Controls.Add($ipSummaryPanel)

        Write-Log -Message "$($MyInvocation.MyCommand.Name) - write values to '$($name)'"
        $thisRow = 0
        $boxHeight = 250
        $boxWidth = 400
        $netCount = 0
        $script:ipAssignmentObjects = @{}
        $script:firstTable = ""
        foreach($network in $activeStamp.Supernets.Networks)
        {
            $netCount++
            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")
                }
                $thisDGV = New-IPAssignmentTable -Name $netName -Number $netCount
                if([string]::IsNullOrEmpty($firstTable))
                {
                    $script:firstTable = $netName
                }
                $thisRow += 20
                $thisBox = New-Object System.Windows.Forms.GroupBox
                $thisBox.Location = New-Object System.Drawing.Point($lblLeft,$thisRow)
                $thisBox.Size = New-Object System.Drawing.Size($boxWidth,$boxHeight)
                $thisBox.Text = $netName
                $thisBox.Name = "Group$($netCount)"
                $thisBox.Visible = $true
                $ipAssignmentObjects.Add($netName,@{Table=$thisDGV;Box=$thisBox})

                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(20,30)
                if($ipAssignmentObjects.$netName.Table.RowCount -le 8)
                {
                    $ipAssignmentObjects.$netName.Table.Size = (Get-DataGridViewSize $ipAssignmentObjects.$netName.Table)
                }
                else
                {
                    $rowHeight = $ipAssignmentObjects.$netName.Table.Rows.GetRowsHeight([System.Windows.Forms.DataGridViewElementStates]::Visible)
                    if($rowHeight -gt 420)
                    {
                        $rowHeight = 420
                    }
                    $ipAssignmentObjects.$netName.Table.Size = New-Object System.Drawing.Size(($thisWidth+4),$rowHeight)
                }
                $ipAssignmentObjects.$netName.Table.ScrollBars = [System.Windows.Forms.ScrollBars]::None
                $thisHeight = $ipAssignmentObjects.$netName.Table.Size.Height
                $thisWidth = $ipAssignmentObjects.$netName.Table.Columns.GetColumnsWidth([System.Windows.Forms.DataGridViewElementStates]::Visible)
                $thisBox.Size = New-Object System.Drawing.Size($boxWidth,($thisHeight+40))
                $thisBox.Controls.Add($ipAssignmentObjects.$netName.Table)
                $ipSummaryPanel.Controls.Add($thisBox)
                $thisRow += $thisHeight+40
            }
        }
        $script:ipSummaryHeight = $thisRow-40
    }
    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($true -eq $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")
                    {
                        $script:isNoBMC = $true
                        $activeStamp.IsNoBMC = $true
                        if(($rack.Index -gt 1) -and ($switchMake -eq "CiscoUCS"))
                        {
                            $skipThis = $true
                        }
                    }
                    if(-not $skipThis)
                    {
                        if($true -eq $isNoBMC)
                        {
                            Write-Log -Message "$($MyInvocation.MyCommand.Name) - $($rack.Name) set IsNoBMC to True"
                            $activeStamp.IsNoBMC = $true
                        }
                        else
                        {
                            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
                            $bmc.Model = $lstBMCModel.SelectedItem
                            $bmc.FIRMWAREVERSION = $lstBMCFirmware.SelectedItem
                            $bmc.Asn = $scaleUnitTabs.$suTab.Objects.txtTORASN.Text
                        }
                        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
                        $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')
        {
            Write-Log -Message "$($MyInvocation.MyCommand.Name) - set EDGE switch values"
            $edge = @($activeStamp.Switches | Where-Object Hostname -like ("*-Edge-Edge-*"))
            foreach($sw in $edge)
            {
                Write-Log -Message "$($MyInvocation.MyCommand.Name) - $($sw.Hostname) : set Make = $($edgeMake)"
                Write-Log -Message "$($MyInvocation.MyCommand.Name) - $($sw.Hostname) : set Model = $($edgeModel)"
                Write-Log -Message "$($MyInvocation.MyCommand.Name) - $($sw.Hostname) : set Firmware = $($edgeFirmware)"
                $sw.Make = $edgeMake
                $sw.Model = $edgeModel
                $sw.FIRMWAREVERSION = $edgeFirmware
                if(-not $isStaticRouting)
                {
                    Write-Log -Message "$($MyInvocation.MyCommand.Name) - $($sw.Hostname) : set BGP ASN = '$($txtEdgeBGP.Text)'"
                    $sw.Asn = $txtEdgeBGP.Text
                }
            }
        }
        if($true -eq $isExtStor)
        {
            Write-Log -Message "$($MyInvocation.MyCommand.Name) - set BACKEND switch values"
            $back = @($activeStamp.Switches | Where-Object Hostname -like ("*-BACKEND-*"))
            foreach($sw in $back)
            {
                Write-Log -Message "$($MyInvocation.MyCommand.Name) - $($sw.Hostname) : set Make = $($backMake)"
                Write-Log -Message "$($MyInvocation.MyCommand.Name) - $($sw.Hostname) : set Model = $($backModel)"
                Write-Log -Message "$($MyInvocation.MyCommand.Name) - $($sw.Hostname) : set Firmware = $($backFirmware)"
                $sw.Make = $backMake
                $sw.Model = $backModel
                $sw.FIRMWAREVERSION = $backFirmware
            }
        }
        if($true -eq $chkDataBoundary.Checked)
        {
            $activeStamp.Clouds[0].IsEuDataResidency = $true
        }
        else
        {
            $activeStamp.Clouds[0].IsEuDataResidency = $false
        }
        # 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)
        [bool]$isValid = $isValid -and (Invoke-SupplimentalValidation)
        if($true -eq $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
        {
            # Display validation errors message box
            Write-Log -Message "$($MyInvocation.MyCommand.Name) - WARNING: deployment data failed validation" -Force
            $errList = @($errorText.Value.ToString().Split("`n"))
            [string[]]$message = @(Get-ValidationError -ErrorList $errList)
            if($message.Count -gt 4)
            {
                [string[]]$message = $locString.msgMultiValidFail
                [string[]]$message += $locString.msgFixValidationErrors
            }
            else
            {
                $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"]
                }
                Start-Sleep -Seconds 3
                $script:prevSwitchVendor
                Invoke-Command -ScriptBlock $lstSwitchVendor_Changed
                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([int]$maxSU -eq 1)
            {
                Write-Log -Message "$($MyInvocation.MyCommand.Name) - WARNING: Multi-ScaleUnit is not supported in this version" -Force
            }
            elseif(-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"
            Enable-PermitNetworks
            OnClick_menuItemExtStor
        }
        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)'"
                            $script:openUplinkSpeed = $linkSpeed
                            if($lstUplinkSpeed.Items.Contains($linkSpeed))
                            {
                                $lstUplinkSpeed.SelectedItem = $linkSpeed
                            }
                            else
                            {
                                Write-Log -Message "$($MyInvocation.MyCommand.Name) - WARNING: '$($linkSpeed)' is not a valid '$($property)' for this device" -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
            if([int]$suCount -gt [int]$maxSU)
            {
                Write-Log  -Message "$($MyInvocation.MyCommand.Name) - WARNING: Forcing SU count to '$($maxSU)' as the imported value is not supported" -Force
                [string]$suCount = $maxSU
            }
            $txtScaleUnitCount.Text = $suCount
            Invoke-Command -ScriptBlock $txtScaleUnitCount_Changed
            if([int]$suCount -gt 1)
            {
                $txtScaleUnitCount.Enabled = $true
                $txtScaleUnitCount.ReadOnly = $false
            }
            $chkDataBoundary.Checked = $false
            if($true -eq [System.Convert]::ToBoolean($cloud.IsEuDataResidency))
            {
                $chkDataBoundary.Checked = $true
            }
        }
        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.SelectedIndex = $lstSwitchVendor.FindStringExact($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
        $validTOR = Get-SwitchModels -Make $switchMake -Type 'TOR'
        $validTOR | Foreach-Object `
        {
            if(-not $lstTORModel.Items.Contains($PSItem))
            {
                [void]$lstTORModel.Items.Add($PSItem)
            }
        }
        $validBMC = Get-SwitchModels -Make $switchMake -Type 'BMC'
        $validBMC | Foreach-Object `
        {
            if(-not $lstBMCModel.Items.Contains($PSItem))
            {
                [void]$lstBMCModel.Items.Add($PSItem)
            }
        }
        if($switchMake -ne "CiscoUCS")
        {
            Clear-ListSelection $lstBMCModel
            Clear-ListSelection $lstBMCFirmware
            if($switchMake -ne "Mellanox")
            {
                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) - set 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) - set 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")
            {
                Write-Log -Message "$($MyInvocation.MyCommand.Name) - process: BMC Switch settings"
                if($bmcObj.Count -gt 0)
                {
                    if($true -eq $activeStamp.IsNoBmc)
                    {
                        Write-Log -Message "$($MyInvocation.MyCommand.Name) - set BMC Model: -none-"
                        if($true -eq $lstBMCModel.Items.Contains('-none-'))
                        {
                            $lstBMCModel.SelectedIndex = $lstBMCModel.FindStringExact('-none-')
                        }
                        else
                        {
                            if(($switchMake -eq "Mellanox") -and ($lstBMCModel.Items.Count -eq 0))
                            {
                                Write-Log -Message "$($MyInvocation.MyCommand.Name) - WARNING: This Mellanox TOR does not have the SupportNoBMC option set" -Force
                                if(-not $lstBMCModel.Items.Contains('-none-'))
                                {
                                    $null = $lstBMCModel.Items.Add('-none-')
                                }
                                $lstBMCModel.SelectedIndex = $lstBMCModel.FindStringExact('-none-')
                                Disable-ComboBoxList -Control $lstBMCModel -Label $lblBMCModel
                                Disable-ComboBoxList -Control $lstBMCFirmware -Label $lblBMCFirmware
                            }
                            else
                            {
                                Write-Log -Message "$($MyInvocation.MyCommand.Name) - WARNING: The selected TOR model does not support the NoBMC option" -Force
                                $lstBMCModel.SelectedIndex = -1
                            }
                        }
                    }
                    else
                    {
                        Write-Log -Message "$($MyInvocation.MyCommand.Name) - set BMC Model: $($bmcObj[0].Model)"
                        $validBMC = Get-SwitchModels -Make $switchMake -Type 'BMC'
                        if(($bmcObj.Count -gt 0) -and ($null -ne $bmcObj[0].Model))
                        {
                            Write-Log -Message "$($MyInvocation.MyCommand.Name) - set 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) - set 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
                        }
                    }
                }
                else
                {
                    Write-Log -Message "$($MyInvocation.MyCommand.Name) - WARNING: No BMC switches could be found in the deployment object" -Force
                    $invalidSwitchImportData += "BMC Model: null"
                    Clear-ListSelection $lstBMCModel
                }
            }
            if(($lstSwitchVendor.SelectedItem -eq "CiscoUCS") -or (($lstSwitchVendor.SelectedItem -eq "Mellanox") -and ($lstBMCModel.Item.Count -eq 0)))
            {
                Disable-ComboBoxList -Control $lstBMCModel -Label $lblBMCModel
                Disable-ComboBoxList -Control $lstBMCFirmware -Label $lblBMCFirmware
            }
        }
        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))
                {
                    Write-Log -Message "$($MyInvocation.MyCommand.Name) - set SPINE Model: $($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)"
            if([int]($su.Index) -gt [int]$maxSU)
            {
                Write-Log -Message "$($MyInvocation.MyCommand.Name) - skipping: Scale-Unit '$($suID)'"
            }
            else
            {
                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
                }
            }
        }
        $script:arrUplinkSpeed = @()
        $script:arrUplinkSpeed += Get-UplinkSpeedList -SpecificMake $lstSwitchVendor.SelectedItem -SpecificModel $lstTORModel.SelectedItem
        if ($arrUplinkSpeed.Count -gt 0)
        {
            Invoke-Command -ScriptBlock $UpdateUplinkSpeedItems
            Enable-UplinkSpeed
        }
        if((-not [string]::IsNullOrEmpty($script:openUplinkSpeed)) -and ($true -eq $lstUplinkSpeed.Enabled))
        {
            Write-Log -Message "$($MyInvocation.MyCommand.Name) - set 'UplinkSpeed' to '$($script:openUplinkSpeed)'"
            if($lstUplinkSpeed.Items -contains $script:openUplinkSpeed)
            {
                $lstUplinkSpeed.SelectedItem = $script:openUplinkSpeed
            }
            else
            {
                Write-Log -Message "$($MyInvocation.MyCommand.Name) - WARNING: '$($script:openUplinkSpeed)' is not a valid 'UplinkSpeed' for this device" -Force
            }
        }
        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
    {
        $order1 = @()
        $order2 = @()
        $order3 = @()
        $order4 = @()
        if($openData.$TabName)
        {
            ($openData.$TabName | Get-Member -Type NoteProperty).Name | ForEach-Object `
            {
                if($PSItem -eq "pnlDNSForwarder") {
                    $order1 += "txtDNSForwarder"
                    continue
                }
                if($PSItem -eq "lstSwitchVendor") {
                    $order1 += $PSItem
                }
                elseif($PSItem -eq "lstTORModel") {
                    $order2 += $PSItem
                }
                elseif(($PSItem -like "*Model") -or ($PSItem -like "txtBorderBGP*"))
                {
                    $order3 += $PSItem
                }
                elseif($PSItem -like "*Firmware")
                {
                    $order4 += $PSItem
                }
                else
                {
                    $order2 += $PSItem
                }
            }
            $orderList += $order1
            $orderList += $order2
            $orderList += $order3
            $orderList += $order4
        }
    }
    # Special case for Switch Vendor to support IsNoBMC
    $script:openTORModel = $openData.'tabNetwork'.'lstTORModel'
    foreach($item in $orderList)
    {
        if($openData.$TabName.$item)
        {
            Write-Log -Message "$($MyInvocation.MyCommand.Name) - set control named '$($item)' to '$($openData.$TabName.$item)'"
            # Special case for controls that have been moved inside of GroupBox controls
            $thisItem = $null
            if($null -eq $thisTab.Controls[$item])
            {
                foreach($thisGroupBox in $groupBoxList)
                {
                    if($null -ne $thisGroupBox.Controls[$item])
                    {
                        $thisItem = $thisGroupBox.Controls[$item]
                        break
                    }
                }
            }
            else
            {
                $thisItem = $thisTab.Controls[$item]
            }
            # Special case for Mellanox and NoBMC
            if(($item -eq "lstBMCModel") -and ($openData.$TabName.$item -eq '-none-') -and ($lstSwitchVendor.SelectedItem -eq "Mellanox"))
            {
                if($false -eq $lstBMCModel.Items.Contains('-none-'))
                {
                    $lstBMCModel.Items.Add('-none-')
                }
                $thisItem.SelectedIndex = $thisItem.FindStringExact($openData.$TabName.$item)
                continue
            }
            # Special case ListBox items
            if(($item -like "lstPermitNetworks*") -and ($thisItem))
            {
                ($openData.$TabName.$item).Split(',') | ForEach-Object `
                {
                    [void]$thisItem.Items.Add($PSItem)
                }
                continue
            }
            # Special case for DNSForwarder
            if($item -eq "txtDNSForwarder")
            {
                $txtDNSForwarder.Text = $openData.$TabName.$item
                $validationEvent = Get-Variable -Name ('txtDNSForwarder_Changed') -ErrorAction SilentlyContinue
                if($validationEvent)
                {
                    Invoke-Command -ScriptBlock $validationEvent.Value
                }
                continue
            }
            # Special case for SU count greater than supported
            if($item -eq "txtScaleUnitCount")
            {
                $val = $openData.$TabName.$item
                if([int]$val -gt [int]$maxSU)
                {
                    Write-Log  -Message "$($MyInvocation.MyCommand.Name) - WARNING: Forcing SU count to '$($maxSU)' as the imported value is not supported" -Force
                    $val = $maxSU
                }
                $txtScaleUnitCount.Text = $val
                $validationEvent = Get-Variable -Name ('txtScaleUnitCount_Changed') -ErrorAction SilentlyContinue
                if($validationEvent)
                {
                    Invoke-Command -ScriptBlock $validationEvent.Value
                }
                continue
            }
            # Skip SPINE setting if SU count is 1
            if(($item -like "lstSpine*") -and ($txtScaleUnitCount.Text -eq '1'))
            {
                Write-Log  -Message "$($MyInvocation.MyCommand.Name) - Skipping SPINE switch settings as SU count is '1'" -Force
                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(($thisItem -is [System.Windows.Forms.TextBox]) -or ($thisItem -is [System.Windows.Forms.RichTextBox]))
            {
                $thisItem.Text = $openData.$TabName.$item
                $validationEvent = Get-Variable -Name ($item + '_Changed') -ErrorAction SilentlyContinue
                if($validationEvent)
                {
                    Invoke-Command -ScriptBlock $validationEvent.Value
                }
            }
            elseif($thisItem -is [System.Windows.Forms.ComboBox])
            {
                if($thisItem.Items -contains $openData.$TabName.$item)
                {
                    $thisItem.SelectedIndex = $thisItem.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
                        $validTOR = Get-SwitchModels -Make $lstSwitchVendor.SelectedItem -Type 'TOR'
                        $validTOR | Foreach-Object `
                        {
                            if(-not $lstTORModel.Items.Contains($PSItem))
                            {
                                [void]$lstTORModel.Items.Add($PSItem)
                            }
                        }
                        $validBMC = Get-SwitchModels -Make $lstSwitchVendor.SelectedItem -Type 'BMC'
                        $validBMC | Foreach-Object `
                        {
                            if(-not $lstBMCModel.Items.Contains($PSItem))
                            {
                                [void]$lstBMCModel.Items.Add($PSItem)
                            }
                        }
                        # TODO - Update SPINE list when MSU support is added
                    }
                    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 $thisItem
                    $script:invalidOpenData += $item
                }
            }
            elseif($thisItem -is [System.Windows.Forms.Checkbox])
            {
                $thisItem.Checked = [System.Convert]::ToBoolean($openData.$TabName.$item)
            }
            else
            {
                Write-Log -Message "$($MyInvocation.MyCommand.Name) - WARNING: '$($item)' does not exist on the tab named '$($TabName)'" -Force
                $script:invalidOpenData += $item
            }
        }
    }
    $script:openTORModel = $null
    if(($lstSwitchVendor.SelectedItem -eq "Mellanox")  -and ($lstBMCModel.Item.Count -eq 0))
    {
        Disable-ComboBoxList -Control $lstBMCModel -Label $lblBMCModel
        Disable-ComboBoxList -Control $lstBMCFirmware -Label $lblBMCFirmware
    }
    Write-Log -Message "$($MyInvocation.MyCommand.Name) - completed"
}

function Invoke-SupplimentalValidation
{
    param (
        [switch]$IgnoreNonCritical
    )

    $validationPassed = $true
    if($true -eq $lstUplinkSpeed.Enabled)
    {
        if($null -eq $lstUplinkSpeed.SelectedItem)
        {
            $validationPassed = $false
            [string]$thisErr = $locString.msgValidationEmpty -f $locString.lblUplinkSpeed.Replace(':','')
            [void]$ref.Insert(0,"$($thisErr)`n")
        }
    }
    if($lstIdentityStore.SelectedIndex -eq -1)
    {
        $validationPassed = $false
        [string]$thisErr = $locString.msgValidationEmpty -f $locString.lblIdentityStore.Replace(':','')
        [void]$ref.Insert(0,"$($thisErr)`n")
    }
    if(-not $IgnoreNonCritical)
    {
        if($true -eq $lstTORFirmware.Enabled)
        {
            if($null -eq $lstTORFirmware.SelectedItem)
            {
                $validationPassed = $false
                [string]$thisErr = $locString.msgValidationEmpty -f $locString.lblTORFirmware.Replace(':','')
                [void]$ref.Insert(0,"$($thisErr)`n")
            }
        }
        if($true -eq $lstBMCFirmware.Enabled)
        {
            if($null -eq $lstBMCFirmware.SelectedItem)
            {
                $validationPassed = $false
                [string]$thisErr = $locString.msgValidationEmpty -f $locString.lblBMCFirmware.Replace(':','')
                [void]$ref.Insert(0,"$($thisErr)`n")
            }
        }
    }
    return $validationPassed
}

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)
        [bool]$isValid = $isValid -and (Invoke-SupplimentalValidation -IgnoreNonCritical)
        if($true -eq $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
# MIInlAYJKoZIhvcNAQcCoIInhTCCJ4ECAQExDzANBglghkgBZQMEAgEFADB5Bgor
# BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG
# KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCC9KiDEP0MO7OgO
# Ej7cF6e3mnMr/JQOQJDpKDhNtK0n76CCDXYwggX0MIID3KADAgECAhMzAAADTrU8
# esGEb+srAAAAAANOMA0GCSqGSIb3DQEBCwUAMH4xCzAJBgNVBAYTAlVTMRMwEQYD
# VQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNy
# b3NvZnQgQ29ycG9yYXRpb24xKDAmBgNVBAMTH01pY3Jvc29mdCBDb2RlIFNpZ25p
# bmcgUENBIDIwMTEwHhcNMjMwMzE2MTg0MzI5WhcNMjQwMzE0MTg0MzI5WjB0MQsw
# CQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9u
# ZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMR4wHAYDVQQDExVNaWNy
# b3NvZnQgQ29ycG9yYXRpb24wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB
# AQDdCKiNI6IBFWuvJUmf6WdOJqZmIwYs5G7AJD5UbcL6tsC+EBPDbr36pFGo1bsU
# p53nRyFYnncoMg8FK0d8jLlw0lgexDDr7gicf2zOBFWqfv/nSLwzJFNP5W03DF/1
# 1oZ12rSFqGlm+O46cRjTDFBpMRCZZGddZlRBjivby0eI1VgTD1TvAdfBYQe82fhm
# WQkYR/lWmAK+vW/1+bO7jHaxXTNCxLIBW07F8PBjUcwFxxyfbe2mHB4h1L4U0Ofa
# +HX/aREQ7SqYZz59sXM2ySOfvYyIjnqSO80NGBaz5DvzIG88J0+BNhOu2jl6Dfcq
# jYQs1H/PMSQIK6E7lXDXSpXzAgMBAAGjggFzMIIBbzAfBgNVHSUEGDAWBgorBgEE
# AYI3TAgBBggrBgEFBQcDAzAdBgNVHQ4EFgQUnMc7Zn/ukKBsBiWkwdNfsN5pdwAw
# RQYDVR0RBD4wPKQ6MDgxHjAcBgNVBAsTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEW
# MBQGA1UEBRMNMjMwMDEyKzUwMDUxNjAfBgNVHSMEGDAWgBRIbmTlUAXTgqoXNzci
# tW2oynUClTBUBgNVHR8ETTBLMEmgR6BFhkNodHRwOi8vd3d3Lm1pY3Jvc29mdC5j
# b20vcGtpb3BzL2NybC9NaWNDb2RTaWdQQ0EyMDExXzIwMTEtMDctMDguY3JsMGEG
# CCsGAQUFBwEBBFUwUzBRBggrBgEFBQcwAoZFaHR0cDovL3d3dy5taWNyb3NvZnQu
# Y29tL3BraW9wcy9jZXJ0cy9NaWNDb2RTaWdQQ0EyMDExXzIwMTEtMDctMDguY3J0
# MAwGA1UdEwEB/wQCMAAwDQYJKoZIhvcNAQELBQADggIBAD21v9pHoLdBSNlFAjmk
# mx4XxOZAPsVxxXbDyQv1+kGDe9XpgBnT1lXnx7JDpFMKBwAyIwdInmvhK9pGBa31
# TyeL3p7R2s0L8SABPPRJHAEk4NHpBXxHjm4TKjezAbSqqbgsy10Y7KApy+9UrKa2
# kGmsuASsk95PVm5vem7OmTs42vm0BJUU+JPQLg8Y/sdj3TtSfLYYZAaJwTAIgi7d
# hzn5hatLo7Dhz+4T+MrFd+6LUa2U3zr97QwzDthx+RP9/RZnur4inzSQsG5DCVIM
# pA1l2NWEA3KAca0tI2l6hQNYsaKL1kefdfHCrPxEry8onJjyGGv9YKoLv6AOO7Oh
# JEmbQlz/xksYG2N/JSOJ+QqYpGTEuYFYVWain7He6jgb41JbpOGKDdE/b+V2q/gX
# UgFe2gdwTpCDsvh8SMRoq1/BNXcr7iTAU38Vgr83iVtPYmFhZOVM0ULp/kKTVoir
# IpP2KCxT4OekOctt8grYnhJ16QMjmMv5o53hjNFXOxigkQWYzUO+6w50g0FAeFa8
# 5ugCCB6lXEk21FFB1FdIHpjSQf+LP/W2OV/HfhC3uTPgKbRtXo83TZYEudooyZ/A
# Vu08sibZ3MkGOJORLERNwKm2G7oqdOv4Qj8Z0JrGgMzj46NFKAxkLSpE5oHQYP1H
# tPx1lPfD7iNSbJsP6LiUHXH1MIIHejCCBWKgAwIBAgIKYQ6Q0gAAAAAAAzANBgkq
# hkiG9w0BAQsFADCBiDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24x
# EDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlv
# bjEyMDAGA1UEAxMpTWljcm9zb2Z0IFJvb3QgQ2VydGlmaWNhdGUgQXV0aG9yaXR5
# IDIwMTEwHhcNMTEwNzA4MjA1OTA5WhcNMjYwNzA4MjEwOTA5WjB+MQswCQYDVQQG
# EwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwG
# A1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSgwJgYDVQQDEx9NaWNyb3NvZnQg
# Q29kZSBTaWduaW5nIFBDQSAyMDExMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIIC
# CgKCAgEAq/D6chAcLq3YbqqCEE00uvK2WCGfQhsqa+laUKq4BjgaBEm6f8MMHt03
# a8YS2AvwOMKZBrDIOdUBFDFC04kNeWSHfpRgJGyvnkmc6Whe0t+bU7IKLMOv2akr
# rnoJr9eWWcpgGgXpZnboMlImEi/nqwhQz7NEt13YxC4Ddato88tt8zpcoRb0Rrrg
# OGSsbmQ1eKagYw8t00CT+OPeBw3VXHmlSSnnDb6gE3e+lD3v++MrWhAfTVYoonpy
# 4BI6t0le2O3tQ5GD2Xuye4Yb2T6xjF3oiU+EGvKhL1nkkDstrjNYxbc+/jLTswM9
# sbKvkjh+0p2ALPVOVpEhNSXDOW5kf1O6nA+tGSOEy/S6A4aN91/w0FK/jJSHvMAh
# dCVfGCi2zCcoOCWYOUo2z3yxkq4cI6epZuxhH2rhKEmdX4jiJV3TIUs+UsS1Vz8k
# A/DRelsv1SPjcF0PUUZ3s/gA4bysAoJf28AVs70b1FVL5zmhD+kjSbwYuER8ReTB
# w3J64HLnJN+/RpnF78IcV9uDjexNSTCnq47f7Fufr/zdsGbiwZeBe+3W7UvnSSmn
# Eyimp31ngOaKYnhfsi+E11ecXL93KCjx7W3DKI8sj0A3T8HhhUSJxAlMxdSlQy90
# lfdu+HggWCwTXWCVmj5PM4TasIgX3p5O9JawvEagbJjS4NaIjAsCAwEAAaOCAe0w
# ggHpMBAGCSsGAQQBgjcVAQQDAgEAMB0GA1UdDgQWBBRIbmTlUAXTgqoXNzcitW2o
# ynUClTAZBgkrBgEEAYI3FAIEDB4KAFMAdQBiAEMAQTALBgNVHQ8EBAMCAYYwDwYD
# VR0TAQH/BAUwAwEB/zAfBgNVHSMEGDAWgBRyLToCMZBDuRQFTuHqp8cx0SOJNDBa
# BgNVHR8EUzBRME+gTaBLhklodHRwOi8vY3JsLm1pY3Jvc29mdC5jb20vcGtpL2Ny
# bC9wcm9kdWN0cy9NaWNSb29DZXJBdXQyMDExXzIwMTFfMDNfMjIuY3JsMF4GCCsG
# AQUFBwEBBFIwUDBOBggrBgEFBQcwAoZCaHR0cDovL3d3dy5taWNyb3NvZnQuY29t
# L3BraS9jZXJ0cy9NaWNSb29DZXJBdXQyMDExXzIwMTFfMDNfMjIuY3J0MIGfBgNV
# HSAEgZcwgZQwgZEGCSsGAQQBgjcuAzCBgzA/BggrBgEFBQcCARYzaHR0cDovL3d3
# dy5taWNyb3NvZnQuY29tL3BraW9wcy9kb2NzL3ByaW1hcnljcHMuaHRtMEAGCCsG
# AQUFBwICMDQeMiAdAEwAZQBnAGEAbABfAHAAbwBsAGkAYwB5AF8AcwB0AGEAdABl
# AG0AZQBuAHQALiAdMA0GCSqGSIb3DQEBCwUAA4ICAQBn8oalmOBUeRou09h0ZyKb
# C5YR4WOSmUKWfdJ5DJDBZV8uLD74w3LRbYP+vj/oCso7v0epo/Np22O/IjWll11l
# hJB9i0ZQVdgMknzSGksc8zxCi1LQsP1r4z4HLimb5j0bpdS1HXeUOeLpZMlEPXh6
# I/MTfaaQdION9MsmAkYqwooQu6SpBQyb7Wj6aC6VoCo/KmtYSWMfCWluWpiW5IP0
# wI/zRive/DvQvTXvbiWu5a8n7dDd8w6vmSiXmE0OPQvyCInWH8MyGOLwxS3OW560
# STkKxgrCxq2u5bLZ2xWIUUVYODJxJxp/sfQn+N4sOiBpmLJZiWhub6e3dMNABQam
# ASooPoI/E01mC8CzTfXhj38cbxV9Rad25UAqZaPDXVJihsMdYzaXht/a8/jyFqGa
# J+HNpZfQ7l1jQeNbB5yHPgZ3BtEGsXUfFL5hYbXw3MYbBL7fQccOKO7eZS/sl/ah
# XJbYANahRr1Z85elCUtIEJmAH9AAKcWxm6U/RXceNcbSoqKfenoi+kiVH6v7RyOA
# 9Z74v2u3S5fi63V4GuzqN5l5GEv/1rMjaHXmr/r8i+sLgOppO6/8MO0ETI7f33Vt
# Y5E90Z1WTk+/gFcioXgRMiF670EKsT/7qMykXcGhiJtXcVZOSEXAQsmbdlsKgEhr
# /Xmfwb1tbWrJUnMTDXpQzTGCGXQwghlwAgEBMIGVMH4xCzAJBgNVBAYTAlVTMRMw
# EQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVN
# aWNyb3NvZnQgQ29ycG9yYXRpb24xKDAmBgNVBAMTH01pY3Jvc29mdCBDb2RlIFNp
# Z25pbmcgUENBIDIwMTECEzMAAANOtTx6wYRv6ysAAAAAA04wDQYJYIZIAWUDBAIB
# BQCgga4wGQYJKoZIhvcNAQkDMQwGCisGAQQBgjcCAQQwHAYKKwYBBAGCNwIBCzEO
# MAwGCisGAQQBgjcCARUwLwYJKoZIhvcNAQkEMSIEIKw07qe7ifdUolnWWBm9i6dc
# fuDYD8bBCwBWPJMWku0cMEIGCisGAQQBgjcCAQwxNDAyoBSAEgBNAGkAYwByAG8A
# cwBvAGYAdKEagBhodHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20wDQYJKoZIhvcNAQEB
# BQAEggEAXoB1pYFlqLtIdbd+38CGwfnlqpdf2PuTxOPh8evja043JdLxTz/Up6QW
# lXBbNpikieH5UC0niNE+Aytk5x2ttIUELY8xUXNiWu+pgUcfXegq4cevKJgaM+3Z
# e1MK7k0DXxuFqVL1AcAlBYucFYgjO5pL+4cVgUYMpJmsFG69z+M3Z12wTWfbknzZ
# iDnpX/zO3Xm2ehLrmONEPPa5c3062eTc0moTOoWJM9zAXYKzWYE7HiRcDkuxoQer
# D75gu3m2fJqCQ6mGlbp/4kF1dh4blkWq42RohBVXkk78nvzTKP51XqqfwhFYhKFn
# JWKW7mgEuRrjQx85HgfW8yWzWzZRgKGCFv4wghb6BgorBgEEAYI3AwMBMYIW6jCC
# FuYGCSqGSIb3DQEHAqCCFtcwghbTAgEDMQ8wDQYJYIZIAWUDBAIBBQAwggFPBgsq
# hkiG9w0BCRABBKCCAT4EggE6MIIBNgIBAQYKKwYBBAGEWQoDATAxMA0GCWCGSAFl
# AwQCAQUABCBDWQjqmMlNNq5OS382NCLv4zt+NHQeGgR+vW86w5wOjwIGZGzUfHho
# GBEyMDIzMDUyNjE3MTA1My40WjAEgAIB9KCB0KSBzTCByjELMAkGA1UEBhMCVVMx
# EzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoT
# FU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjElMCMGA1UECxMcTWljcm9zb2Z0IEFtZXJp
# Y2EgT3BlcmF0aW9uczEmMCQGA1UECxMdVGhhbGVzIFRTUyBFU046N0JGMS1FM0VB
# LUI4MDgxJTAjBgNVBAMTHE1pY3Jvc29mdCBUaW1lLVN0YW1wIFNlcnZpY2WgghFX
# MIIHDDCCBPSgAwIBAgITMwAAAcj5sO5n7eprRgABAAAByDANBgkqhkiG9w0BAQsF
# ADB8MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMH
# UmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSYwJAYDVQQD
# Ex1NaWNyb3NvZnQgVGltZS1TdGFtcCBQQ0EgMjAxMDAeFw0yMjExMDQxOTAxMzda
# Fw0yNDAyMDIxOTAxMzdaMIHKMQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGlu
# Z3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBv
# cmF0aW9uMSUwIwYDVQQLExxNaWNyb3NvZnQgQW1lcmljYSBPcGVyYXRpb25zMSYw
# JAYDVQQLEx1UaGFsZXMgVFNTIEVTTjo3QkYxLUUzRUEtQjgwODElMCMGA1UEAxMc
# TWljcm9zb2Z0IFRpbWUtU3RhbXAgU2VydmljZTCCAiIwDQYJKoZIhvcNAQEBBQAD
# ggIPADCCAgoCggIBALnLnX4oT4MkVsJ5yLiQqnN0wrRZNFzophXCo1PW0ulhZ4Np
# fhayIndJj2eylrOhMI9tV/mZBpDOsfqMx5ZjbZx0kOPKYyBXZSlyKIe30qONQOJ9
# LUCJcIbPLiGCKHKe9scAwRykmcRmhGv9O9O4sjsPml2VzXE8YDYkRAEm/bYZS2TO
# +ZnySIAVb1JnS2XPlzlGMM//LpeyBM7TzR+HJ/Ap9LSYSBX6dp3ra/BmDaIsiKsb
# nhu/rErdsV5e/4oHOXBz2Z1oX5fT2gePkrrawPMwIc1R054+EX38QOEd5OAUWz3R
# qMEHzn/eWXmMS0x3+WsAdJ6mcTmPsrhloX+oaeURkaLnPW3lWb/fDNXq2GueUtWo
# WYBg0CcnFbRMvNkZZin02GNrjZpTy1rZw+7A5aVZIWEBirk1p7sECqvWU0hPtFdn
# B/CWlgWCUDCLlljPN7yz6NyE/0v+Y09qGTkvZ7CYMMAsEVLWLcLavPzybb7dVela
# BuFndvzAIn4BCTpBxewET6iPV79vPq4qiWeZoyxs/OHT9q8qHtJcWEs9z0+RKyqE
# tPsyaWDQHMW79lF4k5N1o/5md7bpc+s+F50peYMtDy3q/hFY8+BvehnGYCtnfRvr
# mMpR1sAZ3iL/WBUI+sjHVv+LBKZBG66YiTXpnMVApgYqWe0nN+a7+LrZCeP1AgMB
# AAGjggE2MIIBMjAdBgNVHQ4EFgQUtVwvSYG60sCw04iSU+IiuzVL5PUwHwYDVR0j
# BBgwFoAUn6cVXQBeYl2D9OXSZacbUzUZ6XIwXwYDVR0fBFgwVjBUoFKgUIZOaHR0
# cDovL3d3dy5taWNyb3NvZnQuY29tL3BraW9wcy9jcmwvTWljcm9zb2Z0JTIwVGlt
# ZS1TdGFtcCUyMFBDQSUyMDIwMTAoMSkuY3JsMGwGCCsGAQUFBwEBBGAwXjBcBggr
# BgEFBQcwAoZQaHR0cDovL3d3dy5taWNyb3NvZnQuY29tL3BraW9wcy9jZXJ0cy9N
# aWNyb3NvZnQlMjBUaW1lLVN0YW1wJTIwUENBJTIwMjAxMCgxKS5jcnQwDAYDVR0T
# AQH/BAIwADATBgNVHSUEDDAKBggrBgEFBQcDCDANBgkqhkiG9w0BAQsFAAOCAgEA
# w9ZyUshDw6koJ35BYnyG7XiW+zZGQm0Y+cgRok9X/X0xr1G+Qlu5N3kqd7sVKqJv
# S1spHDX0Gv2H8NJYmxIpvWEY1DXC+NqCBaraf+65fYIOtaIgfDwfuryxJo15GMVF
# EfFoyiguUOXcfegdtX2knKNjDpr42MRlEewJ/uOdvZUDzy6mxCkY3DUG/qfppEB9
# l3jG7IeREApE7mAIphU4J7otmTxSxLgacQSuc9h5yp3h1CfYuKXClnQqhdQDlTfI
# FyB9EdBX5THAoOU1vL/dFcMf5j92hNiIHtbhPDPWDQZ+e++j8ZgNemfnNrPoWCPv
# bCWYYUQPGkmGnK5S8+2lP+ejrxGEREUGbbbgBZK1fjPyfrQnhjb9Bne+WOJSizKe
# f9MaDr4jwXQdqw+Qv1PgfKrdWwakC1WdcB0ctP+h4ScWzotiIBiWjgmCxl9s89FQ
# E+pmdgU1qyZnCukg7+61+6bIAvgn9bdEdWlgpAhybaWLsio3+1KHUWs3rzn+ldNV
# 5KwERyqyO20KBxS/rLp9pYiQgYzedZg1qBIFAhL8Ad5fkRWAC9GiIrHOXE2hl/0/
# BfogG8/o9hhlbpvOYBEEvbi80nnaGD8hF8U25cHsBnQMaiGWiqM0QaMOPTpy/uQo
# KKFTH92UqqcXNkAtBNN5enP7woZGMqjF6uy8HeD5x6owggdxMIIFWaADAgECAhMz
# AAAAFcXna54Cm0mZAAAAAAAVMA0GCSqGSIb3DQEBCwUAMIGIMQswCQYDVQQGEwJV
# UzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UE
# ChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMTIwMAYDVQQDEylNaWNyb3NvZnQgUm9v
# dCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkgMjAxMDAeFw0yMTA5MzAxODIyMjVaFw0z
# MDA5MzAxODMyMjVaMHwxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9u
# MRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRp
# b24xJjAkBgNVBAMTHU1pY3Jvc29mdCBUaW1lLVN0YW1wIFBDQSAyMDEwMIICIjAN
# BgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA5OGmTOe0ciELeaLL1yR5vQ7VgtP9
# 7pwHB9KpbE51yMo1V/YBf2xK4OK9uT4XYDP/XE/HZveVU3Fa4n5KWv64NmeFRiMM
# tY0Tz3cywBAY6GB9alKDRLemjkZrBxTzxXb1hlDcwUTIcVxRMTegCjhuje3XD9gm
# U3w5YQJ6xKr9cmmvHaus9ja+NSZk2pg7uhp7M62AW36MEBydUv626GIl3GoPz130
# /o5Tz9bshVZN7928jaTjkY+yOSxRnOlwaQ3KNi1wjjHINSi947SHJMPgyY9+tVSP
# 3PoFVZhtaDuaRr3tpK56KTesy+uDRedGbsoy1cCGMFxPLOJiss254o2I5JasAUq7
# vnGpF1tnYN74kpEeHT39IM9zfUGaRnXNxF803RKJ1v2lIH1+/NmeRd+2ci/bfV+A
# utuqfjbsNkz2K26oElHovwUDo9Fzpk03dJQcNIIP8BDyt0cY7afomXw/TNuvXsLz
# 1dhzPUNOwTM5TI4CvEJoLhDqhFFG4tG9ahhaYQFzymeiXtcodgLiMxhy16cg8ML6
# EgrXY28MyTZki1ugpoMhXV8wdJGUlNi5UPkLiWHzNgY1GIRH29wb0f2y1BzFa/Zc
# UlFdEtsluq9QBXpsxREdcu+N+VLEhReTwDwV2xo3xwgVGD94q0W29R6HXtqPnhZy
# acaue7e3PmriLq0CAwEAAaOCAd0wggHZMBIGCSsGAQQBgjcVAQQFAgMBAAEwIwYJ
# KwYBBAGCNxUCBBYEFCqnUv5kxJq+gpE8RjUpzxD/LwTuMB0GA1UdDgQWBBSfpxVd
# AF5iXYP05dJlpxtTNRnpcjBcBgNVHSAEVTBTMFEGDCsGAQQBgjdMg30BATBBMD8G
# CCsGAQUFBwIBFjNodHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20vcGtpb3BzL0RvY3Mv
# UmVwb3NpdG9yeS5odG0wEwYDVR0lBAwwCgYIKwYBBQUHAwgwGQYJKwYBBAGCNxQC
# BAweCgBTAHUAYgBDAEEwCwYDVR0PBAQDAgGGMA8GA1UdEwEB/wQFMAMBAf8wHwYD
# VR0jBBgwFoAU1fZWy4/oolxiaNE9lJBb186aGMQwVgYDVR0fBE8wTTBLoEmgR4ZF
# aHR0cDovL2NybC5taWNyb3NvZnQuY29tL3BraS9jcmwvcHJvZHVjdHMvTWljUm9v
# Q2VyQXV0XzIwMTAtMDYtMjMuY3JsMFoGCCsGAQUFBwEBBE4wTDBKBggrBgEFBQcw
# AoY+aHR0cDovL3d3dy5taWNyb3NvZnQuY29tL3BraS9jZXJ0cy9NaWNSb29DZXJB
# dXRfMjAxMC0wNi0yMy5jcnQwDQYJKoZIhvcNAQELBQADggIBAJ1VffwqreEsH2cB
# MSRb4Z5yS/ypb+pcFLY+TkdkeLEGk5c9MTO1OdfCcTY/2mRsfNB1OW27DzHkwo/7
# bNGhlBgi7ulmZzpTTd2YurYeeNg2LpypglYAA7AFvonoaeC6Ce5732pvvinLbtg/
# SHUB2RjebYIM9W0jVOR4U3UkV7ndn/OOPcbzaN9l9qRWqveVtihVJ9AkvUCgvxm2
# EhIRXT0n4ECWOKz3+SmJw7wXsFSFQrP8DJ6LGYnn8AtqgcKBGUIZUnWKNsIdw2Fz
# Lixre24/LAl4FOmRsqlb30mjdAy87JGA0j3mSj5mO0+7hvoyGtmW9I/2kQH2zsZ0
# /fZMcm8Qq3UwxTSwethQ/gpY3UA8x1RtnWN0SCyxTkctwRQEcb9k+SS+c23Kjgm9
# swFXSVRk2XPXfx5bRAGOWhmRaw2fpCjcZxkoJLo4S5pu+yFUa2pFEUep8beuyOiJ
# Xk+d0tBMdrVXVAmxaQFEfnyhYWxz/gq77EFmPWn9y8FBSX5+k77L+DvktxW/tM4+
# pTFRhLy/AsGConsXHRWJjXD+57XQKBqJC4822rpM+Zv/Cuk0+CQ1ZyvgDbjmjJnW
# 4SLq8CdCPSWU5nR0W2rRnj7tfqAxM328y+l7vzhwRNGQ8cirOoo6CGJ/2XBjU02N
# 7oJtpQUQwXEGahC0HVUzWLOhcGbyoYICzjCCAjcCAQEwgfihgdCkgc0wgcoxCzAJ
# BgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25k
# MR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xJTAjBgNVBAsTHE1pY3Jv
# c29mdCBBbWVyaWNhIE9wZXJhdGlvbnMxJjAkBgNVBAsTHVRoYWxlcyBUU1MgRVNO
# OjdCRjEtRTNFQS1CODA4MSUwIwYDVQQDExxNaWNyb3NvZnQgVGltZS1TdGFtcCBT
# ZXJ2aWNloiMKAQEwBwYFKw4DAhoDFQDfzhNQu8Y1NApSA10AYrnAGO7LsqCBgzCB
# gKR+MHwxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQH
# EwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xJjAkBgNV
# BAMTHU1pY3Jvc29mdCBUaW1lLVN0YW1wIFBDQSAyMDEwMA0GCSqGSIb3DQEBBQUA
# AgUA6BtHOzAiGA8yMDIzMDUyNjIyNTY1OVoYDzIwMjMwNTI3MjI1NjU5WjB3MD0G
# CisGAQQBhFkKBAExLzAtMAoCBQDoG0c7AgEAMAoCAQACAh9iAgH/MAcCAQACAhJF
# MAoCBQDoHJi7AgEAMDYGCisGAQQBhFkKBAIxKDAmMAwGCisGAQQBhFkKAwKgCjAI
# AgEAAgMHoSChCjAIAgEAAgMBhqAwDQYJKoZIhvcNAQEFBQADgYEA3Z5+Hb9b4UUo
# 8juerWRLDN83YiGCfRscA1dBYiAMTo1gP4RtUHGAllX7elsq6DuRhXruPHKEQltm
# DdVKMFXTUkxaq7CGNlXM5JZI847BCuydhLd9fciRFgm60Y+2zqw6em8wBhd3Q0Vh
# BM5R5np4bcJSEIb6i5fh/rMgG3q7wlgxggQNMIIECQIBATCBkzB8MQswCQYDVQQG
# EwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwG
# A1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSYwJAYDVQQDEx1NaWNyb3NvZnQg
# VGltZS1TdGFtcCBQQ0EgMjAxMAITMwAAAcj5sO5n7eprRgABAAAByDANBglghkgB
# ZQMEAgEFAKCCAUowGgYJKoZIhvcNAQkDMQ0GCyqGSIb3DQEJEAEEMC8GCSqGSIb3
# DQEJBDEiBCCgJtXBQGZAHknC+NLPmZ0KMvkoHrG1oySdY+t3cAHnizCB+gYLKoZI
# hvcNAQkQAi8xgeowgecwgeQwgb0EIGIAmM/NP22jL8cOo3Kkry9BVuE3bwNPZ8R3
# 7D4bhANmMIGYMIGApH4wfDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0
# b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3Jh
# dGlvbjEmMCQGA1UEAxMdTWljcm9zb2Z0IFRpbWUtU3RhbXAgUENBIDIwMTACEzMA
# AAHI+bDuZ+3qa0YAAQAAAcgwIgQg6gj6Q0rcg9Vl6PBJKo2GpC01sDohVzOi4JQ0
# /U9ipgYwDQYJKoZIhvcNAQELBQAEggIAU/awI2EZjBRwRS9HyOtI4sIBmSj/A9RM
# Qfav4aLuPiFXEBgbk720o2+GsI1fpmRceD1oS50ZLIiltaJYaEO+cA7bXRD98gSj
# +22WyAF/K1E6S8OtH7OqQ73brvVLl6/cbhELc7cXBqv6AJG5X7JpYr+mpUs67/jJ
# GhxmDXvcjjankLbfjq315UVTxz0ZnWCemLH+b048e/aVdo4nl+emYqtE0iWyey3y
# 4Ywi9VgWaH4NCJx4EKLkqgISyM9Kn+vN6bPirvu39TvvrEccNIHO0SEtX/e0i286
# iV+8o3BAtKMTQJmOreVtZ4r6+dqMWmYOoipDcvptX0WUv7k5ug5k/e8unkpBMWwB
# XftpBQJEFPBsy+oUJnCmWSxNPqHbPuhCejloyO/84odus36FlJOjPU0PFZhDZSdu
# 62fk8XbRbfw+5zNp+1vQRyct2M423vo7q3jbMq+33vPS8sUDT4Eq9DwcsKS269vv
# jJyyOe6v/hvFOQ7nDl19fznXfLPf/aggOYLizbSeuii+LDIx5dKfkrQTsQSfu5qT
# kJz8h8/s9Ndg1t6FJSJPRK69LtDh8Cwi4r1JlTi4h2AKVHLZ8c9xWRMFSIacf56f
# OAOTVCXJLHvYGutqoJkAu6oR/hJmeFWEz1trVwDG8ncaSZXwtPzONKj8hYBysi0k
# uZhnjKKbyqg=
# SIG # End signature block