Role-based security in Dynamics 365 for Operations. What changed and what stayed the same.

New version of AX has a couple of changes in the security architecture. Process cycles are removed (no one really used them in AX 2012) and record level security is finally obsolete.

However, because of new code architecture and restrictions that came with it, there are some changes in the way how we create new security artifacts.  Previously, in AX 2012, all security objects were stored in AOT as a metadata, even if you did security setup from UI new objects were created or changed in AOT. Now, because of .Net platform, we cannot generate assemblies on the go, so there are two ways how to create security objects:

  1. Create security objects in Visual Studio.

Nothing has changed here, developer can create or edit new roles, duties and privileges in AOT. Then they can be deployed via deployable packages.  For today’s blog I created simple role, duty and couple of privileges.

  1. Create security object from UI.

In current version experience is similar to AX 2012, where user can create and edit security objects from UI, but under the hood AX does not create any objects! All changes are stored as a data. On screenshot below you can see new role created by me for this article.

myrole

New role has one duty and this duty has only one privilege. Now I’m going to add one more privilege “My PrivilegeTwo” to my duty. To do this you need to select a duty you want to modify, click “Add references” and select a privilege you want to add.

MyRoleChange.jpg

After that, you may notice “unpublished objects” and you can either publish them or undo.

myrolepublishchanges

Let’s add one more privilege in AOT.

MyDutyAOTChanged.jpg

As you can see, in AOT my duty consists only from two privileges, however, in UI AX shows three:

MyRoleAOTUIChanged.jpg

Two of them were created by developer in AOT and one was done in UI and is stored as data.

In next blog post I will show how to deploy security data changes across environments and how to use Security diagnostics tool.

AX 7. Link class instances to a form.

In one of my previous blogs we discussed how to override form control methods or form data source field methods without overlaying using registerOverrideMethod(). But what if we need to link class instance to a form? We have two options: use form extension classes with [ExtensionOf] attribute or register class instance on the form using formRun.registerExtensionInstance() method. Main difference between them is that with extension classes you add new methods or state extending form functionality and using registerExtensionInstance() we link class instance with new logic to a from, so we can have one class that could handle multiple forms. First approach is described in this blog post, so today we will focus on the second one.

Under the hood FromRun class has map variable to store registered instances:

Map formExtensionInstance = new Map(Types::string, Types::Class);

And 3 methods to work with it:

/// <summary>
/// Registers an extension instance.
/// </summary>
/// <param name="_extensionKey">The key for the extension.</param>
/// <param name="_extensionInstance">The instance for the extension.</param>
public void registerExtensionInstance(str _extensionKey, Object _extensionInstance)
{
    formExtensionInstance.insert(_extensionKey, _extensionInstance);
}
/// <summary>
/// Gets a registered extension instance.
/// </summary>
/// <param name="_extensionKey">The key for the extension.</param>
/// <returns>The instance for the extension if one is found.</returns>
public Object getExtensionInstance(str _extensionKey)
{
    return formExtensionInstance.lookup(_extensionKey);
}
/// <summary>
/// Gets a Boolean value indicating if an extension with the specified key is registered.
/// </summary>
/// <param name="_extensionKey">The key for the extension.</param>
/// <returns>true if an exteinsion with the specified key is registered; otherwise; false.</returns>
public boolean hasExtensionInstance(str _extensionKey)
{
    return formExtensionInstance.exists(_extensionKey);
}

There is a common way in standard code how to build extension classes:

  1. Create new class and add variable to store FormRun object, flag that indicates if instance is initialized and other required variables. Let’s add FormStringControl for example.
public class MySampleExtension
{
    FormRun element;
    boolean initialized;

    FormStringControl myFormStringControl;
}
  1. Create new method to initialize FormRun and register current instance on the form.
public void new(FormRun _formRunInstance)
{
    initialized = false;
    element     = _formRunInstance;

    // register this extension with the main form
    element.registerExtensionInstance(classStr(MySampleExtension), this);
}
  1. Create init() method to initialize all required variables and register override if needed.
private void init()
{
    if (initialized)
    {
        return;
    }

    initialized = true;

    myFormStringControl = element.design().controlName(formControlStr(MySampleForm, MyFormStringControl));

    //register overrides here if needed
}
  1. Subscribe to form pre init() event to register extension instance.
[FormEventHandler(formStr(MySampleForm), FormEventType::Initializing)]
public static void MySampleForm_OnInitializing(xFormRun _sender, FormEventArgs _e)
{
    MySampleExtension extensionInstance = new MySampleExtension(sender);
}
  1. Subscribe to form post init() event to initialize extension instance.
[FormEventHandler(formStr(MySampleForm), FormEventType::Initialized)]
{
    // Initialize the instance of this form extension handler now that the controls exist
    FormRun mySampleForm = _sender as FormRun;
    MySampleExtension extensionInstance  = mySampleForm.getExtensionInstance(classStr(MySampleExtension));

    extensionInstance.init();
}
  1. Use extension class
[FormControlEventHandler(formControlStr(MySampleForm, MySampleButton), FormControlEventType::Clicked)]
public static void mySampleButton_OnClicked(FormControl _sender, FormControlEventArgs _e)
{
    if (_sender && _sender.formRun())
    {
        FormRun formRun = _sender.formRun();

        MySampleExtension extensionInstance = formRun.getExtensionInstance(classStr(MySampleExtension));

        extensionInstance.mySampleButtonClicked();
    }
}

public void mySampleButtonClicked()
{
    myFormStringControl.visible(false);
}

That’s pretty much all of it.

AX 7. Extending forms using Form Parts.

Update 3 brought significant improvement to a form extension experience – ability to add form parts. Why it’s important? Previously we were able to add new data sources, new controls or modify some properties of existing controls. However, we did not have option to override form data source methods, we were able to use events only and it is not enough in some cases. Classic example is adding new InventDim data source on a form, where you need to comment out super() of data source write() method.

Now you can create new form, where you have full control over all the code and use it as a form part on a form extension.

Let’s look how it’s done in a standard application using CaseDetail.Extension form extension as an example.

casedetail-extension

This extension has a couple of new form parts. Each form part is a simple form:

CaseMoreInformation_CustTable.jpg

As you can see, it has one data source and a group with several fields. Here it is used to show fields in a details format, however, you can use grid and action panes if you want. A good example is Addresses and Contact information tabs on customer form that are done with form parts as well.

The most interesting part is how to link new form part with form extension. First, you need to add new “From part” control to a form extension design and specify display menu item name of a form part. After that you will see new “Links” node.  It supports four different types of links:

Field relation link Link the form data source with the form part data source by the field.
Field value link Link the form data source with the form part data source by specifying fixed value for the form part data source field.
Table relation link Link the form data source with the form part data source using relation on the form data source table to the form part data source table.
Target table relation link Link the form data source with the form part data source using relation on the form part data source table to the form data source table.

Choose link type that suits, set properties and you are good to go!

AX 7. Clear Extension Framework cache from UI.

Extension Framework uses attribute to instantiate descendants and you need to flush cache after creating or removing an attribute. There is a blog post how to do this from code, but we have a way to do the same from UI (and it’s quite important because you cannot go and create a job in new AX anymore to flush your cache after deployment).

Go to Transportation management->Setup->Load building->Load building strategies form and click on Generate class list button. Under the hood AX will call SysFlushAOD::main() to clear all caches.

GenerateClassList.jpg

This button will clear various of different caches, not only  Extension Framework one, so could be used in different scenarios! Kudos to my fellow colleague Alexey Borisov for pointing this out!

AX 7. Azure Logic Apps, CDM and PowerApps. Part 2.

In previous blog post we built simple scenario where we created customer in AX from external web request using Azure Logic App.

This time I am going to extend Logic App that was created before to support Common Data Model (CDM). There are couple of APIs that we can use:

cdmapis

In this case, I want to create a new record in CDM every time when a customer is created in AX.

We can achieve this simply by adding new API to our App and mapping the fields. I used “Customer” entity from CDM, which comes out of the box with CDM database.

logicappcdm

After request is sent and customer is created, we have two options to check it in CDM: view records using Excel add-in or build new PowerApp.

Option 1: Go to powerapps.microsoft.com, find your entity and open it in Excel.

Excel Add-in supports all features that we like in AX 7, so you can create, update and delete records with it.

CDMExcelAddin.jpg

Option 2: Create from powerapps.microsoft.com new app in PowerApps Studio for Windows or PowerApps Studio for Web.  I personally prefer desktop version, because it works faster for me.

powerappsstudio

From PowerApps studio you can create new App for CDM, CRM or even for AX. When you create new CDM App you need to specify connection to database and select entity, same as above we will select “Customers”.

After that studio will automatically generate an App for us.

powerappsdesigner

By default, new App has 3 screens: Browse, Details and Edit.

We will modify the layout and delete non relevant fields. So instead of Fax, Phone and Address we will put Customer Id and Description on Browse screen and add contact information to Details screen.

PowerAppsScreens.jpg

That’s all, now we can run the App and see what we’ve got.

powerappsbrowsescreen

powerappsdetailsscreen

Nice and simple way to build new App to work with customers without even a line of code!

AX 7. Azure Logic Apps, CDM and PowerApps. Part 1.

We are all exited with Dynamics365 coming. As we know, it is based on PowerApps, Common Data Model and Flow. Finally, we can put all these features together with connector to AX online which is released recently (https://flow.microsoft.com/en-us/blog/more-september-updates/).

Today, I will show how Azure Logic Apps can be used in integration scenarios. For example, we want to create a new customer in AX from an external web service. In previous versions it was non-trivial task, but now it could be done in couple of clicks!

First of all, we need to create a new Logic App. We can do this either from Visual Studio or directly from Azure portal.

This app will receive requests from external web services and to handle this we will add HTTP request trigger that accepts JSON.

httprequestrigger

To build JSON schema you can use one of dozens online services. Here is example from one of them:

jsonshemabuild

As you can see, my request contains AccountNum, DataAreaId, CurrencyCode, CustomerGroupId, name and AddressCountryRegionId fields.

On the next step we will add Dynamics AX connector that uses values from JSON request to create new customer. After specifying connection to AX 7, you can select any OData entity from drop down list and it will automatically add all mandatory fields. Each filed you can map with value from JSON simply selecting them from a list.

dynamicsaxconnection

That’s all, save it and we can start testing.

To send the request I used Postman, but in real life scenario it would be a web service.

postman

Voila! New customer has been created in AX.

newcustomer

On Azure portal you can track execution status and debug into each step, where you can see all data that was sent.

execution-status

execution-status

What’s next? You can add a response to notify web service if record was successfully created, send email with confirmation or even SMS.

In my next post I’m going to look at CDM and PowerApps, stay tuned!