Connectimo.psm1

function Test-ForestConnectivity { 
    [CmdletBinding()]
    param()
    Try {
        $null = Get-ADForest
        return $true
    } catch { return $False }
}
function Get-InstalledApplication {
    <#
    .EXAMPLE
    Get-InstalledApplication -Type UserInstalled -DisplayName 'JetBrains dotCover 2017.1.1'
    #>

    [CmdletBinding()]
    Param(
        [string[]] $DisplayName, # = "Microsoft Exchange Online Powershell Module",
        [ValidateSet('UserInstalled', 'SystemWide', 'ClickOnce')][string] $Type = 'UserInstalled',
        [switch] $All
    )
    if ($Type -eq 'UserInstalled' -or $Type -eq 'ClickOnce') {
        $Registry = 'HKCU'
    } else {
        $Registry = 'HKLM'
    }

    $InstalledApplications = Get-ChildItem -Path "$Registry`:\Software\Microsoft\Windows\CurrentVersion\Uninstall" | ForEach-Object { Get-ItemProperty $_.PsPath }
    if ($DisplayName) {
        $InstalledApplications | Where-Object { $DisplayName -contains $_.DisplayName }
    } else {
        $InstalledApplications
    }
}
function Install-ApplicationClickOnce {
    [CmdletBinding()]
    Param(
        [string] $Manifest,
        [switch] $ElevatePermissions
    )
    Try {

        Add-Type -AssemblyName System.Deployment

        Write-Verbose "Install-ApplicationClickOnce - Start installation of ClickOnce Application $Manifest"

        $RemoteURI = [URI]::New( $Manifest , [UriKind]::Absolute)
        if (-not $Manifest) {
            Write-Warning "Invalid Manifest (URL) parameter $RemoteURI"
            return
        }

        $HostingManager = New-Object System.Deployment.Application.InPlaceHostingManager -ArgumentList $RemoteURI , $False

        #register an event to trigger custom event (yep, its a hack)
        $null = Register-ObjectEvent -InputObject $HostingManager -EventName GetManifestCompleted -Action {
            New-Event -SourceIdentifier "ManifestDownloadComplete"
        }
        #register an event to trigger custom event (yep, its a hack)
        $null = Register-ObjectEvent -InputObject $HostingManager -EventName DownloadApplicationCompleted -Action {
            New-Event -SourceIdentifier "DownloadApplicationCompleted"
        }

        #get the Manifest
        $HostingManager.GetManifestAsync()

        #Waitfor up to 5s for our custom event
        $event = Wait-Event -SourceIdentifier "ManifestDownloadComplete" -Timeout 5
        if ($event ) {
            $event | Remove-Event
            Write-Verbose "Install-ApplicationClickOnce - ClickOnce Manifest Download Completed"

            $HostingManager.AssertApplicationRequirements($ElevatePermissions)
            #todo :: can this fail ?

            #Download Application
            $HostingManager.DownloadApplicationAsync()
            #register and wait for completion event
            # $HostingManager.DownloadApplicationCompleted
            $event = Wait-Event -SourceIdentifier "DownloadApplicationCompleted" -Timeout 15
            if ($event ) {
                $event | Remove-Event
                Write-Verbose "Install-ApplicationClickOnce - ClickOnce Application Download Completed"
            } else {
                Write-Error "Install-ApplicationClickOnce - ClickOnce Application Download did not complete in time (15s)"
            }
        } else {
            Write-Error "Install-ApplicationClickOnce - ClickOnce Manifest Download did not complete in time (5s)"
        }

        #Clean Up
    } finally {
        #get rid of our eventhandlers
        Get-EventSubscriber | Where-Object { $_.SourceObject.ToString() -eq 'System.Deployment.Application.InPlaceHostingManager' } | Unregister-Event
    }
}
function Test-AvailabilityCommands {
    param (
        [string[]] $Commands
    )
    $CommandsStatus = foreach ($Command in $Commands) {
        [bool] $Exists = Get-Command -Name $Command -ErrorAction SilentlyContinue
        if ($Exists) {
            Write-Verbose "Test-AvailabilityCommands - Command $Command is available."
        } else {
            Write-Verbose "Test-AvailabilityCommands - Command $Command is not available."
        }
        $Exists
    }
    return $CommandsStatus
}
function Test-ConfigurationCredentials {
    [CmdletBinding()]
    param (
        [Object] $Configuration,
        $AllowEmptyKeys
    )
    $Object = foreach ($Key in $Configuration.Keys) {
        if ($AllowEmptyKeys -notcontains $Key -and [string]::IsNullOrWhiteSpace($Configuration.$Key)) {
            Write-Verbose "Test-ConfigurationCredentials - Configuration $Key is Null or Empty! Terminating"
            @{ Status = $false; Output = $User.SamAccountName; Extended = "Credentials configuration $Key is Null or Empty!" }
        }
    }
    return $Object
}
Function Test-InstalledApplication {
    [CmdletBinding()]
    Param(
        [alias('ApplicationName')] [string] $DisplayName
    )
    $App = Get-InstalledApplication -DisplayName $DisplayName -Type UserInstalled
    return $App
}

function Uninstall-ApplicationClickOnce {
    [CmdletBinding()]
    Param(
        [alias('ApplicationName')] $DisplayName
    )
    $App = Get-InstalledApplication -DisplayName $DisplayName -Type UserInstalled
    if ($App) {
        $selectedUninstallString = $App.UninstallString
        #Seperate cmd from parameters (First Space)
        $parts = $selectedUninstallString.Split(' ', 2)
        Start-Process -FilePath $parts[0] -ArgumentList $parts[1] -Wait
        #ToDo : Automatic press of OK
        #Start-Sleep 5
        #$wshell = new-object -com wscript.shell
        #$wshell.sendkeys("`"OK`"~")

        $app = Get-InstalledApplication -DisplayName $DisplayName -Type UserInstalled
        if ($app) {
            Write-Verbose 'Uninstall-ApplicationClickOnce - Uninstallation was not successfull.'
            return $false
        } else {
            Write-Verbose 'Uninstall-ApplicationClickOnce - Uninstallation was not successfull.'
            return $true
        }

    } else {
        return
    }
}

function Connect-WinAzure {
    [CmdletBinding()]
    param(
        [string] $SessionName = 'Azure MSOL',
        [string] $Username,
        [string] $Password,
        [alias('PasswordAsSecure')][switch] $AsSecure,
        [alias('PasswordFromFile')][switch] $FromFile,
        [alias('mfa')][switch] $MultiFactorAuthentication,
        [switch] $Output #, [System.Collections.IDictionary] $Credentials
    )
    if (-not $MultiFactorAuthentication) {
        Write-Verbose "Connect-WinAzure - Running connectivity without MFA"
        $Credentials = Request-Credentials -UserName $Username `
            -Password $Password `
            -AsSecure:$AsSecure `
            -FromFile:$FromFile `
            -Service $SessionName `
            -Output

        if ($Credentials -isnot [PSCredential]) {
            if ($Output) {
                return $Credentials
            } else {
                return
            }
        }
    }
    try {
        # If it's mfa $Credentials will be $null so it will ask with a prompt
        Connect-MsolService -Credential $Credentials -ErrorAction Stop
        $Connected = $true
    } catch {
        $Connected = $false
        $ErrorMessage = $_.Exception.Message -replace "`n", " " -replace "`r", " "
        if ($Output) {
            return @{ Status = $false; Output = $SessionName; Extended = "Connection failed with $ErrorMessage" }
        } else {
            Write-Warning "Connect-WinAzure - Failed with error message: $ErrorMessage"
            return
        }
    }
    if ($Connected -eq $false) {
        if ($Output) {
            return @{ Status = $false; Output = $SessionName; Extended = 'Connection Failed.' }
        } else {
            return
        }
    } else {
        if ($Output) {
            return @{ Status = $true; Output = $SessionName; Extended = 'Connection Established.' }
        } else {
            return
        }
    }
}
function Connect-WinAzureAD {
    [CmdletBinding()]
    param(
        [string] $SessionName = 'Azure AD',
        [string] $Username,
        [string] $Password,
        [alias('PasswordAsSecure')][switch] $AsSecure,
        [alias('PasswordFromFile')][switch] $FromFile,
        [alias('mfa')][switch] $MultiFactorAuthentication,
        [switch] $Output
    )
    if (-not $MultiFactorAuthentication) {
        Write-Verbose "Connect-WinAzureAD - Running connectivity without MFA"
        $Credentials = Request-Credentials -UserName $Username `
            -Password $Password `
            -AsSecure:$AsSecure `
            -FromFile:$FromFile `
            -Service $SessionName `
            -Output

        if ($Credentials -isnot [PSCredential]) {
            if ($Output) {
                return $Credentials
            } else {
                return
            }
        }
    }
    try {
        # If it's mfa $Credentials will be $null so it will ask with a prompt
        $Session = Connect-AzureAD -Credential $Credentials -ErrorAction Stop
    } catch {
        $Session = $null
        $ErrorMessage = $_.Exception.Message -replace "`n", " " -replace "`r", " "
        if ($Output) {
            return @{ Status = $false; Output = $SessionName; Extended = "Connection failed with $ErrorMessage" }
        } else {
            Write-Warning "Connect-WinAzureAD - Failed with error message: $ErrorMessage"
            return
        }
    }
    if (-not $Session) {
        if ($Output) {
            return @{ Status = $false; Output = $SessionName; Extended = 'Connection Failed.' }
        } else {
            return
        }
    }
    if ($Output) {
        return @{ Status = $true; Output = $SessionName; Extended = 'Connection Established.' }
    }
}
function Connect-WinConnectivity {
    [CmdletBinding()]
    param(
        [string] $UserName,
        [string] $Password,
        [string] $FilePath,
        [switch] $AsSecure,
        [switch] $MultiFactorAuthentication,
        [Parameter(Mandatory = $true)][ValidateSet('All', 'AzureAD', 'ExchangeOnline', 'MSOnline', 'SecurityCompliance', 'SharePointOnline', 'SkypeOnline', 'Teams')][string[]] $Service,
        [string] $Tenant
    )

    if ($FilePath) {
        $PasswordFromFile = $true
        if (Test-Path -LiteralPath $FilePath) {
            $Password = $FilePath
        } else {
            #Write-Color "[-]", " File with password doesn't exists. Path doesn't exists: $FilePath" -Color Red, Yellow
            Write-Verbose "File with password doesn't exists. Path doesn't exists: $FilePath"
            return
        }
    } else {
        $PasswordFromFile = $false
    }

    $Configuration = @{
        Options    = @{
            LogsPath = 'C:\Support\Logs\Automated.log'
        }
        Office365  = [ordered] @{
            Credentials        = [ordered] @{
                Username                  = $UserName
                Password                  = $Password
                PasswordAsSecure          = $AsSecure.IsPresent
                PasswordFromFile          = $PasswordFromFile
                MultiFactorAuthentication = $MultiFactorAuthentication.IsPresent
            }
            MSOnline           = [ordered] @{
                Use         = $false
                SessionName = 'O365 Azure MSOL' # MSOL
            }
            AzureAD            = [ordered] @{
                Use         = $false
                SessionName = 'O365 Azure AD' # Azure
                Prefix      = ''
            }
            ExchangeOnline     = [ordered] @{
                Use            = $false
                Authentication = 'Basic'
                ConnectionURI  = 'https://outlook.office365.com/powershell-liveid/'
                Prefix         = 'O365'
                SessionName    = 'O365 Exchange'
            }
            SecurityCompliance = [ordered] @{
                Use            = $false
                Authentication = 'Basic'
                ConnectionURI  = 'https://ps.compliance.protection.outlook.com/PowerShell-LiveId'
                Prefix         = 'O365'
                SessionName    = 'O365 Security And Compliance'
            }
            SharePointOnline   = [ordered] @{
                Use           = $false
                ConnectionURI = "https://$($Tenant)-admin.sharepoint.com"
            }
            SkypeOnline        = [ordered] @{
                Use         = $false
                SessionName = 'O365 Skype'
            }
            Teams              = [ordered] @{
                Use         = $false
                Prefix      = ''
                SessionName = 'O365 Teams'
            }
        }
        OnPremises = @{
            Credentials = [ordered] @{
                Username         = 'przemyslaw.klys@evotec.pl'
                Password         = 'C:\Support\Important\Password-O365-Evotec.txt'
                PasswordAsSecure = $true
                PasswordFromFile = $true
            }
            Exchange    = [ordered] @{
                Use            = $false
                Authentication = 'Kerberos'
                ConnectionURI  = 'http://PLKATO365Exch.evotec.pl/PowerShell'
                Prefix         = ''
                SessionName    = 'Exchange'
            }
        }
    }

    if ($Service -eq 'All') {
        foreach ($_ in $Configuration.Office365.Keys | Where-Object { $_ -ne 'Credentials' }) {
            if ($_ -eq 'SharePointOnline') {
                if (-not $Tenant) {
                    #Write-Color "[-]", " Tenant parameter not provided. Skipping connection to SharePoint Online." -Color Red, Yellow
                    Write-Verbose "Tenant parameter not provided. Skipping connection to SharePoint Online."
                    continue
                }
            }
            $Configuration.Office365.($_).Use = $true
        }
    } else {
        foreach ($_ in $Service) {
            $Configuration.Office365.($_).Use = $true
        }
    }

    $BundleCredentials = $Configuration.Office365.Credentials
    $BundleCredentialsOnPremises = $Configuration.OnPremises.Credentials

    $Connected = @(
        if ($Configuration.Office365.MSOnline.Use) {
            Connect-WinAzure @BundleCredentials -Output -SessionName $Configuration.Office365.MSOnline.SessionName -Verbose
        }
        if ($Configuration.Office365.AzureAD.Use) {
            Connect-WinAzureAD @BundleCredentials -Output -SessionName $Configuration.Office365.AzureAD.SessionName -Verbose
        }
        if ($Configuration.Office365.ExchangeOnline.Use) {
            Connect-WinExchange @BundleCredentials -Output -SessionName $Configuration.Office365.ExchangeOnline.SessionName -ConnectionURI $Configuration.Office365.ExchangeOnline.ConnectionURI -Authentication $Configuration.Office365.ExchangeOnline.Authentication -Verbose
        }
        if ($Configuration.Office365.SecurityCompliance.Use) {
            Connect-WinSecurityCompliance @BundleCredentials -Output -SessionName $Configuration.Office365.SecurityCompliance.SessionName -ConnectionURI $Configuration.Office365.SecurityCompliance.ConnectionURI -Authentication $Configuration.Office365.SecurityCompliance.Authentication -Verbose
        }
        if ($Configuration.Office365.SkypeOnline.Use) {
            Connect-WinSkype @BundleCredentials -Output -SessionName $Configuration.Office365.SkypeOnline.SessionName -Verbose
        }
        if ($Configuration.Office365.SharePointOnline.Use) {
            Connect-WinSharePoint @BundleCredentials -Output -SessionName $Configuration.Office365.SharePointOnline.SessionName -ConnectionURI $Configuration.Office365.SharePointOnline.ConnectionURI -Verbose
        }
        if ($Configuration.Office365.Teams.Use) {
            Connect-WinTeams @BundleCredentials -Output -SessionName $Configuration.Office365.Teams.SessionName -Verbose
        }
        if ($Configuration.OnPremises.Exchange.Use) {
            Connect-WinExchange @BundleCredentialsOnPremises -Output -SessionName $Configuration.OnPremises.Exchange.SessionName -ConnectionURI $Configuration.OnPremises.Exchange.ConnectionURI -Authentication $Configuration.OnPremises.Exchange.Authentication -Verbose
        }
    )
    if ($Connected.Status -contains $false) {
        foreach ($C in $Connected | Where-Object { $_.Status -eq $false }) {
            #Write-Color -Text 'Connecting to tenant failed for ', $C.Output, ' with error ', $Connected.Extended -Color White, Red, White, Red -LogFile $Configuration.Options.LogsPath
            Write-Verbose "Connecting to tenant failed for $($C.Output) with error $($Connected.Extended)"
        }
        return
    }
}
function Connect-WinExchange {
    [CmdletBinding()]
    param(
        [string] $SessionName = 'Exchange',
        [string] $ConnectionURI,
        [ValidateSet("Basic", "Kerberos")][String] $Authentication = 'Kerberos',
        [alias('UserPrincipalName')][string] $Username,
        [string] $Password,
        [alias('PasswordAsSecure')][switch] $AsSecure,
        [alias('PasswordFromFile')][switch] $FromFile,
        [alias('mfa')][switch] $MultiFactorAuthentication,
        [string] $Prefix,
        [switch] $Output
    )
    $Object = @()
    if ($MultiFactorAuthentication) {
        Write-Verbose 'Connect-WinExchange - Using MFA option'
        try {
            Import-Module -ErrorAction Stop $((Get-ChildItem -Path $($env:LOCALAPPDATA + "\Apps\2.0\") -Filter Microsoft.Exchange.Management.ExoPowershellModule.dll -Recurse).FullName | Where-Object { $_ -notmatch "_none_" } | Select-Object -First 1)
        } catch {
            if ($Output) {
                return @{ Status = $false; Output = $SessionName; Extended = "Connection failed. Couldn't find Exchange Online module to load." }
            } else {
                Write-Warning -Message "Connect-WinExchange - Connection failed. Couldn't find Exchange Online module to load."
                return
            }
        }
    } else {
        Write-Verbose 'Connect-WinExchange - Using Non-MFA option'
        if ($Authentication -ne 'Kerberos') {
            $Credentials = Request-Credentials -UserName $Username `
                -Password $Password `
                -AsSecure:$AsSecure `
                -FromFile:$FromFile `
                -Service $SessionName `
                -Output

            if ($Credentials -isnot [PSCredential]) {
                if ($Output) {
                    return $Credentials
                } else {
                    return
                }
            }
        } else {
            # Credentials should be null for Kerberos - Current user will run it
            $Credentials = $null
        }
    }
    $ExistingSession = Get-PSSession -Name $SessionName -ErrorAction SilentlyContinue
    if ($ExistingSession.Availability -contains 'Available') {
        foreach ($UsedSession in $ExistingSession) {
            if ($UsedSession.Availability -eq 'Available') {
                if ($Output) {
                    $Object += @{ Status = $true; Output = $SessionName; Extended = "Will reuse established session to $($Session.ComputerName)" }
                } else {
                    Write-Verbose -Message "Connect-WinExchange - reusing session $($Session.ComputerName)"
                }
                $Session = $UsedSession
                break
            }
        }
    } else {
        if ($MultiFactorAuthentication) {
            Write-Verbose -Message "Connect-WinExchange - Establishing MFA Connection"
            $PSSessionOption = New-PSSessionOption -ProxyAccessType IEConfig
            try {
                $Session = New-ExoPSSession -UserPrincipalName $UserName -PSSessionOption $PSSessionOption
                $Session.Name = $SessionName
            } catch {
                $Session = $null
                $ErrorMessage = $_.Exception.Message -replace "`n", " " -replace "`r", " "
                if ($Output) {
                    $Object += @{ Status = $false; Output = $SessionName; Extended = "Connection failed with $ErrorMessage" }
                    return $Object
                } else {
                    Write-Warning -Message "Connect-WinExchange - Failed with error message: $ErrorMessage"
                    return
                }
            }
        } else {
            Write-Verbose -Message "Connect-WinExchange - Creating Session to URI: $ConnectionURI"
            $SessionOption = New-PSSessionOption -SkipRevocationCheck -SkipCACheck -SkipCNCheck -Verbose:$false
            try {
                if ($Credentials) {
                    Write-Verbose 'Connect-WinExchange - Creating new session using Credentials'
                    $Session = New-PSSession -Credential $Credentials -ConfigurationName Microsoft.Exchange -ConnectionUri $ConnectionURI -Authentication $Authentication -SessionOption $sessionOption -Name $SessionName -AllowRedirection -ErrorAction Stop -Verbose:$false
                } else {
                    Write-Verbose 'Connect-WinExchange - Creating new session without Credentials'
                    $Session = New-PSSession -ConfigurationName Microsoft.Exchange -ConnectionUri $ConnectionURI -Authentication $Authentication -SessionOption $sessionOption -Name $SessionName -AllowRedirection -Verbose:$false -ErrorAction Stop
                }
            } catch {
                $Session = $null
                $ErrorMessage = $_.Exception.Message -replace "`n", " " -replace "`r", " "
                if ($Output) {
                    $Object += @{ Status = $false; Output = $SessionName; Extended = "Connection failed with $ErrorMessage" }
                    return $Object
                } else {
                    Write-Warning "Connect-WinExchange - Failed with error message: $ErrorMessage"
                    return
                }
            }
        }
    }


    # Failed connecting to session
    if (-not $Session) {
        if ($Output) {
            $Object += @{ Status = $false; Output = $SessionName; Extended = 'Connection failed.' }
            return $Object
        } else {
            return
        }
    }


    $CurrentVerbosePreference = $VerbosePreference; $VerbosePreference = 'SilentlyContinue' # weird but -Verbose:$false doesn't do anything
    $CurrentWarningPreference = $WarningPreference; $WarningPreference = 'SilentlyContinue' # weird but -Verbose:$false doesn't do anything
    if ($Prefix) {
        Import-Module (Import-PSSession -Session $Session -AllowClobber -DisableNameChecking -Prefix $Prefix -Verbose:$false) -Global -Prefix $Prefix
    } else {
        Import-Module (Import-PSSession -Session $Session -AllowClobber -DisableNameChecking -Verbose:$false) -Global
    }
    $VerbosePreference = $CurrentVerbosePreference
    $WarningPreference = $CurrentWarningPreference

    ## Verify Connectivity
    #$CheckAvailabilityCommands = Test-AvailabilityCommands -Commands "Get-$($Service.Prefix)ExchangeServer", "Get-$($Service.Prefix)MailboxDatabase", "Get-$($Service.Prefix)PublicFolderDatabase"
    $CheckAvailabilityCommands = Test-AvailabilityCommands -Commands "Get-$($Prefix)MailContact", "Get-$($Prefix)Mailbox"
    if ($CheckAvailabilityCommands -contains $false) {
        if ($Output) {
            $Object += @{ Status = $false; Output = $SessionName; Extended = 'Commands unavailable.' }
            return $Object
        } else {
            return
        }
    }

    if ($Output) {
        if ($Prefix) {
            $Object += @{ Status = $true; Output = $SessionName; Extended = "Connection established $($Session.ComputerName) - prefix: $Prefix" }
        } else {
            $Object += @{ Status = $true; Output = $SessionName; Extended = "Connection established $($Session.ComputerName) - prefix: n/a" }
        }
        return $Object
    } else {
        if ($Prefix) {
            Write-Verbose -Message "Connect-WinExchange - Connection established $($Session.ComputerName) - prefix: $Prefix"
        } else {
            Write-Verbose -Message "Connect-WinExchange - Connection established $($Session.ComputerName) - prefix: n/a"
        }
    }
    return $Object
}
function Connect-WinSecurityCompliance {
    [CmdletBinding()]
    param(
        [string] $SessionName = 'Security and Compliance',
        [string] $ConnectionURI,
        [ValidateSet("Basic", "Kerberos")][String] $Authentication = 'Basic',
        [alias('UserPrincipalName')][string] $Username,
        [string] $Password,
        [alias('PasswordAsSecure')][switch] $AsSecure,
        [alias('PasswordFromFile')][switch] $FromFile,
        [alias('mfa')][switch] $MultiFactorAuthentication,
        [string] $Prefix,
        [switch] $Output
    )
    $Object = @()
    if ($MultiFactorAuthentication) {
        Write-Verbose 'Connect-WinSecurityCompliance - Using MFA option'
        try {
            Import-Module $((Get-ChildItem -Path $($env:LOCALAPPDATA + "\Apps\2.0\") -Filter Microsoft.Exchange.Management.ExoPowershellModule.dll -Recurse).FullName | ? { $_ -notmatch "_none_" } | select -First 1)
        } catch {
            if ($Output) {
                $Object += @{ Status = $false; Output = $SessionName; Extended = "Connection failed. Couldn't find Exchange Online module to load." }
                return $Object
            } else {
                Write-Warning -Message "Connect-WinSecurityCompliance - Connection failed. Couldn't find Exchange Online module to load."
                return
            }
        }
    } else {
        Write-Verbose 'Connect-WinSecurityCompliance - Using Non-MFA option'
        if ($Authentication -ne 'Kerberos') {
            $Credentials = Request-Credentials -UserName $Username `
                -Password $Password `
                -AsSecure:$AsSecure `
                -FromFile:$FromFile `
                -Service $SessionName `
                -Output

            if ($Credentials -isnot [PSCredential]) {
                if ($Output) {
                    return $Credentials
                } else {
                    return
                }
            }
        } else {
            # Credentials should be null for Kerberos - Current user will run it
            $Credentials = $null
        }
    }
    $ExistingSession = Get-PSSession -Name $SessionName -ErrorAction SilentlyContinue
    if ($ExistingSession.Availability -contains 'Available') {
        foreach ($UsedSession in $ExistingSession) {
            if ($UsedSession.Availability -eq 'Available') {
                if ($Output) {
                    $Object += @{ Status = $true; Output = $SessionName; Extended = "Will reuse established session to $($Session.ComputerName)" }
                } else {
                    Write-Verbose -Message "Connect-WinSecurityCompliance - reusing session $($Session.ComputerName)"
                }
                $Session = $UsedSession
                break
            }
        }
    } else {
        if ($MultiFactorAuthentication) {
            Write-Verbose -Message "Connect-WinSecurityCompliance - Establishing MFA Connection"
            $PSSessionOption = New-PSSessionOption -ProxyAccessType IEConfig
            try {
                $Session = New-ExoPSSession -UserPrincipalName $UserName -PSSessionOption $PSSessionOption
                $Session.Name = $SessionName
            } catch {
                $Session = $null
                $ErrorMessage = $_.Exception.Message -replace "`n", " " -replace "`r", " "
                if ($Output) {
                    $Object += @{ Status = $false; Output = $SessionName; Extended = "Connection failed with $ErrorMessage" }
                    return $Object
                } else {
                    Write-Warning -Message "Connect-WinSecurityCompliance - Failed with error message: $ErrorMessage"
                    return
                }
            }
        } else {
            Write-Verbose -Message "Connect-WinSecurityCompliance - Creating Session to URI: $ConnectionURI"
            $SessionOption = New-PSSessionOption -SkipRevocationCheck -SkipCACheck -SkipCNCheck -Verbose:$false
            try {
                if ($Credentials) {
                    Write-Verbose 'Connect-WinSecurityCompliance - Creating new session using Credentials'
                    $Session = New-PSSession -Credential $Credentials -ConfigurationName Microsoft.Exchange -ConnectionUri $ConnectionURI -Authentication $Authentication -SessionOption $sessionOption -Name $SessionName -AllowRedirection -ErrorAction Stop -Verbose:$false
                } else {
                    Write-Verbose 'Connect-WinSecurityCompliance - Creating new session without Credentials'
                    $Session = New-PSSession -ConfigurationName Microsoft.Exchange -ConnectionUri $ConnectionURI -Authentication $Authentication -SessionOption $sessionOption -Name $SessionName -AllowRedirection -Verbose:$false -ErrorAction Stop
                }
            } catch {
                $Session = $null
                $ErrorMessage = $_.Exception.Message -replace "`n", " " -replace "`r", " "
                if ($Output) {
                    $Object += @{ Status = $false; Output = $SessionName; Extended = "Connection failed with $ErrorMessage" }
                    return $Object
                } else {
                    Write-Warning "Connect-WinSecurityCompliance - Failed with error message: $ErrorMessage"
                    return
                }
            }
        }
    }


    # Failed connecting to session
    if (-not $Session) {
        if ($Output) {
            $Object += @{ Status = $false; Output = $SessionName; Extended = 'Connection failed.' }
            return $Object
        } else {
            return
        }
    }


    $CurrentVerbosePreference = $VerbosePreference; $VerbosePreference = 'SilentlyContinue' # weird but -Verbose:$false doesn't do anything
    $CurrentWarningPreference = $WarningPreference; $WarningPreference = 'SilentlyContinue' # weird but -Verbose:$false doesn't do anything
    if ($Prefix) {
        Import-Module (Import-PSSession -Session $Session -AllowClobber -DisableNameChecking -Prefix $Prefix -Verbose:$false) -Global -Prefix $Prefix
    } else {
        Import-Module (Import-PSSession -Session $Session -AllowClobber -DisableNameChecking -Verbose:$false) -Global
    }
    $VerbosePreference = $CurrentVerbosePreference
    $WarningPreference = $CurrentWarningPreference

    ## Verify Connectivity
    #$CheckAvailabilityCommands = Test-AvailabilityCommands -Commands "Get-$($Service.Prefix)ExchangeServer", "Get-$($Service.Prefix)MailboxDatabase", "Get-$($Service.Prefix)PublicFolderDatabase"
    $CheckAvailabilityCommands = Test-AvailabilityCommands -Commands "Get-$($Prefix)ProtectionAlert"
    if ($CheckAvailabilityCommands -contains $false) {
        if ($Output) {
            $Object += @{ Status = $false; Output = $SessionName; Extended = 'Commands unavailable.' }
            return $Object
        } else {
            return
        }
    }

    if ($Output) {
        if ($Prefix) {
            $Object += @{ Status = $true; Output = $SessionName; Extended = "Connection established $($Session.ComputerName) - prefix: $Prefix" }
        } else {
            $Object += @{ Status = $true; Output = $SessionName; Extended = "Connection established $($Session.ComputerName) - prefix: n/a" }
        }
        return $Object
    } else {
        if ($Prefix) {
            Write-Verbose -Message "Connect-WinSecurityCompliance - Connection established $($Session.ComputerName) - prefix: $Prefix"
        } else {
            Write-Verbose -Message "Connect-WinSecurityCompliance - Connection established $($Session.ComputerName) - prefix: n/a"
        }
    }
    return $Object
}
function Connect-WinService {
    [CmdletBinding()]
    param (
        [Object] $Credentials,
        [Object] $Service,
        [string] $Type,
        [switch] $Output
    )
    $Object = @()
    if ($Service.Use) {
        switch ($Type) {
            'ActiveDirectory' {
                # Prepare Data AD
                $CheckAvailabilityCommandsAD = Test-AvailabilityCommands -Commands 'Get-ADForest', 'Get-ADDomain', 'Get-ADRootDSE', 'Get-ADGroup', 'Get-ADUser', 'Get-ADComputer'
                if ($CheckAvailabilityCommandsAD -contains $false) {
                    if ($Output) {
                        $Object += @{ Status = $false; Output = $Service.SessionName; Extended = 'Commands unavailable.' }
                        return $Object
                    } else {
                        Write-Warning "Active Directory documentation can't be started as commands are unavailable. Check if you have Active Directory module available (part of RSAT) and try again."
                        return
                    }
                } else {
                    #if ($Output) {
                    # $Object += @{ Status = $true; Output = 'ActiveDirectory'; Extended = 'Commands available.' }
                    #}
                }
                if (-not (Test-ForestConnectivity)) {
                    if ($Output) {
                        $Object += @{ Status = $false; Output = $Service.SessionName; Extended = 'No connectivity to forest/domain.' }
                        return $Object
                    } else {
                        Write-Warning 'Active Directory - No connectivity to forest/domain.'
                        return
                    }
                } else {
                    #if ($Output) {
                    #$Object += @{ Status = $true; Output = 'ActiveDirectory'; Extended = 'Connectivity to forest/domain available.' }
                    #}
                }
                if ($Output) {
                    $Object += @{ Status = $true; Output = $Service.SessionName; Extended = 'Connection Established.' }
                    return $Object
                }
            }
            'Azure' {
                # Check Credentials
                $CheckCredentials = Test-ConfigurationCredentials -Configuration $Credentials
                if ($CheckCredentials.Status -contains $false) {
                    if ($Output) {
                        $Object += @{ Status = $false; Output = $Service.SessionName; Extended = 'Credentials configuration is wrong.' }
                        return $Object
                    } else {
                        return
                    }
                }

                $OutputCommand = Connect-WinAzure -SessionName $Service.SessionName `
                    -Username $Credentials.Username `
                    -Password $Credentials.Password `
                    -AsSecure:$Credentials.PasswordAsSecure `
                    -FromFile:$Credentials.PasswordFromFile `
                    -MultiFactorAuthentication:$Credentials.MultiFactorAuthentication `
                    -Output
                return $OutputCommand
            }
            'AzureAD' {
                # Check Credentials
                $CheckCredentials = Test-ConfigurationCredentials -Configuration $Credentials
                if ($CheckCredentials.Status -contains $false) {
                    if ($Output) {
                        $Object += @{ Status = $false; Output = $Service.SessionName; Extended = 'Credentials configuration is wrong.' }
                        return $Object
                    } else {
                        return
                    }
                }
                $OutputCommand = Connect-WinAzureAD -SessionName $Service.SessionName `
                    -Username $Credentials.Username `
                    -Password $Credentials.Password `
                    -AsSecure:$Credentials.PasswordAsSecure `
                    -FromFile:$Credentials.PasswordFromFile `
                    -MultiFactorAuthentication:$Credentials.MultiFactorAuthentication `
                    -Output
                return $OutputCommand
            }
            'Exchange' {
                $CheckCredentials = Test-ConfigurationCredentials -Configuration $Document.DocumentExchange.Configuration -AllowEmptyKeys 'Username', 'Password'
                if ($CheckCredentials.Status -contains $false) {
                    if ($Output) {
                        $Object += @{ Status = $false; Output = $Service.SessionName; Extended = 'Credentials configuration is wrong.' }
                        return $Object
                    } else {
                        return
                    }
                }
                $OutputCommand = Connect-WinExchange -SessionName $Service.SessionName `
                    -ConnectionURI $Service.ConnectionURI `
                    -Authentication $Service.Authentication `
                    -Username $Credentials.Username `
                    -Password $Credentials.Password `
                    -AsSecure:$Credentials.PasswordAsSecure `
                    -FromFile:$Credentials.PasswordFromFile `
                    -MultiFactorAuthentication:$Credentials.MultiFactorAuthentication `
                    -Prefix $Service.Prefix `
                    -Output
                return $OutputCommand
            }
            'ExchangeOnline' {
                $CheckCredentials = Test-ConfigurationCredentials -Configuration $Credentials
                if ($CheckCredentials.Status -contains $false) {
                    if ($Output) {
                        $Object += @{ Status = $false; Output = $Service.SessionName; Extended = 'Credentials configuration is wrong.' }
                        return $Object
                    } else {
                        return
                    }
                }
                # Build Session
                $OutputCommand = Connect-WinExchange -SessionName $Service.SessionName `
                    -ConnectionURI $Service.ConnectionURI `
                    -Authentication $Service.Authentication `
                    -Username $Credentials.Username `
                    -Password $Credentials.Password `
                    -AsSecure:$Credentials.PasswordAsSecure `
                    -FromFile:$Credentials.PasswordFromFile `
                    -MultiFactorAuthentication:$Credentials.MultiFactorAuthentication `
                    -Prefix $Service.Prefix `
                    -Output
                return $OutputCommand

            }
            'SecurityCompliance' {
                $CheckCredentials = Test-ConfigurationCredentials -Configuration $Credentials
                if ($CheckCredentials.Status -contains $false) {
                    if ($Output) {
                        $Object += @{ Status = $false; Output = $Service.SessionName; Extended = 'Credentials configuration is wrong.' }
                        return $Object
                    } else {
                        return
                    }
                }
                # Build Session
                $OutputCommand = Connect-WinSecurityCompliance -SessionName $Service.SessionName `
                    -ConnectionURI $Service.ConnectionURI `
                    -Authentication $Service.Authentication `
                    -Username $Credentials.Username `
                    -Password $Credentials.Password `
                    -AsSecure:$Credentials.PasswordAsSecure `
                    -FromFile:$Credentials.PasswordFromFile `
                    -MultiFactorAuthentication:$Credentials.MultiFactorAuthentication `
                    -Prefix $Service.Prefix `
                    -Output
                return $OutputCommand

            }
            'SharePointOnline' {
                $CheckCredentials = Test-ConfigurationCredentials -Configuration $Credentials
                if ($CheckCredentials.Status -contains $false) {
                    if ($Output) {
                        $Object += @{ Status = $false; Output = $Service.SessionName; Extended = 'Credentials configuration is wrong.' }
                        return $Object
                    } else {
                        return
                    }
                }
                # Build Session
                $OutputCommand = Connect-WinSharePoint -SessionName $Service.SessionName `
                    -ConnectionURI $Service.ConnectionURI `
                    -Authentication $Service.Authentication `
                    -Username $Credentials.Username `
                    -Password $Credentials.Password `
                    -AsSecure:$Credentials.PasswordAsSecure `
                    -FromFile:$Credentials.PasswordFromFile `
                    -MultiFactorAuthentication:$Credentials.MultiFactorAuthentication `
                    -Prefix $Service.Prefix `
                    -Output
                return $OutputCommand

            }
            'SkypeOnline' {
                $CheckCredentials = Test-ConfigurationCredentials -Configuration $Credentials
                if ($CheckCredentials.Status -contains $false) {
                    if ($Output) {
                        $Object += @{ Status = $false; Output = $Service.SessionName; Extended = 'Credentials configuration is wrong.' }
                        return $Object
                    } else {
                        return
                    }
                }
                # Build Session
                $OutputCommand = Connect-WinSkype -SessionName $Service.SessionName `
                    -Username $Credentials.Username `
                    -Password $Credentials.Password `
                    -AsSecure:$Credentials.PasswordAsSecure `
                    -FromFile:$Credentials.PasswordFromFile `
                    -MultiFactorAuthentication:$Credentials.MultiFactorAuthentication `
                    -Prefix $Service.Prefix `
                    -Output
                return $OutputCommand

            }
            'MicrosoftTeams' {
                # Check Credentials
                $CheckCredentials = Test-ConfigurationCredentials -Configuration $Credentials
                if ($CheckCredentials.Status -contains $false) {
                    if ($Output) {
                        $Object += @{ Status = $false; Output = $Service.SessionName; Extended = 'Credentials configuration is wrong.' }
                        return $Object
                    } else {
                        return
                    }
                }
                $OutputCommand = Connect-WinTeams -SessionName $Service.SessionName `
                    -Username $Credentials.Username `
                    -Password $Credentials.Password `
                    -AsSecure:$Credentials.PasswordAsSecure `
                    -FromFile:$Credentials.PasswordFromFile `
                    -MultiFactorAuthentication:$Credentials.MultiFactorAuthentication `
                    -Output
                return $OutputCommand
            }
        }

    }
}
function Connect-WinSharePoint {
    [CmdletBinding()]
    param(
        [string] $SessionName = 'Microsoft SharePoint',
        [string] $Username,
        [string] $Password,
        [alias('PasswordAsSecure')][switch] $AsSecure,
        [alias('PasswordFromFile')][switch] $FromFile,
        [alias('mfa')][switch] $MultiFactorAuthentication,
        [alias('uri', 'url', 'ConnectionUrl')][Uri] $ConnectionURI,
        [switch] $Output
    )
    if (-not $MultiFactorAuthentication) {
        $Credentials = Request-Credentials -UserName $Username `
            -Password $Password `
            -AsSecure:$AsSecure `
            -FromFile:$FromFile `
            -Service $SessionName `
            -Output

        if ($Credentials -isnot [PSCredential]) {
            if ($Output) {
                return $Credentials
            } else {
                return
            }
        }
    }
    try {
        # If it's mfa $Credentials will be $null so it will ask with a prompt
        Import-Module Microsoft.Online.SharePoint.PowerShell -DisableNameChecking
        $Session = Connect-SPOService -Url $ConnectionURI -Credential $Credentials
    } catch {
        $Session = $null
        $ErrorMessage = $_.Exception.Message -replace "`n", " " -replace "`r", " "
        if ($Output) {
            return @{ Status = $false; Output = $SessionName; Extended = "Connection failed with $ErrorMessage" }
        } else {
            Write-Warning "Connect-WinSharePoint - Failed with error message: $ErrorMessage"
            return
        }
    }
    if ($Output) {
        return @{ Status = $true; Output = $SessionName; Extended = 'Connection Established.' }
    }
}
function Connect-WinSkype {
    [CmdletBinding()]
    param(
        [string] $SessionName = 'Microsoft Skype',
        [string] $Username,
        [string] $Password,
        [alias('PasswordAsSecure')][switch] $AsSecure,
        [alias('PasswordFromFile')][switch] $FromFile,
        [alias('mfa')][switch] $MultiFactorAuthentication,
        [switch] $Output
    )
    $Object = @()
    if (-not $MultiFactorAuthentication) {
        Write-Verbose "Connect-WinSkype - Running connectivity without MFA"
        $Credentials = Request-Credentials -UserName $Username `
            -Password $Password `
            -AsSecure:$AsSecure `
            -FromFile:$FromFile `
            -Service $SessionName `
            -Output

        if ($Credentials -isnot [PSCredential]) {
            if ($Output) {
                return $Credentials
            } else {
                return
            }
        }
    }
    $ExistingSession = Get-PSSession -Name $SessionName -ErrorAction SilentlyContinue
    if ($ExistingSession.Availability -contains 'Available') {
        foreach ($UsedSession in $ExistingSession) {
            if ($UsedSession.Availability -eq 'Available') {
                if ($Output) {
                    $Object += @{ Status = $true; Output = $SessionName; Extended = "Will reuse established session to $($Session.ComputerName)" }
                } else {
                    Write-Verbose -Message "Connect-WinSkype - reusing session $($Session.ComputerName)"
                }
                $Session = $UsedSession
                break
            }
        }
    } else {
        try {
            if ($MultiFactorAuthentication) {
                $Session = New-CsOnlineSession -UserName $Username -ErrorAction Stop
            } else {
                $Session = New-CsOnlineSession -Credential $Credentials -ErrorAction Stop
            }
            $Session.Name = $SessionName
        } catch {
            $Session = $null
            $ErrorMessage = $_.Exception.Message -replace "`n", " " -replace "`r", " "
            if ($Output) {
                return @{ Status = $false; Output = $SessionName; Extended = "Connection failed with $ErrorMessage" }
            } else {
                Write-Warning -Message "Connect-WinSkype - Failed with error message: $ErrorMessage"
                return
            }
        }
    }
    # Failed connecting to session
    if (-not $Session) {
        if ($Output) {
            $Object += @{ Status = $false; Output = $SessionName; Extended = 'Connection failed.' }
            return $Object
        } else {
            return
        }
    }


    $CurrentVerbosePreference = $VerbosePreference; $VerbosePreference = 'SilentlyContinue' # weird but -Verbose:$false doesn't do anything
    $CurrentWarningPreference = $WarningPreference; $WarningPreference = 'SilentlyContinue' # weird but -Verbose:$false doesn't do anything
    if ($Prefix) {
        Import-Module (Import-PSSession -Session $Session -AllowClobber -DisableNameChecking -Prefix $Prefix -Verbose:$false) -Global -Prefix $Prefix
    } else {
        Import-Module (Import-PSSession -Session $Session -AllowClobber -DisableNameChecking -Verbose:$false) -Global
    }
    $VerbosePreference = $CurrentVerbosePreference
    $WarningPreference = $CurrentWarningPreference

    ## Verify Connectivity
    #$CheckAvailabilityCommands = Test-AvailabilityCommands -Commands "Get-$($Service.Prefix)ExchangeServer", "Get-$($Service.Prefix)MailboxDatabase", "Get-$($Service.Prefix)PublicFolderDatabase"
    $CheckAvailabilityCommands = Test-AvailabilityCommands -Commands "Get-$($Prefix)CsExternalAccessPolicy"
    if ($CheckAvailabilityCommands -contains $false) {
        if ($Output) {
            $Object += @{ Status = $false; Output = $SessionName; Extended = 'Commands unavailable.' }
            return $Object
        } else {
            return
        }
    }

    if ($Output) {
        if ($Prefix) {
            $Object += @{ Status = $true; Output = $SessionName; Extended = "Connection established $($Session.ComputerName) - prefix: $Prefix" }
        } else {
            $Object += @{ Status = $true; Output = $SessionName; Extended = "Connection established $($Session.ComputerName) - prefix: n/a" }
        }
        return $Object
    } else {
        if ($Prefix) {
            Write-Verbose -Message "Connect-WinSkype - Connection established $($Session.ComputerName) - prefix: $Prefix"
        } else {
            Write-Verbose -Message "Connect-WinSkype - Connection established $($Session.ComputerName) - prefix: n/a"
        }
    }
    return $Object
}
function Connect-WinTeams {
    [CmdletBinding()]
    param(
        [string] $SessionName = 'Microsoft Teams',
        [string] $Username,
        [string] $Password,
        [alias('PasswordAsSecure')][switch] $AsSecure,
        [alias('PasswordFromFile')][switch] $FromFile,
        [alias('mfa')][switch] $MultiFactorAuthentication,
        [switch] $Output
    )
    if (-not $MultiFactorAuthentication) {
        $Credentials = Request-Credentials -UserName $Username `
            -Password $Password `
            -AsSecure:$AsSecure `
            -FromFile:$FromFile `
            -Service $SessionName `
            -Output

        if ($Credentials -isnot [PSCredential]) {
            if ($Output) {
                return $Credentials
            } else {
                return
            }
        }
    }
    try {
        # If it's mfa $Credentials will be $null so it will ask with a prompt
        $Session = Connect-MicrosoftTeams -Credential $Credentials -ErrorAction Stop
    } catch {
        $Session = $null
        $ErrorMessage = $_.Exception.Message -replace "`n", " " -replace "`r", " "
        if ($Output) {
            return @{ Status = $false; Output = $SessionName; Extended = "Connection failed with $ErrorMessage" }
        } else {
            Write-Warning "Connect-WinTeams - Failed with error message: $ErrorMessage"
            return
        }
    }
    if (-not $Session) {
        if ($Output) {
            return @{ Status = $false; Output = $SessionName; Extended = 'Connection Failed.' }
        } else {
            return
        }
    }
    if ($Output) {
        return @{ Status = $true; Output = $SessionName; Extended = 'Connection Established.' }
    }
}
function Disconnect-WinSkype {
    [CmdletBinding()]
    param(
        [string] $SessionName = "Microsoft Skype",
        [switch] $Output
    )
    $Object = @()
    $ExistingSession = Get-PSSession -Name $SessionName -ErrorAction SilentlyContinue
    if ($ExistingSession) {
        try {
            Remove-PSSession -Name $SessionName -ErrorAction Stop
        } catch {
            $ErrorMessage = $_.Exception.Message -replace "`n", " " -replace "`r", " "
            if ($Output) {
                $Object += @{ Status = $false; Output = $SessionName; Extended = "Disconnection failed. Error: $ErrorMessage" }
                return $Object
            } else {
                Write-Warning "Disconnect-WinSkype - Failed with error message: $ErrorMessage"
                return
            }
        }
        if ($Output) {
            $Object += @{ Status = $true; Output = $SessionName; Extended = "Disconnection succeeded." }
            return $Object
        }
    } else {
        if ($Output) {
            $Object += @{ Status = $false; Output = $SessionName; Extended = "Disconnection failed. No connection exists." }
            return $Object
        }
    }

}
function Disconnect-WinAzure {
    [CmdletBinding()]
    param(
        [string] $SessionName = 'Azure MSOL',
        [switch] $Output,
        [switch] $Force
    )
    $Object = @()
    if (-not $Force) {
        if ($Output) {
            $Object += @{ Status = $true; Output = $SessionName; Extended = "No way to do this. Kill PowerShell session manually." }
            return $Object
        } else {
            Write-Warning "Disconnect-WinAzure - There is no other way to disconnect from $Session then killing PowerShell session. Do this manually!"
            return
        }
    } else {
        Exit
    }
}
function Disconnect-WinAzureAD {
    [CmdletBinding()]
    param(
        [string] $SessionName = 'Azure AD',
        [switch] $Output
    )
    $Object = @()
    try {
        Disconnect-AzureAD -ErrorAction Stop
    } catch {
        $ErrorMessage = $_.Exception.Message -replace "`n", " " -replace "`r", " "
        if ($ErrorMessage -like "*Object reference not set to an instance of an object.*") {
            $Object += @{ Status = $false; Output = $SessionName; Extended = "Disconnection failed. No connection exists." }
        } else {
            $Object += @{ Status = $false; Output = $SessionName; Extended = "Disconnection failed. Error: $ErrorMessage" }
        }
        if ($Output) {
            return $Object
        } else {
            Write-Warning "Disconnect-WinAzureAD - Failed with error message: $ErrorMessage"
            return
        }
    }
    if ($Output) {
        $Object += @{ Status = $true; Output = $SessionName; Extended = "Disconnection succeeded." }
        return $Object
    }
}
function Disconnect-WinExchange {
    [CmdletBinding()]
    param(
        [string] $SessionName = "Exchange",
        [switch] $Output
    )
    $Object = @()
    $ExistingSession = Get-PSSession -Name $SessionName -ErrorAction SilentlyContinue
    if ($ExistingSession) {
        try {
            Remove-PSSession -Name $SessionName -ErrorAction Stop
        } catch {
            $ErrorMessage = $_.Exception.Message -replace "`n", " " -replace "`r", " "
            if ($Output) {
                $Object += @{ Status = $false; Output = $SessionName; Extended = "Disconnection failed. Error: $ErrorMessage" }
                return $Object
            } else {
                Write-Warning "Disconnect-WinExchange - Failed with error message: $ErrorMessage"
                return
            }
        }
        if ($Output) {
            $Object += @{ Status = $true; Output = $SessionName; Extended = "Disconnection succeeded." }
            return $Object
        }
    } else {
        if ($Output) {
            $Object += @{ Status = $false; Output = $SessionName; Extended = "Disconnection failed. No connection exists." }
            return $Object
        }
    }

}
function Disconnect-WinSecurityCompliance {
    [CmdletBinding()]
    param(
        [string] $SessionName = 'Security and Compliance',
        [switch] $Output
    )
    $Object = @()
    $ExistingSession = Get-PSSession -Name $SessionName -ErrorAction SilentlyContinue
    if ($ExistingSession) {
        try {
            Remove-PSSession -Name $SessionName -ErrorAction Stop
        } catch {
            $ErrorMessage = $_.Exception.Message -replace "`n", " " -replace "`r", " "
            if ($Output) {
                $Object += @{ Status = $false; Output = $SessionName; Extended = "Disconnection failed. Error: $ErrorMessage" }
                return $Object
            } else {
                Write-Warning "Disconnect-WinSecurityCompliance - Failed with error message: $ErrorMessage"
                return
            }
        }
        if ($Output) {
            $Object += @{ Status = $true; Output = $SessionName; Extended = "Disconnection succeeded." }
            return $Object
        }
    } else {
        if ($Output) {
            $Object += @{ Status = $false; Output = $SessionName; Extended = "Disconnection failed. No connection exists." }
            return $Object
        }
    }

}
function Disconnect-WinService {
    [CmdletBinding()]
    param(

    )
    Get-PSSession | Remove-PSSession
}
function Disconnect-WinTeams {
    [CmdletBinding()]
    param(
        [string] $SessionName = 'Microsoft Teams',
        [switch] $Output
    )
    $Object = @()
    try {
        Disconnect-MicrosoftTeams -ErrorAction Stop
    } catch {
        $ErrorMessage = $_.Exception.Message -replace "`n", " " -replace "`r", " "
        if ($ErrorMessage -like "*Object reference not set to an instance of an object.*") {
            $Object += @{ Status = $false; Output = $SessionName; Extended = "Disconnection failed. No connection exists." }
        } else {
            $Object += @{ Status = $false; Output = $SessionName; Extended = "Disconnection failed. Error: $ErrorMessage" }
        }
        if ($Output) {
            return $Object
        } else {
            Write-Warning "Disconnect-MicrosoftTeams - Failed with error message: $ErrorMessage"
            return
        }
    }
    if ($Output) {
        $Object += @{ Status = $true; Output = $SessionName; Extended = "Disconnection succeeded." }
        return $Object
    }
}
function Install-WinConnectity {
    [CmdletBinding()]
    param(
        [ValidateSet('MSOnline', 'AzureAD', 'SharePoint', 'ExchangeOnline', 'ExchangeOnlineV2', 'SkypeOnline', 'Teams' )][string[]] $Module,
        [switch] $All,
        [switch] $Force
    )

    $Splat = @{
        Force = $Force
    }

    if ($Module -eq 'MSOnline' -or $All) {
        Write-Verbose "Installing MSOnline Powershell Module"
        Install-Module -Name MSOnline @Splat
    }
    if ($Module -eq 'AzureAD' -or $All) {
        Write-Verbose "Installing AzureAD Powershell Module"
        Install-Module -Name AzureADPreview @Splat
    }
    #Install-Module SkypeOnlineConnector
    if ($Module -eq 'SharePoint' -or $All) {
        Write-Verbose "Installing Microsoft SharePoint Online Powershell Module"
        Install-Module -Name Microsoft.Online.SharePoint.PowerShell @Splat
    }
    if ($Module -eq 'ExchangeOnline' -or $All) {
        Write-Verbose "Checking for Microsoft Exchange Online Powershell Module"
        $App = Test-InstalledApplication -DisplayName "Microsoft Exchange Online Powershell Module"
        if ($null -eq $App) {
            # Manifest for Exchange Online Click Once App
            Write-Verbose "Installing Microsoft Exchange Online Powershell Module"
            Install-ApplicationClickOnce -Manifest "https://cmdletpswmodule.blob.core.windows.net/exopsmodule/Microsoft.Online.CSE.PSModule.Client.application" -ElevatePermissions
        }
    }
    if ($Module -eq 'Teams' -or $All) {
        Write-Verbose "Installing Microsoft Teams Powershell Module"
        Install-Module -Name MicrosoftTeams @Splat
    }
    if ($Module -eq 'SkypeOnline' -or $All) {
        Write-Verbose "Installing Microsoft Skype Online PowerShell Module"
        # TO DO
    }
    if ($Module -eq 'ExchangeOnlineV2' -or $All) {
        Write-Verbose "Installing Exchange Online V2 PowerShell Module"
        Install-Module -Name ExchangeOnlineManagement @Splat
    }
}
function Request-Credentials {
    [CmdletBinding()]
    param(
        [string] $UserName,
        [string] $Password,
        [switch] $AsSecure,
        [switch] $FromFile,
        [switch] $Output,
        [switch] $NetworkCredentials,
        [string] $Service
    )
    if ($FromFile) {
        if (($Password -ne '') -and (Test-Path $Password)) {
            # File is there and we are reading it into Password
            Write-Verbose "Request-Credentials - Reading password from file $Password"
            $Password = Get-Content -Path $Password
        } else {
            # File is not there or couldn't be read
            if ($Output) {
                return @{ Status = $false; Output = $Service; Extended = 'File with password unreadable.' }
            } else {
                Write-Warning "Request-Credentials - Secure password from file couldn't be read. File not readable. Terminating."
                return
            }
        }
    }
    if ($AsSecure) {
        try {
            $NewPassword = $Password | ConvertTo-SecureString -ErrorAction Stop
        } catch {
            $ErrorMessage = $_.Exception.Message -replace "`n", " " -replace "`r", " "
            if ($ErrorMessage -like '*Key not valid for use in specified state*') {
                if ($Output) {
                    return @{ Status = $false; Output = $Service; Extended = "Couldn't use credentials provided. Most likely using credentials from other user/session/computer." }
                } else {
                    Write-Warning -Message "Request-Credentials - Couldn't use credentials provided. Most likely using credentials from other user/session/computer."
                    return
                }
            } else {
                if ($Output) {
                    return @{ Status = $false; Output = $Service; Extended = $ErrorMessage }
                } else {
                    Write-Warning -Message "Request-Credentials - $ErrorMessage"
                    return
                }
            }
        }

    } else {
        $NewPassword = $Password
    }
    if ($UserName -and $NewPassword) {
        if ($AsSecure) {
            $Credentials = New-Object System.Management.Automation.PSCredential($Username, $NewPassword)
        } else {
            Try {
                $SecurePassword = $Password | ConvertTo-SecureString -asPlainText -Force -ErrorAction Stop
            } catch {
                $ErrorMessage = $_.Exception.Message -replace "`n", " " -replace "`r", " "
                if ($ErrorMessage -like '*Key not valid for use in specified state*') {
                    if ($Output) {
                        return  @{ Status = $false; Output = $Service; Extended = "Couldn't use credentials provided. Most likely using credentials from other user/session/computer." }
                    } else {
                        Write-Warning -Message "Request-Credentials - Couldn't use credentials provided. Most likely using credentials from other user/session/computer."
                        return
                    }
                } else {
                    if ($Output) {
                        return @{ Status = $false; Output = $Service; Extended = $ErrorMessage }
                    } else {
                        Write-Warning -Message "Request-Credentials - $ErrorMessage"
                        return
                    }
                }
            }
            $Credentials = New-Object System.Management.Automation.PSCredential($Username, $SecurePassword)
        }
    } else {
        if ($Output) {
            return @{ Status = $false; Output = $Service; Extended = 'Username or/and Password is empty' }
        } else {
            Write-Warning -Message 'Request-Credentials - UserName or Password are empty.'
            return
        }
    }
    if ($NetworkCredentials) {
        return $Credentials.GetNetworkCredential()
    } else {
        return $Credentials
    }
}




Export-ModuleMember `
    -Function @('Connect-WinAzure', 'Connect-WinAzureAD', 'Connect-WinConnectivity', 'Connect-WinExchange', 'Connect-WinSecurityCompliance', 'Connect-WinService', 'Connect-WinSharePoint', 'Connect-WinSkype', 'Connect-WinTeams', 'Disconnect-WinAzure', 'Disconnect-WinAzureAD', 'Disconnect-WinExchange', 'Disconnect-WinSecurityCompliance', 'Disconnect-WinService', 'Disconnect-WinSkype', 'Disconnect-WinTeams', 'Install-WinConnectity', 'Request-Credentials') `
    -Alias @()