Power BI : se connecter aux API en mode REST avec un Service Principal Azure AD

Dans cet article, nous allons voir comment se connecter aux API Power BI en mode REST en utilisant un service princial (SP). Pour rappel, nous avons déjà utilisé la méthode du SP avec Azure Data Factory dans un de mes précédents articles =>  ICI. L’idée maintenant c’est d’utiliser cette méthode avec n’importe quelles technologies et langages avec du REST.

L’utilisation des SP est très pratique, car ainsi nous n’avons plus besoin d’avoir un compte utilisateur AD à gérer (comme avec les modes UserOwnsData ou AppOwnsData), et en plus cela fait aussi économiser une licence Power BI Pro, il n’y a pas de petites économies Smile

YoungCandidCrossbill-size_restricted

Commencez par créer un SP dans Azure AD et configurer Power BI pour activer l’authentification via service principal, la doc MS explique tout ça : https://docs.microsoft.com/fr-fr/power-bi/developer/embedded/embed-service-principal

Puis récupérez l’application ID et le tenant ID, vous pouvez notamment passer par le portail Azure dans les paramètres de votre SP (le menu s’appelle App registrations) :

MbcpAm3hXR

Et créez aussi un secret pour votre SP, il faut le sauvegarder quelque part car il est accessible que lors de sa création :

NZumIlukgX

Comme le SP va se connecter directement à Power BI, n’oubliez pas de lui donner des droits sur les Workspaces auxquels il aura accès. Attention, cela ne fonctionne qu’avec les Workspace V2 !

Pour se connecter aux API, il va d’abord falloir récupérer un token d’authentification en envoyant une requête POST au service d’’authentification Microsoft “login.microsoftonline.com” en passant les bonnes informations de connexion en body. Le body devra avoir le format suivant et contenir votre application ID et son secret :

{
	"Grant_type": "client_credentials",
	"Resource": "https://analysis.windows.net/powerbi/api",
	"Client_id": "<Application ID>",
	"Client_secret": "<Application Secret>",
	"Scope": "https://analysis.windows.net/powerbi/api/.default"
}

L’url à appeler devra aussi contenir le tenant id de votre AD : https://login.microsoftonline.com/<tenantid>/oauth2/token

En réponse de l’appel, vous pourrez récupérer un token :

{
    "token_type":  "Bearer",
    "expires_in":  "3599",
    "ext_expires_in":  "3599",
    "expires_on":  "1594665662",
    "not_before":  "1594661762",
    "resource":  "https://analysis.windows.net/powerbi/api",
    "access_token":  "<ICI>"
}

Vous pourrez alors l’utiliser dans tous vos appels aux API Power BI en le mettant dans le paramètre authorization du header :

{
    "Authorization":  "Bearer  <ICI>",
    "Content-Type":  "application/json"
}

Et c’est gagné !

Attention, le token expire au bout d’un certain temps, il faut prévoir de le regénérer si besoin.

Pour finir, voici un exemple complet en PowerShell qui liste au final les Workspaces auxquels le SP à accès :

#Specify SP informations
$tenantid = '<Tenant ID>'
$appID = '<Application ID>'
$appSecret = '<Application Secret>'


#Get token
$TokenArgs = @{
    Grant_type = 'client_credentials'
    Resource   = 'https://analysis.windows.net/powerbi/api'
    Client_id  = $appID
    Client_secret = $appSecret
    Scope = "https://analysis.windows.net/powerbi/api/.default"
}
$out = Invoke-RestMethod -Uri https://login.microsoftonline.com/$tenantid/oauth2/token -Body $TokenArgs -Method POST
 
#Save token
$tokenaccess = $out.access_token

#Get group API test
$header = @{
    'Content-Type'='application/json'
    'Authorization'= "Bearer  $tokenaccess" 
}
$uri = "https://api.powerbi.com/v1.0/myorg/groups"
$outrequest = Invoke-RestMethod -Method Get -Uri $uri -Headers $header

#Show results
$outrequest.value 

Il vous reste plus qu’à parcourir la documentation des API Power BI pour trouver la ou les bonnes méthodes à appeler en fonction de vos besoins : https://docs.microsoft.com/en-us/rest/api/power-bi/

FIN !

Databricks : on stream des données dans Power BI en PUSH

Aujourd’hui nous allons voir comment pousser des données en temps réel dans un Dashbord Power BI avec Databricks grâce à la technologie structure streaming de Spark. Le but sera bien sûr de réaliser de magnifiques Dashboard avec des données qui se mettent à jour en temps réel et c’est vraiment la classe à Dallas !

giphy

Il existe déjà des tuto sur différents sites qui parlent de streaming avec Databricks et Power BI. Généralement, ils vont soit utiliser un dataset en directquery connecté avec le driver Spark de Power BI, ce qui d’un point de vue perf n’est pas tiptop, soit ajouter d’autres composants comme Azure Stream Analytic en “passe plat” pour qu’il pousse les données dans Power BI. Avec la solution que je vous propose dans cet article, Databricks va directement pousser les données dans Power BI.

Voici comment faire…

Côté Power BI

On va d’abord créer un streaming dataset dans un workspace Power BI

chrome_nvLSgSxTVB

Choisir le type “API”

chrome_djJWg882Wk

Définir le nom et la structure de la table : un champ “timestamp” de type DateTime et un champ “value” de type Nombre. Activer aussi l’analyse de données d’historique.

chrome_i3g0lHiaI0

Puis très important, récupérer l’url push que l’on utilisera plus tard

chrome_Munuoscl4s

Créer un nouveau Dashboard

chrome_nrECpSWht3

Puis ajouter des vignettes de type données en temps réel

9f0F5YBY928MQR7PA11N

Sélectionner le dataset streaming que vous venez de créer

VcoqF2mqNg

Choisir par exemple le type carte avec le champ value qui permettra d’afficher la dernière valeur reçu dans le dataset streaming

OrjNimGBog

Ajouter d’autres vignettes de la même façon dans votre Dashboard

chrome_7wEsRRcGC3

Coté Databricks

Connectez vous à votre workspace Databricks puis créer un cluster en choisissant la version 2.11 de Scala

10qRY255nn

Ajouter la librairie “org.scalaj:scalaj-http_2.11:2.4.2” via Maven

e3Yi3N4VF5

Nous allons maintenant créer un nouveau notebook scala connecté à ce cluster, puis coller le code suivant en remplaçant <URL PUSH> par la vraie url du dataset streaming copiée lors de l’étape de création du dataset au début de l’article

import org.joda.time._
import org.joda.time.format._
import scalaj.http._

import org.apache.spark.sql.ForeachWriter
import org.apache.spark.sql._

var urlpbi = "<URL PUSH>"
var stream = spark.readStream.format("rate").load()

def json(timestamp: String, value:String): String = s"""{"timestamp" :"$timestamp","value" :"$value"}"""

val writer = new ForeachWriter[Row] {
  override def open(partitionId: Long, version: Long) = true
  override def process(row: Row) = {
    //println(row)
    val url = urlpbi
    val result = Http(url).postData(json(row(0).toString, row(1).toString))
    .header("Content-Type", "application/json")
    .header("Charset", "UTF-8")
    .option(HttpOptions.readTimeout(10000)).asString
    
  }
  override def close(errorOrNull: Throwable) = {}
}

val query =
  stream.writeStream
 .foreach(writer)
 .start()

Et si tout ce passe bien lorsque l’on exécute le notebook cela donnera ça :

dbxpbi2

Cet exemple utilise une source streaming de démo qui affiche un compteur qui s’incrémente toutes les secondes.

chrome_Onj61LCp5Z

Dans un cas réel, il faudra bien sûr se brancher à une source streaming comme Azure Event Hub ou Azure CosmosDB Changefeed avec de vraies données. Et il faudra aussi prévoir un stockage type Data Lake en parallèle car les Dataset Power BI ne sont pas vraiment persistants : rétention de 200 000 lignes max, puis suppression des lignes les plus anciennes en mode FIFO… Pour la partie code, on peut aussi aller plus loin et faire plus propre en utilisant les API Power BI avec une véritable authentification, au lieu des URL “guest” utilisée dans cet article… mais le principe reste le même Smile

Plus d’infos :

FIN

Azure Data Factory : Actualiser un modèle Azure Analysis Services et Power BI sans code grâce au MSI !!

Aujourd’hui, nous allons voir comment demander à un modèle Power BI déployé dans le service ou dans Azure Analysis Services (AAS) de se mettre à jour directement via Azure Data Factory (ADF).

Dans les deux cas (Power BI ou AAS) nous allons utiliser le MSI (Managed Service Identity) de ADF qui va surtout permettre à ADF de réaliser directement des actions via les API des services Power BI ou AAS sans passer par un outil tiers comme Azure Automation, Azure Function ou Azure Logic App. Le MSI est un mode d’identification d’Azure qui va donner à votre ressource ADF un identité propre dans l’Azure Active Directory. Nous l’avions d’ailleurs déjà utilisé dans mon article ici dans lequel la ressource ADF se connectait directement à Azure Key Vault pour récupérer la valeur d’un secret.

Plus d’info sur le MSI : https://docs.microsoft.com/fr-fr/azure/active-directory/managed-identities-azure-resources/overview

Pour récupérer les informations d’identité de la ressource ADF, vous pouvez notamment passer par le portail Azure dans les paramètres de la ressource ADF :

fmkf0Pg5IU

Ou dans l’AAD en recherchant le nom de votre ADF dans la rubrique entreprise application :

ABcpsgNMMG

Power BI

Bien sûr, il est possible de mettre en place une planification automatique dans Power BI pour qu’un modèle se mette-à-jour à une fréquence définie (tous les jours à 9h, 2 fois par jour le lundi, mardi…)… Le problème c’est que cela n’est pas synchronisé avec les traitements de chargement de données en amont. Et si par exemple ces traitements de chargement se terminent un jour à 9h10 au lieu de 8h, le Power BI se mettra à jour avec des données incohérentes !

charlo

On va donc utiliser cette méthode pour ordonnancer les traitements et être sûr que notre dataset Power BI sera mis à jour après les chargements de données sources.

Pour donner les droits à ADF de rafraichir le dataset Power BI, il va falloir activer la fonctionnalité “Autoriser les principaux de services à utiliser les API Power BI” dans le portail d’administration Power BI : cf. étape 3 –> https://docs.microsoft.com/fr-fr/power-bi/developer/embedded/embed-service-principal#step-3—enable-the-power-bi-service-admin-settings.

Je recommande d’activer cette fonctionnalité que pour un groupe AAD spécifique et de mettre les administrateurs techniques et les identités des ADF dedans, cela évitera aux utilisateurs lambda de faire des bêtises !

sbN4hYgJgt

Une fois que c’est fait, toutes les identités de l’AAD seront visibles dans Power BI et il faut alors ajouter l’ADF en administrateur du workspace contenant le dataset :

chrome_Suq7BHo8cq

Maintenant dans ADF vous allez pouvoir créer un activité Web avec pour paramètres :

  1. URL : l’url de votre dataset https://api.powerbi.com/v1.0/myorg/groups/<groupid>/datasets/<datasetid>/refreshes
    en remplaçant <groupid> par l‘id du workspace et <datasetid> par
    l’id du dataset. Pour les retrouver vous pouvez vous rendre sur les paramètres du dataset dans le portail Power BI et les récupérer dans l’url : https://app.powerbi.com/groups/<groupid>/settings/datasets/<datasetid>
  2. Method : POST
  3. Body : { « notifyOption »: « NoNotification » }
  4. Authentication : MSI
  5. Resource : https://analysis.windows.net/powerbi/api

58VwOTbTqx

Plus d’info sur l’API de refresh de dataset Power BI ici : https://docs.microsoft.com/en-us/rest/api/power-bi/datasets/refreshdatasetingroup

Azure Analysis Services

Pour AAS c’est un peu le même principe, il faut d’abord autoriser l’ADF à mettre à jour un modèle de l’instance AAS. Pour cela, nous allons nous connecter à l’instance AAS via SQL Server Management Studio avec un compte administrateur, faire un clic droit sur l’instance et cliquer sur “Propriétés”

5ClYLXiGJB

Dans l’onglet “Sécurité” cliquer sur “Ajouter…”, dans “Entrée manuelle” entrer app:<applicationID>@<tenantID> en remplaçant l’application id et le tenant id de l’ADF (voir plus haut pour savoir comment récupérer ces 2 informations) puis cliquer sur “Ajouter” et enfin “OK”

RfZWnsen1y

Reste ensuite à créer une activité Web dans un pipeline avec pour paramètres :

  1. URL : l’url de votre modèle AAS https://<region>.asazure.windows.net/servers/<servername>/models/<modelname>/refreshes en remplaçant <servername> par le nom du serveur AAS, <region> par la région de l’instance AAS (westeurope…) et <modelname> par le nom du modèle AAS à mettre à jour. Pour les retrouver vous pouvez vous rendre sur le portail Azure et tout sera visible sur la page de votre ressource AAS.
  2. Method : POST
  3. Body : {type : « full », maxParallelism : 10}
  4. Authentication : MSI
  5. Resource : https://<region>.asazure.windows.net en remplaçant <region> par la région de votre ressource AAS (westeurope…)

t42cee86Kh

Plus d’info sur l’api de refresh AAS ici : https://docs.microsoft.com/fr-fr/azure/analysis-services/analysis-services-async-refresh

Attention ces méthodes sont asynchrones : on donne l’ordre de lancer des rafraichissements et on attend pas la fin, donc les pipelines ADF s’arrêteront avant la fin des rafraichissements. Nous verrons éventuellement dans un prochain article comment surveiller les statuts des rafraichissements avec ADF … A suivre !

FIN !

Databricks : Il se connecte aux API en mode Azure AD sans PAT tout le monde hallucine (PRANK)

De base pour se connecter à un workspace Databricks et utiliser les API, il faut utiliser un token appelé aussi PAT que l’on va passer dans le header des appels REST (cf. mon article sur le CI/CD et Databricks dans lequel j’utilise cette méthode : ici )

Voir ici pour s’authentifier avec un PAT de façon “classique” : https://docs.databricks.com/dev-tools/api/latest/authentication.html 

Seulement les problèmes avec ce PAT :

  • c’est d’abord qu’il est attaché à un utilisateur et à un workspace donc pour de l’administration et de la sécurité c’est pas top. Si l’utilisateur quitte l’entreprise par exemple on ne peut pas supprimer ou retirer les droits sur son compte sans tout casser.
  • de plus il n’est possible de le créer qu’à la main à partir de l’UI du workspace Databricks, ou via les API si on a déjà un PAT. Or, il n’y a pas de PAT disponible directement à la suite de la création d’un workspace. DONC c’est un gros problème si l’on cherche à automatiser une chaine complète dans un workflow CI/CD ! Par exemple, lors d’un déploiement d’un nouveau projet en production, nous ne pourrons pas créer le workspace et déployer dessus des notebooks ou configurer des clusters directement après, vu que nous n’avons pas de PAT Sad smile

Heureusement, il existe aujourd’hui un moyen de se connecter autrement qu’avec le PAT : directement avec une authentification Azure AD et un compte de service Azure qu’on appelle un service principal (SP). 

Dans cet article on va réaliser toutes les différentes actions en Powershell afin de montrer que cela est scriptable et intégrable dans Azure DevOps par exemple. Mais vu que nous sommes sur des techno REST, il est bien sûr possible de faire ça avec n’importe quel langage.

J’ajoute que dans cet article je me concentre sur l’authentification via Azure AD aux API, je pars du principe que la ressource Databricks existe déjà. Mais nous verrons dans de prochains articles comment automatiser la création des ressources et d’architectures complètes via des templates ARM ou Terraform.

C’est parti, il va donc falloir :

Créer un SP dans Azure AD et enregistrer son application ID et son secret key :

# Connect to Azure
Connect-AzAccount

# Create SP if not exists
if ($null -eq (Get-AzADServicePrincipal -DisplayName $ServicePrincipleName)) {
    $mySP = New-AzADServicePrincipal -DisplayName $servicePrincipleName -ErrorAction 'Stop'
    $ClientId = $mySP.ApplicationId
    $ClientSecret = [pscredential]::new($servicePrincipleName, $mySP.Secret).GetNetworkCredential().Password
}

Mettre le service principal en owner (propriétaire) du workspace Databricks :

# Set RBAC on Databricks workspace

New-AzRoleAssignment -ApplicationId $ClientId `
    -RoleDefinitionName "Owner" `
    -ResourceGroupName $ResourceGroupName `
    -ResourceName  $WorkspaceName `
    -ResourceType "Microsoft.Databricks/workspaces"

Se connecter au service au workspace Databricks avec le service principal, pour cela il va falloir dans un premier temps faire 2 appels REST au à l’entité d’authentification Azure avec votre SP : https://login.microsoftonline.com/<TenantId>/oauth2/token et récupérer 2 tokens:

– un token pour le service correspondant à l’application AD du service Databricks enregistré nativement dans l’Azure AD de votre tenant. Généralement, cette application a l’id 2ff814a6-3304-4ab8-85cb-cd0e6f879c1d, mais il est aussi possible de retrouver cet id dans Azure AD en recherchant l’application AzureDatabricks :

VUw7BcTyBm

– un token pour le service de gestion Azure https://management.core.windows.net/ (qui sert d’ailleurs pour toutes les connexions classiques aux API Azure)

# Get AzureDatabricks app token
$RequestAccessTokenUri = "https://login.microsoftonline.com/$TenantId/oauth2/token"
$Resource = "https://management.core.windows.net/"
$DBXressource = "2ff814a6-3304-4ab8-85cb-cd0e6f879c1d" # CF. AzureDatabricks AzureAD application 

$body = "grant_type=client_credentials&client_id=$ClientId&client_secret=$ClientSecret&resource=$DBXressource"

$Token = Invoke-RestMethod -Method Post -Uri $RequestAccessTokenUri -Body $body -ContentType 'application/x-www-form-urlencoded'

Write-Host "Print Token" -ForegroundColor Green
Write-Output $Token.access_token
$apiKey = $Token.access_token

# Get Azure Management token

$bodyManagement = "grant_type=client_credentials&client_id=$ClientId&client_secret=$ClientSecret&resource=$Resource"

$Token = Invoke-RestMethod -Method Post -Uri $RequestAccessTokenUri -Body $bodyManagement -ContentType 'application/x-www-form-urlencoded'

Write-Host "Print Token" -ForegroundColor Green
Write-Output $Token.access_token
$apiKeyManagement = $Token.access_token

Enfin avec ces 2 tokens, vous allez pouvoir faire des appels aux API Databricks en passant ces tokens dans le header de vos appels :

# Call Azure Databricks API

$headers = @{
    "Authorization"="Bearer $apiKey";
    "X-Databricks-Azure-SP-Management-Token"=$apiKeyManagement;
    "X-Databricks-Azure-Workspace-Resource-Id"="/subscriptions/$SubscriptionId/resourceGroups/$ResourceGroupName/providers/Microsoft.Databricks/workspaces/$WorkspaceName"
}
$uri = "$uriroot/2.0/dbfs/list?path=/"
Invoke-RestMethod -Method 'Get' -Uri $uri -Headers $headers 

Voici le script Powershell complet :

# Variables

$ServicePrincipleName = "MySP"
$TenantId = "" # Enter Tenant Id.
$ClientId = "" # Enter Client Id if exists
$ClientSecret = "" # Enter Client Secret if exists
$SubscriptionId = "" # Enter Subscription ID
$DBXressource = "2ff814a6-3304-4ab8-85cb-cd0e6f879c1d" # CF. AzureDatabricks AzureAD application 
$ResourceGroupName = "" # Enter the RG name where the Databricks ressource is
$WorkspaceName = "" # Enter the name of the Databricks ressource
$Resource = https://management.core.windows.net/


$RequestAccessTokenUri = "https://login.microsoftonline.com/$TenantId/oauth2/token"

# Connect to Azure
Connect-AzAccount
Set-AzContext -SubscriptionId $SubscriptionId

# Get the Databricks URL
$dbxurl = (Get-AzResource -Name $WorkspaceName -ResourceGroupName $ResourceGroupName -ExpandProperties).Properties.workspaceUrl
$uriroot = "https://$dbxurl/api" 


# Create SP if not exists
if ($null -eq (Get-AzADServicePrincipal -DisplayName $ServicePrincipleName)) {
    $mySP = New-AzADServicePrincipal -DisplayName $servicePrincipleName -ErrorAction 'Stop'
    $ClientId = $mySP.ApplicationId
    $ClientSecret = [pscredential]::new($servicePrincipleName, $mySP.Secret).GetNetworkCredential().Password
}

# Set RBAC on Databricks workspace

New-AzRoleAssignment -ApplicationId $ClientId `
    -RoleDefinitionName "Owner" `
    -ResourceGroupName $ResourceGroupName `
    -ResourceName  $WorkspaceName `
    -ResourceType "Microsoft.Databricks/workspaces"

# Get AzureDatabricks app token

$body = "grant_type=client_credentials&client_id=$ClientId&client_secret=$ClientSecret&resource=$DBXressource"

$Token = Invoke-RestMethod -Method Post -Uri $RequestAccessTokenUri -Body $body -ContentType 'application/x-www-form-urlencoded'

Write-Host "Print Token" -ForegroundColor Green
Write-Output $Token.access_token
$apiKey = $Token.access_token

# Get Azure Management token

$bodyManagement = "grant_type=client_credentials&client_id=$ClientId&client_secret=$ClientSecret&resource=$Resource"

$Token = Invoke-RestMethod -Method Post -Uri $RequestAccessTokenUri -Body $bodyManagement -ContentType 'application/x-www-form-urlencoded'

Write-Host "Print Token" -ForegroundColor Green
Write-Output $Token.access_token
$apiKeyManagement = $Token.access_token

# Call Azure Databricks API

$headers = @{
    "Authorization"="Bearer $apiKey";
    "X-Databricks-Azure-SP-Management-Token"=$apiKeyManagement;
    "X-Databricks-Azure-Workspace-Resource-Id"="/subscriptions/$SubscriptionId/resourceGroups/$ResourceGroupName/providers/Microsoft.Databricks/workspaces/$WorkspaceName"
}
$uri = "$uriroot/2.0/dbfs/list?path=/"
Invoke-RestMethod -Method 'Get' -Uri $uri -Headers $headers 

Et voila !

Une bonne pratique par la suite est d’aller générer un PAT Databricks classique avec cette méthode via l’API “api/2.0/token/create” (la doc ici) et l’enregistrer dans un secret Azure Key Vault pour que d’autre applicatifs l’utilisent comme Azure Data Factory par exemple.

Pour comprendre ce mode de connexion, j’ai du faire du reverse engineering sur le module Powershell azure.databricks.cicd.tools qui propose aussi une authentification Azure AD. Je vous recommande d’ailleurs vraiment ce module qui est très pratique : https://www.powershellgallery.com/packages/azure.databricks.cicd.tools/ 

EDIT :

Suite à la mise à jour des URL de workspace Databricks, j’ai ajouté dans le code un moyen pour récupérer dynamiquement l’URL du workspace via une commande Az PowerShell :

$dbxurl = (Get-AzResource -Name $WorkspaceName -ResourceGroupName $ResourceGroupName -ExpandProperties).Properties.workspaceUrl
$uriroot = "https://$dbxurl/api" 

La doc MS au sujet des URL : https://docs.microsoft.com/fr-fr/azure/databricks/workspace/workspace-details

Databricks : TOP 3 des façons de créer un environnement de développement ou de test !

Si vous êtes intéressés par Databricks et que vous souhaitez vous y mettre, il existe aujourd’hui plusieurs façons de se créer un environnement de test et de développement :

  1. Utiliser Azure Databricks directement
  2. Utiliser Databricks Community Edition 
  3. Installer un environnement local sur votre ordinateur

Bien sur toutes ces solutions ont chacune leurs avantages et inconvénients, et c’est que nous allons voir dans cet article !

Azure Databricks

Azure Databricks sera la version la plus complète et la plus proche de ce que vous pourrez faire réellement avec Databricks dans Azure.

Pour le créer, il vous faudra d’abord une souscription Azure, soit celle de votre entreprise, soit un version trial (=> https://azure.microsoft.com/fr-fr/offers/ms-azr-0044p/). Vous allez ensuite créer une ressource Azure Databricks dans votre souscription et vous aurez accès à un workspace. Vous pourrez en plus créer tous les autres composants Azure dont vous aurez besoins comme Azure Data Lake Store, Azure Data Factory, Azure Key Vault…

Avantages :

  • Simple à créer : il suffit de créer une ressource Azure Databricks
  • C’est la seule solution qui permet une expérience et une intégration complète avec Azure enrichissant fortement Databricks :
      • Azure Data Factory => ELT
      • Azure AD => Sécurisation des utilisateurs
      • Azure DevOps => CI/CD
      • Azure Key Vault => Sécurisation des connexions
  • Si vous créez votre workspace dans la même région Azure que vos données, vous ne payerez pas de coût de sorti de vos données.
  • Complètement scalable avec toutes les créations de clusters possibles : autant de taille et de noeuds que l’on veut, des clusters dynamiques, interactifs…
  • Utilisation des Token et donc toutes les fonctionnalités d’automatisation via les API ou autre sont possibles.

Inconvénients : 

  • La solution la plus couteuse, car elle est payante dès la création d’un cluster et que l’on veut exécuter un notebook. En même temps, c’est la plus scalable et donc forcément si vous voulez essayer de gros cluster pour traiter de grosses volumétries, cela se paye.

Databricks Community Edition

En dehors de Azure, Databricks propose un environnement très proche de la version Azure que l’on peut utiliser gratuitement : https://databricks.com/product/faq/community-edition

Ces workspaces sont hébergés sur AWS et vous donnent accès à de “petits” cluster de 1 noeud avec quelques Go de RAM (6 Go il me semble).

Il vous suffit de vous inscrire avec un simple mail (ca marche même avec un gmail !) et de vous connecter avec : https://community.cloud.databricks.com/login.html 

Avantages :

  • Simple à créer : juste un mail et quelques clics.
  • La plus part des fonctionnalités Databricks sont disponibles : le runtime Spark Databricks complet, l’expérience de développement du workspace complet (notebooks, metastore, gestion des librairies…)
  • C’est totalement gratuit.

Inconvénients :

  • Databricks Community tourne sur AWS sur de petits clusters, donc attention aux volumétries :
    • les sorties de données d’Azure sont payantes si vous attaquez des données
      Azure en source
    • l’environnement n’est pas scalable donc pas possible d’attaquer de
      réelles volumétries
  • Il n’est pas possible de générer des Token PAT donc :
    • pas de fonctionnalités d’administration
    • pas d’accès aux API
    • pas d’interaction avec Azure Data Factory et autres produits “externes”.

Environnent local

Databricks étant une solution utilisant le moteur Spark, vous pouvez aussi installer sur votre propre ordinateur une version standalone de Spark open-source (communauté) et d’autres composants comme Anaconda et Jupyter vous permettant de simuler des notebooks, comme dans un vrai workspace Databricks.

Spark peut s’installer sur Windows, Linux et Mac OSX. Personnellement je suis sur Windows 10 et je préfère utiliser Windows Bash (WSL 2) et tout installer dessus plutôt que des versions Windows. Car généralement les documentations que l’on va trouver sur le sujet sur internet sont sur Linux, et cela garanti une plus grande compatibilité avec d’autres briques de l’écosystème, comme Hadoop ou Blobfuse pour monter un chemin réseau vers un blob par exemple.

Vous pouvez aussi utiliser des images Docker si vous voulez et si vous maitrisez la création d’image et de conteneur, car les images déjà existantes avec tous les composants déjà installés sont rares.

Avantages :

  • C’est totalement gratuit : juste le prix de votre ordinateur… mais en fait si votre ordi c’est un MacBook ou un serveur à 16 Cores et 128Go de RAM, on va dire que c’est pas si gratuit que ça Open-mouthed smile
  • Vos réalisations restent en local sur votre ordinateur, et vous pouvez les synchroniser avec un repo GIT comme n’importe quel documents et fichiers.

Inconvénients :

  • Assez compliqué à installer et à maintenir, il faut tout faire soit-même, les documentations d’installation des composants ne sont pas toujours simple à prendre en main.
  • Même problématique que pour la version Community de Databricks, donc attention aussi aux volumétries :
      • les sorties de données d’Azure sont payantes si vous attaquez des données Azure en source
      • l’environnement n’est pas scalable donc pas possible d’attaquer de réelles volumétries
  • Pas de partage de notebook possible en mode web.
  • Pas complètement compatible avec la version Spark de Databricks, qui propose des fonctionnalité en avance non présentes dans la version open-source de Spark.

Conclusion

Pour un projet complet avec pour objectif d’aller jusqu’en production et/ou avec de grosses volumétries et/ou une compatibilité avec d’autres outils Azure comme Azure Data Factory, Azure Key Vault.. : Azure Databricks

Pour de l’apprentissage et découvrir Databricks gratuitement : Databricks Community Edition

Pour ceux qui veulent creuser la version Spark open-source, et si vous aimez installer des trucs et vous prendre la tête avec de la configuration système : Environnement Local

Azure Data Factory : Il essaie de récupérer un secret Azure Key Vault dans un Pipeline, ça tourne mal !

Azure Key Vault (AKV) est un produit Azure assez pratique pour stocker de façon sécurisée des secrets (entre autres) et Azure Data Factory (ADF) permet nativement d’y accéder. Ca s’utilise généralement dans la configuration de nos linked services ADF, cf. la doc officielle pour voir comment faire : https://docs.microsoft.com/en-us/azure/data-factory/store-credentials-in-key-vault

On va donc pouvoir utiliser AKV pour gérer toute la configuration de nos ADF : les users, les mots de passe, les chaines de connexions, les chemins, les tokens…

47306e90e47927fd0cd4b65648854841

Bon récupérer des secrets dans des linked services c’est cool…

…Mais maintenant, il y a des cas où ce n’est pas suffisant, et je vais vous montrer comment allez un peu plus loin, et récupérer des secrets directement dans un pipeline pour ensuite utiliser la valeur du secret dans n’importe quelle activité !

Il n’existe malheureusement pas, au moment où j’écris cet article, de moyen natif pour faire cela, par contre c’est possible via une simple activité web. Pour ce faire, on va falloir :

  1. S’assurer que le MSI de notre ADF a le bien droit de récupérer les secrets (GET) de votre AKV, voir le l’étape 2 de la doc ‘”In your key vault -> Access policies -> Add new -> search this managed identity application ID to grant Get permission in Secret permissions dropdown” et surtout oubliez pas de cliquer sur “save” pour enregistrer votre affectation :
  2. https://docs.microsoft.com/en-us/azure/data-factory/store-credentials-in-key-vault#steps

  3. Ajouter une activité web dans votre pipeline ADF avec la conf suivante :
    1. URL : l’url de votre secret, https://<AKVName>.vault.azure.net/secrets/<SecretName>?api-version=2016-10-01 en remplaçant <AKVName> par le nom de votre AKV et <SecretName> par le nom de votre secret
    2. Method : GET
    3. Authentication : MSI
    4. Resource : https://vault.azure.net

chrome_tINXRL6kx2

Vous pourrez ensuite récupérer la valeur du secret dans les activités suivantes et dans n’importe quelle expression avec la formule @activity(‘<NomDeVotreActiviteWeb>’).output.value, en remplaçant <NomDeVotreActiviteWeb> par le nom de votre activité web.

Si vous voulez sécuriser encore plus la valeur de votre secret je vous conseille d’aller dans l’onglet General de votre activité web et de cocher les paramètres “Secure output” et “Secure input”, sinon la valeur de votre secret sera affichée en clair dans le monitoring de votre pipeline. Ce qui n’est pas top si on va chercher un mot de passe par exemple Smile 

chrome_Y5dqX2NMIq

Voici au final, le code Json de votre activité web :

{
    "name": "GetSecret",
    "type": "WebActivity",
    "dependsOn": [],
    "policy": {
        "timeout": "7.00:00:00",
        "retry": 0,
        "retryIntervalInSeconds": 30,
        "secureOutput": true,
        "secureInput": true
    },
    "userProperties": [],
    "typeProperties": {
        "url": "https://<AKVName>.vault.azure.net/secrets/<SecretName>?api-version=2016-10-01",
        "method": "GET",
        "authentication": {
            "type": "MSI",
            "resource": "https://vault.azure.net"
        }
    }
}

Attention aussi avec l’activité “set value” de ADF, qui elle aussi affiche la valeur qu’on affecte dans une variable en clair dans la log. La sécurité s’est important !

Jupyter Notebooks : #LIFEHACK exécuter du SQL sur des Dataframes Pandas directement dans une cellule magic

Aujourd’hui petit article un peu spécial, dans lequel je vous propose de vous montrer comment requêter des Dataframes Pandas en SQL !

Pour ce faire, il va falloir utiliser en plus de Pandas la librairie pandasql (https://pypi.org/project/pandasql/). Elle est plutôt simple à utiliser, par exemple :

import pandas as pd
import numpy as np
from pandasql import sqldf

df_customer = pd.DataFrame([
                        [1, 'Customer A', '123 Street', np.nan],
                        [2, 'Customer B', '444 Street', '333 Street'],
                        [3, 'Customer C', '444 Street', '666 Street']
                        ], 
                columns=['ID', 'Customer', 'Billing Address', 'Shipping Address'])

query = "select * from df_customer"				
sqldf(query, globals())

Ainsi vous aller créer d’abord vos Dataframes (“df_customer” dans l’exemple), puis exécuter une requête SQL dans un string via la commande “sqldf()” qui vous retournera le résultat sous forme d’un nouveau Dataframe. Donc c’est top si vous n’avez pas le courage d’apprendre comment transformer vos données avec le langage Pandas, et que vous maitriser déjà le SQL Open-mouthed smile. Attention, pandasql utilise la syntaxe de sqlite (https://www.sqlite.org/lang.html), donc pour les adeptes de SQL Server, c’est proche mais ce n’est pas exactement du TSQL.

Maintenant pour rendre l’usage de cette fonctionnalité encore plus interactive, on va utiliser et développer une classe custom magic afin de pouvoir lancer du code SQL dans une cellule de nos notebooks grâce au mot clé %%sql.

Pour cela, on va donc d’abord créer un script de classe python à côté appelé “sqlpandas.py”, qui contiendra le bout de code suivant :

from IPython.core.magic import line_magic, line_cell_magic, Magics, magics_class
import pandasql as ps

@magics_class
class sqlpandas(Magics):

   @line_cell_magic  
   def sql(self, line, cell=None):
        return ps.sqldf(cell or line, get_ipython().user_ns)

ip = get_ipython()
ip.register_magics(sqlpandas)

Voila ensuite comment utiliser tout cela dans un notebook Jupyter :

Vous voyez que c’est plutôt simple à utiliser et que cela fonctionne, il est même possible de récupérer les résultats d’une cellule dans un autre Dataframe. Retrouvez tous les bouts de code dans mon github ici : https://github.com/fabienadato/magicSQLJupyter 

Plus d’info sur les classes custom magic : https://ipython.readthedocs.io/en/stable/config/custommagics.html

Databricks : CI/CD avec Azure DevOps et 3 méthodes de déploiement de notebooks en masse, la 3eme va vous étonner !

Avant de parler de méthode de déploiement, on va parler un peu de Databricks et de CI/CD.

Il n’y a pas toujours besoin de mettre en place du CI/CD, surtout lorsqu’on a des usages “self-service” et que l’on travaille directement production. Mais par exemple lorsqu’on a besoin d’automatiser des traitements (ça doit tourner tous les jours en automatique ou en continu via du streaming) et que l’on veut pas tout casser en production en cas d’erreur suite à une modification de code. Dans ces cas là, il vaut mieux travailler sur différents environnements identiques et indépendants. Et notamment au moins avec un environnement de dev pour pouvoir coder et tester son code tranquillement avant de modifier la prod. Beaucoup de personnes écrivent des livres entiers sur le sujet, mais nous on va simplifier et résumer le CI/CD à 2 choses :

  1. bien gérer la vie de son code grâce à un contrôleur de sources (un repo) => je ne perds plus de code, je trace les modifications, je travaille à plusieurs…
  2. déployer facilement et de façon automatisée le code dans les différents environnements => j’appuie sur un bouton et ça part en prod !

Databricks est compatible avec ces usages de développement et de déploiement en continue pour tous vos projets de data science et de data engineer.

Tout d’abord, il est possible de se connecter nativement à Github, Bitbucket Cloud ou dans Azure DevOps et de synchroniser ses notebooks automatiquement dans un repository GIT. Personnellement, j’utilise surtout Azure DevOps qui est simple à utiliser et sécurisé donc adapté à un usage en entreprise. Pour savoir comment mettre ca en place, la documentation Databricks est plutôt complète: https://docs.azuredatabricks.net/user-guide/notebooks/azure-devops-services-version-control.html#notebook-integration

Attention, il va  falloir mettre en place la synchro pour chaque notebook et après modification du notebook dans Databricks, surtout ne pas oublier de cliquer sur “Save now”  et mettre un commentaire (un vrai commentaire avec la description de vos modifications, pas de “zezg” ou “ghherhe” !) pour lancer un commit et synchroniser avec le script du repo, sinon bah ça fait rien Smile !

chrome_TdhduWhbxW

Une fois synchronisés, les notebooks se retrouvent alors dans le repo sous forme de fichiers textes Python, Scala, R ou SQL en fonction du type de notebook (même si ce notebook contient des blocks d’un autre langage).

rdjj1g4dYe

Tout ces principes de synchro seront à mettre en place dans le workspace Databricks de développement seulement, pas besoin pour les autres environnements.

Maintenant lorsque vos développements sont terminés, vous allez vouloir déployer vos notebooks sur d’autre workspaces Databricks pour l’UAT, la Préprod, la Prod … il est possible d’utiliser aussi Azure DevOps et ses pipelines de Build et de Release :

  • La partie Build va aller chercher les scripts dans le repo et générer un objet qu’on appelle un Artifact (l’équivalent d’un package de déploiement)
  • La partie Release elle va récupérer l’Artifact et le déployer dans le workspace Databricks de l’environnement souhaité.

POWERPNT_HuxGoRoZ8D

Sans rentrer trop dans les détails, le Build est simple, il suffit d’aller chercher les scripts dans le repo, la Release est un peu plus compliquée car il faut une méthode pour déployer les composants (code, notebooks…) et avec Azure DevOps il existe déjà des composants proposés par la communauté dans le marketplace :

Moi je vous propose une troisième solution de déploiement en masse de notebooks un peu plus “roots” via un script PowerShell :

Param(
    [string]$apiKey,
    [string]$rootDirectory,
    [string]$uriroot
)

Set-Location -Path $rootDirectory

# Create directory
foreach($directory in Get-ChildItem ".\" -Recurse -Directory)
{
    write-host $directory.FullName.Replace($rootDirectory,"")
    $body = @{
        path = $directory.FullName.Replace($rootDirectory,"").replace("\","/")
    }
    $bodyjson = (ConvertTo-Json $body)
    $headers = @{
        'Authorization' = "Bearer $apiKey"
    }
    $uri = "$uriroot/2.0/workspace/mkdirs"
    Invoke-RestMethod -Method 'Post' -Uri $uri -Headers $headers -Body $bodyjson
}

# Deploy Notebooks
foreach($file in Get-ChildItem ".\" -Recurse -File -Filter "*.*")
{
     
    write-host $file.FullName.Replace($rootDirectory,"")
    
    $EncodedText = [System.Convert]::ToBase64String([System.IO.File]::ReadAllBytes($file.FullName))
    
    $lng = ""
    if($file.Extension -eq ".py")
    {
        $lng = "PYTHON"
    }
    elseif($file.Extension -eq ".scala")
    {
        $lng = "SCALA"
    }
    elseif($file.Extension -eq ".sql")
    {
        $lng = "SQL"
    }
    elseif($file.Extension -eq ".r")
    {
        $lng = "R"
    }
    else {
        continue
    }
    $body = @{
        content = $EncodedText
        path = $file.FullName.Replace($rootDirectory,"").replace("\","/").replace($file.Extension,"")
        language = $lng
        overwrite = $TRUE
        format = "SOURCE"
    }
    $bodyjson = (ConvertTo-Json $body)
    $headers = @{
        'Authorization' = "Bearer $apiKey"
    }
    $uri = "$uriroot/2.0/workspace/import"
    Invoke-RestMethod -Method 'Post' -Uri $uri -Headers $headers -Body $bodyjson

}

L’intérêt de ce script PowerShell par rapport aux autres solutions est surtout qu’il fonctionnera aussi en dehors de Azure DevOps Smile

Les paramètres en entré sont :

En local il s’appelle de cette façon : .\deploynotebooks.ps1 -apiKey « <accesKey> » -rootDirectory « C:\pathTo\notebooks » -uriroot https://westeurope.azuredatabricks.net/api

Et dans Azure DevOps, mettez le script quelque part dans votre repo et vous pourrez passer par une tache d’exécution de script PowerShell de votre pipeline de release :

9IcLrYTvC6

Databricks : configurer le répertoire par défaut dataware

Par défaut lorsque l’on crée une table sans spécifier le paramètre location dans Databricks en SparkSQL, les fichiers de données sont stockée dans le DBFS interne du workspace Databricks configuré dans le metastore. On peut retrouver les fichiers de données dans le répertoire dbfs:/user/hive/warehouse/.

L’intérêt du répertoire par défaut géré par le metastore, c’est qu’il va automatiquement créer l’arborescence de dossiers/fichiers sur le stockage lorsque l’on crée une table et même gérer les partitions.

  • Table “normale” : dbfs:/user/hive/warehouse/<database_name>.db/<table_name>/
  • Table partitionnée : dbfs:/user/hive/warehouse/<database_name>.db/<table_name>/<partition_name>=<partition_key>/

Ce comportement est bien sûr un héritage du metastore Hive qui fonctionne de la même façon.

Or, ce n’est pas une bonne pratique de stocker de la données dans le DBFS interne car il n’est pas facilement accessible de l’extérieur ni compatible nativement avec d’autres technologie comme Azure Data Factory, Azure Stream Analytics ou Azure Function. Il vaut donc mieux utiliser à la place un autre stockage externe comme Azure Storage Blob ou Azure Data Lake Store. https://docs.azuredatabricks.net/user-guide/databricks-file-system.html#dbfs-root

On peut bien sûr spécifier le chemin de stockage de chaque table grâce à le mot clé LOCATION du CREATE TABLE, mais c’est un peu fastidieux et si l’on veut garder le fonctionnement automatique de création d’arborescence du metastore avec un stockage externe, il va falloir :

  1. Configurer les accès au stockage externe (blob ou adls)
  2. Changer la valeur de la configuration du répertoire par défaut spark.sql.warehouse.dir en mettant le chemin racine du stockage externe

Voici un exemple de config pour le blob storage :

spark.hadoop.fs.azure.account.key.<storageaccountname>.blob.core.windows.net <storagekey>
spark.sql.warehouse.dir wasb://<containeurname>@<storageaccountname>.blob.core.windows.net/<rep>

Cette configuration peut être directement paramétrée dans la config Spark d’un cluster :

ApplicationFrameHost_o4fxFwUZCY

Ainsi la configuration sera valable automatiquement pour tous les notebooks qui s’exécute sur le cluster et toutes les nouvelles tables créées dans une nouvelles database seront correctement stockées dans le bon stockage externe ici : wasb://<containeurname>@<storageaccountname>.blob.core.windows.net/<rep>/<database_name>.db/<table_name>/

Si la database existait déjà avant le changement de configuration vers le stockage externe, alors cela ne fonctionnera pas pour les nouvelles tables car elles continueront à utiliser le répertoire source de la base dbfs:/user/hive/warehouse/<database_name>.db/…

Voici un bout de code scala pour vérifier les locations des bases de données du metastore :

display(spark.catalog.listDatabases)

Et un bout de code SQL pour tester que cela fonctionne correctement :

create database if not exists testwasbdb;
create table if not exists testwasbdb.testtable (col1 int);
describe DETAIL testwasbdb.testtable;

chrome_sPO72pCORT

Databricks (SPARK) : réparer toutes les tables partitionnées d’un seul coup

Si vous avez des tables partitionnées dans votre metastore HIVE-Spark et que vos données sont écrites sur le stockage dans des sous-répertoires partitionnées (/<pathToTable>/<partitionName>=<value>/) par un autre traitement que Spark ou Hive (Azure Stream Analytics, Event Hub, Azure Function ….), par exemple :

  • /logs/day=2019-08-10/
  • /logs/day=2019-08-11/

Les tables partitionnées ne verront pas les données des nouveaux répertoires tant qu’il n’existera pas de partition spécifiquement crée pour ce sous répertoire.

Mais heureusement il est possible de recréer automatiquement toutes les partition d’une table partitionnée avec la commande MSCK REPAIR <TABLENAME>, ce qui est plutôt pratique mais c’est à faire à la main pour chaque table ! https://docs.cloudera.com/HDPDocuments/HDP3/HDP-3.1.0/using-hiveql/content/hive-msck-repair.html

Maintenant, si vous voulez réparer toutes les partitions de toutes les tables partitionnées d’un seul coup, je vous propose le script pyspark suivant qui boucle sur toutes les bases et toutes les tables afin de les réparer (seulement si elles sont partitionnées bien sûr) :

#Repair partition foreach tables

df_databases = spark.sql("show databases")

for database in df_databases.collect():
  dbname = database.databaseName
  print(dbname)
  query = "use " + dbname 
  spark.sql(query)
  df_tables = spark.sql("show tables")
  
  for table in df_tables.collect():
    tablename = table.tableName
    print("_" + tablename)
    query = "DESCRIBE DETAIL " + tablename
    
    try:
      df_desc = spark.sql(query)
      nb_partitionColumn = len(df_desc.select("partitionColumns").take(1)[0][0])
      if nb_partitionColumn > 0:
        print("__" + tablename + " : is a partition table")
        query = "MSCK REPAIR TABLE " + tablename
        spark.sql(query).collect()
      else:
        print("__" + tablename + " : is not a partition table")
    except:
      print("__" + tablename + " : error with the table")

Ce script est compatible avec tout ce qui lance du Spark et du PySpark, donc Azure Databricks ! Il peut aussi être utilisé lors d’une migration de HDInsight Spark vers Databricks ou d’un workspace Databricks vers un autre workspace :

  1. Vous rejouez les create table sur le workspace cible en faisant bien attention à faire pointer les locations des tables vers les stockages sources existants (Azure Blob / Azure Data Lake Store…)
  2. Vous jouez le script dans un notebook python
  3. Magie toutes les partitions sont recrées !
Older posts