BounShell.psm1

<#
    .SYNOPSIS
 
    This is a tool to help users manage multiple Office 365 tenants
 
    .DESCRIPTION
 
    Created by James Arber. www.UcMadScientist.com
     
    .NOTES
 
    Version : 0.6.5
    Date : 18/09/2019
    Lync Version : Tested against Skype4B 2015
    Author : James Arber
    Header stolen from : Greig Sheridan who stole it from Pat Richard's amazing "Get-CsConnections.ps1"
    Special Thanks to : My Beta Testers. Greig Sheridan, Pat Richard and Justin O'Meara
 
 
    :v0.6.5: Lets Make Some Mayhem Release
    Quick bugfix whilst I get 0.7 with it's new GUI ready.
    Added fix to Skype Online sessions with modern auth.
    No longer hangs the ISE trying to display a window for reconnection
     
 
    :v0.6.4: Public Beta Release
    PowerShell package management, (installs, updates and removes old versions of required modules)
    AutoUpdate's moves to package management
    Fixed AzureAD module deleting credentials out of unrelated variables
    Refractoring to support more than 10 tenants (internal testing only)
    Fixed the Autoupdate and Modern Auth checkboxes not saving to the config file
    Improved Modern Auth clipboard behaviour
    Fixed up alot of formatting and readibility issues with ISE Steroids
     
 
 
    :v0.6.3: Limited Beta Release
    Enabled AD Security and Compliance Center connection
    Enabled Azure AD connection
    Initial work for config file 0.3 to allow for more than 10 tenants
    Cleaned out a redundant function
    Lots of capitalization fixes
    Log file fixes
 
    :v0.6.2: Limited Beta Release
    Fixed PowerShell Nuget packaging
    Fixed bug in update code cause by move to SymVer (Thanks Greig)
    Fixed Typo's in configuration grid
     
    :v0.6.1: Limited Beta Release
    Moved to SymVer versioning
    Pubished to PowerShell gallery
           
 
    :v0.6: Closed Beta Release
    Enabled Modern Auth Support
    Formating changes
    Broke up alot of my one-liners to make it easier for others to read/ understand the flow
    Updated error messages
    Better code comments
    Fixed an issue with the Compliance Portal code
    Added Module checker and installer based off Andrew Price's "Detect-MicrosoftTeams-Version" http://www.blogabout.cloud/2018/09/240/
    Now Gluten Free
    Finally stopped feature creep
 
    :v0.5: Closed Beta Release
 
    Disclaimer: Whilst I take considerable effort to ensure this script is error free and wont harm your enviroment.
    I have no way to test every possible senario it may be used in. I provide these scripts free
    to the Lync and Skype4B community AS IS without any warranty on its appropriateness for use in
    your environment. I disclaim all implied warranties including,
    without limitation, any implied warranties of merchantability or of fitness for a particular
    purpose. The entire risk arising out of the use or performance of the sample scripts and
    documentation remains with you. In no event shall I be liable for any damages whatsoever
    (including, without limitation, damages for loss of business profits, business interruption,
    loss of business information, or other pecuniary loss) arising out of the use of or inability
    to use the script or documentation.
 
    Acknowledgements
    : Testing and Advice
    Greig Sheridan https://greiginsydney.com/about/ @greiginsydney
 
    : Auto Update Code
    Pat Richard https://ucunleashed.com @patrichard
 
    : Proxy Detection
    Michel de Rooij http://eightwone.com
 
    .LINK
    https://www.UcMadScientist.com/BounShell
 
    .KNOWN ISSUES
    Check https://github.com/Atreidae/BounShell/issues/
 
    .EXAMPLE
    Loads the Module
    PS C:\> Start-BounShell.ps1
 
#>


[CmdletBinding(DefaultParametersetName = 'Common')]
param
(
   [switch]$SkipUpdateCheck,
   [String]$ConfigFilePath = $null,
   [String]$LogFileLocation = $null,
   [float]$Tenant = $null

)

#region config
[Net.ServicePointManager]::SecurityProtocol = 'tls12, tls11, tls'
$StartTime                          = Get-Date
$VerbosePreference                  = 'SilentlyContinue' #TODO
[String]$ScriptVersion              = '0.6.5'
[string]$GithubRepo                 = 'BounShell'
[string]$GithubBranch               = 'master' #todo
[string]$BlogPost                   = 'https://www.UcMadScientist.com/BounShell/' 

#Supported Modules

[String]$TestedTeamsModule          = 'MicrosoftTeams'
[String]$TestedTeamsModuleVer       = '1.0.0'
[String]$TestedExchangeModule       = 'ExchangeOnlineShell' #Using the community version without MFA support. Official version is a clickonce app
[String]$TestedExchangeModuleVer    = '2.0.3.2'
[String]$TestedMSOnlineModule       = 'MsOnline'
[String]$TestedMSOnlineModuleVer    = '1.1.183.17'
[String]$TestedSkype4BOModule       = 'SkypeOnlineConnector'
[String]$TestedSkype4BOModuleVer    = '7.0.0'
[String]$TestedSharepointModule     = 'Microsoft.Online.Sharepoint.PowerShell' #Not used yet
[String]$TestedSharepointModuleVer  = '16.0.8812.1200' #Not used yet
[String]$TestedAzureADModule        = 'AzureAD' #Not used yet
[String]$TestedAzureADModuleVer     = '2.0.2.16' #Not used yet
[String]$TestedAzureADRMModule      = 'AADRM' #Not used yet
[String]$TestedAzureADRMModuleVer   = '2.13.1.0' #Not used yet
#[String]$TestedComplianceModule = #Not used. Uses New-PSSession
#[String]$TestedComplianceModuleVer =

#Check to see if paths were specified, Otherwise set defaults
If (!$LogFileLocation) 
{
  $global:LogFileLocation = "$ENV:UserProfile\BounShell.log"
}

If (!$ConfigFilePath) 
{
  $global:ConfigFilePath = "$ENV:UserProfile\BounShell.xml"
}

#endregion config


Function Write-Log
{
  <#
      .SYNOPSIS
      Function to output messages to the console based on their severity and create log files
 
      .DESCRIPTION
      It's a logger.
 
      .PARAMETER Message
      The message to write
 
      .PARAMETER Path
      The location of the logfile.
 
      .PARAMETER Severity
      Sets the severity of the log message, Higher severities will call Write-Warning or Write-Error
 
      .PARAMETER Component
      Used to track the module or function that called "Write-Log"
 
      .PARAMETER LogOnly
      Forces Write-Log to not display anything to the user
 
      .EXAMPLE
      Write-Log -Message 'This is a log message' -Severity 3 -component 'Example Component'
      Writes a log file message and displays a warning to the user
 
      .NOTES
      N/A
 
      .LINK
      http://www.UcMadScientist.com
 
      .INPUTS
      This function does not accept pipelined input
 
      .OUTPUTS
      This function does not create pipelined output
  #>

  [CmdletBinding()]
  PARAM
  (
    [String]$Message,
    [String]$Path = $global:LogFileLocation,
    [int]$Severity = 1,
    [string]$Component = 'Default',
    [switch]$LogOnly
  )
  $Date             = Get-Date -Format 'HH:mm:ss'
  $Date2            = Get-Date -Format 'MM-dd-yyyy'
  $MaxLogFileSizeMB = 10
  
  If(Test-Path -Path $Path)
  {
    if(((Get-ChildItem -Path $Path).length/1MB) -gt $MaxLogFileSizeMB) # Check the size of the log file and archive if over the limit.
    {
      $ArchLogfile = $Path.replace('.log', "_$(Get-Date -Format dd-MM-yyy_hh-mm-ss).lo_")
      Rename-Item -Path ren -NewName $Path -Path $ArchLogfile
    }
  }
         
  "$env:ComputerName date=$([char]34)$Date2$([char]34) time=$([char]34)$Date$([char]34) component=$([char]34)$Component$([char]34) type=$([char]34)$Severity$([char]34) Message=$([char]34)$Message$([char]34)"| Out-File -FilePath $Path -Append -NoClobber -Encoding default
  If (!$LogOnly) 
  {
    #If LogOnly is set, we dont want to write anything to the screen as we are capturing data that might look bad onscreen
      
      
    #If the log entry is just Verbose (1), output it to verbose
    if ($Severity -eq 1) 
    {
      "$Date $Message"| Write-Verbose
    }
      
    #If the log entry is just informational (2), output it to write-host
    if ($Severity -eq 2) 
    {
      "Info: $Date $Message"| Write-Host -ForegroundColor Green
    }
    #If the log entry has a severity of 3 assume it's a warning and write it to write-warning
    if ($Severity -eq 3) 
    {
      "$Date $Message"| Write-Warning
    }
    #If the log entry has a severity of 4 or higher, assume it's an error and display an error message (Note, critical errors are caught by throw statements so may not appear here)
    if ($Severity -ge 4) 
    {
      "$Date $Message"| Write-Error
    }
  }
}

Function Get-IEProxy
{
  Write-Log -component $function -Message 'Checking for Proxy' -severity 2
  If ( (Get-ItemProperty -Path 'HKCU:\Software\Microsoft\Windows\CurrentVersion\Internet Settings').ProxyEnable -ne 0)
  {
    $proxies = (Get-ItemProperty -Path 'HKCU:\Software\Microsoft\Windows\CurrentVersion\Internet Settings').proxyServer
    if ($proxies) 
    {
      if ($proxies -ilike '*=*')
      {
        return $proxies -replace '=', '://' -split (';') | Select-Object -First 1
      }
      
      Else 
      {
        return ('http://{0}' -f $proxies)
      }
    }
    
    Else 
    {
      return $null
    }
  }
  Else 
  {
    return $null
  }
}

Function Get-ScriptUpdate 
{
  $function = 'Get-ScriptUpdate'
  Write-Log -component $function -Message 'Checking for Script Update' -severity 1
  Write-Log -component $function -Message 'Checking for Proxy' -severity 1
  $ProxyURL = Get-IEProxy
  
  If ($ProxyURL)
  
  {
    Write-Log -component $function -Message "Using proxy address $ProxyURL" -severity 1
  }
  
  Else
  {
    Write-Log -component $function -Message 'No proxy setting detected, using direct connection' -severity 1
  }

  Write-Log -component $function -Message "Polling https://raw.githubusercontent.com/atreidae/$GithubRepo/$GithubBranch/version" -severity 1
  $GitHubScriptVersion = Invoke-WebRequest -Uri "https://raw.githubusercontent.com/atreidae/$GithubRepo/$GithubBranch/version" -TimeoutSec 10 -Proxy $ProxyURL #todo change back to master!
  
  If ($GitHubScriptVersion.Content.length -eq 0) 
  {
    #Empty data, throw an error
    Write-Log -component $function -Message 'Error checking for new version. You can check manually here' -severity 3
    Write-Log -component $function -Message $BlogPost -severity 1 #Todo Update URL
    Write-Log -component $function -Message 'Pausing for 5 seconds' -severity 1
    Start-Sleep -Seconds 5
  }
  else
  {
    #Process the returned data
    #Symver support!
    [string]$Symver = ($GitHubScriptVersion.Content)
    $splitgitver = $Symver.split('.') 
    $splitver = $ScriptVersion.split('.')
    $needsupdate = $false
    #Check for Major version

    if ([single]$splitgitver[0] -gt [single]$splitver[0])
    {
      $Needupdate = $true
      #New Major Build available, #Prompt user to download
      Write-Log -component $function -Message 'New Major Version Available' -severity 3
      $title = 'Update Available'
      $Message = 'a major update to this script is available, did you want to download it?'
    }

    if ([single]$splitgitver[1] -gt [single]$splitver[1])
    {
      $Needupdate = $true
      #New Major Build available, #Prompt user to download
      Write-Log -component $function -Message 'New Minor Version Available' -severity 3
      $title = 'Update Available'
      $Message = 'a minor update to this script is available, did you want to download it?'
    }

    if ([single]$splitgitver[2] -gt [single]$splitver[2])
    {
      $Needupdate = $true
      #New Major Build available, #Prompt user to download
      Write-Log -component $function -Message 'New Bugfix Available' -severity 3
      $title = 'Update Available'
      $Message = 'a bugfix update to this script is available, did you want to download it?'
    }

    If($Needupdate)
    {
      $yes = New-Object -TypeName System.Management.Automation.Host.ChoiceDescription -ArgumentList '&Yes', `
      'Update the installed PowerShell Module'

      $no = New-Object -TypeName System.Management.Automation.Host.ChoiceDescription -ArgumentList '&No', `
      'No thanks.'

      $options = [System.Management.Automation.Host.ChoiceDescription[]]($yes, $no)

      $result = $host.ui.PromptForChoice($title, $Message, $options, 0) 

      switch ($result)
      {
        0 
        {
          #User said yes
          Write-Log -component $function -Message 'User opted to download update' -severity 1
          #start $BlogPost
          Repair-BsInstalledModules -ModuleName 'BounShell' -Operation 'Update'
          Write-Log -component $function -Message 'Exiting Script' -severity 3
          Pause
          exit
        }
        #User said no
        1 
        {
          Write-Log -component $function -Message 'User opted to skip update' -severity 1
        }
      }
    }
    
    #We already have the latest version
    Else
    {
      Write-Log -component $function -Message 'Script is upto date' -severity 1
    }
  }
}

Function Upgrade-BsConfigFile
{
  <#
      .SYNOPSIS
      Function to upgrade BounShell Config files
 
      .LINK
      http://www.UcMadScientist.com
 
      .INPUTS
      This function does not accept pipelined input
 
      .OUTPUTS
      This function does not create pipelined output
  #>

  $function = 'Upgrade-BsConfigFile'
  Write-Log -component $function
  


  Write-Log -component $function -Message "Found Config File Version $($global:Config.ConfigFileVersion)" -severity 2

  #Backup the current config
  Try
  {
    $ShrtDate = Get-Date -Format 'dd-MM-yy'
    $global:Config| Export-Clixml -Path "$ENV:UserProfile\BounShell-backup-$ShrtDate.xml"
    Write-Log -component $function -Message 'Backup File Saved' -severity 2
  }
  Catch 
  {
    Write-Log -component $function -Message 'Error writing Config Backup file' -severity 4
    Write-Log -component $function -Message "Sorry, something went wrong here and I couldn't backup your BounShell config. Please check permissions to create $ENV:UserProfile\BounShell-backup-$ShrtDate.xml" -severity 3
    Throw 'Bad File Operation, Abort Script'
  }
 
  If ($global:Config.ConfigFileVersion -lt '0.2')
  {
    Write-Log -component $function -Message 'Adding Version 0.2 changes' -severity 2
    #Config File Version 0.2 additions
    $global:Config.AutoUpdatesEnabled = $true
    $global:Config.ModernAuthClipboardEnabled = $true
    $global:Config.ModernAuthWarningAccepted = $false
    $global:Config.Tenant1.ConnectToAzureAD = $false
    $global:Config.Tenant1.ConnectToCompliance = $false
    $global:Config.Tenant2.ConnectToAzureAD = $false
    $global:Config.Tenant2.ConnectToCompliance = $false
    $global:Config.Tenant3.ConnectToAzureAD = $false
    $global:Config.Tenant3.ConnectToCompliance = $false
    $global:Config.Tenant4.ConnectToAzureAD = $false
    $global:Config.Tenant4.ConnectToCompliance = $false
    $global:Config.Tenant5.ConnectToAzureAD = $false
    $global:Config.Tenant5.ConnectToCompliance = $false
    $global:Config.Tenant6.ConnectToAzureAD = $false
    $global:Config.Tenant6.ConnectToCompliance = $false
    $global:Config.Tenant7.ConnectToAzureAD = $false
    $global:Config.Tenant7.ConnectToCompliance = $false
    $global:Config.Tenant8.ConnectToAzureAD = $false
    $global:Config.Tenant8.ConnectToCompliance = $false
    $global:Config.Tenant9.ConnectToAzureAD = $false
    $global:Config.Tenant9.ConnectToCompliance = $false
    $global:Config.Tenant10.ConnectToAzureAD = $false
    $global:Config.Tenant10.ConnectToCompliance = $false
    [Float]$global:Config.ConfigFileVersion = '0.2'
  } 
 
 

  #Write the XML File
  Try
  {
    $global:Config| Export-Clixml -Path "$ENV:UserProfile\BounShell.xml"
    Write-Log -component $function -Message "Config File Updated to Version $($global:Config.ConfigFileVersion)" -severity 2
  }
  Catch 
  {
    Write-Log -component $function -Message 'Error writing Config file' -severity 3
  }    
}

Function Read-BsConfigFile
{
  $function = 'Read-BsConfigFile'
  Write-Log -component $function
  Write-Log -component $function -Message "Reading Config file $($global:ConfigFilePath)" -severity 2
  If(!(Test-Path $global:ConfigFilePath)) 
  
  {
    #Cant locate test file, Throw error
    Write-Log -component $function -Message 'Could not locate config file!' -severity 3
    Write-Log -component $function -Message 'Error reading Config, Loading Defaults' -severity 3
    Import-BsDefaultConfig
  }
  Else
  {
    #Found the config file
    Write-Log -component $function -Message 'Found Config file in the specified folder' -severity 1
  }

  Write-Log -component $function -Message 'Pulling XML data' -severity 1
  $null = (Remove-Variable -Name Config -Scope Global -ErrorAction SilentlyContinue )
  
  Try
  {
    #Load the Config
    $global:Config = @{}
    $global:Config = (Import-Clixml -Path $global:ConfigFilePath)
    Write-Log -component $function -Message 'Config File Read OK' -severity 2
    
    #Check the config file version
    If ($global:Config.ConfigFileVersion -lt 0.2)
    {
      Write-Log -component $function -Message 'Old Config File Detected. Upgrading config file' -severity 3
      Upgrade-BsConfigFile
    }
    



    #Config file is good.



    #Update the Gui options if we are loaded in the ISE
    If($PSISE) 
    {
      #Update the PS ISE Addon Menu
      Update-BsAddonMenu
    }
    
    #Populate with Values
    $null =  $Global:grid_Tenants.Rows.Clear()
    $null =  $Global:grid_Tenants.Rows.Add('1',$global:Config.Tenant1.DisplayName,$global:Config.Tenant1.SignInAddress,'****',$global:Config.Tenant1.ModernAuth,$global:Config.Tenant1.ConnectToTeams,$global:Config.Tenant1.ConnectToSkype,$global:Config.Tenant1.ConnectToExchange,$global:Config.Tenant1.ConnectToAzureAD,$global:Config.Tenant1.ConnectToCompliance)
    $null =  $Global:grid_Tenants.Rows.Add('2',$global:Config.Tenant2.DisplayName,$global:Config.Tenant2.SignInAddress,'****',$global:Config.Tenant2.ModernAuth,$global:Config.Tenant2.ConnectToTeams,$global:Config.Tenant2.ConnectToSkype,$global:Config.Tenant2.ConnectToExchange,$global:Config.Tenant2.ConnectToAzureAD,$global:Config.Tenant2.ConnectToCompliance)
    $null =  $Global:grid_Tenants.Rows.Add('3',$global:Config.Tenant3.DisplayName,$global:Config.Tenant3.SignInAddress,'****',$global:Config.Tenant3.ModernAuth,$global:Config.Tenant3.ConnectToTeams,$global:Config.Tenant3.ConnectToSkype,$global:Config.Tenant3.ConnectToExchange,$global:Config.Tenant3.ConnectToAzureAD,$global:Config.Tenant3.ConnectToCompliance)
    $null =  $Global:grid_Tenants.Rows.Add('4',$global:Config.Tenant4.DisplayName,$global:Config.Tenant4.SignInAddress,'****',$global:Config.Tenant4.ModernAuth,$global:Config.Tenant4.ConnectToTeams,$global:Config.Tenant4.ConnectToSkype,$global:Config.Tenant4.ConnectToExchange,$global:Config.Tenant4.ConnectToAzureAD,$global:Config.Tenant4.ConnectToCompliance)
    $null =  $Global:grid_Tenants.Rows.Add('5',$global:Config.Tenant5.DisplayName,$global:Config.Tenant5.SignInAddress,'****',$global:Config.Tenant5.ModernAuth,$global:Config.Tenant5.ConnectToTeams,$global:Config.Tenant5.ConnectToSkype,$global:Config.Tenant5.ConnectToExchange,$global:Config.Tenant5.ConnectToAzureAD,$global:Config.Tenant5.ConnectToCompliance)
    $null =  $Global:grid_Tenants.Rows.Add('6',$global:Config.Tenant6.DisplayName,$global:Config.Tenant6.SignInAddress,'****',$global:Config.Tenant6.ModernAuth,$global:Config.Tenant6.ConnectToTeams,$global:Config.Tenant6.ConnectToSkype,$global:Config.Tenant6.ConnectToExchange,$global:Config.Tenant6.ConnectToAzureAD,$global:Config.Tenant6.ConnectToCompliance)
    $null =  $Global:grid_Tenants.Rows.Add('7',$global:Config.Tenant7.DisplayName,$global:Config.Tenant7.SignInAddress,'****',$global:Config.Tenant7.ModernAuth,$global:Config.Tenant7.ConnectToTeams,$global:Config.Tenant7.ConnectToSkype,$global:Config.Tenant7.ConnectToExchange,$global:Config.Tenant7.ConnectToAzureAD,$global:Config.Tenant7.ConnectToCompliance)
    $null =  $Global:grid_Tenants.Rows.Add('8',$global:Config.Tenant8.DisplayName,$global:Config.Tenant8.SignInAddress,'****',$global:Config.Tenant8.ModernAuth,$global:Config.Tenant8.ConnectToTeams,$global:Config.Tenant8.ConnectToSkype,$global:Config.Tenant8.ConnectToExchange,$global:Config.Tenant8.ConnectToAzureAD,$global:Config.Tenant8.ConnectToCompliance)
    $null =  $Global:grid_Tenants.Rows.Add('9',$global:Config.Tenant9.DisplayName,$global:Config.Tenant9.SignInAddress,'****',$global:Config.Tenant9.ModernAuth,$global:Config.Tenant9.ConnectToTeams,$global:Config.Tenant9.ConnectToSkype,$global:Config.Tenant9.ConnectToExchange,$global:Config.Tenant9.ConnectToAzureAD,$global:Config.Tenant9.ConnectToCompliance)
    $null =  $Global:grid_Tenants.Rows.Add('10',$global:Config.Tenant10.DisplayName,$global:Config.Tenant10.SignInAddress,'****',$global:Config.Tenant10.ModernAuth,$global:Config.Tenant10.ConnectToTeams,$global:Config.Tenant10.ConnectToSkype,$global:Config.Tenant10.ConnectToExchange,$global:Config.Tenant10.ConnectToAzureAD,$global:Config.Tenant10.ConnectToCompliance)
    
    
    $Global:cbx_AutoUpdates.Checked = $Global:Config.AutoUpdatesEnabled
    $Global:cbx_ClipboardAuth.Checked = $Global:Config.ModernAuthClipboardEnabled
  }
    
  Catch
  {
    #For some reason we ran into an issue updating variables, throw an error and revert to defaults
    Write-Log -component $function -Message 'Error reading Config or updating GUI, Loading Defaults' -severity 3
    Import-BsDefaultConfig
  }
}

Function Write-BsConfigFile
{
  $function = 'Write-BsConfigFile'
  Write-Log -component $function -Message 'Writing Config file' -severity 2
  
  #Grab items from the GUI and stuff them into something useful

  $global:Config.Tenant1.DisplayName = $Global:grid_Tenants.Rows[0].Cells[1].Value
  $global:Config.Tenant1.SignInAddress = $Global:grid_Tenants.Rows[0].Cells[2].Value
  $global:Config.Tenant1.ModernAuth = $Global:grid_Tenants.Rows[0].Cells[4].Value
  $global:Config.Tenant1.ConnectToTeams = $Global:grid_Tenants.Rows[0].Cells[5].Value
  $global:Config.Tenant1.ConnectToSkype = $Global:grid_Tenants.Rows[0].Cells[6].Value
  $global:Config.Tenant1.ConnectToExchange = $Global:grid_Tenants.Rows[0].Cells[7].Value
  $global:Config.Tenant1.ConnectToAzureAD = $Global:grid_Tenants.Rows[0].Cells[8].Value
  $global:Config.Tenant1.ConnectToCompliance = $Global:grid_Tenants.Rows[0].Cells[9].Value
 

  $global:Config.Tenant2.DisplayName = $Global:grid_Tenants.Rows[1].Cells[1].Value
  $global:Config.Tenant2.SignInAddress = $Global:grid_Tenants.Rows[1].Cells[2].Value
  $global:Config.Tenant2.ModernAuth = $Global:grid_Tenants.Rows[1].Cells[4].Value
  $global:Config.Tenant2.ConnectToTeams = $Global:grid_Tenants.Rows[1].Cells[5].Value
  $global:Config.Tenant2.ConnectToSkype = $Global:grid_Tenants.Rows[1].Cells[6].Value
  $global:Config.Tenant2.ConnectToExchange = $Global:grid_Tenants.Rows[1].Cells[7].Value
  $global:Config.Tenant2.ConnectToAzureAD = $Global:grid_Tenants.Rows[1].Cells[8].Value
  $global:Config.Tenant2.ConnectToCompliance = $Global:grid_Tenants.Rows[1].Cells[9].Value


  $global:Config.Tenant3.DisplayName = $Global:grid_Tenants.Rows[2].Cells[1].Value
  $global:Config.Tenant3.SignInAddress = $Global:grid_Tenants.Rows[2].Cells[2].Value
  $global:Config.Tenant3.ModernAuth = $Global:grid_Tenants.Rows[2].Cells[4].Value
  $global:Config.Tenant3.ConnectToTeams = $Global:grid_Tenants.Rows[2].Cells[5].Value
  $global:Config.Tenant3.ConnectToSkype = $Global:grid_Tenants.Rows[2].Cells[6].Value
  $global:Config.Tenant3.ConnectToExchange = $Global:grid_Tenants.Rows[2].Cells[7].Value
  $global:Config.Tenant3.ConnectToAzureAD = $Global:grid_Tenants.Rows[2].Cells[8].Value
  $global:Config.Tenant3.ConnectToCompliance = $Global:grid_Tenants.Rows[2].Cells[9].Value

 
  $global:Config.Tenant4.DisplayName = $Global:grid_Tenants.Rows[3].Cells[1].Value
  $global:Config.Tenant4.SignInAddress = $Global:grid_Tenants.Rows[3].Cells[2].Value
  $global:Config.Tenant4.ModernAuth = $Global:grid_Tenants.Rows[3].Cells[4].Value
  $global:Config.Tenant4.ConnectToTeams = $Global:grid_Tenants.Rows[3].Cells[5].Value
  $global:Config.Tenant4.ConnectToSkype = $Global:grid_Tenants.Rows[3].Cells[6].Value
  $global:Config.Tenant4.ConnectToExchange = $Global:grid_Tenants.Rows[3].Cells[7].Value
  $global:Config.Tenant4.ConnectToAzureAD = $Global:grid_Tenants.Rows[3].Cells[8].Value
  $global:Config.Tenant4.ConnectToCompliance = $Global:grid_Tenants.Rows[3].Cells[9].Value

 
  $global:Config.Tenant5.DisplayName = $Global:grid_Tenants.Rows[4].Cells[1].Value
  $global:Config.Tenant5.SignInAddress = $Global:grid_Tenants.Rows[4].Cells[2].Value
  $global:Config.Tenant5.ModernAuth = $Global:grid_Tenants.Rows[4].Cells[4].Value
  $global:Config.Tenant5.ConnectToTeams = $Global:grid_Tenants.Rows[4].Cells[5].Value
  $global:Config.Tenant5.ConnectToSkype = $Global:grid_Tenants.Rows[4].Cells[6].Value
  $global:Config.Tenant5.ConnectToExchange = $Global:grid_Tenants.Rows[4].Cells[7].Value
  $global:Config.Tenant5.ConnectToAzureAD = $Global:grid_Tenants.Rows[4].Cells[8].Value
  $global:Config.Tenant5.ConnectToCompliance = $Global:grid_Tenants.Rows[4].Cells[9].Value

 
  $global:Config.Tenant6.DisplayName = $Global:grid_Tenants.Rows[5].Cells[1].Value
  $global:Config.Tenant6.SignInAddress = $Global:grid_Tenants.Rows[5].Cells[2].Value
  $global:Config.Tenant6.ModernAuth = $Global:grid_Tenants.Rows[5].Cells[4].Value
  $global:Config.Tenant6.ConnectToTeams = $Global:grid_Tenants.Rows[5].Cells[5].Value
  $global:Config.Tenant6.ConnectToSkype = $Global:grid_Tenants.Rows[5].Cells[6].Value
  $global:Config.Tenant6.ConnectToExchange = $Global:grid_Tenants.Rows[5].Cells[7].Value
  $global:Config.Tenant6.ConnectToAzureAD = $Global:grid_Tenants.Rows[5].Cells[8].Value
  $global:Config.Tenant6.ConnectToCompliance = $Global:grid_Tenants.Rows[5].Cells[9].Value

 
  $global:Config.Tenant7.DisplayName = $Global:grid_Tenants.Rows[6].Cells[1].Value
  $global:Config.Tenant7.SignInAddress = $Global:grid_Tenants.Rows[6].Cells[2].Value
  $global:Config.Tenant7.ModernAuth = $Global:grid_Tenants.Rows[6].Cells[4].Value
  $global:Config.Tenant7.ConnectToTeams = $Global:grid_Tenants.Rows[6].Cells[5].Value
  $global:Config.Tenant7.ConnectToSkype = $Global:grid_Tenants.Rows[6].Cells[6].Value
  $global:Config.Tenant7.ConnectToExchange = $Global:grid_Tenants.Rows[6].Cells[7].Value
  $global:Config.Tenant7.ConnectToAzureAD = $Global:grid_Tenants.Rows[6].Cells[8].Value
  $global:Config.Tenant7.ConnectToCompliance = $Global:grid_Tenants.Rows[6].Cells[9].Value

 
  $global:Config.Tenant8.DisplayName = $Global:grid_Tenants.Rows[7].Cells[1].Value
  $global:Config.Tenant8.SignInAddress = $Global:grid_Tenants.Rows[7].Cells[2].Value
  $global:Config.Tenant8.ModernAuth = $Global:grid_Tenants.Rows[7].Cells[4].Value
  $global:Config.Tenant8.ConnectToTeams = $Global:grid_Tenants.Rows[7].Cells[5].Value
  $global:Config.Tenant8.ConnectToSkype = $Global:grid_Tenants.Rows[7].Cells[6].Value
  $global:Config.Tenant8.ConnectToExchange = $Global:grid_Tenants.Rows[7].Cells[7].Value
  $global:Config.Tenant8.ConnectToAzureAD = $Global:grid_Tenants.Rows[7].Cells[8].Value
  $global:Config.Tenant8.ConnectToCompliance = $Global:grid_Tenants.Rows[7].Cells[9].Value

 
  $global:Config.Tenant9.DisplayName = $Global:grid_Tenants.Rows[8].Cells[1].Value
  $global:Config.Tenant9.SignInAddress = $Global:grid_Tenants.Rows[8].Cells[2].Value
  $global:Config.Tenant9.ModernAuth = $Global:grid_Tenants.Rows[8].Cells[4].Value
  $global:Config.Tenant9.ConnectToTeams = $Global:grid_Tenants.Rows[8].Cells[5].Value
  $global:Config.Tenant9.ConnectToSkype = $Global:grid_Tenants.Rows[8].Cells[6].Value
  $global:Config.Tenant9.ConnectToExchange = $Global:grid_Tenants.Rows[8].Cells[7].Value
  $global:Config.Tenant9.ConnectToAzureAD = $Global:grid_Tenants.Rows[8].Cells[8].Value
  $global:Config.Tenant9.ConnectToCompliance = $Global:grid_Tenants.Rows[8].Cells[9].Value


  $global:Config.Tenant10.DisplayName = $Global:grid_Tenants.Rows[9].Cells[1].Value
  $global:Config.Tenant10.SignInAddress = $Global:grid_Tenants.Rows[9].Cells[2].Value
  $global:Config.Tenant10.ModernAuth = $Global:grid_Tenants.Rows[9].Cells[4].Value
  $global:Config.Tenant10.ConnectToTeams = $Global:grid_Tenants.Rows[9].Cells[5].Value
  $global:Config.Tenant10.ConnectToSkype = $Global:grid_Tenants.Rows[9].Cells[6].Value
  $global:Config.Tenant10.ConnectToExchange = $Global:grid_Tenants.Rows[9].Cells[7].Value
  $global:Config.Tenant10.ConnectToAzureAD = $Global:grid_Tenants.Rows[9].Cells[8].Value
  $global:Config.Tenant10.ConnectToCompliance = $Global:grid_Tenants.Rows[9].Cells[9].Value

  #Encrypt passwords
  If ($Global:grid_Tenants.Rows[0].Cells[3].Value -ne '****') 
  {
    $global:Config.Tenant1.Credential = ($Global:grid_Tenants.Rows[0].Cells[3].Value | ConvertTo-SecureString -AsPlainText -Force)
  }
  If ($Global:grid_Tenants.Rows[1].Cells[3].Value -ne '****') 
  {
    $global:Config.Tenant2.Credential = ($Global:grid_Tenants.Rows[1].Cells[3].Value | ConvertTo-SecureString -AsPlainText -Force)
  }
  If ($Global:grid_Tenants.Rows[2].Cells[3].Value -ne '****') 
  {
    $global:Config.Tenant3.Credential = ($Global:grid_Tenants.Rows[2].Cells[3].Value | ConvertTo-SecureString -AsPlainText -Force)
  }
  If ($Global:grid_Tenants.Rows[3].Cells[3].Value -ne '****') 
  {
    $global:Config.Tenant4.Credential = ($Global:grid_Tenants.Rows[3].Cells[3].Value | ConvertTo-SecureString -AsPlainText -Force)
  }
  If ($Global:grid_Tenants.Rows[4].Cells[3].Value -ne '****') 
  {
    $global:Config.Tenant5.Credential = ($Global:grid_Tenants.Rows[4].Cells[3].Value | ConvertTo-SecureString -AsPlainText -Force)
  }
  If ($Global:grid_Tenants.Rows[5].Cells[3].Value -ne '****') 
  {
    $global:Config.Tenant6.Credential = ($Global:grid_Tenants.Rows[5].Cells[3].Value | ConvertTo-SecureString -AsPlainText -Force)
  }
  If ($Global:grid_Tenants.Rows[6].Cells[3].Value -ne '****') 
  {
    $global:Config.Tenant7.Credential = ($Global:grid_Tenants.Rows[6].Cells[3].Value | ConvertTo-SecureString -AsPlainText -Force)
  }
  If ($Global:grid_Tenants.Rows[7].Cells[3].Value -ne '****') 
  {
    $global:Config.Tenant8.Credential = ($Global:grid_Tenants.Rows[7].Cells[3].Value | ConvertTo-SecureString -AsPlainText -Force)
  }
  If ($Global:grid_Tenants.Rows[8].Cells[3].Value -ne '****') 
  {
    $global:Config.Tenant9.Credential = ($Global:grid_Tenants.Rows[8].Cells[3].Value | ConvertTo-SecureString -AsPlainText -Force)
  }
  If ($Global:grid_Tenants.Rows[9].Cells[3].Value -ne '****') 
  {
    $global:Config.Tenant10.Credential = ($Global:grid_Tenants.Rows[10].Cells[3].Value | ConvertTo-SecureString -AsPlainText -Force)
  }

  #Clear the password fields
  $Global:grid_Tenants.Rows[0].Cells[3].Value = '****'
  $Global:grid_Tenants.Rows[1].Cells[3].Value = '****'
  $Global:grid_Tenants.Rows[2].Cells[3].Value = '****'
  $Global:grid_Tenants.Rows[3].Cells[3].Value = '****'
  $Global:grid_Tenants.Rows[4].Cells[3].Value = '****'
  $Global:grid_Tenants.Rows[5].Cells[3].Value = '****'
  $Global:grid_Tenants.Rows[6].Cells[3].Value = '****'
  $Global:grid_Tenants.Rows[7].Cells[3].Value = '****'
  $Global:grid_Tenants.Rows[8].Cells[3].Value = '****'
  $Global:grid_Tenants.Rows[9].Cells[3].Value = '****'

  #Add the Global Settings

  $global:Config.AutoUpdatesEnabled = $Global:cbx_AutoUpdates.Checked
  $global:Config.ModernAuthClipboardEnabled = $Global:cbx_ClipboardAuth.Checked
 

  #Write the XML File
  Try
  {
    $global:Config| Export-Clixml -Path "$ENV:UserProfile\BounShell.xml"
    Write-Log -component $function -Message 'Config File Saved' -severity 2
  }
  Catch 
  {
    Write-Log -component $function -Message 'Error writing Config file' -severity 3
  }
}

Function Import-BsDefaultConfig 
{
  #Set Variables to Defaults
  #Remove and re-create the Config Array
  $null = (Remove-Variable -Name Config -Scope Global -ErrorAction SilentlyContinue)
  $global:Config = @{}
  #Populate with Defaults
  $null =  $Global:grid_Tenants.Rows.Clear()
  
  $global:Config.Tenant1 = @{}
  $global:Config.Tenant1.DisplayName = 'Undefined'
  $global:Config.Tenant1.SignInAddress = 'user1@fabrikam.com'
  $global:Config.Tenant1.Credential = '****'
  $global:Config.Tenant1.ModernAuth = $false
  $global:Config.Tenant1.ConnectToTeams = $false
  $global:Config.Tenant1.ConnectToSkype = $false
  $global:Config.Tenant1.ConnectToExchange = $false
  $global:Config.Tenant1.ConnectToAzureAD = $false
  $global:Config.Tenant1.ConnectToCompliance = $false
  $null =  $Global:grid_Tenants.Rows.Add('1','Undefined','user1@fabrikam.com','****',$false,$false,$false,$false,$false,$false)
  
  
  $global:Config.Tenant2 = @{}
  $global:Config.Tenant2.DisplayName = 'Undefined'
  $global:Config.Tenant2.SignInAddress = 'user2@fabrikam.com'
  $global:Config.Tenant2.Credential = '****'
  $global:Config.Tenant2.ModernAuth = $false
  $global:Config.Tenant2.ConnectToTeams = $false
  $global:Config.Tenant2.ConnectToSkype = $false
  $global:Config.Tenant2.ConnectToExchange = $false
  $global:Config.Tenant2.ConnectToAzureAD = $false
  $global:Config.Tenant2.ConnectToCompliance = $false
  $null =  $Global:grid_Tenants.Rows.Add('2','Undefined','user2@fabrikam.com','****',$false,$false,$false,$false,$false,$false)
  
  $global:Config.Tenant3 = @{}
  $global:Config.Tenant3.DisplayName = 'Undefined'
  $global:Config.Tenant3.SignInAddress = 'user3@fabrikam.com'
  $global:Config.Tenant3.Credential = '****'
  $global:Config.Tenant3.ModernAuth = $false
  $global:Config.Tenant3.ConnectToTeams = $false
  $global:Config.Tenant3.ConnectToSkype = $false
  $global:Config.Tenant3.ConnectToExchange = $false
  $global:Config.Tenant3.ConnectToAzureAD = $false
  $global:Config.Tenant3.ConnectToCompliance = $false
  $null =  $Global:grid_Tenants.Rows.Add('3','Undefined','user3@fabrikam.com','****',$false,$false,$false,$false,$false,$false)
  
  $global:Config.Tenant4 = @{}
  $global:Config.Tenant4.DisplayName = 'Undefined'
  $global:Config.Tenant4.SignInAddress = 'user4@fabrikam.com'
  $global:Config.Tenant4.Credential = '****'
  $global:Config.Tenant4.ModernAuth = $false
  $global:Config.Tenant4.ConnectToTeams = $false
  $global:Config.Tenant4.ConnectToSkype = $false
  $global:Config.Tenant4.ConnectToExchange = $false
  $global:Config.Tenant4.ConnectToAzureAD = $false
  $global:Config.Tenant4.ConnectToCompliance = $false
  $null =  $Global:grid_Tenants.Rows.Add('4','Undefined','user4@fabrikam.com','****',$false,$false,$false,$false,$false,$false)
  
  $global:Config.Tenant5 = @{}
  $global:Config.Tenant5.DisplayName = 'Undefined'
  $global:Config.Tenant5.SignInAddress = 'user5@fabrikam.com'
  $global:Config.Tenant5.Credential = '****'
  $global:Config.Tenant5.ModernAuth = $false
  $global:Config.Tenant5.ConnectToTeams = $false
  $global:Config.Tenant5.ConnectToSkype = $false
  $global:Config.Tenant5.ConnectToExchange = $false
  $global:Config.Tenant5.ConnectToAzureAD = $false
  $global:Config.Tenant5.ConnectToCompliance = $false
  $null =  $Global:grid_Tenants.Rows.Add('5','Undefined','user5@fabrikam.com','****',$false,$false,$false,$false,$false,$false)
  
  $global:Config.Tenant6 = @{}
  $global:Config.Tenant6.DisplayName = 'Undefined'
  $global:Config.Tenant6.SignInAddress = 'user6@fabrikam.com'
  $global:Config.Tenant6.Credential = '****'
  $global:Config.Tenant6.ModernAuth = $false
  $global:Config.Tenant6.ConnectToTeams = $false
  $global:Config.Tenant6.ConnectToSkype = $false
  $global:Config.Tenant6.ConnectToExchange = $false
  $global:Config.Tenant6.ConnectToAzureAD = $false
  $global:Config.Tenant6.ConnectToCompliance = $false
  $null =  $Global:grid_Tenants.Rows.Add('6','Undefined','user6@fabrikam.com','****',$false,$false,$false,$false,$false,$false)
  
  $global:Config.Tenant7 = @{}
  $global:Config.Tenant7.DisplayName = 'Undefined'
  $global:Config.Tenant7.SignInAddress = 'user@fabrikam.com'
  $global:Config.Tenant7.Credential = '****'
  $global:Config.Tenant7.ModernAuth = $false
  $global:Config.Tenant7.ConnectToTeams = $false
  $global:Config.Tenant7.ConnectToSkype = $false
  $global:Config.Tenant7.ConnectToExchange = $false
  $global:Config.Tenant7.ConnectToAzureAD = $false
  $global:Config.Tenant7.ConnectToCompliance = $false
  $null =  $Global:grid_Tenants.Rows.Add('7','Undefined','user7@fabrikam.com','****',$false,$false,$false,$false,$false,$false)
  
  $global:Config.Tenant8 = @{}
  $global:Config.Tenant8.DisplayName = 'Undefined'
  $global:Config.Tenant8.SignInAddress = 'user8@fabrikam.com'
  $global:Config.Tenant8.Credential = '****'
  $global:Config.Tenant8.ModernAuth = $false
  $global:Config.Tenant8.ConnectToTeams = $false
  $global:Config.Tenant8.ConnectToSkype = $false
  $global:Config.Tenant8.ConnectToExchange = $false
  $global:Config.Tenant8.ConnectToAzureAD = $false
  $global:Config.Tenant8.ConnectToCompliance = $false
  $null =  $Global:grid_Tenants.Rows.Add('8','Undefined','user8@fabrikam.com','****',$false,$false,$false,$false,$false,$false)
    
  $global:Config.Tenant9 = @{}
  $global:Config.Tenant9.DisplayName = 'Undefined'
  $global:Config.Tenant9.SignInAddress = 'user@fabrikam.com'
  $global:Config.Tenant9.Credential = '****'
  $global:Config.Tenant9.ModernAuth = $false
  $global:Config.Tenant9.ConnectToTeams = $false
  $global:Config.Tenant9.ConnectToSkype = $false
  $global:Config.Tenant9.ConnectToExchange = $false
  $global:Config.Tenant9.ConnectToAzureAD = $false
  $global:Config.Tenant9.ConnectToCompliance = $false
  $null =  $Global:grid_Tenants.Rows.Add('9','Undefined','user9@fabrikam.com','****',$false,$false,$false,$false,$false,$false)
  
  $global:Config.Tenant10 = @{}
  $global:Config.Tenant10.DisplayName = 'Undefined'
  $global:Config.Tenant10.SignInAddress = 'user@fabrikam.com'
  $global:Config.Tenant10.Credential = '****'
  $global:Config.Tenant10.ModernAuth = $false
  $global:Config.Tenant10.ConnectToTeams = $false
  $global:Config.Tenant10.ConnectToSkype = $false
  $global:Config.Tenant10.ConnectToExchange = $false
  $global:Config.Tenant10.ConnectToAzureAD = $false
  $global:Config.Tenant10.ConnectToCompliance = $false
  $null =  $Global:grid_Tenants.Rows.Add('10','Undefined','user10@fabrikam.com','****',$false,$false,$false,$false,$false,$false)
  
  [Float]$global:Config.ConfigFileVersion = '0.2'
  [string]$global:Config.Description = 'BounShell Configuration file. See UcMadScientist.com/BounShell for more information'
  
  #Config File Version 0.2 additions
  $global:Config.AutoUpdatesEnabled = $true
  $global:Config.ModernAuthClipboardEnabled = $true
  $global:Config.ModernAuthWarningAccepted = $false
  
  $global:Config.Tenant1.ConnectToAzureAD = $false
  $global:Config.Tenant1.ConnectToCompliance = $false
  $global:Config.Tenant2.ConnectToAzureAD = $false
  $global:Config.Tenant2.ConnectToCompliance = $false
  $global:Config.Tenant3.ConnectToAzureAD = $false
  $global:Config.Tenant3.ConnectToCompliance = $false
  $global:Config.Tenant4.ConnectToAzureAD = $false
  $global:Config.Tenant4.ConnectToCompliance = $false
  $global:Config.Tenant5.ConnectToAzureAD = $false
  $global:Config.Tenant5.ConnectToCompliance = $false
  $global:Config.Tenant6.ConnectToAzureAD = $false
  $global:Config.Tenant6.ConnectToCompliance = $false
  $global:Config.Tenant7.ConnectToAzureAD = $false
  $global:Config.Tenant7.ConnectToCompliance = $false
  $global:Config.Tenant8.ConnectToAzureAD = $false
  $global:Config.Tenant8.ConnectToCompliance = $false
  $global:Config.Tenant9.ConnectToAzureAD = $false
  $global:Config.Tenant9.ConnectToCompliance = $false
  $global:Config.Tenant10.ConnectToAzureAD = $false
  $global:Config.Tenant10.ConnectToCompliance = $false
}

Function Invoke-BsNewTenantTab 
{
  <#
      .SYNOPSIS
      Function to Open new tab in ISE and call Connect-BsO365Tenant to connect to relevant services
 
      .DESCRIPTION
         
      .PARAMETER Tenant
      The tenant number to pass to Connect-BsO365Tenant
 
      .PARAMETER Tabname
      The name for the new tab
 
      .EXAMPLE
      Invoke-BsNewTenantTab -Tenant 1 -Tabname "Skype4badmin"
      Opens a new tab and connects to the Tenant stored in slot 1
 
      .NOTES
      N/A
 
      .LINK
      http://www.UcMadScientist.com
 
      .INPUTS
      This function does not accept pipelined input
 
      .OUTPUTS
      This function does not create pipelined output
  #>

  param
  (
    [Parameter(Mandatory)] [string]$Tabname,
    [Parameter(Mandatory)] [float]$Tenant
  )
  
  $function = 'Invoke-BsNewTenantTab'
  Write-Log -component $function -Message "Called Invoke-BsNewTenantTab to connect to Tenant $Tenant with a Tabname of $Tabname" -severity 1 
  if($Tabname -ne 'Undefined') 
  {
    Try
    {
      #kick off a new tab and call it tabname
      Write-Log -component $function -Message 'Opening new ISE tab...' -severity 1 
      $TabNameTab = $PSISE.PowerShellTabs.Add()
      $TabNameTab.DisplayName = $Tabname
      
      #Wait for the tab to wake up
      Write-Log -component $function -Message 'Waiting for tab to become invokable' -severity 1 
      Do 
      {
        Start-Sleep -Milliseconds 100
      }
      While (!$TabNameTab.CanInvoke)
      
      #Kick off the connection
      Write-Log -component $function -Message "Invoking Command: Connect-BsO365Tenant -Tenant $Tenant" -severity 1
      $TabNameTab.Invoke("Connect-BsO365Tenant -Tenant $Tenant")
    }
    
    Catch
    {
      #Something went wrong opening a new tab, probably already a tab with that name open
      Write-Log -component $function -Message 'Failed to open new tab. Is there already a connection open to that tenant?' -severity 3
    }
  }
  
  Else
  {
    #Tabname is "undefined", user clicked a tenant thats not confgured yey
    Write-Log -component $function -Message "Sorry, I can't find a config for Tenant $Tenant" -severity 3
  }
}

Function Connect-BsO365Tenant
{
  <#
      .SYNOPSIS
      Connects to relevant Office365 services based on the configuration of the tenant we are passed
 
      .DESCRIPTION
         
      .PARAMETER Tenant
      The tenant number to connect to
 
      .EXAMPLE
      Connect-BsO365Tenant -Tenant 1
      Connects to the Tenant stored in slot 1 based on the current context
 
      .NOTES
      N/A
 
      .LINK
      http://www.UcMadScientist.com
 
      .INPUTS
      This function does not accept pipelined input
 
      .OUTPUTS
      This function does not create pipelined output
  #>


  [CmdletBinding()]
  PARAM
  (
    $Tenant
  )
  [string]$function = 'Connect-BsO365Tenant'
  [bool]$ModernAuth = $false
  [bool]$ConnectToTeams = $false
  [bool]$ConnectToSkype = $false
  [bool]$ConnectToExchange = $false
  [bool]$ConnectToSharepoint = $false
  [bool]$ConnectToAzureAD = $false
  [bool]$ConnectToCompliance = $false
  $ModernAuthPassword = ConvertTo-SecureString -String 'Foo' -AsPlainText -Force
  [string]$ModernAuthUsername
  

  #Check to see if we are running in the ISE
  If ($PSISE)
  {  
    #Clean up any stale sessions (we shouldn't have any, but whatever)
    Get-PSSession | Remove-PSSession
  }
  #load the gui stuff for configuration #todo, put check here and only load it if it's not loaded.
  Import-BsGuiElements

  #Import the Config file so we have data
  Read-BsConfigFile


  #check to see if a tenant was specified
  If ($Tenant.length -eq 0) 
  {
    Write-Log -Message 'Connect-BsO365Tenant called without a tenant, displaying menu' -severity 1
            
    #Menu code thanks to Greig.

    #Dodgy hack until I refactor the config code #TODO
    $Tenants = @()
    $Tenants += ($global:Config.Tenant1.DisplayName)
    $Tenants += ($global:Config.Tenant2.DisplayName)
    $Tenants += ($global:Config.Tenant3.DisplayName)
    $Tenants += ($global:Config.Tenant4.DisplayName)
    $Tenants += ($global:Config.Tenant5.DisplayName)
    $Tenants += ($global:Config.Tenant6.DisplayName)
    $Tenants += ($global:Config.Tenant7.DisplayName)
    $Tenants += ($global:Config.Tenant8.DisplayName)
    $Tenants += ($global:Config.Tenant9.DisplayName)
    $Tenants += ($global:Config.Tenant10.DisplayName)


    #First figure out the maximum width of the item's name (for the tabular menu):
    $width = 0
    foreach ($Tenant in ($Tenants)) 
    {
      if ($Tenant.Length -gt $width) 
      {
        $width = $Tenant.Length
      }
    }

    #Provide an on-screen menu of tenants for the user to choose from:
    $index = 1
    Write-Host -Object ''
    Write-Host -Object ('ID '), ('Tenant Name'.Padright($width + 1), ' ')
    foreach ($Tenant in ($Tenants)) 
    {
      Write-Host -Object ($index.ToString()).PadRight(2, ' '), ' | ', ($Tenant.Padright($width + 1), ' ')
      $index++
    }
    $index--    #Undo that last increment
    Write-Host
    Write-Host -Object 'Choose the tenant you wish to use'
    $chosen = Read-Host -Prompt 'Or any other value to quit'
    Write-Log -Message "User input $chosen" -severity 1
    if ($chosen -notmatch '^\d$') 
    {
      Exit
    }
    if ([int]$chosen -lt 0) 
    {
      Exit
    }
    if ([int]$chosen -gt $index) 
    {
      Exit
    }
    $Tenant = $chosen
  }


  Write-Log -component $function -Message "Called to connect to Tenant $Tenant" -severity 1
  #Set the global Modern Auth flag
  if ($global:Config.ModernAuthClipboardEnabled -eq $true)
  {
    [bool]$NoPassword = $true
    Write-Log -component $function -Message "Modern Auth Password integration enabled" -severity 2
  }
  Else
  {
    [bool]$NoPassword = $False
    Write-Log -component $function -Message "Modern Auth Password integration disabled" -severity 2
  }

  #change config based on tenant
  #region tenantswitch
  switch ($Tenant)
  {
    1 #Tenant 1
    {
      #Set Connection flags
      [bool]$ConnectToTeams = $global:Config.Tenant1.ConnectToTeams
      [bool]$ConnectToSkype = $global:Config.Tenant1.ConnectToSkype
      [bool]$ConnectToExchange = $global:Config.Tenant1.ConnectToExchange
      [bool]$ConnectToSharepoint = $false
      [bool]$ConnectToAzureAD = $global:Config.Tenant1.ConnectToAzureAD
      [bool]$ConnectToCompliance = $global:Config.Tenant1.ConnectToCompliance
      Write-Log -component $function -Message "Loading $($global:Config.Tenant1.DisplayName) Settings" -severity 2
      #Check to see if the tenant is configured for modern auth
     
      If (!$global:Config.Tenant1.ModernAuth) 
      {
        #Not using modern auth
        $global:StoredPsCred = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList ($global:Config.Tenant1.SignInAddress, $global:Config.Tenant1.Credential)
        ($global:StoredPsCred).Password.MakeReadOnly() #Thanks for spotting this Greig!
      }
      Else
      {
        #Using modern auth
        $ModernAuth = $true
        #Convert the config into something we can work with later
        $ModernAuthPassword = $global:Config.Tenant1.Credential
        $ModernAuthUsername = $global:Config.Tenant1.SignInAddress
        
      }
    }
    
    
    2 #Tenant 2
    {
      #Set Connection flags
      [bool]$ConnectToTeams = $global:Config.Tenant2.ConnectToTeams
      [bool]$ConnectToSkype = $global:Config.Tenant2.ConnectToSkype
      [bool]$ConnectToExchange = $global:Config.Tenant2.ConnectToExchange
      [bool]$ConnectToSharepoint = $false
      [bool]$ConnectToAzureAD = $global:Config.Tenant2.ConnectToAzureAD
      [bool]$ConnectToCompliance = $global:Config.Tenant2.ConnectToCompliance
      Write-Log -component $function -Message "Loading $($global:Config.Tenant2.DisplayName) Settings" -severity 2
      #Check to see if the tenant is configured for modern auth
      If (!$global:Config.Tenant2.ModernAuth) 
      {
        #Not using modern auth
        $global:StoredPsCred = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList ($global:Config.Tenant2.SignInAddress, $global:Config.Tenant2.Credential)
        ($global:StoredPsCred).Password.MakeReadOnly() #Thanks for spotting this Greig!
      }
      Else
      {
        #Using modern auth
        $ModernAuth = $true
        #Convert the config into something we can work with later
        $ModernAuthPassword = $global:Config.Tenant2.Credential
        $ModernAuthUsername = $global:Config.Tenant2.SignInAddress
      }
    }
    
    
    3 #Tenant 3
    {
      #Set Connection flags
      [bool]$ConnectToTeams = $global:Config.Tenant3.ConnectToTeams
      [bool]$ConnectToSkype = $global:Config.Tenant3.ConnectToSkype
      [bool]$ConnectToExchange = $global:Config.Tenant3.ConnectToExchange
      [bool]$ConnectToSharepoint = $false
      [bool]$ConnectToAzureAD = $global:Config.Tenant3.ConnectToAzureAD
      [bool]$ConnectToCompliance = $global:Config.Tenant3.ConnectToCompliance
      Write-Log -component $function -Message "Loading $($global:Config.Tenant3.DisplayName) Settings" -severity 2
      #Check to see if the tenant is configured for modern auth
      If (!$global:Config.Tenant3.ModernAuth) 
      {
        #Not using modern auth
        $global:StoredPsCred = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList ($global:Config.Tenant3.SignInAddress, $global:Config.Tenant3.Credential)
        ($global:StoredPsCred).Password.MakeReadOnly() #Thanks for spotting this Greig!
      }
      Else
      {
        #Using modern auth
        $ModernAuth = $true
        #Convert the config into something we can work with later
        $ModernAuthPassword = $global:Config.Tenant3.Credential
        $ModernAuthUsername = $global:Config.Tenant3.SignInAddress
      }
    }
    
    
    4 #Tenant 4
    {
      #Set Connection flags
      [bool]$ConnectToTeams = $global:Config.Tenant4.ConnectToTeams
      [bool]$ConnectToSkype = $global:Config.Tenant4.ConnectToSkype
      [bool]$ConnectToExchange = $global:Config.Tenant4.ConnectToExchange
      [bool]$ConnectToSharepoint = $false
      [bool]$ConnectToAzureAD = $global:Config.Tenant4.ConnectToAzureAD
      [bool]$ConnectToCompliance = $global:Config.Tenant4.ConnectToCompliance
      Write-Log -component $function -Message "Loading $($global:Config.Tenant4.DisplayName) Settings" -severity 2
      #Check to see if the tenant is configured for modern auth
      If (!$global:Config.Tenant4.ModernAuth) 
      {
        #Not using modern auth
        $global:StoredPsCred = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList ($global:Config.Tenant4.SignInAddress, $global:Config.Tenant4.Credential)
        ($global:StoredPsCred).Password.MakeReadOnly() #Thanks for spotting this Greig!
      }
      Else
      {
        #Using modern auth
        $ModernAuth = $true
        #Convert the config into something we can work with later
        $ModernAuthPassword = $global:Config.Tenant4.Credential
        $ModernAuthUsername = $global:Config.Tenant4.SignInAddress
      }
    }
    
    
    5 #Tenant 5
    {
      #Set Connection flags
      [bool]$ConnectToTeams = $global:Config.Tenant5.ConnectToTeams
      [bool]$ConnectToSkype = $global:Config.Tenant5.ConnectToSkype
      [bool]$ConnectToExchange = $global:Config.Tenant5.ConnectToExchange
      [bool]$ConnectToSharepoint = $false
      [bool]$ConnectToAzureAD = $global:Config.Tenant5.ConnectToAzureAD
      [bool]$ConnectToCompliance = $global:Config.Tenant5.ConnectToCompliance
      Write-Log -component $function -Message "Loading $($global:Config.Tenant5.DisplayName) Settings" -severity 2
      #Check to see if the tenant is configured for modern auth
      If (!$global:Config.Tenant5.ModernAuth) 
      {
        #Not using modern auth
        $global:StoredPsCred = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList ($global:Config.Tenant5.SignInAddress, $global:Config.Tenant5.Credential)
        ($global:StoredPsCred).Password.MakeReadOnly() #Thanks for spotting this Greig!
      }
      Else
      {
        #Using modern auth
        $ModernAuth = $true
        #Convert the config into something we can work with later
        $ModernAuthPassword = $global:Config.Tenant5.Credential
        $ModernAuthUsername = $global:Config.Tenant5.SignInAddress
      }
    }
    
    
    6  #Tenant 6
    {
      #Set Connection flags
      [bool]$ConnectToTeams = $global:Config.Tenant6.ConnectToTeams
      [bool]$ConnectToSkype = $global:Config.Tenant6.ConnectToSkype
      [bool]$ConnectToExchange = $global:Config.Tenant6.ConnectToExchange
      [bool]$ConnectToSharepoint = $false
      [bool]$ConnectToAzureAD = $global:Config.Tenant6.ConnectToAzureAD
      [bool]$ConnectToCompliance = $global:Config.Tenant6.ConnectToCompliance
      Write-Log -component $function -Message "Loading $($global:Config.Tenant6.DisplayName) Settings" -severity 2
      #Check to see if the tenant is configured for modern auth
      If (!$global:Config.Tenant6.ModernAuth) 
      {
        #Not using modern auth
        $global:StoredPsCred = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList ($global:Config.Tenant6.SignInAddress, $global:Config.Tenant6.Credential)
        ($global:StoredPsCred).Password.MakeReadOnly() #Thanks for spotting this Greig!
      }
      Else
      {
        #Using modern auth
        $ModernAuth = $true
        #Convert the config into something we can work with later
        $ModernAuthPassword = $global:Config.Tenant6.Credential
        $ModernAuthUsername = $global:Config.Tenant6.SignInAddress
      }
    }
    
    
    7  #Tenant 7
    {
      #Set Connection flags
      [bool]$ConnectToTeams = $global:Config.Tenant7.ConnectToTeams
      [bool]$ConnectToSkype = $global:Config.Tenant7.ConnectToSkype
      [bool]$ConnectToExchange = $global:Config.Tenant7.ConnectToExchange
      [bool]$ConnectToSharepoint = $false
      [bool]$ConnectToAzureAD = $global:Config.Tenant7.ConnectToAzureAD
      [bool]$ConnectToCompliance = $global:Config.Tenant7.ConnectToCompliance
      Write-Log -component $function -Message "Loading $($global:Config.Tenant7.DisplayName) Settings" -severity 2
      #Check to see if the tenant is configured for modern auth
      If (!$global:Config.Tenant7.ModernAuth) 
      {
        #Not using modern auth
        $global:StoredPsCred = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList ($global:Config.Tenant7.SignInAddress, $global:Config.Tenant7.Credential)
        ($global:StoredPsCred).Password.MakeReadOnly() #Thanks for spotting this Greig!
      }
      Else
      {
        #Using modern auth
        $ModernAuth = $true
        #Convert the config into something we can work with later
        $ModernAuthPassword = $global:Config.Tenant7.Credential
        $ModernAuthUsername = $global:Config.Tenant7.SignInAddress
      }
    }
    
    
    8  #Tenant 8
    {
      #Set Connection flags
      [bool]$ConnectToTeams = $global:Config.Tenant8.ConnectToTeams
      [bool]$ConnectToSkype = $global:Config.Tenant8.ConnectToSkype
      [bool]$ConnectToExchange = $global:Config.Tenant8.ConnectToExchange
      [bool]$ConnectToSharepoint = $false
      [bool]$ConnectToAzureAD = $global:Config.Tenant8.ConnectToAzureAD
      [bool]$ConnectToCompliance = $global:Config.Tenant8.ConnectToCompliance
      Write-Log -component $function -Message "Loading $($global:Config.Tenant8.DisplayName) Settings" -severity 2
      #Check to see if the tenant is configured for modern auth
      If (!$global:Config.Tenant8.ModernAuth) 
      {
        #Not using modern auth
        $global:StoredPsCred = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList ($global:Config.Tenant8.SignInAddress, $global:Config.Tenant8.Credential)
        ($global:StoredPsCred).Password.MakeReadOnly() #Thanks for spotting this Greig!
      }
      Else
      {
        #Using modern auth
        $ModernAuth = $true
        #Convert the config into something we can work with later
        $ModernAuthPassword = $global:Config.Tenant8.Credential
        $ModernAuthUsername = $global:Config.Tenant8.SignInAddress
      }
    }
    
    
    9  #Tenant 9
    {
      #Set Connection flags
      [bool]$ConnectToTeams = $global:Config.Tenant9.ConnectToTeams
      [bool]$ConnectToSkype = $global:Config.Tenant9.ConnectToSkype
      [bool]$ConnectToExchange = $global:Config.Tenant9.ConnectToExchange
      [bool]$ConnectToSharepoint = $false
      [bool]$ConnectToAzureAD = $global:Config.Tenant9.ConnectToAzureAD
      [bool]$ConnectToCompliance = $global:Config.Tenant9.ConnectToCompliance
      
      Write-Log -component $function -Message "Loading $($global:Config.Tenant9.DisplayName) Settings" -severity 2
      #Check to see if the tenant is configured for modern auth
      If (!$global:Config.Tenant9.ModernAuth) 
      {
        #Not using modern auth
        $global:StoredPsCred = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList ($global:Config.Tenant9.SignInAddress, $global:Config.Tenant9.Credential)
        ($global:StoredPsCred).Password.MakeReadOnly() #Thanks for spotting this Greig!
      }
      Else
      {
        #Using modern auth
        $ModernAuth = $true
        #Convert the config into something we can work with later
        $ModernAuthPassword = $global:Config.Tenant9.Credential
        $ModernAuthUsername = $global:Config.Tenant9.SignInAddress
      }
    }
    
    
    10 #Tenant 10
    {
      #Set Connection flags
      [bool]$ConnectToTeams = $global:Config.Tenant10.ConnectToTeams
      [bool]$ConnectToSkype = $global:Config.Tenant10.ConnectToSkype
      [bool]$ConnectToExchange = $global:Config.Tenant10.ConnectToExchange
      [bool]$ConnectToSharepoint = $false
      [bool]$ConnectToAzureAD = $global:Config.Tenant10.ConnectToAzureAD
      [bool]$ConnectToCompliance = $global:Config.Tenant10.ConnectToCompliance
      Write-Log -component $function -Message "Loading $($global:Config.Tenant10.DisplayName) Settings" -severity 2
      #Check to see if the tenant is configured for modern auth
      If (!$global:Config.Tenant10.ModernAuth) 
      {
        #Not using modern auth
        $global:StoredPsCred = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList ($global:Config.Tenant10.SignInAddress, $global:Config.Tenant10.Credential)
        ($global:StoredPsCred).Password.MakeReadOnly() #Thanks for spotting this Greig!
      }
      Else
      {
        #Using modern auth
        $ModernAuth = $true
        #Convert the config into something we can work with later
        $ModernAuthPassword = $global:Config.Tenant10.Credential
        $ModernAuthUsername = $global:Config.Tenant10.SignInAddress
      }
    }
    11 #Tenant 11 should never happen. This is refactoring for 0.3 config file with array support, it's here for testing.
    {
      #Set Connection flags
      [bool]$ConnectToTeams = $global:Config.Tenant[$Tenant].ConnectToTeams
      [bool]$ConnectToSkype = $global:Config.Tenant[$Tenant].ConnectToSkype
      [bool]$ConnectToExchange = $global:Config.Tenant[$Tenant].ConnectToExchange
      [bool]$ConnectToSharepoint = $false
      [bool]$ConnectToAzureAD = $global:Config.Tenant[$Tenant].ConnectToAzureAD
      [bool]$ConnectToCompliance = $global:Config.Tenant[$Tenant].ConnectToCompliance
      Write-Log -component $function -Message "Loading $($global:Config.Tenant[$Tenant].DisplayName) Settings" -severity 2
      #Check to see if the tenant is configured for modern auth
      If (!$global:Config.Tenant[$Tenant].ModernAuth) 
      {
        #Not using modern auth
        $global:StoredPsCred = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList ($global:Config.Tenant[$Tenant].SignInAddress, $global:Config.Tenant[$Tenant].Credential)
      }
      Else
      {
        #Using modern auth
        $ModernAuth = $true
        #Convert the config into something we can work with later
        $ModernAuthPassword = $global:Config.Tenant[$Tenant].Credential
        $ModernAuthUsername = $global:Config.Tenant[$Tenant].SignInAddress
      }
    }
    
  }

  #endregion tenantswitch
  
  #check to see if the Modern Auth flag has been set and use the appropriate connection method
  If ($ModernAuth) 
  {
    # We are using Modern Auth, Check to see if the user accepted the warning. If not, prompt them
    If ($global:Config.ModernAuthWarningAccepted -eq $false) 
    { 


      #We should only warn them if the feature is actually on.
      If ($global:Config.ModernAuthClipboardEnabled = $true)
      {
        Write-Log -Message "User hasn't accepted the Modern Auth disclaimer prompt" -Severity 1 -Component $function
        Write-Host -Object 'Modern Auth Clipboard integration is currently enabled'
        Write-Host -Object 'Your Username and password will be placed into the clipboard to facilitate login'
        Write-Host -Object 'You can disable this feature in Add-ons > BounShell > Settings'
        Write-Host -Object 'More information on this is available at https://UcMadScientist.com/BounShell/'
        Write-Host -Object 'You will only be shown this warning once.'
        Write-Host -Object '.'
        Write-Host -Object '**************************************************************************************'
        Write-Host -Object '***** Whilst all care is taken, you are still responsible for your own security. *****'
        Write-Host -Object '** I cannot be held liable if your tenant is compromised, explodes or catches fire. **'
        Write-Host -Object "** To remove this warning and enable this feature, type 'I Accept' and press enter. **"
        Write-Host -Object '**************************************************************************************'
        Write-Host -Object 'Press Ctrl+C to abort this connection or'
        $disclaimer = (Read-Host -Prompt "Type 'I Accept' to continue")
        if ($disclaimer -eq 'I Accept')
        {
          Write-Log -Message 'User chose to accept' -Severity 3 -Component $function
          $global:Config.ModernAuthWarningAccepted = $true
          Write-BsConfigFile
        }
        Else
        {
          Write-Log -Message 'User did not accept' -Severity 3 -Component $function
          $global:Config.ModernAuthWarningAccepted = $false
          Throw 'User did not accept modern auth warning, aborting connection'
        }
      }
    }
    If ($global:Config.ModernAuthClipboardEnabled = $true)
    {     
      Write-Host -Object 'Your Username has been copied to the clipboard'
      Write-Host -Object 'You can paste it into the Modern Auth Window using CTRL+V to speed up the sign in process,'
      Write-Host -Object 'then paste your password using CTRL+V and sign in.'
      Write-Host -Object 'Upon pasting this, BounShell will clear the clipboard and overwrite the memory just in case.'
    }
    Else
    {
      Write-Host -Object 'Your Username has been copied to the clipboard'
      Write-Host -Object 'You can paste it into the Modern Auth Window using CTRL+V to speed up the sign in process.'
      Write-Host -Object 'You will need to enter your password manually. You can enable password support in Settings.'
    }
    #As we are dealing with modern auth we need to convert the password back to an unsecure string do that here
    $BSTR = [System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($ModernAuthPassword)
    $UnsecurePassword = [System.Runtime.InteropServices.Marshal]::PtrToStringAuto($BSTR)
    
    
    If ($ConnectToTeams) 
    {
      Try
      {
        # So now we need to kick off a new window that waits for the clipboard events
        Write-Log -Message 'Connecting to Microsoft Teams' -Severity 2 -Component $function
        #Create a script block with the expanded variables
        if ($global:Config.ModernAuthClipboardEnabled -eq $true) #workaround a bug where PowerShell converts the bool to a string and can't convert back
        {
          [String]$cmd = "Watch-BsCredentials -ModernAuthUsername $ModernAuthUsername -UnsecurePassword $UnsecurePassword -NoPassword"
        }
        Else
        {
          [String]$cmd = "Watch-BsCredentials -ModernAuthUsername $ModernAuthUsername -UnsecurePassword $UnsecurePassword"
        }
        [ScriptBlock]$sb = [ScriptBlock]::Create($cmd) 
        
        #and now call it
        Start-Process PowerShell $sb

        #Sleep for a few seconds to let the PowerShell window pop and fill the clipboard.
        Start-Sleep -Seconds 3
        
        #Now we can invoke the session
        Connect-MicrosoftTeams
      } 
      Catch 
      {
        $ErrorMessage = $_.Exception.Message
        Write-Log -Message $ErrorMessage -Severity 3 -Component $function 
        Write-Log -Message 'Error connecting to Microsoft Teams' -Severity 3 -Component $function
      }
    }
    
    #Check for the Exchange connection flag
    If ($ConnectToExchange)
    {
      #Flag is set, connect to Exchange
      Try
      {
        #Exchange connection try block
        Write-Log -Message 'Connecting to Exchange Online' -Severity 2 -Component $function
        #So Now we need to kick off a new window that waits for the clipboard events
        #Create a script block with the expanded variables
        if ($global:Config.ModernAuthClipboardEnabled -eq $true) #workaround a bug where PowerShell converts the bool to a string and can't convert back
        {
          [String]$cmd = "Watch-BsCredentials -ModernAuthUsername $ModernAuthUsername -UnsecurePassword $UnsecurePassword -NoPassword"
        }
        Else
        {
          [String]$cmd = "Watch-BsCredentials -ModernAuthUsername $ModernAuthUsername -UnsecurePassword $UnsecurePassword"
        }
        [ScriptBlock]$sb = [ScriptBlock]::Create($cmd) 
        
        #and now call it
        Start-Process PowerShell $sb

        #Sleep for a few seconds to let the PowerShell window pop and fill the clipboard.
        Start-Sleep -Seconds 3

        #Now we invoke the session
        $O365Session = (Connect-ExchangeOnlineShell)
        #Write-Log -Message "Importing Session" -Severity 1 -Component $function
        #$VerbosePreference = "SilentlyContinue" #Todo. fix for import-psmodule ignoring the -Verbose:$false flag
        #Import-Module (Import-PSSession -Session $O365Session -AllowClobber -DisableNameChecking) -Global -DisableNameChecking
        #$VerbosePreference = "Continue" #Todo. fix for import-psmodule ignoring the -Verbose:$false flag
      } 
      Catch 
      {
        #We had an issue connecting to Exchange
        $ErrorMessage = $_.Exception.Message
        Write-Log -Message $ErrorMessage -Severity 3 -Component $function
        Write-Log -Message 'Error connecting to Exchange Online' -Severity 3 -Component $function
      }
    }

    #Check for the Skype4B connection flag
    If ($ConnectToSkype) 
    {
      #Flag is set, connect to Skype4B
      Try
      {
        #Skype connection try block
        Write-Log -Message 'Connecting to Skype4B Online' -Severity 2 -Component $function
        #So Now we need to kick off a new window that waits for the clipboard events
        #Create a script block with the expanded variables
        if ($global:Config.ModernAuthClipboardEnabled -eq $true) #workaround a bug where PowerShell converts the bool to a string and can't convert back
        {
          [String]$cmd = "Watch-BsCredentials -ModernAuthUsername $ModernAuthUsername -UnsecurePassword $UnsecurePassword -NoPassword"
        }
        Else
        {
          [String]$cmd = "Watch-BsCredentials -ModernAuthUsername $ModernAuthUsername -UnsecurePassword $UnsecurePassword"
        }
        [ScriptBlock]$sb = [ScriptBlock]::Create($cmd) 
        
        #and now call it
        Start-Process PowerShell $sb

        #Sleep for a few seconds to let the PowerShell window pop and fill the clipboard.
        Start-Sleep -Seconds 3

        #Now we invoke the session
        $S4BOSession = (New-CsOnlineSession)
        $VerbosePreference = 'SilentlyContinue' #Todo. fix for import-psmodule ignoring the -Verbose:$false flag
        Import-Module (Import-PSSession -Session $S4BOSession -AllowClobber -DisableNameChecking) -Global -DisableNameChecking
        $VerbosePreference = 'Continue' #Todo. fix for import-psmodule ignoring the -Verbose:$false flag
        Enable-CsOnlineSessionForReconnection #Fix for ISE Lockup!
      } 
      Catch
      {
        #We had an issue connecting to Skype
        $ErrorMessage = $_.Exception.Messag
        Write-Log -Message $ErrorMessage -Severity 3 -Component $function 
        Write-Log -Message 'Error connecting to Skype4B Online' -Severity 3 -Component $function
      }
    }
    
    
    #Check for the Sharepoint connection flag
    If ($ConnectToSharepoint) 
    {
      #Flag is set, connect to Sharepoint
      Try
      {
        #Sharepoint connection try block
        Write-Log -Message 'Connecting to Sharepoint Online' -Severity 2 -Component $function
        
        #So Now we need to kick off a new window that waits for the clipboard events
        #Create a script block with the expanded variables
        if ($global:Config.ModernAuthClipboardEnabled -eq $true) #workaround a bug where PowerShell converts the bool to a string and can't convert back
        {
          [String]$cmd = "Watch-BsCredentials -ModernAuthUsername $ModernAuthUsername -UnsecurePassword $UnsecurePassword -NoPassword"
        }
        Else
        {
          [String]$cmd = "Watch-BsCredentials -ModernAuthUsername $ModernAuthUsername -UnsecurePassword $UnsecurePassword"
        }
        [ScriptBlock]$sb = [ScriptBlock]::Create($cmd) 
        
        #and now call it
        Start-Process PowerShell $sb

        #Sleep for a few seconds to let the PowerShell window pop and fill the clipboard.
        Start-Sleep -Seconds 3

        #Now we invoke the session

        $SharepointSession = (Connect-SPOService)
        $VerbosePreference = 'SilentlyContinue' #Todo. fix for import-psmodule ignoring the -Verbose:$false flag
        Import-Module (Import-PSSession -Session $SharepointSession -AllowClobber -DisableNameChecking) -Global -DisableNameChecking
        $VerbosePreference = 'Continue' #Todo. fix for import-psmodule ignoring the -Verbose:$false flag
      }
      Catch
      {
        #We had an issue connecting to Sharepoint
        $ErrorMessage = $_.Exception.Message
        Write-Log -Message $ErrorMessage -Severity 3 -Component $function 
        Write-Log -Message 'Error connecting to Sharepoint Online' -Severity 3 -Component $function
      }
    }
    
    #Check for the AzureAD connection flag
    If ($ConnectToAzureAD) 
    {
      #Flag is set, connect to AzureAD
      Try
      {
        #Azure AD try block
        Write-Log -Message 'Connecting to Azure AD' -Severity 2 -Component $function
        #So Now we need to kick off a new window that waits for the clipboard events
        #Create a script block with the expanded variables
        if ($global:Config.ModernAuthClipboardEnabled -eq $true) #workaround a bug where PowerShell converts the bool to a string and can't convert back
        {
          [String]$cmd = "Watch-BsCredentials -ModernAuthUsername $ModernAuthUsername -UnsecurePassword $UnsecurePassword -NoPassword"
        }
        Else
        {
          [String]$cmd = "Watch-BsCredentials -ModernAuthUsername $ModernAuthUsername -UnsecurePassword $UnsecurePassword"
        }
        [ScriptBlock]$sb = [ScriptBlock]::Create($cmd) 
        
        #and now call it
        Start-Process PowerShell $sb

        #Sleep for a few seconds to let the PowerShell window pop and fill the clipboard.
        Start-Sleep -Seconds 3

        #Now we invoke the session

        $AADSession = (Connect-AzureAD)
        $VerbosePreference = 'SilentlyContinue' #Todo. fix for import-psmodule ignoring the -Verbose:$false flag
        Import-Module (Import-PSSession -Session $AADSession -AllowClobber -DisableNameChecking) -Global -DisableNameChecking
        $VerbosePreference = 'Continue' #Todo. fix for import-psmodule ignoring the -Verbose:$false flag
      }
      Catch
      {
        #We had an issue connecting to AzureAD
        $ErrorMessage = $_.Exception.Message
        Write-Log -Message $ErrorMessage -Severity 3 -Component $function 
        Write-Log -Message 'Error connecting to Azure AD' -Severity 3 -Component $function
      }
    }
    
    #Check for the 365 Compliance Centre flag
    If ($ConnectToCompliance)
    {
      #Flag is set, connect to Compliance Centre
      Try
      {
        Write-Log -Message 'Connecting to Office 365 Security and Compliance Centre' -Severity 2 -Component $function

        #So Now we need to kick off a new window that waits for the clipboard events
        #Create a script block with the expanded variables
        if ($global:Config.ModernAuthClipboardEnabled -eq $true) #workaround a bug where PowerShell converts the bool to a string and can't convert back
        {
          [String]$cmd = "Watch-BsCredentials -ModernAuthUsername $ModernAuthUsername -UnsecurePassword $UnsecurePassword -NoPassword"
        }
        Else
        {
          [String]$cmd = "Watch-BsCredentials -ModernAuthUsername $ModernAuthUsername -UnsecurePassword $UnsecurePassword"
        }
        [ScriptBlock]$sb = [ScriptBlock]::Create($cmd) 
        
        #and now call it
        Start-Process PowerShell $sb

        #Sleep for a few seconds to let the PowerShell window pop and fill the clipboard.
        Start-Sleep -Seconds 3

        #Now we invoke the session
        
        $ComplianceSession = (New-PSSession -ConfigurationName Microsoft.Exchange -ConnectionUri https://ps.compliance.protection.outlook.com/powershell-liveid/ -AllowRedirection)
        $VerbosePreference = 'SilentlyContinue' #Todo. fix for import-psmodule ignoring the -Verbose:$false flag
        Import-Module (Import-PSSession -Session $ComplianceSession -AllowClobber -DisableNameChecking) -Global -DisableNameChecking
        $VerbosePreference = 'Continue' #Todo. fix for import-psmodule ignoring the -Verbose:$false flag
      }
      Catch
      {
        #We had an issue connecting to the Compliance Centre
        $ErrorMessage = $_.Exception.Message
        Write-Log -Message $ErrorMessage -Severity 3 -Component $function 
        Write-Log -Message 'Error connecting to Office 365 Security Compliance Centre' -Severity 3 -Component $function
      }
    }
  }
  
  



  #region NoModern
  #If the modern auth flag hasn't been set, we can simply connect to the services using secure credentials
  If (!$ModernAuth) 

  {
    #See if we got passed creds
    Write-Log -Message 'Checking for Office365 Credentials' -Severity 1 -Component $function
    If ($global:StoredPsCred -eq $null) 
    {
      #No credentials, prompt user for some
      Write-Log -Message 'No Office365 credentials Found, Prompting user for creds' -Severity 3 -Component $function
      $global:StoredPsCred = Get-Credential
    }
    Else
    {
      #Found creds, continue
      Write-Log -Message "Found Office365 Creds for Username: $($global:StoredPsCred.username)" -Severity 1 -Component $function
    }
    
    #Check for the Exchange connection flag
    If ($ConnectToExchange)
    {
      #Flag is set, connect to Exchange
      Try
      {
        $pscred = $global:StoredPsCred
        #Exchange connection try block
        Write-Log -Message 'Connecting to Exchange Online' -Severity 2 -Component $function
        $O365Session = (New-PSSession -ConfigurationName Microsoft.Exchange -ConnectionUri https://outlook.office365.com/powershell-liveid/ -Credential $pscred -Authentication Basic -AllowRedirection )
        Write-Log -Message 'Importing Session' -Severity 1 -Component $function
        $VerbosePreference = 'SilentlyContinue' #Todo. fix for import-psmodule ignoring the -Verbose:$false flag
        Import-Module (Import-PSSession -Session $O365Session -AllowClobber -DisableNameChecking) -Global -DisableNameChecking
        $VerbosePreference = 'Continue' #Todo. fix for import-psmodule ignoring the -Verbose:$false flag
      } 
      Catch 
      {
        #We had an issue connecting to Exchange
        $ErrorMessage = $_.Exception.Message
        Write-Log -Message $ErrorMessage -Severity 3 -Component $function
        Write-Log -Message 'Error connecting to Exchange Online' -Severity 3 -Component $function
      }
    }

    #Check for the Skype4B connection flag
    If ($ConnectToSkype) 
    {
      #Flag is set, connect to Skype4B
      Try
      {
        $pscred = $global:StoredPsCred
        #Skype connection try block
        Write-Log -Message 'Connecting to Skype4B Online' -Severity 2 -Component $function
        $S4BOSession = (New-CsOnlineSession -Credential $pscred)
        $VerbosePreference = 'SilentlyContinue' #Todo. fix for import-psmodule ignoring the -Verbose:$false flag
        Import-Module (Import-PSSession -Session $S4BOSession -AllowClobber -DisableNameChecking) -Global -DisableNameChecking
        $VerbosePreference = 'Continue' #Todo. fix for import-psmodule ignoring the -Verbose:$false flag
      } 
      Catch
      {
        #We had an issue connecting to Skype
        $ErrorMessage = $_.Exception.Messag
        Write-Log -Message $ErrorMessage -Severity 3 -Component $function 
        Write-Log -Message 'Error connecting to Skype4B Online' -Severity 3 -Component $function
      }
    }
    
    #Check for the Teams connection flag
    If ($ConnectToTeams)
    {
      #Flag is set, connect to Teams
      Try 
      {
        $pscred = $global:StoredPsCred
        #Teams connection try block
        Write-Log -Message 'Connecting to Microsoft Teams' -Severity 2 -Component $function
        $TeamsSession = (Connect-MicrosoftTeams -Credential $pscred)
        $VerbosePreference = 'SilentlyContinue' #Todo. fix for import-psmodule ignoring the -Verbose:$false flag
        #No need to import the session. Import-Module (Import-PSSession -Session $TeamsSession -AllowClobber -DisableNameChecking) -Global -DisableNameChecking
        $VerbosePreference = 'Continue' #Todo. fix for import-psmodule ignoring the -Verbose:$false flag
      } 
      Catch
      {
        #We had an issue connecting to Teams
        $ErrorMessage = $_.Exception.Message
        Write-Log -Message $ErrorMessage -Severity 3 -Component $function 
        Write-Log -Message 'Error connecting to Microsoft Teams' -Severity 3 -Component $function
      }
    }
    
    #Check for the Sharepoint connection flag
    If ($ConnectToSharepoint) 
    {
      #Flag is set, connect to Sharepoint
      Try
      {
        $pscred = $global:StoredPsCred
        #Sharepoint connection try block
        Write-Log -Message 'Connecting to Sharepoint Online' -Severity 2 -Component $function
        $SharepointSession = (Connect-SPOService -Credential $pscred)
        $VerbosePreference = 'SilentlyContinue' #Todo. fix for import-psmodule ignoring the -Verbose:$false flag
        Import-Module (Import-PSSession -Session $SharepointSession -AllowClobber -DisableNameChecking) -Global -DisableNameChecking
        $VerbosePreference = 'Continue' #Todo. fix for import-psmodule ignoring the -Verbose:$false flag
      }
      Catch
      {
        #We had an issue connecting to Sharepoint
        $ErrorMessage = $_.Exception.Message
        Write-Log -Message $ErrorMessage -Severity 3 -Component $function 
        Write-Log -Message 'Error connecting to Sharepoint Online' -Severity 3 -Component $function
      }
    }

    #Check for the AzureAD connection flag
    If ($ConnectToAzureAD) 
    {
      #Flag is set, connect to AzureAD
      Try
      {
        $pscred = $global:StoredPsCred
        #Azure AD try block
        Write-Log -Message 'Connecting to Azure AD' -Severity 2 -Component $function
        $AADSession = (Connect-AzureAD -Credential $pscred)
        #$VerbosePreference = "SilentlyContinue" #Todo. fix for import-psmodule ignoring the -Verbose:$false flag
        #Import-Module (Import-PSSession -Session $AADSession -AllowClobber -DisableNameChecking) -Global -DisableNameChecking
        #$VerbosePreference = "Continue" #Todo. fix for import-psmodule ignoring the -Verbose:$false flag
      }
      Catch
      {
        #We had an issue connecting to AzureAD
        $ErrorMessage = $_.Exception.Message
        Write-Log -Message $ErrorMessage -Severity 3 -Component $function 
        Write-Log -Message 'Error connecting to Azure AD' -Severity 3 -Component $function
      }
    }
    
    #Check for the 365 Compliance Centre flag
    If ($ConnectToCompliance)
    {
      #Flag is set, connect to Compliance Centre
      Try
      {
        $pscred = $global:StoredPsCred
        Write-Log -Message 'Connecting to Office 365 Compliance Centre' -Severity 2 -Component $function
        $ComplianceSession = (New-PSSession -ConfigurationName Microsoft.Exchange -ConnectionUri https://ps.compliance.protection.outlook.com/powershell-liveid/ -Credential $pscred -Authentication Basic -AllowRedirection)
        $VerbosePreference = 'SilentlyContinue' #Todo. fix for import-psmodule ignoring the -Verbose:$false flag
        Import-Module (Import-PSSession -Session $ComplianceSession -AllowClobber -DisableNameChecking) -Global -DisableNameChecking
        $VerbosePreference = 'Continue' #Todo. fix for import-psmodule ignoring the -Verbose:$false flag
      }
      Catch
      {
        #We had an issue connecting to the Compliance Centre
        $ErrorMessage = $_.Exception.Message
        Write-Log -Message $ErrorMessage -Severity 3 -Component $function 
        Write-Log -Message 'Error connecting to Office 365 Compliance Centre' -Severity 3 -Component $function
      }
    }
  }
  #endregion NoModern
}

Function Update-BsAddonMenu 
{
  #Check to see if we are loaded, if we are then cleanup after ourselves
  if (($PSISE.CurrentPowerShellTab.AddOnsMenu.Submenus).displayname -eq '_BounShell') 
  {
    $null = $PSISE.CurrentPowerShellTab.AddOnsMenu.Submenus.remove($Global:isemenuitem)
  }

  #Create Initial Menu Object
  $null = ($Global:isemenuitem = ($PSISE.CurrentPowerShellTab.AddOnsMenu.Submenus.Add('_BounShell',$null ,$null)))

  #Add the Settings Button

  $null = ($Global:isemenuitem.Submenus.add('_Settings...', {
        Show-BsGuiElements
  }, $null) )


  #Now add each Tenant

  #Need to put a 'for each' code loop in here that adds Tenant 1 through 10
  $null = ($Global:isemenuitem.Submenus.add("$($global:Config.Tenant1.DisplayName)",{
        Invoke-BsNewTenantTab -Tabname $global:Config.Tenant1.DisplayName -Tenant 1
  }, 'Ctrl+Alt+1'))
  $null = ($Global:isemenuitem.Submenus.add("$($global:Config.Tenant2.DisplayName)",{
        Invoke-BsNewTenantTab -Tabname $global:Config.Tenant2.DisplayName -Tenant 2
  }, 'Ctrl+Alt+2'))
  $null = ($Global:isemenuitem.Submenus.add("$($global:Config.Tenant3.DisplayName)",{
        Invoke-BsNewTenantTab -Tabname $global:Config.Tenant3.DisplayName -Tenant 3
  }, 'Ctrl+Alt+3'))
  $null = ($Global:isemenuitem.Submenus.add("$($global:Config.Tenant4.DisplayName)",{
        Invoke-BsNewTenantTab -Tabname $global:Config.Tenant4.DisplayName -Tenant 4
  }, 'Ctrl+Alt+4'))
  $null = ($Global:isemenuitem.Submenus.add("$($global:Config.Tenant5.DisplayName)",{
        Invoke-BsNewTenantTab -Tabname $global:Config.Tenant5.DisplayName -Tenant 5
  }, 'Ctrl+Alt+5'))
  $null = ($Global:isemenuitem.Submenus.add("$($global:Config.Tenant6.DisplayName)",{
        Invoke-BsNewTenantTab -Tabname $global:Config.Tenant6.DisplayName -Tenant 6
  }, 'Ctrl+Alt+6'))
  $null = ($Global:isemenuitem.Submenus.add("$($global:Config.Tenant7.DisplayName)",{
        Invoke-BsNewTenantTab -Tabname $global:Config.Tenant7.DisplayName -Tenant 7
  }, 'Ctrl+Alt+7'))
  $null = ($Global:isemenuitem.Submenus.add("$($global:Config.Tenant8.DisplayName)",{
        Invoke-BsNewTenantTab -Tabname $global:Config.Tenant8.DisplayName -Tenant 8
  }, 'Ctrl+Alt+8'))
  $null = ($Global:isemenuitem.Submenus.add("$($global:Config.Tenant9.DisplayName)",{
        Invoke-BsNewTenantTab -Tabname $global:Config.Tenant9.DisplayName -Tenant 9
  }, 'Ctrl+Alt+9'))
  $null = ($Global:isemenuitem.Submenus.add("$($global:Config.Tenant10.DisplayName)",{
        Invoke-BsNewTenantTab -Tabname $global:Config.Tenant10.DisplayName -Tenant 10
  }, 'Ctrl+Alt+0'))
}

Function Import-BsGuiElements 
{
  #First we need to import the Functions so they exist for the GUI items
  Import-BsGuiFunctions

  #region Gui
  $null = [System.Reflection.Assembly]::Load('System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a')
  $null = [System.Reflection.Assembly]::Load('System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089')
  $Global:SettingsForm = New-Object -TypeName System.Windows.Forms.Form
  [System.Windows.Forms.DataGridView]$Global:grid_Tenants = $null
  [System.Windows.Forms.Button]$Global:btn_CancelConfig = $null
  [System.Windows.Forms.Button]$Global:Btn_ReloadConfig = $null
  [System.Windows.Forms.Button]$Global:Btn_SaveConfig = $null
  [System.Windows.Forms.Button]$Global:Btn_Default = $null
  [System.Windows.Forms.DataGridViewTextBoxColumn]$Global:Tenant_ID = $null
  [System.Windows.Forms.DataGridViewTextBoxColumn]$Global:Tenant_DisplayName = $null
  [System.Windows.Forms.DataGridViewTextBoxColumn]$Global:Tenant_Email = $null
  [System.Windows.Forms.DataGridViewTextBoxColumn]$Global:Tenant_Credentials = $null
  [System.Windows.Forms.DataGridViewCheckBoxColumn]$Global:Tenant_ModernAuth = $null
  [System.Windows.Forms.DataGridViewCheckBoxColumn]$Global:Tenant_Teams = $null
  [System.Windows.Forms.DataGridViewCheckBoxColumn]$Global:Tenant_Skype = $null
  [System.Windows.Forms.DataGridViewCheckBoxColumn]$Global:Tenant_Exchange = $null
  [System.Windows.Forms.CheckBox]$Global:cbx_AutoUpdates = $null

  [System.Windows.Forms.DataGridViewCellStyle]$Global:dataGridViewCellStyle1 = (New-Object -TypeName System.Windows.Forms.DataGridViewCellStyle)
  [System.Windows.Forms.DataGridViewCellStyle]$Global:dataGridViewCellStyle2 = (New-Object -TypeName System.Windows.Forms.DataGridViewCellStyle)
  [System.Windows.Forms.DataGridViewCellStyle]$Global:dataGridViewCellStyle3 = (New-Object -TypeName System.Windows.Forms.DataGridViewCellStyle)
  $Global:btn_CancelConfig = (New-Object -TypeName System.Windows.Forms.Button)
  $Global:Btn_ReloadConfig = (New-Object -TypeName System.Windows.Forms.Button)
  $Global:Btn_SaveConfig = (New-Object -TypeName System.Windows.Forms.Button)
  $Global:cbx_AutoUpdates = (New-Object -TypeName System.Windows.Forms.CheckBox)
  $Global:grid_Tenants = (New-Object -TypeName System.Windows.Forms.DataGridView)
  $Global:Btn_Default = (New-Object -TypeName System.Windows.Forms.Button)
  $Global:Tenant_ID = (New-Object -TypeName System.Windows.Forms.DataGridViewTextBoxColumn)
  $Global:Tenant_DisplayName = (New-Object -TypeName System.Windows.Forms.DataGridViewTextBoxColumn)
  $Global:Tenant_Email = (New-Object -TypeName System.Windows.Forms.DataGridViewTextBoxColumn)
  $Global:Tenant_Credentials = (New-Object -TypeName System.Windows.Forms.DataGridViewTextBoxColumn)
  $Global:Tenant_ModernAuth = (New-Object -TypeName System.Windows.Forms.DataGridViewCheckBoxColumn)
  $Global:Tenant_Teams = (New-Object -TypeName System.Windows.Forms.DataGridViewCheckBoxColumn)
  $Global:Tenant_Skype = (New-Object -TypeName System.Windows.Forms.DataGridViewCheckBoxColumn)
  $Global:Tenant_Exchange = (New-Object -TypeName System.Windows.Forms.DataGridViewCheckBoxColumn)
  $Global:Tenant_AzureAD = (New-Object -TypeName System.Windows.Forms.DataGridViewCheckBoxColumn)
  $Global:Tenant_Compliance = (New-Object -TypeName System.Windows.Forms.DataGridViewCheckBoxColumn)
  $Global:cbx_ClipboardAuth = (New-Object -TypeName System.Windows.Forms.CheckBox)
  $Global:cliplabel = (New-Object -TypeName System.Windows.Forms.LinkLabel)
  ([System.ComponentModel.ISupportInitialize]$Global:grid_Tenants).BeginInit()
  $Global:SettingsForm.SuspendLayout()
  #
  #btn_CancelConfig
  #
  $Global:btn_CancelConfig.BackColor = [System.Drawing.Color]::White
  $Global:btn_CancelConfig.FlatStyle = [System.Windows.Forms.FlatStyle]::Flat
  $Global:btn_CancelConfig.Font = (New-Object -TypeName System.Drawing.Font -ArgumentList @([System.String]'Microsoft Sans Serif', [System.Single]8.25, [System.Drawing.FontStyle]::Bold, [System.Drawing.GraphicsUnit]::Pixel, ([System.Byte][System.Byte]0)))
  $Global:btn_CancelConfig.ForeColor = [System.Drawing.Color]::FromArgb(([System.Int32]([System.Byte][System.Byte]8)),([System.Int32]([System.Byte][System.Byte]116)),([System.Int32]([System.Byte][System.Byte]170)))

  $Global:btn_CancelConfig.Location = (New-Object -TypeName System.Drawing.Point -ArgumentList @([System.Int32]936, [System.Int32]368))
  $Global:btn_CancelConfig.Name = [System.String]'btn_CancelConfig'
  $Global:btn_CancelConfig.Size = (New-Object -TypeName System.Drawing.Size -ArgumentList @([System.Int32]94, [System.Int32]23))
  $Global:btn_CancelConfig.TabIndex = [System.Int32]59
  $Global:btn_CancelConfig.Text = [System.String]'Cancel'
  $Global:btn_CancelConfig.UseVisualStyleBackColor = $false
  $Global:btn_CancelConfig.add_Click($Global:btn_CancelConfig_Click)
  #
  #Btn_ReloadConfig
  #
  $Global:Btn_ReloadConfig.BackColor = [System.Drawing.Color]::White
  $Global:Btn_ReloadConfig.FlatStyle = [System.Windows.Forms.FlatStyle]::Flat
  $Global:Btn_ReloadConfig.Font = (New-Object -TypeName System.Drawing.Font -ArgumentList @([System.String]'Microsoft Sans Serif', [System.Single]8.25, [System.Drawing.FontStyle]::Bold, [System.Drawing.GraphicsUnit]::Pixel, ([System.Byte][System.Byte]0)))
  $Global:Btn_ReloadConfig.ForeColor = [System.Drawing.Color]::FromArgb(([System.Int32]([System.Byte][System.Byte]8)),([System.Int32]([System.Byte][System.Byte]116)),([System.Int32]([System.Byte][System.Byte]170)))

  $Global:Btn_ReloadConfig.Location = (New-Object -TypeName System.Drawing.Point -ArgumentList @([System.Int32]704, [System.Int32]368))
  $Global:Btn_ReloadConfig.Name = [System.String]'Btn_ReloadConfig'
  $Global:Btn_ReloadConfig.Size = (New-Object -TypeName System.Drawing.Size -ArgumentList @([System.Int32]110, [System.Int32]23))
  $Global:Btn_ReloadConfig.TabIndex = [System.Int32]58
  $Global:Btn_ReloadConfig.Text = [System.String]'Reload Config'
  $Global:Btn_ReloadConfig.UseVisualStyleBackColor = $true
  $Global:Btn_ReloadConfig.add_Click($Global:Btn_ConfigReload_Click)
  #
  #Btn_SaveConfig
  #
  $Global:Btn_SaveConfig.BackColor = [System.Drawing.Color]::White
  $Global:Btn_SaveConfig.FlatStyle = [System.Windows.Forms.FlatStyle]::Flat
  $Global:Btn_SaveConfig.Font = (New-Object -TypeName System.Drawing.Font -ArgumentList @([System.String]'Microsoft Sans Serif', [System.Single]8.25, [System.Drawing.FontStyle]::Bold, [System.Drawing.GraphicsUnit]::Pixel, ([System.Byte][System.Byte]0)))
  $Global:Btn_SaveConfig.ForeColor = [System.Drawing.Color]::FromArgb(([System.Int32]([System.Byte][System.Byte]8)),([System.Int32]([System.Byte][System.Byte]116)),([System.Int32]([System.Byte][System.Byte]170)))

  $Global:Btn_SaveConfig.Location = (New-Object -TypeName System.Drawing.Point -ArgumentList @([System.Int32]820, [System.Int32]368))
  $Global:Btn_SaveConfig.Name = [System.String]'Btn_SaveConfig'
  $Global:Btn_SaveConfig.Size = (New-Object -TypeName System.Drawing.Size -ArgumentList @([System.Int32]110, [System.Int32]23))
  $Global:Btn_SaveConfig.TabIndex = [System.Int32]57
  $Global:Btn_SaveConfig.Text = [System.String]'Save Config'
  $Global:Btn_SaveConfig.UseVisualStyleBackColor = $false
  $Global:Btn_SaveConfig.add_Click($Global:Btn_SaveConfig_Click)
  #
  #cbx_AutoUpdates
  #
  $Global:cbx_AutoUpdates.AutoSize = $true
  $Global:cbx_AutoUpdates.Checked = $true
  $Global:cbx_AutoUpdates.CheckState = [System.Windows.Forms.CheckState]::Checked
  $Global:cbx_AutoUpdates.ForeColor = [System.Drawing.Color]::FromArgb(([System.Int32]([System.Byte][System.Byte]8)),([System.Int32]([System.Byte][System.Byte]116)),([System.Int32]([System.Byte][System.Byte]170)))

  $Global:cbx_AutoUpdates.Location = (New-Object -TypeName System.Drawing.Point -ArgumentList @([System.Int32]27, [System.Int32]370))
  $Global:cbx_AutoUpdates.Name = [System.String]'cbx_AutoUpdates'
  $Global:cbx_AutoUpdates.Size = (New-Object -TypeName System.Drawing.Size -ArgumentList @([System.Int32]183, [System.Int32]17))
  $Global:cbx_AutoUpdates.TabIndex = [System.Int32]75
  $Global:cbx_AutoUpdates.Text = [System.String]'Automatically Check for Updates'
  $Global:cbx_AutoUpdates.UseVisualStyleBackColor = $true
  $Global:cbx_AutoUpdates.add_CheckedChanged($Global:cbx_NoIntLCD_CheckedChanged_1)
  #
  #grid_Tenants
  #
  $Global:grid_Tenants.AllowUserToAddRows = $false
  $Global:grid_Tenants.AllowUserToDeleteRows = $false
  $Global:dataGridViewCellStyle1.Alignment = [System.Windows.Forms.DataGridViewContentAlignment]::MiddleLeft
  $Global:dataGridViewCellStyle1.BackColor = [System.Drawing.SystemColors]::Control
  $Global:dataGridViewCellStyle1.Font = (New-Object -TypeName System.Drawing.Font -ArgumentList @([System.String]'Microsoft Sans Serif', [System.Single]8.25, [System.Drawing.FontStyle]::Regular, [System.Drawing.GraphicsUnit]::Pixel, ([System.Byte][System.Byte]0)))
  $Global:dataGridViewCellStyle1.ForeColor = [System.Drawing.SystemColors]::WindowText
  $Global:dataGridViewCellStyle1.SelectionBackColor = [System.Drawing.SystemColors]::Highlight
  $Global:dataGridViewCellStyle1.SelectionForeColor = [System.Drawing.SystemColors]::HighlightText
  $Global:dataGridViewCellStyle1.WrapMode = [System.Windows.Forms.DataGridViewTriState]::True
  $Global:grid_Tenants.ColumnHeadersDefaultCellStyle = $Global:dataGridViewCellStyle1
  $Global:grid_Tenants.ColumnHeadersHeightSizeMode = [System.Windows.Forms.DataGridViewColumnHeadersHeightSizeMode]::AutoSize
  $Global:grid_Tenants.Columns.AddRange($Global:Tenant_ID,$Global:Tenant_DisplayName,$Global:Tenant_Email,$Global:Tenant_Credentials,$Global:Tenant_ModernAuth,$Global:Tenant_Teams,$Global:Tenant_Skype,$Global:Tenant_Exchange,$Global:Tenant_AzureAD,$Global:Tenant_Compliance)
  $Global:dataGridViewCellStyle2.Alignment = [System.Windows.Forms.DataGridViewContentAlignment]::MiddleLeft
  $Global:dataGridViewCellStyle2.BackColor = [System.Drawing.SystemColors]::Window
  $Global:dataGridViewCellStyle2.Font = (New-Object -TypeName System.Drawing.Font -ArgumentList @([System.String]'Microsoft Sans Serif', [System.Single]8.25, [System.Drawing.FontStyle]::Regular, [System.Drawing.GraphicsUnit]::Pixel, ([System.Byte][System.Byte]0)))
  $Global:dataGridViewCellStyle2.ForeColor = [System.Drawing.Color]::FromArgb(([System.Int32]([System.Byte][System.Byte]8)),([System.Int32]([System.Byte][System.Byte]116)),([System.Int32]([System.Byte][System.Byte]170)))

  $Global:dataGridViewCellStyle2.SelectionBackColor = [System.Drawing.SystemColors]::Highlight
  $Global:dataGridViewCellStyle2.SelectionForeColor = [System.Drawing.SystemColors]::HighlightText
  $Global:dataGridViewCellStyle2.WrapMode = [System.Windows.Forms.DataGridViewTriState]::False
  $Global:grid_Tenants.DefaultCellStyle = $Global:dataGridViewCellStyle2
  $Global:grid_Tenants.Location = (New-Object -TypeName System.Drawing.Point -ArgumentList @([System.Int32]12, [System.Int32]12))
  $Global:grid_Tenants.Name = [System.String]'grid_Tenants'
  $Global:dataGridViewCellStyle3.Alignment = [System.Windows.Forms.DataGridViewContentAlignment]::MiddleLeft
  $Global:dataGridViewCellStyle3.BackColor = [System.Drawing.SystemColors]::Control
  $Global:dataGridViewCellStyle3.Font = (New-Object -TypeName System.Drawing.Font -ArgumentList @([System.String]'Microsoft Sans Serif', [System.Single]8.25, [System.Drawing.FontStyle]::Regular, [System.Drawing.GraphicsUnit]::Pixel, ([System.Byte][System.Byte]0)))
  $Global:dataGridViewCellStyle3.ForeColor = [System.Drawing.SystemColors]::WindowText
  $Global:dataGridViewCellStyle3.SelectionBackColor = [System.Drawing.SystemColors]::Highlight
  $Global:dataGridViewCellStyle3.SelectionForeColor = [System.Drawing.SystemColors]::HighlightText
  $Global:dataGridViewCellStyle3.WrapMode = [System.Windows.Forms.DataGridViewTriState]::True
  $Global:grid_Tenants.RowHeadersDefaultCellStyle = $Global:dataGridViewCellStyle3
  $Global:grid_Tenants.Size = (New-Object -TypeName System.Drawing.Size -ArgumentList @([System.Int32]1020, [System.Int32]336))
  $Global:grid_Tenants.TabIndex = [System.Int32]76
  $Global:grid_Tenants.add_CellContentClick($Global:grid_Tenants_CellContentClick)

  #
  #Btn_Default
  #
  $Global:Btn_Default.BackColor = [System.Drawing.Color]::White
  $Global:Btn_Default.FlatStyle = [System.Windows.Forms.FlatStyle]::Flat
  $Global:Btn_Default.Font = (New-Object -TypeName System.Drawing.Font -ArgumentList @([System.String]'Microsoft Sans Serif', [System.Single]8.25, [System.Drawing.FontStyle]::Bold, [System.Drawing.GraphicsUnit]::Pixel, ([System.Byte][System.Byte]0)))
  $Global:Btn_Default.ForeColor = [System.Drawing.Color]::FromArgb(([System.Int32]([System.Byte][System.Byte]8)),([System.Int32]([System.Byte][System.Byte]116)),([System.Int32]([System.Byte][System.Byte]170)))

  $Global:Btn_Default.Location = (New-Object -TypeName System.Drawing.Point -ArgumentList @([System.Int32]588, [System.Int32]368))
  $Global:Btn_Default.Name = [System.String]'Btn_Default'
  $Global:Btn_Default.Size = (New-Object -TypeName System.Drawing.Size -ArgumentList @([System.Int32]110, [System.Int32]23))
  $Global:Btn_Default.TabIndex = [System.Int32]77
  $Global:Btn_Default.Text = [System.String]'Reset to Default'
  $Global:Btn_Default.UseVisualStyleBackColor = $true
  $Global:Btn_Default.add_Click($Global:Btn_Default_Click)
  #
  #Tenant_ID
  #
  $Global:Tenant_ID.AutoSizeMode = [System.Windows.Forms.DataGridViewAutoSizeColumnMode]::AllCells
  $Global:Tenant_ID.Frozen = $true
  $Global:Tenant_ID.HeaderText = [System.String]'ID'
  $Global:Tenant_ID.Name = [System.String]'Tenant_ID'
  $Global:Tenant_ID.ReadOnly = $true
  $Global:Tenant_ID.Width = [System.Int32]43
  #
  #Tenant_DisplayName
  #
  $Global:Tenant_DisplayName.AutoSizeMode = [System.Windows.Forms.DataGridViewAutoSizeColumnMode]::AllCells
  $Global:Tenant_DisplayName.Frozen = $true
  $Global:Tenant_DisplayName.HeaderText = [System.String]'Display Name'
  $Global:Tenant_DisplayName.Name = [System.String]'Tenant_DisplayName'
  $Global:Tenant_DisplayName.SortMode = [System.Windows.Forms.DataGridViewColumnSortMode]::NotSortable
  $Global:Tenant_DisplayName.Width = [System.Int32]78
  #
  #Tenant_Email
  #
  $Global:Tenant_Email.AutoSizeMode = [System.Windows.Forms.DataGridViewAutoSizeColumnMode]::AllCells
  $Global:Tenant_Email.Frozen = $true
  $Global:Tenant_Email.HeaderText = [System.String]'Sign-In Address'
  $Global:Tenant_Email.Name = [System.String]'Tenant_Email'
  $Global:Tenant_Email.SortMode = [System.Windows.Forms.DataGridViewColumnSortMode]::NotSortable
  $Global:Tenant_Email.Width = [System.Int32]78
  #
  #Tenant_Credentials
  #
  $Global:Tenant_Credentials.Frozen = $true
  $Global:Tenant_Credentials.HeaderText = [System.String]'Credentials'
  $Global:Tenant_Credentials.Name = [System.String]'Tenant_Credentials'
  #
  #Tenant_ModernAuth
  #
  $Global:Tenant_ModernAuth.HeaderText = [System.String]'Uses Modern Auth?'
  $Global:Tenant_ModernAuth.Name = [System.String]'Tenant_ModernAuth'
  #
  #Tenant_Teams
  #
  $Global:Tenant_Teams.HeaderText = [System.String]'Connect to Teams?'
  $Global:Tenant_Teams.Name = [System.String]'Tenant_Teams'
  #
  #Tenant_Skype
  #
  $Global:Tenant_Skype.HeaderText = [System.String]'Connect to Skype?'
  $Global:Tenant_Skype.Name = [System.String]'Tenant_Skype'
  #
  #Tenant_Exchange
  #
  $Global:Tenant_Exchange.HeaderText = [System.String]'Connect to Exchange?'
  $Global:Tenant_Exchange.Name = [System.String]'Tenant_Exchange'
  #
  #Tenant_AzureAD
  #
  $Global:Tenant_AzureAD.HeaderText = [System.String]'Connect to Azure AD?'
  $Global:Tenant_AzureAD.Name = [System.String]'Tenant_AzureAD'
  #
  #Tenant_Compliance
  #
  $Global:Tenant_Compliance.HeaderText = [System.String]'Connect to Compliance Centre?'
  $Global:Tenant_Compliance.Name = [System.String]'Tenant_Compliance'
  #
  #cbx_ClipboardAuth
  #
  $Global:cbx_ClipboardAuth.AutoSize = $true
  $Global:cbx_ClipboardAuth.ForeColor = [System.Drawing.Color]::FromArgb(([System.Int32]([System.Byte][System.Byte]8)),([System.Int32]([System.Byte][System.Byte]116)),([System.Int32]([System.Byte][System.Byte]170)))

  $Global:cbx_ClipboardAuth.Location = (New-Object -TypeName System.Drawing.Point -ArgumentList @([System.Int32]230, [System.Int32]370))
  $Global:cbx_ClipboardAuth.Name = [System.String]'cbx_ClipboardAuth'
  $Global:cbx_ClipboardAuth.Size = (New-Object -TypeName System.Drawing.Size -ArgumentList @([System.Int32]228, [System.Int32]17))
  $Global:cbx_ClipboardAuth.TabIndex = [System.Int32]78
  $Global:cbx_ClipboardAuth.Text = [System.String]'Enable Modern Auth Clipboard Integration'
  $Global:cbx_ClipboardAuth.UseVisualStyleBackColor = $true
  #
  #cliplabel
  #
  $Global:cliplabel.AutoSize = $true
  $Global:cliplabel.Location = (New-Object -TypeName System.Drawing.Point -ArgumentList @([System.Int32]460, [System.Int32]370))
  $Global:cliplabel.Name = [System.String]'cliplabel'
  $Global:cliplabel.Size = (New-Object -TypeName System.Drawing.Size -ArgumentList @([System.Int32]53, [System.Int32]13))
  $Global:cliplabel.TabIndex = [System.Int32]79
  $Global:cliplabel.TabStop = $true
  $Global:cliplabel.Text = [System.String]'more info'
  $Global:cliplabel.add_Click($Global:cliplabel_click)
  #
  #Global:SettingsForm
  #
  $Global:SettingsForm.BackColor = [System.Drawing.Color]::White
  $Global:SettingsForm.ClientSize = (New-Object -TypeName System.Drawing.Size -ArgumentList @([System.Int32]1044, [System.Int32]404))
  $Global:SettingsForm.Controls.Add($Global:Btn_Default)
  $Global:SettingsForm.Controls.Add($Global:grid_Tenants)
  $Global:SettingsForm.Controls.Add($Global:cbx_AutoUpdates)
  $Global:SettingsForm.Controls.Add($Global:btn_CancelConfig)
  $Global:SettingsForm.Controls.Add($Global:Btn_ReloadConfig)
  $Global:SettingsForm.Controls.Add($Global:Btn_SaveConfig)
  $Global:SettingsForm.Controls.Add($Global:cliplabel)
  $Global:SettingsForm.Controls.Add($Global:cbx_ClipboardAuth)
  $Global:SettingsForm.Name = [System.String]'Global:SettingsForm'
  $Global:SettingsForm.add_Load($Global:SettingsForm_Load)
  ([System.ComponentModel.ISupportInitialize]$Global:grid_Tenants).EndInit()
  $Global:SettingsForm.ResumeLayout($false)
  $Global:SettingsForm.PerformLayout()
  Add-Member -InputObject $Global:SettingsForm -Name base -Value $base -MemberType NoteProperty
  Add-Member -InputObject $Global:SettingsForm -Name grid_Tenants -Value $Global:grid_Tenants -MemberType NoteProperty
  Add-Member -InputObject $Global:SettingsForm -Name btn_CancelConfig -Value $Global:btn_CancelConfig -MemberType NoteProperty
  Add-Member -InputObject $Global:SettingsForm -Name Btn_ReloadConfig -Value $Global:Btn_ReloadConfig -MemberType NoteProperty
  Add-Member -InputObject $Global:SettingsForm -Name Btn_SaveConfig -Value $Global:Btn_SaveConfig -MemberType NoteProperty
  Add-Member -InputObject $Global:SettingsForm -Name Btn_Default -Value $Global:Btn_Default -MemberType NoteProperty
  Add-Member -InputObject $Global:SettingsForm -Name Tenant_ID -Value $Global:Tenant_ID -MemberType NoteProperty
  Add-Member -InputObject $Global:SettingsForm -Name Tenant_DisplayName -Value $Global:Tenant_DisplayName -MemberType NoteProperty
  Add-Member -InputObject $Global:SettingsForm -Name Tenant_Email -Value $Global:Tenant_Email -MemberType NoteProperty
  Add-Member -InputObject $Global:SettingsForm -Name Tenant_Credentials -Value $Global:Tenant_Credentials -MemberType NoteProperty
  Add-Member -InputObject $Global:SettingsForm -Name Tenant_ModernAuth -Value $Global:Tenant_ModernAuth -MemberType NoteProperty
  Add-Member -InputObject $Global:SettingsForm -Name Tenant_Teams -Value $Global:Tenant_Teams -MemberType NoteProperty
  Add-Member -InputObject $Global:SettingsForm -Name Tenant_Skype -Value $Global:Tenant_Skype -MemberType NoteProperty
  Add-Member -InputObject $Global:SettingsForm -Name Tenant_Exchange -Value $Tenant_Exchange -MemberType NoteProperty
  Add-Member -InputObject $Global:SettingsForm -Name Tenant_AzureAD -Value $Global:Tenant_AzureAD -MemberType NoteProperty
  Add-Member -InputObject $Global:SettingsForm -Name Tenant_Compliance -Value $Global:Tenant_Compliance -MemberType NoteProperty
  Add-Member -InputObject $Global:SettingsForm -Name cbx_ClipboardAuth -Value $Global:cbx_ClipboardAuth -MemberType NoteProperty
  Add-Member -InputObject $Global:SettingsForm -Name cliplabel -Value $Global:cliplabel -MemberType NoteProperty
  Add-Member -InputObject $Global:SettingsForm -Name cbx_AutoUpdates -Value $Global:cbx_AutoUpdates -MemberType NoteProperty
  #endregion Gui
}

Function Import-BsGuiFunctions 
{
  #Gui Cancel button
  $Global:btn_CancelConfig_Click = 
  {
    Read-BsConfigFile
    Hide-BsGuiElements
  }

  #Gui Save Config Button
  $Global:Btn_SaveConfig_Click = 
  {
    $Global:btn_CancelConfig.Text = [System.String]'Close'
    Write-BsConfigFile
    If ($PSISE)
    {
      Update-BsAddonMenu
    }
  }

  #Gui Set Defaults Button
  $Global:Btn_Default_Click = 
  {
    Import-BsDefaultConfig
    If ($PSISE)
    {
      Update-BsAddonMenu
    }
  }

  #Gui Button to Reload Config
  $Global:Btn_ConfigReload_Click = 
  {
    Read-BsConfigFile
    If ($PSISE)
    {
      Update-BsAddonMenu
    }
  }
  #Gui link object to open a browser for more info on the modern auth clipboard
  $Global:cliplabel_click = 
  {
    Start-Process -FilePath 'https://UcMadScientist.com/BounShell/'
  }
}

Function Show-BsGuiElements
{
  #Reset the cancel button
  $Global:btn_CancelConfig.Text = [System.String]'Cancel'
  $null = $Global:SettingsForm.ShowDialog()
}

Function Hide-BsGuiElements
{
  $null = $Global:SettingsForm.Hide()
  If ($global:Config.AutoUpdatesEnabled)
  { 
    #Check for the required modules
    Write-Log -component $function -Message 'Checking for required modules based on selections. This can take some time.' -severity 2
    #Teams Module Check
    if ($global:Config.Tenant1.ConnectToTeams -or $global:Config.Tenant2.ConnectToTeams -or $global:Config.Tenant3.ConnectToTeams -or $global:Config.Tenant4.ConnectToTeams -or $global:Config.Tenant5.ConnectToTeams -or $global:Config.Tenant6.ConnectToTeams -or $global:Config.Tenant7.ConnectToTeams -or $global:Config.Tenant8.ConnectToTeams -or $global:Config.Tenant9.ConnectToTeams -or $global:Config.Tenant10.ConnectToTeams)
    {
      Test-BsInstalledModules -ModuleName $TestedTeamsModule -ModuleVersion $TestedTeamsModuleVer
    }

    #Exchange Module Check
    if ($global:Config.Tenant1.ConnectToExchange -or $global:Config.Tenant2.ConnectToExchange -or $global:Config.Tenant3.ConnectToExchange -or $global:Config.Tenant4.ConnectToExchange -or $global:Config.Tenant5.ConnectToExchange -or $global:Config.Tenant6.ConnectToExchange -or $global:Config.Tenant7.ConnectToExchange -or $global:Config.Tenant8.ConnectToExchange -or $global:Config.Tenant9.ConnectToExchange -or $global:Config.Tenant10.ConnectToExchange)
    {
      Test-BsInstalledModules -ModuleName $TestedExchangeModule -ModuleVersion $TestedExchangeModuleVer
    }
    
    #MsOnline Module Check
    if ($global:Config.Tenant1.ConnectToAzureAD -or $global:Config.Tenant2.ConnectToAzureAD -or $global:Config.Tenant3.ConnectToAzureAD -or $global:Config.Tenant4.ConnectToAzureAD -or $global:Config.Tenant5.ConnectToAzureAD -or $global:Config.Tenant6.ConnectToAzureAD -or $global:Config.Tenant7.ConnectToAzureAD -or $global:Config.Tenant8.ConnectToAzureAD -or $global:Config.Tenant9.ConnectToAzureAD -or $global:Config.Tenant10.ConnectToAzureAD)
    {
      Test-BsInstalledModules -ModuleName $TestedMSOnlineModule -ModuleVersion $TestedMSOnlineModuleVer
    }
    
    #Skype4B Module Check
    if ($global:Config.Tenant1.ConnectToSkype -or $global:Config.Tenant2.ConnectToSkype -or $global:Config.Tenant3.ConnectToSkype -or $global:Config.Tenant4.ConnectToSkype -or $global:Config.Tenant5.ConnectToSkype -or $global:Config.Tenant6.ConnectToSkype -or $global:Config.Tenant7.ConnectToSkype -or $global:Config.Tenant8.ConnectToSkype -or $global:Config.Tenant9.ConnectToSkype -or $global:Config.Tenant10.ConnectToSkype)
    {
      Test-BsInstalledModules -ModuleName $TestedSkype4BOModule -ModuleVersion $TestedSkype4BOModuleVer
    }
    
    Write-Log -component $function -Message 'Module check complete' -severity 2
  }
}

Function Start-BounShell
{
  $function = 'Start-BounShell'
  #Allows us to separate all the "onetime" run objects in case we get dot sourced.
  Write-Log -component $function -Message "Script executed from $PSScriptRoot" -severity 1
  Write-Log -component $function -Message 'Loading BounShell...' -severity 2

  #Load the Gui Elements
  Import-BsGuiElements

  #check for config file then load the default

  #Check for and load the config file if present
  If(Test-Path $global:ConfigFilePath)
  {
    Write-Log -component $function -Message "Found $ConfigFilePath, loading..." -severity 1
    Read-BsConfigFile
  }

  Else
  {
    Write-Log -component $function -Message 'Could not locate config file, Using Defaults' -severity 3
    #If there is no config file. Load a default
    Import-BsDefaultConfig

    Write-Log -component $function -Message "As we didn't find a config file we will assume this is a first run." -severity 3
    Write-Log -component $function -Message 'Thus we will remind you that while all care is taken to store your credentials in a safe manner, we cannot be held responsible for any data breaches.' -severity 3
    Write-Log -component $function -Message "If someone was to get a hold of your BounShell.xml AND your user profile private encryption key it's possible to reverse-engineer stored credentials." -severity 3
    Write-Log -component $function -Message "Seriously, whilst the password store is encrypted, it's not perfect!" -severity 3
    Pause
  }

  #check for script update
  if ($SkipUpdateCheck -eq $false)
  {
    Get-ScriptUpdate
  } #todo enable update checking

  #Check for Modules
  #Test-ManagementTools #todo fix
  
  #Now Create the Objects in the ISE
  If($PSISE) 
  {
    Update-BsAddonMenu
  }
  
  
  Write-Log -component $function -Message 'BounShell Loaded' -severity 2
  
  #Check we are actually in the ISE
  If(!$PSISE) 
  {   
    Write-Log -component $function -Message 'Could not locate $PSISE Variable' -severity 1
    Write-Log -component $function -Message 'Launched BounShell without ISE Support, Keyboard Shortcuts will be unavailable' -severity 2
    Write-Host -Object ''
    Write-Log -component $function -Message 'To configure BounShell tenants run Show-BsGuiElements' -severity 2
    Write-Log -component $function -Message 'To connect to a tenant run Connect-BsO365Tenant'  -severity 2
    Return #Yes I know Return sucks, but I think it's better than Throw.
  }
}

Function Watch-BsCredentials
{
  [CmdletBinding()]
  PARAM
  (
    $ModernAuthUsername,
    $UnsecurePassword,
    [switch]$NoPassword
  )
  [string]$function = 'Watch-BsCredentials'
  If (!$ModernAuthUsername)
  {
    Write-Log -component $function -Message "This Cmdlet is for BoundShell's internal use only. Please use 'Start-Bounshell' to launch the tool" -severity 3
    Pause
    return
  }
  Write-Log -component $function -Message "Called to connect to $ModernAuthUsername" -severity 3
  # Load the API we need for Keypresses
  $signature = @'
    [DllImport("user32.dll", CharSet=CharSet.Auto, ExactSpelling=true)]
    public static extern short GetAsyncKeyState(int virtualKeyCode);
'@


  # load signatures and make members available
  $API = Add-Type -MemberDefinition $signature -Name 'Keypress' -Namespace API -PassThru
    
  # define that we are waiting for 'v' keypress
  $waitFor = 'v'
  $ascii = [byte][char]$waitFor.ToUpper()

  Set-Clipboard -Value $ModernAuthUsername
  Write-Log -component $function -Message "$ModernAuthUsername placed into Clipboard" -severity 1
  Write-Log -component $function -Message "Press 'Ctrl+v' to paste the username $ModernAuthUsername in the modern auth window" -severity 2


  do 
  {
    Start-Sleep -Milliseconds 40
  }
  until ($API::GetAsyncKeyState($ascii) -eq -32767)
  
  Start-Sleep -Milliseconds 1000
  if ($NoPassword)
  {
    Set-Clipboard -Value $UnsecurePassword
    Write-Log -component $function -Message 'Password placed into Clipboard' -severity 1
    Write-Log -component $function -Message "Press 'Ctrl+v' to paste the password in the modern auth window" -severity 2

    do 
    {
      Start-Sleep -Milliseconds 40
    }
    until ($API::GetAsyncKeyState($ascii) -eq -32767)

    Set-Clipboard -Value 'Thanks for using BounShell'
  }
}

Function Test-BsInstalledModules 
{
  param
  (
    [Parameter(Mandatory)] [string]$ModuleName,
    [Parameter(Mandatory)] [string]$ModuleVersion
  )
  [string]$function = 'Test-BsInstalledModules'
  Write-Log -component $function -Message "Called to check $ModuleName" -severity 1
   
  $NeedsInstall = $false
  $needsupdate = $false
  $NeedsCleaup = $false
  $LatestModuleVersion = $null
  
  #Pull the module from the local machine
  $Module = Get-Module -Name $ModuleName -ListAvailable
  
  #If we have more than one version we need to clean up
  if($Module.count -gt 1)
  {
    $needscleanup = $true
    
    # Identify modules with multiple versions installed
    $MultiModules = $Module | Group-Object -Property name -NoElement | Where-Object -Property count -GT -Value 1

    $title = "Multiple copies of $ModuleName installed"
    $Message = "I've detected multiple installs of $ModuleName. Should I remove them and install the latest version?"
    $yes = New-Object -TypeName System.Management.Automation.Host.ChoiceDescription -ArgumentList '&Yes', `
    'Cleans up the installed PowerShell Modules'

    $no = New-Object -TypeName System.Management.Automation.Host.ChoiceDescription -ArgumentList '&No', `
    'No thanks.'

    $options = [System.Management.Automation.Host.ChoiceDescription[]]($yes, $no)

    $result = $host.ui.PromptForChoice($title, $Message, $options, 0) 

    switch ($result)
    {
      0 
      {
        #User said yes
        Write-Log -component $function -Message 'User opted to cleanup modules' -severity 1
        #start $BlogPost
        Repair-BsInstalledModules -ModuleName $ModuleName -Operation 'Cleanup'
        Write-Log -component $function -Message 'Cleanup completed' -severity 1
      }
      #User said no
      1 
      {
        Write-Log -component $function -Message 'User opted to skip cleanup' -severity 1
        Write-Log -component $function -Message "Found multiple copies of $ModuleName" -severity 3
        Write-Log -component $function -Message "To correct this later run 'Repair-BsInstalledModules -Operation Cleanup -ModuleName $ModuleName'" -severity 3
      }
    }
  }
   
  #If we have one module, check it's up to date
  if($Module.count -eq 1)
  {
    Write-Log -component $function -Message "Found one copy of $ModuleName" -severity 1
  }
  

  #Module not installed
  if($Module.count -eq 0)
  {
    Write-Log -component $function -Message "$ModuleName Module not installed on local computer" -severity 3
    $NeedsInstall = $true
    $title = "$ModuleName not found"
    $Message = "$ModuleName is not installed on this computer, it is required to connect to services. Can I install it for you?"
    $yes = New-Object -TypeName System.Management.Automation.Host.ChoiceDescription -ArgumentList '&Yes', `
    'Installs required PowerShell modules'

    $no = New-Object -TypeName System.Management.Automation.Host.ChoiceDescription -ArgumentList '&No', `
    'No thanks.'

    $options = [System.Management.Automation.Host.ChoiceDescription[]]($yes, $no)

    $result = $host.ui.PromptForChoice($title, $Message, $options, 0) 

    switch ($result)
    {
      0 
      {
        #User said yes
        Write-Log -component $function -Message 'User opted to install module' -severity 1
        #start $BlogPost
        Repair-BsInstalledModules -ModuleName $ModuleName -Operation 'Install'
        Write-Log -component $function -Message 'Install completed' -severity 1
      }
      #User said no
      1 
      {
        Write-Log -component $function -Message 'User opted to skip Install' -severity 1
        Write-Log -component $function -Message "$ModuleName not found" -severity 3
        Write-Log -component $function -Message "To correct this later run 'Repair-BsInstalledModules -Operation Install -ModuleName $ModuleName'" -severity 3
      }
    }
  }

  #Okay, we have checked if everything is installed, now lets check and report on the version
 
  Write-Log -component $function -Message "Checking for the latest version of $ModuleName in the PSGallery" -severity 2
  $gallery = $Module.where({
      $_.repositorysourcelocation
  })

  foreach ($Module in $gallery) 
  {
    #find the current version in the gallery
    Try 
    {
      $online = Find-Module -Name $Module.name -Repository PSGallery -ErrorAction Stop
    }
    Catch 
    {
      #Todo What the f**k?
      Write-Warning -Message ('Module {0} was not found in the PSGallery' -f $Module.name)
    }

    #compare versions
    if ($online.version -gt $Module.version) 
    {
      $needsupdate = $true
      Write-Log -component $function -Message "An updated version of the $ModuleName module is available in the PSGallery" -severity 2
      $title = "Update for $ModuleName found"
      $Message = "There is a newer version of $ModuleName on the PowerShell gallery, would you like me to install it?"
      $yes = New-Object -TypeName System.Management.Automation.Host.ChoiceDescription -ArgumentList '&Yes', `
      'Updates the PowerShell module'

      $no = New-Object -TypeName System.Management.Automation.Host.ChoiceDescription -ArgumentList '&No', `
      'No thanks.'

      $options = [System.Management.Automation.Host.ChoiceDescription[]]($yes, $no)

      $result = $host.ui.PromptForChoice($title, $Message, $options, 0) 

      switch ($result)
      {
        0 
        {
          #User said yes
          Write-Log -component $function -Message 'User opted to update module' -severity 1
          #start $BlogPost
          Repair-BsInstalledModules -ModuleName $ModuleName -Operation 'Update'
          Write-Log -component $function -Message 'Update completed' -severity 1
        }
        #User said no
        1 
        {
          Write-Log -component $function -Message 'User opted to skip update' -severity 1
          Write-Log -component $function -Message "Found updated version of $ModuleName" -severity 3
          Write-Log -component $function -Message "To update this later run 'Repair-BsInstalledModules -Operation Update -ModuleName $ModuleName'" -severity 3
        }
      }
    }
     
    else 
    {
      Write-Log -component $function -Message "Your version of the $ModuleName module is up to date" -severity 2
    }
  }
  

  ## for the official Excahnge online module
  ## Connect-IPPSSession
  ##https://cmdletpswmodule.blob.core.windows.net/exopsmodule/Microsoft.Online.CSE.PSModule.Client.application
}

Function Repair-BsInstalledModules 
{
  param
  (
    [Parameter(Mandatory)] [string]$ModuleName,
    [Parameter(Mandatory)] [string]$Operation #Install, Cleaup, Update
  )
 
  [string]$function = 'Repair-BsInstalledModules'
  Write-Log -component $function -Message "Called to $Operation $ModuleName" -severity 1
  Write-Log -component $function -Message 'Checking for elevated session' -severity 1
  #Check we are running as admin, we dont want to install modules in the users context
  if (!(([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole] 'Administrator')))
  {
    # throw 'Please Note: You are trying to run this script without elevated Administator Privileges. In order to run this script you will require PowerShell running in Administrator Mode'
    Write-Log -component $function -Message 'Not running as Administrator, invoking new session' -severity 2
    $newProcess = New-Object -TypeName System.Diagnostics.ProcessStartInfo -ArgumentList 'PowerShell'
   
    # Specify the current script path and name as a parameter
    $newProcess.Arguments = "Repair-BsInstalledModules -modulename $ModuleName -operation $Operation"
   
    # Indicate that the process should be elevated
    $newProcess.Verb = 'runas'
   
    # Start the new process
    $foo = [System.Diagnostics.Process]::Start($newProcess)
  }
  else 
  {
    Write-Log -component $function -Message "Running as Administrator, performing $Operation" -severity 2
  
    if($ModuleName -eq 'MSExchange')
    {
      Write-Log -component $function -Message 'The Exchange Online PowerShell Module is not in the PsGallery, it is installed via your browser as a ClickOnce app' -severity 2
      Write-Log -component $function -Message 'Attempting to install now, if this fails please visit' -severity 2
      Write-Log -component $function -Message 'https://cmdletpswmodule.blob.core.windows.net/exopsmodule/Microsoft.Online.CSE.PSModule.Client.application ' -severity 2
      Write-Log -component $function -Message 'using Internet Explorer' -severity 2
      Start-Process -FilePath 'Iexplore https://cmdletpswmodule.blob.core.windows.net/exopsmodule/Microsoft.Online.CSE.PSModule.Client.application'
    }
    else
    {
      Switch ($Operation)

      {
        'Install'
        { 
          $output = (Install-Module -name $ModuleName)
          Write-Log -component $function -Message 'Complete!' -severity 2
          Get-Module -Name $ModuleName -ListAvailable
          Start-Sleep -Seconds 5
        }
        'Update'
        { 
          $output = (Update-Module -name $ModuleName)
          Write-Log -component $function -Message 'Complete!' -severity 2
          Get-Module -Name $ModuleName -ListAvailable
          Start-Sleep -Seconds 5
        }
        'Cleanup'
        { 
          #Pull the currently installed modules
          $Modules = Get-Module -Name $ModuleName -ListAvailable
          # Identify modules with multiple versions installed
          $gallery = $Modules.where({
              $_.repositorysourcelocation
          })
        
          foreach ($mod in $gallery)
          {
            Write-Log -component $function -Message "Removing $($mod.version)" -severity 2
            Uninstall-Module -Name $ModuleName -RequiredVersion $mod.version     
          }
          Install-Module -name $ModuleName
        }
 
      }
    }
  }
}
 

#now we export the relevant stuff

Export-ModuleMember -Function Read-BsConfigFile
Export-ModuleMember -Function Write-BsConfigFile
Export-ModuleMember -Function Import-BsDefaultConfig
Export-ModuleMember -Function Invoke-BsNewTenantTab
Export-ModuleMember -Function Connect-BsO365Tenant
Export-ModuleMember -Function Update-BsAddonMenu
Export-ModuleMember -Function Import-BsGuiElements
Export-ModuleMember -Function Import-BsGuiFunctions
Export-ModuleMember -Function Show-BsGuiElements
Export-ModuleMember -Function Hide-BsGuiElements
Export-ModuleMember -Function Start-BounShell
Export-ModuleMember -Function Watch-BsCredentials
Export-ModuleMember -Function Test-BsInstalledModules
Export-ModuleMember -Function Repair-BsInstalledModules