Private/Connect-Sentinel.ps1

function Connect-Sentinel {
    <#
    .SYNOPSIS
        Authenticates to Azure and resolves the target Sentinel workspace.
    .OUTPUTS
        PSCustomObject with SubscriptionId, ResourceGroup, WorkspaceName,
        WorkspaceId, ResourceId, and Token properties.
    #>

    [CmdletBinding()]
    param(
        [Parameter(Mandatory)][string]$SubscriptionId,
        [Parameter(Mandatory)][string]$ResourceGroup,
        [Parameter(Mandatory)][string]$WorkspaceName,
        [string]$WorkspaceId
    )

    # Authenticate only if not already connected to the right subscription
    $ctx = Get-AzContext -ErrorAction SilentlyContinue
    if (-not $ctx -or $ctx.Subscription.Id -ne $SubscriptionId) {
        Write-Verbose "Authenticating to subscription $SubscriptionId …"
        $prevWarning = $WarningPreference
        $WarningPreference = 'SilentlyContinue'
        Connect-AzAccount -SubscriptionId $SubscriptionId -ErrorAction Stop | Out-Null
        $WarningPreference = $prevWarning
        $ctx = Get-AzContext
    }
    else {
        Write-Verbose "Already connected to subscription $SubscriptionId."
    }

    # Resolve workspace
    $resourceId = "/subscriptions/$SubscriptionId/resourceGroups/$ResourceGroup" +
                  "/providers/Microsoft.OperationalInsights/workspaces/$WorkspaceName"

    $ws = Get-AzResource -ResourceId $resourceId -ErrorAction Stop
    $resolvedWsId = $ws.Properties.customerId   # Log Analytics workspace GUID

    if ($WorkspaceId -and $resolvedWsId -ne $WorkspaceId) {
        Write-Warning "Supplied WorkspaceId ($WorkspaceId) differs from resolved ID ($resolvedWsId). Using resolved."
    }

    # Acquire ARM token
    $token = Resolve-AzToken -ResourceUrl 'https://management.azure.com'

    # Acquire Log Analytics token
    $laToken = Resolve-AzToken -ResourceUrl 'https://api.loganalytics.io'

    # Check if Defender XDR unified experience is enabled
    $sentinelResourceId = "/subscriptions/$SubscriptionId/resourceGroups/$ResourceGroup" +
                          "/providers/Microsoft.OperationalInsights/workspaces/$WorkspaceName" +
                          "/providers/Microsoft.SecurityInsights/onboardingStates/default"

    $defenderUnified = $false
    try {
        $headers = @{ Authorization = "Bearer $token" }
        $onboardUri = "https://management.azure.com${sentinelResourceId}?api-version=2024-03-01"
        $onboard = Invoke-RestMethod -Uri $onboardUri -Headers $headers -ErrorAction Stop
        if ($onboard.properties) {
            # If the onboarding state exists, workspace is onboarded to Sentinel
            $defenderUnified = $true
        }
    }
    catch {
        Write-Verbose 'Could not determine Defender unified experience status.'
    }

    [PSCustomObject]@{
        SubscriptionId  = $SubscriptionId
        TenantId        = $ctx.Tenant.Id
        ResourceGroup   = $ResourceGroup
        WorkspaceName   = $WorkspaceName
        WorkspaceId     = $resolvedWsId
        ResourceId      = $resourceId
        ArmToken        = $token
        LaToken         = $laToken
        DefenderUnified = $defenderUnified
        Region          = $ws.Location
    }
}

function Resolve-AzToken {
    <#
    .SYNOPSIS
        Wraps Get-AzAccessToken and handles both plain-string and SecureString
        token formats across Az module versions.
    #>

    param([string]$ResourceUrl)

    $tokenObj = Get-AzAccessToken -ResourceUrl $ResourceUrl -ErrorAction Stop
    $raw = $tokenObj.Token

    if ($raw -is [System.Security.SecureString]) {
        [System.Net.NetworkCredential]::new('', $raw).Password
    }
    else {
        [string]$raw
    }
}