Private/Export-IBConfig.ps1

function Export-IBConfig
{
    [CmdletBinding()]
    param()

    $curProfile = Get-CurrentProfile
    $profiles = Get-Profiles

    # For vault-based config storage, each profile will exist as a unique secret
    # in the vault. For local config storage, we'll continue using a single JSON
    # file as in 3.x.

    if ($vaultCfg = Get-VaultConfig) {
        if ($vaultProfiles = Get-VaultProfiles -VaultConfig $vaultCfg) {

            # delete any vault profiles that no longer exist in memory
            foreach ($profName in @($vaultProfiles.Keys)) {
                if ($profName -notin $profiles.Keys) {
                    $secretName = $vaultCfg.Template -f $profName
                    Write-Debug "Removing vault profile '$secretName'."
                    $vaultProfiles.Remove($profName)
                    Remove-Secret -Vault $vaultCfg.Name -Name $secretName
                }
            }
        }
    }
    else {
        # prep the local config object we'll be converting to JSON later
        $cfgToExport = @{
            CurrentProfile = $curProfile
            Profiles = @{}
        }
    }

    foreach ($profName in $profiles.Keys) {

        $profRaw = @{
            WAPIHost             = $profiles.$profName.WAPIHost
            WAPIVersion          = $profiles.$profName.WAPIVersion
            Credential           = $null
            SkipCertificateCheck = $profiles.$profName.SkipCertificateCheck
        }

        if ($vaultCfg) {
            # Since we can't rely on secret metadata to store whether this profile
            # is the current profile, we'll just add it to the JSON blob instead.
            $profRaw.Current = if ($curProfile -eq $profName) { $true } else { $false }
        }
        else {
            # add the raw profile to the export object
            $cfgToExport.Profiles.$profName = $profRaw
        }

        # deal with the credential
        $credSerialized = @{
            Username = $profiles.$profName.Credential.Username
        }

        # For vault storage, we're going to leave the serialized password
        # as plain text and rely on the vault's native encryption to protect it.
        # For local storage, we'll continue to use the DPAPI/Base64 method
        # depending on the platform since there's still no good way to encrypt
        # serialized SecureString values cross-platform in PowerShell 7+ unless
        # you provide your own key.

        if ($vaultCfg) {
            # store the plaintext password
            $credSerialized.Password = $profiles.$profName.Credential.GetNetworkCredential().Password
        }
        elseif ($IsWindows -or $PSEdition -eq 'Desktop') {
            # store the DPAPI encrypted password
            $credSerialized.Password = ConvertFrom-SecureString $profiles.$profName.Credential.Password
        }
        else {
            # store the Base64 encoded password
            $passPlain = $profiles.$profName.Credential.GetNetworkCredential().Password
            $credSerialized.Password = [Convert]::ToBase64String(
                [Text.Encoding]::Unicode.GetBytes($passPlain)
            )
            $credSerialized.IsBase64 = $true
        }

        $profRaw.Credential = $credSerialized

        # For vault profiles, we only want to store if it changed or doesn't already exist
        if ($vaultCfg) {

            $vaultProf = $vaultProfiles.$profName

            if ($profName -notin $vaultProfiles.Keys -or
                $profRaw.WAPIHost             -ne $vaultProf.WAPIHost -or
                $profRaw.WAPIVersion          -ne $vaultProf.WAPIVersion -or
                $profRaw.Credential.Username  -ne $vaultProf.Credential.Username -or
                $profRaw.Credential.Password  -ne $vaultProf.Credential.Password -or
                $profRaw.SkipCertificateCheck -ne $vaultProf.SkipCertificateCheck -or
                $profRaw.Current              -ne $vaultProf.Current
            ) {

                $secretName = $vaultCfg.Template -f $profName
                Write-Debug "Storing vault profile '$secretName'."
                $secretJson = $profRaw | ConvertTo-Json -Compress

                Set-Secret -Vault $vaultCfg.Name -Name $secretName -Secret $secretJson
            }


        }

    }

    # we're done if we used the vault
    if ($vaultCfg) { return }

    # otherwise, save to disk if we have anything to save
    if ($profiles.Count -gt 0) {
        # Make sure the config folder exists
        $configFolder = Get-ConfigFolder

        New-Item $configFolder -Type Directory -ErrorAction Ignore

        # Save it to disk
        $configFile = Get-ConfigFile
        $cfgToExport | ConvertTo-Json -Depth 5 | Out-File $configFile -Encoding utf8
    }

}