D365O. Issues with Microsoft Dynamics App for Office setup.

Recently I’ve helped my colleague to setup Word Templates for D365O. We followed Office integration tutorial on the wiki, but it has few gaps.

First of all, it’s not clear where to find share that contains the Microsoft Dynamics App manifest ( step 1.d). Use base URL for the cloud environment, for example, https://topo00dfa4stbobaos.cloudax.test.dynamics.com/

Second step where we stuck for a while is not so obvious. 4.b says that you need to select Template Designer after loading applets. However, there was nothing to select. To fix this issue you actually need to go to D365O -> System Administration -> Setup -> Office app parameters and on “Registered applets” tab check that there is a record for Microsoft.Dynamics.Platform.Integration.Office.WordDesignerApplet in a grid. 

OfficeAppParameters.jpg

If it’s not there click on “Initialize applet registration” button and AX will auto populate the grid.

 

D365O. How to deploy Demo VM using Visual Studio subscription.

Many of us have Visual Studio subscription (formerly called MSDN subscription) and there is monthly Azure credit coming with it. It could be used to spin up D365O demo VM, but the whole process is not straightforward. I would describe it step by step today.

  1. Activate monthly Azure credit. You will get an email with subscription id after completion.ActivateSubscription.png
  2.  Go to Azure portal where you can check details of new subscription.AzurePortal.jpgAzurePortalSubscription.jpg
  3. Different types of subscriptions give you different amount, enterprise is the best (but costs a lot) and gives you 150$ monthly credit, that is enough to run D12V2 VM for 235 hours per month.
  4. With new subscription you get new tenant. Now we need to activate D365 trial on this tenant. That’s a mandatory step for ARM onboarding. Existing customers can follow steps on the customer source  to request access via email. Partners can find details on the partner source , you have 2 options there: request new tenant with trial or activate trial on existing tenant. We will activate trial because we already have a tenant with subscription, but it’s possible to transfer subscription to new tenant as well.
  5. To activate trial on existing tenant
    1. Create new user .AzureActiveDirectoryAddUser.jpg
    2. Make new user Owner of the subscriptionAzureSubscriptionOwner.jpgAzureSubscriptionOwnerNew.jpg
    3. Make new user Global admin of Active Directory.AzureActiveDirectoryUserRole.jpg
    4. Follow a link to trial offer from partner source article. You need to login using credentials of newly created user.ActivateTrial.jpgconfirmtrial
  6. No we are ready to create new LCS project. Go to LCS and login using user created on step 4. LCSNewProject.jpg
  7. Fill all the details required: Version of AX, project name and methodology.LCSNewProjectDetails.jpg
  8. Setup connection to Azure.LCSMicrosoftAzureSettings.jpg
  9. In the organizations list you will see tenants of project users. You can invite users from different tenants to a project and you will see their organization in the list as well.  lcsnewprojectauthorizeCurrent user should be administrator of the tenant to complete authorization.LCSNewProjectAuthorization.jpgLCSNewProjectAuthorizationComplete.jpg
  10. Add Dynamics Deployment Services [wsfed-enabled] to the subscriptionAddDynamicsDeploymentServices.jpg
  11. Setup Azure connector. Check “Configure to use Azure Resource Manager” checkbox or you won’t be able to deploy any VM starting from Update 2. Azure Subscription Id and tenant name could be found on Azure portal.LCSSetupAzureConnector.jpgLCSSetupAzureConnector2.jpgLCSSetupAzureConnector3.jpg
  12. For ARM deployments we don’t need to download any certificate, so just click “Next” on this step.lcssetupazureconnector4
  13. Select Azure region. Please note that not all regions are available for Visual Studio Subscriptions. If you select wrong region you will have to create new connector with another region because all deployments would fail.LCSSetupAzureConnectorRegion.jpg
  14. Deploy new Demo VMLCSDeployNewVM.jpgLCSDeployNewVM2.jpgLCSDeployNewVM3.jpg
  15. Enter VM name and go to Advance settingsLCSDeployNewVM5.jpg
  16. Select demo data package or “None” for blank environment. Here you could select demo data from ISV partners as well if they shared it with you.LCSDeployNewVM6.jpg
  17. Select number of disks. Select 7 if you want to scale it down to D12V2 later, otherwise you would have to manually delete them, that is not an easy task, because it’s not possible to change size if target VM has different disks configuration. LCSDeployNewVM7.jpg
  18. Select GER configuration to be deployed. You can deploy any later from assets library if you missed this step.LCSDeployNewVM8.jpg
  19. Select VM size. Different sizes have different hardware configuration and costs. To find costs you can use Pricing calculator. I prefer to use D13V2 and D12V2 because I think they have best  price–performance ratio.LCSDeployNewVM9.jpg

That’s all. Usually deployment takes up to 5 hours. Don’t forget to setup auto shutdown to save your costs!

Links

Filter OData entity by enum field.

As you may know, logic apps do not have any triggers available for Dynamics365 for Operations. So we have to use recurrence trigger to read data periodically from a data entity and obviously we want to filter a query. This is pretty straightforward with string or integer fields, but not with enums.  Under the hood AX enums are enumeration in OData.

For example, I have new entity “RentalInvoices” and I want to filter it by AXCRMTransferStatus field and select only “pending” records.

Designer view:

LogicAppODataFilterDesignerView.png

Code view:

LogicAppODataFilterCodeView.png

Role-based security in Dynamics 365 for Operations. Export security changes and Security diagnostics tool.

As we know from my previous post we can create security artifacts from Visual Studio and user interface. Now we want to deploy them. All objects created in VS could be deployed via deployable package, there is no difference with any code you created. Changes done from UI could be deployed as data via data package. Out of the box we have three data entity to work with security customizations:

SecurityCustomizationEntities.jpg

In the export file for duty customization we will get duty name and xml object contains all privileges assigned to the duty. Other two (roles and privileges) have similar structure.

ExportedDutyChanges.jpg

ExportedDutyChangesXML.jpg

That’s a big improvement in security setup, because in AX 2012 it was hard to track what changes were done in test environment and should be transferred to live.

Security diagnostics tool

To easy the problem of security setup in AX 2012 we have Security Development Tool, unfortunately, it’s not available in current version (Microsoft is working on the replacement). However, there is another useful tool – Security diagnostics tool.

Each form in AX has “Options” tab on the action pane where you can find “Security diagnostics” button. It shows you a list of all roles, duties and privileges that grant access to current form. Using buttons on the top of the dialog you can add role to specific user, duty to specific role or privilege to specific duty to quickly grant access to current form.

SecurityDiagnosticsFromForm.jpg

Using task recorder to setup security.

Task recorder  has been re-built in current release and could be used in different scenarios apart of actual task recording. One of them is security setup, now you can analyse task recording with Security diagnostics tool.

Create task recording.

taskrecorder

taskrecordersteps

Save it.

taskrecordersavesteps

Go to System administrations -> Security -> Security diagnostics for task recordings and open saved recording.

SecurityDiagnosticsOpenRecording.jpg

As the result you will see all menu items involved. When you select user AX shows if he already has permission to entry points.

SecurityDiagnosticsSelectUser.jpg

“Add reference” button will open you Security diagnostics dialog, exactly the same we saw using Security diagnostics tool, where you can do all the setup.

SecurityDiagnosticsAddtoUsersRole.jpg

Dynamics 365 for Operations. Code to disable XDS policy.

To disable XDS policy in AX 2012 we can use

xdsServices.setXDSState(0);

However, this method does not exist in new AX any more. Now we need to wrap code where we want to disable XDS policy with unchecked(Uncheck::XDS)

unchecked(Uncheck::XDS)
{
    select ValidTimeState(_asOfDate) maxof(ValidTo) from hcmEmployment
        where hcmEmployment.Worker == _workerRecId;
}

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.