Private/Crypto/New-PukCngRandomValue.ps1

function New-PukCngRandomValue {
    [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseShouldProcessForStateChangingFunctions', '', Justification = 'Pure in-memory computation (draws cryptographically random digits via CNG); changes no external or persistent state.')]
    [CmdletBinding()]
    [OutputType([string])]
    param(
        [Parameter(Mandatory)]
        [ValidateRange(1, [int]::MaxValue)]
        [int]$Length
    )

    # Instance GetBytes() (not the static .NET 6+ GetInt32/Fill methods) so this runs
    # identically on Windows PowerShell 5.1 (.NET Framework) and PowerShell 7+ (.NET).
    # On Windows both call into CNG's BCryptGenRandom.
    $rng = [System.Security.Cryptography.RandomNumberGenerator]::Create()
    try {
        $digits = [System.Text.StringBuilder]::new($Length)
        $buffer = [byte[]]::new(1)

        while ($digits.Length -lt $Length) {
            $rng.GetBytes($buffer)
            $byteValue = $buffer[0]

            # 256 is not evenly divisible by 10; discard bytes >= 250 to avoid modulo bias.
            if ($byteValue -lt 250) {
                [void]$digits.Append([string]($byteValue % 10))
            }
        }

        return $digits.ToString()
    }
    finally {
        $rng.Dispose()
    }
}