CORS Issue with Azure Functions inside AKS Cluster - c#

I have a AKS Cluster in Azure which is running my Azure functions project. I got it working by following this guide.
https://markheath.net/post/azure-functions-aks-keda
The service is running, however any requests from my site fail with a CORS error. If you notice on the guide the CORS option is set to * in the local.settings.json file.
I noticed that azure functions does not seem to read the local.settings.json or settings.json files when running inside a container.
I am not sure why but to get it running locally I had to set the connection strings as environment variables.
It looks like the func kubernetetes deploy --dry-run > deploy.yml does the same, as the yaml looks something like this:
data:
AzureWebJobsStorage: ConnectionStringHere
AzureSignalRConnectionString: ConnectionStringHere
AzureBlobStorage: ConnectionStringHere
FUNCTIONS_WORKER_RUNTIME: ZG90bmV0
FUNCTIONS_V2_COMPATIBILITY_MODE: dHJ1ZQ==
apiVersion: v1
kind: Secret
metadata:
name: my-app-live
namespace: default
---
apiVersion: v1
Note, there is no reference to CORS in there at all, even against the LoadBalancer.
I have done some research and it looks like others change the load balancer to nginx as a reverse proxy to deal with this. I am not sure this an option for me or what the repercussions would be as this is using DurableFunctions and KEDA for scaling and I don't want to do anything that might break that functionality.
The FunctionApp is written in C#
I am very new to Kubernetes so please give as much detail as possible if you can help.

Related

How to get access to kubernetes cluster from containerized application running inside node hosted on same cluster?

I am using kubernetes client library for c#. This method should ideally provide me with k8s config of the cluster it is running inside
var k8sConfig = KubernetesClientConfiguration.InClusterConfig();
Inside docker container when this run it gives
k8s.Autorest.HttpOperationException: Operation returned an invalid status code 'Forbidden'
Expected behaviour is to get cluster inside application so as to use its configmaps and secrets.
Is there any other method to do this or are there any pre-requisites to use this method?
Please note: I am using token login from web UI for cluster dashboard
If you really want to use the configmap values you can mount it to your pod as shown here.(same for secret)
https://kubernetes.io/docs/tasks/configure-pod-container/configure-pod-configmap/
Then these would be available to your pod as env variables.
https://github.com/kubernetes-client/csharp/discussions/891
I asked over github too, which was answered by moderators. And this is what we need to do here. Keep "clusterrolbinding" concept in mind.

What's the correct way to set Blazor Webassembly environment?

My goal is to be able to change the environment name from configuration file. If the server is configured for X, then the client should also use X environment.
You know, in ASP.NET we can use launchSettings.json to configure IIS server to Development, and the "real" server which is published to Production. The WASM client should see the same configuration. I would like to configure the environment only here to reduce the resik of forgetting something when publishing the server.
There are multiple articles describing Blazor environments, but I'm confused setting it up correctly.
To get the environment saw by the client, I added this line to the Client/Program.cs file:
Console.WriteLine("Using environment " + builder.HostEnvironment.Environment);
Experiemets:
If I change the Server/launchSettings.json/profiles/IIS Express/environmentVariables/ASPNETCORE_ENVIRONMENT from Development to Production, the website simply won't load (returns 404).
In some other cases, ASPNETCORE_ENVIRONMENT is just ignored / not accessible by the client.
When running an app locally, the environment defaults to Development. When the app is published, the environment defaults to Production.
That's true sometimes for me, but not always. Some cases the environment is Production even if I run locally.
Blazor.start({ environment: "Staging" }); described in this article has no effect. Also that takes a string, how could I pass the server side config variable's value?
Using web.config, I can always overwrite the environment name. There's two problems:
Why maintain another configuration file? There's already launchSettings.json, and the server will depend on that file. As far as I know, I can't reference other settings from the web.config file.
It's really not the blazor-environment header which controls the environment name. Why?
Remove web.config
In Server/Startup.cs, add:
app.Use(async (context, next) =>
{
context.Response.Headers.Add("blazor-environment", env.EnvironmentName);
await next.Invoke();
});
In Chrome DevTools, I can see the value of the header is indeed Development. But the client prints Production.
If the header is ignored, why would I set it in web.config? It it's set, but the clinet WASM project doesn't read the header, how else it knowns the name? Is the referenced Blazor WASM JavaScript file changed, the environment name is compiled into that?
There is also Client/launchSettings.json generated, which seem to be completely ignored.
There are at least 10 ways described to get the environment name. Some use appsettings.json (which is separately download, or IWebAssemblyHostEnvironment, or IConfiguration, or Razor component ect. All I want is to read the global environment name in C# code parts.
I guess this chaos comes from the fact Blazor was changed in the recent years. I'm a bit lost. Which one should I use? Which should always work? Can I get rid of the need to change both web.config and launchSettings.json?
If your wasm is hosted by an asp.net core app, the call to
app.UseBlazorFrameworkFiles();
should add the headers required automatically. You can check this by looking at the headers passed back in a network monitor for the blazor.boot.json file, which is the request that blazor uses to read the blazor-environment header from.
Note that in the sample where you manually added that header, if you didn't add it before the 'AddStaticFiles', that header probably would have been added to your index page, but NOT the blazor.boot.json file, so it would have had no effect.
One other thing to take note of is that the call to app.UseBlazorFrameworkFiles() MUST go before app.UseStaticFiles(). That one tripped me up, hopefully that helps anyone who comes in here searching for an answer :)
ref:
code where blazor wasm reads header: https://github.com/dotnet/aspnetcore/blob/5fed81205944c528cbf7bb3a4ce369ca37bd3676/src/Components/Web.JS/src/Platform/BootConfig.ts#L29
I'm using Blazor Server and not Blazor Web Assembly, but I'm thinking you can do what I do and just set the environment in the ASPNETCORE_ENVIRONMENT variable. When debugging in Visual Studio, set the variable in the launchSettings.json file (note if you have multiple profiles such as IIS Express and YourProject, the variable should exist for each profile (best to keep the value for the variable the same)). When running the app on a server, the ASPNETCORE_ENVIRONMENT variable should exist as an environment variable on the server (VM or physical on-prem etc) or for Azure Web Site (aka Azure App Service) as a variable under Settings->Configuration. Note for environment variables you typically have to log out the runtime user and restart the service before the new value will be read.
If you have code such as env.IsDevelopment(), env.IsStaging(), or env.IsProduction, you will need to use the standard environment names (Development, Staging, and Production).
For Blazor Server, inside Program.cs CreateHostBuilder method where you call Host.CreateDefaultBuilder(args).CongigureWebHostDefaults(webHostBuilder =>
you can read the environment variable this way:
ConfigurationBuilder = new ConfigurationBuilder()
.AddEnvironmentVariables("ASPNETCORE_");
const string aspnetCoreEnv = "ASPNETCORE_ENVIRONMENT";
var environmentName = Environment.GetEnvironmentVariable(aspnetCoreEnv);
Now add the appropriate appsettings file containing the environment name:
ConfigurationBuilder.AddJsonFile($"appsettings.{EnvironmentName}.json", true, true);
var configuration = ConfigurationBuilder.Build();
webHostBuilder.UseConfiguration(configuration);
var hostBuilderEnvironmentSetting = webHostBuilder.GetSetting(WebHostDefaults.EnvironmentKey);
if (string.IsNullOrEmpty(hostBuilderEnvironmentSetting))
webHostBuilder.UseSetting(WebHostDefaults.EnvironmentKey, EnvironmentName);
If you are using Startup.cs, then you can use constructor injection so you can access the environment within Startup:
public Startup(IConfiguration configuration, IWebHostEnvironment hostingEnvironment)
If Blazor Web Assembly configures differently, maybe you can still achieve the equivalent logic to make it load the appsettings that match the environment.
With this approach, I have not encountered issues where the app does not identify the environment properly. I don't have a web.config file so haven't needed to maintain values there. The only "won't load" issue I have hit was in Azure where my publish config specified a "Microsoft Identity Platform" which references an Azure App Registration and that app registration didn't have under Redirect URIs a Web Platform with URI for my environment-specific app service.

Disable Azure Function from C# for runtime version 3.x?

How can I disable an Azure Function using code in C#?
I'm using Azure Functions Runtime version 3.x
I'm implementing a distributed circuit-breaker inspired by Serverless circuit breakers with Durable Entities. When the circuit opens I need to disable a queue-trigged Azure Function, instead of stopping the entire function app.
I see from How to disable functions in Azure Functions that the recommended way to disable a function is to set the AzureWebJobs.<FUNCTION_NAME>.Disabled app setting. But I haven't found an API for doing that in C#. I'm hoping there is something that I can call from my C# code that is equivalent to the Azure CLI's az functionapp config appsettings set command.
I saw similar questions on SO like:
azure set environment variable programmatically to disable an azure function
and How to Enable/Disable Azure Function programmatically
But those have answers from back in 2017 that use kudu APIs to change the disabled property in the function.json file, and I'm hoping that there is a better way to do that now. Especially because the Docs at How to disable functions in Azure Functions say:
The generated function.json file for a class library function is not
meant to be edited directly. If you edit that file, whatever you do to
the disabled property will have no effect.
Unfortunately I was not able to find any documentation as such. The closest I got was
https://learn.microsoft.com/en-us/rest/api/appservice/webapps/createfunction
For instance to create the function :
https://management.azure.com/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.Web/sites/{name}/functions/{functionName}?api-version=2019-08-01
However this documentation also did not take me near to your requirement of updating the Config File. Or I may have overlooked few modules. Request you check further before implementing the below steps
So here's is what I did, I was kind of trying to reverse engineer, I ran the commands in Azure CLI and captured the traces - my thought process - the Azure CLI internally run on python and issues the API request to the Azure.
Ran the below command and captured Fiddler :
az functionapp config appsettings set --name <myFunctionApp> \
--resource-group <myResourceGroup> \
--settings AzureWebJobs.QueueTrigger.Disabled=true
And Yes ! The python process was issuing request to https://management.azure.com to update appsetting :
The set property is sent in the Request Body :
We can hardcode the properties or get it dynamically.
So I ran the below Azure CLI command
az functionapp config appsettings list --name <> --resource-group <>
I was able to see the above properties that was passed along the PUT request
Took the fiddler for the above command
Saw there is a POST Request to the below endpoint :
https://management.azure.com/subscriptions//resourceGroups//providers/Microsoft.Web/sites//config/appsettings/list?api-version=2019-08-01
These are the same set of property bags which are sent as the request bodies in the PUT in order to set the property.
So in your case you will have to request the above end point to get the list of properties. It is json output. Update the value of AzureWebJobs.QueueTrigger.Disabled to True.
Issue the Updated properties using the PUT method along with the headers such as Bearer Token & Content-Type: application/json; charset=utf-8
Request URI :
https://management.azure.com/subscriptions//resourceGroups//providers/Microsoft.Web/sites//config/appsettings?api-version=2019-08-01
Headers :
Authorization: Bearer <> Content-Type: application/json;
charset=utf-8`
Request Body:
{"kind": "<class 'str'>", "properties": }
I hope you will be able to achieve your requirement.
I hope this helps you :)
I don't recommend this for your prod. Pls try and monitor in your Dev env.

Can I configure a default value for an Azure Functions binding expression?

I want to simplify deployment of my Azure Functions v3 project as much as possible.
As an example, take this function, which uses a binding expression to refer to the DoTheThingSchedule appsetting:
[FunctionName("DoTheThing")]
public async Task Run([TimerTrigger("%DoTheThingSchedule%")]TimerInfo timer)
{
// do the thing
}
For local development I can configure a value for DoTheThingSchedule in local.settings.json, but I haven't found a way to configure a default value in production, which means I'll always have to explicitly configure this setting in Azure.
Does anyone know of a way to work around this?
Azure needs you to add settings in configuration settings in azure. And it will not upload local.settings.json file to azure. But if you are using VS to publish, you can set the settings before publish:

Invalid storage account when developing Azure Function in Visual Studio

I'm developing Azure Function in Visual Studio using C#. And I'n running it locally on my development machine which sits behind a proxy. However keep getting this error:
Exception binding parameter Invalid storage account Please make sure your credentials are correct
In my C# class I have following function which have an output binding to a Service Bus queue.
[FunctionName("MyTestFunction")]
public static async Task<HttpResponseMessage> Run([HttpTrigger(AuthorizationLevel.Anonymous, "get", "post", Route = null)]HttpRequestMessage req, [Queue("myqueue")]IAsyncCollector<string> myQueue, TraceWriter log)
In local.settings.json, I populated AzureWebJobsStorage and AzureWebJobsDashboard with connection string copied from Azure Storage Explorer
{
"IsEncrypted": false,
"Values": {"AzureWebJobsStorage":"DefaultEndpointsProtocol=https;AccountName=storageaccountname;AccountKey=sNFYlzkTtIVejJqU36rhByzDq91Nyv+JQ==;BlobEndpoint=https://storageaccount.blob.core.windows.net/;QueueEndpoint=https://storageaccount.queue.core.windows.net/;TableEndpoint=https://storageaccount.table.core.windows.net/;FileEndpoint=https://storageaccount.file.core.windows.net/;",
"AzureWebJobsDashboard": "DefaultEndpointsProtocol=https;AccountName=storageaccountname;AccountKey=sNFYlzkTtIVejJqU36rhByzDq91Nyv+JQ==;BlobEndpoint=https://storageaccount.blob.core.windows.net/;QueueEndpoint=https://storageaccount.queue.core.windows.net/;TableEndpoint=https://storageaccount.table.core.windows.net/;FileEndpoint=https://storageaccount.file.core.windows.net/;"
}
}
It worked for me for a while, but then stopped working all together. I triple checked everything and still couldn't figure out what I did wrong. Can someone point me to the right direction for this please?
The value I have for AzureWebJobsStorage and AzureWebJobsDashboard are straight copy from the Primary Connection String of my storage account in Azure Storage Explorer.
I had a similar error message:
Missing storage secret credentials.
Following this document, I used the Azure Functions CLI (func.exe) (Command Line Interface) to update all necessary credentials in my local.settings.json.
Ensure you have node.js
Install Azure CLI if not already done so: npm install -g azure-functions-core-tools
In your Azure Functions v2 project root directory, execute func azure functionapp fetch-app-settings myawesomefunctionappname
You should see your credentials getting updated in local.settings.js. Local debugging might require you to have non-encrypted credentials. Simply set IsEncrypted: false in that file.
Here's a sample:
{
"IsEncrypted": false,
"Values": {
"AzureWebJobsStorage": "DefaultEndpointsProtocol=https;AccountName=...",
"FUNCTIONS_WORKER_RUNTIME": "dotnet",
"APPINSIGHTS_INSTRUMENTATIONKEY": "...",
"FUNCTIONS_EXTENSION_VERSION": "~2",
"WEBSITE_CONTENTAZUREFILECONNECTIONSTRING": "DefaultEndpointsProtocol=https;AccountName=...",
"WEBSITE_CONTENTSHARE": "...",
"WEBSITE_NODE_DEFAULT_VERSION": "8.11.1",
"WEBSITE_LOAD_CERTIFICATES": "*",
"WEBSITE_RUN_FROM_PACKAGE": "1"
},
"ConnectionStrings": {}
}
For those experiencing the same problem - developing Azure Functions locally (using Visual Studio) behind a proxy.
Here is how I fixed the issue - adding proxy settings in the func.exe.Config. You will be able to find the file under C:\Users\{yourAccountName}\AppData\Local\Azure.Functions.Cli\1.0.0\func.exe.Config
Add your proxy settings as:
<system.net>
<defaultProxy useDefaultCredentials="true">
<proxy usesystemdefault="True" proxyaddress="http://my.proxy.com:8080" bypassonlocal="True" autoDetect="True" />
</defaultProxy>
</system.net>
Hope this helps someone.
I really wish the error message returned actually tells us the CLI is actually not able to connect to Azure, rather than keep telling me I'm using an invalid storage account.
We were recently facing a similar issue with one of our web job which was working fine earlier but suddenly stopped working. Web job tried to restart every minute but it was getting failed with the error message Invalid storage account ABStorage. Please make sure your credentials are correct. Web job remained in Pending Restart status. Upon investigation, we found out that the storage connection string is perfectly fine while there is some bug in Azure storage SDK due to which CORS rules configured on Azure portal against the storage account were not being interpreted correctly. In the Azure portal, we had only 1 CORS rule on Azure storage having all allowed HTTP methods selected. We updated the CORS rule on the storage account to have one HTTP method per domain and the web job was able to start immediately.

Categories