For a while now there has been a gap in the Power Platform tooling related to plugin packages. Plugin Packages provide a lot of capabilities when building plugins that for a long time required unsupported workarounds in Dataverse. However, getting plugin packages to participate in ALM has been bothersome when using source control as the source of truth in the Power Platform.
What’s the Problem?
If you follow the recommended practices using the Power Platform CLI for exporting and unpacking solutions in CI/CD pipelines, you can use the solution add-reference command to add code-first components to the solution project (.cdsproj) created by solution clone, including references to PCF projects (.pcfproj) and plugin library projects (.csproj). Building the solution project results in the code-first projects being built and their outputs added to the solution.
Unfortunately, if you do the same thing for a plugin package project (also a .csproj) when you attempt to build the solution you will get the following error.
error: Unable to find assembly registration configuration for ...\MF.Plugins.dll in the destination: obj\Debug\Metadata\PluginAssemblies
The issue here is that the Power apps build tasks are trying to find metadata in the pluginpackage.xml for the .dll and not the .nuget that we want when the plugin project is built. This is the current bug in the Power Platform CLI that prevents building plugin packages the same way you would a plugin library. Unfortunately, there has been no real recognition or communication around a fix for this from Microsoft despite it being reported to several different teams as a problem.
So, what’s the solution?
Well, to be honest, adding a solution reference to the cdsproj, and building it, doesn’t really do that much. Essentially, all it does is
- Tell msbuild or dotnet build to build the plugin project before building the solution project.
- Copy the output plugin library to the correct location for it to be packed with the solution.
In a build automation process this is pretty simple stuff, and we could do it using shell commands along with msbuild. However, if we want to ensure that it works anywhere, we are building the solution project, it’s best to update the build tasks directly in the solution project following the steps below.
Remove the plugin project reference from the solution project
If you are here, and you’ve already run the solution add-reference command to add your plugin project to the solution project, it’s easy enough to remove the reference. You’ll see it in the Solution Project (.cdsproj) as a ProjectReference node.
Remove this if it exists in your solution project.
<ItemGroup>
<ProjectReference Include="plugins\MF.Plugins.csproj" />
</ItemGroup>
Add a target to build the plugin package
Create a new target in the Solution project (.cdsproj) to build the plugins. Insert the following anywhere inside of the <Project></Project> nodes and replace the properties in the property group with the paths and file names in your project.
<Target Name="BuildPlugins">
<PropertyGroup>
<LinkedProject>MF.Plugins\MF.Plugins.csproj</LinkedProject>
<SolutionPluginPackage>src\pluginpackages\mf_MF.Plugins\package\mf_MF.Plugins.nupkg</SolutionPluginPackage>
<BuildPluginPackage>MF.Plugins\bin\$(Configuration)\MF.Plugins.1.0.0.nupkg</BuildPluginPackage>
</PropertyGroup>
<!-- Build the linked project -->
<MSBuild Projects="$(LinkedProject)" Targets="Clean;Build;Pack">
</MSBuild>
<!-- Copy the built package to the solution folder -->
<Copy SourceFiles="$(BuildPluginPackage)" DestinationFiles="$(SolutionPluginPackage)" OverwriteReadOnlyFiles="true" />
</Target>
Add the target to the DefaultTargets for your project
The project node in the solution project will contain an attribute called DefaultTargets. Update that attribute to include your BuildPlugins target.
<Project ToolsVersion="15.0" DefaultTargets="BuildPlugins;Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
Exclude the package binary from source control
Since we are not adding the project reference to the solution (.cdsproj), the package gets exported during the clone/sync process and saved to disk with other solution files. However, if you include the project as a reference, the clone/sync process knows to not do this, so the binaries aren’t potentially checked into source control. Ideally, we want to keep compiled files out of source control. So, a small change to the .gitignore in our solution directory will take care of this.
# Exclude plugin package binaries
/src/**/*.nupkg
FINAL THOUGHTS
I started using a map file for copying the built NuGet package to the solution directory. While it seems possible with extra steps (i.e., the struck-out steps below), I couldn’t get the NuGet package to copy using this method. Perhaps someone else can make it work; if you do, let me know. However, it shouldn’t be necessary to take these extra steps, since we can handle copying the NuGet package in the build.
Finally, note that if you’re using MSBuild to build the solution project, make sure not to use the -t argument as suggested in some of the documentation. We want both default targets to run, so the -t argument isn’t necessary. If you do use the -t argument, you’ll need to specify both projects, like this: -t:BuildPlugins;Build.
You can view the sample project for this post in this GitHub Repo.
Add a map file to the solution project
A map file will tell the solution build (.cdsproj) where your plugin package is after it’s built and where to copy it in the unpacked solution hierarchy.
In the same directory as your solution project (.cdsproj) create a new file called map.xml and add xml to the file like the following.
<Mapping>
<FileToFile map="pluginpackages/Plugins/package/MF.Plugins.nupkg" to="../plugins/bin/Debug/Plugins.1.0.0.nupkg" />
</Mapping>
NOTE: The name of the nupkg file specified in the map attribute must be the same name as is specified in the package node of the pluginpackage.xml like in the following example:
<pluginpackage uniquename="MF.Plugins">
<exportkeyversion>1</exportkeyversion>
<iscustomizable>1</iscustomizable>
<name>MF.Plugins</name>
<package mimetype="application/octet-stream">MF.Plugins.nupkg</package>
<statecode>0</statecode>
<statuscode>1</statuscode>
<version>1.0.0</version>
</pluginpackage>
Add a reference to the map file in the solution project
In the PropertyGroup node that contains the SolutionPackageType add a new node as below
<PropertyGroup>
<SolutionPackageType>Both</SolutionPackageType>
...
<SolutionPackageMapFilePath>map.xml</SolutionPackageMapFilePath>
</PropertyGroup>







Leave a Reply