Private/Set-CIEMConfig.ps1

function Set-CIEMConfig {
    <#
    .SYNOPSIS
        Updates the CIEM configuration in config.json.

    .DESCRIPTION
        Accepts a hashtable of settings to update, reads the existing config.json,
        merges user-provided settings, writes back to config.json, and reloads
        the $script:Config variable.

        Dev-only fields (prowler.*, checksPath, azure.endpoints.*) are preserved
        and not exposed through this function.

    .PARAMETER Settings
        A hashtable containing the settings to update. Supports nested paths using
        dot notation in the hashtable keys.

    .PARAMETER ConfigPath
        Optional path to a custom config.json file. If not specified,
        uses the default config.json in the module root.

    .EXAMPLE
        Set-CIEMConfig -Settings @{
            'azure.authentication.method' = 'ServicePrincipal'
            'azure.authentication.servicePrincipal.tenantId' = '12345-6789'
            'scan.throttleLimit' = 20
        }

    .EXAMPLE
        # Reset to defaults
        $defaults = @{
            'azure.authentication.method' = 'ServicePrincipalSecret'
            'azure.subscriptionFilter' = @()
            'scan.throttleLimit' = 10
            'scan.timeoutSeconds' = 300
            'scan.continueOnError' = $true
            'output.verboseLogging' = $false
            'pam.remediationUrl' = 'https://devolutions.net/pam'
        }
        Set-CIEMConfig -Settings $defaults
    #>

    [CmdletBinding(SupportsShouldProcess)]
    param(
        [Parameter(Mandatory)]
        [hashtable]$Settings,

        [Parameter()]
        [string]$ConfigPath = (Join-Path $script:ModuleRoot 'config.json')
    )

    begin {
        $ErrorActionPreference = 'Stop'

        # Helper function to set nested hashtable values using dot notation
        function Resolve-NestedHashtableValue {
            param(
                [hashtable]$Hashtable,
                [string]$Path,
                $Value
            )

            $parts = $Path -split '\.'
            $current = $Hashtable

            for ($i = 0; $i -lt $parts.Count - 1; $i++) {
                $part = $parts[$i]
                if (-not $current.ContainsKey($part)) {
                    $current[$part] = @{}
                }
                elseif ($current[$part] -isnot [hashtable]) {
                    # Convert PSCustomObject to hashtable if needed
                    $current[$part] = @{}
                }
                $current = $current[$part]
            }

            $finalKey = $parts[-1]
            $current[$finalKey] = $Value
        }
    }

    process {
        if (-not (Test-Path $ConfigPath)) {
            throw "Configuration file not found: $ConfigPath"
        }

        # Read existing config to preserve dev-only fields
        $config = Get-Content $ConfigPath -Raw | ConvertFrom-Json -AsHashtable

        # Apply each setting from the provided hashtable
        foreach ($key in $Settings.Keys) {
            $value = $Settings[$key]

            # Skip dev-only fields if someone tries to set them
            if ($key -match '^prowler\.' -or $key -eq 'checksPath' -or $key -match '^azure\.endpoints\.') {
                Write-Warning "Skipping dev-only setting: $key"
                continue
            }

            Resolve-NestedHashtableValue -Hashtable $config -Path $key -Value $value
        }

        if ($PSCmdlet.ShouldProcess($ConfigPath, 'Update configuration')) {
            # Write the updated config back to file
            $jsonContent = $config | ConvertTo-Json -Depth 10
            Set-Content -Path $ConfigPath -Value $jsonContent -Encoding UTF8

            # Reload $script:Config
            $script:Config = Get-CIEMConfig -ConfigPath $ConfigPath

            Write-Verbose "Configuration updated successfully"
        }
    }
}