When deploy Azure function to Azure how did it know that it should read the AzureFunctionSettings from App settings instead of from local.setting.json - azure

I created my first Azure Function which integrate with SharePoint Online list, using those main points:-
1-I created an Azure App with self-sign certificate to authorize my Azure function.
2-I created a new Azure Function project using Visual Studio 2019. here are the main components
-Function.cs:-
using System;
using Microsoft.Azure.WebJobs;
using Microsoft.Azure.WebJobs.Host;
using Microsoft.Extensions.Logging;
using PnP.Core.Services;
using PnP.Core.Model.SharePoint;
using System.Collections.Generic;
namespace FunctionApp1
{
public class Function1
{
private readonly IPnPContextFactory pnpContextFactory;
public Function1(IPnPContextFactory pnpContextFactory)
{
this.pnpContextFactory = pnpContextFactory;
}
[FunctionName("Function1")]
public void Run([TimerTrigger("0 */5 * * * *")]TimerInfo myTimer, ILogger log)
{
log.LogInformation($"C# Timer trigger function executed at: {DateTime.Now}");
using (var context = pnpContextFactory.Create("Default"))
{
var myList = context.Web.Lists.GetByTitle("SubFolders");
Dictionary<string, object> values = new Dictionary<string, object>
{
{ "Title", System.DateTime.Now }
};
// Use the AddBatch method to add the request to the current batch
myList.Items.AddBatch(values);
context.Execute();
}
}
}
}
-Startup.cs:-
using Microsoft.Azure.Functions.Extensions.DependencyInjection;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using PnP.Core.Auth;
using System.Security.Cryptography.X509Certificates;
[assembly: FunctionsStartup(typeof(FunctionApp1.Startup))]
namespace FunctionApp1
{
class Startup :FunctionsStartup
{
public override void Configure(IFunctionsHostBuilder builder)
{
var config = builder.GetContext().Configuration;
var azureFunctionSettings = new AzureFunctionSettings();
config.Bind(azureFunctionSettings);
builder.Services.AddPnPCore(options =>
{
options.DisableTelemetry = true;
var authProvider = new X509CertificateAuthenticationProvider(azureFunctionSettings.ClientId,
azureFunctionSettings.TenantId,
StoreName.My,
StoreLocation.CurrentUser,
azureFunctionSettings.CertificateThumbprint);
options.DefaultAuthenticationProvider = authProvider;
options.Sites.Add("Default", new PnP.Core.Services.Builder.Configuration.PnPCoreSiteOptions
{
SiteUrl = azureFunctionSettings.SiteUrl,
AuthenticationProvider = authProvider
});
});
}
}
}
-local.setting.json:-
{
"IsEncrypted": false,
"Values": {
"AzureWebJobsStorage": "UseDevelopmentStorage=true",
"FUNCTIONS_WORKER_RUNTIME": "dotnet",
"SiteUrl": "https://***.sharepoint.com/",
"TenantId": "0b***",
"ClientId": "92***",
"CertificateThumbPrint": "EB***",
"WEBSITE_LOAD_CERTIFICATES": "EB***"
}
}
then i deploy it to Azure and it is working well, where each 5 minutes it adds a new list item.
But what i am unable to understand, is that when i test the function locally, the function reads its setting from the local.settings.json file, but after deploying it to Azure it start reading its settings from the online Azure App settings.. so how it did this behind the senses ?

This is by design.
App settings in a function app contain configuration options that affect all functions for that function app. When you run locally, these settings are accessed as local environment variables.
and
You can use application settings to override host.json setting values without having to change the host.json file itself. This is helpful for scenarios where you need to configure or modify specific host.json settings for a specific environment. This also lets you change host.json settings without having to republish your project.
Taken from App settings reference for Azure Functions.

Related

Building my first Azure functions , am i doing things correctly?

I created my first Azure Function which integrate with SharePoint Online list, using those main points:-
1-I created an Azure App with self-sign certificate to authorize my Azure function.
2-I created a new Azure Function project using Visual Studio 2019. here are the main components Function.cs:-
using System;
using Microsoft.Azure.WebJobs;
using Microsoft.Azure.WebJobs.Host;
using Microsoft.Extensions.Logging;
using PnP.Core.Services;
using PnP.Core.Model.SharePoint;
using System.Collections.Generic;
namespace FunctionApp1
{
public class Function1
{
private readonly IPnPContextFactory pnpContextFactory;
public Function1(IPnPContextFactory pnpContextFactory)
{
this.pnpContextFactory = pnpContextFactory;
}
[FunctionName("Function1")]
public void Run([TimerTrigger("0 */5 * * * *")]TimerInfo myTimer, ILogger log)
{
log.LogInformation($"C# Timer trigger function executed at: {DateTime.Now}");
using (var context = pnpContextFactory.Create("Default"))
{
var myList = context.Web.Lists.GetByTitle("SubFolders");
Dictionary<string, object> values = new Dictionary<string, object>
{
{ "Title", System.DateTime.Now }
};
// Use the AddBatch method to add the request to the current batch
myList.Items.AddBatch(values);
context.Execute();
}
}
}
}
Startup.cs:-
using Microsoft.Azure.Functions.Extensions.DependencyInjection;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using PnP.Core.Auth;
using System.Security.Cryptography.X509Certificates;
[assembly: FunctionsStartup(typeof(FunctionApp1.Startup))]
namespace FunctionApp1
{
class Startup :FunctionsStartup
{
public override void Configure(IFunctionsHostBuilder builder)
{
var config = builder.GetContext().Configuration;
var azureFunctionSettings = new AzureFunctionSettings();
config.Bind(azureFunctionSettings);
builder.Services.AddPnPCore(options =>
{
options.DisableTelemetry = true;
var authProvider = new X509CertificateAuthenticationProvider(azureFunctionSettings.ClientId,
azureFunctionSettings.TenantId,
StoreName.My,
StoreLocation.CurrentUser,
azureFunctionSettings.CertificateThumbprint);
options.DefaultAuthenticationProvider = authProvider;
options.Sites.Add("Default", new PnP.Core.Services.Builder.Configuration.PnPCoreSiteOptions
{
SiteUrl = azureFunctionSettings.SiteUrl,
AuthenticationProvider = authProvider
});
});
}
}
}
local.setting.json:-
{
"IsEncrypted": false,
"Values": {
"AzureWebJobsStorage": "UseDevelopmentStorage=true",
"FUNCTIONS_WORKER_RUNTIME": "dotnet",
"SiteUrl": "https://***.sharepoint.com/",
"TenantId": "0b***",
"ClientId": "92***",
"CertificateThumbPrint": "EB***",
"WEBSITE_LOAD_CERTIFICATES": "EB***"
}
}
then i deploy it to Azure and it is working well, where each 5 minutes it adds a new list item. but i have those questions:-
Am i am doing things correctly, especially from a security perspective? as at the end my Azure function will have a public URL as follow https://functionapp1*****.azurewebsites.net and this can be called by any anonymous user.. so is this a security hole? if so, then how i can fix it?
I am currently using self-Signed certificate, so is it fine for Production? if not, then what i need to do , to get a proper SSL ?
Thanks
EDIT. Here is the url for the fucntion

Azure app insights TelemetryConfiguration not working in Startup.cs file of azure function

my startup file looks like this
[assembly: FunctionsStartup(typeof(Startup))]
{
public override void Configure(IFunctionsHostBuilder builder)
{
var context = builder.GetContext();
var _config = context.Configuration;
var telemetryConfig = new TelemetryConfiguration();
telemetryConfig.ConnectionString = _config.GetConnectionString("ApplicationInsights");
builder.Services.AddSingleton<TelemetryClient>(x => new TelemetryClient(telemetryConfig));
}
}
and the settings file looks like this
{
"IsEncrypted": false,
"Values": {
"AzureWebJobsStorage": "UseDevelopmentStorage=true",
"FUNCTIONS_WORKER_RUNTIME": "dotnet",
"APPINSIGHTS_INSTRUMENTATIONKEY": "xxxxxx-fbc9-441b-9869-70bcb4afc93a",
"TimerInterval": "0 */5 * * * *"
},
"ConnectionStrings": {
"ApplicationInsights": "InstrumentationKey=xxxxxx-fbc9-441b-9869-
70bcb4afc93a;IngestionEndpoint=https://xxx-
in.applicationinsights.azure.com/;LiveEndpoint=https://xxxxxx.livediagnostics.monitor.azure.c
om/" }
}
problem here is that telemetryConfig is set properly i can see the InstrumentationKey and ConnString
but new TelemetryClient(telemetryConfig) has InstrumentationKey empty and most of the properties set to null.
even if i set InstrumentationKey directly (which is obsolete) the dependency injected value in the code has empty InstrumentationKey
in code i am using it like this
private readonly TelemetryClient _telemetry;
public FHIRExtract(ILogger<FHIRExtract> logger, TelemetryClient telemetry, IConfiguration configuration)
{
_logger = logger;
_config = configuration;
_telemetry = telemetry;
}
[FunctionName("FHIRExtract")]
public async Task Run([TimerTrigger("%TimerInterval%"
_telemetry has InstrumentationKey empty
but when i set this value in the code
//_telemetry.InstrumentationKey = "xxxxxx-fbc9-441b-9869-70bcb4afc93a";
it works and i am able to send telemetry data
I am trying to use non obsolete functions to configure Telemetry client and want to use it by DI.
what am i doing wrong ?
You shouldn't setup Application Insights this way.
Install the Microsoft.Azure.WebJobs.Logging.ApplicationInsights NuGet package in the azure functions project
In the Configure override add the logging:
builder.Services.AddLogging();
Configure the instrumentatio key by setting APPINSIGHTS_INSTRUMENTATIONKEY in the settings.json file
Inject a TelemetryConfiguration in your function and create an instance of TelemetryClient:
public FHIRExtract(ILogger<FHIRExtract> logger, TelemetryConfiguration
telemetryConfig, IConfiguration configuration)
{
_logger = logger;
_config = configuration;
_telemetry = new TelemetryClient(telemetryConfig);
}
For some reason the custom telemetry logging in azure functions breaks when only setting APPLICATIONINSIGHTS_CONNECTION_STRING in the configuration but I am sure that will be fixed in the near future. In my experience the application insights integration always lags behind a bit.
References:
Offical docs
Sample repository demoing various Application Insights techniques

ASP.NET Core Web App .NET 6, reading configuration from within a Razor pages' cs file

I'm trying to find a simple way to access a value from the configuration of a .NET 6, ASP.NET Core Web App, in a Razor page's .cs file, using Visual Studio 2022. There's a lot of examples of how to do this online the old way. I'm trying to get up to speed with ASP.NET Core, and .NET 6, and I don't want to use the old techniques.
Right now I can connect to a SQLite database with a very simple configuration. Here's the code in my Index.cshtml.cs file,
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.Extensions.Configuration;
using System.Data.SQLite;
namespace Something.Pages
{
public class IndexModel : PageModel
{
private readonly ILogger<IndexModel> _logger;
public IndexModel(ILogger<IndexModel> logger)
{
_logger = logger;
}
public void OnGet()
{
var cnnStrBuilder = new SQLiteConnectonStringBuilder();
cnnStrBuild.DataSource = "DBFile.SQLite";
using (SQLiteConnection cnn = new(cnnStrBuilder.ConnectionString))
{
cnn.Open();
...
}
}
}
}
This works. I can read and display data without the need for a DBContext, or EntityFramework, or anything else. All I want to change is to get the connection string from the appsettings.json file. My appsettings.json looks like this,
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"ConnectionStrings": {
"MyConStr" : "DBFile.SQLite"
},
"AllowedHosts": "*"
}
in my Program.cs file is the builder object, which I could use to get the configuration.
var builder = WebApplication.CreateBuilder(args);
// Add services to the container.
builder.Services.AddRazorPages();
var app = builder.Build();
// Configure the HTTP request pipeline.
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler("/Error");
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthorization();
app.MapRazorPages();
app.Run();
but how do I get access to the builder object in the Razor page's .cs file so I can get to the configuration? .NET 6 is supposed to make things like this easier, I thought.
BTW, I purposefully left 'connection string' out of the title of this post because I feel like this should be a generic thing. It shouldn't be so hard to retrieve a string value from the configuration settings from anywhere in the application. What am I missing?
You can inject an instance of IConfiguration in your PageModel which you can use to access appsettings.json values.
public class IndexModel : PageModel
{
private readonly ILogger<IndexModel> _logger;
private readonly IConfiguration _configuration;
public IndexModel(ILogger<IndexModel> logger, IConfiguration configuration)
{
_logger = logger;
_configuration = configuration;
}
public void OnGet()
{
var cnnStrBuilder = new SQLiteConnectonStringBuilder();
cnnStrBuild.DataSource = _configuration.GetConnectionString("MyConStr");
using (SQLiteConnection cnn = new(cnnStrBuilder.ConnectionString))
{
cnn.Open();
...
}
}
}
You can also check this article for more info:
https://www.learnrazorpages.com/configuration/

Azure Function (Service Bus Trigger) Not Getting started when a new message comes into the service bus queue

Created an Azure Function which is a service bus triggered in Visual Studio and published to Azure from Visual Studio.
Whenever a message goes to queue, the function is running fine from local when manually run. But the expectation is the function should automatically trigger when a message is in the queue.
I am just adding a new message manually and seeing the logs if the function got triggered automatically but it is not. When I checked the Application Insight I found the below error logs
The listener for function 'ProcessVideos' was unable to start. Service Bus account connection string 'connection' does not exist. Make sure that it is a defined App Setting.*"
Code for local.settings.json where the Service Bus connection string is set.
{
"IsEncrypted": false,
"Values": {
"AzureWebJobsStorage": "UseDevelopmentStorage=true",
"FUNCTIONS_WORKER_RUNTIME": "dotnet",
"connection": "Endpoint=sb://videoupload10000.servicebus.windows.net/;SharedAccessKeyName=Listen;SharedAccessKey=80n8a0MCmh+3UZN4+4B7gDy4gp3hKCxfDI/9urDmaP8=;"
}
}
Code for the actual function.
using System;
using System.Text;
using System.Text.Json;
using System.Threading.Tasks;
using Azure.Messaging.ServiceBus;
using Azure.Storage.Blobs;
using Azure.Storage.Blobs.Models;
using Microsoft.Azure.Cosmos;
using Microsoft.Azure.WebJobs;
using Microsoft.Azure.WebJobs.Host;
using Microsoft.Extensions.Logging;
namespace ReceiveMessages
{
public static class Process
{
private static string blob_connection_string = "DefaultEndpointsProtocol=https;AccountName=videostorage1000;AccountKey=y6CVtXafqKuShZuv6BMbVj9DrymzVdNDpjDVxp6hZMvuRRjcCz/i8TrOGfM5T/JCvfG33sY3xqqW+ASt3p6V+Q==;EndpointSuffix=core.windows.net";
private static string source_container_name = "unprocessed";
private static string destination_container_name = "processed";
private static readonly string _connection_string = "AccountEndpoint=https://videodbupdate.documents.azure.com:443/;AccountKey=gmR051bG7uq7o2i519m7J9nh6tb4LLctfOQ3nPMUxMu9QJWsmh1SPiY8ylvxoY3bn7kWR4cS2qwanBdIoXSrpg==;";
private static readonly string _database_name = "appdb";
private static readonly string _container_name = "video";
[FunctionName("ProcessVideos")]
public static async Task Run([ServiceBusTrigger("videoqueue", Connection = "connection")]ServiceBusReceivedMessage myQueueItem, ILogger log)
{
ReceivedMessage _message = JsonSerializer.Deserialize<ReceivedMessage>(Encoding.UTF8.GetString(myQueueItem.Body));
BlobServiceClient _client = new BlobServiceClient(blob_connection_string);
BlobContainerClient _source_container_client = _client.GetBlobContainerClient(source_container_name);
BlobClient _source_blob_client = _source_container_client.GetBlobClient(_message.VideoName);
BlobContainerClient _destination_container_client = _client.GetBlobContainerClient(destination_container_name);
BlobClient _destination_blob_client = _destination_container_client.GetBlobClient(_message.VideoName);
CosmosClient _cosmosclient = new CosmosClient(_connection_string, new CosmosClientOptions());
Container _container = _cosmosclient.GetContainer(_database_name, _container_name);
BlobDownloadInfo _info = _source_blob_client.Download();
// Copy the blob to the destination container
await _destination_blob_client.StartCopyFromUriAsync(_source_blob_client.Uri);
log.LogInformation(_info.Details.LastModified.ToString());
log.LogInformation(_info.ContentLength.ToString());
BlobDetails _blobdetails = new BlobDetails();
_blobdetails.BlobName = _message.VideoName;
_blobdetails.BlobLocation = "https://videostorage100.blob.core.windows.net/processed/" + _message.VideoName;
_blobdetails.ContentLength = _info.ContentLength.ToString();
_blobdetails.LastModified = _info.Details.LastModified.ToString();
_blobdetails.id = Guid.NewGuid().ToString();
//_container.CreateItemAsync(_blobdetails, new PartitionKey(_message.VideoName)).GetAwaiter().GetResult();
// await _container.CreateItemAsync(_blobdetails, new PartitionKey(_message.VideoName));
Console.WriteLine("Item created");
// Delete the blob from the unprocessed container
_source_blob_client.Delete();
// Add the details of the blob to an Azure Cosmos DB account
}
}
}
The local settings are not uploaded to the cloud. to add your connection string you need to do the following. Go to your function app in azure. Select "configuration" under "settings" from the left side menu items. On this screen, you should click on the button "+ New Application Settings". Once the popup opens add "connection" as the name and your connection string as the value. Click on "OK" and then on the next screen click on "save" to save and apply the settings. Hope this helps
For a python project, your connection value in function.json needs to refer to the value in local.settings.json. Should be similar for you:
function.json:
"connection": "AzureWebJobsMyServiceBus"
local.settings.json:
"AzureWebJobsMyServiceBus": "Endpoint=sb://..."

Project level Culture setting for Azure Functions App

Is it possible to change culture on a project level on Azure Functions App?
https://learn.microsoft.com/en-us/azure/azure-functions/functions-app-settings
The app is using Consumption plan or Premium plan, not via ASP.NET Core.
My Startup.cs file is like below:
public class Startup : FunctionsStartup
{
public override void Configure(IFunctionsHostBuilder builder)
{
}
}
Can ASP.NET Core that is based on different Startup.cs not like above use Consumption plan or Premium plan??
Asp.net Core that must use App Service plan like below:
https://andrewlock.net/adding-localisation-to-an-asp-net-core-application/
When migrating legacy application running on servers to Azure you always need to take care of Timezone and Culture settings that originally are fetched from the machine. For Azure Functions you can set the timezone in the app settings:
WEBSITE_TIME_ZONE=Europe/London
Possible values found here https://en.wikipedia.org/wiki/List_of_tz_database_time_zones. This actually differs for app services that seem to use TZ for Linux and WEBSITE_TIME_ZONE for Windows.
For culture it is more complicated. Using aspnet core you define it in Configure in the Startup class
public void Configure(IApplicationBuilder app, IWebHostEnvironment env,
ILoggerFactory loggerFactory)
{
var cultureInfo = new CultureInfo("en-US");
app.UseRequestLocalization(new RequestLocalizationOptions
{
DefaultRequestCulture = new RequestCulture(cultureInfo),
SupportedCultures = new List<CultureInfo>
{
cultureInfo,
},
SupportedUICultures = new List<CultureInfo>
{
cultureInfo,
}
});
}
That is not possible in Azure Function Apps. What you can do is to create a Setup class and then set the culture for the appdomain and the current thread. This will probably work as long Azure isnt altering the Appdomain.
[assembly: FunctionsStartup(typeof(Startup))]
namespace FunctionApp
{
public class Startup : IWebJobsStartup
{
public void Configure(IWebJobsBuilder app)
{
var cultureInfo = new CultureInfo("en-US");
CultureInfo.DefaultThreadCurrentCulture = cultureInfo;
CultureInfo.DefaultThreadCurrentUICulture = cultureInfo;
Thread.CurrentThread.CurrentCulture = cultureInfo;
Thread.CurrentThread.CurrentUICulture = cultureInfo;
}
}
}
Azure Function didn't provide a built-in method to change culture.
Put this at the starting of your function app:
using System.Threading;
using System.Globalization;
//......
string culture = "en-US";
CultureInfo CI = new CultureInfo(culture);
Thread.CurrentThread.CurrentUICulture = CI;
Above code will change the culture to en-US. You can set it to other value.
This is the document:
https://learn.microsoft.com/en-us/dotnet/api/system.threading.thread.currentculture?view=netcore-3.1
Does this solved your problem?

Resources