Как перечислить виртуальные машины Classic в Azure - PullRequest
0 голосов
/ 19 октября 2018

Я хотел бы программно перечислить и контролировать классические (старые) виртуальные машины в Azure.Для управляемых это не проблема, есть библиотеки и остальные API работают, но как только я вызываю старый API для перечисления классики, я получаю 403 (Запрещено).

Является ликод хорошо?Нужно ли управлять учетными данными для старого API в другом месте?

Мой код находится здесь:

static void Main(string[] args)
{
    string apiNew = "https://management.azure.com/subscriptions/xxxxxxxxxxxxxxxxxxxxxxxx/providers/Microsoft.Compute/virtualMachines?api-version=2018-06-01";
    string apiOld = "https://management.core.windows.net/xxxxxxxxxxxxxxxxxxxxxxxx/services/vmimages"

    AzureRestClient client = new AzureRestClient(credentials.TenantId, credentials.ClientId, credentials.ClientSecret);

    //OK - I can list the managed VMs.         
    string resultNew = client.GetRequestAsync(apiNew).Result;

    // 403 forbidden
    string resultOld = client.GetRequestAsync(apiOld).Result;        
}

public class AzureRestClient : IDisposable
{
    private readonly HttpClient _client;

    public AzureRestClient(string tenantName, string clientId, string clientSecret)
    {
        _client = CreateClient(tenantName, clientId, clientSecret).Result;
    }

    private async Task<string> GetAccessToken(string tenantName, string clientId, string clientSecret)
    {
        string authString = "https://login.microsoftonline.com/" + tenantName;
        string resourceUrl = "https://management.core.windows.net/";

        var authenticationContext = new AuthenticationContext(authString, false);
        var clientCred = new ClientCredential(clientId, clientSecret);
        var authenticationResult = await authenticationContext.AcquireTokenAsync(resourceUrl, clientCred);
        var token = authenticationResult.AccessToken;

        return token;
    }

    async Task<HttpClient> CreateClient(string tenantName, string clientId, string clientSecret)
    {
        string token = await GetAccessToken(tenantName, clientId, clientSecret);
        HttpClient client = new HttpClient();
        client.DefaultRequestHeaders.Accept.Clear();
        client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token);        
        return client;
    }

    public async Task<string> GetRequestAsync(string url)
    {           
        return await _client.GetStringAsync(url);            
    }
}

ОБНОВЛЕНИЕ 1:

Подробности ответа:

HTTP/1.1 403 Forbidden
Content-Length: 288
Content-Type: application/xml; charset=utf-8
Server: Microsoft-HTTPAPI/2.0
Date: Mon, 22 Oct 2018 11:03:40 GMT

HTTP/1.1 403 Forbidden
Content-Length: 288
Content-Type: application/xml; charset=utf-8
Server: Microsoft-HTTPAPI/2.0
Date: Mon, 22 Oct 2018 11:03:40 GMT

<Error xmlns="http://schemas.microsoft.com/windowsazure" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
    <Code>ForbiddenError</Code>
    <Message>The server failed to authenticate the request.
      Verify that the certificate is valid and is associated with this subscription.</Message>
</Error>

Обновление 2:

Я обнаружил, что тот же API-интерфейс используется командой powershell Get-AzureVMImage, и он работает из powershell.Powershell сначала попросит меня войти в Azure с помощью интерактивных окон входа в систему по электронной почте и паролю, а запрос будет использовать заголовок Bearer для аутентификации, как мой код.

Если я получаю токен доступа (заголовок Bearer) из сообщения, созданного Powershell, Я могу успешно связаться с этим API.

Обновление 3: решено, ответ ниже.

Ответы [ 5 ]

0 голосов
/ 28 октября 2018

1.Причина вызова 403 при вызове List VM Images API

Это потому, что ваше зарегистрированное приложение Azure AD не использует делегированные разрешения «API управления службами Windows Azure» правильно.Я говорю это потому, что вижу, что ваш код получает токен напрямую, используя идентификатор приложения (ClientCredential), а не как пользователь.

Пожалуйста, смотрите скриншоты ниже.Очевидно, что Window Azure Service Management API не предоставляет никаких разрешений для приложений, единственное, что можно использовать, - это делегированное разрешение.Если вы хотите больше узнать о разнице между двумя типами разрешений, прочитайте Разрешения в Azure AD .Короче говоря, при использовании делегированных разрешений приложению делегируются полномочия выступать в качестве зарегистрированного пользователя при совершении вызовов API.Поэтому должен быть зарегистрированный пользователь.

Я смог воспроизвести ошибку 403, используя ваш код, а затем заставить ее работать и вернуть список классических ВМ с некоторыми изменениями.Далее я объясню необходимые изменения.

Перейдите в Azure AD> Регистрация приложений> Ваше приложение> Настройки> Необходимые разрешения:

enter image description here

enter image description here

2.Изменения, необходимые для его работы

Изменение будет заключаться в том, чтобы получить токен как зарегистрированный пользователь, а не напрямую, используя clientId и секретный ключ приложения.Поскольку ваше приложение является консольным приложением, имеет смысл сделать что-то вроде этого, которое предложит пользователю ввести учетные данные:

var authenticationResult = await authenticationContext.AcquireTokenAsync(resourceUrl, clientId, new Uri(redirectUri), new PlatformParameters(PromptBehavior.Auto));

Кроме того, поскольку ваше приложение является консольным приложением, было бы лучшезарегистрировать его как «родное» приложение вместо веб-приложения, как у вас есть сейчас.Я говорю это потому, что консольные приложения или настольные клиентские приложения, которые могут работать в пользовательских системах, не защищены от обработки секретов приложений, поэтому вам не следует регистрировать их как «Веб-приложение / API» и не использовать в них никаких секретов, так как это представляет угрозу безопасности.

В общем, 2 изменения, и вам нужно идти.Как я уже говорил ранее, я попробовал это и вижу, что код работает нормально и получает список классических виртуальных машин.

a.Зарегистрируйте свое приложение в Azure AD как собственное приложение (т. Е. Тип приложения должен быть собственным, а не веб-приложением / API), затем в необходимых разрешениях добавьте «Window Azure Service Management API» и проверьте делегированные разрешения в соответствии с предыдущими снимками экрана в пункте 1.

enter image description here

b.Измените способ получения токена, чтобы делегированные разрешения могли использоваться в соответствии с зарегистрированным пользователем.Конечно, вошедший в систему пользователь должен иметь разрешения для виртуальной машины, которую вы пытаетесь перечислить, или, если у вас есть несколько пользователей, список будет отражать те виртуальные машины, к которым в данный момент вошел пользователь, имеющий доступ.

Вот всерабочий код после того, как я его изменил.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Microsoft.IdentityModel.Clients.ActiveDirectory;
using System.Net.Http;
using System.Net.Http.Headers;

namespace ListVMsConsoleApp
{
    class Program
    {
        static void Main(string[] args)
        {
            string tenantId = "xxxxxx";
            string clientId = "xxxxxx";
            string redirectUri = "https://ListClassicVMsApp";

            string apiNew = "https://management.azure.com/subscriptions/xxxxxxxx/providers/Microsoft.Compute/virtualMachines?api-version=2018-06-01";
            string apiOld = "https://management.core.windows.net/xxxxxxxx/services/vmimages";

            AzureRestClient client = new AzureRestClient(tenantId, clientId, redirectUri);

            //OK - I can list the managed VMs.         
            //string resultNew = client.GetRequestAsync(apiNew).Result;

            // 403 forbidden - should work now
            string resultOld = client.GetRequestAsync(apiOld).Result;
        }

    }

    public class AzureRestClient
    {
        private readonly HttpClient _client;

        public AzureRestClient(string tenantName, string clientId, string redirectUri)
        {
            _client = CreateClient(tenantName, clientId, redirectUri).Result;
        }

        private async Task<string> GetAccessToken(string tenantName, string clientId, string redirectUri)
        {
            string authString = "https://login.microsoftonline.com/" + tenantName;
            string resourceUrl = "https://management.core.windows.net/";

            var authenticationContext = new AuthenticationContext(authString, false);
            var authenticationResult = await authenticationContext.AcquireTokenAsync(resourceUrl, clientId, new Uri(redirectUri), new PlatformParameters(PromptBehavior.Auto));

            return authenticationResult.AccessToken;
        }

        async Task<HttpClient> CreateClient(string tenantName, string clientId, string redirectUri)
        {
            string token = await GetAccessToken(tenantName, clientId, redirectUri);
            HttpClient client = new HttpClient();
            client.DefaultRequestHeaders.Accept.Clear();
            client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token);
            client.DefaultRequestHeaders.Add("x-ms-version", "2014-02-01");
            return client;
        }

        public async Task<string> GetRequestAsync(string url)
        {
            return await _client.GetStringAsync(url);
        }
    }
}
0 голосов
/ 24 октября 2018

Finnaly Я получил его на работу:

Сначала откройте Powershell:

Get-AzurePublishSettingsFile

и сохраните этот файл.

, затем введите Powershell

Import-AzurePublishSettingsFile [mypublishsettingsfile]

Открыть хранилище сертификатов и найти импортированный сертификат.И используйте этот сертификат одновременно с учетными данными в HttpClient.

0 голосов
/ 22 октября 2018

Согласно связанной документации у вас отсутствует требуемый заголовок запроса при запросе классического API REST

x-ms-version - Обязательно .Указывает версию операции, которая будет использоваться для этого запроса.Этот заголовок должен быть установлен на 2014-02-01 или выше.

Ссылка Список образов виртуальных машин: заголовки запросов

Чтобы разрешить включение заголовка, создайте перегрузку для запросов GET в AzureRestClient

public async Task<string> GetRequestAsync(string url, Dictionary<string, string> headers) {
    var request = new HttpRequestMessage(HttpMethod.Get, url);
    if (headers != null)
        foreach (var header in headers) {
            request.Headers.TryAddWithoutValidation(header.Key, header.Value);
        }
    var response = await _client.SendAsync(request);
    return await response.Content.ReadAsStringAsync();
}

и включите необходимый заголовок при вызове apiOld

var headers = new Dictionary<string, string>();
headers["x-ms-version"] = "2014-02-01";

string resultOld = client.GetRequestAsync(apiOld, headers).GetAwaiter().GetResult();
0 голосов
/ 24 октября 2018

Я отлично воспроизвел вашу проблему.К сожалению, я не получил рабочий исходный код со старым API, отвечающим вашим потребностям.

Хотя я нашел поставщика Microsoft.ClassicCompute вместо обычного используемого Microsoft.Compute, но все равно не смогиметь рабочий тест.

Я почти уверен, что вам больше не нужно « вручную » использовать старый устаревший API и использовать современные пакеты Microsoft, позволяющие управлять классическими и «нормальными» элементамикак виртуальные машины или учетные записи хранения.

Пакет ключей: Microsoft.Azure.Management.Compute.Fluent

Документацию можно найти здесь: https://docs.microsoft.com/en-us/dotnet/api/microsoft.azure.management.compute.fluent?view=azure-dotnet

Дайте мне знать, если вам все еще нужна помощь.

0 голосов
/ 19 октября 2018

На основании моего теста вам необходимо получить токен доступа в интерактивном режиме.

...