I have an Android app in App Center and I'd like to schedule nightly builds.
Is there any way to do this?
Answer
Today, App Center Build doesn't yet allow us to schedule recurring builds.
Luckily, App Center has a complete suite of APIs we can leverage to schedule an Azure Timer Function (essentially a cron job in the cloud) to trigger a build every night.
For a complete solution, see the UITestSampleApp.Functions project in this repo: https://github.com/brminnick/UITestSampleApp
Walkthrough
For a complete walkthrough, follow this post: https://www.codetraveler.io/2019/06/06/scheduling-app-center-builds/
This shows how to gather the required metadata and API Token from App Center, then how to build an Azure Timer Function that will trigger a build via the App Center APIs.
1. Get App Center Metadata
First, get the App Center metadata we'll need to Post to the App Center API
2. Generate App Center API Token
Then generate an App Center API Token
3. Create an Azure Timer Function
This Azure Timer Function uses the cron schedule 0 0 9 * * * to trigger every day at 0900 UTC.
using System;
using System.Net;
using System.Net.Http;
using System.Threading.Tasks;
using Microsoft.Azure.WebJobs;
using Microsoft.Extensions.Logging;
const string owner = "[Your App Owner]"; //change to your app owner
const string appName = "[Your App Name]"; // change to your app name
const string branch = "[Your Repo's Branch]"; //change to your repo's branch
readonly static Lazy<HttpClient> clientHolder = new Lazy<HttpClient>(() =>
{
var client = new HttpClient();
client.DefaultRequestHeaders.Add("X-API-Token", Environment.GetEnvironmentVariable("AppCenterApiToken"));
return client;
});
static HttpClient Client => clientHolder.Value;
[FunctionName("AppCenterScheduledBuildFunction")]
public static async Task Run([TimerTrigger("0 0 9 * * *")]TimerInfo myTimer, ILogger log)
{
var httpContent = new StringContent("{ \"debug\": true }", System.Text.Encoding.UTF8, "application/json");
var result = await Client.PostAsync($"https://api.appcenter.ms/v0.1/apps/{owner}/{appName}/branches/{branch}/builds", httpContent);
result.EnsureSuccessStatusCode();
}
4. Add App Center API Token to Azure Function
In the Azure Function Application Settings, add the App Center API Token, generated in step 2, using AppCenterApiToken for its name.
Related
I try hard to push a Notification into aour Teams Channel if a new Action is happend in our Azure Container Registry.
The Webhook works on a nother Eventhandler
If i push it to MS Teams i got the Response: Text is missing
Question is:
How can i add some more Information into the Payload of the Webhook?
I cant find any documentation for it
#Matthias, Thank you for your question.
As the architectural diagram shows, an Azure Functions app is used between both services to translate the payload received from the ACR webhook. It’s also the custom function, which will call into Microsoft Teams to create the custom message.
Configure incoming Webhook in Microsoft Teams
To send messages into Microsoft Teams, an Incoming Webhook is required. To create and configure a new Incoming Webhook, navigate to the desired team and select CONNECTORS from the Options Menu.
Look for Incoming Webhook, and ADD it. Once added, the Incoming Webhook has to be configured. Using the official ACR logo for demonstration purposes and set the name to “Azure Container Registry”.
Add Webhook URL to the Azure Functions app
Having the Incoming Webhook connector configured in Microsoft Teams, you must store the webhook URL on the Azure Functions app. Again, use Azure CLI to get this done:
# Variables
FUNCTION_APP_NAME=fnapp-acr-teams
RESOURCE_GROUP_NAME=rg-acr-teams
WEBHOOK_URL=<your webhook url provided by Microsoft Teams>
# Set appSettings on Azure Functions App
az functionapp config appsettings set \
-n $FUNCTION_APP_NAME \
-g $RESOURCE_GROUP_NAME \
--settings "ContainerRegistryTeams__TeamsWebhookUrl=$WEBHOOK_URL"
Implement Azure Functions
We have used Azure Functions Core Tools (func) to get everything done when it comes to Azure Functions. If you are more on the UI side of things, use your preferred IDE or editor to create a new Azure Functions project.
# Create a project dir
mkdir -p ~/projects/acr-teams-integration
cd ~/projects/acr-teams-integration
# Create an Azure Functions project
func init --worker-runtime dotnet
# Create a new function
func new -l csharp --template "Http Trigger" \
-n FnNotifyTeams -a function
To use dependency injection (DI) and configuration capabilities from .NET in an Azure Functions project, you have to install the following NuGet packages:
dotnet add package Microsoft.NET.Sdk.Functions
dotnet add package Microsoft.Azure.Functions.Extensions
dotnet add package Microsoft.Extensions.DependencyInjection
Add a custom FunctionsStartup class
If you want to use IOptions<T> to access configuration data using strongly type C# classes, consider adding a custom implementation of FunctionsStartup as shown here:
[assembly: FunctionsStartup(typeof(Thns.Functions.Startup))]
namespace Thns.Functions
{
public class Startup: FunctionsStartup
{
public override void Configure(IFunctionsHostBuilder builder)
{
builder.Services.AddOptions<TeamsConfig>()
.Configure<IConfiguration>((settings, configuration) =>
{
configuration.GetSection("ContainerRegistryTeams")
.Bind(settings);
});
}
}
}
The configuration for our function is pretty simple. It’s just the webhook URL. That said, the TeamsConfig class is simple:
public class TeamsConfig
{
public string TeamsWebhookUrl { get; set; }
}
You can now use regular DI patterns to get the configuration data during a request:
public class FnNotifyTeams
{
public FnNotifyTeams(IOptions<TeamsConfig> options)
{
Config = options.Value;
}
protected TeamsConfig Config { get; }
// omitted
}
The FnNotifyTeams function
Let’s implement our Azure function based on an HTTPTrigger now. The function is responsible for:
Validating incoming requests from ACR
Constructing the payload for the Microsoft Teams message
Calling into the Microsoft Teams webhook
[FunctionName("FnNotifyTeams")]
public async Task<IActionResult> Run(
[HttpTrigger(AuthorizationLevel.Function, "POST", Route = "images")] HttpRequest req, ILogger log)
{
string body = await new StreamReader(req.Body).ReadToEndAsync();
dynamic d = JsonConvert.DeserializeObject(body);
string action = d?.action;
if (action != "push" && action != "chart_push")
{
log.LogWarning($"Action {action} is not supported.");
return new BadRequestResult();
}
var metadata = ImageMetadata.FromPayload(d);
if(metadata == null)
{
log.LogWarning($"Received invalid request. Got {body}");
return new BadRequestResult();
}
var message = new {
Title = $"New Container Image published in ACR",
Text = $"`{metadata.Repository}:{metadata.Tag}` has been published at `{metadata.Registry}`. You can pull it now using: {Environment.NewLine}`docker pull {metadata.Registry}/{metadata.Repository}:{metadata.Tag}{Environment.NewLine}`"
};
var h = new HttpClient();
var content = new StringContent(JsonConvert.SerializeObject(message), Encoding.UTF8, "application/json");
var r = await h.PostAsync(Config.TeamsWebhookUrl, content);
if (r.IsSuccessStatusCode){
return new OkResult();
}
log.LogError($"Teams response -> {r.StatusCode}: {r.ReasonPhrase}");
return new StatusCodeResult(500);
}
The preceding code takes the raw payload received from ACR, extracts and validates essential metadata. Suppose all necessary information is available and ACR issued the call because of a push or chart_push action. In that case, the request payload for calling into Microsoft Teams is constructed and send using the HttpClient::PostAsync method.
Encapsulated inbound message validation mainly into the ImageMetadata class, as you can see here:
public class ImageMetadata
{
public string Registry { get; private set; }
public string Repository { get; private set; }
public string Tag { get; private set; }
public static ImageMetadata FromPayload(dynamic d)
{
string repository = d?.target?.repository;
string tag = d?.target?.tag;
string registry = d?.request?.host;
if (string.IsNullOrWhiteSpace(repository) ||
string.IsNullOrWhiteSpace(tag) ||
string.IsNullOrWhiteSpace(registry))
{
return null;
}
return new ImageMetadata
{
Registry = registry,
Repository = repository,
Tag = tag,
};
}
}
Publish it to the cloud now:
# Variables
FUNCTION_APP_NAME=fnapp-acr-teams
# go to the functions directory
cd projects/acr-teams-integration
# deploy the current code to Azure Functions app
func azure functionapp publish $FUNCTION_APP_NAME -b local
Grab the Azure Function URL with the authentication key
Now that the code is deployed to the Azure Functions app, you have to grab the function’s URL, including the function key used for authentication. Again you can do that straight from the command line using the func CLI:
# Variables
FUNCTION_APP_NAME=fnapp-acr-teams
# Grab function url with function key
func azure functionapp list-functions $FUNCTION_APP_NAME --show-keys
Copy the invoke URL from the output.
Add a Webhook to ACR
Last but not least, we have to create a new outgoing webhook in ACR. At this point, we’re interested in two events exposed from ACR via webhook push and chart_push:
# Variables
FUNCTION_URL=<paste function app url including function-key>
ACR_NAME=acrteams2021
# create a webhook in ACR
az acr webhook create -n webhook-notify-teams \
-r $ACR_NAME \
--uri $FUNCTION_URL \
--actions push chart_push
Test the integration of Azure Container Registry and Microsoft Teams
You can use any Docker image or OCI compliant artifact to test the integration. We’ll use an alpine Linux container image as an example here:
# Variables
ACR_NAME=acrteams2021
docker pull alpine:latest
docker tag alpine:latest $ACR_NAME.azurecr.io/alpine:0.0.1
# Login to ACR
az acr login -n $ACR_NAME
# Push the container image to ACR
docker push $ACR_NAME.azurecr.io/alpine:0.0.1
And after a few seconds, you should see a message appearing in Microsoft Teams.
Reference: https://www.thorsten-hans.com/send-microsoft-teams-message-oci-artifacts-azure-container-registry/
I am developing an application which is based on GCP (Specifically it runs on the Google Cloud Composer (a managed version of Airflow)). From there I would like to connect to the Microsoft Graph API of another organization. So I want the application to be running in the background and every x minutes retrieve new emails from the inbox of an external organization, then do some processing and based on that perform some actions in said mailbox through the Graph API.
I am trying to figure out the best practice on how to secure this connection. I believe I could use the client secret of the registered application and then store that in an azure keyvault. Now I am not sure how I should authenticate my registered app such that it can retrieve this client secret which in turn can be used to access the Graph API?
As you can probably tell I'm not quite sure whether this makes sense or what a better approach would be? Almost all the information I am finding is concerning managed identities, however if I understood correctly these can only be used when the application is running natively on Azure, which for me is not the case.
Would really appreciate any help!
If I don't misunderstand, you can refer to my sample code, pls note you need to add key vault access policy first. And you may refer to this doc to know about the default azure credential.
using Azure.Identity;
using Azure.Security.KeyVault.Secrets;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Identity.Client;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace test0430callingapi.Controllers
{
public class HelloController : Controller
{
public async Task<string> IndexAsync()
{
const string secretName = "clientsecret";
var kvUri = "https://key_vault_name.vault.azure.net/";
var a = new DefaultAzureCredential();
var client = new SecretClient(new Uri(kvUri), a);
var secret = await client.GetSecretAsync(secretName);
string secretVaule = secret.Value.Value;
//azure ad client credential flow to generate access token
IConfidentialClientApplication app;
app = ConfidentialClientApplicationBuilder.Create("azure_ad_app_clientid")
.WithClientSecret(secretVaule)
.WithAuthority(new Uri("https://login.microsoftonline.com/your_tanent_name.onmicrosoft.com"))
.Build();
AuthenticationResult result = null;
string[] scopes = new string[] { "https://graph.microsoft.com/.default" };
result = await app.AcquireTokenForClient(scopes)
.ExecuteAsync();
string accesstoken = result.AccessToken;
// this token can be used to call https://graph.microsoft.com/v1.0/users/user_id/mailFolders/{id}/messages
return accesstoken;
}
}
}
I used client credential flow here which don't need to make users sign in, and this api support application permission. If you use an api which only supports delegated permission, it can't work. Here's the api calling response.
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.
I want to call this API from azure resource manager to get the storage keys:
https://learn.microsoft.com/en-us/rest/api/storagerp/storageaccounts/listkeys
I want to use user authentication for this call and possibly .net sdk.
Is there any .net sdk(Nuget package) I can include in my c# project to call this API? I am seeing many solution which is using .net sdk but they are using AAD app secret, but I cannot use secret in the app since it is a desktop app. I think there should be a way to call these API with user auth and .net sdk.
The Microsoft.Azure.Services.AppAuthentication for .NET library may meet your requirement. It uses the developer's credentials to authenticate during local development. When the solution is later deployed to Azure, the library automatically switches to application credentials.
The Microsoft.Azure.Services.AppAuthentication library supports local development with Microsoft Visual Studio, Azure CLI, or Azure AD Integrated Authentication.
Sample:
using Microsoft.Azure.Management.Storage;
using Microsoft.Azure.Services.AppAuthentication;
using Microsoft.Rest;
namespace ConsoleApp6
{
class Program
{
static void Main(string[] args)
{
AzureServiceTokenProvider azureServiceTokenProvider = new AzureServiceTokenProvider();
string accessToken = azureServiceTokenProvider.GetAccessTokenAsync("https://management.azure.com/").Result;
var Credentials = new TokenCredentials(accessToken);
var storageManagementClient = new StorageManagementClient(Credentials);
storageManagementClient.SubscriptionId = "subscription id";
var storageAccountKey = storageManagementClient.StorageAccounts.ListKeysAsync("resource grouop name", "storage account name");
string storage_Account_Key = storageAccountKey.Result.Keys[0].Value;
System.Console.WriteLine(storage_Account_Key);
}
}
}
For more details about the authentication, you could take a look at this link.
I have a xamarin app That will send notifications via Azure functions and its notification hub out put binding.
In xamarin app i have an android service that gets the token and stores in cache
[Service]
[IntentFilter(new[] { "com.google.firebase.INSTANCE_ID_EVENT" })]
public class MyFirebaseIIDService : FirebaseInstanceIdService
{
private App _app => (App)Xamarin.Forms.Application.Current;
private readonly ICachingService _cachingService;
public MyFirebaseIIDService()
{
var xx = typeof(ICachingService);
_cachingService = (CachingService)App.Instance.Container.Resolve(xx);
}
public override void OnTokenRefresh()
{
var refreshedToken = FirebaseInstanceId.Instance.Token;
_cachingService.FireBaseToken = refreshedToken;
Console.WriteLine($"Token received: {refreshedToken}");
// SendRegistrationToServerAsync(refreshedToken);
}
}
after the user is logged in i want to use its Id as tag and i call another android service that tries to register a device with this tag
var client = new MobileServiceClient(App.MobileServiceUrl);
var push = client.GetPush();
var reg = new Registration(token, tags);
await push.RegisterAsync(reg);
at this point i have token and tags list that contains userId because later i want that this user only gets notification meant for them only.
on the push.RegisterAsyn method i get an error.
The resource you are looking for has been removed, had its name changed, or is temporarily unavailable.
please note that the App.MobileServiceUrl is the url of Azure Mobile App that is connected to notification hub
Other than that this Azure Mobile App have just the default TODO controllers and everything that comes with the template. My sending notification code will be in azure functions using notification hub output binding.
I also updated all azure related nugets no diffrence
it tries to hit this url for registration.
https://xxxx.azurewebsites.net/push/registrations?deviceId=dTd4wba1KTU:APA91bHKOZRX9LFzEGD-yqyz4p-whqh6UsoEAlgpFHfBxu00MhLo-------yyyyyyyyyyyyyyyyeuoRmH4h9czeQbvGRgbwt4zMlrvRIlvLDZ-kTu_Dcu2iHx9I5u0gheQQ3Z2tYq66O&platform=gcm
i was using a wrong nuget. i had to use
Microsoft.Azure.Mobile.Client
instead i was using Azure Mobile Services nuget.
both have MobileServiceClient class thats why i got confused. now i dont get the exception.