Tag: NAV Service Tier

  • Dynamics NAV Web Client – Server Error in Application

    When attempting to access the Dynamics NAV Web Client the following error is displayed in the browser:

    Dynamics NAV Web Client - Server Error in Application
    Dynamics NAV Web Client – Server Error in Application
    Server Error in '/' Application.
    
    Configuration Error
     Description: An error occurred during the processing of a configuration file required to service this request. Please review the specific error details below and modify your configuration file appropriately.
    
    Parser Error Message: It is an error to use a section registered as allowDefinition='MachineToApplication' beyond application level. This error can be caused by a virtual directory not being configured as an application in IIS.
    
    Source Error:
    
    Line 8:      <compilation debug="false" defaultLanguage="c#" targetFramework="4.0"/>
    Line 9:      <httpRuntime requestValidationMode="2.0" />
    Line 10:     <sessionState mode="InProc"/>
    Line 11:     <trust legacyCasModel="true" level="Full" />
    Line 12:     <!-- Uncomment to only allow https connection authentication mode
    
    Source File: C:\inetpub\wwwroot\sr-dev\webclient\web.config Line: 10

    As the parser error message states above “This error can be caused by a virtual directory not being configured as an application in IIS.”, we need to check that the Web Client directory in IIS has been set as an application. To do this:

    1. Open IIS Manager (hit the Windows key and type IIS) on the server hosting the Web Client components.
    2. Look for the Web Client directory, usually under Sites/Default Web Site.
    3. Expand to show the directory called WebClient, right click on this directory and select Convert to Application:
    Web Client Components - Convert to Application
    Web Client Components – Convert to Application

    After clicking Convert to Application a dialog box will pop up, click OK. You should notice the icon on the WebClient directory has changed.

    Try to open the Web Client again and hopefully the issue is fixed!

    Bonus points if anyone can tell me why it broke in the first place…

  • Update Dynamics NAV Licenses on Remote Hosts

    To update Dynamics NAV licenses of multiple databases used to be a bit of a headache. We have customers running independent NAV instances in the hundreds using LS Retail POS. Imagine having to log into each machine manually:

    1. Connect to the remote host.
    2. Upload the Dynamics NAV license file to the remote host.
    3. Import the license file.
    4. Restart the Dynamics NAV Service Tiers.

    Maybe you have multiple development / test machines which need a yearly license update?

    In this blog post I demonstrate how you can update NAV licenses on multiple remote hosts all from the comfort of your own PowerShell prompt. Hopefully, along the way you’ll pick up some PowerShell tricks to make your life easier!

    Update Dynamics NAV License Centrally on Multiple NAV Servers
    Update Dynamics NAV License Centrally on Multiple NAV Servers

    Before we get started there are a few prerequisites to this technique:

    • The remote hosts are on the same domain.
    • The user account running the PowerShell script has local admin on the host.

    Connecting to a remote host with PowerShell

    PowerShell comes with a utility called PowerShell Remoting. This allows us to connect to remote servers which have PowerShell Remoting enabled. Windows Server 2012 R2 and above have PowerShell Remoting enabled by default, but if we need to enable this manually we can run the following command on the host:

    > Enable-PSRemoting -force

    If the -force option is not used a prompt asking if we want to continue will occur.

    Group Policy can be used to centrally enable PowerShell Remoting.

    With Remoting enabled we can start a remote session using the Enter-PSSession cmdlet:

    > Enter-PSSession -ComputerName <hostname>

    Specifying a different set of credentials than the current Windows credentials can be done by adding the -Credential parameter:

    > Enter-PSSession -ComputerName <hostname> -Credential <domain\user.name>

    A password prompt will appear if we specify a user account.

    The Enter-PSSession cmdlet is handy when we want to enter into an interactive session with a remote host, but it will come up short when we want to start automating our scripts.

    New-PSSession is used to create a PowerShell session. This session can be a remote session and we can assign this session to a variable. This session variable can be used with the Invoke-Command cmdlet.

    For example, we could create a remote session and get a list of services on the remote host with:

    $session = New-PSSession -Computer <hostname>
    
    Invoke-Command -Session $session -ScriptBlock { Get-Service }

    In a scenario where we have a list of remote hosts to process, we will likely want to keep a note of any hosts that fail to respond. A simple method is to write any failures out to a text file. We can catch these failures in a try / catch block:

    $RemoteHosts = "Host1", "Host2", "Host3"
    
    foreach ($RemoteHost in $RemoteHosts) {
      try {
        $session = New-PSSession -Computer $RemoteHost
    
        Invoke-Command -Session $session -ScriptBlock { Get-Service }
      } catch {
        Add-Content -Path "C:\Temp\ErrorLog.txt" -Value ("Error creating remote session on: " + $RemoteHost)
      }
    }

    The Add-Content cmdlet will append values to the end of the file specified, creating the file if it doesn’t exist.

    Send the local NAV license file to the host machine

    I thought about a few options for getting the license file on the host machine:

    • Put the license file on a web server or fileshare and download from the host.
    • Copy the file through the Copy-Item Cmdlet using the -FromSession parameter.

    But the most convenient in this scenario is to put the license data in a byte array and pass it into the Import-NAVServerLicense Cmdlet using the -LicenseData parameter.

    To create a byte array from the NAV license file we can use the following code:

    > [byte[]]$LicenseData = Get-Content -Path "C:\Temp\License.flf" -Encoding Byte

    $LicenseData now contains the new NAV license, but we’ve created this variable in our local PowerShell session. Once we’ve created the remote session we will need to pass this variable in to the script block we are going to execute in the remote session. To do this, we can utilise script block parameters:

    [byte[]] $LicenseData = Get-Content -Path "C:\Temp\License.flf" -Encoding Byte
    
    $ScriptBlock = {
      Param($LicenseDataParam)
      # some code here...
    }
    
    $RemoteSession = New-PSSesssion -ComputerName <RemoteHost>
    
    Invoke-Command -Session $RemoteSession -ScriptBlock $ScriptBlock -ArgumentList (,$LicenseData)
    
    

    You may have noticed the -ArgumentList value looks a bit weird: (,$LicenseData). This is because the -ArgumentList parameter expects an array value, and will pass our byte array through as individual elements. That said, I’ve no idea why this syntax works… Stack Overflow to the rescue!

    If you don’t use the (,$ArrayVar) syntax, expect an error message like this:

    The license file is corrupt. Error Code: -200.
     + CategoryInfo : NotSpecified: (0:Int32) [Import-NAVServerLicense], FaultException`1
     + FullyQualifiedErrorId : MicrosoftDynamicsNavServer$DynamicsNAV110,Microsoft.Dynamics.Nav.Management.Cmdlets.ImportNavServerLicense
     + PSComputerName : REMOTEHOST

    Importing the license into Dynamics NAV

    So far we can create a remote session, move the license data to the remote host and execute script block on the remote host. Next we need to import the license into the remote Dynamics NAV instance. This is done using the Import-NAVServerLicense cmdlet:

    > Import-NAVServerLicense <NAV Service Instance> -LicenseData <License Data>

    The minimum parameters we need are the Dynamics NAV service instance name and the license data. We’ve already got the license data in the $LicenseData byte array, so lets look at getting a service to use.

    To make this simple, I’m going to apply the license to any running services on the host. I’ve blogged about getting running services previously, here’s some PowerShell to return the service names of any running NAV services:

    > Get-NAVServerInstance | Where-Object {$_.State -eq 'running'} | Select-Object -ExpandProperty "ServerInstance"

    The following script will get a list of running Dynamics NAV services, apply the license using the first service found and then restart all Dynamics NAV services:

    # Put license data in Byte array
    [Byte[]]$LicenseData = Get-Content -Path "C:\Temp\License.flf" -Encoding Byte
    
    # Get list of running NAV services
    $NAVServices = Get-NAVServerInstance | Where-Object {$_.State -eq 'running'} | Select-Object -ExpandProperty "ServerInstance"
    
    # If variable is an array get the first index
    If ($NAVServices -is [array]) {
     $NAVService = $NAVServices[0]
    } else {
     $NAVService = $NAVServices
    }
    
    # Check we have a NAV service before we try to import a license
    if (-Not [string]::IsNullOrEmpty($NAVService)) {
     Import-NAVServerLicense $NAVService -LicenseData $LicenseData
    }
    
    # Restart the service(s)
    Restart-Service -Name $NAVServices

    Putting it all together: Update Dynamics NAV Licenses on remote machines

    OK, we’ve run through all the parts. Lets put this all in to one script:

    # Array of host machines
    $RemoteHosts = "NAVSERV01", "NAVSERV02", "NAVSERV03"
    
    # Text file with list of host machines
    # $RemoteHosts = Get-Content -Path "C:\Temp\NAVHostsList.txt"
    
    [Byte[]]$LicenseData = Get-Content -Path "C:\Temp\License.flf" -Encoding Byte
    
    $ScriptBlock = {
     param ($LicenseData)
    
     Import-Module 'C:\Program Files\Microsoft Dynamics NAV\110\Service\NAVAdminTool.ps1'
    
     $NAVServices = Get-NAVServerInstance | Where-Object {$_.state -eq 'running'} | Select-Object -ExpandProperty "ServerInstance"
    
     # If variable is an array get the first index
     If ($NAVServices -is [array]) {
       $NAVService = $NAVServices[0]
     } else {
       $NAVService = $NAVServices
     }
    
    # Check we have a NAV service before we try to import a license
     if (-Not [string]::IsNullOrEmpty($NAVService)) {
       Import-NAVServerLicense $NAVService -LicenseData $LicenseData
    
      # Restart the service(s)
       Restart-Service -Name $NAVServices
     }
    }
    foreach ($RemoteHost in $RemoteHosts) {
     try {
       $session = New-PSSession -Computer $RemoteHost
    
       Invoke-Command -Session $session -ScriptBlock $ScriptBlock -ArgumentList (,$LicenseData)
    
     } catch {
       Add-Content -Path "C:\Temp\ErrorLog.txt" -Value ("Error updating license on: " + $RemoteHost)
     }
    }

    Whilst this does the job, there is plenty of room for improvement. Error handling is very lightweight for a start. Issues connecting to a remote host will be logged in the ErrorLog file, but if any errors occur in the remote session you’ll only be able to view them from the console output. Also, this script only allows for a single version of NAV and assumes all services on a remote host are connected to the same database.

    So, feel free to use and adapt this script.. but do so at your own risk.

  • Restart Dynamics NAV Services with Powershell

    In this blog post I explain how you can restart Dynamics NAV services with Powershell, while also exploring a few of the available Cmdlets for selecting services and properties.

    Restarting a NAV service

    Restart-Service is a standard Powershell Cmdlet that will stop and then start Windows services. If the service is already stopped, then it will simply start the service.

    Restarting the default NAV 2017 service can be done as follows:

    PS C:\Windows\System32>Restart-Service 'MicrosoftDynamicsNavServer$DynamicsNAV100'

    The unnamed parameter ‘MicrosoftDynamicsNavServer$DynamicsNAV100’ is the service name, this is always the Dynamics NAV Server instance name prefixed with MicrosoftDynamicsNavServer$.

    NAV Service Properties
    The properties of the default NAV 2017 service

    Restarting multiple NAV services

    Restarting multiple services can be done simply by using the * wildcard in the service name parameter:

    PS C:\Windows\System32>Restart-Service 'MicrosoftDynamicsNavServer*'

    The above command will restart every service with a name beginning with MicrosoftDynamicsNavServer.

    So far the services selected for restart have been very indiscriminate, we’ve simply restarted all NAV services on the machine… What if we need to be a bit more selective?

    Get-Service provides us with the ability to get objects representing Windows services. To get all services on your machine run Get-Service with no parameters:

    PS C:\Windows\System32>Get-Service

    The above command will print out a list of services and there statuses:

    Output of the Get-Service Cmdlet

    The same wildcard used for the Restart-Service Cmdlet can be used with Get-Service using the -Name parameter:

    PS C:\Windows\system32> Get-Service -Name 'MicrosoftDynamicsNavServer*'

    The benefit of using Get-Service however, is the ability to pipe the output to the Where-Object Cmdlet:

    PS C:\Windows\system32> Get-Service -Name 'MicrosoftDynamicsNavServer*' | Where-Object {$_.Status -eq "Running"}

    We now have a list of services starting with the name “MicrosoftDynamicsNavServer”, with a status of running:

    Restart Dynamics NAV Service with Powershell
    Get-Service running services

    To keep things consistent, lets delegate all the filtering to Where-Object:

    PS C:\Windows\system32> Get-Service | Where-Object {$_.Status -eq "Running" -and $_.Name -like 'MicrosoftDynamicsNavServer*'}

    Get running NAV services with Get-Service and Where-Object

    The output of Where-Object can be piped into Restart-Service.. so by piping Get-Service into Where-Object into Restart-Service, we can now restart all currently running NAV services with the following:

    PS C:\Windows\system32> Get-Service | Where-Object {$_.Status -eq "Running" -and $_.Name -like 'MicrosoftDynamicsNavServer*'} | Restart-Service

    Get-NAVServiceInstance

    As always, there are many ways to skin a cat.. We could also approach this problem from another direction. The Get-NavServerInstance Cmdlet is available in the NavAdminTool.ps1 script file found in the NAV service folder. The script file will need to be loaded into your Powershell environement using the Import-Module Cmdlet:

    PS C:\Windows\system32> Import-Module 'C:\Program Files\Microsoft Dynamics NAV\100\Service\NavAdminTool.ps1'

    If you get the following error:

    Execution policy error

    You can change the execution policy using the Execution-Policy Cmdlet. In the following image you can see that I get the current execution policy for reference and then after changing the execution policy, I am able to run the Import-Module Cmdlet:

    Powershell change execution policy

    For security reasons it’s good practice to change the execution policy back:

    PS C:\Windows\system32> Set-ExecutionPolicy -ExecutionPolicy Restricted

    Get-NavServerInstance returns a list of NAV services, but the output is not compatable with the Restart-Service Cmdlet:

    Output of Get-NavServerInstance

    We can however pipe the output into the Select-Object Cmdlet which we can use to get the service names as string objects:
    PS C:\Windows\system32> Get-NavServerInstance | Select-Object -ExpandProperty "ServerInstance"

    Get-NavServerInstance ServerInstance Property

    The string objects can then be piped directly into the Restart-Service Cmdlet:

    PS C:\Windows\system32> Get-NavServerInstance | Select-Object -Expand "ServerInstance" | Restart-Service

    Or we can pipe into Get-Service first, followed by Where-Object for more filtering options:

    PS C:\Windows\system32> Get-NavServerInstance | Select-Object -ExpandProperty "ServerInstance" | Get-Service | Where-Object {$_.Status -eq 'running'} | Restart-Service