DEV Community

Cover image for Automating Changing Solution Owner in the Power Platform
david wyatt
david wyatt Subscriber

Posted on • Edited on

Automating Changing Solution Owner in the Power Platform

I wrote here Change-Solution-Owner how it is possible to manually change the owner of a solution. But the Power Platform is all about efficiency and automation, so let's see how to automate it.
This also links into Create-Your-Own-Pipeline, as we need to be able to automatically change the solution we imported back to its original owner.


Before we start we need to understand how the Dataverse tables we need and their relationships. We need to use the following tables

  • Solutions
  • Solution Components
  • Users
  • Process
  • Canvas Apps
  • Connections
  • Environment Variable Definition

But this can be extended to other parts of solutions, like the Connectors table for Custom Connectors.
There is also the Environment Variable Value table, which is part of the solution but I have never needed to change the owner to the environment variable value.

The User table is because the owner(s) is a relationship to this table, so we need to lookup the userid guid based on the new owners email address.

The Solution table is the obvious start point, we need this to find the solutionid (the table guid), as we just have the solution name. If you look in the flow you see solutionid field, but that is a red herring. This is a system solution (as a flow can be in multiple solutions). So instead there is the Solution Component table. Every time you add something to a solution you add a row to that table. So we go Solution to Solution Components (1 to many) to the Components(many to 1).

database relationships


Get User

As I said we need the user table guid when we set the new owner. We List rows with the email address as the filter, there are a few email address but I've had success with internalemailaddress field.

Filter



internalemailaddress eq '@{triggerBody()['text_1']}'


Enter fullscreen mode Exit fullscreen mode

list users

Solution to Solution Components

So the flow trigger includes the solution name (not display name). We use the Dataverse List rows but filter to the solution name, using the uniquename field:

Filter



uniquename eq '@{triggerBody()['text']}'


Enter fullscreen mode Exit fullscreen mode

Now we need to return all the components in the solution from the Solution Components table. To avoid an annoying ForAll (because List Rows returns an array) I use the array position 0.
The filter query will be

Filter



_solutionid_value eq '@{outputs('List_rows_to_find_solution')?['body/value'][0]?['solutionid']}'


Enter fullscreen mode Exit fullscreen mode

The flow will look like this:

solution list rows

Looping the Components

The Solution Components includes everything in the solution, so we need a way to identify which component it is and which table it is in. This can be done by the componenttype field. I use a switch to select the table based on the component type. To add a little complexity this is an integer, but we can see the display name with this field:



 items('Apply_to_each')?['componenttype@OData.Community.Display.V1.FormattedValue']


Enter fullscreen mode Exit fullscreen mode
Component Type Dataverse Table
29 Process (flows)
300 Canvs Apps
380 Environment Variable Def
381 Environment Variable Value
10039 Connection References
372 Connector

Flows (Process Table)

The Process table is used to update our flows with the update item action, the row id is the worflowid. The Component field that holds this value is the objected, so we get:

Row ID



items('Apply_to_each')?['objected']}


Enter fullscreen mode Exit fullscreen mode

We then have to update the owner, and as its a flow turn on the flow.

Owner (List_rows_Find_User is output from user table)



outputs('List_rows_Find_User')?['body/value'][0]?['@odata.id']


Enter fullscreen mode Exit fullscreen mode

Status Activated (turn on flow)

update item

Canvas Apps

The Canvas Apps table its a little different, as the objected from the Solution Component table isn't the row id. So we need to do a List rows with a filter trick.

The objected is the uiquecanvaseappid, but the row id is the canvasappid.

Filter



uniquecanvasappid eq '@{items('Apply_to_each')?['objected']}'


Enter fullscreen mode Exit fullscreen mode

Row ID



outputs('List_rows_canvasapps')?['body/value'][0]?['canvasappid']


Enter fullscreen mode Exit fullscreen mode

And we update the owner the same way as the flows, but we don't need to turn on anything.

list apps

Some interesting has started happening, now even though the app owner is changing, it is not shared with the new owner. To fix it you can use the 'Set App Owner' action. The new owner id is in the user dataverse table, it's the azureactivedirectoryobjectid field.

set app ower

New PowerApp Owner



outputs('List_rows_Find_User')?['body/value'][0]?['azureactivedirectoryobjectid']


Enter fullscreen mode Exit fullscreen mode

Environment Variable (Definitions)

The Environment Variable Definitions table is pretty much identical to the flows process, without the need to turn on (so just change owner). The Row ID is environmentvariabledefinitionid
which is the objected again.

update item

Connection References

This time identical to Environment Variable Definitions, we just update the owner and the Row ID is connectionreferenceid which is the objectid.
Incase you missed it, you can see the row id / guide is normally just the table name and id

update item

Exceptions

This is an optional extra but one I recommend. For each Update Item I add an exception catcher, the reason is it's quite possible most will succeed but the odd one will fail. In my experience the only fails are for the flows (but seems good practice to do catch it for them all). A couple of examples I had were:

  • Connections used instead of connection references
  • Connection Reference empty
  • Custom Connector missing from environment

To grab the exception we use the standard actions expression, and append it to a string variable.



actions('Update_a_process')


Enter fullscreen mode Exit fullscreen mode

exception handling


And that's it, your flow should look something like this

full flow

Top comments (0)

Some comments have been hidden by the post's author - find out more