Pelajari dasar-dasar Amazon EC2 dengan AWS SDK - AWS SDKContoh Kode

Ada lebih banyak AWS SDK contoh yang tersedia di GitHub repo SDKContoh AWS Dokumen.

Terjemahan disediakan oleh mesin penerjemah. Jika konten terjemahan yang diberikan bertentangan dengan versi bahasa Inggris aslinya, utamakan versi bahasa Inggris.

Pelajari dasar-dasar Amazon EC2 dengan AWS SDK

Contoh-contoh kode berikut ini menunjukkan cara untuk melakukan:

  • Membuat pasangan kunci dan grup keamanan.

  • Pilih Amazon Machine Image (AMI) dan jenis instans yang kompatibel, lalu buat instance.

  • Menghentikan dan memulai ulang instans.

  • Kaitkan alamat IP Elastis dengan instans Anda.

  • Connect ke instans Anda denganSSH, lalu bersihkan sumber daya.

.NET
AWS SDK for .NET
catatan

Ada lebih banyak tentang GitHub. Temukan contoh lengkapnya dan pelajari cara pengaturan dan menjalankannya di Repositori Contoh Kode AWS.

Menjalankan skenario di prompt perintah.

/// <summary> /// Show Amazon Elastic Compute Cloud (Amazon EC2) Basics actions. /// </summary> public class EC2Basics { /// <summary> /// Perform the actions defined for the Amazon EC2 Basics scenario. /// </summary> /// <param name="args">Command line arguments.</param> /// <returns>A Task object.</returns> static async Task Main(string[] args) { // Set up dependency injection for Amazon EC2 and Amazon Simple Systems // Management Service. using var host = Microsoft.Extensions.Hosting.Host.CreateDefaultBuilder(args) .ConfigureServices((_, services) => services.AddAWSService<IAmazonEC2>() .AddAWSService<IAmazonSimpleSystemsManagement>() .AddTransient<EC2Wrapper>() .AddTransient<SsmWrapper>() ) .Build(); // Now the client is available for injection. var ec2Client = host.Services.GetRequiredService<IAmazonEC2>(); var ec2Methods = new EC2Wrapper(ec2Client); var ssmClient = host.Services.GetRequiredService<IAmazonSimpleSystemsManagement>(); var ssmMethods = new SsmWrapper(ssmClient); var uiMethods = new UiMethods(); var uniqueName = Guid.NewGuid().ToString(); var keyPairName = "mvp-example-key-pair" + uniqueName; var groupName = "ec2-scenario-group" + uniqueName; var groupDescription = "A security group created for the EC2 Basics scenario."; // Start the scenario. uiMethods.DisplayOverview(); uiMethods.PressEnter(); // 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 ec2Methods.CreateKeyPair(keyPairName); // Save key pair information to a temporary file. var tempFileName = ec2Methods.SaveKeyPair(keyPair); Console.WriteLine($"Created the key pair: {keyPair.KeyName} and saved it to: {tempFileName}"); string? answer; do { Console.Write("Would you like to list your existing key pairs? "); answer = Console.ReadLine(); } while (answer!.ToLower() != "y" && answer.ToLower() != "n"); if (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 ec2Methods.DescribeKeyPairs(""); keyPairs.ForEach(kp => { Console.WriteLine($"{kp.KeyName} created at: {kp.CreateTime} Fingerprint: {kp.KeyFingerprint}"); }); } uiMethods.PressEnter(); // Create the security group. Console.WriteLine("Let's create a security group to manage access to your instance."); var secGroupId = await ec2Methods.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 ec2Methods.DescribeSecurityGroups(secGroupId); Console.WriteLine($"Created security group {groupName} in your default VPC."); secGroups.ForEach(group => { ec2Methods.DisplaySecurityGroupInfoAsync(group); }); uiMethods.PressEnter(); Console.WriteLine("Now we'll authorize the security group we just created so that it can"); Console.WriteLine("access the EC2 instances you create."); var success = await ec2Methods.AuthorizeSecurityGroupIngress(groupName); secGroups = await ec2Methods.DescribeSecurityGroups(secGroupId); Console.WriteLine($"Now let's look at the permissions again."); secGroups.ForEach(group => { ec2Methods.DisplaySecurityGroupInfoAsync(group); }); uiMethods.PressEnter(); // Get list of available Amazon Linux 2 Amazon Machine Images (AMIs). var parameters = await ssmMethods.GetParametersByPath("/aws/service/ami-amazon-linux-latest"); List<string> imageIds = parameters.Select(param => param.Value).ToList(); var images = await ec2Methods.DescribeImages(imageIds); var i = 1; images.ForEach(image => { Console.WriteLine($"\t{i++}\t{image.Description}"); }); int choice; bool validNumber = false; 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 ec2Methods.DescribeInstanceTypes(selectedImage.Architecture); i = 1; instanceTypes.ForEach(instanceType => { Console.WriteLine($"\t{i++}\t{instanceType.InstanceType}"); }); 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"); var instanceId = await ec2Methods.RunInstances(selectedImage.ImageId, selectedInstanceType, keyPairName, secGroupId); Console.Write("Waiting for the instance to start."); var isRunning = false; do { isRunning = await ec2Methods.WaitForInstanceState(instanceId, InstanceStateName.Running); } while (!isRunning); uiMethods.PressEnter(); var instance = await ec2Methods.DescribeInstance(instanceId); uiMethods.DisplayTitle("New Instance Information"); ec2Methods.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(); Console.WriteLine("Now we'll stop the instance and then start it again to see what's changed."); await ec2Methods.StopInstances(instanceId); var hasStopped = false; do { hasStopped = await ec2Methods.WaitForInstanceState(instanceId, InstanceStateName.Stopped); } while (!hasStopped); Console.WriteLine("\nThe instance has stopped."); Console.WriteLine("Now let's start it up again."); await ec2Methods.StartInstances(instanceId); Console.Write("Waiting for instance to start. "); isRunning = false; do { isRunning = await ec2Methods.WaitForInstanceState(instanceId, InstanceStateName.Running); } while (!isRunning); Console.WriteLine("\nLet's see what changed."); instance = await ec2Methods.DescribeInstance(instanceId); uiMethods.DisplayTitle("New Instance Information"); ec2Methods.DisplayInstanceInformation(instance); Console.WriteLine("\nNotice the change in the SSH information:"); Console.WriteLine($"\tssh -i {tempFileName} ec2-user@{instance.PublicIpAddress}"); uiMethods.PressEnter(); 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 ec2Methods.StopInstances(instanceId); hasStopped = false; do { hasStopped = await ec2Methods.WaitForInstanceState(instanceId, InstanceStateName.Stopped); } while (!hasStopped); Console.WriteLine("\nThe instance has stopped."); uiMethods.PressEnter(); 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 allocationId = await ec2Methods.AllocateAddress(); Console.WriteLine("Now we will associate the Elastic IP address with our instance."); var associationId = await ec2Methods.AssociateAddress(allocationId, instanceId); // Start the instance again. Console.WriteLine("Now let's start the instance again."); await ec2Methods.StartInstances(instanceId); Console.Write("Waiting for instance to start. "); isRunning = false; do { isRunning = await ec2Methods.WaitForInstanceState(instanceId, InstanceStateName.Running); } while (!isRunning); Console.WriteLine("\nLet's see what changed."); instance = await ec2Methods.DescribeInstance(instanceId); uiMethods.DisplayTitle("Instance information"); ec2Methods.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(); await ec2Methods.StopInstances(instanceId); hasStopped = false; do { hasStopped = await ec2Methods.WaitForInstanceState(instanceId, InstanceStateName.Stopped); } while (!hasStopped); Console.WriteLine("\nThe instance has stopped."); Console.WriteLine("Now let's start it up again."); await ec2Methods.StartInstances(instanceId); Console.Write("Waiting for instance to start. "); isRunning = false; do { isRunning = await ec2Methods.WaitForInstanceState(instanceId, InstanceStateName.Running); } while (!isRunning); instance = await ec2Methods.DescribeInstance(instanceId); uiMethods.DisplayTitle("New Instance Information"); ec2Methods.DisplayInstanceInformation(instance); Console.WriteLine("Note that the IP address did not change this time."); uiMethods.PressEnter(); uiMethods.DisplayTitle("Clean up resources"); Console.WriteLine("Now let's clean up the resources we created."); // Terminate the instance. Console.WriteLine("Terminating the instance we created."); var stateChange = await ec2Methods.TerminateInstances(instanceId); // Wait for the instance state to be terminated. var hasTerminated = false; do { hasTerminated = await ec2Methods.WaitForInstanceState(instanceId, InstanceStateName.Terminated); } while (!hasTerminated); Console.WriteLine($"\nThe instance {instanceId} has been terminated."); Console.WriteLine("Now we can disassociate the Elastic IP address and release it."); // Disassociate the Elastic IP address. var disassociated = ec2Methods.DisassociateIp(associationId); // Delete the Elastic IP address. var released = ec2Methods.ReleaseAddress(allocationId); // Delete the security group. Console.WriteLine($"Deleting the Security Group: {groupName}."); success = await ec2Methods.DeleteSecurityGroup(secGroupId); if (success) { Console.WriteLine($"Successfully deleted {groupName}."); } // Delete the RSA key pair. Console.WriteLine($"Deleting the key pair: {keyPairName}"); await ec2Methods.DeleteKeyPair(keyPairName); Console.WriteLine("Deleting the temporary file with the key information."); ec2Methods.DeleteTempFile(tempFileName); uiMethods.PressEnter(); uiMethods.DisplayTitle("EC2 Basics Scenario completed."); uiMethods.PressEnter(); } }

Tentukan kelas yang membungkus EC2 tindakan.

/// <summary> /// Methods of this class perform Amazon Elastic Compute Cloud (Amazon EC2). /// </summary> public class EC2Wrapper { private readonly IAmazonEC2 _amazonEC2; public EC2Wrapper(IAmazonEC2 amazonService) { _amazonEC2 = amazonService; } /// <summary> /// Allocate an Elastic IP address. /// </summary> /// <returns>The allocation Id of the allocated address.</returns> public async Task<string> AllocateAddress() { var request = new AllocateAddressRequest(); var response = await _amazonEC2.AllocateAddressAsync(request); return response.AllocationId; } /// <summary> /// Associate an Elastic IP address to an EC2 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) { var request = new AssociateAddressRequest { AllocationId = allocationId, InstanceId = instanceId }; var response = await _amazonEC2.AssociateAddressAsync(request); return response.AssociationId; } /// <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) { // 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; } /// <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. /// </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) { var request = new CreateKeyPairRequest { KeyName = keyPairName, }; var response = await _amazonEC2.CreateKeyPairAsync(request); if (response.HttpStatusCode == HttpStatusCode.OK) { var kp = response.KeyPair; return kp; } else { Console.WriteLine("Could not create key pair."); return null; } } /// <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. /// </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) { var response = await _amazonEC2.CreateSecurityGroupAsync( new CreateSecurityGroupRequest(groupName, groupDescription)); return response.GroupId; } /// <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 (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) { var response = await _amazonEC2.DeleteSecurityGroupAsync(new DeleteSecurityGroupRequest { GroupId = groupId }); return response.HttpStatusCode == HttpStatusCode.OK; } /// <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 existing EC2 images. /// </summary> /// <returns>Async task.</returns> public async Task DescribeInstances() { // List all EC2 instances. await GetInstanceDescriptions(); string tagName = "IncludeInList"; string tagValue = "Yes"; await GetInstanceDescriptionsFiltered(tagName, tagValue); } /// <summary> /// Get information for all existing Amazon EC2 instances. /// </summary> /// <returns>Async task.</returns> public async Task GetInstanceDescriptions() { Console.WriteLine("Showing all instances:"); var paginator = _amazonEC2.Paginators.DescribeInstances(new DescribeInstancesRequest()); 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}"); } } } } /// <summary> /// Get information about EC2 instances filtered by a tag name and value. /// </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>Async task.</returns> public async Task GetInstanceDescriptionsFiltered(string tagName, string tagValue) { // This tag filters the results of the instance list. var filters = new List<Filter> { new Filter { Name = $"tag:{tagName}", Values = new List<string> { tagValue, }, }, }; var request = new DescribeInstancesRequest { Filters = filters, }; Console.WriteLine("\nShowing instances with tag: \"IncludeInList\" set to \"Yes\"."); 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}"); } } } } /// <summary> /// Describe the instance types available. /// </summary> /// <returns>A list of instance type information.</returns> public async Task<List<InstanceTypeInfo>> DescribeInstanceTypes(ArchitectureValues architecture) { 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; } /// <summary> /// Display the instance type information returned by DescribeInstanceTypesAsync. /// </summary> /// <param name="instanceTypes">The list of instance type information.</param> public void DisplayInstanceTypeInfo(List<InstanceTypeInfo> instanceTypes) { instanceTypes.ForEach(type => { Console.WriteLine($"{type.InstanceType}\t{type.MemoryInfo}"); }); } /// <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) { 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(); } /// <summary> /// Retrieve information for an Amazon EC2 security group. /// </summary> /// <param name="groupId">The Id of the Amazon EC2 security group.</param> /// <returns>A list of security group information.</returns> public async Task<List<SecurityGroup>> DescribeSecurityGroups(string groupId) { var request = new DescribeSecurityGroupsRequest(); var groupIds = new List<string> { groupId }; request.GroupIds = groupIds; var response = await _amazonEC2.DescribeSecurityGroupsAsync(request); return response.SecurityGroups; } /// <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) { var response = await _amazonEC2.DisassociateAddressAsync( new DisassociateAddressRequest { AssociationId = associationId }); return response.HttpStatusCode == HttpStatusCode.OK; } /// <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 EC2 instances. /// </summary> /// <param name="ec2InstanceId">The instance Id of the instances that will be rebooted.</param> /// <returns>Async task.</returns> public async Task RebootInstances(string ec2InstanceId) { var request = new RebootInstancesRequest { InstanceIds = new List<string> { ec2InstanceId }, }; var response = await _amazonEC2.RebootInstancesAsync(request); if (response.HttpStatusCode == System.Net.HttpStatusCode.OK) { Console.WriteLine("Instances successfully rebooted."); } else { Console.WriteLine("Could not reboot one or more instances."); } } /// <summary> /// Release an Elastic IP address. /// </summary> /// <param name="allocationId">The allocation Id of the Elastic IP address.</param> /// <returns>A Boolean value indicating the success of the action.</returns> public async Task<bool> ReleaseAddress(string allocationId) { var request = new ReleaseAddressRequest { AllocationId = allocationId }; var response = await _amazonEC2.ReleaseAddressAsync(request); return response.HttpStatusCode == HttpStatusCode.OK; } /// <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) { 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); return response.Reservation.Instances[0].InstanceId; } /// <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) { var request = new StartInstancesRequest { InstanceIds = new List<string> { ec2InstanceId }, }; var response = await _amazonEC2.StartInstancesAsync(request); if (response.StartingInstances.Count > 0) { var instances = response.StartingInstances; instances.ForEach(i => { Console.WriteLine($"Successfully started the EC2 instance with instance ID: {i.InstanceId}."); }); } } /// <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) { // In addition to the list of instance Ids, the // request can also include the following properties: // Force When true, forces the instances to // stop but you must check the integrity // of the file system. Not recommended on // Windows instances. // Hibernate When true, hibernates the instance if the // instance was enabled for hibernation when // it was launched. var request = new StopInstancesRequest { InstanceIds = new List<string> { ec2InstanceId }, }; var response = await _amazonEC2.StopInstancesAsync(request); if (response.StoppingInstances.Count > 0) { var instances = response.StoppingInstances; instances.ForEach(i => { Console.WriteLine($"Successfully stopped the EC2 Instance " + $"with InstanceID: {i.InstanceId}."); }); } } /// <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) { var request = new TerminateInstancesRequest { InstanceIds = new List<string> { ec2InstanceId } }; var response = await _amazonEC2.TerminateInstancesAsync(request); return response.TerminatingInstances; } /// <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 running. 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; } }
Bash
AWS CLI dengan skrip Bash
catatan

Ada lebih banyak tentang GitHub. Temukan contoh lengkapnya dan pelajari cara pengaturan dan menjalankannya di Repositori Contoh Kode AWS.

Jalankan skenario interaktif di penggugah/prompt perintah.

############################################################################### # function get_started_with_ec2_instances # # Runs an interactive scenario that shows how to get started using EC2 instances. # # "EC2 access" permissions are needed to run this code. # # Returns: # 0 - If successful. # 1 - If an error occurred. ############################################################################### function get_started_with_ec2_instances() { # Requires version 4 for mapfile. local required_version=4.0 # Get the current Bash version # Check if BASH_VERSION is set local current_version if [[ -n "$BASH_VERSION" ]]; then # Convert BASH_VERSION to a number for comparison current_version=$BASH_VERSION else # Get the current Bash version using the bash command current_version=$(bash --version | head -n 1 | awk '{ print $4 }') fi # Convert version strings to numbers for comparison local required_version_num current_version_num required_version_num=$(echo "$required_version" | awk -F. '{ print ($1 * 10000) + ($2 * 100) + $3 }') current_version_num=$(echo "$current_version" | awk -F. '{ print ($1 * 10000) + ($2 * 100) + $3 }') # Compare versions if ((current_version_num < required_version_num)); then echo "Error: This script requires Bash version $required_version or higher." echo "Your current Bash version is number is $current_version." exit 1 fi { if [ "$EC2_OPERATIONS_SOURCED" != "True" ]; then source ./ec2_operations.sh fi } echo_repeat "*" 88 echo "Welcome to the Amazon Elastic Compute Cloud (Amazon EC2) get started with instances demo." echo_repeat "*" 88 echo echo "Let's create an RSA key pair that you can be use to securely connect to " echo "your EC2 instance." echo -n "Enter a unique name for your key: " get_input local key_name key_name=$get_input_result local temp_dir temp_dir=$(mktemp -d) local key_file_name="$temp_dir/${key_name}.pem" if ec2_create_keypair -n "${key_name}" -f "${key_file_name}"; then echo "Created a key pair $key_name and saved the private key to $key_file_name" echo else errecho "The key pair failed to create. This demo will exit." return 1 fi chmod 400 "${key_file_name}" if yes_no_input "Do you want to list some of your key pairs? (y/n) "; then local keys_and_fingerprints keys_and_fingerprints="$(ec2_describe_key_pairs)" && { local image_name_and_id while IFS=$'\n' read -r image_name_and_id; do local entries IFS=$'\t' read -ra entries <<<"$image_name_and_id" echo "Found rsa key ${entries[0]} with fingerprint:" echo " ${entries[1]}" done <<<"$keys_and_fingerprints" } fi echo_repeat "*" 88 echo_repeat "*" 88 echo "Let's create a security group to manage access to your instance." echo -n "Enter a unique name for your security group: " get_input local security_group_name security_group_name=$get_input_result local security_group_id security_group_id=$(ec2_create_security_group -n "$security_group_name" \ -d "Security group for EC2 instance") || { errecho "The security failed to create. This demo will exit." clean_up "$key_name" "$key_file_name" return 1 } echo "Security group created with ID $security_group_id" echo local public_ip public_ip=$(curl -s http://checkip.amazonaws.com) echo "Let's add a rule to allow SSH only from your current IP address." echo "Your public IP address is $public_ip" echo -n "press return to add this rule to your security group." get_input if ! ec2_authorize_security_group_ingress -g "$security_group_id" -i "$public_ip" -p tcp -f 22 -t 22; then errecho "The security group rules failed to update. This demo will exit." clean_up "$key_name" "$key_file_name" "$security_group_id" return 1 fi echo "Security group rules updated" local security_group_description security_group_description="$(ec2_describe_security_groups -g "${security_group_id}")" || { errecho "Failed to describe security groups. This demo will exit." clean_up "$key_name" "$key_file_name" "$security_group_id" return 1 } mapfile -t parameters <<<"$security_group_description" IFS=$'\t' read -ra entries <<<"${parameters[0]}" echo "Security group: ${entries[0]}" echo " ID: ${entries[1]}" echo " VPC: ${entries[2]}" echo "Inbound permissions:" IFS=$'\t' read -ra entries <<<"${parameters[1]}" echo " IpProtocol: ${entries[0]}" echo " FromPort: ${entries[1]}" echo " ToPort: ${entries[2]}" echo " CidrIp: ${parameters[2]}" local parameters parameters="$(ssm_get_parameters_by_path -p "/aws/service/ami-amazon-linux-latest")" || { errecho "Failed to get parameters. This demo will exit." clean_up "$key_name" "$key_file_name" "$security_group_id" return 1 } local image_ids="" mapfile -t parameters <<<"$parameters" for image_name_and_id in "${parameters[@]}"; do IFS=$'\t' read -ra values <<<"$image_name_and_id" if [[ "${values[0]}" == *"amzn2"* ]]; then image_ids+="${values[1]} " fi done local images images="$(ec2_describe_images -i "$image_ids")" || { errecho "Failed to describe images. This demo will exit." clean_up "$key_name" "$key_file_name" "$security_group_id" return 1 } new_line_and_tab_to_list "$images" local images=("${list_result[@]}") # Get the size of the array local images_count=${#images[@]} if ((images_count == 0)); then errecho "No images found. This demo will exit." clean_up "$key_name" "$key_file_name" "$security_group_id" return 1 fi echo_repeat "*" 88 echo_repeat "*" 88 echo "Let's create an instance from an Amazon Linux 2 AMI. Here are some options:" for ((i = 0; i < images_count; i += 3)); do echo "$(((i / 3) + 1)) - ${images[$i]}" done integer_input "Please enter the number of the AMI you want to use: " 1 "$((images_count / 3))" local choice=$get_input_result choice=$(((choice - 1) * 3)) echo "Great choice." echo local architecture=${images[$((choice + 1))]} local image_id=${images[$((choice + 2))]} echo "Here are some instance types that support the ${architecture} architecture of the image:" response="$(ec2_describe_instance_types -a "${architecture}" -t "*.micro,*.small")" || { errecho "Failed to describe instance types. This demo will exit." clean_up "$key_name" "$key_file_name" "$security_group_id" return 1 } local instance_types mapfile -t instance_types <<<"$response" # Get the size of the array local instance_types_count=${#instance_types[@]} echo "Here are some options:" for ((i = 0; i < instance_types_count; i++)); do echo "$((i + 1)) - ${instance_types[$i]}" done integer_input "Which one do you want to use? " 1 "${#instance_types[@]} " choice=$get_input_result local instance_type=${instance_types[$((choice - 1))]} echo "Another great choice." echo echo "Creating your instance and waiting for it to start..." local instance_id instance_id=$(ec2_run_instances -i "$image_id" -t "$instance_type" -k "$key_name" -s "$security_group_id") || { errecho "Failed to run instance. This demo will exit." clean_up "$key_name" "$key_file_name" "$security_group_id" return 1 } ec2_wait_for_instance_running -i "$instance_id" echo "Your instance is ready:" echo local instance_details instance_details="$(ec2_describe_instances -i "${instance_id}")" echo print_instance_details "${instance_details}" local public_ip public_ip=$(echo "${instance_details}" | awk '{print $6}') echo echo "You can use SSH to connect to your instance" echo "If the connection attempt times out, you might have to manually update the SSH ingress rule" echo "for your IP address in the AWS Management Console." connect_to_instance "$key_file_name" "$public_ip" echo -n "Press Enter when you're ready to continue the demo: " get_input echo_repeat "*" 88 echo_repeat "*" 88 echo "Let's stop and start your instance to see what changes." echo "Stopping your instance and waiting until it's stopped..." ec2_stop_instances -i "$instance_id" ec2_wait_for_instance_stopped -i "$instance_id" echo "Your instance is stopped. Restarting..." ec2_start_instances -i "$instance_id" ec2_wait_for_instance_running -i "$instance_id" echo "Your instance is running again." local instance_details instance_details="$(ec2_describe_instances -i "${instance_id}")" print_instance_details "${instance_details}" public_ip=$(echo "${instance_details}" | awk '{print $6}') echo "Every time your instance is restarted, its public IP address changes" connect_to_instance "$key_file_name" "$public_ip" echo -n "Press Enter when you're ready to continue the demo: " get_input echo_repeat "*" 88 echo_repeat "*" 88 echo "You can allocate an Elastic IP address and associate it with your instance" echo "to keep a consistent IP address even when your instance restarts." local result result=$(ec2_allocate_address -d vpc) || { errecho "Failed to allocate an address. This demo will exit." clean_up "$key_name" "$key_file_name" "$security_group_id" "$instance_id" return 1 } local elastic_ip allocation_id elastic_ip=$(echo "$result" | awk '{print $1}') allocation_id=$(echo "$result" | awk '{print $2}') echo "Allocated static Elastic IP address: $elastic_ip" local association_id association_id=$(ec2_associate_address -i "$instance_id" -a "$allocation_id") || { errecho "Failed to associate an address. This demo will exit." clean_up "$key_name" "$key_file_name" "$security_group_id" "$instance_id" "$allocation_id" return 1 } echo "Associated your Elastic IP with your instance." echo "You can now use SSH to connect to your instance by using the Elastic IP." connect_to_instance "$key_file_name" "$elastic_ip" echo -n "Press Enter when you're ready to continue the demo: " get_input echo_repeat "*" 88 echo_repeat "*" 88 echo "Let's stop and start your instance to see what changes." echo "Stopping your instance and waiting until it's stopped..." ec2_stop_instances -i "$instance_id" ec2_wait_for_instance_stopped -i "$instance_id" echo "Your instance is stopped. Restarting..." ec2_start_instances -i "$instance_id" ec2_wait_for_instance_running -i "$instance_id" echo "Your instance is running again." local instance_details instance_details="$(ec2_describe_instances -i "${instance_id}")" print_instance_details "${instance_details}" echo "Because you have associated an Elastic IP with your instance, you can" echo "connect by using a consistent IP address after the instance restarts." connect_to_instance "$key_file_name" "$elastic_ip" echo -n "Press Enter when you're ready to continue the demo: " get_input echo_repeat "*" 88 echo_repeat "*" 88 if yes_no_input "Do you want to delete the resources created in this demo: (y/n) "; then clean_up "$key_name" "$key_file_name" "$security_group_id" "$instance_id" \ "$allocation_id" "$association_id" else echo "The following resources were not deleted." echo "Key pair: $key_name" echo "Key file: $key_file_name" echo "Security group: $security_group_id" echo "Instance: $instance_id" echo "Elastic IP address: $elastic_ip" fi } ############################################################################### # function clean_up # # This function cleans up the created resources. # $1 - The name of the ec2 key pair to delete. # $2 - The name of the key file to delete. # $3 - The ID of the security group to delete. # $4 - The ID of the instance to terminate. # $5 - The ID of the elastic IP address to release. # $6 - The ID of the elastic IP address to disassociate. # # Returns: # 0 - If successful. # 1 - If an error occurred. ############################################################################### function clean_up() { local result=0 local key_pair_name=$1 local key_file_name=$2 local security_group_id=$3 local instance_id=$4 local allocation_id=$5 local association_id=$6 if [ -n "$association_id" ]; then # bashsupport disable=BP2002 if (ec2_disassociate_address -a "$association_id"); then echo "Disassociated elastic IP address with ID $association_id" else errecho "The elastic IP address disassociation failed." result=1 fi fi if [ -n "$allocation_id" ]; then # bashsupport disable=BP2002 if (ec2_release_address -a "$allocation_id"); then echo "Released elastic IP address with ID $allocation_id" else errecho "The elastic IP address release failed." result=1 fi fi if [ -n "$instance_id" ]; then # bashsupport disable=BP2002 if (ec2_terminate_instances -i "$instance_id"); then echo "Started terminating instance with ID $instance_id" ec2_wait_for_instance_terminated -i "$instance_id" else errecho "The instance terminate failed." result=1 fi fi if [ -n "$security_group_id" ]; then # bashsupport disable=BP2002 if (ec2_delete_security_group -i "$security_group_id"); then echo "Deleted security group with ID $security_group_id" else errecho "The security group delete failed." result=1 fi fi if [ -n "$key_pair_name" ]; then # bashsupport disable=BP2002 if (ec2_delete_keypair -n "$key_pair_name"); then echo "Deleted key pair named $key_pair_name" else errecho "The key pair delete failed." result=1 fi fi if [ -n "$key_file_name" ]; then rm -f "$key_file_name" fi return $result } ############################################################################### # function ssm_get_parameters_by_path # # This function retrieves one or more parameters from the AWS Systems Manager Parameter Store # by specifying a parameter path. # # Parameters: # -p parameter_path - The path of the parameter(s) to retrieve. # # And: # 0 - If successful. # 1 - If it fails. ############################################################################### function ssm_get_parameters_by_path() { local parameter_path response local option OPTARG # Required to use getopts command in a function. # bashsupport disable=BP5008 function usage() { echo "function ssm_get_parameters_by_path" echo "Retrieves one or more parameters from the AWS Systems Manager Parameter Store by specifying a parameter path." echo " -p parameter_path - The path of the parameter(s) to retrieve." echo "" } # Retrieve the calling parameters. while getopts "p:h" option; do case "${option}" in p) parameter_path="${OPTARG}" ;; h) usage return 0 ;; \?) echo "Invalid parameter" usage return 1 ;; esac done export OPTIND=1 if [[ -z "$parameter_path" ]]; then errecho "ERROR: You must provide a parameter path with the -p parameter." usage return 1 fi response=$(aws ssm get-parameters-by-path \ --path "$parameter_path" \ --query "Parameters[*].[Name, Value]" \ --output text) || { aws_cli_error_log $? errecho "ERROR: AWS reports get-parameters-by-path operation failed.$response" return 1 } echo "$response" return 0 } ############################################################################### # function print_instance_details # # This function prints the details of an Amazon Elastic Compute Cloud (Amazon EC2) instance. # # Parameters: # instance_details - The instance details in the format "InstanceId ImageId InstanceType KeyName VpcId PublicIpAddress State.Name". # # Returns: # 0 - If successful. # 1 - If it fails. ############################################################################### function print_instance_details() { local instance_details="$1" if [[ -z "${instance_details}" ]]; then echo "Error: Missing required instance details argument." return 1 fi local instance_id image_id instance_type key_name vpc_id public_ip state instance_id=$(echo "${instance_details}" | awk '{print $1}') image_id=$(echo "${instance_details}" | awk '{print $2}') instance_type=$(echo "${instance_details}" | awk '{print $3}') key_name=$(echo "${instance_details}" | awk '{print $4}') vpc_id=$(echo "${instance_details}" | awk '{print $5}') public_ip=$(echo "${instance_details}" | awk '{print $6}') state=$(echo "${instance_details}" | awk '{print $7}') echo " ID: ${instance_id}" echo " Image ID: ${image_id}" echo " Instance type: ${instance_type}" echo " Key name: ${key_name}" echo " VPC ID: ${vpc_id}" echo " Public IP: ${public_ip}" echo " State: ${state}" return 0 } ############################################################################### # function connect_to_instance # # This function displays the public IP address of an Amazon Elastic Compute Cloud (Amazon EC2) instance and prompts the user to connect to the instance via SSH. # # Parameters: # $1 - The name of the key file used to connect to the instance. # $2 - The public IP address of the instance. # # Returns: # None ############################################################################### function connect_to_instance() { local key_file_name="$1" local public_ip="$2" # Validate the input parameters if [[ -z "$key_file_name" ]]; then echo "ERROR: You must provide a key file name as the first argument." >&2 return 1 fi if [[ -z "$public_ip" ]]; then echo "ERROR: You must provide a public IP address as the second argument." >&2 return 1 fi # Display the public IP address and connection command echo "To connect, run the following command:" echo " ssh -i ${key_file_name} ec2-user@${public_ip}" # Prompt the user to connect to the instance if yes_no_input "Do you want to connect now? (y/n) "; then echo "After you have connected, you can return to this example by typing 'exit'" ssh -i "${key_file_name}" ec2-user@"${public_ip}" fi } ############################################################################### # function get_input # # This function gets user input from the command line. # # Outputs: # User input to stdout. # # Returns: # 0 ############################################################################### function get_input() { if [ -z "${mock_input+x}" ]; then read -r get_input_result else if [ "$mock_input_array_index" -lt ${#mock_input_array[@]} ]; then get_input_result="${mock_input_array[$mock_input_array_index]}" # bashsupport disable=BP2001 # shellcheck disable=SC2206 ((mock_input_array_index++)) echo -n "$get_input_result" else echo "MOCK_INPUT_ARRAY has no more elements" 1>&2 return 1 fi fi return 0 } ############################################################################### # function yes_no_input # # This function requests a yes/no answer from the user, following to a prompt. # # Parameters: # $1 - The prompt. # # Returns: # 0 - If yes. # 1 - If no. ############################################################################### function yes_no_input() { if [ -z "$1" ]; then echo "Internal error yes_no_input" return 1 fi local index=0 local response="N" while [[ $index -lt 10 ]]; do index=$((index + 1)) echo -n "$1" if ! get_input; then return 1 fi response=$(echo "$get_input_result" | tr '[:upper:]' '[:lower:]') if [ "$response" = "y" ] || [ "$response" = "n" ]; then break else echo -e "\nPlease enter or 'y' or 'n'." fi done echo if [ "$response" = "y" ]; then return 0 else return 1 fi } ############################################################################### # function integer_input # # This function prompts the user to enter an integer within a specified range # and validates the input. # # Parameters: # $1 - The prompt message to display to the user. # $2 - The minimum value of the accepted range. # $3 - The maximum value of the accepted range. # # Returns: # The valid integer input from the user. # If the input is invalid or out of range, the function will continue # prompting the user until a valid input is provided. ############################################################################### function integer_input() { local prompt="$1" local min_value="$2" local max_value="$3" local input="" while true; do # Display the prompt message and wait for user input echo -n "$prompt" if ! get_input; then return 1 fi input="$get_input_result" # Check if the input is a valid integer if [[ "$input" =~ ^-?[0-9]+$ ]]; then # Check if the input is within the specified range if ((input >= min_value && input <= max_value)); then return 0 else echo "Error: Input, $input, must be between $min_value and $max_value." fi else echo "Error: Invalid input- $input. Please enter an integer." fi done } ############################################################################### # function new_line_and_tab_to_list # # This function takes a string input containing newlines and tabs, and # converts it into a list (array) of elements. # # Parameters: # $1 - The input string containing newlines and tabs. # # Returns: # The resulting list (array) is stored in the global variable # 'list_result'. ############################################################################### function new_line_and_tab_to_list() { local input=$1 export list_result list_result=() mapfile -t lines <<<"$input" local line for line in "${lines[@]}"; do IFS=$'\t' read -ra parameters <<<"$line" list_result+=("${parameters[@]}") done } ############################################################################### # function echo_repeat # # This function prints a string 'n' times to stdout. # # Parameters: # $1 - The string. # $2 - Number of times to print the string. # # Outputs: # String 'n' times to stdout. # # Returns: # 0 ############################################################################### function echo_repeat() { local end=$2 for ((i = 0; i < end; i++)); do echo -n "$1" done echo }

Fungsi DynamoDB yang digunakan dalam skenario ini.

############################################################################### # function ec2_create_keypair # # This function creates an Amazon Elastic Compute Cloud (Amazon EC2) ED25519 or 2048-bit RSA key pair # and writes it to a file. # # Parameters: # -n key_pair_name - A key pair name. # -f file_path - File to store the key pair. # # And: # 0 - If successful. # 1 - If it fails. ############################################################################### function ec2_create_keypair() { local key_pair_name file_path response local option OPTARG # Required to use getopts command in a function. # bashsupport disable=BP5008 function usage() { echo "function ec2_create_keypair" echo "Creates an Amazon Elastic Compute Cloud (Amazon EC2) ED25519 or 2048-bit RSA key pair" echo " and writes it to a file." echo " -n key_pair_name - A key pair name." echo " -f file_path - File to store the key pair." echo "" } # Retrieve the calling parameters. while getopts "n:f:h" option; do case "${option}" in n) key_pair_name="${OPTARG}" ;; f) file_path="${OPTARG}" ;; h) usage return 0 ;; \?) echo "Invalid parameter" usage return 1 ;; esac done export OPTIND=1 if [[ -z "$key_pair_name" ]]; then errecho "ERROR: You must provide a key name with the -n parameter." usage return 1 fi if [[ -z "$file_path" ]]; then errecho "ERROR: You must provide a file path with the -f parameter." usage return 1 fi response=$(aws ec2 create-key-pair \ --key-name "$key_pair_name" \ --query 'KeyMaterial' \ --output text) || { aws_cli_error_log ${?} errecho "ERROR: AWS reports create-access-key operation failed.$response" return 1 } if [[ -n "$file_path" ]]; then echo "$response" >"$file_path" fi return 0 } ############################################################################### # function ec2_describe_key_pairs # # This function describes one or more Amazon Elastic Compute Cloud (Amazon EC2) key pairs. # # Parameters: # -h - Display help. # # And: # 0 - If successful. # 1 - If it fails. ############################################################################### function ec2_describe_key_pairs() { local option OPTARG # Required to use getopts command in a function. # bashsupport disable=BP5008 function usage() { echo "function ec2_describe_key_pairs" echo "Describes one or more Amazon Elastic Compute Cloud (Amazon EC2) key pairs." echo " -h - Display help." echo "" } # Retrieve the calling parameters. while getopts "h" option; do case "${option}" in h) usage return 0 ;; \?) echo "Invalid parameter" usage return 1 ;; esac done export OPTIND=1 local response response=$(aws ec2 describe-key-pairs \ --query 'KeyPairs[*].[KeyName, KeyFingerprint]' \ --output text) || { aws_cli_error_log ${?} errecho "ERROR: AWS reports describe-key-pairs operation failed.$response" return 1 } echo "$response" return 0 } ############################################################################### # function ec2_create_security_group # # This function creates an Amazon Elastic Compute Cloud (Amazon EC2) security group. # # Parameters: # -n security_group_name - The name of the security group. # -d security_group_description - The description of the security group. # # Returns: # The ID of the created security group, or an error message if the operation fails. # And: # 0 - If successful. # 1 - If it fails. # ############################################################################### function ec2_create_security_group() { local security_group_name security_group_description response # Function to display usage information function usage() { echo "function ec2_create_security_group" echo "Creates an Amazon Elastic Compute Cloud (Amazon EC2) security group." echo " -n security_group_name - The name of the security group." echo " -d security_group_description - The description of the security group." echo "" } # Parse the command-line arguments while getopts "n:d:h" option; do case "${option}" in n) security_group_name="${OPTARG}" ;; d) security_group_description="${OPTARG}" ;; h) usage return 0 ;; \?) echo "Invalid parameter" usage return 1 ;; esac done export OPTIND=1 # Validate the input parameters if [[ -z "$security_group_name" ]]; then errecho "ERROR: You must provide a security group name with the -n parameter." return 1 fi if [[ -z "$security_group_description" ]]; then errecho "ERROR: You must provide a security group description with the -d parameter." return 1 fi # Create the security group response=$(aws ec2 create-security-group \ --group-name "$security_group_name" \ --description "$security_group_description" \ --query "GroupId" \ --output text) || { aws_cli_error_log ${?} errecho "ERROR: AWS reports create-security-group operation failed." errecho "$response" return 1 } echo "$response" return 0 } ############################################################################### # function ec2_describe_security_groups # # This function describes one or more Amazon Elastic Compute Cloud (Amazon EC2) security groups. # # Parameters: # -g security_group_id - The ID of the security group to describe (optional). # # And: # 0 - If successful. # 1 - If it fails. ############################################################################### function ec2_describe_security_groups() { local security_group_id response local option OPTARG # Required to use getopts command in a function. # bashsupport disable=BP5008 function usage() { echo "function ec2_describe_security_groups" echo "Describes one or more Amazon Elastic Compute Cloud (Amazon EC2) security groups." echo " -g security_group_id - The ID of the security group to describe (optional)." echo "" } # Retrieve the calling parameters. while getopts "g:h" option; do case "${option}" in g) security_group_id="${OPTARG}" ;; h) usage return 0 ;; \?) echo "Invalid parameter" usage return 1 ;; esac done export OPTIND=1 local query="SecurityGroups[*].[GroupName, GroupId, VpcId, IpPermissions[*].[IpProtocol, FromPort, ToPort, IpRanges[*].CidrIp]]" if [[ -n "$security_group_id" ]]; then response=$(aws ec2 describe-security-groups --group-ids "$security_group_id" --query "${query}" --output text) else response=$(aws ec2 describe-security-groups --query "${query}" --output text) fi local error_code=${?} if [[ $error_code -ne 0 ]]; then aws_cli_error_log $error_code errecho "ERROR: AWS reports describe-security-groups operation failed.$response" return 1 fi echo "$response" return 0 } ############################################################################### # function ec2_authorize_security_group_ingress # # This function authorizes an ingress rule for an Amazon Elastic Compute Cloud (Amazon EC2) security group. # # Parameters: # -g security_group_id - The ID of the security group. # -i ip_address - The IP address or CIDR block to authorize. # -p protocol - The protocol to authorize (e.g., tcp, udp, icmp). # -f from_port - The start of the port range to authorize. # -t to_port - The end of the port range to authorize. # # And: # 0 - If successful. # 1 - If it fails. ############################################################################### function ec2_authorize_security_group_ingress() { local security_group_id ip_address protocol from_port to_port response local option OPTARG # Required to use getopts command in a function. # bashsupport disable=BP5008 function usage() { echo "function ec2_authorize_security_group_ingress" echo "Authorizes an ingress rule for an Amazon Elastic Compute Cloud (Amazon EC2) security group." echo " -g security_group_id - The ID of the security group." echo " -i ip_address - The IP address or CIDR block to authorize." echo " -p protocol - The protocol to authorize (e.g., tcp, udp, icmp)." echo " -f from_port - The start of the port range to authorize." echo " -t to_port - The end of the port range to authorize." echo "" } # Retrieve the calling parameters. while getopts "g:i:p:f:t:h" option; do case "${option}" in g) security_group_id="${OPTARG}" ;; i) ip_address="${OPTARG}" ;; p) protocol="${OPTARG}" ;; f) from_port="${OPTARG}" ;; t) to_port="${OPTARG}" ;; h) usage return 0 ;; \?) echo "Invalid parameter" usage return 1 ;; esac done export OPTIND=1 if [[ -z "$security_group_id" ]]; then errecho "ERROR: You must provide a security group ID with the -g parameter." usage return 1 fi if [[ -z "$ip_address" ]]; then errecho "ERROR: You must provide an IP address or CIDR block with the -i parameter." usage return 1 fi if [[ -z "$protocol" ]]; then errecho "ERROR: You must provide a protocol with the -p parameter." usage return 1 fi if [[ -z "$from_port" ]]; then errecho "ERROR: You must provide a start port with the -f parameter." usage return 1 fi if [[ -z "$to_port" ]]; then errecho "ERROR: You must provide an end port with the -t parameter." usage return 1 fi response=$(aws ec2 authorize-security-group-ingress \ --group-id "$security_group_id" \ --cidr "${ip_address}/32" \ --protocol "$protocol" \ --port "$from_port-$to_port" \ --output text) || { aws_cli_error_log ${?} errecho "ERROR: AWS reports authorize-security-group-ingress operation failed.$response" return 1 } return 0 } ############################################################################### # function ec2_describe_images # # This function describes one or more Amazon Elastic Compute Cloud (Amazon EC2) images. # # Parameters: # -i image_ids - A space-separated list of image IDs (optional). # -h - Display help. # # And: # 0 - If successful. # 1 - If it fails. ############################################################################### function ec2_describe_images() { local image_ids response local option OPTARG # Required to use getopts command in a function. # bashsupport disable=BP5008 function usage() { echo "function ec2_describe_images" echo "Describes one or more Amazon Elastic Compute Cloud (Amazon EC2) images." echo " -i image_ids - A space-separated list of image IDs (optional)." echo " -h - Display help." echo "" } # Retrieve the calling parameters. while getopts "i:h" option; do case "${option}" in i) image_ids="${OPTARG}" ;; h) usage return 0 ;; \?) echo "Invalid parameter" usage return 1 ;; esac done export OPTIND=1 local aws_cli_args=() if [[ -n "$image_ids" ]]; then # shellcheck disable=SC2206 aws_cli_args+=("--image-ids" $image_ids) fi response=$(aws ec2 describe-images \ "${aws_cli_args[@]}" \ --query 'Images[*].[Description,Architecture,ImageId]' \ --output text) || { aws_cli_error_log ${?} errecho "ERROR: AWS reports describe-images operation failed.$response" return 1 } echo "$response" return 0 } ############################################################################### # ec2_describe_instance_types # # This function describes EC2 instance types filtered by processor architecture # and optionally by instance type. It takes the following arguments: # # -a, --architecture ARCHITECTURE Specify the processor architecture (e.g., x86_64) # -t, --type INSTANCE_TYPE Comma-separated list of instance types (e.g., t2.micro) # -h, --help Show the usage help # # The function prints the instance type and supported architecture for each # matching instance type. ############################################################################### function ec2_describe_instance_types() { local architecture="" local instance_types="" # bashsupport disable=BP5008 function usage() { echo "Usage: ec2_describe_instance_types [-a|--architecture ARCHITECTURE] [-t|--type INSTANCE_TYPE] [-h|--help]" echo " -a, --architecture ARCHITECTURE Specify the processor architecture (e.g., x86_64)" echo " -t, --type INSTANCE_TYPE Comma-separated list of instance types (e.g., t2.micro)" echo " -h, --help Show this help message" } while [[ $# -gt 0 ]]; do case "$1" in -a | --architecture) architecture="$2" shift 2 ;; -t | --type) instance_types="$2" shift 2 ;; -h | --help) usage return 0 ;; *) echo "Unknown argument: $1" return 1 ;; esac done if [[ -z "$architecture" ]]; then errecho "Error: Architecture not specified." usage return 1 fi if [[ -z "$instance_types" ]]; then errecho "Error: Instance type not specified." usage return 1 fi local tmp_json_file="temp_ec2.json" echo -n '[ { "Name": "processor-info.supported-architecture", "Values": [' >"$tmp_json_file" local items IFS=',' read -ra items <<<"$architecture" local array_size array_size=${#items[@]} for i in $(seq 0 $((array_size - 1))); do echo -n '"'"${items[$i]}"'"' >>"$tmp_json_file" if [[ $i -lt $((array_size - 1)) ]]; then echo -n ',' >>"$tmp_json_file" fi done echo -n ']}, { "Name": "instance-type", "Values": [' >>"$tmp_json_file" IFS=',' read -ra items <<<"$instance_types" local array_size array_size=${#items[@]} for i in $(seq 0 $((array_size - 1))); do echo -n '"'"${items[$i]}"'"' >>"$tmp_json_file" if [[ $i -lt $((array_size - 1)) ]]; then echo -n ',' >>"$tmp_json_file" fi done echo -n ']}]' >>"$tmp_json_file" local response response=$(aws ec2 describe-instance-types --filters file://"$tmp_json_file" \ --query 'InstanceTypes[*].[InstanceType]' --output text) local error_code=$? rm "$tmp_json_file" if [[ $error_code -ne 0 ]]; then aws_cli_error_log $error_code echo "ERROR: AWS reports describe-instance-types operation failed." return 1 fi echo "$response" return 0 } ############################################################################### # function ec2_run_instances # # This function launches one or more Amazon Elastic Compute Cloud (Amazon EC2) instances. # # Parameters: # -i image_id - The ID of the Amazon Machine Image (AMI) to use. # -t instance_type - The instance type to use (e.g., t2.micro). # -k key_pair_name - The name of the key pair to use. # -s security_group_id - The ID of the security group to use. # -c count - The number of instances to launch (default: 1). # -h - Display help. # # Returns: # 0 - If successful. # 1 - If it fails. ############################################################################### function ec2_run_instances() { local image_id instance_type key_pair_name security_group_id count response local option OPTARG # Required to use getopts command in a function. # bashsupport disable=BP5008 function usage() { echo "function ec2_run_instances" echo "Launches one or more Amazon Elastic Compute Cloud (Amazon EC2) instances." echo " -i image_id - The ID of the Amazon Machine Image (AMI) to use." echo " -t instance_type - The instance type to use (e.g., t2.micro)." echo " -k key_pair_name - The name of the key pair to use." echo " -s security_group_id - The ID of the security group to use." echo " -c count - The number of instances to launch (default: 1)." echo " -h - Display help." echo "" } # Retrieve the calling parameters. while getopts "i:t:k:s:c:h" option; do case "${option}" in i) image_id="${OPTARG}" ;; t) instance_type="${OPTARG}" ;; k) key_pair_name="${OPTARG}" ;; s) security_group_id="${OPTARG}" ;; c) count="${OPTARG}" ;; h) usage return 0 ;; \?) echo "Invalid parameter" usage return 1 ;; esac done export OPTIND=1 if [[ -z "$image_id" ]]; then errecho "ERROR: You must provide an Amazon Machine Image (AMI) ID with the -i parameter." usage return 1 fi if [[ -z "$instance_type" ]]; then errecho "ERROR: You must provide an instance type with the -t parameter." usage return 1 fi if [[ -z "$key_pair_name" ]]; then errecho "ERROR: You must provide a key pair name with the -k parameter." usage return 1 fi if [[ -z "$security_group_id" ]]; then errecho "ERROR: You must provide a security group ID with the -s parameter." usage return 1 fi if [[ -z "$count" ]]; then count=1 fi response=$(aws ec2 run-instances \ --image-id "$image_id" \ --instance-type "$instance_type" \ --key-name "$key_pair_name" \ --security-group-ids "$security_group_id" \ --count "$count" \ --query 'Instances[*].[InstanceId]' \ --output text) || { aws_cli_error_log ${?} errecho "ERROR: AWS reports run-instances operation failed.$response" return 1 } echo "$response" return 0 } ############################################################################### # function ec2_describe_instances # # This function describes one or more Amazon Elastic Compute Cloud (Amazon EC2) instances. # # Parameters: # -i instance_id - The ID of the instance to describe (optional). # -q query - The query to filter the response (optional). # -h - Display help. # # Returns: # 0 - If successful. # 1 - If it fails. ############################################################################### function ec2_describe_instances() { local instance_id query response local option OPTARG # Required to use getopts command in a function. # bashsupport disable=BP5008 function usage() { echo "function ec2_describe_instances" echo "Describes one or more Amazon Elastic Compute Cloud (Amazon EC2) instances." echo " -i instance_id - The ID of the instance to describe (optional)." echo " -q query - The query to filter the response (optional)." echo " -h - Display help." echo "" } # Retrieve the calling parameters. while getopts "i:q:h" option; do case "${option}" in i) instance_id="${OPTARG}" ;; q) query="${OPTARG}" ;; h) usage return 0 ;; \?) echo "Invalid parameter" usage return 1 ;; esac done export OPTIND=1 local aws_cli_args=() if [[ -n "$instance_id" ]]; then # shellcheck disable=SC2206 aws_cli_args+=("--instance-ids" $instance_id) fi local query_arg="" if [[ -n "$query" ]]; then query_arg="--query '$query'" else query_arg="--query Reservations[*].Instances[*].[InstanceId,ImageId,InstanceType,KeyName,VpcId,PublicIpAddress,State.Name]" fi # shellcheck disable=SC2086 response=$(aws ec2 describe-instances \ "${aws_cli_args[@]}" \ $query_arg \ --output text) || { aws_cli_error_log ${?} errecho "ERROR: AWS reports describe-instances operation failed.$response" return 1 } echo "$response" return 0 } ############################################################################### # function ec2_stop_instances # # This function stops one or more Amazon Elastic Compute Cloud (Amazon EC2) instances. # # Parameters: # -i instance_id - The ID(s) of the instance(s) to stop (comma-separated). # -h - Display help. # # Returns: # 0 - If successful. # 1 - If it fails. ############################################################################### function ec2_stop_instances() { local instance_ids local option OPTARG # Required to use getopts command in a function. # bashsupport disable=BP5008 function usage() { echo "function ec2_stop_instances" echo "Stops one or more Amazon Elastic Compute Cloud (Amazon EC2) instances." echo " -i instance_id - The ID(s) of the instance(s) to stop (comma-separated)." echo " -h - Display help." echo "" } # Retrieve the calling parameters. while getopts "i:h" option; do case "${option}" in i) instance_ids="${OPTARG}" ;; h) usage return 0 ;; \?) echo "Invalid parameter" usage return 1 ;; esac done export OPTIND=1 if [[ -z "$instance_ids" ]]; then errecho "ERROR: You must provide one or more instance IDs with the -i parameter." usage return 1 fi response=$(aws ec2 stop-instances \ --instance-ids "${instance_ids}") || { aws_cli_error_log ${?} errecho "ERROR: AWS reports stop-instances operation failed with $response." return 1 } return 0 } ############################################################################### # function ec2_start_instances # # This function starts one or more Amazon Elastic Compute Cloud (Amazon EC2) instances. # # Parameters: # -i instance_id - The ID(s) of the instance(s) to start (comma-separated). # -h - Display help. # # Returns: # 0 - If successful. # 1 - If it fails. ############################################################################### function ec2_start_instances() { local instance_ids local option OPTARG # Required to use getopts command in a function. # bashsupport disable=BP5008 function usage() { echo "function ec2_start_instances" echo "Starts one or more Amazon Elastic Compute Cloud (Amazon EC2) instances." echo " -i instance_id - The ID(s) of the instance(s) to start (comma-separated)." echo " -h - Display help." echo "" } # Retrieve the calling parameters. while getopts "i:h" option; do case "${option}" in i) instance_ids="${OPTARG}" ;; h) usage return 0 ;; \?) echo "Invalid parameter" usage return 1 ;; esac done export OPTIND=1 if [[ -z "$instance_ids" ]]; then errecho "ERROR: You must provide one or more instance IDs with the -i parameter." usage return 1 fi response=$(aws ec2 start-instances \ --instance-ids "${instance_ids}") || { aws_cli_error_log ${?} errecho "ERROR: AWS reports start-instances operation failed with $response." return 1 } return 0 } ############################################################################### # function ec2_allocate_address # # This function allocates an Elastic IP address for use with Amazon Elastic Compute Cloud (Amazon EC2) instances in a specific AWS Region. # # Parameters: # -d domain - The domain for the Elastic IP address (either 'vpc' or 'standard'). # # Returns: # The allocated Elastic IP address, or an error message if the operation fails. # And: # 0 - If successful. # 1 - If it fails. # ############################################################################### function ec2_allocate_address() { local domain response # Function to display usage information function usage() { echo "function ec2_allocate_address" echo "Allocates an Elastic IP address for use with Amazon Elastic Compute Cloud (Amazon EC2) instances in a specific AWS Region." echo " -d domain - The domain for the Elastic IP address (either 'vpc' or 'standard')." echo "" } # Parse the command-line arguments while getopts "d:h" option; do case "${option}" in d) domain="${OPTARG}" ;; h) usage return 0 ;; \?) echo "Invalid parameter" usage return 1 ;; esac done export OPTIND=1 # Validate the input parameters if [[ -z "$domain" ]]; then errecho "ERROR: You must provide a domain with the -d parameter (either 'vpc' or 'standard')." return 1 fi if [[ "$domain" != "vpc" && "$domain" != "standard" ]]; then errecho "ERROR: Invalid domain value. Must be either 'vpc' or 'standard'." return 1 fi # Allocate the Elastic IP address response=$(aws ec2 allocate-address \ --domain "$domain" \ --query "[PublicIp,AllocationId]" \ --output text) || { aws_cli_error_log ${?} errecho "ERROR: AWS reports allocate-address operation failed." errecho "$response" return 1 } echo "$response" return 0 } ############################################################################### # function ec2_associate_address # # This function associates an Elastic IP address with an Amazon Elastic Compute Cloud (Amazon EC2) instance. # # Parameters: # -a allocation_id - The allocation ID of the Elastic IP address to associate. # -i instance_id - The ID of the EC2 instance to associate the Elastic IP address with. # # Returns: # 0 - If successful. # 1 - If it fails. # ############################################################################### function ec2_associate_address() { local allocation_id instance_id response # Function to display usage information function usage() { echo "function ec2_associate_address" echo "Associates an Elastic IP address with an Amazon Elastic Compute Cloud (Amazon EC2) instance." echo " -a allocation_id - The allocation ID of the Elastic IP address to associate." echo " -i instance_id - The ID of the EC2 instance to associate the Elastic IP address with." echo "" } # Parse the command-line arguments while getopts "a:i:h" option; do case "${option}" in a) allocation_id="${OPTARG}" ;; i) instance_id="${OPTARG}" ;; h) usage return 0 ;; \?) echo "Invalid parameter" usage return 1 ;; esac done export OPTIND=1 # Validate the input parameters if [[ -z "$allocation_id" ]]; then errecho "ERROR: You must provide an allocation ID with the -a parameter." return 1 fi if [[ -z "$instance_id" ]]; then errecho "ERROR: You must provide an instance ID with the -i parameter." return 1 fi # Associate the Elastic IP address response=$(aws ec2 associate-address \ --allocation-id "$allocation_id" \ --instance-id "$instance_id" \ --query "AssociationId" \ --output text) || { aws_cli_error_log ${?} errecho "ERROR: AWS reports associate-address operation failed." errecho "$response" return 1 } echo "$response" return 0 } ############################################################################### # function ec2_disassociate_address # # This function disassociates an Elastic IP address from an Amazon Elastic Compute Cloud (Amazon EC2) instance. # # Parameters: # -a association_id - The association ID that represents the association of the Elastic IP address with an instance. # # And: # 0 - If successful. # 1 - If it fails. # ############################################################################### function ec2_disassociate_address() { local association_id response # Function to display usage information function usage() { echo "function ec2_disassociate_address" echo "Disassociates an Elastic IP address from an Amazon Elastic Compute Cloud (Amazon EC2) instance." echo " -a association_id - The association ID that represents the association of the Elastic IP address with an instance." echo "" } # Parse the command-line arguments while getopts "a:h" option; do case "${option}" in a) association_id="${OPTARG}" ;; h) usage return 0 ;; \?) echo "Invalid parameter" usage return 1 ;; esac done export OPTIND=1 # Validate the input parameters if [[ -z "$association_id" ]]; then errecho "ERROR: You must provide an association ID with the -a parameter." return 1 fi response=$(aws ec2 disassociate-address \ --association-id "$association_id") || { aws_cli_error_log ${?} errecho "ERROR: AWS reports disassociate-address operation failed." errecho "$response" return 1 } return 0 } ############################################################################### # function ec2_release_address # # This function releases an Elastic IP address from an Amazon Elastic Compute Cloud (Amazon EC2) instance. # # Parameters: # -a allocation_id - The allocation ID of the Elastic IP address to release. # # Returns: # 0 - If successful. # 1 - If it fails. # ############################################################################### function ec2_release_address() { local allocation_id response # Function to display usage information function usage() { echo "function ec2_release_address" echo "Releases an Elastic IP address from an Amazon Elastic Compute Cloud (Amazon EC2) instance." echo " -a allocation_id - The allocation ID of the Elastic IP address to release." echo "" } # Parse the command-line arguments while getopts "a:h" option; do case "${option}" in a) allocation_id="${OPTARG}" ;; h) usage return 0 ;; \?) echo "Invalid parameter" usage return 1 ;; esac done export OPTIND=1 # Validate the input parameters if [[ -z "$allocation_id" ]]; then errecho "ERROR: You must provide an allocation ID with the -a parameter." return 1 fi response=$(aws ec2 release-address \ --allocation-id "$allocation_id") || { aws_cli_error_log ${?} errecho "ERROR: AWS reports release-address operation failed." errecho "$response" return 1 } return 0 } ############################################################################### # function ec2_terminate_instances # # This function terminates one or more Amazon Elastic Compute Cloud (Amazon EC2) # instances using the AWS CLI. # # Parameters: # -i instance_ids - A space-separated list of instance IDs. # -h - Display help. # # Returns: # 0 - If successful. # 1 - If it fails. ############################################################################### function ec2_terminate_instances() { local instance_ids response local option OPTARG # Required to use getopts command in a function. # bashsupport disable=BP5008 function usage() { echo "function ec2_terminate_instances" echo "Terminates one or more Amazon Elastic Compute Cloud (Amazon EC2) instances." echo " -i instance_ids - A space-separated list of instance IDs." echo " -h - Display help." echo "" } # Retrieve the calling parameters. while getopts "i:h" option; do case "${option}" in i) instance_ids="${OPTARG}" ;; h) usage return 0 ;; \?) echo "Invalid parameter" usage return 1 ;; esac done export OPTIND=1 # Check if instance ID is provided if [[ -z "${instance_ids}" ]]; then echo "Error: Missing required instance IDs parameter." usage return 1 fi # shellcheck disable=SC2086 response=$(aws ec2 terminate-instances \ "--instance-ids" $instance_ids \ --query 'TerminatingInstances[*].[InstanceId,CurrentState.Name]' \ --output text) || { aws_cli_error_log ${?} errecho "ERROR: AWS reports terminate-instances operation failed.$response" return 1 } return 0 } ############################################################################### # function ec2_delete_security_group # # This function deletes an Amazon Elastic Compute Cloud (Amazon EC2) security group. # # Parameters: # -i security_group_id - The ID of the security group to delete. # # And: # 0 - If successful. # 1 - If it fails. ############################################################################### function ec2_delete_security_group() { local security_group_id response local option OPTARG # Required to use getopts command in a function. # bashsupport disable=BP5008 function usage() { echo "function ec2_delete_security_group" echo "Deletes an Amazon Elastic Compute Cloud (Amazon EC2) security group." echo " -i security_group_id - The ID of the security group to delete." echo "" } # Retrieve the calling parameters. while getopts "i:h" option; do case "${option}" in i) security_group_id="${OPTARG}" ;; h) usage return 0 ;; \?) echo "Invalid parameter" usage return 1 ;; esac done export OPTIND=1 if [[ -z "$security_group_id" ]]; then errecho "ERROR: You must provide a security group ID with the -i parameter." usage return 1 fi response=$(aws ec2 delete-security-group --group-id "$security_group_id" --output text) || { aws_cli_error_log ${?} errecho "ERROR: AWS reports delete-security-group operation failed.$response" return 1 } return 0 } ############################################################################### # function ec2_delete_keypair # # This function deletes an Amazon EC2 ED25519 or 2048-bit RSA key pair. # # Parameters: # -n key_pair_name - A key pair name. # # And: # 0 - If successful. # 1 - If it fails. ############################################################################### function ec2_delete_keypair() { local key_pair_name response local option OPTARG # Required to use getopts command in a function. # bashsupport disable=BP5008 function usage() { echo "function ec2_delete_keypair" echo "Deletes an Amazon EC2 ED25519 or 2048-bit RSA key pair." echo " -n key_pair_name - A key pair name." echo "" } # Retrieve the calling parameters. while getopts "n:h" option; do case "${option}" in n) key_pair_name="${OPTARG}" ;; h) usage return 0 ;; \?) echo "Invalid parameter" usage return 1 ;; esac done export OPTIND=1 if [[ -z "$key_pair_name" ]]; then errecho "ERROR: You must provide a key pair name with the -n parameter." usage return 1 fi response=$(aws ec2 delete-key-pair \ --key-name "$key_pair_name") || { aws_cli_error_log ${?} errecho "ERROR: AWS reports delete-key-pair operation failed.$response" return 1 } return 0 }

Fungsi utilitas yang digunakan dalam skenario ini.

############################################################################### # function errecho # # This function outputs everything sent to it to STDERR (standard error output). ############################################################################### function errecho() { printf "%s\n" "$*" 1>&2 } ############################################################################## # function aws_cli_error_log() # # This function is used to log the error messages from the AWS CLI. # # The function expects the following argument: # $1 - The error code returned by the AWS CLI. # # Returns: # 0: - Success. # ############################################################################## function aws_cli_error_log() { local err_code=$1 errecho "Error code : $err_code" if [ "$err_code" == 1 ]; then errecho " One or more S3 transfers failed." elif [ "$err_code" == 2 ]; then errecho " Command line failed to parse." elif [ "$err_code" == 130 ]; then errecho " Process received SIGINT." elif [ "$err_code" == 252 ]; then errecho " Command syntax invalid." elif [ "$err_code" == 253 ]; then errecho " The system environment or configuration was invalid." elif [ "$err_code" == 254 ]; then errecho " The service returned an error." elif [ "$err_code" == 255 ]; then errecho " 255 is a catch-all error." fi return 0 }
Java
SDKuntuk Java 2.x
catatan

Ada lebih banyak tentang GitHub. Temukan contoh lengkapnya dan pelajari cara pengaturan dan menjalankannya di Repositori Contoh Kode AWS.

Menjalankan skenario di prompt perintah.

import org.slf4j.Logger; import org.slf4j.LoggerFactory; import software.amazon.awssdk.services.ec2.model.CreateKeyPairResponse; import software.amazon.awssdk.services.ec2.model.DeleteKeyPairResponse; import software.amazon.awssdk.services.ec2.model.DescribeKeyPairsResponse; import software.amazon.awssdk.services.ec2.model.DisassociateAddressResponse; import software.amazon.awssdk.services.ec2.model.Ec2Exception; import software.amazon.awssdk.services.ec2.model.ReleaseAddressResponse; import software.amazon.awssdk.services.ssm.model.GetParametersByPathResponse; import software.amazon.awssdk.services.ssm.model.Parameter; import java.net.InetAddress; import java.net.UnknownHostException; import java.util.List; import java.util.Scanner; import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletionException; /** * Before running this Java (v2) code example, set up your development * environment, including your credentials. * * For more information, see the following documentation topic: * * https://docs.aws.amazon.com/sdk-for-java/latest/developer-guide/get-started.html * * This Java example performs the following tasks: * * 1. Creates an RSA key pair and saves the private key data as a .pem file. * 2. Lists key pairs. * 3. Creates a security group for the default VPC. * 4. Displays security group information. * 5. Gets a list of Amazon Linux 2 AMIs and selects one. * 6. Gets additional information about the image. * 7. Gets a list of instance types that are compatible with the selected AMI’s * architecture. * 8. Creates an instance with the key pair, security group, AMI, and an * instance type. * 9. Displays information about the instance. * 10. Stops the instance and waits for it to stop. * 11. Starts the instance and waits for it to start. * 12. Allocates an Elastic IP address and associates it with the instance. * 13. Displays SSH connection info for the instance. * 14. Disassociates and deletes the Elastic IP address. * 15. Terminates the instance and waits for it to terminate. * 16. Deletes the security group. * 17. Deletes the key pair. */ public class EC2Scenario { public static final String DASHES = new String(new char[80]).replace("\0", "-"); private static final Logger logger = LoggerFactory.getLogger(EC2Scenario.class); public static void main(String[] args) throws InterruptedException, UnknownHostException { logger.info(""" Usage: <keyName> <fileName> <groupName> <groupDesc> Where: keyName - A key pair name (for example, TestKeyPair).\s fileName - A file name where the key information is written to.\s groupName - The name of the security group.\s groupDesc - The description of the security group.\s """); Scanner scanner = new Scanner(System.in); EC2Actions ec2Actions = new EC2Actions(); String keyName = "TestKeyPair7" ; String fileName = "ec2Key.pem"; String groupName = "TestSecGroup7" ; String groupDesc = "Test Group" ; String vpcId = ec2Actions.describeFirstEC2VpcAsync().join().vpcId(); InetAddress localAddress = InetAddress.getLocalHost(); String myIpAddress = localAddress.getHostAddress(); logger.info(""" Amazon Elastic Compute Cloud (EC2) is a web service that provides secure, resizable compute capacity in the cloud. It allows developers and organizations to easily launch and manage virtual server instances, known as EC2 instances, to run their applications. EC2 provides a wide range of instance types, each with different compute, memory, and storage capabilities, to meet the diverse needs of various workloads. Developers can choose the appropriate instance type based on their application's requirements, such as high-performance computing, memory-intensive tasks, or GPU-accelerated workloads. The `Ec2AsyncClient` interface in the AWS SDK for Java 2.x provides a set of methods to programmatically interact with the Amazon EC2 service. This allows developers to automate the provisioning, management, and monitoring of EC2 instances as part of their application deployment pipelines. With EC2, teams can focus on building and deploying their applications without having to worry about the underlying infrastructure required to host and manage physical servers. This scenario walks you through how to perform key operations for this service. Let's get started... """); waitForInputToContinue(scanner); logger.info(DASHES); logger.info(DASHES); logger.info("1. Create an RSA key pair and save the private key material as a .pem file."); logger.info(""" An RSA key pair for Amazon EC2 is a security mechanism used to authenticate and secure access to your EC2 instances. It consists of a public key and a private key, which are generated as a pair. """); waitForInputToContinue(scanner); try { CompletableFuture<CreateKeyPairResponse> future = ec2Actions.createKeyPairAsync(keyName, fileName); CreateKeyPairResponse response = future.join(); logger.info("Key Pair successfully created. Key Fingerprint: " + response.keyFingerprint()); } catch (RuntimeException rt) { Throwable cause = rt.getCause(); if (cause instanceof Ec2Exception ec2Ex) { if (ec2Ex.getMessage().contains("already exists")) { // Key pair already exists. logger.info("The key pair '" + keyName + "' already exists. Moving on..."); } else { logger.info("EC2 error occurred: Error message: {}, Error code {}", ec2Ex.getMessage(), ec2Ex.awsErrorDetails().errorCode()); return; } } else { logger.info("An unexpected error occurred: " + (rt.getMessage())); return; } } waitForInputToContinue(scanner); logger.info(DASHES); logger.info(DASHES); logger.info("2. List key pairs."); waitForInputToContinue(scanner); try { CompletableFuture<DescribeKeyPairsResponse> future = ec2Actions.describeKeysAsync(); DescribeKeyPairsResponse keyPairsResponse = future.join(); keyPairsResponse.keyPairs().forEach(keyPair -> logger.info( "Found key pair with name {} and fingerprint {}", keyPair.keyName(), keyPair.keyFingerprint())); } catch (RuntimeException rt) { Throwable cause = rt.getCause(); if (cause instanceof Ec2Exception ec2Ex) { logger.info("EC2 error occurred: Error message: {}, Error code {}", ec2Ex.getMessage(), ec2Ex.awsErrorDetails().errorCode()); return; } else { logger.info("An unexpected error occurred: {}", (cause != null ? cause.getMessage() : rt.getMessage())); return; } } waitForInputToContinue(scanner); logger.info(DASHES); logger.info(DASHES); logger.info("3. Create a security group."); logger.info(""" An AWS EC2 Security Group is a virtual firewall that controls the inbound and outbound traffic to an EC2 instance. It acts as a first line of defense for your EC2 instances, allowing you to specify the rules that govern the network traffic entering and leaving your instances. """); waitForInputToContinue(scanner); String groupId = ""; try { CompletableFuture<String> future = ec2Actions.createSecurityGroupAsync(groupName, groupDesc, vpcId, myIpAddress); future.join(); logger.info("Created security group") ; } catch (RuntimeException rt) { Throwable cause = rt.getCause(); if (cause instanceof Ec2Exception ec2Ex) { if (ec2Ex.awsErrorDetails().errorMessage().contains("already exists")) { logger.info("The Security Group already exists. Moving on..."); } else { logger.error("An unexpected error occurred: {}", ec2Ex.awsErrorDetails().errorMessage()); return; } } else { logger.error("An unexpected error occurred: {}", cause.getMessage()); return; } } waitForInputToContinue(scanner); logger.info(DASHES); logger.info(DASHES); logger.info("4. Display security group information for the new security group."); waitForInputToContinue(scanner); try { CompletableFuture<String> future = ec2Actions.describeSecurityGroupArnByNameAsync(groupName); groupId = future.join(); logger.info("The security group Id is "+groupId); } catch (RuntimeException rt) { Throwable cause = rt.getCause(); if (cause instanceof Ec2Exception ec2Ex) { String errorCode = ec2Ex.awsErrorDetails().errorCode(); if ("InvalidGroup.NotFound".equals(errorCode)) { logger.info("Security group '{}' does not exist. Error Code: {}", groupName, errorCode); } else { logger.info("EC2 error occurred: Message {}, Error Code: {}", ec2Ex.getMessage(), errorCode); } } else { logger.info("An unexpected error occurred: {}", cause.getMessage()); } } waitForInputToContinue(scanner); logger.info(DASHES); logger.info(DASHES); logger.info("5. Get a list of Amazon Linux 2 AMIs and select one with amzn2 in the name."); logger.info(""" An Amazon EC2 AMI (Amazon Machine Image) is a pre-configured virtual machine image that serves as a template for launching EC2 instances. It contains all the necessary software and configurations required to run an application or operating system on an EC2 instance. """); waitForInputToContinue(scanner); String instanceAMI=""; try { CompletableFuture<GetParametersByPathResponse> future = ec2Actions.getParaValuesAsync(); GetParametersByPathResponse pathResponse = future.join(); List<Parameter> parameterList = pathResponse.parameters(); for (Parameter para : parameterList) { if (filterName(para.name())) { instanceAMI = para.value(); break; } } } catch (RuntimeException rt) { Throwable cause = rt.getCause(); if (cause instanceof Ec2Exception ec2Ex) { logger.info("EC2 error occurred: Message {}, Error Code:{}", ec2Ex.getMessage(), ec2Ex.awsErrorDetails().errorCode()); return; } else { logger.info("An unexpected error occurred: {}", cause.getMessage()); return; } } logger.info("The AMI value with amzn2 is: {}", instanceAMI); waitForInputToContinue(scanner); logger.info(DASHES); logger.info(DASHES); logger.info("6. Get the (Amazon Machine Image) AMI value from the amzn2 image."); logger.info(""" An AMI value represents a specific version of a virtual machine (VM) or server image. It uniquely identifies a particular version of an EC2 instance, including its operating system, pre-installed software, and any custom configurations. This allows you to consistently deploy the same VM image across your infrastructure. """); waitForInputToContinue(scanner); String amiValue; try { CompletableFuture<String> future = ec2Actions.describeImageAsync(instanceAMI); amiValue = future.join(); } catch (CompletionException ce) { Throwable cause = ce.getCause(); if (cause instanceof Ec2Exception) { Ec2Exception ec2Ex = (Ec2Exception) cause; logger.info("EC2 error occurred: Message {}, Error Code:{}", ec2Ex.getMessage(), ec2Ex.awsErrorDetails().errorCode()); return; } else { logger.info("An unexpected error occurred: {}", cause.getMessage()); return; } } waitForInputToContinue(scanner); logger.info(DASHES); logger.info(DASHES); logger.info("7. Retrieves an instance type available in the current AWS region."); waitForInputToContinue(scanner); String instanceType; try { CompletableFuture<String> future = ec2Actions.getInstanceTypesAsync(); instanceType = future.join(); if (!instanceType.isEmpty()) { logger.info("Found instance type: " + instanceType); } else { logger.info("Desired instance type not found."); } } catch (RuntimeException rt) { Throwable cause = rt.getCause(); if (cause instanceof Ec2Exception ec2Ex) { logger.info("EC2 error occurred: Message {}, Error Code:{}", ec2Ex.getMessage(), ec2Ex.awsErrorDetails().errorCode()); return; } else { logger.info("An unexpected error occurred: {}", cause.getMessage()); return; } } waitForInputToContinue(scanner); logger.info(DASHES); logger.info(DASHES); logger.info("8. Create an Amazon EC2 instance using the key pair, the instance type, the security group, and the EC2 AMI value."); logger.info("Once the EC2 instance is created, it is placed into a running state."); waitForInputToContinue(scanner); String newInstanceId; try { CompletableFuture<String> future = ec2Actions.runInstanceAsync(instanceType, keyName, groupName, amiValue); newInstanceId = future.join(); } catch (RuntimeException rt) { Throwable cause = rt.getCause(); if (cause instanceof Ec2Exception) { Ec2Exception ec2Ex = (Ec2Exception) cause; switch (ec2Ex.awsErrorDetails().errorCode()) { case "InvalidParameterValue": logger.info("EC2 error occurred: Message {}, Error Code:{}", ec2Ex.getMessage(), ec2Ex.awsErrorDetails().errorCode()); break; case "InsufficientInstanceCapacity": // Handle insufficient instance capacity. logger.info("Insufficient instance capacity: {}, {}", ec2Ex.getMessage(), ec2Ex.awsErrorDetails().errorCode()); break; case "InvalidGroup.NotFound": // Handle security group not found. logger.info("Security group not found: {},{}", ec2Ex.getMessage(), ec2Ex.awsErrorDetails().errorCode()); break; default: logger.info("EC2 error occurred: {} (Code: {}", ec2Ex.getMessage(), ec2Ex.awsErrorDetails().errorCode()); break; } return; } else { logger.info("An unexpected error occurred: {}", (cause != null ? cause.getMessage() : rt.getMessage())); return; } } logger.info("The instance Id is " + newInstanceId); waitForInputToContinue(scanner); logger.info(DASHES); logger.info(DASHES); logger.info("9. Display information about the running instance. "); waitForInputToContinue(scanner); String publicIp; try { CompletableFuture<String> future = ec2Actions.describeEC2InstancesAsync(newInstanceId); publicIp = future.join(); logger.info("EC2 instance public IP {}", publicIp); } catch (RuntimeException rt) { Throwable cause = rt.getCause(); if (cause instanceof Ec2Exception ec2Ex) { logger.info("EC2 error occurred: Message {}, Error Code:{}", ec2Ex.getMessage(), ec2Ex.awsErrorDetails().errorCode()); return; } else { logger.info("An unexpected error occurred: {}", cause.getMessage()); return; } } logger.info("You can SSH to the instance using this command:"); logger.info("ssh -i " + fileName + " ec2-user@" + publicIp); waitForInputToContinue(scanner); logger.info(DASHES); logger.info(DASHES); logger.info("10. Stop the instance using a waiter (this may take a few mins)."); // Remove the 2nd one waitForInputToContinue(scanner); try { CompletableFuture<Void> future = ec2Actions.stopInstanceAsync(newInstanceId); future.join(); } catch (RuntimeException rt) { Throwable cause = rt.getCause(); if (cause instanceof Ec2Exception ec2Ex) { logger.info("EC2 error occurred: Message {}, Error Code:{}", ec2Ex.getMessage(), ec2Ex.awsErrorDetails().errorCode()); return; } else { logger.info("An unexpected error occurred: {}", cause.getMessage()); return; } } waitForInputToContinue(scanner); logger.info(DASHES); logger.info(DASHES); logger.info("11. Start the instance using a waiter (this may take a few mins)."); try { CompletableFuture<Void> future = ec2Actions.startInstanceAsync(newInstanceId); future.join(); } catch (RuntimeException rt) { Throwable cause = rt.getCause(); if (cause instanceof Ec2Exception ec2Ex) { // Handle EC2 exceptions. logger.info("EC2 error occurred: Message {}, Error Code:{}", ec2Ex.getMessage(), ec2Ex.awsErrorDetails().errorCode()); return; } else { logger.info("An unexpected error occurred: {}", cause.getMessage()); return; } } waitForInputToContinue(scanner); logger.info(DASHES); logger.info(DASHES); logger.info("12. Allocate an Elastic IP address and associate it with the instance."); logger.info(""" An Elastic IP address is a static public IP address that you can associate with your EC2 instance. This allows you to have a fixed, predictable IP address that remains the same even if your instance is stopped, terminated, or replaced. This is particularly useful for applications or services that need to be accessed consistently from a known IP address. An EC2 Allocation ID (also known as a Reserved Instance Allocation ID) is a unique identifier associated with a Reserved Instance (RI) that you have purchased in AWS. When you purchase a Reserved Instance, AWS assigns a unique Allocation ID to it. This Allocation ID is used to track and identify the specific RI you have purchased, and it is important for managing and monitoring your Reserved Instances. """); waitForInputToContinue(scanner); String allocationId; try { CompletableFuture<String> future = ec2Actions.allocateAddressAsync(); allocationId = future.join(); logger.info("Successfully allocated address with ID: " +allocationId); } catch (RuntimeException rt) { Throwable cause = rt.getCause(); if (cause instanceof Ec2Exception ec2Ex) { logger.info("EC2 error occurred: Message {}, Error Code:{}", ec2Ex.getMessage(), ec2Ex.awsErrorDetails().errorCode()); return; } else { logger.info("An unexpected error occurred: {}", cause.getMessage()); return; } } logger.info("The allocation Id value is " + allocationId); waitForInputToContinue(scanner); String associationId; try { CompletableFuture<String> future = ec2Actions.associateAddressAsync(newInstanceId, allocationId); associationId = future.join(); logger.info("Successfully associated address with ID: " +associationId); } catch (RuntimeException rt) { Throwable cause = rt.getCause(); if (cause instanceof Ec2Exception ec2Ex) { logger.info("EC2 error occurred: Message {}, Error Code:{}", ec2Ex.getMessage(), ec2Ex.awsErrorDetails().errorCode()); return; } else { logger.info("An unexpected error occurred: {}", cause.getMessage()); return; } } waitForInputToContinue(scanner); logger.info(DASHES); logger.info(DASHES); logger.info("13. Describe the instance again. Note that the public IP address has changed"); waitForInputToContinue(scanner); try { CompletableFuture<String> future = ec2Actions.describeEC2InstancesAsync(newInstanceId); publicIp = future.join(); logger.info("EC2 instance public IP: " + publicIp); logger.info("You can SSH to the instance using this command:"); logger.info("ssh -i " + fileName + " ec2-user@" + publicIp); } catch (RuntimeException rt) { Throwable cause = rt.getCause(); if (cause instanceof Ec2Exception ec2Ex) { logger.info("EC2 error occurred: Message {}, Error Code:{}", ec2Ex.getMessage(), ec2Ex.awsErrorDetails().errorCode()); return; } else { logger.info("An unexpected error occurred: {}", cause.getMessage()); return; } } waitForInputToContinue(scanner); logger.info(DASHES); logger.info(DASHES); logger.info("14. Disassociate and release the Elastic IP address."); waitForInputToContinue(scanner); try { CompletableFuture<DisassociateAddressResponse> future = ec2Actions.disassociateAddressAsync(associationId); future.join(); logger.info("Address successfully disassociated."); } catch (RuntimeException rt) { Throwable cause = rt.getCause(); if (cause instanceof Ec2Exception ec2Ex) { // Handle EC2 exceptions. logger.info("EC2 error occurred: Message {}, Error Code:{}", ec2Ex.getMessage(), ec2Ex.awsErrorDetails().errorCode()); return; } else { logger.info("An unexpected error occurred: {}", cause.getMessage()); return; } } waitForInputToContinue(scanner); try { CompletableFuture<ReleaseAddressResponse> future = ec2Actions.releaseEC2AddressAsync(allocationId); future.join(); // Wait for the operation to complete logger.info("Elastic IP address successfully released."); } catch (RuntimeException rte) { logger.info("An unexpected error occurred: {}", rte.getMessage()); return; } waitForInputToContinue(scanner); logger.info(DASHES); logger.info(DASHES); logger.info("15. Terminate the instance and use a waiter (this may take a few mins)."); waitForInputToContinue(scanner); try { CompletableFuture<Object> future = ec2Actions.terminateEC2Async(newInstanceId); future.join(); logger.info("EC2 instance successfully terminated."); } catch (RuntimeException rt) { Throwable cause = rt.getCause(); if (cause instanceof Ec2Exception ec2Ex) { // Handle EC2 exceptions. logger.info("EC2 error occurred: Message {}, Error Code:{}", ec2Ex.getMessage(), ec2Ex.awsErrorDetails().errorCode()); return; } else { logger.info("An unexpected error occurred: {}", cause.getMessage()); return; } } logger.info(DASHES); logger.info(DASHES); logger.info("16. Delete the security group."); waitForInputToContinue(scanner); try { CompletableFuture<Void> future = ec2Actions.deleteEC2SecGroupAsync(groupId); future.join(); logger.info("Security group successfully deleted."); } catch (RuntimeException rt) { Throwable cause = rt.getCause(); if (cause instanceof Ec2Exception ec2Ex) { logger.info("EC2 error occurred: Message {}, Error Code:{}", ec2Ex.getMessage(), ec2Ex.awsErrorDetails().errorCode()); return; } else { logger.info("An unexpected error occurred: {}", cause.getMessage()); return; } } waitForInputToContinue(scanner); logger.info(DASHES); logger.info(DASHES); logger.info("17. Delete the key."); waitForInputToContinue(scanner); try { CompletableFuture<DeleteKeyPairResponse> future = ec2Actions.deleteKeysAsync(keyName); future.join(); logger.info("Successfully deleted key pair named " + keyName); } catch (RuntimeException rt) { Throwable cause = rt.getCause(); if (cause instanceof Ec2Exception ec2Ex) { logger.info("EC2 error occurred: Message {}, Error Code:{}", ec2Ex.getMessage(), ec2Ex.awsErrorDetails().errorCode()); return; } else { logger.info("An unexpected error occurred: {}", cause.getMessage()); return; } } waitForInputToContinue(scanner); logger.info(DASHES); logger.info(DASHES); logger.info("You successfully completed the Amazon EC2 scenario."); logger.info(DASHES); } public static boolean filterName(String name) { String[] parts = name.split("/"); String myValue = parts[4]; return myValue.contains("amzn2"); } private static void waitForInputToContinue(Scanner scanner) { while (true) { logger.info(""); logger.info("Enter 'c' followed by <ENTER> to continue:"); String input = scanner.nextLine(); if (input.trim().equalsIgnoreCase("c")) { logger.info("Continuing with the program..."); logger.info(""); break; } else { // Handle invalid input. logger.info("Invalid input. Please try again."); } } } }

Tentukan kelas yang membungkus EC2 tindakan.

import org.slf4j.Logger; import org.slf4j.LoggerFactory; import software.amazon.awssdk.core.client.config.ClientOverrideConfiguration; import software.amazon.awssdk.http.async.SdkAsyncHttpClient; import software.amazon.awssdk.http.nio.netty.NettyNioAsyncHttpClient; import software.amazon.awssdk.regions.Region; import software.amazon.awssdk.services.ec2.Ec2AsyncClient; import software.amazon.awssdk.services.ec2.model.AllocateAddressRequest; import software.amazon.awssdk.services.ec2.model.AllocateAddressResponse; import software.amazon.awssdk.services.ec2.model.AssociateAddressRequest; import software.amazon.awssdk.services.ec2.model.AssociateAddressResponse; import software.amazon.awssdk.services.ec2.model.AuthorizeSecurityGroupIngressRequest; import software.amazon.awssdk.services.ec2.model.CreateKeyPairRequest; import software.amazon.awssdk.services.ec2.model.CreateKeyPairResponse; import software.amazon.awssdk.services.ec2.model.CreateSecurityGroupRequest; import software.amazon.awssdk.services.ec2.model.DeleteKeyPairRequest; import software.amazon.awssdk.services.ec2.model.DeleteKeyPairResponse; import software.amazon.awssdk.services.ec2.model.DeleteSecurityGroupRequest; import software.amazon.awssdk.services.ec2.model.DeleteSecurityGroupResponse; import software.amazon.awssdk.services.ec2.model.DescribeImagesRequest; import software.amazon.awssdk.services.ec2.model.DescribeInstanceTypesRequest; import software.amazon.awssdk.services.ec2.model.DescribeInstanceTypesResponse; import software.amazon.awssdk.services.ec2.model.DescribeInstancesRequest; import software.amazon.awssdk.services.ec2.model.DescribeKeyPairsResponse; import software.amazon.awssdk.services.ec2.model.DescribeSecurityGroupsRequest; import software.amazon.awssdk.services.ec2.model.DescribeSecurityGroupsResponse; import software.amazon.awssdk.services.ec2.model.DescribeVpcsRequest; import software.amazon.awssdk.services.ec2.model.DisassociateAddressRequest; import software.amazon.awssdk.services.ec2.model.DisassociateAddressResponse; import software.amazon.awssdk.services.ec2.model.DomainType; import software.amazon.awssdk.services.ec2.model.Ec2Exception; import software.amazon.awssdk.services.ec2.model.Filter; import software.amazon.awssdk.services.ec2.model.InstanceTypeInfo; import software.amazon.awssdk.services.ec2.model.IpPermission; import software.amazon.awssdk.services.ec2.model.IpRange; import software.amazon.awssdk.services.ec2.model.ReleaseAddressRequest; import software.amazon.awssdk.services.ec2.model.ReleaseAddressResponse; import software.amazon.awssdk.services.ec2.model.RunInstancesRequest; import software.amazon.awssdk.services.ec2.model.RunInstancesResponse; import software.amazon.awssdk.services.ec2.model.StopInstancesRequest; import software.amazon.awssdk.services.ec2.model.StartInstancesRequest; import software.amazon.awssdk.services.ec2.model.TerminateInstancesRequest; import software.amazon.awssdk.services.ec2.model.Vpc; import software.amazon.awssdk.services.ec2.paginators.DescribeImagesPublisher; import software.amazon.awssdk.services.ec2.paginators.DescribeInstancesPublisher; import software.amazon.awssdk.services.ec2.paginators.DescribeSecurityGroupsPublisher; import software.amazon.awssdk.services.ec2.paginators.DescribeVpcsPublisher; import software.amazon.awssdk.services.ec2.waiters.Ec2AsyncWaiter; import software.amazon.awssdk.services.ssm.SsmAsyncClient; import software.amazon.awssdk.services.ssm.model.GetParametersByPathRequest; import software.amazon.awssdk.services.ssm.model.GetParametersByPathResponse; import software.amazon.awssdk.services.ec2.model.TerminateInstancesResponse; import java.io.BufferedWriter; import java.io.FileWriter; import java.io.IOException; import java.time.Duration; import java.util.List; import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletionException; import java.util.concurrent.atomic.AtomicReference; public class EC2Actions { private static final Logger logger = LoggerFactory.getLogger(EC2Actions.class); private static Ec2AsyncClient ec2AsyncClient; /** * Retrieves an asynchronous Amazon Elastic Container Registry (ECR) client. * * @return the configured ECR asynchronous client. */ private static Ec2AsyncClient getAsyncClient() { if (ec2AsyncClient == null) { /* The `NettyNioAsyncHttpClient` class is part of the AWS SDK for Java, version 2, and it is designed to provide a high-performance, asynchronous HTTP client for interacting with AWS services. It uses the Netty framework to handle the underlying network communication and the Java NIO API to provide a non-blocking, event-driven approach to HTTP requests and responses. */ SdkAsyncHttpClient httpClient = NettyNioAsyncHttpClient.builder() .maxConcurrency(50) // Adjust as needed. .connectionTimeout(Duration.ofSeconds(60)) // Set the connection timeout. .readTimeout(Duration.ofSeconds(60)) // Set the read timeout. .writeTimeout(Duration.ofSeconds(60)) // Set the write timeout. .build(); ClientOverrideConfiguration overrideConfig = ClientOverrideConfiguration.builder() .apiCallTimeout(Duration.ofMinutes(2)) // Set the overall API call timeout. .apiCallAttemptTimeout(Duration.ofSeconds(90)) // Set the individual call attempt timeout. .build(); ec2AsyncClient = Ec2AsyncClient.builder() .region(Region.US_EAST_1) .httpClient(httpClient) .overrideConfiguration(overrideConfig) .build(); } return ec2AsyncClient; } /** * Deletes a key pair asynchronously. * * @param keyPair the name of the key pair to delete * @return a {@link CompletableFuture} that represents the result of the asynchronous operation. * The {@link CompletableFuture} will complete with a {@link DeleteKeyPairResponse} object * that provides the result of the key pair deletion operation. */ public CompletableFuture<DeleteKeyPairResponse> deleteKeysAsync(String keyPair) { DeleteKeyPairRequest request = DeleteKeyPairRequest.builder() .keyName(keyPair) .build(); // Initiate the asynchronous request to delete the key pair. CompletableFuture<DeleteKeyPairResponse> response = getAsyncClient().deleteKeyPair(request); return response.whenComplete((resp, ex) -> { if (ex != null) { throw new RuntimeException("Failed to delete key pair: " + keyPair, ex); } else if (resp == null) { throw new RuntimeException("No response received for deleting key pair: " + keyPair); } }); } /** * Deletes an EC2 security group asynchronously. * * @param groupId the ID of the security group to delete * @return a CompletableFuture that completes when the security group is deleted */ public CompletableFuture<Void> deleteEC2SecGroupAsync(String groupId) { DeleteSecurityGroupRequest request = DeleteSecurityGroupRequest.builder() .groupId(groupId) .build(); CompletableFuture<DeleteSecurityGroupResponse> response = getAsyncClient().deleteSecurityGroup(request); return response.whenComplete((resp, ex) -> { if (ex != null) { throw new RuntimeException("Failed to delete security group with Id " + groupId, ex); } else if (resp == null) { throw new RuntimeException("No response received for deleting security group with Id " + groupId); } }).thenApply(resp -> null); } /** * Terminates an EC2 instance asynchronously and waits for it to reach the terminated state. * * @param instanceId the ID of the EC2 instance to terminate * @return a {@link CompletableFuture} that completes when the instance has been terminated * @throws RuntimeException if there is no response from the AWS SDK or if there is a failure during the termination process */ public CompletableFuture<Object> terminateEC2Async(String instanceId) { TerminateInstancesRequest terminateRequest = TerminateInstancesRequest.builder() .instanceIds(instanceId) .build(); CompletableFuture<TerminateInstancesResponse> responseFuture = getAsyncClient().terminateInstances(terminateRequest); return responseFuture.thenCompose(terminateResponse -> { if (terminateResponse == null) { throw new RuntimeException("No response received for terminating instance " + instanceId); } System.out.println("Going to terminate an EC2 instance and use a waiter to wait for it to be in terminated state"); return getAsyncClient().waiter() .waitUntilInstanceTerminated(r -> r.instanceIds(instanceId)) .thenApply(waiterResponse -> null); }).exceptionally(throwable -> { // Handle any exceptions that occurred during the async call throw new RuntimeException("Failed to terminate EC2 instance: " + throwable.getMessage(), throwable); }); } /** * Releases an Elastic IP address asynchronously. * * @param allocId the allocation ID of the Elastic IP address to be released * @return a {@link CompletableFuture} representing the asynchronous operation of releasing the Elastic IP address */ public CompletableFuture<ReleaseAddressResponse> releaseEC2AddressAsync(String allocId) { ReleaseAddressRequest request = ReleaseAddressRequest.builder() .allocationId(allocId) .build(); CompletableFuture<ReleaseAddressResponse> response = getAsyncClient().releaseAddress(request); response.whenComplete((resp, ex) -> { if (ex != null) { throw new RuntimeException("Failed to release Elastic IP address", ex); } }); return response; } /** * Disassociates an Elastic IP address from an instance asynchronously. * * @param associationId The ID of the association you want to disassociate. * @return a {@link CompletableFuture} representing the asynchronous operation of disassociating the address. The * {@link CompletableFuture} will complete with a {@link DisassociateAddressResponse} when the operation is * finished. * @throws RuntimeException if the disassociation of the address fails. */ public CompletableFuture<DisassociateAddressResponse> disassociateAddressAsync(String associationId) { Ec2AsyncClient ec2 = getAsyncClient(); DisassociateAddressRequest addressRequest = DisassociateAddressRequest.builder() .associationId(associationId) .build(); // Disassociate the address asynchronously. CompletableFuture<DisassociateAddressResponse> response = ec2.disassociateAddress(addressRequest); response.whenComplete((resp, ex) -> { if (ex != null) { throw new RuntimeException("Failed to disassociate address", ex); } }); return response; } /** * Associates an Elastic IP address with an EC2 instance asynchronously. * * @param instanceId the ID of the EC2 instance to associate the Elastic IP address with * @param allocationId the allocation ID of the Elastic IP address to associate * @return a {@link CompletableFuture} that completes with the association ID when the operation is successful, * or throws a {@link RuntimeException} if the operation fails */ public CompletableFuture<String> associateAddressAsync(String instanceId, String allocationId) { AssociateAddressRequest associateRequest = AssociateAddressRequest.builder() .instanceId(instanceId) .allocationId(allocationId) .build(); CompletableFuture<AssociateAddressResponse> responseFuture = getAsyncClient().associateAddress(associateRequest); return responseFuture.thenApply(response -> { if (response.associationId() != null) { return response.associationId(); } else { throw new RuntimeException("Association ID is null after associating address."); } }).whenComplete((result, ex) -> { if (ex != null) { throw new RuntimeException("Failed to associate address", ex); } }); } /** * Allocates an Elastic IP address asynchronously in the VPC domain. * * @return a {@link CompletableFuture} containing the allocation ID of the allocated Elastic IP address */ public CompletableFuture<String> allocateAddressAsync() { AllocateAddressRequest allocateRequest = AllocateAddressRequest.builder() .domain(DomainType.VPC) .build(); CompletableFuture<AllocateAddressResponse> responseFuture = getAsyncClient().allocateAddress(allocateRequest); return responseFuture.thenApply(AllocateAddressResponse::allocationId).whenComplete((result, ex) -> { if (ex != null) { throw new RuntimeException("Failed to allocate address", ex); } }); } /** * Asynchronously describes the state of an EC2 instance. * The paginator helps you iterate over multiple pages of results. * * @param newInstanceId the ID of the EC2 instance to describe * @return a {@link CompletableFuture} that, when completed, contains a string describing the state of the EC2 instance */ public CompletableFuture<String> describeEC2InstancesAsync(String newInstanceId) { DescribeInstancesRequest request = DescribeInstancesRequest.builder() .instanceIds(newInstanceId) .build(); DescribeInstancesPublisher paginator = getAsyncClient().describeInstancesPaginator(request); AtomicReference<String> publicIpAddressRef = new AtomicReference<>(); return paginator.subscribe(response -> { response.reservations().stream() .flatMap(reservation -> reservation.instances().stream()) .filter(instance -> instance.instanceId().equals(newInstanceId)) .findFirst() .ifPresent(instance -> publicIpAddressRef.set(instance.publicIpAddress())); }).thenApply(v -> { String publicIpAddress = publicIpAddressRef.get(); if (publicIpAddress == null) { throw new RuntimeException("Instance with ID " + newInstanceId + " not found."); } return publicIpAddress; }).exceptionally(ex -> { logger.info("Failed to describe instances: " + ex.getMessage()); throw new RuntimeException("Failed to describe instances", ex); }); } /** * Runs an EC2 instance asynchronously. * * @param instanceType The instance type to use for the EC2 instance. * @param keyName The name of the key pair to associate with the EC2 instance. * @param groupName The name of the security group to associate with the EC2 instance. * @param amiId The ID of the Amazon Machine Image (AMI) to use for the EC2 instance. * @return A {@link CompletableFuture} that completes with the ID of the started EC2 instance. * @throws RuntimeException If there is an error running the EC2 instance. */ public CompletableFuture<String> runInstanceAsync(String instanceType, String keyName, String groupName, String amiId) { RunInstancesRequest runRequest = RunInstancesRequest.builder() .instanceType(instanceType) .keyName(keyName) .securityGroups(groupName) .maxCount(1) .minCount(1) .imageId(amiId) .build(); CompletableFuture<RunInstancesResponse> responseFuture = getAsyncClient().runInstances(runRequest); return responseFuture.thenCompose(response -> { String instanceIdVal = response.instances().get(0).instanceId(); System.out.println("Going to start an EC2 instance and use a waiter to wait for it to be in running state"); return getAsyncClient().waiter() .waitUntilInstanceExists(r -> r.instanceIds(instanceIdVal)) .thenCompose(waitResponse -> getAsyncClient().waiter() .waitUntilInstanceRunning(r -> r.instanceIds(instanceIdVal)) .thenApply(runningResponse -> instanceIdVal)); }).exceptionally(throwable -> { // Handle any exceptions that occurred during the async call throw new RuntimeException("Failed to run EC2 instance: " + throwable.getMessage(), throwable); }); } /** * Asynchronously retrieves the instance types available in the current AWS region. * <p> * This method uses the AWS SDK's asynchronous API to fetch the available instance types * and then processes the response. It logs the memory information, network information, * and instance type for each instance type returned. Additionally, it returns a * {@link CompletableFuture} that resolves to the instance type string for the "t2.2xlarge" * instance type, if it is found in the response. If the "t2.2xlarge" instance type is not * found, an empty string is returned. * </p> * * @return a {@link CompletableFuture} that resolves to the instance type string for the * "t2.2xlarge" instance type, or an empty string if the instance type is not found */ public CompletableFuture<String> getInstanceTypesAsync() { DescribeInstanceTypesRequest typesRequest = DescribeInstanceTypesRequest.builder() .maxResults(10) .build(); CompletableFuture<DescribeInstanceTypesResponse> response = getAsyncClient().describeInstanceTypes(typesRequest); response.whenComplete((resp, ex) -> { if (resp != null) { List<InstanceTypeInfo> instanceTypes = resp.instanceTypes(); for (InstanceTypeInfo type : instanceTypes) { logger.info("The memory information of this type is " + type.memoryInfo().sizeInMiB()); logger.info("Network information is " + type.networkInfo().toString()); logger.info("Instance type is " + type.instanceType().toString()); } } else { throw (RuntimeException) ex; } }); return response.thenApply(resp -> { for (InstanceTypeInfo type : resp.instanceTypes()) { String instanceType = type.instanceType().toString(); if (instanceType.equals("t2.2xlarge")) { return instanceType; } } return ""; }); } /** * Asynchronously describes an AWS EC2 image with the specified image ID. * * @param imageId the ID of the image to be described * @return a {@link CompletableFuture} that, when completed, contains the ID of the described image * @throws RuntimeException if no images are found with the provided image ID, or if an error occurs during the AWS API call */ public CompletableFuture<String> describeImageAsync(String imageId) { DescribeImagesRequest imagesRequest = DescribeImagesRequest.builder() .imageIds(imageId) .build(); AtomicReference<String> imageIdRef = new AtomicReference<>(); DescribeImagesPublisher paginator = getAsyncClient().describeImagesPaginator(imagesRequest); return paginator.subscribe(response -> { response.images().stream() .filter(image -> image.imageId().equals(imageId)) .findFirst() .ifPresent(image -> { logger.info("The description of the image is " + image.description()); logger.info("The name of the image is " + image.name()); imageIdRef.set(image.imageId()); }); }).thenApply(v -> { String id = imageIdRef.get(); if (id == null) { throw new RuntimeException("No images found with the provided image ID."); } return id; }).exceptionally(ex -> { logger.info("Failed to describe image: " + ex.getMessage()); throw new RuntimeException("Failed to describe image", ex); }); } /** * Retrieves the parameter values asynchronously using the AWS Systems Manager (SSM) API. * * @return a {@link CompletableFuture} that holds the response from the SSM API call to get parameters by path */ public CompletableFuture<GetParametersByPathResponse> getParaValuesAsync() { SsmAsyncClient ssmClient = SsmAsyncClient.builder() .region(Region.US_EAST_1) .build(); GetParametersByPathRequest parameterRequest = GetParametersByPathRequest.builder() .path("/aws/service/ami-amazon-linux-latest") .build(); // Create a CompletableFuture to hold the final result. CompletableFuture<GetParametersByPathResponse> responseFuture = new CompletableFuture<>(); ssmClient.getParametersByPath(parameterRequest) .whenComplete((response, exception) -> { if (exception != null) { responseFuture.completeExceptionally(new RuntimeException("Failed to get parameters by path", exception)); } else { responseFuture.complete(response); } }); return responseFuture; } /** * Asynchronously describes the security groups for the specified group ID. * * @param groupName the name of the security group to describe * @return a {@link CompletableFuture} that represents the asynchronous operation * of describing the security groups. The future will complete with a * {@link DescribeSecurityGroupsResponse} object that contains the * security group information. */ public CompletableFuture<String> describeSecurityGroupArnByNameAsync(String groupName) { DescribeSecurityGroupsRequest request = DescribeSecurityGroupsRequest.builder() .groupNames(groupName) .build(); DescribeSecurityGroupsPublisher paginator = getAsyncClient().describeSecurityGroupsPaginator(request); AtomicReference<String> groupIdRef = new AtomicReference<>(); return paginator.subscribe(response -> { response.securityGroups().stream() .filter(securityGroup -> securityGroup.groupName().equals(groupName)) .findFirst() .ifPresent(securityGroup -> groupIdRef.set(securityGroup.groupId())); }).thenApply(v -> { String groupId = groupIdRef.get(); if (groupId == null) { throw new RuntimeException("No security group found with the name: " + groupName); } return groupId; }).exceptionally(ex -> { logger.info("Failed to describe security group: " + ex.getMessage()); throw new RuntimeException("Failed to describe security group", ex); }); } /** * Creates a new security group asynchronously with the specified group name, description, and VPC ID. It also * authorizes inbound traffic on ports 80 and 22 from the specified IP address. * * @param groupName the name of the security group to create * @param groupDesc the description of the security group * @param vpcId the ID of the VPC in which to create the security group * @param myIpAddress the IP address from which to allow inbound traffic (e.g., "192.168.1.1/0" to allow traffic from * any IP address in the 192.168.1.0/24 subnet) * @return a CompletableFuture that, when completed, returns the ID of the created security group * @throws RuntimeException if there was a failure creating the security group or authorizing the inbound traffic */ public CompletableFuture<String> createSecurityGroupAsync(String groupName, String groupDesc, String vpcId, String myIpAddress) { CreateSecurityGroupRequest createRequest = CreateSecurityGroupRequest.builder() .groupName(groupName) .description(groupDesc) .vpcId(vpcId) .build(); return getAsyncClient().createSecurityGroup(createRequest) .thenCompose(createResponse -> { String groupId = createResponse.groupId(); IpRange ipRange = IpRange.builder() .cidrIp(myIpAddress + "/32") .build(); IpPermission ipPerm = IpPermission.builder() .ipProtocol("tcp") .toPort(80) .fromPort(80) .ipRanges(ipRange) .build(); IpPermission ipPerm2 = IpPermission.builder() .ipProtocol("tcp") .toPort(22) .fromPort(22) .ipRanges(ipRange) .build(); AuthorizeSecurityGroupIngressRequest authRequest = AuthorizeSecurityGroupIngressRequest.builder() .groupName(groupName) .ipPermissions(ipPerm, ipPerm2) .build(); return getAsyncClient().authorizeSecurityGroupIngress(authRequest) .thenApply(authResponse -> groupId); }) .whenComplete((result, exception) -> { if (exception != null) { if (exception instanceof CompletionException && exception.getCause() instanceof Ec2Exception) { throw (Ec2Exception) exception.getCause(); } else { throw new RuntimeException("Failed to create security group: " + exception.getMessage(), exception); } } }); } /** * Asynchronously describes the key pairs associated with the current AWS account. * * @return a {@link CompletableFuture} containing the {@link DescribeKeyPairsResponse} object, which provides * information about the key pairs. */ public CompletableFuture<DescribeKeyPairsResponse> describeKeysAsync() { CompletableFuture<DescribeKeyPairsResponse> responseFuture = getAsyncClient().describeKeyPairs(); responseFuture.whenComplete((response, exception) -> { if (exception != null) { throw new RuntimeException("Failed to describe key pairs: " + exception.getMessage(), exception); } }); return responseFuture; } /** * Creates a new key pair asynchronously. * * @param keyName the name of the key pair to create * @param fileName the name of the file to write the key material to * @return a {@link CompletableFuture} that represents the asynchronous operation * of creating the key pair and writing the key material to a file */ public CompletableFuture<CreateKeyPairResponse> createKeyPairAsync(String keyName, String fileName) { CreateKeyPairRequest request = CreateKeyPairRequest.builder() .keyName(keyName) .build(); CompletableFuture<CreateKeyPairResponse> responseFuture = getAsyncClient().createKeyPair(request); responseFuture.whenComplete((response, exception) -> { if (response != null) { try { BufferedWriter writer = new BufferedWriter(new FileWriter(fileName)); writer.write(response.keyMaterial()); writer.close(); } catch (IOException e) { throw new RuntimeException("Failed to write key material to file: " + e.getMessage(), e); } } else { throw new RuntimeException("Failed to create key pair: " + exception.getMessage(), exception); } }); return responseFuture; } /** * Describes the first default VPC asynchronously and using a paginator. * * @return a {@link CompletableFuture} that, when completed, contains the first default VPC found.\ */ public CompletableFuture<Vpc> describeFirstEC2VpcAsync() { Filter myFilter = Filter.builder() .name("is-default") .values("true") .build(); DescribeVpcsRequest request = DescribeVpcsRequest.builder() .filters(myFilter) .build(); DescribeVpcsPublisher paginator = getAsyncClient().describeVpcsPaginator(request); AtomicReference<Vpc> vpcRef = new AtomicReference<>(); return paginator.subscribe(response -> { response.vpcs().stream() .findFirst() .ifPresent(vpcRef::set); }).thenApply(v -> { Vpc vpc = vpcRef.get(); if (vpc == null) { throw new RuntimeException("Default VPC not found"); } return vpc; }).exceptionally(ex -> { logger.info("Failed to describe VPCs: " + ex.getMessage()); throw new RuntimeException("Failed to describe VPCs", ex); }); } /** * Stops the EC2 instance with the specified ID asynchronously and waits for the instance to stop. * * @param instanceId the ID of the EC2 instance to stop * @return a {@link CompletableFuture} that completes when the instance has been stopped, or exceptionally if an error occurs */ public CompletableFuture<Void> stopInstanceAsync(String instanceId) { StopInstancesRequest stopRequest = StopInstancesRequest.builder() .instanceIds(instanceId) .build(); DescribeInstancesRequest describeRequest = DescribeInstancesRequest.builder() .instanceIds(instanceId) .build(); Ec2AsyncWaiter ec2Waiter = Ec2AsyncWaiter.builder() .client(getAsyncClient()) .build(); CompletableFuture<Void> resultFuture = new CompletableFuture<>(); logger.info("Stopping instance " + instanceId + " and waiting for it to stop."); getAsyncClient().stopInstances(stopRequest) .thenCompose(response -> { if (response.stoppingInstances().isEmpty()) { return CompletableFuture.failedFuture(new RuntimeException("No instances were stopped. Please check the instance ID: " + instanceId)); } return ec2Waiter.waitUntilInstanceStopped(describeRequest); }) .thenAccept(waiterResponse -> { logger.info("Successfully stopped instance " + instanceId); resultFuture.complete(null); }) .exceptionally(throwable -> { logger.error("Failed to stop instance " + instanceId + ": " + throwable.getMessage(), throwable); resultFuture.completeExceptionally(new RuntimeException("Failed to stop instance: " + throwable.getMessage(), throwable)); return null; }); return resultFuture; } /** * Starts an Amazon EC2 instance asynchronously and waits until it is in the "running" state. * * @param instanceId the ID of the instance to start * @return a {@link CompletableFuture} that completes when the instance has been started and is in the "running" state, or exceptionally if an error occurs */ public CompletableFuture<Void> startInstanceAsync(String instanceId) { StartInstancesRequest startRequest = StartInstancesRequest.builder() .instanceIds(instanceId) .build(); Ec2AsyncWaiter ec2Waiter = Ec2AsyncWaiter.builder() .client(getAsyncClient()) .build(); DescribeInstancesRequest describeRequest = DescribeInstancesRequest.builder() .instanceIds(instanceId) .build(); logger.info("Starting instance " + instanceId + " and waiting for it to run."); CompletableFuture<Void> resultFuture = new CompletableFuture<>(); return getAsyncClient().startInstances(startRequest) .thenCompose(response -> ec2Waiter.waitUntilInstanceRunning(describeRequest) ) .thenAccept(waiterResponse -> { logger.info("Successfully started instance " + instanceId); resultFuture.complete(null); }) .exceptionally(throwable -> { resultFuture.completeExceptionally(new RuntimeException("Failed to start instance: " + throwable.getMessage(), throwable)); return null; }); } }
JavaScript
SDKuntuk JavaScript (v3)
catatan

Ada lebih banyak tentang GitHub. Temukan contoh lengkapnya dan pelajari cara pengaturan dan menjalankannya di Repositori Contoh Kode AWS.

File ini berisi daftar tindakan umum yang digunakanEC2. Langkah-langkah dibangun dengan kerangka Skenario yang menyederhanakan menjalankan contoh interaktif. Untuk konteks lengkap, kunjungi GitHub repositori.

import { tmpdir } from "node:os"; import { writeFile, mkdtemp, rm } from "node:fs/promises"; import { join } from "node:path"; import { get } from "node:http"; import { AllocateAddressCommand, AssociateAddressCommand, AuthorizeSecurityGroupIngressCommand, CreateKeyPairCommand, CreateSecurityGroupCommand, DeleteKeyPairCommand, DeleteSecurityGroupCommand, DisassociateAddressCommand, paginateDescribeImages, paginateDescribeInstances, paginateDescribeInstanceTypes, ReleaseAddressCommand, RunInstancesCommand, StartInstancesCommand, StopInstancesCommand, TerminateInstancesCommand, waitUntilInstanceStatusOk, waitUntilInstanceStopped, waitUntilInstanceTerminated, } from "@aws-sdk/client-ec2"; import { ScenarioAction, ScenarioInput, ScenarioOutput, } from "@aws-doc-sdk-examples/lib/scenario/index.js"; import { paginateGetParametersByPath, SSMClient } from "@aws-sdk/client-ssm"; /** * @typedef {{ * ec2Client: import('@aws-sdk/client-ec2').EC2Client, * errors: Error[], * keyPairId?: string, * tmpDirectory?: string, * securityGroupId?: string, * ipAddress?: string, * images?: import('@aws-sdk/client-ec2').Image[], * image?: import('@aws-sdk/client-ec2').Image, * instanceTypes?: import('@aws-sdk/client-ec2').InstanceTypeInfo[], * instanceId?: string, * instanceIpAddress?: string, * allocationId?: string, * allocatedIpAddress?: string, * associationId?: string, * }} State */ /** * A skip function provided to the `skipWhen` of a Step when you want * to ignore that step if any errors have occurred. * @param {State} state */ const skipWhenErrors = (state) => state.errors.length > 0; const MAX_WAITER_TIME_IN_SECONDS = 60 * 8; export const confirm = new ScenarioInput("confirmContinue", "Continue?", { type: "confirm", skipWhen: skipWhenErrors, }); export const exitOnNoConfirm = new ScenarioAction( `exitOnConfirmContinueFalse`, (/** @type { { earlyExit: boolean } & Record<string, any>} */ state) => { if (!state[confirm.name]) { state.earlyExit = true; } }, { skipWhen: skipWhenErrors, }, ); export const greeting = new ScenarioOutput( "greeting", `Welcome to the Amazon EC2 basic usage scenario. Before you launch an instances, you'll need to provide a few things: • A key pair - This is for SSH access to your EC2 instance. You only need to provide the name. • A security group - This is used for configuring access to your instance. Again, only the name is needed. • An IP address - Your public IP address will be fetched. • An Amazon Machine Image (AMI) • A compatible instance type`, { header: true, preformatted: true, skipWhen: skipWhenErrors }, ); export const provideKeyPairName = new ScenarioInput( "keyPairName", "Provide a name for a new key pair.", { type: "input", default: "ec2-example-key-pair", skipWhen: skipWhenErrors }, ); export const createKeyPair = new ScenarioAction( "createKeyPair", async (/** @type {State} */ state) => { try { // Create a key pair in Amazon EC2. const { KeyMaterial, KeyPairId } = await state.ec2Client.send( // A unique name for the key pair. Up to 255 ASCII characters. new CreateKeyPairCommand({ KeyName: state[provideKeyPairName.name] }), ); state.keyPairId = KeyPairId; // Save the private key in a temporary location. state.tmpDirectory = await mkdtemp(join(tmpdir(), "ec2-scenario-tmp")); await writeFile( `${state.tmpDirectory}/${state[provideKeyPairName.name]}.pem`, KeyMaterial, { mode: 0o400, }, ); } catch (caught) { if ( caught instanceof Error && caught.name === "InvalidKeyPair.Duplicate" ) { caught.message = `${caught.message}. Try another key name.`; } state.errors.push(caught); } }, { skipWhen: skipWhenErrors }, ); export const logKeyPair = new ScenarioOutput( "logKeyPair", (/** @type {State} */ state) => `Created the key pair ${state[provideKeyPairName.name]}.`, { skipWhen: skipWhenErrors }, ); export const confirmDeleteKeyPair = new ScenarioInput( "confirmDeleteKeyPair", "Do you want to delete the key pair?", { type: "confirm", // Don't do anything when a key pair was never created. skipWhen: (/** @type {State} */ state) => !state.keyPairId, }, ); export const maybeDeleteKeyPair = new ScenarioAction( "deleteKeyPair", async (/** @type {State} */ state) => { try { // Delete a key pair by name from EC2 await state.ec2Client.send( new DeleteKeyPairCommand({ KeyName: state[provideKeyPairName.name] }), ); } catch (caught) { if ( caught instanceof Error && // Occurs when a required parameter (e.g. KeyName) is undefined. caught.name === "MissingParameter" ) { caught.message = `${caught.message}. Did you provide the required value?`; } state.errors.push(caught); } }, { // Don't do anything when there's no key pair to delete or the user chooses // to keep it. skipWhen: (/** @type {State} */ state) => !state.keyPairId || !state[confirmDeleteKeyPair.name], }, ); export const provideSecurityGroupName = new ScenarioInput( "securityGroupName", "Provide a name for a new security group.", { type: "input", default: "ec2-scenario-sg", skipWhen: skipWhenErrors }, ); export const createSecurityGroup = new ScenarioAction( "createSecurityGroup", async (/** @type {State} */ state) => { try { // Create a new security group that will be used to configure ingress/egress for // an EC2 instance. const { GroupId } = await state.ec2Client.send( new CreateSecurityGroupCommand({ GroupName: state[provideSecurityGroupName.name], Description: "A security group for the Amazon EC2 example.", }), ); state.securityGroupId = GroupId; } catch (caught) { if (caught instanceof Error && caught.name === "InvalidGroup.Duplicate") { caught.message = `${caught.message}. Please provide a different name for your security group.`; } state.errors.push(caught); } }, { skipWhen: skipWhenErrors }, ); export const logSecurityGroup = new ScenarioOutput( "logSecurityGroup", (/** @type {State} */ state) => `Created the security group ${state.securityGroupId}.`, { skipWhen: skipWhenErrors }, ); export const confirmDeleteSecurityGroup = new ScenarioInput( "confirmDeleteSecurityGroup", "Do you want to delete the security group?", { type: "confirm", // Don't do anything when a security group was never created. skipWhen: (/** @type {State} */ state) => !state.securityGroupId, }, ); export const maybeDeleteSecurityGroup = new ScenarioAction( "deleteSecurityGroup", async (/** @type {State} */ state) => { try { // Delete the security group if the 'skipWhen' condition below is not met. await state.ec2Client.send( new DeleteSecurityGroupCommand({ GroupId: state.securityGroupId, }), ); } catch (caught) { if ( caught instanceof Error && caught.name === "InvalidGroupId.Malformed" ) { caught.message = `${caught.message}. Please provide a valid GroupId.`; } state.errors.push(caught); } }, { // Don't do anything when there's no security group to delete // or the user chooses to keep it. skipWhen: (/** @type {State} */ state) => !state.securityGroupId || !state[confirmDeleteSecurityGroup.name], }, ); export const authorizeSecurityGroupIngress = new ScenarioAction( "authorizeSecurity", async (/** @type {State} */ state) => { try { // Get the public IP address of the machine running this example. const ipAddress = await new Promise((res, rej) => { get("http://checkip.amazonaws.com", (response) => { let data = ""; response.on("data", (chunk) => (data += chunk)); response.on("end", () => res(data.trim())); }).on("error", (err) => { rej(err); }); }); state[`ipAddress`] = ipAddress; // Allow ingress from the IP address above to the security group. // This will allow you to SSH into the EC2 instance. const command = new AuthorizeSecurityGroupIngressCommand({ GroupId: state.securityGroupId, IpPermissions: [ { IpProtocol: "tcp", FromPort: 22, ToPort: 22, IpRanges: [{ CidrIp: `${ipAddress}/32` }], }, ], }); await state.ec2Client.send(command); } catch (caught) { if ( caught instanceof Error && caught.name === "InvalidGroupId.Malformed" ) { caught.message = `${caught.message}. Please provide a valid GroupId.`; } state.errors.push(caught); } }, { skipWhen: skipWhenErrors }, ); export const logSecurityGroupIngress = new ScenarioOutput( "logSecurityGroupIngress", (/** @type {State} */ state) => `Allowed SSH access from your public IP: ${state.ipAddress}.`, { skipWhen: skipWhenErrors }, ); export const getImages = new ScenarioAction( "images", async (/** @type {State} */ state) => { const AMIs = []; // Some AWS services publish information about common artifacts as AWS Systems Manager (SSM) // public parameters. For example, the Amazon Elastic Compute Cloud (Amazon EC2) // service publishes information about Amazon Machine Images (AMIs) as public parameters. // Create the paginator for getting images. Actions that return multiple pages of // results have paginators to simplify those calls. const getParametersByPathPaginator = paginateGetParametersByPath( { // Not storing this client in state since it's only used once. client: new SSMClient({}), }, { // The path to the public list of the latest amazon-linux instances. Path: "/aws/service/ami-amazon-linux-latest", }, ); try { for await (const page of getParametersByPathPaginator) { page.Parameters.forEach((param) => { // Filter by Amazon Linux 2 if (param.Name.includes("amzn2")) { AMIs.push(param.Value); } }); } } catch (caught) { if (caught instanceof Error && caught.name === "InvalidFilterValue") { caught.message = `${caught.message} Please provide a valid filter value for paginateGetParametersByPath.`; } state.errors.push(caught); return; } const imageDetails = []; const describeImagesPaginator = paginateDescribeImages( { client: state.ec2Client }, // The images found from the call to SSM. { ImageIds: AMIs }, ); try { // Get more details for the images found above. for await (const page of describeImagesPaginator) { imageDetails.push(...(page.Images || [])); } // Store the image details for later use. state["images"] = imageDetails; } catch (caught) { if (caught instanceof Error && caught.name === "InvalidAMIID.NotFound") { caught.message = `${caught.message}. Please provide a valid image id.`; } state.errors.push(caught); } }, { skipWhen: skipWhenErrors }, ); export const provideImage = new ScenarioInput( "image", "Select one of the following images.", { type: "select", choices: (/** @type { State } */ state) => state.images.map((image) => ({ name: `${image.ImageId} - ${image.Description}`, value: image, })), default: (/** @type { State } */ state) => state.images[0], skipWhen: skipWhenErrors, }, ); export const getCompatibleInstanceTypes = new ScenarioAction( "getCompatibleInstanceTypes", async (/** @type {State} */ state) => { // Get more details about instance types that match the architecture of // the provided image. const paginator = paginateDescribeInstanceTypes( { client: state.ec2Client, pageSize: 25 }, { Filters: [ { Name: "processor-info.supported-architecture", // The value selected from provideImage() Values: [state.image.Architecture], }, // Filter for smaller, less expensive, types. { Name: "instance-type", Values: ["*.micro", "*.small"] }, ], }, ); const instanceTypes = []; try { for await (const page of paginator) { if (page.InstanceTypes.length) { instanceTypes.push(...(page.InstanceTypes || [])); } } if (!instanceTypes.length) { state.errors.push( "No instance types matched the instance type filters.", ); } } catch (caught) { if (caught instanceof Error && caught.name === "InvalidParameterValue") { caught.message = `${caught.message}. Please check the provided values and try again.`; } state.errors.push(caught); } state["instanceTypes"] = instanceTypes; }, { skipWhen: skipWhenErrors }, ); export const provideInstanceType = new ScenarioInput( "instanceType", "Select an instance type.", { choices: (/** @type {State} */ state) => state.instanceTypes.map((instanceType) => ({ name: `${instanceType.InstanceType} - Memory:${instanceType.MemoryInfo.SizeInMiB}`, value: instanceType.InstanceType, })), type: "select", default: (/** @type {State} */ state) => state.instanceTypes[0].InstanceType, skipWhen: skipWhenErrors, }, ); export const runInstance = new ScenarioAction( "runInstance", async (/** @type { State } */ state) => { const { Instances } = await state.ec2Client.send( new RunInstancesCommand({ KeyName: state[provideKeyPairName.name], SecurityGroupIds: [state.securityGroupId], ImageId: state.image.ImageId, InstanceType: state[provideInstanceType.name], // Availability Zones have capacity limitations that may impact your ability to launch instances. // The `RunInstances` operation will only succeed if it can allocate at least the `MinCount` of instances. // However, EC2 will attempt to launch up to the `MaxCount` of instances, even if the full request cannot be satisfied. // If you need a specific number of instances, use `MinCount` and `MaxCount` set to the same value. // If you want to launch up to a certain number of instances, use `MaxCount` and let EC2 provision as many as possible. // If you require a minimum number of instances, but do not want to exceed a maximum, use both `MinCount` and `MaxCount`. MinCount: 1, MaxCount: 1, }), ); state.instanceId = Instances[0].InstanceId; try { // Poll `DescribeInstanceStatus` until status is "ok". await waitUntilInstanceStatusOk( { client: state.ec2Client, maxWaitTime: MAX_WAITER_TIME_IN_SECONDS, }, { InstanceIds: [Instances[0].InstanceId] }, ); } catch (caught) { if (caught instanceof Error && caught.name === "TimeoutError") { caught.message = `${caught.message}. Try increasing the maxWaitTime in the waiter.`; } state.errors.push(caught); } }, { skipWhen: skipWhenErrors }, ); export const logRunInstance = new ScenarioOutput( "logRunInstance", "The next step is to run your EC2 instance for the first time. This can take a few minutes.", { header: true, skipWhen: skipWhenErrors }, ); export const describeInstance = new ScenarioAction( "describeInstance", async (/** @type { State } */ state) => { /** @type { import("@aws-sdk/client-ec2").Instance[] } */ const instances = []; try { const paginator = paginateDescribeInstances( { client: state.ec2Client, }, { // Only get our created instance. InstanceIds: [state.instanceId], }, ); for await (const page of paginator) { for (const reservation of page.Reservations) { instances.push(...reservation.Instances); } } if (instances.length !== 1) { throw new Error(`Instance ${state.instanceId} not found.`); } // The only info we need is the IP address for SSH purposes. state.instanceIpAddress = instances[0].PublicIpAddress; } catch (caught) { if (caught instanceof Error && caught.name === "InvalidParameterValue") { caught.message = `${caught.message}. Please check provided values and try again.`; } state.errors.push(caught); } }, { skipWhen: skipWhenErrors }, ); export const logSSHConnectionInfo = new ScenarioOutput( "logSSHConnectionInfo", (/** @type { State } */ state) => `You can now SSH into your instance using the following command: ssh -i ${state.tmpDirectory}/${state[provideKeyPairName.name]}.pem ec2-user@${state.instanceIpAddress}`, { preformatted: true, skipWhen: skipWhenErrors }, ); export const logStopInstance = new ScenarioOutput( "logStopInstance", "Stopping your EC2 instance.", { skipWhen: skipWhenErrors }, ); export const stopInstance = new ScenarioAction( "stopInstance", async (/** @type { State } */ state) => { try { await state.ec2Client.send( new StopInstancesCommand({ InstanceIds: [state.instanceId], }), ); await waitUntilInstanceStopped( { client: state.ec2Client, maxWaitTime: MAX_WAITER_TIME_IN_SECONDS, }, { InstanceIds: [state.instanceId] }, ); } catch (caught) { if (caught instanceof Error && caught.name === "TimeoutError") { caught.message = `${caught.message}. Try increasing the maxWaitTime in the waiter.`; } state.errors.push(caught); } }, // Don't try to stop an instance that doesn't exist. { skipWhen: (/** @type { State } */ state) => !state.instanceId }, ); export const logIpAddressBehavior = new ScenarioOutput( "logIpAddressBehavior", [ "When you run an instance, by default it's assigned an IP address.", "That IP address is not static. It will change every time the instance is restarted.", "The next step is to stop and restart your instance to demonstrate this behavior.", ].join(" "), { header: true, skipWhen: skipWhenErrors }, ); export const logStartInstance = new ScenarioOutput( "logStartInstance", (/** @type { State } */ state) => `Starting instance ${state.instanceId}`, { skipWhen: skipWhenErrors }, ); export const startInstance = new ScenarioAction( "startInstance", async (/** @type { State } */ state) => { try { await state.ec2Client.send( new StartInstancesCommand({ InstanceIds: [state.instanceId], }), ); await waitUntilInstanceStatusOk( { client: state.ec2Client, maxWaitTime: MAX_WAITER_TIME_IN_SECONDS, }, { InstanceIds: [state.instanceId] }, ); } catch (caught) { if (caught instanceof Error && caught.name === "TimeoutError") { caught.message = `${caught.message}. Try increasing the maxWaitTime in the waiter.`; } state.errors.push(caught); } }, { skipWhen: skipWhenErrors }, ); export const logIpAllocation = new ScenarioOutput( "logIpAllocation", [ "It is possible to have a static IP address.", "To demonstrate this, an IP will be allocated and associated to your EC2 instance.", ].join(" "), { header: true, skipWhen: skipWhenErrors }, ); export const allocateIp = new ScenarioAction( "allocateIp", async (/** @type { State } */ state) => { try { // An Elastic IP address is allocated to your AWS account, and is yours until you release it. const { AllocationId, PublicIp } = await state.ec2Client.send( new AllocateAddressCommand({}), ); state.allocationId = AllocationId; state.allocatedIpAddress = PublicIp; } catch (caught) { if (caught instanceof Error && caught.name === "MissingParameter") { caught.message = `${caught.message}. Did you provide these values?`; } state.errors.push(caught); } }, { skipWhen: skipWhenErrors }, ); export const associateIp = new ScenarioAction( "associateIp", async (/** @type { State } */ state) => { try { // Associate an allocated IP address to an EC2 instance. An IP address can be allocated // with the AllocateAddress action. const { AssociationId } = await state.ec2Client.send( new AssociateAddressCommand({ AllocationId: state.allocationId, InstanceId: state.instanceId, }), ); state.associationId = AssociationId; // Update the IP address that is being tracked to match // the one just associated. state.instanceIpAddress = state.allocatedIpAddress; } catch (caught) { if ( caught instanceof Error && caught.name === "InvalidAllocationID.NotFound" ) { caught.message = `${caught.message}. Did you provide the ID of a valid Elastic IP address AllocationId?`; } state.errors.push(caught); } }, { skipWhen: skipWhenErrors }, ); export const logStaticIpProof = new ScenarioOutput( "logStaticIpProof", "The IP address should remain the same even after stopping and starting the instance.", { header: true, skipWhen: skipWhenErrors }, ); export const logCleanUp = new ScenarioOutput( "logCleanUp", "That's it! You can choose to clean up the resources now, or clean them up on your own later.", { header: true, skipWhen: skipWhenErrors }, ); export const confirmDisassociateAddress = new ScenarioInput( "confirmDisassociateAddress", "Do you want to disassociate and release the static IP address created earlier?", { type: "confirm", skipWhen: (/** @type { State } */ state) => !state.associationId, }, ); export const maybeDisassociateAddress = new ScenarioAction( "maybeDisassociateAddress", async (/** @type { State } */ state) => { try { await state.ec2Client.send( new DisassociateAddressCommand({ AssociationId: state.associationId, }), ); } catch (caught) { if ( caught instanceof Error && caught.name === "InvalidAssociationID.NotFound" ) { caught.message = `${caught.message}. Please provide a valid association ID.`; } state.errors.push(caught); } }, { skipWhen: (/** @type { State } */ state) => !state[confirmDisassociateAddress.name] || !state.associationId, }, ); export const maybeReleaseAddress = new ScenarioAction( "maybeReleaseAddress", async (/** @type { State } */ state) => { try { await state.ec2Client.send( new ReleaseAddressCommand({ AllocationId: state.allocationId, }), ); } catch (caught) { if ( caught instanceof Error && caught.name === "InvalidAllocationID.NotFound" ) { caught.message = `${caught.message}. Please provide a valid AllocationID.`; } state.errors.push(caught); } }, { skipWhen: (/** @type { State } */ state) => !state[confirmDisassociateAddress.name] || !state.allocationId, }, ); export const confirmTerminateInstance = new ScenarioInput( "confirmTerminateInstance", "Do you want to terminate the instance?", // Don't do anything when an instance was never run. { skipWhen: (/** @type { State } */ state) => !state.instanceId, type: "confirm", }, ); export const maybeTerminateInstance = new ScenarioAction( "terminateInstance", async (/** @type { State } */ state) => { try { await state.ec2Client.send( new TerminateInstancesCommand({ InstanceIds: [state.instanceId], }), ); await waitUntilInstanceTerminated( { client: state.ec2Client }, { InstanceIds: [state.instanceId] }, ); } catch (caught) { if (caught instanceof Error && caught.name === "TimeoutError") { caught.message = `${caught.message}. Try increasing the maxWaitTime in the waiter.`; } state.errors.push(caught); } }, { // Don't do anything when there's no instance to terminate or the // use chooses not to terminate. skipWhen: (/** @type { State } */ state) => !state.instanceId || !state[confirmTerminateInstance.name], }, ); export const deleteTemporaryDirectory = new ScenarioAction( "deleteTemporaryDirectory", async (/** @type { State } */ state) => { try { await rm(state.tmpDirectory, { recursive: true }); } catch (caught) { state.errors.push(caught); } }, ); export const logErrors = new ScenarioOutput( "logErrors", (/** @type {State}*/ state) => { const errorList = state.errors .map((err) => `• ${err.name}: ${err.message}`) .join("\n"); return `Scenario errors found:\n${errorList}`; }, { preformatted: true, header: true, // Don't log errors when there aren't any! skipWhen: (/** @type {State} */ state) => state.errors.length === 0, }, );
Kotlin
SDKuntuk Kotlin
catatan

Ada lebih banyak tentang GitHub. Temukan contoh lengkapnya dan pelajari cara pengaturan dan menjalankannya di Repositori Contoh Kode AWS.

/** Before running this Kotlin code example, set up your development environment, including your credentials. For more information, see the following documentation topic: https://docs.aws.amazon.com/sdk-for-kotlin/latest/developer-guide/setup.html This Kotlin example performs the following tasks: 1. Creates an RSA key pair and saves the private key data as a .pem file. 2. Lists key pairs. 3. Creates a security group for the default VPC. 4. Displays security group information. 5. Gets a list of Amazon Linux 2 AMIs and selects one. 6. Gets more information about the image. 7. Gets a list of instance types that are compatible with the selected AMI’s architecture. 8. Creates an instance with the key pair, security group, AMI, and an instance type. 9. Displays information about the instance. 10. Stops the instance and waits for it to stop. 11. Starts the instance and waits for it to start. 12. Allocates an Elastic IP address and associates it with the instance. 13. Displays SSH connection info for the instance. 14. Disassociates and deletes the Elastic IP address. 15. Terminates the instance. 16. Deletes the security group. 17. Deletes the key pair. */ val DASHES = String(CharArray(80)).replace("\u0000", "-") suspend fun main(args: Array<String>) { val usage = """ Usage: <keyName> <fileName> <groupName> <groupDesc> <vpcId> <myIpAddress> Where: keyName - A key pair name (for example, TestKeyPair). fileName - A file name where the key information is written to. groupName - The name of the security group. groupDesc - The description of the security group. vpcId - A VPC ID. You can get this value from the AWS Management Console. myIpAddress - The IP address of your development machine. """ if (args.size != 6) { println(usage) exitProcess(0) } val keyName = args[0] val fileName = args[1] val groupName = args[2] val groupDesc = args[3] val vpcId = args[4] val myIpAddress = args[5] var newInstanceId: String? = "" println(DASHES) println("Welcome to the Amazon EC2 example scenario.") println(DASHES) println(DASHES) println("1. Create an RSA key pair and save the private key material as a .pem file.") createKeyPairSc(keyName, fileName) println(DASHES) println(DASHES) println("2. List key pairs.") describeEC2KeysSc() println(DASHES) println(DASHES) println("3. Create a security group.") val groupId = createEC2SecurityGroupSc(groupName, groupDesc, vpcId, myIpAddress) println(DASHES) println(DASHES) println("4. Display security group info for the newly created security group.") describeSecurityGroupsSc(groupId.toString()) println(DASHES) println(DASHES) println("5. Get a list of Amazon Linux 2 AMIs and select one with amzn2 in the name.") val instanceId = getParaValuesSc() if (instanceId == "") { println("The instance Id value isn't valid.") exitProcess(0) } println("The instance Id is $instanceId.") println(DASHES) println(DASHES) println("6. Get more information about an amzn2 image and return the AMI value.") val amiValue = instanceId?.let { describeImageSc(it) } if (instanceId == "") { println("The instance Id value is invalid.") exitProcess(0) } println("The AMI value is $amiValue.") println(DASHES) println(DASHES) println("7. Get a list of instance types.") val instanceType = getInstanceTypesSc() println(DASHES) println(DASHES) println("8. Create an instance.") if (amiValue != null) { newInstanceId = runInstanceSc(instanceType, keyName, groupName, amiValue) println("The instance Id is $newInstanceId") } println(DASHES) println(DASHES) println("9. Display information about the running instance. ") var ipAddress = describeEC2InstancesSc(newInstanceId) println("You can SSH to the instance using this command:") println("ssh -i " + fileName + "ec2-user@" + ipAddress) println(DASHES) println(DASHES) println("10. Stop the instance.") if (newInstanceId != null) { stopInstanceSc(newInstanceId) } println(DASHES) println(DASHES) println("11. Start the instance.") if (newInstanceId != null) { startInstanceSc(newInstanceId) } ipAddress = describeEC2InstancesSc(newInstanceId) println("You can SSH to the instance using this command:") println("ssh -i " + fileName + "ec2-user@" + ipAddress) println(DASHES) println(DASHES) println("12. Allocate an Elastic IP address and associate it with the instance.") val allocationId = allocateAddressSc() println("The allocation Id value is $allocationId") val associationId = associateAddressSc(newInstanceId, allocationId) println("The associate Id value is $associationId") println(DASHES) println(DASHES) println("13. Describe the instance again.") ipAddress = describeEC2InstancesSc(newInstanceId) println("You can SSH to the instance using this command:") println("ssh -i " + fileName + "ec2-user@" + ipAddress) println(DASHES) println(DASHES) println("14. Disassociate and release the Elastic IP address.") disassociateAddressSc(associationId) releaseEC2AddressSc(allocationId) println(DASHES) println(DASHES) println("15. Terminate the instance and use a waiter.") if (newInstanceId != null) { terminateEC2Sc(newInstanceId) } println(DASHES) println(DASHES) println("16. Delete the security group.") if (groupId != null) { deleteEC2SecGroupSc(groupId) } println(DASHES) println(DASHES) println("17. Delete the key pair.") deleteKeysSc(keyName) println(DASHES) println(DASHES) println("You successfully completed the Amazon EC2 scenario.") println(DASHES) } suspend fun deleteKeysSc(keyPair: String) { val request = DeleteKeyPairRequest { keyName = keyPair } Ec2Client { region = "us-west-2" }.use { ec2 -> ec2.deleteKeyPair(request) println("Successfully deleted key pair named $keyPair") } } suspend fun deleteEC2SecGroupSc(groupIdVal: String) { val request = DeleteSecurityGroupRequest { groupId = groupIdVal } Ec2Client { region = "us-west-2" }.use { ec2 -> ec2.deleteSecurityGroup(request) println("Successfully deleted security group with Id $groupIdVal") } } suspend fun terminateEC2Sc(instanceIdVal: String) { val ti = TerminateInstancesRequest { instanceIds = listOf(instanceIdVal) } println("Wait for the instance to terminate. This will take a few minutes.") Ec2Client { region = "us-west-2" }.use { ec2 -> ec2.terminateInstances(ti) ec2.waitUntilInstanceTerminated { // suspend call instanceIds = listOf(instanceIdVal) } println("$instanceIdVal is terminated!") } } suspend fun releaseEC2AddressSc(allocId: String?) { val request = ReleaseAddressRequest { allocationId = allocId } Ec2Client { region = "us-west-2" }.use { ec2 -> ec2.releaseAddress(request) println("Successfully released Elastic IP address $allocId") } } suspend fun disassociateAddressSc(associationIdVal: String?) { val addressRequest = DisassociateAddressRequest { associationId = associationIdVal } Ec2Client { region = "us-west-2" }.use { ec2 -> ec2.disassociateAddress(addressRequest) println("You successfully disassociated the address!") } } suspend fun associateAddressSc( instanceIdVal: String?, allocationIdVal: String?, ): String? { val associateRequest = AssociateAddressRequest { instanceId = instanceIdVal allocationId = allocationIdVal } Ec2Client { region = "us-west-2" }.use { ec2 -> val associateResponse = ec2.associateAddress(associateRequest) return associateResponse.associationId } } suspend fun allocateAddressSc(): String? { val allocateRequest = AllocateAddressRequest { domain = DomainType.Vpc } Ec2Client { region = "us-west-2" }.use { ec2 -> val allocateResponse = ec2.allocateAddress(allocateRequest) return allocateResponse.allocationId } } suspend fun startInstanceSc(instanceId: String) { val request = StartInstancesRequest { instanceIds = listOf(instanceId) } Ec2Client { region = "us-west-2" }.use { ec2 -> ec2.startInstances(request) println("Waiting until instance $instanceId starts. This will take a few minutes.") ec2.waitUntilInstanceRunning { // suspend call instanceIds = listOf(instanceId) } println("Successfully started instance $instanceId") } } suspend fun stopInstanceSc(instanceId: String) { val request = StopInstancesRequest { instanceIds = listOf(instanceId) } Ec2Client { region = "us-west-2" }.use { ec2 -> ec2.stopInstances(request) println("Waiting until instance $instanceId stops. This will take a few minutes.") ec2.waitUntilInstanceStopped { // suspend call instanceIds = listOf(instanceId) } println("Successfully stopped instance $instanceId") } } suspend fun describeEC2InstancesSc(newInstanceId: String?): String { var pubAddress = "" var isRunning = false val request = DescribeInstancesRequest { instanceIds = listOf(newInstanceId.toString()) } while (!isRunning) { Ec2Client { region = "us-west-2" }.use { ec2 -> val response = ec2.describeInstances(request) val state = response.reservations ?.get(0) ?.instances ?.get(0) ?.state ?.name ?. value if (state != null) { if (state.compareTo("running") == 0) { println("Image id is ${response.reservations!!.get(0).instances?.get(0)?.imageId}") println("Instance type is ${response.reservations!!.get(0).instances?.get(0)?.instanceType}") println("Instance state is ${response.reservations!!.get(0).instances?.get(0)?.state}") pubAddress = response.reservations!! .get(0) .instances ?.get(0) ?.publicIpAddress .toString() println("Instance address is $pubAddress") isRunning = true } } } } return pubAddress } suspend fun runInstanceSc( instanceTypeVal: String, keyNameVal: String, groupNameVal: String, amiIdVal: String, ): String { val runRequest = RunInstancesRequest { instanceType = InstanceType.fromValue(instanceTypeVal) keyName = keyNameVal securityGroups = listOf(groupNameVal) maxCount = 1 minCount = 1 imageId = amiIdVal } Ec2Client { region = "us-west-2" }.use { ec2 -> val response = ec2.runInstances(runRequest) val instanceId = response.instances?.get(0)?.instanceId println("Successfully started EC2 Instance $instanceId based on AMI $amiIdVal") return instanceId.toString() } } // Get a list of instance types. suspend fun getInstanceTypesSc(): String { var instanceType = "" val filterObs = ArrayList<Filter>() val filter = Filter { name = "processor-info.supported-architecture" values = listOf("arm64") } filterObs.add(filter) val typesRequest = DescribeInstanceTypesRequest { filters = filterObs maxResults = 10 } Ec2Client { region = "us-west-2" }.use { ec2 -> val response = ec2.describeInstanceTypes(typesRequest) response.instanceTypes?.forEach { type -> println("The memory information of this type is ${type.memoryInfo?.sizeInMib}") println("Maximum number of network cards is ${type.networkInfo?.maximumNetworkCards}") instanceType = type.instanceType.toString() } return instanceType } } // Display the Description field that corresponds to the instance Id value. suspend fun describeImageSc(instanceId: String): String? { val imagesRequest = DescribeImagesRequest { imageIds = listOf(instanceId) } Ec2Client { region = "us-west-2" }.use { ec2 -> val response = ec2.describeImages(imagesRequest) println("The description of the first image is ${response.images?.get(0)?.description}") println("The name of the first image is ${response.images?.get(0)?.name}") // Return the image Id value. return response.images?.get(0)?.imageId } } // Get the Id value of an instance with amzn2 in the name. suspend fun getParaValuesSc(): String? { val parameterRequest = GetParametersByPathRequest { path = "/aws/service/ami-amazon-linux-latest" } SsmClient { region = "us-west-2" }.use { ssmClient -> val response = ssmClient.getParametersByPath(parameterRequest) response.parameters?.forEach { para -> println("The name of the para is: ${para.name}") println("The type of the para is: ${para.type}") println("") if (para.name?.let { filterName(it) } == true) { return para.value } } } return "" } fun filterName(name: String): Boolean { val parts = name.split("/").toTypedArray() val myValue = parts[4] return myValue.contains("amzn2") } suspend fun describeSecurityGroupsSc(groupId: String) { val request = DescribeSecurityGroupsRequest { groupIds = listOf(groupId) } Ec2Client { region = "us-west-2" }.use { ec2 -> val response = ec2.describeSecurityGroups(request) for (group in response.securityGroups!!) { println("Found Security Group with id " + group.groupId.toString() + " and group VPC " + group.vpcId) } } } suspend fun createEC2SecurityGroupSc( groupNameVal: String?, groupDescVal: String?, vpcIdVal: String?, myIpAddress: String?, ): String? { val request = CreateSecurityGroupRequest { groupName = groupNameVal description = groupDescVal vpcId = vpcIdVal } Ec2Client { region = "us-west-2" }.use { ec2 -> val resp = ec2.createSecurityGroup(request) val ipRange = IpRange { cidrIp = "$myIpAddress/0" } val ipPerm = IpPermission { ipProtocol = "tcp" toPort = 80 fromPort = 80 ipRanges = listOf(ipRange) } val ipPerm2 = IpPermission { ipProtocol = "tcp" toPort = 22 fromPort = 22 ipRanges = listOf(ipRange) } val authRequest = AuthorizeSecurityGroupIngressRequest { groupName = groupNameVal ipPermissions = listOf(ipPerm, ipPerm2) } ec2.authorizeSecurityGroupIngress(authRequest) println("Successfully added ingress policy to Security Group $groupNameVal") return resp.groupId } } suspend fun describeEC2KeysSc() { Ec2Client { region = "us-west-2" }.use { ec2 -> val response = ec2.describeKeyPairs(DescribeKeyPairsRequest {}) response.keyPairs?.forEach { keyPair -> println("Found key pair with name ${keyPair.keyName} and fingerprint ${ keyPair.keyFingerprint}") } } } suspend fun createKeyPairSc( keyNameVal: String, fileNameVal: String, ) { val request = CreateKeyPairRequest { keyName = keyNameVal } Ec2Client { region = "us-west-2" }.use { ec2 -> val response = ec2.createKeyPair(request) val content = response.keyMaterial if (content != null) { File(fileNameVal).writeText(content) } println("Successfully created key pair named $keyNameVal") } }
Python
SDKuntuk Python (Boto3)
catatan

Ada lebih banyak tentang GitHub. Temukan contoh lengkapnya dan pelajari cara pengaturan dan menjalankannya di Repositori Contoh Kode AWS.

Jalankan skenario interaktif di prompt perintah.

class Ec2InstanceScenario: """Runs an interactive scenario that shows how to get started using EC2 instances.""" def __init__(self, inst_wrapper, key_wrapper, sg_wrapper, eip_wrapper, ssm_client): """ :param inst_wrapper: An object that wraps instance actions. :param key_wrapper: An object that wraps key pair actions. :param sg_wrapper: An object that wraps security group actions. :param eip_wrapper: An object that wraps Elastic IP actions. :param ssm_client: A Boto3 AWS Systems Manager client. """ self.inst_wrapper = inst_wrapper self.key_wrapper = key_wrapper self.sg_wrapper = sg_wrapper self.eip_wrapper = eip_wrapper self.ssm_client = ssm_client @demo_func def create_and_list_key_pairs(self): """ 1. Creates an RSA key pair and saves its private key data as a .pem file in secure temporary storage. The private key data is deleted after the example completes. 2. Lists the first five key pairs for the current account. """ print( "Let's create an RSA key pair that you can be use to securely connect to " "your EC2 instance." ) key_name = q.ask("Enter a unique name for your key: ", q.non_empty) self.key_wrapper.create(key_name) print( f"Created a key pair {self.key_wrapper.key_pair.key_name} and saved the " f"private key to {self.key_wrapper.key_file_path}.\n" ) if q.ask("Do you want to list some of your key pairs? (y/n) ", q.is_yesno): self.key_wrapper.list(5) @demo_func def create_security_group(self): """ 1. Creates a security group for the default VPC. 2. Adds an inbound rule to allow SSH. The SSH rule allows only inbound traffic from the current computer’s public IPv4 address. 3. Displays information about the security group. This function uses 'http://checkip.amazonaws.com' to get the current public IP address of the computer that is running the example. This method works in most cases. However, depending on how your computer connects to the internet, you might have to manually add your public IP address to the security group by using the AWS Management Console. """ print("Let's create a security group to manage access to your instance.") sg_name = q.ask("Enter a unique name for your security group: ", q.non_empty) security_group = self.sg_wrapper.create( sg_name, "Security group for example: get started with instances." ) print( f"Created security group {security_group.group_name} in your default " f"VPC {security_group.vpc_id}.\n" ) ip_response = urllib.request.urlopen("http://checkip.amazonaws.com") current_ip_address = ip_response.read().decode("utf-8").strip() print("Let's add a rule to allow SSH only from your current IP address.") print(f"Your public IP address is {current_ip_address}.") q.ask("Press Enter to add this rule to your security group.") response = self.sg_wrapper.authorize_ingress(current_ip_address) if response["Return"]: print("Security group rules updated.") else: print("Couldn't update security group rules.") self.sg_wrapper.describe() @demo_func def create_instance(self): """ 1. Gets a list of Amazon Linux 2 AMIs from AWS Systems Manager. Specifying the '/aws/service/ami-amazon-linux-latest' path returns only the latest AMIs. 2. Gets and displays information about the available AMIs and lets you select one. 3. Gets a list of instance types that are compatible with the selected AMI and lets you select one. 4. Creates an instance with the previously created key pair and security group, and the selected AMI and instance type. 5. Waits for the instance to be running and then displays its information. """ ami_paginator = self.ssm_client.get_paginator("get_parameters_by_path") ami_options = [] for page in ami_paginator.paginate(Path="/aws/service/ami-amazon-linux-latest"): ami_options += page["Parameters"] amzn2_images = self.inst_wrapper.get_images( [opt["Value"] for opt in ami_options if "amzn2" in opt["Name"]] ) print( "Let's create an instance from an Amazon Linux 2 AMI. Here are some options:" ) image_choice = q.choose( "Which one do you want to use? ", [opt.description for opt in amzn2_images] ) print("Great choice!\n") print( f"Here are some instance types that support the " f"{amzn2_images[image_choice].architecture} architecture of the image:" ) inst_types = self.inst_wrapper.get_instance_types( amzn2_images[image_choice].architecture ) inst_type_choice = q.choose( "Which one do you want to use? ", [it["InstanceType"] for it in inst_types] ) print("Another great choice.\n") print("Creating your instance and waiting for it to start...") self.inst_wrapper.create( amzn2_images[image_choice], inst_types[inst_type_choice]["InstanceType"], self.key_wrapper.key_pair, [self.sg_wrapper.security_group], ) print(f"Your instance is ready:\n") self.inst_wrapper.display() print("You can use SSH to connect to your instance.") print( "If the connection attempt times out, you might have to manually update " "the SSH ingress rule for your IP address in the AWS Management Console." ) self._display_ssh_info() def _display_ssh_info(self): """ Displays an SSH connection string that can be used to connect to a running instance. """ print("To connect, open another command prompt and run the following command:") if self.eip_wrapper.elastic_ip is None: print( f"\tssh -i {self.key_wrapper.key_file_path} " f"ec2-user@{self.inst_wrapper.instance.public_ip_address}" ) else: print( f"\tssh -i {self.key_wrapper.key_file_path} " f"ec2-user@{self.eip_wrapper.elastic_ip.public_ip}" ) q.ask("Press Enter when you're ready to continue the demo.") @demo_func def associate_elastic_ip(self): """ 1. Allocates an Elastic IP address and associates it with the instance. 2. Displays an SSH connection string that uses the Elastic IP address. """ print( "You can allocate an Elastic IP address and associate it with your instance\n" "to keep a consistent IP address even when your instance restarts." ) elastic_ip = self.eip_wrapper.allocate() print(f"Allocated static Elastic IP address: {elastic_ip.public_ip}.") self.eip_wrapper.associate(self.inst_wrapper.instance) print(f"Associated your Elastic IP with your instance.") print( "You can now use SSH to connect to your instance by using the Elastic IP." ) self._display_ssh_info() @demo_func def stop_and_start_instance(self): """ 1. Stops the instance and waits for it to stop. 2. Starts the instance and waits for it to start. 3. Displays information about the instance. 4. Displays an SSH connection string. When an Elastic IP address is associated with the instance, the IP address stays consistent when the instance stops and starts. """ print("Let's stop and start your instance to see what changes.") print("Stopping your instance and waiting until it's stopped...") self.inst_wrapper.stop() print("Your instance is stopped. Restarting...") self.inst_wrapper.start() print("Your instance is running.") self.inst_wrapper.display() if self.eip_wrapper.elastic_ip is None: print( "Every time your instance is restarted, its public IP address changes." ) else: print( "Because you have associated an Elastic IP with your instance, you can \n" "connect by using a consistent IP address after the instance restarts." ) self._display_ssh_info() @demo_func def cleanup(self): """ 1. Disassociate and delete the previously created Elastic IP. 2. Terminate the previously created instance. 3. Delete the previously created security group. 4. Delete the previously created key pair. """ print("Let's clean everything up. This example created these resources:") print(f"\tElastic IP: {self.eip_wrapper.elastic_ip.allocation_id}") print(f"\tInstance: {self.inst_wrapper.instance.id}") print(f"\tSecurity group: {self.sg_wrapper.security_group.id}") print(f"\tKey pair: {self.key_wrapper.key_pair.name}") if q.ask("Ready to delete these resources? (y/n) ", q.is_yesno): self.eip_wrapper.disassociate() print("Disassociated the Elastic IP from the instance.") self.eip_wrapper.release() print("Released the Elastic IP.") print("Terminating the instance and waiting for it to terminate...") self.inst_wrapper.terminate() print("Instance terminated.") self.sg_wrapper.delete() print("Deleted security group.") self.key_wrapper.delete() print("Deleted key pair.") def run_scenario(self): logging.basicConfig(level=logging.INFO, format="%(levelname)s: %(message)s") print("-" * 88) print( "Welcome to the Amazon Elastic Compute Cloud (Amazon EC2) get started with instances demo." ) print("-" * 88) self.create_and_list_key_pairs() self.create_security_group() self.create_instance() self.stop_and_start_instance() self.associate_elastic_ip() self.stop_and_start_instance() self.cleanup() print("\nThanks for watching!") print("-" * 88) if __name__ == "__main__": try: scenario = Ec2InstanceScenario( InstanceWrapper.from_resource(), KeyPairWrapper.from_resource(), SecurityGroupWrapper.from_resource(), ElasticIpWrapper.from_resource(), boto3.client("ssm"), ) scenario.run_scenario() except Exception: logging.exception("Something went wrong with the demo.")

Tentukan kelas yang membungkus aksi pasangan kunci.

class KeyPairWrapper: """Encapsulates Amazon Elastic Compute Cloud (Amazon EC2) key pair actions.""" def __init__(self, ec2_resource, key_file_dir, key_pair=None): """ :param ec2_resource: A Boto3 Amazon EC2 resource. This high-level resource is used to create additional high-level objects that wrap low-level Amazon EC2 service actions. :param key_file_dir: The folder where the private key information is stored. This should be a secure folder. :param key_pair: A Boto3 KeyPair object. This is a high-level object that wraps key pair actions. """ self.ec2_resource = ec2_resource self.key_pair = key_pair self.key_file_path = None self.key_file_dir = key_file_dir @classmethod def from_resource(cls): ec2_resource = boto3.resource("ec2") return cls(ec2_resource, tempfile.TemporaryDirectory()) def create(self, key_name): """ Creates a key pair that can be used to securely connect to an EC2 instance. The returned key pair contains private key information that cannot be retrieved again. The private key data is stored as a .pem file. :param key_name: The name of the key pair to create. :return: A Boto3 KeyPair object that represents the newly created key pair. """ try: self.key_pair = self.ec2_resource.create_key_pair(KeyName=key_name) self.key_file_path = os.path.join( self.key_file_dir.name, f"{self.key_pair.name}.pem" ) with open(self.key_file_path, "w") as key_file: key_file.write(self.key_pair.key_material) except ClientError as err: logger.error( "Couldn't create key %s. Here's why: %s: %s", key_name, err.response["Error"]["Code"], err.response["Error"]["Message"], ) raise else: return self.key_pair def list(self, limit): """ Displays a list of key pairs for the current account. :param limit: The maximum number of key pairs to list. """ try: for kp in self.ec2_resource.key_pairs.limit(limit): print(f"Found {kp.key_type} key {kp.name} with fingerprint:") print(f"\t{kp.key_fingerprint}") except ClientError as err: logger.error( "Couldn't list key pairs. Here's why: %s: %s", err.response["Error"]["Code"], err.response["Error"]["Message"], ) raise def delete(self): """ Deletes a key pair. """ if self.key_pair is None: logger.info("No key pair to delete.") return key_name = self.key_pair.name try: self.key_pair.delete() self.key_pair = None except ClientError as err: logger.error( "Couldn't delete key %s. Here's why: %s : %s", key_name, err.response["Error"]["Code"], err.response["Error"]["Message"], ) raise

Menentukan kelas yang menggabungkan tindakan grup keamanan.

class SecurityGroupWrapper: """Encapsulates Amazon Elastic Compute Cloud (Amazon EC2) security group actions.""" def __init__(self, ec2_resource, security_group=None): """ :param ec2_resource: A Boto3 Amazon EC2 resource. This high-level resource is used to create additional high-level objects that wrap low-level Amazon EC2 service actions. :param security_group: A Boto3 SecurityGroup object. This is a high-level object that wraps security group actions. """ self.ec2_resource = ec2_resource self.security_group = security_group @classmethod def from_resource(cls): ec2_resource = boto3.resource("ec2") return cls(ec2_resource) def create(self, group_name, group_description): """ Creates a security group in the default virtual private cloud (VPC) of the current account. :param group_name: The name of the security group to create. :param group_description: The description of the security group to create. :return: A Boto3 SecurityGroup object that represents the newly created security group. """ try: self.security_group = self.ec2_resource.create_security_group( GroupName=group_name, Description=group_description ) except ClientError as err: logger.error( "Couldn't create security group %s. Here's why: %s: %s", group_name, err.response["Error"]["Code"], err.response["Error"]["Message"], ) raise else: return self.security_group def authorize_ingress(self, ssh_ingress_ip): """ Adds a rule to the security group to allow access to SSH. :param ssh_ingress_ip: The IP address that is granted inbound access to connect to port 22 over TCP, used for SSH. :return: The response to the authorization request. The 'Return' field of the response indicates whether the request succeeded or failed. """ if self.security_group is None: logger.info("No security group to update.") return try: ip_permissions = [ { # SSH ingress open to only the specified IP address. "IpProtocol": "tcp", "FromPort": 22, "ToPort": 22, "IpRanges": [{"CidrIp": f"{ssh_ingress_ip}/32"}], } ] response = self.security_group.authorize_ingress( IpPermissions=ip_permissions ) except ClientError as err: logger.error( "Couldn't authorize inbound rules for %s. Here's why: %s: %s", self.security_group.id, err.response["Error"]["Code"], err.response["Error"]["Message"], ) raise else: return response def describe(self): """ Displays information about the security group. """ if self.security_group is None: logger.info("No security group to describe.") return try: print(f"Security group: {self.security_group.group_name}") print(f"\tID: {self.security_group.id}") print(f"\tVPC: {self.security_group.vpc_id}") if self.security_group.ip_permissions: print(f"Inbound permissions:") pp(self.security_group.ip_permissions) except ClientError as err: logger.error( "Couldn't get data for security group %s. Here's why: %s: %s", self.security_group.id, err.response["Error"]["Code"], err.response["Error"]["Message"], ) raise def delete(self): """ Deletes the security group. """ if self.security_group is None: logger.info("No security group to delete.") return group_id = self.security_group.id try: self.security_group.delete() except ClientError as err: logger.error( "Couldn't delete security group %s. Here's why: %s: %s", group_id, err.response["Error"]["Code"], err.response["Error"]["Message"], ) raise

Menentukan kelas yang menggabungkan tindakan instans.

class InstanceWrapper: """Encapsulates Amazon Elastic Compute Cloud (Amazon EC2) instance actions.""" def __init__(self, ec2_resource, instance=None): """ :param ec2_resource: A Boto3 Amazon EC2 resource. This high-level resource is used to create additional high-level objects that wrap low-level Amazon EC2 service actions. :param instance: A Boto3 Instance object. This is a high-level object that wraps instance actions. """ self.ec2_resource = ec2_resource self.instance = instance @classmethod def from_resource(cls): ec2_resource = boto3.resource("ec2") return cls(ec2_resource) def create(self, image, instance_type, key_pair, security_groups=None): """ Creates a new EC2 instance. The instance starts immediately after it is created. The instance is created in the default VPC of the current account. :param image: A Boto3 Image object that represents an Amazon Machine Image (AMI) that defines attributes of the instance that is created. The AMI defines things like the kind of operating system and the type of storage used by the instance. :param instance_type: The type of instance to create, such as 't2.micro'. The instance type defines things like the number of CPUs and the amount of memory. :param key_pair: A Boto3 KeyPair or KeyPairInfo object that represents the key pair that is used to secure connections to the instance. :param security_groups: A list of Boto3 SecurityGroup objects that represents the security groups that are used to grant access to the instance. When no security groups are specified, the default security group of the VPC is used. :return: A Boto3 Instance object that represents the newly created instance. """ try: instance_params = { "ImageId": image.id, "InstanceType": instance_type, "KeyName": key_pair.name, } if security_groups is not None: instance_params["SecurityGroupIds"] = [sg.id for sg in security_groups] self.instance = self.ec2_resource.create_instances( **instance_params, MinCount=1, MaxCount=1 )[0] self.instance.wait_until_running() except ClientError as err: logging.error( "Couldn't create instance with image %s, instance type %s, and key %s. " "Here's why: %s: %s", image.id, instance_type, key_pair.name, err.response["Error"]["Code"], err.response["Error"]["Message"], ) raise else: return self.instance def display(self, indent=1): """ Displays information about an instance. :param indent: The visual indent to apply to the output. """ if self.instance is None: logger.info("No instance to display.") return try: self.instance.load() ind = "\t" * indent print(f"{ind}ID: {self.instance.id}") print(f"{ind}Image ID: {self.instance.image_id}") print(f"{ind}Instance type: {self.instance.instance_type}") print(f"{ind}Key name: {self.instance.key_name}") print(f"{ind}VPC ID: {self.instance.vpc_id}") print(f"{ind}Public IP: {self.instance.public_ip_address}") print(f"{ind}State: {self.instance.state['Name']}") except ClientError as err: logger.error( "Couldn't display your instance. Here's why: %s: %s", err.response["Error"]["Code"], err.response["Error"]["Message"], ) raise def terminate(self): """ Terminates an instance and waits for it to be in a terminated state. """ if self.instance is None: logger.info("No instance to terminate.") return instance_id = self.instance.id try: self.instance.terminate() self.instance.wait_until_terminated() self.instance = None except ClientError as err: logging.error( "Couldn't terminate instance %s. Here's why: %s: %s", instance_id, err.response["Error"]["Code"], err.response["Error"]["Message"], ) raise def start(self): """ Starts an instance and waits for it to be in a running state. :return: The response to the start request. """ if self.instance is None: logger.info("No instance to start.") return try: response = self.instance.start() self.instance.wait_until_running() except ClientError as err: logger.error( "Couldn't start instance %s. Here's why: %s: %s", self.instance.id, err.response["Error"]["Code"], err.response["Error"]["Message"], ) raise else: return response def stop(self): """ Stops an instance and waits for it to be in a stopped state. :return: The response to the stop request. """ if self.instance is None: logger.info("No instance to stop.") return try: response = self.instance.stop() self.instance.wait_until_stopped() except ClientError as err: logger.error( "Couldn't stop instance %s. Here's why: %s: %s", self.instance.id, err.response["Error"]["Code"], err.response["Error"]["Message"], ) raise else: return response def get_images(self, image_ids): """ Gets information about Amazon Machine Images (AMIs) from a list of AMI IDs. :param image_ids: The list of AMIs to look up. :return: A list of Boto3 Image objects that represent the requested AMIs. """ try: images = list(self.ec2_resource.images.filter(ImageIds=image_ids)) except ClientError as err: logger.error( "Couldn't get images. Here's why: %s: %s", err.response["Error"]["Code"], err.response["Error"]["Message"], ) raise else: return images def get_instance_types(self, architecture): """ Gets instance types that support the specified architecture and are designated as either 'micro' or 'small'. When an instance is created, the instance type you specify must support the architecture of the AMI you use. :param architecture: The kind of architecture the instance types must support, such as 'x86_64'. :return: A list of instance types that support the specified architecture and are either 'micro' or 'small'. """ try: inst_types = [] it_paginator = self.ec2_resource.meta.client.get_paginator( "describe_instance_types" ) for page in it_paginator.paginate( Filters=[ { "Name": "processor-info.supported-architecture", "Values": [architecture], }, {"Name": "instance-type", "Values": ["*.micro", "*.small"]}, ] ): inst_types += page["InstanceTypes"] except ClientError as err: logger.error( "Couldn't get instance types. Here's why: %s: %s", err.response["Error"]["Code"], err.response["Error"]["Message"], ) raise else: return inst_types

Menentukan kelas yang menggabungkan tindakan IP Elastis.

class ElasticIpWrapper: """Encapsulates Amazon Elastic Compute Cloud (Amazon EC2) Elastic IP address actions.""" def __init__(self, ec2_resource, elastic_ip=None): """ :param ec2_resource: A Boto3 Amazon EC2 resource. This high-level resource is used to create additional high-level objects that wrap low-level Amazon EC2 service actions. :param elastic_ip: A Boto3 VpcAddress object. This is a high-level object that wraps Elastic IP actions. """ self.ec2_resource = ec2_resource self.elastic_ip = elastic_ip @classmethod def from_resource(cls): ec2_resource = boto3.resource("ec2") return cls(ec2_resource) def allocate(self): """ 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. :return: The newly created Elastic IP object. By default, the address is not associated with any instance. """ try: response = self.ec2_resource.meta.client.allocate_address(Domain="vpc") self.elastic_ip = self.ec2_resource.VpcAddress(response["AllocationId"]) except ClientError as err: logger.error( "Couldn't allocate Elastic IP. Here's why: %s: %s", err.response["Error"]["Code"], err.response["Error"]["Message"], ) raise else: return self.elastic_ip def associate(self, instance): """ 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. :param instance: A Boto3 Instance object. This is a high-level object that wraps Amazon EC2 instance actions. :return: A response that contains the ID of the association. """ if self.elastic_ip is None: logger.info("No Elastic IP to associate.") return try: response = self.elastic_ip.associate(InstanceId=instance.id) except ClientError as err: logger.error( "Couldn't associate Elastic IP %s with instance %s. Here's why: %s: %s", self.elastic_ip.allocation_id, instance.id, err.response["Error"]["Code"], err.response["Error"]["Message"], ) raise return response def disassociate(self): """ Removes an association between an Elastic IP address and an instance. When the association is removed, the instance is assigned a new public IP address. """ if self.elastic_ip is None: logger.info("No Elastic IP to disassociate.") return try: self.elastic_ip.association.delete() except ClientError as err: logger.error( "Couldn't disassociate Elastic IP %s from its instance. Here's why: %s: %s", self.elastic_ip.allocation_id, err.response["Error"]["Code"], err.response["Error"]["Message"], ) raise def release(self): """ Releases an Elastic IP address. After the Elastic IP address is released, it can no longer be used. """ if self.elastic_ip is None: logger.info("No Elastic IP to release.") return try: self.elastic_ip.release() except ClientError as err: logger.error( "Couldn't release Elastic IP address %s. Here's why: %s: %s", self.elastic_ip.allocation_id, err.response["Error"]["Code"], err.response["Error"]["Message"], ) raise
Rust
SDKuntuk Rust
catatan

Ada lebih banyak tentang GitHub. Temukan contoh lengkapnya dan pelajari cara pengaturan dan menjalankannya di Repositori Contoh Kode AWS.

EC2InstanceScenarioImplementasinya berisi logika untuk menjalankan contoh secara keseluruhan.

//! Scenario that uses the AWS SDK for Rust (the SDK) with Amazon Elastic Compute Cloud //! (Amazon EC2) to do the following: //! //! * Create a key pair that is used to secure SSH communication between your computer and //! an EC2 instance. //! * Create a security group that acts as a virtual firewall for your EC2 instances to //! control incoming and outgoing traffic. //! * Find an Amazon Machine Image (AMI) and a compatible instance type. //! * Create an instance that is created from the instance type and AMI you select, and //! is configured to use the security group and key pair created in this example. //! * Stop and restart the instance. //! * Create an Elastic IP address and associate it as a consistent IP address for your instance. //! * Connect to your instance with SSH, using both its public IP address and your Elastic IP //! address. //! * Clean up all of the resources created by this example. use std::net::Ipv4Addr; use crate::{ ec2::{EC2Error, EC2}, getting_started::{key_pair::KeyPairManager, util::Util}, ssm::SSM, }; use aws_sdk_ssm::types::Parameter; use super::{ elastic_ip::ElasticIpManager, instance::InstanceManager, security_group::SecurityGroupManager, util::ScenarioImage, }; pub struct Ec2InstanceScenario { ec2: EC2, ssm: SSM, util: Util, key_pair_manager: KeyPairManager, security_group_manager: SecurityGroupManager, instance_manager: InstanceManager, elastic_ip_manager: ElasticIpManager, } impl Ec2InstanceScenario { pub fn new(ec2: EC2, ssm: SSM, util: Util) -> Self { Ec2InstanceScenario { ec2, ssm, util, key_pair_manager: Default::default(), security_group_manager: Default::default(), instance_manager: Default::default(), elastic_ip_manager: Default::default(), } } pub async fn run(&mut self) -> Result<(), EC2Error> { self.create_and_list_key_pairs().await?; self.create_security_group().await?; self.create_instance().await?; self.stop_and_start_instance().await?; self.associate_elastic_ip().await?; self.stop_and_start_instance().await?; Ok(()) } /// 1. Creates an RSA key pair and saves its private key data as a .pem file in secure /// temporary storage. The private key data is deleted after the example completes. /// 2. Optionally, lists the first five key pairs for the current account. pub async fn create_and_list_key_pairs(&mut self) -> Result<(), EC2Error> { println!( "Let's create an RSA key pair that you can be use to securely connect to your EC2 instance."); let key_name = self.util.prompt_key_name()?; self.key_pair_manager .create(&self.ec2, &self.util, key_name) .await?; println!( "Created a key pair {} and saved the private key to {:?}.", self.key_pair_manager .key_pair() .key_name() .ok_or_else(|| EC2Error::new("No key name after creating key"))?, self.key_pair_manager .key_file_path() .ok_or_else(|| EC2Error::new("No key file after creating key"))? ); if self.util.should_list_key_pairs()? { for pair in self.key_pair_manager.list(&self.ec2).await? { println!( "Found {:?} key {} with fingerprint:\t{:?}", pair.key_type(), pair.key_name().unwrap_or("Unknown"), pair.key_fingerprint() ); } } Ok(()) } /// 1. Creates a security group for the default VPC. /// 2. Adds an inbound rule to allow SSH. The SSH rule allows only /// inbound traffic from the current computer’s public IPv4 address. /// 3. Displays information about the security group. /// /// This function uses <http://checkip.amazonaws.com> to get the current public IP /// address of the computer that is running the example. This method works in most /// cases. However, depending on how your computer connects to the internet, you /// might have to manually add your public IP address to the security group by using /// the AWS Management Console. pub async fn create_security_group(&mut self) -> Result<(), EC2Error> { println!("Let's create a security group to manage access to your instance."); let group_name = self.util.prompt_security_group_name()?; self.security_group_manager .create( &self.ec2, &group_name, "Security group for example: get started with instances.", ) .await?; println!( "Created security group {} in your default VPC {}.", self.security_group_manager.group_name(), self.security_group_manager .vpc_id() .unwrap_or("(unknown vpc)") ); let check_ip = self.util.do_get("https://checkip.amazonaws.com").await?; let current_ip_address: Ipv4Addr = check_ip.trim().parse().map_err(|e| { EC2Error::new(format!( "Failed to convert response {} to IP Address: {e:?}", check_ip )) })?; println!("Your public IP address seems to be {current_ip_address}"); if self.util.should_add_to_security_group() { match self .security_group_manager .authorize_ingress(&self.ec2, current_ip_address) .await { Ok(_) => println!("Security group rules updated"), Err(err) => eprintln!("Couldn't update security group rules: {err:?}"), } } println!("{}", self.security_group_manager); Ok(()) } /// 1. Gets a list of Amazon Linux 2 AMIs from AWS Systems Manager. Specifying the /// '/aws/service/ami-amazon-linux-latest' path returns only the latest AMIs. /// 2. Gets and displays information about the available AMIs and lets you select one. /// 3. Gets a list of instance types that are compatible with the selected AMI and /// lets you select one. /// 4. Creates an instance with the previously created key pair and security group, /// and the selected AMI and instance type. /// 5. Waits for the instance to be running and then displays its information. pub async fn create_instance(&mut self) -> Result<(), EC2Error> { let ami = self.find_image().await?; let instance_types = self .ec2 .list_instance_types(&ami.0) .await .map_err(|e| e.add_message("Could not find instance types"))?; println!( "There are several instance types that support the {} architecture of the image.", ami.0 .architecture .as_ref() .ok_or_else(|| EC2Error::new(format!("Missing architecture in {:?}", ami.0)))? ); let instance_type = self.util.select_instance_type(instance_types)?; println!("Creating your instance and waiting for it to start..."); self.instance_manager .create( &self.ec2, ami.0 .image_id() .ok_or_else(|| EC2Error::new("Could not find image ID"))?, instance_type, self.key_pair_manager.key_pair(), self.security_group_manager .security_group() .map(|sg| vec![sg]) .ok_or_else(|| EC2Error::new("Could not find security group"))?, ) .await .map_err(|e| e.add_message("Scenario failed to create instance"))?; while let Err(err) = self .ec2 .wait_for_instance_ready(self.instance_manager.instance_id(), None) .await { println!("{err}"); if !self.util.should_continue_waiting() { return Err(err); } } println!("Your instance is ready:\n{}", self.instance_manager); self.display_ssh_info(); Ok(()) } async fn find_image(&mut self) -> Result<ScenarioImage, EC2Error> { let params: Vec<Parameter> = self .ssm .list_path("/aws/service/ami-amazon-linux-latest") .await .map_err(|e| e.add_message("Could not find parameters for available images"))? .into_iter() .filter(|param| param.name().is_some_and(|name| name.contains("amzn2"))) .collect(); let amzn2_images: Vec<ScenarioImage> = self .ec2 .list_images(params) .await .map_err(|e| e.add_message("Could not find images"))? .into_iter() .map(ScenarioImage::from) .collect(); println!("We will now create an instance from an Amazon Linux 2 AMI"); let ami = self.util.select_scenario_image(amzn2_images)?; Ok(ami) } // 1. Stops the instance and waits for it to stop. // 2. Starts the instance and waits for it to start. // 3. Displays information about the instance. // 4. Displays an SSH connection string. When an Elastic IP address is associated // with the instance, the IP address stays consistent when the instance stops // and starts. pub async fn stop_and_start_instance(&self) -> Result<(), EC2Error> { println!("Let's stop and start your instance to see what changes."); println!("Stopping your instance and waiting until it's stopped..."); self.instance_manager.stop(&self.ec2).await?; println!("Your instance is stopped. Restarting..."); self.instance_manager.start(&self.ec2).await?; println!("Your instance is running."); println!("{}", self.instance_manager); if self.elastic_ip_manager.public_ip() == "0.0.0.0" { println!("Every time your instance is restarted, its public IP address changes."); } else { println!( "Because you have associated an Elastic IP with your instance, you can connect by using a consistent IP address after the instance restarts." ); } self.display_ssh_info(); Ok(()) } /// 1. Allocates an Elastic IP address and associates it with the instance. /// 2. Displays an SSH connection string that uses the Elastic IP address. async fn associate_elastic_ip(&mut self) -> Result<(), EC2Error> { self.elastic_ip_manager.allocate(&self.ec2).await?; println!( "Allocated static Elastic IP address: {}", self.elastic_ip_manager.public_ip() ); self.elastic_ip_manager .associate(&self.ec2, self.instance_manager.instance_id()) .await?; println!("Associated your Elastic IP with your instance."); println!("You can now use SSH to connect to your instance by using the Elastic IP."); self.display_ssh_info(); Ok(()) } /// Displays an SSH connection string that can be used to connect to a running /// instance. fn display_ssh_info(&self) { let ip_addr = if self.elastic_ip_manager.has_allocation() { self.elastic_ip_manager.public_ip() } else { self.instance_manager.instance_ip() }; let key_file_path = self.key_pair_manager.key_file_path().unwrap(); println!("To connect, open another command prompt and run the following command:"); println!("\nssh -i {} ec2-user@{ip_addr}\n", key_file_path.display()); let _ = self.util.enter_to_continue(); } /// 1. Disassociate and delete the previously created Elastic IP. /// 2. Terminate the previously created instance. /// 3. Delete the previously created security group. /// 4. Delete the previously created key pair. pub async fn clean_up(self) { println!("Let's clean everything up. This example created these resources:"); println!( "\tKey pair: {}", self.key_pair_manager .key_pair() .key_name() .unwrap_or("(unknown key pair)") ); println!( "\tSecurity group: {}", self.security_group_manager.group_name() ); println!( "\tInstance: {}", self.instance_manager.instance_display_name() ); if self.util.should_clean_resources() { if let Err(err) = self.elastic_ip_manager.remove(&self.ec2).await { eprintln!("{err}") } if let Err(err) = self.instance_manager.delete(&self.ec2).await { eprintln!("{err}") } if let Err(err) = self.security_group_manager.delete(&self.ec2).await { eprintln!("{err}"); } if let Err(err) = self.key_pair_manager.delete(&self.ec2, &self.util).await { eprintln!("{err}"); } } else { println!("Ok, not cleaning up any resources!"); } } } pub async fn run(mut scenario: Ec2InstanceScenario) { println!("--------------------------------------------------------------------------------"); println!( "Welcome to the Amazon Elastic Compute Cloud (Amazon EC2) get started with instances demo." ); println!("--------------------------------------------------------------------------------"); if let Err(err) = scenario.run().await { eprintln!("There was an error running the scenario: {err}") } println!("--------------------------------------------------------------------------------"); scenario.clean_up().await; println!("Thanks for running!"); println!("--------------------------------------------------------------------------------"); }

EC2ImplStruct berfungsi sebagai titik automock untuk pengujian, dan fungsinya membungkus panggilan. EC2 SDK

use std::{net::Ipv4Addr, time::Duration}; use aws_sdk_ec2::{ client::Waiters, error::ProvideErrorMetadata, operation::{ allocate_address::AllocateAddressOutput, associate_address::AssociateAddressOutput, }, types::{ DomainType, Filter, Image, Instance, InstanceType, IpPermission, IpRange, KeyPairInfo, SecurityGroup, Tag, }, Client as EC2Client, }; use aws_sdk_ssm::types::Parameter; use aws_smithy_runtime_api::client::waiters::error::WaiterError; #[cfg(test)] use mockall::automock; #[cfg(not(test))] pub use EC2Impl as EC2; #[cfg(test)] pub use MockEC2Impl as EC2; #[derive(Clone)] pub struct EC2Impl { pub client: EC2Client, } #[cfg_attr(test, automock)] impl EC2Impl { pub fn new(client: EC2Client) -> Self { EC2Impl { client } } pub async fn create_key_pair(&self, name: String) -> Result<(KeyPairInfo, String), EC2Error> { tracing::info!("Creating key pair {name}"); let output = self.client.create_key_pair().key_name(name).send().await?; let info = KeyPairInfo::builder() .set_key_name(output.key_name) .set_key_fingerprint(output.key_fingerprint) .set_key_pair_id(output.key_pair_id) .build(); let material = output .key_material .ok_or_else(|| EC2Error::new("Create Key Pair has no key material"))?; Ok((info, material)) } pub async fn list_key_pair(&self) -> Result<Vec<KeyPairInfo>, EC2Error> { let output = self.client.describe_key_pairs().send().await?; Ok(output.key_pairs.unwrap_or_default()) } pub async fn delete_key_pair(&self, key_pair_id: &str) -> Result<(), EC2Error> { let key_pair_id: String = key_pair_id.into(); tracing::info!("Deleting key pair {key_pair_id}"); self.client .delete_key_pair() .key_pair_id(key_pair_id) .send() .await?; Ok(()) } pub async fn create_security_group( &self, name: &str, description: &str, ) -> Result<SecurityGroup, EC2Error> { tracing::info!("Creating security group {name}"); let create_output = self .client .create_security_group() .group_name(name) .description(description) .send() .await .map_err(EC2Error::from)?; let group_id = create_output .group_id .ok_or_else(|| EC2Error::new("Missing security group id after creation"))?; let group = self .describe_security_group(&group_id) .await? .ok_or_else(|| { EC2Error::new(format!("Could not find security group with id {group_id}")) })?; tracing::info!("Created security group {name} as {group_id}"); Ok(group) } /// Find a single security group, by ID. Returns Err if multiple groups are found. pub async fn describe_security_group( &self, group_id: &str, ) -> Result<Option<SecurityGroup>, EC2Error> { let group_id: String = group_id.into(); let describe_output = self .client .describe_security_groups() .group_ids(&group_id) .send() .await?; let mut groups = describe_output.security_groups.unwrap_or_default(); match groups.len() { 0 => Ok(None), 1 => Ok(Some(groups.remove(0))), _ => Err(EC2Error::new(format!( "Expected single group for {group_id}" ))), } } /// Add an ingress rule to a security group explicitly allowing IPv4 address /// as {ip}/32 over TCP port 22. pub async fn authorize_security_group_ssh_ingress( &self, group_id: &str, ingress_ips: Vec<Ipv4Addr>, ) -> Result<(), EC2Error> { tracing::info!("Authorizing ingress for security group {group_id}"); self.client .authorize_security_group_ingress() .group_id(group_id) .set_ip_permissions(Some( ingress_ips .into_iter() .map(|ip| { IpPermission::builder() .ip_protocol("tcp") .from_port(22) .to_port(22) .ip_ranges(IpRange::builder().cidr_ip(format!("{ip}/32")).build()) .build() }) .collect(), )) .send() .await?; Ok(()) } pub async fn delete_security_group(&self, group_id: &str) -> Result<(), EC2Error> { tracing::info!("Deleting security group {group_id}"); self.client .delete_security_group() .group_id(group_id) .send() .await?; Ok(()) } pub async fn list_images(&self, ids: Vec<Parameter>) -> Result<Vec<Image>, EC2Error> { let image_ids = ids.into_iter().filter_map(|p| p.value).collect(); let output = self .client .describe_images() .set_image_ids(Some(image_ids)) .send() .await?; let images = output.images.unwrap_or_default(); if images.is_empty() { Err(EC2Error::new("No images for selected AMIs")) } else { Ok(images) } } /// List instance types that match an image's architecture and are free tier eligible. pub async fn list_instance_types(&self, image: &Image) -> Result<Vec<InstanceType>, EC2Error> { let architecture = format!( "{}", image.architecture().ok_or_else(|| EC2Error::new(format!( "Image {:?} does not have a listed architecture", image.image_id() )))? ); let free_tier_eligible_filter = Filter::builder() .name("free-tier-eligible") .values("false") .build(); let supported_architecture_filter = Filter::builder() .name("processor-info.supported-architecture") .values(architecture) .build(); let response = self .client .describe_instance_types() .filters(free_tier_eligible_filter) .filters(supported_architecture_filter) .send() .await?; Ok(response .instance_types .unwrap_or_default() .into_iter() .filter_map(|iti| iti.instance_type) .collect()) } pub async fn create_instance<'a>( &self, image_id: &'a str, instance_type: InstanceType, key_pair: &'a KeyPairInfo, security_groups: Vec<&'a SecurityGroup>, ) -> Result<String, EC2Error> { let run_instances = self .client .run_instances() .image_id(image_id) .instance_type(instance_type) .key_name( key_pair .key_name() .ok_or_else(|| EC2Error::new("Missing key name when launching instance"))?, ) .set_security_group_ids(Some( security_groups .iter() .filter_map(|sg| sg.group_id.clone()) .collect(), )) .min_count(1) .max_count(1) .send() .await?; if run_instances.instances().is_empty() { return Err(EC2Error::new("Failed to create instance")); } let instance_id = run_instances.instances()[0].instance_id().unwrap(); let response = self .client .create_tags() .resources(instance_id) .tags( Tag::builder() .key("Name") .value("From SDK Examples") .build(), ) .send() .await; match response { Ok(_) => tracing::info!("Created {instance_id} and applied tags."), Err(err) => { tracing::info!("Error applying tags to {instance_id}: {err:?}"); return Err(err.into()); } } tracing::info!("Instance is created."); Ok(instance_id.to_string()) } /// Wait for an instance to be ready and status ok (default wait 60 seconds) pub async fn wait_for_instance_ready( &self, instance_id: &str, duration: Option<Duration>, ) -> Result<(), EC2Error> { self.client .wait_until_instance_status_ok() .instance_ids(instance_id) .wait(duration.unwrap_or(Duration::from_secs(60))) .await .map_err(|err| match err { WaiterError::ExceededMaxWait(exceeded) => EC2Error(format!( "Exceeded max time ({}s) waiting for instance to start.", exceeded.max_wait().as_secs() )), _ => EC2Error::from(err), })?; Ok(()) } pub async fn describe_instance(&self, instance_id: &str) -> Result<Instance, EC2Error> { let response = self .client .describe_instances() .instance_ids(instance_id) .send() .await?; let instance = response .reservations() .first() .ok_or_else(|| EC2Error::new(format!("No instance reservations for {instance_id}")))? .instances() .first() .ok_or_else(|| { EC2Error::new(format!("No instances in reservation for {instance_id}")) })?; Ok(instance.clone()) } pub async fn start_instance(&self, instance_id: &str) -> Result<(), EC2Error> { tracing::info!("Starting instance {instance_id}"); self.client .start_instances() .instance_ids(instance_id) .send() .await?; tracing::info!("Started instance."); Ok(()) } pub async fn stop_instance(&self, instance_id: &str) -> Result<(), EC2Error> { tracing::info!("Stopping instance {instance_id}"); self.client .stop_instances() .instance_ids(instance_id) .send() .await?; self.wait_for_instance_stopped(instance_id, None).await?; tracing::info!("Stopped instance."); Ok(()) } pub async fn reboot_instance(&self, instance_id: &str) -> Result<(), EC2Error> { tracing::info!("Rebooting instance {instance_id}"); self.client .reboot_instances() .instance_ids(instance_id) .send() .await?; Ok(()) } pub async fn wait_for_instance_stopped( &self, instance_id: &str, duration: Option<Duration>, ) -> Result<(), EC2Error> { self.client .wait_until_instance_stopped() .instance_ids(instance_id) .wait(duration.unwrap_or(Duration::from_secs(60))) .await .map_err(|err| match err { WaiterError::ExceededMaxWait(exceeded) => EC2Error(format!( "Exceeded max time ({}s) waiting for instance to stop.", exceeded.max_wait().as_secs(), )), _ => EC2Error::from(err), })?; Ok(()) } pub async fn delete_instance(&self, instance_id: &str) -> Result<(), EC2Error> { tracing::info!("Deleting instance with id {instance_id}"); self.stop_instance(instance_id).await?; self.client .terminate_instances() .instance_ids(instance_id) .send() .await?; self.wait_for_instance_terminated(instance_id).await?; tracing::info!("Terminated instance with id {instance_id}"); Ok(()) } async fn wait_for_instance_terminated(&self, instance_id: &str) -> Result<(), EC2Error> { self.client .wait_until_instance_terminated() .instance_ids(instance_id) .wait(Duration::from_secs(60)) .await .map_err(|err| match err { WaiterError::ExceededMaxWait(exceeded) => EC2Error(format!( "Exceeded max time ({}s) waiting for instance to terminate.", exceeded.max_wait().as_secs(), )), _ => EC2Error::from(err), })?; Ok(()) } pub async fn allocate_ip_address(&self) -> Result<AllocateAddressOutput, EC2Error> { self.client .allocate_address() .domain(DomainType::Vpc) .send() .await .map_err(EC2Error::from) } pub async fn deallocate_ip_address(&self, allocation_id: &str) -> Result<(), EC2Error> { self.client .release_address() .allocation_id(allocation_id) .send() .await?; Ok(()) } pub async fn associate_ip_address( &self, allocation_id: &str, instance_id: &str, ) -> Result<AssociateAddressOutput, EC2Error> { let response = self .client .associate_address() .allocation_id(allocation_id) .instance_id(instance_id) .send() .await?; Ok(response) } pub async fn disassociate_ip_address(&self, association_id: &str) -> Result<(), EC2Error> { self.client .disassociate_address() .association_id(association_id) .send() .await?; Ok(()) } } #[derive(Debug)] pub struct EC2Error(String); impl EC2Error { pub fn new(value: impl Into<String>) -> Self { EC2Error(value.into()) } pub fn add_message(self, message: impl Into<String>) -> Self { EC2Error(format!("{}: {}", message.into(), self.0)) } } impl<T: ProvideErrorMetadata> From<T> for EC2Error { fn from(value: T) -> Self { EC2Error(format!( "{}: {}", value .code() .map(String::from) .unwrap_or("unknown code".into()), value .message() .map(String::from) .unwrap_or("missing reason".into()), )) } } impl std::error::Error for EC2Error {} impl std::fmt::Display for EC2Error { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "{}", self.0) } }

SSMStruct berfungsi sebagai titik automock untuk pengujian, dan fungsinya membungkus SSM SDK panggilan.

use aws_sdk_ssm::{types::Parameter, Client}; use aws_smithy_async::future::pagination_stream::TryFlatMap; use crate::ec2::EC2Error; #[cfg(test)] use mockall::automock; #[cfg(not(test))] pub use SSMImpl as SSM; #[cfg(test)] pub use MockSSMImpl as SSM; pub struct SSMImpl { inner: Client, } #[cfg_attr(test, automock)] impl SSMImpl { pub fn new(inner: Client) -> Self { SSMImpl { inner } } pub async fn list_path(&self, path: &str) -> Result<Vec<Parameter>, EC2Error> { let maybe_params: Vec<Result<Parameter, _>> = TryFlatMap::new( self.inner .get_parameters_by_path() .path(path) .into_paginator() .send(), ) .flat_map(|item| item.parameters.unwrap_or_default()) .collect() .await; // Fail on the first error let params = maybe_params .into_iter() .collect::<Result<Vec<Parameter>, _>>()?; Ok(params) } }

Skenario ini menggunakan beberapa struct gaya “Manajer” untuk menangani akses ke sumber daya yang dibuat dan dihapus di seluruh skenario.

use aws_sdk_ec2::operation::{ allocate_address::AllocateAddressOutput, associate_address::AssociateAddressOutput, }; use crate::ec2::{EC2Error, EC2}; /// ElasticIpManager tracks the lifecycle of a public IP address, including its /// allocation from the global pool and association with a specific instance. #[derive(Debug, Default)] pub struct ElasticIpManager { elastic_ip: Option<AllocateAddressOutput>, association: Option<AssociateAddressOutput>, } impl ElasticIpManager { pub fn has_allocation(&self) -> bool { self.elastic_ip.is_some() } pub fn public_ip(&self) -> &str { if let Some(allocation) = &self.elastic_ip { if let Some(addr) = allocation.public_ip() { return addr; } } "0.0.0.0" } pub async fn allocate(&mut self, ec2: &EC2) -> Result<(), EC2Error> { let allocation = ec2.allocate_ip_address().await?; self.elastic_ip = Some(allocation); Ok(()) } pub async fn associate(&mut self, ec2: &EC2, instance_id: &str) -> Result<(), EC2Error> { if let Some(allocation) = &self.elastic_ip { if let Some(allocation_id) = allocation.allocation_id() { let association = ec2.associate_ip_address(allocation_id, instance_id).await?; self.association = Some(association); return Ok(()); } } Err(EC2Error::new("No ip address allocation to associate")) } pub async fn remove(mut self, ec2: &EC2) -> Result<(), EC2Error> { if let Some(association) = &self.association { if let Some(association_id) = association.association_id() { ec2.disassociate_ip_address(association_id).await?; } } self.association = None; if let Some(allocation) = &self.elastic_ip { if let Some(allocation_id) = allocation.allocation_id() { ec2.deallocate_ip_address(allocation_id).await?; } } self.elastic_ip = None; Ok(()) } } use std::fmt::Display; use aws_sdk_ec2::types::{Instance, InstanceType, KeyPairInfo, SecurityGroup}; use crate::ec2::{EC2Error, EC2}; /// InstanceManager wraps the lifecycle of an EC2 Instance. #[derive(Debug, Default)] pub struct InstanceManager { instance: Option<Instance>, } impl InstanceManager { pub fn instance_id(&self) -> &str { if let Some(instance) = &self.instance { if let Some(id) = instance.instance_id() { return id; } } "Unknown" } pub fn instance_name(&self) -> &str { if let Some(instance) = &self.instance { if let Some(tag) = instance.tags().iter().find(|e| e.key() == Some("Name")) { if let Some(value) = tag.value() { return value; } } } "Unknown" } pub fn instance_ip(&self) -> &str { if let Some(instance) = &self.instance { if let Some(public_ip_address) = instance.public_ip_address() { return public_ip_address; } } "0.0.0.0" } pub fn instance_display_name(&self) -> String { format!("{} ({})", self.instance_name(), self.instance_id()) } /// Create an EC2 instance with the given ID on a given type, using a /// generated KeyPair and applying a list of security groups. pub async fn create( &mut self, ec2: &EC2, image_id: &str, instance_type: InstanceType, key_pair: &KeyPairInfo, security_groups: Vec<&SecurityGroup>, ) -> Result<(), EC2Error> { let instance_id = ec2 .create_instance(image_id, instance_type, key_pair, security_groups) .await?; let instance = ec2.describe_instance(&instance_id).await?; self.instance = Some(instance); Ok(()) } /// Start the managed EC2 instance, if present. pub async fn start(&self, ec2: &EC2) -> Result<(), EC2Error> { if self.instance.is_some() { ec2.start_instance(self.instance_id()).await?; } Ok(()) } /// Stop the managed EC2 instance, if present. pub async fn stop(&self, ec2: &EC2) -> Result<(), EC2Error> { if self.instance.is_some() { ec2.stop_instance(self.instance_id()).await?; } Ok(()) } pub async fn reboot(&self, ec2: &EC2) -> Result<(), EC2Error> { if self.instance.is_some() { ec2.reboot_instance(self.instance_id()).await?; ec2.wait_for_instance_stopped(self.instance_id(), None) .await?; ec2.wait_for_instance_ready(self.instance_id(), None) .await?; } Ok(()) } /// Terminate and delete the managed EC2 instance, if present. pub async fn delete(self, ec2: &EC2) -> Result<(), EC2Error> { if self.instance.is_some() { ec2.delete_instance(self.instance_id()).await?; } Ok(()) } } impl Display for InstanceManager { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { if let Some(instance) = &self.instance { writeln!(f, "\tID: {}", instance.instance_id().unwrap_or("(Unknown)"))?; writeln!( f, "\tImage ID: {}", instance.image_id().unwrap_or("(Unknown)") )?; writeln!( f, "\tInstance type: {}", instance .instance_type() .map(|it| format!("{it}")) .unwrap_or("(Unknown)".to_string()) )?; writeln!( f, "\tKey name: {}", instance.key_name().unwrap_or("(Unknown)") )?; writeln!(f, "\tVPC ID: {}", instance.vpc_id().unwrap_or("(Unknown)"))?; writeln!( f, "\tPublic IP: {}", instance.public_ip_address().unwrap_or("(Unknown)") )?; let instance_state = instance .state .as_ref() .map(|is| { is.name() .map(|isn| format!("{isn}")) .unwrap_or("(Unknown)".to_string()) }) .unwrap_or("(Unknown)".to_string()); writeln!(f, "\tState: {instance_state}")?; } else { writeln!(f, "\tNo loaded instance")?; } Ok(()) } } use std::{env, path::PathBuf}; use aws_sdk_ec2::types::KeyPairInfo; use crate::ec2::{EC2Error, EC2}; use super::util::Util; /// KeyPairManager tracks a KeyPairInfo and the path the private key has been /// written to, if it's been created. #[derive(Debug)] pub struct KeyPairManager { key_pair: KeyPairInfo, key_file_path: Option<PathBuf>, key_file_dir: PathBuf, } impl KeyPairManager { pub fn new() -> Self { Self::default() } pub fn key_pair(&self) -> &KeyPairInfo { &self.key_pair } pub fn key_file_path(&self) -> Option<&PathBuf> { self.key_file_path.as_ref() } pub fn key_file_dir(&self) -> &PathBuf { &self.key_file_dir } /// Creates a key pair that can be used to securely connect to an EC2 instance. /// The returned key pair contains private key information that cannot be retrieved /// again. The private key data is stored as a .pem file. /// /// :param key_name: The name of the key pair to create. pub async fn create( &mut self, ec2: &EC2, util: &Util, key_name: String, ) -> Result<KeyPairInfo, EC2Error> { let (key_pair, material) = ec2 .create_key_pair(key_name.clone()) .await .map_err(|e| e.add_message(format!("Couldn't create key {key_name}")))?; let path = self.key_file_dir.join(format!("{key_name}.pem")); util.write_secure(&key_name, &path, material)?; self.key_file_path = Some(path); self.key_pair = key_pair.clone(); Ok(key_pair) } pub async fn delete(self, ec2: &EC2, util: &Util) -> Result<(), EC2Error> { if let Some(key_pair_id) = self.key_pair.key_pair_id() { ec2.delete_key_pair(key_pair_id).await?; if let Some(key_path) = self.key_file_path() { if let Err(err) = util.remove(key_path) { eprintln!("Failed to remove {key_path:?} ({err:?})"); } } } Ok(()) } pub async fn list(&self, ec2: &EC2) -> Result<Vec<KeyPairInfo>, EC2Error> { ec2.list_key_pair().await } } impl Default for KeyPairManager { fn default() -> Self { KeyPairManager { key_pair: KeyPairInfo::builder().build(), key_file_path: Default::default(), key_file_dir: env::temp_dir(), } } } use std::net::Ipv4Addr; use aws_sdk_ec2::types::SecurityGroup; use crate::ec2::{EC2Error, EC2}; /// SecurityGroupManager tracks the lifecycle of a SecurityGroup for an instance, /// including adding a rule to allow SSH from a public IP address. #[derive(Debug, Default)] pub struct SecurityGroupManager { group_name: String, group_description: String, security_group: Option<SecurityGroup>, } impl SecurityGroupManager { pub async fn create( &mut self, ec2: &EC2, group_name: &str, group_description: &str, ) -> Result<(), EC2Error> { self.group_name = group_name.into(); self.group_description = group_description.into(); self.security_group = Some( ec2.create_security_group(group_name, group_description) .await .map_err(|e| e.add_message("Couldn't create security group"))?, ); Ok(()) } pub async fn authorize_ingress(&self, ec2: &EC2, ip_address: Ipv4Addr) -> Result<(), EC2Error> { if let Some(sg) = &self.security_group { ec2.authorize_security_group_ssh_ingress( sg.group_id() .ok_or_else(|| EC2Error::new("Missing security group ID"))?, vec![ip_address], ) .await?; }; Ok(()) } pub async fn delete(self, ec2: &EC2) -> Result<(), EC2Error> { if let Some(sg) = &self.security_group { ec2.delete_security_group( sg.group_id() .ok_or_else(|| EC2Error::new("Missing security group ID"))?, ) .await?; }; Ok(()) } pub fn group_name(&self) -> &str { &self.group_name } pub fn vpc_id(&self) -> Option<&str> { self.security_group.as_ref().and_then(|sg| sg.vpc_id()) } pub fn security_group(&self) -> Option<&SecurityGroup> { self.security_group.as_ref() } } impl std::fmt::Display for SecurityGroupManager { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match &self.security_group { Some(sg) => { writeln!( f, "Security group: {}", sg.group_name().unwrap_or("(unknown group)") )?; writeln!(f, "\tID: {}", sg.group_id().unwrap_or("(unknown group id)"))?; writeln!(f, "\tVPC: {}", sg.vpc_id().unwrap_or("(unknown group vpc)"))?; if !sg.ip_permissions().is_empty() { writeln!(f, "\tInbound Permissions:")?; for permission in sg.ip_permissions() { writeln!(f, "\t\t{permission:?}")?; } } Ok(()) } None => writeln!(f, "No security group loaded."), } } }

Titik masuk utama untuk skenario.

use ec2_code_examples::{ ec2::EC2, getting_started::{ scenario::{run, Ec2InstanceScenario}, util::UtilImpl, }, ssm::SSM, }; #[tokio::main] async fn main() { tracing_subscriber::fmt::init(); let sdk_config = aws_config::load_from_env().await; let ec2 = EC2::new(aws_sdk_ec2::Client::new(&sdk_config)); let ssm = SSM::new(aws_sdk_ssm::Client::new(&sdk_config)); let util = UtilImpl {}; let scenario = Ec2InstanceScenario::new(ec2, ssm, util); run(scenario).await; }