Microsoft MVP성태의 닷넷 이야기
.NET Framework: 736. C# - API를 사용해 Azure에 접근하는 방법 [링크 복사], [링크+제목 복사],
조회: 21979
글쓴 사람
정성태 (techsharer at outlook.com)
홈페이지
첨부 파일
(연관된 글이 2개 있습니다.)
(시리즈 글이 6개 있습니다.)
.NET Framework: 736. C# - API를 사용해 Azure에 접근하는 방법
; https://www.sysnet.pe.kr/2/0/11480

개발 환경 구성: 374. Azure - Runbook 기능 소개
; https://www.sysnet.pe.kr/2/0/11511

.NET Framework: 745. Azure runbook을 PowerShell 또는 C# 코드로 실행하는 방법
; https://www.sysnet.pe.kr/2/0/11516

.NET Framework: 746. Azure runbook 예제 - 6시간 동안 수행 중인 VM을 중지
; https://www.sysnet.pe.kr/2/0/11517

.NET Framework: 800. C# - Azure REST API 사용을 위한 인증 획득
; https://www.sysnet.pe.kr/2/0/11758

개발 환경 구성: 418. Azure - Runbook 내에서 또 다른 Runbook 스크립트를 실행
; https://www.sysnet.pe.kr/2/0/11760




C# - API를 사용해 Azure에 접근하는 방법

Azure를 Portal 웹 사이트의 UI가 아닌, 프로그래밍으로 다루는 것도 가능한데 다음의 문서에 그 방법이 나옵니다.

Azure Management Libraries for .NET generally available now
 - Working with the Azure Management Libraries for .NET
; https://azure.microsoft.com/en-us/blog/azure-management-libraries-for-net-generally-available-now/

RESTful API 형식도 제공하지만 C# 개발자라면 전용 .NET 라이브러리를 사용하면 좀 더 쉽게 개발할 수 있습니다. 물론, 그 라이브러리는 NuGet을 통해 Microsoft.Azure.Management.Fluent 어셈블리로 제공됩니다.

Microsoft.Azure.Management.Fluent 
; https://www.nuget.org/packages/Microsoft.Azure.Management.Fluent/

이 라이브러리를 사용해 azure 정보를 구하려면 당연히 인증이 선행되어야 할 텐데, 따라서 지난번 글에서 다룬 방법으로 미리 계정을 생성해 둬야 합니다.

Azure - PowerShell로 Access control(IAM)에 새로운 계정 만드는 방법
; https://www.sysnet.pe.kr/2/0/11479

그런 다음, 위의 계정 정보와 연관된 인증 정보를 제공하는 방법 중의 하나로 파일을 사용할 수 있고, 대략 다음과 같은 정보를 담고 있어야 합니다.

# sample management library properties file
subscription=
client=
key=
tenant=
managementURI=https://management.core.windows.net/
baseURL=https://management.azure.com/
authURL=https://login.windows.net/
graphURL=https://graph.windows.net/

이 중에서 빈 값들을 하나씩 채워볼 텐데요, 우선 subscription, tenant 먼저 구해보겠습니다. 이를 위해 "Microsoft Azure Command Prompt"를 열어 Powershell을 실행한 후 "Login-AzureRmAccount" 명령어를 실행하면 다음과 같이 로그인 대화창이 떠 계정 정보를 입력받게 됩니다.

azure_rest_api_1.png

(참고로, Visual Studio에서 "Azure development" 구성 요소를 선택했으면 기본적으로 Microsoft Azure Command Prompt가 제공되는 듯합니다.)

정상적으로 입력했으면 아래와 같이 Account, SubscriptionName, SubscriptionId, TenantId, Environment 필드로 이뤄진 출력이 보입니다.

Windows Azure SDK Shell

C:\Program Files\Microsoft SDKs\Azure\.NET SDK\v2.9>powershell
Windows PowerShell
Copyright (C) Microsoft Corporation. All rights reserved.

PS> Login-AzureRmAccount


Account          : testuser@mydomain.net
SubscriptionName : Windows Azure
SubscriptionId   : 461e7265-0395-49b1-94b3-4c891430a893
TenantId         : 5A7266AE-1C5D-4386-9D0D-2131CC3B2A2C
Environment      : AzureCloud


(참고로, SubscriptionId의 경우 Azure Portal의 구독자를, TenantId는 Azure Active Directory를 구분합니다.)

위의 정보에서 SubscriptionId, TenantId 값을 이용해 다음과 같이 인증 파일의 내용을 채우면 됩니다.

# sample management library properties file
subscription=461e7265-0395-49b1-94b3-4c891430a893
client=
key=
tenant=5A7266AE-1C5D-4386-9D0D-2131CC3B2A2C
managementURI=https://management.core.windows.net/
baseURL=https://management.azure.com/
authURL=https://login.windows.net/
graphURL=https://graph.windows.net/

남은 것은, client와 key 값인데요. 이는 이전 글에서 New-AzureRmADServicePrincipal을 실습하면서 얻은 결과를 사용하면 됩니다.

PS> $sp = New-AzureRmADServicePrincipal -DisplayName "AzureDotNetTest" -Password [...encrypted_password...]

PS> New-AzureRmRoleAssignment -ServicePrincipalName $sp.ApplicationId -RoleDefinitionName Contributor

RoleAssignmentId   : /subscriptions/461e7265-0395-49b1-94b3-4c891430a893/providers/Microsoft.Authorization/roleAssignm
                     ents/e286d5b1-9fae-4241-a271-79f9a2c6dbcd
Scope              : /subscriptions/461e7265-0395-49b1-94b3-4c891430a893
DisplayName        : AzureDotNetTest
SignInName         :
RoleDefinitionName : Contributor
RoleDefinitionId   : 741AEC3B-8A4E-408A-8809-1A21DF0F2812
ObjectId           : 663743FA-2EE9-4D45-B39E-37640E3A6DEF
ObjectType         : ServicePrincipal
CanDelegate        : False

PS> $sp | Select DisplayName, ApplicationId

DisplayName     ApplicationId
-----------     -------------
AzureDotNetTest C5129BAC-96F4-43FE-B5D7-DEE8CE7ED2A0

위의 결과로 구한 ApplicationId 역시 Azure Portal에서 저 계정 정보로 들어가서 구할 수 있습니다. 그래서 최종적으로 다음과 같이 채울 수 있습니다.

# sample management library properties file
subscription=461e7265-0395-49b1-94b3-4c891430a893
client=3FC47033-C110-4B8A-B364-1CFC42BA4D47
key=...encrypted string...
tenant=C5129BAC-96F4-43FE-B5D7-DEE8CE7ED2A0
managementURI=https://management.core.windows.net/
baseURL=https://management.azure.com/
authURL=https://login.windows.net/
graphURL=https://graph.windows.net/

문제는, 저기서 key 값입니다. 평문을 넣어도 C# 코드에서 인증 시 이런 오류가 발생합니다.

Microsoft.IdentityModel.Clients.ActiveDirectory.AdalServiceException
  HResult=0x80131500
  Message=AADSTS70002: Error validating credentials. AADSTS50012: Invalid client secret is provided.
Trace ID: 88898fd1-8cd6-4b79-845d-73e0ae350500
Correlation ID: 407bfcfb-cf1e-4ee9-b2ac-f9bc8c40ec11
Timestamp: 2018-04-05 04:42:12Z
  Source=Microsoft.IdentityModel.Clients.ActiveDirectory
  StackTrace:
   at Microsoft.IdentityModel.Clients.ActiveDirectory.HttpHelper.<SendPostRequestAndDeserializeJsonResponseAsync>d__0`1.MoveNext()
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) in f:\dd\ndp\clr\src\BCL\system\runtime\compilerservices\TaskAwaiter.cs:line 184
...[생략]...
   at Microsoft.Azure.Management.ResourceManager.Fluent.Core.Extensions.Synchronize[TResult](Func`1 function)
   at Microsoft.Azure.Management.ResourceManager.Fluent.Core.TopLevelModifiableResources`5.List()
   at ConsoleApp1.Program.Main(String[] args) in E:\cloud_drive\Dropbox\articles\azure_api\ConsoleApp1\ConsoleApp1\Program.cs:line 17

Inner Exception 1:
WebException: The remote server returned an error: (401) Unauthorized.

참고로 SecureString을 이렇게 변환할 수 있습니다.

PS> $secureString
System.Security.SecureString

PS> $StandardString = ConvertFrom-SecureString $secureString
PS> $StandardString
01000000d08c9ddf0115d1118c7a00c04fc297eb0100000024044cdd8d0ac54...[생략]...81162dfc600e837ac5f2a4b64fc77bcae9159e3c2baa98ee68d0698a8c263f1b9bf6c04de5a28269d8b5

하지만 저 값을 기록해도 역시 예외가 발생합니다. 혹시 저 key 값에 어떤 것을 넣는지 아시는 분 있으면 ^^ 덧글 부탁드립니다. (2018-04-18 업데이트: New-AzureRmADServicePrincipal로 생성한 계정의 clientSecret, key 값을 구하는 방법)




결국 저 방식으로는 해결을 못하고 다른 방법을 찾게 되었습니다. 다른 방법도 있습니다.

Authentication in Azure Management Libraries for .NET
; https://github.com/Azure/azure-libraries-for-net/blob/master/AUTH.md

Azure CLI 명령어를 이용하는 것인데, 이를 위해 az login으로 명령행에서 credential 문맥을 생성하고,

C:\Program Files (x86)\Microsoft Visual Studio\2017\Enterprise>az login
To sign in, use a web browser to open the page https://microsoft.com/devicelogin and enter the code H54CJFXHV to authenticate.
[
  {
    "cloudName": "AzureCloud",
    "id": "461e7265-0395-49b1-94b3-4c891430a893",
    "isDefault": true,
    "name": "Windows Azure",
    "state": "Enabled",
    "tenantId": "C5129BAC-96F4-43FE-B5D7-DEE8CE7ED2A0",
    "user": {
      "name": "testuser@service.net",
      "type": "user"
    }
  }
]

다음과 같이 Access Control(IAM) 계정을 Powershell과 유사하게 생성할 수 있습니다.

C:\Program Files (x86)\Microsoft Visual Studio\2017\Enterprise>az ad sp create-for-rbac --sdk-auth
Retrying role assignment creation: 1/36
{
  "clientId": "3FC47033-C110-4B8A-B364-1CFC42BA4D47",
  "clientSecret": "6EB901DB-DB6B-452C-9F0F-4468F9D1F616",
  "subscriptionId": "461e7265-0395-49b1-94b3-4c891430a893",
  "tenantId": "C5129BAC-96F4-43FE-B5D7-DEE8CE7ED2A0",
  "activeDirectoryEndpointUrl": "https://login.microsoftonline.com",
  "resourceManagerEndpointUrl": "https://management.azure.com/",
  "activeDirectoryGraphResourceId": "https://graph.windows.net/",
  "sqlManagementEndpointUrl": "https://management.core.windows.net:8443/",
  "galleryEndpointUrl": "https://gallery.azure.com/",
  "managementEndpointUrl": "https://management.core.windows.net/"
}

바로 저 출력 결과로 나오는, "{", "}" 내용을 파일로 저장하고,

{
  "clientId": "3FC47033-C110-4B8A-B364-1CFC42BA4D47",
  "clientSecret": "6EB901DB-DB6B-452C-9F0F-4468F9D1F616",
  "subscriptionId": "461e7265-0395-49b1-94b3-4c891430a893",
  "tenantId": "C5129BAC-96F4-43FE-B5D7-DEE8CE7ED2A0",
  "activeDirectoryEndpointUrl": "https://login.microsoftonline.com",
  "resourceManagerEndpointUrl": "https://management.azure.com/",
  "activeDirectoryGraphResourceId": "https://graph.windows.net/",
  "sqlManagementEndpointUrl": "https://management.core.windows.net:8443/",
  "galleryEndpointUrl": "https://gallery.azure.com/",
  "managementEndpointUrl": "https://management.core.windows.net/"
}

이를 C# 코드에서 사용하면 됩니다.




우여곡절 끝에 인증 파일을 만들었으면, 이제 다음과 같이 C# 코드에서 Azure 서비스로 인증이 가능합니다.

// credFile - 인증 정보를 담고 있는 파일 경로
IAzure azure = Azure.Authenticate(credFile).WithDefaultSubscription();

이후 ARM으로 관리되는 모든 자원을 접근할 수 있는데, 참고로 여기서 classic 유형의 자원들은 Microsoft.Azure.Management.Fluent에서 제외됩니다. (즉, 예전의 Cloud services - classic, Virtual machiens - classic 유형들의 자원은 접근할 수 없습니다.)

ARM 방식의 Azure 관리 모델은 "Resource Group"으로 대표하는데, 따라서 우선 첫 단계로 리소스 그룹 목록을 다음과 같이 구할 수 있습니다.

IAzure azure = Azure.Authenticate(credFile).WithDefaultSubscription();
var resGroups = azure.ResourceGroups.List();
foreach (var resGroup in resGroups)
{
}

이후 해당 리소스 그룹에 속한 모든 자원을 다음과 같이 열거할 수 있습니다.

using Microsoft.Azure.Management.Compute.Fluent;
using Microsoft.Azure.Management.Fluent;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ConsoleApp1
{
    class Program
    {
        static void Main(string[] args)
        {
            string credFile = @"D:\Settings\azure_sub1_prop.txt";

            IAzure azure = Azure.Authenticate(credFile).WithDefaultSubscription();

            var resGroups = azure.ResourceGroups.List();
            Console.WriteLine(resGroups.Count());

            foreach (var resGroup in resGroups)
            {
                Console.WriteLine(resGroup.Name);

                var vmList = azure.VirtualMachines.ListByResourceGroup(resGroup.Name);
                Console.WriteLine($"\t[VirtualMachines: {vmList.Count()}]");
                foreach (var vm in vmList)
                {
                    Console.WriteLine("\t\t" + vm.Name);
                }

                var deployments = azure.Deployments.ListByResourceGroup(resGroup.Name);
                Console.WriteLine($"\t[Deployments: {deployments.Count()}]");
                foreach (var deployment in deployments)
                {
                    Console.WriteLine("\t\t" + deployment.Name);
                }

                var containers = azure.ContainerGroups.ListByResourceGroup(resGroup.Name);
                Console.WriteLine($"\t[ContainerGroups: {containers.Count()}]");
                foreach (var container in containers)
                {
                    Console.WriteLine("\t\t" + container.Name);
                }

                var vmScaleSets = azure.VirtualMachineScaleSets.ListByResourceGroup(resGroup.Name);
                Console.WriteLine($"\t[VMScaleSets: {vmScaleSets.Count()}]");
                foreach (var vmScaleSet in vmScaleSets)
                {
                    Console.WriteLine("\t\t" + vmScaleSet.Name);
                }

                var webApps = azure.WebApps.ListByResourceGroup(resGroup.Name);
                Console.WriteLine($"\t[WebApps: {webApps.Count()}]");
                foreach (var webApp in webApps)
                {
                    Console.WriteLine("\t\t" + webApp.Name);
                }

                var containerServices = azure.ContainerServices.ListByResourceGroup(resGroup.Name);
                Console.WriteLine($"\t[ContainerServices: {containerServices.Count()}]");
                foreach (var containerService in containerServices)
                {
                    Console.WriteLine("\t\t" + containerService.Name);
                }

                var availabilitySets = azure.AvailabilitySets.ListByResourceGroup(resGroup.Name);
                Console.WriteLine($"\t[AvailabilitySets: {availabilitySets.Count()}]");
                foreach (var availabilitySet in availabilitySets)
                {
                    Console.WriteLine("\t\t" + availabilitySet.Name);
                }

                var disks = azure.Disks.ListByResourceGroup(resGroup.Name);
                Console.WriteLine($"\t[Disks: {disks.Count()}]");
                foreach (var disk in disks)
                {
                    Console.WriteLine("\t\t" + disk.Name);
                }

                var loadBalancers = azure.LoadBalancers.ListByResourceGroup(resGroup.Name);
                Console.WriteLine($"\t[LoadBalancers: {loadBalancers.Count()}]");
                foreach (var loadBalancer in loadBalancers)
                {
                    Console.WriteLine("\t\t" + loadBalancer.Name);
                }

                var managementLocks = azure.ManagementLocks.ListByResourceGroup(resGroup.Name);
                Console.WriteLine($"\t[ManagementLocks: {managementLocks.Count()}]");
                foreach (var managementLock in managementLocks)
                {
                    Console.WriteLine("\t\t" + managementLock.ToString());
                }

                var publicIPAddresses = azure.PublicIPAddresses.ListByResourceGroup(resGroup.Name);
                Console.WriteLine($"\t[PublicIPAddresses: {publicIPAddresses.Count()}]");
                foreach (var publicIPAddress in publicIPAddresses)
                {
                    Console.WriteLine("\t\t" + publicIPAddress.ToString());
                }

                var searchServices = azure.SearchServices.ListByResourceGroup(resGroup.Name);
                Console.WriteLine($"\t[SearchServices: {searchServices.Count()}]");
                foreach (var searchService in searchServices)
                {
                    Console.WriteLine("\t\t" + searchService.ToString());
                }

                var snapshots = azure.Snapshots.ListByResourceGroup(resGroup.Name);
                Console.WriteLine($"\t[Snapshots: {snapshots.Count()}]");
                foreach (var snapshot in snapshots)
                {
                    Console.WriteLine("\t\t" + snapshot.ToString());
                }

                var storageAccounts = azure.StorageAccounts.ListByResourceGroup(resGroup.Name);
                Console.WriteLine($"\t[StorageAccounts: {storageAccounts.Count()}]");
                foreach (var storageAccount in storageAccounts)
                {
                    Console.WriteLine("\t\t" + storageAccount.ToString());
                }

                var virtualMachineCustomImages = azure.VirtualMachineCustomImages.ListByResourceGroup(resGroup.Name);
                Console.WriteLine($"\t[VirtualMachineCustomImages: {virtualMachineCustomImages.Count()}]");
                foreach (var virtualMachineCustomImage in virtualMachineCustomImages)
                {
                    Console.WriteLine("\t\t" + virtualMachineCustomImage.Name);
                }
            }

            // Azure에서 제공되는 템플릿 목록
            //var computeSkus = azure.ComputeSkus.ListByResourceType(ComputeResourceType.VirtualMachines);
            //Console.WriteLine($"\t[ComputeSkus: {computeSkus.Count()}]");
            //foreach (var computeSku in computeSkus)
            //{
            //    Console.WriteLine("\t\t" + computeSku.Name);
            //}
        }
    }
}

뭐... 더 설명이 필요하진 않겠죠? ^^




서두에서 이야기한 데로 RESTful API 방식으로도 접근이 가능합니다. 위의 소스 코드를 실행 후 Azure.Authenticate 메서드 수행 시 Fiddler로 HTTP 요청을 가로채면 다음과 같은 결과를 얻을 수 있습니다.

[요청]

POST https://login.microsoftonline.com/358f4aef-d1c5-4103-8f67-255bcb9d7670/oauth2/token HTTP/1.1
Content-Type: application/x-www-form-urlencoded
client-request-id: c931089a-2038-4858-9ed9-6e900cd9a761
return-client-request-id: true
x-client-SKU: .NET
x-client-Ver: 2.28.3.860
x-client-CPU: x64
x-client-OS: Microsoft Windows NT 6.2.9200.0
Host: login.microsoftonline.com
Content-Length: 181
Expect: 100-continue
Connection: Keep-Alive

resource=https%3A%2F%2Fmanagement.core.windows.net%2F&client_id=5e7c8cb5-b3bc-4014-819b-855226fe7a16&client_secret=639f447b-d126-495d-ac50-ac32b5b22e3f&grant_type=client_credentials

[응답]

HTTP/1.1 200 OK
Cache-Control: no-cache, no-store
Pragma: no-cache
Content-Type: application/json; charset=utf-8
Expires: -1
Server: Microsoft-IIS/10.0
Strict-Transport-Security: max-age=31536000; includeSubDomains
X-Content-Type-Options: nosniff
client-request-id: c931089a-2038-4858-9ed9-6e900cd9a761
x-ms-request-id: f9201cf0-0e6d-42fd-8d7b-015c6e3a1200
x-ms-clitelem: 1,0,0,,
P3P: CP="DSP CUR OTPi IND OTRi ONL FIN"
Set-Cookie: esctx=AQABAAAAAAB,...[생략]...cpp02WwgAA; domain=.login.microsoftonline.com; path=/; secure; HttpOnly
Set-Cookie: x-ms-gateway-slice=018; path=/; secure; HttpOnly
Set-Cookie: stsservicecookie=ests; path=/; secure; HttpOnly
Date: Mon, 09 Apr 2018 02:59:55 GMT
Content-Length: 1376

{"token_type":"Bearer","expires_in":"3600","ext_expires_in":"262800","expires_on":"1523246396","not_before":"1523242496","resource":"https://management.core.windows.net/","access_token":"eyJ0eXAiOiJ...[생략]...kCD8Pg"}

이후 응답으로 받은 access_token을 RESTful API의 인증 헤더에 전달하는 것을 볼 수 있습니다.

[요청]

GET https://management.azure.com/subscriptions/461e7265-0395-49b1-94b3-4c891430a893/resourcegroups?api-version=2017-05-10 HTTP/1.1
x-ms-client-request-id: 9a7d1f87-5d3f-4881-b3d1-d34d801f4fbf
accept-language: en-US
Authorization: Bearer eyJ0eXAiOiJ...[생략]...kCD8Pg
User-Agent: FxVersion/4.7.2633.0 OSName/Windows10Pro OSVersion/6.3.16299 Microsoft.Azure.Management.ResourceManager.Fluent.ResourceManagementClient/1.8.0.0 MacAddressHash/43504081d3c9421b9ad902d9c6cb0361fbe4ad97cb7f434b8a5f61e981f91d9a
Host: management.azure.com

[응답]

HTTP/1.1 200 OK
Cache-Control: no-cache
Pragma: no-cache
Content-Type: application/json; charset=utf-8
Expires: -1
x-ms-ratelimit-remaining-subscription-reads: 14997
x-ms-request-id: 1e63417f-9ee9-44f0-a255-05b7518347eb
x-ms-correlation-request-id: 1e63417f-9ee9-44f0-a255-05b7518347eb
x-ms-routing-request-id: KOREACENTRAL:20180409T025956Z:1e63417f-9ee9-44f0-a255-05b7518347eb
Strict-Transport-Security: max-age=31536000; includeSubDomains
X-Content-Type-Options: nosniff
Date: Mon, 09 Apr 2018 02:59:55 GMT
Content-Length: 790

{"value":[{"id":"/subscriptions/...[생략]...":{"provisioningState":"Succeeded"}}]}

따라서, RESTful API를 이용하는 코드를 다음과 같이 작성할 수 있습니다.

private static async void UsingRESTFulAPI()
{
    string accessToken = await GetAccessToken();
    Console.WriteLine("AccessToken: " + accessToken);

    string resourceList = await GetResourceGroups(accessToken);
    Console.WriteLine(resourceList);
}

private static async Task<string> GetResourceGroups(string accessToken)
{
    HttpClient hc = new HttpClient();
    hc.DefaultRequestHeaders.Add("Authorization", "Bearer " + accessToken);

    string url = $"https://management.azure.com/subscriptions/{subscriptionId}/resourcegroups?api-version=2017-05-10";

    HttpResponseMessage hrm = await hc.GetAsync(url);
    string result = await hrm.Content.ReadAsStringAsync();

    return result;
}

private static async Task<string> GetAccessToken()
{
    HttpClient hc = new HttpClient();
    string url = "https://login.microsoftonline.com/358f4aef-d1c5-4103-8f67-255bcb9d7670/oauth2/token";

    Dictionary<string, string> dict = new Dictionary<string, string>();
    dict.Add("resource", resourceUrl);
    dict.Add("client_id", clientId);
    dict.Add("client_secret", clientSecret);
    dict.Add("grant_type", "client_credentials");

    FormUrlEncodedContent content = new FormUrlEncodedContent(dict);
    HttpResponseMessage hrm = await hc.PostAsync(url, content);
    string result = await hrm.Content.ReadAsStringAsync();

    AzureAccessToken token = JsonConvert.DeserializeObject<AzureAccessToken>(result);

    return token.access_token;
}

(첨부 파일은 이 글의 예제 코드를 포함합니다.)

그 외에, 원한다면 다음의 문서에 나오는 규격에 맞게 REST API를 사용해 원하는 기능을 구현할 수 있습니다.

Azure REST API Reference
; https://learn.microsoft.com/en-us/rest/api/

가령, 특정 Resource Gorup에 포함된 가상 머신을 나열하고 싶다면 다음의 문서에 따라 REST API 요청을 보내면 됩니다.

Virtual Machines - List All
; https://learn.microsoft.com/en-us/rest/api/compute/virtualmachines/virtualmachines_listall

GET https://management.azure.com/subscriptions/{subscriptionId}/providers/Microsoft.Compute/virtualMachines?api-version=2017-12-01




Access Token을 정상적으로 얻어온 후 이후 쿼리를 수행하는 작업에서 다음과 같은 오류가 발생할 수 있습니다.

{"error":{"code":"InvalidAuthenticationTokenAudience","message":"The access token has been obtained from wrong audience or resource 'https://management.core.windows.net'. It should exactly match (including forward slash) with one of the allowed audiences 'https://management.core.windows.net/','https://management.azure.com/'."}}

왜냐하면, AccessToken을 받아오는 resource URL을 다음과 같이 주는 경우,

POST https://login.microsoftonline.com/358f4aef-d1c5-4103-8f67-255bcb9d7670/oauth2/token HTTP/1.1
Content-Type: application/x-www-form-urlencoded
client-request-id: c931089a-2038-4858-9ed9-6e900cd9a761
return-client-request-id: true
x-client-SKU: .NET
x-client-Ver: 2.28.3.860
x-client-CPU: x64
x-client-OS: Microsoft Windows NT 6.2.9200.0
Host: login.microsoftonline.com
Content-Length: 181
Expect: 100-continue
Connection: Keep-Alive

resource=https%3A%2F%2Fmanagement.core.windows.net&client_id=5e7c8cb5-b3bc-4014-819b-855226fe7a16&client_secret=639f447b-d126-495d-ac50-ac32b5b22e3f&grant_type=client_credentials

그런 오류가 발생할 수 있습니다. 즉, "https://management.core.windows.net"이 아니고 "https://management.core.windows.net/"으로 설정해야 합니다.




[이 글에 대해서 여러분들과 의견을 공유하고 싶습니다. 틀리거나 미흡한 부분 또는 의문 사항이 있으시면 언제든 댓글 남겨주십시오.]

[연관 글]






[최초 등록일: ]
[최종 수정일: 11/27/2024]

Creative Commons License
이 저작물은 크리에이티브 커먼즈 코리아 저작자표시-비영리-변경금지 2.0 대한민국 라이센스에 따라 이용하실 수 있습니다.
by SeongTae Jeong, mailto:techsharer at outlook.com

비밀번호

댓글 작성자
 



2018-05-22 11시33분
RESTful API를 사용하는 경우, 마이크로소프트의 Azure 도움말 문서에서 자신이 소유한 구독을 대상으로 직접 실행해 보는 것도 가능합니다. 가령, 다음의 도움말을 방문해,

Web Apps - List
; https://learn.microsoft.com/en-us/rest/api/appservice/webapps/list

"Try It" 버튼을 누르면 우측에 패널이 열리면서 소유한 Azure 구독을 대상으로 곧바로 Request/Response를 확인할 수 있습니다.
정성태
2018-10-19 07시13분
Web Apps - List
; https://learn.microsoft.com/en-us/rest/api/appservice/webapps/webapps_list

(참고로, 기존 api 링크들의 마지막 메서드에 이전 단계의 경로가 추가됨. 가령 기존에는 .../webapps/list였지만 새롭게 바뀐 도움말 문서에서는 .../webapps/webapps_list로 바뀜)
정성태

... 91  92  93  94  95  96  97  98  99  100  101  102  103  [104]  105  ...
NoWriterDateCnt.TitleFile(s)
11321정성태10/11/201719757.NET Framework: 688. NGen 모듈과 .NET Profiler
11320정성태10/11/201720526.NET Framework: 687. COR_PRF_USE_PROFILE_IMAGES 옵션과 NGen의 "profiler-enhanced images" [1]
11319정성태10/11/201728138.NET Framework: 686. C# - string 배열을 담은 구조체를 직렬화하는 방법
11318정성태10/7/201720898VS.NET IDE: 122. 비주얼 스튜디오에서 관리자 권한을 요구하는 C# 콘솔 프로그램 제작 [1]
11317정성태10/4/201726059VC++: 120. std::copy 등의 함수 사용 시 _SCL_SECURE_NO_WARNINGS 에러 발생
11316정성태9/30/201724109디버깅 기술: 99. (닷넷) 프로세스(EXE)에 디버거가 연결되어 있는지 아는 방법 [4]
11315정성태9/29/201740205기타: 68. "시작하세요! C# 6.0 프로그래밍: 기본 문법부터 실전 예제까지" 구매하신 분들을 위한 C# 7.0/7.1 추가 문법 PDF [8]
11314정성태9/28/201721940디버깅 기술: 98. windbg - 덤프 파일로부터 닷넷 버전 확인하는 방법
11313정성태9/25/201719270디버깅 기술: 97. windbg - 메모리 덤프로부터 DateTime 형식의 값을 알아내는 방법파일 다운로드1
11312정성태9/25/201722276.NET Framework: 685. C# - 구조체(값 형식)의 필드를 리플렉션을 이용해 값을 바꾸는 방법파일 다운로드1
11311정성태9/20/201716817.NET Framework: 684. System.Diagnostics.Process 객체의 명시적인 해제 권장
11310정성태9/19/201720224.NET Framework: 683. WPF의 Window 객체를 생성했는데 GC 수집 대상이 안 되는 이유 [3]
11309정성태9/13/201718351개발 환경 구성: 335. Octave의 명령 창에서 실행한 결과를 복사하는 방법
11308정성태9/13/201719388VS.NET IDE: 121. 비주얼 스튜디오에서 일부 텍스트 파일을 무조건 메모장으로만 여는 문제파일 다운로드1
11307정성태9/13/201721902오류 유형: 421. System.Runtime.InteropServices.SEHException - 0x80004005
11306정성태9/12/201719950.NET Framework: 682. 아웃룩 사용자를 위한 중국어 스팸 필터 Add-in
11305정성태9/12/201721467개발 환경 구성: 334. 기존 프로젝트를 Visual Studio를 이용해 Github의 신규 생성된 repo에 올리는 방법 [1]
11304정성태9/11/201718605개발 환경 구성: 333. 3ds Max를 Hyper-V VM에서 실행하는 방법
11303정성태9/11/201721898개발 환경 구성: 332. Inno Setup 파일의 관리자 권한을 제거하는 방법
11302정성태9/11/201718124개발 환경 구성: 331. SQL Server Express를 위한 방화벽 설정
11301정성태9/11/201717037오류 유형: 420. SQL Server Express 연결 오류 - A network-related or instance-specific error occurred while establishing a connection to SQL Server.
11300정성태9/10/201720833.NET Framework: 681. dotnet.exe - run, exec, build, restore, publish 차이점 [3]
11299정성태9/9/201719603개발 환경 구성: 330. Hyper-V VM의 Internal Network를 Private 유형으로 만드는 방법
11298정성태9/8/201722876VC++: 119. EnumProcesses / EnumProcessModules API 사용 시 주의점 [1]
11297정성태9/8/201719531디버깅 기술: 96. windbg - 풀 덤프에 포함된 모든 닷넷 모듈을 파일로 저장하는 방법
11296정성태9/8/201722741웹: 36. Edge - "이 웹 사이트는 이전 기술에서 실행되며 Internet Explorer에서만 작동합니다." 끄는 방법
... 91  92  93  94  95  96  97  98  99  100  101  102  103  [104]  105  ...