Search code examples
powershelldatabricksazure-databricks

Unable to create token in databricks using service principal


I have the below code to create token in the databricks workspace, script is using the SPN for authentication which is added as user to the workspace and member to the admin group.

function Get-DatabricksTokenWithRestAPI {
    [CmdletBinding()]
    param (
        [Parameter(Mandatory=$true)]
        [string]$DatabricksInstanceUrl,
        [Parameter(Mandatory=$true)]
        [string]$ClientId,
        [Parameter(Mandatory=$true)]
        [string]$ClientSecret,
        [Parameter(Mandatory=$true)]
        [string]$TenantId
    )

    # Define the REST API endpoint for listing the user's tokens
    $listTokensEndpoint = "$DatabricksInstanceUrl/api/2.0/token/list"

    # Define the body of the REST API request
    $body = @{
        "client_id" = $ClientId
        "client_secret" = $ClientSecret
        "tenant_id" = $TenantId
    }

    # Send the list tokens REST API request and retrieve the response
    $listTokensResponse = Invoke-RestMethod -Method Post -Uri $listTokensEndpoint -Body ($body | ConvertTo-Json)

    # Check if a Databricks token already exists in the workspace
    foreach ($tokenInfo in $listTokensResponse.token_infos) {
        if ($tokenInfo.comment -eq "Databricks CLI token") {
            Write-Host "Using existing Databricks token"
            return $tokenInfo.token_value
        }
    }

    # Define the REST API endpoint for generating a token
    $generateTokenEndpoint = "$DatabricksInstanceUrl/api/2.0/token/create"

    # Send the generate token REST API request and retrieve the response
    $generateTokenResponse = Invoke-RestMethod -Method Post -Uri $generateTokenEndpoint -Body ($body | ConvertTo-Json)

    # Retrieve the token from the response
    $token = $generateTokenResponse.token_value
    return $token
}

I continue to get unauthorized access error, am I doing something wrong here?


Solution

  • I tried to reproduce the same in my environment and got the same error as below:

    enter image description here

    The error usually occurs if the required roles is not granted to the Service Principal or if permissions are missing to perform the action.

    Make sure to grant API permissions to the Azure AD Application like below:

    enter image description here

    Now, grant roles to the service principal like below:

    Go to Azure Portal -> Azure Databricks ->Select your Databricks -> Access control (IAM) -> Add a role assignment -> Select Owner -> Search your service principal -> Save

    enter image description here

    Note that : Make sure to assign owner or contributor role to the Azure AD Application. Refer this.

    If still the issue persists, try the below PowerShell script:

    Function  Connect-Databricks  {  
    [cmdletbinding(DefaultParameterSetName  =  'Bearer')]  
    param  (  
    [parameter(Mandatory  =  $false,  ParameterSetName  =  'AzContext')]  
    [switch]$UseAzContext,    
    [parameter(Mandatory  =  $true,  ParameterSetName  =  'Bearer')]  
    [string]$BearerToken,    
    [parameter(Mandatory  =  $true,  ParameterSetName  =  'Bearer')]  
    [parameter(Mandatory  =  $true,  ParameterSetName  =  'AzContext')]  
    [parameter(Mandatory  =  $true,  ParameterSetName  =  'AADwithOrgId')]  
    [parameter(Mandatory  =  $true,  ParameterSetName  =  'AADwithResource')]  
    [string]$Region,  
    [parameter(Mandatory  =  $false,  ParameterSetName  =  'Bearer')]  
    [parameter(Mandatory  =  $false,  ParameterSetName  =  'AADwithOrgId')]  
    [parameter(Mandatory  =  $false,  ParameterSetName  =  'AADwithResource')]  
    [string]$DatabricksURISuffix  =  "azuredatabricks.net"  ,  
    [parameter(Mandatory  =  $false,  ParameterSetName  =  'AADwithOrgId')]  
    [parameter(Mandatory  =  $false,  ParameterSetName  =  'AADwithResource')]  
    [string]$oauthLogin  =  "login.microsoftonline.com"  ,  
    [parameter(Mandatory  =  $true,  ParameterSetName  =  'AADwithOrgId')]  
    [parameter(Mandatory  =  $true,  ParameterSetName  =  'AADwithResource')]  
    [string]$ApplicationId,  
    [parameter(Mandatory  =  $true,  ParameterSetName  =  'AADwithOrgId')]  
    [parameter(Mandatory  =  $true,  ParameterSetName  =  'AADwithResource')]  
    [string]$Secret,  
    [parameter(Mandatory  =  $true,  ParameterSetName  =  'AADwithOrgId')]  
    [parameter(Mandatory  =  $true,  ParameterSetName  =  'AzContext')]  
    [string]$DatabricksOrgId,   
    [parameter(Mandatory  =  $true,  ParameterSetName  =  'AADwithOrgId')]  
    [parameter(Mandatory  =  $true,  ParameterSetName  =  'AADwithResource')]  
    [string]$TenantId,  
    [parameter(Mandatory  =  $true,  ParameterSetName  =  'AADwithResource')]  
    [string]$SubscriptionId,  
    [parameter(Mandatory  =  $true,  ParameterSetName  =  'AADwithResource')]  
    [string]$ResourceGroupName,  
    [parameter(Mandatory  =  $true,  ParameterSetName  =  'AADwithResource')]  
    [string]$WorkspaceName,   
    [parameter(Mandatory  =  $false,  ParameterSetName  =  'AADwithOrgId')]  
    [parameter(Mandatory  =  $false,  ParameterSetName  =  'AADwithResource')]  
    [switch]$Force,  
    [switch]$TestConnectDatabricks  
    )  
      
    Write-Verbose  "Globals at start of Connect:"  
    Write-Globals  
      
    if  ($Force)  {  
    Write-Verbose  "-Force set - clearing global variables"  
    Set-GlobalsNull  
    }  
      
    [Net.ServicePointManager]::SecurityProtocol  =  [Net.SecurityProtocolType]::Tls12  
    $AzureRegion  =  $Region.Replace(" ",  "")  
    $AzureDatabricksURISuffix  =  $DatabricksURISuffix.Trim(".",  " ").Replace(" ",  "")  
    $AzureOauthLogin  =  $oauthLogin.Trim("/",  " ").Replace(" ",  "")  
    $URI  =  "https://$AzureOauthLogin/$tenantId/oauth2/token/"  
    if  ($PSCmdlet.ParameterSetName  -eq  "Bearer")  {  
    Set-GlobalsNull  
    
    $global:DatabricksAccessToken  =  "Bearer $BearerToken"  
     
    $global:DatabricksTokenExpires  =  (Get-Date).AddDays(90)  
    $global:Headers  =  @{"Authorization"  =  "$global:DatabricksAccessToken"  }  
    }  
    elseif  ($PSCmdlet.ParameterSetName  -eq  "AzContext")  {  
    $ADResponseToken  =  Get-AzAccessToken  -ResourceUrl  "2ff814a6-3304-4ab8-85cb-cd0e6f879c1d"  
    $global:DatabricksAccessToken  =  $ADResponseToken.Token  
    $global:DatabricksTokenExpires  =  ($ADResponseToken.ExpiresOn).dateTime  
    $global:Headers  =  @{"Authorization"  =  "Bearer $DatabricksAccessToken";  
    "X-Databricks-Org-Id"  =  "$DatabricksOrgId"  
    }  
    $global:DatabricksOrgId  =  $DatabricksOrgId  
    }  
    elseif  ($PSCmdlet.ParameterSetName  -eq  "AADwithOrgId")  {  
    Get-AADDatabricksToken  
    $global:Headers  =  @{"Authorization"  =  "Bearer $DatabricksAccessToken";  
    "X-Databricks-Org-Id"  =  "$DatabricksOrgId"  
    }  
    $global:DatabricksOrgId  =  $DatabricksOrgId  
    }  
    elseif  ($PSCmdlet.ParameterSetName  -eq  "AADwithResource")  {  
    Get-AADManagementToken  
    Get-AADDatabricksToken  
    $global:Headers  =  @{"Authorization"  =  "Bearer $global:DatabricksAccessToken";  
    "X-Databricks-Azure-SP-Management-Token"  =  $global:ManagementAccessToken;  
    "X-Databricks-Azure-Workspace-Resource-Id"  =  "/subscriptions/$SubscriptionId/resourceGroups/$ResourceGroupName/providers/Microsoft.Databricks/workspaces/$WorkspaceName"  
    }  
    }  
    $global:DatabricksURI  =  "https://$AzureRegion.$AzureDatabricksURISuffix"  
    Write-Verbose  "Globals at end of Connect:"  
    Write-Globals  
    if  ($PSBoundParameters.ContainsKey('TestConnectDatabricks'))  {  
    Write-Verbose  "Connecting to Workspace to verify connection details are correct:"  
    if  ($PSCmdlet.ParameterSetName  -eq  "Bearer")  {  
    Test-ConnectDatabricks  -Region  $AzureRegion  -BearerToken  $BearerToken  
    }  
    else  {  
    Test-ConnectDatabricks  
    }  
    }  
    }
    

    enter image description here

    References:

    PowerShell Gallery | Public/Connect-Databricks.ps1

    Oauth 2.0 - access Azure Databricks API through Active Directory by Carl Zhao