M365PSProfile.psm1

##############################################################################
# M365 PS Profile
# Installs and Updates the Required PowerShell Modules for M365 Management
##############################################################################

##############################################################################
# Global variable for standard modules
##############################################################################
[array]$global:M365StandardModules = @(
    "ExchangeOnlineManagement",
    "Icewolf.EXO.SpamAnalyze",
    "MicrosoftTeams",
    "Microsoft.Online.SharePoint.PowerShell",
    "PnP.PowerShell",
    "ORCA",
    "O365CentralizedAddInDeployment",
    "M365PSProfile"
    "MSCommerce",
    "WhiteboardAdmin",
    "Microsoft.Graph",
    "Microsoft.Graph.Beta",
    "MSIdentityTools",
    "PSMSALNet"
)

##############################################################################
# Get-M365ModulePath
# Returns the Path for the Modules
##############################################################################
Function Get-M365ModulePath {
    <#
        .SYNOPSIS
        Returns the Path for the Modules based ont the Scope Parameter
 
        .DESCRIPTION
        Returns the Path for the Modules based ont the Scope Parameter
 
        .PARAMETER Scope
        Sets the Scope [CurrentUser/AllUsers] for the Installation of the PowerShell Modules. Default value is CurrentUser.
 
        .EXAMPLE
        Get-M365ModulePath -Scope CurrentUser
 
        .EXAMPLE
        Get-M365ModulePath -Scope AllUsers
 
        .LINK
        https://github.com/fabrisodotps1/M365PSProfile
    #>


    PARAM(
        [parameter(mandatory = $false)][ValidateSet("CurrentUser", "AllUsers")][string]$Scope = "CurrentUser"
    )

    $Personal = [environment]::getfolderpath("mydocuments")
    $ProgramFiles = [environment]::getfolderpath("ProgramFiles")
    If ([System.Runtime.InteropServices.RuntimeInformation]::IsOSPlatform([System.Runtime.InteropServices.OSPlatform]::Windows))
    {
        #Windows
        If ($Host.Version -ge "6.0")
        {
            $Path = "PowerShell"
        } else {
            $path = "WindowsPowerShell"
        }

        If ($Scope -eq "CurrentUser")
        {
            $LocalUserDir = Join-Path -Path $Personal -ChildPath $Path
            return $LocalUserDir + "\Modules\"
        }

        If ($Scope -eq "AllUsers")
        {
            $AllUsersDir = Join-Path -Path $ProgramFiles -ChildPath $Path
            return $AllUsersDir + "\Modules\"
        }
    }

    #Unix / OSX
    #$LocalUserDir = Join-Path -Path $Env:Home -ChildPath ".local", "share", "powershell"
    #$AllUsersDir = Join-Path -Path "/usr" -ChildPath "local", "share", "powershell"
    #Return $LocalUserDir, $AllUsersDir
}

##############################################################################
# Get-M365StandardModules
# Returns the M365StandardModules global variable
##############################################################################
Function Get-M365StandardModule {
    <#
        .SYNOPSIS
        Returns the M365StandardModules global variable.
 
        .DESCRIPTION
        Returns the M365StandardModules global variable which contains the standard modules for M365 Management.
 
        .EXAMPLE
        Get-M365StandardModule
 
        .LINK
        https://github.com/fabrisodotps1/M365PSProfile
    #>


    return $global:M365StandardModules
}

##############################################################################
# Function AsciiArt
##############################################################################
Function Invoke-AsciiArt {
    <#
        .SYNOPSIS
        Generates M365PSProfile AsciiArt
 
        .DESCRIPTION
        Generates M365PSProfile AsciiArt
 
        .EXAMPLE
        Invoke-AsciiArt
 
        .LINK
        https://github.com/fabrisodotps1/M365PSProfile
    #>


    Write-Host " __ __ ____ __ _____ _____ _____ _____ __ _ _ "
    Write-Host "| \/ |___ \ / /| ____| __ \ / ____| __ \ / _(_) | "
    Write-Host "| \ / | __) |/ /_| |__ | |__) | (___ | |__) | __ ___ | |_ _| | ___ "
    Write-Host "| |\/| ||__ <| '_ \___ \| ___/ \___ \| ___/ '__/ _ \| _| | |/ _ \"
    Write-Host "| | | |___) | (_) |__) | | ____) | | | | | (_) | | | | | __/"
    Write-Host "|_| |_|____/ \___/____/|_| |_____/|_| |_| \___/|_| |_|_|\___|"
    Write-Host "Version:" $MyInvocation.MyCommand.ScriptBlock.Module.Version
}

##############################################################################
# Add-M365PSProfile
# Add M365PSProfile, if no PowerShell Profile exists
##############################################################################
Function Add-M365PSProfile {
    <#
        .SYNOPSIS
        Add PowerShell Profile with M365PSProfile setup
 
        .DESCRIPTION
        Add PowerShell Profile with M365PSProfile setup.
 
        Needs to be executed separately for PowerShell v5 and v7.
 
        .EXAMPLE
        Add-M365PSProfile
 
        .LINK
        https://github.com/fabrisodotps1/M365PSProfile
    #>

    [CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'Medium')]
        param()

    $M365PSProfileContent = @"
 
#M365PSProfile: Install or updates the default Modules (what we think every M365 Admin needs) in the CurrentUser Scope
Import-Module -Name M365PSProfile
Install-M365Module
"@


    If (-not(Test-Path -Path $Profile)) {
        #No Profile found
        Write-Host "No PowerShell Profile exists. A new Profile with the M365PSProfile setup is created."
        $M365PSProfileContent | Out-File -FilePath $Profile -Encoding utf8 -Force
    } else {
        #Profile found
        $ProfileContent = Get-Content -Path $Profile -Encoding utf8
        #$ProfileContent | Where-Object {$_ -match "Import-Module -Name M365PSProfile"}
        $Match = $ProfileContent | Where-Object {$_ -match "Install-M365Module"}
        If ($Null -ne $Match)
        {
            #M365PSProfile already in Profile
            Write-Host "PowerShell Profile already exists. M365PSProfile is already in the Profile." -ForegroundColor Yellow
        } else {
            #M365PSProfile not in Profile
            Write-Host "PowerShell Profile already exists. Adding M365PSProfile to it" -ForegroundColor Yellow
            Add-Content -Path $Profile -Value $M365PSProfileContent -Encoding utf8
        }
    }
}

##############################################################################
# Uninstall-M365Modules
# Remove Modules in -Modules Parameter
##############################################################################
Function Uninstall-M365Module {
    <#
        .SYNOPSIS
        Uninstall M365 PowerShell Modules
 
        .DESCRIPTION
        Uninstall of all defined M365 modules
 
        .PARAMETER Modules
        Array of Module Names that will be uninstalled. Default value are the default modules (see Get-M365StandardModule) or an Array with the Modules to uninstall.
 
        .PARAMETER Scope
        Sets the Scope [CurrentUser/AllUsers] for the Installation of the PowerShell Modules. Default value is CurrentUser.
 
        .PARAMETER Repository
        [string]Repository specifies which PowerShell Repository should be used [Default is PSGallery]
 
        .PARAMETER FileMode
        [switch]FileMode uses the File System to remove the Modules
 
        .EXAMPLE
        Uninstall-M365Modules
 
        .EXAMPLE
        Uninstall-M365Modules -Modules "Az","MSOnline","PnP.PowerShell","Microsoft.Graph" -Scope CurrentUser
 
        .LINK
        https://github.com/fabrisodotps1/M365PSProfile
    #>


    param (
        [Parameter(Mandatory = $false)][array]$Modules = $global:M365StandardModules,
        [parameter(mandatory = $false)][ValidateSet("CurrentUser", "AllUsers")][string]$Scope = "CurrentUser",
        [parameter(mandatory = $false)][switch]$FileMode = $false
    )

    If ([System.Runtime.InteropServices.RuntimeInformation]::IsOSPlatform([System.Runtime.InteropServices.OSPlatform]::Windows))
    {
        #Windows
        $currentPrincipal = New-Object Security.Principal.WindowsPrincipal([Security.Principal.WindowsIdentity]::GetCurrent()) -ErrorAction SilentlyContinue
        $IsAdmin = $currentPrincipal.IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)
    }

    foreach ($Module in $Modules) {
        [Array]$InstalledModules = Get-InstalledPSResource -Name $Module -Scope $Scope -ErrorAction SilentlyContinue | Sort-Object Version -Descending

        if ($InstalledModules) {
            # Module found
            if (($IsAdmin -eq $false) -and ($Scope -eq "AllUsers")) {
                Write-Host "WARNING: PS must be running <As Administrator> to uninstall the Module" -ForegroundColor Red
            } else {
                # Uninstall all versions of the module
                Write-Host "Uninstall Module: $Module $($InstalledModules.Version.ToString())" -ForegroundColor Yellow
                Uninstall-PSResource -Name $Module -Scope $Scope -SkipDependencyCheck -WarningAction SilentlyContinue
            }

            #If AZ also Uninstall all AZ.* Modules
            If ($Module -eq "AZ")
            {
                Write-Host "Uninstall AZ.* Modules" -ForegroundColor Yellow
                #Get-InstalledPSResource -Name "AZ.*" -Scope $Scope -ErrorAction SilentlyContinue | Uninstall-PSResource -Scope $Scope -SkipDependencyCheck
                $InstalledAZModules = Get-InstalledPSResource -Name "AZ.*" -Scope $Scope -ErrorAction SilentlyContinue
                Foreach ($AZModule in $InstalledAZModules)
                {
                    Write-Host "Uninstall Module: $($AZModule.Name) $($AZModule.Version.ToString())" -ForegroundColor Yellow
                    Uninstall-PSResource -Name $AZModule.Name -Scope $Scope -SkipDependencyCheck -WarningAction SilentlyContinue

                    #FileMode
                    If ($FileMode -eq $true)
                    {
                        Write-Host "Using FileMode. Remove all AZ.* Modules" -ForegroundColor Yellow
                        $ModulesPath = Get-M365ModulePath -Scope $Scope
                        Get-ChildItem -Path $ModulesPath -Filter "AZ.*" -Recurse | Remove-Item -Force -Recurse
                    }
                }
            }

            #If Microsoft.Graph also Uninstall all Microsoft.Graph.* Modules
            If ($Module -eq "Microsoft.Graph")
            {
                Write-Host "Uninstall Microsoft.Graph.* Modules" -ForegroundColor Yellow
                Get-InstalledPSResource -Name "Microsoft.Graph.*" -Scope $Scope -ErrorAction SilentlyContinue | Where-Object {$_.Name -notmatch "Microsoft.Graph.Beta"} | Uninstall-PSResource -Scope $Scope -SkipDependencyCheck

                #FileMode
                If ($FileMode -eq $true)
                {
                    Write-Host "Using FileMode. Remove all Microsoft.Graph.* Modules" -ForegroundColor Yellow
                    $ModulesPath = Get-M365ModulePath -Scope $Scope
                    Get-ChildItem -Path $ModulesPath -Filter "Microsoft.Graph.*" -Recurse | Remove-Item -Force -Recurse
                }
            }

            #If Microsoft.Graph.Beta also Uninstall all Microsoft.Graph.Beta.* Modules
            If ($Module -eq "Microsoft.Graph.Beta")
            {
                Write-Host "Uninstall Microsoft.Graph.Beta.* Modules" -ForegroundColor Yellow
                Get-InstalledPSResource -Name "Microsoft.Graph.Beta*" -Scope $Scope -ErrorAction SilentlyContinue | Uninstall-PSResource -Scope $Scope -SkipDependencyCheck

                #FileMode
                If ($FileMode -eq $true)
                {
                    Write-Host "Using FileMode. Remove all Microsoft.Graph.Beta* Modules" -ForegroundColor Yellow
                    $ModulesPath = Get-M365ModulePath -Scope $Scope
                    Get-ChildItem -Path $ModulesPath -Filter "Microsoft.Graph.Beta*" -Recurse | Remove-Item -Force -Recurse
                }
            }
        } else {
            #Module Notfound
            If ($FileMode -eq $true)
            {
                Write-Host "Using FileMode. Remove all $Module Modules" -ForegroundColor Yellow
                $ModulesPath = Get-M365ModulePath -Scope $Scope
                Get-ChildItem -Path $ModulesPath -Filter "$Module" -Recurse | Remove-Item -Force -Recurse
            }

            #If AZ also Uninstall all AZ.* Modules
            If ($Module -eq "AZ")
            {
                Write-Host "NO AZ Root Module. Uninstall AZ.* Modules" -ForegroundColor Yellow
                Get-InstalledPSResource -Name "AZ.*" -Scope $Scope -ErrorAction SilentlyContinue | Uninstall-PSResource -Scope $Scope -SkipDependencyCheck

                #FileMode
                If ($FileMode -eq $true)
                {
                    Write-Host "Using FileMode. Remove all AZ.* Modules" -ForegroundColor Yellow
                    $ModulesPath = Get-M365ModulePath -Scope $Scope
                    Get-ChildItem -Path $ModulesPath -Filter "AZ.*" -Recurse | Remove-Item -Force -Recurse
                }
            }

            #If Microsoft.Graph also Uninstall all Microsoft.Graph.* Modules
            If ($Module -eq "Microsoft.Graph")
            {
                Write-Host "NO Microsoft.Graph Root Module. Uninstall Microsoft.Graph.* Modules" -ForegroundColor Yellow
                Get-InstalledPSResource -Name "Microsoft.Graph.*" -Scope $Scope -ErrorAction SilentlyContinue | Where-Object {$_.Name -notmatch "Microsoft.Graph.Beta"} | Uninstall-PSResource -Scope $Scope -SkipDependencyCheck #-ErrorAction SilentlyContinue

                #FileMode
                If ($FileMode -eq $true)
                {
                    Write-Host "Using FileMode. Remove all Microsoft.Graph.* Modules" -ForegroundColor Yellow
                    $ModulesPath = Get-M365ModulePath -Scope $Scope
                    Get-ChildItem -Path $ModulesPath -Filter "Microsoft.Graph.*" -Recurse | Remove-Item -Force -Recurse
                }
            }

            #If Microsoft.Graph.Beta also Uninstall all Microsoft.Graph.Beta.* Modules
            If ($Module -eq "Microsoft.Graph.Beta")
            {
                Write-Host "NO Microsoft.Graph.Beta Module. Uninstall Microsoft.Graph.Beta.* Modules" -ForegroundColor Yellow
                Get-InstalledPSResource -Name "Microsoft.Graph.Beta*" -Scope $Scope -ErrorAction SilentlyContinue | Uninstall-PSResource -Scope $Scope -SkipDependencyCheck #-ErrorAction SilentlyContinue

                #FileMode
                If ($FileMode -eq $true)
                {
                    Write-Host "Using FileMode. Remove all Microsoft.Graph.Beta* Modules" -ForegroundColor Yellow
                    $ModulesPath = Get-M365ModulePath -Scope $Scope
                    Get-ChildItem -Path $ModulesPath -Filter "Microsoft.Graph.Beta*" -Recurse | Remove-Item -Force -Recurse
                }
            }
        }
    }
}

##############################################################################
# Remove existing PS Connections
##############################################################################
Function Disconnect-All {
    <#
        .SYNOPSIS
        Disconnect all Connections to Microsoft 365 Services
 
        .DESCRIPTION
        Disconnect all Connections of the Modules MicrosoftTeams, ExchangeOnlineManagement, Microsoft.Online.SharePoint.PowerShell, Microsoft.Graph and removes remote PS Sessions
 
        .EXAMPLE
        Disconnect-All
 
        .LINK
        https://github.com/fabrisodotps1/M365PSProfile
    #>


    Get-PSSession | Remove-PSSession

    <#
    try {
        Disconnect-SPOService -ErrorAction SilentlyContinue
    } catch {
        #Write-Host "Disconnect-SPOService failed" -ForegroundColor Yellow
    }
    #>


    If (Get-Module -Name "Microsoft.Online.SharePoint.PowerShell")
    {
        Disconnect-SPOService -ErrorAction SilentlyContinue
    }

    Disconnect-MicrosoftTeams -ErrorAction SilentlyContinue
    If (Get-Module -Name "ExchangeOnlineManagement")
    {
        Disconnect-ExchangeOnline -confirm:$false -ErrorAction SilentlyContinue
    }

    Disconnect-MgGraph -ErrorAction SilentlyContinue | Out-Null
    If (Get-Module -Name "PnP.PowerShell")
    {
        Disconnect-PnPOnline -ErrorAction SilentlyContinue
    }

    Disconnect-MgGraph -ErrorAction SilentlyContinue | Out-Null
}

#############################################################################
# Set-WindowTitle Function
#############################################################################
Function Set-WindowTitle {
    <#
        .SYNOPSIS
        Set the Window Title
 
        .DESCRIPTION
        Set the Window Title
 
        .EXAMPLE
        Set-WindowTitle
 
        .EXAMPLE
        Set-WindowTitle -Title "My Title"
 
        .LINK
        https://github.com/fabrisodotps1/M365PSProfile
    #>


    [CmdletBinding(SupportsShouldProcess)]
    PARAM (
        [string]$Title = "Windows PowerShell"
    )
    If ($PSCmdlet.ShouldProcess($Title))
    {
        $host.ui.RawUI.WindowTitle = $Title
    }
}

##############################################################################
# Main Program
##############################################################################

Function Install-M365Module {
    <#
        .SYNOPSIS
        M365PSProfile installs and keeps the PowerShell Modules needed for Microsoft 365 Management up to date.
        It provides a simple way to add it to the PowerShell Profile
 
        .DESCRIPTION
        M365PSProfile installs and keeps the PowerShell Modules needed for Microsoft 365 Management up to date.
        It provides a simple way to add it to the PowerShell Profile
 
        .PARAMETER Modules
        [array]$Modules = @(<ModuleName1>,<Modulename2>)
        [array]$Modules = @("AZ", "MSOnline", "AzureADPreview", "ExchangeOnlineManagement", "Icewolf.EXO.SpamAnalyze", "MicrosoftTeams", "Microsoft.Online.SharePoint.PowerShell", "PnP.PowerShell" , "ORCA", "O365CentralizedAddInDeployment", "MSCommerce", "WhiteboardAdmin", "Microsoft.Graph", "Microsoft.Graph.Beta", "MSAL.PS", "MSIdentityTools" )
 
        .PARAMETER Scope
        Sets the Scope [CurrentUser/AllUsers] for the Installation of the PowerShell Modules
 
        .PARAMETER AsciiArt
        [bool]AsciiArt controls the AsciiArt Screen at the Start
 
        .PARAMETER $RunInVSCode
        [bool]$RunInVSCode controls if the Script will run in VSCode [Default is $false]
 
        .PARAMETER Repository
        [string]Repository specifies which PowerShell Repository should be used [Default is PSGallery]
 
        .LINK
        https://github.com/fabrisodotps1/M365PSProfile
 
        .EXAMPLE
        #Installs and updates the Default Modules in CurrentUser Scope
        Install-M365Modules
 
        .EXAMPLE
        #Installs and updates the specified Modules
        Install-M365Modules -Modules @("ExchangeOnlineManagement", "MicrosoftTeams", "Microsoft.Online.SharePoint.PowerShell", "PnP.PowerShell") -Scope [CurrentUser|AllUsers]
 
        .EXAMPLE
        #Installs and updates the specified Modules without showing AsciiArt at the Start
        Install-M365Modules -Modules @("ExchangeOnlineManagement", "MicrosoftTeams", "Microsoft.Online.SharePoint.PowerShell", "PnP.PowerShell") -Scope [CurrentUser|AllUsers] -AsciiArt $False
 
        .EXAMPLE
        #Installs and updates the Default Modules in CurrentUser Scope and use a custom repository called "MyRepo"
        Install-M365Modules -Repository "MyRepo"
    #>


    #Parameter for the Module
    param(
        [parameter(mandatory = $false)][array]$Modules = $global:M365StandardModules,
        [parameter(mandatory = $false)][ValidateSet("CurrentUser", "AllUsers")][string]$Scope = "CurrentUser",
        [parameter(mandatory = $false)][bool]$AsciiArt = $true,
        [parameter(mandatory = $false)][bool]$RunInVSCode = $false,
        [parameter(mandatory = $false)][string]$Repository = "PSGallery"
    )

    #Check if it is running in VSCode
    if ($env:TERM_PROGRAM -eq 'vscode') {
        If ($RunInVSCode -eq $false) {
            Exit
        }
    }

    If ($AsciiArt -eq $true) {
        #Show AsciArt
        Invoke-AsciiArt
    }

    If ([System.Runtime.InteropServices.RuntimeInformation]::IsOSPlatform([System.Runtime.InteropServices.OSPlatform]::Windows))
    {
        #Windows
        $currentPrincipal = New-Object Security.Principal.WindowsPrincipal([Security.Principal.WindowsIdentity]::GetCurrent()) -ErrorAction SilentlyContinue
        $IsAdmin = $currentPrincipal.IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)
    }

    Import-Module  Microsoft.PowerShell.PSResourceGet

    if($Repository -eq "PSGallery") {
        $PSGallery = Get-PSResourceRepository -Name PSGallery
        If ($PSGallery.Trusted -eq $false) {
            Write-Host "Warning: PSGallery is not Trusted" -ForegroundColor Yellow
        }
    }

    #Check if VSCode or PowerShell is running
    [array]$process = Get-Process | Where-Object { $_.ProcessName -eq "powershell" -or $_.ProcessName -eq "pwsh" -or $_.ProcessName -eq "code" }

    If ($process.count -gt 1) {
        Write-Host "PowerShell or Visual Studio Code running? Please close it, Modules in use can't be updated..." -ForegroundColor Yellow
        $process

        #count back from 5 to 1 and start the update
        5..1 | ForEach-Object {
        Write-Host "M365PSProfile Update starts in $_ seconds... (Hit Ctrl+C to cancel)" -ForegroundColor Yellow
        Start-Sleep -Seconds 1
        }
    }

    Write-Host "Checking Modules..."
    #Check Microsoft.PowerShell.PSResourceGet
    #Can't uninstall loaded DLL's so you have to uninstall next time you start PowerShell
    #[System.AppDomain]::CurrentDomain.GetAssemblies() | where {$_.Location -match "Microsoft.PowerShell.PSResourceGet"}

    $Module = "Microsoft.PowerShell.PSResourceGet"
    [Array]$InstalledModules = Get-InstalledPSResource -Name $Module -Scope $Scope -ErrorAction SilentlyContinue | Sort-Object Version -Descending

    Write-Host "Checking Module: $Module $($InstalledModules[0].Version.ToString())" -ForegroundColor Green

    If ($InstalledModules.Count -gt 1)
    {
        $Version = $InstalledModules[$InstalledModules.Count - 1].Version
        Write-Host "Uninstall Module $Module $Version" -ForegroundColor Yellow
        Uninstall-PSResource -Name $Module -Scope $Scope -Version $Version -SkipDependencyCheck
    } else {
        #Only one Version found
        [System.Version]$InstalledModuleVersion = $($InstalledModules.Version.ToString())

        #Get Module from PowerShell Gallery (or another repository if specified)
        $PSGalleryModule = Find-PSResource -Name $Module -Repository $Repository
        $PSGalleryVersion = $PSGalleryModule.Version.ToString()

        #Version Check
        If ($PSGalleryVersion -gt $InstalledModuleVersion)
        {
            Write-Host "Install newest Module $Module $PSGalleryVersion" -ForegroundColor Yellow
            Install-PSResource $Module -Scope $Scope -TrustRepository -WarningAction SilentlyContinue -Repository $Repository
        }
    }

    Foreach ($Module in $Modules) {
        #Get Array of installed Modules
        [Array]$InstalledModules = Get-InstalledPSResource -Name $Module -Scope $Scope -ErrorAction SilentlyContinue | Sort-Object Version -Descending

        If ($Null -eq $InstalledModules) {
            #Module not found
            Write-Host "$Module Module not found. Try to install..."
            If ($IsAdmin -eq $false -and $Scope -eq "AllUsers") {
                Write-Host "WARNING: PS must be running <As Administrator> to install the Module" -ForegroundColor Red
            } else {
                #Get Module from PowerShell Gallery
                $PSGalleryModule = Find-PSResource -Name $Module -Repository $Repository #-Prerelease
                $PSGalleryVersion = $PSGalleryModule.Version.ToString()
                Write-Host "Install newest Module $Module $PSGalleryVersion" -ForegroundColor Yellow

                Install-PSResource $Module -Scope $Scope -TrustRepository -WarningAction SilentlyContinue -Repository $Repository #-Prerelease
            }
        } else {
            #Module found

            #Get Module from PowerShell Gallery (or another repository if specified)
            $PSGalleryModule = Find-PSResource -Name $Module -Repository $Repository #-Prerelease
            [System.Version]$PSGalleryVersion = $PSGalleryModule.Version.ToString()

            #Check if Multiple Modules are installed
            If (($InstalledModules.count) -gt 1) {

                Write-Host "WARNING: $Module > Multiple Versions found. Uninstall old Versions? (Default is Yes)" -ForegroundColor Yellow
                $Readhost = Read-Host " ( y / n ) "
                Switch ($ReadHost) {
                    Y {
                        #Uninstall all Modules
                        Write-Host "Uninstall Module"
                        Uninstall-PSResource -Name $Module -Scope $Scope -SkipDependencyCheck

                        #Install newest Module
                        Write-Host "Install newest Module $Module $PSGalleryVersion" -ForegroundColor Yellow
                        Install-PSResource -Name $Module -Scope $Scope -TrustRepository -WarningAction SilentlyContinue -Repository $Repository #-Prerelease
                    }
                    N {
                        Write-Host "Skip Uninstall old Modules"
                    }
                    Default {
                        #Uninstall all Modules
                        Write-Host "Uninstall Module"
                        Uninstall-PSResource -Name $Module -Scope $Scope -SkipDependencyCheck

                        #Install newest Module
                        Write-Host "Install newest Module $Module $PSGalleryVersion" -ForegroundColor Yellow
                        Install-PSResource -Name $Module -Scope $Scope -TrustRepository -WarningAction SilentlyContinue -Repository $Repository #-Prerelease
                    }
                }
            } else {
                #Only one Module found
                [System.Version]$InstalledModuleVersion = $($InstalledModules.Version.ToString())

                #Version Check
                If ($PSGalleryVersion -gt $InstalledModuleVersion) {
                    #Uninstall Module
                    Write-Host "Uninstall Module: $Module $($InstalledModules.Version.ToString())" -ForegroundColor Yellow
                    Uninstall-PSResource -Name $Module -Scope $Scope -SkipDependencyCheck

                    #If AZ also Uninstall all AZ.* Modules
                    If ($Module -eq "AZ")
                    {
                        Write-Host "Uninstall AZ.* Modules" -ForegroundColor Yellow
                        #Uninstall-PSResource AZ.* -Scope $Scope -SkipDependencyCheck
                        Get-InstalledPSResource -Name "AZ.*" -Scope $Scope | Uninstall-PSResource -SkipDependencyCheck
                    }

                    #If Microsoft.Graph also Uninstall all Microsoft.Graph.* Modules
                    If ($Module -eq "Microsoft.Graph")
                    {
                        Write-Host "Uninstall Microsoft.Graph.* Modules" -ForegroundColor Yellow
                        Get-InstalledPSResource -Name "Microsoft.Graph.*" -Scope $Scope | Where-Object {$_.Name -notmatch "Microsoft.Graph.Beta"} | Uninstall-PSResource -SkipDependencyCheck
                    }

                    #If Microsoft.Graph.Beta also Uninstall all Microsoft.Graph.Beta.* Modules
                    If ($Module -eq "Microsoft.Graph.Beta")
                    {
                        Write-Host "Uninstall Microsoft.Graph.Beta.* Modules" -ForegroundColor Yellow
                        Get-InstalledPSResource -Name "Microsoft.Graph.Beta*" -Scope $Scope | Uninstall-PSResource -SkipDependencyCheck
                    }

                    #Install Module
                    Write-Host "Install Module: $Module $PSGalleryVersion" -ForegroundColor Yellow
                    Install-PSResource -Name $Module -Scope $Scope -TrustRepository -WarningAction SilentlyContinue -Repository $Repository #-Prerelease
                } else {
                    #Write Module Name
                    Write-Host "Checking Module: $Module $($InstalledModules.Version.ToString())" -ForegroundColor Green
                }
            }
        }
    }
}

##############################################################################
# Import Module
##############################################################################
If (-not(Test-Path -Path $Profile))
{
    Write-Host "No PowerShell Profile exists. You can add the M365PSProfile Update check with Add-M365PSProfile" -ForegroundColor Yellow
} else {
    $Content = Get-Content -Path $Profile -Encoding utf8
    If ($Content -match "Install-M365Module")
    {
        #Match found
    } else {
        #No Match found
        Write-Host  "You have a PowerShell Profile. You can add the M365PSProfile Update check with Add-M365PSProfile" -ForegroundColor Yellow
    }
}