Modules/businessdev.ALbuild.Containers/Private/ConvertTo-BcEncodedCommand.ps1

function ConvertTo-BcEncodedCommand {
    <#
    .SYNOPSIS
        Builds a Base64 -EncodedCommand payload for running a script block inside a container.
    .DESCRIPTION
        Internal, pure helper. Prepends a strict error preference, an import of the Business Central
        management module (the 'docker exec ... powershell -NoProfile' session does not load it, so
        NAV cmdlets such as Publish-NAVApp would otherwise be unrecognised) and optional variable
        assignments (passed as JSON to survive the process boundary), then UTF-16LE/Base64 encodes
        the result for 'powershell -EncodedCommand'. Pure (no I/O), so it is unit-testable.
    #>

    [CmdletBinding()]
    [OutputType([string])]
    param(
        [Parameter(Mandatory)]
        [scriptblock] $ScriptBlock,

        [hashtable] $Variables = @{},

        # Return the assembled script text instead of the Base64 payload. The caller can then decide
        # whether to pass it via -EncodedCommand or - for scripts too long for the command line - via
        # a staged -File.
        [switch] $AsText
    )

    # Make the BC management cmdlets (Get-NAVAppInfo, Publish-/Sync-/Install-NAVApp, Get-NAVServerInstance,
    # ...) available inside the container; the 'docker exec' session does not auto-load them. Both the
    # assembly NAME/location and the PowerShell edition that can load it vary by BC version:
    # Windows PowerShell (.NET Framework) -> Microsoft.Dynamics.Nav.*.Management.dll (BC <= ~27)
    # PowerShell 7 (.NET) -> Microsoft.BusinessCentral.*.Management.dll (BC 28+, .NET 8)
    # Pick the assemblies matching the running edition (a .NET 8 DLL won't load in Windows PowerShell and
    # vice versa), search recursively (location moved across versions), and import each until both the app
    # and server cmdlets resolve. Tolerant (no throw) so commands needing no NAV cmdlets still run.
    $importNavManagement = @'
if (-not (Get-Command 'Get-NAVAppInfo' -ErrorAction SilentlyContinue) -or -not (Get-Command 'Get-NAVServerInstance' -ErrorAction SilentlyContinue)) {
    $__navDllNames = if ($PSVersionTable.PSEdition -eq 'Core') {
        @('Microsoft.BusinessCentral.Apps.Management.dll', 'Microsoft.BusinessCentral.Management.dll',
          'Microsoft.Dynamics.Nav.Apps.Management.dll', 'Microsoft.Dynamics.Nav.Management.dll')
    } else {
        @('Microsoft.Dynamics.Nav.Apps.Management.dll', 'Microsoft.Dynamics.Nav.Management.dll')
    }
    foreach ($__navDll in $__navDllNames) {
        if ((Get-Command 'Get-NAVAppInfo' -ErrorAction SilentlyContinue) -and (Get-Command 'Get-NAVServerInstance' -ErrorAction SilentlyContinue)) { break }
        foreach ($__navFile in @(Get-ChildItem 'C:\Program Files\Microsoft Dynamics NAV' -Recurse -Filter $__navDll -ErrorAction SilentlyContinue)) {
            try { Import-Module $__navFile.FullName -DisableNameChecking -ErrorAction Stop } catch { }
        }
    }
}
'@


    $sb = [System.Text.StringBuilder]::new()
    [void]$sb.AppendLine('$ErrorActionPreference = ''Stop''')
    [void]$sb.AppendLine($importNavManagement)
    foreach ($key in $Variables.Keys) {
        $json = ($Variables[$key] | ConvertTo-Json -Compress -Depth 20)
        $escaped = $json -replace "'", "''"
        [void]$sb.AppendLine("`$$key = '$escaped' | ConvertFrom-Json")
    }
    [void]$sb.AppendLine($ScriptBlock.ToString())

    if ($AsText) { return $sb.ToString() }
    $bytes = [System.Text.Encoding]::Unicode.GetBytes($sb.ToString())
    return [System.Convert]::ToBase64String($bytes)
}