source/Private/GraphHelpers.ps1

function Invoke-RJMgGraphCommand {

    [CmdletBinding()]
    param(
        [Parameter(Mandatory = $true, Position = 0)]
        [ScriptBlock]$ScriptBlock
    )

    try {
        if (-not $PSDefaultParameterValues.ContainsKey("*Mg*:ErrorAction")) {
            $PSDefaultParameterValues.Add('*Mg*:ErrorAction', 'Stop')
        }
        if (-not $PSDefaultParameterValues.ContainsKey("*Mg*:ErrorVariable")) {
            $PSDefaultParameterValues.Add('*Mg*:ErrorVariable', 'RJMSGraphError')
        }
        & $ScriptBlock
    }
    catch {
        $errorInfo = @{
            StatusCode = $null
            StatusText = $null
            ErrorCode  = $null
            Message    = $null
        }

        $text = $RJMSGraphError[0].Message

        if ($text -match 'Status:\s*(\d{3})\s*\(([^)]+)\)') {
            $errorInfo.StatusCode = [int]$matches[1]
            $errorInfo.StatusText = $matches[2]
        }

        if ($text -match 'ErrorCode:\s*(\S+)') {
            $errorInfo.ErrorCode = $matches[1]
        }

        if ($text -match 'Message:\s*(.+)') {
            $errorInfo.Message = $matches[1]
        }
        else {
            $errorInfo.Message = $text
        }

        if ($errorInfo.StatusCode -eq 404) {
            Write-Verbose "Resource not found (404)"
            return $null
        }

        Write-Verbose ("Full error details:`n" + ($RJMSGraphError[0].ErrorRecord | Format-List -Force | Out-String))

        # Use the original error record to preserve full context
        $PSCmdlet.ThrowTerminatingError($RJMSGraphError[0].ErrorRecord)
    }
}

function Invoke-GraphLogin {
    [CmdletBinding()]
    param()

    # Check whether already connected to Microsoft Graph
    $context = Invoke-RJMgGraphCommand { Get-MgContext }
    if ($context -and $context.Scopes) {
        $hasRequiredScopes = $true

        foreach ($scope in $RequiredGraphScopes) {
            if ($scope -notin $context.Scopes) {
                $hasRequiredScopes = $false
                break
            }
        }

        if ($hasRequiredScopes) {
            Write-Verbose "Already connected to Microsoft Graph with required scopes"
            Write-Verbose "Connected as: $($context.Account) to tenant: $($context.TenantId)"
            return $context
        }
        else {
            Write-Warning "Connected to Microsoft Graph but missing required scopes. Reconnecting..."
            Disconnect-MgGraph -ErrorAction SilentlyContinue
        }
    }

    # Connect to Microsoft Graph with required scopes
    Write-Information "Connecting to Microsoft Graph with required scopes..."

    Connect-MgGraph -Scopes $RequiredGraphScopes -NoWelcome

    # Verify connection
    # Get the current context to check if connected
    $context = Invoke-RJMgGraphCommand { Get-MgContext }
    if (-not $context) {
        throw "Failed to establish Microsoft Graph connection"
    }

    Write-Verbose "Successfully connected to Microsoft Graph"
    Write-Verbose "Connected as: $($context.Account) to tenant: $($context.TenantId)"

    return $context
}

function Get-TenantInfo {

    [CmdletBinding()]
    param()

    Write-Verbose "Retrieving current tenant information"

    $mgContext = Get-MgContext
    if (-not $mgContext) {
        throw "Not connected to Microsoft Graph. Please run Connect-MgGraph first."
    }

    $tenantId = $mgContext.TenantId

    # Get tenant display name from Microsoft Graph
    Write-Verbose "Getting tenant display name from Microsoft Graph"
    $displayName = "Unknown"

    $organization = Invoke-RJMgGraphCommand { Get-MgOrganization -Property Id, DisplayName }
    if ($organization) {
        $displayName = $organization.DisplayName #Always contains a single organization
    }
    else {
        throw "Failed to retrieve tenant display name from Microsoft Graph"
    }

    # Create simplified tenant info object
    $tenantInfo = [PSCustomObject]@{
        TenantId    = $tenantId
        DisplayName = $displayName
    }

    Write-Verbose "Current tenant: $($tenantInfo.DisplayName) ($($tenantInfo.TenantId))"

    return $tenantInfo
}