Installing Application X++ Updates Still Sucks (part one)


In the future, Microsoft is going to “seal” off all customizations, and soon after they’ll be automatically rolling out updates without us having to worry about it. But since that hypothetical rosy future could be years off for slow upgraders (and because I have suspicions about how rosy it will be), some of us are going to have to keep applying application hotfixes. And applying hotfixes sucks.

It’s not quite as bad as it used to be, but it’s still bad.

For sake of these blog posts, I’m going to assume you have a general idea what an application hotfix is for Dynamics 365 for Finance and Operations (what they’re naming the product this week) and that you know how it is different from a binary hotfix. In general:

  • Application hotfix: This is changes to X++ code in the Microsoft-controlled models (Application Foundation, Application Suite, etc.). When you look at or extend classes, tables, etc., in these models, this is the code that an application hotfix can change. When we get “application updates” (7.1/”1611″, 7.2/”July 2017″, 7.3/”December 2017″, 8.0/???), it includes all of these and more.
  • Binary hotfix: This is a change to binary code we can’t see, customize, or extend. They are always cumulative. When we get “platform updates,” it includes these and more. We won’t be talking about these right now.

I’ll also assume you know how you’re supposed to apply an application hotfix, and maybe you’ve even done so. (Note: as of this writing, the linked wiki documentation still uses the command line tool to prepare and apply the hotfix. There is now a tool in the Visual Studio GUI that makes it a little easier, under Dynamics 365 > Addins > Apply hotfix) The general steps are:

  1. Download the hotfix to a development VM (“onebox”) from Lifecycle Services, either through “tiles” or “issue search.”
  2. Prepare the hotfix in the development VM, either through SCDPBundleInstall or the GUI.
  3. Check in the “prepare” files. This is your insurance policy if things go bad.
  4. Apply the hotfix in the development VM, either through SCDPBundleInstall or the GUI.
  5. Resolve conflicts; and build/compile locally. If you have customized/extended the objects changed in the hotfix (or the hotfix has dependencies on other hotfixes), you can’t take this step for granted.
  6. Check the hotfix in to source control.
  7. Build & deploy to sandboxes, test, promote to MAIN/production… same as any other code.

It is easy and works fine… like, 95% of the time. But if you are here, something has probably gone wrong, or you are afraid something might (good instincts, you). You might be wondering if you did something wrong. Maybe you DID do something wrong. But maybe not.

After all, in one year of working with D365, I have seen application hotfixes that:

  • Were marked “binary” instead of “application update” and couldn’t be downloaded until Microsoft fixed that marking.
  • Broke customizations/overlays. (Expected.)
  • Broke extensions. (Surprise!)
  • Required followup actions in the GUI, which were not documented on the hotfix page (or possibly anywhere?), after deployment.
  • Had undocumented dependencies on other hotfixes.
  • Just plain didn’t do what they were supposed to do.

When a hotfix failed, I have lost hours, or even days (if it got into source control), fixing the damage. Arguably, I was dumb, and it didn’t need to be so bad. No reason YOU need to be dumb too, though.

In my next blog post, I’m going to talk about the steps you can use, proactively, to minimize the likelihood of problems when applying application hotfixes.

Configuring Remote Desktop (RDP) to a Dynamics 365 for Operations sandbox in Lifecycle Services

Sometimes you need to use Remote Desktop (RDP) to get into your cloud-hosted sandboxes for Dynamics 365 for Operations. Whether it’s to poke into a SQL database, clean off a full drive on a build machine, or… well, I can’t think of a lot of other reasons, but those are good ones.

As you should already know, to do this, you want to go to the Lifecycle Services (LCS) page for your VM and use the links (and usernames and passwords) there to RDP and log in. Those basics are out of scope for this blog post… but they should be pretty intuitive anyway.

But: For security reasons, you are now required to whitelist the IP addresses that are allowed to connect. If you don’t do that, you’ll get an error that looks like “Remote Desktop can’t connect to the remote computer…”:

RDP error

Adding your IP address seems pretty easy at first. So, you happily pull up a Command Prompt, run IPCONFIG, and on the environments LCS page go to Maintain > Enable access:

LCS whitelist

LCS whitelist 2

“Easy!,” you think.

Then it still doesn’t work.

Here’s the deal: Most internet service providers (whether they be your home provider or your work/office) use different internal IP addresses than the ones they use out on the internet. To get the actual IP address that Microsoft’s filters think you are using, you need to ask the internet outside of where you are. Go to a site like , or just search Google for “what is my ip address” to get it:

what is my ip address

Plug in this IP address instead, and you’ll be good to go.

Just hope Microsoft doesn’t accidentally erase them. If you are managing this list for a bunch of other people, maybe keep a copy somewhere.

Checking in references to other models

As you might be aware, sometimes you need to add a reference to other models in your own models. You do this by going to Dynamics 365 > Model Management > Update model parameters, selecting your model, and checking the box for the model you want to reference.

Addinf references to a model

For example, here you see references added for FiscalBooks, GeneralLedger, Ledger, and Measurement. (A full discussion of references is too much for this blog post, though!)

I discovered, to my surprise, that even experienced developers sometimes don’t know how to check in these changes. If code that requires the reference is checked in, but the new reference is not, the build breaks with potentially mysterious and hard-to-diagnose errors!

How do you check in changes to references? You need to check in the DESCRIPTOR file for the model. It will be an XML file named after the model, and it will be in \AOSService\PackagesLocalDirectory\<ModelName>\Descriptor; it might excluded by default, and you need to make sure to include it in your changes. For example, if you add a reference to a model named “Czar Extensions Model,” you want to see something like this in your pending changes for the check-in:

Pending changes with Descriptor file

I hope this helps. May all your builds be “green”!


Upgrade development VM from Windows evaluation version to “full” version

If you’re anything like me, you’ve got a lot of ISVs to shuffle, making it very difficult to keep up with the current rapid release cadence of Dynamics 365 for Operations. Maybe things will get easier as the cadence slows down, or when they lock out customizations/overlayering. But for now– and, I expect, probably for another couple of years– I’m not easily able to change platform updates every month or two.

Point is, the development VM (“onebox”) comes on an evaluation copy of Windows Server 2016 Datacenter, and after a while it starts nagging you– with increasing frequency– that the evaluation is about to end. Or worse, the development VM / onebox shuts down after exactly one hour, with no obvious reason why.

For a while, you can use the SLMGR command-line tool to reset the evaluation period (“re-arm” in their term). Basically you need to fire up a command prompt as administrator and run this (you’ll need to restart after):

slmgr.vbs /rearm

But to work uninterrupted (or work at all beyond a point), you need to switch to a full version, and it isn’t really obvious how.

Step one: you need a valid key for Windows Server 2016 Datacenter. You didn’t think that was a magic freebie, did you? Fortunately, you’ve got an MSDN subscription (or you sure as heck SHOULD if you’re developing for Dynamics 365 for Operations!) which gives you five keys. In MSDN, you can go to your account, choose “Use your benefits,” and then click “Product keys.” As of this writing, you can also try going straight to this URL: Search for Windows Server 2016 Datacenter. There, you can claim a key. (I’m not sure yet if there is a way to deactivate or return the key when you finally move to a new version and can get rid of the old VM… please comment below if you do!)

Step two: Pretty much follow these instructions: (Give this kind fellow the traffic and leave them a nice comment!) Although their writeup is nicer and easier to follow, for sake of posterity, you need to open an Administrator command prompt and run:

DISM /online /Set-Edition:ServerDatacenter /ProductKey:XXXXX-XXXXX-XXXXX-XXXXX-XXXXX /AcceptEula

…filling in the product key. The Edition value of “ServerDatacenter” might change in the future, so check the link for full instructions on what to do.

Step three? Wait a while. The process is slow, and will be prompted to reboot at least once. Then just reset your desktop wallpaper to a soothing plain black background, and enjoy the lack of evaluation expiration pop-up warnings. 🙂

Resolving X++ and metadata conflicts when merging code

In the near future, Microsoft will seal off customizations/overlayering in Dynamics 365 for Operations. Once that happens, and VAR/ISVs catch up, you won’t have the same problems with conflicts while merging code from various sources (including Microsoft’s own X++ hotfixes). Until then, though… I found the process of properly resolving conflicts to be not completely intuitive, and ended up doing unnecessary manual work and comparison for a while. Here’s how to do it the right way (also the easier way), with a little-known secret or two to help.

You probably already know that after merging in new models, you need to go to Dynamics 365 > Addins > Create project from conflicts in Visual Studio to check for conflicts.

Resolve Conflicts 0a

After doing so, you need to select which model to look for conflicts in. Yes, you need to do them one at a time. Yes, the dialog disappears after each check, making it a hassle to use if you are juggling models from a bunch of different ISVs.

Resolve Conflicts 0b

To save time, be smart about which models you check. You only need to check third party “customization” models, not “extension” models. (Comment if you know of a way conflicts can appear in extension models!) If you’re savvy, you might be able to limit it further, if you know who is changing which objects. But play it safe; you don’t want to miss any conflicts!

Assuming you actually hit a conflict… it’s going to need to create a project. I like to use a naming scheme like ModelNameConflicts_YYYYMMDD to make it easy to clean up later.

The project will be created, and it will contain any and all objects that have conflicts. Here’s where it got confusing for me; intuitively, I thought I’d want to right-click the object and choose “Compare and Merge Code” or something. Nope! Start out by just opening the object.

Resolve Conflicts 1

Once you open it, it won’t be obvious where the conflicts are. They’re marked, but if it’s a big complex object, it might be hard to find them.

The secret?… put “cf:” in the search bar and hit enter.

Resolve Conflicts 2

The object will be filtered to show you the conflicts. (See the little red double arrows icon?)

What you do next depends on whether the conflict is in metadata or in code. It’s not that different, though. One at a time, right click each object, and choose either “Resolve property conflicts” or “Resolve code conflicts.”

If it’s a property conflict:

Resolve Conflicts 3

If you are resolving a property conflict, you’ll get a GUI that you hopefully find intuitive.

Resolve Conflicts 4

If it’s a code conflict:

Resolve Conflicts 5

If you are resolving a code conflict, you’ll be given a DIFF tool to do the merge. This post is not meant to fully explain code merges, but it might be intuitive for you anyway, even if you aren’t already familiar with the concept.

Resolve Conflicts 6

Whether you’ve done a property merge or a code merge, you’ll be able to mark the conflict as resolved, so it won’t come up if you do another check for conflicts.

However, re-merging models might cause the conflict to pop up again, if your ISVs (or whomever you are merging from) do not integrate the merged changes. So, be prepared to re-check in the future, and possibly correct the same conflicts repeatedly!

Doing a code merge when a VAR/ISV has renamed an object

This is probably general to Visual Studio (or any other development tool and source control), and not specific to Dynamics 365 for Operations; but after it ate up several hours of my life figuring out why a build wasn’t working, I thought I should share.

What might be unique (or at least more prevalent) in our world of Dynamics 365 for Operations is the need to do a “code merge” from multiple ISVs or VARs. Chances are it will become almost routine for you, and unless Visual Studio gets better at it, after a few months you’ll be routinely promoting changes that aren’t automatically picked up in “Pending Changes.”

Unfortunately, if you get used to promoting both adds and deletes all over, you might miss that renames should be treated specially. You might inadvertently end up with two copies of the object in source control, one with the old name and one with the new name… and, unless you’re sharper than me (certainly possible!) you’ll spend a lot of time puzzling out why your build won’t work but your local compile does, even though your source seems synched. (But, you might eventually notice, the names in your Application Explorer don’t match your file names…!)

Anyway, the meat of the matter is: make sure to watch out for renames, and promote them by highlighting the relevant “delete” and “add” together, right-clicking, and choosing “Promote as Rename.”

Promote as Rename


And as a tip  to those working for an ISV/VAR: try to avoid renaming objects once they’ve been checked in, and pass along warning if you do. Maybe your client will have no problem if you don’t… but there’s a chance you’ll be saving them a long night if you do!

Table Browser

How to use the table browser in the D365O web interface.

This neat tip can be found in a few other blogs, but I’ve found it so useful that I want to increase its visibility… and put it together with some other useful info in one place.

Although there isn’t a direct method to get to it through the UI, you can open the “table browser” (which you might recognize from inside Visual Studio) to look at the contents of one of the back end database tables. You’ve got to take your URL and change the part after the slash like so:


Obviously, you need to fill in the appropriate <host>, <tn>, and <company>; for example:

…will produce, on your dev VM, the table browser for table CompanyInfo and the DAT company:

Table Browser

If you see this tip elsewhere, you will sometimes see a few other URL parameters in there; lng=<language>limitednav=true (hides parts of the UI like the navigation pane and filters), and prt=<partition> (deprecated partition functionality) are commonly added. I don’t think you are likely to need them, but you can look at other blog posts explaining what those (and other) optional parameters do.

Catch CLR errors in X++ in Dynamics 365 for Operations

Although there are useful posts out there on catching CLR errors in X++ for older versions of Dynamics AX, as of this writing, I couldn’t find one that address the current version. It took me a little experimentation and trial-and-error to figure out how to do it; in particular, to actually return a useful error message, instead of just a generic CLR error. Below is the pattern I use. For simplicity, I’m leaving out some of the other types of CATCH you might use, as well as other detail irrelevant to the pattern.

As usual, I would greatly appreciate comments on whether this works for you, and if you know of any improvements.

System.Exception e;
InteropPermission interopPermission;
interopPermission = new InteropPermission(InteropKind::ComInterop);
catch (Exception::CLRError)
e = CLRInterop::getLastException();
if (e != null)
e = e.InnerException;
throw error(e.ToString());
//…or whatever you want to do with that error text
throw error("null CLR error caught blah blah blah");
//other error handling

Logging in to SQL Server in the developer VM

Connecting to SQL Server in the Dynamics 365 for Operations development VM

This post assumes you are comfortable in the use of SQL Server. If you are not, this is a good entry to go ahead and skip.

EDIT: This post might not be necessary. I am told that due to UAC, all you need to do is launch SSMS using “run as administrator” instead. I’ll leave what I wrote here for historical purposes (it’s still interesting) but you don’t need it!

Coming from a SQL Server background, I have frequently found it helpful to dig through the tables in AxDB, the SQL Server database used as a datastore for Dynamics AX / Dynamics 365 for Operations. Although we do not have direct access to them when programming, and the “Tables” in the AOS are an abstraction that sometimes differs, they are often analogous; it can be easier to plan out a join in familiar T-SQL before trying it in X++. It’s also a good troubleshooting and debugging tool.

And, let’s face it… Microsoft might not like it, but once in a blue moon it can be easier to do certain cleanup or bulk updates that way, if you know what you are doing. Not the best practice, but certainly a fast way to clear a new table you are building, or activate a hundred imported users in one shot…

Working on platform update 3 and earlier, all you had to do was fire up SSMS (SQL Server Management Studio) while you were logged in as the VM Administrator, and (per defaults for most SQL Server installations) you were a sysadmin. After moving to a fresh Platform Update 5 VM (skipping PU4), however, I found that they’d configured SQL Server to no longer allow you access. Not cool, Microsoft.

However, there is a workaround. As you might know, there is a web.config file that contains information Dynamics 365 for Operations uses to connect to its datastore, including a sysadmin user and password. Although the password is stored “encrypted,” you can decrypt it temporarily. Just do that and find the relevant entries in the web.config file:

Although “security through obscurity” ain’t great, I’m not going to put the actual platform update 5 default password here, especially since it might change anyway.

If you want long-term convenience, you might want to immediately log in with these credentials and re-add the local administrator as a login with the sysadmin role. (If you don’t know how to do that, maybe you shouldn’t be mucking around in the SQL Server after all!) Then you don’t need to remember or save the password you dug out of here.

Oh, and… don’t forget to re-encrypt the web.config file when you’re done.

ADDENDUM: In case something happens to the linked post, here are the commands to decrypt and re-encrypt the web.config file. But please, go through and comment on the original post… and comment here too… it motivates bloggers to keep going when we get the occasional response. Just a quick “thank you” is plenty!

C:\AOSService\webroot\bin\Microsoft.Dynamics.AX.Framework.ConfigEncryptor.exe -decrypt C:\AOSService\webroot\web.config

C:\AOSService\webroot\bin\Microsoft.Dynamics.AX.Framework.ConfigEncryptor.exe -encrypt C:\AOSService\webroot\web.config

WRITING to Financial Dimensions (DefaultDimension) in X++

One of my initial programming tasks with Dynamics 365 for Operations (AX 7) has been writing routines to import data from external systems. These complex routines are writing to multiple tables, and sending email when there are problems (a subject for a future post)… and, recently, took another step up in complexity.

When I found out I had to write a routine to import data to “Financial Dimensions” (sometimes called “Default Dimensions” for reasons I’ll soon explain) on employees/workers (more specifically, their employment records, the HcmEmployment table) I was flummoxed for a while.

It took me a bit of reading to figure out what Financial Dimensions even were. Basically, they are attribute/value pairs that you can define yourself. There is more to them then that, and I won’t say I fully understand them yet… that’s a subject for other posts, so I’m just giving you some basics here in case you are unfamiliar. A few are set up by default, but you can also create your own. (General ledger > Chart of accounts > Dimensions > Financial dimensions) So you or an admin can go create a Favorite_Color dimension, give it a bunch of choices (01=Red, 02=Blue, etc.) and then attach a favorite color to a bunch of entities. Just look for a DefaultDimension column. Once that’s done, users can see and set them. For example, in my case, I could go to Human resources > Workers > Worker, look under “Employment” for a given worker, and see the Financial dimensions section listed at the bottom.

Unfortunately, the way they are stored and managed is complex. It’ll take a bunch of reading and looking at code samples to figure out how to get data out of them; I won’t repeat that here, it’s easy to find. But for all my searching, I could not find a way to WRITE to financial dimensions in X++. Finally, with a little help (thanks to Steeve Gilbert in the AXUG forums), I was able top work out a reusable method (or three) to help.

I’m sharing my sample code here to save you the pain I experienced. For sake of example, I’m assuming you put this in a class named MyDimensionHandlerClass, along with any other generic/reusable dimension-related methods you want.

In future blog posts I will share sample code for reading and validating attributes, just for completeness… although there are other samples for those out there.

/// <summary>
/// Generic dimension updater. Call this with the DefaultDimension of any table, and update that table’s DefaultDimension with the returned value.
/// Sample usage:
/// hcmEmployment.DefaultDimension = xczDimensionHandler::PutDimension(hcmEmployment.DefaultDimension, ‘Favorite_Color’, ’01’);
/// hcmEmployment.validTimeStateUpdateMode(ValidTimeStateUpdate::Correction); // This may or may not be appropriate for your circumstances
/// hcmEmployment.update();
/// Based on sample code courtesy Steeve Gilbert.
/// </summary>
/// <param name = "_DimensionAttributeValueSet">Value in the "DefaultDimension" column of the linked table (i.e. HcmEmployee, etc.). Links to DimensionAttributeValueSet.RecId. You will need to update the calling table this came from.</param>
/// <param name="_attribute">Attribute (e.g. "Department").</param>
/// <param name="_value">Potential value for the Attribute (e.g. "950").</param>
public static RecId PutDimension (RecId _defaultDimension, str _attribute, str _value)
DimensionAttribute dimAttribute;
DimensionAttributeValue dimAttributeValue;
DimensionAttributeValueSetStorage dimStorage;
// Verify that the value passed for _attribute identifies a real dimension.
select firstonly dimAttribute
where (dimAttribute.Name == _attribute);
if (!dimAttribute)
throw error(“Invalid _attribute passed to PutDimension: ” + _attribute);
// Find or create a storage dimension that contains each dimension attribute and their value.
dimStorage = DimensionAttributeValueSetStorage::find(_defaultDimension);
// Update or delete the value within that storage dimension.
if (_value)
dimAttributeValue = DimensionAttributeValue::findByDimensionAttributeAndValue(dimAttribute, _value, true, true);
//If there’s no value, that means we want to remove that Dimension Attribute

view raw
hosted with ❤ by GitHub