Januar 2018

Band 33, Nummer 1

Office: Erstellen der API für Ihre Organisation mit Microsoft Graph und Azure Functions

Von Mike Ammerlaan | Januar 2018

Wenn Sie Ihre Organisation als API betrachten, wie würde sie aussehen? 

Sie fangen wahrscheinlich mit den Menschen an (dem Herzstück einer Organisation) und den verschiedenen Rollen und Funktionen, die sie erfüllen. Häufig werden solche Personen in definierten und virtuellen Teams gruppiert, die Aufgaben und Projekte ausführen. Sie würden Ressourcen hinzufügen, einschließlich der Orte, an denen Menschen arbeiten, und der Tools, mit denen sie ihre Arbeit erledigen. Sie würden dann Prozesse und Arbeitsaktivitäten hinzufügen – vielleicht sind dies Methoden in der API? Obwohl „widgetMarketingTeam.runCampaign()“ vielleicht zu stark vereinfachend ist, erhalten Sie mit einer API für Ihr Unternehmen dennoch einen guten Einblick in die Funktionsweise Ihrer Organisation und können die Produktivität durch effizientere Prozesse und Tools steigern.

Der Schlüssel liegt darin, jede Ressource konsistent verfügbar und logisch miteinander verknüpft bereitzustellen, sodass Sie umfassende Prozesse entwickeln können, die genau auf die Art und Weise abgestimmt sind, wie Einzelpersonen und Teams arbeiten möchten. Je mehr APIs Sie zusammenführen und verbinden können, umso nützlicher ist die von Ihnen erstellte Produktmenge, die bei weitem größer sein kann als die Summe ihrer Teile.

Zu diesem Zweck bieten wir Microsoft Graph an – eine API, die wichtige Datensätze innerhalb Ihres Unternehmens umfasst und es Ihnen ermöglicht, alles zusammenzuführen, was Sie benötigen, um die Art und Weise, wie die Aufgaben ausgeführt werden, zu transformieren. Darüber hinaus arbeitet Microsoft Graph mit Consumerdiensten wie OneDrive und Mail (Outlook.com) zusammen und ermöglicht es Ihnen, auch die persönliche Produktivität zu transformieren.

Lösen des Problems der API-Diversität

Die Vielfalt der eingesetzten Softwaresysteme innerhalb eines Unternehmens kann sehr unterschiedlich sein. Für Entwickler stellt jedes dieser Systeme eine einzigartige Struktur dar, typischerweise mit einer bestimmten Menge von APIs, Authentifizierungsanforderungen und Interaktionsformen. Häufig besteht eine große Herausforderung bei Softwareprojekten darin, einfach eine Brücke zwischen diesen unterschiedlichen Systemen bereitzustellen, um ein höheres Maß an Einblicken zu ermöglichen. Dies kann die Abstraktion verschiedener APIs und die Berücksichtigung individueller Authentifizierungsschemas beinhalten.

In der Vergangenheit arbeiteten die einzelnen APIs aus verschiedenen Produktteams (in meinem Fall bei Microsoft) unterschiedlich und erforderten eine produktübergreifende Integration. Noch vor fünf Jahren war es für den Vorgang, das vollständige Profil und das Foto eines Benutzers abzurufen, erforderlich, sowohl Exchange-APIs (um Informationen zu einer Person zu erhalten) als auch SharePoint-APIs (um ein Foto aus dem verwalteten Profil eines Benutzers abzurufen) aufzurufen. Jede API besaß ihre eigene Authentifizierung, ihr eigenes API-Schema und unterschiedliche Anforderungen. Wie mussten Sie vorgehen, wenn Sie anschließend Informationen zum Vorgesetzten einer Person abrufen wollten? Das hätte die Abfrage eines dritten Systems bedeutet, um eine Organisationshierarchie abzurufen. Diese Vorgänge ließen sich alle zusammenführen, aber sie waren komplexer als erforderlich.

Microsoft Graph wurde aus dem Wunsch heraus geboren, dieses Problem zu lösen. Durch die Vereinheitlichung von Daten und Authentifizierung und die Bereitstellung konsistenter Systeme wird die Verwendung des Netzes von APIs wesentlich einfacher und praktischer. Microsoft Graph führt verschiedene Systeme aus dem gesamten Unternehmen zusammen, die wichtige Facetten und Funktionen in einem Unternehmen darstellen. Seit der Markteinführung vor zwei Jahren hat Microsoft Graph kontinuierlich an Funktionalität und Leistungsfähigkeit gewonnen, sodass das Tool wirklich als Basis-API für Ihr Unternehmen dienen kann.

Der Kern von Microsoft Graph ist die Menge der Benutzer: in der Regel alle Mitarbeiter mit einem Konto in einer Organisation. Vereinfachte, zentralisierte Gruppen sind ein neues Konzept in Microsoft Graph, das im Allgemeinen mit einer Liste von Benutzern und anderen Sicherheitsgruppen beginnt. Gruppen können über eine Reihe von Ressourcen verfügen, z.B. einen chatbasierten Arbeitsbereich für Microsoft-Teams, ein Planungs-Task Board und eine SharePoint-Website mit Dokumentbibliotheken und Dateien. Von dort aus werden verschiedene Arbeitstools für Benutzer und Gruppen dargestellt, einschließlich Dateien über die Drive-API, Aufgaben über die Planungs-API, eingehende E-Mail für Benutzer und Gruppen, Kontakte, Kalenderfunktionen und mehr, wie Abbildung 1 zeigt.

Produktivitäts-APIs von Microsoft Graph
Abbildung 1: Produktivitäts-APIs von Microsoft Graph

Im Lauf der Zeit wurden in den APIs von Microsoft Graph neue Funktionen hinzugefügt. Eine neue Funktion zum persistenten Speichern von benutzerdefinierten Metadaten zusammen mit Elementen in Microsoft Graph gibt Ihnen die Möglichkeit, diese Elemente tiefgreifend anzupassen. Jetzt ist eine Gruppe nicht mehr nur eine Gruppe: Mit zusätzlichen Metadaten, die das Thema, den Dozenten und das Timing beschreiben, könnte eine Gruppe einen Kurs in einer Bildungseinrichtung darstellen. Sie könnten diese Metadaten verwenden, um dann Abfragen auszuführen, um z.B. alle Gruppen zu ermitteln, die naturwissenschaftliche Kurse darstellen. Alternativ können Sie Ihre Systeme in Microsoft Graph einbinden, indem Sie Bezeichner aus Ihrem System den zugehörigen Entitäten in Microsoft Graph hinzufügen.

Microsoft Graph bietet mehr als CRUD-APIs (Create, Read, Update, Delete) für Kernobjekte. Ein wesentliches Feature ist eine Schicht von Einblicken, die hinter den Kulissen generiert werden, während die Benutzer arbeiten. Obwohl Graph beispielsweise eine vollständige Organisationshierarchie und eine Sammlung von Gruppen enthält, stellen diese nicht immer die beste Darstellung der Arbeitsweise von Teams dar. Durch eine Analyse der Arbeitsaufgaben erhalten Sie eine Liste der am engsten in Beziehung stehenden Personen (virtuelle Teams) und Dateien, mit denen ein Benutzer verbunden sein könnte. Darüber hinaus werden allgemeine Hilfsprogramme (z.B. Tools zum Ermitteln einer verfügbaren Besprechungszeit für eine Gruppe von Benutzern) als Methoden zur Verfügung gestellt.

Azure Functions

Microsoft Graph wurde dafür konzipiert, in umfangreicheren Systemen und Prozessen verwendet und angepasst zu werden. Als einfache REST-API und gekoppelt mit einer Vielzahl von SDKs ist Microsoft Graph einfach zu verwenden. Eine natürliche Ergänzung für das Erstellen von Prozessen und Integrationen in Microsoft Graph ist Azure Functions (functions.azure.com). Mit dieser Anwendung können Sie punktgenaue Codeblöcke dort hinzufügen, wo Sie sie benötigen, während Sie nur inkrementell für Code zahlen, wenn dieser verwendet wird. Azure Functions unterstützt die sprachübergreifende Entwicklung (einschließlich C# und Node.js).

Kürzlich wurde eine Reihe neuer Integrationen in Azure Functions eingeführt, die es einfacher machen, eine Verbindung mit Microsoft Graph herzustellen. Die Azure Functions-Bindungserweiterungen (jetzt als Preview mit der Azure Functions 2.0-Laufzeit verfügbar) automatisieren einige der üblichen Aufgaben beim Arbeiten mit Microsoft Graph, z.B. auch die Authentifizierung und das Arbeiten mit der Mechanik von Webhooks.

Sehen wir uns ein Beispiel an, um mit dem Arbeiten mit Microsoft Graph zu beginnen.

Erstellen von Aufgaben über Azure Functions

Stellen Sie sich vor, dass Vorgesetzte eine von einem Mitglied ihres Teams ausgeführte Aktion überprüfen und genehmigen sollen. Benutzeraufgaben sind eine Möglichkeit, Benutzer aufzufordern, eine Aktion auszuführen, um Mitarbeiteraktionen zu konvertieren und nachzuverfolgen. In diesem Fall möchte ich einen einfachen Webdienst implementieren, der eine Aufgabe erstellt, die dem Vorgesetzten eines Benutzers zugewiesen wird. 

Der erste Anlaufpunkt in jedem Microsoft Graph-Projekt ist in der Regel der Graph-Tester. Der Graph-Tester ist eine Anwendungswebsite, die es Ihnen ermöglicht, Microsoft Graph-Aufrufe schnell zu modellieren, ihre Ergebnisse zu untersuchen und alle Aktionen, die Sie ggf. ausführen können, vollständig zu verstehen. Der Graph-Tester ist unter developer.microsoft.com/graph verfügbar und bietet Ihnen die Möglichkeit, einen schreibgeschützten Demomandanten zu verwenden oder sich bei Ihrem eigenen Mandanten anzumelden. Sie können sich mit Ihrem Organisationskonto anmelden und direkt auf Ihre eigenen Daten zugreifen. Es wird empfohlen, einen Entwicklermandanten zu verwenden, der im Office Developer Program unter dev.office.com/devprogram verfügbar ist. Auf diese Weise erhalten Sie einen separaten Mandanten, in dem Sie vorbehaltlos mit Ihrer Entwicklung experimentieren können.

In diesem Fall können Sie zwei einfache URLs eingeben, um zu sehen, welche Art von Aufrufen Sie in diesem Beispiel ausführen. Zuerst sollten Sie die Option „get a user's manager“ aktivieren, die Sie im Graph-Tester anzeigen können, indem Sie das Beispiel „GET my manager“ wie in Abbildung 2 gezeigt auswählen. Die zugehörige URL wird im Feld „Run Query“ angezeigt.

Ergebnisse der Auswahl von „GET my manager“
Abbildung 2: Ergebnisse der Auswahl von „GET my manager“

Der zweite Teil des Vorgangs besteht darin, eine Planeraufgabe zu erstellen. Im Graph-Tester können Sie die Beispielsammlung erweitern, um Beispiele für Planeraufgaben hinzuzufügen. In dieser Beispielsammlung sehen Sie den Vorgang zum Erstellen einer Planeraufgabe (POST an https://graph.microsoft.com/v1.0/planner/tasks).

Nachdem Sie nun die damit verbundenen Webdienstanforderungen verstehen, können Sie mit Azure Functions eine Funktion erstellen.

Erstellen Sie zunächst eine neue Azure Functions-Anwendung. Im Allgemeinen sollten Sie die Anweisungen unter aka.ms/azfnmsgraph befolgen, um dies zu erreichen. Da sich die neue Funktion „Azure Functions-Bindungserweiterungen“ in der Vorschauphase befindet, müssen Sie kurz gesagt Ihre Azure Functions-Anwendung auf die 2.0 Preview-Laufzeit („Beta“) umstellen. Außerdem müssen Sie die Microsoft Graph-Erweiterung installieren und die App Service-Authentifizierung konfigurieren.

Während Sie die Microsoft Graph-Anwendungsregistrierung konfigurieren, müssen Sie für dieses Beispiel einige weitere Berechtigungen hinzufügen, um das Lesen von Vorgesetzteninformationen und Aufgaben zu unterstützen, z.B. die folgenden Berechtigungen:

  • CRUD-Benutzeraufgaben und -Projekte (Tasks.ReadWrite)
  • Grundlegendes Profil von Benutzern anzeigen (profile)
  • Alle Gruppen lesen und schreiben (Group.ReadWrite.All)
  • Grundlegendes Profil aller Benutzer lesen (User.ReadBasic.All)

Sie werden die Azure Functions-Bindungserweiterungen für Microsoft Graph nutzen, um die Authentifizierung zu verarbeiten und sicherzustellen, dass Sie über ein authentifiziertes Zugriffstoken verfügen, mit dem Sie auf Microsoft Graph-APIs zugreifen können. Zu diesem Zweck erstellen Sie einen HTTP C#-Standardtrigger. Wählen Sie unter „Integrieren“ die Option „Erweiterter Editor“ aus, und verwenden Sie die in Abbildung 3 gezeigten Bindungen. Dies erfordert, dass sich der Benutzer anmelden, authentifizieren und Ihre Anwendung genehmigen muss, bevor sie verwendet werden kann.

Abbildung 3: Erstellen eines HTTP-Triggers zur Verarbeitung der Authentifizierung

{
  "bindings": [
    {
      "name": "req",
      "type": "httpTrigger",
      "direction": "in"
    },
    {
      "type": "token",
      "direction": "in",
      "name": "accessToken",
      "resource": "https://graph.microsoft.com",
      "identity": "userFromRequest"
    },
    {
      "name": "$return",
      "type": "http",
      "direction": "out"
    }
  ],
  "disabled": false
}

Der Code für die Funktion wird in Abbildung 4 gezeigt. Beachten Sie, dass Sie für die Funktionsanwendung namens „PlanId“ eine Umgebungsvariable konfigurieren müssen, die den Bezeichner des Planerplans enthält, den Sie für Ihre Aufgaben verwenden möchten. Dies kann über die Anwendungseinstellungen für die Funktionsanwendung erfolgen.

Abbildung 4: Posten einer Aufgabe, die der Azure Functions-Quelle „Manager“ eines Benutzers zugewiesen wurde

#r "Newtonsoft.Json"
using System.Net;
using System.Threading.Tasks;
using System.Configuration;
using System.Net.Mail;
using System.IO;
using System.Web;
using System.Text;
using Newtonsoft.Json.Linq;
public static HttpResponseMessage Run(HttpRequestMessage req, string accessToken, TraceWriter log)
{
  log.Info("Processing incoming task creation requests.");
  // Retrieve data from query string
  // Expected format is taskTitle=task text&taskBucket=bucket
  // title&taskPriority=alert
  var values = HttpUtility.ParseQueryString(req.RequestUri.Query);
  string taskTitle = values["taskTitle"];
  string taskBucket = values["taskBucket"];
  string taskPriority = values["taskPriority"];
  if (String.IsNullOrEmpty(taskTitle))
  {
    log.Info("Incomplete request received - no title.");
    return new HttpResponseMessage(HttpStatusCode.BadRequest);
  }
  string planId = System.Environment.GetEnvironmentVariable("PlanId");
  // Retrieve the incoming users' managers ID
  string managerJson = GetJson(
    "https://graph.microsoft.com/v1.0/me/manager/", accessToken, log);
    dynamic manager = JObject.Parse(managerJson);
  string managerId = manager.id;
  string appliedCategories = "{}";
  if (taskPriority == "alert" || taskPriority == "1")
  {
    appliedCategories = "{ \"category1\": true }";
  }
  else
  {
    appliedCategories = "{ \"category2\": true }";
  }
  string now =  DateTime.UtcNow.ToString("yyyy-MM-ddTHH\\:mm\\:ss.fffffffzzz");
  string due =  DateTime.UtcNow.AddDays(5).ToString(
    "yyyy-MM-ddTHH\\:mm\\:ss.fffffffzzz");
  string bucketId = "";
  // If the incoming request wants to place a task in a bucket,
  // find the bucket ID to add it to
  if (!String.IsNullOrEmpty(taskBucket))
  {
    // Retrieve a list of planner buckets so that you can match
    // the task to a bucket, where possible
    string bucketsJson = GetJson(
      "https://graph.microsoft.com/v1.0/planner/plans/" + planId +
      "/buckets", accessToken, log);
    if (!String.IsNullOrEmpty(bucketsJson))
    {
      dynamic existingBuckets = JObject.Parse(bucketsJson);
      taskBucket = taskBucket.ToLower();
      foreach (var bucket in existingBuckets.value)
      {
        var existingBucketTitle = bucket.name.ToString().ToLower();
        if (taskBucket.IndexOf(existingBucketTitle) >= 0)
        {
          bucketId = ", \"bucketId\": \"" + bucket.id.ToString() + "\"";
        }
      }
    }
  }
  string jsonOutput = String.Format(" {{ \"planId\": \"{0}\", \"title\": \"{1}\", \"orderHint\": \" !\", \"startDateTime\": \"{2}\", \"dueDateTime\": \"{6}\", \"appliedCategories\": {3}, \"assignments\": {{ \"{4}\": {{ \"@odata.type\": \"#microsoft.graph.plannerAssignment\",  \"orderHint\": \" !\"  }} }}{5} }}",
    planId, taskTitle, now, appliedCategories, managerId, bucketId, due);
  log.Info("Creating new task: " + jsonOutput);
  PostJson("https://graph.microsoft.com/v1.0/planner/tasks",
    jsonOutput, accessToken, log);
  return new HttpResponseMessage(HttpStatusCode.OK);
}
private static string GetJson(string url, string token, TraceWriter log)
{
  HttpWebRequest hwr = (HttpWebRequest)WebRequest.CreateHttp(url);
  log.Info("Getting Json from endpoint '" + url + "'");
  hwr.Headers.Add("Authorization", "Bearer " + token);
  hwr.ContentType = "application/json";
  WebResponse response = null;
  try
  {
    response = hwr.GetResponse();
    using (Stream stream = response.GetResponseStream())
    {
      using (StreamReader sr = new StreamReader(stream))
      {
        return sr.ReadToEnd();
      }
     }
  }
  catch (Exception e)
  {
    log.Info("Error: " + e.Message);
  }
  return null;
}
private static string PostJson(string url, string body, string token, TraceWriter log)
{
  HttpWebRequest hwr = (HttpWebRequest)WebRequest.CreateHttp(url);
  log.Info("Posting to endpoint " + url);
  hwr.Method = "POST";
  hwr.Headers.Add("Authorization", "Bearer " + token);
  hwr.ContentType = "application/json";
  var postData = Encoding.UTF8.GetBytes(body.ToString());
  using (var stream = hwr.GetRequestStream())
  {
  stream.Write(postData, 0, postData.Length);
  }
  WebResponse response = null;
  try
  {
    response = hwr.GetResponse();
    using (Stream stream = response.GetResponseStream())
    {
      using (StreamReader sr = new StreamReader(stream))
      {
        return sr.ReadToEnd();
      }
    }
  }
  catch (Exception e)
  {
    log.Info("Error: " + e.Message);
  }
  return null;
}

Dieses Beispiel zeigt, wie Sie unterschiedliche Datensätze (in diesem Fall die Manager- und Planeraufgaben eines Benutzers) in einem Codeteil mit einem Authentifizierungstoken zusammenfassen können. Das Erstellen und Zuweisen von Aufgaben ist eine gängige Methode, um Aktivitäten über Teams hinweg zu steuern. Daher ist die Möglichkeit, Aufgaben im Handumdrehen zu erstellen und vorhandene Planerfunktionen zu nutzen, sehr hilfreich. Es ist nicht ganz „widgetMarketingTeam.launchCampaign()“, aber zumindest können Sie sehen, wie Sie das Anfangspaket von Aufgaben erstellen, die dem Team zu einem fokussierten, strukturierten Start verhelfen würden.

Verarbeiten von Dateien in OneDrive

Eine weitere Aufgabe, die Sie ausführen können, ist die Verarbeitung von Dateien, die im OneDrive-Speicher eines Benutzers vorhanden sind. In diesem Fall nutzen Sie die Vorteile der Azure Functions-Bindungserweiterungen für Microsoft Graph, um eine Datei für die Verwendung vorzubereiten. Sie übergeben sie dann an die Cognitive Services-APIs für die Spracherkennung. Dies ist ein Beispiel für Datenverarbeitung, das eine nützliche Möglichkeit sein kann, um einen größeren Nutzen aus Dateien in OneDrive und SharePoint zu ziehen.

Um zu beginnen, befolgen Sie einige der gleichen Schritte wie im vorherigen Beispiel (einschließlich der Einrichtung einer Funktionen-App und einer Azure Active Directory-Registrierung). Beachten Sie, dass die Azure Active Directory-Anwendungsregistrierung, die Sie für dieses Beispiel verwenden, über die Berechtigung „Alle Dateien lesen, auf die der Benutzer zugreifen kann“ (Files.Read.All) verfügen muss. Außerdem benötigen Sie einen Cognitive Services-Sprach-API-Schlüssel, den Sie unter aka.ms/tryspeechapi abrufen können.

Beginnen Sie wie zuvor mit den Azure Functions-Bindungserweiterungen, und richten Sie einen neuen HTTP C#-Trigger ein. Verwenden Sie auf der Registerkarte „Integrieren“ Ihrer Funktion das in Abbildung 5 gezeigte Bindungsmarkup, um Ihre Funktion mit einer Bindungserweiterung zu verbinden. In diesem Fall bindet die Bindungserweiterung den myOneDriveFile-Parameter in Ihrer Azure-Funktion an die onedrive-Bindungserweiterung.

Abbildung 5: Einrichten eines neuen Triggers für das Abrufen einer Datei auf OneDrive

{
  "bindings": [
    {
      "name": "req",
      "type": "httpTrigger",
      "direction": "in"
    },
    {
      "name": "myOneDriveFile",
      "type": "onedrive",
      "direction": "in",
      "path": "{query.filename}",
      "identity": "userFromRequest",
    },
    {
      "name": "$return",
      "type": "http",
      "direction": "out"
    }
  ],
  "disabled": false
}

Nun ist es Zeit für den Code, der in Abbildung 6 gezeigt wird.

Abbildung 6: Transkribieren einer Audiodatei aus OneDrive

#r "Newtonsoft.Json"
using System.Net;
using System.Text;
using System.Configuration;
using Newtonsoft.Json.Linq;
public static  async Task<HttpResponseMessage> Run(HttpRequestMessage req,
  Stream myOneDriveFile, TraceWriter log)
{
  // Download the contents of the audio file
  log.Info("Downloading audio file contents...");
  byte[] audioBytes;
  audioBytes = StreamToBytes(myOneDriveFile);
  // Transcribe the file using cognitive services APIs
  log.Info($"Retrieving the cognitive services access token...");
  var accessToken =
    System.Environment.GetEnvironmentVariable("SpeechApiKey");
  var bingAuthToken = await FetchCognitiveAccessTokenAsync(accessToken);
  log.Info($"Transcribing the file...");
  var transcriptionValue = await RequestTranscriptionAsync(
    audioBytes, "en-us", bingAuthToken, log);
  HttpResponseMessage hrm = new HttpResponseMessage(HttpStatusCode.OK);
  if (null != transcriptionValue)
  {
    hrm.Content = new StringContent(transcriptionValue, Encoding.UTF8, "text/html");
  }
  else
  {
    hrm.Content = new StringContent("Content could not be transcribed.");
  }
  return hrm;
}
private static async Task<string> RequestTranscriptionAsync(byte[] audioBytes,
  string languageCode, string authToken, TraceWriter log)
{
  string conversation_url = $"https://speech.platform.bing.com/speech/recognition/conversation/cognitiveservices/v1?language={languageCode}";
  string dictation_url = $"https://speech.platform.bing.com/speech/recognition/dictation/cognitiveservices/v1?language={languageCode}";
  HttpResponseMessage response = null;
  string responseJson = "default";
  try
  {
    response = await PostAudioRequestAsync(conversation_url, audioBytes, authToken);
    responseJson = await response.Content.ReadAsStringAsync();
    JObject data = JObject.Parse(responseJson);
    return data["DisplayText"].ToString();
  }
  catch (Exception ex)
  {
    log.Error($"Unexpected response from transcription service A: {ex.Message} |" +
      responseJson + "|" + response.StatusCode  + "|" +
      response.Headers.ToString() +"|");
    return null;
  }
}
private static async Task<HttpResponseMessage> PostAudioRequestAsync(
  string url, byte[] bodyContents, string authToken)
{
  var payload = new ByteArrayContent(bodyContents);
  HttpResponseMessage response;
  using (var client = new HttpClient())
  {
    client.DefaultRequestHeaders.Add("Authorization", "Bearer " + authToken);
    payload.Headers.TryAddWithoutValidation("content-type", "audio/wav");
    response = await client.PostAsync(url, payload);
  }
  return response;
}
private static byte[] StreamToBytes(Stream stream)
{
  using (MemoryStream ms = new MemoryStream())
  {
    stream.CopyTo(ms);
    return ms.ToArray();
  }
}
private static async Task<string> FetchCognitiveAccessTokenAsync(
  string subscriptionKey)
{
  string fetchUri = "https://api.cognitive.microsoft.com/sts/v1.0";
  using (var client = new HttpClient())
  {
    client.DefaultRequestHeaders.Add("Ocp-Apim-Subscription-Key", subscriptionKey);
    UriBuilder uriBuilder = new UriBuilder(fetchUri);
    uriBuilder.Path += "/issueToken";
    var response = await client.PostAsync(uriBuilder.Uri.AbsoluteUri, null);
    return await response.Content.ReadAsStringAsync();
  }
}

Mit dieser Funktion kann ein Benutzer einen Dateinamenparameter angeben, nachdem er sich bei seiner Azure-Funktion angemeldet hat. Wenn eine Datei einen WAV-Dateinamen besitzt und englischsprachigen Inhalt enthält, wird dieser in englischsprachigen Text transkribiert. Da dies mit Azure Functions implementiert wird, verursacht Ihre Funktion normalerweise nur Kosten, wie sie aufgerufen wird. Dies bietet eine flexible Möglichkeit, die Daten zu erweitern, die in Microsoft Graph vorhanden sind.

Azure Functions und Microsoft Graph

Die beiden hier vorgestellten Beispiele zeigen, wie sowohl menschliche als auch technische Prozesse auf Daten in Microsoft Graph aufgebaut werden können. Kombiniert mit der Breite der Verwendungsmöglichkeiten von Microsoft Graph und der Fähigkeit, Workloads zu kreuzen (z.B. Organisationshierarchie und Aufgaben, wie es bei dem Aufgabenbeispiel in diesem Artikel der Fall war), können Sie in Ihrer gesamten Organisation Wertvorteile erstellen und hinzufügen. Durch die Kombination von Microsoft Graph und Azure Functions können Sie eine vollständige API für Ihre Organisation erstellen und die Produktivität für alle Beteiligten steigern. Beginnen Sie mit der Entwicklung von Lösungen für Ihr Unternehmen, indem Sie developer.microsoft.com/graph besuchen und mit Azure Functions unter functions.azure.com arbeiten.


Mike Ammerlaan ist Direktor des Produktmarketings im Microsoft Office Ecosystem-Team und hilft Benutzern dabei, ansprechende Lösungen mit Office 365 zu entwickeln. Davor arbeitete er 18 Jahre lang bei Microsoft als Program Manager und entwickelte Produkte wie SharePoint, Excel, Yammer, Bing Maps und Combat Flight Simulator.

Unser Dank gilt den folgenden technischen Experten von Microsoft für die Durchsicht dieses Artikels:  Ryan Gregg, Matthew Henderson und Dan Silver


Diesen Artikel im MSDN Magazine-Forum diskutieren