Blogs


  • How to create a Web Job in Azure


    Hi, the purpose of this blog post is to show developers how to create a Web Job.
    -Go to Azure Portal. 
    -Search for Resource group. Create a Resource group if you don’t have any:

     
     
    -Search for APP service and create an app service 
    -Give any name you want to the instance:
     
      
     
    -Select the publish code, or if you need any docker, select Runtime stack (either core or asp.net frame work or tomcat). Go to Monitoring tab if you want to enable Application insights. By default, it will be enabled.
     
    -Click on Review + Create and click on Create.
    -Go to Azure portal -> Resource groups -> Resource (which you have created) -> WebJob App Service. Click on “Get Publish profile” and download and import it into Visual studio to publish the web job.
    -Go to Azure portal and search for Storage accounts and click on it. 
    -Click on Add. 
    -Click on Review +Create.
     
    -Go to Storage account, click on the Storage account previously created. Click on Access keys and copy the connection string.
     
      
     
    Now go to Visual Studio to create a Console APP
     
    File->New->Project
     
      
     
    Right click on solution -> click add -> New Item -> select JSON type and name it Settings.job
     
      
     
    Open the “Settings.job” and add the following CRON expression:
     
    {
    "schedule": "0 0 */4 * * *"
    }
     
    Note: The basic format of the CRON expressions in Azure is:
     
    {second} {minute} {hour} {day} {month} {day of the week}

    Here more documentation about this:
    https://docs.microsoft.com/en-us/azure/azure-functions/functions-bindings-timer?tabs=in-process&pivots=programming-language-csharp#ncrontab-expressions 
     
    Go to App.config and add the below line in the connectionstring tag
     
    <add name="AzureWebJobsDashboard" connectionString= ”<connection string copied from storageaccount> " />
    <add name="AzureWebJobsStorage" connectionString = ”<connection string copied from storageaccount>" />  
     
    Click on "Microsoft Azure App service" or Import the profile which we downloaded in the earlier steps.   
     
    Below, the screen should appear. Go to the settings tab, set configuration to “Release,” and publish.
     
    Wait for the message below to appear in the output window of Visual Studio to assure that the deployment was successful.
     
    Go to the Azure Portal on the below screen and click on WebJob. You should be able to find your WebJob here.
     
    Original Source:
    https://www.c-sharpcorner.com/blogs/how-to-create-a-webjob-in-azure-using-visual-studio

    Official Documentation:
    https://docs.microsoft.com/en-us/azure/app-service/webjobs-dotnet-deploy-vs

    Enjoy it!


    Continue reading...



  • D365 CRM: How to access in Canvas APP the form fields of an Entity record


    By default, when you create a Canvas App and try to access the form fields of an entity record, these fields will not be present in the designer:

     

    To do this you need to add first the Canvas APP in the form. Copy your Canvas APP Id in the Details section:

     

    And paste it in the form editor:


     
    The Entity name field will auto populate the entity logical name of the form, in this case Contact.

    Then click the Customize button. This will be open a new window to edit the Canvas APP but now with a new component: ModelDrivenFormIntegration.
     
    Then select the control you need to use to show the field value, for example fullname. In the Text function put “[@ModelDrivenFormIntegration].Item.'Full Name'”. But this will throw an error because we need to specify to ModelDrivenFormIntegration the DataSource. To do this add the Entity in the Data section:

     

    And map that in the DataSource property of the ModelDrivenFormIntegration.
     
    And now you will see the values correctly:

     

    Save and publish the Canvas APP.
    Save and publish the form.
     
    Enjoy it!

    Continue reading...



  • Edit Form, our new Product


    In the previous versions of D365/Dynamics CRM you could access in the Form Editor clicking this button in the ribbon, and Microsoft removes/hides this button.

    Now with our product Edit Form you can recover this button in the new versions.

    With the Edit form solution you can easily edit the form of the table you are working on, click on Edit Form and select the modern or classic option.

    This solution helps you edit the form without having to go and find the solution where the form is installed.

    NOTE: you need to be System Administrator or System Customizer to be able to see the Edit Form button.

    Check our product section to download Edit Form FREE and install it in your D365 environment.
     


    Continue reading...



  • D365 CRM: Call Azure Function from Dynamics CRM using Plugin Webhook


    D365 CRM has the feature to call an Azure Function app as a plugin step triggered in a target event of an entity record, for example in the create of an account record. To archive this you need to develop in Visual Studio a project of type Function App an create a Http Trigger Get function in this project with the following code to read the request of CRM:
     


    This simple code will get the request body as a string that you can parse in a JSON object. You can use RemoteExecutionContext class to actually get all the contextual information into the Function app and then use it further.
    Once ready your code, Publish in Azure.
    Open Plugin Registration Tool to register the new Webhook:
     


    Enter the Webhook details. Select Authentication type as WebhookKey:

     

    To get the URL and the key, go to the Function App deployed in portal and look </> Get function URL to copy the function URL:

     

    The key will be the value after code= in the URL. Paste it in the Webhook and Save.
     
    Then create a New Step inside of the Webhook in a target event of the entity (for example in the Create of account) and test it.
     
    You can check the log of the function to see the request body that CRM sent to the Function App:
     
    And with this, you can call Azure Function using Plugins with D365 CRM.

    Enjoy it!

    Continue reading...



  • D365 CRM: How to get the connection string of your CRM Organization stored in an Azure Key Vault


    It is a good practice for security purpose to store in an Azure Key Vault your credentials and/or connections to the different system such as the D365 CRM connection string. Here I will share a portion of code to retrieve a Secret stored in an Azure Key Vault, for example a Connection String to use in the CRM API.
    Important Note: You will need for this a Client ID and a Secret Key generated to authenticate to the Key Vault.
    -First of all, create in your Azure Key Vault a Secret that contains your connection string of CRM.

     

    -Take a note of your Client ID, Secret Key, Vault URI, and the Secret Name created in the previous step.
    -Use the following code to get the Secret value of the Key Vault:

     

    This will return the secret value stored in that Key Vault Secret, in this case the connection string to connect to the CRM API.

    Enjoy it!
     

    Continue reading...



  • D365 CRM: Execute server-side bulk operations in Dynamics CRM


    In Microsoft Dynamics 365 Customer Engagement server-side automation, ex. plugins and custom workflow activities, we can write code to create, retrieve, update and delete individual records. When we need to carry out these operations on multiple records, we can use a loop and query the database for each record. However, a much more efficient way to carry out bulk operations is to use the ExecuteMultipleRequest message (to Create, Update and Delete records in bulk). In this example I will show a code to execute a bulk update of thousands of accounts records that would make the process a bit slow if you do it by one by one.

    1.    EntityCollection accountActiveCollection = GetActiveAccounts(); // Your query to get the accounts
    2.    List<Entity> accountList = new List<Entity>();
    3.     
    4.    foreach (Entity account in accountActiveCollection.Entities)
    5.    {
    6.        Entity up = new Entity("account", account.Id);
    7.        up["remcod_legalname"] = account["name"].ToString();
    8.     
    9.        accountList.Add(up);
    10.     
    11.        if (accountList.Count > 500) // When the batch list is more than 500 then I will call the execute request to update                
    12.        {
    13.            Console.WriteLine("Update");
    14.     
    15.            ExecuteMultipleRequest requestWithResults = new ExecuteMultipleRequest();
    16.            requestWithResults = new ExecuteMultipleRequest()
    17.            {
    18.                // Assign settings that define execution behavior: continue on error, return responses. 
    19.                Settings = new ExecuteMultipleSettings()
    20.                {
    21.                    ContinueOnError = true,
    22.                    ReturnResponses = false
    23.                },
    24.                // Create an empty organization request collection.
    25.                Requests = new OrganizationRequestCollection()
    26.            };
    27.     
    28.            foreach (Entity entity2 in accountList)
    29.            {
    30.                UpdateRequest UpdateRequest = new UpdateRequest { Target = entity2 };
    31.                requestWithResults.Requests.Add(UpdateRequest);
    32.            }
    33.     
    34.            try
    35.            {
    36.                ExecuteMultipleResponse responseWithResults = (ExecuteMultipleResponse)Service.Execute(requestWithResults);
    37.            }
    38.            catch (Exception ex)
    39.            {
    40.                Console.WriteLine(ex.ToString());
    41.            }
    42.     
    43.            accountList.Clear(); // Clean the list to start again to fill it until 500
    44.        }
    45.    }
    46.     
    47.    if (accountList.Count > 0) // Execute the remaining records
    48.    {
    49.        Console.WriteLine("Update");
    50.     
    51.        ExecuteMultipleRequest requestWithResults = new ExecuteMultipleRequest();
    52.        requestWithResults = new ExecuteMultipleRequest()
    53.        {
    54.            // Assign settings that define execution behavior: continue on error, return responses. 
    55.            Settings = new ExecuteMultipleSettings()
    56.            {
    57.                ContinueOnError = true,
    58.                ReturnResponses = false
    59.            },
    60.            // Create an empty organization request collection.
    61.            Requests = new OrganizationRequestCollection()
    62.        };
    63.     
    64.        foreach (Entity entity2 in accountList)
    65.        {
    66.            UpdateRequest UpdateRequest = new UpdateRequest { Target = entity2 };
    67.            requestWithResults.Requests.Add(UpdateRequest);
    68.        }
    69.     
    70.        try
    71.        {
    72.            ExecuteMultipleResponse responseWithResults = (ExecuteMultipleResponse)Service.Execute(requestWithResults);
    73.        }
    74.        catch (Exception ex)
    75.        {
    76.            Console.WriteLine(ex.ToString());
    77.        }
    78.     
    79.        accountList.Clear();
    80.    }

    Let explain the code:
    -First, I get by query all the active accounts.
    -Then for each one I take the account name and set the remcod_legalname (custom field) with that value (here put your business operation).
    -I add in a custom List collection, the record to update.
    -When that list has more than 500 items, then I will generate ExecuteMutipleRequest with these records to update and clean the list. I recommend this max number of items to send to the request to not generate time out exceptions in CRM.
    -When the for each end, I check if the list contains some remaining account record to execute them.

    Original Source:
    https://itsfascinating.com/d365/tag/updaterequest/

    Official Documentation:
    https://docs.microsoft.com/en-us/dotnet/api/microsoft.xrm.sdk.messages.updaterequest?view=dataverse-sdk-latest
    Enjoy it!

    Continue reading...



  • D365 CRM: How to get de Audit Details of an Entity record using the SDK API


    If you need to get the audit details of an entity record here is a simple code using the SDK API of CRM:
    First, we need to get the audit collection of the entity record:

                QueryExpression auditQuery = new QueryExpression();
                auditQuery.EntityName = "audit";
                auditQuery.PageInfo.PageNumber = 1;
                auditQuery.PageInfo.PagingCookie = null;
                auditQuery.ColumnSet = new ColumnSet(true);

                FilterExpression filter = new FilterExpression();
                filter.FilterOperator = LogicalOperator.And;

                ConditionExpression condExp = new ConditionExpression();
                condExp.AttributeName = "objectid";
                condExp.Operator = ConditionOperator.Equal;
                condExp.Values.Add(recordId); // The record Id to get the auditing
                filter.AddCondition(condExp);

         // You can add here another condition to get the audit of a specific date using the created-on attribute filter

                OrderExpression order = new OrderExpression();
                order.AttributeName = "createdon";
                order.OrderType = Microsoft.Xrm.Sdk.Query.OrderType.Ascending;
                auditQuery.Orders.Add(order);
                auditQuery.Criteria = filter;

                EntityCollection auditCollection = Service.RetrieveMultiple(auditQuery);

    Then we need to get for each audit retrieved, the details to see the difference of the attribute value:

    foreach (Entity audit in auditCollection.Entities)
    {
    string action = audit.FormattedValues["action"].ToString(); // The action of the change for ex. Create/Update/Delete
    RetrieveAuditDetailsRequest auditDetailsRequest = new RetrieveAuditDetailsRequest
    {
            AuditId = audit.Id
    };

    RetrieveAuditDetailsResponse auditDetailsResponse = (RetrieveAuditDetailsResponse)Service.Execute(auditDetailsRequest);
    if (((AttributeAuditDetail)auditDetailsResponse.AuditDetail).NewValue != null)
    {
       if (((AttributeAuditDetail)auditDetailsResponse.AuditDetail).NewValue.Attributes.Count > 0)
          {
              var oldDetail = ((AttributeAuditDetail)auditDetailsResponse.AuditDetail).OldValue; // The Old value
              var newDetail = ((AttributeAuditDetail)auditDetailsResponse.AuditDetail).NewValue; // The New value
          }
    }
    }

    Enjoy it!
     

    Continue reading...



  • D365 CRM: Use Certificate Thumbprint to connect to D365 CRM API


    We know that we can use User/Password or App ID/Secret key to connect a D365 Organization CRM API but if you need a high security level of connection, using of a certificate is available. For this you need to configure a valid Certificate in your Azure Tenant where your D365 CRM is installed. 
    You need to run the following Nugets in your project to access the different classes to archive this:

    Install-Package System.Security.Cryptography.X509Certificates
    Install-Package Microsoft.CrmSdk.XrmTooling.CoreAssembly

    The code to connect to a CRM API using thumbprint is the following:

    1.    private CrmServiceClient InitializeOrganizationService()
    2.    {
    3.           // Force TLS 1.2 for Dynamics 365 v9.0 instances
    4.    System.Net.ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;
    5.    string tenantID = “your TENANT ID”;
    6.    string thumbprint = “the THUMBPRINT of your certificate”;
    7.    // Get the Access Token using the thumbprint certificate
    8.    AuthenticationContext authenticationContext = new AuthenticationContext(“https://login.microsoftonline.com/” + tenantID);
    9.    // Find the Certificate by thumbprint in Azure
    10.    X509Certificate2 userCert = FindCertificate(thumbprint, StoreName.My);
    11.     
    12.    var clientCertificate = new ClientAssertionCertificate(clientId, userCert);
    13.     
    14.    AuthenticationResult authenticationResult = authenticationContext.AcquireToken(organizationUrl, clientCertificate);
    15.    string diferencesDate = (authenticationResult.ExpiresOn.Offset.Hours * -1).ToString();
    16.     
    17.    var requestedToken = authenticationResult.AccessToken;
    18.     
    19.    OrganizationWebProxyClient orgWebProxService = new OrganizationWebProxyClient(GetServiceUrl(organizationUrl), false);
    20.     
    21.    orgWebProxService.HeaderToken = requestedToken;
    22.    CrmServiceClient service = new CrmServiceClient(orgWebProxService);
    23.    if (!service.IsReady)
    24.           {
    25.              throw new Exception(service.LastCrmError);
    26.    }
    27.    // Return the Service connected to the CRM Organization
    28.    return service;
    29.    }

    And use this code to find the certificate by thumbprint in your tenant:

    1.    private static X509Certificate2 FindCertificate(string certificateThumbprint, StoreName storeName)
    2.    {
    3.           var storeLocationArray = new[]
    4.           {
    5.               StoreLocation.CurrentUser,
    6.               StoreLocation.LocalMachine
    7.           };
    8.     
    9.          try
    10.          {
    11.             X509Certificate2Collection certificates = null;
    12.             if (storeLocationArray.Any(storeLocation => TryFindCertificatesInStore(certificateThumbprint, storeLocation, storeName, out certificates)))
    13.             {
    14.                 return certificates[0];
    15.             }
    16.          }
    17.          catch (Exception ex)
    18.          {
    19.             throw new Exception(string.Format("Failed to find certificate with thumbprint: {0}.", certificateThumbprint), ex);
    20.          }
    21.     
    22.          throw new Exception(string.Format("Failed to find certificate with thumbprint: {0}.", certificateThumbprint));
    23.    }
    24.     
    25.    private static bool TryFindCertificatesInStore(string thumbprint, StoreLocation location, StoreName storeName, out X509Certificate2Collection certificates)
    26.    {
    27.          X509Store store = new X509Store(storeName, location);
    28.          store.Open(OpenFlags.ReadOnly);
    29.          certificates = store.Certificates.Find(X509FindType.FindByThumbprint, thumbprint, false);
    30.          store.Close();
    31.     
    32.          return certificates.Count > 0;
    33.    }

    Enjoy it!





    Continue reading...



  • D365 CRM: Configure Application User to use it in the Connection String


    Now is a good practice to use an Application User instead of a User/Password to connect to the SDK API for security purpose. To archive this, you need to follow these steps in your Azure Tenant:
    -Create an App Registration under Azure Active Directory:
     


    -Set the name that will be displayed in D365. Leave the Supported account type as Single Tenant (default value).
    -Once created, copy the Application (client) ID to use later as the Client ID and the Directory (tenant) ID to use later as the Tenant ID:
     
    -Navigate to Certificates & secrets and click New client secret, under Client secrets:
     
    -Enter a description for the client secret and select the expiry date for the client secret. The maximum you can select is 24 months. After the expiry, a new secret will need to be generated, and any applications using the secret will need to be updated. It’s worth setting a reminder for this, to avoid any service interruptions.
    -Once the secret is created, copy the Value. This will be used as the Client Secret later. This is the only chance you’ll have to copy the secret, so be sure to save it somewhere safe. If you lose the value, you can always generate another later.



    -Navigate to API permissions and select Add a permission:

    -From the Request API permissions screen, located Dynamics CRM:
     
    -Under Delegated permissions, select user_impersonation and then Add permissions:
     
    -Open the Power Platform Admin Center as a System Administrator, and select your Dynamics 365 Environment, select Settings, under Users + permissions select Application users:
     
    -Click New app user, click Add an app, select the App you created earlier. Then select the Business Unit and add the System Administrator role:

    To finish, in your code that needs to consume the SDK API, configure the connection string using the Client ID, the Client Secret and the Tenant ID copied in the previous steps like this:
    AuthType=Secrets;url=https://{CRM_ORGANZATION}.api.crm.dynamics.com;secret={CLIENT_SECRET};AppId={CLIENT_ID};tenantid={TENANT_ID};RequireNewInstance=true;SkipDiscovery=true

    Original Source:
    https://blog.magnetismsolutions.com/blog/paulnieuwelaar/2021/09/21/setting-up-an-application-user-in-dynamics-365

    Enjoy it!

    Continue reading...



  • D365 CRM: Update user setting using C# SDK API


    Sometimes you need to update a specific configuration of the users, for example the default time zone of the system. By default, these settings are Personal, so every user needs to be notified to change this by hand. But you can do it using an entity in the API called UserSettings. Let’s take this example: you need to update the default time zone of all users in your CRM system. To do that you need to retrieve all the active users first and for each one you need to update his/her time zone:

    1.    ColumnSet myColumnSet = new ColumnSet();
    2.    myColumnSet.AddColumn("fullname");
    3.    myColumnSet.AddColumn("systemuserid");
    4.    myColumnSet.AddColumn("businessunitid");
    5.    
    6.    QueryExpression query = new QueryExpression();
    7.    query.EntityName = "systemuser";
    8.    query.ColumnSet = myColumnSet;
    9.     
    10.    ConditionExpression condition = new ConditionExpression();
    11.    condition.AttributeName = "isdisabled";
    12.    condition.Values.Add(false);
    13.    condition.Operator = ConditionOperator.Equal;
    14.    query.Criteria.AddCondition(condition);
    15.     
    16.    // Get all the active users
    17.    EntityCollection results = Service.RetrieveMultiple(query);
    18.    if (results != null && results.Entities.Count > 0)
    19.    {
    20.       foreach (Entity item in results.Entities)
    21.       {
    22.            // Generate the setting entity
    23.            Entity settings = new Entity("usersettings");
    24.            settings["systemuserid"] = item.Id;
    25.            settings["timezonecode"] = 4; // Pacifit Time (US & Canada) GMT -08:00
    26.            // Generate the request and response of the Update User Setting
    27.            UpdateUserSettingsSystemUserRequest settingsUpdReq = new UpdateUserSettingsSystemUserRequest();
    28.            UpdateUserSettingsSystemUserResponse settingsUpdRsp = new UpdateUserSettingsSystemUserResponse();
    29.            settingsUpdReq.Settings = settings;
    30.            settingsUpdReq.UserId = item.Id;
    31.            settingsUpdRsp = (UpdateUserSettingsSystemUserResponse)Service.Execute(settingsUpdReq);
    32.       }
    33.    }

    The complete reference documentation about this entity:
    https://docs.microsoft.com/en-us/dynamics365/customerengagement/on-premises/developer/entities/usersettings?view=op-9-1 

    Enjoy it!

    Continue reading...