Functions/Get-BcServerInstance.ps1

<#
.SYNOPSIS
    Returns managable object(s) for all or the specified Business Central Server instance(s).
.DESCRIPTION
    Use the Get-BCServerInstance cmdlet to obtain service details for all or the specified Business Central Server instances.
     
    When not a service instance is provided all instances of all versions for the specific machine are returned.
    With VersionFilter, StateFilter and ExcludeServerInstance the returned list can be filtered.
.EXAMPLE
    # Get managable objects for each local installed BC server instances
    Get-BCServerInstance
.EXAMPLE
    # Get managable objects for each BC server instances from the remote computer.
    # Remote PowerShell should be enabled on the target computer.
    Get-BCServerInstance -Computer 'remote.domain.eu'
 
    # Multiple remote computers can be specified
    Get-BCServerInstance -Computer @('DevSrv01', 'DevSrv02', 'DevSrv03')
.EXAMPLE
    # Get a managable object for the BC server instanced named 'BC210'.
    Get-BCServerInstance -ServiceInstance 'BC210'
.EXAMPLE
    # Get all the server instances that match a certain filter condition.
 
    # Get all ServerInstances that are stopped
    Get-BCServerInstance -StateFilter Stopped | Format-Table
 
    # Get all ServerInstances that start automatically on server startup
    Get-BCServerInstance -StartModeFilter Auto | Format-Table
 
    # Get all Business Central 21 ServerInstances, except ServerInstance BC210
    Get-BCServerInstance -VersionFilter '21' -ExcludeServerInstance 'BC210'
 
    # Combine filters to narrow result. Get all BC services that:
    # - Start automatically on server startup
    # - Are currently stopped
    # - Are on the BC21 platform
    # - ServerInstance name is not BC210.
    Get-BCServerInstance -StateFilter Stopped -StartModeFilter Auto -VersionFilter '21' -ExcludeServerInstance 'BC210'
.EXAMPLE
    # Example managing the application settings
     
    # Get the managable object for BC server instance BC210
    $ServerInstance = Get-BCServerInstance -ServiceInstance 'BC210'
     
    # Print the application configuration to host
    $ServerInstance.AppSettings
 
    # Search for keys in the app settings
    $ServerInstance.AppSettings.Search('ServicesEnabled')
    $ServerInstance.AppSettings.Search('ServicesPort')
 
    # Get the Microsoft help text for a specific key
    ServerInstance.AppSettings.Help('SqlLockTimeoutOverride')
 
    # Update a key to a new value. The new value is saved to the config file automatically.
    $ServerInstance.AppSettings.DatabaseName = 'CRONUS'
    $ServerInstance.AppSettings.DeveloperServicesEnabled = 'true'
#>

function Get-BCServerInstance {
    Param (
        # The name of the BC Server instance. E.g. 'BC210'
        # Multiple server instances can be specified. E.g. @('bc190', 'bc200').
        # Supports wildcards. E.g. 'BC*' would returns both BC200 and BC210 instances.
        [Parameter(
            Position = 0,
            ValueFromPipeline=$true, 
            ValueFromPipelineByPropertyName = $true)]
        [string[]] $ServerInstance,

        # Applies a filter on the version number of the BC server instances.
        # E.g. '21' to return only the server instances on BC 2022 Wave 2 (BC21).
        [Parameter(ValueFromPipelineByPropertyName = $true)]
        [string] $VersionFilter,

        # Applies a filter on the current state of the BC service.
        # E.g. 'Running' will return all BC server instances that are currently running.
        [Parameter(ValueFromPipelineByPropertyName = $true)]
        [ValidateSet("Running", "Stopped", "Starting", "Stopping", "All")]
        [string] $StateFilter = "All",

        # Applies a filter on the Windows Service start-up mode of the BC server instance.
        # E.g. 'Auto' will return all the server instances that automatically start up on server startup.
        [Parameter(ValueFromPipelineByPropertyName = $true)]
        [ValidateSet("Auto", "Manual", "Disabled", "All")]
        [string] $StartModeFilter = 'All',

        # Applies a filter on BC server instance name to exclude server instance(s) from the result.
        # E.g. 'BC210' will exclude the BC service 'BC210' from the returned BC server instances.
        # E.g. @('BC210', 'BC200') will exclude both services.
        [Parameter(ValueFromPipelineByPropertyName = $true)]
        [string[]] $ExcludeServerInstance,

        # The targeted computers to retrieve the BC server instance(s) from.
        # E.g. 'DevSrv01.domain.com' will retrieve all server instances from the specified computer.
        # Multiple computers can be specified. E.g. @('DevSrv01', 'DevSrv02').
        # Note that remote PowerShell should be enabled on the targeted computer.
        [Parameter(ValueFromPipelineByPropertyName = $true, Position = 1)]
        [string[]] $Computer = $env:COMPUTERNAME
    )
    
    begin {       
        $results = @()

        for ($i = 0; $i -lt $ServerInstance.Count; $i++) {
            if($ServerInstance[$i] -like 'MicrosoftDynamicsNavServer$*'){
                $ServerInstance[$i] = $ServerInstance[$i].Split('$')[1]
            }
        }        

        [scriptblock] $ScriptBlock = {
            param(
                $ServerInstance,
                $VersionFilter,
                $StateFilter,
                $StartModeFilter,
                $ExcludeServerInstance
            )

            function Get-ServiceVersion {
                param(
                    [Parameter(ValueFromPipeline=$true, Mandatory=$true)]
                    [System.Management.ManagementBaseObject] $Service
                )

                $Regex = '.*"(?<ServicePath>.*?.exe)".*'
                $Match = $Service.PathName | Select-String -Pattern $Regex
                $ExecutablePath = $Match.Matches[0].Groups['ServicePath'].Value
                
                if((Test-Path $ExecutablePath)){
                    [version] $ExecutableVersion = (Get-Item $ExecutablePath).VersionInfo.FileVersion
                } else {
                    Write-Warning ('The Business Central installation is not found for Server Instance ''{0}'' on location: {1}' -f 
                        ($Service.Name.Split('$'))[1], $ExecutablePath)
                    return [version] '0.0.0.0'
                }
                return $ExecutableVersion
            }
            
            $results = @()

            # Get Business Central Service Instances
            $bcServices = @()

            if($ServerInstance){
                foreach ($instance in $ServerInstance){
                    $bcServices += Get-WmiObject win32_service | Where-Object Name -like ('MicrosoftDynamicsNavServer`${0}' -f $instance)
                }
            } 
            else{
                $bcServices = Get-WmiObject win32_service | Where-Object Name -like 'MicrosoftDynamicsNavServer*'
            }

            # Remove duplicated (can occure when wild cards are used in param $ServerInstance)
            $bcServices = $bcServices | Select-Object -Unique

            # Apply Exclude filter
            if($ExcludeServerInstance){
                $bcServices = $bcServices | ForEach-Object {
                    if($_.Name -notin $ExcludeServerInstance -and $_.Name.Split('$')[1] -notin $ExcludeServerInstance){
                        $_
                    }
                }
            }

            # Apply version filter
            if($VersionFilter){
                $bcServices = $bcServices | ForEach-Object {
                    $ServiceVersion = ($_ | Get-ServiceVersion).ToString()
                    if($ServiceVersion -like ('{0}*' -f $VersionFilter)){
                        $_
                    }
                }
            }

            # Apply start-up type filter
            if($StartModeFilter -and $StartModeFilter -ne 'All'){
                $bcServices = $bcServices | ForEach-Object {
                    if($_.StartMode -eq $StartModeFilter){
                        $_
                    }
                }
            }
            
            # Apply State filter
            if($StateFilter -and $StateFilter -ne 'All'){
                $bcServices = $bcServices | ForEach-Object {
                    if($_.State -eq $StateFilter){
                        $_
                    }
                }            
            }

            if($bcServices.count -eq 0){
                'No Server Instance found. Make sure the Server Instance exists and is not exluded with the set filters.' | Write-Warning
                return
            }

            foreach ($service in $BcServices){
                $results += @{
                    'Service'  = $service.Name
                    'Computer' = $env:COMPUTERNAME
                }
            }

            return $results
        }
    }
    
    process {
        foreach ($server in $Computer){
            
            # Do not add the computerName parameter if the host and target machine are the same.
            # This removes the requirement to have remote PowerShell enabled.
            $additionParams = @{}
            if ($server -ne 'localhost' -and $server -ne $env:COMPUTERNAME) {
                $additionParams = @{'ComputerName' = $server}
            }
            
            $result = Invoke-Command @additionParams -ScriptBlock $scriptBlock -ArgumentList `
                $ServerInstance,
                $VersionFilter,
                $StateFilter,
                $StartModeFilter,
                $ExcludeServerInstance

            $result | ForEach-Object {
                $results += [BcServerInstance]::new($_.Service, $_.Computer)
            }
        }
    }
    end {
        return $results
    }
}
Export-ModuleMember -Function Get-BCServerInstance