Reputation: 701
What I want to do: create and configure a B2C tenant via the REST API.
What I did:
I created an app registration named CDTester and a secret for it in my default directory, then added it to the target subscription with the role Contributor
.
I got a config object cfg loaded from user-secrets with the application id and secret of CDTester, with the tenant id of my default directory and with the id of the target subscription.
In code I authenticate with ConfidentialClientApplicationBuilder
.
Then I use the token I get to do a few things:
Steps 1-3 work fine. But step 4 gives me a 401/Unauthorized. The body of the response is formatted as content-type HTML (instead of JSON) and contains only
You do not have permission to view this directory or page.
Because steps 1-3 work I know that I am authenticating correctly. I can even access the B2C specific API (which I use in step 3).
Next I went into the Portal and made CDTester
an owner of the subscription, so there ought to be no action it is not allowed to do. Alas, to no avail, I got the same result.
For reference, here are relevant portions of my code:
// getting the access token
var app = ConfidentialClientApplicationBuilder
.Create(authConfig.ApplicationId)
.WithTenantId(authConfig.LoginTenantId)
.WithClientSecret(authConfig.ApplicationSecret)
.Build();
var authResult = await app.AcquireTokenForClient(new[] {"https://management.azure.com/.default"})
.ExecuteAsync();
// I use FlurlHttp and set the token
FlurlHttp.Configure(s => s.BeforeCall = c => c.Request.Headers.Add("Authorization", $"Bearer {authResult.AccessToken}"));
// this call works just fine
var nameAvailabilityResponse = await $"https://management.azure.com/subscriptions/{authConfig.SubscriptionId}/providers/Microsoft.AzureActiveDirectory/checkNameAvailability?api-version=2019-01-01-preview"
.PostJsonAsync(new
{
name = creation.FullDirectoryName,
countryCode = creation.CountryCode
})
.ReceiveJson();
if (!nameAvailabilityResponse.nameAvailable)
{
await Console.Error.WriteLineAsync(nameAvailabilityResponse.message);
return ExitCodes.DirectoryNameIsNotAvailable;
}
var displayName = creation.EnvironmentName == "production"
? "DEON"
: $"DEON - {creation.EnvironmentName.ToUpperInvariant()}";
// this one gives the 401
var creationResponse = await $"https://management.azure.com/subscriptions/{authConfig.SubscriptionId}/resourceGroups/{creation.ResourceGroupName}/providers/Microsoft.AzureActiveDirectory/b2cDirectories/{creation.FullDirectoryName}?api-version=2019-01-01-preview"
.AllowAnyHttpStatus()
.PutJsonAsync(new
{
location = "Europe",
properties = new
{
createTenantProperties = new
{
countryCode = creation.CountryCode,
displayName
}
},
sku = new
{
name = "Standard",
tier = "A0"
}
});
Googling brought exactly zero pertinent or even just semi-pertinent hits. Now I'm stuck.
Upvotes: 2
Views: 1154
Reputation: 136306
Based on the answer provided here: https://learn.microsoft.com/en-us/answers/questions/481152/creating-azure-b2c-tenant-via-rest-api-fails-with.html, it seems you cannot create an Azure AD B2C Tenant using a Service Principal (at least as of today).
You will need to execute this request in context of an actual user. In other words, you will need to acquire a token to execute Service Management API with user_impersonation
scope which is not possible with a Service Principal.
Upvotes: 3