Today's blog post was written by Hayden Thomas, Associate Developer at Sonoma Partners.
Integrating Power BI online with Dynamics CRM On-Premise is not currently supported natively. Recently we had a need to integrate a Power BI Report with a Dynamics CRM On-Premise environment, so we needed to create a custom solution to enable embedding reports and dashboards from Power BI into CRM.
Power BI natively allows reports to be ‘Published to Web.’ Doing this would allow us to simply IFrame the report on a Dynamics form or dashboard, but this makes it accessible to anyone who may have the link which is not very secure. This is unsuitable as the reports we’re looking to embed may have sensitive data which we want to make absolutely sure no one has outside access to.
In our case, we are connecting to Power BI through Azure. Azure uses OAuth 2.0 and Active Directory services for authentication. We need to be able to store:
- A Client ID that represents a connection to Power BI through Azure.
- A Tenant ID for our Azure Active Directory.
- An access token that will allow us to request a report from Power BI.
- A refresh token that will allow us to programmatically keep our authentication alive, so that we don’t need to continuously keep putting in our username and password.
- The lifespan of the authentication and the date-time we obtained it, so we can check if our current authentication is still good.
The Client and Tenant ID are the same for all of the users in our org, so we simply created a configuration record to hold these values.
The other fields will either be created for every user and we need to ask the user to enter their authentication to populate these fields, or we need to create a service account that can be authenticated in the background without the user needing any information on how the reports are being displayed.
Our solution used the second option. Our reports are shared amongst a group in Power BI. In order to have everyone log in and have their own tokens, it would require them to all be added to that group (and in turn, require everyone to have a Power BI Pro license). This also allowed us to just add the authentication fields to the configuration record, along with the service account’s username and password.
Our next step is to make sure we actually have an app registered to our Azure AD that we can authenticate this user against. If we log in to dev.powerbi.com/apps, we can register one directly to ensure that it’s set up correctly. In order to make sure we don’t need to handle anything with redirect URLs, since we expect to move this around to different orgs without much issue, we use Native app from the App Type drop down, and for our redirect URL we use this link. For our case, where we only want to be able to read dashboards/reports, we only give it the read all dashboards and read all reports access levels. Once done, we can click Register App to obtain the Client ID we will use for our configuration record.
We have our app created, but haven’t yet given permission from our service account to the app to be able to log the user in programmatically. In order to give permission, we wrote a LINQPad script that does nothing but connect to the Client ID of our app, and allow the user to log in to give access.
Running the script will pop up a dialog to allow a user to log into the App created with the specified client ID.
It then triggers a custom action that takes in both of those parameters. The custom action triggers some plugin code that loads the configuration record, ensures the authentication is up to date, and then queries Power BI for the embed URL for that report.
For ensuring our authentication is up to date, we see if we have an access token or if our token has expired (based on the authentication lifespan and authentication obtained date time fields we have on our configuration). If we don’t have a refresh token, we need to use the password grant_type along with the service account username and password. (If we can refresh, we do something similar using grant_type refresh and the refresh token that we have stored in our configuration record. More details on Azure OAuth operations can be found here: https://docs.microsoft.com/en-us/azure/active-directory/develop/active-directory-v2-protocols-oauth-code). In this example, we deserialize into an AccessToken model class that’s described by the documentation above.
With our access token, we can query Power BI for the reports which are shared with the Group ID we sent added as a parameter by doing an HTTP GET request against https://api.powerbi.com/v1.0/myorg/groups/GROUP_ID/reports with an Authorization: Bearer ACCESS_TOKEN header.
This will give us a response that’s a JSON string which will be an array of all of the reports for the group. Each entry in the array will contain the report ID and the embed URL. There are additional fields, such as the display name for the report, but they’re unimportant for what we’re doing. We simply need to find the entry that has the report ID that we passed in, and return the embed URL and access token back to the web resource.
Once we have those fields in the client side, we can simply set the source of our Iframe to the embed URL we received, and post the access token to it.
Now we can see our Power BI report as an iFrame. In this case we embedded as a Dashboard in Dynamics CRM.