Back to Blog
4 min read

Azure Confidential Ledger: Tamper-Proof Record Keeping

Azure Confidential Ledger provides a tamper-proof, append-only ledger backed by blockchain technology and Trusted Execution Environments (TEEs). It’s ideal for scenarios requiring immutable audit trails.

Understanding Confidential Ledger

Key features:

  • Tamper-evident blockchain
  • Runs in Azure Confidential Computing enclaves
  • Cryptographically verifiable
  • No single point of trust

Creating a Confidential Ledger

resource confidentialLedger 'Microsoft.ConfidentialLedger/ledgers@2022-05-13' = {
  name: 'ledger-${environment}'
  location: location
  properties: {
    ledgerType: 'Private'
    aadBasedSecurityPrincipals: [
      {
        principalId: adminPrincipalId
        ledgerRoleName: 'Administrator'
      }
      {
        principalId: appPrincipalId
        ledgerRoleName: 'Contributor'
      }
    ]
    certBasedSecurityPrincipals: []
  }
}

output ledgerUri string = confidentialLedger.properties.ledgerUri
output identityServiceUri string = confidentialLedger.properties.identityServiceUri

Writing to the Ledger

using Azure.Identity;
using Azure.Security.ConfidentialLedger;

public class AuditLedgerService
{
    private readonly ConfidentialLedgerClient _client;

    public AuditLedgerService(string ledgerUri)
    {
        var credential = new DefaultAzureCredential();
        _client = new ConfidentialLedgerClient(new Uri(ledgerUri), credential);
    }

    public async Task<string> WriteAuditEntryAsync(AuditEntry entry)
    {
        var content = JsonSerializer.Serialize(entry);

        // Write to ledger - this is immutable once committed
        var operation = await _client.PostLedgerEntryAsync(
            WaitUntil.Completed,
            RequestContent.Create(new { contents = content }),
            collectionId: "audit-log");

        var response = operation.Value;
        return response.TransactionId;
    }

    public async Task<AuditEntry> ReadAuditEntryAsync(string transactionId)
    {
        var response = await _client.GetLedgerEntryAsync(transactionId);
        var entry = response.Value;

        return JsonSerializer.Deserialize<AuditEntry>(entry.Contents);
    }

    public async Task<bool> VerifyTransactionAsync(string transactionId)
    {
        // Get receipt for cryptographic verification
        var receipt = await _client.GetReceiptAsync(transactionId);

        // Verify the receipt against the ledger
        return await _client.VerifyReceiptAsync(receipt.Value);
    }
}

public class AuditEntry
{
    public string EventType { get; set; }
    public string UserId { get; set; }
    public string Action { get; set; }
    public string ResourceId { get; set; }
    public Dictionary<string, object> Details { get; set; }
    public DateTime Timestamp { get; set; }
}

Python SDK Example

from azure.identity import DefaultAzureCredential
from azure.confidentialledger import ConfidentialLedgerClient
from azure.confidentialledger.identity_service import ConfidentialLedgerIdentityServiceClient
import json
from datetime import datetime

class ConfidentialAuditLog:
    def __init__(self, ledger_url: str, identity_url: str):
        credential = DefaultAzureCredential()

        # Get ledger identity
        identity_client = ConfidentialLedgerIdentityServiceClient(identity_url)
        ledger_cert = identity_client.get_ledger_identity(ledger_url.split('//')[1].split('.')[0])

        self.client = ConfidentialLedgerClient(
            ledger_url,
            credential=credential,
            ledger_certificate_path=ledger_cert.ledger_tls_certificate
        )

    def log_event(self, event_type: str, user_id: str, action: str, details: dict) -> str:
        """Log an immutable audit event."""
        entry = {
            "eventType": event_type,
            "userId": user_id,
            "action": action,
            "details": details,
            "timestamp": datetime.utcnow().isoformat()
        }

        # Post to ledger
        result = self.client.create_ledger_entry(
            entry={"contents": json.dumps(entry)},
            collection_id="audit-events"
        )

        # Wait for commit
        transaction_id = result["transactionId"]
        self.client.wait_until_committed(transaction_id)

        return transaction_id

    def get_audit_trail(self, collection_id: str = "audit-events"):
        """Retrieve all entries from the audit trail."""
        entries = []
        for entry in self.client.list_ledger_entries(collection_id=collection_id):
            entries.append({
                "transactionId": entry.transaction_id,
                "contents": json.loads(entry.contents),
                "collectionId": entry.collection_id
            })
        return entries

    def verify_entry(self, transaction_id: str) -> bool:
        """Verify an entry hasn't been tampered with."""
        receipt = self.client.get_receipt(transaction_id)
        # Verification logic using Merkle tree proof
        return self._verify_merkle_proof(receipt)

# Usage
ledger = ConfidentialAuditLog(
    "https://my-ledger.confidential-ledger.azure.com",
    "https://identity.confidential-ledger.azure.com"
)

tx_id = ledger.log_event(
    event_type="DATA_ACCESS",
    user_id="user@company.com",
    action="EXPORT",
    details={
        "table": "customers",
        "records": 1000,
        "format": "csv"
    }
)

Use Cases

Financial Audit Trail

public class FinancialAuditService
{
    private readonly AuditLedgerService _ledger;

    public async Task RecordTransactionAsync(FinancialTransaction transaction)
    {
        var auditEntry = new AuditEntry
        {
            EventType = "FINANCIAL_TRANSACTION",
            UserId = transaction.InitiatedBy,
            Action = transaction.Type.ToString(),
            ResourceId = transaction.TransactionId,
            Details = new Dictionary<string, object>
            {
                ["amount"] = transaction.Amount,
                ["currency"] = transaction.Currency,
                ["fromAccount"] = transaction.FromAccount,
                ["toAccount"] = transaction.ToAccount,
                ["approvedBy"] = transaction.ApprovedBy
            },
            Timestamp = DateTime.UtcNow
        };

        var txId = await _ledger.WriteAuditEntryAsync(auditEntry);

        // Store transaction ID for future verification
        transaction.LedgerTransactionId = txId;
    }

    public async Task<bool> VerifyTransactionIntegrityAsync(string ledgerTxId)
    {
        return await _ledger.VerifyTransactionAsync(ledgerTxId);
    }
}

Healthcare Compliance

public class HealthcareAuditService
{
    public async Task LogPatientDataAccessAsync(
        string userId,
        string patientId,
        string accessType,
        string purpose)
    {
        var entry = new AuditEntry
        {
            EventType = "PATIENT_DATA_ACCESS",
            UserId = userId,
            Action = accessType,
            ResourceId = patientId,
            Details = new Dictionary<string, object>
            {
                ["purpose"] = purpose,
                ["hipaaCompliance"] = true,
                ["accessLocation"] = GetAccessLocation()
            },
            Timestamp = DateTime.UtcNow
        };

        await _ledger.WriteAuditEntryAsync(entry);
    }
}

Querying the Ledger

public async Task<List<AuditEntry>> GetAuditTrailAsync(
    string resourceId,
    DateTime? fromDate = null,
    DateTime? toDate = null)
{
    var entries = new List<AuditEntry>();

    await foreach (var page in _client.GetLedgerEntriesAsync(collectionId: "audit-log"))
    {
        var entry = JsonSerializer.Deserialize<AuditEntry>(page.Contents);

        if (entry.ResourceId == resourceId &&
            (fromDate == null || entry.Timestamp >= fromDate) &&
            (toDate == null || entry.Timestamp <= toDate))
        {
            entries.Add(entry);
        }
    }

    return entries;
}

Azure Confidential Ledger provides cryptographically verifiable, tamper-proof record keeping for compliance-critical scenarios.

Michael John Peña

Michael John Peña

Senior Data Engineer based in Sydney. Writing about data, cloud, and technology.