Azure Function as a Web API performance and pricing - azure

We are planning to build a web application, and I was hoping someone could help us to decide whether to use Azure App Service or Azure Function for providing rest API to the client side.
Our requirements are as follows.
Authentication and authorization
CRUD on Azure SQL and Cosmos DB
Multi region
100,000,000 API calls per month
At first, we were going to build the backend using Azure App Service. But after studying pros and cons on Azure Functions, Azure Functions became more appealing to us.
So is it even a good idea to build a web application that depends on Azure Functions as a REST API provider?
Does anyone have an experience building, managing and scaling up and out Azure Functions as a REST API provider?

Is it even a good idea to build a web application that depends on Azure Functions as a REST API provider?
It seems you are planning to have REST API using Web Service or Azure Function. Your decision is perfect I would say. For Azure Function its not mandatory to have web service for that. Azure function would be best option for you. You can implement all the feature that Web API provides. So if your target is only develop API then you can start with Azure Function with no other alternative. Its outstanding actually!
Does anyone have an experience building, managing and scaling up and out Azure Functions as a REST API provider?
I am working with Azure Function for our AI Base Bot with LUIS integration. From my understanding it's a very easily maintainable, fastest response time, you can build it from anywhere. So you can undoubtedly go with Azure function.
Why Choose Azure Function:
It's stateless, need not any server to run
Full REST, can call from anywhere any Region
Can develop both Azure Portal and local Visual Studio
Cost-effective, you can pay only how much you use.
Multiple language support
Easy Authorization and Authentication functionality
No limit of calling as per your consumption plan
Do A Lot With Azure Function:
You can develop a robust API service with Azure functions. It has many outstanding features. Please check Check here
Authorization and Authentication:
You can simply integrate your authorization and authentication on your function App. Even you can implement it on each function separately or on a full application. It supports most of the popular authentication provider for example:
Azure Active Directory
Microsoft Identity
Goggle
Facebook
Twitter auth
See how can you implement authentication:
Step:1
Step:2
Rest Function Code Sample:
Here I am giving you a simple code snippet to start with: though it's on Azure Table Storage, but help you to develop azure function and CRUD concept.
Your Sample Class:
public class YourSampleClass
{
public string PartitionKey { get; set; }
public string RowKey { get; set; }
}
Table Storage Class:
public class TableStorageClass
{
public TableStorageClass()
{
}
public TableStorageClass(DynamicTableEntity entity)
{
PartitionKey = entity.PartitionKey;
RowKey = entity.RowKey;
}
public string PartitionKey { get; set; }
public string RowKey { get; set; }
}
Azure Function V2 Example:
public static class FunctionReadFromTableStorage
{
[FunctionName("FunctionReadFromTableStorage")]
public static async Task<IActionResult> Run(
[HttpTrigger(AuthorizationLevel.Anonymous, "get", "post", Route = null)] HttpRequest req,
ILogger log)
{
log.LogInformation("C# HTTP trigger function processed a request.");
//Read Request Body
var content = await new StreamReader(req.Body).ReadToEndAsync();
//Extract Request Body and Parse To Class
YourSampleClass objYourSampleClass = JsonConvert.DeserializeObject<YourSampleClass>(content);
// Validate param because PartitionKey and RowKey is required to read from Table storage In this case , so I am checking here.
dynamic validationMessage;
if (string.IsNullOrEmpty(objYourSampleClass.PartitionKey))
{
validationMessage = new OkObjectResult("PartitionKey is required!");
return (IActionResult)validationMessage;
}
if (string.IsNullOrEmpty(objYourSampleClass.RowKey))
{
validationMessage = new OkObjectResult("RowKey is required!");
return (IActionResult)validationMessage;
}
// Table Storage operation with credentials
var client = new CloudTableClient(new Uri("https://YourStorageURL.table.core.windows.net/"),
new Microsoft.WindowsAzure.Storage.Auth.StorageCredentials("YourStorageName", "xtaguZokAWbfYG4QDkBjT+YourStorageKey+T/kId/Ng+cl3TfYHtg=="));
var table = client.GetTableReference("YourTableName");
//Query filter
var query = new TableQuery()
{
FilterString = string.Format("PartitionKey eq '{0}' and RowKey eq '{1}'", objYourSampleClass.PartitionKey, objYourSampleClass.RowKey)
};
//Request for storage query with query filter
var continuationToken = new TableContinuationToken();
var storageTableQueryResults = new List<TableStorageClass>();
foreach (var entity in table.ExecuteQuerySegmentedAsync(query, continuationToken).GetAwaiter().GetResult().Results)
{
var request = new TableStorageClass(entity);
storageTableQueryResults.Add(request);
}
//As we have to return IAction Type So converting to IAction Class Using OkObjectResult We Even Can Use OkResult
var result = new OkObjectResult(storageTableQueryResults);
return (IActionResult)result;
}
}
Point To Remember:
In case of Azure Portal execution just get rid of FunctionReadFromTableStorage class
You Need following reference to execute above code
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;
using Microsoft.WindowsAzure.Storage.Table;
using System.Collections.Generic;
Postman Request Pattern:
Function Invoke Sample:
{
"PartitionKey": "Your Param According to Table Storage Design" ,
"RowKey": "Your Param According to Table Storage Design",
"Directory": "Your Param According to Table Storage Design"
}
See The Screen Shot:
Post Man Response:
Response is subject to my own table design
[
{
"partitionKey": "Microsoft SharePoint Server",
"rowKey": "2016"
}
]
See The Screen Shot Below:
Note: For CosmosDb Integration you could check here. Azure SQL with Function take a look here.

Related

Calling WebApi from Azure function securely

I have a scenario where I have asp.net core web api and a azure function. I call web api from azure time triggered function for every 1 hour. I do not have authentication enabled on the web api and I do not want public to access it. Only my azure function should be able to access the web api. How can I restrict web api to access from public but only from azure function with out implementing authentication.
I tried the below,
In webapi appsettings file, I updated "AllowedHosts":"https://testfuntionapp.azurewebsites.net". My testfuntionapp is unable to access web api with this change.
I am trying for a cost effective solution.
Please check if my findings help to:
How can I restrict web api to access from public but only from azure function without implementing authentication.
This is where the Azure Virtual Networks comes to.
Create the Virtual Network, configure the Azure Function to only be callable on this VNet and also can configure your core app access to the VNet.
By using Private Endpoints, resources are accessible only via your Virtual Network.
If Virtual Network Integration enabled, then the Azure Function is able to access the designated resource via the configured private endpoints, which is a higher level of security.
References:
Michael S Collier's article on Azure Functions with Private Endpoints.
Access an App Service integrated with a Virtual Network from other Azure resources like Azure Functions
I would also recommend private endpoints as suggested by HariKrishnaRajoli.
In theory it would also suffice to just configure http header filtering for access restriction rules and only allow requests containing a secret header known only to your azure function code. This is probably comparable to "basic" HTTP authentication security, and weaker than the other options.
As an alternative, you can use AAD authentication without really "implementing" much authentication code.
Configure Easy Auth on the WebApi
Configure ManagedIdentity for your Azure Function
Give your the Managed Identity access to call the WebApi using "App Role Assignments"
Extend your Azure Function to acquire and pass access token when calling the WebApi
If you only want your Azure function to be able to access your web API, you can restrict access to your web API by IP address.
In your web API's appsettings.json file, add the following:
"AllowedHosts": "https://testfuntionapp.azurewebsites.net"
This will restrict access to your web API to only requests coming from the specified Azure function.
The fastest and "dirtiest" way to do this, is to pass an API key as a header to your REST API from the function.
You can hardcode the API key to your config file or load it from your DB.
My implementation:
namespace CoreApi.Middleware
{
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
public class RequirePartnerApiKeyAttribute : Attribute, IAsyncActionFilter
{
private const string ApiKeyHeaderName = "x-partner-apikey";
public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
{
//----------------------------------------------------
// Validate Api Key
//----------------------------------------------------
if (!context.HttpContext.Request.Headers.TryGetValue(ApiKeyHeaderName, out var apiKey) ||
string.IsNullOrEmpty(apiKey))
{
context.Result = new UnauthorizedResult();
return;
}
var partnerService = context.HttpContext.RequestServices.GetRequiredService<IPartnerService>();
var apiRequest = new PartnerAuthenticateRequest { PartnerApiKey = apiKey };
var partner = partnerService.Authenticate(apiRequest, new CancellationToken()).Result;
if (partner == null)
{
context.Result = new UnauthorizedResult();
return;
}
else
{
if (Enum.IsDefined(typeof(ApiKeyTypes), partner.PartnerName))
context.HttpContext.Items["APIKeyName"] = Enum.Parse(typeof(ApiKeyTypes), partner.PartnerName);
}
//----------------------------------------------------
await next();
}
}
}
You can access the API key from any middleware by calling: context.HttpContext.Items["APIKeyName"]

How to ensure user identity in Azure Function Call? ASP NET Core Web App

I am building an ASP NET Core web application that will perform a delete operation for a user on a remote datastore. Currently, I have a frontend web app that communicates with Azure Functions to delete a user. I have a concern with security. How can I make sure the user is the logged-in user? Currently, in my ASP Net Core Web App, I have the user authenticate with AAD using Microsoft as an Identity Provider. From a client-side, the user is verified and taken care of. However, my app calls the Azure Functions endpoints. Is there an extra level of security that I could add. I only want to give my web app (registered in Azure) the ability to call the endpoints.
There are two main ways to authenticate your users. One is to use the App Services authentication options: https://learn.microsoft.com/en-us/azure/app-service/configure-authentication-provider-aad. This is by far the easier method.
If you need more control over things than what is available in the service, then you can implement validation within the Function itself as Thiago mentioned. Ben Morris did a fairly detailed post on how to implement your own OAUTH provider inside your Function App with C#: https://www.ben-morris.com/custom-token-authentication-in-azure-functions-using-bindings/ If you can take care of the validation in the Function's DI container, validation inside a specific function is fairly clean as seen in the post:
public class ExampleHttpFunction
{
private readonly IAccessTokenProvider _tokenProvider;
public ExampleHttpFunction(IAccessTokenProvider tokenProvider)
{
_tokenProvider = tokenProvider;
}
[FunctionName("ExampleHttpFunction")]
public IActionResult Run(
[HttpTrigger(AuthorizationLevel.Anonymous, "get", Route = "example")] HttpRequest req, ILogger log)
{
var result = _tokenProvider.ValidateToken(req);
if (result.Status == AccessTokenStatus.Valid)
{
log.LogInformation($"Request received for {result.Principal.Identity.Name}.");
return new OkResult();
}
else
{
return new UnauthorizedResult();
}
}
}

Is there a way to secure an Azure Function that will only be called from a specific Azure Logic App?

I understand that Azure Functions are potentially open endpoints on the internet if I read Microsoft’s documentation correctly and per conversations with a friend who has some experience working with web development paradigms that Azure Functions leverages. A cursory reading of security forums and stack overflow questions on the topic leads me to understand at least a couple options of securing them namely
Azure Active Directory
Shared Access Signatures (SAS) and
Azure Virtual Networks.
Context/ What does my Azure Function do? It manages a blob container related to an ETL of vendor data from a SFTP source to a SQL Endpoint which this ETL utilizes an intermediary blob container for file transfer and long term cold storage of source data. The Azure Function moves the blobs from one container to an archive container after they have been loaded to the SQL endpoint. Why Azure Function to manage the blob containers?
SSIS lacks ability to perform blob manipulation (i.e copy and delete)
Logic App lacks ability to perform a join (of files loaded to SQL endpoint and file names in blob container)
An example of one of the functions is shown here below:
using System.IO;
using System.Threading.Tasks;
using Microsoft.Azure.WebJobs;
using Microsoft.Azure.WebJobs.Extensions.Http;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
using System.Net.Http;
using System.Net;
using Microsoft.WindowsAzure.Storage.Blob;
using System.Collections.Generic;
using System.Text;
namespace AFA_ArchiveBlob
{
public static class HttpTrigger_BlobInput
{
[FunctionName("HttpTrigger_BlobInput")]
public static async Task<HttpResponseMessage> Run(
//public static async Task<IActionResult> Run(
[HttpTrigger(AuthorizationLevel.Function, "get", Route = "{name}")] HttpRequest req,
string name,
ILogger log,
[Blob("{name}/blobname",FileAccess.ReadWrite,Connection = "AzureWebJobsStorage")] CloudBlobContainer myCloudBlobContainer
)
{
//Execution Logged.
log.LogInformation($"HttpTrigger_BlobInput - C# HTTP trigger function processed a request.");
//Run the query against the blob to list the contents.
BlobContinuationToken continuationToken = null;
List<IListBlobItem> results = new List<IListBlobItem>();
do
{
var response = await myCloudBlobContainer.ListBlobsSegmentedAsync(continuationToken);
continuationToken = response.ContinuationToken;
results.AddRange(response.Results);
}
while (continuationToken != null);
//Query the names of the blobs. Todo: can this be a single line linq query select instead?
List<string> listBlobNames = new List<string>();
foreach (CloudBlockBlob b in results)
{
listBlobNames.Add(b.Name);
}
//Serialize the list of blob names to json for passing to function caller via return statement
var jsonReturn = JsonConvert.SerializeObject(listBlobNames);
log.LogInformation("Returning the following JSON");
log.LogInformation(jsonReturn);
return new HttpResponseMessage(HttpStatusCode.OK)
{
Content = new StringContent(jsonReturn, Encoding.UTF8, "application/json")
};
}
}
}
Firstly, even though using keys might be convenient, I see that official documentation advises against using keys to secure function endpoint in production scenarios.
I suggest it would be a better choice to go with Azure Active Directory for security.. as explained here Secure an HTTP endpoint in production
How to Implement
I see two possible approaches:
1. Simple Approach: Check that calling application is your Azure logic app specifically
Enable Azure Active Directory Authentication for your Azure Function App. You can simply use Express settings (with create a new Azure AD app)
Enable Managed Service Identity for your Logic App.
Find out appid for Managed Service Identity associated with your logic app.. go to Azure Portal > Azure Active Directory > Enterprise Applications > All Applications > Relevant Service Principal (Explained in more detail with screenshots in another SO post here)
Authenticate your logic app to Azure function using Managed Service Identity as explained here.. Authenticate with managed identity in logic app.. note that resource being accessed will be your Azure function.
In your function code, now you can check that appid claim in access token should exactly match the appid for logic app (i.e. logic app is the one calling your function).. otherwise you can reject the call with Unauthorized exception.
2. A more declarative Approach: Have an application permission defined for Azure function app and check for this permission/role being present in auth token from client calling your function
This approach is a little more declarative, as you define an application permission that needs to be assigned to any application that can call your Azure function.
Enable Azure Active Directory Authentication for your Azure Function App. You can simply use Express settings (with create a new Azure AD app)
Now go to Azure Active Directory > App Registrations > App registration for your function app > Manifest
Add a new application role.. using json like this:
"appRoles": [
{
"allowedMemberTypes": [
"Application"
],
"displayName": "Can invoke my function",
"id": "fc803414-3c61-4ebc-a5e5-cd1675c14bbb",
"isEnabled": true,
"description": "Apps that have this role have the ability to invoke my Azure function",
"value": "MyFunctionValidClient"
}]
Enable Managed Service Identity for your Logic App.
Find out appid for Managed Service Identity associated with your logic app.. as already explained in approach 1 above
Assign the app permission to this managed service identity..
New-AzureADServiceAppRoleAssignment -ObjectId <logicappmsi.ObjectId> -PrincipalId <logicappmsi.ObjectId> -Id "fc803414-3c61-4ebc-a5e5-cd1675c14bbb" -ResourceId <yourfunctionaadapp.ObjectId>
Authenticate your logic app to Azure function using Managed Service Identity.. as already explained in approach 1 above
Now, in the auth token received by your function, you can check that the role claims collection must contain a role named "MyFunctionValidClient" otherwise you can reject the call with Unauthorized exception.
In addition to the above steps explained by #Rohit Below step is important:
Go to Host.json of the function.
Default authLevel : "function" should be changed to "authLevel": "anonymous".
This does not mean anyone can access the function as with Log on AD sign-in authentication required sign user however with managed identity in logic app function authenticate with service principle.

Azure Maps API workaround for unsupported regions

I am using Azure Maps API to for geocode reversal. Sometimes, it works and sometimes it doesn't. While researching I found a document- https://learn.microsoft.com/en-us/azure/azure-maps/about-azure-maps. It says that the API is currently not supported for below countries-
Argentina
China
India
Morocco
Pakistan
South Korea
I am looking for a workaround. Is there any way I can use the API from India?
One option is to use a VPN into a supported region. Note, if you do this, don't share the map publicly as it will show things that don't align with the views of these countries and can get your company into some trouble. Many of these countries will likely be unblocked in early 2019.
Here's a workaround which I figured out.
Create a function app in Azure and make an Http call to the Azure maps API. Make sure the app is hosted in a region where the Azure maps API is allowed (e.g. Central US).
using System.Net;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Primitives;
using Newtonsoft.Json;
using System.Net.Http;
public static async Task<IActionResult> Run(HttpRequest req, ILogger log)
{
string lattlong = req.Query["lattlong"];
HttpClient httpClient = new HttpClient();
string location = httpClient.GetStringAsync("https://atlas.microsoft.com/search/address/reverse/json?api-version=1.0&subscription-key=<secret_key>&query=" + lattlong).Result;
dynamic data = Newtonsoft.Json.Linq.JObject.Parse(location);
return lattlong != null
? (ActionResult)new OkObjectResult(data.addresses[0].address.municipality + ", " + data.addresses[0].address.country)
: new BadRequestObjectResult("Failed to get location.");
}
The function app should take the lattidue and longitude as parameters and make the Http call.
https://<function_app>.azurewebsites.net/api/GetLocation?code=1/<function_secret_key>&lattlong=13.097045900000001,77.59058569999999
The response from the Azure Maps API is returned successfully by the function app.

Track asynchronous Azure operations using the fluent API

I know you can track normal operations using the standard API: https://learn.microsoft.com/en-us/azure/azure-resource-manager/resource-manager-async-operations
However I was wondering if there was a known way to utilize the Fluent Azure Management Libraries to track long async operations such as VM operations etc. For example the VM restart method is a void Task which does not return an operation-id for tracking.
async Task IVirtualMachineScaleSetVM.RestartAsync(CancellationToken cancellationToken)
{
await this.RestartAsync(cancellationToken);
}
Cheers!
AFAIK, it seems that it's hard to trace VM restart status which does not return an operationId.
Logging in the fluent Azure management libraries for .NET leverages the underlying AutoRest service client tracing.
Create a class that implements Microsoft.Rest.IServiceClientTracingInterceptor. This class will be responsible for intercepting log messages and passing them to whatever logging mechanism you're using.
class ConsoleTracer : IServiceClientTracingInterceptor
{
public void ReceiveResponse(string invocationId, HttpResponseMessage response) { }
}
Before creating the Microsoft.Azure.Management.Fluent.Azure object, initialize the IServiceClientTracingInterceptor you created above by calling ServiceClientTracing.AddTracingInterceptor() and set ServiceClientTracing.IsEnabled to true. When you create the Azure object, include the .WithDelegatingHandler() and .WithLogLevel() methods to wire up the client to AutoRest's service client tracing.
ServiceClientTracing.AddTracingInterceptor(new ConsoleTracer());
ServiceClientTracing.IsEnabled = true;
var azure = Azure
.Configure()
.WithDelegatingHandler(new HttpLoggingDelegatingHandler())
.WithLogLevel(HttpLoggingDelegatingHandler.Level.Basic)
.Authenticate(credentials)
.WithDefaultSubscription();
For more details, you could refer to this article.

Resources