Providers/Authentik/Private/ConvertFrom-AuthentikSetting.ps1

function ConvertFrom-AuthentikSetting {
    <#
    .SYNOPSIS
        Turns a key=value;key=value CSV cell into a typed hashtable

    .DESCRIPTION
        Several seed files carry an object's settings in one cell rather than a column per
        setting, because the settings differ by type: a password policy has a minimum length
        and a GeoIP policy has a country list, and a CSV with a column for each would be
        mostly empty. The cell is key=value pairs separated by semicolons. A value of TRUE or
        FALSE becomes a boolean, a whole number becomes an integer, a value containing | becomes
        an array of those parts, each typed the same way, and anything else stays a string. A
        comma is not a separator, so an error message can contain one.

    .PARAMETER Text
        The cell. Empty or whitespace yields an empty hashtable.

    .OUTPUTS
        System.Collections.Specialized.OrderedDictionary

    .EXAMPLE
        PS> ConvertFrom-AuthentikSetting -Text 'length_min=12;check_zxcvbn=TRUE;countries=US|GB'

        DESCRIPTION: Parses three settings
        OUTPUT: An ordered hashtable with an int, a bool and a two-element string array
        USE CASE: Building the body of a typed policy from its CSV row

    .NOTES
        Author: Jeffrey Stuhr
        Blog: https://www.techbyjeff.net
        LinkedIn: https://www.linkedin.com/in/jeffrey-stuhr-034214aa/
    #>


    [CmdletBinding()]
    [OutputType([System.Collections.Specialized.OrderedDictionary])]
    param(
        [Parameter()]
        [AllowEmptyString()]
        [AllowNull()]
        [string]$Text
    )

    $typed = {
        param([string]$value)
        $trimmed = $value.Trim()
        if ($trimmed -eq 'TRUE') { return $true }
        if ($trimmed -eq 'FALSE') { return $false }
        if ($trimmed -match '^-?\d+$') { return [int]$trimmed }
        return $trimmed
    }

    $result = [ordered]@{}
    if ([string]::IsNullOrWhiteSpace($Text)) { return $result }

    foreach ($pair in @($Text -split ';' | Where-Object { -not [string]::IsNullOrWhiteSpace($_) })) {
        $index = $pair.IndexOf('=')
        if ($index -lt 1) { throw "Setting '$pair' is not in key=value form." }
        $key = $pair.Substring(0, $index).Trim()
        $value = $pair.Substring($index + 1)

        if ($value.Contains('|')) {
            $result[$key] = @($value -split '\|' | ForEach-Object { & $typed $_ })
        }
        else {
            $result[$key] = & $typed $value
        }
    }

    return $result
}