This article talks about building and creating pipelines on Azure DevOps for Azure functions .NET core/.NET 6
The steps to build a build and release pipelines are follows:
Select your Repo
Configure a pipeline template ex. ASP.NET, ASP.NET core, Starter
Under a starter pipeline – There are 3 steps to create a starter pipeline.
Build a script for compiling and building the code
A script to archive code files in a zip folder
Publish the artifact
The first step is to create a build pipeline.
In Azure DevOps -> create a new pipeline -> Select Azure Repos Git -> Select the Repository -> Select a starter pipeline
In Review your pipeline step, remove the default steps from the script. We will use the assistant to build the scripts. From the assistant on the right side, select .NET core task and add the command, path to project, and arguments, and click on the Add button
The Arguments are: –output $(Build.BinariesDirectory)/publish_output –configuration Release
You can also make a variable for the argument value and use the variable.
Let’s add the code to archive this project into a zip file.
Select Archive files from the Task assistant. Archive the files under publish_output folder and uncheck the prepend option
This is going to create an archive under the staging directory.
Let’s go ahead and publish this artifact. Select “publish build artifacts” from the Task assistant
Click “Save and Run”
You can see the published artifact in the build jobThe published folder will look this:
In power automate, if you run into the following error:
The ‘inputs.parameters’ of workflow operation ‘Get_a_row_by_ID_3’ of type ‘OpenApiConnection’ is not valid. Error details: The resolved string values for the following parameters are invalid, they may not be null or empty: ‘recordId’
Check your previous steps, and make sure the parameter that you’re passing into your step (in this case, Get Row by ID) Row Id gets a valid record id (guid). In the above flow, in Get row by ID 2, I was retrieving a record and selecting only one column in Select columns, this attribute is a lookup on the entity record. This wasn’t bringing the result in the desired result in the body upon running the flow, and I was referencing this value in the Get row by Id 3 step, hence the error, I am referring to a value which doesn’t exist.
The way to reference the column in the select column is _new_accountid_value
After I added the above column, the flow ran successfully, and the value was available in the next steps.
Canvas Apps can be launched from within the Dynamics CE using a ribbon button and calling a javascript funtion to open the Canvas App in a new window using window.open.
However, once a Canvas app window is launched, you can’t close the window. Microsoft provides Exit() and Exit(true) functions. Exit() navigates away from the current page and redirects to the list of canvas apps, while Exit(true) signs out the user and navigates to the login page. In either case, the window should be closed manually by the user.
I found a way to close this window using PCF button embedded within a canvas app. This a two step process:
Creating and deploying PCF button control to the Dynamics CE/CDS/Dataverse environment
Adding this PCF button control to the Canvas App
Creating and deploying the PCF button control to the Dynamics CE environment:
You can follow this blogto create and deploy the PCF button control with one additional changes. In the submitClicked(event:Event) handler, add the following code
2. Adding the PCF button control to the Canvas App
To enable PCF control to be used within the Canvas App, you need to:
Enable power apps component framework for canvas app in the power platform admin center
Enable Components in Preview Features of the Canvas App settings
Enable power apps component framework for canvas app in the power platform admin center
Go to admin.powerplatform.microsoft.com
Select the Environment where you want to embed the PCF control
Click Settings -> Product -> Features
Enable the Power Apps component framework for canvas apps
The next step is to Enable Components in Preview Features of the Canvas App settings
Open the canvas app
Select File -> Settings -> Advance Settings
Under Preview Features -> Enable Components
Now that the PCF control components is enabled for your CDS/Dataverse environment and the Canvas App, let’s embed this control in your Canvas App.
Go to the canvas App
Select the Insert tab -> Custom -> Import component
You will notice two tabs, Canvas and Code. Select the “Code” tab
Select your PCF control (this is visible only if your pcf control is deployed to this CDS environment, see step 1 – creating and deploying the pcf control) from the list, and click on Import
Importing this component will enable the Code components section in your Insert tab in the left navigation and display your PCF control
Drag this control across to your Canvas App
Save and Publish
Test this out by opening the canvas app from the ribbon button. Now, clicking on this button should close the Canvas App.
2. Open Visual Studio Developer 2019 command prompt and navigate to the NewPcfButton folder, and type the following pcf command: pac pcf init –namespace CanvasAppClosePcfControl –name CanvasAppClose –template field
You should see a pcf project getting created in the NewPcfButton folder.
3. Open Visual studio code, and open this project by navigating to the NewPcfButton folder
4. Now, we will need to install the project dependencies. Click on Terminal in the visual studio code ribbon and select New Terminal. Type the command in the terminal – npm install
You’ll notice a new folder “node_modules” created in the NewPcfButton folder5. Next, type npm run build to build the solution
6. Type npm start to watch the pcf control in your local browser. You can debug and test your pcf control here
Now that we have the pcf project created, built, and run successfully, let’s go ahead and customize and add a new PCF button control. In this post, we will have not have either a bound/unbound control. Therefore, in your ControlManifest.Input.xml file, command out the following line:
Next, In your index.ts file, define the following global variables and an eventSubmitClicked event
private _context:ComponentFramework.Context<IInputs>; private theNotifyOutputChanged:() => void; private _container:HTMLDivElement; //Html Elements - Creating a text box and a button private _eleMainContainer:HTMLDivElement; private _eleButton:HTMLButtonElement; private eventSubmitClicked:EventListenerOrEventListenerObject;
Next, inside the init function, write the following code:
this._container = container; this._context = context; this.theNotifyOutputChanged = notifyOutputChanged; //The assignment of the event listener to the function should be done before creating the UI        this.eventSubmitClicked = this.submitClicked.bind(this); //Create UI        //Main Container this._eleMainContainer = document.createElement("div"); this._eleMainContainer.className = "mydiv"; //Define a button this._eleButton = document.createElement("button"); this._eleButton.className = "canvasAppButton";             this._eleButton.innerHTML = "Finish"; this._eleButton.addEventListener("click", this.eventSubmitClicked); //Add the button inside the main container, and the main container inside the container this._eleMainContainer.appendChild(this._eleButton); this._container.appendChild(this._eleMainContainer);
Let’s give this button some css styling. Notice, I have a css class “canvasAppButton” attached to this button. The idea here is to apply css to the button control, the same css styling as that of the Canvas App buttons. Create a new subfolder “css” under the main project folder and add the canvas.css file.
Inside the css file, apply the css properties to the class
.canvasAppButton {     background-color:#2d5297;     color:white;     width:150px;     height:40px;     border-radius: 8px;     border-color:transparent;     font-family: “Open Sans”, sans-serif;     font-weight: 600;     font-size: 16pt; }
Reference this css file under the ControlManifest.Input.xml file in the <resources> section.
Now, that the button is created, we bind the button to the submitClicked event. Write the following function below your init function.
//Custom event handler – for the button private submitClicked(event: Event): void{ alert(“Button is clicked”); }
Next, build the project using npm run build, and npm start to see the button in action
Click on the button.
The pcf button control works!!
Deployment:
Let’s work on deploying this pcf control to CRM
The first step is to create a solution folder inside the NewPcfControl project folder
The next step is to create a publisher and publisher prefix. This will bind the control to the given publisher and the publisher prefix. When this is imported into CRM, a new publisher and the publisher prefix is created with the given names and the pcf control is bound to that prefix. You could also make use of your existing publisher and publisher prefix if you don’t want to create a new one. Type the following command in the Developer Command prompt for VS 2019
pac solution init –publisher-name MyCompany –publisher-prefix new
The next step is add the pcf control project solution reference to the solution folder. Type the following command, but make sure you replace the path with your project path
Next, build the solution directory, type the following command:
msbuild /t:build /restore
This command will build the CRM zip file, that you will use to import your solution into CRM. Let’s look at the Solution and see where we would find the solution zip file
The CRM zip file is found inside the bin folder
Import the Solution.zip file into CRM, and use the PCF control as you see fit in any entity.
Additional Notes:
By default, the solution imported will have the display name and Name of the solution as “Solution”. The default solution version is 1.0 This is because the solution.xml contains the unique name, and description of the CRM solution as “Solution”. You can change the Unique name and Name of the solution as per your solution naming convention. See below.
If you make changes to the Pcf control code and wish to import the new changes, just run the build command (npm run build) and make directory (msbuild /t:build /restore), and import the solution. It’s a good practice to update the solution version on every build.
While implementing the PCF control, after building the solution, if you run into the following issue while importing the custom control solution
String”>An error has occurred. Try this action again. If the problem continues, check the Microsoft Dynamics 365 Community for solutions or contact your organization’s Microsoft Dynamics 365 Administrator. Finally, you can contact Microsoft Support. : Microsoft.Crm.Tools.ImportExportPublish.ImportCustomControlException: CustomControl with name failed to import with error: The import manifest file is invalid. XSD validation failed with the following error: ‘The import manifest file is invalid. XSD validation failed with the following error: ‘The ‘version’ attribute is invalid – The value ‘1.0’ is invalid according to its datatype ‘versionType’ – The Pattern constraint failed.’.”‘.” —> Microsoft.Crm.CrmException: The import manifest file is invalid. XSD validation failed with the following error: ‘The import manifest file is invalid. XSD validation failed with the following error: ‘The ‘version’ attribute is invalid – The value ‘1.0’ is invalid according to its datatype ‘versionType’ – The Pattern constraint failed.’.”‘.”at Microsoft.Crm.Tools.ImportExportPublish.CustomControlsInstaller.ValidateSchema()at Microsoft.Crm.Tools.ImportExportPublish.CustomControlsInstaller.Validate(ExecutionContext context)at Microsoft.Crm.Tools.ImportExportPublish.CustomControlsInstaller.Install() — End of inner exception stack trace — at Microsoft.Crm.Tools.ImportExportPublish.CustomControlsInstaller.Install()at Microsoft.Crm.Tools.ImportExportPublish.ImportCustomControlsHandler.ImportItem()
It is most likely due the version attribute being invalid. Make sure the version is 1.0.0 in your ControlManifest.Input.xml file
In Power Automate Flow, you may encounter the error below when tryin to save the flow
Request to XRM API failed with error: ‘Message: Flow client error returned with status code “BadRequest” and details “{“error”:{“code”:”InvalidOpenApiFlow”,”message”:”Flow save failed with code ‘InvalidTemplate’ and message ‘The template validation failed: ‘The repetition action(s) ‘Apply_to_each’ referenced by ‘inputs’ in action ‘Create_a_new_record’ are not defined in the template.’.’.”}}”. Code: 0x80060467 InnerError: Type: System.ServiceModel.FaultException`1[[Microsoft.Xrm.Sdk.OrganizationServiceFault, Microsoft.Xrm.Sdk, Version=9.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35]]
There issue may happen when you’re trying to copy a value (example, lookup) from one flow to the other because the flow may have different collection (list of records for example) names. Therefore, when you’re copying one value from one flow to the other, you’re also copying the collection name.
In the other flow, if the collection name is different, and you’re copying the above value and pasting in the flow, you’ll see the “Bad Request” error.
To fix the issue, I deleted the value, selected the lookup field from the existing flow using dynamic content. It let me save the flow. This is a simple issue, often that people may figure it out, but I wanted to put it out there if someone run into this issue.
The first step in implementing the Azure DevOps pipeline process for Dynamics 365 is to extract/decompose the CRM solution into its individual components. This is done through the solution packager provided by Microsoft. You can download and install the solution packager from this link
After you install the the solution packager on your disk, you will see the following list
You can find the SolutionPackager.exe file in the CoreTools
Open windows powershell command prompt and navigate to the SolutionPackager.exe folder – D:\NuGetTools\Tools\CoreTools
Before we go to the next step, in the CRM instance, create a new empty solution and export the solution, this solution file is a zip file containing the customizations and solutions xml
We will try to extract this solution using the solution packager and see what it looks like. Go back to the powershell command, and type the following
Because, our solution was empty, we did not find the Enities, Plugins, Webresources folders. Let’s add few components to our Dynamics CRM solution
Export this solution, and run the SolutionPackager.exe from the powershell again, you’ll notice extraction of the different components
After you navigate to the output folder, you’ll see the different folders
Navigating inside the Entities folder, you’ll notice that the entities are split into their own folders and files containing the forms, views, charts etc.
Now, you can upload the root folder to the Git/GitHub/TFS, or any other repo, and take the next step in the integration of Azure Dev Ops pipeline for CRM.
In Dynamics 365 data import, the data imported will create records with today’s date and the data created will be logged against the user who is currently logged in even though the original createdon and createdby user could be different.
In many cases, when you’re importing an existing or legacy data in your Dynamics 365, you want to retain the original createdon date and the user who created that record. If you want to import your data with the date when the data was created, you can use overridecreatedon to override the data with the createdon date. You can also create this record against the original createdby user through impersonation. There are few ways to achieve this in Dynamics 365. You can use the kingswaysoft SSIS package to achieve this. However, in this blog, you will see how overridecreatedon and created by can achieved through xRM solution.
We have to use the OrganizationServiceProxy class to impersonate the createdby user. The class object has a property called “CallerId” that will be used to impersonate a user.
Here’s the code:
using Microsoft.Crm.Sdk.Messages;
using Microsoft.Xrm.Sdk;
using Microsoft.Xrm.Sdk.Client;
using System;
using System.Net;
using System.ServiceModel.Description;
using Microsoft.Xrm.Sdk.Query;namespace DynamicsProxy
{
class Program
{
static void Main(string[] args)
{
try
{
string crmUserName = "xxxx@onmicrosoft.com";
string crmPassword = "xxxxxx";
string crmOrgHost = "https://orgname.crm.dynamics.com/";
ClientCredentials clientCredentials = new ClientCredentials();
clientCredentials.UserName.UserName = crmUserName;
clientCredentials.UserName.Password = crmPassword;
// For Dynamics 365 Customer Engagement V9.X, set Security Protocol as TLS12
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;
string crmApiPath = "XRMServices/2011/Organization.svc";
Uri crmUri = new Uri(crmOrgHost + crmApiPath);
OrganizationServiceProxy orServiceProxy = new OrganizationServiceProxy(crmUri,null,clientCredentials,null);
orServiceProxy.EnableProxyTypes();
//Caller Id: this is the guid of the user we want to impersonate - 4b591077-9fb8-e911-a86e-000d3a372124
orServiceProxy.CallerId = new Guid("47d57787-433b-eb11-a813-000d3a31c841");
Entity ent = new Entity("contact");
ent["lastname"] = "Hameed";
ent["overriddencreatedon"] = new DateTime(2021, 1, 19);
orServiceProxy.Create(ent);
}
catch (Exception ex)
{
Console.WriteLine("Exception: "+ex.Message);
}
}
}
}
During the code execution, you may run into an error:
{“Principal user (Id=xxxx-xxx-xxx-xxx-000d3a31c841, type=8, roleCount=5, privilegeCount=811, accessMode=0), is missing prvOverrideCreatedOnCreatedBy privilege (Id=d48cf22f-f8c2-xxxx-89eb-49f8281dxxxx)
To resolve this issue, you need to enable the override createdon privilege on the security role that the user is currently assigned to
In Power automate, in the parse json, if the response doesn’t align with the schema of the parse json, you’ll notice different errors, one of them being “Invalid type. Expected Object but got Array”.
The Parse json schema was expecting an object as a response.
But, instead, received an array of objects, which results in the following error
This error happens when you’re passing a list of array oject records from the child workflow or list records from the previous steps.
So the parse json was expecting an object in the format of
{
“Var1″:”var1_value”,
“Var2″:”var2_value”
}
But instead receiving an array of objects:
[
{
“Var1″:”var1_value”,
“Var2″:”var2_value”
}
]
The output value outputs(‘Get_List_record_of_Person_Engagement_Event’)?[‘body/value’] returns an array/list of objects
To solve this problem, you need to return just the first object value from the result. Use the following expression (in the above Response body value) to return the first value from the array