使用 的 Amazon EC2 範例 AWS SDK for .NET - AWS SDK 程式碼範例

文件 AWS SDK AWS 範例 SDK 儲存庫中有更多可用的 GitHub 範例。

本文為英文版的機器翻譯版本,如內容有任何歧義或不一致之處,概以英文版為準。

使用 的 Amazon EC2 範例 AWS SDK for .NET

下列程式碼範例示範如何搭配 Amazon EC2 AWS SDK for .NET 使用 來執行動作和實作常見案例。

基本概念是程式碼範例,示範如何在服務內執行基本操作。

Actions 是大型程式的程式碼摘錄,必須在內容中執行。雖然 動作會示範如何呼叫個別服務函數,但您可以在其相關案例中查看內容中的動作。

案例是程式碼範例,示範如何透過呼叫服務內的多個函數或與其他函數結合來完成特定任務 AWS 服務。

每個範例都包含完整原始程式碼的連結,您可以在其中找到如何在內容中設定和執行程式碼的指示。

開始使用

下列程式碼範例示範如何開始使用 Amazon EC2。

AWS SDK for .NET
注意

還有更多 on GitHub。尋找完整範例,並了解如何在 AWS 程式碼範例儲存庫中設定和執行。

namespace EC2Actions; public class HelloEc2 { /// <summary> /// HelloEc2 lists the existing security groups for the default users. /// </summary> /// <param name="args">Command line arguments</param> /// <returns>Async task.</returns> static async Task Main(string[] args) { // Set up dependency injection for Amazon Elastic Compute Cloud (Amazon EC2). using var host = Microsoft.Extensions.Hosting.Host.CreateDefaultBuilder(args) .ConfigureServices((_, services) => services.AddAWSService<IAmazonEC2>() .AddTransient<EC2Wrapper>() ) .Build(); // Now the client is available for injection. var ec2Client = host.Services.GetRequiredService<IAmazonEC2>(); try { // Retrieve information for up to 10 Amazon EC2 security groups. var request = new DescribeSecurityGroupsRequest { MaxResults = 10, }; var securityGroups = new List<SecurityGroup>(); var paginatorForSecurityGroups = ec2Client.Paginators.DescribeSecurityGroups(request); await foreach (var securityGroup in paginatorForSecurityGroups.SecurityGroups) { securityGroups.Add(securityGroup); } // Now print the security groups returned by the call to // DescribeSecurityGroupsAsync. Console.WriteLine("Welcome to the EC2 Hello Service example. " + "\nLet's list your Security Groups:"); securityGroups.ForEach(group => { Console.WriteLine( $"Security group: {group.GroupName} ID: {group.GroupId}"); }); } catch (AmazonEC2Exception ex) { Console.WriteLine($"An Amazon EC2 service error occurred while listing security groups. {ex.Message}"); } catch (Exception ex) { Console.WriteLine($"An error occurred while listing security groups. {ex.Message}"); } } }

基本概念

以下程式碼範例顯示做法:

  • 建立金鑰對和安全群組。

  • 選取 Amazon Machine Image (AMI) 和相容的執行個體類型,然後建立執行個體。

  • 停止並重新啟動執行個體。

  • 將彈性 IP 地址與您的執行個體建立關聯.

  • 使用 SSH 連線至執行個體,然後清除資源。

AWS SDK for .NET
注意

還有更多 on GitHub。尋找完整範例,並了解如何在 AWS 程式碼範例儲存庫中設定和執行。

在命令提示中執行案例。

/// <summary> /// Show Amazon Elastic Compute Cloud (Amazon EC2) Basics actions. /// </summary> public class EC2Basics { public static ILogger<EC2Basics> _logger = null!; public static EC2Wrapper _ec2Wrapper = null!; public static SsmWrapper _ssmWrapper = null!; public static UiMethods _uiMethods = null!; public static string associationId = null!; public static string allocationId = null!; public static string instanceId = null!; public static string keyPairName = null!; public static string groupName = null!; public static string tempFileName = null!; public static string secGroupId = null!; public static bool isInteractive = true; /// <summary> /// Perform the actions defined for the Amazon EC2 Basics scenario. /// </summary> /// <param name="args">Command line arguments.</param> /// <returns>A Task object.</returns> public static async Task Main(string[] args) { // Set up dependency injection for Amazon EC2 and Amazon Simple Systems // Management (Amazon SSM) Service. using var host = Microsoft.Extensions.Hosting.Host.CreateDefaultBuilder(args) .ConfigureServices((_, services) => services.AddAWSService<IAmazonEC2>() .AddAWSService<IAmazonSimpleSystemsManagement>() .AddTransient<EC2Wrapper>() .AddTransient<SsmWrapper>() ) .Build(); SetUpServices(host); var uniqueName = Guid.NewGuid().ToString(); keyPairName = "mvp-example-key-pair" + uniqueName; groupName = "ec2-scenario-group" + uniqueName; var groupDescription = "A security group created for the EC2 Basics scenario."; try { // Start the scenario. _uiMethods.DisplayOverview(); _uiMethods.PressEnter(isInteractive); // Create the key pair. _uiMethods.DisplayTitle("Create RSA key pair"); Console.Write("Let's create an RSA key pair that you can be use to "); Console.WriteLine("securely connect to your EC2 instance."); var keyPair = await _ec2Wrapper.CreateKeyPair(keyPairName); // Save key pair information to a temporary file. tempFileName = _ec2Wrapper.SaveKeyPair(keyPair); Console.WriteLine( $"Created the key pair: {keyPair.KeyName} and saved it to: {tempFileName}"); string? answer = ""; if (isInteractive) { do { Console.Write("Would you like to list your existing key pairs? "); answer = Console.ReadLine(); } while (answer!.ToLower() != "y" && answer.ToLower() != "n"); } if (!isInteractive || answer == "y") { // List existing key pairs. _uiMethods.DisplayTitle("Existing key pairs"); // Passing an empty string to the DescribeKeyPairs method will return // a list of all existing key pairs. var keyPairs = await _ec2Wrapper.DescribeKeyPairs(""); keyPairs.ForEach(kp => { Console.WriteLine( $"{kp.KeyName} created at: {kp.CreateTime} Fingerprint: {kp.KeyFingerprint}"); }); } _uiMethods.PressEnter(isInteractive); // Create the security group. Console.WriteLine( "Let's create a security group to manage access to your instance."); secGroupId = await _ec2Wrapper.CreateSecurityGroup(groupName, groupDescription); Console.WriteLine( "Let's add rules to allow all HTTP and HTTPS inbound traffic and to allow SSH only from your current IP address."); _uiMethods.DisplayTitle("Security group information"); var secGroups = await _ec2Wrapper.DescribeSecurityGroups(secGroupId); Console.WriteLine($"Created security group {groupName} in your default VPC."); secGroups.ForEach(group => { _ec2Wrapper.DisplaySecurityGroupInfoAsync(group); }); _uiMethods.PressEnter(isInteractive); Console.WriteLine( "Now we'll authorize the security group we just created so that it can"); Console.WriteLine("access the EC2 instances you create."); await _ec2Wrapper.AuthorizeSecurityGroupIngress(groupName); secGroups = await _ec2Wrapper.DescribeSecurityGroups(secGroupId); Console.WriteLine($"Now let's look at the permissions again."); secGroups.ForEach(group => { _ec2Wrapper.DisplaySecurityGroupInfoAsync(group); }); _uiMethods.PressEnter(isInteractive); // Get list of available Amazon Linux 2 Amazon Machine Images (AMIs). var parameters = await _ssmWrapper.GetParametersByPath( "/aws/service/ami-amazon-linux-latest"); List<string> imageIds = parameters.Select(param => param.Value).ToList(); var images = await _ec2Wrapper.DescribeImages(imageIds); var i = 1; images.ForEach(image => { Console.WriteLine($"\t{i++}\t{image.Description}"); }); int choice = 1; bool validNumber = false; if (isInteractive) { do { Console.Write("Please select an image: "); var selImage = Console.ReadLine(); validNumber = int.TryParse(selImage, out choice); } while (!validNumber); } var selectedImage = images[choice - 1]; // Display available instance types. _uiMethods.DisplayTitle("Instance Types"); var instanceTypes = await _ec2Wrapper.DescribeInstanceTypes(selectedImage.Architecture); i = 1; instanceTypes.ForEach(instanceType => { Console.WriteLine($"\t{i++}\t{instanceType.InstanceType}"); }); if (isInteractive) { do { Console.Write("Please select an instance type: "); var selImage = Console.ReadLine(); validNumber = int.TryParse(selImage, out choice); } while (!validNumber); } var selectedInstanceType = instanceTypes[choice - 1].InstanceType; // Create an EC2 instance. _uiMethods.DisplayTitle("Creating an EC2 Instance"); instanceId = await _ec2Wrapper.RunInstances(selectedImage.ImageId, selectedInstanceType, keyPairName, secGroupId); _uiMethods.PressEnter(isInteractive); var instance = await _ec2Wrapper.DescribeInstance(instanceId); _uiMethods.DisplayTitle("New Instance Information"); _ec2Wrapper.DisplayInstanceInformation(instance); Console.WriteLine( "\nYou can use SSH to connect to your instance. For example:"); Console.WriteLine( $"\tssh -i {tempFileName} ec2-user@{instance.PublicIpAddress}"); _uiMethods.PressEnter(isInteractive); Console.WriteLine( "Now we'll stop the instance and then start it again to see what's changed."); await _ec2Wrapper.StopInstances(instanceId); Console.WriteLine("Now let's start it up again."); await _ec2Wrapper.StartInstances(instanceId); Console.WriteLine("\nLet's see what changed."); instance = await _ec2Wrapper.DescribeInstance(instanceId); _uiMethods.DisplayTitle("New Instance Information"); _ec2Wrapper.DisplayInstanceInformation(instance); Console.WriteLine("\nNotice the change in the SSH information:"); Console.WriteLine( $"\tssh -i {tempFileName} ec2-user@{instance.PublicIpAddress}"); _uiMethods.PressEnter(isInteractive); Console.WriteLine( "Now we will stop the instance again. Then we will create and associate an"); Console.WriteLine("Elastic IP address to use with our instance."); await _ec2Wrapper.StopInstances(instanceId); _uiMethods.PressEnter(isInteractive); _uiMethods.DisplayTitle("Allocate Elastic IP address"); Console.WriteLine( "You can allocate an Elastic IP address and associate it with your instance\nto keep a consistent IP address even when your instance restarts."); var allocationResponse = await _ec2Wrapper.AllocateAddress(); allocationId = allocationResponse.AllocationId; Console.WriteLine( "Now we will associate the Elastic IP address with our instance."); associationId = await _ec2Wrapper.AssociateAddress(allocationId, instanceId); // Start the instance again. Console.WriteLine("Now let's start the instance again."); await _ec2Wrapper.StartInstances(instanceId); Console.WriteLine("\nLet's see what changed."); instance = await _ec2Wrapper.DescribeInstance(instanceId); _uiMethods.DisplayTitle("Instance information"); _ec2Wrapper.DisplayInstanceInformation(instance); Console.WriteLine("\nHere is the SSH information:"); Console.WriteLine( $"\tssh -i {tempFileName} ec2-user@{instance.PublicIpAddress}"); Console.WriteLine("Let's stop and start the instance again."); _uiMethods.PressEnter(isInteractive); await _ec2Wrapper.StopInstances(instanceId); Console.WriteLine("\nThe instance has stopped."); Console.WriteLine("Now let's start it up again."); await _ec2Wrapper.StartInstances(instanceId); instance = await _ec2Wrapper.DescribeInstance(instanceId); _uiMethods.DisplayTitle("New Instance Information"); _ec2Wrapper.DisplayInstanceInformation(instance); Console.WriteLine("Note that the IP address did not change this time."); _uiMethods.PressEnter(isInteractive); await Cleanup(); } catch (Exception ex) { _logger.LogError(ex, "There was a problem with the scenario, starting cleanup."); await Cleanup(); } _uiMethods.DisplayTitle("EC2 Basics Scenario completed."); _uiMethods.PressEnter(isInteractive); } /// <summary> /// Set up the services and logging. /// </summary> /// <param name="host"></param> public static void SetUpServices(IHost host) { var loggerFactory = LoggerFactory.Create(builder => { builder.AddConsole(); }); _logger = new Logger<EC2Basics>(loggerFactory); // Now the client is available for injection. _ec2Wrapper = host.Services.GetRequiredService<EC2Wrapper>(); _ssmWrapper = host.Services.GetRequiredService<SsmWrapper>(); _uiMethods = new UiMethods(); } /// <summary> /// Clean up any resources from the scenario. /// </summary> /// <returns></returns> public static async Task Cleanup() { _uiMethods.DisplayTitle("Clean up resources"); Console.WriteLine("Now let's clean up the resources we created."); Console.WriteLine("Disassociate the Elastic IP address and release it."); // Disassociate the Elastic IP address. await _ec2Wrapper.DisassociateIp(associationId); // Delete the Elastic IP address. await _ec2Wrapper.ReleaseAddress(allocationId); // Terminate the instance. Console.WriteLine("Terminating the instance we created."); await _ec2Wrapper.TerminateInstances(instanceId); // Delete the security group. Console.WriteLine($"Deleting the Security Group: {groupName}."); await _ec2Wrapper.DeleteSecurityGroup(secGroupId); // Delete the RSA key pair. Console.WriteLine($"Deleting the key pair: {keyPairName}"); await _ec2Wrapper.DeleteKeyPair(keyPairName); Console.WriteLine("Deleting the temporary file with the key information."); _ec2Wrapper.DeleteTempFile(tempFileName); _uiMethods.PressEnter(isInteractive); } }

定義包裝 EC2 動作的類別。

/// <summary> /// Methods of this class perform Amazon Elastic Compute Cloud (Amazon EC2). /// </summary> public class EC2Wrapper { private readonly IAmazonEC2 _amazonEC2; private readonly ILogger<EC2Wrapper> _logger; /// <summary> /// Constructor for the EC2Wrapper class. /// </summary> /// <param name="amazonScheduler">The injected EC2 client.</param> /// <param name="logger">The injected logger.</param> public EC2Wrapper(IAmazonEC2 amazonService, ILogger<EC2Wrapper> logger) { _amazonEC2 = amazonService; _logger = logger; } /// <summary> /// Allocates an Elastic IP address that can be associated with an Amazon EC2 // instance. By using an Elastic IP address, you can keep the public IP address // constant even when you restart the associated instance. /// </summary> /// <returns>The response object for the allocated address.</returns> public async Task<AllocateAddressResponse> AllocateAddress() { var request = new AllocateAddressRequest(); try { var response = await _amazonEC2.AllocateAddressAsync(request); Console.WriteLine($"Allocated IP: {response.PublicIp} with allocation ID {response.AllocationId}."); return response; } catch (AmazonEC2Exception ec2Exception) { if (ec2Exception.ErrorCode == "AddressLimitExceeded") { // For more information on Elastic IP address quotas, see: // https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/elastic-ip-addresses-eip.html#using-instance-addressing-limit _logger.LogError($"Unable to allocate Elastic IP, address limit exceeded. {ec2Exception.Message}"); } throw; } catch (Exception ex) { _logger.LogError($"An error occurred while allocating Elastic IP.: {ex.Message}"); throw; } } /// <summary> /// Associates an Elastic IP address with an instance. When this association is /// created, the Elastic IP's public IP address is immediately used as the public /// IP address of the associated instance. /// </summary> /// <param name="allocationId">The allocation Id of an Elastic IP address.</param> /// <param name="instanceId">The instance Id of the EC2 instance to /// associate the address with.</param> /// <returns>The association Id that represents /// the association of the Elastic IP address with an instance.</returns> public async Task<string> AssociateAddress(string allocationId, string instanceId) { try { var request = new AssociateAddressRequest { AllocationId = allocationId, InstanceId = instanceId }; var response = await _amazonEC2.AssociateAddressAsync(request); return response.AssociationId; } catch (AmazonEC2Exception ec2Exception) { if (ec2Exception.ErrorCode == "InvalidInstanceId") { _logger.LogError( $"InstanceId is invalid, unable to associate address. {ec2Exception.Message}"); } throw; } catch (Exception ex) { _logger.LogError( $"An error occurred while associating the Elastic IP.: {ex.Message}"); throw; } } /// <summary> /// Authorize the local computer ingress to EC2 instances associated /// with the virtual private cloud (VPC) security group. /// </summary> /// <param name="groupName">The name of the security group.</param> /// <returns>A Boolean value indicating the success of the action.</returns> public async Task<bool> AuthorizeSecurityGroupIngress(string groupName) { try { // Get the IP address for the local computer. var ipAddress = await GetIpAddress(); Console.WriteLine($"Your IP address is: {ipAddress}"); var ipRanges = new List<IpRange> { new IpRange { CidrIp = $"{ipAddress}/32" } }; var permission = new IpPermission { Ipv4Ranges = ipRanges, IpProtocol = "tcp", FromPort = 22, ToPort = 22 }; var permissions = new List<IpPermission> { permission }; var response = await _amazonEC2.AuthorizeSecurityGroupIngressAsync( new AuthorizeSecurityGroupIngressRequest(groupName, permissions)); return response.HttpStatusCode == HttpStatusCode.OK; } catch (AmazonEC2Exception ec2Exception) { if (ec2Exception.ErrorCode == "InvalidPermission.Duplicate") { _logger.LogError( $"The ingress rule already exists. {ec2Exception.Message}"); } throw; } catch (Exception ex) { _logger.LogError( $"An error occurred while authorizing ingress.: {ex.Message}"); throw; } } /// <summary> /// Authorize the local computer for ingress to /// the Amazon EC2 SecurityGroup. /// </summary> /// <returns>The IPv4 address of the computer running the scenario.</returns> private static async Task<string> GetIpAddress() { var httpClient = new HttpClient(); var ipString = await httpClient.GetStringAsync("https://checkip.amazonaws.com"); // The IP address is returned with a new line // character on the end. Trim off the whitespace and // return the value to the caller. return ipString.Trim(); } /// <summary> /// Create an Amazon EC2 key pair with a specified name. /// </summary> /// <param name="keyPairName">The name for the new key pair.</param> /// <returns>The Amazon EC2 key pair created.</returns> public async Task<KeyPair?> CreateKeyPair(string keyPairName) { try { var request = new CreateKeyPairRequest { KeyName = keyPairName, }; var response = await _amazonEC2.CreateKeyPairAsync(request); var kp = response.KeyPair; // Return the key pair so it can be saved if needed. // Wait until the key pair exists. int retries = 5; while (retries-- > 0) { Console.WriteLine($"Checking for new KeyPair {keyPairName}..."); var keyPairs = await DescribeKeyPairs(keyPairName); if (keyPairs.Any()) { return kp; } Thread.Sleep(5000); retries--; } _logger.LogError($"Unable to find newly created KeyPair {keyPairName}."); throw new DoesNotExistException("KeyPair not found"); } catch (AmazonEC2Exception ec2Exception) { if (ec2Exception.ErrorCode == "InvalidKeyPair.Duplicate") { _logger.LogError( $"A key pair called {keyPairName} already exists."); } throw; } catch (Exception ex) { _logger.LogError( $"An error occurred while creating the key pair.: {ex.Message}"); throw; } } /// <summary> /// Save KeyPair information to a temporary file. /// </summary> /// <param name="keyPair">The name of the key pair.</param> /// <returns>The full path to the temporary file.</returns> public string SaveKeyPair(KeyPair keyPair) { var tempPath = Path.GetTempPath(); var tempFileName = $"{tempPath}\\{Path.GetRandomFileName()}"; var pemFileName = Path.ChangeExtension(tempFileName, "pem"); // Save the key pair to a file in a temporary folder. using var stream = new FileStream(pemFileName, FileMode.Create); using var writer = new StreamWriter(stream); writer.WriteLine(keyPair.KeyMaterial); return pemFileName; } /// <summary> /// Create an Amazon EC2 security group with a specified name and description. /// </summary> /// <param name="groupName">The name for the new security group.</param> /// <param name="groupDescription">A description of the new security group.</param> /// <returns>The group Id of the new security group.</returns> public async Task<string> CreateSecurityGroup(string groupName, string groupDescription) { try { var response = await _amazonEC2.CreateSecurityGroupAsync( new CreateSecurityGroupRequest(groupName, groupDescription)); // Wait until the security group exists. int retries = 5; while (retries-- > 0) { var groups = await DescribeSecurityGroups(response.GroupId); if (groups.Any()) { return response.GroupId; } Thread.Sleep(5000); retries--; } _logger.LogError($"Unable to find newly created group {groupName}."); throw new DoesNotExistException("security group not found"); } catch (AmazonEC2Exception ec2Exception) { if (ec2Exception.ErrorCode == "ResourceAlreadyExists") { _logger.LogError( $"A security group with the name {groupName} already exists. {ec2Exception.Message}"); } throw; } catch (Exception ex) { _logger.LogError( $"An error occurred while creating the security group.: {ex.Message}"); throw; } } /// <summary> /// Create a new Amazon EC2 VPC. /// </summary> /// <param name="cidrBlock">The CIDR block for the new security group.</param> /// <returns>The VPC Id of the new VPC.</returns> public async Task<string?> CreateVPC(string cidrBlock) { try { var response = await _amazonEC2.CreateVpcAsync(new CreateVpcRequest { CidrBlock = cidrBlock, }); Vpc vpc = response.Vpc; Console.WriteLine($"Created VPC with ID: {vpc.VpcId}."); return vpc.VpcId; } catch (AmazonEC2Exception ex) { Console.WriteLine($"Couldn't create VPC because: {ex.Message}"); return null; } } /// <summary> /// Delete an Amazon EC2 key pair. /// </summary> /// <param name="keyPairName">The name of the key pair to delete.</param> /// <returns>A Boolean value indicating the success of the action.</returns> public async Task<bool> DeleteKeyPair(string keyPairName) { try { await _amazonEC2.DeleteKeyPairAsync(new DeleteKeyPairRequest(keyPairName)).ConfigureAwait(false); return true; } catch (AmazonEC2Exception ec2Exception) { if (ec2Exception.ErrorCode == "InvalidKeyPair.NotFound") { _logger.LogError($"KeyPair {keyPairName} does not exist and cannot be deleted. Please verify the key pair name and try again."); } return false; } catch (Exception ex) { Console.WriteLine($"Couldn't delete the key pair because: {ex.Message}"); return false; } } /// <summary> /// Delete the temporary file where the key pair information was saved. /// </summary> /// <param name="tempFileName">The path to the temporary file.</param> public void DeleteTempFile(string tempFileName) { if (File.Exists(tempFileName)) { File.Delete(tempFileName); } } /// <summary> /// Delete an Amazon EC2 security group. /// </summary> /// <param name="groupName">The name of the group to delete.</param> /// <returns>A Boolean value indicating the success of the action.</returns> public async Task<bool> DeleteSecurityGroup(string groupId) { try { var response = await _amazonEC2.DeleteSecurityGroupAsync( new DeleteSecurityGroupRequest { GroupId = groupId }); return response.HttpStatusCode == HttpStatusCode.OK; } catch (AmazonEC2Exception ec2Exception) { if (ec2Exception.ErrorCode == "InvalidGroup.NotFound") { _logger.LogError( $"Security Group {groupId} does not exist and cannot be deleted. Please verify the ID and try again."); } return false; } catch (Exception ex) { Console.WriteLine($"Couldn't delete the security group because: {ex.Message}"); return false; } } /// <summary> /// Delete an Amazon EC2 VPC. /// </summary> /// <returns>A Boolean value indicating the success of the action.</returns> public async Task<bool> DeleteVpc(string vpcId) { var request = new DeleteVpcRequest { VpcId = vpcId, }; var response = await _amazonEC2.DeleteVpcAsync(request); return response.HttpStatusCode == System.Net.HttpStatusCode.OK; } /// <summary> /// Get information about existing Amazon EC2 images. /// </summary> /// <returns>A list of image information.</returns> public async Task<List<Image>> DescribeImages(List<string>? imageIds) { var request = new DescribeImagesRequest(); if (imageIds is not null) { // If the imageIds list is not null, add the list // to the request object. request.ImageIds = imageIds; } var response = await _amazonEC2.DescribeImagesAsync(request); return response.Images; } /// <summary> /// Display the information returned by DescribeImages. /// </summary> /// <param name="images">The list of image information to display.</param> public void DisplayImageInfo(List<Image> images) { images.ForEach(image => { Console.WriteLine($"{image.Name} Created on: {image.CreationDate}"); }); } /// <summary> /// Get information about an Amazon EC2 instance. /// </summary> /// <param name="instanceId">The instance Id of the EC2 instance.</param> /// <returns>An EC2 instance.</returns> public async Task<Instance> DescribeInstance(string instanceId) { var response = await _amazonEC2.DescribeInstancesAsync( new DescribeInstancesRequest { InstanceIds = new List<string> { instanceId } }); return response.Reservations[0].Instances[0]; } /// <summary> /// Display EC2 instance information. /// </summary> /// <param name="instance">The instance Id of the EC2 instance.</param> public void DisplayInstanceInformation(Instance instance) { Console.WriteLine($"ID: {instance.InstanceId}"); Console.WriteLine($"Image ID: {instance.ImageId}"); Console.WriteLine($"{instance.InstanceType}"); Console.WriteLine($"Key Name: {instance.KeyName}"); Console.WriteLine($"VPC ID: {instance.VpcId}"); Console.WriteLine($"Public IP: {instance.PublicIpAddress}"); Console.WriteLine($"State: {instance.State.Name}"); } /// <summary> /// Get information about EC2 instances with a particular state. /// </summary> /// <param name="tagName">The name of the tag to filter on.</param> /// <param name="tagValue">The value of the tag to look for.</param> /// <returns>True if successful.</returns> public async Task<bool> GetInstancesWithState(string state) { try { // Filters the results of the instance list. var filters = new List<Filter> { new Filter { Name = $"instance-state-name", Values = new List<string> { state, }, }, }; var request = new DescribeInstancesRequest { Filters = filters, }; Console.WriteLine($"\nShowing instances with state {state}"); var paginator = _amazonEC2.Paginators.DescribeInstances(request); await foreach (var response in paginator.Responses) { foreach (var reservation in response.Reservations) { foreach (var instance in reservation.Instances) { Console.Write($"Instance ID: {instance.InstanceId} "); Console.WriteLine($"\tCurrent State: {instance.State.Name}"); } } } return true; } catch (AmazonEC2Exception ec2Exception) { if (ec2Exception.ErrorCode == "InvalidParameterValue") { _logger.LogError( $"Invalid parameter value for filtering instances."); } return false; } catch (Exception ex) { Console.WriteLine($"Couldn't list instances because: {ex.Message}"); return false; } } /// <summary> /// Describe the instance types available. /// </summary> /// <returns>A list of instance type information.</returns> public async Task<List<InstanceTypeInfo>> DescribeInstanceTypes(ArchitectureValues architecture) { try { var request = new DescribeInstanceTypesRequest(); var filters = new List<Filter> { new Filter("processor-info.supported-architecture", new List<string> { architecture.ToString() }) }; filters.Add(new Filter("instance-type", new() { "*.micro", "*.small" })); request.Filters = filters; var instanceTypes = new List<InstanceTypeInfo>(); var paginator = _amazonEC2.Paginators.DescribeInstanceTypes(request); await foreach (var instanceType in paginator.InstanceTypes) { instanceTypes.Add(instanceType); } return instanceTypes; } catch (AmazonEC2Exception ec2Exception) { if (ec2Exception.ErrorCode == "InvalidParameterValue") { _logger.LogError( $"Parameters are invalid. Ensure architecture and size strings conform to DescribeInstanceTypes API reference."); } throw; } catch (Exception ex) { Console.WriteLine($"Couldn't delete the security group because: {ex.Message}"); throw; } } /// <summary> /// Get information about an Amazon EC2 key pair. /// </summary> /// <param name="keyPairName">The name of the key pair.</param> /// <returns>A list of key pair information.</returns> public async Task<List<KeyPairInfo>> DescribeKeyPairs(string keyPairName) { try { var request = new DescribeKeyPairsRequest(); if (!string.IsNullOrEmpty(keyPairName)) { request = new DescribeKeyPairsRequest { KeyNames = new List<string> { keyPairName } }; } var response = await _amazonEC2.DescribeKeyPairsAsync(request); return response.KeyPairs.ToList(); } catch (AmazonEC2Exception ec2Exception) { if (ec2Exception.ErrorCode == "InvalidKeyPair.NotFound") { _logger.LogError( $"A key pair called {keyPairName} does not exist."); } throw; } catch (Exception ex) { _logger.LogError( $"An error occurred while describing the key pair.: {ex.Message}"); throw; } } /// <summary> /// Retrieve information for one or all Amazon EC2 security group. /// </summary> /// <param name="groupId">The optional Id of a specific Amazon EC2 security group.</param> /// <returns>A list of security group information.</returns> public async Task<List<SecurityGroup>> DescribeSecurityGroups(string groupId) { try { var securityGroups = new List<SecurityGroup>(); var request = new DescribeSecurityGroupsRequest(); if (!string.IsNullOrEmpty(groupId)) { var groupIds = new List<string> { groupId }; request.GroupIds = groupIds; } var paginatorForSecurityGroups = _amazonEC2.Paginators.DescribeSecurityGroups(request); await foreach (var securityGroup in paginatorForSecurityGroups.SecurityGroups) { securityGroups.Add(securityGroup); } return securityGroups; } catch (AmazonEC2Exception ec2Exception) { if (ec2Exception.ErrorCode == "InvalidGroup.NotFound") { _logger.LogError( $"A security group {groupId} does not exist."); } throw; } catch (Exception ex) { _logger.LogError( $"An error occurred while listing security groups. {ex.Message}"); throw; } } /// <summary> /// Display the information returned by the call to /// DescribeSecurityGroupsAsync. /// </summary> /// <param name="securityGroup">A list of security group information.</param> public void DisplaySecurityGroupInfoAsync(SecurityGroup securityGroup) { Console.WriteLine($"{securityGroup.GroupName}"); Console.WriteLine("Ingress permissions:"); securityGroup.IpPermissions.ForEach(permission => { Console.WriteLine($"\tFromPort: {permission.FromPort}"); Console.WriteLine($"\tIpProtocol: {permission.IpProtocol}"); Console.Write($"\tIpv4Ranges: "); permission.Ipv4Ranges.ForEach(range => { Console.Write($"{range.CidrIp} "); }); Console.WriteLine($"\n\tIpv6Ranges:"); permission.Ipv6Ranges.ForEach(range => { Console.Write($"{range.CidrIpv6} "); }); Console.Write($"\n\tPrefixListIds: "); permission.PrefixListIds.ForEach(id => Console.Write($"{id.Id} ")); Console.WriteLine($"\n\tTo Port: {permission.ToPort}"); }); Console.WriteLine("Egress permissions:"); securityGroup.IpPermissionsEgress.ForEach(permission => { Console.WriteLine($"\tFromPort: {permission.FromPort}"); Console.WriteLine($"\tIpProtocol: {permission.IpProtocol}"); Console.Write($"\tIpv4Ranges: "); permission.Ipv4Ranges.ForEach(range => { Console.Write($"{range.CidrIp} "); }); Console.WriteLine($"\n\tIpv6Ranges:"); permission.Ipv6Ranges.ForEach(range => { Console.Write($"{range.CidrIpv6} "); }); Console.Write($"\n\tPrefixListIds: "); permission.PrefixListIds.ForEach(id => Console.Write($"{id.Id} ")); Console.WriteLine($"\n\tTo Port: {permission.ToPort}"); }); } /// <summary> /// Disassociate an Elastic IP address from an EC2 instance. /// </summary> /// <param name="associationId">The association Id.</param> /// <returns>A Boolean value indicating the success of the action.</returns> public async Task<bool> DisassociateIp(string associationId) { try { var response = await _amazonEC2.DisassociateAddressAsync( new DisassociateAddressRequest { AssociationId = associationId }); return response.HttpStatusCode == HttpStatusCode.OK; } catch (AmazonEC2Exception ec2Exception) { if (ec2Exception.ErrorCode == "InvalidAssociationID.NotFound") { _logger.LogError( $"AssociationId is invalid, unable to disassociate address. {ec2Exception.Message}"); } return false; } catch (Exception ex) { _logger.LogError( $"An error occurred while disassociating the Elastic IP.: {ex.Message}"); return false; } } /// <summary> /// Retrieve a list of available Amazon Linux images. /// </summary> /// <returns>A list of image information.</returns> public async Task<List<Image>> GetEC2AmiList() { var filter = new Filter { Name = "architecture", Values = new List<string> { "x86_64" } }; var filters = new List<Filter> { filter }; var response = await _amazonEC2.DescribeImagesAsync(new DescribeImagesRequest { Filters = filters }); return response.Images; } /// <summary> /// Reboot a specific EC2 instance. /// </summary> /// <param name="ec2InstanceId">The instance Id of the instance that will be rebooted.</param> /// <returns>Async Task.</returns> public async Task<bool> RebootInstances(string ec2InstanceId) { try { var request = new RebootInstancesRequest { InstanceIds = new List<string> { ec2InstanceId }, }; await _amazonEC2.RebootInstancesAsync(request); // Wait for the instance to be running. Console.Write("Waiting for the instance to start."); await WaitForInstanceState(ec2InstanceId, InstanceStateName.Running); return true; } catch (AmazonEC2Exception ec2Exception) { if (ec2Exception.ErrorCode == "InvalidInstanceId") { _logger.LogError( $"InstanceId {ec2InstanceId} is invalid, unable to reboot. {ec2Exception.Message}"); } return false; } catch (Exception ex) { _logger.LogError( $"An error occurred while rebooting the instance {ec2InstanceId}.: {ex.Message}"); return false; } } /// <summary> /// Release an Elastic IP address. After the Elastic IP address is released, /// it can no longer be used. /// </summary> /// <param name="allocationId">The allocation Id of the Elastic IP address.</param> /// <returns>True if successful.</returns> public async Task<bool> ReleaseAddress(string allocationId) { try { var request = new ReleaseAddressRequest { AllocationId = allocationId }; var response = await _amazonEC2.ReleaseAddressAsync(request); return response.HttpStatusCode == HttpStatusCode.OK; } catch (AmazonEC2Exception ec2Exception) { if (ec2Exception.ErrorCode == "InvalidAllocationID.NotFound") { _logger.LogError( $"AllocationId {allocationId} was not found. {ec2Exception.Message}"); } return false; } catch (Exception ex) { _logger.LogError( $"An error occurred while releasing the AllocationId {allocationId}.: {ex.Message}"); return false; } } /// <summary> /// Create and run an EC2 instance. /// </summary> /// <param name="ImageId">The image Id of the image used as a basis for the /// EC2 instance.</param> /// <param name="instanceType">The instance type of the EC2 instance to create.</param> /// <param name="keyName">The name of the key pair to associate with the /// instance.</param> /// <param name="groupId">The Id of the Amazon EC2 security group that will be /// allowed to interact with the new EC2 instance.</param> /// <returns>The instance Id of the new EC2 instance.</returns> public async Task<string> RunInstances(string imageId, string instanceType, string keyName, string groupId) { try { var request = new RunInstancesRequest { ImageId = imageId, InstanceType = instanceType, KeyName = keyName, MinCount = 1, MaxCount = 1, SecurityGroupIds = new List<string> { groupId } }; var response = await _amazonEC2.RunInstancesAsync(request); var instanceId = response.Reservation.Instances[0].InstanceId; Console.Write("Waiting for the instance to start."); await WaitForInstanceState(instanceId, InstanceStateName.Running); return instanceId; } catch (AmazonEC2Exception ec2Exception) { if (ec2Exception.ErrorCode == "InvalidGroupId.NotFound") { _logger.LogError( $"GroupId {groupId} was not found. {ec2Exception.Message}"); } throw; } catch (Exception ex) { _logger.LogError( $"An error occurred while running the instance.: {ex.Message}"); throw; } } /// <summary> /// Start an EC2 instance. /// </summary> /// <param name="ec2InstanceId">The instance Id of the Amazon EC2 instance /// to start.</param> /// <returns>Async task.</returns> public async Task StartInstances(string ec2InstanceId) { try { var request = new StartInstancesRequest { InstanceIds = new List<string> { ec2InstanceId }, }; await _amazonEC2.StartInstancesAsync(request); Console.Write("Waiting for instance to start. "); await WaitForInstanceState(ec2InstanceId, InstanceStateName.Running); } catch (AmazonEC2Exception ec2Exception) { if (ec2Exception.ErrorCode == "InvalidInstanceId") { _logger.LogError( $"InstanceId is invalid, unable to start. {ec2Exception.Message}"); } throw; } catch (Exception ex) { _logger.LogError( $"An error occurred while starting the instance.: {ex.Message}"); throw; } } /// <summary> /// Stop an EC2 instance. /// </summary> /// <param name="ec2InstanceId">The instance Id of the EC2 instance to /// stop.</param> /// <returns>Async task.</returns> public async Task StopInstances(string ec2InstanceId) { try { var request = new StopInstancesRequest { InstanceIds = new List<string> { ec2InstanceId }, }; await _amazonEC2.StopInstancesAsync(request); Console.Write("Waiting for the instance to stop."); await WaitForInstanceState(ec2InstanceId, InstanceStateName.Stopped); Console.WriteLine("\nThe instance has stopped."); } catch (AmazonEC2Exception ec2Exception) { if (ec2Exception.ErrorCode == "InvalidInstanceId") { _logger.LogError( $"InstanceId is invalid, unable to stop. {ec2Exception.Message}"); } throw; } catch (Exception ex) { _logger.LogError( $"An error occurred while stopping the instance.: {ex.Message}"); throw; } } /// <summary> /// Terminate an EC2 instance. /// </summary> /// <param name="ec2InstanceId">The instance Id of the EC2 instance /// to terminate.</param> /// <returns>Async task.</returns> public async Task<List<InstanceStateChange>> TerminateInstances(string ec2InstanceId) { try { var request = new TerminateInstancesRequest { InstanceIds = new List<string> { ec2InstanceId } }; var response = await _amazonEC2.TerminateInstancesAsync(request); Console.Write("Waiting for the instance to terminate."); await WaitForInstanceState(ec2InstanceId, InstanceStateName.Terminated); Console.WriteLine($"\nThe instance {ec2InstanceId} has been terminated."); return response.TerminatingInstances; } catch (AmazonEC2Exception ec2Exception) { if (ec2Exception.ErrorCode == "InvalidInstanceId") { _logger.LogError( $"InstanceId is invalid, unable to terminate. {ec2Exception.Message}"); } throw; } catch (Exception ex) { _logger.LogError( $"An error occurred while terminating the instance.: {ex.Message}"); throw; } } /// <summary> /// Wait until an EC2 instance is in a specified state. /// </summary> /// <param name="instanceId">The instance Id.</param> /// <param name="stateName">The state to wait for.</param> /// <returns>A Boolean value indicating the success of the action.</returns> public async Task<bool> WaitForInstanceState(string instanceId, InstanceStateName stateName) { var request = new DescribeInstancesRequest { InstanceIds = new List<string> { instanceId } }; // Wait until the instance is in the specified state. var hasState = false; do { // Wait 5 seconds. Thread.Sleep(5000); // Check for the desired state. var response = await _amazonEC2.DescribeInstancesAsync(request); var instance = response.Reservations[0].Instances[0]; hasState = instance.State.Name == stateName; Console.Write(". "); } while (!hasState); return hasState; } }

動作

下列程式碼範例示範如何使用 AllocateAddress

AWS SDK for .NET
注意

還有更多 on GitHub。尋找完整範例,並了解如何在 AWS 程式碼範例儲存庫中設定和執行。

/// <summary> /// Allocates an Elastic IP address that can be associated with an Amazon EC2 // instance. By using an Elastic IP address, you can keep the public IP address // constant even when you restart the associated instance. /// </summary> /// <returns>The response object for the allocated address.</returns> public async Task<AllocateAddressResponse> AllocateAddress() { var request = new AllocateAddressRequest(); try { var response = await _amazonEC2.AllocateAddressAsync(request); Console.WriteLine($"Allocated IP: {response.PublicIp} with allocation ID {response.AllocationId}."); return response; } catch (AmazonEC2Exception ec2Exception) { if (ec2Exception.ErrorCode == "AddressLimitExceeded") { // For more information on Elastic IP address quotas, see: // https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/elastic-ip-addresses-eip.html#using-instance-addressing-limit _logger.LogError($"Unable to allocate Elastic IP, address limit exceeded. {ec2Exception.Message}"); } throw; } catch (Exception ex) { _logger.LogError($"An error occurred while allocating Elastic IP.: {ex.Message}"); throw; } }
  • 如需 API 詳細資訊,請參閱 AllocateAddress AWS SDK for .NET 參考中的 API

下列程式碼範例示範如何使用 AssociateAddress

AWS SDK for .NET
注意

還有更多 on GitHub。尋找完整範例,並了解如何在 AWS 程式碼範例儲存庫中設定和執行。

/// <summary> /// Associates an Elastic IP address with an instance. When this association is /// created, the Elastic IP's public IP address is immediately used as the public /// IP address of the associated instance. /// </summary> /// <param name="allocationId">The allocation Id of an Elastic IP address.</param> /// <param name="instanceId">The instance Id of the EC2 instance to /// associate the address with.</param> /// <returns>The association Id that represents /// the association of the Elastic IP address with an instance.</returns> public async Task<string> AssociateAddress(string allocationId, string instanceId) { try { var request = new AssociateAddressRequest { AllocationId = allocationId, InstanceId = instanceId }; var response = await _amazonEC2.AssociateAddressAsync(request); return response.AssociationId; } catch (AmazonEC2Exception ec2Exception) { if (ec2Exception.ErrorCode == "InvalidInstanceId") { _logger.LogError( $"InstanceId is invalid, unable to associate address. {ec2Exception.Message}"); } throw; } catch (Exception ex) { _logger.LogError( $"An error occurred while associating the Elastic IP.: {ex.Message}"); throw; } }
  • 如需 API 詳細資訊,請參閱 AssociateAddress AWS SDK for .NET 參考中的 API

下列程式碼範例示範如何使用 AuthorizeSecurityGroupIngress

AWS SDK for .NET
注意

還有更多 on GitHub。尋找完整範例,並了解如何在 AWS 程式碼範例儲存庫中設定和執行。

/// <summary> /// Authorize the local computer ingress to EC2 instances associated /// with the virtual private cloud (VPC) security group. /// </summary> /// <param name="groupName">The name of the security group.</param> /// <returns>A Boolean value indicating the success of the action.</returns> public async Task<bool> AuthorizeSecurityGroupIngress(string groupName) { try { // Get the IP address for the local computer. var ipAddress = await GetIpAddress(); Console.WriteLine($"Your IP address is: {ipAddress}"); var ipRanges = new List<IpRange> { new IpRange { CidrIp = $"{ipAddress}/32" } }; var permission = new IpPermission { Ipv4Ranges = ipRanges, IpProtocol = "tcp", FromPort = 22, ToPort = 22 }; var permissions = new List<IpPermission> { permission }; var response = await _amazonEC2.AuthorizeSecurityGroupIngressAsync( new AuthorizeSecurityGroupIngressRequest(groupName, permissions)); return response.HttpStatusCode == HttpStatusCode.OK; } catch (AmazonEC2Exception ec2Exception) { if (ec2Exception.ErrorCode == "InvalidPermission.Duplicate") { _logger.LogError( $"The ingress rule already exists. {ec2Exception.Message}"); } throw; } catch (Exception ex) { _logger.LogError( $"An error occurred while authorizing ingress.: {ex.Message}"); throw; } } /// <summary> /// Authorize the local computer for ingress to /// the Amazon EC2 SecurityGroup. /// </summary> /// <returns>The IPv4 address of the computer running the scenario.</returns> private static async Task<string> GetIpAddress() { var httpClient = new HttpClient(); var ipString = await httpClient.GetStringAsync("https://checkip.amazonaws.com"); // The IP address is returned with a new line // character on the end. Trim off the whitespace and // return the value to the caller. return ipString.Trim(); }

下列程式碼範例示範如何使用 CreateKeyPair

AWS SDK for .NET
注意

還有更多 on GitHub。尋找完整範例,並了解如何在 AWS 程式碼範例儲存庫中設定和執行。

/// <summary> /// Create an Amazon EC2 key pair with a specified name. /// </summary> /// <param name="keyPairName">The name for the new key pair.</param> /// <returns>The Amazon EC2 key pair created.</returns> public async Task<KeyPair?> CreateKeyPair(string keyPairName) { try { var request = new CreateKeyPairRequest { KeyName = keyPairName, }; var response = await _amazonEC2.CreateKeyPairAsync(request); var kp = response.KeyPair; // Return the key pair so it can be saved if needed. // Wait until the key pair exists. int retries = 5; while (retries-- > 0) { Console.WriteLine($"Checking for new KeyPair {keyPairName}..."); var keyPairs = await DescribeKeyPairs(keyPairName); if (keyPairs.Any()) { return kp; } Thread.Sleep(5000); retries--; } _logger.LogError($"Unable to find newly created KeyPair {keyPairName}."); throw new DoesNotExistException("KeyPair not found"); } catch (AmazonEC2Exception ec2Exception) { if (ec2Exception.ErrorCode == "InvalidKeyPair.Duplicate") { _logger.LogError( $"A key pair called {keyPairName} already exists."); } throw; } catch (Exception ex) { _logger.LogError( $"An error occurred while creating the key pair.: {ex.Message}"); throw; } } /// <summary> /// Save KeyPair information to a temporary file. /// </summary> /// <param name="keyPair">The name of the key pair.</param> /// <returns>The full path to the temporary file.</returns> public string SaveKeyPair(KeyPair keyPair) { var tempPath = Path.GetTempPath(); var tempFileName = $"{tempPath}\\{Path.GetRandomFileName()}"; var pemFileName = Path.ChangeExtension(tempFileName, "pem"); // Save the key pair to a file in a temporary folder. using var stream = new FileStream(pemFileName, FileMode.Create); using var writer = new StreamWriter(stream); writer.WriteLine(keyPair.KeyMaterial); return pemFileName; }
  • 如需 API 詳細資訊,請參閱 CreateKeyPair AWS SDK for .NET 參考中的 API

下列程式碼範例示範如何使用 CreateLaunchTemplate

AWS SDK for .NET
注意

還有更多 on GitHub。尋找完整範例,並了解如何在 AWS 程式碼範例儲存庫中設定和執行。

/// <summary> /// Creates an Amazon EC2 launch template to use with Amazon EC2 Auto Scaling. /// The launch template specifies a Bash script in its user data field that runs after /// the instance is started. This script installs the Python packages and starts a Python /// web server on the instance. /// </summary> /// <param name="startupScriptPath">The path to a Bash script file that is run.</param> /// <param name="instancePolicyPath">The path to a permissions policy to create and attach to the profile.</param> /// <returns>The template object.</returns> public async Task<Amazon.EC2.Model.LaunchTemplate> CreateTemplate(string startupScriptPath, string instancePolicyPath) { try { await CreateKeyPair(_keyPairName); await CreateInstanceProfileWithName(_instancePolicyName, _instanceRoleName, _instanceProfileName, instancePolicyPath); var startServerText = await File.ReadAllTextAsync(startupScriptPath); var plainTextBytes = System.Text.Encoding.UTF8.GetBytes(startServerText); var amiLatest = await _amazonSsm.GetParameterAsync( new GetParameterRequest() { Name = _amiParam }); var amiId = amiLatest.Parameter.Value; var launchTemplateResponse = await _amazonEc2.CreateLaunchTemplateAsync( new CreateLaunchTemplateRequest() { LaunchTemplateName = _launchTemplateName, LaunchTemplateData = new RequestLaunchTemplateData() { InstanceType = _instanceType, ImageId = amiId, IamInstanceProfile = new LaunchTemplateIamInstanceProfileSpecificationRequest() { Name = _instanceProfileName }, KeyName = _keyPairName, UserData = System.Convert.ToBase64String(plainTextBytes) } }); return launchTemplateResponse.LaunchTemplate; } catch (AmazonEC2Exception ec2Exception) { if (ec2Exception.ErrorCode == "InvalidLaunchTemplateName.AlreadyExistsException") { _logger.LogError($"Could not create the template, the name {_launchTemplateName} already exists. " + $"Please try again with a unique name."); } throw; } catch (Exception ex) { _logger.LogError($"An error occurred while creating the template.: {ex.Message}"); throw; } }

下列程式碼範例示範如何使用 CreateSecurityGroup

AWS SDK for .NET
注意

還有更多 on GitHub。尋找完整範例,並了解如何在 AWS 程式碼範例儲存庫中設定和執行。

/// <summary> /// Create an Amazon EC2 security group with a specified name and description. /// </summary> /// <param name="groupName">The name for the new security group.</param> /// <param name="groupDescription">A description of the new security group.</param> /// <returns>The group Id of the new security group.</returns> public async Task<string> CreateSecurityGroup(string groupName, string groupDescription) { try { var response = await _amazonEC2.CreateSecurityGroupAsync( new CreateSecurityGroupRequest(groupName, groupDescription)); // Wait until the security group exists. int retries = 5; while (retries-- > 0) { var groups = await DescribeSecurityGroups(response.GroupId); if (groups.Any()) { return response.GroupId; } Thread.Sleep(5000); retries--; } _logger.LogError($"Unable to find newly created group {groupName}."); throw new DoesNotExistException("security group not found"); } catch (AmazonEC2Exception ec2Exception) { if (ec2Exception.ErrorCode == "ResourceAlreadyExists") { _logger.LogError( $"A security group with the name {groupName} already exists. {ec2Exception.Message}"); } throw; } catch (Exception ex) { _logger.LogError( $"An error occurred while creating the security group.: {ex.Message}"); throw; } }

下列程式碼範例示範如何使用 DeleteKeyPair

AWS SDK for .NET
注意

還有更多 on GitHub。尋找完整範例,並了解如何在 AWS 程式碼範例儲存庫中設定和執行。

/// <summary> /// Delete an Amazon EC2 key pair. /// </summary> /// <param name="keyPairName">The name of the key pair to delete.</param> /// <returns>A Boolean value indicating the success of the action.</returns> public async Task<bool> DeleteKeyPair(string keyPairName) { try { await _amazonEC2.DeleteKeyPairAsync(new DeleteKeyPairRequest(keyPairName)).ConfigureAwait(false); return true; } catch (AmazonEC2Exception ec2Exception) { if (ec2Exception.ErrorCode == "InvalidKeyPair.NotFound") { _logger.LogError($"KeyPair {keyPairName} does not exist and cannot be deleted. Please verify the key pair name and try again."); } return false; } catch (Exception ex) { Console.WriteLine($"Couldn't delete the key pair because: {ex.Message}"); return false; } } /// <summary> /// Delete the temporary file where the key pair information was saved. /// </summary> /// <param name="tempFileName">The path to the temporary file.</param> public void DeleteTempFile(string tempFileName) { if (File.Exists(tempFileName)) { File.Delete(tempFileName); } }
  • 如需 API 詳細資訊,請參閱 DeleteKeyPair AWS SDK for .NET 參考中的 API

下列程式碼範例示範如何使用 DeleteLaunchTemplate

AWS SDK for .NET
注意

還有更多 on GitHub。尋找完整範例,並了解如何在 AWS 程式碼範例儲存庫中設定和執行。

/// <summary> /// Delete a launch template by name. /// </summary> /// <param name="templateName">The name of the template to delete.</param> /// <returns>Async task.</returns> public async Task DeleteTemplateByName(string templateName) { try { await _amazonEc2.DeleteLaunchTemplateAsync( new DeleteLaunchTemplateRequest() { LaunchTemplateName = templateName }); } catch (AmazonEC2Exception ec2Exception) { if (ec2Exception.ErrorCode == "InvalidLaunchTemplateName.NotFoundException") { _logger.LogError( $"Could not delete the template, the name {_launchTemplateName} was not found."); } throw; } catch (Exception ex) { _logger.LogError($"An error occurred while deleting the template.: {ex.Message}"); throw; } }

下列程式碼範例示範如何使用 DeleteSecurityGroup

AWS SDK for .NET
注意

還有更多 on GitHub。尋找完整範例,並了解如何在 AWS 程式碼範例儲存庫中設定和執行。

/// <summary> /// Delete an Amazon EC2 security group. /// </summary> /// <param name="groupName">The name of the group to delete.</param> /// <returns>A Boolean value indicating the success of the action.</returns> public async Task<bool> DeleteSecurityGroup(string groupId) { try { var response = await _amazonEC2.DeleteSecurityGroupAsync( new DeleteSecurityGroupRequest { GroupId = groupId }); return response.HttpStatusCode == HttpStatusCode.OK; } catch (AmazonEC2Exception ec2Exception) { if (ec2Exception.ErrorCode == "InvalidGroup.NotFound") { _logger.LogError( $"Security Group {groupId} does not exist and cannot be deleted. Please verify the ID and try again."); } return false; } catch (Exception ex) { Console.WriteLine($"Couldn't delete the security group because: {ex.Message}"); return false; } }

下列程式碼範例示範如何使用 DescribeAvailabilityZones

AWS SDK for .NET
注意

還有更多 on GitHub。尋找完整範例,並了解如何在 AWS 程式碼範例儲存庫中設定和執行。

/// <summary> /// Get a list of Availability Zones in the AWS Region of the Amazon EC2 Client. /// </summary> /// <returns>A list of availability zones.</returns> public async Task<List<string>> DescribeAvailabilityZones() { try { var zoneResponse = await _amazonEc2.DescribeAvailabilityZonesAsync( new DescribeAvailabilityZonesRequest()); return zoneResponse.AvailabilityZones.Select(z => z.ZoneName).ToList(); } catch (AmazonEC2Exception ec2Exception) { _logger.LogError($"An Amazon EC2 error occurred while listing availability zones.: {ec2Exception.Message}"); throw; } catch (Exception ex) { _logger.LogError($"An error occurred while listing availability zones.: {ex.Message}"); throw; } }

下列程式碼範例示範如何使用 DescribeIamInstanceProfileAssociations

AWS SDK for .NET
注意

還有更多 on GitHub。尋找完整範例,並了解如何在 AWS 程式碼範例儲存庫中設定和執行。

/// <summary> /// Get the instance profile association data for an instance. /// </summary> /// <param name="instanceId">The Id of the instance.</param> /// <returns>Instance profile associations data.</returns> public async Task<IamInstanceProfileAssociation> GetInstanceProfile(string instanceId) { try { var response = await _amazonEc2.DescribeIamInstanceProfileAssociationsAsync( new DescribeIamInstanceProfileAssociationsRequest() { Filters = new List<Amazon.EC2.Model.Filter>() { new("instance-id", new List<string>() { instanceId }) }, }); return response.IamInstanceProfileAssociations[0]; } catch (AmazonEC2Exception ec2Exception) { if (ec2Exception.ErrorCode == "InvalidInstanceID.NotFound") { _logger.LogError(ec2Exception, $"Instance {instanceId} not found"); } throw; } catch (Exception ex) { _logger.LogError(ex, $"An error occurred while creating the template.: {ex.Message}"); throw; } }

下列程式碼範例示範如何使用 DescribeInstanceTypes

AWS SDK for .NET
注意

還有更多 on GitHub。尋找完整範例,並了解如何在 AWS 程式碼範例儲存庫中設定和執行。

/// <summary> /// Describe the instance types available. /// </summary> /// <returns>A list of instance type information.</returns> public async Task<List<InstanceTypeInfo>> DescribeInstanceTypes(ArchitectureValues architecture) { try { var request = new DescribeInstanceTypesRequest(); var filters = new List<Filter> { new Filter("processor-info.supported-architecture", new List<string> { architecture.ToString() }) }; filters.Add(new Filter("instance-type", new() { "*.micro", "*.small" })); request.Filters = filters; var instanceTypes = new List<InstanceTypeInfo>(); var paginator = _amazonEC2.Paginators.DescribeInstanceTypes(request); await foreach (var instanceType in paginator.InstanceTypes) { instanceTypes.Add(instanceType); } return instanceTypes; } catch (AmazonEC2Exception ec2Exception) { if (ec2Exception.ErrorCode == "InvalidParameterValue") { _logger.LogError( $"Parameters are invalid. Ensure architecture and size strings conform to DescribeInstanceTypes API reference."); } throw; } catch (Exception ex) { Console.WriteLine($"Couldn't delete the security group because: {ex.Message}"); throw; } }

下列程式碼範例示範如何使用 DescribeInstances

AWS SDK for .NET
注意

還有更多 on GitHub。尋找完整範例,並了解如何在 AWS 程式碼範例儲存庫中設定和執行。

/// <summary> /// Get information about EC2 instances with a particular state. /// </summary> /// <param name="tagName">The name of the tag to filter on.</param> /// <param name="tagValue">The value of the tag to look for.</param> /// <returns>True if successful.</returns> public async Task<bool> GetInstancesWithState(string state) { try { // Filters the results of the instance list. var filters = new List<Filter> { new Filter { Name = $"instance-state-name", Values = new List<string> { state, }, }, }; var request = new DescribeInstancesRequest { Filters = filters, }; Console.WriteLine($"\nShowing instances with state {state}"); var paginator = _amazonEC2.Paginators.DescribeInstances(request); await foreach (var response in paginator.Responses) { foreach (var reservation in response.Reservations) { foreach (var instance in reservation.Instances) { Console.Write($"Instance ID: {instance.InstanceId} "); Console.WriteLine($"\tCurrent State: {instance.State.Name}"); } } } return true; } catch (AmazonEC2Exception ec2Exception) { if (ec2Exception.ErrorCode == "InvalidParameterValue") { _logger.LogError( $"Invalid parameter value for filtering instances."); } return false; } catch (Exception ex) { Console.WriteLine($"Couldn't list instances because: {ex.Message}"); return false; } }
  • 如需 API 詳細資訊,請參閱 DescribeInstances AWS SDK for .NET 參考中的 API

下列程式碼範例示範如何使用 DescribeKeyPairs

AWS SDK for .NET
注意

還有更多 on GitHub。尋找完整範例,並了解如何在 AWS 程式碼範例儲存庫中設定和執行。

/// <summary> /// Get information about an Amazon EC2 key pair. /// </summary> /// <param name="keyPairName">The name of the key pair.</param> /// <returns>A list of key pair information.</returns> public async Task<List<KeyPairInfo>> DescribeKeyPairs(string keyPairName) { try { var request = new DescribeKeyPairsRequest(); if (!string.IsNullOrEmpty(keyPairName)) { request = new DescribeKeyPairsRequest { KeyNames = new List<string> { keyPairName } }; } var response = await _amazonEC2.DescribeKeyPairsAsync(request); return response.KeyPairs.ToList(); } catch (AmazonEC2Exception ec2Exception) { if (ec2Exception.ErrorCode == "InvalidKeyPair.NotFound") { _logger.LogError( $"A key pair called {keyPairName} does not exist."); } throw; } catch (Exception ex) { _logger.LogError( $"An error occurred while describing the key pair.: {ex.Message}"); throw; } }
  • 如需 API 詳細資訊,請參閱 DescribeKeyPairs AWS SDK for .NET 參考中的 API

下列程式碼範例示範如何使用 DescribeSecurityGroups

AWS SDK for .NET
注意

還有更多 on GitHub。尋找完整範例,並了解如何在 AWS 程式碼範例儲存庫中設定和執行。

/// <summary> /// Retrieve information for one or all Amazon EC2 security group. /// </summary> /// <param name="groupId">The optional Id of a specific Amazon EC2 security group.</param> /// <returns>A list of security group information.</returns> public async Task<List<SecurityGroup>> DescribeSecurityGroups(string groupId) { try { var securityGroups = new List<SecurityGroup>(); var request = new DescribeSecurityGroupsRequest(); if (!string.IsNullOrEmpty(groupId)) { var groupIds = new List<string> { groupId }; request.GroupIds = groupIds; } var paginatorForSecurityGroups = _amazonEC2.Paginators.DescribeSecurityGroups(request); await foreach (var securityGroup in paginatorForSecurityGroups.SecurityGroups) { securityGroups.Add(securityGroup); } return securityGroups; } catch (AmazonEC2Exception ec2Exception) { if (ec2Exception.ErrorCode == "InvalidGroup.NotFound") { _logger.LogError( $"A security group {groupId} does not exist."); } throw; } catch (Exception ex) { _logger.LogError( $"An error occurred while listing security groups. {ex.Message}"); throw; } } /// <summary> /// Display the information returned by the call to /// DescribeSecurityGroupsAsync. /// </summary> /// <param name="securityGroup">A list of security group information.</param> public void DisplaySecurityGroupInfoAsync(SecurityGroup securityGroup) { Console.WriteLine($"{securityGroup.GroupName}"); Console.WriteLine("Ingress permissions:"); securityGroup.IpPermissions.ForEach(permission => { Console.WriteLine($"\tFromPort: {permission.FromPort}"); Console.WriteLine($"\tIpProtocol: {permission.IpProtocol}"); Console.Write($"\tIpv4Ranges: "); permission.Ipv4Ranges.ForEach(range => { Console.Write($"{range.CidrIp} "); }); Console.WriteLine($"\n\tIpv6Ranges:"); permission.Ipv6Ranges.ForEach(range => { Console.Write($"{range.CidrIpv6} "); }); Console.Write($"\n\tPrefixListIds: "); permission.PrefixListIds.ForEach(id => Console.Write($"{id.Id} ")); Console.WriteLine($"\n\tTo Port: {permission.ToPort}"); }); Console.WriteLine("Egress permissions:"); securityGroup.IpPermissionsEgress.ForEach(permission => { Console.WriteLine($"\tFromPort: {permission.FromPort}"); Console.WriteLine($"\tIpProtocol: {permission.IpProtocol}"); Console.Write($"\tIpv4Ranges: "); permission.Ipv4Ranges.ForEach(range => { Console.Write($"{range.CidrIp} "); }); Console.WriteLine($"\n\tIpv6Ranges:"); permission.Ipv6Ranges.ForEach(range => { Console.Write($"{range.CidrIpv6} "); }); Console.Write($"\n\tPrefixListIds: "); permission.PrefixListIds.ForEach(id => Console.Write($"{id.Id} ")); Console.WriteLine($"\n\tTo Port: {permission.ToPort}"); }); }

下列程式碼範例示範如何使用 DescribeSubnets

AWS SDK for .NET
注意

還有更多 on GitHub。尋找完整範例,並了解如何在 AWS 程式碼範例儲存庫中設定和執行。

/// <summary> /// Get all the subnets for a Vpc in a set of availability zones. /// </summary> /// <param name="vpcId">The Id of the Vpc.</param> /// <param name="availabilityZones">The list of availability zones.</param> /// <returns>The collection of subnet objects.</returns> public async Task<List<Subnet>> GetAllVpcSubnetsForZones(string vpcId, List<string> availabilityZones) { try { var subnets = new List<Subnet>(); var subnetPaginator = _amazonEc2.Paginators.DescribeSubnets( new DescribeSubnetsRequest() { Filters = new List<Amazon.EC2.Model.Filter>() { new("vpc-id", new List<string>() { vpcId }), new("availability-zone", availabilityZones), new("default-for-az", new List<string>() { "true" }) } }); // Get the entire list using the paginator. await foreach (var subnet in subnetPaginator.Subnets) { subnets.Add(subnet); } return subnets; } catch (AmazonEC2Exception ec2Exception) { if (ec2Exception.ErrorCode == "InvalidVpcID.NotFound") { _logger.LogError(ec2Exception, $"The specified VPC ID {vpcId} does not exist."); } throw; } catch (Exception ex) { _logger.LogError(ex, $"An error occurred while describing the subnets.: {ex.Message}"); throw; } }
  • 如需 API 詳細資訊,請參閱 DescribeSubnets AWS SDK for .NET 參考中的 API

下列程式碼範例示範如何使用 DescribeVpcs

AWS SDK for .NET
注意

還有更多 on GitHub。尋找完整範例,並了解如何在 AWS 程式碼範例儲存庫中設定和執行。

/// <summary> /// Get the default VPC for the account. /// </summary> /// <returns>The default VPC object.</returns> public async Task<Vpc> GetDefaultVpc() { try { var vpcResponse = await _amazonEc2.DescribeVpcsAsync( new DescribeVpcsRequest() { Filters = new List<Amazon.EC2.Model.Filter>() { new("is-default", new List<string>() { "true" }) } }); return vpcResponse.Vpcs[0]; } catch (AmazonEC2Exception ec2Exception) { if (ec2Exception.ErrorCode == "UnauthorizedOperation") { _logger.LogError(ec2Exception, $"You do not have the necessary permissions to describe VPCs."); } throw; } catch (Exception ex) { _logger.LogError(ex, $"An error occurred while describing the vpcs.: {ex.Message}"); throw; } }
  • 如需 API 詳細資訊,請參閱 DescribeVpcs AWS SDK for .NET 參考中的 API

下列程式碼範例示範如何使用 DisassociateAddress

AWS SDK for .NET
注意

還有更多 on GitHub。尋找完整範例,並了解如何在 AWS 程式碼範例儲存庫中設定和執行。

/// <summary> /// Disassociate an Elastic IP address from an EC2 instance. /// </summary> /// <param name="associationId">The association Id.</param> /// <returns>A Boolean value indicating the success of the action.</returns> public async Task<bool> DisassociateIp(string associationId) { try { var response = await _amazonEC2.DisassociateAddressAsync( new DisassociateAddressRequest { AssociationId = associationId }); return response.HttpStatusCode == HttpStatusCode.OK; } catch (AmazonEC2Exception ec2Exception) { if (ec2Exception.ErrorCode == "InvalidAssociationID.NotFound") { _logger.LogError( $"AssociationId is invalid, unable to disassociate address. {ec2Exception.Message}"); } return false; } catch (Exception ex) { _logger.LogError( $"An error occurred while disassociating the Elastic IP.: {ex.Message}"); return false; } }

下列程式碼範例示範如何使用 RebootInstances

AWS SDK for .NET
注意

還有更多 on GitHub。尋找完整範例,並了解如何在 AWS 程式碼範例儲存庫中設定和執行。

依執行個體的 ID 重新啟動執行個體。

/// <summary> /// Reboot a specific EC2 instance. /// </summary> /// <param name="ec2InstanceId">The instance Id of the instance that will be rebooted.</param> /// <returns>Async Task.</returns> public async Task<bool> RebootInstances(string ec2InstanceId) { try { var request = new RebootInstancesRequest { InstanceIds = new List<string> { ec2InstanceId }, }; await _amazonEC2.RebootInstancesAsync(request); // Wait for the instance to be running. Console.Write("Waiting for the instance to start."); await WaitForInstanceState(ec2InstanceId, InstanceStateName.Running); return true; } catch (AmazonEC2Exception ec2Exception) { if (ec2Exception.ErrorCode == "InvalidInstanceId") { _logger.LogError( $"InstanceId {ec2InstanceId} is invalid, unable to reboot. {ec2Exception.Message}"); } return false; } catch (Exception ex) { _logger.LogError( $"An error occurred while rebooting the instance {ec2InstanceId}.: {ex.Message}"); return false; } } /// <summary> /// Wait until an EC2 instance is in a specified state. /// </summary> /// <param name="instanceId">The instance Id.</param> /// <param name="stateName">The state to wait for.</param> /// <returns>A Boolean value indicating the success of the action.</returns> public async Task<bool> WaitForInstanceState(string instanceId, InstanceStateName stateName) { var request = new DescribeInstancesRequest { InstanceIds = new List<string> { instanceId } }; // Wait until the instance is in the specified state. var hasState = false; do { // Wait 5 seconds. Thread.Sleep(5000); // Check for the desired state. var response = await _amazonEC2.DescribeInstancesAsync(request); var instance = response.Reservations[0].Instances[0]; hasState = instance.State.Name == stateName; Console.Write(". "); } while (!hasState); return hasState; }

取代執行個體的設定檔,重新開機,然後重新啟動 Web 伺服器。

/// <summary> /// Replace the profile associated with a running instance. After the profile is replaced, the instance /// is rebooted to ensure that it uses the new profile. When the instance is ready, Systems Manager is /// used to restart the Python web server. /// </summary> /// <param name="instanceId">The Id of the instance to update.</param> /// <param name="credsProfileName">The name of the new profile to associate with the specified instance.</param> /// <param name="associationId">The Id of the existing profile association for the instance.</param> /// <returns>Async task.</returns> public async Task ReplaceInstanceProfile(string instanceId, string credsProfileName, string associationId) { try { await _amazonEc2.ReplaceIamInstanceProfileAssociationAsync( new ReplaceIamInstanceProfileAssociationRequest() { AssociationId = associationId, IamInstanceProfile = new IamInstanceProfileSpecification() { Name = credsProfileName } }); // Allow time before resetting. Thread.Sleep(25000); await _amazonEc2.RebootInstancesAsync( new RebootInstancesRequest(new List<string>() { instanceId })); Thread.Sleep(25000); var instanceReady = false; var retries = 5; while (retries-- > 0 && !instanceReady) { var instancesPaginator = _amazonSsm.Paginators.DescribeInstanceInformation( new DescribeInstanceInformationRequest()); // Get the entire list using the paginator. await foreach (var instance in instancesPaginator.InstanceInformationList) { instanceReady = instance.InstanceId == instanceId; if (instanceReady) { break; } } } Console.WriteLine("Waiting for instance to be running."); await WaitForInstanceState(instanceId, InstanceStateName.Running); Console.WriteLine("Instance ready."); Console.WriteLine($"Sending restart command to instance {instanceId}"); await _amazonSsm.SendCommandAsync( new SendCommandRequest() { InstanceIds = new List<string>() { instanceId }, DocumentName = "AWS-RunShellScript", Parameters = new Dictionary<string, List<string>>() { { "commands", new List<string>() { "cd / && sudo python3 server.py 80" } } } }); Console.WriteLine($"Restarted the web server on instance {instanceId}"); } catch (AmazonEC2Exception ec2Exception) { if (ec2Exception.ErrorCode == "InvalidInstanceID.NotFound") { _logger.LogError(ec2Exception, $"Instance {instanceId} not found"); } throw; } catch (Exception ex) { _logger.LogError(ex, $"An error occurred while replacing the template.: {ex.Message}"); throw; } }
  • 如需 API 詳細資訊,請參閱 RebootInstances AWS SDK for .NET 參考中的 API

下列程式碼範例示範如何使用 ReleaseAddress

AWS SDK for .NET
注意

還有更多 on GitHub。尋找完整範例,並了解如何在 AWS 程式碼範例儲存庫中設定和執行。

/// <summary> /// Release an Elastic IP address. After the Elastic IP address is released, /// it can no longer be used. /// </summary> /// <param name="allocationId">The allocation Id of the Elastic IP address.</param> /// <returns>True if successful.</returns> public async Task<bool> ReleaseAddress(string allocationId) { try { var request = new ReleaseAddressRequest { AllocationId = allocationId }; var response = await _amazonEC2.ReleaseAddressAsync(request); return response.HttpStatusCode == HttpStatusCode.OK; } catch (AmazonEC2Exception ec2Exception) { if (ec2Exception.ErrorCode == "InvalidAllocationID.NotFound") { _logger.LogError( $"AllocationId {allocationId} was not found. {ec2Exception.Message}"); } return false; } catch (Exception ex) { _logger.LogError( $"An error occurred while releasing the AllocationId {allocationId}.: {ex.Message}"); return false; } }
  • 如需 API 詳細資訊,請參閱 ReleaseAddress AWS SDK for .NET 參考中的 API

下列程式碼範例示範如何使用 ReplaceIamInstanceProfileAssociation

AWS SDK for .NET
注意

還有更多 on GitHub。尋找完整範例,並了解如何在 AWS 程式碼範例儲存庫中設定和執行。

/// <summary> /// Replace the profile associated with a running instance. After the profile is replaced, the instance /// is rebooted to ensure that it uses the new profile. When the instance is ready, Systems Manager is /// used to restart the Python web server. /// </summary> /// <param name="instanceId">The Id of the instance to update.</param> /// <param name="credsProfileName">The name of the new profile to associate with the specified instance.</param> /// <param name="associationId">The Id of the existing profile association for the instance.</param> /// <returns>Async task.</returns> public async Task ReplaceInstanceProfile(string instanceId, string credsProfileName, string associationId) { try { await _amazonEc2.ReplaceIamInstanceProfileAssociationAsync( new ReplaceIamInstanceProfileAssociationRequest() { AssociationId = associationId, IamInstanceProfile = new IamInstanceProfileSpecification() { Name = credsProfileName } }); // Allow time before resetting. Thread.Sleep(25000); await _amazonEc2.RebootInstancesAsync( new RebootInstancesRequest(new List<string>() { instanceId })); Thread.Sleep(25000); var instanceReady = false; var retries = 5; while (retries-- > 0 && !instanceReady) { var instancesPaginator = _amazonSsm.Paginators.DescribeInstanceInformation( new DescribeInstanceInformationRequest()); // Get the entire list using the paginator. await foreach (var instance in instancesPaginator.InstanceInformationList) { instanceReady = instance.InstanceId == instanceId; if (instanceReady) { break; } } } Console.WriteLine("Waiting for instance to be running."); await WaitForInstanceState(instanceId, InstanceStateName.Running); Console.WriteLine("Instance ready."); Console.WriteLine($"Sending restart command to instance {instanceId}"); await _amazonSsm.SendCommandAsync( new SendCommandRequest() { InstanceIds = new List<string>() { instanceId }, DocumentName = "AWS-RunShellScript", Parameters = new Dictionary<string, List<string>>() { { "commands", new List<string>() { "cd / && sudo python3 server.py 80" } } } }); Console.WriteLine($"Restarted the web server on instance {instanceId}"); } catch (AmazonEC2Exception ec2Exception) { if (ec2Exception.ErrorCode == "InvalidInstanceID.NotFound") { _logger.LogError(ec2Exception, $"Instance {instanceId} not found"); } throw; } catch (Exception ex) { _logger.LogError(ex, $"An error occurred while replacing the template.: {ex.Message}"); throw; } }

下列程式碼範例示範如何使用 RunInstances

AWS SDK for .NET
注意

還有更多 on GitHub。尋找完整範例,並了解如何在 AWS 程式碼範例儲存庫中設定和執行。

/// <summary> /// Create and run an EC2 instance. /// </summary> /// <param name="ImageId">The image Id of the image used as a basis for the /// EC2 instance.</param> /// <param name="instanceType">The instance type of the EC2 instance to create.</param> /// <param name="keyName">The name of the key pair to associate with the /// instance.</param> /// <param name="groupId">The Id of the Amazon EC2 security group that will be /// allowed to interact with the new EC2 instance.</param> /// <returns>The instance Id of the new EC2 instance.</returns> public async Task<string> RunInstances(string imageId, string instanceType, string keyName, string groupId) { try { var request = new RunInstancesRequest { ImageId = imageId, InstanceType = instanceType, KeyName = keyName, MinCount = 1, MaxCount = 1, SecurityGroupIds = new List<string> { groupId } }; var response = await _amazonEC2.RunInstancesAsync(request); var instanceId = response.Reservation.Instances[0].InstanceId; Console.Write("Waiting for the instance to start."); await WaitForInstanceState(instanceId, InstanceStateName.Running); return instanceId; } catch (AmazonEC2Exception ec2Exception) { if (ec2Exception.ErrorCode == "InvalidGroupId.NotFound") { _logger.LogError( $"GroupId {groupId} was not found. {ec2Exception.Message}"); } throw; } catch (Exception ex) { _logger.LogError( $"An error occurred while running the instance.: {ex.Message}"); throw; } }
  • 如需 API 詳細資訊,請參閱 RunInstances AWS SDK for .NET 參考中的 API

下列程式碼範例示範如何使用 StartInstances

AWS SDK for .NET
注意

還有更多 on GitHub。尋找完整範例,並了解如何在 AWS 程式碼範例儲存庫中設定和執行。

/// <summary> /// Start an EC2 instance. /// </summary> /// <param name="ec2InstanceId">The instance Id of the Amazon EC2 instance /// to start.</param> /// <returns>Async task.</returns> public async Task StartInstances(string ec2InstanceId) { try { var request = new StartInstancesRequest { InstanceIds = new List<string> { ec2InstanceId }, }; await _amazonEC2.StartInstancesAsync(request); Console.Write("Waiting for instance to start. "); await WaitForInstanceState(ec2InstanceId, InstanceStateName.Running); } catch (AmazonEC2Exception ec2Exception) { if (ec2Exception.ErrorCode == "InvalidInstanceId") { _logger.LogError( $"InstanceId is invalid, unable to start. {ec2Exception.Message}"); } throw; } catch (Exception ex) { _logger.LogError( $"An error occurred while starting the instance.: {ex.Message}"); throw; } } /// <summary> /// Wait until an EC2 instance is in a specified state. /// </summary> /// <param name="instanceId">The instance Id.</param> /// <param name="stateName">The state to wait for.</param> /// <returns>A Boolean value indicating the success of the action.</returns> public async Task<bool> WaitForInstanceState(string instanceId, InstanceStateName stateName) { var request = new DescribeInstancesRequest { InstanceIds = new List<string> { instanceId } }; // Wait until the instance is in the specified state. var hasState = false; do { // Wait 5 seconds. Thread.Sleep(5000); // Check for the desired state. var response = await _amazonEC2.DescribeInstancesAsync(request); var instance = response.Reservations[0].Instances[0]; hasState = instance.State.Name == stateName; Console.Write(". "); } while (!hasState); return hasState; }
  • 如需 API 詳細資訊,請參閱 StartInstances AWS SDK for .NET 參考中的 API

下列程式碼範例示範如何使用 StopInstances

AWS SDK for .NET
注意

還有更多 on GitHub。尋找完整範例,並了解如何在 AWS 程式碼範例儲存庫中設定和執行。

/// <summary> /// Stop an EC2 instance. /// </summary> /// <param name="ec2InstanceId">The instance Id of the EC2 instance to /// stop.</param> /// <returns>Async task.</returns> public async Task StopInstances(string ec2InstanceId) { try { var request = new StopInstancesRequest { InstanceIds = new List<string> { ec2InstanceId }, }; await _amazonEC2.StopInstancesAsync(request); Console.Write("Waiting for the instance to stop."); await WaitForInstanceState(ec2InstanceId, InstanceStateName.Stopped); Console.WriteLine("\nThe instance has stopped."); } catch (AmazonEC2Exception ec2Exception) { if (ec2Exception.ErrorCode == "InvalidInstanceId") { _logger.LogError( $"InstanceId is invalid, unable to stop. {ec2Exception.Message}"); } throw; } catch (Exception ex) { _logger.LogError( $"An error occurred while stopping the instance.: {ex.Message}"); throw; } } /// <summary> /// Wait until an EC2 instance is in a specified state. /// </summary> /// <param name="instanceId">The instance Id.</param> /// <param name="stateName">The state to wait for.</param> /// <returns>A Boolean value indicating the success of the action.</returns> public async Task<bool> WaitForInstanceState(string instanceId, InstanceStateName stateName) { var request = new DescribeInstancesRequest { InstanceIds = new List<string> { instanceId } }; // Wait until the instance is in the specified state. var hasState = false; do { // Wait 5 seconds. Thread.Sleep(5000); // Check for the desired state. var response = await _amazonEC2.DescribeInstancesAsync(request); var instance = response.Reservations[0].Instances[0]; hasState = instance.State.Name == stateName; Console.Write(". "); } while (!hasState); return hasState; }
  • 如需 API 詳細資訊,請參閱 StopInstances AWS SDK for .NET 參考中的 API

下列程式碼範例示範如何使用 TerminateInstances

AWS SDK for .NET
注意

還有更多 on GitHub。尋找完整範例,並了解如何在 AWS 程式碼範例儲存庫中設定和執行。

/// <summary> /// Terminate an EC2 instance. /// </summary> /// <param name="ec2InstanceId">The instance Id of the EC2 instance /// to terminate.</param> /// <returns>Async task.</returns> public async Task<List<InstanceStateChange>> TerminateInstances(string ec2InstanceId) { try { var request = new TerminateInstancesRequest { InstanceIds = new List<string> { ec2InstanceId } }; var response = await _amazonEC2.TerminateInstancesAsync(request); Console.Write("Waiting for the instance to terminate."); await WaitForInstanceState(ec2InstanceId, InstanceStateName.Terminated); Console.WriteLine($"\nThe instance {ec2InstanceId} has been terminated."); return response.TerminatingInstances; } catch (AmazonEC2Exception ec2Exception) { if (ec2Exception.ErrorCode == "InvalidInstanceId") { _logger.LogError( $"InstanceId is invalid, unable to terminate. {ec2Exception.Message}"); } throw; } catch (Exception ex) { _logger.LogError( $"An error occurred while terminating the instance.: {ex.Message}"); throw; } } /// <summary> /// Wait until an EC2 instance is in a specified state. /// </summary> /// <param name="instanceId">The instance Id.</param> /// <param name="stateName">The state to wait for.</param> /// <returns>A Boolean value indicating the success of the action.</returns> public async Task<bool> WaitForInstanceState(string instanceId, InstanceStateName stateName) { var request = new DescribeInstancesRequest { InstanceIds = new List<string> { instanceId } }; // Wait until the instance is in the specified state. var hasState = false; do { // Wait 5 seconds. Thread.Sleep(5000); // Check for the desired state. var response = await _amazonEC2.DescribeInstancesAsync(request); var instance = response.Reservations[0].Instances[0]; hasState = instance.State.Name == stateName; Console.Write(". "); } while (!hasState); return hasState; }

案例

下列程式碼範例會示範如何建立負載平衡的 Web 服務,以傳回書籍、影片和歌曲建議。此範例顯示服務如何回應失故障,以及如何在發生故障時重組服務以提高復原能力。

  • 使用 Amazon EC2 Auto Scaling 群組,根據啟動範本建立 Amazon Elastic Compute Cloud (Amazon EC2) 執行個體,並將執行個體數量保持在指定範圍內。

  • 使用 Elastic Load Balancing 處理和分發 HTTP 請求。

  • 監控 Auto Scaling 群組中執行個體的運作狀態,並且只將請求轉送給運作良好的執行個體。

  • 在每個 EC2 執行個體上執行 Python Web 伺服器來處理 HTTP 請求。Web 伺服器會回應建議和運作狀態檢查。

  • 使用 Amazon DynamoDB 資料表模擬建議服務。

  • 透過更新 AWS Systems Manager 參數來控制 Web 伺服器對請求和運作狀態檢查的回應。

AWS SDK for .NET
注意

還有更多 on GitHub。尋找完整範例,並了解如何在 AWS 程式碼範例儲存庫中設定和執行。

在命令提示中執行互動式案例。

static async Task Main(string[] args) { _configuration = new ConfigurationBuilder() .SetBasePath(Directory.GetCurrentDirectory()) .AddJsonFile("settings.json") // Load settings from .json file. .AddJsonFile("settings.local.json", true) // Optionally, load local settings. .Build(); // Set up dependency injection for the AWS services. using var host = Host.CreateDefaultBuilder(args) .ConfigureLogging(logging => logging.AddFilter("System", LogLevel.Debug) .AddFilter<DebugLoggerProvider>("Microsoft", LogLevel.Information) .AddFilter<ConsoleLoggerProvider>("Microsoft", LogLevel.Trace)) .ConfigureServices((_, services) => services.AddAWSService<IAmazonIdentityManagementService>() .AddAWSService<IAmazonDynamoDB>() .AddAWSService<IAmazonElasticLoadBalancingV2>() .AddAWSService<IAmazonSimpleSystemsManagement>() .AddAWSService<IAmazonAutoScaling>() .AddAWSService<IAmazonEC2>() .AddTransient<AutoScalerWrapper>() .AddTransient<ElasticLoadBalancerWrapper>() .AddTransient<SmParameterWrapper>() .AddTransient<Recommendations>() .AddSingleton<IConfiguration>(_configuration) ) .Build(); ServicesSetup(host); ResourcesSetup(); try { Console.WriteLine(new string('-', 80)); Console.WriteLine("Welcome to the Resilient Architecture Example Scenario."); Console.WriteLine(new string('-', 80)); await Deploy(true); Console.WriteLine("Now let's begin the scenario."); Console.WriteLine(new string('-', 80)); await Demo(true); Console.WriteLine(new string('-', 80)); Console.WriteLine("Finally, let's clean up our resources."); Console.WriteLine(new string('-', 80)); await DestroyResources(true); Console.WriteLine(new string('-', 80)); Console.WriteLine("Resilient Architecture Example Scenario is complete."); Console.WriteLine(new string('-', 80)); } catch (Exception ex) { Console.WriteLine(new string('-', 80)); Console.WriteLine($"There was a problem running the scenario: {ex.Message}"); await DestroyResources(true); Console.WriteLine(new string('-', 80)); } } /// <summary> /// Setup any common resources, also used for integration testing. /// </summary> public static void ResourcesSetup() { _httpClient = new HttpClient(); } /// <summary> /// Populate the services for use within the console application. /// </summary> /// <param name="host">The services host.</param> private static void ServicesSetup(IHost host) { _elasticLoadBalancerWrapper = host.Services.GetRequiredService<ElasticLoadBalancerWrapper>(); _iamClient = host.Services.GetRequiredService<IAmazonIdentityManagementService>(); _recommendations = host.Services.GetRequiredService<Recommendations>(); _autoScalerWrapper = host.Services.GetRequiredService<AutoScalerWrapper>(); _smParameterWrapper = host.Services.GetRequiredService<SmParameterWrapper>(); } /// <summary> /// Deploy necessary resources for the scenario. /// </summary> /// <param name="interactive">True to run as interactive.</param> /// <returns>True if successful.</returns> public static async Task<bool> Deploy(bool interactive) { var protocol = "HTTP"; var port = 80; var sshPort = 22; Console.WriteLine( "\nFor this demo, we'll use the AWS SDK for .NET to create several AWS resources\n" + "to set up a load-balanced web service endpoint and explore some ways to make it resilient\n" + "against various kinds of failures.\n\n" + "Some of the resources create by this demo are:\n"); Console.WriteLine( "\t* A DynamoDB table that the web service depends on to provide book, movie, and song recommendations."); Console.WriteLine( "\t* An EC2 launch template that defines EC2 instances that each contain a Python web server."); Console.WriteLine( "\t* An EC2 Auto Scaling group that manages EC2 instances across several Availability Zones."); Console.WriteLine( "\t* An Elastic Load Balancing (ELB) load balancer that targets the Auto Scaling group to distribute requests."); Console.WriteLine(new string('-', 80)); Console.WriteLine("Press Enter when you're ready to start deploying resources."); if (interactive) Console.ReadLine(); // Create and populate the DynamoDB table. var databaseTableName = _configuration["databaseName"]; var recommendationsPath = Path.Join(_configuration["resourcePath"], "recommendations_objects.json"); Console.WriteLine($"Creating and populating a DynamoDB table named {databaseTableName}."); await _recommendations.CreateDatabaseWithName(databaseTableName); await _recommendations.PopulateDatabase(databaseTableName, recommendationsPath); Console.WriteLine(new string('-', 80)); // Create the EC2 Launch Template. Console.WriteLine( $"Creating an EC2 launch template that runs 'server_startup_script.sh' when an instance starts.\n" + "\nThis script starts a Python web server defined in the `server.py` script. The web server\n" + "listens to HTTP requests on port 80 and responds to requests to '/' and to '/healthcheck'.\n" + "For demo purposes, this server is run as the root user. In production, the best practice is to\n" + "run a web server, such as Apache, with least-privileged credentials."); Console.WriteLine( "\nThe template also defines an IAM policy that each instance uses to assume a role that grants\n" + "permissions to access the DynamoDB recommendation table and Systems Manager parameters\n" + "that control the flow of the demo."); var startupScriptPath = Path.Join(_configuration["resourcePath"], "server_startup_script.sh"); var instancePolicyPath = Path.Join(_configuration["resourcePath"], "instance_policy.json"); await _autoScalerWrapper.CreateTemplate(startupScriptPath, instancePolicyPath); Console.WriteLine(new string('-', 80)); Console.WriteLine( "Creating an EC2 Auto Scaling group that maintains three EC2 instances, each in a different\n" + "Availability Zone.\n"); var zones = await _autoScalerWrapper.DescribeAvailabilityZones(); await _autoScalerWrapper.CreateGroupOfSize(3, _autoScalerWrapper.GroupName, zones); Console.WriteLine(new string('-', 80)); Console.WriteLine( "At this point, you have EC2 instances created. Once each instance starts, it listens for\n" + "HTTP requests. You can see these instances in the console or continue with the demo.\n"); Console.WriteLine(new string('-', 80)); Console.WriteLine("Press Enter when you're ready to continue."); if (interactive) Console.ReadLine(); Console.WriteLine("Creating variables that control the flow of the demo."); await _smParameterWrapper.Reset(); Console.WriteLine( "\nCreating an Elastic Load Balancing target group and load balancer. The target group\n" + "defines how the load balancer connects to instances. The load balancer provides a\n" + "single endpoint where clients connect and dispatches requests to instances in the group."); var defaultVpc = await _autoScalerWrapper.GetDefaultVpc(); var subnets = await _autoScalerWrapper.GetAllVpcSubnetsForZones(defaultVpc.VpcId, zones); var subnetIds = subnets.Select(s => s.SubnetId).ToList(); var targetGroup = await _elasticLoadBalancerWrapper.CreateTargetGroupOnVpc(_elasticLoadBalancerWrapper.TargetGroupName, protocol, port, defaultVpc.VpcId); await _elasticLoadBalancerWrapper.CreateLoadBalancerAndListener(_elasticLoadBalancerWrapper.LoadBalancerName, subnetIds, targetGroup); await _autoScalerWrapper.AttachLoadBalancerToGroup(_autoScalerWrapper.GroupName, targetGroup.TargetGroupArn); Console.WriteLine("\nVerifying access to the load balancer endpoint..."); var endPoint = await _elasticLoadBalancerWrapper.GetEndpointForLoadBalancerByName(_elasticLoadBalancerWrapper.LoadBalancerName); var loadBalancerAccess = await _elasticLoadBalancerWrapper.VerifyLoadBalancerEndpoint(endPoint); if (!loadBalancerAccess) { Console.WriteLine("\nCouldn't connect to the load balancer, verifying that the port is open..."); var ipString = await _httpClient.GetStringAsync("https://checkip.amazonaws.com"); ipString = ipString.Trim(); var defaultSecurityGroup = await _autoScalerWrapper.GetDefaultSecurityGroupForVpc(defaultVpc); var portIsOpen = _autoScalerWrapper.VerifyInboundPortForGroup(defaultSecurityGroup, port, ipString); var sshPortIsOpen = _autoScalerWrapper.VerifyInboundPortForGroup(defaultSecurityGroup, sshPort, ipString); if (!portIsOpen) { Console.WriteLine( "\nFor this example to work, the default security group for your default VPC must\n" + "allows access from this computer. You can either add it automatically from this\n" + "example or add it yourself using the AWS Management Console.\n"); if (!interactive || GetYesNoResponse( "Do you want to add a rule to the security group to allow inbound traffic from your computer's IP address?")) { await _autoScalerWrapper.OpenInboundPort(defaultSecurityGroup.GroupId, port, ipString); } } if (!sshPortIsOpen) { if (!interactive || GetYesNoResponse( "Do you want to add a rule to the security group to allow inbound SSH traffic for debugging from your computer's IP address?")) { await _autoScalerWrapper.OpenInboundPort(defaultSecurityGroup.GroupId, sshPort, ipString); } } loadBalancerAccess = await _elasticLoadBalancerWrapper.VerifyLoadBalancerEndpoint(endPoint); } if (loadBalancerAccess) { Console.WriteLine("Your load balancer is ready. You can access it by browsing to:"); Console.WriteLine($"\thttp://{endPoint}\n"); } else { Console.WriteLine( "\nCouldn't get a successful response from the load balancer endpoint. Troubleshoot by\n" + "manually verifying that your VPC and security group are configured correctly and that\n" + "you can successfully make a GET request to the load balancer endpoint:\n"); Console.WriteLine($"\thttp://{endPoint}\n"); } Console.WriteLine(new string('-', 80)); Console.WriteLine("Press Enter when you're ready to continue with the demo."); if (interactive) Console.ReadLine(); return true; } /// <summary> /// Demonstrate the steps of the scenario. /// </summary> /// <param name="interactive">True to run as an interactive scenario.</param> /// <returns>Async task.</returns> public static async Task<bool> Demo(bool interactive) { var ssmOnlyPolicy = Path.Join(_configuration["resourcePath"], "ssm_only_policy.json"); Console.WriteLine(new string('-', 80)); Console.WriteLine("Resetting parameters to starting values for demo."); await _smParameterWrapper.Reset(); Console.WriteLine("\nThis part of the demonstration shows how to toggle different parts of the system\n" + "to create situations where the web service fails, and shows how using a resilient\n" + "architecture can keep the web service running in spite of these failures."); Console.WriteLine(new string('-', 88)); Console.WriteLine("At the start, the load balancer endpoint returns recommendations and reports that all targets are healthy."); if (interactive) await DemoActionChoices(); Console.WriteLine($"The web service running on the EC2 instances gets recommendations by querying a DynamoDB table.\n" + $"The table name is contained in a Systems Manager parameter named '{_smParameterWrapper.TableParameter}'.\n" + $"To simulate a failure of the recommendation service, let's set this parameter to name a non-existent table.\n"); await _smParameterWrapper.PutParameterByName(_smParameterWrapper.TableParameter, "this-is-not-a-table"); Console.WriteLine("\nNow, sending a GET request to the load balancer endpoint returns a failure code. But, the service reports as\n" + "healthy to the load balancer because shallow health checks don't check for failure of the recommendation service."); if (interactive) await DemoActionChoices(); Console.WriteLine("Instead of failing when the recommendation service fails, the web service can return a static response."); Console.WriteLine("While this is not a perfect solution, it presents the customer with a somewhat better experience than failure."); await _smParameterWrapper.PutParameterByName(_smParameterWrapper.FailureResponseParameter, "static"); Console.WriteLine("\nNow, sending a GET request to the load balancer endpoint returns a static response."); Console.WriteLine("The service still reports as healthy because health checks are still shallow."); if (interactive) await DemoActionChoices(); Console.WriteLine("Let's reinstate the recommendation service.\n"); await _smParameterWrapper.PutParameterByName(_smParameterWrapper.TableParameter, _smParameterWrapper.TableName); Console.WriteLine( "\nLet's also substitute bad credentials for one of the instances in the target group so that it can't\n" + "access the DynamoDB recommendation table.\n" ); await _autoScalerWrapper.CreateInstanceProfileWithName( _autoScalerWrapper.BadCredsPolicyName, _autoScalerWrapper.BadCredsRoleName, _autoScalerWrapper.BadCredsProfileName, ssmOnlyPolicy, new List<string> { "AmazonSSMManagedInstanceCore" } ); var instances = await _autoScalerWrapper.GetInstancesByGroupName(_autoScalerWrapper.GroupName); var badInstanceId = instances.First(); var instanceProfile = await _autoScalerWrapper.GetInstanceProfile(badInstanceId); Console.WriteLine( $"Replacing the profile for instance {badInstanceId} with a profile that contains\n" + "bad credentials...\n" ); await _autoScalerWrapper.ReplaceInstanceProfile( badInstanceId, _autoScalerWrapper.BadCredsProfileName, instanceProfile.AssociationId ); Console.WriteLine( "Now, sending a GET request to the load balancer endpoint returns either a recommendation or a static response,\n" + "depending on which instance is selected by the load balancer.\n" ); if (interactive) await DemoActionChoices(); Console.WriteLine("\nLet's implement a deep health check. For this demo, a deep health check tests whether"); Console.WriteLine("the web service can access the DynamoDB table that it depends on for recommendations. Note that"); Console.WriteLine("the deep health check is only for ELB routing and not for Auto Scaling instance health."); Console.WriteLine("This kind of deep health check is not recommended for Auto Scaling instance health, because it"); Console.WriteLine("risks accidental termination of all instances in the Auto Scaling group when a dependent service fails."); Console.WriteLine("\nBy implementing deep health checks, the load balancer can detect when one of the instances is failing"); Console.WriteLine("and take that instance out of rotation."); await _smParameterWrapper.PutParameterByName(_smParameterWrapper.HealthCheckParameter, "deep"); Console.WriteLine($"\nNow, checking target health indicates that the instance with bad credentials ({badInstanceId})"); Console.WriteLine("is unhealthy. Note that it might take a minute or two for the load balancer to detect the unhealthy"); Console.WriteLine("instance. Sending a GET request to the load balancer endpoint always returns a recommendation, because"); Console.WriteLine("the load balancer takes unhealthy instances out of its rotation."); if (interactive) await DemoActionChoices(); Console.WriteLine("\nBecause the instances in this demo are controlled by an auto scaler, the simplest way to fix an unhealthy"); Console.WriteLine("instance is to terminate it and let the auto scaler start a new instance to replace it."); await _autoScalerWrapper.TryTerminateInstanceById(badInstanceId); Console.WriteLine($"\nEven while the instance is terminating and the new instance is starting, sending a GET"); Console.WriteLine("request to the web service continues to get a successful recommendation response because"); Console.WriteLine("starts and reports as healthy, it is included in the load balancing rotation."); Console.WriteLine("Note that terminating and replacing an instance typically takes several minutes, during which time you"); Console.WriteLine("can see the changing health check status until the new instance is running and healthy."); if (interactive) await DemoActionChoices(); Console.WriteLine("\nIf the recommendation service fails now, deep health checks mean all instances report as unhealthy."); await _smParameterWrapper.PutParameterByName(_smParameterWrapper.TableParameter, "this-is-not-a-table"); Console.WriteLine($"\nWhen all instances are unhealthy, the load balancer continues to route requests even to"); Console.WriteLine("unhealthy instances, allowing them to fail open and return a static response rather than fail"); Console.WriteLine("closed and report failure to the customer."); if (interactive) await DemoActionChoices(); await _smParameterWrapper.Reset(); Console.WriteLine(new string('-', 80)); return true; } /// <summary> /// Clean up the resources from the scenario. /// </summary> /// <param name="interactive">True to ask the user for cleanup.</param> /// <returns>Async task.</returns> public static async Task<bool> DestroyResources(bool interactive) { Console.WriteLine(new string('-', 80)); Console.WriteLine( "To keep things tidy and to avoid unwanted charges on your account, we can clean up all AWS resources\n" + "that were created for this demo." ); if (!interactive || GetYesNoResponse("Do you want to clean up all demo resources? (y/n) ")) { await _elasticLoadBalancerWrapper.DeleteLoadBalancerByName(_elasticLoadBalancerWrapper.LoadBalancerName); await _elasticLoadBalancerWrapper.DeleteTargetGroupByName(_elasticLoadBalancerWrapper.TargetGroupName); await _autoScalerWrapper.TerminateAndDeleteAutoScalingGroupWithName(_autoScalerWrapper.GroupName); await _autoScalerWrapper.DeleteKeyPairByName(_autoScalerWrapper.KeyPairName); await _autoScalerWrapper.DeleteTemplateByName(_autoScalerWrapper.LaunchTemplateName); await _autoScalerWrapper.DeleteInstanceProfile( _autoScalerWrapper.BadCredsProfileName, _autoScalerWrapper.BadCredsRoleName ); await _recommendations.DestroyDatabaseByName(_recommendations.TableName); } else { Console.WriteLine( "Ok, we'll leave the resources intact.\n" + "Don't forget to delete them when you're done with them or you might incur unexpected charges." ); } Console.WriteLine(new string('-', 80)); return true; }

建立包裝 Auto Scaling 和 Amazon EC2 動作的類別。

/// <summary> /// Encapsulates Amazon EC2 Auto Scaling and EC2 management methods. /// </summary> public class AutoScalerWrapper { private readonly IAmazonAutoScaling _amazonAutoScaling; private readonly IAmazonEC2 _amazonEc2; private readonly IAmazonSimpleSystemsManagement _amazonSsm; private readonly IAmazonIdentityManagementService _amazonIam; private readonly ILogger<AutoScalerWrapper> _logger; private readonly string _instanceType = ""; private readonly string _amiParam = ""; private readonly string _launchTemplateName = ""; private readonly string _groupName = ""; private readonly string _instancePolicyName = ""; private readonly string _instanceRoleName = ""; private readonly string _instanceProfileName = ""; private readonly string _badCredsProfileName = ""; private readonly string _badCredsRoleName = ""; private readonly string _badCredsPolicyName = ""; private readonly string _keyPairName = ""; public string GroupName => _groupName; public string KeyPairName => _keyPairName; public string LaunchTemplateName => _launchTemplateName; public string InstancePolicyName => _instancePolicyName; public string BadCredsProfileName => _badCredsProfileName; public string BadCredsRoleName => _badCredsRoleName; public string BadCredsPolicyName => _badCredsPolicyName; /// <summary> /// Constructor for the AutoScalerWrapper. /// </summary> /// <param name="amazonAutoScaling">The injected AutoScaling client.</param> /// <param name="amazonEc2">The injected EC2 client.</param> /// <param name="amazonIam">The injected IAM client.</param> /// <param name="amazonSsm">The injected SSM client.</param> public AutoScalerWrapper( IAmazonAutoScaling amazonAutoScaling, IAmazonEC2 amazonEc2, IAmazonSimpleSystemsManagement amazonSsm, IAmazonIdentityManagementService amazonIam, IConfiguration configuration, ILogger<AutoScalerWrapper> logger) { _amazonAutoScaling = amazonAutoScaling; _amazonEc2 = amazonEc2; _amazonSsm = amazonSsm; _amazonIam = amazonIam; _logger = logger; var prefix = configuration["resourcePrefix"]; _instanceType = configuration["instanceType"]; _amiParam = configuration["amiParam"]; _launchTemplateName = prefix + "-template"; _groupName = prefix + "-group"; _instancePolicyName = prefix + "-pol"; _instanceRoleName = prefix + "-role"; _instanceProfileName = prefix + "-prof"; _badCredsPolicyName = prefix + "-bc-pol"; _badCredsRoleName = prefix + "-bc-role"; _badCredsProfileName = prefix + "-bc-prof"; _keyPairName = prefix + "-key-pair"; } /// <summary> /// Create a policy, role, and profile that is associated with instances with a specified name. /// An instance's associated profile defines a role that is assumed by the /// instance.The role has attached policies that specify the AWS permissions granted to /// clients that run on the instance. /// </summary> /// <param name="policyName">Name to use for the policy.</param> /// <param name="roleName">Name to use for the role.</param> /// <param name="profileName">Name to use for the profile.</param> /// <param name="ssmOnlyPolicyFile">Path to a policy file for SSM.</param> /// <param name="awsManagedPolicies">AWS Managed policies to be attached to the role.</param> /// <returns>The Arn of the profile.</returns> public async Task<string> CreateInstanceProfileWithName( string policyName, string roleName, string profileName, string ssmOnlyPolicyFile, List<string>? awsManagedPolicies = null) { var assumeRoleDoc = "{" + "\"Version\": \"2012-10-17\"," + "\"Statement\": [{" + "\"Effect\": \"Allow\"," + "\"Principal\": {" + "\"Service\": [" + "\"ec2.amazonaws.com\"" + "]" + "}," + "\"Action\": \"sts:AssumeRole\"" + "}]" + "}"; var policyDocument = await File.ReadAllTextAsync(ssmOnlyPolicyFile); var policyArn = ""; try { var createPolicyResult = await _amazonIam.CreatePolicyAsync( new CreatePolicyRequest { PolicyName = policyName, PolicyDocument = policyDocument }); policyArn = createPolicyResult.Policy.Arn; } catch (EntityAlreadyExistsException) { // The policy already exists, so we look it up to get the Arn. var policiesPaginator = _amazonIam.Paginators.ListPolicies( new ListPoliciesRequest() { Scope = PolicyScopeType.Local }); // Get the entire list using the paginator. await foreach (var policy in policiesPaginator.Policies) { if (policy.PolicyName.Equals(policyName)) { policyArn = policy.Arn; } } if (policyArn == null) { throw new InvalidOperationException("Policy not found"); } } try { await _amazonIam.CreateRoleAsync(new CreateRoleRequest() { RoleName = roleName, AssumeRolePolicyDocument = assumeRoleDoc, }); await _amazonIam.AttachRolePolicyAsync(new AttachRolePolicyRequest() { RoleName = roleName, PolicyArn = policyArn }); if (awsManagedPolicies != null) { foreach (var awsPolicy in awsManagedPolicies) { await _amazonIam.AttachRolePolicyAsync(new AttachRolePolicyRequest() { PolicyArn = $"arn:aws:iam::aws:policy/{awsPolicy}", RoleName = roleName }); } } } catch (EntityAlreadyExistsException) { Console.WriteLine("Role already exists."); } string profileArn = ""; try { var profileCreateResponse = await _amazonIam.CreateInstanceProfileAsync( new CreateInstanceProfileRequest() { InstanceProfileName = profileName }); // Allow time for the profile to be ready. profileArn = profileCreateResponse.InstanceProfile.Arn; Thread.Sleep(10000); await _amazonIam.AddRoleToInstanceProfileAsync( new AddRoleToInstanceProfileRequest() { InstanceProfileName = profileName, RoleName = roleName }); } catch (EntityAlreadyExistsException) { Console.WriteLine("Policy already exists."); var profileGetResponse = await _amazonIam.GetInstanceProfileAsync( new GetInstanceProfileRequest() { InstanceProfileName = profileName }); profileArn = profileGetResponse.InstanceProfile.Arn; } return profileArn; } /// <summary> /// Create a new key pair and save the file. /// </summary> /// <param name="newKeyPairName">The name of the new key pair.</param> /// <returns>Async task.</returns> public async Task CreateKeyPair(string newKeyPairName) { try { var keyResponse = await _amazonEc2.CreateKeyPairAsync( new CreateKeyPairRequest() { KeyName = newKeyPairName }); await File.WriteAllTextAsync($"{newKeyPairName}.pem", keyResponse.KeyPair.KeyMaterial); Console.WriteLine($"Created key pair {newKeyPairName}."); } catch (AlreadyExistsException) { Console.WriteLine("Key pair already exists."); } } /// <summary> /// Delete the key pair and file by name. /// </summary> /// <param name="deleteKeyPairName">The key pair to delete.</param> /// <returns>Async task.</returns> public async Task DeleteKeyPairByName(string deleteKeyPairName) { try { await _amazonEc2.DeleteKeyPairAsync( new DeleteKeyPairRequest() { KeyName = deleteKeyPairName }); File.Delete($"{deleteKeyPairName}.pem"); } catch (FileNotFoundException) { Console.WriteLine($"Key pair {deleteKeyPairName} not found."); } } /// <summary> /// Creates an Amazon EC2 launch template to use with Amazon EC2 Auto Scaling. /// The launch template specifies a Bash script in its user data field that runs after /// the instance is started. This script installs the Python packages and starts a Python /// web server on the instance. /// </summary> /// <param name="startupScriptPath">The path to a Bash script file that is run.</param> /// <param name="instancePolicyPath">The path to a permissions policy to create and attach to the profile.</param> /// <returns>The template object.</returns> public async Task<Amazon.EC2.Model.LaunchTemplate> CreateTemplate(string startupScriptPath, string instancePolicyPath) { try { await CreateKeyPair(_keyPairName); await CreateInstanceProfileWithName(_instancePolicyName, _instanceRoleName, _instanceProfileName, instancePolicyPath); var startServerText = await File.ReadAllTextAsync(startupScriptPath); var plainTextBytes = System.Text.Encoding.UTF8.GetBytes(startServerText); var amiLatest = await _amazonSsm.GetParameterAsync( new GetParameterRequest() { Name = _amiParam }); var amiId = amiLatest.Parameter.Value; var launchTemplateResponse = await _amazonEc2.CreateLaunchTemplateAsync( new CreateLaunchTemplateRequest() { LaunchTemplateName = _launchTemplateName, LaunchTemplateData = new RequestLaunchTemplateData() { InstanceType = _instanceType, ImageId = amiId, IamInstanceProfile = new LaunchTemplateIamInstanceProfileSpecificationRequest() { Name = _instanceProfileName }, KeyName = _keyPairName, UserData = System.Convert.ToBase64String(plainTextBytes) } }); return launchTemplateResponse.LaunchTemplate; } catch (AmazonEC2Exception ec2Exception) { if (ec2Exception.ErrorCode == "InvalidLaunchTemplateName.AlreadyExistsException") { _logger.LogError($"Could not create the template, the name {_launchTemplateName} already exists. " + $"Please try again with a unique name."); } throw; } catch (Exception ex) { _logger.LogError($"An error occurred while creating the template.: {ex.Message}"); throw; } } /// <summary> /// Get a list of Availability Zones in the AWS Region of the Amazon EC2 Client. /// </summary> /// <returns>A list of availability zones.</returns> public async Task<List<string>> DescribeAvailabilityZones() { try { var zoneResponse = await _amazonEc2.DescribeAvailabilityZonesAsync( new DescribeAvailabilityZonesRequest()); return zoneResponse.AvailabilityZones.Select(z => z.ZoneName).ToList(); } catch (AmazonEC2Exception ec2Exception) { _logger.LogError($"An Amazon EC2 error occurred while listing availability zones.: {ec2Exception.Message}"); throw; } catch (Exception ex) { _logger.LogError($"An error occurred while listing availability zones.: {ex.Message}"); throw; } } /// <summary> /// Create an EC2 Auto Scaling group of a specified size and name. /// </summary> /// <param name="groupSize">The size for the group.</param> /// <param name="groupName">The name for the group.</param> /// <param name="availabilityZones">The availability zones for the group.</param> /// <returns>Async task.</returns> public async Task CreateGroupOfSize(int groupSize, string groupName, List<string> availabilityZones) { try { await _amazonAutoScaling.CreateAutoScalingGroupAsync( new CreateAutoScalingGroupRequest() { AutoScalingGroupName = groupName, AvailabilityZones = availabilityZones, LaunchTemplate = new Amazon.AutoScaling.Model.LaunchTemplateSpecification() { LaunchTemplateName = _launchTemplateName, Version = "$Default" }, MaxSize = groupSize, MinSize = groupSize }); Console.WriteLine($"Created EC2 Auto Scaling group {groupName} with size {groupSize}."); } catch (EntityAlreadyExistsException) { Console.WriteLine($"EC2 Auto Scaling group {groupName} already exists."); } } /// <summary> /// Get the default VPC for the account. /// </summary> /// <returns>The default VPC object.</returns> public async Task<Vpc> GetDefaultVpc() { try { var vpcResponse = await _amazonEc2.DescribeVpcsAsync( new DescribeVpcsRequest() { Filters = new List<Amazon.EC2.Model.Filter>() { new("is-default", new List<string>() { "true" }) } }); return vpcResponse.Vpcs[0]; } catch (AmazonEC2Exception ec2Exception) { if (ec2Exception.ErrorCode == "UnauthorizedOperation") { _logger.LogError(ec2Exception, $"You do not have the necessary permissions to describe VPCs."); } throw; } catch (Exception ex) { _logger.LogError(ex, $"An error occurred while describing the vpcs.: {ex.Message}"); throw; } } /// <summary> /// Get all the subnets for a Vpc in a set of availability zones. /// </summary> /// <param name="vpcId">The Id of the Vpc.</param> /// <param name="availabilityZones">The list of availability zones.</param> /// <returns>The collection of subnet objects.</returns> public async Task<List<Subnet>> GetAllVpcSubnetsForZones(string vpcId, List<string> availabilityZones) { try { var subnets = new List<Subnet>(); var subnetPaginator = _amazonEc2.Paginators.DescribeSubnets( new DescribeSubnetsRequest() { Filters = new List<Amazon.EC2.Model.Filter>() { new("vpc-id", new List<string>() { vpcId }), new("availability-zone", availabilityZones), new("default-for-az", new List<string>() { "true" }) } }); // Get the entire list using the paginator. await foreach (var subnet in subnetPaginator.Subnets) { subnets.Add(subnet); } return subnets; } catch (AmazonEC2Exception ec2Exception) { if (ec2Exception.ErrorCode == "InvalidVpcID.NotFound") { _logger.LogError(ec2Exception, $"The specified VPC ID {vpcId} does not exist."); } throw; } catch (Exception ex) { _logger.LogError(ex, $"An error occurred while describing the subnets.: {ex.Message}"); throw; } } /// <summary> /// Delete a launch template by name. /// </summary> /// <param name="templateName">The name of the template to delete.</param> /// <returns>Async task.</returns> public async Task DeleteTemplateByName(string templateName) { try { await _amazonEc2.DeleteLaunchTemplateAsync( new DeleteLaunchTemplateRequest() { LaunchTemplateName = templateName }); } catch (AmazonEC2Exception ec2Exception) { if (ec2Exception.ErrorCode == "InvalidLaunchTemplateName.NotFoundException") { _logger.LogError( $"Could not delete the template, the name {_launchTemplateName} was not found."); } throw; } catch (Exception ex) { _logger.LogError($"An error occurred while deleting the template.: {ex.Message}"); throw; } } /// <summary> /// Detaches a role from an instance profile, detaches policies from the role, /// and deletes all the resources. /// </summary> /// <param name="profileName">The name of the profile to delete.</param> /// <param name="roleName">The name of the role to delete.</param> /// <returns>Async task.</returns> public async Task DeleteInstanceProfile(string profileName, string roleName) { try { await _amazonIam.RemoveRoleFromInstanceProfileAsync( new RemoveRoleFromInstanceProfileRequest() { InstanceProfileName = profileName, RoleName = roleName }); await _amazonIam.DeleteInstanceProfileAsync( new DeleteInstanceProfileRequest() { InstanceProfileName = profileName }); var attachedPolicies = await _amazonIam.ListAttachedRolePoliciesAsync( new ListAttachedRolePoliciesRequest() { RoleName = roleName }); foreach (var policy in attachedPolicies.AttachedPolicies) { await _amazonIam.DetachRolePolicyAsync( new DetachRolePolicyRequest() { RoleName = roleName, PolicyArn = policy.PolicyArn }); // Delete the custom policies only. if (!policy.PolicyArn.StartsWith("arn:aws:iam::aws")) { await _amazonIam.DeletePolicyAsync( new Amazon.IdentityManagement.Model.DeletePolicyRequest() { PolicyArn = policy.PolicyArn }); } } await _amazonIam.DeleteRoleAsync( new DeleteRoleRequest() { RoleName = roleName }); } catch (NoSuchEntityException) { Console.WriteLine($"Instance profile {profileName} does not exist."); } } /// <summary> /// Gets data about the instances in an EC2 Auto Scaling group by its group name. /// </summary> /// <param name="group">The name of the auto scaling group.</param> /// <returns>A collection of instance Ids.</returns> public async Task<IEnumerable<string>> GetInstancesByGroupName(string group) { var instanceResponse = await _amazonAutoScaling.DescribeAutoScalingGroupsAsync( new DescribeAutoScalingGroupsRequest() { AutoScalingGroupNames = new List<string>() { group } }); var instanceIds = instanceResponse.AutoScalingGroups.SelectMany( g => g.Instances.Select(i => i.InstanceId)); return instanceIds; } /// <summary> /// Get the instance profile association data for an instance. /// </summary> /// <param name="instanceId">The Id of the instance.</param> /// <returns>Instance profile associations data.</returns> public async Task<IamInstanceProfileAssociation> GetInstanceProfile(string instanceId) { try { var response = await _amazonEc2.DescribeIamInstanceProfileAssociationsAsync( new DescribeIamInstanceProfileAssociationsRequest() { Filters = new List<Amazon.EC2.Model.Filter>() { new("instance-id", new List<string>() { instanceId }) }, }); return response.IamInstanceProfileAssociations[0]; } catch (AmazonEC2Exception ec2Exception) { if (ec2Exception.ErrorCode == "InvalidInstanceID.NotFound") { _logger.LogError(ec2Exception, $"Instance {instanceId} not found"); } throw; } catch (Exception ex) { _logger.LogError(ex, $"An error occurred while creating the template.: {ex.Message}"); throw; } } /// <summary> /// Replace the profile associated with a running instance. After the profile is replaced, the instance /// is rebooted to ensure that it uses the new profile. When the instance is ready, Systems Manager is /// used to restart the Python web server. /// </summary> /// <param name="instanceId">The Id of the instance to update.</param> /// <param name="credsProfileName">The name of the new profile to associate with the specified instance.</param> /// <param name="associationId">The Id of the existing profile association for the instance.</param> /// <returns>Async task.</returns> public async Task ReplaceInstanceProfile(string instanceId, string credsProfileName, string associationId) { try { await _amazonEc2.ReplaceIamInstanceProfileAssociationAsync( new ReplaceIamInstanceProfileAssociationRequest() { AssociationId = associationId, IamInstanceProfile = new IamInstanceProfileSpecification() { Name = credsProfileName } }); // Allow time before resetting. Thread.Sleep(25000); await _amazonEc2.RebootInstancesAsync( new RebootInstancesRequest(new List<string>() { instanceId })); Thread.Sleep(25000); var instanceReady = false; var retries = 5; while (retries-- > 0 && !instanceReady) { var instancesPaginator = _amazonSsm.Paginators.DescribeInstanceInformation( new DescribeInstanceInformationRequest()); // Get the entire list using the paginator. await foreach (var instance in instancesPaginator.InstanceInformationList) { instanceReady = instance.InstanceId == instanceId; if (instanceReady) { break; } } } Console.WriteLine("Waiting for instance to be running."); await WaitForInstanceState(instanceId, InstanceStateName.Running); Console.WriteLine("Instance ready."); Console.WriteLine($"Sending restart command to instance {instanceId}"); await _amazonSsm.SendCommandAsync( new SendCommandRequest() { InstanceIds = new List<string>() { instanceId }, DocumentName = "AWS-RunShellScript", Parameters = new Dictionary<string, List<string>>() { { "commands", new List<string>() { "cd / && sudo python3 server.py 80" } } } }); Console.WriteLine($"Restarted the web server on instance {instanceId}"); } catch (AmazonEC2Exception ec2Exception) { if (ec2Exception.ErrorCode == "InvalidInstanceID.NotFound") { _logger.LogError(ec2Exception, $"Instance {instanceId} not found"); } throw; } catch (Exception ex) { _logger.LogError(ex, $"An error occurred while replacing the template.: {ex.Message}"); throw; } } /// <summary> /// Try to terminate an instance by its Id. /// </summary> /// <param name="instanceId">The Id of the instance to terminate.</param> /// <returns>Async task.</returns> public async Task TryTerminateInstanceById(string instanceId) { var stopping = false; Console.WriteLine($"Stopping {instanceId}..."); while (!stopping) { try { await _amazonAutoScaling.TerminateInstanceInAutoScalingGroupAsync( new TerminateInstanceInAutoScalingGroupRequest() { InstanceId = instanceId, ShouldDecrementDesiredCapacity = false }); stopping = true; } catch (ScalingActivityInProgressException) { Console.WriteLine($"Scaling activity in progress for {instanceId}. Waiting..."); Thread.Sleep(10000); } } } /// <summary> /// Tries to delete the EC2 Auto Scaling group. If the group is in use or in progress, /// waits and retries until the group is successfully deleted. /// </summary> /// <param name="groupName">The name of the group to try to delete.</param> /// <returns>Async task.</returns> public async Task TryDeleteGroupByName(string groupName) { var stopped = false; while (!stopped) { try { await _amazonAutoScaling.DeleteAutoScalingGroupAsync( new DeleteAutoScalingGroupRequest() { AutoScalingGroupName = groupName }); stopped = true; } catch (Exception e) when ((e is ScalingActivityInProgressException) || (e is Amazon.AutoScaling.Model.ResourceInUseException)) { Console.WriteLine($"Some instances are still running. Waiting..."); Thread.Sleep(10000); } } } /// <summary> /// Terminate instances and delete the Auto Scaling group by name. /// </summary> /// <param name="groupName">The name of the group to delete.</param> /// <returns>Async task.</returns> public async Task TerminateAndDeleteAutoScalingGroupWithName(string groupName) { var describeGroupsResponse = await _amazonAutoScaling.DescribeAutoScalingGroupsAsync( new DescribeAutoScalingGroupsRequest() { AutoScalingGroupNames = new List<string>() { groupName } }); if (describeGroupsResponse.AutoScalingGroups.Any()) { // Update the size to 0. await _amazonAutoScaling.UpdateAutoScalingGroupAsync( new UpdateAutoScalingGroupRequest() { AutoScalingGroupName = groupName, MinSize = 0 }); var group = describeGroupsResponse.AutoScalingGroups[0]; foreach (var instance in group.Instances) { await TryTerminateInstanceById(instance.InstanceId); } await TryDeleteGroupByName(groupName); } else { Console.WriteLine($"No groups found with name {groupName}."); } } /// <summary> /// Get the default security group for a specified Vpc. /// </summary> /// <param name="vpc">The Vpc to search.</param> /// <returns>The default security group.</returns> public async Task<SecurityGroup> GetDefaultSecurityGroupForVpc(Vpc vpc) { var groupResponse = await _amazonEc2.DescribeSecurityGroupsAsync( new DescribeSecurityGroupsRequest() { Filters = new List<Amazon.EC2.Model.Filter>() { new ("group-name", new List<string>() { "default" }), new ("vpc-id", new List<string>() { vpc.VpcId }) } }); return groupResponse.SecurityGroups[0]; } /// <summary> /// Verify the default security group of a Vpc allows ingress from the calling computer. /// This can be done by allowing ingress from this computer's IP address. /// In some situations, such as connecting from a corporate network, you must instead specify /// a prefix list Id. You can also temporarily open the port to any IP address while running this example. /// If you do, be sure to remove public access when you're done. /// </summary> /// <param name="vpc">The group to check.</param> /// <param name="port">The port to verify.</param> /// <param name="ipAddress">This computer's IP address.</param> /// <returns>True if the ip address is allowed on the group.</returns> public bool VerifyInboundPortForGroup(SecurityGroup group, int port, string ipAddress) { var portIsOpen = false; foreach (var ipPermission in group.IpPermissions) { if (ipPermission.FromPort == port) { foreach (var ipRange in ipPermission.Ipv4Ranges) { var cidr = ipRange.CidrIp; if (cidr.StartsWith(ipAddress) || cidr == "0.0.0.0/0") { portIsOpen = true; } } if (ipPermission.PrefixListIds.Any()) { portIsOpen = true; } if (!portIsOpen) { Console.WriteLine("The inbound rule does not appear to be open to either this computer's IP\n" + "address, to all IP addresses (0.0.0.0/0), or to a prefix list ID."); } else { break; } } } return portIsOpen; } /// <summary> /// Add an ingress rule to the specified security group that allows access on the /// specified port from the specified IP address. /// </summary> /// <param name="groupId">The Id of the security group to modify.</param> /// <param name="port">The port to open.</param> /// <param name="ipAddress">The IP address to allow access.</param> /// <returns>Async task.</returns> public async Task OpenInboundPort(string groupId, int port, string ipAddress) { await _amazonEc2.AuthorizeSecurityGroupIngressAsync( new AuthorizeSecurityGroupIngressRequest() { GroupId = groupId, IpPermissions = new List<IpPermission>() { new IpPermission() { FromPort = port, ToPort = port, IpProtocol = "tcp", Ipv4Ranges = new List<IpRange>() { new IpRange() { CidrIp = $"{ipAddress}/32" } } } } }); } /// <summary> /// Attaches an Elastic Load Balancing (ELB) target group to this EC2 Auto Scaling group. /// The /// </summary> /// <param name="autoScalingGroupName">The name of the Auto Scaling group.</param> /// <param name="targetGroupArn">The Arn for the target group.</param> /// <returns>Async task.</returns> public async Task AttachLoadBalancerToGroup(string autoScalingGroupName, string targetGroupArn) { await _amazonAutoScaling.AttachLoadBalancerTargetGroupsAsync( new AttachLoadBalancerTargetGroupsRequest() { AutoScalingGroupName = autoScalingGroupName, TargetGroupARNs = new List<string>() { targetGroupArn } }); } /// <summary> /// Wait until an EC2 instance is in a specified state. /// </summary> /// <param name="instanceId">The instance Id.</param> /// <param name="stateName">The state to wait for.</param> /// <returns>A Boolean value indicating the success of the action.</returns> public async Task<bool> WaitForInstanceState(string instanceId, InstanceStateName stateName) { var request = new DescribeInstancesRequest { InstanceIds = new List<string> { instanceId } }; // Wait until the instance is in the specified state. var hasState = false; do { // Wait 5 seconds. Thread.Sleep(5000); // Check for the desired state. var response = await _amazonEc2.DescribeInstancesAsync(request); var instance = response.Reservations[0].Instances[0]; hasState = instance.State.Name == stateName; Console.Write(". "); } while (!hasState); return hasState; } }

建立包裝 Elastic Load Balancing 動作的類別。

/// <summary> /// Encapsulates Elastic Load Balancer actions. /// </summary> public class ElasticLoadBalancerWrapper { private readonly IAmazonElasticLoadBalancingV2 _amazonElasticLoadBalancingV2; private string? _endpoint = null; private readonly string _targetGroupName = ""; private readonly string _loadBalancerName = ""; HttpClient _httpClient = new(); public string TargetGroupName => _targetGroupName; public string LoadBalancerName => _loadBalancerName; /// <summary> /// Constructor for the Elastic Load Balancer wrapper. /// </summary> /// <param name="amazonElasticLoadBalancingV2">The injected load balancing v2 client.</param> /// <param name="configuration">The injected configuration.</param> public ElasticLoadBalancerWrapper( IAmazonElasticLoadBalancingV2 amazonElasticLoadBalancingV2, IConfiguration configuration) { _amazonElasticLoadBalancingV2 = amazonElasticLoadBalancingV2; var prefix = configuration["resourcePrefix"]; _targetGroupName = prefix + "-tg"; _loadBalancerName = prefix + "-lb"; } /// <summary> /// Get the HTTP Endpoint of a load balancer by its name. /// </summary> /// <param name="loadBalancerName">The name of the load balancer.</param> /// <returns>The HTTP endpoint.</returns> public async Task<string> GetEndpointForLoadBalancerByName(string loadBalancerName) { if (_endpoint == null) { var endpointResponse = await _amazonElasticLoadBalancingV2.DescribeLoadBalancersAsync( new DescribeLoadBalancersRequest() { Names = new List<string>() { loadBalancerName } }); _endpoint = endpointResponse.LoadBalancers[0].DNSName; } return _endpoint; } /// <summary> /// Return the GET response for an endpoint as text. /// </summary> /// <param name="endpoint">The endpoint for the request.</param> /// <returns>The request response.</returns> public async Task<string> GetEndPointResponse(string endpoint) { var endpointResponse = await _httpClient.GetAsync($"http://{endpoint}"); var textResponse = await endpointResponse.Content.ReadAsStringAsync(); return textResponse!; } /// <summary> /// Get the target health for a group by name. /// </summary> /// <param name="groupName">The name of the group.</param> /// <returns>The collection of health descriptions.</returns> public async Task<List<TargetHealthDescription>> CheckTargetHealthForGroup(string groupName) { List<TargetHealthDescription> result = null!; try { var groupResponse = await _amazonElasticLoadBalancingV2.DescribeTargetGroupsAsync( new DescribeTargetGroupsRequest() { Names = new List<string>() { groupName } }); var healthResponse = await _amazonElasticLoadBalancingV2.DescribeTargetHealthAsync( new DescribeTargetHealthRequest() { TargetGroupArn = groupResponse.TargetGroups[0].TargetGroupArn }); ; result = healthResponse.TargetHealthDescriptions; } catch (TargetGroupNotFoundException) { Console.WriteLine($"Target group {groupName} not found."); } return result; } /// <summary> /// Create an Elastic Load Balancing target group. The target group specifies how the load balancer forwards /// requests to instances in the group and how instance health is checked. /// /// To speed up this demo, the health check is configured with shortened times and lower thresholds. In production, /// you might want to decrease the sensitivity of your health checks to avoid unwanted failures. /// </summary> /// <param name="groupName">The name for the group.</param> /// <param name="protocol">The protocol, such as HTTP.</param> /// <param name="port">The port to use to forward requests, such as 80.</param> /// <param name="vpcId">The Id of the Vpc in which the load balancer exists.</param> /// <returns>The new TargetGroup object.</returns> public async Task<TargetGroup> CreateTargetGroupOnVpc(string groupName, ProtocolEnum protocol, int port, string vpcId) { var createResponse = await _amazonElasticLoadBalancingV2.CreateTargetGroupAsync( new CreateTargetGroupRequest() { Name = groupName, Protocol = protocol, Port = port, HealthCheckPath = "/healthcheck", HealthCheckIntervalSeconds = 10, HealthCheckTimeoutSeconds = 5, HealthyThresholdCount = 2, UnhealthyThresholdCount = 2, VpcId = vpcId }); var targetGroup = createResponse.TargetGroups[0]; return targetGroup; } /// <summary> /// Create an Elastic Load Balancing load balancer that uses the specified subnets /// and forwards requests to the specified target group. /// </summary> /// <param name="name">The name for the new load balancer.</param> /// <param name="subnetIds">Subnets for the load balancer.</param> /// <param name="targetGroup">Target group for forwarded requests.</param> /// <returns>The new LoadBalancer object.</returns> public async Task<LoadBalancer> CreateLoadBalancerAndListener(string name, List<string> subnetIds, TargetGroup targetGroup) { var createLbResponse = await _amazonElasticLoadBalancingV2.CreateLoadBalancerAsync( new CreateLoadBalancerRequest() { Name = name, Subnets = subnetIds }); var loadBalancerArn = createLbResponse.LoadBalancers[0].LoadBalancerArn; // Wait for load balancer to be available. var loadBalancerReady = false; while (!loadBalancerReady) { try { var describeResponse = await _amazonElasticLoadBalancingV2.DescribeLoadBalancersAsync( new DescribeLoadBalancersRequest() { Names = new List<string>() { name } }); var loadBalancerState = describeResponse.LoadBalancers[0].State.Code; loadBalancerReady = loadBalancerState == LoadBalancerStateEnum.Active; } catch (LoadBalancerNotFoundException) { loadBalancerReady = false; } Thread.Sleep(10000); } // Create the listener. await _amazonElasticLoadBalancingV2.CreateListenerAsync( new CreateListenerRequest() { LoadBalancerArn = loadBalancerArn, Protocol = targetGroup.Protocol, Port = targetGroup.Port, DefaultActions = new List<Action>() { new Action() { Type = ActionTypeEnum.Forward, TargetGroupArn = targetGroup.TargetGroupArn } } }); return createLbResponse.LoadBalancers[0]; } /// <summary> /// Verify this computer can successfully send a GET request to the /// load balancer endpoint. /// </summary> /// <param name="endpoint">The endpoint to check.</param> /// <returns>True if successful.</returns> public async Task<bool> VerifyLoadBalancerEndpoint(string endpoint) { var success = false; var retries = 3; while (!success && retries > 0) { try { var endpointResponse = await _httpClient.GetAsync($"http://{endpoint}"); Console.WriteLine($"Response: {endpointResponse.StatusCode}."); if (endpointResponse.IsSuccessStatusCode) { success = true; } else { retries = 0; } } catch (HttpRequestException) { Console.WriteLine("Connection error, retrying..."); retries--; Thread.Sleep(10000); } } return success; } /// <summary> /// Delete a load balancer by its specified name. /// </summary> /// <param name="name">The name of the load balancer to delete.</param> /// <returns>Async task.</returns> public async Task DeleteLoadBalancerByName(string name) { try { var describeLoadBalancerResponse = await _amazonElasticLoadBalancingV2.DescribeLoadBalancersAsync( new DescribeLoadBalancersRequest() { Names = new List<string>() { name } }); var lbArn = describeLoadBalancerResponse.LoadBalancers[0].LoadBalancerArn; await _amazonElasticLoadBalancingV2.DeleteLoadBalancerAsync( new DeleteLoadBalancerRequest() { LoadBalancerArn = lbArn } ); } catch (LoadBalancerNotFoundException) { Console.WriteLine($"Load balancer {name} not found."); } } /// <summary> /// Delete a TargetGroup by its specified name. /// </summary> /// <param name="groupName">Name of the group to delete.</param> /// <returns>Async task.</returns> public async Task DeleteTargetGroupByName(string groupName) { var done = false; while (!done) { try { var groupResponse = await _amazonElasticLoadBalancingV2.DescribeTargetGroupsAsync( new DescribeTargetGroupsRequest() { Names = new List<string>() { groupName } }); var targetArn = groupResponse.TargetGroups[0].TargetGroupArn; await _amazonElasticLoadBalancingV2.DeleteTargetGroupAsync( new DeleteTargetGroupRequest() { TargetGroupArn = targetArn }); Console.WriteLine($"Deleted load balancing target group {groupName}."); done = true; } catch (TargetGroupNotFoundException) { Console.WriteLine( $"Target group {groupName} not found, could not delete."); done = true; } catch (ResourceInUseException) { Console.WriteLine("Target group not yet released, waiting..."); Thread.Sleep(10000); } } } }

建立使用 DynamoDB 模擬建議服務的類別。

/// <summary> /// Encapsulates a DynamoDB table to use as a service that recommends books, movies, and songs. /// </summary> public class Recommendations { private readonly IAmazonDynamoDB _amazonDynamoDb; private readonly DynamoDBContext _context; private readonly string _tableName; public string TableName => _tableName; /// <summary> /// Constructor for the Recommendations service. /// </summary> /// <param name="amazonDynamoDb">The injected DynamoDb client.</param> /// <param name="configuration">The injected configuration.</param> public Recommendations(IAmazonDynamoDB amazonDynamoDb, IConfiguration configuration) { _amazonDynamoDb = amazonDynamoDb; _context = new DynamoDBContext(_amazonDynamoDb); _tableName = configuration["databaseName"]!; } /// <summary> /// Create the DynamoDb table with a specified name. /// </summary> /// <param name="tableName">The name for the table.</param> /// <returns>True when ready.</returns> public async Task<bool> CreateDatabaseWithName(string tableName) { try { Console.Write($"Creating table {tableName}..."); var createRequest = new CreateTableRequest() { TableName = tableName, AttributeDefinitions = new List<AttributeDefinition>() { new AttributeDefinition() { AttributeName = "MediaType", AttributeType = ScalarAttributeType.S }, new AttributeDefinition() { AttributeName = "ItemId", AttributeType = ScalarAttributeType.N } }, KeySchema = new List<KeySchemaElement>() { new KeySchemaElement() { AttributeName = "MediaType", KeyType = KeyType.HASH }, new KeySchemaElement() { AttributeName = "ItemId", KeyType = KeyType.RANGE } }, ProvisionedThroughput = new ProvisionedThroughput() { ReadCapacityUnits = 5, WriteCapacityUnits = 5 } }; await _amazonDynamoDb.CreateTableAsync(createRequest); // Wait until the table is ACTIVE and then report success. Console.Write("\nWaiting for table to become active..."); var request = new DescribeTableRequest { TableName = tableName }; TableStatus status; do { Thread.Sleep(2000); var describeTableResponse = await _amazonDynamoDb.DescribeTableAsync(request); status = describeTableResponse.Table.TableStatus; Console.Write("."); } while (status != "ACTIVE"); return status == TableStatus.ACTIVE; } catch (ResourceInUseException) { Console.WriteLine($"Table {tableName} already exists."); return false; } } /// <summary> /// Populate the database table with data from a specified path. /// </summary> /// <param name="databaseTableName">The name of the table.</param> /// <param name="recommendationsPath">The path of the recommendations data.</param> /// <returns>Async task.</returns> public async Task PopulateDatabase(string databaseTableName, string recommendationsPath) { var recommendationsText = await File.ReadAllTextAsync(recommendationsPath); var records = JsonSerializer.Deserialize<RecommendationModel[]>(recommendationsText); var batchWrite = _context.CreateBatchWrite<RecommendationModel>(); foreach (var record in records!) { batchWrite.AddPutItem(record); } await batchWrite.ExecuteAsync(); } /// <summary> /// Delete the recommendation table by name. /// </summary> /// <param name="tableName">The name of the recommendation table.</param> /// <returns>Async task.</returns> public async Task DestroyDatabaseByName(string tableName) { try { await _amazonDynamoDb.DeleteTableAsync( new DeleteTableRequest() { TableName = tableName }); Console.WriteLine($"Table {tableName} was deleted."); } catch (ResourceNotFoundException) { Console.WriteLine($"Table {tableName} not found"); } } }

建立包裝 Systems Manager 動作的類別。

/// <summary> /// Encapsulates Systems Manager parameter operations. This example uses these parameters /// to drive the demonstration of resilient architecture, such as failure of a dependency or /// how the service responds to a health check. /// </summary> public class SmParameterWrapper { private readonly IAmazonSimpleSystemsManagement _amazonSimpleSystemsManagement; private readonly string _tableParameter = "doc-example-resilient-architecture-table"; private readonly string _failureResponseParameter = "doc-example-resilient-architecture-failure-response"; private readonly string _healthCheckParameter = "doc-example-resilient-architecture-health-check"; private readonly string _tableName = ""; public string TableParameter => _tableParameter; public string TableName => _tableName; public string HealthCheckParameter => _healthCheckParameter; public string FailureResponseParameter => _failureResponseParameter; /// <summary> /// Constructor for the SmParameterWrapper. /// </summary> /// <param name="amazonSimpleSystemsManagement">The injected Simple Systems Management client.</param> /// <param name="configuration">The injected configuration.</param> public SmParameterWrapper(IAmazonSimpleSystemsManagement amazonSimpleSystemsManagement, IConfiguration configuration) { _amazonSimpleSystemsManagement = amazonSimpleSystemsManagement; _tableName = configuration["databaseName"]!; } /// <summary> /// Reset the Systems Manager parameters to starting values for the demo. /// </summary> /// <returns>Async task.</returns> public async Task Reset() { await this.PutParameterByName(_tableParameter, _tableName); await this.PutParameterByName(_failureResponseParameter, "none"); await this.PutParameterByName(_healthCheckParameter, "shallow"); } /// <summary> /// Set the value of a named Systems Manager parameter. /// </summary> /// <param name="name">The name of the parameter.</param> /// <param name="value">The value to set.</param> /// <returns>Async task.</returns> public async Task PutParameterByName(string name, string value) { await _amazonSimpleSystemsManagement.PutParameterAsync( new PutParameterRequest() { Name = name, Value = value, Overwrite = true }); } }