Public/Connect-NLBaselineCA.ps1

function Connect-NLBaselineCA {
    <#
    .SYNOPSIS
    Connect to Microsoft 365 Graph API using App Registration
     
    .DESCRIPTION
    Authenticates with Microsoft 365 Graph API using App Registration credentials
     
    .EXAMPLE
    Connect-NLBaselineCA
    #>

    
    [CmdletBinding()]
    param()
    
    try {
        Write-Host "Connecting to Microsoft 365 using App Registration..." -ForegroundColor Yellow
        Write-Host ""
        
        # Get storage path from module config
        $moduleConfigPath = Get-ConfigPath
        if (-not (Test-Path $moduleConfigPath)) {
            Write-Error "Module configuration not found. Run Quick Start first."
            return $null
        }
        
        $moduleConfig = Get-Content $moduleConfigPath | ConvertFrom-Json
        $storagePath = $moduleConfig.StoragePath
        
        if (-not (Test-Path $storagePath)) {
            Write-Error "Storage path not found: $storagePath. Run Quick Start to configure."
            return $null
        }
        
        # Get config.json from storage path
        $configPath = Join-Path $storagePath "config.json"
        if (-not (Test-Path $configPath)) {
            Write-Error "Configuration file not found: $configPath. Run Quick Start to configure."
            return $null
        }
        
        $config = Get-Content $configPath | ConvertFrom-Json
        
        # Validate required fields
        if ([string]::IsNullOrWhiteSpace($config.TenantId)) {
            Write-Error "Tenant ID not configured. Run Quick Start to configure."
            return $null
        }
        
        if ([string]::IsNullOrWhiteSpace($config.ClientId)) {
            Write-Error "Client ID not configured. Run Quick Start to configure."
            return $null
        }
        
        if ([string]::IsNullOrWhiteSpace($config.ClientSecret)) {
            Write-Error "Client Secret not configured. Run Quick Start to configure."
            return $null
        }
        
        # Check if cmdlets are already available - this is the key!
        $hasConnectMgGraph = Get-Command Connect-MgGraph -ErrorAction SilentlyContinue
        $hasGetMgPolicy = Get-Command Get-MgIdentityConditionalAccessPolicy -ErrorAction SilentlyContinue
        
        if ($hasConnectMgGraph -and $hasGetMgPolicy) {
            Write-Host "Microsoft Graph cmdlets already available" -ForegroundColor Gray
        }
        else {
            Write-Host "Loading Microsoft Graph modules..." -ForegroundColor Yellow
            
            # Ensure modules are installed
            $requiredModules = @('Microsoft.Graph.Authentication', 'Microsoft.Graph.Identity.SignIns')
            foreach ($module in $requiredModules) {
                if (-not (Get-Module -ListAvailable -Name $module)) {
                    Write-Host "Installing $module..." -ForegroundColor Yellow
                    Install-Module -Name $module -Scope CurrentUser -Force -AllowClobber -ErrorAction Stop
                }
            }
            
            # Try importing - if it fails due to assembly conflict, check if cmdlets work anyway
            if (-not $hasConnectMgGraph) {
                try {
                    Import-Module Microsoft.Graph.Authentication -Force -ErrorAction Stop
                    $hasConnectMgGraph = Get-Command Connect-MgGraph -ErrorAction SilentlyContinue
                }
                catch {
                    # Assembly conflict - but check if cmdlet works anyway
                    Start-Sleep -Milliseconds 200
                    $hasConnectMgGraph = Get-Command Connect-MgGraph -ErrorAction SilentlyContinue
                    if ($hasConnectMgGraph) {
                        Write-Host "Using existing Microsoft.Graph.Authentication (assembly conflict resolved)" -ForegroundColor Gray
                    }
                    else {
                        Write-Warning "Could not import Microsoft.Graph.Authentication: $_"
                        Write-Host "Attempting to use cmdlets from loaded assemblies..." -ForegroundColor Yellow
                        # Try to find and use the cmdlet from loaded assemblies
                        $allCommands = Get-Command -ErrorAction SilentlyContinue | Where-Object { $_.Name -eq 'Connect-MgGraph' }
                        if ($allCommands) {
                            $hasConnectMgGraph = $allCommands[0]
                            Write-Host "Found Connect-MgGraph in loaded assemblies" -ForegroundColor Gray
                        }
                    }
                }
            }
            
            if (-not $hasGetMgPolicy) {
                try {
                    Import-Module Microsoft.Graph.Identity.SignIns -Force -ErrorAction Stop
                    $hasGetMgPolicy = Get-Command Get-MgIdentityConditionalAccessPolicy -ErrorAction SilentlyContinue
                }
                catch {
                    # Assembly conflict - but check if cmdlet works anyway
                    Start-Sleep -Milliseconds 200
                    $hasGetMgPolicy = Get-Command Get-MgIdentityConditionalAccessPolicy -ErrorAction SilentlyContinue
                    if ($hasGetMgPolicy) {
                        Write-Host "Using existing Microsoft.Graph.Identity.SignIns (assembly conflict resolved)" -ForegroundColor Gray
                    }
                    else {
                        Write-Warning "Could not import Microsoft.Graph.Identity.SignIns: $_"
                        Write-Host "Attempting to use cmdlets from loaded assemblies..." -ForegroundColor Yellow
                        # Try to find and use the cmdlet from loaded assemblies
                        $allCommands = Get-Command -ErrorAction SilentlyContinue | Where-Object { $_.Name -eq 'Get-MgIdentityConditionalAccessPolicy' }
                        if ($allCommands) {
                            $hasGetMgPolicy = $allCommands[0]
                            Write-Host "Found Get-MgIdentityConditionalAccessPolicy in loaded assemblies" -ForegroundColor Gray
                        }
                    }
                }
            }
            
            # Final check - if still not available, we need to fail gracefully
            if (-not $hasConnectMgGraph) {
                Write-Error "Connect-MgGraph cmdlet is not available."
                Write-Host "Please restart PowerShell session to resolve assembly conflicts." -ForegroundColor Yellow
                return $null
            }
            
            if (-not $hasGetMgPolicy) {
                Write-Error "Get-MgIdentityConditionalAccessPolicy cmdlet is not available."
                Write-Host "Please restart PowerShell session to resolve assembly conflicts." -ForegroundColor Yellow
                return $null
            }
        }
        
        # Connect using App Registration
        Write-Host "Connecting to tenant: $($config.TenantId)" -ForegroundColor Gray
        Write-Host "Using App Registration: $($config.ClientId)" -ForegroundColor Gray
        Write-Host ""
        
        # Connect using client credentials
        # Connect-MgGraph requires ClientSecretCredential (PSCredential object)
        $secureSecret = ConvertTo-SecureString $config.ClientSecret -AsPlainText -Force
        $clientSecretCredential = New-Object System.Management.Automation.PSCredential($config.ClientId, $secureSecret)
        
        try {
            # Connect-MgGraph doesn't return a value, it sets the context
            Connect-MgGraph `
                -TenantId $config.TenantId `
                -ClientSecretCredential $clientSecretCredential `
                -NoWelcome `
                -ErrorAction Stop
            
            # Verify connection by checking context
            Start-Sleep -Milliseconds 500
            $context = Get-MgContext -ErrorAction Stop
            
            if ($context -and $context.TenantId) {
                Write-Host ""
                Write-Host "========================================" -ForegroundColor Green
                Write-Host " Successfully Connected!" -ForegroundColor Green
                Write-Host "========================================" -ForegroundColor Green
                Write-Host "Tenant: $($context.TenantId)" -ForegroundColor White
                Write-Host "App ID: $($config.ClientId)" -ForegroundColor White
                if ($context.Scopes) {
                    Write-Host "Scopes: $($context.Scopes -join ', ')" -ForegroundColor White
                }
                Write-Host "========================================" -ForegroundColor Green
                Write-Host ""
                
                $script:ModuleConfig.GraphConnection = $context
                return $context
            }
            else {
                throw "Connection established but context is invalid"
            }
        }
        catch {
            throw "Failed to connect: $_"
        }
    }
    catch {
        Write-Error "Error connecting to Microsoft 365: $_"
        Write-Host ""
        Write-Host "Troubleshooting:" -ForegroundColor Yellow
        Write-Host "1. Verify your App Registration has the required API permissions:" -ForegroundColor Gray
        Write-Host " - Policy.Read.All" -ForegroundColor Gray
        Write-Host " - Policy.ReadWrite.ConditionalAccess" -ForegroundColor Gray
        Write-Host " - Directory.Read.All" -ForegroundColor Gray
        Write-Host "2. Make sure admin consent is granted for the permissions" -ForegroundColor Gray
        Write-Host "3. Verify the Client Secret is correct and not expired" -ForegroundColor Gray
        Write-Host "4. Check your Tenant ID and Client ID are correct" -ForegroundColor Gray
        Write-Host "5. If assembly conflicts persist, restart PowerShell session" -ForegroundColor Gray
        return $null
    }
}