403 (Forbidden) while calling one azure function from another - azure

I need to call an azure function; fn(b), from another azure function; fn(a).
fn(a) -> fn(b)
Both these functions are in same function app. The problem is whenever I try to call (b), I get 403-Forbidden "data at the root level is invalid".
Is it possible to call an azure function from another azure function within same function app?
Function 1
public static class Function1
{
[FunctionName("Function1")]
public static async Task<HttpResponseMessage> Run(
[HttpTrigger(AuthorizationLevel.Anonymous, "get", "post", Route = null)]
HttpRequestMessage req, TraceWriter log)
{
log.Info("---- C# HTTP trigger function 1 processed a request.");
UploadToF2(log);
return null;
}
private static IRestResponse UploadToF2(TraceWriter log)
{
SomeObject payload = new SomeObject();
payload.One = "One";
payload.Two = 2;
payload.Three = false;
payload.Four = 4.4;
var Fn2Url = Convert.ToString(ConfigurationManager.AppSettings["F2Url"]);
log.Info("Hitting F2 at " + Fn2Url);
var method = Method.POST;
var client = new RestClient(Fn2Url);
var body = JsonConvert.SerializeObject(payload);
var request = new RestRequest(method);
request.RequestFormat = DataFormat.Json;
request.AddHeader("Content-Type", "application/json");
request.AddBody(payload); // uses JsonSerializer
IRestResponse response = client.Execute(request);
return response;
}
}
class SomeObject
{
public string One { get; set; }
public int Two { get; set; }
public bool Three { get; set; }
public double Four { get; set; }
}
Function 2
public static class Function2
{
[FunctionName("Function2")]
public static async Task<HttpResponseMessage> Run([HttpTrigger(AuthorizationLevel.Anonymous, "get", "post", Route = null)]HttpRequestMessage req, TraceWriter log)
{
log.Info("---- C# HTTP trigger function 2 processed a request.");
string payload = await req.Content.ReadAsStringAsync();
log.Info("payload == "+payload);
return null;
}
}
Additional Information:
F2Url is a fully qualified url coming from config.
I tried running both functions in localhost. It works. I.e. fn(a) can call fn(b) in localhost. However when I host both of them in Azure, fn(b) is not callable from fn(a).
I tried a hybrid test too. I.e. I kept one function in local and another one in Azure. It works this way too. I.e. I kept fn(a) in local and fn(b) in Azure, fn(b) is callable.
I tried calling fn(b) directly from Postman and again it works.
authLevel is anonymous for both functions
I have IP restrictions (Platform features > Networking > IP restrictions) applied to the Function app. When I remove IP restrictions, Function1 is able to call Function2. However keeping IP restrictions, the call is not allowed.
The only condition when fn(a) cannot call fn(b) is when both these functions are hosted in Azure.

403 (Forbidden) while calling one azure function from another
If don't add the client Ip in the IP restrictions, then you test it in you client will get 403 error. Not only call on azure function from another ,but also all functions are restricted if you don't add the client IP in the IP restrictions.
In your case, you need to add your test client Ip in the IP restrictions, then it will work.
Update:
Add the test result.

Works locally through a GET to Function1 when using:
var Fn2Url = "http://localhost:7071/api/Function2";
What value are you using in your configuration?

Call Function #2 by its full URL, since there's a front end layer that gets hit first before the request makes it to your function app. This is true even for functions calling each other within the same function app.
GET https://{func-app-name}.azurewebsites.net/api/function2
If the authLevel is not anonymous in function.json, pass in the API key as ?code= —
https://{func-app-name}.azurewebsites.net/api/function2?code={API-key}
or as a header —
GET https://{func-app-name}.azurewebsites.net/api/function2
x-functions-key: {API-key}
When running locally (Visual Studio/func host start), the value of authLevel is ignored. Looking at your decorator, AuthorizationLevel.Anonymous is present so most probably that's not it.
More on authorization keys here.
On top of that, you could do better that returning null in Function #2: 200 OK, 202 Accepted, 204 No Content, all valid choices depending on what's supposed to happen next (async/sync processing).

Related

How to return error message in Rest API when return value is a Byte array? [duplicate]

I have an Azure Function 2.x that reside on a static class that looks like this
[FunctionName("Register")]
public static async Task<IActionResult> Run([HttpTrigger(AuthorizationLevel.Anonymous, "post")]HttpRequest req, ILogger log)
{
MyTypeClass defReturn = new MyTypeClass();
HttpStatusCode defCode = HttpStatusCode.BadRequest;
/*
* Logics that might or might not changes
* defReturn and defCode value
*/
return StatusCode((int) defCode, JsonConvert.SerializeObject(defReturn))
}
How can i achieve the return StatusCode((int) defCode, JsonConvert.SerializeObject(defReturn)) part ? is there any such method or equivalent in Azure Functions 2.x ?
in Azure Functions 1.x i can do the equivalent with req.CreateResponse(defCode, defReturn) where req is HttpRequestMessage , but i'm trying to stick with 2.x template/standard
Additional explanation : The said Code should return HTTP 400 Bad Request with the defReturn as it's response body to the client. But when i change the defCode to HttpStatusCode.Accepted, it should return HTTP 202 Accepted with the same response body. How can i achieve this ?
Additional explanation#2 : (If i remember correctly) in ASP.NET Core 1.x i can exactly do like that, returning IActionResult by calling a static method StatusCode not StatusCodes (which is a static class that contains HTTP codes constants
Thank you
Quite late reply, but I was stumbling into the same problem today, so maybe this is helpful for other searchers
Option 1: Default Codes
This is stated in detail on the blog Here
Some codes like 200 and 400 are predefined and can be used by
return new OkObjectResult("Your message"); // 200
return new BadRequestObjectResult("Your error message"); // 400
These functions are not available for every known Status Codes but some of the most frequent.
Option 2: Manual setting Code
If you need specific codes, that are not provided by default, you can use the base classes and create them yourself.
To achieve the Teapot Response for example, you can just use
using Microsoft.AspNetCore.Http;
var result = new ObjectResult("Your message");
result.StatusCode = StatusCodes.Status418ImATeapot;
return result;
In this example, the Statuscode is used from the StatusCodes class, but you can use enter other codes as well (usually, just stick to these codes)
Also, the ObjectResult class offers additional formatting options, if needed.
You can create a model class in which you can define two properties, i.e. one form your status code and one for you Json object and later on you can return the complete model. Code sample would be like below:
public static class QueueTriggerTableOutput
{
[FunctionName("QueueTriggerTableOutput")]
[return: Table("outTable", Connection = "MY_TABLE_STORAGE_ACCT_APP_SETTING")]
public static Person Run(
[QueueTrigger("myqueue-items", Connection = "MY_STORAGE_ACCT_APP_SETTING")]JObject order,
ILogger log)
{
return new Person() {
PartitionKey = "Orders",
RowKey = Guid.NewGuid().ToString(),
Name = order["Name"].ToString(),
MobileNumber = order["MobileNumber"].ToString() };
}
}
public class Person
{
public string PartitionKey { get; set; }
public string RowKey { get; set; }
public string Name { get; set; }
public string MobileNumber { get; set; }
}
on the receiving front, you can catch both the property.
P.S.- you have to change the return type of your function.
Hope it helps.

SignalR (serverless) .NET console client not receiving messages

I'm trying to learn SignalR by creating a sample .NET console application that receive messages through a serverless SignalR, via a hosted Azure function app; I've been following this tutorial https://www.nikouusitalo.com/blog/qr-code-pings-with-azure-functions-and-azure-signalr/ but even though I get the connection stablished, I never get any message when running a POST request against a given Azure function in Postman. This is what I have:
Azure Functions
public static class Function1
{
[FunctionName("Function1")]
public static async Task<IActionResult> Run(
[HttpTrigger(AuthorizationLevel.Anonymous, "get", "post", Route = null)] HttpRequest req,
[SignalR(HubName = "QRCodeRehash")] IAsyncCollector<SignalRMessage> signalRMessages,
ILogger log)
{
log.LogInformation("C# HTTP trigger function processed a request.");
await signalRMessages.AddAsync(
new SignalRMessage
{
Target = "pingQR",
Arguments = new[] { "ping" }
});
var responseMessage = "Success";
return new OkObjectResult(responseMessage);
}
[FunctionName("negotiate")]
public static SignalRConnectionInfo GetOrderNotificationsSignalRInfo(
[HttpTrigger(AuthorizationLevel.Anonymous, "post")] HttpRequest req,
[SignalRConnectionInfo(HubName = "QRCodeRehash")] SignalRConnectionInfo connectionInfo)
{
return connectionInfo;
}
}
.NET Console application to receive message
public class SignalRConnection
{
public async void Start()
{
var url = "https://sample.azurewebsites.net/api";
var connection = new HubConnectionBuilder()
.WithUrl(url)
.WithAutomaticReconnect()
.Build();
// receive a message from the hub
connection.On<string, string>("pingQR", (user, message) => OnReceiveMessage(user, message));
await connection.StartAsync();
}
private void OnReceiveMessage(string user, string message)
{
Console.WriteLine($"{user}: {message}");
}
}
And following the tutorial's steps, I just call Function1 in Postman:
I always get "200 OK" and the logs can be seen in the Azure function as well; also, the negotiate works ok as it seems to connect every time to SignalR:
I've set CORS in the Azure function app to allow anything while I get this to work:
I would appreciate your help on this; it's odd how it works for the tutorial's owner, however, maybe something was left out that I need to do on my end, so any thoughts would be highly appreciated.
Thanks a lot!
Update: Thanks #Brennan's comment, my mistake was in to providing a different number of arguments than the ones detailed in the connection in the client. It's working as expected now.

Is Attribute Routing possible in Azure Functions

I am trying to enforce a route parameter to be guid but getting below error
"Exception while executing function: GetUser -> One or more errors
occurred. -> Exception binding parameter 'req' -> Invalid cast from
'System.String' to 'System.Guid'."
public static async Task<HttpResponseMessage> Run(
[HttpTrigger(AuthorizationLevel.Admin, "get", Route = "GetUser/{userId:guid}")] HttpRequestMessage req,
Guid userId, ILogger log)
{
}
The request i am making is http://localhost:7071/api/GetUser/246fb962-604d-4699-9443-fa3fa840e9eb/
Am i missing some thing? Cannot we enforce route parameter to be guid ?
Invalid cast from 'System.String' to 'System.Guid'
I can reproduce same issue when use Route constraint {userId:guid} in Azure httptrigger function on my side, you can try to open an issue to give a feedback.
Besides, if possible, you can try to call Guid.TryParse method to convert the string back to Guid value in function code, the following code is for your reference.
public static string Run([HttpTrigger(AuthorizationLevel.Function, "get", "post", Route = "GetUser/{userId:guid}")]HttpRequestMessage req, string userId, TraceWriter log)
{
log.Info("C# HTTP trigger function processed a request.");
Guid newGuid;
var resmes = "";
if (Guid.TryParse(userId, out newGuid))
{
resmes = "userid: " + newGuid;
}
else {
resmes = "error";
}
return resmes;
}

Why do I need to add an out parameter to my output bindings?

Suppose I have this signature for my Azure Function:
[FunctionName("DoStuff")]
public static async Task<HttpResponseMessage> Run(
[HttpTrigger(AuthorizationLevel.Function, "get", "post", Route = null)]HttpRequestMessage req,
TraceWriter log,
[Queue("output-queue")] string outputQueue)
{
}
This method only works for me if I add an out parameter to the output binding, outputQueue. I'm using VS 2017.3.2
The Microsoft examples DO NOT use the out parameter. Why do I need to add the out parameter?
You need to use 'out' when your function is doing an assignment to one of the parameters. For example if your parameter is a string, or a byte[], or a poco, you'll need to do an assignment.
Here's one example from the documentation where out is required:
#load "..\shared\order.csx"
using System;
public static void Run(Order myQueueItem, out Order outputQueueItem,TraceWriter log)
{
log.Info($"C# Queue trigger function processed order...");
log.Info(myQueueItem.ToString());
outputQueueItem = myQueueItem;
}
You don't need to use out if your function is calling methods on the parameter. Stream, ICollector and IAsyncCollector all fall in this category. Here are two examples:
public async static Task ProcessQueueMessageAsync(
string blobName,
Stream blobInput,
Stream blobOutput)
{
await blobInput.CopyToAsync(blobOutput, 4096, token);
}
And:
#load "..\shared\order.csx"
using System.Net;
public static async Task<HttpResponseMessage> Run(Order req, IAsyncCollector<Order> outputQueueItem, TraceWriter log)
{
log.Info("C# HTTP trigger function received an order.");
log.Info(req.ToString());
log.Info("Submitting to processing queue.");
if (req.orderId == null)
{
return new HttpResponseMessage(HttpStatusCode.BadRequest);
}
else
{
await outputQueueItem.AddAsync(req);
return new HttpResponseMessage(HttpStatusCode.OK);
}
}
You mentioned that your example code had a problem with your IAsyncCollector parameter. As mentioned by others, it looks like the problem is you're missing your [Queue(..)] attribute.
That documentation which you are referring might be old.
Here is why out is needed.
I want to pass set of inputs to function, (Multiple Triggers) and set of outputs from a function (Send data to Queue, Output Reference of completion)
We need an indication what needs to be passed and what needs to be sent out of the function.
function(Class1 i, Class2 j, out Class3 k, out Class4 l) {
}
out refers to the reference of the object that is sending out of function and does not need deserialization of the object during the call.
while i and j in the above needs deserialization, while Class3 and Class4 are not.
There need to be extra cycle need to determine the mapping and auto recognize whether it is in or out parameters.
Having an 'out' make the process simple and helps to execute the code quicker.
I believe you are rather missing an attribute to denote the type of your output binding. For instance, if you want to use a Storage Queue, declare your function as
[FunctionName("DoStuff")]
public static async Task<HttpResponseMessage> Run(
[HttpTrigger(AuthorizationLevel.Function, "get", "post", Route = null)]HttpRequestMessage req,
TraceWriter log,
[Queue("my-out-queue")] IAsyncCollector<DetailInfo> outputQueue)
{
}

How to get client IP address in Azure Functions C#?

I'm writing a function in C# using Azure Functions and need to get the ip address of the client that called the function, is this possible?
Here is an answer based on the one here.
#r "System.Web"
using System.Net;
using System.Web;
public static HttpResponseMessage Run(HttpRequestMessage req, TraceWriter log)
{
string clientIP = ((HttpContextWrapper)req.Properties["MS_HttpContext"]).Request.UserHostAddress;
return req.CreateResponse(HttpStatusCode.OK, $"The client IP is {clientIP}");
}
you should use these function Get the IP address of the remote host
request.Properties["MS_HttpContext"] is not available if you debug precompiled functions local
request.Properties[RemoteEndpointMessageProperty.Name] is not available on azure
private string GetClientIp(HttpRequestMessage request)
{
if (request.Properties.ContainsKey("MS_HttpContext"))
{
return ((HttpContextWrapper)request.Properties["MS_HttpContext"]).Request.UserHostAddress;
}
if (request.Properties.ContainsKey(RemoteEndpointMessageProperty.Name))
{
RemoteEndpointMessageProperty prop;
prop = (RemoteEndpointMessageProperty)request.Properties[RemoteEndpointMessageProperty.Name];
return prop.Address;
}
return null;
}
Update 21.08.2018:
Now Azure Functions are behind a LoadBalancer --> we have to inspect Request-Headers to determine the correct Client IP
private static string GetIpFromRequestHeaders(HttpRequestMessage request)
{
IEnumerable<string> values;
if (request.Headers.TryGetValues("X-Forwarded-For", out values))
{
return values.FirstOrDefault().Split(new char[] { ',' }).FirstOrDefault().Split(new char[] { ':' }).FirstOrDefault();
}
return "";
}
Here is an extension method based on what I am seeing in
.Net Core 3.1
public static IPAddress GetClientIpn(this HttpRequest request)
{
IPAddress result = null;
if (request.Headers.TryGetValue("X-Forwarded-For", out StringValues values))
{
var ipn = values.FirstOrDefault().Split(new char[] { ',' }).FirstOrDefault().Split(new char[] { ':' }).FirstOrDefault();
IPAddress.TryParse(ipn, out result);
}
if (result == null)
{
result = request.HttpContext.Connection.RemoteIpAddress;
}
return result;
}
.NET 6.+
public static IPAddress GetClientIpn(this HttpRequestMessage request)
{
IPAddress result = null;
if (request.Headers.TryGetValues("X-Forwarded-For", out IEnumerable<string> values))
{
var ipn = values.FirstOrDefault().Split(new char[] { ',' }).FirstOrDefault().Split(new char[] { ':' }).FirstOrDefault();
IPAddress.TryParse(ipn, out result);
}
return result;
}
Now that Azure functions get an HttpRequest parameter, and they're behind a load balancer, this function to get the IP address works for me:
private static string GetIpFromRequestHeaders(HttpRequest request)
{
return (request.Headers["X-Forwarded-For"].FirstOrDefault() ?? "").Split(new char[] { ':' }).FirstOrDefault();
}
Update 18-Oct-2019:
The solution I tried is much easier and quicker and is mentioned below stepwise. But some more lengthy/tricky alternates are available # https://learn.microsoft.com/en-us/azure/azure-monitor/app/ip-collection:
Login into Azure portal.
Open a new tab in same browser while you are logged in and dial “http://Resources.Azure.Com”
This is Azure back end services portal so being slightly careful in making changes would be great.
Expand SUBSCRIPTIONS section from the left panel and expand your Azure Subscription where app insight resource is located.
Expand Resource Groups section and expand the Resource Group where app insights resource is.
Expand the Providers section and find the Microsoft.Insights provider and expand it.
Expand the Components section and find and select your App Insight Instance by name.
On the right top change your mode to Read Write from Read Only.
Click EDIT button on the Rest API call.
ADD NEW “"DisableIpMasking": true property to properties section.
Press PUT button to apply changes.
Now your App Insight is enabled to start collecting Client IP addresses.
Do some queries on the Function.
Refresh and Test the App Insights data after about 5 to 10 minutes.
As mentioned already by others, the old method of looking at MS_HttpContext no longer works. Further, while the method of looking at the headers for X-Forwarded-For does work, it only works after being published in Azure - it doesn't return a value when you're running locally. That may matter if you prefer testing locally to minimize any potential cost-impact, but still want to be able to see that everything works correctly.
To see the IP address even when running locally, try this instead:
using Microsoft.AspNetCore.Http;
And then:
String RemoteIP = ((DefaultHttpContext)req.Properties["HttpContext"])?.Connection?.RemoteIpAddress?.ToString();
This is working for me currently in Azure Functions V3.0.
In a .NET 6.0 function, within the Run() function of the operation, this can be accessed of the HttpRequest req object:
public static class PingOperation
{
[FunctionName("ping")]
public static async Task<IActionResult> Run(
[HttpTrigger(AuthorizationLevel.Function, "get", "post", Route = null)] HttpRequest req,
ILogger log)
{
string requestBody = await new StreamReader(req.Body).ReadToEndAsync();
log.LogInformation($"PingOperation requested from: {req.HttpContext.Connection.RemoteIpAddress}:{req.HttpContext.Connection.RemotePort}");
string responseMessage = "This HTTP triggered function executed successfully.";
return new OkObjectResult(responseMessage);
}
}

Resources