Mounting ADLS gen2 with AAD passthrough in Azure Databricks with Terraform - azure-active-directory

I am trying to mount my ADLS gen2 storage containers into DBFS, with Azure Active Directory passthrough, using the Databricks Terraform provider. I'm following the instructions here and here, but I'm getting the following error when Terraform attempts to deploy the mount resource:
Error: Could not find ADLS Gen2 Token
My Terraform code looks like the below (it's very similar to the example in the provider documentation) and I am deploying with an Azure Service Principal, which creates the Databricks workspace in the same module:
provider "databricks" {
host = azurerm_databricks_workspace.this.workspace_url
azure_workspace_resource_id = azurerm_databricks_workspace.this.id
}
data "databricks_node_type" "smallest" {
local_disk = true
depends_on = [azurerm_databricks_workspace.this]
}
data "databricks_spark_version" "latest" {
depends_on = [azurerm_databricks_workspace.this]
}
resource "databricks_cluster" "passthrough" {
cluster_name = "terraform-mount"
spark_version = data.databricks_spark_version.latest.id
node_type_id = data.databricks_node_type.smallest.id
autotermination_minutes = 10
num_workers = 1
spark_conf = {
"spark.databricks.cluster.profile" = "serverless",
"spark.databricks.repl.allowedLanguages" = "python,sql",
"spark.databricks.passthrough.enabled" = "true",
"spark.databricks.pyspark.enableProcessIsolation" = "true"
}
custom_tags = {
"ResourceClass" = "Serverless"
}
}
resource "databricks_mount" "mount" {
for_each = toset(var.storage_containers)
name = each.value
cluster_id = databricks_cluster.passthrough.id
uri = "abfss://${each.value}#${var.sa_name}.dfs.core.windows.net"
extra_configs = {
"fs.azure.account.auth.type" = "CustomAccessToken",
"fs.azure.account.custom.token.provider.class" = "{{sparkconf/spark.databricks.passthrough.adls.gen2.tokenProviderClassName}}",
}
depends_on = [
azurerm_storage_container.data
]
}
(For clarity's sake, azurerm_storage_container.data is a set of storage containers with names from var.storage_containers, which are created in the azurerm_storage_account with name var.sa_name; hence the URI.)
I feel like this error is due to a fundamental misunderstanding on my part, rather than a simple omission. My underlying assumption is that I can mount storage containers for the workspace, with AAD passthrough, as a convenience when I deploy the infrastructure in its entirety. That is, whenever users come to use the workspace, any new passthrough cluster will be able to use these mounts with zero setup.
I can mount storage containers manually, following the AAD passthrough instructions: Spin up a high-concurrency cluster with passthrough enabled, then mount with dbutils.fs.mount. This is while logged in to the Databricks workspace with my user identity (rather than the Service Principal). Is this the root of the problem; is a Service Principal not appropriate for this task?
(Interestingly, the Databricks runtime gives me exactly the same error if I try to access files on the manually created mount using a cluster without passthrough enabled.)

Yes, that's problem arise from the use of service principal for that operation. Azure docs for credentials passthrough says:
You cannot use a cluster configured with ADLS credentials, for example, service principal credentials, with credential passthrough.

Related

How to get azure blob storage data into react app that is hosted on azure app service

Hi I am working on react project ,I want to download huge files ( more than 2.5gb) from azure blob storage to react application , ( scenario is when user click on export button I have text files in azure blob storage I want them to be downloaded to local system ) , I have been looking at few approaches, since I am new to azure I am bit confused
using azure AD we can get access to azure blob storage but since my application is hosted on app service how we can connect these two together or we can have direct access to files through azure app services ?
approach I am currently looking at : here
If all the resources are from azure, then we should use manage identity or service principle (which also use manage identity under the hood) link in your case.
In your case, you have two azure resources
Azure blob storage
App Service (which hosted as reactjs application)
So Here is there is step by step explanation to how you connect and read blob
In AppService(which hosted as reactjs application)
Go to your Appservice
Then Click on Identity in Left panel
Then On System assigned managed identity
After clicking save button then it generate Object Id.
In Azure Blob Storage
Go to Your blob storage account
Clicked Access Control(IAM)
Click Role Assignment (RBAC)
Click Add > Add Role assignment
Select Role as per your need like Storage Blob Data Reader
Click Next > Select Managed Identity > Select Member
Then Select your Subscription then App Service
Then List of Managed identity are shown > Select your App Service one which need to connect with storage
Then click on Select and then Next
Then You get the below screen. Match object id which generated in step 4 to below grid
Then Click Next > Next > Review + assign
Now In React Js Application
We can add these two Dependencies in package.json and do an npm i to install.
Now connect blob storage with DefaultAzureCredential from #azure/identity package :- when we give permission /access of one azure to another azure resource directly using service principle or managed identity then we use default azure credential then azure automatically validate them.
Code
For Import package
import { DefaultAzureCredential } from "#azure/identity";
// we're using these objects from the storage sdk - there are others for different needs
import { BlobServiceClient, BlobItem } from "#azure/storage-blob";
Create service client and container
const blobStorageClient = new BlobServiceClient(
// this is the blob endpoint of your storage acccount. Available from the portal
// they follow this format: <accountname>.blob.core.windows.net for Azure global
// the endpoints may be slightly different from national clouds like US Gov or Azure China
"https://<your storage account name>.blob.core.windows.net/",
new DefaultAzureCredential()
)
// this uses our container we created earlier
var containerClient = blobStorageClient.getContainerClient("your container name");
Get list of blob
let blobs = containerClient.listBlobsFlat();
for await (const blob of blobs) {
console.log(`Blob ${i++}: ${blob.name}`);
}
Download blob
const blobClient = containerClient.getBlobClient(blobName);
// Get blob content from position 0 to the end
// In Node.js, get downloaded data by accessing downloadBlockBlobResponse.readableStreamBody
const downloadBlockBlobResponse = await blobClient.download();
const downloaded = (
await streamToBuffer(downloadBlockBlobResponse.readableStreamBody)
).toString();
console.log("Downloaded blob content:", downloaded);
// [Node.js only] A helper method used to read a Node.js readable stream into a Buffer
async function streamToBuffer(readableStream) {
return new Promise((resolve, reject) => {
const chunks = [];
readableStream.on("data", (data) => {
chunks.push(data instanceof Buffer ? data : Buffer.from(data));
});
readableStream.on("end", () => {
resolve(Buffer.concat(chunks));
});
readableStream.on("error", reject);
});
}
For More Details, Go through the below links
Azure Storage Blob client library for JavaScript - version 12.12.0
Quickstart: Manage blobs with JavaScript SDK in Node.js

Access SSM Parameter store value in an aws amplify react js application

I have an amplify application built using React JS, I have a scenario for which I am manually storing API keys in my SSM parameter store in my AWS account. However, I want to retrieve/get those values(JSON object) based on a key from my React JS app (client side). So, I have installed the aws-sdk, the AWS JavaScript sdk, and using the below code snipped I am trying to access the ssms parameter store
const AWS = require('aws-sdk');
AWS.config.update({region:'us-east-1'});
const ssm = new AWS.SSM();
const getSecret = async (secretName) => {
console.log(`Getting secret for ${secretName}`);
const params = {
Name: secretName,
WithDecryption: true
};
const result = await ssm.getParameter(params).promise();
return result.Parameter.Value;
};
module.exports = {getSecret};
I am receiving this error on running my application and while accessing the store using the getSecret function.
Unhandled Rejection (CredentialsError): Missing credentials in config,
if using AWS_CONFIG_FILE, set AWS_SDK_LOAD_CONFIG=1
I believe that amplify configures the environment implicitly but since, the SSM Secrets manager is not supported yet by Amplify hence, I have to use the JS AWS SDK for this purpose. Can anyone help me spot the issue while configuring the service using AWS SDK? Or is there another or a better way to access parameter store from the client side?
Also, after surfing I have found a package named dotenv
Is it okay to store aws credentials in such a way?
Your code to fetch parameter store keys/values shouldn't be at client side considering security implications. It should be done at server-side and functionality can be exposed over endpoint for client-side.
You can read the credentials programmatically something like below:
var AWS = require("aws-sdk");
var credentials = new AWS.SharedIniFileCredentials({profile: 'profile name'});
AWS.config.credentials = credentials;
Refrence:
loading-node-credentials-shared
global-config-object

How to use Terraform `google_app_engine_domain_mapping` with service account?

I'm trying to create a GCP App Engine domain mapping via Terraform with the following configuration:
provider "google" {
version = "3.36.0"
region = var.region
}
resource "google_app_engine_domain_mapping" "domain_mapping" {
project = local.project_id
domain_name = var.domain_name
ssl_settings {
ssl_management_type = "AUTOMATIC"
}
depends_on = [
google_app_engine_application.backend_app
]
}
Terraform is configured to use an organization level service account for the GCP provider with the following IAM permissions (no conditions):
Billing Account User
Project Creator
Service Config Editor (I've added this thinking it would resolve the issue based on this and this doc page.)
The Google account that is the owner of the organization has verified the domain in question, yet I'm getting the following error:
Error: Error creating DomainMapping: googleapi: Error 403: Caller is not authorized to administer the domain 'testing.redacted.com'. If you own 'testing.redacted.com', you can obtain authorization by verifying ownership of the domain, or any of its parent domains, via the Webmaster Central portal: https://www.google.com/webmasters/verification/verification?domain=testing.redacted.com. We recommend verifying ownership of the largest scope you wish to use with subdomains (eg. verify 'example.com' if you wish to map 'subdomain.example.com').
I've also tried adding the service account's email as a user in the Google Search Console to the domain to no avail.
The solution is rather simple but sort of hidden in the docs. You need to add your service account email as owner of the domain.
Go here
Select the property you want
Tap the "Add an owner" button at the bottom of the page and add the email address (e.g. terraform#<PROJECT_ID>.iam.gserviceaccount.com)

Aquire Token with ADAL.Net throws Unknown User Type for Managed AD Account

I am trying to call a web (api) service using a OAuth2 token based on a AAD managed user account logged in to an AAD joined machine using ADAL.Net - specifically using this example:
https://github.com/AzureAD/azure-activedirectory-library-for-dotnet/wiki/AcquireTokenSilentAsync-using-Integrated-authentication-on-Windows-(Kerberos)
However I keep getting the exception: Unknown User Type
In my setup I have logged onto a machine inside an AAD private network with a synced AAD user account. I then run the example code using WindowsAuthentication.
After some debugging I can narrow the exception to be thrown from this method in ADAL.Net
protected internal /* internal for test only */ override async Task PreTokenRequestAsync()
{
await base.PreTokenRequestAsync().ConfigureAwait(false);
if (!SupportADFS)
{
var userRealmResponse = await _commonNonInteractiveHandler.QueryUserRealmDataAsync(Authenticator.UserRealmUriPrefix)
.ConfigureAwait(false);
if (string.Equals(userRealmResponse.AccountType, "federated", StringComparison.OrdinalIgnoreCase))
{
WsTrustResponse wsTrustResponse = await _commonNonInteractiveHandler.PerformWsTrustMexExchangeAsync(
userRealmResponse.FederationMetadataUrl,
userRealmResponse.CloudAudienceUrn,
UserAuthType.IntegratedAuth).ConfigureAwait(false);
// We assume that if the response token type is not SAML 1.1, it is SAML 2
_userAssertion = new UserAssertion(wsTrustResponse.Token, (wsTrustResponse.TokenType == WsTrustResponse.Saml1Assertion) ? OAuthGrantType.Saml11Bearer : OAuthGrantType.Saml20Bearer);
}
else
{
throw new AdalException(AdalError.UnknownUserType);
}
}
}
Since everything in my setup is managed with AAD I do not see why the user account type needs to be "federated" in order for a token to be retrieved.
So I suspect that I need to get my token in another way!?
Any help will be appreciated ;)
After investigating we found that the above code (ADAL.Net) can only be used with a federated setup.
Federation means that you have an on premise network - which holds your windows user accounts - connected to an Azure AD network - which then "federates" these accounts to Azure AD. However it would be good to have a member of the ADAL team to comment on this.
Obtaining a token for a windows user account in a pure Azure AD (Managed setup) can supposedly be done using this code:
var connString = $"RunAs=App;AppId={appId};TenantId={tenantId};AppKey={appKey};";
var azureServiceTokenProvider = new AzureServiceTokenProvider(connString2);
var accessToken = azureServiceTokenProvider.GetAccessTokenAsync(service, tenantId).Result;
which is descripbed here: https://learn.microsoft.com/en-us/azure/key-vault/service-to-service-authentication#running-the-application-using-managed-identity
Again its not that well documented so any clarity from microsoft would be good.

Connection to Azure Vault using MSI

I am trying to connect to my azure vault from a console application with using MSI
For this vault i have added my user as the Selected Principle
the code i am using to connect is
var azureServiceTokenProvider = new AzureServiceTokenProvider();
var keyVaultClient = new KeyVaultClient(new KeyVaultClient.AuthenticationCallback(azureServiceTokenProvider.KeyVaultTokenCallback));
var secret = await keyVaultClient.GetSecretAsync("https://<vaultname>.vault.azure.net/secrets/<SecretName>").ConfigureAwait(false);
I get the following exception
Microsoft.Azure.Services.AppAuthentication.AzureServiceTokenProviderException:
Parameters: Connectionstring: [No connection string specified],
Resource: https://vault.azure.net, Authority
Enable Managed Service Identity in the Configuration blade under your virtual machine.
Search for NameOfYourVM service principal and add it to your Key Vault under Access Policies. Add key/secret/certificate permissions.
On your Azure VM, run the console app.
class Program
{
// Target C# 7.1+ in your .csproj for async Main
static async Task Main()
{
var azureServiceTokenProvider = new AzureServiceTokenProvider();
var keyVaultClient = new KeyVaultClient(
new KeyVaultClient.AuthenticationCallback(
azureServiceTokenProvider.KeyVaultTokenCallback));
var secret = await keyVaultClient.GetSecretAsync(
"https://VAULT-NAME.vault.azure.net/secrets/SECRET-NAME");
Console.WriteLine(secret.Value);
Console.ReadLine();
}
}
To run locally, create your very own Azure AD application registration (Web App/Web API type to make it a confidential client), add it to Key Vault and use its client_id and client_secret when acquiring the access token —
https://learn.microsoft.com/en-us/azure/key-vault/key-vault-use-from-web-application#gettoken
As Varun mentioned in the comments, there's now a better way to get an access token when running locally without exposing a service principal —
https://learn.microsoft.com/en-us/azure/key-vault/service-to-service-authentication#local-development-authentication
To run locally.
install Azure Cli
Open Windows Powershell
write az login command (it will give an url and code )
Open Url and enter the code which is given with az login
then get the secret value like this
var secret = await keyVaultClient.GetSecretAsync("https://VAULT-NAME.vault.azure.net/secrets/SECRET-NAME");
secret.Value; //your secret.
a correct answer is already given above, here's an additional one :-)
Azure MSI applying with App Service & Vault
Enable System Assigned Managed Identity for your App Service, check Identity section under settings.
Add Policy under Vault
configure your code behind

Resources