HTTP trigger azure function get image from blob storage error - c#

I have an issue with my http trigger in azure functions. When I use the http trigger local the trigger is correctly getting the picture from the online azure storage container. Once azure function is deployed it no longer works.
Here is my code for the http trigger that work locally but not once deployed:
using System;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Azure.WebJobs;
using Microsoft.Azure.WebJobs.Extensions.Http;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging;
using Azure.Storage.Blobs;
namespace PlaygroundAzureFunctions
{
public static class HttpFromStorage
{
[FunctionName("GetSnapsnot")]
public static async Task<IActionResult> GetSnapsnot(
[HttpTrigger(AuthorizationLevel.Anonymous, "get", Route = "Snapshot")] HttpRequest req,
ILogger log)
{
log.LogInformation($"Snapsnot requested at: {DateTime.Now}");
string Connection = Environment.GetEnvironmentVariable("AzureWebJobsStorage");
string containerName = Environment.GetEnvironmentVariable("ContainerName");
var blobClient = new BlobContainerClient(Connection, containerName);
var blob = blobClient.GetBlobClient("TestPicture.jpeg");
var image = await blob.OpenReadAsync();
log.LogInformation($"Snapsnot request returned at: {DateTime.Now}");
return new OkObjectResult(image);
}
}
}
Here is my local.settings.json:
{
"IsEncrypted": false,
"Values": {
"AzureWebJobsStorage": "StringTakenFromCorrectStorageAccessKeysJustHiddenHere",
"ContainerName": "file-upload", // container name
"FUNCTIONS_WORKER_RUNTIME": "dotnet"
}
}
I was aiming for once deployed i could use the public to the internet app and use its url to call the api to trigger that specific picture to be shown.
The errors in the function monitor is as follows:

local.settings.json is used only when you run locally and when the function is deployed the settings are missing.
You generally don't want to store your production secrets in your git repository and you should set them separately (as part of a deployment pipeline) but if this is for testing or if the resources are publicly available you can just put the settings in
settings.json (no local).
You can also go to portal.azure.com->Your_App->Configuration=>Application Settings in Azure and set ContainerName directly there.

Related

How to read config values either using .ENV file or user secrets in Azure Functions

I created an Azure function with .Net Core 3.1, that is triggered when a new messages to the Service Bus Queue is added. This function works well by hardcoding the connection string directly to the local.settings.json file.
Now, I want to go one step further I avoid hardcoding this value by putting it either in an .ENV file or user secrets. I tried those two but is not working so far.
The local.host.json
{
"IsEncrypted": false,
"Values": {
"AzureWebJobsStorage": "UseDevelopmentStorage=true",
"FUNCTIONS_WORKER_RUNTIME": "dotnet",
"MyServiceBusConnString": "${MY_CONNECTIONSTRING}"
}
}
This is the Startup.cs I created
using System;
using System.Reflection;
using DemoFunction;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Azure.Functions.Extensions.DependencyInjection;
// register the assembly
[assembly: FunctionsStartup(typeof(Startup))]
namespace DemoFunction
{
// inherit
public class Startup : FunctionsStartup
{
private IConfiguration configuration;
// override
public override void Configure(IFunctionsHostBuilder builder)
{
// In other examples the registration of services goes here
}
public override void ConfigureAppConfiguration(IFunctionsConfigurationBuilder builder)
{
// local dev no Key Vault
builder.ConfigurationBuilder
.SetBasePath(Environment.CurrentDirectory)
.AddJsonFile("local.settings.json", true)
.AddUserSecrets(Assembly.GetExecutingAssembly(), true)
.AddEnvironmentVariables()
.Build();
}
}
}
This Startup code is the last one I tried but I have seem different approaches how to do it. Already tried them without any luck. Here are the links:
AZURE FUNCTIONS CONFIGURATION AND SECRETS MANAGEMENT
Using JSON and User Secrets configuration with Azure Functions
And this part of my function:
public class MyFunction
{
private ILogger _logger;
[FunctionName("MyFunction")]
public string Run([ServiceBusTrigger("myQueue", Connection = "MyServiceBusConnString")] string queueItem, ILogger log)
{
_logger = log;
_logger.LogInformation($"Starting Azure Function...");
// custom code...
}
}
Look at the Connection property of ServiceBusTrigger. It's referencing the key from the local.host.json which in turn is trying to read the value as mentioned before from .ENV file or user secrets.
When I run the Azure Function, this is the error I am getting:
At this point, I'm kind of lost on how to achieve this. So, any comment on how to fix this or any guidance is very appreciated.
For local development you can leave as it is, however, when publishing to Azure you should use Azure Key Vault to proper store the secrets of your application.
https://learn.microsoft.com/en-us/azure/app-service/app-service-key-vault-references#reference-syntax

dotnet core Google Cloud Function with Firebase Admin SDK

I have a Flutter client app that authorises the user using Google Provider. I want to use the dotnet core C# Google Cloud function to read the authorisation token and read the FireStore using Firebase Admin SDK. The documentation for this is scant and/or written for other languages.
Anyone have any examples or links to dotnet core documentation to do this as I've spent all day trying to figure this out?
I've got a simple function as per: https://cloud.google.com/functions/docs/quickstart-dotnet#whats-next
and I see some tests that give a clue to how some of the SDK works: https://github.com/firebase/firebase-admin-dotnet/blob/f6babbd4e59655f01be4a43230b5be198fc4f8cd/FirebaseAdmin/FirebaseAdmin.Snippets/FirebaseAuthSnippets.cs#L608-L629
using Google.Cloud.Functions.Framework;
using Microsoft.AspNetCore.Http;
using System.Threading.Tasks;
using FirebaseAdmin;
using FirebaseAdmin.Auth;
using Microsoft.Extensions.Logging;
using Google.Apis.Auth.OAuth2;
namespace HelloHttpFunction
{
public class Function : IHttpFunction
{
private readonly ILogger _logger;
public Function(ILogger<Function> logger) =>
_logger = logger;
//how to use firebase auth to check token and get uid from calling client
//how to read a set of docs from firestore
//already setup the Google environment variables
public async Task HandleAsync(HttpContext context)
{
HttpRequest request = context.Request;
// this my all guesswork below:
FirebaseApp.Create(new AppOptions()
{
Credential = GoogleCredential.GetApplicationDefault(),
});
FirebaseToken decodedToken = await FirebaseAuth.DefaultInstance.VerifyIdTokenAsync(idToken);
string uid = decodedToken.Uid;
var user = FirebaseAuth.DefaultInstance.GetUserAsync(uid);
await context.Response.WriteAsync("Hello, Functions Framework. ");
}
}
}

Azure Functions: Cannot apply attribute class 'Queue<T>' because it is generic

I'm using Visual Studio Code to develop an Azure Durable Function, following some online examples. I'm completely new to Azure and adapting to C# from VB.NET. I already developed a working durable function that stores data in a blob table. This is a slight adaptation of that which aims to put the information into a queue.
I'm following the instructions here, and have code that I think should work, except that it tells me this:
Cannot apply attribute class 'Queue<T>' because it is generic
However I've looked at some other examples, including the one from Microsoft, and don't anything special being done for typing of queue. Clearly, though, I'm missing something.
using System;
using System.IO;
using System.Linq;
using System.Collections.Generic;
using System.Net.Http;
using System.Threading.Tasks;
using Microsoft.Azure.WebJobs;
using Microsoft.Azure.WebJobs.Extensions.DurableTask;
using Microsoft.Azure.WebJobs.Extensions.Http;
using Microsoft.Azure.WebJobs.Host;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
using Microsoft.AspNetCore.Mvc;
namespace Test.Groove
{
public static class GrooveWebhook
{
[FunctionName("GrooveWebhook")]
public static async Task<List<string>> RunOrchestrator(
[OrchestrationTrigger] IDurableOrchestrationContext context)
{
var outputs = new List<string>();
GrooveItem data = context.GetInput<GrooveItem>();
string res = await context.CallActivityAsync<string>("AddToQueue", data);
return outputs;
}
[FunctionName("AddToQueue")]
public static async Task<String> Run(
[ActivityTrigger] GrooveItem trans,
[Queue("incoming-groove-webhooks")] IAsyncCollector<GrooveItem> GrooveData,
ILogger log)
{
await GrooveData.AddAsync (trans);
return $"Added Groove transaction for {trans.firstname} {trans.lastname}: {trans.email}";
}
[FunctionName("GrooveWebhook_HttpStart")]
public static async Task<HttpResponseMessage> HttpStart(
[HttpTrigger(AuthorizationLevel.Anonymous, "get", "post")] HttpRequestMessage req,
[DurableClient] IDurableOrchestrationClient starter,
ILogger log)
{
// Function input comes from the request content.
var data = await req.Content.ReadAsAsync<GrooveItem>();
string instanceId = await starter.StartNewAsync("GrooveWebhook", data);
log.LogInformation($"Started Groove webhook orchestration with ID = '{instanceId}'.");
return starter.CreateCheckStatusResponse(req, instanceId);
}
}
}
Now, here's the really weird part. I followed along literally step by step with their http trigger example, code as follows, and yet don't have that same error:
using System;
using System.IO;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Azure.WebJobs;
using Microsoft.Azure.WebJobs.Extensions.Http;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
namespace My.Functions
{
public static class HttpExample
{
[FunctionName("HttpExample")]
public static async Task<IActionResult> Run(
[HttpTrigger(AuthorizationLevel.Anonymous, "get", "post", Route = null)] HttpRequest req,
[Queue("outqueue"),StorageAccount("AzureWebJobsStorage")] ICollector<string> msg,
ILogger log)
{
log.LogInformation("C# HTTP trigger function processed a request.");
string name = req.Query["name"];
string requestBody = await new StreamReader(req.Body).ReadToEndAsync();
dynamic data = JsonConvert.DeserializeObject(requestBody);
name = name ?? data?.name;
string responseMessage = string.IsNullOrEmpty(name)
? "This HTTP triggered function executed successfully. Pass a name in the query string or in the request body for a personalized response."
: $"Hello, {name}. This HTTP triggered function executed successfully.";
return new OkObjectResult(responseMessage);
}
}
}
So then, as if that wasn't weird enough, I went back to my original project that added data to a table, and just added this little queue binding statement, and I don't have the error! This is so crazy. Code begins:
using System;
using System.Collections.Generic;
using System.IO;
using System.Net.Http;
using System.Threading.Tasks;
using Microsoft.Azure.WebJobs;
using Microsoft.Azure.WebJobs.Extensions.DurableTask;
using Microsoft.Azure.WebJobs.Extensions.Http;
using Microsoft.Azure.WebJobs.Host;
using Microsoft.Extensions.Logging;
using Microsoft.AspNetCore.Mvc;
namespace Company.Function
{
public static class GrooveOrchestrationTest
{
[FunctionName("GrooveOrchestrationTest")]
public static async Task<List<string>> RunOrchestrator(
[OrchestrationTrigger] IDurableOrchestrationContext context,
[Table ("GrooveData")] IAsyncCollector<GrooveItem> GrooveData,
[Queue ("GrooveQueue")] IAsyncCollector<GrooveItem> GrooveQueue)
There are no additional using directives in either of the latter solutions that successfully bind the queue for output.
One Additional Thought
I created another version from scratch with the exact same problem, even though my other one is still fine. I have no explanation for this. However, I will note that they are connected to DIFFERENT Azure accounts. Both are on the free tier, both just signed up in the last couple of days. I can't fathom why that should matter, but it's all I can think of.
This problem is caused by the following reference added automatically by VS Code into the .csproj file:
<PackageReference Include="Microsoft.NET.Sdk.Functions" Version="3.0.7" />
This is confirmed by changing it to match the reference from the other solution where the exact same code works perfectly:
<PackageReference Include="Microsoft.NET.Sdk.Functions" Version="3.0.3" />
With this change in reference, it works as expected.
What lead me to this is the fact that my queue binding worked fine when pasted into my older solution from just a few days ago, but the exact same code didn't work in the new solution. To ensure all things were equal, I pasted the entire contents of the .cs file so there would be no difference. I.e. the old solution and the new one had identical .cs files, but one worked and one didn't.
Since my code was identical in both solutions, it seemed clear that there must be something that VS Code had done behind the scenes. I did a stare-and-compare on all its automatically generated files between the old solution and the new one. I discovered that this one reference is literally the only difference between the two solutions, and when I change it to the older version, things work beautifully.
I'll reference the older package for now so I can get this done, and then when I get some free time figure out how to make the new SDK.Functions happy.
You might be lacking reference to Microsoft.Azure.WebJobs.Extensions.Storage.

How to connect from .NET .dll file to Azure using Linked Service

I want to write a code, similar to the code at the bottom of this link (https://azure.microsoft.com/en-us/blog/automating-azure-analysis-services-processing-with-azure-functions/) in Visual Studio and building a DLL file. However instead of using the connection string, i would like to use an existing Linked Service from my Azure portal.
The goal is to create a DLL that refreshes my Cube, while at the same time using an existing Linked Service which is already in my Azure Portal.
Is this possible?
Thanks.
#r "Microsoft.AnalysisServices.Tabular.DLL"
#r "Microsoft.AnalysisServices.Core.DLL"
#r "System.Configuration"
using System;
using System.Configuration;
using Microsoft.AnalysisServices.Tabular;
public static void Run(TimerInfo myTimer, TraceWriter log)
{
log.Info($"C# Timer trigger function started at: {DateTime.Now}");
try
{
Microsoft.AnalysisServices.Tabular.Server asSrv = new Microsoft.AnalysisServices.Tabular.Server();
var connStr = ConfigurationManager.ConnectionStrings["AzureASConnString"].ConnectionString; // Change this to a Linked Service connection
asSrv.Connect(connStr);
Database db = asSrv.Databases["AWInternetSales2"];
Model m = db.Model;
db.Model.RequestRefresh(RefreshType.Full); // Mark the model for refresh
//m.RequestRefresh(RefreshType.Full); // Mark the model for refresh
m.Tables["Date"].RequestRefresh(RefreshType.Full); // Mark only one table for refresh
db.Model.SaveChanges(); //commit which will execute the refresh
asSrv.Disconnect();
}
catch (Exception e)
{
log.Info($"C# Timer trigger function exception: {e.ToString()}");
}
log.Info($"C# Timer trigger function finished at: {DateTime.Now}");
}
So I guess you're using the Data Factory and you want to process your analysis services model from your pipeline. I don't see what your question actually has to do with the Data lake store.
To trigger Azure Functions from the Data Factory (v2 only), you'll have to use a web activity. It is possible to pass a Linked Service as part of your payload, as shown in the documentation. It looks like this:
{
"body": {
"myMessage": "Sample",
"linkedServices": [{
"name": "MyService1",
"properties": {
...
}
}]
}
However, there is no Analysis services linked service in the Data Factory, at least, I didn't hear of such a thing. Passing in a connectionstring from the pipeline seems like a good idea however. You could pass it as a pipeline parameter in your body of the webrequest.
Create a parameter in your pipeline
Add it to your Web Activity Payload
{
"body": {
"AzureASConnString": "#pipeline().parameters.AzureASConnString"
}
You can retrieve this value from functions like described here

Calling an external webservice method from Azure function returns 401 but the same code works in a console app

I have added a web reference (WSDL) to a class library and then referenced that dll in a timer-triggered c# Azure function (read more about azure functions). The class library has a class EmployeeService which calls a method from the web service (sort of a webservice wrapper). When I call the class lib method (GetEmployees) from a console application, it authenticates to the web service and returns result but when I run the azure function for the same code and creds it returns 401. Not sure what I am doing wrong here :
#r "MyConsult.Service.dll"
#r "Newtonsoft.Json.dll"
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Timers;
using Newtonsoft.Json;
using System.Net;
using MyConsult.Service.Service;
public static void Run(TimerInfo myTimer, TraceWriter log)
{
log.Info($"C# Timer trigger function executed at: {DateTime.Now}");
try
{
EmployeeService _empService = new EmployeeService();
var emps = _empService.GetEmployees();
int count = emps.Where(x => !string.IsNullOrEmpty(x.Email)).Select(x => x.Email).Distinct().Count();
log.Info($"employee count : {count}");
}
catch (Exception ex)
{
log.Info($"Exception Message: {ex.Message}");
log.Info($"Exception Stack Trace: { ex.StackTrace}");
}
}
Your console application is likely authenticating based on service configuration settings in your app.config. For your function, you'll need to programmatically apply those settings when constructing the client/proxy.

Categories