Private/Get-IslSetting.ps1

function Get-IslSetting {
    <#
    .SYNOPSIS
        Finds and validates the IntuneScriptLab.settings.psd1 that applies to a script.
 
    .DESCRIPTION
        Settings travel with the scripts: a file named IntuneScriptLab.settings.psd1 in the
        script's folder or any folder above it (the nearest wins) sets what a team would
        otherwise repeat on every call. Keys, all optional:
 
            ExcludeRule rule names to skip (wildcards)
            IncludeRule rule names to run, everything else skipped (wildcards)
            MinimumSeverity Information, Warning or Error
            Severity @{ RuleName = 'Information' | 'Warning' | 'Error' } overrides
            ScriptType Detection, Remediation, PlatformScript, Win32Detection,
                                  Win32Requirement: the type when a script's directive says nothing
            Context System or User, the same way
            Architecture x86, x64 or arm64, the same way
            EnforceSignatureCheck $true or $false, the same way
 
        Explicit parameters beat settings, and a directive in a script beats the type, context,
        architecture and signature settings. -Settings takes a file path or a hashtable instead of
        the search; an empty hashtable means "no settings".
 
    .PARAMETER Path
        The script (or folder) the settings are looked up for.
 
    .PARAMETER Settings
        A settings file path or a hashtable in the file's shape, instead of searching.
 
    .PARAMETER Cache
        A hashtable the caller keeps across scripts so each folder is searched once.
 
    .EXAMPLE
        Get-IslSetting -Path .\Remediations\Widget\Detect.ps1
 
        The nearest settings file above Detect.ps1, validated, or an empty settings object.
    #>

    [CmdletBinding()]
    [OutputType('IntuneScriptLab.Settings')]
    param(
        [Parameter(Mandatory)]
        [string]$Path,

        $Settings,

        [hashtable]$Cache = @{}
    )

    $validTypes = 'Detection', 'Remediation', 'PlatformScript', 'Win32Detection', 'Win32Requirement'
    $validSeverities = 'Information', 'Warning', 'Error'
    $knownKeys = 'ExcludeRule', 'IncludeRule', 'MinimumSeverity', 'Severity', 'ScriptType', 'Context',
    'Architecture', 'EnforceSignatureCheck'

    function ConvertTo-SettingsObject {
        param([hashtable]$Table, [string]$Source)
        $unknown = @($Table.Keys | Where-Object { $_ -notin $knownKeys })
        if ($unknown) {
            Write-Warning "Unknown settings key(s) in ${Source}: $($unknown -join ', ')"
        }
        if ($Table.MinimumSeverity -and "$($Table.MinimumSeverity)" -notin $validSeverities) {
            throw "${Source}: MinimumSeverity must be one of $($validSeverities -join ', ')"
        }
        if ($Table.ScriptType -and "$($Table.ScriptType)" -notin $validTypes) {
            throw "${Source}: ScriptType must be one of $($validTypes -join ', ')"
        }
        if ($Table.Context -and "$($Table.Context)" -notin 'System', 'User') {
            throw "${Source}: Context must be System or User"
        }
        if ($Table.Architecture -and "$($Table.Architecture)" -notin 'x86', 'x64', 'arm64') {
            throw "${Source}: Architecture must be x86, x64 or arm64"
        }
        $severity = @{}
        foreach ($key in @($Table.Severity.Keys | Where-Object { $null -ne $_ })) {
            if ("$($Table.Severity[$key])" -notin $validSeverities) {
                throw "${Source}: Severity for $key must be one of $($validSeverities -join ', ')"
            }
            $severity[$key] = "$($Table.Severity[$key])"
        }
        [pscustomobject]@{
            PSTypeName            = 'IntuneScriptLab.Settings'
            Source                = $Source
            ExcludeRule           = @($Table.ExcludeRule | Where-Object { $_ })
            IncludeRule           = @($Table.IncludeRule | Where-Object { $_ })
            MinimumSeverity       = if ($Table.MinimumSeverity) { "$($Table.MinimumSeverity)" } else { $null }
            Severity              = $severity
            ScriptType            = if ($Table.ScriptType) { "$($Table.ScriptType)" } else { $null }
            Context               = if ($Table.Context) { "$($Table.Context)" } else { $null }
            Architecture          = if ($Table.Architecture) { "$($Table.Architecture)" } else { $null }
            EnforceSignatureCheck = if ($Table.ContainsKey('EnforceSignatureCheck')) {
                [bool]$Table.EnforceSignatureCheck
            }
            else { $null }
        }
    }

    if ($null -ne $Settings) {
        if ($Settings -is [hashtable]) { return ConvertTo-SettingsObject -Table $Settings -Source '-Settings' }
        $file = (Resolve-Path -LiteralPath "$Settings" -ErrorAction Stop).ProviderPath
        return ConvertTo-SettingsObject -Table (Import-PowerShellDataFile -LiteralPath $file) -Source $file
    }

    $resolved = (Resolve-Path -LiteralPath $Path -ErrorAction Stop).ProviderPath
    $folder = if (Test-Path -LiteralPath $resolved -PathType Container) { $resolved }
    else { Split-Path -Path $resolved -Parent }
    if ($Cache.ContainsKey($folder)) { return $Cache[$folder] }

    $found = $null
    $probe = $folder
    while ($probe) {
        $candidate = Join-Path -Path $probe -ChildPath 'IntuneScriptLab.settings.psd1'
        if (Test-Path -LiteralPath $candidate -PathType Leaf) {
            $found = ConvertTo-SettingsObject -Table (Import-PowerShellDataFile -LiteralPath $candidate) `
                -Source $candidate
            break
        }
        $probe = Split-Path -Path $probe -Parent
    }
    if (-not $found) { $found = ConvertTo-SettingsObject -Table @{} -Source '' }
    $Cache[$folder] = $found
    $found
}