0

I am wanting to use an MSAL access token created for a Power BI access scope in order to query a Power BI dataset. The MSAL access token appears to be generated successfully. However, when I use the following connection string I fail to create a connection using OleDB. Any pointers would be greatly appreciated. It is worth noting that I can use interactive security in my successfully, but not via the App Reg.

    // Function to resolve the service:
    // ResolveCubeType => function() 
    public static string ResolveCubeType (
        string cubeServerName
    ){
        
        // Resolve the cube type: 
        string cubeType = cubeServerName.Contains("powerbi") ? "powerbi":"aas"; 

        // Explicitly define the returned object: 
        // string => env
        return cubeType;

    }

    // Function to resolve the resource uri: 
    // ResolveResourceUri => fucntion()
    public static string ResolveResourceUri(
        string cubeType, 
        string aasRegion
    ){

        // Derive the resource uri using the cube Type: 
        string resourceUri = (
            cubeType == "powerbi" ? "https://analysis.windows.net/powerbi/api":$"https://{aasRegion}.asazure.windows.net"
        );

        // Explicitly define the returned object: string scalar => env
        return resourceUri;

    }

    // Function to resolve the authority uri: 
    // ResovleAuthorityUri => function() 
    public static string ResolveAuthorityUri(
        string tenantId
    ){
        // Derive the uri to log into microsoft: authorityUri => string scalar
        string authorityUri = $"https://login.microsoftonline.com/{tenantId}";
        // Explicitly define the returned object: string scalar => env 
        return authorityUri; 
    }

    // Function to resolve authorisation uri: 
    // ResolveAuthorityUri => function() 
    public static string ResolveAccessScope(
        string resourceUri
    ){
        // Derive the authority uri: authorityUri => string scalar
        string authorityUri = $"{resourceUri}/.default";

        // Explicitly define the returned object: string scalar => env
        return authorityUri;
    }
    
    // Function to generate access token: 
    // GenerateAccessToken => function()
    public static async Task<string> GenerateAccessToken(
        string accessScope,
        string clientId, 
        string clientSecret,
        string authorityUri,
        string redirectUri
        ){
            // Create a list of access authority uris
            // to add to the access scopes: 
            // scopeList => list of string scalars
            List<string> scopeList = new List<string>();
            // Add the access scope to the list:
            // scopeList => List of string scalars
            scopeList.Add(accessScope);
            // Instantiate am authentication request client object:
            // confidentialClient => request client object
            var confidentialClient = ConfidentialClientApplicationBuilder
                    .Create(clientId)
                    .WithClientSecret(clientSecret)
                    .WithAuthority(new Uri(authorityUri))
                    .WithRedirectUri(redirectUri)
                    .Build();
            // Instantiate an accessTokenRequest object: 
            var accessTokenRequest = confidentialClient.AcquireTokenForClient(scopeList);
            // Authenticate and store the result: 
            // authResult
            var authResult = await accessTokenRequest.ExecuteAsync();
            // Return the access token as string: string scalar => env
            return authResult.AccessToken.ToString();

    }

        // Instantiate a Data Lake Service Client object:
        // dataLakeServiceClient => DataLakeServiceClient object
        // Derive the path to the Data Lake storage account
        string adlsUri = $"https://{adlsStorageAccountName}.dfs.core.windows.net";


        // Resolve the cube type from the cube sever string:
        // cubeType => string scalar 
        string cubeType = ResolveCubeType(
            cubeServer
        );

        // Resolve the cubeResourceUri: 
        // cubeResourceUri => string scalar 
        string cubeResourceUri = ResolveResourceUri(
            cubeType, 
            "australiaeast"
        ); 
        
        // Resovle the access scope: 
        // accessScope => string scalar
        string accessScope = ResolveAccessScope(
            cubeResourceUri
        );

        // Resolve the authority uri: 
        // authorityUri => string scalar
        string authorityUri = ResolveAuthorityUri(
            tenantId.Value.ToString()
        );

        // Store the value of the redirectUri: 
        // redirectUri => string scalar
        string redirectUri = "urn:ietf:wg:oauth:2.0:oob"; 

       // Get and access token via MSAL: accessToken => string scalar
       string accessToken = await GenerateAccessToken(
            accessScope,
            clientId.Value.ToString(), 
            clientSecret.Value.ToString(),
            authorityUri,
            redirectUri
        );


         string connectionString = $"Provider=MSOLAP.8;" +
            $"Data Source={cubeServer};" +
            "Update Isolation Level=2;" +
            $"Initial Catalog={cubeName};" +
            $"User ID=;" +
            $"Password={accessToken};" +
            $"Persist Security Info=True;" +
            $"Impersonation Level=Impersonate";

  //Create OLEDB connection: connection => OleDbConnection variable
            using (var connection = new OleDbConnection(connectionString))
            //using (var connection = new AdomdConnection(connectionString))
            {
                // Create a flag to be used for flow control: exceptionCaught => boolean scalar (default false)
                bool exceptionCaught = false;
                // Open the connection: connection => OleDbConnection variable
                try
                {
                    connection.Open();
                }
                // If an exception occurs: 
                catch(Exception e)
                {
                    // Write out the error message to the console: string scalar => stdout(console)
                    Console.WriteLine("{0} Error:", e);
                    // Invert the flag value: exceptionCaught => string scalar
                    exceptionCaught = true;
                    // leave the function:
                    return "";
                }

And the MSOLAP driver version:

enter image description here

Edit can't connect to workspace with powershell:

enter image description here

hello_friend
  • 5,682
  • 1
  • 11
  • 15
  • 1
    What scope/resource are you generating an access token for? – David Browne - Microsoft Jul 14 '22 at 16:51
  • 1
    And what version of the OleDb driver do you have installed? https://learn.microsoft.com/en-us/analysis-services/client-libraries?view=azure-analysis-services-current&preserve-view=true#oleddb-msolap – David Browne - Microsoft Jul 14 '22 at 17:47
  • @DavidBrowne-Microsoft thanks for your quick reply, I have updated the posted question ! – hello_friend Jul 14 '22 at 23:26
  • @hello_friend Could you please confirm that you did not try **service account**, most commonly you have to retain integrated mode, but as a way out I've used special non-interactive accounts - explicitly setting that for these one there is no need in MFA etc. Then you can log on with them programmatically and the rest work as usual? – Maksym Jul 25 '22 at 07:44
  • @Maksym i did try using the client, tenant and secret values for the SP in the connection string, but it fails with error: System.Data.OleDb.OleDbException (0x80004005): COM error: COM error: Microsoft.AnalysisServices.AzureClient, Failed to resolve PBI workspace. Im not sure what you mean by special non-interactive accounts could you please elaborate ? – hello_friend Jul 25 '22 at 07:56
  • @hello_friend SA allow to skip MFA, but seems you already ok with that. Did you check this link https://stackoverflow.com/questions/21603897/dts-e-oledberror-error-code-0x80004005-difference-between-sql-native-client-an ? This does not seems to be related to Power BI or Azure itself. I would also check DNS resolution and make sure that host resolved correctly in both cases. – Maksym Jul 25 '22 at 08:09
  • @Maksym the Provider is defined as MSOLAP.8 in the connection string ^. It is definitely related to the connection string / and whether or not the SP / MI has access to the cube, because as previously stated I can connect and exectue the query using Integrated Authentication. – hello_friend Jul 25 '22 at 12:21
  • @hello_friend Here https://stackoverflow.com/questions/70168326/error-reopening-connection-to-power-bi-xmla-endpoint-with-python and on MSDN https://learn.microsoft.com/en-us/power-bi/enterprise/service-premium-service-principal "MSOLAP" name used, not "MSOLAP.8" for Provider Name. This can be one thing, another one did you add your SP as Admin or Member as suggested in msdn? – Maksym Jul 25 '22 at 12:39
  • @hello_friend a few more details $"Impersonation Level=Impersonate" - is a default level and can be removed, $"Persist Security Info=True;" - can be added later is not helping you now. "User ID=;" - can be removed. And the most important access token must be regenerated after permissions update. – Maksym Jul 25 '22 at 12:47
  • Thanks for the update Maksym, do you have an example of a working program that connects via OleDB or Adomd using MI or SP to query a cube, either AAS or PBI ? The connection string is, where I am guessing, my trouble is lying. Either that or its cube access permission related. – hello_friend Jul 27 '22 at 04:03

1 Answers1

1

Can you connect with this Powershell code?

$clientId = 'a469xxxxx19a'
$clientSecret = 'BgK8QxxxxxxhXA0aju'
$tenantId = 'b4948xxxxx42ec21e1'
$datasetName = 'Advexxxxxxxort'
$server = "powerbi://api.powerbi.com/v1.0/myorg/Rxxxxxxing"


$password = ConvertTo-SecureString $clientSecret -AsPlainText -Force
$cred = New-Object PSCredential($clientId, $password)

Connect-PowerBIServiceAccount -ServicePrincipal -Tenant $tenantId -Credential $cred

$t = Get-PowerBIAccessToken
$accessToken = $t["Authorization"].Split(' ')[1]

$con = new-object System.Data.OleDb.OleDbConnection
$con.ConnectionString = "Provider=MSOLAP;Data Source=$server;Cube=$datasetName;User ID=;Password=$accessToken"
$con.Open()

If that doesn't work, verify that you can connect using an access token generated for your interactive login, instead of the Service Principal, eg

$password = ConvertTo-SecureString $clientSecret -AsPlainText -Force
$cred = New-Object PSCredential($clientId, $password)

#Connect-PowerBIServiceAccount -ServicePrincipal -Tenant $tenantId -Credential $cred
Connect-PowerBIServiceAccount 

$t = Get-PowerBIAccessToken
$accessToken = $t["Authorization"].Split(' ')[1]

$con = new-object System.Data.OleDb.OleDbConnection
$con.ConnectionString = "Provider=MSOLAP;Data Source=$server;Cube=$datasetName;User ID=;Password=$accessToken"
$con.Open()
David Browne - Microsoft
  • 80,331
  • 6
  • 39
  • 67
  • Hi David, thanks heaps for your speedy replies. No I can't connect via the powershell code. Please see the error message from the terminal above – hello_friend Jul 15 '22 at 01:58
  • Interactive login works as is. However, this method of auth is out of the question for the productionised durable function application as it has to run in Azure as part of an ETL pipeline orchestrated in ADF. – hello_friend Jul 15 '22 at 03:14
  • What do you reckon the underlying problem is, do you reckon there is another way I could solve it using the App Reg and non-interactive auth ? thanks again for all your help so far ! – hello_friend Jul 15 '22 at 03:17
  • That's purely for troubleshooting. So the code is fine. Did your Power BI Admin enable Service Principals for API Access? https://learn.microsoft.com/en-us/power-bi/enterprise/service-premium-service-principal#enable-service-principals And does your Service Principal have rights in your Power BI Workspace? – David Browne - Microsoft Jul 15 '22 at 03:18
  • I will most certainly check whether or not our Service Principal has rights for API access, I will do this right now and get back to you. With respect to have rights in the Power BI workspace, yes the service principal has cube editor rights to the workspace. – hello_friend Jul 15 '22 at 03:22
  • We have added the SP and I still get the can not find workspace error, weird ! – hello_friend Jul 15 '22 at 06:23