Strange behavior when trying to connect to Azure App Configuration with .Net Framework - asp.net

I have encountered a strange behavior when trying to connect to Azure App Configuration with an Azure Identity from an ASP.Net Web Application. Current environment is .Net Framework 4.8, but the behavior is the same on 4.7.2 and 4.6.1.
I am using Microsoft.Extensions.Configuration.AzureAppConfiguration 4.0.0 and Azure.Identity 1.3.0. During local development I am using a service principal (AZURE_TENANT_ID, AZURE_CLIENT_ID and AZURE_CLIENT_SECRET defined in environment) and on Azure the system assigned ManagedIdentity of the app service.
private IConfiguration GetConfiguration(string label = null)
{
TokenCredential credential = new DefaultAzureCredential();
return new Microsoft.Extensions.Configuration.ConfigurationBuilder()
.AddAzureAppConfiguration(options =>
{
options = options.Connect(
new Uri(Environment.GetEnvironmentVariable("APP_CONFIGURATION_ENDPOINT")), credential)
.ConfigureKeyVault(kv => { kv.SetCredential(credential); });
if (!string.IsNullOrEmpty(label))
{
options.Select(KeyFilter.Any, label);
}
else
{
options.Select(KeyFilter.Any, LabelFilter.Null);
}
})
.Build();
}
This code snippet works, when executed during application startup, e.g. RouteConfig. All subsequent calls are working, also refresh does work without problems.
The problems are starting when first calling App Configuration at a later time, e.g. during a request. The code hangs without an error message. In my special situation I am not able to connect to Azure App Configuration at startup time.
When using .Net Core, everything works like a charm but currently this is not an option.
Here are the logs.
When executed during startup:
Starting IIS Express ...
Successfully registered URL "http://localhost:5000/" for site "AppConfigTest3" application "/"
Registration completed for site "AppConfigTest3"
IIS Express is running.
Enter 'Q' to stop IIS Express
Loading configuration ...
Created DefaultAzureCredential: Azure.Identity.DefaultAzureCredential
Using Azure environment
Created ManagedIdentityCredential: Azure.Identity.DefaultAzureCredential
Connected via APP_CONFIGURATION_ENDPOINT: https://my-test-appconfig.azconfig.io
Before configure KeyVault credential
After configure KeyVault credential
Before executing Build()
EscalationMonitorInterval: 300
Created DefaultAzureCredential: Azure.Identity.DefaultAzureCredential
Using Azure environment
Created ManagedIdentityCredential: Azure.Identity.DefaultAzureCredential
Connected via APP_CONFIGURATION_ENDPOINT: https://my-test-appconfig.azconfig.io
Before configure KeyVault credential
After configure KeyVault credential
Before executing Build()
Azure Config loaded
Anforderung gestartet: "GET" http://localhost:5000/
If called the first time during a request, I'm getting the following log:
Starting IIS Express ...
Successfully registered URL "http://localhost:5000/" for site "AppConfigTest3" application "/"
Registration completed for site "AppConfigTest3"
IIS Express is running.
Enter 'Q' to stop IIS Express
Azure Config loaded
Anforderung gestartet: "GET" http://localhost:5000/
Response sent: http://localhost:5000/ with HTTP status 200.0
Anforderung gestartet: "GET" http://localhost:5000/Home/About
Loading configuration ...
Created DefaultAzureCredential: Azure.Identity.DefaultAzureCredential
Using Azure environment
Created ManagedIdentityCredential: Azure.Identity.DefaultAzureCredential
Connected via APP_CONFIGURATION_ENDPOINT: https://my-test-appconfig.azconfig.io
Before configure KeyVault credential
After configure KeyVault credential
Before executing Build()
Nothing happens and the request does not return.
I'm stucked at this point. Does someone has a solution for this problem? Thank you.
Regards,
Stati

This is because of a bug introduced in Azure.Core package version 1.4.1. The bug has been fixed in Azure.Core version 1.9.0. Since you are referencing Azure.Identity version 1.3.0, you have an indirect dependency on Azure.Core version 1.6.0.
Your problem can be fixed by adding an explicit dependency on Azure.Core v1.9.0 or later.

Related

Minimal lambda throws exception when uploaded to AWS

My local dotnet version is 6.0.400 and dotnet lambda --help shows version 5.4.5.
I have scaffolded a test lambda project with dotnet new serverless.AspNetCoreMinimalAPI -n TestCalc.
If I load this in VSCode and hit F5 it runs just fine; Giving me the classic localhost:5000/calculator responses.
If I then:
Create a new Lambda in the AWS Console
Set the Function URL Auth type to NONE
Check Configure cross-origin resource sharing (CORS)
Set the handler to TestCalc
Set the TestCalc.csproj value PublishReadyToRun to false
Publish the TestCalc project locally with dotnet publish -c Release --output=publish
Zip the contents of the \TestCalc\publish folder
Upload the publish.zip file to the Lambda
And hit the function URL, I get this error logged to CloudWatch:
System.NullReferenceException: Object reference not set to an instance of an object.
at Amazon.Lambda.AspNetCoreServer.APIGatewayProxyFunction.MarshallRequest(InvokeFeatures features, APIGatewayProxyRequest apiGatewayRequest, ILambdaContext lambdaContext)
at Amazon.Lambda.AspNetCoreServer.AbstractAspNetCoreFunction`2.FunctionHandlerAsync(TREQUEST request, ILambdaContext lambdaContext)
at Amazon.Lambda.RuntimeSupport.HandlerWrapper.<>c__DisplayClass26_0`2.<<GetHandlerWrapper>b__0>d.MoveNext()
--- End of stack trace from previous location ---
at Amazon.Lambda.RuntimeSupport.LambdaBootstrap.InvokeOnceAsync(CancellationToken cancellationToken)
It appears (to me) that the function is being invoked but the AWS Lambda library is failing to receive something it expects.
How can I give it what it needs?
The generated code (plus some logging from me) is this:
var builder = WebApplication.CreateBuilder(args);
// Add services to the container.
builder.Services.AddControllers();
// Add AWS Lambda support. When application is run in Lambda Kestrel is swapped out as the web server with Amazon.Lambda.AspNetCoreServer. This
// package will act as the webserver translating request and responses between the Lambda event source and ASP.NET Core.
builder.Services.AddAWSLambdaHosting(LambdaEventSource.RestApi);
var app = builder.Build();
app.UseHttpsRedirection();
app.UseAuthorization();
app.MapControllers();
app.MapGet("/", () => "Welcome to running ASP.NET Core Minimal API on AWS Lambda");
app.Logger.LogInformation($"before app.Run()"); // appears in CloudWatch
app.Run();
app.Logger.LogInformation($"after app.Run()"); // does NOT appear in CloudWatch
After finding this github post I figured I should check the options on the LambdaEventSource and switched it to LambdaEventSource.HttpApi. This solved the problem.

Microsft Graph Helper fails in the response

I have an ASP.Net App and I recent added login with office 365 consuming Graph Api. It works fine in my local machine and I get token and user info from the Graph Api. But when I deploy the solution in production server I m getting an error in the response from the API. Production server is a virtual windows server 2016
var graphClient = new GraphServiceClient(
new DelegateAuthenticationProvider(
async (requestMessage) =>
{
requestMessage.Headers.Authorization =
new AuthenticationHeaderValue("Bearer", accessToken);
}));
var user = await graphClient.Me.Request()
.Select(u => new {
u.DisplayName,
u.Mail,
u.UserPrincipalName
})
.GetAsync();
This is the code to get the info from the user, after calling this method I get an exeption with the next error:
Method not found: 'System.Threading.Tasks.Task`1<!!0>Microsoft.Graph.BaseRequest.SendAsyc(System.Object, System.Threading.CancellationToken, System.Net.Http.HttpCompletionOption)'.
The most rare is that this happen only in the production server, in test server and local machine it works fine. Thanks in advance!
You are likely missing some dependencies. make sure in your bin folder of the app in production you see the Microsoft.Graph.Core.dll file, sometimes when you deploy things it doesn't update the packages for whatever reason. you should see such a file in your test server and local machines. if not there, copy it over. if it is there, then check that you have the same .net versions installed in prod as you have in other environments. also make sure that in your development environment you have the latest graph api sdk being used.
Finally the solution to solve this problem was updating the .Net framework version in the server.

How to specify server certificate for a self-contained Kestrel/Blazor application deployed on a production server?

I created a Blazor application to be run using Kestrel (.Net core 3.1).
Import NuGet package Microsoft.AspNetCore.Authentication.Negotiate
Add the following code in ConfigureService() in Startup.cs.
services.AddAuthentication(NegotiateDefaults.AuthenticationScheme).AddNegotiate();
services.AddSingleton<ValidateAuthentication>();
Add the following code in Configure() in Startup.cs. They are added between app.UseRouting(); and app.UseEndpoints(...;
app.UseAuthentication();
app.UseAuthorization();
app.UseMiddleware<ValidateAuthentication>();
Add the class
internal class ValidateAuthentication : IMiddleware
{
public async Task InvokeAsync(HttpContext context, RequestDelegate next)
{
if (context.User.Identity.IsAuthenticated)
await next(context);
else
await context.ChallengeAsync();
}
}
Add the following code in the Program.cs (To make sure the remote machine can access the website)
webBuilder.UseUrls(new string[] { "https://0.0.0.0:5001", "http://0.0.0.0:5000" });
I published (as self hosted) the application to a local folder and it works fine on my PC. Running .\myApp.exe and then browsing http://localhost.5000 will redirect to https://localhost:5001 and show the page.
Then the published folder was copied to a Windows 2012 Server. However, running the application gets error:
PS C:\Website\Portal> .\MyApp.exe
crit: Microsoft.AspNetCore.Server.Kestrel[0]
Unable to start Kestrel.
System.InvalidOperationException: Unable to configure HTTPS endpoint. No server certificate was specified, and the defau
lt developer certificate could not be found or is out of date.
To generate a developer certificate run 'dotnet dev-certs https'. To trust the certificate (Windows and macOS only) run
'dotnet dev-certs https --trust'.
For more information on configuring HTTPS see https://go.microsoft.com/fwlink/?linkid=848054.
at Microsoft.AspNetCore.Hosting.ListenOptionsHttpsExtensions.UseHttps(ListenOptions listenOptions, Action`1 configure
Options)
at Microsoft.AspNetCore.Hosting.ListenOptionsHttpsExtensions.UseHttps(ListenOptions listenOptions)
at Microsoft.AspNetCore.Server.Kestrel.Core.Internal.AddressBinder.AddressesStrategy.BindAsync(AddressBindContext con
text)
How to specify a server certificate for the application?
This Microsoft Docs article explains how to startup an ASP.Net Core app with certificates when it ships as a Docker container. You basically set ennvironment variables, e.g.
ASPNETCORE_URLS
ASPNETCORE_HTTPS_PORT
ASPNETCORE_Kestrel__Certificates__Default__Path
ASPNETCORE_Kestrel__Certificates__Default__Password
I guess you may do the same on the host directly but keep in mind that those settings won't be isolated and may affect other apps. Also note that you would be storing a password in an environment variable. I also found this article explaining how to configure Kestrel via the launch settings json.
I came across this question by accident. Solution is not tested. Just replying since there is no better suggestion yet.

Exception using Azure Managed Service Identity across tenants

I'm building an Azure web app for a client that will be provisioned into many other directories for their customers. This app will call a web API in my client's directory, which will then call back to another web API in the customer's directory. Something like this:
Other Customer AAD1 --------- My client AAD2
App --------------------------------> Web API 2
Web API 1 <-------------------------- Web API 2
We have been able to get the first call to work. This requires a corresponding App Registation for Web API 2 in AAD1. We figure that we could get the callback to work by following the same pattern, with a registration for Web API1 in AAD2. However, that might be a LOT of these 'proxy' registration in my client's AAD, so we're looking at alternatives.
We are exploring using Managed Service Identity, which we think will allow us to get tokens that are valid for resources in other tenants. If there's a better way, I'm certainly interested in knowing about it.
I've followed the code example from here using the Microsoft.Azure.Services.AppAuthentication library: https://learn.microsoft.com/en-us/azure/app-service/app-service-managed-service-identity#obtaining-tokens-for-azure-resources
// In Web API 2
using Microsoft.Azure.Services.AppAuthentication;
// ...
var azureServiceTokenProvider = new AzureServiceTokenProvider();
string accessToken = await azureServiceTokenProvider.GetAccessTokenAsync(
"https://<App ID URI for Web API1>");
Web API2 is configured to have a Managed Service Identity.
I'm currently running this on my local machine, and I've installed Azure CLI and I'm logged in. I've tried 'az account get-access-token', and I get a valid token.
When Web API2 tries to get the token to be able to call Web API1, I get an exception:
Parameters: Connectionstring: [No connection string specified], Resource: , Authority: . Exception Message: Tried the following 2 methods to get an access token, but none of them worked.
Parameters: Connectionstring: [No connection string specified], Resource: , Authority: . Exception Message: Tried to get token using Managed Service Identity. Unable to connect to the Managed Service Identity (MSI) endpoint. Please check that you are running on an Azure resource that has MSI setup.
Parameters: Connectionstring: [No connection string specified], Resource: , Authority: . Exception Message: Tried to get token using Azure CLI. Access token could not be acquired. ERROR: Get Token request returned http error: 400 and server response: {"error":"invalid_grant","error_description":"AADSTS65001: The user or administrator has not consented to use the application with ID '04b07795-8ddb-461a-bbee-02f9e1bf7b46' named 'Web API 1'. Send an interactive authorization request for this user and resource.\r\nTrace ID: f5bb0d4d-6f92-4fdd-81b7-e82a78720a00\r\nCorrelation ID: 04f92114-8d9d-40c6-b292-965168d6a919\r\nTimestamp: 2017-10-19 16:39:22Z","error_codes":[65001],"timestamp":"2017-10-19 16:39:22Z","trace_id":"f5bb0d4d-6f92-4fdd-81b7-e82a78720a00","correlation_id":"04f92114-8d9d-40c6-b292-965168d6a919"}
What's interesting is that there's no application with ID '04b07795-8ddb-461a-bbee-02f9e1bf7b46' in either AAD1 or AAD2. Is this a known Azure app? I thought that it might be the Service Management API, but I'm not sure.
In any case, I'm not sure of the proper way to grant permission. I've tried building different content URLs like this into my browser, but none of them seem to have done the trick:
https://login.microsoftonline.com/(AAD1 ID)/adminconsent
?client_id=(App ID)
&redirect_uri=https://localhost:44341
&resource=(App ID URI for Web API1)
&prompt=admin_consent
https://login.microsoftonline.com/(AAD1 ID)/adminconsent
?client_id=04b07795-8ddb-461a-bbee-02f9e1bf7b46
&redirect_uri=https://localhost:44341
&resource=(App ID URI for Web API1)
&prompt=admin_consent
(This last one tells me that the reply URL is incorrect; since it's not one of my apps, I can't find the reply URL)
Note that the tenant is AAD1.
Am I missing something, or am I not using this feature correctly?
Thanks in advance.
AzureServiceTokenProvider uses Azure CLI (among other options) for local development. For a scenario where a service calls an Azure Service, this works using the developer identity from Azure CLI, since Azure services allow access to both users and applications.
For a scenario where a service calls another custom service (like your scenario), you need to use a service principal for local development. For this, you have two options:
Login to Azure CLI using a service principal.
First, create a service principal for local development
https://learn.microsoft.com/en-us/cli/azure/create-an-azure-service-principal-azure-cli?view=azure-cli-latest
Then login to Azure CLI using it.
az login --service-principal -u 25922285-eab9-4262-ba61-8083533a929b --password <<pwd>> --tenant 72f988bf-86f1-41af-91ab-2d7cd011db47 --allow-no-subscriptions
Use the --allow-no-subscriptions argument since this service principal may not have access to any subscription.
Now, AzureServiceTokenProvider will get a token using this service principal for local development.
Specify service principal details in an environment variable. AzureServiceTokenProvider will use the specified service principal for local development. Please see the section Running the application using a service principal in local development environment in this sample on how to do that. https://github.com/Azure-Samples/app-service-msi-keyvault-dotnet
Note: Ths is only for local development. AzureServiceTokenProvider will use MSI when deployed to App Service.

ASP.NET Core MVC Health Check failing

I try to run a ASP.NET Core MVC Web application on the Swisscom Appcloud. But when I start the Application I get following Error-Message in the Console:
2017-01-24 14:29:53 [CELL/0] ERR Timed out after 1m0s: health check never passed.
Its looks like the Appcloud cannot check the Health of my Application. Do I need to install a Nuget-Package or something else to get this up and running?
Thanks for your effort
By default, Cloud Foundry makes a health check by trying to connect to the port which the application is exposing.
If your application is not exposing ANY port (e.g., it's not a web service with APIs and so on), then you should add the health-check-type attribute to none, as described here.
If after that you still get errors, then I suggest you to find where your application is listening to a given port. In Cloud Foundry you must listen to $PORT, which is a environment variable. You can check an example of that here.
As gsmachado has already mentioned, you must listen to a specific port.
The .NET Core buildpack configures the app web server automatically so you don’t have to handle this yourself. But you have to prepare your app in a way that allows the buildpack to deliver this information via the command line to your app.
The buildpack will start your app with the following command:
$ dotnet run --server.urls http://0.0.0.0:${PORT}
Therefore you have to add the command line as a configuration provider and then add the UseConfiguration extension to pass the configuration to the WebHostBuilder
e.g.:
var config = new ConfigurationBuilder()
.AddCommandLine(args)
.Build();
var host = new WebHostBuilder()
.UseKestrel()
.UseConfiguration(config)
.UseStartup<Startup>()
.Build();

Resources