Public/UpdateApproval.ps1

#Requires -Version 5.1

<#
.SYNOPSIS
    Update approval and blocklist management functions
.DESCRIPTION
    Provides functions to manage update approvals via local JSON, Intune compliance,
    and external API endpoints for enterprise approval workflows.
#>


#region Local JSON Approval System

function Get-UpdateApproval {
    <#
    .SYNOPSIS
        Gets the current update approval configuration
    .DESCRIPTION
        Returns the approval configuration including blocked KBs, blocked drivers,
        and approved-only mode settings.
    .EXAMPLE
        Get-UpdateApproval
    #>

    [CmdletBinding()]
    param()
    
    $config = Get-ApprovalConfig
    
    return [PSCustomObject]@{
        ApprovedOnly    = $config.ApprovedOnly
        BlockedKBs      = $config.BlockedKBs
        BlockedDrivers  = $config.BlockedDrivers
        ApprovedUpdates = $config.ApprovedUpdates
        LastSynced      = $config.LastSynced
        Source          = $config.Source
    }
}

function Set-UpdateApproval {
    <#
    .SYNOPSIS
        Configures update approval settings
    .DESCRIPTION
        Sets approval mode, adds/removes blocked items, and configures approved updates.
    .PARAMETER ApprovedOnly
        If true, only explicitly approved updates will be installed
    .PARAMETER AddBlockedKB
        KB article IDs to add to the blocklist
    .PARAMETER RemoveBlockedKB
        KB article IDs to remove from the blocklist
    .PARAMETER AddBlockedDriver
        Driver INF names to add to the blocklist
    .PARAMETER RemoveBlockedDriver
        Driver INF names to remove from the blocklist
    .PARAMETER AddApprovedUpdate
        Update identifiers to add to the approved list
    .PARAMETER RemoveApprovedUpdate
        Update identifiers to remove from the approved list
    .EXAMPLE
        Set-UpdateApproval -ApprovedOnly $true
    .EXAMPLE
        Set-UpdateApproval -AddBlockedKB 'KB5001234', 'KB5005678'
    .EXAMPLE
        Set-UpdateApproval -AddBlockedDriver 'nvlddmkm.inf'
    #>

    [CmdletBinding(SupportsShouldProcess)]
    param(
        [Parameter()]
        [bool]$ApprovedOnly,
        
        [Parameter()]
        [string[]]$AddBlockedKB,
        
        [Parameter()]
        [string[]]$RemoveBlockedKB,
        
        [Parameter()]
        [string[]]$AddBlockedDriver,
        
        [Parameter()]
        [string[]]$RemoveBlockedDriver,
        
        [Parameter()]
        [string[]]$AddApprovedUpdate,
        
        [Parameter()]
        [string[]]$RemoveApprovedUpdate
    )
    
    $config = Get-ApprovalConfig
    
    if ($PSBoundParameters.ContainsKey('ApprovedOnly')) {
        if ($PSCmdlet.ShouldProcess("ApprovedOnly mode", "Set to $ApprovedOnly")) {
            $config.ApprovedOnly = $ApprovedOnly
            Write-DriverLog -Message "Set ApprovedOnly mode to: $ApprovedOnly" -Severity Info
        }
    }
    
    # Handle blocked KBs
    if ($AddBlockedKB) {
        foreach ($kb in $AddBlockedKB) {
            $normalizedKB = if ($kb -match '^KB') { $kb } else { "KB$kb" }
            if ($config.BlockedKBs -notcontains $normalizedKB) {
                if ($PSCmdlet.ShouldProcess($normalizedKB, "Add to blocked KBs")) {
                    $config.BlockedKBs = @($config.BlockedKBs) + $normalizedKB
                    Write-DriverLog -Message "Added $normalizedKB to blocklist" -Severity Info
                }
            }
        }
    }
    
    if ($RemoveBlockedKB) {
        foreach ($kb in $RemoveBlockedKB) {
            $normalizedKB = if ($kb -match '^KB') { $kb } else { "KB$kb" }
            if ($PSCmdlet.ShouldProcess($normalizedKB, "Remove from blocked KBs")) {
                $config.BlockedKBs = @($config.BlockedKBs | Where-Object { $_ -ne $normalizedKB })
                Write-DriverLog -Message "Removed $normalizedKB from blocklist" -Severity Info
            }
        }
    }
    
    # Handle blocked drivers
    if ($AddBlockedDriver) {
        foreach ($driver in $AddBlockedDriver) {
            if ($config.BlockedDrivers -notcontains $driver) {
                if ($PSCmdlet.ShouldProcess($driver, "Add to blocked drivers")) {
                    $config.BlockedDrivers = @($config.BlockedDrivers) + $driver
                    Write-DriverLog -Message "Added $driver to driver blocklist" -Severity Info
                }
            }
        }
    }
    
    if ($RemoveBlockedDriver) {
        foreach ($driver in $RemoveBlockedDriver) {
            if ($PSCmdlet.ShouldProcess($driver, "Remove from blocked drivers")) {
                $config.BlockedDrivers = @($config.BlockedDrivers | Where-Object { $_ -ne $driver })
                Write-DriverLog -Message "Removed $driver from driver blocklist" -Severity Info
            }
        }
    }
    
    # Handle approved updates
    if ($AddApprovedUpdate) {
        foreach ($update in $AddApprovedUpdate) {
            if ($config.ApprovedUpdates -notcontains $update) {
                if ($PSCmdlet.ShouldProcess($update, "Add to approved updates")) {
                    $config.ApprovedUpdates = @($config.ApprovedUpdates) + $update
                    Write-DriverLog -Message "Added $update to approved list" -Severity Info
                }
            }
        }
    }
    
    if ($RemoveApprovedUpdate) {
        foreach ($update in $RemoveApprovedUpdate) {
            if ($PSCmdlet.ShouldProcess($update, "Remove from approved updates")) {
                $config.ApprovedUpdates = @($config.ApprovedUpdates | Where-Object { $_ -ne $update })
                Write-DriverLog -Message "Removed $update from approved list" -Severity Info
            }
        }
    }
    
    $config.Source = 'Local'
    Save-ApprovalConfig -Config $config
    
    return Get-UpdateApproval
}

function Test-UpdateApproval {
    <#
    .SYNOPSIS
        Tests if an update is approved for installation
    .DESCRIPTION
        Checks the update against blocklists and approved lists to determine
        if it should be installed.
    .PARAMETER KBArticleID
        The KB article ID to check
    .PARAMETER DriverInf
        The driver INF file name to check
    .PARAMETER UpdateName
        The update name/title to check
    .PARAMETER Update
        An update object with KB and/or Title properties
    .EXAMPLE
        Test-UpdateApproval -KBArticleID 'KB5001234'
    .EXAMPLE
        $update | Test-UpdateApproval
    .OUTPUTS
        PSCustomObject with IsApproved, Reason properties
    #>

    [CmdletBinding(DefaultParameterSetName = 'ByKB')]
    param(
        [Parameter(Mandatory, ParameterSetName = 'ByKB')]
        [string]$KBArticleID,
        
        [Parameter(Mandatory, ParameterSetName = 'ByDriver')]
        [string]$DriverInf,
        
        [Parameter(Mandatory, ParameterSetName = 'ByName')]
        [string]$UpdateName,
        
        [Parameter(Mandatory, ParameterSetName = 'ByObject', ValueFromPipeline)]
        [PSObject]$Update
    )
    
    process {
        $config = Get-ApprovalConfig
        
        # Extract identifiers from object
        if ($Update) {
            $KBArticleID = $Update.KB ?? $Update.KBArticleID ?? $Update.HotFixID
            $DriverInf = $Update.InfName ?? $Update.DriverInf
            $UpdateName = $Update.Title ?? $Update.Name ?? $Update.UpdateName
        }
        
        # Normalize KB
        if ($KBArticleID) {
            $KBArticleID = if ($KBArticleID -match '^KB') { $KBArticleID } else { "KB$KBArticleID" }
        }
        
        $result = [PSCustomObject]@{
            Identifier = $KBArticleID ?? $DriverInf ?? $UpdateName
            IsApproved = $true
            IsBlocked  = $false
            Reason     = 'Not in blocklist'
        }
        
        # Check if blocked
        if ($KBArticleID -and $config.BlockedKBs -contains $KBArticleID) {
            $result.IsApproved = $false
            $result.IsBlocked = $true
            $result.Reason = "KB $KBArticleID is in blocklist"
            return $result
        }
        
        if ($DriverInf -and $config.BlockedDrivers -contains $DriverInf) {
            $result.IsApproved = $false
            $result.IsBlocked = $true
            $result.Reason = "Driver $DriverInf is in blocklist"
            return $result
        }
        
        # Check driver blocklist pattern matching
        if ($DriverInf) {
            foreach ($pattern in $config.BlockedDrivers) {
                if ($DriverInf -like $pattern) {
                    $result.IsApproved = $false
                    $result.IsBlocked = $true
                    $result.Reason = "Driver $DriverInf matches blocklist pattern: $pattern"
                    return $result
                }
            }
        }
        
        # Check approved-only mode
        if ($config.ApprovedOnly) {
            $isInApproved = $false
            
            if ($KBArticleID -and $config.ApprovedUpdates -contains $KBArticleID) {
                $isInApproved = $true
            }
            
            if ($UpdateName) {
                foreach ($approved in $config.ApprovedUpdates) {
                    if ($UpdateName -like $approved -or $UpdateName -eq $approved) {
                        $isInApproved = $true
                        break
                    }
                }
            }
            
            if (-not $isInApproved) {
                $result.IsApproved = $false
                $result.Reason = "ApprovedOnly mode enabled - update not in approved list"
                return $result
            }
            
            $result.Reason = "Explicitly approved"
        }
        
        return $result
    }
}

#endregion

#region Intune Compliance Integration

function Set-IntuneApprovalConfig {
    <#
    .SYNOPSIS
        Configures Intune approval integration
    .DESCRIPTION
        Sets up the connection to Intune for pulling update approval policies.
    .PARAMETER TenantId
        Azure AD tenant ID
    .PARAMETER ClientId
        Application (client) ID for Graph API access
    .PARAMETER ClientSecret
        Client secret for app authentication (use SecureString in production)
    .PARAMETER UseManagedIdentity
        Use Azure Managed Identity for authentication
    .EXAMPLE
        Set-IntuneApprovalConfig -TenantId 'xxx' -ClientId 'yyy' -UseManagedIdentity
    #>

    [CmdletBinding()]
    param(
        [Parameter()]
        [string]$TenantId,
        
        [Parameter()]
        [string]$ClientId,
        
        [Parameter()]
        [string]$ClientSecret,
        
        [Parameter()]
        [switch]$UseManagedIdentity
    )
    
    $intuneConfig = @{
        TenantId = $TenantId
        ClientId = $ClientId
        UseManagedIdentity = $UseManagedIdentity.IsPresent
        Configured = $true
        ConfiguredDate = (Get-Date).ToString('o')
    }
    
    if ($ClientSecret) {
        # Store encrypted (in production, use DPAPI or Azure Key Vault)
        $intuneConfig.ClientSecretEncrypted = $ClientSecret | ConvertTo-SecureString -AsPlainText -Force | ConvertFrom-SecureString
    }
    
    $configPath = Get-IntuneConfigPath
    $configDir = Split-Path $configPath -Parent
    
    if (-not (Test-Path $configDir)) {
        New-Item -Path $configDir -ItemType Directory -Force | Out-Null
    }
    
    $intuneConfig | ConvertTo-Json | Set-Content -Path $configPath -Encoding UTF8
    
    Write-DriverLog -Message "Intune approval configuration saved" -Severity Info
}

function Sync-IntuneUpdateApproval {
    <#
    .SYNOPSIS
        Synchronizes update approval settings from Intune
    .DESCRIPTION
        Pulls Windows Update for Business settings and compliance policies
        from Intune to determine which updates are approved.
    .PARAMETER Force
        Force sync even if recently synced
    .EXAMPLE
        Sync-IntuneUpdateApproval
    #>

    [CmdletBinding()]
    param(
        [Parameter()]
        [switch]$Force
    )
    
    $intuneConfig = Get-IntuneConfig
    
    if (-not $intuneConfig.Configured) {
        throw "Intune approval not configured. Run Set-IntuneApprovalConfig first."
    }
    
    Write-DriverLog -Message "Syncing update approval from Intune" -Severity Info
    
    try {
        # Get access token
        $token = Get-IntuneAccessToken -Config $intuneConfig
        
        if (-not $token) {
            throw "Failed to obtain Intune access token"
        }
        
        $headers = @{
            'Authorization' = "Bearer $token"
            'Content-Type'  = 'application/json'
        }
        
        # Get Windows Update for Business policies
        $wufbUri = "https://graph.microsoft.com/beta/deviceManagement/windowsQualityUpdatePolicies"
        $policies = Invoke-RestMethod -Uri $wufbUri -Headers $headers -Method Get -ErrorAction Stop
        
        # Get device compliance policies for this device
        $deviceId = Get-IntuneDeviceId
        if ($deviceId) {
            $complianceUri = "https://graph.microsoft.com/v1.0/deviceManagement/managedDevices/$deviceId/deviceCompliancePolicyStates"
            $compliance = Invoke-RestMethod -Uri $complianceUri -Headers $headers -Method Get -ErrorAction SilentlyContinue
        }
        
        # Parse policies to extract blocked/deferred updates
        $approvalConfig = Get-ApprovalConfig
        
        foreach ($policy in $policies.value) {
            # Extract quality update deferrals
            if ($policy.qualityUpdatesDeferralPeriodInDays -gt 0) {
                Write-DriverLog -Message "Quality updates deferred by $($policy.qualityUpdatesDeferralPeriodInDays) days" -Severity Info
            }
            
            # Extract paused updates
            if ($policy.qualityUpdatesPaused) {
                Write-DriverLog -Message "Quality updates paused" -Severity Warning
            }
        }
        
        $approvalConfig.LastSynced = (Get-Date).ToString('o')
        $approvalConfig.Source = 'Intune'
        
        Save-ApprovalConfig -Config $approvalConfig
        
        Write-DriverLog -Message "Intune sync completed" -Severity Info
        
        return Get-UpdateApproval
    }
    catch {
        Write-DriverLog -Message "Failed to sync from Intune: $($_.Exception.Message)" -Severity Error
        throw
    }
}

function Get-IntuneDeviceId {
    <#
    .SYNOPSIS
        Gets the Intune device ID for the current machine
    #>

    [CmdletBinding()]
    param()
    
    # Try to get from registry (Intune enrolled devices)
    $intuneReg = "HKLM:\SOFTWARE\Microsoft\Enrollments"
    
    if (Test-Path $intuneReg) {
        $enrollments = Get-ChildItem $intuneReg -ErrorAction SilentlyContinue
        
        foreach ($enrollment in $enrollments) {
            $deviceId = Get-ItemProperty -Path $enrollment.PSPath -Name 'DeviceId' -ErrorAction SilentlyContinue
            if ($deviceId.DeviceId) {
                return $deviceId.DeviceId
            }
        }
    }
    
    # Fallback: try AAD device ID
    $aadReg = "HKLM:\SYSTEM\CurrentControlSet\Control\CloudDomainJoin\JoinInfo"
    if (Test-Path $aadReg) {
        $joinInfo = Get-ChildItem $aadReg -ErrorAction SilentlyContinue | Select-Object -First 1
        if ($joinInfo) {
            return $joinInfo.PSChildName
        }
    }
    
    return $null
}

#endregion

#region External API Endpoint Support

function Set-ApprovalEndpoint {
    <#
    .SYNOPSIS
        Configures an external API endpoint for update approvals
    .DESCRIPTION
        Sets up connection to an enterprise approval system that provides
        update approval decisions via REST API.
    .PARAMETER Uri
        The base URI of the approval API
    .PARAMETER ApiKey
        API key for authentication
    .PARAMETER Headers
        Additional headers to include in requests
    .EXAMPLE
        Set-ApprovalEndpoint -Uri 'https://approvals.company.com/api/v1' -ApiKey 'xxx'
    #>

    [CmdletBinding()]
    param(
        [Parameter(Mandatory)]
        [ValidatePattern('^https?://')]
        [string]$Uri,
        
        [Parameter()]
        [string]$ApiKey,
        
        [Parameter()]
        [hashtable]$Headers
    )
    
    $endpointConfig = @{
        Uri = $Uri.TrimEnd('/')
        Configured = $true
        ConfiguredDate = (Get-Date).ToString('o')
        Headers = $Headers ?? @{}
    }
    
    if ($ApiKey) {
        $endpointConfig.ApiKeyEncrypted = $ApiKey | ConvertTo-SecureString -AsPlainText -Force | ConvertFrom-SecureString
    }
    
    $configPath = Get-ExternalEndpointConfigPath
    $configDir = Split-Path $configPath -Parent
    
    if (-not (Test-Path $configDir)) {
        New-Item -Path $configDir -ItemType Directory -Force | Out-Null
    }
    
    $endpointConfig | ConvertTo-Json -Depth 3 | Set-Content -Path $configPath -Encoding UTF8
    
    # Also allow environment variable override
    if ($env:PSDM_APPROVAL_API) {
        Write-DriverLog -Message "Note: PSDM_APPROVAL_API environment variable will override configured endpoint" -Severity Warning
    }
    
    Write-DriverLog -Message "External approval endpoint configured: $Uri" -Severity Info
}

function Sync-ExternalApproval {
    <#
    .SYNOPSIS
        Synchronizes update approval from external API
    .DESCRIPTION
        Pulls update approval configuration from the configured external API endpoint.
         
        Expected API response format:
        {
            "blockedKBs": ["KB5001234"],
            "blockedDrivers": ["nvlddmkm.inf"],
            "approvedUpdates": ["KB5002345"],
            "approvedOnly": false
        }
    .PARAMETER Force
        Force sync even if recently synced
    .EXAMPLE
        Sync-ExternalApproval
    #>

    [CmdletBinding()]
    param(
        [Parameter()]
        [switch]$Force
    )
    
    $endpointConfig = Get-ExternalEndpointConfig
    
    # Allow environment variable override
    $apiUri = $env:PSDM_APPROVAL_API ?? $endpointConfig.Uri
    
    if (-not $apiUri) {
        throw "External approval endpoint not configured. Run Set-ApprovalEndpoint or set PSDM_APPROVAL_API."
    }
    
    Write-DriverLog -Message "Syncing update approval from: $apiUri" -Severity Info
    
    try {
        $headers = @{
            'Content-Type' = 'application/json'
            'Accept' = 'application/json'
            'X-Computer-Name' = $env:COMPUTERNAME
            'X-OS-Version' = [System.Environment]::OSVersion.Version.ToString()
        }
        
        # Add configured headers
        if ($endpointConfig.Headers) {
            foreach ($key in $endpointConfig.Headers.Keys) {
                $headers[$key] = $endpointConfig.Headers[$key]
            }
        }
        
        # Add API key if configured
        if ($endpointConfig.ApiKeyEncrypted) {
            $apiKey = [System.Runtime.InteropServices.Marshal]::PtrToStringAuto(
                [System.Runtime.InteropServices.Marshal]::SecureStringToBSTR(
                    ($endpointConfig.ApiKeyEncrypted | ConvertTo-SecureString)
                )
            )
            $headers['X-API-Key'] = $apiKey
        }
        
        # Fetch approval config
        $approvalEndpoint = "$apiUri/updates/approval"
        $response = Invoke-RestMethod -Uri $approvalEndpoint -Headers $headers -Method Get -ErrorAction Stop
        
        # Update local config
        $approvalConfig = Get-ApprovalConfig
        
        if ($response.blockedKBs) {
            $approvalConfig.BlockedKBs = @($response.blockedKBs)
        }
        
        if ($response.blockedDrivers) {
            $approvalConfig.BlockedDrivers = @($response.blockedDrivers)
        }
        
        if ($response.approvedUpdates) {
            $approvalConfig.ApprovedUpdates = @($response.approvedUpdates)
        }
        
        if ($null -ne $response.approvedOnly) {
            $approvalConfig.ApprovedOnly = $response.approvedOnly
        }
        
        $approvalConfig.LastSynced = (Get-Date).ToString('o')
        $approvalConfig.Source = 'ExternalAPI'
        $approvalConfig.SourceUri = $apiUri
        
        Save-ApprovalConfig -Config $approvalConfig
        
        Write-DriverLog -Message "External approval sync completed" -Severity Info `
            -Context @{ 
                BlockedKBs = $approvalConfig.BlockedKBs.Count
                BlockedDrivers = $approvalConfig.BlockedDrivers.Count
                ApprovedOnly = $approvalConfig.ApprovedOnly
            }
        
        return Get-UpdateApproval
    }
    catch {
        Write-DriverLog -Message "Failed to sync from external API: $($_.Exception.Message)" -Severity Error
        throw
    }
}

function Send-UpdateReport {
    <#
    .SYNOPSIS
        Sends update status report to external API
    .DESCRIPTION
        Reports installed updates and compliance status to the enterprise approval system.
    .PARAMETER UpdateResult
        The result object from Invoke-DriverManagement or similar
    .EXAMPLE
        $result = Invoke-DriverManagement -Mode Individual
        Send-UpdateReport -UpdateResult $result
    #>

    [CmdletBinding()]
    param(
        [Parameter(Mandatory, ValueFromPipeline)]
        [PSObject]$UpdateResult
    )
    
    process {
        $endpointConfig = Get-ExternalEndpointConfig
        $apiUri = $env:PSDM_APPROVAL_API ?? $endpointConfig.Uri
        
        if (-not $apiUri) {
            Write-DriverLog -Message "External API not configured - skipping report" -Severity Warning
            return
        }
        
        try {
            $headers = @{
                'Content-Type' = 'application/json'
                'X-Computer-Name' = $env:COMPUTERNAME
            }
            
            if ($endpointConfig.ApiKeyEncrypted) {
                $apiKey = [System.Runtime.InteropServices.Marshal]::PtrToStringAuto(
                    [System.Runtime.InteropServices.Marshal]::SecureStringToBSTR(
                        ($endpointConfig.ApiKeyEncrypted | ConvertTo-SecureString)
                    )
                )
                $headers['X-API-Key'] = $apiKey
            }
            
            $report = @{
                computerName = $env:COMPUTERNAME
                timestamp = (Get-Date).ToString('o')
                success = $UpdateResult.Success
                updatesApplied = $UpdateResult.UpdatesApplied
                rebootRequired = $UpdateResult.RebootRequired
                exitCode = $UpdateResult.ExitCode
                correlationId = $UpdateResult.CorrelationId
                details = $UpdateResult.Details
            }
            
            $reportEndpoint = "$apiUri/updates/report"
            Invoke-RestMethod -Uri $reportEndpoint -Headers $headers -Method Post -Body ($report | ConvertTo-Json -Depth 5)
            
            Write-DriverLog -Message "Update report sent to external API" -Severity Info
        }
        catch {
            Write-DriverLog -Message "Failed to send update report: $($_.Exception.Message)" -Severity Warning
        }
    }
}

#endregion

#region Helper Functions

function Get-ApprovalConfigPath {
    $config = $script:ModuleConfig
    $basePath = Split-Path $config.CompliancePath -Parent
    return Join-Path $basePath "approval.json"
}

function Get-IntuneConfigPath {
    $config = $script:ModuleConfig
    $basePath = Split-Path $config.CompliancePath -Parent
    return Join-Path $basePath "intune-config.json"
}

function Get-ExternalEndpointConfigPath {
    $config = $script:ModuleConfig
    $basePath = Split-Path $config.CompliancePath -Parent
    return Join-Path $basePath "external-endpoint.json"
}

function Get-ApprovalConfig {
    $path = Get-ApprovalConfigPath
    
    if (Test-Path $path) {
        return Get-Content $path -Raw | ConvertFrom-Json
    }
    
    # Return default structure
    return [PSCustomObject]@{
        Version = '1.0'
        ApprovedOnly = $false
        BlockedKBs = @()
        BlockedDrivers = @()
        ApprovedUpdates = @()
        LastSynced = $null
        Source = 'Local'
        SourceUri = $null
    }
}

function Save-ApprovalConfig {
    param(
        [Parameter(Mandatory)]
        $Config
    )
    
    $path = Get-ApprovalConfigPath
    $dir = Split-Path $path -Parent
    
    if (-not (Test-Path $dir)) {
        New-Item -Path $dir -ItemType Directory -Force | Out-Null
    }
    
    $Config | ConvertTo-Json -Depth 5 | Set-Content -Path $path -Encoding UTF8
}

function Get-IntuneConfig {
    $path = Get-IntuneConfigPath
    
    if (Test-Path $path) {
        return Get-Content $path -Raw | ConvertFrom-Json
    }
    
    return [PSCustomObject]@{
        Configured = $false
    }
}

function Get-ExternalEndpointConfig {
    $path = Get-ExternalEndpointConfigPath
    
    if (Test-Path $path) {
        return Get-Content $path -Raw | ConvertFrom-Json
    }
    
    return [PSCustomObject]@{
        Configured = $false
        Uri = $null
        Headers = @{}
    }
}

function Get-IntuneAccessToken {
    param($Config)
    
    if ($Config.UseManagedIdentity) {
        # Use Azure Instance Metadata Service for managed identity
        try {
            $tokenUri = "http://169.254.169.254/metadata/identity/oauth2/token?api-version=2018-02-01&resource=https://graph.microsoft.com"
            $response = Invoke-RestMethod -Uri $tokenUri -Headers @{Metadata="true"} -Method Get
            return $response.access_token
        }
        catch {
            Write-DriverLog -Message "Failed to get managed identity token: $($_.Exception.Message)" -Severity Error
            return $null
        }
    }
    else {
        # Client credentials flow
        if (-not $Config.ClientSecretEncrypted) {
            Write-DriverLog -Message "Client secret not configured" -Severity Error
            return $null
        }
        
        $clientSecret = [System.Runtime.InteropServices.Marshal]::PtrToStringAuto(
            [System.Runtime.InteropServices.Marshal]::SecureStringToBSTR(
                ($Config.ClientSecretEncrypted | ConvertTo-SecureString)
            )
        )
        
        $tokenUri = "https://login.microsoftonline.com/$($Config.TenantId)/oauth2/v2.0/token"
        $body = @{
            client_id = $Config.ClientId
            client_secret = $clientSecret
            scope = "https://graph.microsoft.com/.default"
            grant_type = "client_credentials"
        }
        
        try {
            $response = Invoke-RestMethod -Uri $tokenUri -Method Post -Body $body
            return $response.access_token
        }
        catch {
            Write-DriverLog -Message "Failed to get access token: $($_.Exception.Message)" -Severity Error
            return $null
        }
    }
}

#endregion