How to use Azure resource health API in Azure function? - azure

I have a situation where i want to show the Health of my Azure services to the public forum i am trying to architect it something like Azure timer function will get the status of the resources and dump in database which can be displayed by the external App by calling API i tried few stuff but Azure Resource health API is protected by Azure security and which expects the Oauth2 implicit flow which i suppose is initiated in browser and through user interaction is there any way we can bypass this and have simply access the resource health API

In Azure function, we can use Azure MSI to require token then we can call resource health API with the token.
For example
Enable Azure MSI in Azure function
Assign Azure RABC role Contributor role to the MSI
Code
// Install package "Azure.Identity"
private static HttpClient httpClient = new HttpClient();
[FunctionName("Function2")]
public static async Task Run([TimerTrigger("0 */5 * * * *", RunOnStartup=true)]TimerInfo myTimer, ILogger log)
{
log.LogInformation($"C# Timer trigger function executed at: {DateTime.Now}");
var creds = new DefaultAzureCredential();
var scopes = new string[] { "https://management.azure.com/.default" };
AccessToken token= await creds.GetTokenAsync(new TokenRequestContext(scopes));
string uri = " ";
using (HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Get,uri)) {
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", token.Token);
using (HttpResponseMessage response = await httpClient.SendAsync(request)) {
if (response.IsSuccessStatusCode) {
var str = await response.Content.ReadAsStringAsync();
log.LogInformation(str);
}
}
}

Related

How to protect an Azure Function with System assigned Managed Identity

I have a simple azure function (Authorization level set to Anonymous) with an HTTP trigger
public static async Task<IActionResult> Run(HttpRequest req, ILogger log)
{
return new OkObjectResult("This is a response from secured function.");
}
I want to secure it with Managed Identities, so I turned on system-assigned identity
And in my AD enterprise app registration, I can see created a system-assigned identity so I copied its Application ID value
and for testing purposes, I want to trigger it from another azure function
public static async Task<IActionResult> Run(
[HttpTrigger(AuthorizationLevel.Anonymous, "get", "post", Route = null)] HttpRequest req,
ILogger log)
{
var clientID = {application / client ID of system identity}
var azureServiceTokenProvider = new AzureServiceTokenProvider();
var accessToken = await azureServiceTokenProvider.GetAccessTokenAsync(clientID);
// Call secured azure function
using (var client = new HttpClient())
{
client.BaseAddress = new Uri("https://my-secured-function.azurewebsites.net");
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", accessToken);
var response = await client.GetAsync("/api/HttpTrigger1");
if (response.IsSuccessStatusCode)
{
var result = await response.Content.ReadAsStringAsync();
return new OkObjectResult(result);
}
else
{
return new OkObjectResult("Error - " + response.StatusCode);
}
}
}
The code works, it generates & sends a token within the HTTP request. However, the "secured" azure function is still publicly available.
The question is how can I protect the "secured" azure function, so it can be triggered only with an HTTP request with a generated token using managed identities.
The System Assigned Managed Identity you enabled here only gives an identity to your app, through a Service Principal. This is not blocking any access unlike the Firewall/IP Restrictions (that you could use but I assume that you want to rely on Identity only here).
What you are looking for is basically Authentication with Azure AD. From there, you could use the Managed Identity (if this is suitable) of the caller to authenticate against your app through the AAD. That also works with any Service Principal or users.
You need to click "Authentication" on the left panel of your Function app. Then add Microsoft AD as an identity provider.
Add the necessary settings(you can let Azure create an App registration or use the managed identity which you have already created. With this step, you will lock your Azure function app so it is triggered only if a valid AD token is provided to it.

Http Trigger Azure function should not accessible outside from App Service

I am using http trigger azure function in my App Service. I want this http trigger azure function should not accessible publicly and accessible only from the App Service.
Currently I have created host key for http trigger function and I am using this for authenticate request.
Which authentication method should I use for this? Any thoughts.
Azure Function:
public static class RemoveSubscriptionsForPayers
{
[FunctionName(nameof(RemoveSubscriptionsForPayers))]
public static async Task<IActionResult> Run(
[HttpTrigger(AuthorizationLevel.Function, "post", Route = null)] HttpRequest req,
[Inject] ILoggingService loggingService,
[Inject] ICommandValidator commandValidator,
[Inject] ICommandHandler<ResultDto,RemoveSubscriptionsForPayersCommand> commandHandler)
{
var logger = new Logger(loggingService);
try
{
IActionResult actionResult = null;
string requestBody = await new StreamReader(req.Body).ReadToEndAsync();
logger.Info($"RemoveSubscriptionsForPayersCommand received on {nameof(RemoveSubscriptionsForPayers)}");
var command = requestBody.AsPoco<RemoveSubscriptionsForPayersCommand>();
if (commandValidator.Validate<RemoveSubscriptionsForPayersCommand>(req, command, new RemoveSubscriptionsForPayersCommandValidator(), logger, ref actionResult))
{
var response =await commandHandler.HandleAsync(command, logger);
actionResult = new OkObjectResult(response);
}
return actionResult;
}
catch (Exception ex)
{
logger.Error($"Exception while processing {nameof(RemoveSubscriptionsForPayers)}", ex,
nameof(RemoveSubscriptionsForPayers));
throw;
}
}
}
You can use Azure AD to authenticate your functions, which is more secure.
After opening Azure AD authentication, you need to obtain an access token.
Please open Azure active directory in the Azure portal and find App registrations, you need to search for the function you registered in Azure AD in the search box.
you need to find the parameter values of the url and body to obtain the token here.
URL to get access token
Body:
client_id
scope
username and password
grant_type: The value of grant_type is fixed, both are password.
client_secret
You can get Token like this:
Now you can use the access token of Azure AD to access your functions.
The request header name is Authorization, and the header value is Bearer <access-token>:

Azure Function - Call Google API from within Azure Function (C#)

I am attempting to create an Azure Function using .NET Core to call to the YouTube API to retrieve some metrics on my videos.
Before calling the API I need to Authenticate with Google in a server to server method since this function will run daily with NO user interaction.
I've followed a number of examples (https://developers.google.com/api-client-library/dotnet/guide/aaa_oauth) and I'm having no luck getting properly authenticated when running from Azure.
Is this possible? And can anyone point me to an example of this working?
For server-to-server interactions you need a service account, which is an account that belongs to your application instead of to an individual end-user. Your application calls Google APIs on behalf of the service account, and user consent is not required.
public class Program
{
// A known public activity.
private static String ACTIVITY_ID = "z12gtjhq3qn2xxl2o224exwiqruvtda0i";
public static void Main(string[] args)
{
Console.WriteLine("Plus API - Service Account");
Console.WriteLine("==========================");
String serviceAccountEmail = "SERVICE_ACCOUNT_EMAIL_HERE";
var certificate = new X509Certificate2(#"key.p12", "notasecret", X509KeyStorageFlags.Exportable);
ServiceAccountCredential credential = new ServiceAccountCredential(new ServiceAccountCredential.Initializer(serviceAccountEmail)
{
Scopes = new[] { PlusService.Scope.PlusMe }
}.FromCertificate(certificate));
// Create the service.
var service = new PlusService(new BaseClientService.Initializer()
{
HttpClientInitializer = credential,
ApplicationName = "Plus API Sample",
});
Activity activity = service.Activities.Get(ACTIVITY_ID).Execute();
Console.WriteLine(" Activity: " + activity.Object.Content);
Console.WriteLine(" Video: " + activity.Object.Attachments[0].Url);
Console.WriteLine("Press any key to continue...");
Console.ReadKey();
}
}
The above sample code creates a ServiceAccountCredential. The required scopes are set and there is a call to FromCertificate, which loads the private key from the given X509Certificate2. As in all other samples code, the credential is set as HttpClientInitializer.
For more details about service account flow you could refer to this article.

Using Azure Managed Service Identities to scale App Service Plan, but none listed

I am attempting to set up an Azure Function that will scale our App Service Plan to be larger during business hours, and smaller outside of them. I drew from the answer to this question.
public static class ScaleUpWeekdayMornings
{
[FunctionName(nameof(ScaleUpWeekdayMornings))]
public static async Task Run([TimerTrigger("0 0 13 * * 1-5")]TimerInfo myTimer, ILogger log) // Uses GMT timezone
{
var resourceGroup = "DesignServiceResourceGroup";
var appServicePlanName = "DesignService";
var webSiteManagementClient = await Utilities.GetWebSiteManagementClient();
var appServicePlanRequest = await webSiteManagementClient.AppServicePlans.ListByResourceGroupWithHttpMessagesAsync(resourceGroup);
appServicePlanRequest.Body.ToList().ForEach(x => log.LogInformation($">>>{x.Name}"));
var appServicePlan = appServicePlanRequest.Body.Where(x => x.Name.Equals(appServicePlanName)).FirstOrDefault();
if (appServicePlan == null)
{
log.LogError("Could not find app service plan.");
return;
}
appServicePlan.Sku.Family = "P";
appServicePlan.Sku.Name = "P2V2";
appServicePlan.Sku.Size = "P2V2";
appServicePlan.Sku.Tier = "Premium";
appServicePlan.Sku.Capacity = 1;
var updateResult = await webSiteManagementClient.AppServicePlans.CreateOrUpdateWithHttpMessagesAsync(resourceGroup, appServicePlanName, appServicePlan);
}
}
public static class Utilities
{
public static async Task<WebSiteManagementClient> GetWebSiteManagementClient()
{
var azureServiceTokenProvider = new AzureServiceTokenProvider();
var accessToken = await azureServiceTokenProvider.GetAccessTokenAsync("https://management.azure.com/");
var tokenCredentials = new TokenCredentials(accessToken);
return new WebSiteManagementClient(tokenCredentials)
{
SubscriptionId = "<realSubscriptionIdHere>",
};
}
}
When I run this locally, it works, and actually performs the scale up on our Azure App Service Plan (I believe via Azure CLI). However, when this Azure Function is deployed to Azure, it does not find any App Service Plans. It hits the appservicePlan == null block and returns. I have turned on the Managed Service Identity for the deployed Azure Function, and granted it contributor permissions in the App Service I want to scale for.
Am I missing something? Why does
await webSiteManagementClient.AppServicePlans.ListByResourceGroupWithHttpMessagesAsync(resourceGroup);
not return anything when run as part of a published Azure Function?
The problem was the permission was set on the App Service (which was an app on the App Service Plan). It needed to be set on the Resource Group to be able to read the App Service Plans.

Authorization for Azure Function using Managed Service Identity to fetch blob from Azure Storage container

When I attempt to invoke an Azure Function in an Azure Function App using a system assigned managed identity to fetch a blob from an Azure Storage container, I’m encountering:
System.Private.CoreLib: Exception while executing function:<FunctionName>. Microsoft.WindowsAzure.Storage: Unauthorized.
I’m adapting the approach outlined here.
Here’s the code:
[FunctionName("TestFetchTileViaSvcPrinId")]
public static async Task<HttpResponseMessage> RunAsync(
[HttpTrigger(AuthorizationLevel.Anonymous, "get", "post", Route = null)] HttpRequest req,
ILogger log) {
log.LogInformation("C# HTTP trigger function processed a request.");
const string blobName = "https://<storageaccount>.blob.core.windows.net/...path.../<file>.jpg";
// Get the initial access token and the interval at which to refresh it.
var azureServiceTokenProvider = new AzureServiceTokenProvider();
NewTokenAndFrequency tokenAndFrequency = TokenRenewerAsync(azureServiceTokenProvider, CancellationToken.None).GetAwaiter().GetResult();
// Create storage credentials using the initial token, and connect the callback function to renew the token just before it expires
var tokenCredential = new TokenCredential(tokenAndFrequency.Token, TokenRenewerAsync, azureServiceTokenProvider, tokenAndFrequency.Frequency.Value);
var storageCredentials = new StorageCredentials(tokenCredential);
var cloudBlockBlob = new CloudBlockBlob(new Uri(blobName), storageCredentials);
using (var memoryStream = new MemoryStream()) {
await cloudBlockBlob.DownloadToStreamAsync(memoryStream); // Unauthorized exception is thrown here
var httpResponseMessage = new HttpResponseMessage(HttpStatusCode.OK) {
Content = new ByteArrayContent(memoryStream.ToArray())
};
httpResponseMessage.Headers.Add("Cache-Control", "max-age=31536000"); //31536000 seconds ~ 1 year
httpResponseMessage.Content.Headers.ContentType = new MediaTypeHeaderValue("image/jpeg");
return httpResponseMessage;
}
}
The Azure Function App has a system assigned managed identity which has Storage Blob Data Contributor role for the target blob’s entire storage account.
I got this working. As Rohit noticed, the redacted full-path to the blob (as originally posted) incorrectly specified the Azure function path rather than the storage account path. I've subsequently fixed up the question. Nevertheless, I did have a typo in the path as implemented. Correcting the path resolved the issue.

Resources