How to access the environment variables from inside ServiceBusAttribute constructor? - azure

I'm having an azure function that is binded to a service bus, I'm using it like this:
[FunctionName("MyFunctionName")]
public async Task<IActionResult> Run(
[HttpTrigger(AuthorizationLevel.Function, "get", "post", Route = null)] HttpRequest req,
[ServiceBus("myqueueortopicname", Connection = "ServiceBusConnection")] IAsyncCollector<string> messages)
I'm trying to make the service bus queue name to be configurable and to be taken from Environment.GetEnvironmentVariable("MyMessageBusQueueName")
With
[ServiceBus(Environment.GetEnvironmentVariable("MyMessageBusQueueName"), Connection = "ServiceBusConnection")] IAsyncCollector<string> messages)`
local.Settings.json:
{
"IsEncrypted": false,
"Values": {
"MyMessageBusQueueName": "myqueueortopicname",
"ServiceBusConnection": "..."
},
"ConnectionStrings": {}
}
But I'm having an error of:
An attribute argument must be a constant expression, typeof expression
or array

You need to use the binding expression by wrapping the environment variable name with %. Define the queue name as %MyMessageBusQueueName% which would bind to environment variable
[ServiceBus("%MyMessageBusQueueName%", Connection = "ServiceBusConnection")] IAsyncCollector<string> messages)
Read about binding expression at Microsoft documentation ---here.

Related

Azure Functions with NCrontab returning method not found message

We are trying to use the NCrontab package to create a new Microsoft.Azure.WebJobs.Extensions.Timers.CronSchedule object instance.
Using the following code:
[FunctionName("FunctionTest")]
public static async Task<IActionResult> Run(
[HttpTrigger(AuthorizationLevel.Function, "get", "post", Route = null)] HttpRequest req,
ILogger log)
{
// using NCrontab;
var nCrontabSchedule = CrontabSchedule.Parse("5 4 * * *");
//using Microsoft.Azure.WebJobs.Extensions.Timers;
CronSchedule cronSchedule = new CronSchedule(nCrontabSchedule);
return new OkObjectResult("Hey There");
}
All the packages were recognized and we don't have any build errors.
But, when we try to execute this function through HTTP endpoint, the following error is returned:
System.Private.CoreLib: Exception while executing function: Function1. FunctionApp1: Method not found: 'Void Microsoft.Azure.WebJobs.Extensions.Timers.CronSchedule..ctor(NCrontab.CrontabSchedule)'
We saw that the function emulator app uses two different DLL's regarding NCronTab dependency, so we believe that is related to this error.
Here is the single NuGet package that we are using:
<PackageReference Include="Microsoft.NET.Sdk.Functions" Version="3.0.7" />
Has anyone ever seen the same error before?
Thanks,
Renato.
Can we use Cronos instead?
[FunctionName("Function1")]
public static async Task<IActionResult> Run(
[HttpTrigger(AuthorizationLevel.Function, "get", "post", Route = null)] HttpRequest req,
ILogger log)
{
log.LogInformation("C# HTTP trigger function processed a request.");
CronExpression nCrontabSchedule = CronExpression.Parse("5 4 * * *");
DateTime? next = nCrontabSchedule.GetNextOccurrence(DateTime.UtcNow);
You may look into this for different operations such as get occurrences within range ( e.g. for an year) or working with the local times. Will it suffice the requirement?

How do I pass in the storage account connection string for a CosmosDBTrigger?

I'm trying to figure out the proper way to pass in a storage account connection string to a CosmosDBTrigger. I have a function that runs when there is a change on a CosmosDB container. This function copies image blobs from one container to another. If you look at the code below, I have commented out the line where I am trying to fine the storage account that I want to connect to. This function runs when that is commented out. It does not run when I have that un-commented. Why?
public static class Function1
{
[FunctionName("ImageCopier")]
public static async Task Run([CosmosDBTrigger(
databaseName: "MyDatabase",
collectionName: "Orders",
ConnectionStringSetting = "databaseConnection",
CreateLeaseCollectionIfNotExists = true,
LeaseDatabaseName = "TriggerLeases",
LeaseCollectionName = "TriggerLeases",
LeaseCollectionPrefix = "ImageCopier")]IReadOnlyList<Document> input,
//[StorageAccount("MyStorageAccount")]string storageConnectionString,
ILogger log)
{
I have MyStorageAccount defined in my local.settings.json file and I also have it in my Azure Function Configuration settings. I copied the connection string directly from the storage account keys panel.
When you set up a CosmosDB trigger, the information that is supplied in that trigger is specific to the trigger. If you need a setting or configuration not related to the trigger in your code, you can use the Environment.GetEnvironmentVariable method.
In your local environment, you can set these variables by editing the local.settings.json file, specifically the Values array. For example:
{
"IsEncrypted": false,
"Values": {
"JobUri": "https://yourapiendpointurl.com",
"BlobStorageConnectionString" : "the connection string",
"AzureWebJobsStorage": "UseDevelopmentStorage=true",
"FUNCTIONS_WORKER_RUNTIME": "dotnet"
}
}
In your method, you may grab that value like so:
public static class Function1
{
[FunctionName("ImageCopier")]
public static async Task Run([CosmosDBTrigger(
databaseName: "MyDatabase",
...
ILogger log)
{
var connectionString =
Environment.GetEnvironmentVariable("BlobStorageConnectionString");
}
}
The local.settings.json file will not be used when it's running in Azure.
I am not sure that when you publish the function if your local.settings.json file will migrate the settings to your Azure Function app's configuration, so I would check to make sure that your settings are in there after publishing.
Side note: Be carful when committing code to repos .. you don't want "secrets" in your repositories in case someone gets in to your repo and discovers it.
While you can access raw configuration values using GetEnvironmentVariable, a more robust/idiomatic approach with .NET in particular is to leverage the built-in dependency injection of configuration.
Using this, you can accept an IConfiguration or strongly-typed IOptions through the function's constructor and use the values in your code. For example:
public class Function1
{
private readonly IConfiguration configuration;
public Function1(IConfiguration configuration)
{
this.configuration = configuration;
}
[FunctionName("ImageCopier")]
public async Task Run([CosmosDBTrigger(/* trigger params */)] IReadOnlyList<Document> input)
{
var connectionString = configuration["MyStorageAccount"];
// Use connection string
}
}
You can take this further to inject services like an "ImageBlobService" into your function that have already been configured in a common Startup Configure method just like ASP.NET Core. That way the individual functions don't need to know anything about configuration and just ask for the relevant service to use.

The listener for function 'SignalR' was unable to start. Azure function binding with signalr

I'm developing an application where IOT devices are connected with the Azure IOT Hub. and its realtime data can be visible on the web view. However, I'm facing an error, I'm trying to bind the data Azure function with SignalR, but when I run the application I receive the following error message.
The listener for function 'SignalR' was unable to start. Microsoft.Azure.EventHubs.Processor: Encountered error while fetching the list of EventHub PartitionIds. System.Private.CoreLib: The link address '$management' did not match any of the expected formats.
Error Description Image
I've tried everything to fix it but failed every time. I'd really appreciate if someone would help me find the solution to this problem.
Here is the script I'm using from this link
Here is my SignalR.cs class
public static class SignalR
{
[FunctionName("SignalR")]
public static async Task Run(
[IoTHubTrigger("messages/events", Connection = "IoTHubTriggerConnection", ConsumerGroup = "$Default")]EventData message,
[SignalR(HubName = "broadcast")]IAsyncCollector<SignalRMessage> signalRMessages,
ILogger log)
{
var deviceData = JsonConvert.DeserializeObject<DeviceData>(Encoding.UTF8.GetString(message.Body.Array));
deviceData.DeviceId = Convert.ToString(message.SystemProperties["iothub-connection-device-id"]);
log.LogInformation($"C# IoT Hub trigger function processed a message: {JsonConvert.SerializeObject(deviceData)}");
await signalRMessages.AddAsync(new SignalRMessage()
{
Target = "notify",
Arguments = new[] { JsonConvert.SerializeObject(deviceData) }
});
}
}
Here is my SignalRConnection.cs class
public static class SignalRConnection
{
[FunctionName("SignalRConnection")]
public static SignalRConnectionInfo Run(
[HttpTrigger(AuthorizationLevel.Anonymous, "get", Route = null)] HttpRequest req,
[SignalRConnectionInfo(HubName = "broadcast")] SignalRConnectionInfo info,
ILogger log) => info;
}
Here is my local.settings.json file
{
"IsEncrypted": false,
"Values": {
"AzureWebJobsStorage": "UseDevelopmentStorage=true",
"FUNCTIONS_WORKER_RUNTIME": "dotnet",
"AzureSignalRConnectionString": "",
"MSDEPLOY_RENAME_LOCKED_FILES": 1,
"IoTHubTriggerConnection": ""
},
"Host": {
"LocalHttpPort": 7071,
"CORS": "*"
}
}
for IoTHubTriggerConnection, I'm using the connection string of iothubjohnsoncontrol (displayed in image below).
IOT Hub Keys Image
for AzureSignalRConnectionString, I'm using the connection string of signalrjohnsoncontrol (displayed in image below).
SignalR Keys Image
Could you please check if you have given EventHub Compatible name and EventHub compatible connections string from here
Please try replacing messages/events with EventHub-Compatible name and IoTHubTriggerConnection as EventHub compatible endpoint from portal.
Almost similar discussion here :
https://github.com/Azure/azure-event-hubs-dotnet/issues/103
I have a usecase like that to Push Iot data to Azure data explorer and this is what my Function looks like
Iot Hub Connection string which is EventHub compatibale
Hope this helps.

Azure function is giving not a valid Base-64 string error

I have a http-trigger with a CosmosDB output binding and a simplest of a function as below.
public static class AddRequest
{
[FunctionName("AddRequest")]
public static async Task<IActionResult> Run(
[HttpTrigger(AuthorizationLevel.Function, "post", Route = null)] HttpRequest req,
ILogger log, [CosmosDB(
databaseName: "haveThatDB",
collectionName: "Requests",
ConnectionStringSetting = "MongoDBEndPoint",CreateIfNotExists =true)] IAsyncCollector<Request> requestOutput
)
{
string jsonContent = await req.ReadAsStringAsync();
dynamic data = JsonConvert.DeserializeObject(jsonContent);
await requestOutput.AddAsync(data);
return req != null
? (ActionResult)new OkObjectResult($"Hello, ras")
: new BadRequestObjectResult("Please pass a name on the query string or in the request body");
}
}
when i executed i get an error
Exception binding parameter 'requestOutput'. System.Private.CoreLib: The input is not a valid Base-64 string as it contains a non-base 64 character, more than two padding characters, or an illegal character among the padding characters
i am using V2 of azure functions.
i have observed that removing the output binding works. so looks like something is up with this output binding.
local.settings contents are as below
{
"IsEncrypted": false,
"Values": {
"AzureWebJobsStorage": "UseDevelopmentStorage=true",
"FUNCTIONS_WORKER_RUNTIME": "dotnet",
"MongoDBEndPoint": "AccountEndpoint=https://abc.documents.azure.com:10255;AccountKey=xxxxxxxxxxxxyyyyyyyyzzzzzzz",
"MongoDBName": "haveThatDB"
}
}
any help will be appreciated.
Azure Cosmos DB bindings are only supported for use with the SQL API. For all other Azure Cosmos DB APIs, you should access the database from your function by using the static client for your API, including MongoDB API, Cassandra API, Gremlin API, and Table API.
Supported APIs
Azure Cosmos DB bindings for Azure Functions 2.x

Error creating HttpTrigger Azure Function with CosmosDB document output

I want to make an Azure Function that takes a JSON body passed to it and inserts this document in to an Azure COSMOSDB instance.
To do so, I created this function in the Azure Functions Portal:
And implement the function like so:
#r "Newtonsoft.Json"
using Newtonsoft.Json.Linq;
using System.Net;
public static async Task<HttpResponseMessage> Run(HttpRequestMessage req, TraceWriter log, object outputDocument)
{
var requestContent = await req.Content.ReadAsStringAsync();
log.Verbose($#"Received request:\n{requestContent}");
var newDoc = JObject.Parse(requestContent);
newDoc["Id"] = Guid.NewGuid().ToString();
newDoc["shardKey"] = newDoc.Value<string>(#"Id").Substring(8);
outputDocument = newDoc;
return req.CreateResponse(System.Net.HttpStatusCode.Created);
}
In the portal, I put in an easy sample doc:
{
"prop1": 2,
"prop2": "2017-02-20",
}
and click 'Run'
I'm immediately met with
as an overlay in the portal IDE along with
{
"id": "145ee924-f824-4064-8364-f96dc12ab138",
"requestId": "5a27c287-2c91-40f5-be52-6a79c7c86bc2",
"statusCode": 500,
"errorCode": 0,
"message": "'UploadDocumentToCosmos' can't be invoked from Azure WebJobs SDK. Is it missing Azure WebJobs SDK attributes?"
}
in the log area.
There seems to be nothing I can do to fix the issue, yet I sure feel like I'm doing everything right.
What do I need to do to simply take a JSON object as an HTTP Request to an Azure Function, and insert/upsert said object in to an Azure Cosmos DB instance??
For async functions you should use IAsyncCollector:
public static async Task<HttpResponseMessage> Run(
HttpRequestMessage req, TraceWriter log,
IAsyncCollector<object> outputDocuments)
{
...
await outputDocuments.AddAsync(newDoc);
}
Can you try adding out to dynamic outputDocument?
public static async Task<HttpResponseMessage> Run(HttpRequestMessage req, TraceWriter log, out dynamic outputDocument)

Resources