I am trying to automate ADF pipeline runs from .NET. Was able to create a client and when I try to do the below call
var pipeline = client.Pipelines.Get(resourceGroup, dataFactoryName, "test_1");
I am getting the error as Microsoft.Rest.Azure.CloudException: 'The document could not be retrieved because it does not exist.
Complete code below:
// Authenticate and create a data factory management client
IConfidentialClientApplication app = ConfidentialClientApplicationBuilder.Create(applicationId)
.WithAuthority("https://login.microsoftonline.com/" + tenantID)
.WithClientSecret(authenticationKey)
.WithLegacyCacheCompatibility(false)
.WithCacheOptions(CacheOptions.EnableSharedCacheOptions)
.Build();
AuthenticationResult result = await app.AcquireTokenForClient(new string[] { "https://management.azure.com//.default" }).ExecuteAsync();
ServiceClientCredentials cred = new TokenCredentials(result.AccessToken);
DataFactoryManagementClient client = new DataFactoryManagementClient(cred)
{
SubscriptionId = subscriptionId
};
Loos like it was a silly mistake. pipeline, I was trying to access from here was deleted by another process and hence this call failed.
Related
I'm using MSAL for .NET to acquire tokens for my Graph API requests, but out of sudden, I'm getting following error, which I can see a lot of post about, but no solution of reason why Error 80049217 happens? Does anyone know why this error occurs and maybe a solution to avoid the error?
System.InvalidOperationException: CompactToken parsing failed with
error code: 80049217
UPDATE 22-01-10
Example of method to acquire access token (Client is instance of HttpClient reused by all threads using the factory class containing this method. _confidentialClient is an instance of IConfidentialClientApplication in the MSAL .NET library):
private IConfidentialClientApplication _confidentialClient;
public void Initialize()
{
// Construct the ConfidentialClientApplication
_confidentialClient =
ConfidentialClientApplicationBuilder.Create("clientId")
.WithClientSecret("clientSecret")
.WithAuthority("authority")
.Build();
}
// Multiple threads will access this method
private async Task GetAccessToken()
{
try
{
Console.WriteLine("Acquire token....");
// Is the .AcquireTokenForClient method thread safe??
var result = await _confidentialClient.AcquireTokenForClient("https://graph.microsoft.com/.default").ExecuteAsync();
if(Client.DefaultRequestHeaders.Authorization?.Parameter == result.AccessToken)
{
Console.WriteLine("Token havn't changed.");
return;
}
Client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", result.AccessToken);
Console.WriteLine("Acquire token successfully!");
}
catch (Exception e)
{
Console.WriteLine(e);
}
}
All threads interacting with Graph API by this factory class will start by calling the GetAccessToken method to make sure the HttpClient has a valid AccessToken in the Authorization header. As far as I have read about IConfidentialClientApplication, the AcquireTokenForClient() will look for valid tokens in the internal cache, and if there isn't any, acquiring a new one, which is why the method is always invoked by any thread.
UPDATE 22-01-13:
Added some logic of how the IConfidentialClientApplication is built.
It seemed that you wrote this code in your asp.net core backend project, and you wanna a method to help generate access token for different scopes without entering user name/password to sign in, so that it can serve different scenario. But you made a mistake here.
See this document first. In a server/daemon application, you can only use client credential flow to generate access token, so the scope for graph api should be https://graph.microsoft.com/.default, and this section provides the sample code to use client credential flow in your asp.net core app. Here's the snippet.
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 = "common";
// 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);
public static void Main(string[] args)
{
TicketSync ts = new TicketSync();
ts.ScanMailBox();
}
private async void ScanMailBox()
{
if (string.IsNullOrWhiteSpace(_mailboxToOpen)) return;
ExchangeService service = new ExchangeService();
try
{
// Using Microsoft.Identity.Client 4.22.0
// Configure the MSAL client to get tokens
var pcaOptions = new PublicClientApplicationOptions
{
ClientId = "*clientGuid*",
TenantId = "*TenantGuid*"
};
var pca = PublicClientApplicationBuilder
.CreateWithApplicationOptions(pcaOptions).Build();
// The permission scope required for EWS access
var ewsScopes = new string[] { "https://outlook.office365.com/EWS.AccessAsUser.All" };
NetworkCredential credentials = new NetworkCredential("***#***.eu", "*******");
AuthenticationResult authResult = await pca.AcquireTokenByUsernamePassword(ewsScopes, credentials.UserName, credentials.SecurePassword).ExecuteAsync();
when i get to that last line my debugger just stops running, no error, even though there is a try catch around it all, even tried adding a try catch around that line solely, but nothing works. Any idea why this is happening?
application is an automated service that would have to check a mailbox periodically using set credentials. i followed these solutions so far:
https://learn.microsoft.com/en-us/exchange/client-developer/exchange-web-services/how-to-authenticate-an-ews-application-by-using-oauth
How can I access a mailbox with restricted permissions through EWS without interactive login?
okay, i figured it out myself. I added this to the code:
var task = pca.AcquireTokenByUsernamePassword(ewsScopes, credentials.UserName, credentials.SecurePassword).ExecuteAsync();
while(!task.IsCompleted)
{
task.Wait(10000);
}
var authResult = task.Result;
this gave me the actual error at first, altho the website stated admin consent was not necessary for user access, it was necessary. hooray.
after fixing this error it worked. So the wait was necessary for it to work and to show the error
I'm trying to call an Azure Machine Learning Pipeline Endpoint I've set up using C# & the Machine Learning REST api.
I am certain that I have the Service Principal configured correctly, as I can successfully authenticate & hit the endpoint using the azureml-core python sdk:
sp = ServicePrincipalAuthentication(
tenant_id=tenant_id,
service_principal_id=service_principal_id,
service_principal_password=service_principal_password)
ws =Workspace.get(
name=workspace_name,
resource_group=resource_group,
subscription_id=subscription_id,
auth=sp)
endpoint = PipelineEndpoint.get(ws, name='MyEndpoint')
endpoint.submit('Test_Experiment')
I'm using the following example in C# to attempt to run my endpoint: https://learn.microsoft.com/en-us/azure/machine-learning/how-to-deploy-pipelines#run-a-published-pipeline-using-c
I'm attempting to fill auth_key with the following code:
var clientId = Environment.GetEnvironmentVariable("AZURE_CLIENT_ID");
var clientSecret = Environment.GetEnvironmentVariable("AZURE_CLIENT_SECRET");
var tenantId = Environment.GetEnvironmentVariable("AZURE_TENANT_ID");
var cred = new ClientSecretCredential(tenantId, clientId, clientSecret);
var auth_key = cred.GetToken(new Azure.Core.TokenRequestContext(new string[] {".default" }));
I receive a 401 (unauthorized).
What am I am doing wrong?
UPDATE *
I changed the 'scopes' param in the TokenRequestContext to look like:
var auth_key = cred.GetToken(new Azure.Core.TokenRequestContext(new string[] { "http://DataTriggerApp/.default" }));
http://DataTriggerApp is one of the servicePrincipalNames that shows up when i query my Service Principal from the azure CLI.
Now, when I attempt to use the returned token to call the Machine Learning Pipeline Endpoint, I receive a 403 instead of a 401. Maybe some progress?
Ok, through a lot of trial-and-error I was able to come up with two ways of acquiring a token that allows me to hit my Azure Machine Learning Pipeline Endpoint through the REST api. One uses Microsoft.Identity.Client & one uses Azure.Identity.
using Microsoft.Identity.Client;
...
public static async Task<string> GetAccessToken()
{
var clientId = Environment.GetEnvironmentVariable("AZURE_CLIENT_ID");
var clientSecret = Environment.GetEnvironmentVariable("AZURE_CLIENT_SECRET");
var tenantId = Environment.GetEnvironmentVariable("AZURE_TENANT_ID");
var app = ConfidentialClientApplicationBuilder.Create(clientId)
.WithClientSecret(clientSecret)
.WithAuthority(AzureCloudInstance.AzurePublic, tenantId)
.Build();
var result = await app.AcquireTokenForClient(new string[] { "https://ml.azure.com/.default" }).ExecuteAsync();
return result.AccessToken;
}
Or:
using Azure.Identity;
...
public static async Task<string> GetAccessToken()
{
var clientId = Environment.GetEnvironmentVariable("AZURE_CLIENT_ID");
var clientSecret = Environment.GetEnvironmentVariable("AZURE_CLIENT_SECRET");
var tenantId = Environment.GetEnvironmentVariable("AZURE_TENANT_ID");
var cred = new ClientSecretCredential(tenantId, clientId, clientSecret);
var token = await cred.GetTokenAsync(new Azure.Core.TokenRequestContext(new string[] { "https://ml.azure.com/.default" }));
return token.Token;
}
I am using azure graph service client to add/update users and groups in Office365.
I relied on its exceptions which were of type ServiceException and contained status code and message, which I used to determine what to do next with this result.
It worked well, because when I tried to retrieve user I simply used graphClient.Users[email].Request().GetAsync() and I got ServiceException with status of 'NotFound' I knew that user does not exist and what should I do about.
Unfortunately now it has changed and when user does not exist I get System.Exception with following message Object reference not set to an instance of an object.
I'm guessing that it still means that user was not found, but it could also mean something different so my custom logic might break if I catch it and treat it like user does not exist.
Does anybody know why it's like that? Did anything change lately with exceptions from graph client or is it temporar bug on graph service client side?
// UPDATE
Here's the code I used to create graph service client and run the method to retrieve a user:
var confidentialClientApplication = ConfidentialClientApplicationBuilder
.Create("client_id")
.WithTenantId("tenant_id")
.WithClientSecret("client_secret")
.Build();
var scopes = new[] { "https://graph.microsoft.com/.default" };
AuthenticationResult result = null;
try
{
result = await confidentialClientApplication.AcquireTokenForClient(scopes).ExecuteAsync();
}
catch(Exception)
{
// error handling
}
var graphClient = new GraphServiceClient(
new DelegateAuthenticationProvider(
async (requestMessage) =>
{
requestMessage.Headers.Authorization = new AuthenticationHeaderValue("bearer", result.AccessToken);
}));
var user = graphClient.Users["email"].Request().GetAsync();
I want to read a users mail from a .net console app without user interaction.
I would like to give the app access to read only selected user(s) mail and not as global admin that can read all users mail.
I would like to use the .net Microsoft.Graph library and not raw REST interface.
I think i need more or less step-by-step instructions i this i seven possible
I have created an new application registration and a client secret
If i give Application permission to mail it works but i cant get delegated permission to work.
The code is just one of many i have tried , but i cant really find any that do what i want to do.
var tenantId = "domain123.onmicrosoft.com";
var client_Id = "1234567789";
var client_Secret = "123243456777";
var scopes = new[] { "https://graph.microsoft.com/.default" };
// Configure app builder
var authority = $"https://login.microsoftonline.com/{tenantId}";
var app = ConfidentialClientApplicationBuilder
.Create(client_Id)
.WithClientSecret(client_Secret)
.WithAuthority(new Uri(authority))
.WithLogging(MyLoggingMethod, LogLevel.Verbose,
enablePiiLogging: true,
enableDefaultPlatformLogging: true)
.Build();
// Acquire tokens for Graph API
var authenticationResult = await app.AcquireTokenForClient(scopes).ExecuteAsync();
// Create GraphClient and attach auth header to all request (acquired on previous step)
var graphClient = new GraphServiceClient(
new DelegateAuthenticationProvider(requestMessage =>
{
requestMessage.Headers.Authorization =
new AuthenticationHeaderValue("bearer", authenticationResult.AccessToken);
return Task.FromResult(0);
}));
// Call Graph API
var user = await graphClient.Users["user123#domain123.onmicrosoft.com"].Messages.Request().GetAsync();
Code: NoPermissionsInAccessToken
Message: The token contains no permissions, or permissions can not be understood.
Inner error
Can I suggest rather than trying to roll your own DelegateAuthenticationProvider, that you use one of the provided ones? e.g.
IConfidentialClientApplication confidentialClientApplication = ConfidentialClientApplicationBuilder
.Create(clientId)
.WithTenantId(tenantID)
.WithClientSecret(clientSecret)
.Build();
ClientCredentialProvider authProvider = new ClientCredentialProvider(confidentialClientApplication);
var graphClient = new GraphServiceClient(authProvider);
Docs: https://learn.microsoft.com/en-us/graph/sdks/choose-authentication-providers?tabs=CS#ClientCredentialsProvider
Nuget: https://www.nuget.org/packages/Microsoft.Graph.Auth/1.0.0-preview.1
I did try this earlier with success but since the comment on UsernamePasswordProvider is NOT RECOMMENDED I was hoping for a better solution.
To get this to work I did following steps
Create new App registration with redirect as public client/native = myapp://auth
Go into Authentication and set "Treat application as public client" = Yes
To allow app to access user data, go to:
https://login.microsoftonline.com/[tenant]/oauth2/v2.0/authorize?client_id=[client_id]&response_type=code&redirect_uri=myapp://auth&response_mode=query&scope=user.read&state=12345
Login with user that you want to use in app , this page will hand but it ok.
Is this really the best way to do what i want?
static async Task Main(string[] args)
{
var GraphClient = CreateGraphClient();
User me = await GraphClient.Me.Request()
.WithUsernamePassword("user123#domain123.onmicrosoft.com", new NetworkCredential("", "MyPassword").SecurePassword)
.GetAsync();
Console.WriteLine("OK:" + me.DisplayName);
Console.ReadLine();
}
public static GraphServiceClient CreateGraphClient()
{
string clientId = "1234567-1234-1234-12345-1234567890";
string tenantID = "domain123.onmicrosoft.com";
IPublicClientApplication publicClientApplication = PublicClientApplicationBuilder
.Create(clientId)
.WithTenantId(tenantID)
.Build();
UsernamePasswordProvider authProvider = new UsernamePasswordProvider(publicClientApplication, null);
GraphServiceClient graphClient = new GraphServiceClient(authProvider);
return graphClient;
}
Another solution could be to make an application permission and then set access policy using PowerShell new-applicationaccesspolicy
https://learn.microsoft.com/en-us/powershell/module/exchange/organization/new-applicationaccesspolicy?view=exchange-ps
I have not tried that one yet, anyone know if this could help?