Dustins-PowerShell-Module.psm1

    <#
        ===========================================================================
         Created with: SAPIEN Technologies, Inc., PowerShell Studio 2020 v5.7.174
         Created on: 4/25/2020 12:02
         Created by: Dustin Rhodes
         Organization: Perficient
         Filename: Dustins-PowerShell-Module.psm1
        -------------------------------------------------------------------------
         Module Name: Dustins-PowerShell-Module
        ===========================================================================
    #>

    
    function Write-HelloWorld {
        Write-Host "Hello World"
    }
    function Invoke-Menu {
        [CmdletBinding()]
        param(
            [Parameter(Position = 0,Mandatory = $true,HelpMessage = "Enter your menu text")]
            [ValidateNotNullOrEmpty()]
            [string]$Menu,
            [Parameter(Mandatory = $false)]
            [string]$MenuColor,
            [Parameter(Mandatory = $false,HelpMessage = "Optional menu title text")]
            [ValidateNotNullOrEmpty()]
            [string]$Title,
            [Parameter(Mandatory = $false)]
            [string]$TitleColor,
            [Parameter(Mandatory = $false,HelpMessage = "Optional response text to display below menu")]
            [string]$Response,
            [Parameter(Mandatory = $false)]
            [string]$ResponseColor,
            [Parameter(Mandatory = $false)]
            [Alias("cls")]
            [switch]$ClearScreen
        )
        if ($ClearScreen) {
            Clear-Host
        }
        if ($Title) {
            if ($TitleColor) {
                Write-Host "$Title `n" -ForegroundColor $TitleColor
            }
            else {
                Write-Host "$Title `n"
            }
        }
        if ($MenuColor) {
            Write-Host "$Menu `n" -ForegroundColor $MenuColor
        }
        else {
            Write-Host "$Menu `n"
        }
        if ($Response) {
            if ($ResponseColor) {
                Write-Host "$Response `n" -ForegroundColor $ResponseColor
            }
            else {
                Write-Host "$Response `n" -ForegroundColor Magenta
            }
        }
        Read-Host -Prompt "SELECTION"
    }
    function Write-Log {
        [CmdletBinding()]
        param(
            [Parameter(Mandatory = $true,ValueFromPipelineByPropertyName = $true)]
            [ValidateNotNullOrEmpty()]
            [Alias("LogContent")]
            [string]$Message,
            [Parameter(Mandatory = $false)]
            [Alias("LogPath")]
            [string]$Path = $defaultLogFile,
            [Parameter(Mandatory = $false)]
            [ValidateSet("Error","Warning","Info")]
            [string]$Level = "Info",
            [Parameter(Mandatory = $false)]
            [switch]$NoClobber
        )
    
        begin {
            # Set VerbosePreference to Continue so that verbose messages are displayed.
            $VerbosePreference = "Continue"
        }
        process {
            # If the file already exists and NoClobber was specified, do not write to the log.
            if ((Test-Path $Path) -and $NoClobber) {
                Write-Error "Log file $Path already exists, and you specified NoClobber. Either delete the file or specify a different name."
                return
            }
    
            # If attempting to write to a log file in a folder/path that doesn't exist create the file including the path.
            elseif (!(Test-Path $Path)) {
                Write-Verbose "Creating $Path."
                $NewLogFile = New-Item $Path -Force -ItemType File
            }
    
            else {
                # Nothing to see here yet.
            }
    
            # Format Date for our Log File
            $FormattedDate = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
    
            # Write message to error, warning, or verbose pipeline and specify $LevelText
            switch ($Level) {
                "Error" {
                    Write-Error $Message
                    $LevelText = "ERROR:"
                }
                "Warning" {
                    Write-Warning $Message
                    $LevelText = "WARNING:"
                }
                "Info" {
                    Write-Verbose $Message
                    $LevelText = "INFO:"
                }
            }
    
            # Write log entry to $Path
            "$FormattedDate $LevelText $Message" | Out-File -FilePath $Path -Append
        }
        end {
        }
    }
    function Get-FileName () {
        [CmdletBinding()]
        param(
            [Parameter(Mandatory = $false)]
            [string]$Title,
            [Parameter(Mandatory = $false)]
            [string]$InitialDirectory,
            [Parameter(Mandatory = $false)]
            [string]$Filter
    
        )
        $OpenFileDialog = New-Object System.Windows.Forms.OpenFileDialog
        if ($Filter -or $InitialDirectory) {
            $OpenFileDialog.Title = $Title
            $OpenFileDialog.InitialDirectory = $InitialDirectory
            $OpenFileDialog.Filter = "All Files (*.$Filter*)| *.$Filter*"
        }
        else {
            $OpenFileDialog.Title = $Title
            $OpenFileDialog.Filter = "All files (*.*)| *.*"
        }
        $Result = $OpenFileDialog.ShowDialog()
        if ($Result -eq "OK") {
            $OpenFileDialog.FileName
        }
        else {
            $null
        }
    }
    function New-PSStoredCredential {
    <#
        .SYNOPSIS
        New-PSStoredCredential - Create a new stored credential
     
        .DESCRIPTION
        This function will save a new stored credential to a .cred file.
     
        .EXAMPLE
        New-PSStoredCredential
     
        .NOTES
        Written by: Dustin Rhodes
    #>

        if (!(Test-Path $KeyPath)) {
            Write-Warning "The `$KeyPath variable has not been set. Consider adding `$KeyPath to your PowerShell profile to avoid this prompt."
            $Path = Read-Host -Prompt "Enter a path for stored credentials"
            $global:KeyPath = $Path
            if (!(Test-Path $KeyPath)) {
                try {
                    New-Item -ItemType Directory -Path $KeyPath -ErrorAction Stop | Out-Null
                }
                catch {
                    throw $_.Exception.Message
                }
            }
        }
        $Credential = Get-Credential -Message "Enter Credentials"
        $Credential.Password | ConvertFrom-SecureString | Out-File "$($KeyPath)\$($Credential.Username).cred" -Force
    }
    function Get-PSStoredCredential {
    <#
        .SYNOPSIS
        Get-PSStoredCredential - Retrieve or list stored credentials
     
        .DESCRIPTION
        This function can be used to list available credentials on
        the computer, or to retrieve a credential for use in a script
        or command.
     
        .PARAMETER UserName
        Get the stored credential for the username
     
        .PARAMETER List
        List the stored credentials on the computer
     
        .EXAMPLE
        Get-PSStoredCredential -List
     
        .EXAMPLE
        $credential = Get-PSStoredCredential -UserName admin@tenant.onmicrosoft.com
     
        .EXAMPLE
        Get-PSStoredCredential -List
     
        .NOTES
        Written by: Dustin Rhodes
    #>

        param(
            [Parameter(Mandatory = $false,ParameterSetName = "Get")]
            [string]$UserName,
    
            [Parameter(Mandatory = $false,ParameterSetName = "List")]
            [switch]$List
        )
        if (!(Test-Path $KeyPath)) {
            Write-Warning "The `$KeyPath variable has not been set. Consider adding `$KeyPath to your PowerShell profile to avoid this prompt."
            $Path = Read-Host -Prompt "Enter a path for stored credentials"
            $global:KeyPath = $Path
        }
        if ($List) {
            try {
                $CredentialList = @(Get-ChildItem -Path $KeyPath -Filter *.cred -ErrorAction Stop)
                foreach ($Cred in $CredentialList) {
                    Write-Host "Username: $($Cred.BaseName)"
                }
            }
            catch {
                Write-Warning $_.Exception.Message
            }
        }
        if ($UserName) {
            if (Test-Path "$($KeyPath)\$($Username).cred") {
                $PwdSecureString = Get-Content "$($KeyPath)\$($Username).cred" | ConvertTo-SecureString
                $Credential = New-Object System.Management.Automation.PSCredential -ArgumentList $Username,$PwdSecureString
            }
            else {
                throw "Unable to locate a credential for $UserName"
            }
            return $Credential
        }
    }
    function Set-PSWindowTitle {
    <#
        .Synopsis Updates PowerShell window title
    #>

        [CmdletBinding()]
        param(
            [Parameter(Mandatory = $false)]
            [Alias("String")]
            [string]$Text,
            [Parameter(Mandatory = $false)]
            [switch]$Update,
            [Parameter(Mandatory = $false)]
            [switch]$Replace
        )
        begin {
        }
        process {
            if ($null -ne $Text) {
                if ($Update) {
                    $Host.UI.RawUI.WindowTitle += $Text
                }
                elseif ($Replace) {
                    $Host.UI.RawUI.WindowTitle = $Text
                }
                else {
                    if ($Host.UI.RawUI.WindowTitle -match "(Administrator)") {
                        $Host.UI.RawUI.WindowTitle = "$($env:USERNAME) (Administrator)"
                    }
                    else {
                        $Host.UI.RawUI.WindowTitle = $env:USERNAME
                    }
                }
            }
        }
        end {
        }
    }
    function Set-O365PSWindowTitle {
    <#
                .Synopsis Updates PowerShell window title to reflect connected O365 services
            #>

        $o365ServiceConnectionStatus = Get-O365ServiceConnectionStatus -Service AzureAD,ExchangeOnline,MSOnline,SecurityAndCompliance,SharePointOnline,SkypeForBusinessOnline
        foreach ($service in $o365ServiceConnectionStatus) {
            if ($service.Connected) {
                if ($Host.UI.RawUI.WindowTitle -match "Connected To:") {
                    Set-PSWindowTitle -Text " / $($service.Service)" -Update
                }
                else {
                    Set-PSWindowTitle -Text " - Connected To: $($service.Service)" -Update
                }
            }
        }
    }
    function Remove-InvalidPSSession {
    <#
        .Synopsis Remove broken and closed sessions
    #>

        begin {
            $psBroken = Get-PSSession | Where-Object {
                $_.State -like "*Broken*"
            }
            $psClosed = Get-PSSession | Where-Object {
                $_.State -like "*Closed*"
            }
        }
    
        process {
            if ($psBroken.Count -gt 0) {
                for ($i = 0; $i -lt $psBroken.Count; $i++) {
                    Remove-PSSession -Session $psBroken[$i]
                }
            }
    
            if ($psClosed.Count -gt 0) {
                for ($i = 0; $i -lt $psClosed.Count; $i++) {
                    Remove-PSSession -Session $psClosed[$i]
                }
            }
        }
    
        end {
        }
    }
    function Remove-DuplicatePSSession {
    <#
        .Synopsis Remove duplicate PowerShell sessions
    #>

        [CmdletBinding()]
        param(
            [Parameter(Mandatory = $true)]
            [ValidateSet("ExchangeOnline","SecurityAndCompliance","SkypeForBusinessOnline")]
            [string[]]$Service
        )
    
        begin {
    
        }
    
        process {
            foreach ($item in $PSBoundParameters.Service) {
                Write-Verbose "Checking connection status of $item"
                switch ($item) {
                    "ExchangeOnline" {
                        $psEXO = Get-PSSession | Where-Object {
                            ($_.ComputerName -eq "outlook.office365.com") -and ($_.State -eq "Opened") -and ($_.Availability -eq "Available")
                        } | Sort-Object Id -Descending
                        if ($psEXO.Count -gt 1) {
                            for ($i = 1; $i -lt $psEXO.Count; $i++) {
                                Remove-PSSession -Session $psEXO[$i]
                            }
                        }
                    }
                    "SecurityAndCompliance" {
                        $psSCC = Get-PSSession | Where-Object {
                            ($_.ComputerName -like "*.ps.compliance.protection.outlook.com") -and ($_.State -eq "Opened") -and ($_.Availability -eq "Available")
                        } | Sort-Object Id -Descending
                        if ($psSCC.Count -gt 1) {
                            for ($i = 1; $i -lt $psSCC.Count; $i++) {
                                Remove-PSSession -Session $psSCC[$i]
                            }
                        }
                    }
                    "SkypeForBusinessOnline" {
                        $psSfBO = Get-PSSession | Where-Object {
                            ($_.ComputerName -like "*.online.lync.com") -and ($_.State -eq "Opened") -and ($_.Availability -eq "Available")
                        } | Sort-Object Id -Descending
                        if ($psSfBO.Count -gt 1) {
                            for ($i = 1; $i -lt $psSfBO.Count; $i++) {
                                Remove-PSSession -Session $psSfBO[$i]
                            }
                        }
                    }
                }
            }
        }
    
        end {
        }
    }
    function Get-O365ServiceConnectionStatus {
    <#
                .Synopsis Gets connection status to Office 365 services
            #>

        [CmdletBinding()]
        param(
            [Parameter(Mandatory = $true)]
            [ValidateSet("AzureAD","ExchangeOnline","MSOnline","SecurityAndCompliance","SharePointOnline","SkypeForBusinessOnline")]
            [string[]]$Service
        )
    
        begin {
            $serviceStatus = @()
        }
    
        process {
            foreach ($item in $PSBoundParameters.Service) {
                Write-Verbose "Checking connection status of $item"
                switch ($item) {
                    "AzureAD" {
                        try {
                            $azureADSessionInfo = Get-AzureADCurrentSessionInfo -ErrorAction "SilentlyContinue"
                        }
                        catch {
                        }
                        if ($null -ne $azureADSessionInfo) {
                            Write-Verbose "Connected to $item"
                            $serviceStatus += [pscustomobject]@{
                                Service = $item
                                Connected = $true
                                Details = $azureADSessionInfo
                            }
                        }
                        else {
                            Write-Verbose "Not connected to $item"
                            $serviceStatus += [pscustomobject]@{
                                Service = $item
                                Connected = $false
                                Details = $azureADSessionInfo
                            }
                        }
                    }
                    "ExchangeOnline" {
                        try {
                            $exoPSSession = Get-PSSession | Where-Object {
                                ($_.ComputerName -eq "outlook.office365.com") -and ($_.State -eq "Opened") -and ($_.Availability -eq "Available")
                            } | Select-Object -Last 1
                        }
                        catch {
                        }
                        if ($null -ne $exoPSSession) {
                            Write-Verbose "Connected to $item"
                            $serviceStatus += [pscustomobject]@{
                                Service = $item
                                Connected = $true
                                Details = $exoPSSession
                            }
                        }
                        else {
                            Write-Verbose "Not connected to $item"
                            $serviceStatus += [pscustomobject]@{
                                Service = $item
                                Connected = $false
                                Details = $exoPSSession
                            }
                        }
                    }
                    "MSOnline" {
                        try {
                            $MSOnlineDomain = Get-MsolDomain -ErrorAction "SilentlyContinue" | Where-Object {
                                $_.IsInitial -eq $true
                            }
                        }
                        catch {
                        }
                        if ($null -ne $MSOnlineDomain) {
                            Write-Verbose "Connected to $item"
                            $serviceStatus += [pscustomobject]@{
                                Service = $item
                                Connected = $true
                                Details = $MSOnlineDomain
                            }
                        }
                        else {
                            Write-Verbose "Not connected to $item"
                            $serviceStatus += [pscustomobject]@{
                                Service = $item
                                Connected = $false
                                Details = $MSOnlineDomain
                            }
                        }
                    }
                    "SecurityAndCompliance" {
                        try {
                            $SCCPSSession = Get-PSSession | Where-Object {
                                ($_.ComputerName -like "*.ps.compliance.protection.outlook.com") -and ($_.State -eq "Opened") -and ($_.Availability -eq "Available")
                            } | Select-Object -Last 1
                        }
                        catch {
                        }
                        if ($null -ne $SCCPSSession) {
                            Write-Verbose "Connected to Security and Compliance Center"
                            $serviceStatus += [pscustomobject]@{
                                Service = $item
                                Connected = $true
                                Details = $SCCPSSession
                            }
                        }
                        else {
                            Write-Verbose "Not connected to Security and Compliance Center"
                            $serviceStatus += [pscustomobject]@{
                                Service = $item
                                Connected = $false
                                Details = $SCCPSSession
                            }
                        }
                    }
                    "SharePointOnline" {
                        try {
                            $SPOTenant = Get-SPOTenant
                        }
                        catch {
                        }
                        if ($null -ne $SPOTenant) {
                            Write-Verbose "Connected to SharePoint Online"
                            $serviceStatus += [pscustomobject]@{
                                Service = $item
                                Connected = $true
                                Details = $SPOTenant
                            }
                        }
                        else {
                            Write-Verbose "Not connected to SharePoint Online"
                            $serviceStatus += [pscustomobject]@{
                                Service = $item
                                Connected = $false
                                Details = $SPOTenant
                            }
                        }
                    }
                    "SkypeForBusinessOnline" {
                        try {
                            $SfBOPSSession = Get-PSSession | Where-Object {
                                ($_.ComputerName -like "*.online.lync.com") -and ($_.State -eq "Opened") -and ($_.Availability -eq "Available")
                            } | Select-Object -Last 1
                        }
                        catch {
                        }
                        if ($null -ne $SfBOPSSession) {
                            Write-Verbose "Connected to SkypeForBusinessOnline"
                            $serviceStatus += [pscustomobject]@{
                                Service = $item
                                Connected = $true
                                Details = $SfBOPSSession
                            }
                        }
                        else {
                            Write-Verbose "Not connected to SkypeForBusinessOnline"
                            $serviceStatus += [pscustomobject]@{
                                Service = $item
                                Connected = $false
                                Details = $SfBOPSSession
                            }
                        }
                    }
                }
            }
            $serviceStatus
        }
    
        end {
        }
    }
    function Connect-Office365 {
        [CmdletBinding()]
        param
        (
            [Parameter(Mandatory = $true)]
            [ValidateSet('AzureAD','ExchangeOnline','MSOnline','MSTeams','SecurityAndCompliance','SharePointOnline','SkypeForBusinessOnline')]
            [string[]]$Service,
            [Parameter(Mandatory = $false)]
            [Alias('SPOrgName')]
            [string]$SharePointOrganizationName,
            [Parameter(ParameterSetName = 'Credential',
                Mandatory = $false)]
            [pscredential]$Credential,
            [Parameter(ParameterSetName = 'StoredCreds',
                Mandatory = $false)]
            [switch]$UseStoredCredential,
            [Parameter(ParameterSetName = 'StoredCreds',
                Mandatory = $false)]
            [string]$UserName,
            [Parameter(ParameterSetName = 'MFA',
                Mandatory = $false)]
            [switch]$MFA,
            [Parameter(Mandatory = $false)]
            [string]$Prefix
        )
    
        begin {
            #remove invalid PowerShell sessions
            Remove-InvalidPSSession
            #check for existing globally defined credentials
            if ($UseStoredCredential) {
                Write-Verbose "Using stored credentials"
                $Credential = Get-PSStoredCredential -UserName $UserName
                Write-Verbose "Using stored credentials for $($Credential.UserName)"
                Set-PSWindowTitle
                Set-PSWindowTitle -Update -Text " - $($Credential.UserName)"
            }
            elseif ((!$MFA) -and ($null -ne $global:Credential)) {
                $credentialSelection = Read-Host "Credentials found for $($global:Credential.UserName). Use existing credentials? (Y*/N)"
                switch ($credentialSelection) {
                    "Y" {
                        Write-Verbose "Using existing credentials ($($global:Credential.UserName))"
                        $Credential = $global:Credential
                        Set-PSWindowTitle
                        Set-PSWindowTitle -Update -Text " - $($Credential.UserName)"
                    }
                    "N" {
                        Write-Verbose "Getting credentials"
                        $Credential = Get-Credential -Message "Enter Office 365 Credentials"
                        Write-Verbose "Using credentials for $($Credential.UserName)"
                        Set-PSWindowTitle
                        Set-PSWindowTitle -Update -Text " - $($Credential.UserName)"
                    }
                    default {
                        Write-Verbose "Using existing credentials ($($global:Credential.UserName))"
                        $Credential = $global:Credential
                        Set-PSWindowTitle
                        Set-PSWindowTitle -Update -Text " - $($Credential.UserName)"
                    }
                }
            }
            elseif (!$MFA) {
                Write-Verbose "Getting credentials"
                $Credential = Get-Credential -Message "Enter Office 365 Credentials"
                Write-Verbose "Using credentials for $($Credential.UserName)"
                Set-PSWindowTitle
                Set-PSWindowTitle -Update -Text " - $($Credential.UserName)"
            }
            #Get-Module options
            $GetModuleSplat = @{
                ListAvailable = $true
                Verbose = $false
            }
        }
    
        process {
            foreach ($item in $PsBoundParameters.Service) {
                Write-Verbose "Attempting connection to $item"
                switch ($item) {
                    "AzureAD" {
                        #check for AzureAD modules
                        if ($null -eq ((Get-Module @GetModuleSplat -Name "AzureAD") -or (Get-Module @GetModuleSplat -Name "AzureADPreview"))) {
                            Write-Error "AzureAD Module could not be located"
                            continue
                        }
                        else {
                            #get AzureAD connection status
                            $azureADStatus = Get-O365ServiceConnectionStatus -Service AzureAD
                            if ($azureADStatus.Connected) {
                                Write-Verbose "Already connected to $item. Skipping..."
                            }
                            else {
                                #connect to AzureAD using MFA
                                Write-Verbose "Connecting to $item"
                                if ($MFA -eq $true) {
                                    Write-Verbose "Connecting to $item using MFA"
                                    $azureADConnection = Connect-AzureAD
                                    if ($null -ne $azureADConnection) {
                                        Write-Verbose "Connected to $item using MFA"
                                    }
                                    else {
                                        Write-Error "Unable to connect to $item using MFA"
                                    }
                                }
                                else {
                                    #connect to AzureAD using credentials
                                    Write-Verbose "Connecting to $item using credentials"
                                    $azureADConnection = Connect-AzureAD -Credential $Credential
                                    if ($null -ne $azureADConnection) {
                                        Write-Verbose "Connected to $item as $($Credential.Username)"
                                    }
                                    else {
                                        Write-Error "Unable to connect to $item using credentials"
                                    }
                                }
                            }
                        }
                        continue
                    }
                    "ExchangeOnline" {
                        #remove duplicate ExchangeOnline sessions
                        Remove-DuplicatePSSession -Service ExchangeOnline
                        #get ExchangeOnline connection status
                        $EXOStatus = Get-O365ServiceConnectionStatus -Service ExchangeOnline
                        if ($EXOStatus.Connected) {
                            Write-Verbose "Already connected to $item. Skipping..."
                        }
                        else {
                            #connect to ExchangeOnline using MFA
                            Write-Verbose "Connecting to $item"
                            if ($MFA -eq $true) {
                                $getChildItemSplat = @{
                                    Path = "$Env:LOCALAPPDATA\Apps\2.0\*\CreateExoPSSession.ps1"
                                    Recurse = $true
                                    ErrorAction = 'SilentlyContinue'
                                    Verbose = $false
                                }
                                $EXOMFAModule = (Get-ChildItem @getChildItemSplat | Select-Object -ExpandProperty Target -First 1)
                                #check for Exchange Online MFA module
                                if ($null -eq $EXOMFAModule) {
                                    Write-Error "The Exchange Online MFA Module could not be located"
                                    continue
                                }
                                else {
                                    Write-Verbose "Importing Exchange Online MFA Module"
                                    .$EXOMFAModule
                                    Write-Verbose "Connecting to $item using MFA"
                                    $WarningPreference = "SilentlyContinue"
                                    Connect-EXOPSSession
                                    $WarningPreference = "Continue"
                                    $global:EXOPSSession = (Get-PSSession | Where-Object {
                                            ($_.ConfigurationName -eq "Microsoft.Exchange") -and ($_.ComputerName -eq "outlook.office365.com") -and ($_.State -eq "Opened") -and ($_.Availability -eq "Available")
                                        })[0]
                                    if ($null -ne $EXOPSSession) {
                                        if ($Prefix -ne "") {
                                            Import-Module -Global -AsCustomObject (Import-PSSession $EXOPSSession -AllowClobber -DisableNameChecking) -DisableNameChecking -Prefix $Prefix
                                        }
                                        else {
                                            Import-Module -Global -AsCustomObject (Import-PSSession $EXOPSSession -AllowClobber -DisableNameChecking) -DisableNameChecking
                                        }
                                        Write-Verbose "Connected to $item using MFA"
                                    }
                                    else {
                                        Write-Error "Unable to connect to $item using MFA"
                                    }
                                }
                            }
                            else {
                                #connect to ExchangeOnline using credentials
                                $EXOPSSessionSplat = @{
                                    ConfigurationName = "Microsoft.Exchange"
                                    ConnectionUri = "https://outlook.office365.com/powershell-liveid/"
                                    Authentication = "Basic"
                                    Credential = $Credential
                                    AllowRedirection = $true
                                }
                                Write-Verbose "Connecting to $item using credentials"
                                $global:EXOPSSession = New-PSSession @EXOPSSessionSplat
                                $null = Import-PSSession $EXOPSSession -AllowClobber -DisableNameChecking
                                if ($null -ne $EXOPSSession) {
                                    if ($Prefix -ne "") {
                                        Import-Module -Global -AsCustomObject (Import-PSSession $EXOPSSession -AllowClobber -DisableNameChecking) -DisableNameChecking -Prefix $Prefix
                                    }
                                    else {
                                        Import-Module -Global -AsCustomObject (Import-PSSession $EXOPSSession -AllowClobber -DisableNameChecking) -DisableNameChecking
                                    }
                                    Write-Verbose "Connected to $item as $($Credential.UserName)"
                                }
                                else {
                                    Write-Error "Unable to connect to $item using credentials"
                                }
                            }
                        }
                        continue
                    }
                    "MSOnline" {
                        #check for MSOnline module
                        if ($null -eq (Get-Module @GetModuleSplat -Name "MSOnline")) {
                            Write-Error "MSOnline Module could not be located"
                            continue
                        }
                        else {
                            #get MSOnline connection status
                            $MSOnlineStatus = Get-O365ServiceConnectionStatus -Service MSOnline
                            if ($MSOnlineStatus.Connected) {
                                Write-Verbose "Already connected to $item. Skipping..."
                            }
                            else {
                                Write-Verbose "Connecting to $item"
                                if ($MFA -eq $true) {
                                    #connect to MSOnline using MFA
                                    Write-Verbose "Connecting to $item using MFA"
                                    Connect-MsolService
                                    $MSOnlineStatus = Get-O365ServiceConnectionStatus -Service MSOnline
                                    if ($MSOnlineStatus.Connected) {
                                        Write-Verbose "Connected to $item using MFA"
                                    }
                                    else {
                                        Write-Error "Unable to connect to $item using MFA"
                                    }
                                }
                                else {
                                    #connect to MSOnline using credentials
                                    Write-Verbose "Connecting to $item using credentials"
                                    Connect-MsolService -Credential $Credential
                                    $MSOnlineStatus = Get-O365ServiceConnectionStatus -Service MSOnline
                                    if ($MSOnlineStatus.Connected) {
                                        Write-Verbose "Connected to $item as $($Credential.UserName)"
                                    }
                                    else {
                                        Write-Error "Unable to connect to $item using credentials"
                                    }
                                }
                            }
                        }
                        continue
                    }
                    "MSTeams" {
                        if ($null -eq (Get-Module @GetModuleSplat -Name "MicrosoftTeams")) {
                            Write-Error "MicrosoftTeams Module could not be located"
                        }
                        else {
                            Write-Verbose "Connecting to $item using MFA"
                            if ($MFA -eq $true) {
                                $MSTeamsStatus = Connect-MicrosoftTeams
                                if ($null -ne ($MSTeamsStatus)) {
                                    Write-Verbose "Connected to $item using MFA"
                                }
                                else {
                                    Write-Error "Unable to connect to $item using MFA"
                                }
                            }
                            else {
                                $MSTeamsStatus = Connect-MicrosoftTeams -Credential $Credential
                                if ($null -ne ($MSTeamsStatus)) {
                                    Write-Verbose "Connected to $item using credentials"
                                }
                                else {
                                    Write-Error "Unable to connect to $item using MFA"
                                }
                            }
                        }
                        continue
                    }
                    "SecurityAndCompliance" {
                        #remove duplicate SecurityAndCompliance sessions
                        Remove-DuplicatePSSession -Service SecurityAndCompliance
                        #get SecurityAndCompliance connection status
                        $SCCStatus = Get-O365ServiceConnectionStatus -Service SecurityAndCompliance
                        if ($SCCStatus.Connected) {
                            Write-Verbose "Already connected to $item. Skipping..."
                        }
                        else {
                            #connect to SecurityAndCompliance using MFA
                            Write-Verbose "Connecting to $item"
                            if ($MFA -eq $true) {
                                $getChildItemSplat = @{
                                    Path = "$Env:LOCALAPPDATA\Apps\2.0\*\CreateExoPSSession.ps1"
                                    Recurse = $true
                                    ErrorAction = 'SilentlyContinue'
                                    Verbose = $false
                                }
                                $EXOMFAModule = (Get-ChildItem @getChildItemSplat | Select-Object -ExpandProperty Target -First 1)
                                #check for Exchange Online MFA module
                                if ($null -eq $EXOMFAModule) {
                                    Write-Error "The Exchange Online MFA Module could not be located"
                                    continue
                                }
                                else {
                                    Write-Verbose "Importing Exchange Online MFA Module"
                                    .$EXOMFAModule
                                    Write-Verbose "Connecting to $item using MFA"
                                    $WarningPreference = "SilentlyContinue"
                                    Connect-IPPSSession
                                    $WarningPreference = "Continue"
                                    $global:SCCPSSession = (Get-PSSession | Where-Object {
                                            ($_.ConfigurationName -eq "Microsoft.Exchange") -and ($_.ComputerName -like "*.ps.compliance.protection.outlook.com") -and ($_.State -eq "Opened") -and ($_.Availability -eq "Available")
                                        })[0]
                                    if ($null -ne $SCCPSSession) {
                                        if ($Prefix -ne "") {
                                            Import-Module -Global -AsCustomObject (Import-PSSession $EXOPSSession -AllowClobber -DisableNameChecking) -DisableNameChecking -Prefix $Prefix
                                        }
                                        else {
                                            Import-Module -Global -AsCustomObject (Import-PSSession $EXOPSSession -AllowClobber -DisableNameChecking) -DisableNameChecking
                                        }
                                        Write-Verbose "Connected to $item using MFA"
                                    }
                                    else {
                                        Write-Error "Unable to connect to $item using MFA"
                                    }
                                }
                            }
                            else {
                                #connect to SecurityAndCompliance using credentials
                                $SCCPSSessionSplat = @{
                                    ConfigurationName = "Microsoft.Exchange"
                                    ConnectionUri = "https://ps.compliance.protection.outlook.com/powershell-liveid/"
                                    Authentication = "Basic"
                                    Credential = $Credential
                                    AllowRedirection = $true
                                    WarningAction = "SilentlyContinue"
                                }
                                Write-Verbose "Connecting to $item using credentials"
                                $global:SCCPSSession = New-PSSession @SCCPSSessionSplat
                                $null = Import-PSSession $SCCPSSession -AllowClobber -DisableNameChecking
                                if ($null -ne $SCCPSSession) {
                                    if ($Prefix -ne "") {
                                        Import-Module -Global -AsCustomObject (Import-PSSession $EXOPSSession -AllowClobber -DisableNameChecking) -DisableNameChecking -Prefix $Prefix
                                    }
                                    else {
                                        Import-Module -Global -AsCustomObject (Import-PSSession $EXOPSSession -AllowClobber -DisableNameChecking) -DisableNameChecking
                                    }
                                    Write-Verbose "Connected to $item as $($Credential.UserName)"
                                }
                                else {
                                    Write-Error "Unable to connect to $item using credentials"
                                }
                            }
                        }
                        continue
                    }
                    "SharePointOnline" {
                        #check for SharePointOnline module
                        if ($null -eq (Get-Module @GetModuleSplat -Name Microsoft.Online.SharePoint.PowerShell)) {
                            Write-Error "Microsoft.Online.SharePoint.PowerShell Module could not be located"
                            continue
                        }
                        if (-not ($PSBoundParameters.ContainsKey("SharePointOrganizationName"))) {
                            Write-Error "Please provide a valid SharePoint organization name with the -SharePointOrganizationName parameter."
                            continue
                        }
                        $SharePointUrl = "https://{0}-admin.sharepoint.com" -f $SharePointOrganizationName
                        Write-Verbose "Connecting to $item at $SharePointUrl"
                        #get SharePointOnline connection status
                        $SPOStatus = Get-O365ServiceConnectionStatus -Service SharePointOnline
                        if ($SPOStatus.Connected) {
                            Write-Verbose "Already connected to $item. Skipping..."
                        }
                        else {
                            #connect to SharePointOnline using MFA
                            Write-Verbose "Connecting to $item"
                            if ($MFA -eq $true) {
                                Write-Verbose "Connecting to $item using MFA"
                                Connect-SPOService -Url $SharePointURL
                                $SPOStatus = Get-O365ServiceConnectionStatus -Service SharePointOnline
                                if ($SPOStatus.Connected) {
                                    Write-Verbose "Connected to $item using MFA"
                                }
                                else {
                                    Write-Error "Unable to connect to $item using MFA"
                                }
                            }
                            else {
                                #connect to SharePointOnline using credentials
                                Write-Verbose "Connecting to $item using credentials"
                                Connect-SPOService -Url $SharePointURL -Credential $Credential
                                $SPOStatus = Get-O365ServiceConnectionStatus -Service SharePointOnline
                                if ($SPOStatus.Connected) {
                                    Write-Verbose "Connected to $item as $($Credential.UserName)"
                                }
                                else {
                                    Write-Error "Unable to connect to $item using credentials"
                                }
                            }
                        }
                        continue
                    }
                    "SkypeForBusinessOnline" {
                        #check for SkypeOnlineConnector module
                        if ($null -eq (Get-Module @GetModuleSplat -Name "SkypeOnlineConnector")) {
                            Write-Error "SkypeOnlineConnector Module could not be located"
                        }
                        else {
                            #remove duplicate SkypeForBusinessOnline sessions
                            Remove-DuplicatePSSession -Service SkypeForBusinessOnline
                            #get SkypeForBusinessOnline connection status
                            $SfBOStatus = Get-O365ServiceConnectionStatus -Service SkypeForBusinessOnline
                            if ($SfBOStatus.Connected) {
                                Write-Verbose "Already connected to $item. Skipping..."
                            }
                            else {
                                Write-Verbose "Connecting to $item"
                                if ($MFA -eq $true) {
                                    #connect to SkypeForBusinessOnline using MFA
                                    Write-Verbose "Connecting to $item using MFA"
                                    $global:SfBOPSSession = New-CsOnlineSession
                                    if ($null -ne $SfBOPSSession) {
                                        Import-Module -Global -AsCustomObject (Import-PSSession $SfBOPSSession -AllowClobber -DisableNameChecking) -DisableNameChecking
                                        Write-Verbose "Connected to $item using MFA"
                                    }
                                    else {
                                        Write-Error "Unable to connect to $item using MFA"
                                    }
                                }
                                else {
                                    #connect to SkypeForBusinessOnline using credentials
                                    Write-Verbose "Connecting to $item using credentials"
                                    $global:SfBOPSSession = New-CsOnlineSession -Credential $Credential
                                    if ($null -ne $SfBOPSSession) {
                                        Import-Module -Global -AsCustomObject (Import-PSSession $SfBOPSSession -AllowClobber -DisableNameChecking) -DisableNameChecking
                                        Write-Verbose "Connected to $item using MFA"
                                    }
                                    else {
                                        Write-Error "Unable to connect to $item using MFA"
                                    }
                                }
                            }
                        }
                        continue
                    }
                    Default {
                    }
                }
            }
        }
    
        end {
            Set-O365PSWindowTitle
        }
    }
    function Connect-ExchangeServer () {
        param(
            [Parameter(Mandatory = $true)]
            [ValidateNotNullOrEmpty][string]$ComputerName,
            [Parameter(Mandatory = $true)]
            [pscredential]$Credential
        )
        $exchangeUri = "http://$ComputerName/PowerShell"
        #check for existing sessions
        $activeSessions = Get-PSSession | Where-Object {
            ($_.ComputerName -eq $ComputerName) -and ($_.State -eq "Opened") -and ($_.Availability -eq "Available")
        }
        if (!$activeSessions) {
            $exopSession = New-PSSession -ConfigurationName Microsoft.Exchange -ConnectionUri $exchangeUri -Authentication Kerberos -Credential $Credential
            Import-PSSession $exopSession -AllowClobber -DisableNameChecking
            Write-Host "Connected to $ComputerName"
        }
        elseif ($activeSessions) {
            $activeSessions
            Write-Host "Already connected to $computerName" -ForegroundColor Yellow
        }
    }
    function Connect-EWS {
        param(
            [Parameter(Position = 0,Mandatory = $true)]
            [string]$MailboxName,
            [Parameter(Position = 1,Mandatory = $true)]
            [pscredential]$Credential
        )
        begin {
            #Load Managed API dll
            #CHECK FOR EWS MANAGED API, IF PRESENT IMPORT THE HIGHEST VERSION EWS DLL, ELSE EXIT
            $EWSDLL = (($(Get-ItemProperty -Path Registry::$(Get-ChildItem -Path 'Registry::HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Exchange\Web Services' | Sort-Object Name -Descending | Select-Object -First 1 -ExpandProperty Name)). 'Install Directory') + "Microsoft.Exchange.WebServices.dll")
            if (Test-Path $EWSDLL) {
                Import-Module $EWSDLL
            }
            else {
                Write-Host "$(Get-Date -format yyyyMMddHHmmss):
            This script requires the EWS Managed API 1.2 or later.
            Please download and install the current version of the EWS Managed API from
            http://go.microsoft.com/fwlink/?LinkId=255472"
 -ForegroundColor Red -BackgroundColor Black
                return
            }
            #Set Exchange Version (2013 works for EXO)
            $ExchangeVersion = [Microsoft.Exchange.WebServices.Data.ExchangeVersion]::Exchange2013_SP1
            ## Create Exchange Service Object
            $Service = New-Object Microsoft.Exchange.WebServices.Data.ExchangeService ($ExchangeVersion)
            #Set Credentials to use two options are availible Option1 to use explict credentials or Option 2 use the Default (logged On) credentials
            #Credentials Option 1 using UPN for the windows Account
            #$psCred = Get-Credential
            $EWSCredential = New-Object System.Net.NetworkCredential ($Credential.UserName.ToString(),$Credential.GetNetworkCredential().Password.ToString())
            $Service.Credentials = $EWSCredential
            #Credentials Option 2
            #service.UseDefaultCredentials = $true
            #$service.TraceEnabled = $true
            #Choose to ignore any SSL Warning issues caused by Self Signed Certificates
            #HANDLE SSL
            #Create a compilation environment
            $Provider = New-Object Microsoft.CSharp.CSharpCodeProvider
            $Compiler = $Provider.CreateCompiler()
            $Params = New-Object System.CodeDom.Compiler.CompilerParameters
            $Params.GenerateExecutable = $False
            $Params.GenerateInMemory = $True
            $Params.IncludeDebugInformation = $False
            $Params.ReferencedAssemblies.Add("System.DLL") | Out-Null
            $TASource = @"
    namespace Local.ToolkitExtensions.Net.CertificatePolicy{
    public class TrustAll : System.Net.ICertificatePolicy {
        public TrustAll() {
        }
        public bool CheckValidationResult(System.Net.ServicePoint sp,
        System.Security.Cryptography.X509Certificates.X509Certificate cert,
        System.Net.WebRequest req, int problem) {
        return true;
        }
    }
    }
"@

            $TAResults = $Provider.CompileAssemblyFromSource($Params,$TASource)
            $TAAssembly = $TAResults.CompiledAssembly
            #We now create an instance of the TrustAll and attach it to the ServicePointManager
            $TrustAll = $TAAssembly.CreateInstance("Local.ToolkitExtensions.Net.CertificatePolicy.TrustAll")
            [System.Net.ServicePointManager]::CertificatePolicy = $TrustAll
            #Set the URL of the CAS (Client Access Server) to use two options are availbe to use Autodiscover to find the CAS URL or Hardcode the CAS to use
            #CAS URL Option 1 Autodiscover
            #$service.AutodiscoverUrl($MailboxName, { $true })
            #Write-host ("Using CAS Server : " + $Service.url)
            #CAS URL Option 2 Hardcoded
            #$uri=[system.URI] "https://casservername/ews/exchange.asmx"
            $URI = [system.URI]"https://outlook.office365.com/EWS/Exchange.asmx"
            $Service.Url = $URI
    
            ## Optional section for Exchange Impersonation
    
            $Service.ImpersonatedUserId = New-Object Microsoft.Exchange.WebServices.Data.ImpersonatedUserId ([Microsoft.Exchange.WebServices.Data.ConnectingIdType]::SmtpAddress,$MailboxName)
            if (!$Service.Url) {
                throw "Error connecting to EWS"
            }
            else {
                return $Service
            }
        }
    }
    function Get-EWSFolderFromPath {
        param(
            [Parameter(Mandatory = $true)]
            [string]$FolderPath,
            [Parameter(Mandatory = $true)]
            [string]$MailboxName,
            [Parameter(Mandatory = $true)]
            [Microsoft.Exchange.WebServices.Data.ExchangeService]$EWSService,
            [Parameter(Mandatory = $false)]
            [Microsoft.Exchange.WebServices.Data.PropertySet]$PropertySet
        )
        process {
            ## Find and Bind to Folder based on Path
            #Define the path to search should be seperated with \
            #Bind to the MSGFolder Root
            $folderId = New-Object Microsoft.Exchange.WebServices.Data.FolderId ([Microsoft.Exchange.WebServices.Data.WellKnownFolderName]::MsgFolderRoot,$MailboxName)
            $tfTargetFolder = [Microsoft.Exchange.WebServices.Data.Folder]::Bind($EWSService,$folderId)
            #Split the Search path into an array
            $fldArray = $FolderPath.Split("\")
            #Loop through the Split Array and do a Search for each level of folder
            for ($lint = 1; $lint -lt $fldArray.Length; $lint++) {
                #Perform search based on the displayname of each folder level
                $fvFolderView = New-Object Microsoft.Exchange.WebServices.Data.FolderView (1)
                if (![string]::IsNullOrEmpty($PropertySet)) {
                    $fvFolderView.PropertySet = $PropertySet
                }
                $SfSearchFilter = New-Object Microsoft.Exchange.WebServices.Data.SearchFilter+IsEqualTo ([Microsoft.Exchange.WebServices.Data.FolderSchema]::DisplayName,$fldArray[$lint])
                $findFolderResults = $EWSService.FindFolders($tfTargetFolder.Id,$SfSearchFilter,$fvFolderView)
                if ($findFolderResults.TotalCount -gt 0) {
                    foreach ($folder in $findFolderResults.Folders) {
                        $tfTargetFolder = $folder
                    }
                }
                else {
                    Write-Host ("Error Folder Not Found check path and try again")
                    $tfTargetFolder = $null
                    break
                }
            }
            if ($tfTargetFolder -ne $null) {
                return [Microsoft.Exchange.WebServices.Data.Folder]$tfTargetFolder
            }
            else {
                throw ("Folder Not found")
            }
        }
    }
    function Create-NewEWSFolder {
        param(
            [Parameter(Mandatory = $true)]
            [Microsoft.Exchange.WebServices.Data.ExchangeService]$EWSService,
            [Parameter(Mandatory = $true)]
            [pscredential]$Credential,
            [Parameter(Mandatory = $true)]
            [string]$MailboxName,
            [Parameter(Mandatory = $true)]
            [string]$NewFolderName,
            [Parameter(Mandatory = $false)]
            [Microsoft.Exchange.WebServices.Data.Folder]$ParentFolder,
            [Parameter(Mandatory = $false)]
            [string]$FolderClass,
            [Parameter(Mandatory = $false,ParameterSetName = "Retention")]
            [string]$RetentionTag,
            [Parameter(Mandatory = $false,ParameterSetName = "Retention")]
            [int]$RetentionPeriodValue,
            [Parameter(Mandatory = $false,ParameterSetName = "Retention",HelpMessage = "This is usually 129 (Re-Scan)")]
            [int]$RetentionFlagsValue
        )
        begin {
            $Error.Clear()
            $service = $EWSService
            $newFolder = New-Object Microsoft.Exchange.WebServices.Data.Folder ($service)
            $newFolder.DisplayName = $NewFolderName
            #bind to retention tag properties
            $policyTag = New-Object Microsoft.Exchange.WebServices.Data.ExtendedPropertyDefinition (0x3019,[Microsoft.Exchange.WebServices.Data.MapiPropertyType]::Binary);
            $retentionFlags = New-Object Microsoft.Exchange.WebServices.Data.ExtendedPropertyDefinition (0x301D,[Microsoft.Exchange.WebServices.Data.MapiPropertyType]::Integer);
            $retentionPeriod = New-Object Microsoft.Exchange.WebServices.Data.ExtendedPropertyDefinition (0x301A,[Microsoft.Exchange.WebServices.Data.MapiPropertyType]::Integer);
            #determine which retention tag to apply to the current folder
            if ($RetentionTag) {
                $service.ImpersonatedUserId = New-Object Microsoft.Exchange.WebServices.Data.ImpersonatedUserId ([Microsoft.Exchange.WebServices.Data.ConnectingIdType]::SmtpAddress,$MailboxName);
                $tag = ($service.GetUserRetentionPolicyTags().RetentionPolicyTags | Where-Object {
                        $_.DisplayName -eq $RetentionTag
                    })
                $newFolder.SetExtendedProperty($retentionFlags,$RetentionFlagsValue)
                $newFolder.SetExtendedProperty($retentionPeriod,$RetentionPeriodValue)
                try {
                    $newFolder.PolicyTag = New-Object Microsoft.Exchange.WebServices.Data.PolicyTag ($true,$tag.RetentionId)
                    $Error.Clear()
                }
                catch {
                    Write-Host "`t Could not apply $RetentionTag retention tag to: " -ForegroundColor Red -NoNewline; Write-Host $NewFolderName -ForegroundColor Green `r
                    Write-Host "`t" -NoNewline; Write-Host $error[0] -ForegroundColor Red -BackgroundColor Black `r`n
                    $Error.Clear()
                }
            }
            #check folder class
            if (([string]::IsNullOrEmpty($FolderClass))) {
                $newFolder.FolderClass = "IPF.Note"
            }
            else {
                $newFolder.FolderClass = $FolderClass
            }
            #set $ParentFolder to $null and check $ParentFolder
            $ewsParentFolder = $null
            if (([string]::IsNullOrEmpty($ParentFolder))) {
                #bind to the MsgFolderRoot folder
                $folderId = New-Object Microsoft.Exchange.WebServices.Data.FolderId ([Microsoft.Exchange.WebServices.Data.WellKnownFolderName]::MsgFolderRoot,$MailboxName)
                $ewsParentFolder = [Microsoft.Exchange.WebServices.Data.Folder]::Bind($service,$folderId)
            }
            else {
                $ewsParentFolder = $ParentFolder
            }
            Write-Host "Processing folder: $NewFolderName" -ForegroundColor Cyan
            Write-Host "Folder doesn't exist: $NewFolderName. Creating it now..." -ForegroundColor Yellow
            $newFolder.Save($ewsParentFolder.Id)
        }
    }
    function Apply-RetentionPolicy {
        param(
            [Parameter(Mandatory = $true)]
            [Microsoft.Exchange.WebServices.Data.ExchangeService]$EWSService,
            [Parameter(Mandatory = $true)]
            [pscredential]$Credential,
            [Parameter(Mandatory = $true)]
            [string]$MailboxName,
            [Parameter(Mandatory = $true,ParameterSetName = "Retention")]
            [string]$FolderName,
            [Parameter(Mandatory = $true,ParameterSetName = "Retention")]
            [string]$RetentionTag,
            [Parameter(Mandatory = $true,ParameterSetName = "Retention")]
            [int]$RetentionPeriodValue,
            [Parameter(Mandatory = $true,ParameterSetName = "Retention")]
            [int]$RetentionFlagsValue
        )
        begin {
            $service = $EWSService
            # Bind to retention tag properties
            $policyTag = New-Object Microsoft.Exchange.WebServices.Data.ExtendedPropertyDefinition (0x3019,[Microsoft.Exchange.WebServices.Data.MapiPropertyType]::Binary);
            $retentionFlags = New-Object Microsoft.Exchange.WebServices.Data.ExtendedPropertyDefinition (0x301D,[Microsoft.Exchange.WebServices.Data.MapiPropertyType]::Integer);
            $retentionPeriod = New-Object Microsoft.Exchange.WebServices.Data.ExtendedPropertyDefinition (0x301A,[Microsoft.Exchange.WebServices.Data.MapiPropertyType]::Integer);
            $service.ImpersonatedUserId = New-Object Microsoft.Exchange.WebServices.Data.ImpersonatedUserId ([Microsoft.Exchange.WebServices.Data.ConnectingIdType]::SmtpAddress,$MailboxName);
            $folderId = New-Object Microsoft.Exchange.WebServices.Data.FolderId ([Microsoft.Exchange.Webservices.Data.WellKnownFolderName]::MsgFolderRoot,$MailboxName)
            $ewsParentFolder = [Microsoft.Exchange.WebServices.Data.Folder]::Bind($service,$folderId)
            $folderView = New-Object Microsoft.Exchange.WebServices.Data.FolderView (1000)
            $folderView.Traversal = [Microsoft.Exchange.WebServices.Data.FolderTraversal]::Deep
            $foundFolders = $ewsParentFolder.FindFolders($folderView)
            $permanentFolderFoundId = $foundFolders | Where-Object {
                $_.DisplayName -eq $FolderName
            }
            $tag = ($service.GetUserRetentionPolicyTags().RetentionPolicyTags | Where-Object {
                    $_.DisplayName -eq $RetentionTag
                }).RetentionId
            $tagByteArray = ([guid]$tag.GUID).ToByteArray()
            Write-Host -ForegroundColor Yellow "Found $FolderName in mailbox $MailboxName. Setting $RetentionTag retention policy now."
            #never delete assign tag
            $oFolder = [Microsoft.Exchange.WebServices.Data.Folder]::Bind($service,$permanentFolderFoundId.Id)
            $oFolder.SetExtendedProperty($retentionFlags,$RetentionFlagsValue)
            $oFolder.SetExtendedProperty($retentionPeriod,$RetentionPeriodValue)
            $oFolder.SetExtendedProperty($policyTag,$tagByteArray)
            $oFolder.Update()
            Write-Host -ForegroundColor Green "Finished setting $RetentionTag on $FolderName in mailbox $MailboxName"
        }
    }
    ########################################################################################
    function Connect-ExchangeOnPremises {
        param(
            [Parameter(Mandatory,HelpMessage = "http://<ServerFQDN>/powershell")]
            [System.String]$ConnectionUri,
            [Alias("RunAs")]
            [pscredential][System.Management.Automation.Credential()]
            $Credential = [System.Management.Automation.PSCredential]::Empty
        )
        try {
            $Splatting = @{
                ConnectionUri = $ConnectionUri
                ConfigurationName = "Microsoft.Exchange"
            }
            if ($PSBoundParameters["Credential"]) {
                $Splatting.Credential = $Credential
            }
            Import-PSSession -Session (New-PSSession @Splatting)
        }
        catch {
            $PSCmdlet.ThrowTerminatingError($_)
        }
    }
    function Send-Email {
    <#
        .SYNOPSIS
         This function allows you to send email
        .DESCRIPTION
         This function allows you to send email using the NET Class System.Net.Mail
        .PARAMETER To
         A description of the To parameter.
        .PARAMETER From
         A description of the From parameter.
        .PARAMETER FromDisplayName
         Specifies the DisplayName to show for the FROM parameter
        .PARAMETER SenderAddress
         A description of the SenderAddress parameter.
        .PARAMETER SenderDisplayName
         Specifies the DisplayName of the Sender
        .PARAMETER CC
         A description of the CC parameter.
        .PARAMETER BCC
         A description of the BCC parameter.
        .PARAMETER ReplyToList
         Specifies the email address(es) that will be use when the recipient(s) reply to the email.
        .PARAMETER Subject
         Specifies the subject of the email.
        .PARAMETER Body
         Specifies the body of the email.
        .PARAMETER BodyIsHTML
         Specifies that the text format of the body is HTML. Default is Plain Text.
        .PARAMETER Priority
         Specifies the priority of the message. Default is Normal.
        .PARAMETER Encoding
         Specifies the text encoding of the title and the body.
        .PARAMETER Attachment
         Specifies if an attachement must be added to the function
        .PARAMETER Credential
         Specifies the credential to use, default will use the current credential.
        .PARAMETER SMTPServer
         Specifies if the SMTP Server IP or FQDN to use
        .PARAMETER Port
         Specifies if the SMTP Server Port to use. Default is 25.
        .PARAMETER EnableSSL
         Specifies if the email must be sent using SSL.
        .PARAMETER DeliveryNotificationOptions
         Specifies the delivey notification options.
         https://msdn.microsoft.com/en-us/library/system.net.mail.deliverynotificationoptions.aspx
        .PARAMETER EmailCC
         Specifies the Carbon Copy recipient
        .PARAMETER EmailBCC
         Specifies the Blind Carbon Copy recipient
        .PARAMETER EmailTo
         Specifies the recipient of the email
        .PARAMETER EmailFrom
         Specifies the sender of the email
        .PARAMETER Sender
         Specifies the Sender Email address. Sender is the Address of the actual sender acting on behalf of the author listed in the From parameter.
        .EXAMPLE
         Send-email `
            -EmailTo "fxcat@contoso.com" `
            -EmailFrom "powershell@contoso.com" `
            -SMTPServer "smtp.sendgrid.net" `
            -Subject "Test Email" `
            -Body "Test Email"
            This will send an email using the current credential of the current logged user
        .EXAMPLE
            $Cred = [System.Net.NetworkCredential](Get-Credential -Credential testuser)
            Send-email `
            -EmailTo "fxcat@contoso.com" `
            -EmailFrom "powershell@contoso.com" `
            -Credential $cred
            -SMTPServer "smtp.sendgrid.net" `
            -Subject "Test Email" `
            -Body "Test Email"
            This will send an email using the credentials specified in the $Cred variable
        .EXAMPLE
            Send-email `
            -EmailTo "fxcat@contoso.com","SomeoneElse@contoso.com" `
            -EmailFrom "powershell@contoso.com" `
            -SMTPServer "smtp.sendgrid.net" `
            -Subject "Test Email" `
            -Body "Test Email"
            This will send an email using the current credential of the current logged user to two
            fxcat@contoso.com and SomeoneElse@contoso.com
        .NOTES
         
        .LINK
         
    #>

        [CmdletBinding(DefaultParameterSetName = 'Main')]
        param(
            [Parameter(ParameterSetName = 'Main',Mandatory = $true)]
            [Alias('EmailTo')]
            [String[]]$To,
            [Parameter(ParameterSetName = 'Main',Mandatory = $true)]
            [Alias('EmailFrom','FromAddress')]
            [string]$From,
            [Parameter(ParameterSetName = 'Main')]
            [ValidateNotNullOrEmpty()]
            [string]$FromDisplayName,
            [Parameter(ParameterSetName = 'Main')]
            [Alias('EmailCC')]
            [string]$CC,
            [Parameter(ParameterSetName = 'Main')]
            [Alias('EmailBCC')]
            [System.String]$BCC,
            [Parameter(ParameterSetName = 'Main')]
            [ValidateNotNullOrEmpty()]
            [Alias('ReplyTo')]
            [System.String[]]$ReplyToList,
            [Parameter(ParameterSetName = 'Main')]
            [System.String]$Subject = "Email from PowerShell",
            [Parameter(ParameterSetName = 'Main')]
            [System.String]$Body = "Hello World",
            [Parameter(ParameterSetName = 'Main')]
            [switch]$BodyIsHTML = $false,
            [Parameter(ParameterSetName = 'Main')]
            [ValidateNotNullOrEmpty()]
            [System.Net.Mail.MailPriority]$Priority = "Normal",
            [Parameter(ParameterSetName = 'Main')]
            [ValidateSet("Default","ASCII","Unicode","UTF7","UTF8","UTF32")]
            [System.String]$Encoding = "Default",
            [Parameter(ParameterSetName = 'Main')]
            [System.String]$Attachment,
            [Parameter(ParameterSetName = 'Main')]
            [pscredential][System.Net.NetworkCredential]$Credential,
            [Parameter(ParameterSetName = 'Main',Mandatory = $true)]
            #verify that the host is reachable
            [ValidateScript({ Test-Connection -ComputerName $_ -Count 1 -Quiet })]
            [Alias("Server")]
            [string]$SMTPServer,
            [Parameter(ParameterSetName = 'Main')]
            [ValidateRange(1,65535)]
            [Alias("SMTPServerPort")]
            [int]$Port = 25,
            [Parameter(ParameterSetName = 'Main')]
            [switch]$EnableSSL,
            [Parameter(ParameterSetName = 'Main')]
            [ValidateNotNullOrEmpty()]
            [Alias('EmailSender','Sender')]
            [string]$SenderAddress,
            [Parameter(ParameterSetName = 'Main')]
            [ValidateNotNullOrEmpty()]
            [System.String]$SenderDisplayName,
            [Parameter(ParameterSetName = 'Main')]
            [ValidateNotNullOrEmpty()]
            [Alias('DeliveryOptions')]
            [System.Net.Mail.DeliveryNotificationOptions]$DeliveryNotificationOptions
        )
        process {
            try {
                $SMTPMessage = New-Object -TypeName System.Net.Mail.MailMessage
                $SMTPMessage.From = $From
                foreach ($ToAddress in $To) {
                    $SMTPMessage.To.Add($ToAddress)
                }
                $SMTPMessage.Body = $Body
                $SMTPMessage.IsBodyHtml = $BodyIsHTML
                $SMTPMessage.Subject = $Subject
                $SMTPMessage.BodyEncoding = $([System.Text.Encoding]::$Encoding)
                $SMTPMessage.SubjectEncoding = $([System.Text.Encoding]::$Encoding)
                $SMTPMessage.Priority = $Priority
                $SMTPMessage.Sender = $SenderAddress
                if ($PSBoundParameters['SenderDisplayName']) {
                    $SMTPMessage.Sender.DisplayName = $SenderDisplayName
                }
                if ($PSBoundParameters['FromDisplayName']) {
                    $SMTPMessage.From.DisplayName = $FromDisplayName
                }
                if ($PSBoundParameters['CC']) {
                    $SMTPMessage.CC.Add($CC)
                }
                if ($PSBoundParameters['BCC']) {
                    $SMTPMessage.BCC.Add($BCC)
                }
                if ($PSBoundParameters['ReplyToList']) {
                    foreach ($ReplyTo in $ReplyToList) {
                        $SMTPMessage.ReplyToList.Add($ReplyTo)
                    }
                }
                if ($PSBoundParameters['attachment']) {
                    $SMTPattachment = New-Object -TypeName System.Net.Mail.Attachment ($attachment)
                    $SMTPMessage.Attachments.Add($SMTPattachment)
                }
                if ($PSBoundParameters['DeliveryNotificationOptions']) {
                    $SMTPMessage.DeliveryNotificationOptions = $DeliveryNotificationOptions
                }
                $SMTPClient = New-Object -TypeName Net.Mail.SmtpClient
                $SMTPClient.Host = $SmtpServer
                $SMTPClient.Port = $Port
                if ($PSBoundParameters['EnableSSL']) {
                    $SMTPClient.EnableSsl = $true
                }
                if ($PSBoundParameters['Credential']) {
                    $SMTPClient.Credentials = $Credential
                }
                if (-not $PSBoundParameters['Credential']) {
                    # Use the current logged user credential
                    $SMTPClient.UseDefaultCredentials = $true
                }
                $SMTPClient.Send($SMTPMessage)
            }
            catch {
                $PSCmdlet.ThrowTerminatingError($_)
            }
        }
        end {
            Remove-Variable -Name SMTPClient -ErrorAction SilentlyContinue
            Remove-Variable -Name Password -ErrorAction SilentlyContinue
        }
    }
    function Get-ConsoleColor {
        <#
        .SYNOPSIS
        Get all available console colors
        .DESCRIPTION
        Get all available console colors. A preview how they look (foreground and background) can be displayed with the parameter "-Preview".
        .EXAMPLE
        Get-ConsoleColor
        ConsoleColor
        ------------
               Black
            DarkBlue
           DarkGreen
            DarkCyan
             DarkRed
         DarkMagenta
          DarkYellow
                Gray
            DarkGray
                Blue
               Green
                Cyan
                 Red
             Magenta
              Yellow
               White
        .LINK
    #>

        [CmdletBinding()]
        param (
        )
        begin {
        }
        process {
            $Colors = [Enum]::GetValues([ConsoleColor])
            foreach ($Color in $Colors) {
                [pscustomobject] @{
                    ConsoleColor = $Color
                }
            }
        }
        end {
        }
    }