Freigeben über


Verwenden eines Dienstprinzipals mit dem Spark 3-Konnektor für Azure Cosmos DB for NoSQL

In diesem Artikel erfahren Sie, wie Sie eine Microsoft Entra-Anwendung und einen Dienstprinzipal erstellen, die mit der rollenbasierten Zugriffssteuerung verwendet werden können. Anschließend können Sie diesen Dienstprinzipal verwenden, um eine Verbindung mit einem Azure Cosmos DB for NoSQL-Konto von Spark 3 herzustellen.

Voraussetzungen

Erstellen eines Geheimnisses und Notieren der Anmeldeinformationen

In diesem Abschnitt erstellen Sie einen geheimen Clientschlüssel und notieren den Wert für die spätere Verwendung.

  1. Öffnen Sie das Azure-Portal.

  2. Navigieren Sie zu Ihrer vorhandenen Microsoft Entra-Anwendung.

  3. Wechseln Sie zur Seite Zertifikate und Geheimnisse. Erstellen Sie dann ein neues Geheimnis. Speichern Sie den Wert für den geheimen Clientschlüssel, der später in diesem Artikel verwendet werden soll.

  4. Wechseln Sie zur Seite Übersicht. Suchen Sie die Werte für Anwendungs-ID (Client-ID), Objekt-ID und Verzeichnis-ID (Mandanten-ID). Notieren Sie sich diese Werte. Sie verwenden diese Werte auch im weiteren Verlauf dieses Artikels.

  5. Navigieren Sie zu Ihrem vorhandenen Azure Cosmos DB for NoSQL-Konto.

  6. Notieren Sie sich den Wert für URI auf der Seite Übersicht. Notieren Sie sich außerdem die Werte für Abonnement-ID und Ressourcengruppe. Sie benötigen diese Werte im weiteren Verlauf dieses Artikels.

Erstellen einer Definition und einer Zuweisung

In diesem Abschnitt erstellen Sie eine Microsoft Entra ID-Rollendefinition. Anschließend weisen Sie diese Rolle mit Berechtigungen zum Lesen und Schreiben für die Elemente in den Containern zu.

  1. Erstellen Sie eine Rolle mithilfe des Befehls az role definition create. Übergeben Sie den Azure Cosmos DB for NoSQL-Kontonamen und die Ressourcengruppe, gefolgt von einem JSON-Text, der die benutzerdefinierte Rolle definiert. Der Bereich der Rolle wird zudem mit / auf die Kontoebene festgelegt. Stellen Sie sicher, dass Sie einen eindeutigen Namen für Ihre Rolle mithilfe der RoleName-Eigenschaft des Anforderungstextes angeben.

    az cosmosdb sql role definition create \
        --resource-group "<resource-group-name>" \
        --account-name "<account-name>" \
        --body '{
            "RoleName": "<role-definition-name>",
            "Type": "CustomRole",
            "AssignableScopes": ["/"],
            "Permissions": [{
                "DataActions": [
                    "Microsoft.DocumentDB/databaseAccounts/readMetadata",
                    "Microsoft.DocumentDB/databaseAccounts/sqlDatabases/containers/items/*",
                    "Microsoft.DocumentDB/databaseAccounts/sqlDatabases/containers/*"
                ]
            }]
        }'
    
  2. Listen Sie die von Ihnen erstellte Rollendefinition auf, um ihren eindeutigen Bezeichner in der JSON-Ausgabe abzurufen. Notieren Sie sich den id-Wert der JSON-Ausgabe.

    az cosmosdb sql role definition list \
        --resource-group "<resource-group-name>" \
        --account-name "<account-name>"
    
    [
      {
        ...,
        "id": "/subscriptions/<subscription-id>/resourceGroups/<resource-grou-name>/providers/Microsoft.DocumentDB/databaseAccounts/<account-name>/sqlRoleDefinitions/<role-definition-id>",
        ...
        "permissions": [
          {
            "dataActions": [
              "Microsoft.DocumentDB/databaseAccounts/readMetadata",
              "Microsoft.DocumentDB/databaseAccounts/sqlDatabases/containers/items/*",
              "Microsoft.DocumentDB/databaseAccounts/sqlDatabases/containers/*"
            ],
            "notDataActions": []
          }
        ],
        ...
      }
    ]
    
  3. Verwenden Sie az cosmosdb sql role assignment create, um eine Rollenzuweisung zu erstellen. Ersetzen Sie die <aad-principal-id> durch die Objekt-ID, die Sie sich weiter oben in diesem Artikel notiert haben. Ersetzen Sie auch <role-definition-id> durch den id-Wert, der bei der Ausführung des Befehls az cosmosdb sql role definition list in einem vorherigen Schritt abgerufen wurde.

    az cosmosdb sql role assignment create \
        --resource-group "<resource-group-name>" \
        --account-name "<account-name>" \
        --scope "/" \
        --principal-id "<account-name>" \
        --role-definition-id "<role-definition-id>"
    

Verwenden eines Dienstprinzipals

Nachdem Sie nun eine Microsoft Entra-Anwendung und einen Dienstprinzipal erstellt, eine benutzerdefinierte Rolle erstellt und dieser Rolle Berechtigungen für Ihr Azure Cosmos DB for NoSQL-Konto zugewiesen haben, sollten Sie ein Notebook ausführen können.

  1. Öffnen Sie Ihren Azure Databricks-Arbeitsbereich.

  2. Erstellen Sie auf der Oberfläche des Arbeitsbereichs einen neuen Cluster. Konfigurieren Sie den Cluster mit den folgenden (minimalen) Einstellungen:

    Version Wert
    Laufzeitversion 13.3 LTS (Scala 2.12, Spark 3.4.1)
  3. Verwenden Sie die Benutzeroberfläche des Arbeitsbereichs, um in Maven Central nach Maven-Paketen mit der Gruppen-IDcom.azure.cosmos.spark zu suchen. Installieren Sie das für Spark 3.4 spezifische Paket (der Artefakt-ID des Clusters hat das Präfix azure-cosmos-spark_3-4).

  4. Erstellen Sie zuletzt neues Notebook.

    Tipp

    Standardmäßig wird das Notebook an den zuletzt erstellten Cluster angefügt.

  5. Legen Sie im Notebook die Konfigurationseinstellungen des Spark-Connectors von Azure Cosmos DB für den NoSQL-Kontoendpunkt, den Datenbanknamen und den Containernamen fest. Verwenden Sie die Abonnement-ID, die Ressourcengruppe, die Anwendungs-ID (Client-ID), die Verzeichnis-ID (Mandanten-ID) und die Werte für den geheimen Clientschlüssel, die Sie sich weiter oben in diesem Artikel notiert haben.

    # Set configuration settings
    config = {
      "spark.cosmos.accountEndpoint": "<nosql-account-endpoint>",
      "spark.cosmos.auth.type": "ServicePrincipal",
      "spark.cosmos.account.subscriptionId": "<subscription-id>",
      "spark.cosmos.account.resourceGroupName": "<resource-group-name>",
      "spark.cosmos.account.tenantId": "<entra-tenant-id>",
      "spark.cosmos.auth.aad.clientId": "<entra-app-client-id>",
      "spark.cosmos.auth.aad.clientSecret": "<entra-app-client-secret>",
      "spark.cosmos.database": "<database-name>",
      "spark.cosmos.container": "<container-name>"        
    }    
    
    // Set configuration settings
    val config = Map(
      "spark.cosmos.accountEndpoint" -> "<nosql-account-endpoint>",
      "spark.cosmos.auth.type" -> "ServicePrincipal",
      "spark.cosmos.account.subscriptionId" -> "<subscription-id>",
      "spark.cosmos.account.resourceGroupName" -> "<resource-group-name>",
      "spark.cosmos.account.tenantId" -> "<entra-tenant-id>",
      "spark.cosmos.auth.aad.clientId" -> "<entra-app-client-id>",
      "spark.cosmos.auth.aad.clientSecret" -> "<entra-app-client-secret>",
      "spark.cosmos.database" -> "<database-name>",
      "spark.cosmos.container" -> "<container-name>" 
    )
    
  6. Konfigurieren Sie mithilfe von Spark die Katalog-API zum Verwalten der API für NoSQL-Ressourcen.

    # Configure Catalog Api
    spark.conf.set("spark.sql.catalog.cosmosCatalog", "com.azure.cosmos.spark.CosmosCatalog")
    spark.conf.set("spark.sql.catalog.cosmosCatalog.spark.cosmos.accountEndpoint", "<nosql-account-endpoint>")
    spark.conf.set("spark.sql.catalog.cosmosCatalog.spark.cosmos.auth.type", "ServicePrincipal")
    spark.conf.set("spark.sql.catalog.cosmosCatalog.spark.cosmos.account.subscriptionId", "<subscription-id>")
    spark.conf.set("spark.sql.catalog.cosmosCatalog.spark.cosmos.account.resourceGroupName", "<resource-group-name>")
    spark.conf.set("spark.sql.catalog.cosmosCatalog.spark.cosmos.account.tenantId", "<entra-tenant-id>")
    spark.conf.set("spark.sql.catalog.cosmosCatalog.spark.cosmos.auth.aad.clientId", "<entra-app-client-id>")
    spark.conf.set("spark.sql.catalog.cosmosCatalog.spark.cosmos.auth.aad.clientSecret", "<entra-app-client-secret>")
    
    // Configure Catalog Api
    spark.conf.set(s"spark.sql.catalog.cosmosCatalog", "com.azure.cosmos.spark.CosmosCatalog")
    spark.conf.set(s"spark.sql.catalog.cosmosCatalog.spark.cosmos.accountEndpoint", "<nosql-account-endpoint>")
    spark.conf.set(s"spark.sql.catalog.cosmosCatalog.spark.cosmos.auth.type", "ServicePrincipal")
    spark.conf.set(s"spark.sql.catalog.cosmosCatalog.spark.cosmos.account.subscriptionId", "<subscription-id>")
    spark.conf.set(s"spark.sql.catalog.cosmosCatalog.spark.cosmos.account.resourceGroupName", "<resource-group-name>")
    spark.conf.set(s"spark.sql.catalog.cosmosCatalog.spark.cosmos.account.tenantId", "<entra-tenant-id>")
    spark.conf.set(s"spark.sql.catalog.cosmosCatalog.spark.cosmos.auth.aad.clientId", "<entra-app-client-id>")
    spark.conf.set(s"spark.sql.catalog.cosmosCatalog.spark.cosmos.auth.aad.clientSecret", "<entra-app-client-secret>")
    
  7. Erstellen Sie eine neue Datenbank mithilfe von CREATE DATABASE IF NOT EXISTS. Geben Sie Ihren Datenbanknamen an.

    # Create a database using the Catalog API
    spark.sql("CREATE DATABASE IF NOT EXISTS cosmosCatalog.{};".format("<database-name>"))
    
    // Create a database using the Catalog API
    spark.sql(s"CREATE DATABASE IF NOT EXISTS cosmosCatalog.<database-name>;")
    
  8. Erstellen Sie einen neuen Container unter Verwendung des Datenbanknamens, des Containernamens, des Partitionsschlüsselpfads und der von Ihnen angegebenen Durchsatzwerte.

    # Create a products container using the Catalog API
    spark.sql("CREATE TABLE IF NOT EXISTS cosmosCatalog.{}.{} USING cosmos.oltp TBLPROPERTIES(partitionKeyPath = '{}', manualThroughput = '{}')".format("<database-name>", "<container-name>", "<partition-key-path>", "<throughput>"))
    
    // Create a products container using the Catalog API
    spark.sql(s"CREATE TABLE IF NOT EXISTS cosmosCatalog.<database-name>.<container-name> using cosmos.oltp TBLPROPERTIES(partitionKeyPath = '<partition-key-path>', manualThroughput = '<throughput>')")
    
  9. Erstellen Sie ein Beispieldataset.

    # Create sample data    
    products = (
      ("68719518391", "gear-surf-surfboards", "Yamba Surfboard", 12, 850.00, False),
      ("68719518371", "gear-surf-surfboards", "Kiama Classic Surfboard", 25, 790.00, True)
    )
    
    // Create sample data
    val products = Seq(
      ("68719518391", "gear-surf-surfboards", "Yamba Surfboard", 12, 850.00, false),
      ("68719518371", "gear-surf-surfboards", "Kiama Classic Surfboard", 25, 790.00, true)
    )
    
  10. Verwenden Sie spark.createDataFrame und die zuvor gespeicherte OLTP-Konfiguration (Online Transaction Processing), um Beispieldaten zum Zielcontainer hinzuzufügen.

    # Ingest sample data    
    spark.createDataFrame(products) \
      .toDF("id", "category", "name", "quantity", "price", "clearance") \
      .write \
      .format("cosmos.oltp") \
      .options(config) \
      .mode("APPEND") \
      .save()
    
    // Ingest sample data
    spark.createDataFrame(products)
      .toDF("id", "category", "name", "quantity", "price", "clearance")
      .write
      .format("cosmos.oltp")
      .options(config)
      .mode("APPEND")
      .save()
    

    Tipp

    In diesem Schnellstartbeispiel werden den Variablen Anmeldeinformationen im Klartext zugewiesen. Aus Sicherheitsgründen wird empfohlen, Geheimnisse zu verwenden. Weitere Informationen zum Konfigurieren von Geheimnissen finden Sie unter Hinzufügen von Geheimnissen zu Ihrer Spark-Konfiguration.