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)
Related
I have created a Azure Function App. The function app connects to a SQL DB and has the following features
Return all the records in a table
Returns the records based on the column name using the below code
public static async Task<IActionResult> Run(
[HttpTrigger(AuthorizationLevel.Function, "get", "post", Route = null)] HttpRequest req,
ILogger log)
{
string loan_id = req.Query["loanid"];
string loan_amount = req.Query["loanamount"];
if (string.IsNullOrEmpty(loan_id)) {
//Do something when dont give loan id.
} else if (string.IsNullOrEmpty(loan_amount)) {
//DO something when dont give loan amount.
}
return new OkObjectResult("This is a test.");
}
I would like to document the function app using API Management/Swagger. Can you please let me know how this can be achieved?
Thanks in advance
You just need to create an API management service instance from the portal and add the function endpoint using the open api.
You can follow this documentation on how to do the same.
I need to be able to identify the key (ideally key name) provided in the header (x-functions-key) for the POST to the Azure Function in the Run method, e.g.
Run([HttpTrigger(AuthorizationLevel.Function, "get", "post", Route = null)] HttpRequest req, ILogger log, ClaimsPrincipal principal)
It is great to be able to protect access to the Azure Function adding Function Keys in the Azure Portal panel, but I must be able to tell which function key was used. Ideally it would be possible to associate claims on each function key, but as long as I can at least figure out which key was used I will be happy.
Simply get the claim "http://schemas.microsoft.com/2017/07/functions/claims/keyid" from the req.HttpContext.User.Claims object. It contains the key id in case a Function key was used.
Works like a charm, and does not require external lookups.
const string KEY_CLAIM = "http://schemas.microsoft.com/2017/07/functions/claims/keyid";
public static async Task<IActionResult> Run(HttpRequest req, ILogger log)
{
var claim = req.HttpContext.User.Claims.FirstOrDefault(c => c.Type == KEY_CLAIM);
if (claim == null)
{
log.LogError("Something went SUPER wrong");
throw new UnauthorizedAccessException();
}
else
{
log.LogInformation( "Processing call from {callSource}", claim.Value);
}
Sajeetharan answered how you can get the Keys using REST API.
About the ability to use RBAC, you need to use Managed Identities and you can find more information about how to set it up: https://learn.microsoft.com/en-us/azure/app-service/overview-managed-identity?tabs=dotnet
In Azure Functions v1:
[FunctionName("MyAuthenticatedFunction")]
public static async Task<HttpResponseMessage> MyAuthenticatedFunction([HttpTrigger(AuthorizationLevel.Function)] System.Net.Http.HttpRequestMessage reqMsg, ILogger log)
{
if (reqMsg.Properties.TryGetValue("MS_AzureFunctionsKeyId", out object val))
log.LogInformation($"MS_AzureFunctionsKeyId: {val}");
}
Code reference: WebJobs.Script.WebHost/Filters/AuthorizationLevelAttribute.cs#L77
Lets say I have two Azure functions:
public static class MyFunctions
[FunctionName("DoIt")]
public static async Task<HttpResponseMessage> DoIt(
[HttpTrigger(AuthorizationLevel.Function, "post", Route = null)]HttpRequestMessage req,
TraceWriter log)
{}
[FunctionName("DoSOmethingElse")]
public static async Task<HttpResponseMessage> DoOther(
[HttpTrigger(AuthorizationLevel.Function, "post", Route = null)]HttpRequestMessage req,
TraceWriter log)
{}
}
And then I configure the Function AppService to require AzureAD authentication. Lets say I wanted particular permissions like Role membership or other claims. I could do the following in a function I call at the top of each method:
inspect the req parameter for the bearer token, parse the JWT
look at the claims
Use AuthenticationContext or another JWT library to get Microsoft Graph tokens to get additional data
My question is are there options to do any of the following?
Create "before hook" function so every http function I write in that class or deploy to the service container goes through this inspection
Performa any of these authorization via attributes?
Access a claimsIdentity directly?
I've found some code sample related to what you are looking for:
stuartleeks/AzureFunctionsEasyAuth (Github)
So the interesting part is here (will give you the main idea):
[FunctionName("GetClaims")]
public static HttpResponseMessage GetClaims(
[HttpTrigger(AuthorizationLevel.Anonymous, "get", Route = null)]
HttpRequestMessage request,
TraceWriter log)
{
if (Thread.CurrentPrincipal.Identity.IsAuthenticated)
{
var claimsPrincipal = (ClaimsPrincipal)Thread.CurrentPrincipal;
var claims = claimsPrincipal.Claims.ToDictionary(c => c.Type, c => c.Value);
// Could use the claims here. For this sample, just return it!
return request.CreateResponse(HttpStatusCode.OK, claims, "application/json");
}
else
{
return request.CreateErrorResponse(HttpStatusCode.Unauthorized, "Not Authorized");
}
}
The simplistic solution would be to create a function that get the claims and you can call this function in every others functions.
Azure functions has Function Filters (As per the documentation it is still in preview.)
Function Filters provide a way to customize the WebJobs execution pipeline with your own logic. Filters are very similar in to ASP.NET Filters. They can be implemented as declarative attributes that can be applied to your job functions/classes.
Filters allow you to encapsulate common logic to be shared across many different functions. They also allow you to centralize logic for cross cutting concerns (e.g. validation, logging, error handling, etc.).
So you should be able to create a custom authorization filter with these information.
Could someone provide and example of how to use the Azure Function App Function Tester with an Example?
This is some default sample code for a webhook function:
#r "Newtonsoft.Json"
using System;
using System.Net;
using Newtonsoft.Json;
public static async Task<object> Run(HttpRequestMessage req, TraceWriter log)
{
log.Info($"Webhook was triggered!");
string jsonContent = await req.Content.ReadAsStringAsync();
dynamic data = JsonConvert.DeserializeObject(jsonContent);
if (data.first == null || data.last == null)
{
return req.CreateResponse(HttpStatusCode.BadRequest, new
{
error = "Please pass first/last properties in the input object"
});
}
return req.CreateResponse(HttpStatusCode.OK, new
{
greeting = $"Hello {data.first} {data.last}!"
});
}
This is an image of the Function App Function Tester in MS Azure
In the Tester blade, you could set the HTTP method, header, parameter,etc.
For the sample code you gave, you should provide Request body like a JSON format, then click the Run button.
{
"first": "Azure",
"last": "Functions"
}
Then you could find the function log.
Update:
If you want to use different http methods,
you could go to Integrate of the function, then check the Selected HTTP methods option.
you could set the methods in the function.json file.
For example:
"methods": [
"get",
"post",
"delete",
"head"
]
How do I use docuementdb binding with azure function http binding:
using System.Net;
public static async Task<HttpResponseMessage> Run(HttpRequestMessage req, out object locationDocument, TraceWriter log){
log.Info("C# HTTP trigger function processed a request.");
var data = await req.Content.ReadAsStringAsync();
return req.CreateResponse(HttpStatusCode.OK, $"{data}");
}
Getting this error:
error CS1988: Async methods cannot have ref or out parameters
This is not specific to Document DB. If your function is async, and you already used the return value for HTTP output binding, you would need to inject IAsyncCollector<T> for all other output bindings.
See the second example in this answer.