Get-ALOpsBCManagementDlls.ps1

function Get-ALOpsBCModuleManifestRoot()
{
    param(
        [string] $ManifestPath
    )

    # Returns the RootModule declared by a PowerShell module manifest, so the caller can tell a
    # pure-PowerShell module (BC 29 and later, RootModule = *.psm1) from a manifest that only wraps
    # a binary module (BC 28 Admin folder, RootModule = *.dll). Empty when it cannot be determined.
    $RootModule = ""

    try {
        $Manifest = Import-PowerShellDataFile -Path $ManifestPath -ErrorAction Stop
        $RootModule = "$($Manifest.RootModule)"
        if ([string]::IsNullOrEmpty($RootModule)){
            $RootModule = "$($Manifest.ModuleToProcess)"
        }
    } catch {
        foreach ($Line in (Get-Content -Path $ManifestPath -ErrorAction SilentlyContinue)){
            $Trimmed = "$($Line)".Trim()
            if ($Trimmed.StartsWith("RootModule") -or $Trimmed.StartsWith("ModuleToProcess")){
                $Parts = $Trimmed.Split("=")
                if ($Parts.Count -gt 1){
                    $RootModule = $Parts[1].Trim().Trim("'").Trim('"')
                    break
                }
            }
        }
    }

    return $RootModule
}

function Get-ALOpsBCManagementDlls()
{
    param(
        [string] $SearchPath
    )

    # Copy of the ALOps helper (ALOps repo, PSModules\ALOpsV2\Get-ALOpsBCManagementDlls.ps1); keep in sync.
    # Locate the Business Central / NAV management PowerShell modules (GitHub #998, #1016).
    # Tier 1: Microsoft.BusinessCentral.Management.dll outside the Admin folder (BC24+ preview layout).
    # Tier 2: Microsoft.Dynamics.Nav.Management.dll (BC14-BC28, .NET Framework build, loads in PS 5.1 and PS7).
    # Tier 3: the Service\Admin folder (BC29+). BC29 ships Microsoft's pure-PowerShell modules (.psd1 + .psm1,
    # REST based) next to a .NET 10 binary that can no longer be imported in either PowerShell edition.
    # The module manifest is therefore preferred; the .dll stays as fallback for a BC28 Admin layout.
    # The Apps module follows the tier of the management module so BC14-BC28 behaviour stays unchanged:
    # on BC24-BC28 the Admin folder also holds a PowerShell 7 only Apps module that must NOT be picked up.
    $Tier = 0
    $ManagementDll = $null
    $AppsManagementDll = $null
    $IsModuleManifest = $false

    if (-not [string]::IsNullOrEmpty($SearchPath)){
        $ManagementDll = Get-ChildItem -Path $SearchPath -Filter "Microsoft.BusinessCentral.Management.dll" -Recurse -ErrorAction SilentlyContinue | Where-Object { ($_.FullName -notmatch '[\\/]Admin[\\/]') -and (-not $_.FullName.Contains("LegacyDlls")) } | Select-Object -First 1
        if ($null -ne $ManagementDll){
            $Tier = 1
        } else {
            $ManagementDll = Get-ChildItem -Path $SearchPath -Filter "Microsoft.Dynamics.Nav.Management.dll" -Recurse -ErrorAction SilentlyContinue | Where-Object { -not $_.FullName.Contains("LegacyDlls") } | Select-Object -First 1
            if ($null -ne $ManagementDll){
                $Tier = 2
            } else {
                # Only treat the manifest as the target when it really is a script module: the BC 28
                # Admin manifest wraps the binary and must keep the PowerShell 7 handling below.
                $ManifestCandidate = Get-ChildItem -Path $SearchPath -Filter "Microsoft.BusinessCentral.Management.psd1" -Recurse -ErrorAction SilentlyContinue | Where-Object { -not $_.FullName.Contains("LegacyDlls") } | Select-Object -First 1
                if (($null -ne $ManifestCandidate) -and ((Get-ALOpsBCModuleManifestRoot -ManifestPath $ManifestCandidate.FullName) -notlike "*.dll")){
                    $ManagementDll = $ManifestCandidate
                    $Tier = 3
                    $IsModuleManifest = $true
                } else {
                    $ManagementDll = Get-ChildItem -Path $SearchPath -Filter "Microsoft.BusinessCentral.Management.dll" -Recurse -ErrorAction SilentlyContinue | Where-Object { -not $_.FullName.Contains("LegacyDlls") } | Select-Object -First 1
                    if ($null -ne $ManagementDll){
                        $Tier = 3
                    }
                }
            }
        }

        if ($Tier -eq 3){
            if ($IsModuleManifest){
                $AppsCandidate = $null
                $Candidate = Join-Path -Path $ManagementDll.DirectoryName -ChildPath "Microsoft.BusinessCentral.Apps.Management.psd1"
                if (Test-Path -Path $Candidate){
                    $AppsCandidate = Get-Item -Path $Candidate
                } else {
                    $AppsCandidate = Get-ChildItem -Path $SearchPath -Filter "Microsoft.BusinessCentral.Apps.Management.psd1" -Recurse -ErrorAction SilentlyContinue | Where-Object { -not $_.FullName.Contains("LegacyDlls") } | Select-Object -First 1
                }
                if (($null -ne $AppsCandidate) -and ((Get-ALOpsBCModuleManifestRoot -ManifestPath $AppsCandidate.FullName) -notlike "*.dll")){
                    $AppsManagementDll = $AppsCandidate
                }
            }
            if ($null -eq $AppsManagementDll){
                $Candidate = Join-Path -Path $ManagementDll.DirectoryName -ChildPath "Microsoft.BusinessCentral.Apps.Management.dll"
                if (Test-Path -Path $Candidate){
                    $AppsManagementDll = Get-Item -Path $Candidate
                } else {
                    $AppsManagementDll = Get-ChildItem -Path $SearchPath -Filter "Microsoft.BusinessCentral.Apps.Management.dll" -Recurse -ErrorAction SilentlyContinue | Where-Object { -not $_.FullName.Contains("LegacyDlls") } | Select-Object -First 1
                }
            }
        } elseif ($Tier -gt 0){
            $AppsManagementDll = Get-ChildItem -Path $SearchPath -Filter "Microsoft.BusinessCentral.Apps.Management.dll" -Recurse -ErrorAction SilentlyContinue | Where-Object { ($_.FullName -notmatch '[\\/]Admin[\\/]') -and (-not $_.FullName.Contains("LegacyDlls")) } | Select-Object -First 1
            if ($null -eq $AppsManagementDll){
                $AppsManagementDll = Get-ChildItem -Path $SearchPath -Filter "Microsoft.Dynamics.Nav.Apps.Management.dll" -Recurse -ErrorAction SilentlyContinue | Where-Object { -not $_.FullName.Contains("LegacyDlls") } | Select-Object -First 1
            }
        }
    }

    $TierLabel = ""
    switch ($Tier) {
        1 { $TierLabel = "tier 1, BusinessCentral" }
        2 { $TierLabel = "tier 2, Dynamics.Nav" }
        3 {
            if ($IsModuleManifest){
                $TierLabel = "tier 3, Admin folder, PowerShell module manifest"
            } else {
                $TierLabel = "tier 3, Admin folder, PowerShell 7 module"
            }
        }
    }
    if ($Tier -eq 0){
        Write-Host "*** BC Management DLL: not found under [$($SearchPath)]"
    } else {
        Write-Host "*** BC Management DLL [$($TierLabel)]: $($ManagementDll.FullName)"
    }

    # The BC29 module manifest is pure PowerShell and imports in Windows PowerShell 5.1 as well as in
    # PowerShell 7; only the binary fallback is PowerShell 7 only.
    $RequiresPowerShell7 = (($Tier -eq 3) -and (-not $IsModuleManifest))
    if ($RequiresPowerShell7 -and ($PSVersionTable.PSEdition -ne "Core")){
        Write-Warning "The Business Central management module [$($ManagementDll.FullName)] is a PowerShell 7 binary module. This session runs Windows PowerShell $($PSVersionTable.PSVersion). Run the command in PowerShell 7 or make sure PowerShell 7 is installed in the container."
    }

    return [PSCustomObject]@{
        ManagementDll       = $ManagementDll
        AppsManagementDll   = $AppsManagementDll
        Tier                = $Tier
        IsModuleManifest    = $IsModuleManifest
        RequiresPowerShell7 = $RequiresPowerShell7
    }
}