IBTool.psm1

$script:ModuleRoot = $PSScriptRoot
$script:ModuleVersion = (Import-PowerShellDataFile -Path "$($script:ModuleRoot)\IBTool.psd1").ModuleVersion

# Detect whether at some level dotsourcing was enforced
$script:doDotSource = Get-PSFConfigValue -FullName IBTool.Import.DoDotSource -Fallback $false
if ($IBTool_dotsourcemodule) { $script:doDotSource = $true }

<#
Note on Resolve-Path:
All paths are sent through Resolve-Path/Resolve-PSFPath in order to convert them to the correct path separator.
This allows ignoring path separators throughout the import sequence, which could otherwise cause trouble depending on OS.
Resolve-Path can only be used for paths that already exist, Resolve-PSFPath can accept that the last leaf my not exist.
This is important when testing for paths.
#>


# Detect whether at some level loading individual module files, rather than the compiled module was enforced
$importIndividualFiles = Get-PSFConfigValue -FullName IBTool.Import.IndividualFiles -Fallback $false
if ($IBTool_importIndividualFiles) { $importIndividualFiles = $true }
if (Test-Path (Resolve-PSFPath -Path "$($script:ModuleRoot)\..\.git" -SingleItem -NewChild)) { $importIndividualFiles = $true }
if ("<was compiled>" -eq '<was not compiled>') { $importIndividualFiles = $true }
    
function Import-ModuleFile
{
    <#
        .SYNOPSIS
            Loads files into the module on module import.
         
        .DESCRIPTION
            This helper function is used during module initialization.
            It should always be dotsourced itself, in order to proper function.
             
            This provides a central location to react to files being imported, if later desired
         
        .PARAMETER Path
            The path to the file to load
         
        .EXAMPLE
            PS C:\> . Import-ModuleFile -File $function.FullName
     
            Imports the file stored in $function according to import policy
    #>

    [CmdletBinding()]
    Param (
        [string]
        $Path
    )
    
    $resolvedPath = $ExecutionContext.SessionState.Path.GetResolvedPSPathFromPSPath($Path).ProviderPath
    if ($doDotSource) { . $resolvedPath }
    else { $ExecutionContext.InvokeCommand.InvokeScript($false, ([scriptblock]::Create([io.file]::ReadAllText($resolvedPath))), $null, $null) }
}

#region Load individual files
if ($importIndividualFiles)
{
    # Execute Preimport actions
    foreach ($path in (& "$ModuleRoot\internal\scripts\preimport.ps1")) {
        . Import-ModuleFile -Path $path
    }
    
    # Import all internal functions
    foreach ($function in (Get-ChildItem "$ModuleRoot\internal\functions" -Filter "*.ps1" -Recurse -ErrorAction Ignore))
    {
        . Import-ModuleFile -Path $function.FullName
    }
    
    # Import all public functions
    foreach ($function in (Get-ChildItem "$ModuleRoot\functions" -Filter "*.ps1" -Recurse -ErrorAction Ignore))
    {
        . Import-ModuleFile -Path $function.FullName
    }
    
    # Execute Postimport actions
    foreach ($path in (& "$ModuleRoot\internal\scripts\postimport.ps1")) {
        . Import-ModuleFile -Path $path
    }
    
    # End it here, do not load compiled code below
    return
}
#endregion Load individual files

#region Load compiled code
<#
This file loads the strings documents from the respective language folders.
This allows localizing messages and errors.
Load psd1 language files for each language you wish to support.
Partial translations are acceptable - when missing a current language message,
it will fallback to English or another available language.
#>

Import-PSFLocalizedString -Path "$($script:ModuleRoot)\en-us\*.psd1" -Module 'IBTool' -Language 'en-US'

function Add-ArrayToDataGrid {
    <#
    .SYNOPSIS
    Adds passed data to the DataGrid
     
    .DESCRIPTION
    Adds passed Array data to the DataGrid
     
    .PARAMETER ArrayData
    Array data source to add to the DataGrid
 
    .PARAMETER DataGrid
    DataGrid object to add the array data.
     
    .PARAMETER Form
    Windows main Form to be refreshed.
 
    .EXAMPLE
    PS C:\> Add-ArrayToDataGrid -ArrayData $MyData -DataGrid $MyGrid
    Adds array data '$MyData' to the DataGrid '$MyGrid'
    #>

    [CmdletBinding()]
    param (
        [System.Collections.ArrayList]$ArrayData,

        [System.Windows.Forms.DataGridView]$DataGrid,

        [System.Windows.Forms.Form]$Form
    )
    $DataGrid.datasource = $ArrayData
    $DataGrid.AutoResizeColumns()
    $Form.Refresh()
}

function Assert-ServiceConnection {
    <#
    .SYNOPSIS
    Checks current connection status for SCC, EXO and AzureAD
     
    .DESCRIPTION
    Checks current connection status for SCC, EXO and AzureAD
     
    .EXAMPLE
    PS C:\> Assert-ServiceConnection
    Checks current connection status for SCC, EXO and AzureAD
    #>

    [CmdletBinding()]
    param (
        # Parameters
    )
    $Sessions = Get-PSSession
    $ServicesToConnect = New-Object -TypeName "System.Collections.ArrayList"

    # Check if SCC connection
    if ( -not ($Sessions.ComputerName -match "ps.compliance.protection.outlook.com") ) { $null = $ServicesToConnect.add("SCC") }

    # Check if EXO connection
    if ( $Sessions.ComputerName -notcontains "outlook.office365.com" ) { $null = $ServicesToConnect.add("EXO") }

    # Check if AzureAD connection
    try{
        $Null = Get-AzureADCurrentSessionInfo -ErrorAction Stop
    }
    catch {
        $null = $ServicesToConnect.add("AzureAD")
    }
    return $ServicesToConnect
}

function Connect-OnlineServices {
    <#
    .SYNOPSIS
    Connect to Online Services.
 
    .DESCRIPTION
    Use this function to connect to EXO, SCC, MicrosoftTeams, MS Online and AzureAD Online Services.
 
    .PARAMETER Credential
    Credential to use for the connection.
 
    .PARAMETER Services
    List of the desired services to connect to. Current available services: EXO, SCC, MicrosoftTeams, MSOnline, AzureAD, AzureADPreview, Azure.
 
    .PARAMETER Confirm
    If this switch is enabled, you will be prompted for confirmation before executing any operations that change state.
 
    .PARAMETER WhatIf
    If this switch is enabled, no actions are performed but informational messages will be displayed that explain what would happen if the command were to run.
    
    .EXAMPLE
    PS C:\> Connect-OnlineServices -Credential $UserCredential -EXO -AzureAD
    Connects to Exchange and AzureAD Online Services with the passed User Credentials variable.
     
    #>

    [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSUseSingularNouns", "")]
    [CmdletBinding(SupportsShouldProcess = $True, ConfirmImpact = 'Low')]
    param(
        [PSCredential]
        $Credential = (Get-Credential -Message "Please specify O365 Global Admin Credentials"),

        [ValidateSet('EXO', 'SCC', 'MicrosoftTeams', 'MSOnline', 'AzureAD', 'AzureADPreview', 'Azure')]
        [String[]]
        $Services
    )
    if(-not $Credential){
        $Credential = Get-Credential -Message "Please specify O365 Global Admin Credentials"
    }
    if(-not $Credential){
        Stop-PSFFunction -Message "Credentials entered are invalid." -EnableException $true -Cmdlet $PSCmdlet
    }

    Switch ( $Services ) {
        Azure {
            Invoke-PSFProtectedCommand -Action "Connecting to Azure" -Target "Azure" -ScriptBlock {
                Write-PSFHostColor -String  "[$((Get-Date).ToString("HH:mm:ss"))] Connecting to Azure"
                Install-Module Azure -Force -ErrorAction Stop
                Import-Module Azure -ErrorAction Stop
            } -EnableException $true -PSCmdlet $PSCmdlet
        }

        AzureAD {
            Invoke-PSFProtectedCommand -Action "Connecting to AzureAD" -Target "AzureAD" -ScriptBlock {
                Write-PSFHostColor -String  "[$((Get-Date).ToString("HH:mm:ss"))] Connecting to AzureAD"
                if ( !(Get-Module AzureAD -ListAvailable) -and !(Get-Module AzureAD) ) {
                    Install-Module AzureAD -Force -ErrorAction Stop
                }
                try {
                    Import-module AzureAD
                    $null = Connect-AzureAD -Credential $Credential -ErrorAction Stop
                }
                catch {
                    if ( ($_.Exception.InnerException.InnerException.InnerException.InnerException.ErrorCode | ConvertFrom-Json).error -eq 'interaction_required' ) {
                        Write-PSFHostColor -String  "[$((Get-Date).ToString("HH:mm:ss"))] Your account seems to be requiring MFA to connect to Azure AD. Requesting to authenticate"
                        $null = Connect-AzureAD -AccountId $Credential.UserName.toString() -ErrorAction Stop
                    }
                    else {
                        return $_
                    }
                }
            } -EnableException $true -PSCmdlet $PSCmdlet
        }

        AzureADPreview {
            Invoke-PSFProtectedCommand -Action "Connecting to AzureAD Preview" -Target "AzureAD" -ScriptBlock {
                Write-PSFHostColor -String  "[$((Get-Date).ToString("HH:mm:ss"))] Connecting to AzureAD Preview"
                if ( !(Get-Module AzureADPreview -ListAvailable) -and !(Get-Module AzureADPreview) ) {
                    Install-Module AzureADPreview -Force -ErrorAction Stop
                }
                try {
                    Import-module AzureADPreview
                    $null = Connect-AzureAD -Credential $Credential -ErrorAction Stop
                }
                catch {
                    if ( ($_.Exception.InnerException.InnerException.InnerException.InnerException.ErrorCode | ConvertFrom-Json).error -eq 'interaction_required' ) {
                        Write-PSFHostColor -String  "[$((Get-Date).ToString("HH:mm:ss"))] Your account seems to be requiring MFA to connect to Azure AD. Requesting to authenticate"
                        $null = Connect-AzureAD -AccountId $Credential.UserName.toString() -ErrorAction Stop
                    }
                    else {
                        return $_
                    }
                }
            } -EnableException $true -PSCmdlet $PSCmdlet
        }

        MSOnline {
            Invoke-PSFProtectedCommand -Action "Connecting to MSOnline" -Target "MSOnline" -ScriptBlock {
                Write-PSFHostColor -String  "[$((Get-Date).ToString("HH:mm:ss"))] Connecting to MSOnline"
                if ( !(Get-Module MSOnline -ListAvailable) -and !(Get-Module MSOnline) ) {
                    Install-Module MSOnline -Force -ErrorAction Stop
                }
                try {
                    Import-Module MSOnline
                    Connect-MsolService -Credential $Credential -ErrorAction Stop
                }
                catch {
                    Write-PSFHostColor -String  "[$((Get-Date).ToString("HH:mm:ss"))] Your account seems to be requiring MFA to connect to MS Online. Requesting to authenticate"
                    Connect-MsolService -ErrorAction Stop
                }
            } -EnableException $true -PSCmdlet $PSCmdlet
        }

        MicrosoftTeams {
            Invoke-PSFProtectedCommand -Action "Connecting to MicrosoftTeams" -Target "MicrosoftTeams" -ScriptBlock {
                Write-PSFHostColor -String "[$((Get-Date).ToString("HH:mm:ss"))] Connecting to MicrosoftTeams"
                if ( !(Get-Module MicrosoftTeams -ListAvailable) -and !(Get-Module MicrosoftTeams) ) {
                    Install-Module MicrosoftTeams -Force -ErrorAction Stop
                }
                try {
                    #Connect to Microsoft Teams
                    $null = Connect-MicrosoftTeams -Credential $Credential -ErrorAction Stop
    
                    #Connection to Skype for Business Online and import into Ps session
                    $session = New-CsOnlineSession -Credential $Credential -ErrorAction Stop
                    $null = Import-PsSession $session
                }
                catch {
                    if ( ($_.Exception.InnerException.InnerException.InnerException.InnerException.ErrorCode | ConvertFrom-Json).error -eq 'interaction_required' ) {
                        Write-PSFHostColor -String  "[$((Get-Date).ToString("HH:mm:ss"))] Your account seems to be requiring MFA to connect to MicrosoftTeams. Requesting to authenticate"
                        #Connect to Microsoft Teams
                        $null = Connect-MicrosoftTeams -ErrorAction Stop
    
                        #Connection to Skype for Business Online and import into Ps session
                        $session = New-CsOnlineSession -ErrorAction Stop
                        $null = Import-PsSession $session
                    }
                    else {
                        return $_
                    }
                }
            } -EnableException $true -PSCmdlet $PSCmdlet
        }

        SCC {
            Invoke-PSFProtectedCommand -Action "Connecting to Security and Compliance" -Target "SCC" -ScriptBlock {
                Write-PSFHostColor -String "[$((Get-Date).ToString("HH:mm:ss"))] Connecting to Security and Compliance"
                try {
                    Connect-IPPSSession -Credential $Credential -ErrorAction Stop -WarningAction SilentlyContinue
                }
                catch {
                    if ( ($_.Exception.InnerException.InnerException.InnerException.InnerException.ErrorCode | ConvertFrom-Json).error -eq 'interaction_required' ) {
                        Write-PSFHostColor -String  "[$((Get-Date).ToString("HH:mm:ss"))] Your account seems to be requiring MFA to connect to Security and Compliance. Requesting to authenticate"
                        Connect-IPPSSession -UserPrincipalName $Credential.Username.toString() -ErrorAction Stop -WarningAction SilentlyContinue
                    }
                    else {
                        return $_
                    }
                }
            } -EnableException $true -PSCmdlet $PSCmdlet
        }

        EXO {
            Invoke-PSFProtectedCommand -Action "Connecting to Exchange Online" -Target "EXO" -ScriptBlock {
                Write-PSFHostColor -String "[$((Get-Date).ToString("HH:mm:ss"))] Connecting to Exchange Online"
                try {
                    # Getting current PS Sessions
                    $Sessions = Get-PSSession
                    if ($Sessions.ComputerName -eq "outlook.office365.com") { return }
                    else { Connect-ExchangeOnline -Credential $Credential -ShowBanner:$False -ErrorAction Stop }
                }
                catch {
                    if ( ($_.Exception.InnerException.InnerException.InnerException.InnerException.ErrorCode | ConvertFrom-Json).error -eq 'interaction_required' ) {
                        Write-PSFHostColor -String  "[$((Get-Date).ToString("HH:mm:ss"))] Your account seems to be requiring MFA to connect to Exchange Online. Requesting to authenticate"
                        Connect-ExchangeOnline -UserPrincipalName $Credential.Username.toString() -ShowBanner:$False -ErrorAction Stop
                    }
                    else {
                        return $_
                    }
                }
            } -EnableException $true -PSCmdlet $PSCmdlet
        }
    }
}

Function Get-AuditLogStatus {
    <#
    .SYNOPSIS
    Function to check Audit log status in the tenant.
     
    .DESCRIPTION
    Function to check Audit log status in the tenant.
     
    .EXAMPLE
    PS C:\> Get-AuditLogStatus
    Function to check Audit log status in the tenant.
    #>

    [cmdletbinding()]
    Param(
        # Parameters
    )
    # Verify Audit Logging is enabled
    Write-PSFHostColor -String "[$((Get-Date).ToString("HH:mm:ss"))] Verifing if Audit Logging is enabled."
    if ( -not (Get-AdminAuditLogConfig).UnifiedAuditLogIngestionEnabled ){
        Write-PSFHostColor -String "[$((Get-Date).ToString("HH:mm:ss"))] Audit Logging is not enabled. please run 'Set-AdminAuditLogConfig -UnifiedAuditLogIngestionEnabled `$true' and wait at least 1 hour." -DefaultColor Red
        $labelAuditLogStatusValue.ForeColor = "Red"
        $labelAuditLogStatusValue.Text = "False"
    }
    else{
        Write-PSFHostColor -String "[$((Get-Date).ToString("HH:mm:ss"))] Audit Logging is enabled." -DefaultColor Green
        $labelAuditLogStatusValue.ForeColor = "Green"
        $labelAuditLogStatusValue.Text = "True"
    }
}

Function Get-ExchangeABPStatus {
    <#
    .SYNOPSIS
    Function to verify if Exchange AddressBook Policies are in place.
     
    .DESCRIPTION
    Function to verify if Exchange AddressBook Policies are in place.
     
    .EXAMPLE
    PS C:\> Get-ExchangeABPStatus
    Function to verify if Exchange AddressBook Policies are in place.
    #>

    [CmdletBinding()]
    Param(
        # Parameters
    )
    # Verify no Exchange AddressBook Policies are in place
    Write-PSFHostColor -String "[$((Get-Date).ToString("HH:mm:ss"))] Verifing if no Exchange AddressBook Policies are in place."
    if ( Get-AddressBookPolicy ){
        Write-PSFHostColor -String "[$((Get-Date).ToString("HH:mm:ss"))] Existing Address Book Policies in Exchange will get in conflict with Information Barriers." -DefaultColor Red
        $labelABPStatusValue.ForeColor = "Red"
        $labelABPStatusValue.Text = "False"
    }
    else{
        Write-PSFHostColor -String "[$((Get-Date).ToString("HH:mm:ss"))] No Exchange AddressBook Policies were found." -DefaultColor Green
        $labelABPStatusValue.ForeColor = "Green"
        $labelABPStatusValue.Text = "True"
    }
}

Function Get-IBPolicies {
    <#
    .SYNOPSIS
    This function gets the current Information Barriers Policies in the tenant.
     
    .DESCRIPTION
    This function gets the current Information Barriers Policies in the tenant.
 
    .PARAMETER ShowOutputLine
    Use this switch to show a small output line to Powershell session.
     
    .EXAMPLE
    PS C:\> Get-IBPolicies
    This function gets the current Information Barriers Policies in the tenant.
    #>

    [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSUseSingularNouns", "")]
    [OutputType([System.Collections.ArrayList])]
    [CmdletBinding()]
    Param(
        [Switch]$ShowOutputline
    )
    if ( $ShowOutputline ) { Write-PSFHostColor -String "[$((Get-Date).ToString("HH:mm:ss"))] Getting current Information Barriers Policies." }
    $statusBar.Text = "Running..."
    $array = New-Object System.Collections.ArrayList
    $array.AddRange( (Get-InformationBarrierPolicy | Select-Object Name, State, AssignedSegment, SegmentsAllowed, SegmentsBlocked, block*) )
    $statusBar.Text = "Ready. IB Policies found: $($array.count)"
    return $array
}

Function Get-IBPoliciesAppStatus {
    <#
    .SYNOPSIS
    This function will get the current Information Barriers Policies Application Status.
     
    .DESCRIPTION
    This function will get the current Information Barriers Policies Application Status.
     
    .PARAMETER ShowOutputLine
    Use this switch to show a small output line to Powershell session.
 
    .EXAMPLE
    PS C:\> Get-IBPoliciesAppStatus
    This function will get the current Information Barriers Policies Application Status.
 
    #>

    [OutputType([System.Collections.ArrayList])]
    [CmdletBinding()]
    param (
        [Switch]$ShowOutputline
    )
    if ( $ShowOutputline ) { Write-PSFHostColor -String "[$((Get-Date).ToString("HH:mm:ss"))] Getting current Information Barriers Policies Application status." }
    $statusBar.Text = "Running..."
    $array = New-Object System.Collections.ArrayList
    $results = Get-InformationBarrierPoliciesApplicationStatus -All:$true | Select-Object ApplicationStartTime, ApplicationEndTime, Status, PercentProgress
    $results | ForEach-Object { $null = $array.Add($_) }
    $statusBar.Text = "Ready."
    return $array
}

function Get-IBPoliciesRecipientStatus {
    <#
    .SYNOPSIS
    This function gets the current Information Barrier Recipient status.
     
    .DESCRIPTION
    This function gets the current Information Barrier Recipient status between 2 users.
     
    .PARAMETER User1
    Defines the first user identity to compare.
    You can use any value that uniquely identifies each user, such as name, alias, distinguished name, canonical domain name, email address, or GUID.
 
    .PARAMETER User2
    Defines the second user identity to compare.
    You can use any value that uniquely identifies each user, such as name, alias, distinguished name, canonical domain name, email address, or GUID.
     
    .PARAMETER ShowOutputLine
    Use this switch to show a small output line to Powershell session.
     
    .EXAMPLE
    PS C:\> Get-IBPoliciesRecipientStatus -User1 "john@contoso.com" -User2 "mark@contoso.com"
    This function gets the current Information Barrier Recipient status between john@contoso.com and mark@contoso.com.
     
    #>

    [OutputType([System.Collections.ArrayList])]
    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $true, HelpMessage = "Defines the first user identity to compare.")]
        [string]$user1,

        [Parameter(Mandatory = $true, HelpMessage = "Defines the second user identity to compare.")]
        [string]$user2,

        [Switch]$ShowOutputline
    )
    if ( $ShowOutputline ) { Write-PSFHostColor -String "[$((Get-Date).ToString("HH:mm:ss"))] Getting current information Barrier Recipient status between $user1 and $user2." }
    $statusBar.Text = "Running..."
    #$array = New-Object System.Collections.ArrayList
    $array = New-Object System.Collections.ArrayList
    $SegmentsFound1 = New-Object System.Collections.ArrayList
    $SegmentsFound2 = New-Object System.Collections.ArrayList
    $AllSegments = get-OrgSegments
    foreach ($segment in $AllSegments){
        $Members = Get-SegmentMembers -SegmentName $segment.name
        if ( $Members.PrimarySMTPAddress -eq $user1 ) { $null = $SegmentsFound1.add($segment) }
        if ( $Members.PrimarySMTPAddress -eq $user2 ) { $null = $SegmentsFound2.add($segment) }
    }
    $matchedPolicies = Get-IBPolicies | Where-Object { $_.AssignedSegment -eq $SegmentsFound1.name -and ($_.SegmentsAllowed -match $SegmentsFound2.name -or $_.SegmentsBlocked -match $SegmentsFound2.name)}
    $matchedPolicies | ForEach-Object { $null = $array.Add( $_ ) }

    $statusBar.Text = "Ready. Matched: $($array.count)"
    return $array
}

function Get-IBServicePrincipal {
    <#
    .SYNOPSIS
    This function gets the current Information Barriers Service Principal in the tenant.
     
    .DESCRIPTION
    This function gets the current Information Barriers Service Principal in the tenant.
     
    .EXAMPLE
    PS C:\> Get-IBServicePrincipal
    This function gets the current Information Barriers Service Principal in the tenant.
    #>

    [CmdletBinding()]
    param (
        # Parameters
    )
    Write-PSFHostColor -String "[$((Get-Date).ToString("HH:mm:ss"))] Getting Information Barriers Service Principal."
    $Sp = Get-AzureADServicePrincipal -All:$True | Where-Object appid -eq "bcf62038-e005-436d-b970-2a472f8c1982"
    if ($null -eq $sp) {
        $labelIBServicePrincipalValue.ForeColor = "Red"
        $labelIBServicePrincipalValue.Text = "False"
        Write-PSFHostColor -String "[$((Get-Date).ToString("HH:mm:ss"))] Information Barriers Service Principal not found." -DefaultColor "Red"

        $buttonNewServicePrincipal = New-Object System.Windows.Forms.Button
        $buttonNewServicePrincipal.DataBindings.DefaultDataSourceUpdateMode = 0
        $buttonNewServicePrincipal.ForeColor = [System.Drawing.Color]::FromArgb(255,0,0,0)
        $buttonNewServicePrincipal.Location = New-Object System.Drawing.Point(270,57)
        $buttonNewServicePrincipal.Size = New-Object System.Drawing.Size(250,25)
        $buttonNewServicePrincipal.TabIndex = 17
        $buttonNewServicePrincipal.Name = "NewServicePrincipal"
        $buttonNewServicePrincipal.Text = "Add Service Principal and Grant consent"
        $buttonNewServicePrincipal.UseVisualStyleBackColor = $True
        $buttonNewServicePrincipal.add_Click({New-IBServicePrincipal})
        $MainForm.Controls.Add($buttonNewServicePrincipal)
    }
    else{
        $labelIBServicePrincipalValue.ForeColor = "Green"
        $labelIBServicePrincipalValue.text = "True"
        Write-PSFHostColor -String "[$((Get-Date).ToString("HH:mm:ss"))] Information Barriers Service Principal found." -DefaultColor "Green"
    }
}

Function Get-OrgSegments {
    <#
    .SYNOPSIS
    This function gets the current Organization Segments in the tenant.
     
    .DESCRIPTION
    This function gets the current Organization Segments in the tenant.
     
    .PARAMETER ShowOutputLine
    Use this switch to show a small output line to Powershell session.
 
    .EXAMPLE
    PS C:\> Get-OrgSegments
    This function gets the current Organization Segments in the tenant.
    #>

    [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSUseSingularNouns", "")]
    [OutputType([System.Collections.ArrayList])]
    [CmdletBinding()]
    Param(
        [Switch]$ShowOutputline
    )
    if ( $ShowOutputline ) { Write-PSFHostColor -String "[$((Get-Date).ToString("HH:mm:ss"))] Getting current Organization Segments." }
    $statusBar.Text = "Running..."
    $array = New-Object System.Collections.ArrayList
    $array.AddRange( (Get-OrganizationSegment | Select-Object Name,UserGroupFilter,CreatedBy,WhenCreated) )

    $statusBar.Text = "Ready. Segments found: $($array.count)"
    return $array
}

function Get-SegmentMembers {
    <#
    .SYNOPSIS
    This function gets the current Organization Segment members.
 
    .DESCRIPTION
    This function gets the current Organization Segment members for the specified Segment.
 
    .PARAMETER SegmentName
    Defines the Organization Segment name.
 
    .PARAMETER ShowOutputLine
    Use this switch to show a small output line to Powershell session.
     
 
    .EXAMPLE
    PS C:\> Get-SegmentMembers -SegmentName "HR Members"
    Gets the current members for the Organization Segment named 'HR Members'.
 
    #>

    [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSUseSingularNouns", "")]
    [OutputType([System.Collections.ArrayList])]
    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $True, HelpMessage = "Enter Organization Segment Name.")]
        [String]$SegmentName,

        [Switch]$ShowOutputline
    )
    if ( $ShowOutputline ) { Write-PSFHostColor -String "[$((Get-Date).ToString("HH:mm:ss"))] Getting Organization Segment Members." }
    $statusBar.Text = "Running..."
    $array = New-Object System.Collections.ArrayList
    $filter = (Get-OrganizationSegment -Identity $SegmentName).UserGroupFilter
    $array.Add( (Get-EXORecipient -Filter $filter -ResultSize Unlimited | Select-Object Name,PrimarySMTPAddress,*recipientType* ) )

    $statusBar.Text = "Ready. Members found: $($array.count)"
    return $array
}

function New-IBPolicy
{
    <#
    .SYNOPSIS
    Creates a new Information Barrier policy.
     
    .DESCRIPTION
    Creates a new Information Barrier policy.
     
    .PARAMETER PolicyName
    Defines the Information Barrier Policy Name.
     
    .PARAMETER AssignedSegment
    Defines the assigned segment to the policy.
     
    .PARAMETER AssignedAction
    Defines the Policy action to Allow or Block to other segments.
     
    .PARAMETER AorBSegments
    Defines the segment(s) to be Allowed or Blocked in the policy.
     
    .EXAMPLE
    PS C:\> New-IBPolicy -PolicyName "Allowed HR to Sales" -AssignedSegment "HR" -AssignedAction "SegmentsAllowed" -AorBSegments "Sales"
    This function will create the new Information Barrier policy named "Allowed HR to Sales" allowing communications to "Sales" team.
 
    #>

    [CmdletBinding()]
    Param (
        [Parameter(Mandatory = $true, HelpMessage = "Defines the Information Barrier Policy Name.")]
        [String]$PolicyName,

        [Parameter(Mandatory = $true, HelpMessage = "Defines the assigned segment to the policy.")]
        [String]$AssignedSegment,

        [ValidateSet('SegmentsAllowed','SegmentsBlocked')]
        [Parameter(Mandatory = $true, HelpMessage = "Defines the Policy action to Allow or Block to other segments.")]
        [String]$AssignedAction,

        [Parameter(Mandatory = $true, HelpMessage = "Defines the segment(s) to be Allowed or Blocked in the policy.")]
        [String]$AorBSegments
    )
    $statusBar.Text = "Running..."
    try {
        Write-PSFHostColor -String "[$((Get-Date).ToString("HH:mm:ss"))] Creating new Information Barrier Policy '$PolicyName'."
        $segmentsList = New-Object System.Collections.ArrayList
        $List = $AorBSegments.replace("'",'').split(",")
        foreach ($item in $list) { $null = $segmentsList.add($item.Trim() ) }

        if ($AssignedAction -eq "SegmentsAllowed") {
            $null = $segmentsList.add( $AssignedSegment )
            New-InformationBarrierPolicy -Name $PolicyName -AssignedSegment $AssignedSegment -SegmentsAllowed $segmentsList -State Active -Confirm:$False -ErrorAction Stop
        }
        else {
            New-InformationBarrierPolicy -Name $PolicyName -AssignedSegment $AssignedSegment -SegmentsBlocked $segmentsList -State Active -Confirm:$False -ErrorAction Stop
        }
        Write-PSFHostColor -String "[$((Get-Date).ToString("HH:mm:ss"))] Successfully created Information Barrier Policy '$PolicyName'."
        $statusBar.Text = "Ready. Created Information Barrier Policy '$PolicyName'."
    }
    catch {
        Write-PSFHostColor -String "[$((Get-Date).ToString("HH:mm:ss"))] Something failed to create the new Information Barrier Policy '$PolicyName'. $_"
        $statusBar.Text = "Ready. Someting failed to create the new Information Barrier Policy '$PolicyName'. Please see the Powershell window to verify error message."
    }
}

function New-IBServicePrincipal {
    <#
    .SYNOPSIS
    This function creates a new Information Barriers Service Principal in the tenant.
     
    .DESCRIPTION
    This function creates a new Information Barriers Service Principal in the tenant.
 
    .PARAMETER Confirm
    If this switch is enabled, you will be prompted for confirmation before executing any operations that change state.
 
    .PARAMETER WhatIf
    If this switch is enabled, no actions are performed but informational messages will be displayed that explain what would happen if the command were to run.
 
    .EXAMPLE
    PS C:\> New-IBServicePrincipal
    This function creates a new Information Barriers Service Principal in the tenant.
 
    #>

    [CmdletBinding(SupportsShouldProcess = $True, ConfirmImpact = 'Low')]
    param (
        # Parameters
    )
    Write-PSFHostColor -String "[$((Get-Date).ToString("HH:mm:ss"))] Creating new Information Barriers Service Principal in AzureAD."
    $newSP = New-AzureADServicePrincipal -AppId "bcf62038-e005-436d-b970-2a472f8c1982"
    Start-Process "https://login.microsoftonline.com/common/adminconsent?client_id=$($newSp.appId)"
    
    # Refresh IB Service Principal Status
    Get-IBServicePrincipal
    $MainForm.Controls.RemoveByKey("NewServicePrincipal")
    $MainForm.Refresh()

    <#
    $AdminObjectId = (get-AzureAdUser -searchstring (Get-AzureADCurrentSessionInfo).Account).ObjectId
 
    if ( -not (Get-AzureADServiceAppRoleAssignment -ObjectId $newsp.ObjectId -All:$true | Where-Object {$_.PrincipalId -eq $AdminObjectId}) ){
        $AppRoleAssignment = $newSP | New-AzureADServiceAppRoleAssignment -PrincipalId $AdminObjectId -ResourceId $newSP.ObjectId -id "00000000-0000-0000-0000-000000000000" -ErrorAction Stop
    }
 
    if ( -not (Get-AzureADServicePrincipalOAuth2PermissionGrant -ObjectId $newSP.ObjectId -All:$True) ){
        $TenantId = (Get-AzureADTenantDetail).objectid
 
        $result = Login-AzAccount -Credential $credential -ErrorAction Stop
        $context = Get-AzContext
        $refreshToken = @($context.TokenCache.ReadItems() | Where-Object {$_.tenantId -eq $tenantId -and $_.ExpiresOn -gt (Get-Date)}).AccessToken
        $body = "grant_type=refresh_token&refresh_token=$($refreshToken)&resource=$($newsp.ObjectId)"
        $apiToken = Invoke-RestMethod "https://login.windows.net/$tenantId/oauth2/token" -Method POST -Body $body -ContentType 'application/x-www-form-urlencoded' -ErrorAction Stop
        $header = @{
            'Authorization' = 'Bearer ' + $apiToken.access_token
            'X-Requested-With'= 'XMLHttpRequest'
            'x-ms-client-request-id'= [guid]::NewGuid()
            'x-ms-correlation-id' = [guid]::NewGuid()}
        $url = "https://main.iam.ad.ext.azure.com/api/RegisteredApplications/$($newSP.AppId)/Consent?onBehalfOfAll=true"
        Invoke-RestMethod –Uri $url –Headers $header –Method POST -ErrorAction Stop
    }
    #>

}

function New-OrgSegment
{
    <#
    .SYNOPSIS
    Creates a new organization Segment.
     
    .DESCRIPTION
    Creates a new organization Segment to be used in Information Barriers.
     
    .PARAMETER Name
    Defines the Organization Segment Name.
     
    .PARAMETER GroupFilter
    Defines the User Group filter attribute to be use.
     
    .PARAMETER Comparison
    Defines the condition's comparison. Can be "Equals" or "Not Equals".
     
    .PARAMETER AttributeValue
    Defines the attribute value.
     
    .EXAMPLE
    PS C:\> New-OrgSegment -Name "test users" -GroupFilter "Company" -Comparison "equals" -AttributeValue "Contoso.com"
    This command will create the new Organization Segment named "Test users" based on the "Company" user's attribute, being Equals to "Contoso.com".
    #>

    [CmdletBinding()]
    Param (
        [Parameter(Mandatory = $true, HelpMessage = "Defines the Organization Segment Name.")]
        [String]$Name,

        [Parameter(Mandatory = $true, HelpMessage = "Defines the User Group filter attribute to be use.")]
        [String]$GroupFilter,

        [Parameter(Mandatory = $true, HelpMessage = "Defines the condition comparison.")]
        [String]$Comparison,

        [Parameter(Mandatory = $true, HelpMessage = "Defines the attribute value.")]
        [String]$AttributeValue
    )
    if ($Comparison -eq "Equals") {$comp = "eq"}
    else {$comp = "ne"}

    $statusBar.Text = "Running..."
    try {
        Write-PSFHostColor -String "[$((Get-Date).ToString("HH:mm:ss"))] Creating new Organization Segment '$Name'."
        New-OrganizationSegment -Name $Name -UserGroupFilter "$GroupFilter -$comp '$AttributeValue'" -errorAction Stop
        Write-PSFHostColor -String "[$((Get-Date).ToString("HH:mm:ss"))] Successfully created Organization Segment '$Name'."
        $statusBar.Text = "Ready. Created Organization Segment '$Name'."
    }
    catch {
        Write-PSFHostColor -String "[$((Get-Date).ToString("HH:mm:ss"))] Something failed to create the new Organization Segment '$Name'. $_"
        $statusBar.Text = "Ready. Someting failed to create the new Organization Segment '$Name'. Please see the Powershell window to verify error message."
    }
}

function Start-IBpolicyApp
{
    <#
    .SYNOPSIS
    Function to start Information Barrier Policies application to users
     
    .DESCRIPTION
    Function to start Information Barrier Policies application to users.
    Allow 30 minutes for the system to start applying the policies. The system applies policies user by user. The system processes about 5,000 user accounts per hour.
     
    .EXAMPLE
    PS C:\> Start-IBpolicyApp
    Executes Start-InformationBarrierPoliciesApplication
 
    #>

    [CmdletBinding()]
    Param (
        # Parameters
    )
    $statusBar.Text = "Running..."
    try {
        Write-PSFHostColor -String "[$((Get-Date).ToString("HH:mm:ss"))] Starting to apply Information Barrier Policies application."
        Start-InformationBarrierPoliciesApplication -ErrorAction Stop
        Write-PSFHostColor -String "[$((Get-Date).ToString("HH:mm:ss"))] Started successfully. Allow 30 minutes for the system to start applying the policies. The system applies policies user by user. The system processes about 5,000 user accounts per hour."
        $statusBar.Text = "Ready. Started successfully."
    }
    catch {
        Write-PSFHostColor -String "[$((Get-Date).ToString("HH:mm:ss"))] Something failed to start applying Information Barrier Policies application. $_"
    }
}


Function Start-IBTool {
    <#
    .SYNOPSIS
    Function to start the 'Information Barriers' tool.
     
    .DESCRIPTION
    Function to start the 'Information Barriers' tool.
     
    .PARAMETER Confirm
    If this switch is enabled, you will be prompted for confirmation before executing any operations that change state.
 
    .PARAMETER WhatIf
    If this switch is enabled, no actions are performed but informational messages will be displayed that explain what would happen if the command were to run.
 
    .EXAMPLE
    PS C:\> Start-IBTool
    This command will launch the 'Information Barriers' GUI tool.
    #>

    [CmdletBinding(SupportsShouldProcess = $True, ConfirmImpact = 'Low')]
    param (
        # Parameters
    )

    # Check current connection status, and connect if needed
    $ServicesToConnect = Assert-ServiceConnection
    # Connect to services if ArrayList is not empty
    if ( $ServicesToConnect.Count ) { Connect-OnlineServices -Credential $Credentials -Services $ServicesToConnect }

    function GenerateForm {
        #region Import the Assemblies
        Add-Type -AssemblyName System.Windows.Forms
        Add-Type -AssemblyName System.Drawing
        Add-Type -AssemblyName Microsoft.VisualBasic
        [System.Windows.Forms.Application]::EnableVisualStyles()
        #endregion Import the Assemblies

        #region Creating required Forms Objects
        $MainForm = New-Object System.Windows.Forms.Form
        $statusBar = New-Object System.Windows.Forms.StatusBar
        $labelTenantInfo = New-Object System.Windows.Forms.Label
        $labelAuditLogStatus = New-Object System.Windows.Forms.Label
        $labelAuditLogStatusValue = New-Object System.Windows.Forms.Label
        $labelABPStatus = New-Object System.Windows.Forms.Label
        $labelABPStatusValue = New-Object System.Windows.Forms.Label
        $labelIBServicePrincipal = New-Object System.Windows.Forms.Label
        $labelIBServicePrincipalValue = New-Object System.Windows.Forms.Label
        $HorizontalLine1 = New-Object System.Windows.Forms.Label
        $LabelGettingInfo = New-Object System.Windows.Forms.Label
        $buttonGetSegments = New-Object System.Windows.Forms.Button
        $buttonGetIBPolicies = New-Object System.Windows.Forms.Button
        $buttonGetIBPoliciesAppStatus = New-Object System.Windows.Forms.Button
        $HorizontalLine2 = New-Object System.Windows.Forms.Label
        $LabelGetOrgSegmentMemberTitle = New-Object System.Windows.Forms.Label
        $textBoxOrgSegment = New-Object System.Windows.Forms.TextBox
        $buttonGetSegmentMembers = New-Object System.Windows.Forms.Button
        $HorizontalLine3 = New-Object System.Windows.Forms.Label
        $LabelGetIBRecipientStatusTitle = New-Object System.Windows.Forms.Label
        $textBoxUser1 = New-Object System.Windows.Forms.TextBox
        $labelCompareWith = New-Object System.Windows.Forms.Label
        $textBoxUser2 = New-Object System.Windows.Forms.TextBox
        $buttonCompareIdentities = New-Object System.Windows.Forms.Button
        $HorizontalLine4 = New-Object System.Windows.Forms.Label
        $labelNewSegmentTitle = New-Object System.Windows.Forms.Label
        $labelNewSegmentHelp = New-Object System.Windows.Forms.Label
        $labelNewSegmentName = New-Object System.Windows.Forms.Label
        $textNewSegmentName = New-Object System.Windows.Forms.TextBox
        $labelUserGroupFilter = New-Object System.Windows.Forms.Label
        $comboBoxAttributelist = New-Object System.Windows.Forms.ComboBox
        $comboBoxComparison = New-Object System.Windows.Forms.ComboBox
        $textAttributeValue = New-Object System.Windows.Forms.TextBox
        $buttonCreateSegment = New-Object System.Windows.Forms.Button
        $HorizontalLine5 = New-Object System.Windows.Forms.Label
        $labelNewIBPolicyTitle = New-Object System.Windows.Forms.Label
        $labelNewIBPolicyName = New-Object System.Windows.Forms.Label
        $textNewIBPolicyName = New-Object System.Windows.Forms.TextBox
        $labelAssignSegment = New-Object System.Windows.Forms.Label
        $textAssignedSegment = New-Object System.Windows.Forms.TextBox
        $comboBoxSegmentAorB = New-Object System.Windows.Forms.ComboBox
        $textAorBSegment = New-Object System.Windows.Forms.TextBox
        $buttonCreateIBpolicy = New-Object System.Windows.Forms.Button
        $buttonStartIBPolicyApplication = New-Object System.Windows.Forms.Button
        $dataGrid = New-Object System.Windows.Forms.DataGridView
        $InitialFormWindowState = New-Object System.Windows.Forms.FormWindowState
        #endregion Creating required Forms Objects

        #region Internal scriptblocks
        $OnLoadMainWindow_StateCorrection={#Correct the initial state of the form to prevent the .Net maximized form issue
            $MainForm.WindowState = $InitialFormWindowState
        }

        $labelNewSegmentHelp_Click={
            [Microsoft.VisualBasic.Interaction]::MsgBox("This option is to create a simple Organization Segment. If you need to create a more complex segment with more attributes and combinations, please do them with powershell.
 
More info at: https://docs.microsoft.com/en-us/microsoft-365/compliance/information-barriers-policies?view=o365-worldwide#part-1-segment-users
 
Press CTRL + C to copy this message to clipboard."
,[Microsoft.VisualBasic.MsgBoxStyle]::Okonly,"Information Message")
        }

        #endregion Internal scriptblocks

        #region Generated Form Code
        #
        # Main Form
        #
        $statusBar.Name = "statusBar"
        $statusBar.Text = "Ready..."
        $MainForm.Controls.Add($statusBar)
        $MainForm.ClientSize = New-Object System.Drawing.Size(1100,720)
        $MainForm.DataBindings.DefaultDataSourceUpdateMode = [System.Windows.Forms.DataSourceUpdateMode]::OnValidation
        $MainForm.Name = "Main Form"
        $MainForm.Text = "Managing Information Barriers for Microsoft Teams"
        $MainForm.StartPosition = "CenterScreen"
        $MainForm.KeyPreview = $True
        $MainForm.Add_KeyDown({
            if ( $_.KeyCode -eq "Escape" ){ $MainForm.Close() }
        })
        #
        # Label Tenant Info
        #
        $labelTenantInfo.Location = New-Object System.Drawing.Point(10,10)
        $labelTenantInfo.Size = New-Object System.Drawing.Size(125,20)
        $labelTenantInfo.Name = "labelTenantInfo"
        $labelTenantInfo.Text = "Tenant Info"
        $labelTenantInfo.Font = New-Object System.Drawing.Font("Arial",8,[System.Drawing.FontStyle]::Bold)
        $MainForm.Controls.Add($labelTenantInfo)
        #
        # Label Audit Logging Status
        #
        $labelAuditLogStatus.Location = New-Object System.Drawing.Point(10,($labelTenantInfo.Location.Y + 25))
        $labelAuditLogStatus.Size = New-Object System.Drawing.Size(125,20)
        $labelAuditLogStatus.Name = "labelAuditLogStatus"
        $labelAuditLogStatus.Text = "Audit Logging Enabled:"
        $MainForm.Controls.Add($labelAuditLogStatus)
        #
        # Label Audit Logging Status Value
        #
        $labelAuditLogStatusValue.Location = New-Object System.Drawing.Point(155,($labelTenantInfo.Location.Y + 25))
        $labelAuditLogStatusValue.Size = New-Object System.Drawing.Size(35,20)
        $labelAuditLogStatusValue.Name = "labelAuditLogStatusValue"
        Get-AuditLogStatus
        $MainForm.Controls.Add($labelAuditLogStatusValue)
        #
        # Label Exchange Address Book Policy Status
        #
        $labelABPStatus.Location = New-Object System.Drawing.Point(10,($labelAuditLogStatus.Location.Y + 25))
        $labelABPStatus.Size = New-Object System.Drawing.Size(145,20)
        $labelABPStatus.Name = "labelABPStatus"
        $labelABPStatus.Text = "No Exchange ABP applied:"
        $MainForm.Controls.Add($labelABPStatus)
        #
        # Label Exchange Address Book Policy Status Value
        #
        $labelABPStatusValue.Location = New-Object System.Drawing.Point(155,($labelAuditLogStatus.Location.Y + 25))
        $labelABPStatusValue.Size = New-Object System.Drawing.Size(35,20)
        $labelABPStatusValue.Name = "labelABPStatusValue"
        Get-ExchangeABPStatus
        $MainForm.Controls.Add($labelABPStatusValue)
        #
        # Label Information Barrier Service Principal
        #
        $labelIBServicePrincipal.Location = New-Object System.Drawing.Point(10,($labelABPStatus.Location.Y + 25))
        $labelIBServicePrincipal.Size = New-Object System.Drawing.Size(220,20)
        $labelIBServicePrincipal.Name = "labelIBServicePrincipal"
        $labelIBServicePrincipal.Text = "Information Barrier Service Principal found:"
        $MainForm.Controls.Add($labelIBServicePrincipal)
        #
        # Label Information Barrier Service Principal Status
        #
        $labelIBServicePrincipalValue.Location = New-Object System.Drawing.Point(230,($labelABPStatus.Location.Y + 25))
        $labelIBServicePrincipalValue.Size = New-Object System.Drawing.Size(35,20)
        $labelIBServicePrincipalValue.Name = "labelIBServicePrincipalStatus"
        Get-IBServicePrincipal
        $MainForm.Controls.Add($labelIBServicePrincipalValue)
        #
        # Horizontal Line 1
        #
        $HorizontalLine1.Location = New-Object System.Drawing.Point(5,($labelIBServicePrincipal.Location.Y + 25))
        $HorizontalLine1.Size = New-Object System.Drawing.Size(1090,2)
        $HorizontalLine1.Name = "HorizontalLine1"
        $HorizontalLine1.Text = $null
        $HorizontalLine1.BorderStyle = [System.Windows.Forms.BorderStyle]::Fixed3D
        $MainForm.Controls.Add($HorizontalLine1)
        #
        # Label Getting Info
        #
        $LabelGettingInfo.Location = New-Object System.Drawing.Point(10,($HorizontalLine1.Location.Y + 10))
        $LabelGettingInfo.Size = New-Object System.Drawing.Size(250,20)
        $LabelGettingInfo.Name = "LabelGettingInfo"
        $LabelGettingInfo.Text = "Get general Information Barrier Info"
        $LabelGettingInfo.Font = New-Object System.Drawing.Font("Arial",8,[System.Drawing.FontStyle]::Bold)
        $MainForm.Controls.Add($LabelGettingInfo)
        #
        # Button Get Organization Segments
        #
        $buttonGetSegments.DataBindings.DefaultDataSourceUpdateMode = 0
        $buttonGetSegments.ForeColor = [System.Drawing.Color]::FromArgb(255,0,0,0)
        $buttonGetSegments.Location = New-Object System.Drawing.Point(10,($LabelGettingInfo.Location.Y + 25))
        $buttonGetSegments.Size = New-Object System.Drawing.Size(200,25)
        $buttonGetSegments.TabIndex = 17
        $buttonGetSegments.Name = "GetSegments"
        $buttonGetSegments.Text = "Get Organization Segments"
        $buttonGetSegments.UseVisualStyleBackColor = $True
        $buttonGetSegments.add_Click({
            $Segments = Get-OrgSegments -ShowOutputLine
            Add-ArrayToDataGrid -ArrayData $Segments -DataGrid $dataGrid -Form $MainForm
        })
        $MainForm.Controls.Add($buttonGetSegments)
        #
        # Button Get Information Barriers Policies
        #
        $buttonGetIBPolicies.DataBindings.DefaultDataSourceUpdateMode = 0
        $buttonGetIBPolicies.ForeColor = [System.Drawing.Color]::FromArgb(255,0,0,0)
        $buttonGetIBPolicies.Location = New-Object System.Drawing.Point(220,($LabelGettingInfo.Location.Y + 25))
        $buttonGetIBPolicies.Size = New-Object System.Drawing.Size(250,25)
        $buttonGetIBPolicies.TabIndex = 17
        $buttonGetIBPolicies.Name = "GetIBPolicies"
        $buttonGetIBPolicies.Text = "Get Information Barriers Policies"
        $buttonGetIBPolicies.UseVisualStyleBackColor = $True
        $buttonGetIBPolicies.add_Click({
            $IBPolicies = Get-IBPolicies -ShowOutputLine
            Add-ArrayToDataGrid -ArrayData $IBPolicies -DataGrid $dataGrid -Form $MainForm
        })
        $MainForm.Controls.Add($buttonGetIBPolicies)
        #
        # Button Get Information Barriers Policies Application Status
        #
        $buttonGetIBPoliciesAppStatus.DataBindings.DefaultDataSourceUpdateMode = 0
        $buttonGetIBPoliciesAppStatus.ForeColor = [System.Drawing.Color]::FromArgb(255,0,0,0)
        $buttonGetIBPoliciesAppStatus.Location = New-Object System.Drawing.Point(480,($LabelGettingInfo.Location.Y + 25))
        $buttonGetIBPoliciesAppStatus.Size = New-Object System.Drawing.Size(300,25)
        $buttonGetIBPoliciesAppStatus.TabIndex = 17
        $buttonGetIBPoliciesAppStatus.Name = "GetIBPoliciesAppStatus"
        $buttonGetIBPoliciesAppStatus.Text = "Get IB Policies Application Status"
        $buttonGetIBPoliciesAppStatus.UseVisualStyleBackColor = $True
        $buttonGetIBPoliciesAppStatus.add_Click({
            $IBPoliciesAppStatus = Get-IBPoliciesAppStatus -ShowOutputLine
            Add-ArrayToDataGrid -ArrayData $IBPoliciesAppStatus -DataGrid $dataGrid -Form $MainForm
        })
        $MainForm.Controls.Add($buttonGetIBPoliciesAppStatus)
        #
        # Horizontal Line 2
        #
        $HorizontalLine2.Location = New-Object System.Drawing.Point(5,($buttonGetSegments.Location.Y + 40))
        $HorizontalLine2.Size = New-Object System.Drawing.Size(1090,2)
        $HorizontalLine2.Name = "HorizontalLine2"
        $HorizontalLine2.Text = $null
        $HorizontalLine2.BorderStyle = [System.Windows.Forms.BorderStyle]::Fixed3D
        $MainForm.Controls.Add($HorizontalLine2)
        #
        # Label Get Org Segment Member title
        #
        $LabelGetOrgSegmentMemberTitle.Location = New-Object System.Drawing.Point(10,($HorizontalLine2.Location.Y + 10))
        $LabelGetOrgSegmentMemberTitle.Size = New-Object System.Drawing.Size(250,20)
        $LabelGetOrgSegmentMemberTitle.Name = "LabelGetOrgSegmentMemberTitle"
        $LabelGetOrgSegmentMemberTitle.Text = "Get Organization Segment members"
        $LabelGetOrgSegmentMemberTitle.Font = New-Object System.Drawing.Font("Arial",8,[System.Drawing.FontStyle]::Bold)
        $MainForm.Controls.Add($LabelGetOrgSegmentMemberTitle)
        #
        # Text Box Organization Segment Name
        #
        $textBoxOrgSegment.Location = New-Object System.Drawing.Point(10,($LabelGetOrgSegmentMemberTitle.Location.Y + 28))
        $textBoxOrgSegment.Size = New-Object System.Drawing.Size(200,20)
        $textBoxOrgSegment.Name = "textBoxOrgSegment"
        $textBoxOrgSegment.Text = "Sample Organization Segment"
        $MainForm.Controls.Add($textBoxOrgSegment)
        #
        # Button Get Organization Segment Members
        #
        $buttonGetSegmentMembers.DataBindings.DefaultDataSourceUpdateMode = 0
        $buttonGetSegmentMembers.ForeColor = [System.Drawing.Color]::FromArgb(255,0,0,0)
        $buttonGetSegmentMembers.Location = New-Object System.Drawing.Point(220,($LabelGetOrgSegmentMemberTitle.Location.Y + 25))
        $buttonGetSegmentMembers.Size = New-Object System.Drawing.Size(250,25)
        $buttonGetSegmentMembers.TabIndex = 17
        $buttonGetSegmentMembers.Name = "GetSegmentMembers"
        $buttonGetSegmentMembers.Text = "Get Segment Members"
        $buttonGetSegmentMembers.UseVisualStyleBackColor = $True
        $buttonGetSegmentMembers.add_Click({
            $members = Get-SegmentMembers -SegmentName $textBoxOrgSegment.Text.ToString() -ShowOutputLine
            Add-ArrayToDataGrid -ArrayData $members -DataGrid $dataGrid -Form $MainForm
        })
        $MainForm.Controls.Add($buttonGetSegmentMembers)
        #
        # Horizontal Line 3
        #
        $HorizontalLine3.Location = New-Object System.Drawing.Point(5,($textBoxOrgSegment.Location.Y + 40))
        $HorizontalLine3.Size = New-Object System.Drawing.Size(1090,2)
        $HorizontalLine3.Name = "HorizontalLine3"
        $HorizontalLine3.Text = $null
        $HorizontalLine3.BorderStyle = [System.Windows.Forms.BorderStyle]::Fixed3D
        $MainForm.Controls.Add($HorizontalLine3)
        #
        # Label Get Org Recipient Status
        #
        $LabelGetIBRecipientStatusTitle.Location = New-Object System.Drawing.Point(10,($HorizontalLine3.Location.Y + 10))
        $LabelGetIBRecipientStatusTitle.Size = New-Object System.Drawing.Size(250,20)
        $LabelGetIBRecipientStatusTitle.Name = "LabelGetIBRecipientStatusTitle"
        $LabelGetIBRecipientStatusTitle.Text = "Get Information Barrier Recipient Status"
        $LabelGetIBRecipientStatusTitle.Font = New-Object System.Drawing.Font("Arial",8,[System.Drawing.FontStyle]::Bold)
        $MainForm.Controls.Add($LabelGetIBRecipientStatusTitle)
        #
        # Text Box for user1
        #
        $textBoxUser1.Location = New-Object System.Drawing.Point(10,($LabelGetIBRecipientStatusTitle.Location.Y + 25))
        $textBoxUser1.Size = New-Object System.Drawing.Size(150,20)
        $textBoxUser1.Name = "textBoxUser1"
        $textBoxUser1.Text = "Sample User1@domain.com"
        $MainForm.Controls.Add($textBoxUser1)
        #
        # Label Compare with
        #
        $labelCompareWith.Location = New-Object System.Drawing.Point(170,($LabelGetIBRecipientStatusTitle.Location.Y + 28))
        $labelCompareWith.Size = New-Object System.Drawing.Size(80,20)
        $labelCompareWith.Name = "labelABPStatusValue"
        $labelCompareWith.Text = "compare with:"
        $MainForm.Controls.Add($labelCompareWith)
        #
        # Text Box for user2
        #
        $textBoxUser2.Location = New-Object System.Drawing.Point(250,($LabelGetIBRecipientStatusTitle.Location.Y + 25))
        $textBoxUser2.Size = New-Object System.Drawing.Size(150,20)
        $textBoxUser2.Name = "textBoxUser2"
        $textBoxUser2.Text = "Sample User2@domain.com"
        $MainForm.Controls.Add($textBoxUser2)
        #
        # Button to Compare both identities
        #
        $buttonCompareIdentities.DataBindings.DefaultDataSourceUpdateMode = 0
        $buttonCompareIdentities.ForeColor = [System.Drawing.Color]::FromArgb(255,0,0,0)
        $buttonCompareIdentities.Location = New-Object System.Drawing.Point(410,($LabelGetIBRecipientStatusTitle.Location.Y + 23))
        $buttonCompareIdentities.Size = New-Object System.Drawing.Size(150,25)
        $buttonCompareIdentities.TabIndex = 17
        $buttonCompareIdentities.Name = "CompareIdentities"
        $buttonCompareIdentities.Text = "Compare Users"
        $buttonCompareIdentities.UseVisualStyleBackColor = $True
        $buttonCompareIdentities.add_Click({
            $RecipientStatus = @(Get-IBPoliciesRecipientStatus -User1 $textBoxUser1.Text.toString() -User2 $textBoxUser2.Text.toString() -ShowOutputLine)
            Add-ArrayToDataGrid -ArrayData $RecipientStatus -DataGrid $dataGrid -Form $MainForm
        })
        $MainForm.Controls.Add($buttonCompareIdentities)
        #
        # Horizontal Line 4
        #
        $HorizontalLine4.Location = New-Object System.Drawing.Point(5,($textBoxUser1.Location.Y + 40))
        $HorizontalLine4.Size = New-Object System.Drawing.Size(1090,2)
        $HorizontalLine4.Name = "HorizontalLine4"
        $HorizontalLine4.Text = $null
        $HorizontalLine4.BorderStyle = [System.Windows.Forms.BorderStyle]::Fixed3D
        $MainForm.Controls.Add($HorizontalLine4)
        #
        # Label New Segment Name Title
        #
        $labelNewSegmentTitle.Location = New-Object System.Drawing.Point(10,($HorizontalLine4.Location.Y + 10))
        $labelNewSegmentTitle.Size = New-Object System.Drawing.Size(200,20)
        $labelNewSegmentTitle.Name = "labelNewSegmentTitle"
        $labelNewSegmentTitle.Text = "Create a new Organization Segment"
        $labelNewSegmentTitle.Font = New-Object System.Drawing.Font("Arial",8,[System.Drawing.FontStyle]::Bold)
        $MainForm.Controls.Add($labelNewSegmentTitle)
        #
        # Label New Segment Title Help
        #
        $labelNewSegmentHelp.Location = New-Object System.Drawing.Point(220,($HorizontalLine4.Location.Y + 10))
        $labelNewSegmentHelp.Size = New-Object System.Drawing.Size(30,20)
        $labelNewSegmentHelp.Name = "labelNewSegmentHelp"
        $labelNewSegmentHelp.ForeColor = "Blue"
        $labelNewSegmentHelp.Text = "help"
        $labelNewSegmentHelp.add_Click($labelNewSegmentHelp_Click)
        $MainForm.Controls.Add($labelNewSegmentHelp)
        #
        # Label New Segment Name
        #
        $labelNewSegmentName.Location = New-Object System.Drawing.Point(10,($labelNewSegmentTitle.Location.Y + 28))
        $labelNewSegmentName.Size = New-Object System.Drawing.Size(40,20)
        $labelNewSegmentName.Name = "labelNewSegmentName"
        $labelNewSegmentName.Text = "Name:"
        $MainForm.Controls.Add($labelNewSegmentName)
        #
        # Text New Segment Name
        #
        $textNewSegmentName.Location = New-Object System.Drawing.Point(55,($labelNewSegmentTitle.Location.Y + 25))
        $textNewSegmentName.Size = New-Object System.Drawing.Size(150,20)
        $textNewSegmentName.Name = "textNewSegmentName"
        $textNewSegmentName.Text = "Sample Organization Segment"
        $MainForm.Controls.Add($textNewSegmentName)
        #
        # Label user Group Filter
        #
        $labelUserGroupFilter.Location = New-Object System.Drawing.Point(210,($labelNewSegmentTitle.Location.Y + 28))
        $labelUserGroupFilter.Size = New-Object System.Drawing.Size(90,20)
        $labelUserGroupFilter.Name = "labelUserGroupFilter"
        $labelUserGroupFilter.Text = "-UserGroupFilter"
        $MainForm.Controls.Add($labelUserGroupFilter)
        #
        # Combobox Attribute lists
        #
        $comboBoxAttributelist.DataBindings.DefaultDataSourceUpdateMode = 0
        $comboBoxAttributelist.FormattingEnabled = $True
        $comboBoxAttributelist.Location = New-Object System.Drawing.Point(305,($labelNewSegmentTitle.Location.Y + 25))
        $comboBoxAttributelist.Size = New-Object System.Drawing.Size(200,23)
        $comboBoxAttributelist.Items.Add("Co") | Out-Null
        $comboBoxAttributelist.Items.Add("Company") | Out-Null
        $comboBoxAttributelist.Items.Add("Department") | Out-Null
        1..15 | ForEach-Object { $comboBoxAttributelist.Items.Add("CustomAttribute$_") | Out-Null }
        1..5  | ForEach-Object { $comboBoxAttributelist.Items.Add("ExtensionCustomAttribute$_") | Out-Null }
        $comboBoxAttributelist.Items.Add("Alias") | Out-Null
        $comboBoxAttributelist.Items.Add("Office") | Out-Null
        $comboBoxAttributelist.Items.Add("PostalCode") | Out-Null
        $comboBoxAttributelist.Items.Add("EmailAddresses") | Out-Null
        $comboBoxAttributelist.Items.Add("StreetAddress") | Out-Null
        $comboBoxAttributelist.Items.Add("ExternalEmailAddress") | Out-Null
        $comboBoxAttributelist.Items.Add("UsageLocation") | Out-Null
        $comboBoxAttributelist.Items.Add("UserPrincipalName") | Out-Null
        $comboBoxAttributelist.Items.Add("WindowsEmailAddress") | Out-Null
        $comboBoxAttributelist.Items.Add("Description") | Out-Null
        $comboBoxAttributelist.Items.Add("MemberOfGroup") | Out-Null
        $comboBoxAttributelist.Name = "comboBoxAttributelist"
        $MainForm.Controls.Add($comboBoxAttributelist)
        #
        # Combobox Comparison
        #
        $comboBoxComparison.DataBindings.DefaultDataSourceUpdateMode = 0
        $comboBoxComparison.FormattingEnabled = $True
        $comboBoxComparison.Location = New-Object System.Drawing.Point(510,($labelNewSegmentTitle.Location.Y + 25))
        $comboBoxComparison.Size = New-Object System.Drawing.Size(70,23)
        $comboBoxComparison.Items.Add("Equals") | Out-Null
        $comboBoxComparison.Items.Add("Not Equals") | Out-Null
        $comboBoxComparison.Name = "comboBoxComparison"
        $MainForm.Controls.Add($comboBoxComparison)
        #
        # Text Attribute Value
        #
        $textAttributeValue.Location = New-Object System.Drawing.Point(588,($labelNewSegmentTitle.Location.Y + 25))
        $textAttributeValue.Size = New-Object System.Drawing.Size(200,20)
        $textAttributeValue.Name = "textAttributeValue"
        $textAttributeValue.Text = "Sample Attribute Value"
        $MainForm.Controls.Add($textAttributeValue)
        #
        # Button to create Organization Segment
        #
        $buttonCreateSegment.DataBindings.DefaultDataSourceUpdateMode = 0
        $buttonCreateSegment.ForeColor = [System.Drawing.Color]::FromArgb(255,0,0,0)
        $buttonCreateSegment.Location = New-Object System.Drawing.Point(795,($labelNewSegmentTitle.Location.Y + 23))
        $buttonCreateSegment.Size = New-Object System.Drawing.Size(100,25)
        $buttonCreateSegment.TabIndex = 17
        $buttonCreateSegment.Name = "buttonCreateSegment"
        $buttonCreateSegment.Text = "Create"
        $buttonCreateSegment.UseVisualStyleBackColor = $True
        $buttonCreateSegment.add_Click({
            New-OrgSegment -Name $textNewSegmentName.Text.toString() -GroupFilter $comboBoxAttributelist.SelectedItem.ToString() -Comparison $comboBoxComparison.SelectedItem.ToString() -AttributeValue $textAttributeValue.Text.ToString()
        })
        $MainForm.Controls.Add($buttonCreateSegment)

        #
        # Horizontal Line 5
        #
        $HorizontalLine5.Location = New-Object System.Drawing.Point(5,($labelNewSegmentName.Location.Y + 40))
        $HorizontalLine5.Size = New-Object System.Drawing.Size(1090,2)
        $HorizontalLine5.Name = "HorizontalLine5"
        $HorizontalLine5.Text = $null
        $HorizontalLine5.BorderStyle = [System.Windows.Forms.BorderStyle]::Fixed3D
        $MainForm.Controls.Add($HorizontalLine5)
        #
        # Label New IB Policy Title
        #
        $labelNewIBPolicyTitle.Location = New-Object System.Drawing.Point(10,($HorizontalLine5.Location.Y + 10))
        $labelNewIBPolicyTitle.Size = New-Object System.Drawing.Size(250,20)
        $labelNewIBPolicyTitle.Name = "labelNewIBPolicyTitle"
        $labelNewIBPolicyTitle.Text = "Create a new Information Barrier Policy"
        $labelNewIBPolicyTitle.Font = New-Object System.Drawing.Font("Arial",8,[System.Drawing.FontStyle]::Bold)
        $MainForm.Controls.Add($labelNewIBPolicyTitle)
        #
        # Label New IB Policy Name
        #
        $labelNewIBPolicyName.Location = New-Object System.Drawing.Point(10,($labelNewIBPolicyTitle.Location.Y + 28))
        $labelNewIBPolicyName.Size = New-Object System.Drawing.Size(40,20)
        $labelNewIBPolicyName.Name = "labelNewIBPolicyName"
        $labelNewIBPolicyName.Text = "Name:"
        $MainForm.Controls.Add($labelNewIBPolicyName)
        #
        # Text New IB Policy Name
        #
        $textNewIBPolicyName.Location = New-Object System.Drawing.Point(55,($labelNewIBPolicyTitle.Location.Y + 25))
        $textNewIBPolicyName.Size = New-Object System.Drawing.Size(150,20)
        $textNewIBPolicyName.Name = "textNewIBPolicyName"
        $textNewIBPolicyName.Text = "Sample IB Policy"
        $MainForm.Controls.Add($textNewIBPolicyName)
        #
        # Label Assign Segment
        #
        $labelAssignSegment.Location = New-Object System.Drawing.Point(210,($labelNewIBPolicyTitle.Location.Y + 28))
        $labelAssignSegment.Size = New-Object System.Drawing.Size(90,20)
        $labelAssignSegment.Name = "labelAssignSegment"
        $labelAssignSegment.Text = "Assign Segment:"
        $MainForm.Controls.Add($labelAssignSegment)
        #
        # Text Assign Segment name
        #
        $textAssignedSegment.Location = New-Object System.Drawing.Point(305,($labelNewIBPolicyTitle.Location.Y + 25))
        $textAssignedSegment.Size = New-Object System.Drawing.Size(150,20)
        $textAssignedSegment.Name = "textAssignedSegment"
        $textAssignedSegment.Text = "Sample Segment 1"
        $MainForm.Controls.Add($textAssignedSegment)
        #
        # Combobox Segment allowedBlock
        #
        $comboBoxSegmentAorB.DataBindings.DefaultDataSourceUpdateMode = 0
        $comboBoxSegmentAorB.FormattingEnabled = $True
        $comboBoxSegmentAorB.Location = New-Object System.Drawing.Point(460,($labelNewIBPolicyTitle.Location.Y + 25))
        $comboBoxSegmentAorB.Size = New-Object System.Drawing.Size(110,23)
        $comboBoxSegmentAorB.Items.Add("SegmentsAllowed") | Out-Null
        $comboBoxSegmentAorB.Items.Add("SegmentsBlocked") | Out-Null
        $comboBoxSegmentAorB.Name = "comboBoxSegmentAorB"
        $MainForm.Controls.Add($comboBoxSegmentAorB)
        # Arrow label
        $ArrowLabel = New-Object System.Windows.Forms.Label
        $ArrowLabel.Location = New-Object System.Drawing.Point(571,($labelNewIBPolicyTitle.Location.Y + 28))
        $ArrowLabel.Size = New-Object System.Drawing.Size(16,20)
        $ArrowLabel.Name = "ArrowLabel"
        $ArrowLabel.Text = "->"
        $MainForm.Controls.Add($ArrowLabel)
        #
        # Textbox Segments names
        #
        $textAorBSegment.Location = New-Object System.Drawing.Point(588,($labelNewIBPolicyTitle.Location.Y + 25))
        $textAorBSegment.Size = New-Object System.Drawing.Size(200,20)
        $textAorBSegment.Name = "textAorBSegment"
        $textAorBSegment.Text = "'Sample Segment 1', 'Sample Segment 2'"
        $MainForm.Controls.Add($textAorBSegment)
        #
        # Button to create IB policy
        #
        $buttonCreateIBpolicy.DataBindings.DefaultDataSourceUpdateMode = 0
        $buttonCreateIBpolicy.ForeColor = [System.Drawing.Color]::FromArgb(255,0,0,0)
        $buttonCreateIBpolicy.Location = New-Object System.Drawing.Point(795,($labelNewIBPolicyTitle.Location.Y + 23))
        $buttonCreateIBpolicy.Size = New-Object System.Drawing.Size(100,25)
        $buttonCreateIBpolicy.TabIndex = 17
        $buttonCreateIBpolicy.Name = "buttonCreateIBpolicy"
        $buttonCreateIBpolicy.Text = "Create"
        $buttonCreateIBpolicy.UseVisualStyleBackColor = $True
        $buttonCreateIBpolicy.add_Click({
            New-IBPolicy -PolicyName $textNewIBPolicyName.text.ToString() -AssignedSegment $textAssignedSegment.Text.ToString() -AssignedAction $comboBoxSegmentAorB.SelectedItem.ToString() -AorBSegments $textAorBSegment.Text.ToString()
        })
        $MainForm.Controls.Add($buttonCreateIBpolicy)
        #
        # Button to Start IB policy Application
        #
        $buttonStartIBPolicyApplication.DataBindings.DefaultDataSourceUpdateMode = 0
        $buttonStartIBPolicyApplication.ForeColor = [System.Drawing.Color]::FromArgb(255,0,0,0)
        $buttonStartIBPolicyApplication.Location = New-Object System.Drawing.Point(900,($labelNewIBPolicyTitle.Location.Y + 23))
        $buttonStartIBPolicyApplication.Size = New-Object System.Drawing.Size(150,25)
        $buttonStartIBPolicyApplication.TabIndex = 17
        $buttonStartIBPolicyApplication.Name = "buttonStartIBPolicyApplication"
        $buttonStartIBPolicyApplication.Text = "Start IB Policy Application"
        $buttonStartIBPolicyApplication.UseVisualStyleBackColor = $True
        $buttonStartIBPolicyApplication.add_Click({
            Start-IBPolicyApp
        })
        $MainForm.Controls.Add($buttonStartIBPolicyApplication)
        #
        # Data Grid outputs
        #
        $dataGrid.Anchor = 15
        $dataGrid.DataBindings.DefaultDataSourceUpdateMode = 0
        $dataGrid.DataMember = ""
        $dataGrid.Location = New-Object System.Drawing.Point(5,500)
        $dataGrid.Size = New-Object System.Drawing.Size(1090,240)
        $dataGrid.Name = "dataGrid"
        $dataGrid.ReadOnly = $True
        $dataGrid.RowHeadersVisible = $False
        $dataGrid.Visible = $True
        $dataGrid.AllowUserToOrderColumns = $True
        $dataGrid.AllowUserToResizeColumns = $True
        $MainForm.Controls.Add($dataGrid)

        #endregion Generated Form Code

        # Show Form
        #Save the initial state of the form
        $InitialFormWindowState = $MainForm.WindowState
        #Init the OnLoad event to correct the initial state of the form
        $MainForm.add_Load($OnLoadMainWindow_StateCorrection)
        $MainForm.Add_Shown({$MainForm.Activate()})
        $MainForm.ShowDialog() | Out-Null
    } #End Function

    #Call the Function
    GenerateForm
}

<#
This is an example configuration file
 
By default, it is enough to have a single one of them,
however if you have enough configuration settings to justify having multiple copies of it,
feel totally free to split them into multiple files.
#>


<#
# Example Configuration
Set-PSFConfig -Module 'IBTool' -Name 'Example.Setting' -Value 10 -Initialize -Validation 'integer' -Handler { } -Description "Example configuration setting. Your module can then use the setting using 'Get-PSFConfigValue'"
#>


Set-PSFConfig -Module 'IBTool' -Name 'Import.DoDotSource' -Value $false -Initialize -Validation 'bool' -Description "Whether the module files should be dotsourced on import. By default, the files of this module are read as string value and invoked, which is faster but worse on debugging."
Set-PSFConfig -Module 'IBTool' -Name 'Import.IndividualFiles' -Value $false -Initialize -Validation 'bool' -Description "Whether the module files should be imported individually. During the module build, all module code is compiled into few files, which are imported instead by default. Loading the compiled versions is faster, using the individual files is easier for debugging and testing out adjustments."

<#
Stored scriptblocks are available in [PsfValidateScript()] attributes.
This makes it easier to centrally provide the same scriptblock multiple times,
without having to maintain it in separate locations.
 
It also prevents lengthy validation scriptblocks from making your parameter block
hard to read.
 
Set-PSFScriptblock -Name 'IBTool.ScriptBlockName' -Scriptblock {
     
}
#>


<#
# Example:
Register-PSFTeppScriptblock -Name "IBTool.alcohol" -ScriptBlock { 'Beer','Mead','Whiskey','Wine','Vodka','Rum (3y)', 'Rum (5y)', 'Rum (7y)' }
#>


<#
# Example:
Register-PSFTeppArgumentCompleter -Command Get-Alcohol -Parameter Type -Name IBTool.alcohol
#>


New-PSFLicense -Product 'IBTool' -Manufacturer 'agallego' -ProductVersion $script:ModuleVersion -ProductType Module -Name MIT -Version "1.0.0.0" -Date (Get-Date "2020-10-27") -Text @"
Copyright (c) 2020 agallego
 
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
 
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
 
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
"@

#endregion Load compiled code