Automating Changing Solution Owner in the Power Platform
david wyatt
Posted on May 29, 2023
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).
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']}'
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']}'
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']}'
The flow will look like this:
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']
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']}
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']
Status Activated (turn on flow)
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']}'
Row ID
outputs('List_rows_canvasapps')?['body/value'][0]?['canvasappid']
And we update the owner the same way as the flows, but we don't need to turn on anything.
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.
New PowerApp Owner
outputs('List_rows_Find_User')?['body/value'][0]?['azureactivedirectoryobjectid']
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.
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
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')
And that's it, your flow should look something like this
Posted on May 29, 2023
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.