Private/Get-AzTableAuthorizationHeader.ps1

function Get-AzTableAuthorizationHeader {
    <#
    .SYNOPSIS
        Builds the Authorization and date headers for an Azure Table Storage REST request.

    .DESCRIPTION
        Supports three authentication types:
        - SharedKey : HMAC-SHA256 signature using the storage account key
        - AccessToken : Bearer token passed directly in the context (no external dependency)
        - SasToken : No Authorization header; SAS appended to URL by the caller

    .OUTPUTS
        [hashtable] Headers that must be merged into the outgoing request.
    #>

    [CmdletBinding()]
    [OutputType([hashtable])]
    param (
        [Parameter(Mandatory)]
        [PSCustomObject]$Context,

        [Parameter(Mandatory)]
        [ValidateSet('GET', 'POST', 'PUT', 'DELETE', 'PATCH')]
        [string]$Method,

        # Path component only, e.g. "Tables", "mytable()", "mytable(PartitionKey='pk',RowKey='rk')"
        [Parameter(Mandatory)]
        [string]$Resource,

        [string]$ContentType = '',

        [string]$ContentMD5 = ''
    )

    $date = [System.DateTime]::UtcNow.ToString('R')

    $headers = @{
        'x-ms-date'    = $date
        'x-ms-version' = '2019-02-02'
    }

    switch ($Context.AuthType) {
        'SharedKey' {
            # Extract any sub-path from the Endpoint (e.g. "/devstoreaccount1" for Azurite
            # IP-style URLs like http://127.0.0.1:10002/devstoreaccount1). For production
            # endpoints (https://account.table.core.windows.net) the path is "/" which
            # trims to an empty string, preserving the standard /{account}/{resource} form.
            $endpointPath = ''
            if (-not [string]::IsNullOrEmpty($Context.Endpoint)) {
                $endpointUri = [System.Uri]$Context.Endpoint
                $endpointPath = $endpointUri.AbsolutePath.TrimEnd('/')
            }
            $canonicalizedResource = "/$($Context.StorageAccountName)$endpointPath/$Resource"
            # StringToSign format for Table service Shared Key:
            # VERB\nContent-MD5\nContent-Type\nDate\nCanonicalizedResource
            $stringToSign = "$Method`n$ContentMD5`n$ContentType`n$date`n$canonicalizedResource"

            $plainKey  = [System.Net.NetworkCredential]::new('', $Context.StorageAccountKey).Password
            $keyBytes  = [System.Convert]::FromBase64String($plainKey)
            $hmac = [System.Security.Cryptography.HMACSHA256]::new($keyBytes)
            $signBytes = [System.Text.Encoding]::UTF8.GetBytes($stringToSign)
            $signature = [System.Convert]::ToBase64String($hmac.ComputeHash($signBytes))
            $hmac.Dispose()

            $headers['Authorization'] = "SharedKey $($Context.StorageAccountName):$signature"
        }

        'AccessToken' {
            $headers['Authorization'] = "Bearer $($Context.AccessToken)"
        }

        'SasToken' {
            # Authorization header is not used; the SAS token is appended to the request URL.
        }
    }

    return $headers
}