Core/GraphApiClient.ps1

function Connect-SecureScoreGraph {
    <#
    .SYNOPSIS
        Establishes connection to Microsoft Graph API.

    .DESCRIPTION
        Connects to Microsoft Graph with required scopes for Secure Score operations.
        Returns connection context information.

    .PARAMETER UseDeviceCode
        Use device code authentication instead of interactive.

    .PARAMETER TenantId
        Specific tenant ID to connect to.

    .OUTPUTS
        Hashtable containing TenantId and Account information.
    #>

    [CmdletBinding()]
    param(
        [Parameter(Mandatory = $false)]
        [switch]$UseDeviceCode,

        [Parameter(Mandatory = $false)]
        [string]$TenantId
    )

    try {
        # Ensure required modules are imported
        try {
            Import-Module Microsoft.Graph.Authentication -ErrorAction Stop
        }
        catch {
            throw "Failed to import Microsoft.Graph.Authentication module. Ensure it is installed: Install-Module Microsoft.Graph.Authentication"
        }

        try {
            Import-Module Microsoft.Graph.Security -ErrorAction Stop
        }
        catch {
            throw "Failed to import Microsoft.Graph.Security module. Ensure it is installed: Install-Module Microsoft.Graph.Security"
        }

        $graphScopes = @(
            "SecurityEvents.Read.All",
            "Organization.Read.All"
        )

        # Disconnect any existing session to start fresh
        try {
            Disconnect-MgGraph -ErrorAction SilentlyContinue | Out-Null
        }
        catch {
            # Ignore disconnect errors
        }

        # Connect to Microsoft Graph with appropriate method
        try {
            if ($UseDeviceCode) {
                Connect-MgGraph -Scopes $graphScopes -UseDeviceCode -ErrorAction Stop
            }
            elseif ($TenantId) {
                Connect-MgGraph -Scopes $graphScopes -TenantId $TenantId -ErrorAction Stop
            }
            else {
                Connect-MgGraph -Scopes $graphScopes -ErrorAction Stop
            }
        }
        catch {
            $errorMessage = if ($_.Exception.Message) { $_.Exception.Message } else { "Unknown error during Connect-MgGraph" }
            throw "Failed during Connect-MgGraph: $errorMessage"
        }

        $context = Get-MgContext
        if (-not $context) {
            throw "Failed to establish Microsoft Graph connection - context is null"
        }

        return @{
            TenantId = $context.TenantId
            Account = $context.Account
            Scopes = $context.Scopes
        }
    }
    catch {
        $errorMessage = if ($_.Exception.Message) { $_.Exception.Message } else { "Unknown connection error" }
        throw "Failed to connect to Microsoft Graph: $errorMessage"
    }
}

function Get-SecureScoreData {
    <#
    .SYNOPSIS
        Retrieves current secure score from Microsoft Graph API.

    .DESCRIPTION
        Fetches the most recent secure score information including current score,
        max score, and individual control scores.

    .OUTPUTS
        Hashtable containing CurrentScore, MaxScore, and ControlScores.
    #>

    [CmdletBinding()]
    param()

    try {
        # Ensure Security module is loaded
        Import-Module Microsoft.Graph.Security -ErrorAction Stop

        $allScores = Get-MgSecuritySecureScore -Top 1 -ErrorAction Stop

        # Handle both array and single object returns
        if ($allScores -is [Array]) {
            $currentScore = $allScores[0]
        } else {
            $currentScore = $allScores
        }

        if (-not $currentScore) {
            throw "No secure score data returned from API"
        }

        $controlScores = @{}
        if ($currentScore.ControlScores) {
            $controlScoresArray = @($currentScore.ControlScores)
            foreach ($cs in $controlScoresArray) {
                $controlScores[$cs.ControlName] = $cs
            }
        }

        return @{
            CurrentScore = $currentScore.CurrentScore
            MaxScore = $currentScore.MaxScore
            ControlScores = $controlScores
            RawData = $currentScore
        }
    }
    catch {
        throw "Failed to retrieve secure score data: $_"
    }
}

function Get-SecureScoreControlProfiles {
    <#
    .SYNOPSIS
        Retrieves all secure score control profiles from Microsoft Graph API.

    .DESCRIPTION
        Fetches all available secure score control profiles (411+ controls).

    .PARAMETER FilterApplicableOnly
        If specified, returns only controls that are being scored in the tenant.

    .PARAMETER ScoredControlsList
        List of control names that are actively scored in the tenant.

    .OUTPUTS
        Array of control profile objects.
    #>

    [CmdletBinding()]
    param(
        [Parameter(Mandatory = $false)]
        [switch]$FilterApplicableOnly,

        [Parameter(Mandatory = $false)]
        [string[]]$ScoredControlsList
    )

    try {
        # Ensure Security module is loaded
        Import-Module Microsoft.Graph.Security -ErrorAction Stop

        $controls = Get-MgSecuritySecureScoreControlProfile -All -ErrorAction Stop

        if ($FilterApplicableOnly -and $ScoredControlsList) {
            $controls = $controls | Where-Object { $ScoredControlsList -contains $_.Id }
        }

        return $controls
    }
    catch {
        throw "Failed to retrieve secure score control profiles: $_"
    }
}

function Get-OrganizationInfo {
    <#
    .SYNOPSIS
        Retrieves organization information from Microsoft Graph API.

    .DESCRIPTION
        Fetches organization details including display name.

    .OUTPUTS
        Hashtable containing DisplayName and other organization properties.
    #>

    [CmdletBinding()]
    param()

    try {
        # Ensure required module is loaded (part of Authentication module)
        Import-Module Microsoft.Graph.Authentication -ErrorAction Stop

        $organization = Get-MgOrganization -ErrorAction Stop

        if ($organization) {
            return @{
                DisplayName = $organization.DisplayName
                Id = $organization.Id
                RawData = $organization
            }
        }
        else {
            return @{
                DisplayName = "Organization"
                Id = $null
                RawData = $null
            }
        }
    }
    catch {
        Write-Warning "Could not fetch organization name: $_"
        return @{
            DisplayName = "Organization"
            Id = $null
            RawData = $null
        }
    }
}

function Test-GraphConnection {
    <#
    .SYNOPSIS
        Tests if Microsoft Graph connection is established and valid.

    .DESCRIPTION
        Checks if there is an active Microsoft Graph connection context.

    .OUTPUTS
        Boolean indicating connection status.
    #>

    [CmdletBinding()]
    param()

    try {
        $context = Get-MgContext
        return ($null -ne $context)
    }
    catch {
        return $false
    }
}

Export-ModuleMember -Function @(
    'Connect-SecureScoreGraph',
    'Get-SecureScoreData',
    'Get-SecureScoreControlProfiles',
    'Get-OrganizationInfo',
    'Test-GraphConnection'
)