성태의 닷넷 이야기
홈 주인
모아 놓은 자료
프로그래밍
질문/답변
사용자 관리
사용자
메뉴
아티클
외부 아티클
유용한 코드
온라인 기능
MathJax 입력기
최근 덧글
[정성태] VT sequences to "CONOUT$" vs. STD_O...
[정성태] NetCoreDbg is a managed code debugg...
[정성태] Evaluating tail call elimination in...
[정성태] What’s new in System.Text.Json in ....
[정성태] What's new in .NET 9: Cryptography ...
[정성태] 아... 제시해 주신 "https://akrzemi1.wordp...
[정성태] 다시 질문을 정리할 필요가 있을 것 같습니다. 제가 본문에...
[이승준] 완전히 잘못 짚었습니다. 댓글 지우고 싶네요. 검색을 해보...
[정성태] 우선 답글 감사합니다. ^^ 그런데, 사실 저 예제는 (g...
[이승준] 수정이 안되어서... byteArray는 BYTE* 타입입니다...
글쓰기
제목
이름
암호
전자우편
HTML
홈페이지
유형
제니퍼 .NET
닷넷
COM 개체 관련
스크립트
VC++
VS.NET IDE
Windows
Team Foundation Server
디버깅 기술
오류 유형
개발 환경 구성
웹
기타
Linux
Java
DDK
Math
Phone
Graphics
사물인터넷
부모글 보이기/감추기
내용
<div style='display: inline'> <h1 style='font-family: Malgun Gothic, Consolas; font-size: 20pt; color: #006699; text-align: center; font-weight: bold'>C# - API를 사용해 Azure에 접근하는 방법</h1> <p> Azure를 Portal 웹 사이트의 UI가 아닌, 프로그래밍으로 다루는 것도 가능한데 다음의 문서에 그 방법이 나옵니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > Azure Management Libraries for .NET generally available now - Working with the Azure Management Libraries for .NET ; <a target='tab' href='https://azure.microsoft.com/en-us/blog/azure-management-libraries-for-net-generally-available-now/'>https://azure.microsoft.com/en-us/blog/azure-management-libraries-for-net-generally-available-now/</a> </pre> <br /> RESTful API 형식도 제공하지만 C# 개발자라면 전용 .NET 라이브러리를 사용하면 좀 더 쉽게 개발할 수 있습니다. 물론, 그 라이브러리는 NuGet을 통해 Microsoft.Azure.Management.Fluent 어셈블리로 제공됩니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > Microsoft.Azure.Management.Fluent ; <a target='tab' href='https://www.nuget.org/packages/Microsoft.Azure.Management.Fluent/'>https://www.nuget.org/packages/Microsoft.Azure.Management.Fluent/</a> </pre> <br /> 이 라이브러리를 사용해 azure 정보를 구하려면 당연히 인증이 선행되어야 할 텐데, 따라서 지난번 글에서 다룬 방법으로 미리 계정을 생성해 둬야 합니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > Azure - PowerShell로 Access control(IAM)에 새로운 계정 만드는 방법 ; <a target='tab' href='http://www.sysnet.pe.kr/2/0/11479'>http://www.sysnet.pe.kr/2/0/11479</a> </pre> <br /> 그런 다음, 위의 계정 정보와 연관된 인증 정보를 제공하는 방법 중의 하나로 파일을 사용할 수 있고, 대략 다음과 같은 정보를 담고 있어야 합니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > # 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/ </pre> <br /> 이 중에서 빈 값들을 하나씩 채워볼 텐데요, 우선 subscription, tenant 먼저 구해보겠습니다. 이를 위해 "Microsoft Azure Command Prompt"를 열어 Powershell을 실행한 후 "Login-AzureRmAccount" 명령어를 실행하면 다음과 같이 로그인 대화창이 떠 계정 정보를 입력받게 됩니다.<br /> <br /> <img onclick='toggle_img(this)' class='imgView' alt='azure_rest_api_1.png' src='/SysWebRes/bbs/azure_rest_api_1.png' /><br /> <br /> (참고로, Visual Studio에서 "Azure development" 구성 요소를 선택했으면 기본적으로 Microsoft Azure Command Prompt가 제공되는 듯합니다.)<br /> <br /> 정상적으로 입력했으면 아래와 같이 Account, SubscriptionName, SubscriptionId, TenantId, Environment 필드로 이뤄진 출력이 보입니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > Windows Azure SDK Shell C:\Program Files\Microsoft SDKs\Azure\.NET SDK\v2.9><span style='color: blue; font-weight: bold'>powershell</span> Windows PowerShell Copyright (C) Microsoft Corporation. All rights reserved. PS> <span style='color: blue; font-weight: bold'>Login-AzureRmAccount</span> <span style='color: blue; font-weight: bold'>Account : testuser@mydomain.net SubscriptionName : Windows Azure SubscriptionId : <span style='color: blue; font-weight: bold'>461e7265-0395-49b1-94b3-4c891430a893</span> TenantId : <span style='color: blue; font-weight: bold'>5A7266AE-1C5D-4386-9D0D-2131CC3B2A2C</span> Environment : AzureCloud </span> </pre> <br /> (참고로, SubscriptionId의 경우 Azure Portal의 구독자를, TenantId는 Azure Active Directory를 구분합니다.)<br /> <br /> 위의 정보에서 SubscriptionId, TenantId 값을 이용해 다음과 같이 인증 파일의 내용을 채우면 됩니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > # sample management library properties file subscription=<span style='color: blue; font-weight: bold'>461e7265-0395-49b1-94b3-4c891430a893</span> client= key= tenant=<span style='color: blue; font-weight: bold'>5A7266AE-1C5D-4386-9D0D-2131CC3B2A2C</span> managementURI=https://management.core.windows.net/ baseURL=https://management.azure.com/ authURL=https://login.windows.net/ graphURL=https://graph.windows.net/ </pre> <br /> 남은 것은, client와 key 값인데요. 이는 이전 글에서 New-AzureRmADServicePrincipal을 실습하면서 얻은 결과를 사용하면 됩니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > PS> $sp = <span style='color: blue; font-weight: bold'>New-AzureRmADServicePrincipal</span> -DisplayName "AzureDotNetTest" -Password <span style='color: blue; font-weight: bold'>[...encrypted_password...]</span> PS> <span style='color: blue; font-weight: bold'>New-AzureRmRoleAssignment</span> -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> <span style='color: blue; font-weight: bold'>$sp | Select DisplayName, ApplicationId</span> DisplayName ApplicationId ----------- ------------- AzureDotNetTest <span style='color: blue; font-weight: bold'>C5129BAC-96F4-43FE-B5D7-DEE8CE7ED2A0</span> </pre> <br /> 위의 결과로 구한 ApplicationId 역시 Azure Portal에서 저 계정 정보로 들어가서 구할 수 있습니다. 그래서 최종적으로 다음과 같이 채울 수 있습니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > # sample management library properties file subscription=461e7265-0395-49b1-94b3-4c891430a893 client=<span style='color: blue; font-weight: bold'>3FC47033-C110-4B8A-B364-1CFC42BA4D47</span> key=<span style='color: blue; font-weight: bold'>...encrypted string...</span> 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/ </pre> <br /> 문제는, 저기서 key 값입니다. 평문을 넣어도 C# 코드에서 인증 시 이런 오류가 발생합니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > 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. </pre> <br /> 참고로 SecureString을 이렇게 변환할 수 있습니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > PS> $secureString System.Security.SecureString PS> $StandardString = ConvertFrom-SecureString $secureString PS> $StandardString 01000000d08c9ddf0115d1118c7a00c04fc297eb0100000024044cdd8d0ac54...[생략]...81162dfc600e837ac5f2a4b64fc77bcae9159e3c2baa98ee68d0698a8c263f1b9bf6c04de5a28269d8b5 </pre> <br /> 하지만 저 값을 기록해도 역시 예외가 발생합니다. <span style='text-decoration: line-through'>혹시 저 key 값에 어떤 것을 넣는지 아시는 분 있으면 ^^ 덧글 부탁드립니다.</span> <span style='color: blue; font-weight: bold'>(2018-04-18 업데이트: <a target='tab' href='http://www.sysnet.pe.kr/2/0/11501'>New-AzureRmADServicePrincipal로 생성한 계정의 clientSecret, key 값을 구하는 방법</a>)</span><br /> <br /> <hr style='width: 50%' /><br /> <br /> <span style='text-decoration: line-through'>결국 저 방식으로는 해결을 못하고 다른 방법을 찾게 되었습니다.</span> 다른 방법도 있습니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > Authentication in Azure Management Libraries for .NET ; <a target='tab' href='https://github.com/Azure/azure-libraries-for-net/blob/master/AUTH.md'>https://github.com/Azure/azure-libraries-for-net/blob/master/AUTH.md</a> </pre> <br /> Azure CLI 명령어를 이용하는 것인데, 이를 위해 az login으로 명령행에서 credential 문맥을 생성하고,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > C:\Program Files (x86)\Microsoft Visual Studio\2017\Enterprise><span style='color: blue; font-weight: bold'>az login</span> 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" } } ] </pre> <br /> 다음과 같이 Access Control(IAM) 계정을 Powershell과 유사하게 생성할 수 있습니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > C:\Program Files (x86)\Microsoft Visual Studio\2017\Enterprise><span style='color: blue; font-weight: bold'>az ad sp create-for-rbac --sdk-auth</span> 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/" } </pre> <br /> 바로 저 출력 결과로 나오는, "{", "}" 내용을 파일로 저장하고,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > { "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/" } </pre> <br /> 이를 C# 코드에서 사용하면 됩니다.<br /> <br /> <hr style='width: 50%' /><br /> <br /> 우여곡절 끝에 인증 파일을 만들었으면, 이제 다음과 같이 C# 코드에서 Azure 서비스로 인증이 가능합니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > // credFile - 인증 정보를 담고 있는 파일 경로 IAzure azure = Azure.Authenticate(credFile).WithDefaultSubscription(); </pre> <br /> 이후 ARM으로 관리되는 모든 자원을 접근할 수 있는데, 참고로 여기서 classic 유형의 자원들은 Microsoft.Azure.Management.Fluent에서 제외됩니다. (즉, 예전의 Cloud services - classic, Virtual machiens - classic 유형들의 자원은 접근할 수 없습니다.)<br /> <br /> ARM 방식의 Azure 관리 모델은 "Resource Group"으로 대표하는데, 따라서 우선 첫 단계로 리소스 그룹 목록을 다음과 같이 구할 수 있습니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > IAzure azure = Azure.Authenticate(credFile).WithDefaultSubscription(); var resGroups = azure.ResourceGroups.List(); foreach (var resGroup in resGroups) { } </pre> <br /> 이후 해당 리소스 그룹에 속한 모든 자원을 다음과 같이 열거할 수 있습니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > 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); //} } } } </pre> <br /> 뭐... 더 설명이 필요하진 않겠죠? ^^<br /> <br /> <hr style='width: 50%' /><br /> <br /> 서두에서 이야기한 데로 RESTful API 방식으로도 접근이 가능합니다. 위의 소스 코드를 실행 후 Azure.Authenticate 메서드 수행 시 Fiddler로 HTTP 요청을 가로채면 다음과 같은 결과를 얻을 수 있습니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > [요청] 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 </pre> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > [응답] 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/",<span style='color: blue; font-weight: bold'>"access_token":"eyJ0eXAiOiJ...[생략]...kCD8Pg"</span>} </pre> <br /> 이후 응답으로 받은 access_token을 RESTful API의 인증 헤더에 전달하는 것을 볼 수 있습니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > [요청] 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 <span style='color: blue; font-weight: bold'>Authorization: Bearer eyJ0eXAiOiJ...[생략]...kCD8Pg</span> 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 </pre> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > [응답] 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"}}]} </pre> <br /> 따라서, RESTful API를 이용하는 코드를 다음과 같이 작성할 수 있습니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > private static async void UsingRESTFulAPI() { <span style='color: blue; font-weight: bold'>string accessToken = await GetAccessToken();</span> Console.WriteLine("AccessToken: " + accessToken); <span style='color: blue; font-weight: bold'>string resourceList = await GetResourceGroups(accessToken);</span> Console.WriteLine(resourceList); } private static async Task<string> GetResourceGroups(string accessToken) { HttpClient hc = new HttpClient(); <span style='color: blue; font-weight: bold'>hc.DefaultRequestHeaders.Add("Authorization", "Bearer " + accessToken);</span> 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); <span style='color: blue; font-weight: bold'>dict.Add("client_id", clientId); dict.Add("client_secret", clientSecret);</span> 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; } </pre> <br /> (<a target='tab' href='http://www.sysnet.pe.kr/bbs/DownloadAttachment.aspx?fid=1241&boardid=331301885'>첨부 파일은 이 글의 예제 코드를 포함</a>합니다.)<br /> <br /> 그 외에, 원한다면 다음의 문서에 나오는 규격에 맞게 REST API를 사용해 원하는 기능을 구현할 수 있습니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > Azure REST API Reference ; <a target='tab' href='https://docs.microsoft.com/en-us/rest/api/'>https://docs.microsoft.com/en-us/rest/api/</a> </pre> <br /> 가령, 특정 Resource Gorup에 포함된 가상 머신을 나열하고 싶다면 다음의 문서에 따라 REST API 요청을 보내면 됩니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > Virtual Machines - List All ; <a target='tab' href='https://docs.microsoft.com/en-us/rest/api/compute/virtualmachines/virtualmachines_listall'>https://docs.microsoft.com/en-us/rest/api/compute/virtualmachines/virtualmachines_listall</a> GET https://management.azure.com/subscriptions/{subscriptionId}/providers/Microsoft.Compute/virtualMachines?api-version=2017-12-01 </pre> <br /> <hr style='width: 50%' /><br /> <br /> Access Token을 정상적으로 얻어온 후 이후 쿼리를 수행하는 작업에서 다음과 같은 오류가 발생할 수 있습니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > {"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/'."}} </pre> <br /> 왜냐하면, AccessToken을 받아오는 resource URL을 다음과 같이 주는 경우,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > 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=<span style='color: blue; font-weight: bold'>https%3A%2F%2Fmanagement.core.windows.net</span>&client_id=5e7c8cb5-b3bc-4014-819b-855226fe7a16&client_secret=639f447b-d126-495d-ac50-ac32b5b22e3f&grant_type=client_credentials </pre> <br /> 그런 오류가 발생할 수 있습니다. 즉, "<a target='tab' href='https://management.core.windows.net'>https://management.core.windows.net</a>"이 아니고 "<a target='tab' href='https://management.core.windows.net/'>https://management.core.windows.net/</a>"으로 설정해야 합니다.<br /> </p><br /> <br /><hr /><span style='color: Maroon'>[이 글에 대해서 여러분들과 의견을 공유하고 싶습니다. 틀리거나 미흡한 부분 또는 의문 사항이 있으시면 언제든 댓글 남겨주십시오.]</span> </div>
첨부파일
스팸 방지용 인증 번호
8215
(왼쪽의 숫자를 입력해야 합니다.)