0

I'm looking for a current example on how to programmatically create Service Principals in .NET (using another Service Principal). Including setting a certificate credential (ideally one which is stored/generated in Azure Key Vault)

I found this old answer but no current equivalent which would probably need to use the Microsoft.Graph Nuget package?!

silent
  • 14,494
  • 4
  • 46
  • 86

3 Answers3

1

I tried to reproduce the same in my environment and got below results:

I have one certificate named sricert in my Azure Key Vault like below:

enter image description here

Now I registered one Azure AD application and added same certificate in it like below:

enter image description here

Make sure to add and grant admin consent to below API permission that is required to create Azure AD application:

enter image description here

Now I ran below c# code that fetches certificate from Azure key vault and creates new Azure AD application using Microsoft.Graph package via existing service principal like below:

using Azure.Identity;
using Azure.Security.KeyVault.Certificates;
using Azure.Security.KeyVault.Secrets;
using Microsoft.Graph;
using Microsoft.Identity.Client;
using System;
using System.Net.Http.Headers;
using System.Security.Cryptography.X509Certificates;
using System.Threading.Tasks;

namespace ConsoleApp1
{
    class Program
    {
        static async Task Main(string[] args)
        {
            string tenantId = "58e70374-11f2-4e91-af40-xxxxxxxxx";
            string clientId = "cd88c0ed-30e9-4624-bfc6-xxxxxxxx";
            string certificateName = "sricert";
            string keyVaultUri = $"https://srikeyv.vault.azure.net/";

            // Authenticate with Azure Key Vault and obtain an access token
            var credential = new DefaultAzureCredential();
            var certClient = new CertificateClient(new Uri(keyVaultUri), credential);
            var cert = await certClient.GetCertificateAsync(certificateName);

            var secretClient = new SecretClient(new Uri(keyVaultUri), credential);
            var secret = await secretClient.GetSecretAsync(cert.Value.Name, cert.Value.Properties.Version);

            var certificateBytes = Convert.FromBase64String(secret.Value.Value);
            var certificate = new X509Certificate2(certificateBytes);

            var app = ConfidentialClientApplicationBuilder
                .Create(clientId)
                .WithAuthority($"https://login.microsoftonline.com/{tenantId}")
                .WithCertificate(certificate)
                .Build();

            var scopes = new[] { "https://graph.microsoft.com/.default" };
            var result = await app.AcquireTokenForClient(scopes).ExecuteAsync();
            var accessToken = result.AccessToken;

            // Create a new Azure AD application using the Microsoft Graph API
            var graphClient = new GraphServiceClient(new DelegateAuthenticationProvider((requestMessage) =>
            {
                requestMessage.Headers.Authorization = new AuthenticationHeaderValue("bearer", accessToken);
                return Task.FromResult(0);
            }));

            var application = new Application
            {
                DisplayName = "Sri GraphSP",
                SignInAudience = "AzureADMyOrg",
                PublicClient = new Microsoft.Graph.PublicClientApplication
                {
                    RedirectUris = new[] { "https://jwt.ms" },
                },
            };

            await graphClient.Applications
                .Request()
                .AddAsync(application);

            Console.WriteLine("New Azure AD application created successfully");
        }
    }
}

Response:

enter image description here

To confirm that, I checked the same in Azure Portal where new Azure AD application named Sri GraphSP created successfully like below:

enter image description here

Sridevi
  • 10,599
  • 1
  • 4
  • 17
  • Thanks for this! In the meantime I also figured this out. Posted my solution below, including creating the certificate and service principal – silent Feb 24 '23 at 12:26
  • 1
    Take a look at my solution, you can also create the GraphClient with a TokenCredential – silent Feb 24 '23 at 12:27
0

I figured it out after a while, including creating a certificate in Key Vault first:

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

var certName = "my-sp-cert";

var certificateClient = new CertificateClient(new Uri("https://my.vault.azure.net"), new DefaultAzureCredential());
var op = await certificateClient.StartCreateCertificateAsync(certName, CertificatePolicy.Default);
var certResponse = await op.WaitForCompletionAsync();
var cert = certResponse.Value;

// First, we need to create an App Registration, aka "AAD Application"
var application = new Application
{
    DisplayName = "my-app",
    KeyCredentials = new List<KeyCredential>()
    {
        new KeyCredential
        {
            StartDateTime = cert.Properties.CreatedOn,
            EndDateTime = cert.Properties.ExpiresOn,
            Type = "AsymmetricX509Cert",
            Usage = "Verify",
            Key = cert.Cer,
            DisplayName = certName
        }
    }
};
var createdApp = await graphClient.Applications.Request().AddAsync(application);

_logger.LogInformation("Created AAD application {appId}, {objectId}", createdApp.AppId, createdApp.Id);

// Next, we add a Service Principal (SPN) to the App Registration
var spn = new ServicePrincipal()
{
    AppId = createdApp.AppId
};

var createdSpn = await graphClient.ServicePrincipals.Request().AddAsync(spn);

_logger.LogInformation("Created AAD service principal {clientId}, {objectId}", createdSpn.AppId, createdSpn.Id);
silent
  • 14,494
  • 4
  • 46
  • 86
  • I guess I must have misunderstood your last sentence in the question as _`I don't want to` use the Microsoft.Graph Nuget package_ – Tom W Feb 26 '23 at 16:18
-1

az ad sp create-for-rbac using the Azure CLI is current and cross-platform.

Tom W
  • 5,108
  • 4
  • 30
  • 52