C# で Microsoft Entra ID にアプリを登録する方法

Microsoft Graph SDK を使う。Azure SDK ではなかった。

www.nuget.org

アプリが既に存在するか検索し、なければ登録するコードは次の通り。

using Azure.Identity;
using Microsoft.Graph;
using Microsoft.Graph.Applications.Item.AddPassword;
using Microsoft.Graph.Models;

var graphServiceClient = new GraphServiceClient(
    new DefaultAzureCredential());

const string applicationDisplayName = "my-entra-id-application";
var applications = await graphServiceClient.Applications.GetAsync(x =>
{
    x.QueryParameters.Filter = $"displayName eq '{applicationDisplayName}'";
});
var application = applications?.Value?.FirstOrDefault();
if (application == null)
{
    // アプリを登録
    var newApplication = new Application
    {
        DisplayName = applicationDisplayName,
        SignInAudience = "AzureADMyOrg",
    };
    application = await graphServiceClient.Applications
        .PostAsync(newApplication);
}
Console.WriteLine($"AppID: {application?.Id}");
Console.WriteLine($"AppDisplayName: {application?.DisplayName}");

var principals = await graphServiceClient.ServicePrincipals.GetAsync(x =>
{
    x.QueryParameters.Filter = $"appId eq '{application!.AppId}'";
});
var principal = principals?.Value?.FirstOrDefault();
if (principal == null)
{
    // サービスプリンシパルを作成
    var newPrincipal = new ServicePrincipal
    {
        DisplayName = applicationDisplayName,
        AppId = application!.AppId,
        AccountEnabled = true,
    };
    principal = await graphServiceClient.ServicePrincipals.PostAsync(newPrincipal);
}
Console.WriteLine($"ServicePrincipalId:{principal?.Id}");

const string credentialDisplayName = "my-app-secret";
var credential = application?.PasswordCredentials?.Find(x => x.DisplayName == credentialDisplayName);
if (credential == null)
{
    // シークレットを登録
    var newPasswordCredential = new PasswordCredential
    {
        DisplayName = credentialDisplayName,
        EndDateTime = DateTimeOffset.UtcNow.AddYears(1),
        KeyId = Guid.NewGuid(),
    };
    credential = await graphServiceClient.Applications[application!.Id]
        .AddPassword.PostAsync(new AddPasswordPostRequestBody()
        {
            PasswordCredential = newPasswordCredential,
        });

    // SecretText は readonly で、作成直後しか取得できない
    Console.WriteLine($"PasswordCredentialSecretText: {credential?.SecretText}");
}
Console.WriteLine($"PasswordCredentialID:{credential?.KeyId}");
Console.WriteLine($"PasswordCredentialDisplayName: {credential?.DisplayName}");

IAMでアプリにロールを割り当てたいので、サービスプリンシパルも作成しておく必要があった。

シークレットも無ければ登録している。登録した際のレスポンスでしかシークレットの値を取得できない。後で取得しようとしたらnull。後から取得できないのはポータルと同じだな。