Can't able to create linked services using Azure data factory (ADF),
I have read/write permission for linked services at ADF level.
using Microsoft.Azure.Management.ResourceManager;
using Microsoft.Azure.Management.DataFactory;
using Microsoft.Azure.Management.DataFactory.Models;
using Microsoft.IdentityModel.Clients.ActiveDirectory;
LinkedServiceResource storageLinkedService = new
LinkedServiceResource(
new AzureStorageLinkedService
{
ConnectionString = new
SecureString("DefaultEndpointsProtocol=https;AccountName=" +
storageAccount + ";AccountKey=" + storageKey)
}
);
client.LinkedServices.CreateOrUpdate(resourceGroup,
dataFactoryName, storageLinkedServiceName, storageLinkedService);
BTW I used both client credential as well as user credential
ClientCredential cc = new ClientCredential(applicationId,
authenticationKey);
var cc = new UserPasswordCredential(userName, password);
Error Response for using Client credential:
Microsoft.Azure.Management.DataFactory.Models.ErrorResponseException:
Operation returned an invalid status code 'Forbidden'
at Microsoft.Azure.Management.DataFactory.LinkedServicesOperations.
<CreateOrUpdateWithHttpMessagesAsync>d__6.MoveNext() --- End of stack
trace from previous location where exception was thrown ---
Error Response for using User credential:
System.Net.Http.HttpRequestException: Response status code does not
indicate success: 401 (Unauthorized). --->
Microsoft.IdentityModel.Clients.ActiveDirectory.AdalException:
{"error":"invalid_client","error_description":"AADSTS70002: The
request body must contain the following parameter: 'client_secret or
client_assertion'.\r\nTrace ID: 2264d637-8786-4a40-96d4-
5d27b0670300\r\nCorrelation ID: fec688c8-bb92-49c2-86d3-
1e091181fe10\r\nTimestamp: 2017-11-29 05:30:23Z","error_codes":
[70002],"timestamp":"2017-11-29 05:30:23Z","trace_id":"2264d637-8786-
4a40-96d4-5d27b0670300","correlation_id":"fec688c8-bb92-49c2-86d3-
1e091181fe10"}: Unknown error
--- End of inner exception stack trace ---
Accoring to your exception, it seems that you use the resource owner flow from a a web client. A confidential client, such as a web App client, cannot use direct user credentials.
You would need to invoke it as a public client (native client app), not as a confidential client (web app/API). Please refer to this document for more about how to use ADAL,especially the Constraints & Limitations section
No web sites/confidential clients
This is not an ADAL limitation, but an AAD setting. You can only use those flows from a native client. A confidential client, such as a web site, cannot use direct user credentials.
To access resources in your subscription, you need to assign role to the registried App.
Please have a try to use the following to get TokenCredentials, The following is the demo code to create inked services. It works correctly on my side. We also could refer to this document.
private static async Task<string> GetToken(string tenantId, string clientId, string secretKey)
{
var context = new AuthenticationContext("https://login.windows.net/" + tenantId);
ClientCredential clientCredential = new ClientCredential(clientId, secretKey);
var tokenResponse = await context.AcquireTokenAsync("https://management.azure.com/", clientCredential);
var accessToken = tokenResponse.AccessToken;
return accessToken;
}
var token = GetToken(_tenantId, _clientId, _screctKey).Result;
TokenCredentials credentials = new TokenCredentials(token);
DataFactoryManagementClient client = new
DataFactoryManagementClient(credentials) { SubscriptionId = subscriptionId };
DataFactoryManagementClient client = new DataFactoryManagementClient(credentials) { SubscriptionId = subscriptionId };
LinkedServiceResource storageLinkedService = new LinkedServiceResource(new AzureStorageLinkedService{
ConnectionString = new SecureString("DefaultEndpointsProtocol=https;AccountName=" + storageAccount + ";AccountKey=" + storageKey)});
var result =client.LinkedServices.CreateOrUpdateWithHttpMessagesAsync(resourceGroup, factoryName, storageLinkedServiceName, storageLinkedService).Result;
Related
I am trying to upload file on onedrive by using microsoft graph onedrive api.
I am using the method for authentication
Client credentials provider
https://learn.microsoft.com/en-us/graph/sdks/choose-authentication-providers?tabs=CS#client-credentials-provider
Like:
// /.default scope, and preconfigure your permissions on the
// app registration in Azure. An administrator must grant consent
// to those permissions beforehand.
var scopes = new[] { "https://graph.microsoft.com/.default" };
// Multi-tenant apps can use "common",
// single-tenant apps must use the tenant ID from the Azure portal
var tenantId = "my-tenantid";
// Values from app registration
var clientId = "YOUR_CLIENT_ID";
var clientSecret = "YOUR_CLIENT_SECRET";
// using Azure.Identity;
var options = new TokenCredentialOptions
{
AuthorityHost = AzureAuthorityHosts.AzurePublicCloud
};
// https://learn.microsoft.com/dotnet/api/azure.identity.clientsecretcredential
var clientSecretCredential = new ClientSecretCredential(
tenantId, clientId, clientSecret, options);
var graphClient = new GraphServiceClient(clientSecretCredential, scopes);
HttpPostedFileBase file = Request.Files;[0];
int fileSize = file.ContentLength;
string fileName = file.FileName;
string mimeType = file.ContentType;
Stream fileContent = file.InputStream;
var res = await graphClient.Me.Drive.Root.ItemWithPath(fileName).Content
.Request()
.PutAsync<DriveItem>(fileContent);
After executing this code then it gives an error in response.
Message: /me request is only valid with delegated authentication flow.
Inner error:
AdditionalData:
date: 2021-12-29T05:30:08
request-id: b51e50ea-4a62-4dc7-b8d2-b26d75268cdc
client-request-id: b51e50ea-4a62-4dc7-b8d2-b26d75268cdc
ClientRequestId: b51e50ea-4a62-4dc7-b8d2-b26d75268cdc
Client credential flow will generate the token on behalf the app itself, so in this scenario, users don't need to sign in first to generate the token stand for the user and then call the api. And because of the design,when you used Me in the graph SDK, your code/app don't know who is Me so it can't work. You should know the user_id first and use /users/{id | userPrincipalName} instead of /Me, in the SDK, that is graphClient.Users["your_user_id"] instead of graphClient.Me
In your scenario, there're 2 solutions, one way is using delegated authentication flow like what you said in your title, another way is get the user id before calling the graph api so that you can use Users["id"] but not Me
===================== Update=========================
I haven't finished the code yet but I found the correct solution now.
Firstly, we can upload file to one drive by this api, you may check the screenshot if this is one drive or sharepoint:
https://graph.microsoft.com/v1.0/users/user_id/drive/items/root:/testupload2.txt:/content
If it is, then the next is easy, using the code below to get an access token and send http request to calling the api:
var scopes = new[] { "https://graph.microsoft.com/.default" };
var tenantId = "tenant_name.onmicrosoft.com";
var clientId = "your_azuread_clientid";
var clientSecret = "corresponding_client_secret";
var clientSecretCredential = new ClientSecretCredential(
tenantId, clientId, clientSecret);
var tokenRequestContext = new TokenRequestContext(scopes);
var token = clientSecretCredential.GetTokenAsync(tokenRequestContext).Result.Token;
I know it's complex because the api is not the same as this one which has SDK sample, but I think it also deserves to try if they are similar.
I'm working on an application to obtain data through
OAuth of Dynamics 365
to do this use the following example
https://www.youtube.com/watch?v=Td7Bk3IXJ9s
public static async Task Auth()
{
string URL = "https://grupolg.api.crm.dynamics.com/api/data/v9.1/";
AuthenticationParameters API = AuthenticationParameters.CreateFromResourceUrlAsync(new Uri(URL)).Result;
//APP KEY, SECRET KEY
ClientCredential Creds = new ClientCredential("hidden for security", "hidden for security");
AuthenticationContext authContext = new AuthenticationContext(API.Authority);
string token = authContext.AcquireTokenAsync(API.Resource, Creds).Result.AccessToken;
using (HttpClient httpClient = new HttpClient())
{
httpClient.Timeout = new TimeSpan(0,2,0);
httpClient.DefaultRequestHeaders.Authorization =
new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer",token);
HttpResponseMessage res = await httpClient.GetAsync(URL+"/contacts$top=1");
}
}
but I get the following error:
The error is caused by the ADAL version you're using and how the authority URL is generated.
If you downgrade the Microsoft.IdentityModel.Clients.ActiveDirectory library version to 3.9.18 you'll be able to connect without problems. There's an open bug in GitHub where you can track its progress or comment to add more information to get a definitive fix for it.
I need to know exactly how to login to Azure, using c#.
I basically want to do this, but from the code:
]a link](https://learn.microsoft.com/en-us/azure/sql-database/sql-database-export)
Here is the code I copied from the internet trying to achieve this:
But I don't know how to generate the token.
SqlManagementClient managementClient = new SqlManagementClient(new TokenCloudCredentials(subscriptionId, GetAccessToken(tenantId, clientId, secretKey)));
var exportParams = new DacExportParameters()
{
BlobCredentials = new DacExportParameters.BlobCredentialsParameter()
{
StorageAccessKey = storageKey,
Uri = new Uri(baseStorageUri)
},
ConnectionInfo = new DacExportParameters.ConnectionInfoParameter()
{
ServerName = azureSqlServer,
DatabaseName = azureSqlDatabase,
UserName = adminLogin,
Password = adminPassword
}
};
var exportResult = managementClient.Dac.Export(azureSqlServerName, exportParams);
I have a GetToken function, but I have no idea where to take the
tenant + client id + secret
private static string GetAccessToken(string tenantId, string
clientId, string secretKey)
{
var authenticationContext = new
AuthenticationContext($"https://login.windows.net/{tenantId}");
var credential = new ClientCredential(clientId, secretKey);
var result =authenticationContext
.AcquireTokenAsync("https://management.core.windows.net/",
credential);
if (result == null)
{
throw new InvalidOperationException("Failed to obtain the JWT token");
}
var token = result.Result.AccessToken;
return token;
}
This question was asked before
Azure Database export with C#
but I need to see the actual code and explanation on how to get the connection info.
I need to see the actual code and explanation on how to get the connection info.
I would recommend you follow this tutorial about registering your AAD application and adding the secret key. Moreover, you could also follow Using the Azure ARM REST API – Get Access Token.
SqlManagementClient managementClient = new SqlManagementClient(new TokenCloudCredentials(subscriptionId, GetAccessToken(tenantId, clientId, secretKey)));
Based on your code, I assumed that you are using the package Microsoft.WindowsAzure.Management.Sql, if you use the TokenCloudCredentials, you may receive the following error response:
AFAIK, Microsoft.WindowsAzure.Management.Libraries requires the X509Certificate2 authentication, you need to construct the CertificateCloudCredentials for your SqlManagementClient. For uploading a management certificate under your subscription, you could follow Upload an Azure Service Management Certificate. For retrieving the X509Certificate2 instance, you could follow the code snippet under the Authenticate using a management certificate section from here.
For token-based authentication, you could use the package Microsoft.Azure.Management.Sql and construct your SqlManagementClient as follows:
var sqlManagement = new SqlManagementClient(new TokenCredentials("{access-token}"));
Moreover, you need to change the resource from https://management.core.windows.net/ to https://management.azure.com/ when invoking the AcquireTokenAsync method.
Tyring to use the below docs to get this up and running for my console app that I plan to port to Azure Functions. https://learn.microsoft.com/en-us/azure/data-lake-store/data-lake-store-get-started-net-sdk
The code below is where I'm getting stuck
CODE SNIP
// Service principal / appplication authentication with client secret / key
// Use the client ID of an existing AAD "Web App" application.
SynchronizationContext.SetSynchronizationContext(new SynchronizationContext());
var domain = "<AAD-directory-domain>";
var webApp_clientId = "<AAD-application-clientid>";
var clientSecret = "<AAD-application-client-secret>";
var clientCredential = new ClientCredential(webApp_clientId, clientSecret);
var creds = await ApplicationTokenProvider.LoginSilentAsync(domain, clientCredential);
MY IMPLEMENTATION
SynchronizationContext.SetSynchronizationContext(new SynchronizationContext());
var domain = "https://microsoft.onmicrosoft.com";
var webApp_clientId = "<my-client-id>";
var clientSecret = "<my-client-secret>";
var clientCredential = new ClientCredential(webApp_clientId, clientSecret);
var creds = await ApplicationTokenProvider.LoginSilentAsync(domain, clientCredential);
SynchronizationContext.SetSynchronizationContext(new SynchronizationContext());
_adlsClient = new DataLakeStoreAccountManagementClient(creds) { SubscriptionId = _subId };
_adlsFileSystemClient = new DataLakeStoreFileSystemManagementClient(creds);
await _adlsFileSystemClient.FileSystem.MkdirsAsync("<name-of-my-dlstore>", "tempdir");
I also made sure to give my app permissions to my specified folder in Azure Data Lake instance:
I also made sure to give all child folders in this directory the same level or permissions:
Assigned permissions to file path
When I run my implementation I get the below errors. Any advice SO?
System.AggregateException: 'One or more errors occurred.'
Inner Exception 1
AdalServiceException: AADSTS90002: Requested tenant identifier 'https:' is not valid.
Trace ID: d1718b0a-0533-4708-a311-4e1622840100
Correlation ID: a1544df2-692e-43d2-8acf-25a847956fb6
Timestamp: 2017-03-29 01:30:18Z
Inner Exception 2
WebException: The remote server returned an error: (400) Bad Request.
AS PART OF SOLUTION
Need to make sure to give root folder read, write, execute permissions, (this will trickle down to the one folder you want to give these permissions for) then remove those permissions from all other folders you don't want these permissions assigned too, making sure you select the option to remove these permissions for that sub folder and all it's kiddos/children.
AdalServiceException: AADSTS90002: Requested tenant identifier 'https:' is not valid.
That is your tenant name ,like "microsoft.onmicrosoft.com" .
WebException: The remote server returned an error: (400) Bad Request.
What is the detail error message , please refer to tutorial :
https://learn.microsoft.com/en-us/azure/data-lake-store/data-lake-store-get-started-net-sdk
I followed the detail steps to use service-to-service authentication with client secret , and successfully create a directory . Code below is for your reference :
public static async Task CreateDirectory()
{
SynchronizationContext.SetSynchronizationContext(new SynchronizationContext());
var domain = "microsoft.onmicrosoft.com";
var webApp_clientId = "client id";
var clientSecret = "client secret";
var clientCredential = new ClientCredential(webApp_clientId, clientSecret);
var creds = ApplicationTokenProvider.LoginSilentAsync(domain, clientCredential).Result;
SynchronizationContext.SetSynchronizationContext(new SynchronizationContext());
_adlsClient = new DataLakeStoreAccountManagementClient(creds) { SubscriptionId = "_subId" };
_adlsFileSystemClient = new DataLakeStoreFileSystemManagementClient(creds);
await _adlsFileSystemClient.FileSystem.MkdirsAsync("_adlsAccountName", "tempdir");
}
static void Main(string[] args)
{
CreateDirectory().Wait();
}
EDIT
You need to assign Execute permission to your app if you want to create a directory . Tenant ID and tenant name are both available ,means :
var domain = "microsoft.onmicrosoft.com";
//you could also use tenant id
var domain = "Your tenant ID";
I am using the code below to fetch user from the azure AD using the graph API, but somehow I am getting the token access issue while doing so.
static async void MakeRequest()
{
var client = new HttpClient();
var queryString = HttpUtility.ParseQueryString(string.Empty);
/* OAuth2 is required to access this API. For more information visit:
https://msdn.microsoft.com/en-us/office/office365/howto/common-app-authentication-tasks */
// Specify values for the following required parameters
queryString["api-version"] = "1.6";
// Specify values for path parameters (shown as {...})
// var uri = "https://graph.windows.net/microsoft.onmicrosoft.com/users/{v-sidmis#microsoft.com}?" + queryString;
var uri = "https://graph.windows.net/72f988bf-86f1-41af-91ab-2d7cd011db47/users?api-version=1.6";
var response = await client.GetAsync(uri);
if (response.Content != null)
{
var responseString = await response.Content.ReadAsStringAsync();
Console.WriteLine(responseString);
}
}
This code is taken up from TechNet.
It depends on how you want to acquire the token. There are lots of scenario to integrate the application with Azure AD. You can refer it from here.
For example, if you want to use the Azure AD Graph in a daemon or service application, we can use the Client Credential flow.
1 . First we need to register an web application on the portal( detail steps refer here) and grant the permission to read the directory data like figure below:
2 . And then we can get the clientId, secret, tenantId from the portal and use the code below to acquire token(need to install the Active Directory Authentication Library)
string authority = "https://login.microsoftonline.com/{tenantId}";
string clientId = "";
string secret = "";
string resrouce = "https://graph.windows.net";
var credential = new ClientCredential(clientId, secret);
AuthenticationContext authContext = new AuthenticationContext(authority);
var token = authContext.AcquireTokenAsync(resrouce, credential).Result.AccessToken;
Console.WriteLine(token);
3 . Then we can use this token to call the Azure AD Graph REST directly or we can use the graph client library for Azure AD to retrieve the users. Here is the code samples for your reference:
//use the Azure AD client library
string accessToken = "";
string tenantId = "";
string graphResourceId = "https://graph.windows.net";
Uri servicePointUri = new Uri(graphResourceId);
Uri serviceRoot = new Uri(servicePointUri, tenantId);
ActiveDirectoryClient client = new ActiveDirectoryClient(serviceRoot, async () => await Task.FromResult(accessToken));
foreach(var user in client.Users.ExecuteAsync().Result.CurrentPage)
Console.WriteLine(user.DisplayName);
//using the HTTP request
var client = new HttpClient();
var tenantId = "";
var uri = $"https://graph.windows.net/{tenantId}/users?api-version=1.6";
var token = "";
client.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("bearer", token);
var response = client.GetAsync(uri).Result;
var result = response.Content.ReadAsStringAsync().Result;
Console.WriteLine(result);
Update
The secrecy is available for the web application/web API when you create an application. Then you can generate the key by keys section like figure below. After you save the app, you can copy the secrect now.