从守护进程创建新的Azure SQL Server

本文关键字:Azure SQL Server 守护 进程 创建 | 更新日期: 2023-09-27 18:12:26

如何使用Azure SDK从守护进程(技术上是队列处理器)在资源组内创建新的Azure SQL Server ?我遇到的问题是在尝试创建时出现授权错误。下面是例外:

Hyak.Common.CloudException AuthorizationFailed: The client 
'XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX' with object id 'XXXXXXXX- 
XXXX-XXXX-XXXX-XXXXXXXXXXXX' does not have authorization to 
perform action 'Microsoft.Sql/servers/write' over scope 
'/subscriptions/XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX/resourceGroups/{myResourceGroupName}/providers/Microsoft 
.Sql/servers/{newServerName}'.
*(The actual exception has real values in each of the 
placeholders.)*

我正在使用Microsoft.Azure.Management.Sql NuGet包,版本0.38.0-预发布(目前可用的最新版本)。

我之前尝试使用CertificateCloudCredentials进行更改,但是它产生了以下异常:

 Hyak.Common.CloudException AuthenticationFailed: Authentication 
 failed. The 'Authorization' header is not present or provided 
 in an invalid format.

我现在使用的是通过以下方式获得的TokenCloudCredentials:

AuthenticationContext aContext = new AuthenticationContext(string.Format(
            anApiConfiguration.LoginUriFormat,
            anApiConfiguration.TenantId));
ClientCredential aClientCredential = new ClientCredential(
            anApiConfiguration.ClientId, 
            anApiConfiguration.ClientSecretKey);
AuthenticationResult aTokenResponse = await aContext.AcquireTokenAsync(
            anApiConfiguration.BaseResourceUrl, 
            aClientCredential);

从中我成功地获得了一个令牌。然后我使用这个标记如下:

SqlManagementClient sql = new SqlManagementClient(anAuthToken);
ServerCreateOrUpdateProperties someProperties = 
    new ServerCreateOrUpdateProperties {
        AdministratorLogin = someSettings.AdminAccountName,
        AdministratorLoginPassword = someSettings.AdminPassword,
        Version = "12.0"
};
ServerGetResponse aCreateResponse = 
    await sql.Servers.CreateOrUpdateAsync(
        someSettings.GetResourceGroup(aRegionType),
        aServerName, 
        new ServerCreateOrUpdateParameters(someProperties, aRegion));

最后一行,CreateOrUpdateAsync调用,是产生本文顶部异常的原因。

在Azure Active Directory中,客户端应用程序在Windows Azure服务管理API应用程序中被授予Access Azure服务管理(预览)委托权限(唯一存在于该应用程序的权限)。(如果它很重要,我也授予了客户端应用程序的所有可用权限Windows Azure Active Directory应用程序)

从守护进程创建新的Azure SQL Server

我想我已经弄清楚了问题是什么以及如何修复它(我也能够在我的工作计算机上复制这个)。

看起来在示例应用程序中,获取身份验证令牌的默认方法是使用错误的帐户。例如,我的订阅和应用程序在我的example@outlook.com电子邮件帐户下,但是当我从我的工作计算机上运行控制台应用程序时,应用程序获得username@company.com的令牌并尝试在该帐户下执行操作。似乎获取身份验证令牌的代码正在使用来自计算机(IE?)的一些缓存帐户信息。

您看到的错误消息与RBAC(基于角色的访问控制)有关,RBAC限制了经过身份验证但未经授权的帐户对您的资源(https://azure.microsoft.com/en-us/documentation/articles/role-based-access-control-configure/)执行操作。

<<p> 解决方案/strong>

您可以稍微修改一下代码,告诉它在执行操作时使用哪个帐户。将示例应用中的"GetAccessTokenUsingUserCredentials"方法更改为如下所示:

    private static AuthenticationResult GetAccessTokenUsingUserCredentials(UserCredential userCredential)
    {
        AuthenticationContext authContext = new AuthenticationContext
            ("https://login.windows.net/" /* AAD URI */
            + "YOU.onmicrosoft.com" /* Tenant ID or AAD domain */);
        AuthenticationResult token = authContext.AcquireToken(
            "https://management.azure.com/"/* the Azure Resource Management endpoint */,
            "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX" /* application client ID from AAD*/,
            new Uri("XXXX" /* redirect URI */,
            PromptBehavior.Auto,
            new UserIdentifier(userCredential.UserName, UserIdentifierType.RequiredDisplayableId));
        return token;
    }

,并在main函数中将获取标记的行更改为:

        var token = GetAccessTokenUsingUserCredentials(new UserCredential("<desired_account_email>"));

你应该可以稍微修改一下,也可以输入密码,这样AAD登录框就不会弹出。

我希望这对你有用!

编辑:以下是如何让它为web应用程序工作:
  1. 从web应用程序的Configure页面获取您的客户端ID和密钥(您需要为密钥选择持续时间,然后单击保存以生成密钥)
  2. 将应用程序中的认证码更改为如下所示:

    private static AuthenticationResult GetAccessTokenForWebApp()
    {
        AuthenticationContext authContext = new AuthenticationContext
            ("https://login.windows.net/" /* AAD URI */
            + "YOU.onmicrosoft.com" /* Tenant ID or AAD domain */);
        ClientCredential cc = new ClientCredential("<client_id>", "<key>");
        AuthenticationResult token = authContext.AcquireToken(
            "https://management.azure.com/"/* the Azure Resource Management endpoint */,
            cc);
        return token;
    }
    
  3. 在这一点上,如果你试图运行应用程序,你会得到一个认证错误,看起来像这样:

Hyak.Common。clouddexception AuthorizationFailed:对象id为"xxxxxxxx - xxxx - xxxx - xxxx - xxxx - xxxxxxxxxxxx"的客户端没有执行操作"Microsoft"的授权。Sql/servers/write' over scope '/subscriptions/xxxxxxxx - xxxx - xxxx - xxxx - xxxx - xxxxxxxxxxxx/resourceGroups/{myResourceGroupName}/providers/Microsoft.Sql/servers/{newServerName}'.

这是因为您需要为应用程序/服务主体添加一个RBAC角色。从错误消息中,你需要复制"对象ID",这是将用于角色分配的AD对象ID。
  • 获取Windows Azure的最新powershell cmdlet: http://azure.microsoft.com/en-us/downloads/
  • powershell安装完成后执行如下命令:

    Switch-AzureMode AzureResourceManager #_this will be deprecated soon_
    Add-AzureAccount # you will be prompted for your credentials
    New-AzureRoleAssignment -ObjectId <your_object_id> -RoleDefinitionName <role_name> 
    # You can use "Contributor" to give the web app access to basically everything.
    # You can look in portal.azure.com for the roles that are available
    
  • 当上面运行时,您应该看到输出,对象id被添加到角色中。
  • 你的Web-App现在应该可以工作了!

    额外阅读:

    服务主体/应用登录:http://azure.microsoft.com/en-us/documentation/articles/resource-group-authenticate-service-principal/

    For on-behalf-of login: https://msdn.microsoft.com/en-us/library/azure/dn790557.aspx

    我编写了一个控制台应用程序来测试各种场景。虽然我需要尝试更多的情况,但目前我实现的只有两个成功:

    • NativeClientApp with ClientId_RedirectUri_PromptBehavior
    • NativeClientApp with ClientId_RedirectUri_PromptBehavior_UserIdentifier

    如果您感兴趣,源代码在下面。另外,我现在只测试资源组的创建,因为这比创建Sql Server实例要少得多。两者似乎都产生了同样的错误。

    初步结果

    结果输出显示案例、结果和继续的提示。

    WebApp with ClientCertificate
    The method or operation is not implemented.
    Press any key to continue.
    WebApp with ClientAssertion
    The method or operation is not implemented.
    Press any key to continue.
    WebApp with ClientCredential
    AuthorizationFailed: The client '7a31a564-20ba-4ac1-a2ee-4f5e35a70dcc' with object id '7a31a564-20ba-4ac1-a2ee-4f5e35a70dcc' does not have authorization to perform action 'Microsoft.Resources/subscriptions/resourcegroups/write' ov
    er scope '/subscriptions/14929cfc-3501-48cf-a5c9-b24a7daaf694/resourcegroups/MY_RESOURCE_GROUP_NAME635776010237527878'.
    Press any key to continue.
    WebApp with ClientId_UserAssertion
    The method or operation is not implemented.
    Press any key to continue.
    WebApp with ClientCredential_UserAssertion
    AADSTS50027: Invalid JWT token. AADSTS50027: Invalid JWT token. Token format not valid.
    Trace ID: a64f0683-23ae-4461-8546-55293f7ff1d3
    Correlation ID: 62cc80e9-f013-4a74-8031-3294e69d4478
    Timestamp: 2015-09-12 03:43:57Z
    Press any key to continue.
    WebApp with ClientAssertion_UserAssertion
    The method or operation is not implemented.
    Press any key to continue.
    WebApp with ClientCertificate_UserAssertion
    The method or operation is not implemented.
    Press any key to continue.
    WebApp with ClientId_RedirectUri
    The method or operation is not implemented.
    Press any key to continue.
    WebApp with ClientId_UserCredential
    missing_federation_metadata_url: Federation Metadata Url is missing for federated user. This user type is unsupported.
    Press any key to continue.
    WebApp with ClientId_RedirectUri_PromptBehavior
    AADSTS90014: The request body must contain the following parameter: 'client_secret or client_assertion'.
    Trace ID: 06fde160-bd2b-4f16-b49e-0f0ff8e17f48
    Correlation ID: baabb83f-cebb-48ba-b2be-1efb53ec3121
    Timestamp: 2015-09-12 03:44:21Z
    Press any key to continue.
    WebApp with ClientId_RedirectUri_PromptBehavior_UserIdentifier
    AADSTS90014: The request body must contain the following parameter: 'client_secret or client_assertion'.
    Trace ID: 6110ef02-a6b0-4e41-b0e5-db97b13c66ce
    Correlation ID: e6f6526a-8395-480a-8ac7-b75903b324d9
    Timestamp: 2015-09-12 03:44:30Z
    Press any key to continue.
    WebApp with ClientId_RedirectUri_PromptBehavior_UserIdentifier_ExtraQueryParams
    The method or operation is not implemented.
    Press any key to continue.
    NativeClientApp with ClientCertificate
    The method or operation is not implemented.
    Press any key to continue.
    NativeClientApp with ClientAssertion
    The method or operation is not implemented.
    Press any key to continue.
    NativeClientApp with ClientCredential
    Value cannot be null.
    Parameter name: clientSecret
    Press any key to continue.
    NativeClientApp with ClientId_UserAssertion
    The method or operation is not implemented.
    Press any key to continue.
    NativeClientApp with ClientCredential_UserAssertion
    Value cannot be null.
    Parameter name: clientSecret
    Press any key to continue.
    NativeClientApp with ClientAssertion_UserAssertion
    The method or operation is not implemented.
    Press any key to continue.
    NativeClientApp with ClientCertificate_UserAssertion
    The method or operation is not implemented.
    Press any key to continue.
    NativeClientApp with ClientId_RedirectUri
    The method or operation is not implemented.
    Press any key to continue.
    NativeClientApp with ClientId_UserCredential
    missing_federation_metadata_url: Federation Metadata Url is missing for federated user. This user type is unsupported.
    Press any key to continue.
    NativeClientApp with ClientId_RedirectUri_PromptBehavior
    Success! Created MY_RESOURCE_GROUP_NAME635776011040240760
    Press any key to continue.
    NativeClientApp with ClientId_RedirectUri_PromptBehavior_UserIdentifier
    Success! Created MY_RESOURCE_GROUP_NAME635776011079693849
    Press any key to continue.
    NativeClientApp with ClientId_RedirectUri_PromptBehavior_UserIdentifier_ExtraQueryParams
    The method or operation is not implemented.
    Press any key to continue.
    Testing complete.:)
    

    Program.cs

    using System;
    using Microsoft.Azure;
    using Microsoft.Azure.Management.Resources;
    using Microsoft.Azure.Management.Resources.Models;
    using Microsoft.Azure.Management.Sql;
    using Microsoft.Azure.Management.Sql.Models;
    using Microsoft.IdentityModel.Clients.ActiveDirectory;
    namespace CreateNewAzureSQLServer
    {
        class Program
        {
            private static string
                domain = "MY_DOMAIN.onmicrosoft.com",
                resource = "https://management.azure.com/",
                subscriptionId = "xxxxx-xxxxx-xxxxx-xxxxx",
                // web
                clientId_web = "xxxxx-xxxxx-xxxxx-xxxxx",
                clientSecret_web = "xxxxx=",
                redirectUri_web = "http://myWebApp",
                // native
                clientId_native = "xxxxx-xxxxx-xxxxx-xxxxx",
                clientSecret_native = string.Empty,
                redirectUri_native = "http://myNativeClientApp",
                // adminstrator
                userName = "MY_USERNAME",
                userPassword = "MY_PASSWORD",
                // create
                adminAccountName = "MY_ADMIN_ACCOUNT_NAME",
                adminAccountPwd = "MY_ACCOUNT_ADMIN_PWD",
                resourceGroupName = "MY_RESOURCE_GROUP_NAME",
                serverName = "MY_SERVER_NAME",
                location = "West US";
            private static AuthenticationResult GetAccessToken(
                string clientId, string redirectUri, string clientSecret, AuthType type)
            {
                var authority = "https://login.windows.net/" + domain;
                var authContext = new AuthenticationContext(authority);
                authContext.TokenCache.Clear();
                AuthenticationResult token = null;
                switch (type)
                {
                    case AuthType.ClientCertificate:
                        throw new NotImplementedException();
                        break;
                    case AuthType.ClientAssertion:
                        throw new NotImplementedException();
                        break;
                    case AuthType.ClientCredential:                       
                        token = authContext.AcquireToken(resource,
                            new ClientCredential(clientId, clientSecret));
                        break;
                    case AuthType.ClientId_UserAssertion:
                        throw new NotImplementedException();
                        break;
                    case AuthType.ClientCredential_UserAssertion:
                        token = authContext.AcquireToken(resource,
                            new ClientCredential(clientId, clientSecret),
                            new UserAssertion(userPassword, "username", userName));
                        break;
                    case AuthType.ClientAssertion_UserAssertion:
                        throw new NotImplementedException();
                        break;
                    case AuthType.ClientCertificate_UserAssertion:
                        throw new NotImplementedException();
                        break;
                    case AuthType.ClientId_RedirectUri:
                        throw new NotImplementedException();
                        break;
                    case AuthType.ClientId_UserCredential:
                        token = authContext.AcquireToken(resource,
                            clientId,
                            new UserCredential(userName, userPassword));
                        break;
                    case AuthType.ClientId_RedirectUri_PromptBehavior:
                        token = authContext.AcquireToken(resource,
                            clientId,
                            new Uri(redirectUri),
                            PromptBehavior.Auto);
                        break;
                    case AuthType.ClientId_RedirectUri_PromptBehavior_UserIdentifier:                        
                        var cred = new UserCredential(userName);
                        token = authContext.AcquireToken(resource,
                            clientId,
                            new Uri(redirectUri),
                            PromptBehavior.Auto,
                            new UserIdentifier(cred.UserName, UserIdentifierType.RequiredDisplayableId));
                        break;
                    case AuthType.ClientId_RedirectUri_PromptBehavior_UserIdentifier_ExtraQueryParams:
                        throw new NotImplementedException();
                        break;
                    default:
                        break;
                };
                return token;
            }
            static void CreateSqlServer(TokenCloudCredentials creds)
            {
                var client = new SqlManagementClient(creds);
                var someProperties = new ServerCreateOrUpdateProperties
                {
                    AdministratorLogin = adminAccountName,
                    AdministratorLoginPassword = adminAccountPwd,
                    Version = "12"
                };
                var parameters = new ServerCreateOrUpdateParameters(someProperties, location);
                ServerGetResponse aCreateResponse
                    = client.Servers.CreateOrUpdate(resourceGroupName, serverName, parameters);
            }
            static string CreateResourceGroup(TokenCloudCredentials creds)
            {
                var uniqueResourceGroupName = resourceGroupName + DateTime.Now.Ticks.ToString();
                var resourceClient = new ResourceManagementClient(creds);
                var resourceGroupParameters = new ResourceGroup()
                {
                    Location = location
                };
                var resourceGroupResult = resourceClient
                    .ResourceGroups
                    .CreateOrUpdate(uniqueResourceGroupName, resourceGroupParameters);
                return uniqueResourceGroupName;
            }
            static void Main(string[] args)
            {
                foreach (AppType appType in Enum.GetValues(typeof(AppType)))
                {
                    var clientId = appType == AppType.WebApp ? clientId_web : clientId_native;
                    var clientSecret = appType == AppType.WebApp ? clientSecret_web : clientSecret_native;
                    var redirectUri = appType == AppType.WebApp ? redirectUri_web : redirectUri_native;
                    foreach (AuthType authType in Enum.GetValues(typeof(AuthType)))
                    {
                        try
                        {
                            Console.WriteLine(appType.ToString() + " with " + authType.ToString());
                            var token = GetAccessToken(clientId, redirectUri, clientSecret, authType);
                            var creds = new TokenCloudCredentials(subscriptionId, token.AccessToken);
                            var resourceGroupName = CreateResourceGroup(creds);
                            Console.WriteLine("Success! Created " + resourceGroupName);
                        }
                        catch (Exception ex)
                        {
                            Console.WriteLine(ex.Message);
                        }
                        Console.WriteLine("Press any key to continue.");
                        Console.ReadLine();
                    }
                }
                Console.WriteLine("Testing complete.:)");
                Console.ReadLine();
                //CreateSqlServer(creds);
            }
            enum AppType
            {
                WebApp,
                NativeClientApp
            }
            enum AuthType
            {
                ClientCertificate,
                ClientAssertion,
                ClientCredential,
                ClientId_UserAssertion,
                ClientCredential_UserAssertion,
                ClientAssertion_UserAssertion,
                ClientCertificate_UserAssertion,
                ClientId_RedirectUri,
                ClientId_UserCredential,
                ClientId_RedirectUri_PromptBehavior,
                ClientId_RedirectUri_PromptBehavior_UserIdentifier,
                ClientId_RedirectUri_PromptBehavior_UserIdentifier_ExtraQueryParams
            }
        }
    }
    

    packages.config

    <?xml version="1.0" encoding="utf-8"?>
    <packages>
      <package id="Hyak.Common" version="1.0.2" targetFramework="net452" />
      <package id="Microsoft.Azure.Common" version="2.1.0" targetFramework="net452" />
      <package id="Microsoft.Azure.Common.Authentication" version="1.1.5-preview" targetFramework="net452" />
      <package id="Microsoft.Azure.Common.Dependencies" version="1.0.0" targetFramework="net452" />
      <package id="Microsoft.Azure.Management.Resources" version="2.18.7-preview" targetFramework="net452" />
      <package id="Microsoft.Azure.Management.Sql" version="0.38.0-prerelease" targetFramework="net452" />
      <package id="Microsoft.Bcl" version="1.1.9" targetFramework="net452" />
      <package id="Microsoft.Bcl.Async" version="1.0.168" targetFramework="net452" />
      <package id="Microsoft.Bcl.Build" version="1.0.14" targetFramework="net452" />
      <package id="Microsoft.IdentityModel.Clients.ActiveDirectory" version="2.18.206251556" targetFramework="net452" />
      <package id="Microsoft.Net.Http" version="2.2.22" targetFramework="net452" />
      <package id="Microsoft.Rest.ClientRuntime" version="1.2.0" targetFramework="net452" />
      <package id="Microsoft.Rest.ClientRuntime.Azure.Authentication" version="0.9.3" targetFramework="net452" />
      <package id="Newtonsoft.Json" version="6.0.4" targetFramework="net452" />
    </packages>