Functions/ModuleManagement/Get-BcModule.ps1

function Get-BcModule 
{
    [CmdletBinding(DefaultParameterSetName='ServerInstance')]
    Param
    ( 
        # Gets the modules compatible with the supplied serverinstance.
        [Parameter(ParameterSetName='ServerInstance', Mandatory = $true)]
        [string] $ServerInstance,    
        
        # Gets the modules compatible with the supplied BcVersion. E.g. 'bc15' or 'bc13'.
        [Parameter(ParameterSetName='BcVersion', Mandatory = $true)]
        [string] $BcVersion,

        # Gets the modules compatible with the supplied BC Service installation path.
        [Parameter(ParameterSetName='BcInstallationPath', Mandatory = $true)]
        [string] $BcInstallationPath,

        # The target computers to retrieve the BC module paths from.
        [string] $Computer = $env:COMPUTERNAME,
        
        [switch] $ManagementModule,

        [switch] $AppManagementModule,

        [switch] $AppToolsModule,

        [switch] $ModelToolsModule
    )

    Write-Verbose ('hostname: {0}, computer: {1}, PSVersion {2}' -f $env:COMPUTERNAME, $Computer, $PSVersionTable.PsVersion.Major)

    # If not a specific module is specified import all modules.
    if( $ManagementModule    -eq $false -and
        $AppManagementModule -eq $false -and 
        $AppToolsModule      -eq $false -and
        $ModelToolsModule    -eq $false) 
    {
        $ManagementModule    = $true
        $AppManagementModule = $true
        $AppToolsModule      = $true
        $ModelToolsModule    = $true
    }
    
    if($BcVersion){
        if((Get-BcVersion -BcVersion $BcVersion) -eq $false){
            "The supplied BC/NAV version '{0}' is not valid. Valid values are for example 'BC15', 'BC13', 'NAV2017'" -f $BcVersion | Write-Warning
            return
        }
        [hashtable] $BcVersion = Get-BcVersion -BcVersion $BcVersion
    }

    # Get BC version from Service
    if($ServerInstance){
        $ServerInstanceConfig = Get-BCServerInstance -ServerInstance $ServerInstance -Computer $Computer -WarningAction SilentlyContinue
        if(-not $ServerInstanceConfig -or $ServerInstanceConfig.Version -eq '1.0.0.0'){
            'Could not look up the version number for Server Instance ''{0}''. Make sure the Server Instance exists and the installation is valid.' -f 
                $ServerInstance | Write-Warning
            return
        }
        $BcVersionFolder = ([version] $ServerInstanceConfig.Version).Major * 10 #([version](Get-BCServerInstance -ServerInstance $ServerInstance).Version).Major * 10
        [hashtable] $BcVersion = Get-BcVersion -VersionFolder $BcVersionFolder
    }

    # Get BC version from installation directory
    if($BcInstallationPath){

        $regex = '.*\\(?<VersionFolder>\d{3})\\.*'

        $match = [regex]::Match($BcInstallationPath, $regex);
        if ($match.Success){
            $versionFolder = ($match.Groups | Where-Object Name -eq 'VersionFolder').Value
            [hashtable] $BcVersion = Get-BcVersion -VersionFolder $versionFolder
        }
    }

    # Validate PowerShell version
    if ($BcVersion.VersionFolder -ge 240 -and $PSVersionTable.PSVersion.Major -lt 7) {
        Write-Warning 'Searching for the BC PowerShell 5 legacy modules because the current PowerShell session is lower than version 7.'
    }
    if ($BcVersion.VersionFolder -ge 260 -and $PSVersionTable.PSVersion.Major -lt 7) {
        Write-Error 'PowerShell modules compatible with PowerShell 5 are not available for BC26 and up.'
        return
    }

    # Disable app modules for Dynamics NAV.
    if($BcVersion.ProductAbb -eq 'NAV'){
        $AppManagementModule = $false
        $AppToolsModule      = $false
    }
    # Disable model tools, merged into management module in bc18
    if([int] $BcVersion.VersionFolder -gt 170){
        $ModelToolsModule = $false
    }

    # Get installation folders
    $BcComponent = Get-BcComponent -BcVersion $BcVersion.Version -Computer $Computer
    $Service = $BcComponent | Where-Object Component -eq 'NST'
    if([int] $BcVersion.VersionFolder -lt 180){
        $Rtc = $BcComponent | Where-Object Component -eq 'RTC'
    }

    if (-not $Service.IsInstalled) {
        Write-Warning 'Cannot find the BC installation folder in the Windows Registery.'
        $Service.InstallLocation = 'C:\Program Files\Microsoft Dynamics 365 Business Central\{0}\Service' -f $BcVersion.VersionFolder
    }

    if($BcInstallationPath){
        $Service.InstallLocation = $BcInstallationPath
    } 

    # Convert installation path to UNC path if it's on a remote computer.
    if ($Computer -ne 'localhost' -and $Computer -ne $env:COMPUTERNAME) {
        $servicePath = $Service.InstallLocation | ConvertTo-UncPath -Computer $Computer
        if($rtc.InstallLocation){
            $rtcPath = $rtc.InstallLocation | ConvertTo-UncPath -Computer $Computer
        }
    } else {
        $servicePath = $Service.InstallLocation
        $rtcPath = $rtc.InstallLocation
    }

    if((Test-Path $servicePath) -eq $false){ 
        Write-Warning 'Cannot find the BC installation folder on the default location. Make sure the BC server component is installed.'
        return $false
    }

    $Modules = @()

    if($ManagementModule){
        
        if($BcVersion.VersionFolder -ge 240 -and $PSVersionTable.PSVersion.Major -ge 7){
            $Modules +='Microsoft.BusinessCentral.Management'
        } else {
            $Modules += 'Microsoft.Dynamics.Nav.Management'
        }
    }
    if($AppManagementModule){

        if($BcVersion.VersionFolder -ge 240 -and $PSVersionTable.PSVersion.Major -ge 7){
            $Modules +='Microsoft.BusinessCentral.Apps.Management'
        } else {
            $Modules += 'Microsoft.Dynamics.Nav.Apps.Management'
        }
    }
    if($AppToolsModule){

        if($BcVersion.VersionFolder -ge 240 -and $PSVersionTable.PSVersion.Major -ge 7){
            $Modules +='Microsoft.BusinessCentral.Apps.Tools'
        } else {
            $Modules += 'Microsoft.Dynamics.Nav.Apps.Tools'
        }
    }
    if($ModelToolsModule){
        $Modules += 'Microsoft.Dynamics.Nav.Model.Tools'
    }

    $ModulesPath = @()

    foreach($Module in $Modules){

        $ModulePath = (Get-ChildItem (Join-Path -Path $servicePath -ChildPath ($Module + ".psd1")) -Recurse).fullname 

        if($ModulePath -and (Test-Path -Path $ModulePath)){
            $ModulesPath += $ModulePath; continue
        }

        if($BcVersion.VersionFolder -ge 240 -and $PSVersionTable.PSVersion.Major -lt 7){
            $ModulePath = (Get-ChildItem (Join-Path -Path $servicePath -ChildPath ($Module + ".psm1")) -ErrorAction SilentlyContinue).fullname 
        } else {
            $ModulePath = (Get-ChildItem (Join-Path -Path $servicePath -ChildPath ($Module + ".psm1")) -Recurse).fullname
        }

        if($ModulePath -and (Test-Path -Path $ModulePath)){
            $ModulesPath += $ModulePath; continue
        }

        if($rtcPath){
            $ModulePath = (Get-ChildItem (Join-Path -Path $rtcPath -ChildPath ($Module + ".psd1")) -Recurse).fullname
        }

        if($ModulePath -and (Test-Path -Path $ModulePath)){
            $ModulesPath += $ModulePath; continue
        }

        'Could not find module {0}.' -f $Module | Write-Warning
    }

    # Workaround BC24.4 FormatsToProcess file name issue
    if ('Microsoft.BusinessCentral.Apps.Tools.psd1' -in ($ModulesPath | Split-Path -Leaf)){
        $appMgntPath = Join-Path (($ModulesPath | Where-Object { $_ -like '*\Microsoft.BusinessCentral.Apps.Tools.psd1'}) | Split-Path -Parent) 'Microsoft.BusinessCentral.Apps.Tools.format.ps1xml'

        if((Test-Path $appMgntPath) -eq $false){
            $appMgntPathOld = Join-Path ($appMgntPath | Split-Path -Parent) 'Microsoft.Dynamics.Nav.Apps.Tools.format.ps1xml'

            if(Test-Path $appMgntPathOld){
                'Workaround applied. Renamed "{0}" to "{1}".' -f $appMgntPathOld, $appMgntPath | Write-Host
                Rename-Item -Path $appMgntPathOld -NewName $appMgntPath
            }
        }
    }
    if ('Microsoft.BusinessCentral.Management.psd1' -in ($ModulesPath | Split-Path -Leaf)){
        $appMgntPath = Join-Path (($ModulesPath | Where-Object { $_ -like '*\Microsoft.BusinessCentral.Management.psd1'}) | Split-Path -Parent) 'Microsoft.BusinessCentral.Apps.Management.format.ps1xml'

        if((Test-Path $appMgntPath) -eq $false){
            $appMgntPathOld = Join-Path ($appMgntPath | Split-Path -Parent) 'Microsoft.Dynamics.Nav.Apps.Management.format.ps1xml'

            if(Test-Path $appMgntPathOld){
                'Workaround applied. Renamed "{0}" to "{1}".' -f $appMgntPathOld, $appMgntPath | Write-Host
                Rename-Item -Path $appMgntPathOld -NewName $appMgntPath
            }
        }
    }
    # End workaround

    $ModulesPath | ConvertTo-LocalPath
}

Export-ModuleMember -Function Get-BcModule


function ConvertTo-UncPath {

    Param (
        [Parameter(Mandatory=$true, 
                   ValueFromPipeline=$true, 
                   ValueFromPipelineByPropertyName=$true)]
        [string[]]
        $Path,
        
        [string]
        $Computer = $env:COMPUTERNAME
    )

    begin{
        $Paths = @()
    }

    process {
        foreach($Item in $Path){
            if($Item -match '\\\\'){ 
                $Paths += $Item
                continue } 
            $Qualifier = $Item | Split-Path -Qualifier
            $NewQualifier = '\\{0}\{1}' -f $Computer, $Qualifier.Replace(':','$').ToLower()
            $NewPath = $Item.Replace($Qualifier, $NewQualifier)
            $Paths += $NewPath
        }
    }

    end{
        $Paths
    }
}

function ConvertTo-LocalPath {

    Param (
        [Parameter(Mandatory=$true, 
                   ValueFromPipeline=$true, 
                   ValueFromPipelineByPropertyName=$true)]
        [string[]]
        $Path
    )

    begin{
        $Paths = @()
    }

    process {
        foreach($Item in $Path){
            if($Item -notmatch '\\\\'){ 
                $Paths += $Item
                continue 
            } 
            $Drive = [System.IO.Path]::GetPathRoot($Item)
            $Dumps = $Item.Substring($Drive.Length)
            $Drive = $Drive.Substring($Drive.LastIndexOf('\') + 1).Replace('$',':')
            $Paths += "{0}{1}" -f $Drive.ToUpper(), $Dumps
        }
    }

    end{
        $Paths
    }
}