Private/Connect-Intune.ps1

<#
.SYNOPSIS
    Connects to Microsoft Graph/Intune
.DESCRIPTION
    Authenticates with Microsoft Graph using App Registration credentials
.PARAMETER Config
    Configuration object containing App Registration details
.OUTPUTS
    Boolean indicating success
#>

function Connect-Intune {
    [CmdletBinding()]
    param(
        [Parameter(Mandatory=$true)]
        [PSCustomObject]$Config
    )

    try {
        if (-not (Get-Module -ListAvailable -Name 'Microsoft.Graph.Authentication')) {
            throw "Microsoft.Graph.Authentication is required for Intune. Install: Install-Module -Name Microsoft.Graph.Authentication, Microsoft.Graph.DeviceManagement -Scope CurrentUser"
        }
        
        # Check if we're already connected and working
        $ctx = $null
        if (Get-Command -Name 'Get-MgContext' -ErrorAction SilentlyContinue) {
            $ctx = Get-MgContext -ErrorAction SilentlyContinue
        }
        
        # If already connected and context is valid, skip module loading
        if ($ctx -and $ctx.TenantId) {
            Write-Verbose "Already connected to Microsoft Graph (tenant: $($ctx.TenantId))"
            return $true
        }
        
        # Check if module is already loaded and working
        $loadedAuth = Get-Module -Name 'Microsoft.Graph.Authentication' -ErrorAction SilentlyContinue
        $moduleWorking = $false
        
        if ($loadedAuth) {
            # Test if the module is functional by checking for Connect-MgGraph command
            if (Get-Command -Name 'Connect-MgGraph' -ErrorAction SilentlyContinue) {
                $moduleWorking = $true
            }
        }
        
        # Only reload if module is not loaded or not working
        if (-not $moduleWorking) {
            # Disconnect from Graph if already connected to release resources
            if (Get-Command -Name 'Disconnect-MgGraph' -ErrorAction SilentlyContinue) {
                try {
                    $oldCtx = Get-MgContext -ErrorAction SilentlyContinue
                    if ($oldCtx) {
                        Disconnect-MgGraph -ErrorAction SilentlyContinue | Out-Null
                        Start-Sleep -Milliseconds 300
                    }
                }
                catch {
                    Write-Verbose "Disconnect-MgGraph failed (ignored): $_"
                }
            }
            
            # Remove all Graph modules to avoid assembly conflicts
            $graphModules = @('Microsoft.Graph.Authentication', 'Microsoft.Graph.DeviceManagement', 'Microsoft.Graph')
            foreach ($modName in $graphModules) {
                $mod = Get-Module -Name $modName -ErrorAction SilentlyContinue
                if ($mod) {
                    try {
                        Remove-Module -Name $modName -Force -ErrorAction SilentlyContinue
                    }
                    catch {
                        Write-Verbose "Could not remove module $modName : $_"
                    }
                }
            }
            
            # Wait longer to allow assembly unloading
            Start-Sleep -Milliseconds 500
            
            # Import with error handling for assembly conflicts
            try {
                Import-Module Microsoft.Graph.Authentication -Force -ErrorAction Stop -Scope Global
            }
            catch {
                # If import fails due to assembly conflict, try to use the already loaded version
                $errorMsg = $_.Exception.Message
                if ($errorMsg -like "*Assembly with same name is already loaded*" -or $errorMsg -like "*already loaded*") {
                    Write-Verbose "Microsoft.Graph.Authentication assembly already loaded, attempting to use existing version"
                    # Try to get the module again - it might be available despite the error
                    $loadedAuth = Get-Module -Name 'Microsoft.Graph.Authentication' -ErrorAction SilentlyContinue
                    if (-not $loadedAuth -or -not (Get-Command -Name 'Connect-MgGraph' -ErrorAction SilentlyContinue)) {
                        throw "Could not load Microsoft.Graph.Authentication. Assembly conflict detected. Please restart PowerShell session."
                    }
                }
                else {
                    throw
                }
            }
            
            if (Get-Module -ListAvailable -Name 'Microsoft.Graph.DeviceManagement') {
                Import-Module Microsoft.Graph.DeviceManagement -Force -ErrorAction SilentlyContinue -Scope Global
            }
        }

        # Get credentials from config
        $clientId = $Config.AppRegistration.ClientId
        $clientSecret = $Config.AppRegistration.ClientSecret
        $tenantId = $Config.AppRegistration.TenantId

        if ([string]::IsNullOrEmpty($clientId) -or [string]::IsNullOrEmpty($clientSecret) -or [string]::IsNullOrEmpty($tenantId)) {
            throw "App Registration credentials are incomplete. Please configure ClientId, ClientSecret, and TenantId in config.json"
        }

        # Create secure string for client secret (required for Connect-MgGraph; not persisted)
        $secureSecret = ConvertTo-SecureString -String $clientSecret -AsPlainText -Force
        $credential = New-Object System.Management.Automation.PSCredential($clientId, $secureSecret)

        Connect-MgGraph -TenantId $tenantId -ClientSecretCredential $credential -NoWelcome -ErrorAction Stop
        $ctx = Get-MgContext -ErrorAction SilentlyContinue
        if (-not $ctx -or -not $ctx.TenantId) {
            throw "Connect-MgGraph completed but no context. Check credentials and permissions."
        }
        Write-Verbose "Connected to Microsoft Graph (tenant: $($ctx.TenantId))"
        return $true
    }
    catch {
        Write-Error "Failed to connect to Intune: $_"
        return $false
    }
}