[英]How to list Virtual Machines Classic in Azure
我想以編程方式列出和控制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:
我發現powershell命令Get-AzureVMImage使用了相同的API,它正在使用PowerShell。 Powershell首先要求我通過電子郵件和密碼使用交互式登錄窗口登錄Azure,並且請求使用Bearer標頭來驗證我的代碼。
如果我從Powershell創建的通信中嗅探訪問令牌(Bearer標頭),我可以成功地與該API通信。
更新3:已解決,回答如下。
根據鏈接的文檔,您在請求經典REST API時似乎缺少必需的請求標頭
x-ms-version - 必需 。 指定要用於此請求的操作的版本。 此標頭應設置為2014-02-01或更高版本。
要允許包含標頭,請在AzureRestClient
為GET請求創建重載
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();
Finnaly我得到了它的工作:
首次開放的Powershell:
Get-AzurePublishSettingsFile
並保存該文件。
然后輸入Powershell
Import-AzurePublishSettingsFile [mypublishsettingsfile]
打開證書庫並查找導入的證書。 並使用該證書同時使用HttpClient中的憑據。
這是因為您的Azure AD注冊應用程序未正確使用“Windows Azure Service Management API”委派權限。 我這樣說是因為我看到你的代碼是使用應用程序標識(ClientCredential)直接獲取令牌而不是用戶。
請參閱下面的屏幕截圖。 Window Azure Service Management API顯然不提供任何應用程序權限,只能使用的是委派權限。 如果要了解有關這兩種權限之間差異的更多信息,請閱讀Azure AD中的權限 。 簡而言之,在使用委派權限時,應用程序被授予在調用API時充當已登錄用戶的權限。 所以必須有一個登錄用戶。
我能夠使用您的代碼重現403錯誤,然后能夠使其工作並返回經典VM的列表並進行一些更改。 我接下來將解釋所需的更改。
轉到Azure AD>應用程序注冊>您的應用程序>設置>所需權限:
更改將是以簽名用戶身份獲取令牌而不是直接使用應用程序的clientId和secret。 由於您的應用程序是一個控制台應用程序,所以做這樣的事情是有意義的,這將提示用戶輸入憑據:
var authenticationResult = await authenticationContext.AcquireTokenAsync(resourceUrl, clientId, new Uri(redirectUri), new PlatformParameters(PromptBehavior.Auto));
此外,由於您的應用程序是一個控制台應用程序,最好將其注冊為“本機”應用程序,而不是像您現在所擁有的那樣使用Web應用程序。 我這樣說是因為可以在用戶系統上運行的控制台應用程序或基於桌面客戶端的應用程序處理應用程序機密並不安全,因此您不應將它們注冊為“Web應用程序/ API”,也不要在其中使用任何機密,因為它存在安全風險。
總的來說,2個變化,你應該好好去。 正如我之前所說,我已經嘗試過這些,並且可以看到代碼正常運行並獲得經典虛擬機列表。
一個。 在Azure AD中將您的應用程序注冊為本機應用程序(即應用程序類型應該是本機應用程序,而不是Web應用程序/ API),然后在所需權限中添加“Window Azure服務管理API”並檢查委派權限,如第1點中的早期屏幕截圖所示
灣 更改獲取令牌的方式,以便根據已登錄的用戶使用委派的權限。 當然,登錄用戶應該擁有您嘗試列出的虛擬機的權限,或者如果您有多個用戶,該列表將反映當前用戶可以訪問的虛擬機。
修改后,這是整個工作代碼。
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);
}
}
}
根據我的測試,您需要以交互方式獲取訪問令牌。
我完全重現了你的問題。 不幸的是,我沒有得到Old API的工作源代碼,可以滿足您的需求。
雖然我找到了一個Microsoft.ClassicCompute提供程序,而不是通常使用的Microsoft.Compute提供程序,但仍然無法進行工作測試。
我很確定你不應該“ 手動 ”使用舊的過時API,並且應該使用現代Microsoft軟件包來管理Classic和“Normal”元素,如虛擬機或存儲帳戶。
關鍵包是Microsoft.Azure.Management.Compute.Fluent
您可以在此處找到該文檔: https : //docs.microsoft.com/en-us/dotnet/api/microsoft.azure.management.compute.fluent?view = azure-dotnet
如果您仍需要幫助,請告訴我。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.