2

How to enumerate Azure subscriptions and tenants programmatically? This is related to my previous question Login-AzureRmAccount (and related) equivalent(s) in .NET Azure SDK.

Basically I try to replicate the behavior of Login-AzureRmAccount and Get-AzureRmSubscription in desktop or a console application. Thus far I've figured out MSAL seems to always require client ID and tenant ID, so there needs to be some other library to acquire those from. After this I would like to go about creating a service principal programmatically using the most current library, but I suppose that is a subject for further investigation (and questions if needed).

Veksi
  • 3,556
  • 3
  • 30
  • 69

1 Answers1

2

Actually, the Login-AzureRmAccount and Get-AzureRmSubscription use the Microsoft Azure PowerShell app to operate the Azure resource through Resource Manager REST APIs.

To simulate the same operations using REST as PowersShell commands, we can also use this app. However since this app is register on Azure portal(not the v2.0 app) so we are not able to acquire the token using this app via MSAL. We need to use Adal instead of MSAL.

Here is a code sample to list the subscriptions using admin account via Microsoft.WindowsAzure.Management using this app for your reference:

public static void ListSubscriptions()
{
     string authority = "https://login.microsoftonline.com/common";
     string resource = "https://management.core.windows.net/";
     string clientId = "1950a258-227b-4e31-a9cf-717495945fc2";
    Uri redirectUri = new Uri("urn:ietf:wg:oauth:2.0:oob");
    AuthenticationContext authContext = new AuthenticationContext(authority);
    var access_token = authContext.AcquireTokenAsync(resource, clientId, redirectUri, new PlatformParameters (PromptBehavior.Auto)).Result.AccessToken;

    var tokenCred = new Microsoft.Azure.TokenCloudCredentials(access_token);
    var subscriptionClient = new SubscriptionClient(tokenCred);
    foreach (var subscription in subscriptionClient.Subscriptions.List())
    {
        Console.WriteLine(subscription.SubscriptionName);
    }
}

Update:

string resource = "https://management.core.windows.net/";
string clientId = "1950a258-227b-4e31-a9cf-717495945fc2";
string userName = "";
string password = "";

HttpClient client = new HttpClient();
string tokenEndpoint = "https://login.microsoftonline.com/common/oauth2/token";
var body = $"resource={resource}&client_id={clientId}&grant_type=password&username={userName}&password={password}";
var stringContent = new StringContent(body, Encoding.UTF8, "application/x-www-form-urlencoded");

var result = client.PostAsync(tokenEndpoint, stringContent).ContinueWith<string>((response) =>
{
    return response.Result.Content.ReadAsStringAsync().Result;
}).Result;

JObject jobject = JObject.Parse(result);
var token = jobject["access_token"].Value<string>();

client.DefaultRequestHeaders.Add("Authorization", $"bearer {token}");
var subcriptions = client.GetStringAsync("https://management.azure.com/subscriptions?api-version=2014-04-01-preview").Result;

Console.WriteLine(subcriptions);
Fei Xue
  • 14,369
  • 1
  • 19
  • 27
  • Strange. I install NuGet package `Microsoft.IdentityModel.Clients.ActiveDirectory 3.14.0` and if I make`var access_token = authContext.AcquireTokenAsync(resource, clientId, redirectUri, new Microsoft.IdentityModel.Clients.ActiveDirectory.PlatformParameters(PromptBehavior.Auto)).Result;`, there isn't such a class. If I exclude `PromptBehavior.Auto`, I get `NotImplementedException`. Though I see it used [here](https://learn.microsoft.com/en-us/azure/active-directory/develop/active-directory-devquickstarts-dotnet) and elsewhere. Would that come from `Microsoft.WindowsAzure.Management`? – Veksi Jun 27 '17 at 05:53
  • It might be relevant I have a .NET Core console application and it might be missing some constructors. I examine this further in about 12 hours, but it might be that with regards the example you gave (and my woes with regards example codes), .NET Core is the culprit. – Veksi Jun 27 '17 at 06:02
  • @Veksi The code above only works for the .Net Framework. Based on the test, the ADAL doesn't implement the `Microsoft.IdentityModel.Clients.ActiveDirectory.WebUIFactory.CreateAuthenticationDialog(IPlatformParameters parameters)`. I suggest that you rise the feedback for the adal library from [here](https://github.com/AzureAD/azure-activedirectory-library-for-dotnet/issues) if you want it to support it for .net core. – Fei Xue Jun 27 '17 at 07:22
  • And we can also send the HTTP request directly instead of using `Microsoft.WindowsAzure.Management`. Here is the request for your reference:`https://management.azure.com/subscriptions?api-version=2014-04-01-preview`. As a workaround of this issue, you can use the resource owner password to acquire the access token. I also update the code sample in the post. However there are some limitation for this workaround. For example, the users need to grant the permission to the app first. This could suggest OP to use the `Login-AzureRmAccount` command. And this flow also doesn't support MFA. – Fei Xue Jun 27 '17 at 08:03
  • This was very helpful, I'll be able to make some progress. Hopefully this helps others too searching for answers. I think I'll raise an issue about this in GH (or two, other one in Fluent). – Veksi Jun 29 '17 at 05:51