Azure Table Storage query failed with authentication error (Received:Forbidden) from time to time - azure-web-app-service

Environment: Azure app service.
Azure storage SDK: WindowsAzure.Storage (9.3.3)
Invocation (pseudo code):
void QueryAzureTable(){
while(true){
var tableClient = new AzureTable();
var resp = tableClient.Query('table','pk','rk');
// ...
}
}
var tasks = new List<Task>();
for (var i = 0; i < 5; i++)
{
tasks.Add(QueryAzureTable());
}
await Task.WhenAll(tasks).ConfigureAwait(false);
Authorization method for QueryAzureTable: tried with both clientId/secret and managed identity/MSI, same result for both.
Observation:
Around half of the requests failed due to anth issue in QueryAzureTable() (see detailed error msg below).
If I restart the azure app service instance, the auth error will be gone for ~12 hours.
Error Message:
Server failed to authenticate the request. Make sure the value of Authorization header is formed correctly including the signature.
Unexpected response code, Expected:OK or NotFound, Received:Forbidden
I have checked and tried with almost every solutions mentioned in this stackoverflow thread, but no luck.
Guess this specific auth issue might be related with multi-tasks.

Kind of figured out the solution on my own: adding a retry logic to renew the token.
void query(...){
int cnt=0;
while(true){
try{
_client.queryTable(...);
}
catch(AuthException ex){
log.error(ex...);
var token=new Token(...);
_client = new AzureTableClient(token);
cnt++;
if(cnt==3) throw;
}
}
}
The first clue to this solution was whenever there was a app service release, deployment or restart of the app service, the query table function worked well for a while, and then after around 12 hours, errors started showing up. But not 100% failure rate.
If there is any explanation or conclusion that helps to root cause this, please share your opinions. Thanks in advance!
My blind guess is that it has something to do with muti-tasks: WindowsAzure.Storage (9.3.3) does not do a good job of renewing token for muti-tasks.
Hope this could help you.

Related

My Azure Function Intermittantly fails to read the connection string

I have an azure function that runs off of a queue trigger. The repository has method to grab the connection string from the ConnectionStrings collection.
return System.Configuration.ConfigurationManager.ConnectionStrings["MyDataBase"].ToString();
This works great for the most part but I see intermittently that this returns a null exception error.
Is there a way I can make this more robust?
Do azure functions sometimes fail to get the settings?
Should I store the setting in a different section?
I also want to say that this runs thousands of times a day but I see this popup about a 100 times.
Runtime version: 1.0.12299.0
Are you reading the configuration for every function call? You should consider reading it once (e.g. using a Lazy<string> and static) and reusing it for all function invocations.
Maybe there is a concurrency issue when multiple threads access the code. Putting a lock around the code could help as well. ConfigurationManager.ConnectionStrings should be tread-safe, but maybe it isn't in the V1 runtime.
A similar problem was posted here, but this concerned app settings and not connection strings. I don't think using CloudConfigurationManager should be the correct solution.
You can also try putting the connection string into the app settings, unless you are using Entity Framework.
Connection strings should only be used with a function app if you are using entity framework. For other scenarios use App Settings. Click to learn more.
(via Azure Portal)
Not sure if this applies to the V1 runtime as well.
The solution was to add a private static string for the connection string. Then only read from the configuration if it fails. I then added a retry that paused half a second. This basically removed this from happening.
private static string connectionString = String.Empty;
private string getConnectionString(int retryCount)
{
if (String.IsNullOrEmpty(connectionString))
{
if (System.Configuration.ConfigurationManager.ConnectionStrings["MyEntity"] != null)
{
connectionString = System.Configuration.ConfigurationManager.ConnectionStrings["MyEntity"].ToString();
}
else
{
if (retryCount > 2)
{
throw new Exception("Failed to Get Connection String From Application Settings");
}
retryCount++;
getConnectionString(retryCount);
}
}
return connectionString;
}
I don't know if this perfect but it works. I went from seeing this exception 30 times a day to none.

Outbound TCP Connection issue cause be sending data to event hub and data lake from azure function

I'm working on a Azure function with http POST trigger, once client call it and post a json data, I will send it to event hub and save to data lake.
once it got hitted by the high traffic, 20k/hour, azure functino will generate high outbound TCP connection, which will exceed the limitation (1920) of the plan.
does high outbound TCP connection cause by writing to event hub, data lake, or both?
is there a way to reduce it so I don't have to pay more to upgrade our plan?
how to debug it to trouble shooting the problem?
here is the code of send data to event hub:
EventHubClient ehc = EventHubClient.CreateFromConnectionString(cn);
try
{
log.LogInformation($"{CogniPointListener.LogPrefix}Sending {batch.Count} Events: {DateTime.UtcNow}");
await ehc.SendAsync(batch);
await ehc.CloseAsync();
}
catch (Exception exception)
{
log.LogError($"{CogniPointListener.LogPrefix}SendingMessages: {DateTime.UtcNow} > Exception: {exception.Message}");
throw;
}
here is the send data to data lake:
var creds = new ClientCredential(clientId, clientSecret);
var clientCreds = ApplicationTokenProvider.LoginSilentAsync(tenantId, creds).GetAwaiter().GetResult();
// Create ADLS client object
AdlsClient client = AdlsClient.CreateClient(adlsAccountFQDN, clientCreds);
try
{
using (var stream = client.CreateFile(fileName, IfExists.Overwrite))
{
byte[] textByteArray = Encoding.UTF8.GetBytes(str);
stream.Write(textByteArray, 0, textByteArray.Length);
}
// Debug
log.LogInformation($"{CogniPointListener.LogPrefix}SaveDataLake saved ");
}
catch (System.Exception caught)
{
string err = $"{caught.Message}Environment.NewLine{caught.StackTrace}Environment.NewLine";
log.LogError(err, $"{CogniPointListener.LogPrefix}SaveDataLake");
throw;
}
Thanks,
I just raised an issue with Azure SDK https://github.com/Azure/azure-sdk-for-net/issues/26884 reporting the problem of socket exhaustion when using ApplicationTokenProvider.LoginSilentAsync.
The current version 2.4.1 of Microsoft.Rest.ClientRuntime.Azure.Authentication uses the old version 4.3.0 of Microsoft.IdentityModel.Clients.ActiveDirectory that creates a new HttpClientHandler on every call.
Creating HttpClientHandler on every is bad. After HttpClientHandler is disposed, the underlaying socket connections are still active for significant time (in my experience 30+ seconds).
There's a thing called HttpClientFactory that ensures HttpClientHandler is not created frequently. Here's a guide from Microsoft explaining how to use HttpClient and HttpClientHandler properly - Use IHttpClientFactory to implement resilient HTTP requests.
I wish they reviewed their SDKs to ensure they follow their own guidelines.
Possible workaround
Microsoft.IdentityModel.Clients.ActiveDirectory since version 5.0.1-preview supports passing a custom HttpClientFactory.
IHttpClientFactory myHttpClientFactory = new MyHttpClientFactory();
AuthenticationContext authenticationContext = new AuthenticationContext(
authority: "https://login.microsoftonline.com/common",
validateAuthority: true,
tokenCache: <some token cache>,
httpClientFactory: myHttpClientFactory);
So it should be possible to replicate what ApplicationTokenProvider.LoginSilentAsync does in your codebase to create AuthenticationContext passing your own instance of HttpClientFactory.
The things you might need to do:
Ensure Microsoft.IdentityModel.Clients.ActiveDirectory with version of after 5.0.1-preview is added to the project
Since the code is used in Azure functions, HttpClientFactory needs to be set up and injected. More info can be found in another StackOverflow answer
Replace calls ApplicationTokenProvider.LoginSilentAsync(tenantId, creds) with something like that (this code is an inlined version of LoginSilentAsync that passes httpClientFactory to AuthenticationContext
var settings = ActiveDirectoryServiceSettings.Azure;
var audience = settings.TokenAudience.OriginalString;
var context = new AuthenticationContext(settings.AuthenticationEndpoint + domain,
settings.ValidateAuthority,
TokenCache.DefaultShared,
httpClientFactory);
var authenticationProvider = new MemoryApplicationAuthenticationProvider(clientCredential);
var authResult = await authenticationProvider.AuthenticateAsync(clientCredential.ClientId, audience, context).ConfigureAwait(false);
var credentials = new TokenCredentials(
new ApplicationTokenProvider(context, audience, clientCredential.ClientId, authenticationProvider, authResult),
authResult.TenantId,
authResult.UserInfo == null ? null : authResult.UserInfo.DisplayableId);
I really don't replicating the logic in the workaround, but I don't think there's any other option until it's fixed properly in Microsoft.Rest.ClientRuntime.Azure.Authentication
Good luck!
TCP connections are limited in specific numbers depending on the plan you have your functions on (Consumption or a static plan in any level B/S/P).
For high workloads I prefer to either
A: Use a queue with a separate function and limiting the concurrency by the function batch size and other settings
or
B: Use a SemaphoreSlim in order to control concurrency of outgoing traffic. (https://learn.microsoft.com/de-de/dotnet/api/system.threading.semaphoreslim?redirectedfrom=MSDN&view=netframework-4.7.2)

403 when calling API from Azure App Service

I have a strange problem. I have a .NET Core App which works fine on local machine and passes unit tests.
Inside the app it basically calls our platform web service:
using( WebClient client = new WebClient() )
{
NetworkCredential creds = new NetworkCredential(_userName, _password);
CredentialCache credCache = new CredentialCache();
credCache.Add(new System.Uri(_baseUrl), "Basic", creds);
client.Credentials = credCache;
var url = _baseUrl + "/api/v1/Pricing/Rates";
client.Headers.Add(HttpRequestHeader.ContentType, "application/json");
var request = JsonConvert.SerializeObject(data);
System.Console.Out.WriteLine(request);
var response = client.UploadString(url, request);
var responseObject = JObject.Parse(response);
var products = responseObject["PricingProducts"].Children();
var result = new Dictionary<string, double>();
foreach( var product in products )
{
result.Add(product.Value<string>("LoanProgramName"),
product.Value<double>("Rate"));
}
return result;
}
When I execute this on local machine using dotnet run, everything works fine. Unit tests work great too. The logs on the App Service don't tell me much except that I am getting a 403 from the platform web service.
ers.RatesController.Get (AlexaRates) with arguments ((null)) - ModelState is Valid
2018-02-24 06:37:44.418 +00:00 [Information] Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker: Executed action AlexaRates.Controllers.RatesController.Get (AlexaRates) in 201.3483ms
2018-02-24 06:37:44.447 +00:00 [Error] Microsoft.AspNetCore.Server.Kestrel: Connection id "0HLBRA4B41EO8", Request id "0HLBRA4B41EO8:00000002": An unhandled exception was thrown by the application.
System.Net.WebException: The remote server returned an error: (403) Forbidden.
at System.Net.HttpWebRequest.GetResponse()
at System.Net.WebClient.GetWebResponse(WebRequest request)
at System.Net.WebClient.DownloadBits(WebRequest request, Stream writeStream)
at System.Net.WebClient.UploadBits(WebRequest request, Stream readStream, Byte[] buffer, Int32 chunkSize, Byte[] header, Byte[] footer)
at System.Net.WebClient.UploadDataInternal(Uri address, String method, Byte[] data, WebRequest& request)
at System.Net.WebClient.UploadString(Uri address, String method, String data)
at Rates.RetrieveLatest() in D:\home\site\repository\AlexaRates\Rates.cs:line 50
at AlexaRates.Controllers.RatesController.Get() in D:\home\site\repository\AlexaRates\Controllers\RatesController.cs:line 22
at lambda_method(Closure , Object , Object[] )
at Microsoft.Exten
Has anyone experienced anything similar? I see a bunch 403 posts, but they are mostly about people calling a REST API hosted on the service not calling out.
The 403 forbidden error usually means the server understood the request but refuses to authorize it.
According to your error message, it seems that the error happens in Rates class and RatesController class, which you haven’t showed for us. You could set a break point to check the code in these classes by using remote debugging.
You say the project is working fine locally, but get error in Azure, so please make sure you have published all your projects and data sources to Azure. Check whether the ‘_baseUrl ‘ is from Azure. And make sure you have started the Azure App Service.
There may be other causes of 403 forbidden error. Such as page cache and logging in of cookie. You could refer to this article to learn how to fix the 403 Forbidden Error.
Cause of 403 Forbidden Errors
403 errors are almost always caused by issues where you're trying to access something that you don't have access to.
My fix was that I realized that our infrastructure guys added a IP restriction on the azure app. That is why the app was bouncing back with a 403.
I removed the IP restrictions on the "Networking" -> "Access Restrictions" page.
After trying to add headers and doing various other things the end result was the same - I was getting a 403 error on calling out to a web service.
The solution was to convert from a Web App to a VM and deploy the application there using the old school setup. The application worked there.

Azure WebJob with queues and System.Threading.Timer

I'm using Azure WebJob to get messages from a service bus queue with success.
But i wanted to use this same WebJob to run some methods every 5 seconds.
I've tried the following approach, and locally it run fine, but when i publish it only runs once.
No errors on azure logs.
What am i doing wrong?
Thanks for helping.
static void Main()
{
try
{
var testTimer = new System.Threading.Timer(e => TestMethod(), null, TimeSpan.FromSeconds(0), TimeSpan.FromSeconds(5));
SetupJobHost();
}
catch (Exception ex)
{
Console.WriteLine(ex);
}
}
private static void TestMethod()
{
Console.WriteLine("Test");
}
I recommend taking a different approach and using a TimerTrigger. You can use a simple chron expression that will cause your method to be executed on a set schedule. If you go this route, make sure that you deploy your WebJob as a triggered job (not continuous!) and that you call the JobHostConfiguration's UseTimers() method before calling the JobHost's RunAndBlock method. This is a much easier and cleaner approach than rolling your own timer service.
According to your description, I have tested your code and reproduced on my side.
After some trials, I found a problem with the class System.Threading.Timer, if we don’t refer to the Timer instance after the initial assignment, then it will get garbage collected.
Please try below methods to see whether it helps:
Method 1: Deploy your webjob in Debug Mode without changing any code;
Method 2: Change your code as follows and deploy it to Azure in Release Mode.
try
{
var testTimer = new System.Threading.Timer(e => TestMethod(), null, TimeSpan.FromSeconds(0), TimeSpan.FromSeconds(5));
SetupJobHost();
testTimer.Dispose();
}
catch (Exception ex)
{
Console.WriteLine(ex);
}
I recommend you to read this article for a better understanding of this interesting issue.
In addition, you could achieve your purpose by using the following code:
System.Timers.Timer sysTimer = new System.Timers.Timer(TimeSpan.FromSeconds(5).TotalMilliseconds);
sysTimer.Elapsed += (s, e) =>TestMethod();
sysTimer.Enabled = true;

Azure Service Bus Request Time Out Issue

I am getting the time out error, when I called Azure Service Bus with the below code.
So I have applied retry logic, however still I am getting the time out error.
var msgFactory = MessagingFactory.CreateFromConnectionString(connection);
var namespaceManager = NamespaceManager.CreateFromConnectionString(connection);
if (!await namespaceManager.QueueExistsAsync(queueName)) //Time out Error
{///Code}
The request has timed out after 60000 milliseconds. The successful completion of the request cannot be determined. Additional queries should be made to determine whether or not the operation has succeeded. TrackingId:143b4d25-e97c-4270-8714-93a4c6818fea,TimeStamp:1/19/2016 8:30:36 PM
Maybe just because a pair of parentheses missing in:
if (!await namespaceManager.QueueExistsAsync(queueName))
which should be:
if (!(await namespaceManager.QueueExistsAsync(QueueName)))
We need to run the pregame outside of the company firewall, then it will work.

Resources