1
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

Microsoft Graph で Microsoft 365 Enterprise 組織内のメールを取得する

Last updated at Posted at 2020-07-12

Microsoft Graph で Microsoft 365 Enterprise 組織内のメールを取得する方法を試してみました。これを実現させるためには、アプリケーションを許可(サインインしたユーザー無しで、バックグランドサービスまたはデーモンとして実行)するように、Azure Active Directory にアプリケーションを登録する必要があります。

NLPやGraph等と組み合わせると、組織の効率化に繋がる分析に使えそうですね。

Azure Active Directory にアプリケーション登録

まずは、組織の Azure Active Directory にアプリケーションを登録します。Microsoft Azureのポータルから、Azure Active Directory に移動してください。
01.png
左側のメニューの「アプリの登録」から、「新規登録」を選びます。
02.png
登録するアプリの名前を入力し、今回は「シングルテナント」を選択します。入力が終わったら「登録」します。
03.png
登録したアプリ(office-app)の画面になるので、「アプリケーション(クライアント)ID」、「ディレクトリ(テナント)ID」をメモしておいてください。
04.png
「APIのアクセス許可」から「アクセス許可の追加」をします。
06.png
今回は、ディレクトリの読み込みと、ユーザの読み込み、メールの読み込みのアクセスに関して許可を追加します。まずは、「Microsoft Graph」を選択します。
07.png
「アプリケーションの許可」を選択して、「User.Read.All」「Directory.Read.All」「Mail.Read.All」のアクセス許可を追加します。検索すると見つけやすいです。(今回のメールとは関係ありませんが、Files.Read.Allも追加しました)
08.png
追加した後は、組織のテナントに管理者の同意を与えます。(今回のメールとは関係ありませんが、Files.Read.Allも追加してます)
12.png
最後に、アプリからのアクセスに使用するクライアントシークレットを作成します。シークレット(値)は一度しか表示されないので、必ずメモしておいてください。
14.png

バックグランドサービスとして動くアプリを作成

まずは、サインインしたユーザー無しで、バックグランドサービスまたはデーモンとして実行できるように、認証プロバイダを作成します。

ClientSecretAuthProvider.cs
using Microsoft.Graph;
using Microsoft.Identity.Client;
using System;
using System.Net.Http;
using System.Threading.Tasks;

namespace GraphMessagesSample
{
    public class ClientSecretAuthProvider : IAuthenticationProvider
    {
        private IConfidentialClientApplication msalClient;
        private string appId;
        private string clientSecret;
        private string[] scopes;
        private string tenantId;

        public ClientSecretAuthProvider(string appId, string[] scopes, string tenantId, string clientSecret)
        {
            this.appId = appId;
            this.clientSecret = clientSecret;
            this.scopes = scopes;
            this.tenantId = tenantId;

            this.msalClient = ConfidentialClientApplicationBuilder.Create(this.appId)
                .WithAuthority(AzureCloudInstance.AzurePublic, this.tenantId)
                .WithClientSecret(this.clientSecret)
                .Build();
        }

        public async Task<string> GetAuthorizationHeader()
        {
            try
            {
                var result = await this.msalClient
                    .AcquireTokenForClient(this.scopes)
                    .ExecuteAsync();
                return result.CreateAuthorizationHeader();
            }
            catch (Exception exception)
            {
                Console.WriteLine($"Error getting access token: {exception.Message}");
                return null;
            }
        }

        public async Task AuthenticateRequestAsync(HttpRequestMessage requestMessage)
        {
            requestMessage.Headers.Add("Authorization", await GetAuthorizationHeader());
        }
    }
}

次に、Graph API から、セキュリティグループ(GetGroupsAsync)、グループのユーザー(GetGroupMembersAsync)、メール(GetMessagesAsync)を取得するプログラムを作成します。

GraphHelper.cs
using Microsoft.Graph;
using System;
using System.Collections.Generic;
using System.Threading.Tasks;

namespace GraphMessagesSample
{
    public class GraphHelper
    {
        private static GraphServiceClient graphClient;
        public static void Initialize(IAuthenticationProvider authProvider)
        {
            graphClient = new GraphServiceClient(authProvider);
        }

        public static async Task<IEnumerable<Group>> GetGroupsAsync()
        {
            try
            {
                List<Group> groups = new List<Group>();
                var resultPage = await graphClient.Groups.Request().GetAsync();
                while (true)
                {
                    groups.AddRange(resultPage.CurrentPage);
                    if (resultPage.NextPageRequest == null)
                    {
                        break;
                    }
                    resultPage = resultPage.NextPageRequest.GetAsync().Result;
                }
                return groups;
            }
            catch (ServiceException ex)
            {
                Console.WriteLine($"Error getting events: {ex.Message}");
                return null;
            }
        }
        public static async Task<IEnumerable<DirectoryObject>> GetGroupMembersAsync(string groupId)
        {
            try
            {
                List<DirectoryObject> dirobjects = new List<DirectoryObject>();
                var resultPage = await graphClient.Groups[groupId].Members.Request().GetAsync();
                while (true)
                {
                    dirobjects.AddRange(resultPage.CurrentPage);
                    if (resultPage.NextPageRequest == null)
                    {
                        break;
                    }
                    resultPage = resultPage.NextPageRequest.GetAsync().Result;
                }
                return dirobjects;
            }
            catch (ServiceException ex)
            {
                Console.WriteLine($"Error getting events: {ex.Message}");
                return null;
            }
        }
        public static async Task<IEnumerable<Message>> GetMessagesAsync(string userId)
        {
            try
            {
                List<Message> dirobjects = new List<Message>();
                var resultPage = await graphClient.Users[userId].Messages.Request().GetAsync();
                while (true)
                {
                    dirobjects.AddRange(resultPage.CurrentPage);
                    if (resultPage.NextPageRequest == null)
                    {
                        break;
                    }
                    resultPage = resultPage.NextPageRequest.GetAsync().Result;
                }
                return dirobjects;
            }
            catch (ServiceException ex)
            {
                Console.WriteLine($"Error getting events: {ex.Message}");
                return null;
            }
        }
    }
}

あとは、これらを組み合わせてメールを表示するプログラムを作成します。組織のグループに属しているユーザのメールの件名を全て表示するので、特定グループだけ表示したい場合などはプログラムを変更してください。

Program.cs
using System;
using System.Collections.Generic;
using Microsoft.Extensions.Configuration;
using Microsoft.Graph;

namespace GraphMessagesSample
{
    class Program
    {
        static Dictionary<string, string> LoadClientSecretAppSettings()
        {
            Dictionary<string, string> result = null;
            // Get config ftom AppSettings
            var appConfig = new ConfigurationBuilder()
                .AddUserSecrets<Program>()
                .Build();
            var appId = appConfig["appId"];
            var scopes = appConfig["scopes"];
            var tenantId = appConfig["tenantId"];
            var clientSecret = appConfig["clientSecret"];
            if (string.IsNullOrEmpty(appId) == false &&
                string.IsNullOrEmpty(scopes) == false &&
                string.IsNullOrEmpty(tenantId) == false &&
                string.IsNullOrEmpty(clientSecret) == false)
            {
                result = new Dictionary<string, string>()
                {
                    {"appId", appId},
                    {"scopes", scopes},
                    {"tenantId", tenantId},
                    {"clientSecret", clientSecret}
                };
            }
            return result;
        }

        static void GetMessages()
        {
            var groups = GraphHelper.GetGroupsAsync().Result;
            foreach (var group in groups)
            {
                Console.WriteLine($"group.Id: {group.Id}");
                Console.WriteLine($"group.DisplayName: {group.DisplayName}");
                var members = GraphHelper.GetGroupMembersAsync(group.Id).Result;
                foreach (Microsoft.Graph.User member in members)
                {
                    Console.WriteLine($"member.Id: {member.Id}");
                    Console.WriteLine($"member.DisplayName: {member.DisplayName}");
                    Console.WriteLine($"member.UserPrincipalName: {member.UserPrincipalName}");
                    var messages = GraphHelper.GetMessagesAsync(member.Id).Result;
                    foreach (Microsoft.Graph.Message message in messages)
                    {
                        Console.WriteLine($"message.Subject: {message.Subject}");
                    }
                }
            }
        }

        static void Main(string[] args)
        {
            IAuthenticationProvider authProvider = null;

            var appConfig = LoadClientSecretAppSettings();
            if (appConfig == null)
            {
                Console.WriteLine("Missing or invalid AppSettings");
                return;
            }
            var appId = appConfig["appId"];
            var scopesString = appConfig["scopes"];
            var scopes = scopesString.Split(';');
            var tenantId = appConfig["tenantId"];
            var clientSecret = appConfig["clientSecret"];
            // Initialize the auth provider
            authProvider = new ClientSecretAuthProvider(appId, scopes, tenantId, clientSecret);
            // Initialize Graph client
            GraphHelper.Initialize(authProvider);
            // Get messages
            GetMessages();
        }
    }
}

実行する

上で書いたコードは、githubにおいてあるので、cloneします。

git clone https://github.com/KentaroAOKI/GraphMessagesSample.git
cd GraphMessagesSample

Azure Active Directory でアプリケーション登録をした際にメモした値を使って設定します。

dotnet user-secrets init
dotnet user-secrets set appId "アプリケーション(クライアント)ID"
dotnet user-secrets set scopes "https://graph.microsoft.com/.default"
dotnet user-secrets set tenantId "ディレクトリ(テナント)ID"
dotnet user-secrets set clientSecret "クライアントシークレット"

ビルドして実行します。

dotnet build
dotnet run

さいごに

メール件名だけでなく、宛先や本文も取得できます。以下のGraph APIドキュメントを参考にしてください。

メールは正しくパースされているし、件名、本文、宛先など、エンコードが統一されて取得できるので、すごく便利ですね。Graph APIの凄さに改めて驚きました。

1
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?