Dynamics NAV PowerShell: Creating an enhanced Get-NAVServerInstance function

Between the many hours of preparation of all the conventions (there are a few surprises I can only talk about after Directions EMEA – but is eating up a lot of time ..), let’s make time for this tiny little function that I tend to use quite a lot lately. Why am I using it? Simply because I have a lazy attitude 😉

What is it?

Well, it’s a somewhat “Enhanced” (and slower) version of the heavily used “Get-NAVServerInstance”. The reason why I was creating this is because I wanted to combine the details from the cmdLet “Get-NAVServerConfiguration”.

I mean – I might be wrong – but it’s a one-to-one relation anyway, isn’t it? One ServerInstance always has one server-configuration, right? I wanted to get to the most interesting configurationparameters, together with the ServerInstance properties, all with just one commandlet. On top of that, we are all aware that getting specific configuration properties isn’t as easy as it should be. I blogged about that here. Getting to specific settings in PowerShell should be easy. And what is easier then only having to use one commandlet, and being able to browse properties for the specific configurationkeys… .

First attempt

Here is my first solution:

function Get-NAVServerInstance2
{
    [CmdletBinding()]
    param
    (
        [Parameter(Mandatory=$false, Position=0)]
        [System.String]
        $ServerInstance
    )
    
    process {
        $ServerInstanceObjects = Get-NAVServerInstance -ServerInstance $ServerInstance
        
        foreach ($ServerInstanceObject in $ServerInstanceObjects) {
            $ServerInstanceObject | Add-member -MemberType NoteProperty -Name 'Multitenant' -Value (Get-NAVServerConfiguration2 -ServerInstance $ServerInstanceObject.ServerInstance | Where Key -eq Multitenant).Value
            $ServerInstanceObject | Add-member -MemberType NoteProperty -Name 'ClientServicesPort' -Value (Get-NAVServerConfiguration2 -ServerInstance $ServerInstanceObject.ServerInstance | Where Key -eq ClientServicesPort).Value
            $ServerInstanceObject | Add-member -MemberType NoteProperty -Name 'DatabaseName' -Value (Get-NAVServerConfiguration2 -ServerInstance $ServerInstanceObject.ServerInstance | Where Key -eq DatabaseName).Value
            $ServerInstanceObject | Add-member -MemberType NoteProperty -Name 'DatabaseServer' -Value (Get-NAVServerConfiguration2 -ServerInstance $ServerInstanceObject.ServerInstance | Where Key -eq DatabaseServer).Value
            $ServerInstanceObject | Add-member -MemberType NoteProperty -Name 'DatabaseInstance' -Value (Get-NAVServerConfiguration2 -ServerInstance $ServerInstanceObject.ServerInstance | Where Key -eq DatabaseInstance).Value
                    
            $ServerInstanceObject
        }                 
    }           
}

 

You see that I’m using the function from the blogpost, to simplify the statements and still to be able to get up until the configuration key that I needed at a specific place. For the rest, the trick is quite simple: just add properties to the object that I’m getting back from “Get-NAVServerInstance”. Now you can easily do something like this:

Get-NAVServerInstance2 | Select ServerInstance, State, DatabaseServer, DatabaseName | format-Table -Autosize

and get to the combined set of properties from the two commandlets. How cool is that? 🙂

Well, not that cool, because as such, it’s quite slow, and we “only” have 5 extra properties. It’s mainly slow because I’m executing the Get-NAVServerConfiguration commandlet for each property that I would like to get in there .. not ideal, but it serves its purpose.

Second, more generic, attempt

The other day, I came across a nice way to “loop” the members of a certain object .. and “do” stuff with it (too bad I can’t find the specific resource I’m talking about – it was on powershell.com though .. sorry about that). What if I would just loop the keys of the “Get-NAVServerConfiguration” commandlet, and add all the values one-by-one to the object. So instead of hard-coding it (like my first attempt above), just add every key and its value as a new property?

Here’s how you can do that:

function Get-NAVServerInstance3
{
    [CmdletBinding()]
    param
    (
        [Parameter(Mandatory=$false, Position=0)]
        [System.String]
        $ServerInstance
    )
    
    process {
        $ServerInstanceObjects = Get-NAVServerInstance -ServerInstance $ServerInstance
        
        foreach ($ServerInstanceObject in $ServerInstanceObjects) {
            $ServerConfigKeys = Get-NAVServerConfiguration2 -ServerInstance $ServerInstanceObject.ServerInstance 
        
            foreach($ServerConfigKey in $ServerConfigKeys) {
                $PropertyAlreadyExists = $ServerInstanceObject | Get-Member | Where Name -ieq $ServerConfigKey.Key
                if (-not ($PropertyAlreadyExists)){
                    $ServerInstanceObject | Add-member -MemberType NoteProperty -Name $ServerConfigKey.Key -Value $ServerConfigKey.Value
                }                        
            }

            $ServerInstanceObject
        }                    
    }      
}

You see, it’s a bit more generic (which makes it somewhat less readable, but still). As such, I’m doing as described above: looping the keys, adding them to the object. One thing to notice: I’m “only” executing the Get-NAVServerConfiguration commandlet once per ServerInstance, while in the previous function, it was once per key. So in a way, I’m adding much more properties, but it’s still faster as the first one – or at least should be.

With the last function, you can use any property, like here is an example of a list of all serverinstances with the ports

Get-NAVServerInstance3 | Select ServerInstance, State, ManagementServicesPort, ClientServicesPort, SOAPServicesPort, ODataServicesPort | format-table -AutoSize

Or, when using in a script, you can easily reference all the configuration-options as properties, like looping the Databasenames:

$ServerInstances = Get-NAVServerInstance3 foreach ($ServerInstance in $ServerInstances){ $ServerInstance.DatabaseName }

So, use it, but use it with care – do not use these functions when you don’t need the extended properties! Simply because it slows down your script. Do keep that in mind. You can imagine that when you’re in a big loop, and constantly are executing and looping the configuration, that it would create a significant overhead… .

Other than that – me personally, I’m using both of the functions quite regularly – and it definitely makes my script much smaller, and therefore, much more readable :-).

Performance comparison

When writing this post, I was actually curious what the performance-difference was between the three functions … . And the difference is very significant (and consistent)!

I simply executed this a few times:

(Measure-Command {Get-NAVServerInstance | Select ServerInstance, State | format-Table -Autosize}).TotalMilliseconds 
(Measure-Command {Get-NAVServerInstance2 | Select ServerInstance, State | format-Table -Autosize}).TotalMilliseconds 
(Measure-Command {Get-NAVServerInstance3 | Select ServerInstance, State | format-Table -Autosize}).TotalMilliseconds

 

And the results were obvious: 

  • Get-NAVServerinstance: fastest: only 145 milliseconds on average
  • Get-NAVServerInstance2: slowest!: about 7500 milliseconds on average!
  • Get-NAVServerinstance3: still slow: about 4000 milliseconds on average!

You might wonder – are these functions useful?

Well – I don’t care about 4 seconds or 0,1 second, as long as I know what I’m doing. Certainly, 4 seconds in a loop does matter .. but then again – then I don’t know what I’m doing ;-). In a way, using lines like this are possible, faster, but … Hard to read or use (the following is going to list all databasenames, feched from all serverinstances’ configuration – only using default commandlets – and bloody fast):

((Get-NAVServerInstance | Get-NAVServerConfiguration -AsXML) | foreach{$_.configuration.appSettings.add} | Where Key -eq Databasename).value

I’m not saying that the function can be altered in going faster – probably it can. Like, make sure it only has to execute the “Get-Member” only once .. And working with some kind of hashtable. Sure, probably it’s possible – I just didn’t bother. Please don’t hold yourself in improving it, and putting the updated version as a comment 🙂

So – enjoy if you use, ignore if you don’t ;-)!

5.00 avg. rating (98% score) - 2 votes

Permanent link to this article: https://www.waldo.be/2015/09/23/dynamics-nav-powershell-creating-an-enhanced-get-navserverinstance-function/

2 comments

2 pings

    • olivier on September 30, 2015 at 1:16 pm
    • Reply

    I always use my version for this:

    function Get-NavServerConfig
    {
    [CmdletBinding()]
    Param
    (
    [Parameter(ValueFromPipeline=$true,ValueFromPipelineByPropertyName=$true,Position=0)]
    $ServerInstance
    )
    Begin
    {
    $ServerConfigs = @()
    }
    Process
    {
    $ServerInstance | Get-NAVServerInstance | ForEach-Object -Process `
    {
    $ServerConfig = New-Object PSObject
    foreach ($Attribute in $_.Attributes)
    {
    $ServerConfig | Add-Member -MemberType NoteProperty -Name $Attribute.Name -Value $Attribute.Value -Force
    }
    foreach ($Node in ($_ | Get-NavServerConfiguration -AsXml).configuration.appSettings.add)
    {
    $ServerConfig | Add-Member -MemberType NoteProperty -Name $Node.key -Value $Node.value -Force
    }
    $ServerConfigs += $ServerConfig
    }
    }
    End
    {
    return $ServerConfigs
    }

    }

      • waldo on September 30, 2015 at 1:43 pm
        Author

      Thanks for this, Olivier!
      This is a much faster way, because you’re executing the Get-NAVServerConfiguration much more elegant (by pipeline) for multiple serverinstances. Very efficient function in every way! Works like a charm – I’m going to adopt this one ;-)!

      Thanks for sharing!

  1. […] Bron : Waldo’s Blog Lees meer… […]

Leave a Reply to Dynamics NAV PowerShell: Creating an enhanced Get-NAVServerInstance function | Pardaan.com Cancel reply

Your email address will not be published.

This site uses Akismet to reduce spam. Learn how your comment data is processed.