Public/Connect-MgGraphCustom.ps1

<#
.Synopsis
Created on: 28/12/2024
Created by: Ben Whitmore
Filename: Connect-MgGraphCustom.ps1
 
.Description
Function to connect to Microsoft Graph using various authentication methods
 
.PARAMETER LogID
The component (script name) passed as LogID to the 'Write-Log' function
 
.PARAMETER ModuleName
Module Name to use to connect to Graph. Default is Microsoft.Graph.Authentication
 
.PARAMETER PackageProvider
Package Provider. If not specified, the default value NuGet is used
 
.PARAMETER ModuleScope
Module Scope. If not specified, the default value is used for CurrentUser
 
.PARAMETER TenantId
Tenant Id or name to connect to. This parameter is mandatory for obtaining a connection
 
.PARAMETER ClientId
Client Id (App Registration) to connect to. This parameter is mandatory for obtaining a connection
 
.PARAMETER ClientSecret
Client Secret for authentication
 
.PARAMETER ClientCertificateThumbprint
Client certificate thumbprint for authentication
 
.PARAMETER RequiredScopes
The scopes to request from the Microsoft Graph API. If not specified, the default value is used for .default
 
.PARAMETER UseDeviceAuthentication
This parameter will be used to determine if the device authentication flow should be used. If not specified, the default value is used for $false
 
.PARAMETER Interactive
This parameter will be used to determine if the interactive flow should be used. If not specified, the default value is used for $false
 
.EXAMPLE
Delegated Flow Example:
Connect-MgGraphCustom -TenantId 'contoso.onmicrosoft.com' -ClientId '00000000-0000-0000-0000-000000000000'
 
.EXAMPLE
Client Secret Flow Example:
Connect-MgGraphCustom -TenantId 'contoso.onmicrosoft.com' -ClientId '00000000-0000-0000-0000-000000000000' -ClientSecret 'clientsecret'
 
.EXAMPLE
Client Certificate Flow Example:
Connect-MgGraphCustom -TenantId 'contoso.onmicrosoft.com' -ClientId '00000000-0000-0000-0000-000000000000' -ClientCertificateThumbprint '00000000000000000000000000000000'
 
.EXAMPLE
Device Authentication Flow Example:
Connect-MgGraphCustom -TenantId 'contoso.onmicrosoft.com' -ClientId '00000000-0000-0000-0000-000000000000' -UseDeviceAuthentication
#>


function Connect-MgGraphCustom {
    [CmdletBinding(DefaultParameterSetName = 'Interactive')]
    param (
        [Parameter(Mandatory = $false, ValueFromPipeline = $false, Position = 0, HelpMessage = 'The component (script name) passed as LogID to the "Write-Log" function')]
        [string]$LogId = $($MyInvocation.MyCommand).Name,

        [Parameter(Mandatory = $false, ValueFromPipeline = $false, Position = 1, HelpMessage = 'Module Name to connect to Graph. Default is Microsoft.Graph.Authentication')]
        [object]$ModuleNames = ('Microsoft.Graph.Authentication'),

        [Parameter(Mandatory = $false, ValueFromPipeline = $false, Position = 2, HelpMessage = 'If not specified, the default value NuGet is used for PackageProvider')]
        [string]$PackageProvider = 'NuGet',

        [Parameter(Mandatory = $true, ParameterSetName = 'ClientSecret', Position = 3, HelpMessage = 'Tenant Id or name to connect to')]
        [Parameter(Mandatory = $true, ParameterSetName = 'ClientCertificateThumbprint', Position = 3, HelpMessage = 'Tenant Id or name to connect to')]
        [Parameter(Mandatory = $true, ParameterSetName = 'UseDeviceAuthentication', Position = 3, HelpMessage = 'Tenant Id or name to connect to')]
        [Parameter(Mandatory = $true, ParameterSetName = 'Interactive', Position = 3, HelpMessage = 'Tenant Id or name to connect to')]
        [string]$TenantId,

        [Parameter(Mandatory = $true, ParameterSetName = 'ClientSecret', Position = 4, HelpMessage = 'Client Id (App Registration) to connect to')]
        [Parameter(Mandatory = $true, ParameterSetName = 'ClientCertificateThumbprint', Position = 4, HelpMessage = 'Client Id (App Registration) to connect to')]
        [Parameter(Mandatory = $true, ParameterSetName = 'UseDeviceAuthentication', Position = 4, HelpMessage = 'Client Id (App Registration) to connect to')]
        [Parameter(Mandatory = $true, ParameterSetName = 'Interactive', Position = 4, HelpMessage = 'Client Id (App Registration) to connect to')]
        [string]$ClientId,

        [Parameter(Mandatory = $true, ParameterSetName = 'ClientSecret', Position = 5, HelpMessage = 'Client secret for authentication')]
        [string]$ClientSecret,

        [Parameter(Mandatory = $true, ParameterSetName = 'ClientCertificateThumbprint', Position = 5, HelpMessage = 'Client certificate thumbprint for authentication')]
        [string]$ClientCertificateThumbprint,

        [Parameter(Mandatory = $true, ParameterSetName = 'UseDeviceAuthentication', Position = 5, HelpMessage = 'Use device authentication for Microsoft Graph API')]
        [switch]$UseDeviceAuthentication,

        [Parameter(Mandatory = $false, Position = 6, HelpMessage = 'The scopes required for Microsoft Graph API access. Default is DeviceManagementApps.ReadWrite.All')]
        [string[]]$RequiredScopes = ('DeviceManagementApps.ReadWrite.All'),

        [Parameter(Mandatory = $false, ValueFromPipeline = $false, Position = 7, HelpMessage = 'Specifies the scope for installing the module. Default is CurrentUser')]
        [string]$ModuleScope = 'CurrentUser'
    )

    begin {

        Write-LogAndHost -Message 'Function: Connect-MgGraphCustom was called' -LogId $LogId -ForegroundColor Cyan
        Write-LogAndHost -Message "Resolved Parameter Set: $($PSCmdlet.ParameterSetName)" -LogId $LogId -ForegroundColor Cyan

        Initialize-Module -Modules $ModuleNames
    }

    process {

        # First check if we already have a valid connection with required scopes
        if (Test-MgConnection -RequiredScopes $RequiredScopes -TestScopes) {
            Write-LogAndHost -Message "Using existing Microsoft Graph connection" -LogId $LogId -ForegroundColor Green
            return
        }

        # If we don't have required scopes, set the default required scopes to create Win32 apps. This assumes the Connect-MgGraphCustom function is used outside of the New-Win32App function
        if (-not $RequiredScopes) {

            if (Test-Path variable:\global:scopes) {

                [string[]]$RequiredScopes = $global:scopes
                Write-LogAndHost -Message ("Required Scope defined already. Using existing required scopes: {0}" -f $RequiredScopes) -LogId $LogId -ForegroundColor Green
            }
            else {
                [string[]]$global:scopes = ('DeviceManagementApps.ReadWrite.All')
                [string[]]$RequiredScopes = $global:scopes
                Write-LogAndHost -Message ("Required Scope defined yet.Using default required scopes: {0}" -f $RequiredScopes) -LogId $LogId -ForegroundColor Green
            }
        }

        # Determine the authentication method based on provided parameters
        if ($PSCmdlet.ParameterSetName -eq 'ClientSecret') {
            $AuthenticationMethod = 'ClientSecret'
        }
        elseif ($PSCmdlet.ParameterSetName -eq 'ClientCertificateThumbprint') {
            $AuthenticationMethod = 'ClientCertificateThumbprint'
        }
        elseif ($PSCmdlet.ParameterSetName -eq 'UseDeviceAuthentication') {
            $AuthenticationMethod = 'UseDeviceAuthentication'
        }
        else {
            $AuthenticationMethod = 'Interactive'
        }

        # If we don't have a valid connection, proceed with connection based on parameters
        $connectMgParams = [ordered]@{
            TenantId = $TenantId
        }

        switch ($AuthenticationMethod) {
            'ClientSecret' {
                $secureClientSecret = ConvertTo-SecureString -String $ClientSecret -AsPlainText -Force
                $credential = New-Object System.Management.Automation.PSCredential -ArgumentList $ClientId, $secureClientSecret
                $connectMgParams['ClientSecretCredential'] = $credential
            }
            'ClientCertificateThumbprint' {
                $connectMgParams['ClientId'] = $ClientId
                $connectMgParams['CertificateThumbprint'] = $ClientCertificateThumbprint
            }
            'UseDeviceAuthentication' {
                $connectMgParams['ClientId'] = $ClientId
                $connectMgParams['UseDeviceCode'] = $true
                $connectMgParams['Scopes'] = $RequiredScopes
            }
            'Interactive' {
                $connectMgParams['ClientId'] = $ClientId
                $connectMgParams['Scopes'] = $RequiredScopes -join ' '
            }
            default {
                Write-LogAndHost -Message ("Unknown authentication method: {0}" -f $AuthenticationMethod) -LogId $LogId -Severity 3
                break
            }
        }

        # Convert the parameters to a string for logging
        $connectMgParamsString = 'Connect-MgGraph ' + ($connectMgParams.Keys | ForEach-Object { '-{0} {1}' -f $_, $connectMgParams.$_ }) -join ' '
        Write-LogAndHost -Message ("Connecting to Microsoft Graph with the following parameters: {0}" -f $connectMgParamsString) -LogId $LogId -ForegroundColor Cyan
            
        try {
            # Explicitly pass the parameters to Connect-MgGraph
            if ($AuthenticationMethod -eq 'ClientSecret') {
                Connect-MgGraph -TenantId $TenantId -ClientSecretCredential $connectMgParams['ClientSecretCredential'] -NoWelcome
            }
            elseif ($AuthenticationMethod -eq 'ClientCertificateThumbprint') {
                Connect-MgGraph -TenantId $TenantId -ClientId $ClientId -CertificateThumbprint $ClientCertificateThumbprint -NoWelcome
            }
            elseif ($AuthenticationMethod -eq 'UseDeviceAuthentication') {
                Connect-MgGraph -TenantId $TenantId -ClientId $ClientId -UseDeviceCode -Scopes $connectMgParams['Scopes'] -NoWelcome
            }
            else {
                Connect-MgGraph -TenantId $TenantId -ClientId $ClientId -Scopes $connectMgParams['Scopes'] -NoWelcome
            }
        }
        catch {
            Write-LogAndHost -Message ("Failed to connect to Microsoft Graph: {0}" -f $_.Exception.Message) -LogId $LogId -Severity 3
        }
         
        # Check if we have a valid connection with required scopes
        if (Test-MgConnection -LogId $LogId -RequiredScopes $RequiredScopes) {

            Write-LogAndHost -Message "Successfully connected to Microsoft Graph" -LogId $LogId -ForegroundColor Green
                
            # Get and display connection details
            $context = Get-MgContext
            if ($AuthenticationMethod -in @('ClientSecret', 'ClientCertificateThumbprint')) {
                Write-LogAndHost -Message ("Connected using Client Credential Flow with application: {0}" -f $context.AppName) -LogId $LogId -ForegroundColor Green
            }
            else {
                Write-LogAndHost -Message ("Connected using Delegated Flow as: {0}" -f $context.Account) -LogId $LogId -ForegroundColor Green
            }
            Write-LogAndHost -Message ("Scopes: {0}" -f ($context.Scopes -join ', ')) -LogId $LogId -ForegroundColor Green
        }
        else {
            Write-LogAndHost -Message "Failed to establish a valid connection with required scopes" -LogId $LogId -Severity 3
        }
    }
}