Usar uma entidade de serviço com o conector do Spark 3 no Azure Cosmos DB para NoSQL

Neste artigo, você aprenderá a criar um aplicativo do Microsoft Entra e uma entidade de serviço que podem ser usados com controle de acesso baseado em função. Em seguida, você pode usar essa entidade de serviço para se conectar a uma conta do Azure Cosmos DB para NoSQL do Spark 3.

Pré-requisitos

Criar um segredo e registrar as credenciais

Nesta seção, crie um segredo do cliente e registre o valor para uso posterior.

  1. Abra o Portal do Azure.

  2. Acesse seu aplicativo existente do Microsoft Entra.

  3. Acesse a página Certificados e segredos. Em seguida, crie um novo segredo. Salve o valor do Segredo do Cliente para ser usado posteriormente neste artigo.

  4. Vá para a página Visão geral. Localize e registre os valores da ID do aplicativo (cliente), ID do Objetoe ID do Diretório (locatário). Você também usará esses valores mais adiante neste artigo.

  5. Acesse sua conta existente do Azure Cosmos DB for NoSQL.

  6. Registre o valor do URI na página Visão Geral. Registre também os valores da ID da Assinatura e Grupo de Recursos. Você usará esses valores mais adiante neste artigo.

Criar uma definição e uma atribuição

Nesta seção, você criará uma definição de função do Microsoft Entra ID. Em seguida, atribua a essa função permissões de leitura e gravação de itens nos contêineres.

  1. Crie uma função usando o comando az role definition create. Passe o nome da conta e o grupo de recursos do Azure Cosmos DB para NoSQL, seguido de um corpo de JSON que define a função personalizada. A função também tem escopo no nível da conta usando /. Certifique-se de fornecer um nome exclusivo para sua função usando a propriedade RoleName do corpo da solicitação.

    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. Liste a definição de função que você criou para buscar seu identificador exclusivo na saída JSON. Registre o valor id da saída JSON.

    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. Use az cosmosdb sql role assignment create para criar uma atribuição de função. Substitua <aad-principal-id> pela ID do objeto que você registrou anteriormente neste artigo. Além disso, substitua <role-definition-id> pelo valor id buscado da execução do comando az cosmosdb sql role definition list em uma etapa anterior.

    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>"
    

Usar uma entidade de serviço

Agora que você criou um aplicativo e uma entidade de serviço do Microsoft Entra, criou uma função personalizada e atribuiu permissões de função à sua conta do Azure Cosmos DB for NoSQL, você poderá executar um notebook.

  1. Abra o workspace do Azure Databricks.

  2. Na interface do workspace, crie um cluster. Defina o cluster com estas configurações, no mínimo:

    Versão Valor
    Versão de runtime 13.3 LTS (Scala 2.12, Spark 3.4.1)
  3. Use a interface do workspace para pesquisar pacotes do Maven na Central do Maven com uma ID de grupo de com.azure.cosmos.spark. Instale o pacote especificamente para o Spark 3.4 com uma ID de artefato prefixado com azure-cosmos-spark_3-4 no cluster.

  4. Por fim, crie um notebook.

    Dica

    Por padrão, o notebook é anexado ao cluster criado recentemente.

  5. No notebook, defina as configurações do conector Spark do Azure Cosmos DB para o ponto de extremidade da conta NoSQL, o nome do banco de dados e o nome do contêiner. Use os valores de ID da Assinatura, Grupo de Recursos, ID do Aplicativo (cliente), ID do Diretório (locatário) e Segredo do Cliente registrados anteriormente neste artigo.

    # 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. Configure a API do Catálogo para gerenciar a API para recursos NoSQL usando o Spark.

    # 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. Crie um novo banco de dados usando CREATE DATABASE IF NOT EXISTS. Certifique-se de fornecer o nome do banco de dados.

    # 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. Crie um novo contêiner usando o nome do banco de dados, o nome do contêiner, o caminho da chave de partição e os valores de taxa de transferência que você especificar.

    # 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. Crie um conjunto de dados de exemplo.

    # 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. Use spark.createDataFrame e a configuração do OLTP (processamento de transações online) salva anteriormente para adicionar dados de exemplo ao contêiner de destino.

    # 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()
    

    Dica

    Neste exemplo de início rápido, as credenciais são atribuídas a variáveis em texto não criptografado. Por segurança, é recomendável usar segredos. Para obter mais informações sobre como configurar segredos, confira Adicionar segredos à sua configuração do Spark.