

 AWS SDK for .NET V3가 유지 관리 모드로 전환되었습니다.

[AWS SDK for .NET V4](https://docs.aws.amazon.com/sdk-for-net/v4/developer-guide/welcome.html)로 마이그레이션하는 것이 좋습니다. 마이그레이션 방법에 대한 자세한 내용과 정보는 [유지 관리 모드 공지](https://aws.amazon.com/blogs/developer/aws-sdk-for-net-v3-maintenance-mode-announcement/)를 참조하세요.

기계 번역으로 제공되는 번역입니다. 제공된 번역과 원본 영어의 내용이 상충하는 경우에는 영어 버전이 우선합니다.

# Amazon EC2 작업
<a name="ec2-apis-intro"></a>

는 크기 조정 가능한 컴퓨팅 용량을 제공하는 웹 서비스인 [Amazon EC2](https://docs.aws.amazon.com/ec2/)를 AWS SDK for .NET 지원합니다. 이 컴퓨팅 용량을 사용하여 소프트웨어 시스템을 구축하고 호스팅할 수 있습니다.

## API
<a name="w2aac19c15c17b5"></a>

는 Amazon EC2 클라이언트용 APIs AWS SDK for .NET 제공합니다. API를 사용하면 보안 그룹 및 키 페어와 같은 EC2 기능을 사용할 수 있습니다. 또한 API를 사용하여 Amazon EC2 인스턴스를 제어할 수 있습니다. 이 섹션에는 이러한 API로 작업할 때 따를 수 있는 패턴을 보여주는 몇 가지 예제가 포함되어 있습니다. 전체 API 세트를 보려면 [AWS SDK for .NET API 참조](https://docs.aws.amazon.com/sdkfornet/v3/apidocs/)를 참조하세요(그리고 "Amazon.EC2"으로 스크롤하세요).

Amazon EC2 API는 [AWSSDK.EC2](https://www.nuget.org/packages/AWSSDK.EC2) NuGet 패키지를 통해 제공됩니다.

## 사전 조건
<a name="w2aac19c15c17b7"></a>

시작하기 전에 먼저 [환경 및 프로젝트를 설정](net-dg-config.md)해야 합니다. 또한 [SDK 기능](net-dg-sdk-features.md)의 정보를 검토합니다.

## 예시 관련 정보
<a name="ec2-apis-intro-about"></a>

이 섹션의 예시에서는 Amazon EC2 클라이언트 작업 방법과 Amazon EC2 인스턴스를 관리하는 방법을 보여줍니다.

[EC2 스팟 인스턴스 자습서](how-to-spot-instances.md)에서는 Amazon EC2 스팟 인스턴스를 요청하는 방법을 보여줍니다. 스팟 인스턴스를 사용하면 온디맨드 가격보다 저렴한 비용으로 미사용 EC2 용량에 액세스할 수 있습니다.

**Topics**
+ [API](#w2aac19c15c17b5)
+ [사전 조건](#w2aac19c15c17b7)
+ [예시 관련 정보](#ec2-apis-intro-about)
+ [보안 그룹](security-groups.md)
+ [키 페어](key-pairs.md)
+ [리전 및 가용 영역](using-regions-and-availability-zones.md)
+ [EC2 인스턴스](how-to-ec2.md)
+ [스팟 인스턴스 자습서](how-to-spot-instances.md)

# Amazon EC2 에서 보안 그룹 작업
<a name="security-groups"></a>

Amazon EC2에서 *보안 그룹*은 하나 이상의 EC2 인스턴스에 대한 네트워크 트래픽을 제어하는 가상 방화벽 역할을 합니다. 기본적으로 EC2는 인바운드 트래픽을 허용하지 않는 보안 그룹과 인스턴스를 연결합니다. EC2 인스턴스가 특정 트래픽을 받아들이도록 허용하는 보안 그룹을 생성할 수 있습니다. 예를 들어 EC2 Windows 인스턴스에 연결해야 하는 경우 보안 그룹이 RDP 트래픽을 허용하도록 구성해야 합니다.

보안 그룹에 대한 자세한 내용은 [Amazon EC2 사용 설명서](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/)의 [Amazon EC2 보안 그룹](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/ec2-security-groups.html)을 참조하세요.

**주의**  
EC2-Classic은 2022년 8월 15일에 사용 중지되었습니다. EC2-Classic에서 VPC로 마이그레이션하는 것이 좋습니다. 자세한 내용은 블로그 게시물 [EC2-Classic 네트워킹은 사용 중지 중입니다 - 준비 방법은 다음과 같습니다](https://aws.amazon.com/blogs/aws/ec2-classic-is-retiring-heres-how-to-prepare/)를 참조하세요.

API 및 사전 조건에 대한 자세한 내용은 상위 섹션([Amazon EC2 작업](ec2-apis-intro.md))을 참조하세요.

**Topics**
+ [보안 그룹 열거](enumerate-security-groups.md)
+ [보안 그룹 생성](creating-security-group.md)
+ [보안 그룹 업데이트](authorize-ingress.md)

# 보안 그룹 열거
<a name="enumerate-security-groups"></a>

이 예제에서는를 사용하여 보안 그룹을 열거 AWS SDK for .NET 하는 방법을 보여줍니다. [Amazon Virtual Private Cloud](https://docs.aws.amazon.com/vpc/latest/userguide/) ID를 제공하는 경우 애플리케이션은 해당 VPC의 보안 그룹을 열거합니다. 그렇지 않으면 애플리케이션은 사용 가능한 모든 보안 그룹 목록만 표시합니다.

다음 섹션에서는 이 예제의 코드 조각을 제공합니다. [예제의 전체 코드](#enum-sec-groups-complete-code)는 그 뒤에 표시되며, 그대로 빌드하고 실행할 수 있습니다.

**Topics**
+ [보안 그룹 열거](#enum-sec-groups-enum)
+ [전체 코드](#enum-sec-groups-complete-code)
+ [추가 고려 사항](#enum-sec-groups-additional)

## 보안 그룹 열거
<a name="enum-sec-groups-enum"></a>

다음 코드 조각은 보안 그룹을 열거합니다. 모든 그룹을 열거하거나 특정 VPC의 그룹이 있는 경우 해당 그룹을 열거합니다.

[이 주제의 끝 부분에 있는](#enum-sec-groups-complete-code) 예제에서는 사용 중인 이 코드 조각을 보여줍니다.

```
    //
    // Method to enumerate the security groups
    private static async Task EnumerateGroups(IAmazonEC2 ec2Client, string vpcID)
    {
      // A request object, in case we need it.
      var request = new DescribeSecurityGroupsRequest();

      // Put together the properties, if needed
      if(!string.IsNullOrEmpty(vpcID))
      {
        // We have a VPC ID. Find the security groups for just that VPC.
        Console.WriteLine($"\nGetting security groups for VPC {vpcID}...\n");
        request.Filters.Add(new Filter
        {
          Name = "vpc-id",
          Values = new List<string>() { vpcID }
        });
      }

      // Get the list of security groups
      DescribeSecurityGroupsResponse response =
        await ec2Client.DescribeSecurityGroupsAsync(request);

      // Display the list of security groups.
      foreach (SecurityGroup item in response.SecurityGroups)
      {
        Console.WriteLine("Security group: " + item.GroupId);
        Console.WriteLine("\tGroupId: " + item.GroupId);
        Console.WriteLine("\tGroupName: " + item.GroupName);
        Console.WriteLine("\tVpcId: " + item.VpcId);
        Console.WriteLine();
      }
    }
```

## 전체 코드
<a name="enum-sec-groups-complete-code"></a>

이 섹션에는 이 예제에 대한 관련 참조와 전체 코드가 나와 있습니다.

### SDK 레퍼런스
<a name="w2aac19c15c17c13c13c15b5b1"></a>

NuGet 패키지:
+ [AWSSDK.EC2](https://www.nuget.org/packages/AWSSDK.EC2)

프로그래밍 요소:
+ 네임스페이스 [Amazon.EC2](https://docs.aws.amazon.com/sdkfornet/v3/apidocs/items/EC2/NEC2.html)

  클래스 [AmazonEC2Client](https://docs.aws.amazon.com/sdkfornet/v3/apidocs/items/EC2/TEC2Client.html)
+ 네임스페이스 [Amazon.EC2.Model](https://docs.aws.amazon.com/sdkfornet/v3/apidocs/items/EC2/NEC2Model.html)

  클래스 [DescribeSecurityGroupsRequest](https://docs.aws.amazon.com/sdkfornet/v3/apidocs/items/EC2/TDescribeSecurityGroupsRequest.html)

  클래스 [DescribeSecurityGroupsResponse](https://docs.aws.amazon.com/sdkfornet/v3/apidocs/items/EC2/TDescribeSecurityGroupsResponse.html)

  클래스 [Filter](https://docs.aws.amazon.com/sdkfornet/v3/apidocs/items/EC2/TFilter.html)

  클래스 [SecurityGroup](https://docs.aws.amazon.com/sdkfornet/v3/apidocs/items/EC2/TSecurityGroup.html)

### 코드
<a name="w2aac19c15c17c13c13c15b7b1"></a>

```
using System;
using System.Threading.Tasks;
using System.Collections.Generic;
using Amazon.EC2;
using Amazon.EC2.Model;

namespace EC2EnumerateSecGroups
{
  class Program
  {
    static async Task Main(string[] args)
    {
      // Parse the command line
       string vpcID = string.Empty;
      if(args.Length == 0)
      {
        Console.WriteLine("\nEC2EnumerateSecGroups [vpc_id]");
        Console.WriteLine("  vpc_id - The ID of the VPC for which you want to see security groups.");
        Console.WriteLine("\nSince you specified no arguments, showing all available security groups.");
      }
      else
      {
        vpcID = args[0];
      }

      if(vpcID.StartsWith("vpc-") || string.IsNullOrEmpty(vpcID))
      {
        // Create an EC2 client object
        var ec2Client = new AmazonEC2Client();

        // Enumerate the security groups
        await EnumerateGroups(ec2Client, vpcID);
      }
      else
      {
        Console.WriteLine("Could not find a valid VPC ID in the command-line arguments:");
        Console.WriteLine($"{args[0]}");
      }
    }


    //
    // Method to enumerate the security groups
    private static async Task EnumerateGroups(IAmazonEC2 ec2Client, string vpcID)
    {
      // A request object, in case we need it.
      var request = new DescribeSecurityGroupsRequest();

      // Put together the properties, if needed
      if(!string.IsNullOrEmpty(vpcID))
      {
        // We have a VPC ID. Find the security groups for just that VPC.
        Console.WriteLine($"\nGetting security groups for VPC {vpcID}...\n");
        request.Filters.Add(new Filter
        {
          Name = "vpc-id",
          Values = new List<string>() { vpcID }
        });
      }

      // Get the list of security groups
      DescribeSecurityGroupsResponse response =
        await ec2Client.DescribeSecurityGroupsAsync(request);

      // Display the list of security groups.
      foreach (SecurityGroup item in response.SecurityGroups)
      {
        Console.WriteLine("Security group: " + item.GroupId);
        Console.WriteLine("\tGroupId: " + item.GroupId);
        Console.WriteLine("\tGroupName: " + item.GroupName);
        Console.WriteLine("\tVpcId: " + item.VpcId);
        Console.WriteLine();
      }
    }
  }
}
```

## 추가 고려 사항
<a name="enum-sec-groups-additional"></a>
+ VPC의 경우 이름-값 쌍의 `Name` 부분이 "vpc-id"로 설정된 상태로 필터가 구성된다는 점에 유의하세요. 이 이름은 [DescribeSecurityGroupsRequest](https://docs.aws.amazon.com/sdkfornet/v3/apidocs/items/EC2/TDescribeSecurityGroupsRequest.html) 클래스의 `Filters` 속성 설명에서 가져온 것입니다.
+ 전체 보안 그룹 목록을 가져오려면 [파라미터 없이 DescribeSecurityGroupsAsync](https://docs.aws.amazon.com/sdkfornet/v3/apidocs/items/EC2/MEC2DescribeSecurityGroupsAsyncCancellationToken.html)를 사용할 수도 있습니다.
+ [Amazon EC2 콘솔](https://console.aws.amazon.com/ec2/v2/home#SecurityGroups)에서 보안 그룹 목록을 확인하여 결과를 확인할 수 있습니다.

# 보안 그룹 생성
<a name="creating-security-group"></a>

이 예제에서는를 사용하여 보안 그룹을 AWS SDK for .NET 생성하는 방법을 보여줍니다. 기존 VPC의 ID를 제공하여 VPC의 EC2에 대한 보안 그룹을 생성할 수 있습니다. 이러한 ID를 제공하지 않으면 AWS 계정이 이를 지원하는 경우 새 보안 그룹은 EC2-Classic용 입니다.

VPC ID를 제공하지 않고 AWS 계정이 EC2-Classic을 지원하지 않는 경우 새 보안 그룹은 계정의 기본 VPC에 속합니다.

**주의**  
EC2-Classic은 2022년 8월 15일에 사용 중지되었습니다. EC2-Classic에서 VPC로 마이그레이션하는 것이 좋습니다. 자세한 내용은 블로그 게시물 [EC2-Classic 네트워킹은 사용 중지 중입니다 - 준비 방법은 다음과 같습니다](https://aws.amazon.com/blogs/aws/ec2-classic-is-retiring-heres-how-to-prepare/)를 참조하세요.

다음 섹션에서는 이 예제의 코드 조각을 제공합니다. [예제의 전체 코드](#create-sec-groups-complete-code)는 그 뒤에 표시되며, 그대로 빌드하고 실행할 수 있습니다.

**Topics**
+ [기존 보안 그룹 찾기](#create-sec-groups-find)
+ [보안 그룹 생성](#create-sec-groups-enum)
+ [전체 코드](#create-sec-groups-complete-code)

## 기존 보안 그룹 찾기
<a name="create-sec-groups-find"></a>

다음 코드 조각은 지정된 VPC에서 지정된 이름을 가진 기존 보안 그룹을 검색합니다.

[이 주제의 끝 부분에 있는](#create-sec-groups-complete-code) 예제에서는 사용 중인 이 코드 조각을 보여줍니다.

```
    //
    // Method to determine if a security group with the specified name
    // already exists in the VPC
    private static async Task<List<SecurityGroup>> FindSecurityGroups(
      IAmazonEC2 ec2Client, string groupName, string vpcID)
    {
      var request = new DescribeSecurityGroupsRequest();
      request.Filters.Add(new Filter{
        Name = "group-name",
        Values = new List<string>() { groupName }
      });
      if(!string.IsNullOrEmpty(vpcID))
        request.Filters.Add(new Filter{
          Name = "vpc-id",
          Values = new List<string>() { vpcID }
        });

      var response = await ec2Client.DescribeSecurityGroupsAsync(request);
      return response.SecurityGroups;
    }
```

## 보안 그룹 생성
<a name="create-sec-groups-enum"></a>

다음 코드 조각은 해당 이름을 가진 그룹이 지정된 VPC에 없는 경우 새 보안 그룹을 생성합니다. VPC가 제공되지 않고 같은 이름을 가진 그룹이 하나 이상 존재할 경우 코드 조각은 단순히 그룹 목록을 반환합니다.

[이 주제의 끝 부분에 있는](#create-sec-groups-complete-code) 예제에서는 사용 중인 이 코드 조각을 보여줍니다.

```
    //
    // Method to create a new security group (either EC2-Classic or EC2-VPC)
    // If vpcID is empty, the security group will be for EC2-Classic
    private static async Task<List<SecurityGroup>> CreateSecurityGroup(
      IAmazonEC2 ec2Client, string groupName, string vpcID)
    {
      // See if one or more security groups with that name
      // already exist in the given VPC. If so, return the list of them.
      var securityGroups = await FindSecurityGroups(ec2Client, groupName, vpcID);
      if (securityGroups.Count > 0)
      {
        Console.WriteLine(
          $"\nOne or more security groups with name {groupName} already exist.\n");
        return securityGroups;
      }

      // If the security group doesn't already exists, create it.
      var createRequest = new CreateSecurityGroupRequest{
        GroupName = groupName
      };
      if(string.IsNullOrEmpty(vpcID))
      {
        createRequest.Description = "My .NET example security group for EC2-Classic";
      }
      else
      {
        createRequest.VpcId = vpcID;
        createRequest.Description = "My .NET example security group for EC2-VPC";
      }
      CreateSecurityGroupResponse createResponse =
        await ec2Client.CreateSecurityGroupAsync(createRequest);

      // Return the new security group
      DescribeSecurityGroupsResponse describeResponse =
        await ec2Client.DescribeSecurityGroupsAsync(new DescribeSecurityGroupsRequest{
          GroupIds = new List<string>() { createResponse.GroupId }
        });
      return describeResponse.SecurityGroups;
    }
```

## 전체 코드
<a name="create-sec-groups-complete-code"></a>

이 섹션에는 이 예제에 대한 관련 참조와 전체 코드가 나와 있습니다.

### SDK 레퍼런스
<a name="w2aac19c15c17c13c15c23b5b1"></a>

NuGet 패키지:
+ [AWSSDK.EC2](https://www.nuget.org/packages/AWSSDK.EC2)

프로그래밍 요소:
+ 네임스페이스 [Amazon.EC2](https://docs.aws.amazon.com/sdkfornet/v3/apidocs/items/EC2/NEC2.html)

  클래스 [AmazonEC2Client](https://docs.aws.amazon.com/sdkfornet/v3/apidocs/items/EC2/TEC2Client.html)
+ 네임스페이스 [Amazon.EC2.Model](https://docs.aws.amazon.com/sdkfornet/v3/apidocs/items/EC2/NEC2Model.html)

  클래스 [CreateSecurityGroupRequest](https://docs.aws.amazon.com/sdkfornet/v3/apidocs/items/EC2/TCreateSecurityGroupRequest.html)

  클래스 [CreateSecurityGroupResponse](https://docs.aws.amazon.com/sdkfornet/v3/apidocs/items/EC2/TCreateSecurityGroupResponse.html)

  클래스 [DescribeSecurityGroupsRequest](https://docs.aws.amazon.com/sdkfornet/v3/apidocs/items/EC2/TDescribeSecurityGroupsRequest.html)

  클래스 [DescribeSecurityGroupsResponse](https://docs.aws.amazon.com/sdkfornet/v3/apidocs/items/EC2/TDescribeSecurityGroupsResponse.html)

  필터 [Filter ](https://docs.aws.amazon.com/sdkfornet/v3/apidocs/items/EC2/TFilter.html)

  클래스 [SecurityGroup](https://docs.aws.amazon.com/sdkfornet/v3/apidocs/items/EC2/TSecurityGroup.html)

### 코드
<a name="w2aac19c15c17c13c15c23b7b1"></a>

```
using System;
using System.Threading.Tasks;
using System.Collections.Generic;
using Amazon.EC2;
using Amazon.EC2.Model;

namespace EC2CreateSecGroup
{
  // = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
  // Class to create a security group
  class Program
  {
    private const int MaxArgs = 2;

    static async Task Main(string[] args)
    {
      // Parse the command line and show help if necessary
      var parsedArgs = CommandLine.Parse(args);
      if(parsedArgs.Count == 0)
      {
        PrintHelp();
        return;
      }
      if(parsedArgs.Count > MaxArgs)
        CommandLine.ErrorExit("\nThe number of command-line arguments is incorrect." +
          "\nRun the command with no arguments to see help.");

      // Get the application arguments from the parsed list
      var groupName = CommandLine.GetArgument(parsedArgs, null, "-g", "--group-name");
      var vpcID = CommandLine.GetArgument(parsedArgs, null, "-v", "--vpc-id");
      if(string.IsNullOrEmpty(groupName))
        CommandLine.ErrorExit("\nYou must supply a name for the new group." +
          "\nRun the command with no arguments to see help.");
      if(!string.IsNullOrEmpty(vpcID) && !vpcID.StartsWith("vpc-"))
        CommandLine.ErrorExit($"\nNot a valid VPC ID: {vpcID}");

      // groupName has a value and vpcID either has a value or is null (which is fine)
      // Create the new security group and display information about it
      var securityGroups =
        await CreateSecurityGroup(new AmazonEC2Client(), groupName, vpcID);
      Console.WriteLine("Information about the security group(s):");
      foreach(var group in securityGroups)
      {
        Console.WriteLine($"\nGroupName: {group.GroupName}");
        Console.WriteLine($"GroupId: {group.GroupId}");
        Console.WriteLine($"Description: {group.Description}");
        Console.WriteLine($"VpcId (if any): {group.VpcId}");
      }
    }


    //
    // Method to create a new security group (either EC2-Classic or EC2-VPC)
    // If vpcID is empty, the security group will be for EC2-Classic
    private static async Task<List<SecurityGroup>> CreateSecurityGroup(
      IAmazonEC2 ec2Client, string groupName, string vpcID)
    {
      // See if one or more security groups with that name
      // already exist in the given VPC. If so, return the list of them.
      var securityGroups = await FindSecurityGroups(ec2Client, groupName, vpcID);
      if (securityGroups.Count > 0)
      {
        Console.WriteLine(
          $"\nOne or more security groups with name {groupName} already exist.\n");
        return securityGroups;
      }

      // If the security group doesn't already exists, create it.
      var createRequest = new CreateSecurityGroupRequest{
        GroupName = groupName
      };
      if(string.IsNullOrEmpty(vpcID))
      {
        createRequest.Description = "Security group for .NET code example (no VPC specified)";
      }
      else
      {
        createRequest.VpcId = vpcID;
        createRequest.Description = "Security group for .NET code example (VPC: " + vpcID + ")";
      }
      CreateSecurityGroupResponse createResponse =
        await ec2Client.CreateSecurityGroupAsync(createRequest);

      // Return the new security group
      DescribeSecurityGroupsResponse describeResponse =
        await ec2Client.DescribeSecurityGroupsAsync(new DescribeSecurityGroupsRequest{
          GroupIds = new List<string>() { createResponse.GroupId }
        });
      return describeResponse.SecurityGroups;
    }


    //
    // Method to determine if a security group with the specified name
    // already exists in the VPC
    private static async Task<List<SecurityGroup>> FindSecurityGroups(
      IAmazonEC2 ec2Client, string groupName, string vpcID)
    {
      var request = new DescribeSecurityGroupsRequest();
      request.Filters.Add(new Filter{
        Name = "group-name",
        Values = new List<string>() { groupName }
      });
      if(!string.IsNullOrEmpty(vpcID))
        request.Filters.Add(new Filter{
          Name = "vpc-id",
          Values = new List<string>() { vpcID }
        });

      var response = await ec2Client.DescribeSecurityGroupsAsync(request);
      return response.SecurityGroups;
    }


    //
    // Command-line help
    private static void PrintHelp()
    {
      Console.WriteLine(
        "\nUsage: EC2CreateSecGroup -g <group-name> [-v <vpc-id>]" +
        "\n  -g, --group-name: The name you would like the new security group to have." +
        "\n  -v, --vpc-id: The ID of a VPC to which the new security group will belong." +
        "\n     If vpc-id isn't present, the security group will be" +
        "\n     for EC2-Classic (if your AWS account supports this)" +
        "\n     or will use the default VCP for EC2-VPC.");
    }
  }


  // = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
  // Class that represents a command line on the console or terminal.
  // (This is the same for all examples. When you have seen it once, you can ignore it.)
  static class CommandLine
  {
    //
    // Method to parse a command line of the form: "--key value" or "-k value".
    //
    // Parameters:
    // - args: The command-line arguments passed into the application by the system.
    //
    // Returns:
    // A Dictionary with string Keys and Values.
    //
    // If a key is found without a matching value, Dictionary.Value is set to the key
    //  (including the dashes).
    // If a value is found without a matching key, Dictionary.Key is set to "--NoKeyN",
    //  where "N" represents sequential numbers.
    public static Dictionary<string,string> Parse(string[] args)
    {
      var parsedArgs = new Dictionary<string,string>();
      int i = 0, n = 0;
      while(i < args.Length)
      {
        // If the first argument in this iteration starts with a dash it's an option.
        if(args[i].StartsWith("-"))
        {
          var key = args[i++];
          var value = key;

          // Check to see if there's a value that goes with this option?
          if((i < args.Length) && (!args[i].StartsWith("-"))) value = args[i++];
          parsedArgs.Add(key, value);
        }

        // If the first argument in this iteration doesn't start with a dash, it's a value
        else
        {
          parsedArgs.Add("--NoKey" + n.ToString(), args[i++]);
          n++;
        }
      }

      return parsedArgs;
    }

    //
    // Method to get an argument from the parsed command-line arguments
    //
    // Parameters:
    // - parsedArgs: The Dictionary object returned from the Parse() method (shown above).
    // - defaultValue: The default string to return if the specified key isn't in parsedArgs.
    // - keys: An array of keys to look for in parsedArgs.
    public static string GetArgument(
      Dictionary<string,string> parsedArgs, string defaultReturn, params string[] keys)
    {
      string retval = null;
      foreach(var key in keys)
        if(parsedArgs.TryGetValue(key, out retval)) break;
      return retval ?? defaultReturn;
    }

    //
    // Method to exit the application with an error.
    public static void ErrorExit(string msg, int code=1)
    {
      Console.WriteLine("\nError");
      Console.WriteLine(msg);
      Environment.Exit(code);
    }
  }

}
```

# 보안 그룹 업데이트
<a name="authorize-ingress"></a>

이 예제에서는 AWS SDK for .NET 를 사용하여 보안 그룹에 규칙을 추가하는 방법을 보여줍니다. 특히 이 예제에서는 지정된 TCP 포트에서 인바운드 트래픽을 허용하는 규칙을 추가합니다. 이 규칙은 예를 들어 EC2 인스턴스에 대한 원격 연결에 사용할 수 있습니다. 애플리케이션은 기존 보안 그룹의 ID, CIDR 형식의 IP 주소(또는 주소 범위), 그리고 선택적으로 TCP 포트 번호를 사용합니다. 그런 다음 지정된 보안 그룹에 인바운드 규칙을 추가합니다.

**참고**  
이 예제를 사용하려면 CIDR 형식의 IP 주소(또는 주소 범위)가 필요합니다. 로컬 컴퓨터의 IP 주소를 얻는 방법은 이 주제의 끝 부분에 있는 **추가 고려 사항**을 참조하세요.

다음 섹션에서는 이 예제의 코드 조각을 제공합니다. [예제의 전체 코드](#authorize-ingress-complete-code)는 그 뒤에 표시되며, 그대로 빌드하고 실행할 수 있습니다.

**Topics**
+ [인바운드 규칙 추가](#authorize-ingress-add-rule)
+ [전체 코드](#authorize-ingress-complete-code)
+ [추가 고려 사항](#authorize-ingress-additional)

## 인바운드 규칙 추가
<a name="authorize-ingress-add-rule"></a>

다음 코드 조각은 특정 IP 주소(또는 범위) 및 TCP 포트에 대한 보안 그룹에 인바운드 규칙을 추가합니다.

[이 주제의 끝 부분에 있는](#authorize-ingress-complete-code) 예제에서는 사용 중인 이 코드 조각을 보여줍니다.

```
    //
    // Method that adds a TCP ingress rule to a security group
    private static async Task AddIngressRule(
      IAmazonEC2 eC2Client, string groupID, string ipAddress, int port)
    {
      // Create an object to hold the request information for the rule.
      // It uses an IpPermission object to hold the IP information for the rule.
      var ingressRequest = new AuthorizeSecurityGroupIngressRequest{
        GroupId = groupID};
      ingressRequest.IpPermissions.Add(new IpPermission{
        IpProtocol = "tcp",
        FromPort = port,
        ToPort = port,
        Ipv4Ranges = new List<IpRange>() { new IpRange { CidrIp = ipAddress } }
      });

      // Create the inbound rule for the security group
      AuthorizeSecurityGroupIngressResponse responseIngress =
        await eC2Client.AuthorizeSecurityGroupIngressAsync(ingressRequest);
      Console.WriteLine($"\nNew RDP rule was written in {groupID} for {ipAddress}.");
      Console.WriteLine($"Result: {responseIngress.HttpStatusCode}");
    }
```

## 전체 코드
<a name="authorize-ingress-complete-code"></a>

이 섹션에는 이 예제에 대한 관련 참조와 전체 코드가 나와 있습니다.

### SDK 레퍼런스
<a name="w2aac19c15c17c13c17c17b5b1"></a>

NuGet 패키지:
+ [AWSSDK.EC2](https://www.nuget.org/packages/AWSSDK.EC2)

프로그래밍 요소:
+ 네임스페이스 [Amazon.EC2](https://docs.aws.amazon.com/sdkfornet/v3/apidocs/items/EC2/NEC2.html)

  클래스 [AmazonEC2Client](https://docs.aws.amazon.com/sdkfornet/v3/apidocs/items/EC2/TEC2Client.html)
+ 네임스페이스 [Amazon.EC2.Model](https://docs.aws.amazon.com/sdkfornet/v3/apidocs/items/EC2/NEC2Model.html)

  클래스 [AuthorizeSecurityGroupIngressRequest](https://docs.aws.amazon.com/sdkfornet/v3/apidocs/items/EC2/TAuthorizeSecurityGroupIngressRequest.html)

  클래스 [AuthorizeSecurityGroupIngressResponse](https://docs.aws.amazon.com/sdkfornet/v3/apidocs/items/EC2/TAuthorizeSecurityGroupIngressResponse.html)

  클래스 [IpPermission](https://docs.aws.amazon.com/sdkfornet/v3/apidocs/items/EC2/TIpPermission.html)

  클래스 [IpRange](https://docs.aws.amazon.com/sdkfornet/v3/apidocs/items/EC2/TIpRange.html)

### 코드
<a name="w2aac19c15c17c13c17c17b7b1"></a>

```
using System;
using System.Threading.Tasks;
using System.Collections.Generic;
using Amazon.EC2;
using Amazon.EC2.Model;

namespace EC2AddRuleForRDP
{
  // = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
  // Class to add a rule that allows inbound traffic on TCP a port
  class Program
  {
    private const int DefaultPort = 3389;

    static async Task Main(string[] args)
    {
      // Parse the command line and show help if necessary
      var parsedArgs = CommandLine.Parse(args);
      if(parsedArgs.Count == 0)
      {
        PrintHelp();
        return;
      }

      // Get the application arguments from the parsed list
      var groupID = CommandLine.GetArgument(parsedArgs, null, "-g", "--group-id");
      var ipAddress = CommandLine.GetArgument(parsedArgs, null, "-i", "--ip-address");
      var portStr = CommandLine.GetArgument(parsedArgs, DefaultPort.ToString(), "-p", "--port");
      if(string.IsNullOrEmpty(ipAddress))
        CommandLine.ErrorExit("\nYou must supply an IP address in CIDR format.");
      if(string.IsNullOrEmpty(groupID) || !groupID.StartsWith("sg-"))
        CommandLine.ErrorExit("\nThe ID for a security group is missing or incorrect.");
      if(int.Parse(portStr) == 0)
        CommandLine.ErrorExit($"\nThe given TCP port number, {portStr}, isn't allowed.");

      // Add a rule to the given security group that allows
      // inbound traffic on a TCP port
      await AddIngressRule(
        new AmazonEC2Client(), groupID, ipAddress, int.Parse(portStr));
    }


    //
    // Method that adds a TCP ingress rule to a security group
    private static async Task AddIngressRule(
      IAmazonEC2 eC2Client, string groupID, string ipAddress, int port)
    {
      // Create an object to hold the request information for the rule.
      // It uses an IpPermission object to hold the IP information for the rule.
      var ingressRequest = new AuthorizeSecurityGroupIngressRequest{
        GroupId = groupID};
      ingressRequest.IpPermissions.Add(new IpPermission{
        IpProtocol = "tcp",
        FromPort = port,
        ToPort = port,
        Ipv4Ranges = new List<IpRange>() { new IpRange { CidrIp = ipAddress } }
      });

      // Create the inbound rule for the security group
      AuthorizeSecurityGroupIngressResponse responseIngress =
        await eC2Client.AuthorizeSecurityGroupIngressAsync(ingressRequest);
      Console.WriteLine($"\nNew RDP rule was written in {groupID} for {ipAddress}.");
      Console.WriteLine($"Result: {responseIngress.HttpStatusCode}");
    }


    //
    // Command-line help
    private static void PrintHelp()
    {
      Console.WriteLine(
        "\nUsage: EC2AddRuleForRDP -g <group-id> -i <ip-address> [-p <port>]" +
        "\n  -g, --group-id: The ID of the security group to which you want to add the inbound rule." +
        "\n  -i, --ip-address: An IP address or address range in CIDR format." +
        "\n  -p, --port: The TCP port number. Defaults to 3389.");
    }
  }


  // = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
  // Class that represents a command line on the console or terminal.
  // (This is the same for all examples. When you have seen it once, you can ignore it.)
  static class CommandLine
  {
    //
    // Method to parse a command line of the form: "--key value" or "-k value".
    //
    // Parameters:
    // - args: The command-line arguments passed into the application by the system.
    //
    // Returns:
    // A Dictionary with string Keys and Values.
    //
    // If a key is found without a matching value, Dictionary.Value is set to the key
    //  (including the dashes).
    // If a value is found without a matching key, Dictionary.Key is set to "--NoKeyN",
    //  where "N" represents sequential numbers.
    public static Dictionary<string,string> Parse(string[] args)
    {
      var parsedArgs = new Dictionary<string,string>();
      int i = 0, n = 0;
      while(i < args.Length)
      {
        // If the first argument in this iteration starts with a dash it's an option.
        if(args[i].StartsWith("-"))
        {
          var key = args[i++];
          var value = key;

          // Check to see if there's a value that goes with this option?
          if((i < args.Length) && (!args[i].StartsWith("-"))) value = args[i++];
          parsedArgs.Add(key, value);
        }

        // If the first argument in this iteration doesn't start with a dash, it's a value
        else
        {
          parsedArgs.Add("--NoKey" + n.ToString(), args[i++]);
          n++;
        }
      }

      return parsedArgs;
    }

    //
    // Method to get an argument from the parsed command-line arguments
    //
    // Parameters:
    // - parsedArgs: The Dictionary object returned from the Parse() method (shown above).
    // - defaultValue: The default string to return if the specified key isn't in parsedArgs.
    // - keys: An array of keys to look for in parsedArgs.
    public static string GetArgument(
      Dictionary<string,string> parsedArgs, string defaultReturn, params string[] keys)
    {
      string retval = null;
      foreach(var key in keys)
        if(parsedArgs.TryGetValue(key, out retval)) break;
      return retval ?? defaultReturn;
    }

    //
    // Method to exit the application with an error.
    public static void ErrorExit(string msg, int code=1)
    {
      Console.WriteLine("\nError");
      Console.WriteLine(msg);
      Environment.Exit(code);
    }
  }

}
```

## 추가 고려 사항
<a name="authorize-ingress-additional"></a>
+ 포트 번호를 제공하지 않는 경우 애플리케이션은 기본적으로 포트 3389로 설정됩니다. 이 포트는 Windows RDP용 포트로, Windows를 실행하는 EC2 인스턴스에 연결할 수 있습니다. Linux를 실행하는 EC2 인스턴스를 시작하는 경우에는 그 대신에 TCP 포트 22(SSH)를 사용하세요.
+ 참고로 이 예제에서는 `IpProtocol`이 “tcp”로 설정되어 있습니다. `IpProtocol`의 값은 [IpPermission](https://docs.aws.amazon.com/sdkfornet/v3/apidocs/items/EC2/TIpPermission.html) 클래스의 `IpProtocol` 속성에 대한 설명에서 찾을 수 있습니다.
+ 이 예를 사용할 때 로컬 컴퓨터의 IP 주소가 필요할 수 있습니다. 주소를 확인할 수 있는 몇 가지 방법은 다음과 같습니다.
  + EC2 인스턴스에 연결할 로컬 컴퓨터에 정적 퍼블릭 IP 주소가 있는 경우 서비스를 사용하여 해당 주소를 가져올 수 있습니다. 이러한 서비스 중 하나가 [http://checkip.amazonaws.com/](http://checkip.amazonaws.com/)입니다. 인바운드 트래픽 권한 부여에 대한 자세한 내용은 [Amazon EC2 사용 설명서](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/)의 [보안 그룹에 규칙 추가](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/working-with-security-groups.html#adding-security-group-rule) 및 [다양한 사용 사례에 대한 보안 그룹 규칙](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/security-group-rules-reference.html)을 참조하세요.
  + 로컬 컴퓨터의 IP 주소를 얻는 또 다른 방법은 [Amazon EC2 콘솔](https://console.aws.amazon.com/ec2/v2/home#SecurityGroups)을 사용하는 것입니다.

    보안 그룹 중 하나를 선택하고 **인바운드 규칙** 탭을 선택한 다음 **인바운드 규칙 편집**을 선택합니다. 인바운드 규칙에서 **소스** 열의 드롭다운 메뉴를 열고 **내 IP**를 선택하면 로컬 컴퓨터의 IP 주소가 CIDR 형식으로 표시됩니다. 작업을 **취소**해야 합니다.
+ [Amazon EC2 콘솔](https://console.aws.amazon.com/ec2/v2/home#SecurityGroups)에서 보안 그룹 목록을 검사하여 이 예제의 결과를 확인할 수 있습니다.

# Amazon EC2 키 페어로 작업
<a name="key-pairs"></a>

Amazon EC2는 퍼블릭 키 암호화 기법을 사용하여 로그인 정보를 암호화 및 해독합니다. 퍼블릭 키 암호화 기법에서는 퍼블릭 키를 사용하여 데이터를 암호화한 후 수신자가 개인 키를 사용하여 해당 데이터를 해독합니다. 퍼블릭 키와 프라이빗 키를 키 페어라고 합니다. EC2 인스턴스에 로그인하려면 시작 시 키 페어를 지정한 다음 연결할 때 키 페어의 프라이빗 키를 제공해야 합니다.

EC2 인스턴스를 시작할 때 해당 인스턴스에 대해 키 페어를 생성하거나 다른 인스턴스를 시작할 때 이미 사용한 키 페어를 사용할 수 있습니다. Amazon EC2 키 페어에 대한 자세한 내용은 [Amazon EC2 사용 설명서](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/)의 [Amazon EC2 키 페어 작업](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/ec2-key-pairs.html)을 참조하세요.

API 및 사전 조건에 대한 자세한 내용은 상위 섹션([Amazon EC2 작업](ec2-apis-intro.md))을 참조하세요.

**Topics**
+ [키 페어 생성 및 표시](create-save-key-pair.md)
+ [키 페어 삭제](delete-key-pairs.md)

# 키 페어 생성 및 표시
<a name="create-save-key-pair"></a>

이 예제에서는를 사용하여 키 페어를 AWS SDK for .NET 생성하는 방법을 보여줍니다. 애플리케이션은 새 키 페어의 이름과 PEM 파일(“.pem” 확장명)의 이름을 사용합니다. 키 페어를 생성하고 PEM 파일에 프라이빗 키를 쓴 다음 사용 가능한 모든 키 페어를 표시합니다. 명령줄 인수를 제공하지 않으면 애플리케이션은 사용 가능한 모든 키 페어만 표시합니다.

다음 섹션에서는 이 예제의 코드 조각을 제공합니다. [예제의 전체 코드](#create-save-key-pair-complete-code)는 그 뒤에 표시되며, 그대로 빌드하고 실행할 수 있습니다.

**Topics**
+ [키 페어 생성](#create-save-key-pair-create)
+ [사용 가능한 키 페어 표시](#create-save-key-pair-display)
+ [전체 코드](#create-save-key-pair-complete-code)
+ [추가 고려 사항](#create-save-key-pair-additional)

## 키 페어 생성
<a name="create-save-key-pair-create"></a>

다음 코드 조각은 키 페어를 생성한 다음 지정된 PEM 파일에 프라이빗 키를 저장합니다.

[이 주제의 끝 부분에 있는](#create-save-key-pair-complete-code) 예제에서는 사용 중인 이 코드 조각을 보여줍니다.

```
    //
    // Method to create a key pair and save the key material in a PEM file
    private static async Task CreateKeyPair(
      IAmazonEC2 ec2Client, string keyPairName, string pemFileName)
    {
      // Create the key pair
      CreateKeyPairResponse response =
        await ec2Client.CreateKeyPairAsync(new CreateKeyPairRequest{
          KeyName = keyPairName
        });
      Console.WriteLine($"\nCreated new key pair: {response.KeyPair.KeyName}");

      // Save the private key in a PEM file
      using (var s = new FileStream(pemFileName, FileMode.Create))
      using (var writer = new StreamWriter(s))
      {
        writer.WriteLine(response.KeyPair.KeyMaterial);
      }
    }
```

## 사용 가능한 키 페어 표시
<a name="create-save-key-pair-display"></a>

다음 코드 조각은 사용 가능한 키 페어의 목록을 표시합니다.

[이 주제의 끝 부분에 있는](#create-save-key-pair-complete-code) 예제에서는 사용 중인 이 코드 조각을 보여줍니다.

```
    //
    // Method to show the key pairs that are available
    private static async Task EnumerateKeyPairs(IAmazonEC2 ec2Client)
    {
      DescribeKeyPairsResponse response = await ec2Client.DescribeKeyPairsAsync();
      Console.WriteLine("Available key pairs:");
      foreach (KeyPairInfo item in response.KeyPairs)
        Console.WriteLine($"  {item.KeyName}");
    }
```

## 전체 코드
<a name="create-save-key-pair-complete-code"></a>

이 섹션에는 이 예제에 대한 관련 참조와 전체 코드가 나와 있습니다.

### SDK 레퍼런스
<a name="w2aac19c15c17c15c11c19b5b1"></a>

NuGet 패키지:
+ [AWSSDK.EC2](https://www.nuget.org/packages/AWSSDK.EC2)

프로그래밍 요소:
+ 네임스페이스 [Amazon.EC2](https://docs.aws.amazon.com/sdkfornet/v3/apidocs/items/EC2/NEC2.html)

  클래스 [AmazonEC2Client](https://docs.aws.amazon.com/sdkfornet/v3/apidocs/items/EC2/TEC2Client.html)
+ 네임스페이스 [Amazon.EC2.Model](https://docs.aws.amazon.com/sdkfornet/v3/apidocs/items/EC2/NEC2Model.html)

  클래스 [CreateKeyPairRequest](https://docs.aws.amazon.com/sdkfornet/v3/apidocs/items/EC2/TCreateKeyPairRequest.html)

  클래스 [CreateKeyPairResponse](https://docs.aws.amazon.com/sdkfornet/v3/apidocs/items/EC2/TCreateKeyPairResponse.html)

  클래스 [DescribeKeyPairsResponse](https://docs.aws.amazon.com/sdkfornet/v3/apidocs/items/EC2/TDescribeKeyPairsResponse.html)

  클래스 [KeyPairInfo](https://docs.aws.amazon.com/sdkfornet/v3/apidocs/items/EC2/TKeyPairInfo.html)

### 코드
<a name="w2aac19c15c17c15c11c19b7b1"></a>

```
using System;
using System.Threading.Tasks;
using System.IO;
using Amazon.EC2;
using Amazon.EC2.Model;
using System.Collections.Generic;

namespace EC2CreateKeyPair
{
  // = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
  // Class to create and store a key pair
  class Program
  {
    static async Task Main(string[] args)
    {
      // Create the EC2 client
      var ec2Client = new AmazonEC2Client();

      // Parse the command line and show help if necessary
      var parsedArgs = CommandLine.Parse(args);
      if(parsedArgs.Count == 0)
      {
        // In the case of no command-line arguments,
        // just show help and the existing key pairs
        PrintHelp();
        Console.WriteLine("\nNo arguments specified.");
        Console.Write(
          "Do you want to see a list of the existing key pairs? ((y) or n): ");
        string response = Console.ReadLine();
        if((string.IsNullOrEmpty(response)) || (response.ToLower() == "y"))
          await EnumerateKeyPairs(ec2Client);
        return;
      }

      // Get the application arguments from the parsed list
      string keyPairName =
        CommandLine.GetArgument(parsedArgs, null, "-k", "--keypair-name");
      string pemFileName =
        CommandLine.GetArgument(parsedArgs, null, "-p", "--pem-filename");
      if(string.IsNullOrEmpty(keyPairName))
        CommandLine.ErrorExit("\nNo key pair name specified." +
          "\nRun the command with no arguments to see help.");
      if(string.IsNullOrEmpty(pemFileName) || !pemFileName.EndsWith(".pem"))
        CommandLine.ErrorExit("\nThe PEM filename is missing or incorrect." +
          "\nRun the command with no arguments to see help.");

      // Create the key pair
      await CreateKeyPair(ec2Client, keyPairName, pemFileName);
      await EnumerateKeyPairs(ec2Client);
    }


    //
    // Method to create a key pair and save the key material in a PEM file
    private static async Task CreateKeyPair(
      IAmazonEC2 ec2Client, string keyPairName, string pemFileName)
    {
      // Create the key pair
      CreateKeyPairResponse response =
        await ec2Client.CreateKeyPairAsync(new CreateKeyPairRequest{
          KeyName = keyPairName
        });
      Console.WriteLine($"\nCreated new key pair: {response.KeyPair.KeyName}");

      // Save the private key in a PEM file
      using (var s = new FileStream(pemFileName, FileMode.Create))
      using (var writer = new StreamWriter(s))
      {
        writer.WriteLine(response.KeyPair.KeyMaterial);
      }
    }


    //
    // Method to show the key pairs that are available
    private static async Task EnumerateKeyPairs(IAmazonEC2 ec2Client)
    {
      DescribeKeyPairsResponse response = await ec2Client.DescribeKeyPairsAsync();
      Console.WriteLine("Available key pairs:");
      foreach (KeyPairInfo item in response.KeyPairs)
        Console.WriteLine($"  {item.KeyName}");
    }


    //
    // Command-line help
    private static void PrintHelp()
    {
      Console.WriteLine(
        "\nUsage: EC2CreateKeyPair -k <keypair-name> -p <pem-filename>" +
        "\n  -k, --keypair-name: The name you want to assign to the key pair." +
        "\n  -p, --pem-filename: The name of the PEM file to create, with a \".pem\" extension.");
    }
  }


  // = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
  // Class that represents a command line on the console or terminal.
  // (This is the same for all examples. When you have seen it once, you can ignore it.)
  static class CommandLine
  {
    //
    // Method to parse a command line of the form: "--key value" or "-k value".
    //
    // Parameters:
    // - args: The command-line arguments passed into the application by the system.
    //
    // Returns:
    // A Dictionary with string Keys and Values.
    //
    // If a key is found without a matching value, Dictionary.Value is set to the key
    //  (including the dashes).
    // If a value is found without a matching key, Dictionary.Key is set to "--NoKeyN",
    //  where "N" represents sequential numbers.
    public static Dictionary<string,string> Parse(string[] args)
    {
      var parsedArgs = new Dictionary<string,string>();
      int i = 0, n = 0;
      while(i < args.Length)
      {
        // If the first argument in this iteration starts with a dash it's an option.
        if(args[i].StartsWith("-"))
        {
          var key = args[i++];
          var value = key;

          // Check to see if there's a value that goes with this option?
          if((i < args.Length) && (!args[i].StartsWith("-"))) value = args[i++];
          parsedArgs.Add(key, value);
        }

        // If the first argument in this iteration doesn't start with a dash, it's a value
        else
        {
          parsedArgs.Add("--NoKey" + n.ToString(), args[i++]);
          n++;
        }
      }

      return parsedArgs;
    }

    //
    // Method to get an argument from the parsed command-line arguments
    //
    // Parameters:
    // - parsedArgs: The Dictionary object returned from the Parse() method (shown above).
    // - defaultValue: The default string to return if the specified key isn't in parsedArgs.
    // - keys: An array of keys to look for in parsedArgs.
    public static string GetArgument(
      Dictionary<string,string> parsedArgs, string defaultReturn, params string[] keys)
    {
      string retval = null;
      foreach(var key in keys)
        if(parsedArgs.TryGetValue(key, out retval)) break;
      return retval ?? defaultReturn;
    }

    //
    // Method to exit the application with an error.
    public static void ErrorExit(string msg, int code=1)
    {
      Console.WriteLine("\nError");
      Console.WriteLine(msg);
      Environment.Exit(code);
    }
  }

}
```

## 추가 고려 사항
<a name="create-save-key-pair-additional"></a>
+ 예제를 실행한 후 [Amazon EC2 콘솔](https://console.aws.amazon.com/ec2/#KeyPairs)에서 새 키 페어를 확인할 수 있습니다.
+ 나중에 프라이빗 키를 검색할 수 없으므로 키 페어를 생성할 때는 반환되는 프라이빗 키를 저장해야 합니다.

# 키 페어 삭제
<a name="delete-key-pairs"></a>

이 예제에서는를 사용하여 키 페어 AWS SDK for .NET 를 삭제하는 방법을 보여줍니다. 이 애플리케이션은 키 페어 이름을 사용합니다. 키 페어를 삭제한 다음 사용 가능한 모든 키 페어를 표시합니다. 명령줄 인수를 제공하지 않으면 애플리케이션은 사용 가능한 모든 키 페어만 표시합니다.

다음 섹션에서는 이 예제의 코드 조각을 제공합니다. [예제의 전체 코드](#delete-key-pairs-complete-code)는 그 뒤에 표시되며, 그대로 빌드하고 실행할 수 있습니다.

**Topics**
+ [키 페어 삭제](#delete-key-pairs-create)
+ [사용 가능한 키 페어 표시](#delete-key-pairs-display)
+ [전체 코드](#delete-key-pairs-complete-code)

## 키 페어 삭제
<a name="delete-key-pairs-create"></a>

다음 코드 조각은 키 페어를 삭제합니다.

[이 주제의 끝 부분에 있는](#delete-key-pairs-complete-code) 예제에서는 사용 중인 이 코드 조각을 보여줍니다.

```
    //
    // Method to delete a key pair
    private static async Task DeleteKeyPair(IAmazonEC2 ec2Client, string keyName)
    {
      await ec2Client.DeleteKeyPairAsync(new DeleteKeyPairRequest{
        KeyName = keyName});
      Console.WriteLine($"\nKey pair {keyName} has been deleted (if it existed).");
    }
```

## 사용 가능한 키 페어 표시
<a name="delete-key-pairs-display"></a>

다음 코드 조각은 사용 가능한 키 페어의 목록을 표시합니다.

[이 주제의 끝 부분에 있는](#delete-key-pairs-complete-code) 예제에서는 사용 중인 이 코드 조각을 보여줍니다.

```
    //
    // Method to show the key pairs that are available
    private static async Task EnumerateKeyPairs(IAmazonEC2 ec2Client)
    {
      DescribeKeyPairsResponse response = await ec2Client.DescribeKeyPairsAsync();
      Console.WriteLine("Available key pairs:");
      foreach (KeyPairInfo item in response.KeyPairs)
        Console.WriteLine($"  {item.KeyName}");
    }
```

## 전체 코드
<a name="delete-key-pairs-complete-code"></a>

이 섹션에는 이 예제에 대한 관련 참조와 전체 코드가 나와 있습니다.

### SDK 레퍼런스
<a name="w2aac19c15c17c15c13c19b5b1"></a>

NuGet 패키지:
+ [AWSSDK.EC2](https://www.nuget.org/packages/AWSSDK.EC2)

프로그래밍 요소:
+ 네임스페이스 [Amazon.EC2](https://docs.aws.amazon.com/sdkfornet/v3/apidocs/items/EC2/NEC2.html)

  클래스 [AmazonEC2Client](https://docs.aws.amazon.com/sdkfornet/v3/apidocs/items/EC2/TEC2Client.html)
+ 네임스페이스 [Amazon.EC2.Model](https://docs.aws.amazon.com/sdkfornet/v3/apidocs/items/EC2/NEC2Model.html)

  클래스 [https://docs.aws.amazon.com/sdkfornet/v3/apidocs/](https://docs.aws.amazon.com/sdkfornet/v3/apidocs/)

  클래스 [DescribeKeyPairsResponse](https://docs.aws.amazon.com/sdkfornet/v3/apidocs/items/EC2/TDescribeKeyPairsResponse.html)

  클래스 [KeyPairInfo](https://docs.aws.amazon.com/sdkfornet/v3/apidocs/items/EC2/TKeyPairInfo.html)

### 코드
<a name="w2aac19c15c17c15c13c19b7b1"></a>

```
using System;
using System.Threading.Tasks;
using Amazon.EC2;
using Amazon.EC2.Model;

namespace EC2DeleteKeyPair
{
  class Program
  {
    static async Task Main(string[] args)
    {
      // Create the EC2 client
      var ec2Client = new AmazonEC2Client();

      if(args.Length == 1)
      {
        // Delete a key pair (if it exists)
        await DeleteKeyPair(ec2Client, args[0]);

        // Display the key pairs that are left
        await EnumerateKeyPairs(ec2Client);
      }
      else
      {
        Console.WriteLine("\nUsage: EC2DeleteKeyPair keypair-name");
        Console.WriteLine("  keypair-name - The name of the key pair you want to delete.");
        Console.WriteLine("\nNo arguments specified.");
        Console.Write(
          "Do you want to see a list of the existing key pairs? ((y) or n): ");
        string response = Console.ReadLine();
        if((string.IsNullOrEmpty(response)) || (response.ToLower() == "y"))
          await EnumerateKeyPairs(ec2Client);
      }
    }


    //
    // Method to delete a key pair
    private static async Task DeleteKeyPair(IAmazonEC2 ec2Client, string keyName)
    {
      await ec2Client.DeleteKeyPairAsync(new DeleteKeyPairRequest{
        KeyName = keyName});
      Console.WriteLine($"\nKey pair {keyName} has been deleted (if it existed).");
    }


    //
    // Method to show the key pairs that are available
    private static async Task EnumerateKeyPairs(IAmazonEC2 ec2Client)
    {
      DescribeKeyPairsResponse response = await ec2Client.DescribeKeyPairsAsync();
      Console.WriteLine("Available key pairs:");
      foreach (KeyPairInfo item in response.KeyPairs)
        Console.WriteLine($"  {item.KeyName}");
    }
  }
}
```

# Amazon EC2 리전 및 가용 영역 표시
<a name="using-regions-and-availability-zones"></a>

Amazon EC2는 전 세계의 여러 곳에서 호스팅되고 있습니다. 해당 위치는 리전 및 가용 영역으로 구성됩니다. 각 리전은 지리적 개별 영역이며, 가용 영역이라고 알려진 여러 개의 격리된 위치가 있습니다.

리전 및 가용 영역에 대한 자세한 내용은 [Amazon EC2 사용 설명서](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/)의 [리전 및 가용 영역](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/using-regions-availability-zones.html)을 참조하세요.

이 예제에서는 AWS SDK for .NET 를 사용하여 EC2 클라이언트와 관련된 리전 및 가용 영역에 대한 세부 정보를 가져오는 방법을 보여줍니다. 애플리케이션은 EC2 클라이언트가 사용할 수 있는 리전 및 가용 영역 목록을 표시합니다.

## SDK 레퍼런스
<a name="w2aac19c15c17c17b9b1"></a>

NuGet 패키지:
+ [AWSSDK.EC2](https://www.nuget.org/packages/AWSSDK.EC2)

프로그래밍 요소:
+ 네임스페이스 [Amazon.EC2](https://docs.aws.amazon.com/sdkfornet/v3/apidocs/items/EC2/NEC2.html)

  클래스 [AmazonEC2Client](https://docs.aws.amazon.com/sdkfornet/v3/apidocs/items/EC2/TEC2Client.html)
+ 네임스페이스 [Amazon.EC2.Model](https://docs.aws.amazon.com/sdkfornet/v3/apidocs/items/EC2/NEC2Model.html)

  클래스 [DescribeAvailabilityZonesResponse](https://docs.aws.amazon.com/sdkfornet/v3/apidocs/items/EC2/TDescribeAvailabilityZonesResponse.html)

  클래스 [DescribeRegionsResponse](https://docs.aws.amazon.com/sdkfornet/v3/apidocs/items/EC2/TDescribeRegionsResponse.html)

  클래스 [AvailabilityZone](https://docs.aws.amazon.com/sdkfornet/v3/apidocs/items/EC2/TAvailabilityZone.html)

  클래스 [Region](https://docs.aws.amazon.com/sdkfornet/v3/apidocs/items/EC2/TRegion.html)

```
using System;
using System.Threading.Tasks;
using Amazon.EC2;
using Amazon.EC2.Model;

namespace EC2RegionsAndZones
{
  class Program
  {
    static async Task Main(string[] args)
    {
      Console.WriteLine(
        "Finding the Regions and Availability Zones available to an EC2 client...");

      // Create the EC2 client
      var ec2Client = new AmazonEC2Client();

      // Display the Regions and Availability Zones
      await DescribeRegions(ec2Client);
      await DescribeAvailabilityZones(ec2Client);
    }


    //
    // Method to display Regions
    private static async Task DescribeRegions(IAmazonEC2 ec2Client)
    {
      Console.WriteLine("\nRegions that are enabled for the EC2 client:");
      DescribeRegionsResponse response = await ec2Client.DescribeRegionsAsync();
      foreach (Region region in response.Regions)
        Console.WriteLine(region.RegionName);
    }


    //
    // Method to display Availability Zones
    private static async Task DescribeAvailabilityZones(IAmazonEC2 ec2Client)
    {
      Console.WriteLine("\nAvailability Zones for the EC2 client's region:");
      DescribeAvailabilityZonesResponse response =
        await ec2Client.DescribeAvailabilityZonesAsync();
      foreach (AvailabilityZone az in response.AvailabilityZones)
        Console.WriteLine(az.ZoneName);
    }
  }
}
```

# Amazon EC2 인스턴스 작업
<a name="how-to-ec2"></a>

 AWS SDK for .NET 를 사용하여 생성, 시작 및 종료와 같은 작업을 통해 Amazon EC2 인스턴스를 제어할 수 있습니다. 이 섹션의 주제에서는 이를 수행하는 방법의 몇 가지 예제를 제공합니다. EC2 인스턴스에 대한 자세한 내용은 [Amazon EC2 사용 설명서](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/)의 [Amazon EC2 인스턴스](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/Instances.html)를 참조하세요.

API 및 사전 조건에 대한 자세한 내용은 상위 섹션([Amazon EC2 작업](ec2-apis-intro.md))을 참조하세요.

**Topics**
+ [EC2 인스턴스 시작](run-instance.md)
+ [EC2 인스턴스 종료](terminate-instance.md)

# Amazon EC2 인스턴스 시작
<a name="run-instance"></a>

이 예제에서는 AWS SDK for .NET 를 사용하여 동일한 Amazon Machine Image(AMI)에서 하나 이상의 동일하게 구성된 Amazon EC2 인스턴스를 시작하는 방법을 보여줍니다. 애플리케이션은 사용자가 제공한 [여러 입력](#run-instance-gather)을 사용하여 EC2 인스턴스를 시작한 다음 “Pending” 상태가 아닐 때까지 인스턴스를 모니터링합니다.

[(선택 사항) 인스턴스에 연결](#connect-to-instance)에 설명된 대로 EC2 인스턴스가 실행될 때 EC2 인스턴스에 원격으로 연결할 수 있습니다.

**주의**  
EC2-Classic은 2022년 8월 15일에 사용 중지되었습니다. EC2-Classic에서 VPC로 마이그레이션하는 것이 좋습니다. 자세한 내용은 블로그 게시물 [EC2-Classic 네트워킹은 사용 중지 중입니다 - 준비 방법은 다음과 같습니다](https://aws.amazon.com/blogs/aws/ec2-classic-is-retiring-heres-how-to-prepare/)를 참조하세요.

다음 섹션에서는 이 예제의 코드 조각과 기타 정보를 제공합니다. [예제의 전체 코드](#run-instance-complete-code)는 코드 조각 뒤에 표시되며, 그대로 빌드하고 실행할 수 있습니다.

**Topics**
+ [필요한 내용 수집](#run-instance-gather)
+ [인스턴스 시작](#run-instance-launch)
+ [인스턴스 모니터링](#run-instance-monitor)
+ [전체 코드](#run-instance-complete-code)
+ [추가 고려 사항](#run-instance-additional)
+ [(선택 사항) 인스턴스에 연결](#connect-to-instance)
+ [정리](#run-instance-cleanup)

## 필요한 내용 수집
<a name="run-instance-gather"></a>

EC2 인스턴스를 시작하려면 몇 가지 사항이 필요합니다.
+ 인스턴스가 시작될 [VPC](https://docs.aws.amazon.com/vpc/latest/userguide/)입니다. Windows 인스턴스이고 RDP를 통해 연결하려는 경우 VPC에 인터넷 게이트웨이가 연결되어 있어야 할 가능성이 높으며 라우팅 테이블의 인터넷 게이트웨이에 대한 항목도 있어야 합니다. 자세한 내용은 *Amazon VPC 사용 설명서*의 [인터넷 게이트웨이](https://docs.aws.amazon.com/vpc/latest/userguide/VPC_Internet_Gateway.html)를 참조하세요.
+ 인스턴스가 시작될 VPC의 기존 서브넷 ID입니다. [Amazon VPC 콘솔](https://console.aws.amazon.com/vpc/home#subnets)에 로그인하는 것도 이를 찾거나 생성하는 쉬운 방법이지만, [CreateSubnetAsync](https://docs.aws.amazon.com/sdkfornet/v3/apidocs/items/EC2/MEC2CreateSubnetAsyncCreateSubnetRequestCancellationToken.html) 및 [DescribeSubnetsAsync](https://docs.aws.amazon.com/sdkfornet/v3/apidocs/items/EC2/MEC2DescribeSubnetsAsyncDescribeSubnetsRequestCancellationToken.html) 메서드를 사용하여 프로그래밍 방식으로 가져올 수도 있습니다.
**참고**  
이 파라미터를 제공하지 않으면 계정의 기본 VPC에서 새 인스턴스가 시작됩니다.
+ 인스턴스가 시작될 VPC에 속하는 기존 보안 그룹의 ID입니다. 자세한 내용은 [Amazon EC2 에서 보안 그룹 작업](security-groups.md) 단원을 참조하십시오.
+ 새 인스턴스에 연결하려면 앞서 언급한 보안 그룹에 포트 22에서의 SSH 트래픽(Linux 인스턴스) 또는 포트 3389에서의 RDP 트래픽(Windows 인스턴스)을 허용하는 적절한 인바운드 규칙이 있어야 합니다. 이를 수행하는 방법은 해당 주제의 끝 부분에 있는 [추가 고려 사항](authorize-ingress.md#authorize-ingress-additional)을 포함하여 [보안 그룹 업데이트](authorize-ingress.md)을 참조하세요.
+ 인스턴스를 생성하는 데 사용되는 Amazon Machine Image(AMI)입니다. AMI에 대한 자세한 내용은 [Amazon EC2 사용 설명서](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/)의 [Amazon Machine Image(AMI)](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/AMIs.html)를 참조하세요. 특히 [AMI 찾기](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/finding-an-ami.html) 및 [공유 AMI](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/sharing-amis.html)를 참조하세요.
+ 새 인스턴스에 연결하는 데 사용되는 기존 EC2 키 페어의 이름입니다. 자세한 내용은 [Amazon EC2 키 페어로 작업](key-pairs.md) 단원을 참조하십시오.
+ 앞서 언급한 EC2 키 페어의 프라이빗 키가 포함된 PEM 파일의 이름입니다. PEM 파일은 인스턴스에 [원격으로 연결](#connect-to-instance)할 때 사용됩니다.

## 인스턴스 시작
<a name="run-instance-launch"></a>

다음 코드 조각은 EC2 인스턴스를 시작합니다.

[이 주제의 끝 부분에 있는](#run-instance-complete-code) 예제에서는 사용 중인 이 코드 조각을 보여줍니다.

```
    //
    // Method to launch the instances
    // Returns a list with the launched instance IDs
    private static async Task<List<string>> LaunchInstances(
      IAmazonEC2 ec2Client, RunInstancesRequest requestLaunch)
    {
      var instanceIds = new List<string>();
      RunInstancesResponse responseLaunch =
        await ec2Client.RunInstancesAsync(requestLaunch);

      Console.WriteLine("\nNew instances have been created.");
      foreach (Instance item in responseLaunch.Reservation.Instances)
      {
        instanceIds.Add(item.InstanceId);
        Console.WriteLine($"  New instance: {item.InstanceId}");
      }

      return instanceIds;
    }
```

## 인스턴스 모니터링
<a name="run-instance-monitor"></a>

다음 코드 조각은 “Pending” 상태가 아닐 때까지 인스턴스를 모니터링합니다.

[이 주제의 끝 부분에 있는](#run-instance-complete-code) 예제에서는 사용 중인 이 코드 조각을 보여줍니다.

`Instance.State.Code` 속성의 유효한 값은 [InstanceState](https://docs.aws.amazon.com/sdkfornet/v3/apidocs/items/EC2/TInstanceState.html) 클래스를 참조하세요.

```
    //
    // Method to wait until the instances are running (or at least not pending)
    private static async Task CheckState(IAmazonEC2 ec2Client, List<string> instanceIds)
    {
      Console.WriteLine(
        "\nWaiting for the instances to start." +
        "\nPress any key to stop waiting. (Response might be slightly delayed.)");

      int numberRunning;
      DescribeInstancesResponse responseDescribe;
      var requestDescribe = new DescribeInstancesRequest{
        InstanceIds = instanceIds};

      // Check every couple of seconds
      int wait = 2000;
      while(true)
      {
        // Get and check the status for each of the instances to see if it's past pending.
        // Once all instances are past pending, break out.
        // (For this example, we are assuming that there is only one reservation.)
        Console.Write(".");
        numberRunning = 0;
        responseDescribe = await ec2Client.DescribeInstancesAsync(requestDescribe);
        foreach(Instance i in responseDescribe.Reservations[0].Instances)
        {
          // Check the lower byte of State.Code property
          // Code == 0 is the pending state
          if((i.State.Code & 255) > 0) numberRunning++;
        }
        if(numberRunning == responseDescribe.Reservations[0].Instances.Count)
          break;

        // Wait a bit and try again (unless the user wants to stop waiting)
        Thread.Sleep(wait);
        if(Console.KeyAvailable)
          break;
      }

      Console.WriteLine("\nNo more instances are pending.");
      foreach(Instance i in responseDescribe.Reservations[0].Instances)
      {
        Console.WriteLine($"For {i.InstanceId}:");
        Console.WriteLine($"  VPC ID: {i.VpcId}");
        Console.WriteLine($"  Instance state: {i.State.Name}");
        Console.WriteLine($"  Public IP address: {i.PublicIpAddress}");
        Console.WriteLine($"  Public DNS name: {i.PublicDnsName}");
        Console.WriteLine($"  Key pair name: {i.KeyName}");
      }
    }
```

## 전체 코드
<a name="run-instance-complete-code"></a>

이 섹션에는 이 예제에 대한 관련 참조와 전체 코드가 나와 있습니다.

### SDK 레퍼런스
<a name="w2aac19c15c17c19b9c27b5b1"></a>

NuGet 패키지:
+ [AWSSDK.EC2](https://www.nuget.org/packages/AWSSDK.EC2)

프로그래밍 요소:
+ 네임스페이스 [Amazon.EC2](https://docs.aws.amazon.com/sdkfornet/v3/apidocs/items/EC2/NEC2.html)

  클래스 [AmazonEC2Client](https://docs.aws.amazon.com/sdkfornet/v3/apidocs/items/EC2/TEC2Client.html)

  클래스 [InstanceType](https://docs.aws.amazon.com/sdkfornet/v3/apidocs/items/EC2/TInstanceType.html)
+ 네임스페이스 [Amazon.EC2.Model](https://docs.aws.amazon.com/sdkfornet/v3/apidocs/items/EC2/NEC2Model.html)

  클래스 [DescribeInstancesRequest](https://docs.aws.amazon.com/sdkfornet/v3/apidocs/items/EC2/TDescribeInstancesRequest.html)

  클래스 [DescribeInstancesResponse](https://docs.aws.amazon.com/sdkfornet/v3/apidocs/items/EC2/TDescribeInstancesResponse.html)

  클래스 [Instance](https://docs.aws.amazon.com/sdkfornet/v3/apidocs/items/EC2/TInstance.html)

  클래스 [InstanceNetworkInterfaceSpecification](https://docs.aws.amazon.com/sdkfornet/v3/apidocs/items/EC2/TInstanceNetworkInterfaceSpecification.html)

  클래스 [RunInstancesRequest](https://docs.aws.amazon.com/sdkfornet/v3/apidocs/items/EC2/TRunInstancesRequest.html)

  클래스 [RunInstancesResponse](https://docs.aws.amazon.com/sdkfornet/v3/apidocs/items/EC2/TRunInstancesResponse.html)

### 코드
<a name="w2aac19c15c17c19b9c27b7b1"></a>

```
using System;
using System.Threading;
using System.Threading.Tasks;
using System.Collections.Generic;
using Amazon.EC2;
using Amazon.EC2.Model;

namespace EC2LaunchInstance
{
  // = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
  // Class to launch an EC2 instance
  class Program
  {
    static async Task Main(string[] args)
    {
      // Parse the command line and show help if necessary
      var parsedArgs = CommandLine.Parse(args);
      if(parsedArgs.Count == 0)
      {
        PrintHelp();
        return;
      }

      // Get the application arguments from the parsed list
      string groupID =
        CommandLine.GetArgument(parsedArgs, null, "-g", "--group-id");
      string ami =
        CommandLine.GetArgument(parsedArgs, null, "-a", "--ami-id");
      string keyPairName =
        CommandLine.GetArgument(parsedArgs, null, "-k", "--keypair-name");
      string subnetID =
        CommandLine.GetArgument(parsedArgs, null, "-s", "--subnet-id");
      if(   (string.IsNullOrEmpty(groupID) || !groupID.StartsWith("sg-"))
         || (string.IsNullOrEmpty(ami) || !ami.StartsWith("ami-"))
         || (string.IsNullOrEmpty(keyPairName))
         || (!string.IsNullOrEmpty(subnetID) && !subnetID.StartsWith("subnet-")))
        CommandLine.ErrorExit(
          "\nOne or more of the required arguments is missing or incorrect." +
          "\nRun the command with no arguments to see help.");

      // Create an EC2 client
      var ec2Client = new AmazonEC2Client();

      // Create an object with the necessary properties
      RunInstancesRequest request = GetRequestData(groupID, ami, keyPairName, subnetID);

      // Launch the instances and wait for them to start running
      var instanceIds = await LaunchInstances(ec2Client, request);
      await CheckState(ec2Client, instanceIds);
    }


    //
    // Method to put together the properties needed to launch the instance.
    private static RunInstancesRequest GetRequestData(
      string groupID, string ami, string keyPairName, string subnetID)
    {
      // Common properties
      var groupIDs = new List<string>() { groupID };
      var request = new RunInstancesRequest()
      {
        // The first three of these would be additional command-line arguments or similar.
        InstanceType = InstanceType.T1Micro,
        MinCount = 1,
        MaxCount = 1,
        ImageId = ami,
        KeyName = keyPairName
      };

      // Properties specifically for EC2 in a VPC.
      if(!string.IsNullOrEmpty(subnetID))
      {
        request.NetworkInterfaces =
          new List<InstanceNetworkInterfaceSpecification>() {
            new InstanceNetworkInterfaceSpecification() {
              DeviceIndex = 0,
              SubnetId = subnetID,
              Groups = groupIDs,
              AssociatePublicIpAddress = true
            }
          };
      }

      // Properties specifically for EC2-Classic
      else
      {
        request.SecurityGroupIds = groupIDs;
      }
      return request;
    }


    //
    // Method to launch the instances
    // Returns a list with the launched instance IDs
    private static async Task<List<string>> LaunchInstances(
      IAmazonEC2 ec2Client, RunInstancesRequest requestLaunch)
    {
      var instanceIds = new List<string>();
      RunInstancesResponse responseLaunch =
        await ec2Client.RunInstancesAsync(requestLaunch);

      Console.WriteLine("\nNew instances have been created.");
      foreach (Instance item in responseLaunch.Reservation.Instances)
      {
        instanceIds.Add(item.InstanceId);
        Console.WriteLine($"  New instance: {item.InstanceId}");
      }

      return instanceIds;
    }


    //
    // Method to wait until the instances are running (or at least not pending)
    private static async Task CheckState(IAmazonEC2 ec2Client, List<string> instanceIds)
    {
      Console.WriteLine(
        "\nWaiting for the instances to start." +
        "\nPress any key to stop waiting. (Response might be slightly delayed.)");

      int numberRunning;
      DescribeInstancesResponse responseDescribe;
      var requestDescribe = new DescribeInstancesRequest{
        InstanceIds = instanceIds};

      // Check every couple of seconds
      int wait = 2000;
      while(true)
      {
        // Get and check the status for each of the instances to see if it's past pending.
        // Once all instances are past pending, break out.
        // (For this example, we are assuming that there is only one reservation.)
        Console.Write(".");
        numberRunning = 0;
        responseDescribe = await ec2Client.DescribeInstancesAsync(requestDescribe);
        foreach(Instance i in responseDescribe.Reservations[0].Instances)
        {
          // Check the lower byte of State.Code property
          // Code == 0 is the pending state
          if((i.State.Code & 255) > 0) numberRunning++;
        }
        if(numberRunning == responseDescribe.Reservations[0].Instances.Count)
          break;

        // Wait a bit and try again (unless the user wants to stop waiting)
        Thread.Sleep(wait);
        if(Console.KeyAvailable)
          break;
      }

      Console.WriteLine("\nNo more instances are pending.");
      foreach(Instance i in responseDescribe.Reservations[0].Instances)
      {
        Console.WriteLine($"For {i.InstanceId}:");
        Console.WriteLine($"  VPC ID: {i.VpcId}");
        Console.WriteLine($"  Instance state: {i.State.Name}");
        Console.WriteLine($"  Public IP address: {i.PublicIpAddress}");
        Console.WriteLine($"  Public DNS name: {i.PublicDnsName}");
        Console.WriteLine($"  Key pair name: {i.KeyName}");
      }
    }


    //
    // Command-line help
    private static void PrintHelp()
    {
      Console.WriteLine(
        "\nUsage: EC2LaunchInstance -g <group-id> -a <ami-id> -k <keypair-name> [-s <subnet-id>]" +
        "\n  -g, --group-id: The ID of the security group." +
        "\n  -a, --ami-id: The ID of an Amazon Machine Image." +
        "\n  -k, --keypair-name - The name of a key pair." +
        "\n  -s, --subnet-id: The ID of a subnet. Required only for EC2 in a VPC.");
    }
  }


  // = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
  // Class that represents a command line on the console or terminal.
  // (This is the same for all examples. When you have seen it once, you can ignore it.)
  static class CommandLine
  {
    //
    // Method to parse a command line of the form: "--key value" or "-k value".
    //
    // Parameters:
    // - args: The command-line arguments passed into the application by the system.
    //
    // Returns:
    // A Dictionary with string Keys and Values.
    //
    // If a key is found without a matching value, Dictionary.Value is set to the key
    //  (including the dashes).
    // If a value is found without a matching key, Dictionary.Key is set to "--NoKeyN",
    //  where "N" represents sequential numbers.
    public static Dictionary<string,string> Parse(string[] args)
    {
      var parsedArgs = new Dictionary<string,string>();
      int i = 0, n = 0;
      while(i < args.Length)
      {
        // If the first argument in this iteration starts with a dash it's an option.
        if(args[i].StartsWith("-"))
        {
          var key = args[i++];
          var value = key;

          // Check to see if there's a value that goes with this option?
          if((i < args.Length) && (!args[i].StartsWith("-"))) value = args[i++];
          parsedArgs.Add(key, value);
        }

        // If the first argument in this iteration doesn't start with a dash, it's a value
        else
        {
          parsedArgs.Add("--NoKey" + n.ToString(), args[i++]);
          n++;
        }
      }

      return parsedArgs;
    }

    //
    // Method to get an argument from the parsed command-line arguments
    //
    // Parameters:
    // - parsedArgs: The Dictionary object returned from the Parse() method (shown above).
    // - defaultValue: The default string to return if the specified key isn't in parsedArgs.
    // - keys: An array of keys to look for in parsedArgs.
    public static string GetArgument(
      Dictionary<string,string> parsedArgs, string defaultReturn, params string[] keys)
    {
      string retval = null;
      foreach(var key in keys)
        if(parsedArgs.TryGetValue(key, out retval)) break;
      return retval ?? defaultReturn;
    }

    //
    // Method to exit the application with an error.
    public static void ErrorExit(string msg, int code=1)
    {
      Console.WriteLine("\nError");
      Console.WriteLine(msg);
      Environment.Exit(code);
    }
  }

}
```

## 추가 고려 사항
<a name="run-instance-additional"></a>
+ EC2 인스턴스의 상태를 확인할 때 [DescribeInstancesRequest](https://docs.aws.amazon.com/sdkfornet/v3/apidocs/items/EC2/TDescribeInstancesRequest.html) 객체의 `Filter` 속성에 필터를 추가할 수 있습니다. 이 기술을 사용하면 요청을 특정 인스턴스(예: 특정 사용자 지정 태그가 있는 인스턴스)로 제한할 수 있습니다.
+ 간략하게 나타내기 위해 일부 속성에는 일반적인 값이 지정되었습니다. 대신 프로그래밍 방식으로 또는 사용자 입력을 통해 이러한 속성 중 일부 또는 전체를 결정할 수 있습니다.
+ [RunInstancesRequest](https://docs.aws.amazon.com/sdkfornet/v3/apidocs/items/EC2/TRunInstancesRequest.html) 객체의 `MinCount` 및 `MaxCount` 속성에 사용할 수 있는 값은 대상 가용 영역과 해당 인스턴스 유형에 허용되는 최대 인스턴스 수에 따라 결정됩니다. 자세한 내용은 Amazon EC2 일반 FAQ의 [Amazon EC2에서 실행할 수 있는 인스턴스 수는 몇 개입니까](https://aws.amazon.com/ec2/faqs/#How_many_instances_can_I_run_in_Amazon_EC2)를 참조하세요.
+ 이 예제와 다른 인스턴스 유형을 사용하려는 경우 선택할 수 있는 여러 인스턴스 유형이 있습니다. 자세한 내용은 [Amazon EC2 사용 설명서](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/)의 [Amazon EC2 인스턴스 유형](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/instance-types.html)을 참조하세요. [인스턴스 유형 세부 정보](https://aws.amazon.com/ec2/instance-types/) 및 [인스턴스 유형 탐색기](https://aws.amazon.com/ec2/instance-explorer/)도 참조하세요.
+ 인스턴스를 시작할 때 [IAM 역할](net-dg-hosm.md)을 인스턴스에 연결할 수도 있습니다. 이렇게 하려면 `Name` 속성이 IAM 역할의 이름으로 설정된 [IamInstanceProfileSpecification](https://docs.aws.amazon.com/sdkfornet/v3/apidocs/items/EC2/TIamInstanceProfileSpecification.html) 객체를 생성해야 합니다. 그런 다음 해당 객체를 [RunInstancesRequest](https://docs.aws.amazon.com/sdkfornet/v3/apidocs/items/EC2/TRunInstancesRequest.html) 객체의 `IamInstanceProfile` 속성에 추가합니다.
**참고**  
IAM 역할이 연결된 EC2 인스턴스를 시작하려면 IAM 사용자의 구성에 특정 권한이 포함되어야 합니다. 필요한 권한에 대한 자세한 내용은 [Amazon EC2 사용 설명서](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/)의 [IAM 역할을 인스턴스에 전달할 수 있는 사용자 권한 부여](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/iam-roles-for-amazon-ec2.html#permission-to-pass-iam-roles)를 참조하세요.

## (선택 사항) 인스턴스에 연결
<a name="connect-to-instance"></a>

인스턴스가 실행된 후에는 적절한 원격 클라이언트를 사용하여 인스턴스에 원격으로 연결할 수 있습니다. Linux 및 Windows 인스턴스 모두에 대해 인스턴스의 퍼블릭 IP 주소 또는 퍼블릭 DNS 이름이 필요합니다. 또한 다음 항목이 필요합니다.

**Linux 인스턴스의 경우**

SSH 클라이언트를 사용하여 Linux 인스턴스에 연결할 수 있습니다. [보안 그룹 업데이트](authorize-ingress.md)에 설명된 대로 인스턴스를 시작할 때 사용한 보안 그룹이 포트 22에서의 SSH 트래픽을 허용하는지 확인합니다.

또한 인스턴스를 시작하는 데 사용한 키 페어의 비공개 부분, 즉 PEM 파일도 필요합니다.

자세한 내용은 [Amazon EC2 사용 설명서의 Linux 인스턴스에 연결](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/connect-to-linux-instance.html)을 참조하세요.

**Windows 인스턴스의 경우**

RDP 클라이언트를 사용하여 인스턴스에 연결할 수 있습니다. [보안 그룹 업데이트](authorize-ingress.md)에 설명된 대로 인스턴스를 시작할 때 사용한 보안 그룹이 포트 3389에서의 RDP 트래픽을 허용하는지 확인합니다.

또한 관리자 암호가 필요합니다. 다음 예제 코드를 사용하여 이를 얻을 수 있습니다. 이 코드에는 인스턴스 ID와 인스턴스 시작에 사용된 키 페어의 비공개 부분, 즉 PEM 파일이 필요합니다.

자세한 내용은 Amazon EC2 사용 설명서의 [Windows 인스턴스에 연결](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/connecting_to_windows_instance.html)을 참조하세요.

**주의**  
이 예제 코드는 인스턴스의 일반 텍스트 관리자 암호를 반환합니다.

### SDK 레퍼런스
<a name="w2aac19c15c17c19b9c35c23b1"></a>

NuGet 패키지:
+ [AWSSDK.EC2](https://www.nuget.org/packages/AWSSDK.EC2)

프로그래밍 요소:
+ 네임스페이스 [Amazon.EC2](https://docs.aws.amazon.com/sdkfornet/v3/apidocs/items/EC2/NEC2.html)

  클래스 [AmazonEC2Client](https://docs.aws.amazon.com/sdkfornet/v3/apidocs/items/EC2/TEC2Client.html)
+ 네임스페이스 [Amazon.EC2.Model](https://docs.aws.amazon.com/sdkfornet/v3/apidocs/items/EC2/NEC2Model.html)

  클래스 [GetPasswordDataRequest](https://docs.aws.amazon.com/sdkfornet/v3/apidocs/items/EC2/TGetPasswordDataRequest.html)

  클래스 [GetPasswordDataResponse](https://docs.aws.amazon.com/sdkfornet/v3/apidocs/items/EC2/TGetPasswordDataResponse.html)

### 코드
<a name="w2aac19c15c17c19b9c35c25b1"></a>

```
using System;
using System.Collections.Generic;
using System.IO;
using System.Threading.Tasks;
using Amazon.EC2;
using Amazon.EC2.Model;

namespace EC2GetWindowsPassword
{
  // = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
  // Class to get the Administrator password of a Windows EC2 instance
  class Program
  {
    static async Task Main(string[] args)
    {
      // Parse the command line and show help if necessary
      var parsedArgs = CommandLine.Parse(args);
      if(parsedArgs.Count == 0)
      {
        PrintHelp();
        return;
      }

      // Get the application arguments from the parsed list
      string instanceID =
        CommandLine.GetArgument(parsedArgs, null, "-i", "--instance-id");
      string pemFileName =
        CommandLine.GetArgument(parsedArgs, null, "-p", "--pem-filename");
      if(   (string.IsNullOrEmpty(instanceID) || !instanceID.StartsWith("i-"))
         || (string.IsNullOrEmpty(pemFileName) || !pemFileName.EndsWith(".pem")))
        CommandLine.ErrorExit(
          "\nOne or more of the required arguments is missing or incorrect." +
          "\nRun the command with no arguments to see help.");

      // Create the EC2 client
      var ec2Client = new AmazonEC2Client();

      // Get and display the password
      string password = await GetPassword(ec2Client, instanceID, pemFileName);
      Console.WriteLine($"\nPassword: {password}");
    }


    //
    // Method to get the administrator password of a Windows EC2 instance
    private static async Task<string> GetPassword(
      IAmazonEC2 ec2Client, string instanceID, string pemFilename)
    {
      string password = string.Empty;
      GetPasswordDataResponse response =
        await ec2Client.GetPasswordDataAsync(new GetPasswordDataRequest{
          InstanceId = instanceID});
      if(response.PasswordData != null)
      {
        password = response.GetDecryptedPassword(File.ReadAllText(pemFilename));
      }
      else
      {
        Console.WriteLine($"\nThe password is not available for instance {instanceID}.");
        Console.WriteLine($"If this is a Windows instance, the password might not be ready.");
      }
      return password;
    }


    //
    // Command-line help
    private static void PrintHelp()
    {
      Console.WriteLine(
        "\nUsage: EC2GetWindowsPassword -i <instance-id> -p pem-filename" +
        "\n  -i, --instance-id: The name of the EC2 instance." +
        "\n  -p, --pem-filename: The name of the PEM file with the private key.");
    }
  }

  // = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
  // Class that represents a command line on the console or terminal.
  // (This is the same for all examples. When you have seen it once, you can ignore it.)
  static class CommandLine
  {
    //
    // Method to parse a command line of the form: "--key value" or "-k value".
    //
    // Parameters:
    // - args: The command-line arguments passed into the application by the system.
    //
    // Returns:
    // A Dictionary with string Keys and Values.
    //
    // If a key is found without a matching value, Dictionary.Value is set to the key
    //  (including the dashes).
    // If a value is found without a matching key, Dictionary.Key is set to "--NoKeyN",
    //  where "N" represents sequential numbers.
    public static Dictionary<string,string> Parse(string[] args)
    {
      var parsedArgs = new Dictionary<string,string>();
      int i = 0, n = 0;
      while(i < args.Length)
      {
        // If the first argument in this iteration starts with a dash it's an option.
        if(args[i].StartsWith("-"))
        {
          var key = args[i++];
          var value = key;

          // Check to see if there's a value that goes with this option?
          if((i < args.Length) && (!args[i].StartsWith("-"))) value = args[i++];
          parsedArgs.Add(key, value);
        }

        // If the first argument in this iteration doesn't start with a dash, it's a value
        else
        {
          parsedArgs.Add("--NoKey" + n.ToString(), args[i++]);
          n++;
        }
      }

      return parsedArgs;
    }

    //
    // Method to get an argument from the parsed command-line arguments
    //
    // Parameters:
    // - parsedArgs: The Dictionary object returned from the Parse() method (shown above).
    // - defaultValue: The default string to return if the specified key isn't in parsedArgs.
    // - keys: An array of keys to look for in parsedArgs.
    public static string GetArgument(
      Dictionary<string,string> parsedArgs, string defaultReturn, params string[] keys)
    {
      string retval = null;
      foreach(var key in keys)
        if(parsedArgs.TryGetValue(key, out retval)) break;
      return retval ?? defaultReturn;
    }

    //
    // Method to exit the application with an error.
    public static void ErrorExit(string msg, int code=1)
    {
      Console.WriteLine("\nError");
      Console.WriteLine(msg);
      Environment.Exit(code);
    }
  }

}
```

## 정리
<a name="run-instance-cleanup"></a>

EC2 인스턴스가 더 이상 필요하지 않은 경우 [Amazon EC2 인스턴스 종료](terminate-instance.md)의 설명에 따라 인스턴스를 삭제하십시오.

# Amazon EC2 인스턴스 종료
<a name="terminate-instance"></a>

하나 이상의 Amazon EC2 인스턴스가 더 이상 필요하지 않으면 인스턴스를 종료할 수 있습니다.

이 예제에서는를 사용하여 EC2 인스턴스 AWS SDK for .NET 를 종료하는 방법을 보여줍니다. 인스턴스 ID를 입력으로 사용합니다.

## SDK 레퍼런스
<a name="w2aac19c15c17c19c11b7b1"></a>

NuGet 패키지:
+ [AWSSDK.EC2](https://www.nuget.org/packages/AWSSDK.EC2)

프로그래밍 요소:
+ 네임스페이스 [Amazon.EC2](https://docs.aws.amazon.com/sdkfornet/v3/apidocs/items/EC2/NEC2.html)

  클래스 [AmazonEC2Client](https://docs.aws.amazon.com/sdkfornet/v3/apidocs/items/EC2/TEC2Client.html)
+ 네임스페이스 [Amazon.EC2.Model](https://docs.aws.amazon.com/sdkfornet/v3/apidocs/items/EC2/NEC2Model.html)

  클래스 [TerminateInstancesRequest](https://docs.aws.amazon.com/sdkfornet/v3/apidocs/items/EC2/TTerminateInstancesRequest.html)

  클래스 [TerminateInstancesResponse](https://docs.aws.amazon.com/sdkfornet/v3/apidocs/items/EC2/TTerminateInstancesResponse.html)

```
using System;
using System.Threading.Tasks;
using System.Collections.Generic;
using Amazon.EC2;
using Amazon.EC2.Model;

namespace EC2TerminateInstance
{
  class Program
  {
    static async Task Main(string[] args)
    {
      if((args.Length == 1) && (args[0].StartsWith("i-")))
      {
        // Terminate the instance
        var ec2Client = new AmazonEC2Client();
        await TerminateInstance(ec2Client, args[0]);
      }
      else
      {
        Console.WriteLine("\nCommand-line argument missing or incorrect.");
        Console.WriteLine("\nUsage: EC2TerminateInstance instance-ID");
        Console.WriteLine("  instance-ID - The EC2 instance you want to terminate.");
        return;
      }
    }

    //
    // Method to terminate an EC2 instance
    private static async Task TerminateInstance(IAmazonEC2 ec2Client, string instanceID)
    {
      var request = new TerminateInstancesRequest{
        InstanceIds = new List<string>() { instanceID }};
      TerminateInstancesResponse response =
        await ec2Client.TerminateInstancesAsync(new TerminateInstancesRequest{
          InstanceIds = new List<string>() { instanceID }
        });
      foreach (InstanceStateChange item in response.TerminatingInstances)
      {
        Console.WriteLine("Terminated instance: " + item.InstanceId);
        Console.WriteLine("Instance state: " + item.CurrentState.Name);
      }
    }
  }
}
```

예제를 실행한 후에는 [Amazon EC2 콘솔](https://console.aws.amazon.com/ec2/)에 로그인하여 [EC2 인스턴스](https://console.aws.amazon.com/ec2/v2/home#Instances)가 종료되었는지 확인하는 것이 좋습니다.

# Amazon EC2 스팟 인스턴스 자습서
<a name="how-to-spot-instances"></a>

이 자습서에서는를 사용하여 Amazon EC2 스팟 인스턴스 AWS SDK for .NET 를 관리하는 방법을 보여줍니다.

## 개요
<a name="tutor-spot-net-overview"></a>

스팟 인스턴스를 사용하면 온디맨드 가격보다 저렴한 비용으로 미사용 Amazon EC2 용량을 요청할 수 있습니다. 이렇게 하면 중단될 수 있는 애플리케이션에 대한 EC2 비용을 크게 낮출 수 있습니다.

다음은 스팟 인스턴스 요청 및 사용 방법의 전반적인 요약입니다.

1. 지불하고자 하는 최고 가격을 지정하는 스팟 인스턴스 요청을 생성합니다.

1. 요청이 처리되면 다른 Amazon EC2 인스턴스와 마찬가지로 인스턴스를 실행합니다.

1. 원하는 기간만큼 인스턴스를 실행한 다음 종료합니다. 단, *스팟 가격*이 변경되어 인스턴스가 자동으로 종료되는 경우는 예외입니다.

1. 더 이상 필요하지 않은 스팟 인스턴스 요청을 정리하여 스팟 인스턴스가 더 이상 생성되지 않도록 합니다.

여기까지 스팟 인스턴스의 대략적 개요였습니다. 스팟 인스턴스를 더 잘 이해하려면 [Amazon EC2 사용 설명서](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/)의 [스팟 인스턴스](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/using-spot-instances.html)를 참조하세요.

## 이 자습서 소개
<a name="about-spot-instances-tutorial"></a>

이 자습서를 따르면 AWS SDK for .NET 를 사용하여 다음을 수행할 수 있습니다.
+ 스팟 인스턴스 요청 생성
+ 스팟 인스턴스 요청이 이행된 시점 확인
+ 스팟 인스턴스 요청 취소
+ 연결된 인스턴스 종료

다음 섹션에서는 이 예제의 코드 조각과 기타 정보를 제공합니다. [예제의 전체 코드](#tutor-spot-net-main)는 코드 조각 뒤에 표시되며, 그대로 빌드하고 실행할 수 있습니다.

**Topics**
+ [개요](#tutor-spot-net-overview)
+ [이 자습서 소개](#about-spot-instances-tutorial)
+ [사전 조건](#tutor-spot-net-prereq)
+ [필요한 내용 수집](#tutor-spot-net-gather)
+ [스팟 인스턴스 요청 생성](#tutor-spot-net-submit)
+ [스팟 인스턴스 요청 상태 확인](#tutor-spot-net-request-state)
+ [스팟 인스턴스 요청 정리](#tutor-spot-net-clean-up-request)
+ [스팟 인스턴스 정리](#tutor-spot-net-clean-up-instance)
+ [전체 코드](#tutor-spot-net-main)
+ [추가 고려 사항](#tutor-spot-net-additional)

## 사전 조건
<a name="tutor-spot-net-prereq"></a>

API 및 사전 조건에 대한 자세한 내용은 상위 섹션([Amazon EC2 작업](ec2-apis-intro.md))을 참조하세요.

## 필요한 내용 수집
<a name="tutor-spot-net-gather"></a>

스팟 인스턴스 요청을 생성하려면 몇 가지 사항이 필요합니다.
+ 인스턴스 수와 인스턴스 유형입니다. 선택할 수 있는 여러 인스턴스 유형이 있습니다. 자세한 내용은 [Amazon EC2 사용 설명서](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/)의 [Amazon EC2 인스턴스 유형](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/instance-types.html)을 참조하세요. [인스턴스 유형 세부 정보](https://aws.amazon.com/ec2/instance-types/) 및 [인스턴스 유형 탐색기](https://aws.amazon.com/ec2/instance-explorer/)도 참조하세요.

  이 자습서에서 기본 수는 1입니다.
+ 인스턴스를 생성하는 데 사용되는 Amazon Machine Image(AMI)입니다. AMI에 대한 자세한 내용은 [Amazon EC2 사용 설명서](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/)의 [Amazon Machine Image(AMI)](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/AMIs.html)를 참조하세요. 특히 [AMI 찾기](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/finding-an-ami.html) 및 [공유 AMI](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/sharing-amis.html)를 참조하세요.
+ 인스턴스 시간당 지불하려는 최고 가격입니다. [Amazon EC2 요금 페이지](https://aws.amazon.com/ec2/pricing/)에서 모든 인스턴스 유형(온디맨드 인스턴스 및 스팟 인스턴스 모두)의 가격을 확인할 수 있습니다. 이 자습서의 기본 가격은 나중에 설명합니다.
+ 인스턴스에 원격으로 연결하려는 경우 적절한 구성 및 리소스를 갖춘 보안 그룹을 사용합니다. 이에 대한 설명은 [Amazon EC2 에서 보안 그룹 작업](security-groups.md)에 나와 있으며 [필요한 정보를 수집](run-instance.md#run-instance-gather)하고 [인스턴스에 연결](run-instance.md#connect-to-instance)하는 방법에 대한 자세한 내용은 [Amazon EC2 인스턴스 시작](run-instance.md)에 나와 있습니다. 쉽게 설명하기 위해 이 자습서에서는 모든 새 AWS 계정에 있는 **default**라는 보안 그룹을 사용합니다.

스팟 인스턴스를 요청하기 위한 접근 방식에는 여러 가지가 있습니다. 다음은 일반적인 전략입니다.
+ 온디맨드 요금보다 저렴한 요금으로 요청하세요.
+ 결과 계산 값을 기준으로 요청하세요.
+ 가능한 한 빨리 컴퓨팅 용량을 획득하도록 요청하세요.

다음 설명은 [Amazon EC2 사용 설명서](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/)의 [스팟 인스턴스 요금 기록](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/using-spot-instances-history.html)을 참조합니다.

### 온디맨드 이하로 비용 줄이기
<a name="reduce-cost"></a>

실행하는 데 몇 시간 또는 며칠이 걸리는 일괄 처리 작업이 있습니다. 그러나 시작 및 종료 시점은 유연하게 선택할 수 있습니다. 그 작업을 온디맨드 인스턴스보다 적은 비용으로 완료할 수 있는지 확인하고 싶다면

Amazon EC2 콘솔 또는 Amazon EC2 API를 사용하여 인스턴스 유형에 대한 스팟 가격 기록을 살펴보십시오. 특정 가용 영역에서 원하는 인스턴스 유형의 가격 내역을 분석하였다면 요청에 사용할 수 있는 다른 방법이 두 가지 있습니다.
+ 일회적인 스팟 인스턴스 요청이 이행되고 해당 작업을 완료하기에 충분한 연속적 컴퓨팅 시간 동안 실행될 가능성이 높을 것이라는 기대로 스팟 가격 범위의 상단(여전히 온디맨드 가격보다는 낮음)에서 요청을 지정합니다.
+ 가격 범위의 하단에서 요청을 지정하고 영구 요청을 통해 시간이 지남에 따라 시작되는 여러 인스턴스를 결합하는 계획을 세울 수도 있습니다. 인스턴스는 합계 시간 동안 충분히 실행되어 훨씬 더 낮은 총 비용으로도 작업을 완료합니다.

### 결과의 가치에 대한 요금만 지불
<a name="value-of-result"></a>

실행할 데이터 처리 작업이 있는 경우 작업 당사자는 그 작업의 결과가 지니는 가치를 충분히 이해하여 컴퓨팅 비용 측면에서 그 결과의 가치가 얼마나 되는지 알 수 있습니다.

해당 인스턴스 유형의 스팟 가격 내역을 분석한 후 컴퓨팅 시간의 비용이 해당 작업의 결과가 지니는 가치보다 더 많지 않은 가격을 선택합니다. 영구 요청을 생성하여 스팟 가격이 요청 가격까지 또는 그 이하로 변동하는 과정에서 간헐적으로 실행되도록 합니다.

### 신속하게 컴퓨팅 용량 획득
<a name="acquire-quickly"></a>

온디맨드 인스턴스를 통해서는 사용할 수 없는 추가 용량이 예기치 않게 단기간 동안 필요한 경우가 있습니다. 이때는 해당 인스턴스 유형의 스팟 가격 내역을 분석한 후 과거의 최고 가격보다 높은 가격을 선택함으로써 요청이 신속하게 이행되고 완료될 때까지 컴퓨팅이 지속될 수 있는 가능성을 크게 높입니다.

필요한 사항을 수집하고 전략을 선택했으면 스팟 인스턴스를 요청할 준비가 된 것입니다. 이 자습서에서는 기본 스팟 인스턴스 최고 가격이 온디맨드 가격(이 자습서의 경우 0.003 USD)과 동일하게 설정됩니다. 이 방법으로 가격을 설정하면 요청이 이행될 가능성이 극대화됩니다.

## 스팟 인스턴스 요청 생성
<a name="tutor-spot-net-submit"></a>

다음 코드 조각은 이전에 수집한 요소를 사용하여 스팟 인스턴스 요청을 생성하는 방법을 보여줍니다.

[이 주제의 끝 부분에 있는](#tutor-spot-net-main) 예제에서는 사용 중인 이 코드 조각을 보여줍니다.

```
    //
    // Method to create a Spot Instance request
    private static async Task<SpotInstanceRequest> CreateSpotInstanceRequest(
      IAmazonEC2 ec2Client, string amiId, string securityGroupName,
      InstanceType instanceType, string spotPrice, int instanceCount)
    {
      var launchSpecification = new LaunchSpecification{
        ImageId = amiId,
        InstanceType = instanceType
      };
      launchSpecification.SecurityGroups.Add(securityGroupName);
      var request = new RequestSpotInstancesRequest{
        SpotPrice = spotPrice,
        InstanceCount = instanceCount,
        LaunchSpecification = launchSpecification
      };

      RequestSpotInstancesResponse result =
        await ec2Client.RequestSpotInstancesAsync(request);
      return result.SpotInstanceRequests[0];
    }
```

이 메서드에서 반환되는 중요한 값은 반환된 [SpotInstanceRequest](https://docs.aws.amazon.com/sdkfornet/v3/apidocs/items/EC2/TSpotInstanceRequest.html) 객체의 `SpotInstanceRequestId` 멤버에 포함된 스팟 인스턴스 요청 ID입니다.

**참고**  
시작된 모든 스팟 인스턴스에 대해 요금이 부과됩니다. 불필요한 비용을 피하려면 [모든 요청을 취소](#tutor-spot-net-clean-up-request)하고 [인스턴스를 종료](#tutor-spot-net-clean-up-instance)해야 합니다.

## 스팟 인스턴스 요청 상태 확인
<a name="tutor-spot-net-request-state"></a>

다음 코드 조각은 스팟 인스턴스 요청에 대한 정보를 가져오는 방법을 보여줍니다. 이 정보를 사용하여 스팟 인스턴스 요청이 이행될 때까지 계속 기다릴지 여부와 같은 코드의 특정 결정을 내릴 수 있습니다.

[이 주제의 끝 부분에 있는](#tutor-spot-net-main) 예제에서는 사용 중인 이 코드 조각을 보여줍니다.

```
    //
    // Method to get information about a Spot Instance request, including the status,
    // instance ID, etc.
    // It gets the information for a specific request (as opposed to all requests).
    private static async Task<SpotInstanceRequest> GetSpotInstanceRequestInfo(
      IAmazonEC2 ec2Client, string requestId)
    {
      var describeRequest = new DescribeSpotInstanceRequestsRequest();
      describeRequest.SpotInstanceRequestIds.Add(requestId);

      DescribeSpotInstanceRequestsResponse describeResponse =
        await ec2Client.DescribeSpotInstanceRequestsAsync(describeRequest);
      return describeResponse.SpotInstanceRequests[0];
    }
```

이 메서드는 인스턴스 ID, 상태 및 상태 코드와 같은 스팟 인스턴스 요청에 대한 정보를 반환합니다. 스팟 인스턴스 요청의 상태 코드에 대한 자세한 내용은 [Amazon EC2 사용 설명서](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/)의 [스팟 요청 상태](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/spot-bid-status.html#spot-instance-bid-status-understand)를 참조하세요.

## 스팟 인스턴스 요청 정리
<a name="tutor-spot-net-clean-up-request"></a>

스팟 인스턴스를 더 이상 요청할 필요가 없는 경우, 미해결 요청을 모두 취소하여 해당 요청이 다시 처리되지 않도록 하는 것이 중요합니다. 다음 코드 조각은 스팟 인스턴스 요청을 취소하는 방법을 보여줍니다.

[이 주제의 끝 부분에 있는](#tutor-spot-net-main) 예제에서는 사용 중인 이 코드 조각을 보여줍니다.

```
    //
    // Method to cancel a Spot Instance request
    private static async Task CancelSpotInstanceRequest(
      IAmazonEC2 ec2Client, string requestId)
    {
      var cancelRequest = new CancelSpotInstanceRequestsRequest();
      cancelRequest.SpotInstanceRequestIds.Add(requestId);

      await ec2Client.CancelSpotInstanceRequestsAsync(cancelRequest);
    }
```

## 스팟 인스턴스 정리
<a name="tutor-spot-net-clean-up-instance"></a>

불필요한 비용을 피하려면 스팟 인스턴스 요청으로 시작된 인스턴스를 모두 종료하는 것이 중요합니다. 스팟 인스턴스를 취소하는 것만으로는 인스턴스가 종료되지 않는데, 이는 인스턴스에 대해 계속해서 비용이 부과됨을 뜻합니다. 다음 코드 조각은 활성 스팟 인스턴스의 인스턴스 식별자를 획득한 후 인스턴스를 종료하는 방법을 보여줍니다.

[이 주제의 끝 부분에 있는](#tutor-spot-net-main) 예제에서는 사용 중인 이 코드 조각을 보여줍니다.

```
    //
    // Method to terminate a Spot Instance
    private static async Task TerminateSpotInstance(
      IAmazonEC2 ec2Client, string requestId)
    {
      var describeRequest = new DescribeSpotInstanceRequestsRequest();
      describeRequest.SpotInstanceRequestIds.Add(requestId);

      // Retrieve the Spot Instance request to check for running instances.
      DescribeSpotInstanceRequestsResponse describeResponse =
        await ec2Client.DescribeSpotInstanceRequestsAsync(describeRequest);

      // If there are any running instances, terminate them
      if(   (describeResponse.SpotInstanceRequests[0].Status.Code
              == "request-canceled-and-instance-running")
         || (describeResponse.SpotInstanceRequests[0].State == SpotInstanceState.Active))
      {
        TerminateInstancesResponse response =
          await ec2Client.TerminateInstancesAsync(new TerminateInstancesRequest{
            InstanceIds = new List<string>(){
              describeResponse.SpotInstanceRequests[0].InstanceId } });
        foreach (InstanceStateChange item in response.TerminatingInstances)
        {
          Console.WriteLine($"\n  Terminated instance: {item.InstanceId}");
          Console.WriteLine($"  Instance state: {item.CurrentState.Name}\n");
        }
      }
    }
```

## 전체 코드
<a name="tutor-spot-net-main"></a>

다음 코드 예제는 앞에서 설명한 메서드를 호출하여 스팟 인스턴스 요청을 생성 및 취소하고 스팟 인스턴스를 종료합니다.

### SDK 레퍼런스
<a name="w2aac19c15c17c21c43b5b1"></a>

NuGet 패키지:
+ [AWSSDK.EC2](https://www.nuget.org/packages/AWSSDK.EC2)

프로그래밍 요소:
+ 네임스페이스 [Amazon.EC2](https://docs.aws.amazon.com/sdkfornet/v3/apidocs/items/EC2/NEC2.html)

  클래스 [AmazonEC2Client](https://docs.aws.amazon.com/sdkfornet/v3/apidocs/items/EC2/TEC2Client.html)

  클래스 [InstanceType](https://docs.aws.amazon.com/sdkfornet/v3/apidocs/items/EC2/TInstanceType.html)
+ 네임스페이스 [Amazon.EC2.Model](https://docs.aws.amazon.com/sdkfornet/v3/apidocs/items/EC2/NEC2Model.html)

  클래스 [CancelSpotInstanceRequestsRequest](https://docs.aws.amazon.com/sdkfornet/v3/apidocs/items/EC2/TCancelSpotInstanceRequestsRequest.html)

  클래스 [DescribeSpotInstanceRequestsRequest](https://docs.aws.amazon.com/sdkfornet/v3/apidocs/items/EC2/TDescribeSpotInstanceRequestsRequest.html)

  클래스 [DescribeSpotInstanceRequestsResponse](https://docs.aws.amazon.com/sdkfornet/v3/apidocs/items/EC2/TDescribeSpotInstanceRequestsResponse.html)

  클래스 [InstanceStateChange](https://docs.aws.amazon.com/sdkfornet/v3/apidocs/items/EC2/TInstanceStateChange.html)

  클래스 [LaunchSpecification](https://docs.aws.amazon.com/sdkfornet/v3/apidocs/items/EC2/TLaunchSpecification.html)

  클래스 [RequestSpotInstancesRequest](https://docs.aws.amazon.com/sdkfornet/v3/apidocs/items/EC2/TRequestSpotInstancesRequest.html)

  클래스 [RequestSpotInstancesResponse](https://docs.aws.amazon.com/sdkfornet/v3/apidocs/items/EC2/TRequestSpotInstancesResponse.html)

  클래스 [SpotInstanceRequest](https://docs.aws.amazon.com/sdkfornet/v3/apidocs/items/EC2/TSpotInstanceRequest.html)

  클래스 [TerminateInstancesRequest](https://docs.aws.amazon.com/sdkfornet/v3/apidocs/items/EC2/TTerminateInstancesRequest.html)

  클래스 [TerminateInstancesResponse](https://docs.aws.amazon.com/sdkfornet/v3/apidocs/items/EC2/TTerminateInstancesResponse.html)

### 코드
<a name="w2aac19c15c17c21c43b7b1"></a>

```
using System;
using System.Threading;
using System.Threading.Tasks;
using System.Collections.Generic;
using Amazon.EC2;
using Amazon.EC2.Model;

namespace EC2SpotInstanceRequests
{
  class Program
  {
    static async Task Main(string[] args)
    {
      // Some default values.
      // These could be made into command-line arguments instead.
      var instanceType = InstanceType.T1Micro;
      string securityGroupName = "default";
      string spotPrice = "0.003";
      int instanceCount = 1;

      // Parse the command line arguments
      if((args.Length != 1) || (!args[0].StartsWith("ami-")))
      {
        Console.WriteLine("\nUsage: EC2SpotInstanceRequests ami");
        Console.WriteLine("  ami: the Amazon Machine Image to use for the Spot Instances.");
        return;
      }

      // Create the Amazon EC2 client.
      var ec2Client = new AmazonEC2Client();

      // Create the Spot Instance request and record its ID
      Console.WriteLine("\nCreating spot instance request...");
      var req = await CreateSpotInstanceRequest(
        ec2Client, args[0], securityGroupName, instanceType, spotPrice, instanceCount);
      string requestId = req.SpotInstanceRequestId;

      // Wait for an EC2 Spot Instance to become active
      Console.WriteLine(
        $"Waiting for Spot Instance request with ID {requestId} to become active...");
      int wait = 1;
      var start = DateTime.Now;
      while(true)
      {
        Console.Write(".");

        // Get and check the status to see if the request has been fulfilled.
        var requestInfo = await GetSpotInstanceRequestInfo(ec2Client, requestId);
        if(requestInfo.Status.Code == "fulfilled")
        {
          Console.WriteLine($"\nSpot Instance request {requestId} " +
            $"has been fulfilled by instance {requestInfo.InstanceId}.\n");
          break;
        }

        // Wait a bit and try again, longer each time (1, 2, 4, ...)
        Thread.Sleep(wait);
        wait = wait * 2;
      }

      // Show the user how long it took to fulfill the Spot Instance request.
      TimeSpan span = DateTime.Now.Subtract(start);
      Console.WriteLine($"That took {span.TotalMilliseconds} milliseconds");

      // Perform actions here as needed.
      // For this example, simply wait for the user to hit a key.
      // That gives them a chance to look at the EC2 console to see
      // the running instance if they want to.
      Console.WriteLine("Press any key to start the cleanup...");
      Console.ReadKey(true);

      // Cancel the request.
      // Do this first to make sure that the request can't be re-fulfilled
      // once the Spot Instance has been terminated.
      Console.WriteLine("Canceling Spot Instance request...");
      await CancelSpotInstanceRequest(ec2Client, requestId);

      // Terminate the Spot Instance that's running.
      Console.WriteLine("Terminating the running Spot Instance...");
      await TerminateSpotInstance(ec2Client, requestId);

      Console.WriteLine("Done. Press any key to exit...");
      Console.ReadKey(true);
    }


    //
    // Method to create a Spot Instance request
    private static async Task<SpotInstanceRequest> CreateSpotInstanceRequest(
      IAmazonEC2 ec2Client, string amiId, string securityGroupName,
      InstanceType instanceType, string spotPrice, int instanceCount)
    {
      var launchSpecification = new LaunchSpecification{
        ImageId = amiId,
        InstanceType = instanceType
      };
      launchSpecification.SecurityGroups.Add(securityGroupName);
      var request = new RequestSpotInstancesRequest{
        SpotPrice = spotPrice,
        InstanceCount = instanceCount,
        LaunchSpecification = launchSpecification
      };

      RequestSpotInstancesResponse result =
        await ec2Client.RequestSpotInstancesAsync(request);
      return result.SpotInstanceRequests[0];
    }


    //
    // Method to get information about a Spot Instance request, including the status,
    // instance ID, etc.
    // It gets the information for a specific request (as opposed to all requests).
    private static async Task<SpotInstanceRequest> GetSpotInstanceRequestInfo(
      IAmazonEC2 ec2Client, string requestId)
    {
      var describeRequest = new DescribeSpotInstanceRequestsRequest();
      describeRequest.SpotInstanceRequestIds.Add(requestId);

      DescribeSpotInstanceRequestsResponse describeResponse =
        await ec2Client.DescribeSpotInstanceRequestsAsync(describeRequest);
      return describeResponse.SpotInstanceRequests[0];
    }


    //
    // Method to cancel a Spot Instance request
    private static async Task CancelSpotInstanceRequest(
      IAmazonEC2 ec2Client, string requestId)
    {
      var cancelRequest = new CancelSpotInstanceRequestsRequest();
      cancelRequest.SpotInstanceRequestIds.Add(requestId);

      await ec2Client.CancelSpotInstanceRequestsAsync(cancelRequest);
    }


    //
    // Method to terminate a Spot Instance
    private static async Task TerminateSpotInstance(
      IAmazonEC2 ec2Client, string requestId)
    {
      var describeRequest = new DescribeSpotInstanceRequestsRequest();
      describeRequest.SpotInstanceRequestIds.Add(requestId);

      // Retrieve the Spot Instance request to check for running instances.
      DescribeSpotInstanceRequestsResponse describeResponse =
        await ec2Client.DescribeSpotInstanceRequestsAsync(describeRequest);

      // If there are any running instances, terminate them
      if(   (describeResponse.SpotInstanceRequests[0].Status.Code
              == "request-canceled-and-instance-running")
         || (describeResponse.SpotInstanceRequests[0].State == SpotInstanceState.Active))
      {
        TerminateInstancesResponse response =
          await ec2Client.TerminateInstancesAsync(new TerminateInstancesRequest{
            InstanceIds = new List<string>(){
              describeResponse.SpotInstanceRequests[0].InstanceId } });
        foreach (InstanceStateChange item in response.TerminatingInstances)
        {
          Console.WriteLine($"\n  Terminated instance: {item.InstanceId}");
          Console.WriteLine($"  Instance state: {item.CurrentState.Name}\n");
        }
      }
    }
  }
}
```

## 추가 고려 사항
<a name="tutor-spot-net-additional"></a>
+ 자습서를 실행한 후에는 [Amazon EC2 콘솔](https://console.aws.amazon.com/ec2/)에 로그인하여 [스팟 인스턴스 요청](https://console.aws.amazon.com/ec2/home#SpotInstances:)이 취소되고 [스팟 인스턴스](https://console.aws.amazon.com/ec2/v2/home#Instances)가 종료되었는지 확인하는 것이 좋습니다.