Tuesday, December 25, 2018

New book about Dynamics AX 2012 / Dynamics 365 FO performance

Rod Willumsen has written a very fine book, Advanced X++/SQL Tuning Techniques, covering a lot of ground around performance work with Dynamics AX 2012 and Dynamics 365 FO.

The book starts out with a few chapters about various SQL subjects seen from AX/365 FO point of view.

Secondly it walks you through installation of the Performance Analyzer and of how to analyze the results collected by the analyzer.

Collecting traces and analyzing them with the trace parser, both for AX 2012 and D365, is covered in depth too.

You'll also find information about data archiving and data clean up techniques, as well as information about how to deal with very specific issues, like InventDim and InventSum tables, and master planning.

These are the chapters of the book:

  • X++ and SQL
  • Performance killers
  • Improving poor X++ query performance
  • Further query improvements
  • Performance analyzer for Dynamics
  • Microsoft Dynamics AX 2012 Trace Parser
  • D365FO Application Trace Parser
  • Top D365FO performance issues and solutions

DBAs, system administrators and developers as well should be able to pick up useful information and tools from this book.

Friday, November 23, 2018

Modules on the environment do not match with modules in the package

If you try to deploy an 8.0 to 8.1 upgrade to sandbox/UAT where modules have been renamed, you will get the error "Modules on the environment do not match with modules in the package" together with a list of missing modules.

The regular process of renaming a module is to create a new module with the new name, move the code, delete the old module and add the old module name to the ModuleToRemove.txt files for removal during deployment.

When deploying the 8.0 to 8.1 upgrade to sandbox/UAT, Microsoft checks that the package holds at least the same modules as already deployed. But the ModuleToRemove.txt file is not taken into consideration during this check, and thus the modules appears to be missing.

The workaround is to keep and deploy empty models with the old names. Any code must of course be in the new models.

You can uninstall the empty modules at your next regular deployment using the normal process with the ModuleToRemove.txt file.

This is only an issue with 8.0 to 8.1. Not with, for example, 7.3 to 8.1.

Tuesday, November 20, 2018

Thumbprint error when pasting thumbprint into the Regression Suite Automation Tool (RSAT) configuration

You can get an error like this when you paste the thumbprint, from your new certificate, into the configuration tool.

The reason for this is that the certificate UI uses a Unicode enabled RichEdit control to show the value.

You can use a command prompt to list a thumbprint that is suitable for copy-n-paste.
Use "certutil -store my" for the machine certificates. The thumbprint can be located in the line that starts with "Cert Hash(sha1)".

Another workaround is to paste the thumbprint to a new Notepad file, save the txt file, reopen the text file and then copy the thumbprint from there.


Friday, November 16, 2018

Connecting to custom help sites from Finance and Operations

New information and tools from Microsoft to help you connect Dynamics 365 for Finance and Operations to a website that hosts your custom help content. You can base your custom help on our content, on your existing Dynamics AX 2012 content, or on your own content, as long as you add the required new metadata.

White paper:
Tools on GitHub:

Tuesday, October 30, 2018

Debugger not hitting any breakpoints on new 8.1 boxes

I have had a few 8.1 dev boxes where the debugger just wouldn't hit any breakpoints.

The cause is this related to this setting in Visual Studio:

On the new boxes where I have had the problem "Only specified modules" was active, but no modules were specified. You can change this to "All modules, unless excluded" or figure out precisely which modules add.

Thursday, October 11, 2018

Where does the DefaultConfig.xml origin from?

In the DefaultConfig.xml file located under C:\Users\[user]\Documents\Visual Studio 2015\Settings you can change all sorts of settings for how Visual Studio works with Dynamics.

I needed to change something in this file on a local VM today. But the file kept being overwritten whenever I started Visual Studio again. So where did it originate from?

Google didn't have an answer. Ievgen Miroshnikov has though, but his article was just not very Google friendly along the terms of my query.

So I'm basically just able to repeat what Ievgen already has stated: The file originates from \AosService\PackagesLocalDirectory\Bin\DynamicsDevConfig.xml

Tuesday, October 2, 2018

Team Manager license and the Regression Suite Automation Tool

You'll need a Test Manager license to create and maintain test plans (add or remove tests).

Test Manager is included in Visual Studio Enterprise and Visual Studio Test Professional. You can see pricing here:

Alternatively you can buy a Test Manager license as a standalone SKU. You can find pricing here:

Without the license you can still:
  • Create a new task recording, upload it to BPM and get it synchronized to Azure DevOps as a test.
  • Load the test plan into the Regression Suite Automation Tool, run the tests and investigate test results in Azure DevOps.
  • Update the tests with the artifacts generated by the tool. 

If you create a new test, and you don't have the license, you'll need someone with a license to add the test to a test plan.

Read more about the tool here:

You can down the installer and the manual here:

Friday, September 28, 2018

How to setup Azure Key Vault with Dynamics 365 for Finance and Operations

Azure Key Vault is a service that allows you to store secret keys, like passwords or certificates for external web-services, to be used by your different apps. When stored in Key Vault, none of your apps need to know details about or store these secrets by themselves. No more passwords laying around in code, in your version control system, in configuration files etc.

You can read more about Key Vault here.

Dynamics 365 for Finance and Operations has integration with key vault. It's just not very well documented and for now it seems only used a few places for integrations with different authorities.

I can't find any documentation on docs about the setup of Key Vault with Dynamics 365 for Finance and Operations, but I was able to find this support guide in stead:

If you follow the guide you'll be able to do the necessary setup on Azure.

In Dynamics you can find the setup form under System administration / Setup / Key vault parameters:

Let's try to figure out how to add a configuration to this form.

I have created a key vault called "PallesVault":

The "DNS Name" from the vault is what you enter in the "Key Vault URL" field.

I have registered my Dynamics instance as an app, as described in the support article. And I have added a Key to the registration. The Application Id and the secret key I have added goes into the "Key vault client" and "Key vault secret key" fields.

I have added single secret to my vault, which is my phone number ("Telefonnummer" is danish for phone number):

The address of the secret goes into the "Secret" field, as "vault://pallesvault/PallesTelefonnummer"

So, to get the value of this key from Dynamics the setup looks like this:

And here is some quick and dirty test code:

public static void main(Args _args) 

    KeyVaultCertificateTable keyVaultCertificateTable; 

    select firstonly KeyVaultCertificateTable 
        where KeyVaultCertificateTable.Name == "Telefon"; 

    str secretValue = KeyVaultCertificateHelper::getManualSecretValue(KeyVaultCertificateTable.RecId); 

    info (secretValue); 

In regards to pricing for the key vault, there is a Standard and a Premium tier. Standard is nearly free and Premium will set you back around 1$ a month.

Go secure your secret keys.

Friday, September 14, 2018

Timing in PowerApps

Just for fun I tried to build a PowerApp, to be embedded in D365 FinOps, that would show some PDFs for at particular item.

I keep the PDFs in a SharePoint document library and use ItemId as a key.

And resulting the app looks like this:

Of course I need to filter the SharePoint list to only show entries related to the current item, and I added the following to OnStart of the app, like described on docs:

If(!IsBlank(Param("EntityId")), Set(FinOpsInput, Param("EntityId")), Set(FinOpsInput,""));

And here is what I have put in Items of the Gallery showing the SharePoint data:


A problem I have spent hours on, was that the filtering was very flaky. Sometimes it would flicker, showing all my items and then apply the filter, and sometimes it just didn't apply the filter until I manually refreshed the list.

It seems like things run i parallel when you open an app, so it would actually start to load data from SharePoint before or while the OnStart trigger is executed. And in some cases it wouldn't know about the FinOpsInput variable yet when applying my filter.

A solution could be to add a variable to OnStart, letting you know if that is finished, and make a bogus filter until it is done. Like this:

If(!IsBlank(Param("EntityId")),Set(FinOpsInput,Param("EntityId")),Set(FinOpsInput,""));Refresh(ProductPDFs);Set(AppIsStarted, true)

And change Items of the gallery to this:
If (AppIsStarted, If(!IsBlank(FinOpsInput),SortByColumns(Filter(ProductPDFs,ItemId=FinOpsInput),"ItemId",Ascending),SortByColumns(ProductPDFs,"ItemId",Ascending)), Filter(ProductPDFs,ItemId="BOGUS"))

This video explains what you need to know to build the app

Monday, September 10, 2018

A collection of links releated to Dynamics 365 FinOps and PowerApps

A collection of links releated to Dynamics 365 FinOps and PowerApps. Please comments if I have left put something important.

Embed PowerApps apps
How to embed PowerApps in Dynamics 365 for Finance and Operations Video
Getting started with canvas apps
Sample app for looking up exchange rates
Sample app for generating product labels
PowerApps Host control
PowerApps dashboard
PowerApps Documentation
Environment and tenant app migration through Packaging
Free PowerApps Plan 2 trial
PowerApps Community Plan Signup
PowerApps Community Plan Details
Environments overview
PowerApps community
Microsoft Flow community

Wednesday, September 5, 2018

Comparison of Azure prices

It just needed to do a new deployment though my Visual Studio Azure subscription, and as usual I couldn't remember the price variance between regions and types of machines.

I stumbled over this site which gives a good overview: 

I don't know how accurate they are, but I was able to see variances and that was what I was looking for.

Thursday, June 7, 2018

How to load all labels from a label file

Here is a small example of how to load all the labels of a particular label file, in a particular language. It takes about 6-8 seconds on my developer box to load the 122190 label from SYS.

public static void main(Args _args)
    info ("Starting");
    ClrObject labels = new ClrObject("System.Collections.Generic.Dictionary`2[System.String,System.String]");
    labels = Microsoft.Dynamics.Ax.Xpp.LabelHelper::GetAllLabels('SYS', System.Globalization.CultureInfo::CurrentCulture);

    ClrObject labelsEnumerator = labels.GetEnumerator();
        ClrObject keyValuePair = labelsEnumerator.get_Current();

        var currentlabelId = keyValuePair.get_Key();
        var currentTxt = keyValuePair.get_Value();
    info ("Done");

Wednesday, May 2, 2018

Need to work with or add system defined buttons on forms?

You can change the behavior of system defined buttons form, or add buttons, by subscribing to delegates from the SysSystemDefinedButons class.

The class AlertFormExtender has an example of how a button is added to the Options action pane tab.

Monday, April 30, 2018

Why you can't setup sharing of customers and vendors in 8.0

The release notes for 8.0 says that you can now share customers and vendors with the data sharing feature. But it also says: "To enable this functionality, contact your Solution Architect, who will verify that your business requirements and data volumes will be acceptable for data sharing, and then enable the sharing policies in your environment".

So what does that mean?

Microsoft has started using a "flighting" concept where they add features on a release, but keep them turned of until activated for the tenant by Microsoft. This is something you can see Microsoft check in their code.

A new isFlightEnabled method on the Global class does the work of checking if a feature is enabled or not.

If you want to cheat a bit, you could probably add an event handler to this method and return true for the feature you want to enable. I haven't tried that though.

In class SysDataSharingValidation, method validateTableCanBeSharedWithPolicy, you can see an example of how this is called:

Thursday, March 15, 2018

Lifecycle Service companion app

Here is a cool tool for LCS environment managers, written by Tomek Melissa from Microsoft.

The tool is unofficial and comes AS-IS.

Saturday, March 10, 2018

Generic function for switching line numbers on records

Tried to write a generic routine for switching line numbers on records, to help with functions that move records up or down according to a line number sequence.

Comments are welcome and I'll update the post according to the bugs you can find. However I only spent 10 minutes on this and it's provided to you for free, so please bear that in mind before you send me death threats in the comments.

public static void switchLineNumbers(
    Common      _currentTopRecord,
    Common      _currentBottomRecord,
    FieldName   _lineNumberFieldName)
    Common      currentTopRecordUpdate;
    Common      currentBottomRecordUpdate;
    LineNumber  topLineNumber;
    LineNumber  bottomLineNumber;
    FieldId     lineNumberFieldFieldId = fieldName2id(_currentTopRecord.TableId, _lineNumberFieldname);


        // Initialize correct table id on common
        currentTopRecordUpdate = new DictTable(_currentTopRecord.TableId).makeRecord();
        currentBottomRecordUpdate  = new DictTable(_currentBottomRecord.TableId).makeRecord();

        select firstOnly forUpdate currentTopRecordUpdate
            where currentTopRecordUpdate.RecId == _currentTopRecord.RecId;

        // Make a temporary line value, in case there is a unique index with line number,
        // making a free spot to update the currect bottom record with
        topLineNumber = currentTopRecordUpdate.(lineNumberFieldFieldId);

        currentTopRecordUpdate.(lineNumberFieldFieldId) = maxInt();

        select firstOnly forUpdate currentBottomRecordUpdate
            where currentBottomRecordUpdate.RecId == _currentBottomRecord.RecId;

        bottomLineNumber = currentBottomRecordUpdate.(lineNumberFieldFieldId);

        currentBottomRecordUpdate.(lineNumberFieldFieldId) = topLineNumber;

        currentTopRecordUpdate.(lineNumberFieldFieldId) = bottomLineNumber;


    catch (Exception::DuplicateKeyException)
        if (appl.ttsLevel() == 0)
            if (xSession::currentRetryCount() >= #RetryNum)
                throw Exception::DuplicateKeyExceptionNotRecovered;
            throw Exception::DuplicateKeyException;

Use at your own risk.