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')
        $script:arrRoutingMethod = @('BGP Routing','Static Routing')
        $script:objCiscoUCS = @('lblSwitchInfraSubnet','txtSwitchInfraSubnet','lblTORASN','txtTORASN')
        $script:strMinWinVer = '6.1.7601.65536'
        $script:strMinWMFVer = '5.1.0.0'
        $script:strMaxWMFVer = '6.0.0.0'
        $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')
        $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')
        $script:arrCloudProperties = @('NodeCount','ConnectToAzure','NamingPrefix','PhysicalNamingPrefix','CompanyName','RegionName','DomainFQDN','ExternalDomainFQDN','DNSForwarder','InfraAzureEnvironment','InfraAzureDirectoryTenantName','TimeServer','SyslogServerIPv4Address','ADFSForestFQDN','ADFSProviderName','ADFSMetadataUri')
        $script:strPartnerExportFile = 'PartnerData.json'
        
        # 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 = 4
        $script:maxEdge = 4
        $script:validEdge = 2
        $script:validASNMin = 1
        $script:validASNMax = 4294967295
        $script:maxDNSForwarders = 2
        
        #$script:defaultNodeCount = '4'
        $script:defaultSUCount = '1'
        $script:defaultCloudCount = '1'
        $script:defaultSpineCount = '2'
        $script:defaultEdgeCount = '2'
        $script:defaultExternalSubnetSize = '24'
        $script:defaultSpineSubnetSize = '25'
        $script:defaultBMCSubnetSize = '26'
        $script:defaultStorageSubnetSize = '25'
        $script:defaultInfraSubnetSize = '24'
        $script:defaultSwInfraSubnetSize = '26'
        $script:defaultSupernetSize = '24'
        
        $script:defaultMainFormSize = '980,640'
        $script:defaultIPCellWidth = '110'
        $script:lenIPAddress = '15'
        $script:lenText = '50'
        $script:lenDomain = '120'
        $script:lenURI = '180'
        $script:lenPrefix = '8'
        $script:txtPrefixSize = '100,20'
        $script:lblMaxSize = '120,40'
        $script:lblLeft = '10'
        $script:lblLeftNet2 = '500'
        $script:txtLeft = '130'
        $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 = @{}
        
        if(-not($host.Runspace.ApartmentState -eq 'STA'))
        {
            [System.Threading.Thread]::CurrentThread.ApartmentState = [System.Threading.ApartmentState]::STA
        }
        $script:scriptPath = $MyInvocation.MyCommand.Definition
        
        Test-ScriptRequirements
        
        $script:defaultForeColor = [System.Drawing.Color]::Black
        $script:defaultFailColor = [System.Drawing.Color]::Red
        $script:disabledBackColor = [System.Drawing.Color]::LightGray
        $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 = @()
    if($host.Version -lt $strMinWMFVer)
    {
        $reqError += ($locString.errScriptHostVer -f $strMinWMFVer,$strMaxWMFVer)
    }
    if($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
            Add-Assembly (Join-Path $PSScriptRoot $dllIPCalculator)
            Add-Assembly (Join-Path $PSScriptRoot $dllJsonParser)
            Add-Assembly (Join-Path $PSScriptRoot $dllIPNetwork)
        }
        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 Trim-Json
{
    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 = $scriptPath,
        [string]$Description
    )
    $openFolderDialog = New-Object System.Windows.Forms.FolderBrowserDialog
    $openFolderDialog.Description = $Description
    $openFolderDialog.ShowNewFolderButton = $true
    $openFolderDialog.RootFolder = [System.Environment+SpecialFolder]::MyComputer
    $openFolderDialog.SelectedPath = $InitialDirectory
    $res = $openFolderDialog.ShowDialog($frmMain)
    $openFolderDialog.Dispose()
    if($res -eq [System.Windows.Forms.DialogResult]::OK)
    {
        return $openFolderDialog.SelectedPath
    }
}

function Get-SaveFileName
{
    param
    (
        [string]$InitialDirectory = $scriptPath
    )
    Write-Log -Message "$($MyInvocation.MyCommand.Name) - called"
    $saveFileDialog = New-Object System.Windows.Forms.SaveFileDialog
    $saveFileDialog.DefaultExt = 'json'
    $saveFileDialog.Filter = 'JSON files | *.json'
    $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 = $scriptPath,
        [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
    $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)))
        {
            $result = 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 -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 waitForm -ea SilentlyContinue)
    {
        $waitForm.Close()
        $waitForm.Dispose()
        $waitForm = $null
        $waitLabel = $null
    }
}

function Get-SwitchVendors
{
    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)
        {
            $switchInfo = @()
            $portmap = $switches.portmap
            $filter | ForEach-Object `
            {
                if($portmap.$PSItem)
                {
                    $switchInfo += $PSItem
                }
                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
            }
        }
        $switchInfo = $switches.portmap
        return @(($switchInfo | Get-Member -Type NoteProperty).Name)
    }
    catch
    {
        throw $PSItem
    }
}

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

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

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

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

function Add-ScaleUnitTab
{
    param
    (
        [Parameter(Mandatory=$true)]
        [int]$ID
    )
    try
    {
        $tabName = "tabSU$($ID)"
        if(-not($scaleUnitTabs.$tabName))
        {
            $objTab = New-Object System.Windows.Forms.TabPage
            $typTab = $objTab.GetType()
            $prpTab = $typTab.GetProperty('DoubleBuffered',('Instance','NonPublic'))
            $prpTab.SetValue($objTab, $true, $null)
            $scaleUnitTabs.Add($tabName,@{Tab=$objTab;Objects=@{}})
            $scaleUnitTabs.$tabName.Tab.Name = $tabName
            $scaleUnitTabs.$tabName.Tab.Text = "$($locString.lblScaleUnit) $($ID)"
            $tabControl.Controls.Add($scaleUnitTabs.$tabName.Tab)
            
            $tabRow = 20
            
            # ---------------------------- SCALE UNIT ID ------------------------------
            $thisName = 'lblScaleUnit'
            $thisObj = New-Object System.Windows.Forms.Label
            $scaleUnitTabs.$tabName.Objects.Add($thisName,$thisObj)
            $scaleUnitTabs.$tabName.Objects.$thisName.Location = New-Object System.Drawing.Size($lblLeft,($tabRow))
            $scaleUnitTabs.$tabName.Objects.$thisName.MaximumSize = New-Object System.Drawing.Size($lblMaxSize)
            $scaleUnitTabs.$tabName.Objects.$thisName.AutoSize = $true
            $scaleUnitTabs.$tabName.Objects.$thisName.Text = "$($locString.lblScaleUnit) : $($ID)"
            $scaleUnitTabs.$tabName.Objects.$thisName.Font = [System.Drawing.Font]::New("Microsoft Sans Serif", 12, [System.Drawing.FontStyle]::Bold)
            $scaleUnitTabs.$tabName.Tab.Controls.Add($scaleUnitTabs.$tabName.Objects.$thisName)
            
            $tabRow += 60
            
            # ------------------------------ NODE COUNT -------------------------------
            $thisName = 'lblNodeCount'
            $thisObj = New-Object System.Windows.Forms.Label
            $scaleUnitTabs.$tabName.Objects.Add($thisName,$thisObj)
            $scaleUnitTabs.$tabName.Objects.$thisName.Location = New-Object System.Drawing.Size($lblLeft,($tabRow+2))
            $scaleUnitTabs.$tabName.Objects.$thisName.MaximumSize = New-Object System.Drawing.Size($lblMaxSize)
            $scaleUnitTabs.$tabName.Objects.$thisName.AutoSize = $true
            $scaleUnitTabs.$tabName.Objects.$thisName.Text = $locString.lblNodeCount
            $scaleUnitTabs.$tabName.Objects.$thisName.Font = $defaultLabelFont
            $scaleUnitTabs.$tabName.Tab.Controls.Add($scaleUnitTabs.$tabName.Objects.$thisName)
            
            $thisName = 'txtNodeCount'
            $thisObj = New-Object System.Windows.Forms.TextBox
            $scaleUnitTabs.$tabName.Objects.Add($thisName,$thisObj)
            $scaleUnitTabs.$tabName.Objects.$thisName.Location = New-Object System.Drawing.Size($txtLeft,$tabRow)
            $scaleUnitTabs.$tabName.Objects.$thisName.Size = New-Object System.Drawing.Size($txtCountSize)
            $scaleUnitTabs.$tabName.Objects.$thisName.TextAlign = [System.Windows.Forms.HorizontalAlignment]::Center
            $scaleUnitTabs.$tabName.Objects.$thisName.Font = $defaultLabelFont
            $scaleUnitTabs.$tabName.Objects.$thisName.MaxLength = 2
            #$scaleUnitTabs.$tabName.Objects.$thisName.Text = $defaultNodeCount
            $scaleUnitTabs.$tabName.Objects.$thisName.Name = $thisName
            $scaleUnitTabs.$tabName.Tab.Controls.Add($scaleUnitTabs.$tabName.Objects.$thisName)
            $txtNodeCount_Changed = `
            {
                $thisName = 'txtNodeCount'
                $thisTab = $tabControl.SelectedTab
                $tabName = $thisTab.Name
                $scaleUnitTabs.$tabName.Objects.$thisName.Text = ($scaleUnitTabs.$tabName.Objects.$thisName.Text).Trim()
                if(-not([string]::IsNullOrEmpty($scaleUnitTabs.$tabName.Objects.$thisName.Text)))
                {
                    $valid = $true
                    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
                    {
                        [string]$errMessage = ($locString.errNumberNotInRange -f $validNodeMin,$validNodeMax)
                        Close-WaitForm
                        [System.Windows.Forms.MessageBox]::Show($errMessage, $locString.capValidationError, [System.Windows.Forms.MessageBoxButtons]::OK)
                        $scaleUnitTabs.$tabName.Objects.$thisName.ForeColor = $defaultFailColor
                        $scaleUnitTabs.$tabName.Objects.$thisName.Select()
                    }
                    $scaleUnitTabs.$tabName.Objects.$thisName.Refresh()
                }
            }
            $scaleUnitTabs.$tabName.Objects.$thisName.Add_Leave($txtNodeCount_Changed)
            
            $tabRow += 60
            
            # -------------------------- SCALE-UNIT SUBNETS ---------------------------
            $thisName = 'lblScaleUnitSubnets'
            $thisObj = New-Object System.Windows.Forms.Label
            $scaleUnitTabs.$tabName.Objects.Add($thisName,$thisObj)
            $scaleUnitTabs.$tabName.Objects.$thisName.Location = New-Object System.Drawing.Size($lblLeft,($tabRow))
            $scaleUnitTabs.$tabName.Objects.$thisName.AutoSize = $true
            $scaleUnitTabs.$tabName.Objects.$thisName.Text = $locString.lblScaleUnitSubnets
            $scaleUnitTabs.$tabName.Objects.$thisName.Font = [System.Drawing.Font]::New("Microsoft Sans Serif", 10, [System.Drawing.FontStyle]::Bold)
            $scaleUnitTabs.$tabName.Tab.Controls.Add($scaleUnitTabs.$tabName.Objects.$thisName)
            
            $tabRow += 40
            
            # ------------------------------ BMC SUBNET -------------------------------
            $thisName = 'lblBMCSubnet'
            $thisObj = New-Object System.Windows.Forms.Label
            $scaleUnitTabs.$tabName.Objects.Add($thisName,$thisObj)
            $scaleUnitTabs.$tabName.Objects.$thisName.Location = New-Object System.Drawing.Size($lblLeft,($tabRow+2))
            $scaleUnitTabs.$tabName.Objects.$thisName.MaximumSize = New-Object System.Drawing.Size($lblMaxSize)
            $scaleUnitTabs.$tabName.Objects.$thisName.AutoSize = $true
            $scaleUnitTabs.$tabName.Objects.$thisName.Text = $locString.lblBMCSubnet
            $scaleUnitTabs.$tabName.Objects.$thisName.Font = $defaultLabelFont
            $scaleUnitTabs.$tabName.Tab.Controls.Add($scaleUnitTabs.$tabName.Objects.$thisName)
            
            $thisName = 'txtBMCSubnet'
            $thisObj = New-Object System.Windows.Forms.TextBox
            $scaleUnitTabs.$tabName.Objects.Add($thisName,$thisObj)
            $scaleUnitTabs.$tabName.Objects.$thisName.Location = New-Object System.Drawing.Size($txtLeft,$tabRow)
            $scaleUnitTabs.$tabName.Objects.$thisName.Size = New-Object System.Drawing.Size($txtPrefixSize)
            $scaleUnitTabs.$tabName.Objects.$thisName.MaxLength = $lenIPAddress
            $scaleUnitTabs.$tabName.Objects.$thisName.Font = $defaultLabelFont
            $scaleUnitTabs.$tabName.Objects.$thisName.Name = $thisName
            $scaleUnitTabs.$tabName.Tab.Controls.Add($scaleUnitTabs.$tabName.Objects.$thisName)
            $txtBMCSubnet_Changed = `
            {
                $thisName = 'txtBMCSubnet'
                $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()
                    if($scaleUnitTabs.$tabName.Objects.$thisName.Text -notmatch $validIPAddress)
                    {
                        Close-WaitForm
                        [System.Windows.Forms.MessageBox]::Show($locString.errIPAddressInvalid, $locString.capValidationError, [System.Windows.Forms.MessageBoxButtons]::OK)
                        $scaleUnitTabs.$tabName.Objects.$thisName.ForeColor = $defaultFailColor
                        $scaleUnitTabs.$tabName.Objects.$thisName.Select()
                    }
                    elseif(-not(Get-IsValidSubnet -IPAddress $scaleUnitTabs.$tabName.Objects.$thisName.Text -CIDR $scaleUnitTabs.$tabName.Objects.'txtBMCSubnetSize'.Text))
                    {
                        Close-WaitForm
                        $message = ($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.ForeColor = $defaultFailColor
                        $scaleUnitTabs.$tabName.Objects.$thisName.Select()
                    }
                    else
                    {
                        $scaleUnitTabs.$tabName.Objects.$thisName.ForeColor = $defaultForeColor
                    }
                    $scaleUnitTabs.$tabName.Objects.$thisName.Refresh()
                }
            }
            $scaleUnitTabs.$tabName.Objects.$thisName.Add_Leave($txtBMCSubnet_Changed)
            
            $thisName = 'lblBMCSubnetSize'
            $thisObj = New-Object System.Windows.Forms.Label
            $scaleUnitTabs.$tabName.Objects.Add($thisName,$thisObj)
            $scaleUnitTabs.$tabName.Objects.$thisName.Location = New-Object System.Drawing.Size($txtSubnetLeft,($tabRow+2))
            $scaleUnitTabs.$tabName.Objects.$thisName.Size = New-Object System.Drawing.Size($txtCountSize)
            $scaleUnitTabs.$tabName.Objects.$thisName.Text = $locString.lblSubnetSize
            $scaleUnitTabs.$tabName.Objects.$thisName.Font = $defaultLabelFont
            $scaleUnitTabs.$tabName.Tab.Controls.Add($scaleUnitTabs.$tabName.Objects.$thisName)
            
            $thisName = 'txtBMCSubnetSize'
            $thisObj = New-Object System.Windows.Forms.TextBox
            $scaleUnitTabs.$tabName.Objects.Add($thisName,$thisObj)
            $scaleUnitTabs.$tabName.Objects.$thisName.Location = New-Object System.Drawing.Size($txtSubnetSizeLeft,$tabRow)
            $scaleUnitTabs.$tabName.Objects.$thisName.Size = New-Object System.Drawing.Size($txtCountSize)
            $scaleUnitTabs.$tabName.Objects.$thisName.MaxLength = 2
            $scaleUnitTabs.$tabName.Objects.$thisName.Font = $defaultLabelFont
            $scaleUnitTabs.$tabName.Objects.$thisName.Text = $defaultBMCSubnetSize
            $scaleUnitTabs.$tabName.Objects.$thisName.Name = $thisName
            $scaleUnitTabs.$tabName.Objects.$thisName.Enabled = $false
            $scaleUnitTabs.$tabName.Objects.$thisName.ReadOnly = $true
            $scaleUnitTabs.$tabName.Tab.Controls.Add($scaleUnitTabs.$tabName.Objects.$thisName)
            
            $tabRow += 40
            
            # ---------------------------- STORAGE SUBNET -----------------------------
            $thisName = 'lblStorageSubnet'
            $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.lblStorageSubnet
            $scaleUnitTabs.$tabName.Objects.$thisName.Font = $defaultLabelFont
            $scaleUnitTabs.$tabName.Tab.Controls.Add($scaleUnitTabs.$tabName.Objects.$thisName)
            
            $thisName = 'txtStorageSubnet'
            $thisObj = New-Object System.Windows.Forms.TextBox
            $scaleUnitTabs.$tabName.Objects.Add($thisName,$thisObj)
            $scaleUnitTabs.$tabName.Objects.$thisName.Location = New-Object System.Drawing.Size($txtLeft,$tabRow)
            $scaleUnitTabs.$tabName.Objects.$thisName.Size = New-Object System.Drawing.Size($txtPrefixSize)
            $scaleUnitTabs.$tabName.Objects.$thisName.MaxLength = $lenIPAddress
            $scaleUnitTabs.$tabName.Objects.$thisName.Font = $defaultLabelFont
            $scaleUnitTabs.$tabName.Objects.$thisName.Name = $thisName
            $scaleUnitTabs.$tabName.Tab.Controls.Add($scaleUnitTabs.$tabName.Objects.$thisName)
            $txtStorageSubnet_Changed = `
            {
                $thisName = 'txtStorageSubnet'
                $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()
                    if($scaleUnitTabs.$tabName.Objects.$thisName.Text -notmatch $validIPAddress)
                    {
                        Close-WaitForm
                        [System.Windows.Forms.MessageBox]::Show($locString.errIPAddressInvalid, $locString.capValidationError, [System.Windows.Forms.MessageBoxButtons]::OK)
                        $scaleUnitTabs.$tabName.Objects.$thisName.ForeColor = $defaultFailColor
                        $scaleUnitTabs.$tabName.Objects.$thisName.Select()
                    }
                    elseif(-not(Get-IsValidSubnet -IPAddress $scaleUnitTabs.$tabName.Objects.$thisName.Text -CIDR $scaleUnitTabs.$tabName.Objects.'txtStorageSubnetSize'.Text))
                    {
                        Close-WaitForm
                        $message = ($locString.errSubnetInvalid -f $scaleUnitTabs.$tabName.Objects.$thisName.Text,$scaleUnitTabs.$tabName.Objects.'txtStorageSubnetSize'.Text)
                        [System.Windows.Forms.MessageBox]::Show($message, $locString.capValidationError, [System.Windows.Forms.MessageBoxButtons]::OK)
                        $scaleUnitTabs.$tabName.Objects.$thisName.ForeColor = $defaultFailColor
                        $scaleUnitTabs.$tabName.Objects.$thisName.Select()
                    }
                    else
                    {
                        $scaleUnitTabs.$tabName.Objects.$thisName.ForeColor = $defaultForeColor
                    }
                    $scaleUnitTabs.$tabName.Objects.$thisName.Refresh()
                }
            }
            $scaleUnitTabs.$tabName.Objects.$thisName.Add_Leave($txtStorageSubnet_Changed)
            
            $thisName = 'lblStorageSubnetSize'
            $thisObj = New-Object System.Windows.Forms.Label
            $scaleUnitTabs.$tabName.Objects.Add($thisName,$thisObj)
            $scaleUnitTabs.$tabName.Objects.$thisName.Location = New-Object System.Drawing.Size($txtSubnetLeft,($tabRow+2))
            $scaleUnitTabs.$tabName.Objects.$thisName.Size = New-Object System.Drawing.Size($txtCountSize)
            $scaleUnitTabs.$tabName.Objects.$thisName.Text = $locString.lblSubnetSize
            $scaleUnitTabs.$tabName.Objects.$thisName.Font = $defaultLabelFont
            $scaleUnitTabs.$tabName.Tab.Controls.Add($scaleUnitTabs.$tabName.Objects.$thisName)
            if($tabName -eq "tabSU1")
            {
                $thisSize = '24'
            }
            else
            {
                $thisSize = $defaultStorageSubnetSize
            }
            
            $thisName = 'txtStorageSubnetSize'
            $thisObj = New-Object System.Windows.Forms.TextBox
            $scaleUnitTabs.$tabName.Objects.Add($thisName,$thisObj)
            $scaleUnitTabs.$tabName.Objects.$thisName.Location = New-Object System.Drawing.Size($txtSubnetSizeLeft,$tabRow)
            $scaleUnitTabs.$tabName.Objects.$thisName.Size = New-Object System.Drawing.Size($txtCountSize)
            $scaleUnitTabs.$tabName.Objects.$thisName.MaxLength = 2
            $scaleUnitTabs.$tabName.Objects.$thisName.Font = $defaultLabelFont
            $scaleUnitTabs.$tabName.Objects.$thisName.Text = $thisSize
            $scaleUnitTabs.$tabName.Objects.$thisName.Enabled = $false
            $scaleUnitTabs.$tabName.Objects.$thisName.ReadOnly = $true
            $scaleUnitTabs.$tabName.Objects.$thisName.Name = $thisName
            $scaleUnitTabs.$tabName.Tab.Controls.Add($scaleUnitTabs.$tabName.Objects.$thisName)
            if($tabName -eq "tabSU1")
            {
                $thisName = 'lblStorageSubnetSizeNote'
                $thisObj = New-Object System.Windows.Forms.Label
                $scaleUnitTabs.$tabName.Objects.Add($thisName,$thisObj)
                $scaleUnitTabs.$tabName.Objects.$thisName.Location = New-Object System.Drawing.Size(330,($tabRow+2))
                $scaleUnitTabs.$tabName.Objects.$thisName.Size = New-Object System.Drawing.Size(300,20)
                $scaleUnitTabs.$tabName.Objects.$thisName.Text = $locString.strStorageSubnetSizeNote
                $scaleUnitTabs.$tabName.Objects.$thisName.Font = $defaultLabelFont
                $scaleUnitTabs.$tabName.Tab.Controls.Add($scaleUnitTabs.$tabName.Objects.$thisName)
            }
            
            $tabRow += 40
            
            # ----------------------------- INFRA SUBNET ------------------------------
            $thisName = 'lblInfraSubnet'
            $thisObj = New-Object System.Windows.Forms.Label
            $scaleUnitTabs.$tabName.Objects.Add($thisName,$thisObj)
            $scaleUnitTabs.$tabName.Objects.$thisName.Location = New-Object System.Drawing.Size($lblLeft,($tabRow+2))
            $scaleUnitTabs.$tabName.Objects.$thisName.MaximumSize = New-Object System.Drawing.Size($lblMaxSize)
            $scaleUnitTabs.$tabName.Objects.$thisName.AutoSize = $true
            $scaleUnitTabs.$tabName.Objects.$thisName.Text = $locString.lblInfraSubnet
            $scaleUnitTabs.$tabName.Objects.$thisName.Font = $defaultLabelFont
            $scaleUnitTabs.$tabName.Tab.Controls.Add($scaleUnitTabs.$tabName.Objects.$thisName)
            
            $thisName = 'txtInfraSubnet'
            $thisObj = New-Object System.Windows.Forms.TextBox
            $scaleUnitTabs.$tabName.Objects.Add($thisName,$thisObj)
            $scaleUnitTabs.$tabName.Objects.$thisName.Location = New-Object System.Drawing.Size($txtLeft,$tabRow)
            $scaleUnitTabs.$tabName.Objects.$thisName.Size = New-Object System.Drawing.Size($txtPrefixSize)
            $scaleUnitTabs.$tabName.Objects.$thisName.MaxLength = $lenIPAddress
            $scaleUnitTabs.$tabName.Objects.$thisName.Font = $defaultLabelFont
            $scaleUnitTabs.$tabName.Objects.$thisName.Name = $thisName
            $scaleUnitTabs.$tabName.Tab.Controls.Add($scaleUnitTabs.$tabName.Objects.$thisName)
            $txtInfraSubnet_Changed = `
            {
                $thisName = 'txtInfraSubnet'
                $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()
                    if($scaleUnitTabs.$tabName.Objects.$thisName.Text -notmatch $validIPAddress)
                    {
                        Close-WaitForm
                        [System.Windows.Forms.MessageBox]::Show($locString.errIPAddressInvalid, $locString.capValidationError, [System.Windows.Forms.MessageBoxButtons]::OK)
                        $scaleUnitTabs.$tabName.Objects.$thisName.ForeColor = $defaultFailColor
                        $scaleUnitTabs.$tabName.Objects.$thisName.Select()
                    }
                    elseif(-not(Get-IsValidSubnet -IPAddress $scaleUnitTabs.$tabName.Objects.$thisName.Text -CIDR $scaleUnitTabs.$tabName.Objects.'txtInfraSubnetSize'.Text))
                    {
                        Close-WaitForm
                        $message = ($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.ForeColor = $defaultFailColor
                        $scaleUnitTabs.$tabName.Objects.$thisName.Select()
                    }
                    else
                    {
                        $scaleUnitTabs.$tabName.Objects.$thisName.ForeColor = $defaultForeColor
                    }
                    $scaleUnitTabs.$tabName.Objects.$thisName.Refresh()
                }
            }
            $scaleUnitTabs.$tabName.Objects.$thisName.Add_Leave($txtInfraSubnet_Changed)
            
            $thisName = 'lblInfraSubnetSize'
            $thisObj = New-Object System.Windows.Forms.Label
            $scaleUnitTabs.$tabName.Objects.Add($thisName,$thisObj)
            $scaleUnitTabs.$tabName.Objects.$thisName.Location = New-Object System.Drawing.Size($txtSubnetLeft,($tabRow+2))
            $scaleUnitTabs.$tabName.Objects.$thisName.Size = New-Object System.Drawing.Size($txtCountSize)
            $scaleUnitTabs.$tabName.Objects.$thisName.Text = $locString.lblSubnetSize
            $scaleUnitTabs.$tabName.Objects.$thisName.Font = $defaultLabelFont
            $scaleUnitTabs.$tabName.Tab.Controls.Add($scaleUnitTabs.$tabName.Objects.$thisName)
            
            $thisName = 'txtInfraSubnetSize'
            $thisObj = New-Object System.Windows.Forms.TextBox
            $scaleUnitTabs.$tabName.Objects.Add($thisName,$thisObj)
            $scaleUnitTabs.$tabName.Objects.$thisName.Location = New-Object System.Drawing.Size($txtSubnetSizeLeft,$tabRow)
            $scaleUnitTabs.$tabName.Objects.$thisName.Size = New-Object System.Drawing.Size($txtCountSize)
            $scaleUnitTabs.$tabName.Objects.$thisName.MaxLength = 2
            $scaleUnitTabs.$tabName.Objects.$thisName.Font = $defaultLabelFont
            $scaleUnitTabs.$tabName.Objects.$thisName.Text = $defaultInfraSubnetSize
            $scaleUnitTabs.$tabName.Objects.$thisName.Enabled = $false
            $scaleUnitTabs.$tabName.Objects.$thisName.ReadOnly = $true
            $scaleUnitTabs.$tabName.Objects.$thisName.Name = $thisName
            $scaleUnitTabs.$tabName.Tab.Controls.Add($scaleUnitTabs.$tabName.Objects.$thisName)
            
            $tabRow += 40
            
            # -------------------------- SWITCH INFRA SUBNET --------------------------
            $thisName = 'lblSwitchInfraSubnet'
            $thisObj = New-Object System.Windows.Forms.Label
            $scaleUnitTabs.$tabName.Objects.Add($thisName,$thisObj)
            $scaleUnitTabs.$tabName.Objects.$thisName.Location = New-Object System.Drawing.Size($lblLeft,($tabRow+2))
            $scaleUnitTabs.$tabName.Objects.$thisName.MaximumSize = New-Object System.Drawing.Size($lblMaxSize)
            $scaleUnitTabs.$tabName.Objects.$thisName.AutoSize = $true
            $scaleUnitTabs.$tabName.Objects.$thisName.Font = $defaultLabelFont
            $scaleUnitTabs.$tabName.Objects.$thisName.Text = $locString.lblSwitchInfraSubnet
            $scaleUnitTabs.$tabName.Tab.Controls.Add($scaleUnitTabs.$tabName.Objects.$thisName)
            
            $thisName = 'txtSwitchInfraSubnet'
            $thisObj = New-Object System.Windows.Forms.TextBox
            $scaleUnitTabs.$tabName.Objects.Add($thisName,$thisObj)
            $scaleUnitTabs.$tabName.Objects.$thisName.Location = New-Object System.Drawing.Size($txtLeft,$tabRow)
            $scaleUnitTabs.$tabName.Objects.$thisName.Size = New-Object System.Drawing.Size($txtPrefixSize)
            $scaleUnitTabs.$tabName.Objects.$thisName.MaxLength = $lenIPAddress
            $scaleUnitTabs.$tabName.Objects.$thisName.Font = $defaultLabelFont
            $scaleUnitTabs.$tabName.Objects.$thisName.Name = $thisName
            $scaleUnitTabs.$tabName.Tab.Controls.Add($scaleUnitTabs.$tabName.Objects.$thisName)
            $txtSwitchInfraSubnet_Changed = `
            {
                $thisName = 'txtSwitchInfraSubnet'
                $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()
                    if($scaleUnitTabs.$tabName.Objects.$thisName.Text -notmatch $validIPAddress)
                    {
                        Close-WaitForm
                        [System.Windows.Forms.MessageBox]::Show($locString.errIPAddressInvalid, $locString.capValidationError, [System.Windows.Forms.MessageBoxButtons]::OK)
                        $scaleUnitTabs.$tabName.Objects.$thisName.ForeColor = $defaultFailColor
                        $scaleUnitTabs.$tabName.Objects.$thisName.Select()
                    }
                    elseif(-not(Get-IsValidSubnet -IPAddress $scaleUnitTabs.$tabName.Objects.$thisName.Text -CIDR $scaleUnitTabs.$tabName.Objects.'txtSwitchInfraSubnetSize'.Text))
                    {
                        Close-WaitForm
                        $message = ($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.ForeColor = $defaultFailColor
                        $scaleUnitTabs.$tabName.Objects.$thisName.Select()
                    }
                    else
                    {
                        $scaleUnitTabs.$tabName.Objects.$thisName.ForeColor = $defaultForeColor
                    }
                    $scaleUnitTabs.$tabName.Objects.$thisName.Refresh()
                }
            }
            $scaleUnitTabs.$tabName.Objects.$thisName.Add_Leave($txtSwitchInfraSubnet_Changed)
            
            $thisName = 'lblSwitchInfraSubnetSize'
            $thisObj = New-Object System.Windows.Forms.Label
            $scaleUnitTabs.$tabName.Objects.Add($thisName,$thisObj)
            $scaleUnitTabs.$tabName.Objects.$thisName.Location = New-Object System.Drawing.Size($txtSubnetLeft,($tabRow+2))
            $scaleUnitTabs.$tabName.Objects.$thisName.Size = New-Object System.Drawing.Size($txtCountSize)
            $scaleUnitTabs.$tabName.Objects.$thisName.Text = $locString.lblSubnetSize
            $scaleUnitTabs.$tabName.Objects.$thisName.Font = $defaultLabelFont
            $scaleUnitTabs.$tabName.Tab.Controls.Add($scaleUnitTabs.$tabName.Objects.$thisName)
            
            $thisName = 'txtSwitchInfraSubnetSize'
            $thisObj = New-Object System.Windows.Forms.TextBox
            $scaleUnitTabs.$tabName.Objects.Add($thisName,$thisObj)
            $scaleUnitTabs.$tabName.Objects.$thisName.Location = New-Object System.Drawing.Size($txtSubnetSizeLeft,$tabRow)
            $scaleUnitTabs.$tabName.Objects.$thisName.Size = New-Object System.Drawing.Size($txtCountSize)
            $scaleUnitTabs.$tabName.Objects.$thisName.MaxLength = 2
            $scaleUnitTabs.$tabName.Objects.$thisName.Font = $defaultLabelFont
            $scaleUnitTabs.$tabName.Objects.$thisName.Text = $defaultSwInfraSubnetSize
            $scaleUnitTabs.$tabName.Objects.$thisName.Enabled = $false
            $scaleUnitTabs.$tabName.Objects.$thisName.ReadOnly = $true
            $scaleUnitTabs.$tabName.Objects.$thisName.Name = $thisName
            $scaleUnitTabs.$tabName.Tab.Controls.Add($scaleUnitTabs.$tabName.Objects.$thisName)
            
            $tabRow += 60
            
            # ------------------------------ TOR BGP ASN ------------------------------
            $thisName = 'lblTORASN'
            $thisObj = New-Object System.Windows.Forms.Label
            $scaleUnitTabs.$tabName.Objects.Add($thisName,$thisObj)
            $scaleUnitTabs.$tabName.Objects.$thisName.Location = New-Object System.Drawing.Size($lblLeft,($tabRow+2))
            $scaleUnitTabs.$tabName.Objects.$thisName.MaximumSize = New-Object System.Drawing.Size($lblMaxSize)
            $scaleUnitTabs.$tabName.Objects.$thisName.AutoSize = $true
            $scaleUnitTabs.$tabName.Objects.$thisName.Font = $defaultLabelFont
            $scaleUnitTabs.$tabName.Objects.$thisName.Text = $locString.lblTORASN
            $scaleUnitTabs.$tabName.Tab.Controls.Add($scaleUnitTabs.$tabName.Objects.$thisName)
            
            $thisName = 'txtTORASN'
            $thisObj = New-Object System.Windows.Forms.TextBox
            $scaleUnitTabs.$tabName.Objects.Add($thisName,$thisObj)
            $scaleUnitTabs.$tabName.Objects.$thisName.Location = New-Object System.Drawing.Size($txtLeft,$tabRow)
            $scaleUnitTabs.$tabName.Objects.$thisName.Size = New-Object System.Drawing.Size($txtPrefixSize)
            $scaleUnitTabs.$tabName.Objects.$thisName.TextAlign = [System.Windows.Forms.HorizontalAlignment]::Center
            $scaleUnitTabs.$tabName.Objects.$thisName.MaxLength = $lenIPAddress
            $scaleUnitTabs.$tabName.Objects.$thisName.Font = $defaultLabelFont
            $scaleUnitTabs.$tabName.Objects.$thisName.Name = $thisName
            $scaleUnitTabs.$tabName.Tab.Controls.Add($scaleUnitTabs.$tabName.Objects.$thisName)
            $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
                    }
                    else
                    {
                        Close-WaitForm
                        [string]$errMessage = ($locString.errNumberNotInRange -f $validASNMin,$validASNMax)
                        [System.Windows.Forms.MessageBox]::Show($errMessage, $locString.capValidationError, [System.Windows.Forms.MessageBoxButtons]::OK)
                        $scaleUnitTabs.$tabName.Objects.$thisName.ForeColor = $defaultFailColor
                        $scaleUnitTabs.$tabName.Objects.$thisName.Select()
                    }
                    $scaleUnitTabs.$tabName.Objects.$thisName.Refresh()
                }
            }
            $scaleUnitTabs.$tabName.Objects.$thisName.Add_Leave($txtTORASN_Changed)
        }
    }
    catch
    {
        throw $PSItem
    }
}

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

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

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

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

function Get-IsValidURI
{
    param
    (
        [Parameter(Mandatory=$true)]
        [string]$URI
    )
    $sysURI = $URI -as [System.URI]
    $sysURI.AbsoluteURI -ne $null -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-EdgeBGP
{
    param
    (
        [Parameter(Mandatory=$true)]
        [int]$Num
    )
    $thisScenario = $scenario
    if($scenario -eq "AddRack")
    {
         $thisScenario = "Customer"
    }
    switch ($thisScenario)
    {
        "Customer"
        {
            $varLbl = $tabNetwork.Controls | Where-Object Name -eq "lblEdgeBGP$($Num)"
            $varTxt = $tabNetwork.Controls | Where-Object Name -eq "txtEdgeBGP$($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 Edge BGP ASN controls" -Force
            }
        }
        default
        {
        }
    }
}

function Enable-EdgeBGP
{
    param
    (
        [Parameter(Mandatory=$true)]
        [int]$Num
    )
    $thisScenario = $scenario
    if($scenario -eq "AddRack")
    {
         $thisScenario = "Customer"
    }
    switch ($thisScenario)
    {
        "Customer"
        {
            $varLbl = $tabNetwork.Controls | Where-Object Name -eq "lblEdgeBGP$($Num)"
            $varTxt = $tabNetwork.Controls | Where-Object Name -eq "txtEdgeBGP$($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 Edge 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"
        {
            $txtSpineSubnet.Enabled = $true
            $txtSpineSubnet.Visible = $true
            $txtSpineSubnet.Refresh()
            $lblSpineSubnet.Enabled = $true
            $lblSpineSubnet.Visible = $true
            $lblSpineSubnet.Refresh()
            $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"
        {
            $txtSpineSubnet.Enabled = $false
            $txtSpineSubnet.Visible = $false
            $txtSpineSubnet.Text = [string]::Empty
            $txtSpineSubnet.Refresh()
            $lblSpineSubnet.Enabled = $false
            $lblSpineSubnet.Visible = $false
            $lblSpineSubnet.Refresh()
            $lblSpineSubnetSize.Enabled = $false
            $lblSpineSubnetSize.Visible = $false
            $txtSpineSubnetSize.Enabled = $false
            $txtSpineSubnetSize.Visible = $false
        }
        default
        {
        }
    }
}

function Invoke-RefreshEdgeBGP
{
    param
    (
        [string]$EdgeCount
    )
    (1..$maxEdge) | ForEach-Object `
    {
        if($PSItem -le $EdgeCount)
        {
            Enable-EdgeBGP $PSItem
        }
        else
        {
            Disable-EdgeBGP $PSItem
        }
    }
}

function Clear-ExistingData
{
    # Set textbox items
    $objTextBox = `
    @(
        $txtEdgeBGP1
        $txtEdgeBGP2
        $txtEdgeBGP3
        $txtEdgeBGP4
        $txtSpineBGP
        $txtSoftwareBGP
        $txtExternalSubnet
        $txtCompanyName
        $txtRegionName
        $txtExternalDomain
        $txtPrivateDomain
        $txtDeployPrefix
        $txtPhysicalPrefix
        $txtTimeServer
        $txtDNSForwarder
        $txtSyslogServer
        $txtSpineSubnet
        $txtAADTenantName
        $txtADFSForestFQDN
        $txtADFSProviderName
        $txtADFSMetadataUri
    )
    # Set values on the Scale-Unit tab
    $objScaleUnit = `
    @(
        'txtNodeCount'
        'txtBMCSubnet'
        'txtStorageSubnet'
        'txtInfraSubnet'
        'txtSwitchInfraSubnet'
        'txtTORASN'
    )
    # Set combobox items
    $objComboBox = `
    @(
        $lstSwitchVendor
        $lstTORModel
        $lstTORFirmware
        $lstBMCModel
        $lstBMCFirmware
        $lstSpineModel
        $lstSpineFirmware
        $lstAzureEnvironment
    )
    # Clear Scale-Unit objects
    $txtScaleUnitCount.Text = '1'
    Invoke-Command -ScriptBlock $txtScaleUnitCount_Changed
    $objScaleUnit | ForEach-Object `
    {
        $scaleUnitTabs.tabSU1.Objects.$PSItem.Text = [string]::Empty
    }
    # Reset Customer items to default settings
    $lstIdentityStore.SelectedIndex = 0
    $lstRoutingMethod.SelectedIndex = 0
    $txtEdgeCount.Text = $defaultEdgeCount
    $txtExternalSubnetSize.Text = $defaultExternalSubnetSize
    Invoke-RefreshTotalNodeCount
    
    # Clear textbox objects
    $objTextBox | Foreach-Object `
    {
        $PSitem.Text = [string]::Empty
    }
    # Clear combobox selections
    foreach($obj in $objComboBox)
    {
        if($obj.Items.Count -ge 1)
        {
            $obj.SelectedIndex = -1
        }
        else
        {
            $obj.Text = [string]::Empty
        }
    }
    # 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
    Invoke-Command -ScriptBlock $txtEdgeCount_Changed
}

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 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 += 24
        }
        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 += 20
                }
                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 "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 "*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 Start-DeploymentWorksheet
{
    param
    (
        [switch]$EnableLogging,
        [switch]$EnableExperimental
    )
    try
    {
        $script:logging = $false
        Initialize-Environment
        if($EnableLogging)
        {
            $script:logging = $true
            Invoke-InitializeLogging
            Write-Log -Message "$($locString.titleMain) - v$(Get-FileVersionInfo)"
        }
        if($psISE)
        {
            Write-Log -Message "$($locString.powershellISE)" -Quiet
            $bar = '-' * [int]($locString.powershellISE).Length
            Write-Warning $bar
            Write-Warning $locString.powershellISE
            Write-Warning $bar
        }
        if(([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole] "Administrator"))
        {
            Write-Log -Message "$($locString.powershellAdmin)" -Quiet
            $bar = '-' * [int]($locString.powershellAdmin).Length
            Write-Warning $bar
            Write-Warning $locString.powershellAdmin
            Write-Warning $bar
        }
    }
    catch
    {
        throw $PSItem
    }
    
    # =============================== MAIN FORM ===============================
    $script:frmMain = New-Object System.Windows.Forms.Form
    $frmMain.AutoScaleDimensions = $defaultAutoScaleDimensions
    $frmMain.AutoScaleMode = $defaultAutoScaleMode
    $frmMain.AutoSizeMode = $defaultAutoSizeMode
    $frmMain.SuspendLayout()
    $typForm = $frmMain.GetType()
    $prpForm = $typForm.GetProperty('DoubleBuffered',('Instance','NonPublic'))
    $prpForm.SetValue($frmMain, $true, $null)
    $frmMain.Size = New-Object System.Drawing.Size($defaultMainFormSize)
    $frmMain.ShowIcon = $false
    $frmMain.MaximizeBox = $true
    $frmMain.FormBorderStyle = [System.Windows.Forms.FormBorderStyle]::Sizable
    $frmMain.MinimumSize = New-Object System.Drawing.Size($defaultMainFormSize)
    $frmMain.StartPosition = "CenterScreen"
    $frmMain.Text = "$($locString.titleMain) - v$(Get-FileVersionInfo)"
    $frmMain.Name = 'DeploymentWorksheet'
    $frmMain.BackColor = [System.Drawing.Color]::LightSlateGray
    $frmMain.KeyPreview = $true
    $script:btnEnter = New-Object System.Windows.Forms.Button
    $btnEnter.Add_Click({Keystroke_Enter})
    $frmMain.AcceptButton = $btnEnter
    #$frmMain.Add_KeyDown({if($PSItem.KeyCode -eq "Enter"){Keystroke_Enter}})
    #$frmMain.Add_KeyDown({if($PSItem.KeyCode -eq "Escape"){$frmMain.Close()}})
    $frmMain.Add_Resize( { OnResize_frmMain $frmMain $EventArgs } )
    function OnResize_frmMain
    {
        $w = [int]$frmMain.Size.Width - 22
        $h = [int]$frmMain.Size.Height - 70
        $tabControl.Size = New-Object System.Drawing.Size($w,$h)
        $frmMenu.Width = $frmMain.Width
    }
    
    # ---------------------------- MAIN FORM TABS -----------------------------
    $script:tabControl = New-Object System.Windows.Forms.TabControl
    $tabControl.BackColor = [System.Drawing.Color]::SlateGray
    $tabControl.Alignment = 'Top'
    $tabControl.Location = '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))
    
    $script:tabCustomer = New-Object System.Windows.Forms.TabPage
    $typTab = $tabCustomer.GetType()
    $prpTab = $typTab.GetProperty('DoubleBuffered',('Instance','NonPublic'))
    $prpTab.SetValue($tabCustomer, $true, $null)
    $tabCustomer.Name = "tabCustomer"
    $tabCustomer.Text = $locString.tabCustomer
    
    $script:tabNetwork = New-Object System.Windows.Forms.TabPage
    $typTab = $tabNetwork.GetType()
    $prpTab = $typTab.GetProperty('DoubleBuffered',('Instance','NonPublic'))
    $prpTab.SetValue($tabNetwork, $true, $null)
    $tabNetwork.Name = "tabNetwork"
    $tabNetwork.Text = $locString.tabNetwork
    
    $frmMain.Controls.Add($tabControl)
    $tabControl.Controls.Add($tabCustomer)
    $tabControl.Controls.Add($tabNetwork)
    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({[system.Diagnostics.Process]::Start($locString.customerLink)})
    $tabCustomer.Controls.Add($lnkCustomerDoc)
    
    # --------------------------- ON-HOVER TOOLTIPS ---------------------------
    $tooltip1 = New-Object System.Windows.Forms.ToolTip
    $tooltip1.ShowAlways = $true
    $tooltip1.ReshowDelay = 500
    $tooltip1.AutoPopDelay = 10000
    $tooltip1.IsBalloon = $true
    $ShowHelp = `
    {
        # Display popup help - Each value is the name of a control on the form.
        switch ($this.Name)
        {
            "txtTimeServer"
            {
                $tip = $locString.tipTimeServer
                break
            }
            "txtDNSForwarder"
            {
                $tip = $locString.tipDNSForwarder
                break
            }
            "txtSyslogServer"
            {
                $tip = $locString.tipSyslogServer
                break
            }
            "txtADFSForestFQDN"
            {
                $tip = $locString.tipADFSForestFQDN
                break
            }
            "txtADFSProviderName"
            {
                $tip = $locString.tipADFSProviderName
                break
            }
            "txtADFSMetadataUri"
            {
                $tip = $locString.tipADFSMetadataUri
                break
            }
            default
            {
                Write-Log -Message "ShowHelp encountered an unhandled property named '$($this.Name)'"
                break
            }
        }
        $tooltip1.SetToolTip($this,$tip)
    }
    
    # ---------------------------- AZURE IDENTITY -----------------------------
    $script:lblAzureIdentity = New-Object System.Windows.Forms.Label
    $lblAzureIdentity.Location = New-Object System.Drawing.Size($lblLeft,($row))
    $lblAzureIdentity.AutoSize = $true
    $lblAzureIdentity.Text = $locString.lblAzureIdentity
    $lblAzureIdentity.Font = [System.Drawing.Font]::New("Microsoft Sans Serif", 12, [System.Drawing.FontStyle]::Bold)
    $tabCustomer.Controls.Add($lblAzureIdentity)
    
    $row += 40
    
    # --------------------------- IDENTITY STORE ------------------------------
    $script:lblIdentityStore = New-Object System.Windows.Forms.Label
    $lblIdentityStore.Location = New-Object System.Drawing.Size($lblLeft,($row+2))
    $lblIdentityStore.MaximumSize = New-Object System.Drawing.Size($lblMaxSize)
    $lblIdentityStore.AutoSize = $true
    $lblIdentityStore.Font = $defaultLabelFont
    $lblIdentityStore.Text = $locString.lblIdentityStore
    $tabCustomer.Controls.Add($lblIdentityStore)
    
    $script:lstIdentityStore = New-Object System.Windows.Forms.ComboBox
    $lstIdentityStore.DropDownStyle = [System.Windows.Forms.ComboBoxStyle]::DropDownList
    $lstIdentityStore.Location = New-Object System.Drawing.Size($txtLeft,$row)
    $lstIdentityStore.Size = New-Object System.Drawing.Size($txtSize)
    $lstIdentityStore.Font = $defaultLabelFont
    $lstIdentityStore.Name = 'lstIdentityStore'
    [void]$lstIdentityStore.Items.Clear()
    $arrIdentityStore | Foreach-Object `
    {
        [void]$lstIdentityStore.Items.Add($PSItem)
    }
    $lstIdentityStore.SelectedIndex = -1
    $tabCustomer.Controls.Add($lstIdentityStore)
    $lstIdentityStore_Changed = `
    {
        switch ($lstIdentityStore.SelectedItem)
        {
            "Azure Active Directory"
            {
                $lblAADTenantName.Enabled = $true
                $lblAADTenantName.Visible = $true
                $lblAzureEnvironment.Enabled = $true
                $lblAzureEnvironment.Visible = $true
                $txtAADTenantName.Enabled = $true
                $txtAADTenantName.Visible = $true
                $lstAzureEnvironment.Enabled = $true
                $lstAzureEnvironment.Visible = $true
                # Disable ADFS objects
                $txtADFSForestFQDN.Text = $null
                $txtADFSForestFQDN.Enabled = $false
                $txtADFSForestFQDN.Visible = $false
                $txtADFSProviderName.Text = $null
                $txtADFSProviderName.Enabled = $false
                $txtADFSProviderName.Visible = $false
                $txtADFSMetadataUri.Text = $null
                $txtADFSMetadataUri.Enabled = $false
                $txtADFSMetadataUri.Visible = $false
                $lblADFSForestFQDN.Enabled = $false
                $lblADFSProviderName.Enabled = $false
                $lblADFSMetadataUri.Enabled = $false
                $lblADFSForestFQDN.Visible = $false
                $lblADFSProviderName.Visible = $false
                $lblADFSMetadataUri.Visible = $false
            }
            "Active Directory Federation Services"
            {
                $txtADFSForestFQDN.Enabled = $true
                $txtADFSForestFQDN.Visible = $true
                $txtADFSProviderName.Enabled = $true
                $txtADFSProviderName.Visible = $true
                $txtADFSMetadataUri.Enabled = $true
                $txtADFSMetadataUri.Visible = $true
                $lblADFSForestFQDN.Enabled = $true
                $lblADFSProviderName.Enabled = $true
                $lblADFSMetadataUri.Enabled = $true
                $lblADFSForestFQDN.Visible = $true
                $lblADFSProviderName.Visible = $true
                $lblADFSMetadataUri.Visible = $true
                # Disable AAD objects
                $lblAADTenantName.Enabled = $false
                $lblAADTenantName.Visible = $false
                $lblAzureEnvironment.Enabled = $false
                $lblAzureEnvironment.Visible = $false
                $txtAADTenantName.Enabled = $false
                $txtAADTenantName.Visible = $false
                $txtAADTenantName.Text = $null
                $lstAzureEnvironment.Enabled = $false
                $lstAzureEnvironment.Visible = $false
                $lstAzureEnvironment.SelectedIndex = -1
            }
            default
            {
            }
        }
        $lblAADTenantName.Refresh()
        $lblAzureEnvironment.Refresh()
        $txtAADTenantName.Refresh()
        $lstAzureEnvironment.Refresh()
        $txtADFSForestFQDN.Refresh()
        $txtADFSProviderName.Refresh()
        $txtADFSMetadataUri.Refresh()
        $lblADFSForestFQDN.Refresh()
        $lblADFSProviderName.Refresh()
        $lblADFSMetadataUri.Refresh()
    }
    $lstIdentityStore.Add_SelectedIndexChanged($lstIdentityStore_Changed)
    
    $row += 25
    
    # ---------------------------- TENANT NAME --------------------------------
    $script:lblAADTenantName = New-Object System.Windows.Forms.Label
    $lblAADTenantName.Location = New-Object System.Drawing.Size($lblLeft,($row+2))
    $lblAADTenantName.MaximumSize = New-Object System.Drawing.Size($lblMaxSize)
    $lblAADTenantName.AutoSize = $true
    $lblAADTenantName.Font = $defaultLabelFont
    $lblAADTenantName.Text = $locString.lblAADTenantName
    $lblAADTenantName.Enabled = $false
    $lblAADTenantName.Visible = $false
    $tabCustomer.Controls.Add($lblAADTenantName)
    
    $script:txtAADTenantName = New-Object System.Windows.Forms.TextBox
    $txtAADTenantName.Location = New-Object System.Drawing.Size($txtLeft,$row)
    $txtAADTenantName.Size = New-Object System.Drawing.Size($txtSize)
    $txtAADTenantName.Font = $defaultLabelFont
    $txtAADTenantName.Name = 'txtAADTenantName'
    $txtAADTenantName.MaxLength = $lenText
    $txtAADTenantName.Enabled = $false
    $txtAADTenantName.Visible = $false
    $tabCustomer.Controls.Add($txtAADTenantName)
    $txtAADTenantName_Changed = `
    {
        if((-not [string]::IsNullOrEmpty($txtAADTenantName.Text)) -and ($txtAADTenantName.Text -notmatch $validDomain))
        {
            Close-WaitForm
            [System.Windows.Forms.MessageBox]::Show($locString.errDomainInvalid, $locString.capValidationError, [System.Windows.Forms.MessageBoxButtons]::OK)
            $txtAADTenantName.ForeColor = $defaultFailColor
            $txtAADTenantName.Select()
        }
        else
        {
            $txtAADTenantName.ForeColor = $defaultForeColor
        }
        $txtAADTenantName.Refresh()
    }
    $txtAADTenantName.Add_Leave($txtAADTenantName_Changed)
    
    # ----------------------------- ADFS FQDN ---------------------------------
    $script:lblADFSForestFQDN = New-Object System.Windows.Forms.Label
    $lblADFSForestFQDN.Location = New-Object System.Drawing.Size($lblLeft,($row+2))
    $lblADFSForestFQDN.MaximumSize = New-Object System.Drawing.Size($lblMaxSize)
    $lblADFSForestFQDN.AutoSize = $true
    $lblADFSForestFQDN.Font = $defaultLabelFont
    $lblADFSForestFQDN.Text = $locString.lblADFSForestFQDN
    $lblADFSForestFQDN.Enabled = $false
    $lblADFSForestFQDN.Visible = $false
    $tabCustomer.Controls.Add($lblADFSForestFQDN)
    
    $script:txtADFSForestFQDN = New-Object System.Windows.Forms.TextBox
    $txtADFSForestFQDN.Location = New-Object System.Drawing.Size($txtLeft,$row)
    $txtADFSForestFQDN.Size = New-Object System.Drawing.Size($txtSize)
    $txtADFSForestFQDN.Font = $defaultLabelFont
    $txtADFSForestFQDN.Name = 'txtADFSForestFQDN'
    $txtADFSForestFQDN.MaxLength = $lenDomain
    $txtADFSForestFQDN.Enabled = $false
    $txtADFSForestFQDN.Visible = $false
    $txtADFSForestFQDN.Add_MouseHover($ShowHelp)
    $tabCustomer.Controls.Add($txtADFSForestFQDN)
    $txtADFSForestFQDN_Changed = `
    {
        if((-not [string]::IsNullOrEmpty($txtADFSForestFQDN.Text)) -and ($txtADFSForestFQDN.Text -notmatch $validDomain))
        {
            Close-WaitForm
            [System.Windows.Forms.MessageBox]::Show($locString.errDomainInvalid, $locString.capValidationError, [System.Windows.Forms.MessageBoxButtons]::OK)
            $txtADFSForestFQDN.ForeColor = $defaultFailColor
            $txtADFSForestFQDN.Select()
        }
        else
        {
            $txtADFSForestFQDN.ForeColor = $defaultForeColor
        }
        $txtADFSForestFQDN.Refresh()
    }
    $txtADFSForestFQDN.Add_Leave($txtADFSForestFQDN_Changed)
    
    $row += 25
    
    # --------------------------- AZURE DIRECTORY -----------------------------
    $script:lblAzureEnvironment = New-Object System.Windows.Forms.Label
    $lblAzureEnvironment.Location = New-Object System.Drawing.Size($lblLeft,($row+2))
    $lblAzureEnvironment.MaximumSize = New-Object System.Drawing.Size($lblMaxSize)
    $lblAzureEnvironment.AutoSize = $true
    $lblAzureEnvironment.Font = $defaultLabelFont
    $lblAzureEnvironment.Text = $locString.lblAzureEnvironment
    $lblAzureEnvironment.Enabled = $false
    $lblAzureEnvironment.Visible = $false
    $tabCustomer.Controls.Add($lblAzureEnvironment)
    
    $script:lstAzureEnvironment = New-Object System.Windows.Forms.ComboBox
    $lstAzureEnvironment.DropDownStyle = [System.Windows.Forms.ComboBoxStyle]::DropDownList
    $lstAzureEnvironment.Location = New-Object System.Drawing.Size($txtLeft,$row)
    $lstAzureEnvironment.Size = New-Object System.Drawing.Size($txtSize)
    $lstAzureEnvironment.Font = $defaultLabelFont
    $lstAzureEnvironment.Name = 'lstAzureEnvironment'
    $lstAzureEnvironment.Enabled = $false
    $lstAzureEnvironment.Visible = $false
    [void]$lstAzureEnvironment.Items.Clear()
    $arrAzureEnvironment | Foreach-Object `
    {
        [void]$lstAzureEnvironment.Items.Add($PSItem)
    }
    $tabCustomer.Controls.Add($lstAzureEnvironment)
    
    # ----------------------------- ADFS NAME ---------------------------------
    $script:lblADFSProviderName = New-Object System.Windows.Forms.Label
    $lblADFSProviderName.Location = New-Object System.Drawing.Size($lblLeft,($row+2))
    $lblADFSProviderName.MaximumSize = New-Object System.Drawing.Size($lblMaxSize)
    $lblADFSProviderName.AutoSize = $true
    $lblADFSProviderName.Font = $defaultLabelFont
    $lblADFSProviderName.Text = $locString.lblADFSProviderName
    $lblADFSProviderName.Enabled = $false
    $lblADFSProviderName.Visible = $false
    $tabCustomer.Controls.Add($lblADFSProviderName)
    
    $script:txtADFSProviderName = New-Object System.Windows.Forms.TextBox
    $txtADFSProviderName.Location = New-Object System.Drawing.Size($txtLeft,$row)
    $txtADFSProviderName.Size = New-Object System.Drawing.Size($txtSize)
    $txtADFSProviderName.Font = $defaultLabelFont
    $txtADFSProviderName.Name = 'txtADFSProviderName'
    $txtADFSProviderName.MaxLength = $lenText
    $txtADFSProviderName.Enabled = $false
    $txtADFSProviderName.Visible = $false
    $txtADFSProviderName.Add_MouseHover($ShowHelp)
    $tabCustomer.Controls.Add($txtADFSProviderName)
    $txtADFSProviderName_Changed = `
    {
        # Not doing validation on the entered data at this time
        if(-not [string]::IsNullOrEmpty($txtADFSProviderName.Text))
        {
            #Close-WaitForm
            #$txtADFSProviderName.ForeColor = $defaultFailColor
            #$txtADFSProviderName.Select()
        }
        else
        {
            $txtADFSProviderName.ForeColor = $defaultForeColor
        }
        $txtADFSProviderName.Refresh()
    }
    $txtADFSProviderName.Add_Leave($txtADFSProviderName_Changed)
    
    $row += 25
    
    # ------------------------- ADFS METADATA URI -----------------------------
    $script:lblADFSMetadataUri = New-Object System.Windows.Forms.Label
    $lblADFSMetadataUri.Location = New-Object System.Drawing.Size($lblLeft,($row+2))
    $lblADFSMetadataUri.MaximumSize = New-Object System.Drawing.Size($lblMaxSize)
    $lblADFSMetadataUri.AutoSize = $true
    $lblADFSMetadataUri.Font = $defaultLabelFont
    $lblADFSMetadataUri.Text = $locString.lblADFSMetadataUri
    $lblADFSMetadataUri.Enabled = $false
    $lblADFSMetadataUri.Visible = $false
    $tabCustomer.Controls.Add($lblADFSMetadataUri)
    
    $script:txtADFSMetadataUri = New-Object System.Windows.Forms.TextBox
    $txtADFSMetadataUri.Location = New-Object System.Drawing.Size($txtLeft,$row)
    $txtADFSMetadataUri.Size = New-Object System.Drawing.Size($txtSize)
    $txtADFSMetadataUri.Font = $defaultLabelFont
    $txtADFSMetadataUri.Name = 'txtADFSMetadataUri'
    $txtADFSMetadataUri.MaxLength = $lenURI
    $txtADFSMetadataUri.Enabled = $false
    $txtADFSMetadataUri.Visible = $false
    $txtADFSMetadataUri.Add_MouseHover($ShowHelp)
    $tabCustomer.Controls.Add($txtADFSMetadataUri)
    $txtADFSMetadataUri_Changed = `
    {
        if((-not [string]::IsNullOrEmpty($txtADFSMetadataUri.Text)) -and (-not(Get-IsValidURI -URI $txtADFSMetadataUri.Text)))
        {
            Close-WaitForm
            [System.Windows.Forms.MessageBox]::Show($locString.errURIInvalid, $locString.capValidationError, [System.Windows.Forms.MessageBoxButtons]::OK)
            $txtADFSMetadataUri.ForeColor = $defaultFailColor
            $txtADFSMetadataUri.Select()
        }
        else
        {
            $txtADFSMetadataUri.ForeColor = $defaultForeColor
        }
        $txtADFSMetadataUri.Refresh()
    }
    $txtADFSMetadataUri.Add_Leave($txtADFSMetadataUri_Changed)
    
    $row += 40
    
    # --------------------------- CUSTOMER INFO -------------------------------
    $script:lblCustomerInfo = New-Object System.Windows.Forms.Label
    $lblCustomerInfo.Location = New-Object System.Drawing.Size($lblLeft,($row))
    $lblCustomerInfo.AutoSize = $true
    $lblCustomerInfo.Text = $locString.lblCustomerInfo
    $lblCustomerInfo.Font = [System.Drawing.Font]::New("Microsoft Sans Serif", 12, [System.Drawing.FontStyle]::Bold)
    $tabCustomer.Controls.Add($lblCustomerInfo)
    
    $row += 40
    
    # ---------------------------- COMPANY NAME -------------------------------
    $script:lblCompanyName = New-Object System.Windows.Forms.Label
    $lblCompanyName.Location = New-Object System.Drawing.Size($lblLeft,($row+2))
    $lblCompanyName.MaximumSize = New-Object System.Drawing.Size($lblMaxSize)
    $lblCompanyName.AutoSize = $true
    $lblCompanyName.Font = $defaultLabelFont
    $lblCompanyName.Text = $locString.lblCompanyName
    $tabCustomer.Controls.Add($lblCompanyName)
    
    $script:txtCompanyName = New-Object System.Windows.Forms.TextBox
    $txtCompanyName.Location = New-Object System.Drawing.Size($txtLeft,$row)
    $txtCompanyName.Size = New-Object System.Drawing.Size($txtSize)
    $txtCompanyName.Font = $defaultLabelFont
    $txtCompanyName.Name = 'txtCompanyName'
    $txtCompanyName.MaxLength = $lenText
    $tabCustomer.Controls.Add($txtCompanyName)
    
    $row += 30
    
    # --------------------------- EXTERNAL DOMAIN -----------------------------
    $script:lblExternalDomain = New-Object System.Windows.Forms.Label
    $lblExternalDomain.Location = New-Object System.Drawing.Size($lblLeft,($row+2))
    $lblExternalDomain.MaximumSize = New-Object System.Drawing.Size($lblMaxSize)
    $lblExternalDomain.AutoSize = $true
    $lblExternalDomain.Font = $defaultLabelFont
    $lblExternalDomain.Text = $locString.lblExternalDomain
    $tabCustomer.Controls.Add($lblExternalDomain)
    
    $script:txtExternalDomain = New-Object System.Windows.Forms.TextBox
    $txtExternalDomain.Location = New-Object System.Drawing.Size($txtLeft,$row)
    $txtExternalDomain.Size = New-Object System.Drawing.Size($txtSize)
    $txtExternalDomain.Font = $defaultLabelFont
    $txtExternalDomain.Name = 'txtExternalDomain'
    $txtExternalDomain.MaxLength = $lenDomain
    $tabCustomer.Controls.Add($txtExternalDomain)
    $txtExternalDomain_Changed = `
    {
        if((-not [string]::IsNullOrEmpty($txtExternalDomain.Text)) -and ($txtExternalDomain.Text -notmatch $validDomain))
        {
            Close-WaitForm
            [System.Windows.Forms.MessageBox]::Show($locString.errDomainInvalid, $locString.capValidationError, [System.Windows.Forms.MessageBoxButtons]::OK)
            $txtExternalDomain.ForeColor = $defaultFailColor
            $txtExternalDomain.Select()
        }
        else
        {
            $txtExternalDomain.ForeColor = $defaultForeColor
        }
        $txtExternalDomain.Refresh()
    }
    $txtExternalDomain.Add_Leave($txtExternalDomain_Changed)
    
    $row += 30
    
    # --------------------------- PRIVATE DOMAIN ------------------------------
    $script:lblPrivateDomain = New-Object System.Windows.Forms.Label
    $lblPrivateDomain.Location = New-Object System.Drawing.Size($lblLeft,($row+2))
    $lblPrivateDomain.MaximumSize = New-Object System.Drawing.Size($lblMaxSize)
    $lblPrivateDomain.AutoSize = $true
    $lblPrivateDomain.Font = $defaultLabelFont
    $lblPrivateDomain.Text = $locString.lblPrivateDomain
    $tabCustomer.Controls.Add($lblPrivateDomain)
    
    $script:txtPrivateDomain = New-Object System.Windows.Forms.TextBox
    $txtPrivateDomain.Location = New-Object System.Drawing.Size($txtLeft,$row)
    $txtPrivateDomain.Size = New-Object System.Drawing.Size($txtSize)
    $txtPrivateDomain.Font = $defaultLabelFont
    $txtPrivateDomain.Name = 'txtPrivateDomain'
    $txtPrivateDomain.MaxLength = $lenDomain
    $tabCustomer.Controls.Add($txtPrivateDomain)
    $txtPrivateDomain_Changed = `
    {
        if((-not [string]::IsNullOrEmpty($txtPrivateDomain.Text)) -and ($txtPrivateDomain.Text -notmatch $validDomain))
        {
            Close-WaitForm
            [System.Windows.Forms.MessageBox]::Show($locString.errDomainInvalid, $locString.capValidationError, [System.Windows.Forms.MessageBoxButtons]::OK)
            $txtPrivateDomain.ForeColor = $defaultFailColor
            $txtPrivateDomain.Select()
        }
        else
        {
            $txtPrivateDomain.ForeColor = $defaultForeColor
        }
        $txtPrivateDomain.Refresh()
    }
    $txtPrivateDomain.Add_Leave($txtPrivateDomain_Changed)
    
    $row += 30
    
    # ---------------------------- REGION NAME --------------------------------
    $script:lblRegionName = New-Object System.Windows.Forms.Label
    $lblRegionName.Location = New-Object System.Drawing.Size($lblLeft,($row+2))
    $lblRegionName.MaximumSize = New-Object System.Drawing.Size($lblMaxSize)
    $lblRegionName.AutoSize = $true
    $lblRegionName.Font = $defaultLabelFont
    $lblRegionName.Text = $locString.lblRegionName
    $tabCustomer.Controls.Add($lblRegionName)
    
    $script:txtRegionName = New-Object System.Windows.Forms.TextBox
    $txtRegionName.Location = New-Object System.Drawing.Size($txtLeft,$row)
    $txtRegionName.Size = New-Object System.Drawing.Size($txtPrefixSize)
    $txtRegionName.Font = $defaultLabelFont
    $txtRegionName.Name = 'txtRegionName'
    $txtRegionName.MaxLength = $lenText
    $tabCustomer.Controls.Add($txtRegionName)
    $txtRegionName_Changed = `
    {
        if((-not [string]::IsNullOrEmpty($txtRegionName.Text)) -and ($txtRegionName.Text -notmatch $validAlphaNumeric))
        {
            Close-WaitForm
            [System.Windows.Forms.MessageBox]::Show($locString.errAlphaNumericInvalid, $locString.capValidationError, [System.Windows.Forms.MessageBoxButtons]::OK)
            $txtRegionName.ForeColor = $defaultFailColor
            $txtRegionName.Select()
        }
        else
        {
            $txtRegionName.ForeColor = $defaultForeColor
        }
        $txtRegionName.Refresh()
    }
    $txtRegionName.Add_Leave($txtRegionName_Changed)
    
    $row += 30
    
    # --------------------------- DEPLOY PREFIX -------------------------------
    $script:lblDeployPrefix = New-Object System.Windows.Forms.Label
    $lblDeployPrefix.Location = New-Object System.Drawing.Size($lblLeft,($row+2))
    $lblDeployPrefix.MaximumSize = New-Object System.Drawing.Size($lblMaxSize)
    $lblDeployPrefix.AutoSize = $true
    $lblDeployPrefix.Font = $defaultLabelFont
    $lblDeployPrefix.Text = $locString.lblDeployPrefix
    $tabCustomer.Controls.Add($lblDeployPrefix)
    
    $script:txtDeployPrefix = New-Object System.Windows.Forms.TextBox
    $txtDeployPrefix.Location = New-Object System.Drawing.Size($txtLeft,$row)
    $txtDeployPrefix.Size = New-Object System.Drawing.Size($txtPrefixSize)
    $txtDeployPrefix.Font = $defaultLabelFont
    $txtDeployPrefix.Name = 'txtDeployPrefix'
    $txtDeployPrefix.MaxLength = $lenPrefix
    $tabCustomer.Controls.Add($txtDeployPrefix)
    $txtDeployPrefix_Changed = `
    {
        if((-not [string]::IsNullOrEmpty($txtDeployPrefix.Text)) -and ($txtDeployPrefix.Text -notmatch $validAlphaNumeric))
        {
            Close-WaitForm
            [System.Windows.Forms.MessageBox]::Show($locString.errAlphaNumericInvalid, $locString.capValidationError, [System.Windows.Forms.MessageBoxButtons]::OK)
            $txtDeployPrefix.ForeColor = $defaultFailColor
            $txtDeployPrefix.Select()
        }
        else
        {
            $txtDeployPrefix.ForeColor = $defaultForeColor
        }
        $txtDeployPrefix.Refresh()
    }
    $txtDeployPrefix.Add_Leave($txtDeployPrefix_Changed)
    
    $row += 30
    
    # -------------------------- PHYSICAL PREFIX ------------------------------
    $script:lblPhysicalPrefix = New-Object System.Windows.Forms.Label
    $lblPhysicalPrefix.Location = New-Object System.Drawing.Size($lblLeft,($row+2))
    $lblPhysicalPrefix.MaximumSize = New-Object System.Drawing.Size($lblMaxSize)
    $lblPhysicalPrefix.AutoSize = $true
    $lblPhysicalPrefix.Font = $defaultLabelFont
    $lblPhysicalPrefix.Text = $locString.lblPhysicalPrefix
    $tabCustomer.Controls.Add($lblPhysicalPrefix)
    
    $script:txtPhysicalPrefix = New-Object System.Windows.Forms.TextBox
    $txtPhysicalPrefix.Location = New-Object System.Drawing.Size($txtLeft,$row)
    $txtPhysicalPrefix.Size = New-Object System.Drawing.Size($txtPrefixSize)
    $txtPhysicalPrefix.Font = $defaultLabelFont
    $txtPhysicalPrefix.Name = 'txtPhysicalPrefix'
    $txtPhysicalPrefix.MaxLength = $lenPrefix
    $tabCustomer.Controls.Add($txtPhysicalPrefix)
    $txtPhysicalPrefix_Changed = `
    {
        if((-not [string]::IsNullOrEmpty($txtPhysicalPrefix.Text)) -and ($txtPhysicalPrefix.Text -notmatch $validAlphaNumeric))
        {
            Close-WaitForm
            [System.Windows.Forms.MessageBox]::Show($locString.errAlphaNumericInvalid, $locString.capValidationError, [System.Windows.Forms.MessageBoxButtons]::OK)
            $txtPhysicalPrefix.ForeColor = $defaultFailColor
            $txtPhysicalPrefix.Select()
        }
        else
        {
            $txtPhysicalPrefix.ForeColor = $defaultForeColor
        }
        $txtPhysicalPrefix.Refresh()
    }
    $txtPhysicalPrefix.Add_Leave($txtPhysicalPrefix_Changed)
    
    $row += 40
    
    # -------------------------- ENVIRONMENT INFO -----------------------------
    $script:lblEnvironmentInfo = New-Object System.Windows.Forms.Label
    $lblEnvironmentInfo.Location = New-Object System.Drawing.Size($lblLeft,($row))
    $lblEnvironmentInfo.AutoSize = $true
    $lblEnvironmentInfo.Text = $locString.lblEnvironmentInfo
    $lblEnvironmentInfo.Font = [System.Drawing.Font]::New("Microsoft Sans Serif", 12, [System.Drawing.FontStyle]::Bold)
    $tabCustomer.Controls.Add($lblEnvironmentInfo)
    
    $row += 40
    
    # ---------------------------- TIME SERVER --------------------------------
    $script:lblTimeServer = New-Object System.Windows.Forms.Label
    $lblTimeServer.Location = New-Object System.Drawing.Size($lblLeft,($row+2))
    $lblTimeServer.MaximumSize = New-Object System.Drawing.Size($lblMaxSize)
    $lblTimeServer.AutoSize = $true
    $lblTimeServer.Font = $defaultLabelFont
    $lblTimeServer.Text = $locString.lblTimeServer
    $tabCustomer.Controls.Add($lblTimeServer)
    
    $script:txtTimeServer = New-Object System.Windows.Forms.TextBox
    $txtTimeServer.Location = New-Object System.Drawing.Size($txtLeft,$row)
    $txtTimeServer.Size = New-Object System.Drawing.Size($txtSize)
    $txtTimeServer.Font = $defaultLabelFont
    $txtTimeServer.Name = 'txtTimeServer'
    $txtTimeServer.MaxLength = $lenDomain
    $txtTimeServer.Add_MouseHover($ShowHelp)
    $tabCustomer.Controls.Add($txtTimeServer)
    $txtTimeServer_Changed = `
    {
        if(-not([string]::IsNullOrEmpty($txtTimeServer.Text)))
        {
            $valid = $false
            if($txtTimeServer.Text -match $validIPAddress)
            {
                $valid = $true
            }
            elseif($txtTimeServer.Text -match $validDomain)
            {
                $valid = $true
            }
            if(-not $valid)
            {
                Close-WaitForm
                [System.Windows.Forms.MessageBox]::Show($locString.errIPorFQDNInvalid, $locString.capValidationError, [System.Windows.Forms.MessageBoxButtons]::OK)
                $txtTimeServer.ForeColor = $defaultFailColor
                $txtTimeServer.Select()
            }
            else
            {
                $txtTimeServer.ForeColor = $defaultForeColor
            }
        }
        $txtTimeServer.Refresh()
    }
    $txtTimeServer.Add_Leave($txtTimeServer_Changed)
    
    $row += 30
    
    # ----------------------------- DNS SERVER --------------------------------
    $script:lblDNSForwarder = New-Object System.Windows.Forms.Label
    $lblDNSForwarder.Location = New-Object System.Drawing.Size($lblLeft,($row+2))
    $lblDNSForwarder.MaximumSize = New-Object System.Drawing.Size($lblMaxSize)
    $lblDNSForwarder.AutoSize = $true
    $lblDNSForwarder.Font = $defaultLabelFont
    $lblDNSForwarder.Text = $locString.lblDNSForwarder
    $tabCustomer.Controls.Add($lblDNSForwarder)
    
    $script:txtDNSForwarder = New-Object System.Windows.Forms.RichTextBox
    $txtDNSForwarder.Location = New-Object System.Drawing.Size($txtLeft,$row)
    $txtDNSForwarder.Size = New-Object System.Drawing.Size($txtSize)
    $txtDNSForwarder.Font = $defaultLabelFont
    $txtDNSForwarder.Name = 'txtDNSForwarder'
    $txtDNSForwarder.MaxLength = 32
    $txtDNSForwarder.Multiline = $false
    $tabCustomer.Controls.Add($txtDNSForwarder)
    $txtDNSForwarder.Add_MouseHover($ShowHelp)
    $txtDNSForwarder_Changed = `
    {
        if(-not([string]::IsNullOrEmpty($txtDNSForwarder.Text)))
        {
            $txtDNSForwarder.Text = [string]($txtDNSForwarder.Text).Replace(' ','')
            $txtDNSForwarder.Text = [string]($txtDNSForwarder.Text).Replace(';',',')
            $errItem = @()
            $dnsList = @()
            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 $maxDNSForwarders))
                        {
                            $newList += $dns
                        }
                    }
                    $dnsList = $newList
                    $txtDNSForwarder.Text = ($dnsList -join ',')
                }
            }
            else
            {
                $dnsList += $txtDNSForwarder.Text
            }
            foreach($dns in $dnsList)
            {
                if($dns -notmatch $validIPAddress)
                {
                    $errItem += $dns
                    $txtDNSForwarder.SelectionStart = $txtDNSForwarder.Text.IndexOf($dns)
                    $txtDNSForwarder.SelectionLength = $dns.Length
                    $txtDNSForwarder.SelectionColor = $defaultFailColor
                }
                else
                {
                    $txtDNSForwarder.SelectionStart = $txtDNSForwarder.Text.IndexOf($dns)
                    $txtDNSForwarder.SelectionLength = $dns.Length
                    $txtDNSForwarder.SelectionColor = $defaultForeColor
                    $txtDNSForwarder.SelectionStart = 0
                    $txtDNSForwarder.SelectionLength = 0
                }
            }
            if($errItem.Count -gt 0)
            {
                $errItem | ForEach-Object `
                {
                    [string]$errMessage += "$($locString.errInvalidIPList) $($PSItem)`n"
                }
                Close-WaitForm
                [System.Windows.Forms.MessageBox]::Show($errMessage, $locString.capValidationError, [System.Windows.Forms.MessageBoxButtons]::OK)
                $txtDNSForwarder.Select()
            }
            else
            {
                $txtDNSForwarder.SelectionStart = 0
                $txtDNSForwarder.SelectionLength = $txtDNSForwarder.Text.Length
                $txtDNSForwarder.ForeColor = $defaultForeColor
            }
        }
        $txtDNSForwarder.Refresh()
    }
    $txtDNSForwarder.Add_Leave($txtDNSForwarder_Changed)
    
    $row += 30
    
    # ---------------------------- SYSLOG SERVER ------------------------------
    $script:lblSyslogServer = New-Object System.Windows.Forms.Label
    $lblSyslogServer.Location = New-Object System.Drawing.Size($lblLeft,($row+2))
    $lblSyslogServer.MaximumSize = New-Object System.Drawing.Size($lblMaxSize)
    $lblSyslogServer.AutoSize = $true
    $lblSyslogServer.Font = $defaultLabelFont
    $lblSyslogServer.Text = $locString.lblSyslogServer
    $tabCustomer.Controls.Add($lblSyslogServer)
    
    $script:txtSyslogServer = New-Object System.Windows.Forms.TextBox
    $txtSyslogServer.Location = New-Object System.Drawing.Size($txtLeft,$row)
    $txtSyslogServer.Size = New-Object System.Drawing.Size($txtPrefixSize)
    $txtSyslogServer.Font = $defaultLabelFont
    $txtSyslogServer.Name = 'txtSyslogServer'
    $txtSyslogServer.MaxLength = $lenIPAddress
    $txtSyslogServer.Add_MouseHover($ShowHelp)
    $tabCustomer.Controls.Add($txtSyslogServer)
    $txtSyslogServer_Changed = `
    {
        if(-not([string]::IsNullOrEmpty($txtSyslogServer.Text)) -and ($txtSyslogServer.Text -notmatch $validIPAddress))
        {
            Close-WaitForm
            [System.Windows.Forms.MessageBox]::Show($locString.errIPAddressInvalid, $locString.capValidationError, [System.Windows.Forms.MessageBoxButtons]::OK)
            $txtSyslogServer.ForeColor = $defaultFailColor
            $txtSyslogServer.Select()
        }
        else
        {
            $txtSyslogServer.ForeColor = $defaultForeColor
        }
        $txtSyslogServer.Refresh()
    }
    $txtSyslogServer.Add_Leave($txtSyslogServer_Changed)
    
    $script:lblSyslogServerOptional = New-Object System.Windows.Forms.Label
    $lblSyslogServerOptional.Location = New-Object System.Drawing.Size(($txtSyslogServer.Right+8),($txtSyslogServer.Location.Y+2))
    $lblSyslogServerOptional.AutoSize = $true
    $lblSyslogServerOptional.Font = $defaultLabelFont
    $lblSyslogServerOptional.Text = $locString.strOptionalNote
    $tabCustomer.Controls.Add($lblSyslogServerOptional)
    
    # ============================== NETWORK TAB ==============================
    $row = 20
    
    # ----------------------------- DOCUMENT LINK -----------------------------
    $script:lnkNetworkDoc = New-Object System.Windows.Forms.LinkLabel
    $lnkNetworkDoc.Location = New-Object System.Drawing.Size(680,($row+2))
    $lnkNetworkDoc.Size = New-Object System.Drawing.Size(300,20)
    $lnkNetworkDoc.Font = $defaultLabelFont
    $lnkNetworkDoc.AutoSize = $true
    $lnkNetworkDoc.Text = $locString.networkLinkText
    $lnkNetworkDoc.Add_Click({[system.Diagnostics.Process]::Start($locString.networkLink)})
    $tabNetwork.Controls.Add($lnkNetworkDoc)
    
    # ---------------------------- NETWORK SETTINGS ---------------------------
    $script:lblNetworkSettings = New-Object System.Windows.Forms.Label
    $lblNetworkSettings.Location = New-Object System.Drawing.Size($lblLeft,($row))
    $lblNetworkSettings.AutoSize = $true
    $lblNetworkSettings.Text = $locString.lblNetworkSettings
    $lblNetworkSettings.Font = [System.Drawing.Font]::New("Microsoft Sans Serif", 12, [System.Drawing.FontStyle]::Bold)
    $tabNetwork.Controls.Add($lblNetworkSettings)
    
    $row += 40
    
    # ------------------------------- TOPOLOGY --------------------------------
    $script:lblTopology = New-Object System.Windows.Forms.Label
    $lblTopology.Location = New-Object System.Drawing.Size($lblLeft,($row))
    $lblTopology.AutoSize = $true
    $lblTopology.Text = $locString.lblTopology
    $lblTopology.Font = [System.Drawing.Font]::New("Microsoft Sans Serif", 10, [System.Drawing.FontStyle]::Bold)
    $tabNetwork.Controls.Add($lblTopology)
    
    $row += 30
    
    # ----------------------------- SCALE UNITS -------------------------------
    $script:lblScaleUnitCount = New-Object System.Windows.Forms.Label
    $lblScaleUnitCount.Location = New-Object System.Drawing.Size($lblLeft,($row+2))
    $lblScaleUnitCount.MaximumSize = New-Object System.Drawing.Size($lblMaxSize)
    $lblScaleUnitCount.AutoSize = $true
    $lblScaleUnitCount.Font = $defaultLabelFont
    $lblScaleUnitCount.Text = $locString.lblScaleUnitCount
    $tabNetwork.Controls.Add($lblScaleUnitCount)
    
    $script:txtScaleUnitCount = New-Object System.Windows.Forms.TextBox
    $txtScaleUnitCount.Location = New-Object System.Drawing.Size($txtLeftNet,$row)
    $txtScaleUnitCount.Size = New-Object System.Drawing.Size($txtCountSize)
    $txtScaleUnitCount.Font = $defaultLabelFont
    $txtScaleUnitCount.TextAlign = [System.Windows.Forms.HorizontalAlignment]::Center
    $txtScaleUnitCount.Name = 'txtScaleUnitCount'
    $txtScaleUnitCount.MaxLength = 1
    $txtScaleUnitCount.Text = $defaultSUCount
    $txtScaleUnitCount.Enabled = $false
    $txtScaleUnitCount.ReadOnly = $true
    $tabNetwork.Controls.Add($txtScaleUnitCount)
    $txtScaleUnitCount_Changed = `
    {
        if(-not([string]::IsNullOrEmpty($txtScaleUnitCount.Text)) -and ($txtScaleUnitCount.Text -notmatch "^[1-$($maxSU)]+$"))
        {
            [string]$errMessage = ($locString.errNumberNotInRange -f '1',$maxSU)
            Close-WaitForm
            [System.Windows.Forms.MessageBox]::Show($errMessage, $locString.capValidationError, [System.Windows.Forms.MessageBoxButtons]::OK)
            $txtScaleUnitCount.ForeColor = $defaultFailColor
            $txtScaleUnitCount.SelectionStart = 0
            $txtScaleUnitCount.SelectionLength = 1
            $txtScaleUnitCount.Select()
        }
        else
        {
            Remove-GeneratedTabs
            $txtScaleUnitCount.ForeColor = $defaultForeColor
            (1..$maxSU) | Foreach-Object `
            {
                if($PSItem -le [int]$txtScaleUnitCount.Text)
                {
                    Add-ScaleUnitTab -ID $PSItem
                }
                else
                {
                    Remove-ScaleUnit -ID $PSItem
                }
            }
            # Clear and repopulate the switch vendors list to insure all required switch types are available
            $currentSwitchVendor = $lstSwitchVendor.SelectedItem
            [void]$lstSwitchVendor.Items.Clear()
            Get-SwitchVendors | Foreach-Object `
            {
                $make = $PSItem
                $add = $true
                $requireSpine = $false
                if(([int]$txtScaleUnitCount.Text -gt 1) -and ($spineless -notcontains $make))
                {
                    $requireSpine = $true
                }
                foreach($type in $switchTypes)
                {
                    if(-not(($type -eq 'SPINE') -and (-not $requireSpine)))
                    {
                        [string[]]$result = Get-SwitchModels -Make $make -Type $type
                        if($result.Count -eq 0)
                        {
                            $add = $false
                            Write-Log -Message "WARNING: switch make '$($make)' does not contain any models for the required type '$($type)'" -Force
                        }
                    }
                }
                if($add)
                {
                    [void]$lstSwitchVendor.Items.Add($make)
                }
            }
            # If the previously selected switch vendor is still applicable set it back to that vendor
            if($currentSwitchVendor -and $lstSwitchVendor.Items -contains $currentSwitchVendor)
            {
                $lstSwitchVendor.SelectedItem = $currentSwitchVendor
            }
            else
            {
                $lstTORModel.SelectedIndex = -1
                $lstBMCModel.SelectedIndex = -1
                $lstSpineModel.SelectedIndex = -1
            }
            if([int]$txtScaleUnitCount.Text -gt 1)
            {
                # Multi Scale-Unit
                $script:validEdge = 4
                if($lstSwitchVendor.SelectedItem -eq "CiscoUCS")
                {
                    Disable-SpineSubnet
                    $lblSpineFirmware.Enabled = $false
                    $lblSpineFirmware.Visible = $false
                    $lstSpineFirmware.Enabled = $false
                    $lstSpineFirmware.Visible = $false
                    Clear-ListSelection $lstSpineFirmware
                    [void]$lstSpineModel.Items.Add('-none-')
                    $lstSpineModel.Enabled = $false
                    $lstSpineModel.Visible = $false
                    $lblSpineModel.Enabled = $false
                    $lblSpineModel.Visible = $false
                    $lstSpineModel.SelectedIndex = 0
                    Set-ScaleUnitsCiscoUCS -Enabled $false
                }
                else
                {
                    Enable-SpineSubnet
                    $lblSpineFirmware.Enabled = $true
                    $lblSpineFirmware.Visible = $true
                    $lstSpineFirmware.Enabled = $true
                    $lstSpineFirmware.Visible = $true
                    $lblSpineModel.Enabled = $true
                    $lblSpineModel.Visible = $true
                    $lstSpineModel.Enabled = $true
                    $lstSpineModel.Visible = $true
                    if(-not $retainSpineSettings)
                    {
                        Clear-ListSelection $lstSpineFirmware
                        if($lstSpineModel.Items.Count -gt 0)
                        {
                            $lstSpineModel.SelectedIndex = 0
                        }
                        else
                        {
                            Clear-ListSelection $lstSpineModel
                        }
                    }
                    Enable-SpineCount
                    Set-ScaleUnitsCiscoUCS -Enabled $true
                }            
                if($lstSwitchVendor.SelectedItem -eq "CiscoUCS")
                {
                    Disable-SpineBGP
                }
                else
                {
                    Enable-SpineBGP
                }
            }
            else
            {
                # Single Scale-Unit
                Disable-SpineCount
                Disable-SpineBGP
                Disable-SpineSubnet
                $lblSpineFirmware.Enabled = $false
                $lblSpineFirmware.Visible = $false
                $lstSpineFirmware.Enabled = $false
                $lstSpineFirmware.Visible = $false
                Clear-ListSelection $lstSpineFirmware
                $lblSpineModel.Enabled = $false
                $lblSpineModel.Visible = $false
                $lstSpineModel.Enabled = $false
                $lstSpineModel.Visible = $false
                Clear-ListSelection $lstSpineModel
                $script:validEdge = 2
                if($lstSwitchVendor.SelectedItem -eq "CiscoUCS")
                {
                    $script:validEdge = 4
                }
            }
        }
        if([int]$txtEdgeCount.Text -gt $validEdge)
        {
            $txtEdgeCount.Text = $validEdge
        }
        Move-PartnerTab
    }
    $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.ReadOnly = $true
    $tabNetwork.Controls.Add($txtTotalNodeCount)
    
    $row += 40
    $row2 = $row
    
    # ----------------------------- SWITCH INFO -------------------------------
    $script:lblSwitchInfo = New-Object System.Windows.Forms.Label
    $lblSwitchInfo.Location = New-Object System.Drawing.Size($lblLeft,($row))
    $lblSwitchInfo.AutoSize = $true
    $lblSwitchInfo.Text = $locString.lblSwitchInfo
    $lblSwitchInfo.Font = [System.Drawing.Font]::New("Microsoft Sans Serif", 10, [System.Drawing.FontStyle]::Bold)
    $tabNetwork.Controls.Add($lblSwitchInfo)
    
    $row += 30
    
    # ---------------------------- SWITCH VENDOR ------------------------------
    $script:lblSwitchVendor = New-Object System.Windows.Forms.Label
    $lblSwitchVendor.Location = New-Object System.Drawing.Size($lblLeft,($row+2))
    $lblSwitchVendor.MaximumSize = New-Object System.Drawing.Size($lblMaxSize)
    $lblSwitchVendor.AutoSize = $true
    $lblSwitchVendor.Font = $defaultLabelFont
    $lblSwitchVendor.Text = $locString.lblSwitchVendor
    $lblSwitchVendor.Visible = $true
    $tabNetwork.Controls.Add($lblSwitchVendor)
    
    $script:lstSwitchVendor = New-Object System.Windows.Forms.ComboBox
    $lstSwitchVendor.DropDownStyle = [System.Windows.Forms.ComboBoxStyle]::DropDownList
    $lstSwitchVendor.Location = New-Object System.Drawing.Size($txtLeft,$row)
    $lstSwitchVendor.Size = New-Object System.Drawing.Size($txtSizeNet)
    $lstSwitchVendor.Font = $defaultLabelFont
    $lstSwitchVendor.Name = 'lstSwitchVendor'
    $lstSwitchVendor.Visible = $true
    [void]$lstSwitchVendor.Items.Clear()
    Get-SwitchVendors | Foreach-Object `
    {
        [void]$lstSwitchVendor.Items.Add($PSItem)
    }
    $lstSwitchVendor.SelectedIndex = -1
    $lstSwitchVendor_Changed = `
    {
        [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($lstSwitchVendor.SelectedIndex -ge 0)
        {
            Get-SwitchModels -Make $lstSwitchVendor.SelectedItem -Type 'TOR' | Foreach-Object `
            {
                [void]$lstTORModel.Items.Add($PSItem)
            }
            $lstTORModel.SelectedIndex = 0
            $lstTORModel.Refresh()
            if($lstSwitchVendor.SelectedItem -eq "CiscoUCS")
            {
                $script:validEdge = 4
                [void]$lstBMCModel.Items.Add('-none-')
                $lstBMCModel.Enabled = $false
                $lstBMCModel.Visible = $false
                $lblBMCModel.Enabled = $false
                $lblBMCModel.Visible = $false
                
                [void]$lstSpineModel.Items.Add('-none-')
                $lstSpineModel.Enabled = $false
                $lstSpineModel.Visible = $false
                $lblSpineModel.Enabled = $false
                $lblSpineModel.Visible = $false
                $lstSpineModel.SelectedIndex = 0
                
                Disable-SpineBGP
                Disable-SpineCount
                Disable-SpineSubnet
                Set-ScaleUnitsCiscoUCS -Enabled $false
            }
            else
            {
                $script:validEdge = 2
                if([int]$txtScaleUnitCount.Text -gt 1)
                {
                    $script:validEdge = 4
                    Enable-SpineSubnet
                    Enable-SpineBGP
                    Enable-SpineCount
                    Set-ScaleUnitsCiscoUCS -Enabled $true
                }
                else
                {
                    Disable-SpineBGP
                    Disable-SpineCount
                    Disable-SpineSubnet
                }
                Get-SwitchModels -Make $lstSwitchVendor.SelectedItem -Type 'BMC' | Foreach-Object `
                {
                    [void]$lstBMCModel.Items.Add($PSItem)
                }
                $lblBMCModel.Enabled = $true
                $lblBMCModel.Visible = $true
                $lstBMCModel.Enabled = $true
                $lstBMCModel.Visible = $true
                
                $models = Get-SwitchModels -Make $lstSwitchVendor.SelectedItem -Type 'SPINE'
                if($models.Count -gt 0)
                {
                    $models | Foreach-Object `
                    {
                        [void]$lstSpineModel.Items.Add($PSItem)
                    }
                    if($txtScaleUnitCount.Text -eq '1')
                    {
                        $lblSpineModel.Enabled = $false
                        $lblSpineModel.Visible = $false
                        $lstSpineModel.Enabled = $false
                        $lstSpineModel.Visible = $false
                        Clear-ListSelection $lstSpineModel
                    }
                    else
                    {
                        $lblSpineModel.Enabled = $true
                        $lblSpineModel.Visible = $true
                        $lstSpineModel.Enabled = $true
                        $lstSpineModel.Visible = $true
                        $lstSpineModel.SelectedIndex = 0
                    }
                }
                else
                {
                    $lblSpineModel.Enabled = $false
                    $lblSpineModel.Visible = $false
                    $lstSpineModel.Enabled = $false
                    $lstSpineModel.Visible = $false
                    Clear-ListSelection $lstSpineModel
                    
                    $lblSpineFirmware.Enabled = $false
                    $lblSpineFirmware.Visible = $false
                    $lstSpineFirmware.Enabled = $false
                    $lstSpineFirmware.Visible = $false
                    Clear-ListSelection $lstSpineFirmware
                }
            }
            if([int]$txtEdgeCount.Text -gt $validEdge)
            {
                $txtEdgeCount.Text = $validEdge
            }
            $lstBMCModel.SelectedIndex = 0
            $lstBMCModel.Refresh()
            $lstSpineModel.Refresh()
        }
    }
    $lstSwitchVendor.Add_SelectedIndexChanged($lstSwitchVendor_Changed)
    $tabNetwork.Controls.Add($lstSwitchVendor)
    
    $row += 25
    
    # ------------------------------ TOR MODEL --------------------------------
    $script:lblTORModel = New-Object System.Windows.Forms.Label
    $lblTORModel.Location = New-Object System.Drawing.Size($lblLeft,($row+2))
    $lblTORModel.MaximumSize = New-Object System.Drawing.Size($lblMaxSize)
    $lblTORModel.AutoSize = $true
    $lblTORModel.Font = $defaultLabelFont
    $lblTORModel.Text = $locString.lblTORModel
    $lblTORModel.Visible = $true
    $tabNetwork.Controls.Add($lblTORModel)
    
    $script:lstTORModel = New-Object System.Windows.Forms.ComboBox
    $lstTORModel.DropDownStyle = [System.Windows.Forms.ComboBoxStyle]::DropDownList
    $lstTORModel.Location = New-Object System.Drawing.Size($txtLeft,$row)
    $lstTORModel.Size = New-Object System.Drawing.Size($txtSizeNet)
    $lstTORModel.Font = $defaultLabelFont
    $lstTORModel.Name = 'lstTORModel'
    $lstTORModel.Visible = $true
    [void]$lstTORModel.Items.Clear()
    $lstTORModel.SelectedIndex = -1
    $lstTORModel_Changed = `
    {
        [void]$lstTORFirmware.Items.Clear()
        if($lstTORModel.SelectedItem)
        {
            $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
            }
        }
        else
        {
            Clear-ListSelection $lstTORFirmware
        }
        $lstTORFirmware.Refresh()
    }
    $lstTORModel.Add_SelectedIndexChanged($lstTORModel_Changed)
    $tabNetwork