如何列出 Microsoft.Azure.ResourceManager 的订阅?

How to list subscriptions with Microsoft.Azure.ResourceManager?

上下文

我的核心目标是用 C# 编写一个 Azure WebApps 部署工具。过程大致会

  1. 用户登录
  2. 用户选择订阅
  3. 用户选择或创建资源组
  4. 用户为网络应用程序选择或创建存储空间
  5. 用户选择或创建网络服务计划
  6. 用户选择或创建网络应用程序
  7. 工具使用 Kudu 将网络应用程序上传到 POST a zip

由于最后一步无法在门户中完成,我的想法是在 GUI 工具中完成所有操作。

我开始使用 Kudu 的 ARMClient.Authentication and Microsoft.Azure.ResourceManager 1.0.0-preview. However, when it comes to creating a storage account I get a permissions error (The subscription is not registered to use namespace Microsoft.Storage), so my plan B was to do the authentication myself following Brady Gaster's blog post

问题

我已经按照文档设置了一个应用程序,并使用它的 clientIdtenantId 我能够登录并列出租户。但我无法列出任何订阅。 (注意,我已经部分省略了 clientIdtenantId 以防万一完整提供它们存在安全风险)。

        string clientId = "f62903b9-ELIDED";
        string tenantId = "47b6e6c3-ELIDED";
        const string redirectUri = "urn:ietf:wg:oauth:2.0:oob";
        const string baseAuthUri = "https://login.microsoftonline.com/";
        const string resource = "https://management.core.windows.net/";

        var ctx = new AuthenticationContext(baseAuthUri + tenantId);
        var authResult = ctx.AcquireToken(resource, clientId, new Uri(redirectUri), PromptBehavior.Auto);
        var token = new TokenCredentials(authResult.AccessToken);
        var subClient = new SubscriptionClient(token);

        var tenants = await subClient.Tenants.ListAsync();
        foreach (var tenant in tenants) Console.WriteLine(tenant.TenantId);

        var subs = await subClient.Subscriptions.ListAsync();
        foreach (var sub in subs) Console.WriteLine(sub.DisplayName);

当我 运行 这会提示我登录,并列出与我拥有或共同管理的订阅相对应的租户。但它没有列出单个订阅。如果我将 ID 更改为常用的(我认为正式用于 Powershell)值

        clientId = "1950a258-227b-4e31-a9cf-717495945fc2";
        tenantId = "common";

那就一样了

为了获得我的订阅列表,我错过了哪一步?

您可以研究一些事情...

1) 您在创建存储帐户期间看到的错误可能是由于资源提供程序未注册用于订阅。任何 RP 在使用前都需要注册,一些客户端(Portal、PowerShell)会为您注册 RP,因此您永远不会注意到它。请参阅:https://msdn.microsoft.com/en-us/library/azure/dn790548.aspx - 如果用户有足够的权限,您应该能够从您的代码中做到这一点。

2) 您可能无法获得任何订阅,因为您的端点 (management.core.windows.net) 是 Azure 服务管理的端点,而不是 Azure 资源管理器 (management.azure.com)。如果通过 AzureRM 和 RBAC 授予订阅访问权限,旧的 ASM api 将看不到(即无法访问)这些订阅。

您需要遍历租户,在租户中进行身份验证并获取每个租户的订阅列表。

以下代码将像 Get-AzureRmSubscription powershell cmdlet 那样输出订阅。

class Program
{
    private static string m_resource = "https://management.core.windows.net/";
    private static string m_clientId = "1950a258-227b-4e31-a9cf-717495945fc2"; // well-known client ID for Azure PowerShell
    private static string m_redirectURI = "urn:ietf:wg:oauth:2.0:oob"; // redirect URI for Azure PowerShell

    static void Main(string[] args)
    {
        try
        {
            var ctx = new AuthenticationContext("https://login.microsoftonline.com/common");
            // This will show the login window
            var mainAuthRes = ctx.AcquireToken(m_resource, m_clientId, new Uri(m_redirectURI), PromptBehavior.Always);
            var subscriptionCredentials = new TokenCloudCredentials(mainAuthRes.AccessToken);
            var cancelToken = new CancellationToken();
            using (var subscriptionClient = new SubscriptionClient(subscriptionCredentials))
            {
                var tenants = subscriptionClient.Tenants.ListAsync(cancelToken).Result;
                foreach (var tenantDescription in tenants.TenantIds)
                {
                    var tenantCtx = new AuthenticationContext("https://login.microsoftonline.com/" + tenantDescription.TenantId);
                    // This will NOT show the login window
                    var tenantAuthRes = tenantCtx.AcquireToken(
                        m_resource,
                        m_clientId,
                        new Uri(m_redirectURI),
                        PromptBehavior.Never,
                        new UserIdentifier(mainAuthRes.UserInfo.DisplayableId, UserIdentifierType.RequiredDisplayableId));
                    var tenantTokenCreds = new TokenCloudCredentials(tenantAuthRes.AccessToken);
                    using (var tenantSubscriptionClient = new SubscriptionClient(tenantTokenCreds))
                    {
                        var tenantSubscriptioins = tenantSubscriptionClient.Subscriptions.ListAsync(cancelToken).Result;
                        foreach (var sub in tenantSubscriptioins.Subscriptions)
                        {
                            Console.WriteLine($"SubscriptionName : {sub.DisplayName}");
                            Console.WriteLine($"SubscriptionId   : {sub.SubscriptionId}");
                            Console.WriteLine($"TenantId         : {tenantDescription.TenantId}");
                            Console.WriteLine($"State            : {sub.State}");
                            Console.WriteLine();
                        }
                    }
                }
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex.ToString());
        }
        finally
        {
            Console.WriteLine("press something");
            Console.ReadLine();
        }
    }
}