From ed0ab406adeb7a35e372fb0046bad383300ce218 Mon Sep 17 00:00:00 2001
From: Rachel Hagerman <110480692+rlhagerm@users.noreply.github.com>
Date: Mon, 2 Dec 2024 09:59:56 -0600
Subject: [PATCH 1/5] Updates for EC2
---
dotnetv4/DotNetV4Examples.sln | 26 +
dotnetv4/EC2/Actions/EC2Actions.csproj | 19 +
dotnetv4/EC2/Actions/EC2Wrapper.cs | 1046 +++++++++++++++++
dotnetv4/EC2/Actions/HelloEC2.cs | 62 +
dotnetv4/EC2/Actions/SsmWrapper.cs | 47 +
dotnetv4/EC2/Actions/Usings.cs | 9 +
dotnetv4/EC2/EC2Examples.sln | 52 +
dotnetv4/EC2/README.md | 177 +++
.../EC2/Scenarios/EC2_Basics/Basics.csproj | 21 +
.../EC2/Scenarios/EC2_Basics/EC2Basics.cs | 326 +++++
.../EC2/Scenarios/EC2_Basics/UIMethods.cs | 59 +
dotnetv4/EC2/Scenarios/EC2_Basics/Usings.cs | 7 +
dotnetv4/EC2/Tests/EC2Tests.csproj | 43 +
dotnetv4/EC2/Tests/EC2WrapperTests.cs | 79 ++
dotnetv4/EC2/Tests/Usings.cs | 11 +
dotnetv4/EC2/Tests/testsettings.json | 12 +
16 files changed, 1996 insertions(+)
create mode 100644 dotnetv4/EC2/Actions/EC2Actions.csproj
create mode 100644 dotnetv4/EC2/Actions/EC2Wrapper.cs
create mode 100644 dotnetv4/EC2/Actions/HelloEC2.cs
create mode 100644 dotnetv4/EC2/Actions/SsmWrapper.cs
create mode 100644 dotnetv4/EC2/Actions/Usings.cs
create mode 100644 dotnetv4/EC2/EC2Examples.sln
create mode 100644 dotnetv4/EC2/README.md
create mode 100644 dotnetv4/EC2/Scenarios/EC2_Basics/Basics.csproj
create mode 100644 dotnetv4/EC2/Scenarios/EC2_Basics/EC2Basics.cs
create mode 100644 dotnetv4/EC2/Scenarios/EC2_Basics/UIMethods.cs
create mode 100644 dotnetv4/EC2/Scenarios/EC2_Basics/Usings.cs
create mode 100644 dotnetv4/EC2/Tests/EC2Tests.csproj
create mode 100644 dotnetv4/EC2/Tests/EC2WrapperTests.cs
create mode 100644 dotnetv4/EC2/Tests/Usings.cs
create mode 100644 dotnetv4/EC2/Tests/testsettings.json
diff --git a/dotnetv4/DotNetV4Examples.sln b/dotnetv4/DotNetV4Examples.sln
index da9f1814a05..ab7be69d4d9 100644
--- a/dotnetv4/DotNetV4Examples.sln
+++ b/dotnetv4/DotNetV4Examples.sln
@@ -109,6 +109,16 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CloudWatchScenario", "Cloud
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CloudWatchActions", "CloudWatch\Actions\CloudWatchActions.csproj", "{EAF4A3B8-5CD0-48ED-B848-0EA6D451B8D3}"
EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "EC2", "EC2", "{9424FB14-B6DE-44CE-B675-AC2B57EC1E69}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EC2Tests", "EC2\Tests\EC2Tests.csproj", "{C99A0F7C-9477-4985-90F6-8EED38ECAC10}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Scenarios", "Scenarios", "{6C167F25-F97F-4854-8CD8-A2D446B6799B}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Basics", "EC2\Scenarios\EC2_Basics\Basics.csproj", "{D95519CA-BD27-45AE-B83B-3FB02E7AE445}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EC2Actions", "EC2\Actions\EC2Actions.csproj", "{0633CB2B-3508-48E5-A8C2-427A83A5CA6E}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -271,6 +281,18 @@ Global
{EAF4A3B8-5CD0-48ED-B848-0EA6D451B8D3}.Debug|Any CPU.Build.0 = Debug|Any CPU
{EAF4A3B8-5CD0-48ED-B848-0EA6D451B8D3}.Release|Any CPU.ActiveCfg = Release|Any CPU
{EAF4A3B8-5CD0-48ED-B848-0EA6D451B8D3}.Release|Any CPU.Build.0 = Release|Any CPU
+ {C99A0F7C-9477-4985-90F6-8EED38ECAC10}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {C99A0F7C-9477-4985-90F6-8EED38ECAC10}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {C99A0F7C-9477-4985-90F6-8EED38ECAC10}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {C99A0F7C-9477-4985-90F6-8EED38ECAC10}.Release|Any CPU.Build.0 = Release|Any CPU
+ {D95519CA-BD27-45AE-B83B-3FB02E7AE445}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {D95519CA-BD27-45AE-B83B-3FB02E7AE445}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {D95519CA-BD27-45AE-B83B-3FB02E7AE445}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {D95519CA-BD27-45AE-B83B-3FB02E7AE445}.Release|Any CPU.Build.0 = Release|Any CPU
+ {0633CB2B-3508-48E5-A8C2-427A83A5CA6E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {0633CB2B-3508-48E5-A8C2-427A83A5CA6E}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {0633CB2B-3508-48E5-A8C2-427A83A5CA6E}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {0633CB2B-3508-48E5-A8C2-427A83A5CA6E}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -323,6 +345,10 @@ Global
{106FBE12-6FF7-40DC-9B3C-E5F67F335B32} = {CED87D19-7F82-4D67-8A30-3EE085D07E45}
{565A9701-3D9C-49F8-86B7-D256A1D9E074} = {CED87D19-7F82-4D67-8A30-3EE085D07E45}
{EAF4A3B8-5CD0-48ED-B848-0EA6D451B8D3} = {CED87D19-7F82-4D67-8A30-3EE085D07E45}
+ {C99A0F7C-9477-4985-90F6-8EED38ECAC10} = {9424FB14-B6DE-44CE-B675-AC2B57EC1E69}
+ {6C167F25-F97F-4854-8CD8-A2D446B6799B} = {9424FB14-B6DE-44CE-B675-AC2B57EC1E69}
+ {D95519CA-BD27-45AE-B83B-3FB02E7AE445} = {6C167F25-F97F-4854-8CD8-A2D446B6799B}
+ {0633CB2B-3508-48E5-A8C2-427A83A5CA6E} = {9424FB14-B6DE-44CE-B675-AC2B57EC1E69}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {08502818-E8E1-4A91-A51C-4C8C8D4FF9CA}
diff --git a/dotnetv4/EC2/Actions/EC2Actions.csproj b/dotnetv4/EC2/Actions/EC2Actions.csproj
new file mode 100644
index 00000000000..6840737b6e5
--- /dev/null
+++ b/dotnetv4/EC2/Actions/EC2Actions.csproj
@@ -0,0 +1,19 @@
+
+
+
+ Exe
+ net8.0
+ enable
+ enable
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/dotnetv4/EC2/Actions/EC2Wrapper.cs b/dotnetv4/EC2/Actions/EC2Wrapper.cs
new file mode 100644
index 00000000000..691ae97b6be
--- /dev/null
+++ b/dotnetv4/EC2/Actions/EC2Wrapper.cs
@@ -0,0 +1,1046 @@
+// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
+// SPDX-License-Identifier: Apache-2.0
+
+using Microsoft.Extensions.Logging;
+
+namespace EC2Actions;
+
+// snippet-start:[EC2.dotnetv4.EC2WrapperClass]
+///
+/// Methods of this class perform Amazon Elastic Compute Cloud (Amazon EC2).
+///
+public class EC2Wrapper
+{
+ private readonly IAmazonEC2 _amazonEC2;
+ private readonly ILogger _logger;
+
+ ///
+ /// Constructor for the EC2Wrapper class.
+ ///
+ /// The injected EC2 client.
+ /// The injected logger.
+ public EC2Wrapper(IAmazonEC2 amazonService, ILogger logger)
+ {
+ _amazonEC2 = amazonService;
+ _logger = logger;
+ }
+
+ // snippet-start:[EC2.dotnetv4.AllocateAddress]
+ ///
+ /// 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.
+ ///
+ /// The response object for the allocated address.
+ public async Task AllocateAddress()
+ {
+ var request = new AllocateAddressRequest();
+
+ try
+ {
+ var response = await _amazonEC2.AllocateAddressAsync(request);
+ Console.WriteLine($"Allocated IP: {response.PublicIp} with allocation ID {response.AllocationId}.");
+ return response;
+ }
+ catch (AmazonEC2Exception ec2Exception)
+ {
+ if (ec2Exception.ErrorCode == "AddressLimitExceeded")
+ {
+ // For more information on Elastic IP address quotas, see:
+ // https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/elastic-ip-addresses-eip.html#using-instance-addressing-limit
+ _logger.LogError($"Unable to allocate Elastic IP, address limit exceeded. {ec2Exception.Message}");
+ }
+
+ throw;
+ }
+ catch (Exception ex)
+ {
+ _logger.LogError($"An error occurred while allocating Elastic IP.: {ex.Message}");
+ throw;
+ }
+ }
+ // snippet-end:[EC2.dotnetv4.AllocateAddress]
+
+ // snippet-start:[EC2.dotnetv4.AssociateAddress]
+ ///
+ /// 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.
+ ///
+ /// The allocation Id of an Elastic IP address.
+ /// The instance Id of the EC2 instance to
+ /// associate the address with.
+ /// The association Id that represents
+ /// the association of the Elastic IP address with an instance.
+ public async Task AssociateAddress(string allocationId, string instanceId)
+ {
+ try
+ {
+ var request = new AssociateAddressRequest
+ {
+ AllocationId = allocationId,
+ InstanceId = instanceId
+ };
+
+ var response = await _amazonEC2.AssociateAddressAsync(request);
+ return response.AssociationId;
+ }
+ catch (AmazonEC2Exception ec2Exception)
+ {
+ if (ec2Exception.ErrorCode == "InvalidInstanceId")
+ {
+ _logger.LogError(
+ $"InstanceId is invalid, unable to associate address. {ec2Exception.Message}");
+ }
+
+ throw;
+ }
+ catch (Exception ex)
+ {
+ _logger.LogError(
+ $"An error occurred while associating the Elastic IP.: {ex.Message}");
+ throw;
+ }
+ }
+ // snippet-end:[EC2.dotnetv4.AssociateAddress]
+
+ // snippet-start:[EC2.dotnetv4.AuthorizeSecurityGroupIngress]
+ ///
+ /// Authorize the local computer ingress to EC2 instances associated
+ /// with the virtual private cloud (VPC) security group.
+ ///
+ /// The name of the security group.
+ /// A Boolean value indicating the success of the action.
+ public async Task AuthorizeSecurityGroupIngress(string groupName)
+ {
+ try
+ {
+ // Get the IP address for the local computer.
+ var ipAddress = await GetIpAddress();
+ Console.WriteLine($"Your IP address is: {ipAddress}");
+ var ipRanges =
+ new List { new IpRange { CidrIp = $"{ipAddress}/32" } };
+ var permission = new IpPermission
+ {
+ Ipv4Ranges = ipRanges,
+ IpProtocol = "tcp",
+ FromPort = 22,
+ ToPort = 22
+ };
+ var permissions = new List { permission };
+ var response = await _amazonEC2.AuthorizeSecurityGroupIngressAsync(
+ new AuthorizeSecurityGroupIngressRequest(groupName, permissions));
+ return response.HttpStatusCode == HttpStatusCode.OK;
+ }
+ catch (AmazonEC2Exception ec2Exception)
+ {
+ if (ec2Exception.ErrorCode == "InvalidPermission.Duplicate")
+ {
+ _logger.LogError(
+ $"The ingress rule already exists. {ec2Exception.Message}");
+ }
+
+ throw;
+ }
+ catch (Exception ex)
+ {
+ _logger.LogError(
+ $"An error occurred while authorizing ingress.: {ex.Message}");
+ throw;
+ }
+ }
+
+ ///
+ /// Authorize the local computer for ingress to
+ /// the Amazon EC2 SecurityGroup.
+ ///
+ /// The IPv4 address of the computer running the scenario.
+ private static async Task 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();
+ }
+ // snippet-end:[EC2.dotnetv4.AuthorizeSecurityGroupIngress]
+
+ // snippet-start:[EC2.dotnetv4.CreateKeyPair]
+ ///
+ /// Create an Amazon EC2 key pair with a specified name.
+ ///
+ /// The name for the new key pair.
+ /// The Amazon EC2 key pair created.
+ public async Task CreateKeyPair(string keyPairName)
+ {
+ try
+ {
+ var request = new CreateKeyPairRequest { KeyName = keyPairName, };
+
+ var response = await _amazonEC2.CreateKeyPairAsync(request);
+
+ var kp = response.KeyPair;
+ // Return the key pair so it can be saved if needed.
+
+ // Wait until the key pair exists.
+ int retries = 5;
+ while (retries-- > 0)
+ {
+ Console.WriteLine($"Checking for new KeyPair {keyPairName}...");
+ var keyPairs = await DescribeKeyPairs(keyPairName);
+ if (keyPairs.Any())
+ {
+ return kp;
+ }
+
+ Thread.Sleep(5000);
+ retries--;
+ }
+ _logger.LogError($"Unable to find newly created KeyPair {keyPairName}.");
+ throw new DoesNotExistException("KeyPair not found");
+ }
+ catch (AmazonEC2Exception ec2Exception)
+ {
+ if (ec2Exception.ErrorCode == "InvalidKeyPair.Duplicate")
+ {
+ _logger.LogError(
+ $"A key pair called {keyPairName} already exists.");
+ }
+
+ throw;
+ }
+ catch (Exception ex)
+ {
+ _logger.LogError(
+ $"An error occurred while creating the key pair.: {ex.Message}");
+ throw;
+ }
+ }
+
+ ///
+ /// Save KeyPair information to a temporary file.
+ ///
+ /// The name of the key pair.
+ /// The full path to the temporary file.
+ 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;
+ }
+ // snippet-end:[EC2.dotnetv4.CreateKeyPair]
+
+ // snippet-start:[EC2.dotnetv4.CreateSecurityGroup]
+ ///
+ /// Create an Amazon EC2 security group with a specified name and description.
+ ///
+ /// The name for the new security group.
+ /// A description of the new security group.
+ /// The group Id of the new security group.
+ public async Task CreateSecurityGroup(string groupName, string groupDescription)
+ {
+ try
+ {
+ var response = await _amazonEC2.CreateSecurityGroupAsync(
+ new CreateSecurityGroupRequest(groupName, groupDescription));
+
+ // Wait until the security group exists.
+ int retries = 5;
+ while (retries-- > 0)
+ {
+ var groups = await DescribeSecurityGroups(response.GroupId);
+ if (groups.Any())
+ {
+ return response.GroupId;
+ }
+
+ Thread.Sleep(5000);
+ retries--;
+ }
+ _logger.LogError($"Unable to find newly created group {groupName}.");
+ throw new DoesNotExistException("security group not found");
+ }
+ catch (AmazonEC2Exception ec2Exception)
+ {
+ if (ec2Exception.ErrorCode == "ResourceAlreadyExists")
+ {
+ _logger.LogError(
+ $"A security group with the name {groupName} already exists. {ec2Exception.Message}");
+ }
+ throw;
+ }
+ catch (Exception ex)
+ {
+ _logger.LogError(
+ $"An error occurred while creating the security group.: {ex.Message}");
+ throw;
+ }
+ }
+
+ // snippet-end:[EC2.dotnetv4.CreateSecurityGroup]
+
+ // snippet-start:[EC2.dotnetv4.CreateVPC]
+ ///
+ /// Create a new Amazon EC2 VPC.
+ ///
+ /// The CIDR block for the new security group.
+ /// The VPC Id of the new VPC.
+ public async Task 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;
+ }
+ }
+ // snippet-end:[EC2.dotnetv4.CreateVPC]
+
+ // snippet-start:[EC2.dotnetv4.DeleteKeyPair]
+ ///
+ /// Delete an Amazon EC2 key pair.
+ ///
+ /// The name of the key pair to delete.
+ /// A Boolean value indicating the success of the action.
+ public async Task DeleteKeyPair(string keyPairName)
+ {
+ try
+ {
+ await _amazonEC2.DeleteKeyPairAsync(new DeleteKeyPairRequest(keyPairName)).ConfigureAwait(false);
+ return true;
+ }
+ catch (AmazonEC2Exception ec2Exception)
+ {
+ if (ec2Exception.ErrorCode == "InvalidKeyPair.NotFound")
+ {
+ _logger.LogError($"KeyPair {keyPairName} does not exist and cannot be deleted. Please verify the key pair name and try again.");
+ }
+
+ return false;
+ }
+ catch (Exception ex)
+ {
+ Console.WriteLine($"Couldn't delete the key pair because: {ex.Message}");
+ return false;
+ }
+ }
+
+ ///
+ /// Delete the temporary file where the key pair information was saved.
+ ///
+ /// The path to the temporary file.
+ public void DeleteTempFile(string tempFileName)
+ {
+ if (File.Exists(tempFileName))
+ {
+ File.Delete(tempFileName);
+ }
+ }
+ // snippet-end:[EC2.dotnetv4.DeleteKeyPair]
+
+ // snippet-start:[EC2.dotnetv4.DeleteSecurityGroup]
+ ///
+ /// Delete an Amazon EC2 security group.
+ ///
+ /// The name of the group to delete.
+ /// A Boolean value indicating the success of the action.
+ public async Task DeleteSecurityGroup(string groupId)
+ {
+ try
+ {
+ var response =
+ await _amazonEC2.DeleteSecurityGroupAsync(
+ new DeleteSecurityGroupRequest { GroupId = groupId });
+ return response.HttpStatusCode == HttpStatusCode.OK;
+ }
+ catch (AmazonEC2Exception ec2Exception)
+ {
+ if (ec2Exception.ErrorCode == "InvalidGroup.NotFound")
+ {
+ _logger.LogError(
+ $"Security Group {groupId} does not exist and cannot be deleted. Please verify the ID and try again.");
+ }
+
+ return false;
+ }
+ catch (Exception ex)
+ {
+ Console.WriteLine($"Couldn't delete the security group because: {ex.Message}");
+ return false;
+ }
+ }
+ // snippet-end:[EC2.dotnetv4.DeleteSecurityGroup]
+
+ // snippet-start:[EC2.dotnetv4.DeleteVPC]
+ ///
+ /// Delete an Amazon EC2 VPC.
+ ///
+ /// A Boolean value indicating the success of the action.
+ public async Task DeleteVpc(string vpcId)
+ {
+ var request = new DeleteVpcRequest
+ {
+ VpcId = vpcId,
+ };
+
+ var response = await _amazonEC2.DeleteVpcAsync(request);
+
+ return response.HttpStatusCode == System.Net.HttpStatusCode.OK;
+ }
+ // snippet-end:[EC2.dotnetv4.DeleteVPC]
+
+ // snippet-start:[EC2.dotnetv4.DescribeImages]
+ ///
+ /// Get information about existing Amazon EC2 images.
+ ///
+ /// A list of image information.
+ public async Task> DescribeImages(List? 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;
+ }
+
+ ///
+ /// Display the information returned by DescribeImages.
+ ///
+ /// The list of image information to display.
+ public void DisplayImageInfo(List images)
+ {
+ images.ForEach(image =>
+ {
+ Console.WriteLine($"{image.Name} Created on: {image.CreationDate}");
+ });
+
+ }
+ // snippet-end:[EC2.dotnetv4.DescribeImages]
+
+ // snippet-start:[EC2.dotnetv4.DescribeInstance]
+ ///
+ /// Get information about an Amazon EC2 instance.
+ ///
+ /// The instance Id of the EC2 instance.
+ /// An EC2 instance.
+ public async Task DescribeInstance(string instanceId)
+ {
+ var response = await _amazonEC2.DescribeInstancesAsync(
+ new DescribeInstancesRequest { InstanceIds = new List { instanceId } });
+ return response.Reservations[0].Instances[0];
+ }
+
+ ///
+ /// Display EC2 instance information.
+ ///
+ /// The instance Id of the EC2 instance.
+ 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}");
+ }
+ // snippet-end:[EC2.dotnetv4.DescribeInstance]
+
+ // snippet-start:[EC2.dotnetv4.DescribeInstances]
+ ///
+ /// Get information about EC2 instances with a particular state.
+ ///
+ /// The name of the tag to filter on.
+ /// The value of the tag to look for.
+ /// True if successful.
+ public async Task GetInstancesWithState(string state)
+ {
+ try
+ {
+ // Filters the results of the instance list.
+ var filters = new List
+ {
+ new Filter
+ {
+ Name = $"instance-state-name",
+ Values = new List { state, },
+ },
+ };
+ var request = new DescribeInstancesRequest { Filters = filters, };
+
+ Console.WriteLine($"\nShowing instances with state {state}");
+ var paginator = _amazonEC2.Paginators.DescribeInstances(request);
+
+ await foreach (var response in paginator.Responses)
+ {
+ foreach (var reservation in response.Reservations)
+ {
+ foreach (var instance in reservation.Instances)
+ {
+ Console.Write($"Instance ID: {instance.InstanceId} ");
+ Console.WriteLine($"\tCurrent State: {instance.State.Name}");
+ }
+ }
+ }
+
+ return true;
+ }
+ catch (AmazonEC2Exception ec2Exception)
+ {
+ if (ec2Exception.ErrorCode == "InvalidParameterValue")
+ {
+ _logger.LogError(
+ $"Invalid parameter value for filtering instances.");
+ }
+
+ return false;
+ }
+ catch (Exception ex)
+ {
+ Console.WriteLine($"Couldn't list instances because: {ex.Message}");
+ return false;
+ }
+ }
+ // snippet-end:[EC2.dotnetv4.DescribeInstances]
+
+ // snippet-start:[EC2.dotnetv4.DescribeInstanceTypes]
+ ///
+ /// Describe the instance types available.
+ ///
+ /// A list of instance type information.
+ public async Task> DescribeInstanceTypes(ArchitectureValues architecture)
+ {
+ try
+ {
+ var request = new DescribeInstanceTypesRequest();
+
+ var filters = new List
+ {
+ new Filter("processor-info.supported-architecture",
+ new List { architecture.ToString() })
+ };
+ filters.Add(new Filter("instance-type", new() { "*.micro", "*.small" }));
+
+ request.Filters = filters;
+ var instanceTypes = new List();
+
+ var paginator = _amazonEC2.Paginators.DescribeInstanceTypes(request);
+ await foreach (var instanceType in paginator.InstanceTypes)
+ {
+ instanceTypes.Add(instanceType);
+ }
+
+ return instanceTypes;
+ }
+ catch (AmazonEC2Exception ec2Exception)
+ {
+ if (ec2Exception.ErrorCode == "InvalidParameterValue")
+ {
+ _logger.LogError(
+ $"Parameters are invalid. Ensure architecture and size strings conform to DescribeInstanceTypes API reference.");
+ }
+
+ throw;
+ }
+ catch (Exception ex)
+ {
+ Console.WriteLine($"Couldn't delete the security group because: {ex.Message}");
+ throw;
+ }
+ }
+ // snippet-end:[EC2.dotnetv4.DescribeInstanceTypes]
+
+ // snippet-start:[EC2.dotnetv4.DescribeKeyPairs]
+ ///
+ /// Get information about an Amazon EC2 key pair.
+ ///
+ /// The name of the key pair.
+ /// A list of key pair information.
+ public async Task> DescribeKeyPairs(string keyPairName)
+ {
+ try
+ {
+ var request = new DescribeKeyPairsRequest();
+ if (!string.IsNullOrEmpty(keyPairName))
+ {
+ request = new DescribeKeyPairsRequest
+ {
+ KeyNames = new List { keyPairName }
+ };
+ }
+
+ var response = await _amazonEC2.DescribeKeyPairsAsync(request);
+ return response.KeyPairs.ToList();
+ }
+ catch (AmazonEC2Exception ec2Exception)
+ {
+ if (ec2Exception.ErrorCode == "InvalidKeyPair.NotFound")
+ {
+ _logger.LogError(
+ $"A key pair called {keyPairName} does not exist.");
+ }
+
+ throw;
+ }
+ catch (Exception ex)
+ {
+ _logger.LogError(
+ $"An error occurred while describing the key pair.: {ex.Message}");
+ throw;
+ }
+ }
+
+ // snippet-end:[EC2.dotnetv4.DescribeKeyPairs]
+
+ // snippet-start:[EC2.dotnetv4.DescribeSecurityGroups]
+ ///
+ /// Retrieve information for one or all Amazon EC2 security group.
+ ///
+ /// The optional Id of a specific Amazon EC2 security group.
+ /// A list of security group information.
+ public async Task> DescribeSecurityGroups(string groupId)
+ {
+ try
+ {
+ var securityGroups = new List();
+ var request = new DescribeSecurityGroupsRequest();
+
+ if (!string.IsNullOrEmpty(groupId))
+ {
+ var groupIds = new List { groupId };
+ request.GroupIds = groupIds;
+ }
+
+ var paginatorForSecurityGroups =
+ _amazonEC2.Paginators.DescribeSecurityGroups(request);
+
+ await foreach (var securityGroup in paginatorForSecurityGroups.SecurityGroups)
+ {
+ securityGroups.Add(securityGroup);
+ }
+
+ return securityGroups;
+
+ }
+ catch (AmazonEC2Exception ec2Exception)
+ {
+ if (ec2Exception.ErrorCode == "InvalidGroup.NotFound")
+ {
+ _logger.LogError(
+ $"A security group {groupId} does not exist.");
+ }
+
+ throw;
+ }
+ catch (Exception ex)
+ {
+ _logger.LogError(
+ $"An error occurred while listing security groups. {ex.Message}");
+ throw;
+ }
+ }
+
+ ///
+ /// Display the information returned by the call to
+ /// DescribeSecurityGroupsAsync.
+ ///
+ /// A list of security group information.
+ 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}");
+ });
+ }
+
+ // snippet-end:[EC2.dotnetv4.DescribeSecurityGroups]
+
+ // snippet-start:[EC2.dotnetv4.DisassociateAddress]
+ ///
+ /// Disassociate an Elastic IP address from an EC2 instance.
+ ///
+ /// The association Id.
+ /// A Boolean value indicating the success of the action.
+ public async Task DisassociateIp(string associationId)
+ {
+ try
+ {
+ var response = await _amazonEC2.DisassociateAddressAsync(
+ new DisassociateAddressRequest { AssociationId = associationId });
+ return response.HttpStatusCode == HttpStatusCode.OK;
+ }
+ catch (AmazonEC2Exception ec2Exception)
+ {
+ if (ec2Exception.ErrorCode == "InvalidAssociationID.NotFound")
+ {
+ _logger.LogError(
+ $"AssociationId is invalid, unable to disassociate address. {ec2Exception.Message}");
+ }
+
+ return false;
+ }
+ catch (Exception ex)
+ {
+ _logger.LogError(
+ $"An error occurred while disassociating the Elastic IP.: {ex.Message}");
+ return false;
+ }
+ }
+ // snippet-end:[EC2.dotnetv4.DisassociateAddress]
+
+ // snippet-start:[EC2.dotnetv4.GetAMIList]
+ ///
+ /// Retrieve a list of available Amazon Linux images.
+ ///
+ /// A list of image information.
+ public async Task> GetEC2AmiList()
+ {
+ var filter = new Filter { Name = "architecture", Values = new List { "x86_64" } };
+ var filters = new List { filter };
+ var response = await _amazonEC2.DescribeImagesAsync(new DescribeImagesRequest { Filters = filters });
+ return response.Images;
+ }
+ // snippet-end:[EC2.dotnetv4.GetAMIList]
+
+ // snippet-start:[EC2.dotnetv4.RebootInstances]
+ ///
+ /// Reboot a specific EC2 instance.
+ ///
+ /// The instance Id of the instance that will be rebooted.
+ /// Async Task.
+ public async Task RebootInstances(string ec2InstanceId)
+ {
+ try
+ {
+ var request = new RebootInstancesRequest
+ {
+ InstanceIds = new List { ec2InstanceId },
+ };
+
+ await _amazonEC2.RebootInstancesAsync(request);
+
+ // Wait for the instance to be running.
+ Console.Write("Waiting for the instance to start.");
+ await WaitForInstanceState(ec2InstanceId, InstanceStateName.Running);
+
+ return true;
+ }
+ catch (AmazonEC2Exception ec2Exception)
+ {
+ if (ec2Exception.ErrorCode == "InvalidInstanceId")
+ {
+ _logger.LogError(
+ $"InstanceId {ec2InstanceId} is invalid, unable to reboot. {ec2Exception.Message}");
+ }
+ return false;
+ }
+ catch (Exception ex)
+ {
+ _logger.LogError(
+ $"An error occurred while rebooting the instance {ec2InstanceId}.: {ex.Message}");
+ return false;
+ }
+ }
+ // snippet-end:[EC2.dotnetv4.RebootInstances]
+
+ // snippet-start:[EC2.dotnetv4.ReleaseAddress]
+ ///
+ /// Release an Elastic IP address. After the Elastic IP address is released,
+ /// it can no longer be used.
+ ///
+ /// The allocation Id of the Elastic IP address.
+ /// True if successful.
+ public async Task ReleaseAddress(string allocationId)
+ {
+ try
+ {
+ var request = new ReleaseAddressRequest { AllocationId = allocationId };
+
+ var response = await _amazonEC2.ReleaseAddressAsync(request);
+ return response.HttpStatusCode == HttpStatusCode.OK;
+ }
+ catch (AmazonEC2Exception ec2Exception)
+ {
+ if (ec2Exception.ErrorCode == "InvalidAllocationID.NotFound")
+ {
+ _logger.LogError(
+ $"AllocationId {allocationId} was not found. {ec2Exception.Message}");
+ }
+
+ return false;
+ }
+ catch (Exception ex)
+ {
+ _logger.LogError(
+ $"An error occurred while releasing the AllocationId {allocationId}.: {ex.Message}");
+ return false;
+ }
+ }
+ // snippet-end:[EC2.dotnetv4.ReleaseAddress]
+
+ // snippet-start:[EC2.dotnetv4.RunInstances]
+ ///
+ /// Create and run an EC2 instance.
+ ///
+ /// The image Id of the image used as a basis for the
+ /// EC2 instance.
+ /// The instance type of the EC2 instance to create.
+ /// The name of the key pair to associate with the
+ /// instance.
+ /// The Id of the Amazon EC2 security group that will be
+ /// allowed to interact with the new EC2 instance.
+ /// The instance Id of the new EC2 instance.
+ public async Task RunInstances(string imageId, string instanceType, string keyName, string groupId)
+ {
+ try
+ {
+ var request = new RunInstancesRequest
+ {
+ ImageId = imageId,
+ InstanceType = instanceType,
+ KeyName = keyName,
+ MinCount = 1,
+ MaxCount = 1,
+ SecurityGroupIds = new List { groupId }
+ };
+ var response = await _amazonEC2.RunInstancesAsync(request);
+ var instanceId = response.Reservation.Instances[0].InstanceId;
+
+ Console.Write("Waiting for the instance to start.");
+ await WaitForInstanceState(instanceId, InstanceStateName.Running);
+
+ return instanceId;
+ }
+ catch (AmazonEC2Exception ec2Exception)
+ {
+ if (ec2Exception.ErrorCode == "InvalidGroupId.NotFound")
+ {
+ _logger.LogError(
+ $"GroupId {groupId} was not found. {ec2Exception.Message}");
+ }
+
+ throw;
+ }
+ catch (Exception ex)
+ {
+ _logger.LogError(
+ $"An error occurred while running the instance.: {ex.Message}");
+ throw;
+ }
+ }
+
+ // snippet-end:[EC2.dotnetv4.RunInstances]
+
+ // snippet-start:[EC2.dotnetv4.StartInstances]
+ ///
+ /// Start an EC2 instance.
+ ///
+ /// The instance Id of the Amazon EC2 instance
+ /// to start.
+ /// Async task.
+ public async Task StartInstances(string ec2InstanceId)
+ {
+ try
+ {
+ var request = new StartInstancesRequest
+ {
+ InstanceIds = new List { ec2InstanceId },
+ };
+
+ await _amazonEC2.StartInstancesAsync(request);
+
+ Console.Write("Waiting for instance to start. ");
+ await WaitForInstanceState(ec2InstanceId, InstanceStateName.Running);
+ }
+ catch (AmazonEC2Exception ec2Exception)
+ {
+ if (ec2Exception.ErrorCode == "InvalidInstanceId")
+ {
+ _logger.LogError(
+ $"InstanceId is invalid, unable to start. {ec2Exception.Message}");
+ }
+
+ throw;
+ }
+ catch (Exception ex)
+ {
+ _logger.LogError(
+ $"An error occurred while starting the instance.: {ex.Message}");
+ throw;
+ }
+ }
+ // snippet-end:[EC2.dotnetv4.StartInstances]
+
+ // snippet-start:[EC2.dotnetv4.StopInstances]
+ ///
+ /// Stop an EC2 instance.
+ ///
+ /// The instance Id of the EC2 instance to
+ /// stop.
+ /// Async task.
+ public async Task StopInstances(string ec2InstanceId)
+ {
+ try
+ {
+ var request = new StopInstancesRequest
+ {
+ InstanceIds = new List { ec2InstanceId },
+ };
+
+ await _amazonEC2.StopInstancesAsync(request);
+ Console.Write("Waiting for the instance to stop.");
+ await WaitForInstanceState(ec2InstanceId, InstanceStateName.Stopped);
+
+ Console.WriteLine("\nThe instance has stopped.");
+ }
+ catch (AmazonEC2Exception ec2Exception)
+ {
+ if (ec2Exception.ErrorCode == "InvalidInstanceId")
+ {
+ _logger.LogError(
+ $"InstanceId is invalid, unable to stop. {ec2Exception.Message}");
+ }
+
+ throw;
+ }
+ catch (Exception ex)
+ {
+ _logger.LogError(
+ $"An error occurred while stopping the instance.: {ex.Message}");
+ throw;
+ }
+ }
+ // snippet-end:[EC2.dotnetv4.StopInstances]
+
+ // snippet-start:[EC2.dotnetv4.TerminateInstances]
+ ///
+ /// Terminate an EC2 instance.
+ ///
+ /// The instance Id of the EC2 instance
+ /// to terminate.
+ /// Async task.
+ public async Task> TerminateInstances(string ec2InstanceId)
+ {
+ try
+ {
+ var request = new TerminateInstancesRequest
+ {
+ InstanceIds = new List { ec2InstanceId }
+ };
+
+ var response = await _amazonEC2.TerminateInstancesAsync(request);
+ Console.Write("Waiting for the instance to terminate.");
+ await WaitForInstanceState(ec2InstanceId, InstanceStateName.Terminated);
+
+ Console.WriteLine($"\nThe instance {ec2InstanceId} has been terminated.");
+ return response.TerminatingInstances;
+ }
+ catch (AmazonEC2Exception ec2Exception)
+ {
+ if (ec2Exception.ErrorCode == "InvalidInstanceId")
+ {
+ _logger.LogError(
+ $"InstanceId is invalid, unable to terminate. {ec2Exception.Message}");
+ }
+
+ throw;
+ }
+ catch (Exception ex)
+ {
+ _logger.LogError(
+ $"An error occurred while terminating the instance.: {ex.Message}");
+ throw;
+ }
+ }
+ // snippet-end:[EC2.dotnetv4.TerminateInstances]
+
+ // snippet-start:[EC2.dotnetv4.WaitForInstanceState]
+ ///
+ /// Wait until an EC2 instance is in a specified state.
+ ///
+ /// The instance Id.
+ /// The state to wait for.
+ /// A Boolean value indicating the success of the action.
+ public async Task WaitForInstanceState(string instanceId, InstanceStateName stateName)
+ {
+ var request = new DescribeInstancesRequest
+ {
+ InstanceIds = new List { instanceId }
+ };
+
+ // Wait until the instance is in the specified state.
+ var hasState = false;
+ do
+ {
+ // Wait 5 seconds.
+ Thread.Sleep(5000);
+
+ // Check for the desired state.
+ var response = await _amazonEC2.DescribeInstancesAsync(request);
+ var instance = response.Reservations[0].Instances[0];
+ hasState = instance.State.Name == stateName;
+ Console.Write(". ");
+ } while (!hasState);
+
+ return hasState;
+ }
+
+ // snippet-end:[EC2.dotnetv4.WaitForInstanceState]
+}
+// snippet-end:[EC2.dotnetv4.EC2WrapperClass]
\ No newline at end of file
diff --git a/dotnetv4/EC2/Actions/HelloEC2.cs b/dotnetv4/EC2/Actions/HelloEC2.cs
new file mode 100644
index 00000000000..d6ae2731449
--- /dev/null
+++ b/dotnetv4/EC2/Actions/HelloEC2.cs
@@ -0,0 +1,62 @@
+// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
+// SPDX-License-Identifier: Apache-2.0
+
+// snippet-start:[EC2.dotnetv4.HelloEc2]
+
+namespace EC2Actions;
+
+public class HelloEc2
+{
+ ///
+ /// HelloEc2 lists the existing security groups for the default users.
+ ///
+ /// Command line arguments
+ /// Async task.
+ static async Task Main(string[] args)
+ {
+ // Set up dependency injection for Amazon Elastic Compute Cloud (Amazon EC2).
+ using var host = Microsoft.Extensions.Hosting.Host.CreateDefaultBuilder(args)
+ .ConfigureServices((_, services) =>
+ services.AddAWSService()
+ .AddTransient()
+ )
+ .Build();
+
+ // Now the client is available for injection.
+ var ec2Client = host.Services.GetRequiredService();
+
+ try
+ {
+ // Retrieve information for up to 10 Amazon EC2 security groups.
+ var request = new DescribeSecurityGroupsRequest { MaxResults = 10, };
+ var securityGroups = new List();
+
+ var paginatorForSecurityGroups =
+ ec2Client.Paginators.DescribeSecurityGroups(request);
+
+ await foreach (var securityGroup in paginatorForSecurityGroups.SecurityGroups)
+ {
+ securityGroups.Add(securityGroup);
+ }
+
+ // Now print the security groups returned by the call to
+ // DescribeSecurityGroupsAsync.
+ Console.WriteLine("Welcome to the EC2 Hello Service example. " +
+ "\nLet's list your Security Groups:");
+ securityGroups.ForEach(group =>
+ {
+ Console.WriteLine(
+ $"Security group: {group.GroupName} ID: {group.GroupId}");
+ });
+ }
+ catch (AmazonEC2Exception ex)
+ {
+ Console.WriteLine($"An Amazon EC2 service error occurred while listing security groups. {ex.Message}");
+ }
+ catch (Exception ex)
+ {
+ Console.WriteLine($"An error occurred while listing security groups. {ex.Message}");
+ }
+ }
+}
+// snippet-end:[EC2.dotnetv4.HelloEc2]
\ No newline at end of file
diff --git a/dotnetv4/EC2/Actions/SsmWrapper.cs b/dotnetv4/EC2/Actions/SsmWrapper.cs
new file mode 100644
index 00000000000..fe36855451a
--- /dev/null
+++ b/dotnetv4/EC2/Actions/SsmWrapper.cs
@@ -0,0 +1,47 @@
+// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
+// SPDX-License-Identifier: Apache-2.0
+
+namespace EC2Actions;
+
+// snippet-start:[SSM.dotnetv4.SSMActionsClass]
+///
+/// Methods that perform actions from the Simple Systems Management Service.
+///
+public class SsmWrapper
+{
+ private readonly IAmazonSimpleSystemsManagement _amazonSSM;
+
+ public SsmWrapper(IAmazonSimpleSystemsManagement amazonService)
+ {
+ _amazonSSM = amazonService;
+ }
+
+ ///
+ /// Get a list of parameter values based on the service path.
+ ///
+ /// The path used to retrieve parameters.
+ /// Async task.
+ public async Task> GetParametersByPath(string path)
+ {
+ var parameters = new List();
+ var request = new GetParametersByPathRequest { Path = path };
+
+ // Get the whole list with a paginator.
+ var paginatedParametersByPath = _amazonSSM.Paginators.GetParametersByPath(request);
+
+ await foreach (var parametersPage in paginatedParametersByPath.Responses)
+ {
+ parameters.AddRange(parametersPage.Parameters);
+ }
+
+ // Filter out everything except items that
+ // have "amzn2" in the name property.
+ var paramList =
+ from parameter in parameters
+ where parameter.Name.Contains("amzn2")
+ select parameter;
+ return paramList.ToList();
+ }
+}
+
+// snippet-end:[SSM.dotnetv4.SSMActionsClass]
\ No newline at end of file
diff --git a/dotnetv4/EC2/Actions/Usings.cs b/dotnetv4/EC2/Actions/Usings.cs
new file mode 100644
index 00000000000..3125ba51a46
--- /dev/null
+++ b/dotnetv4/EC2/Actions/Usings.cs
@@ -0,0 +1,9 @@
+// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
+// SPDX-License-Identifier: Apache-2.0
+
+global using System.Net;
+global using Amazon.EC2;
+global using Amazon.EC2.Model;
+global using Amazon.SimpleSystemsManagement;
+global using Amazon.SimpleSystemsManagement.Model;
+global using Microsoft.Extensions.DependencyInjection;
\ No newline at end of file
diff --git a/dotnetv4/EC2/EC2Examples.sln b/dotnetv4/EC2/EC2Examples.sln
new file mode 100644
index 00000000000..aee7d5c12ce
--- /dev/null
+++ b/dotnetv4/EC2/EC2Examples.sln
@@ -0,0 +1,52 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio Version 17
+VisualStudioVersion = 17.2.32630.192
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "EC2Actions", "Actions\EC2Actions.csproj", "{796910FA-6E94-460B-8CB4-97DF01B9ADC8}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Basics", "Scenarios\EC2_Basics\Basics.csproj", "{B1731AE1-381F-4044-BEBE-269FF7E24B1F}"
+ ProjectSection(ProjectDependencies) = postProject
+ {796910FA-6E94-460B-8CB4-97DF01B9ADC8} = {796910FA-6E94-460B-8CB4-97DF01B9ADC8}
+ EndProjectSection
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "EC2Tests", "Tests\EC2Tests.csproj", "{6046A2FC-6A39-4C2D-8DD9-AA3740B17B88}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CreateVPCExample", "Scenarios\CreateVPCExample\CreateVPCExample.csproj", "{A0B25DB2-E0BE-409F-950D-19E23FB76319}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CreateVPCforS3Example", "Scenarios\CreateVPCforS3Example\CreateVPCforS3Example.csproj", "{CB5A94D4-1BA1-41CE-B5AE-7CE906B4CA7D}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Release|Any CPU = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {796910FA-6E94-460B-8CB4-97DF01B9ADC8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {796910FA-6E94-460B-8CB4-97DF01B9ADC8}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {796910FA-6E94-460B-8CB4-97DF01B9ADC8}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {796910FA-6E94-460B-8CB4-97DF01B9ADC8}.Release|Any CPU.Build.0 = Release|Any CPU
+ {B1731AE1-381F-4044-BEBE-269FF7E24B1F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {B1731AE1-381F-4044-BEBE-269FF7E24B1F}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {B1731AE1-381F-4044-BEBE-269FF7E24B1F}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {B1731AE1-381F-4044-BEBE-269FF7E24B1F}.Release|Any CPU.Build.0 = Release|Any CPU
+ {6046A2FC-6A39-4C2D-8DD9-AA3740B17B88}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {6046A2FC-6A39-4C2D-8DD9-AA3740B17B88}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {6046A2FC-6A39-4C2D-8DD9-AA3740B17B88}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {6046A2FC-6A39-4C2D-8DD9-AA3740B17B88}.Release|Any CPU.Build.0 = Release|Any CPU
+ {A0B25DB2-E0BE-409F-950D-19E23FB76319}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {A0B25DB2-E0BE-409F-950D-19E23FB76319}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {A0B25DB2-E0BE-409F-950D-19E23FB76319}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {A0B25DB2-E0BE-409F-950D-19E23FB76319}.Release|Any CPU.Build.0 = Release|Any CPU
+ {CB5A94D4-1BA1-41CE-B5AE-7CE906B4CA7D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {CB5A94D4-1BA1-41CE-B5AE-7CE906B4CA7D}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {CB5A94D4-1BA1-41CE-B5AE-7CE906B4CA7D}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {CB5A94D4-1BA1-41CE-B5AE-7CE906B4CA7D}.Release|Any CPU.Build.0 = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+ GlobalSection(ExtensibilityGlobals) = postSolution
+ SolutionGuid = {870D888D-5C8B-4057-8722-F73ECF38E513}
+ EndGlobalSection
+EndGlobal
diff --git a/dotnetv4/EC2/README.md b/dotnetv4/EC2/README.md
new file mode 100644
index 00000000000..06f267e087e
--- /dev/null
+++ b/dotnetv4/EC2/README.md
@@ -0,0 +1,177 @@
+# Amazon EC2 code examples for the SDK for .NET
+
+## Overview
+
+Shows how to use the AWS SDK for .NET to work with Amazon Elastic Compute Cloud (Amazon EC2).
+
+
+
+
+_Amazon EC2 is a web service that provides resizable computing capacity—literally, servers in Amazon's data centers—that you use to build and host your software systems._
+
+## ⚠ Important
+
+* Running this code might result in charges to your AWS account. For more details, see [AWS Pricing](https://aws.amazon.com/pricing/) and [Free Tier](https://aws.amazon.com/free/).
+* Running the tests might result in charges to your AWS account.
+* We recommend that you grant your code least privilege. At most, grant only the minimum permissions required to perform the task. For more information, see [Grant least privilege](https://docs.aws.amazon.com/IAM/latest/UserGuide/best-practices.html#grant-least-privilege).
+* This code is not tested in every AWS Region. For more information, see [AWS Regional Services](https://aws.amazon.com/about-aws/global-infrastructure/regional-product-services).
+
+
+
+
+## Code examples
+
+### Prerequisites
+
+For prerequisites, see the [README](../README.md#Prerequisites) in the `dotnetv3` folder.
+
+
+
+
+
+### Get started
+
+- [Hello Amazon EC2](Actions/HelloEC2.cs#L4) (`DescribeSecurityGroups`)
+
+
+### Basics
+
+Code examples that show you how to perform the essential operations within a service.
+
+- [Learn the basics](Scenarios/EC2_Basics/EC2Basics.cs)
+
+
+### Single actions
+
+Code excerpts that show you how to call individual service functions.
+
+- [AllocateAddress](Actions/EC2Wrapper.cs#L28)
+- [AssociateAddress](Actions/EC2Wrapper.cs#L64)
+- [AuthorizeSecurityGroupIngress](Actions/EC2Wrapper.cs#L107)
+- [CreateKeyPair](Actions/EC2Wrapper.cs#L170)
+- [CreateLaunchTemplate](../cross-service/ResilientService/AutoScalerActions/AutoScalerWrapper.cs#L263)
+- [CreateSecurityGroup](Actions/EC2Wrapper.cs#L242)
+- [DeleteKeyPair](Actions/EC2Wrapper.cs#L319)
+- [DeleteLaunchTemplate](../cross-service/ResilientService/AutoScalerActions/AutoScalerWrapper.cs#L470)
+- [DeleteSecurityGroup](Actions/EC2Wrapper.cs#L361)
+- [DescribeAvailabilityZones](../cross-service/ResilientService/AutoScalerActions/AutoScalerWrapper.cs#L325)
+- [DescribeIamInstanceProfileAssociations](../cross-service/ResilientService/AutoScalerActions/AutoScalerWrapper.cs#L574)
+- [DescribeInstanceTypes](Actions/EC2Wrapper.cs#L531)
+- [DescribeInstances](Actions/EC2Wrapper.cs#L474)
+- [DescribeKeyPairs](Actions/EC2Wrapper.cs#L578)
+- [DescribeSecurityGroups](Actions/EC2Wrapper.cs#L620)
+- [DescribeSubnets](../cross-service/ResilientService/AutoScalerActions/AutoScalerWrapper.cs#L422)
+- [DescribeVpcs](../cross-service/ResilientService/AutoScalerActions/AutoScalerWrapper.cs#L386)
+- [DisassociateAddress](Actions/EC2Wrapper.cs#L714)
+- [RebootInstances](Actions/EC2Wrapper.cs#L761)
+- [ReleaseAddress](Actions/EC2Wrapper.cs#L802)
+- [ReplaceIamInstanceProfileAssociation](../cross-service/ResilientService/AutoScalerActions/AutoScalerWrapper.cs#L611)
+- [RunInstances](Actions/EC2Wrapper.cs#L837)
+- [StartInstances](Actions/EC2Wrapper.cs#L890)
+- [StopInstances](Actions/EC2Wrapper.cs#L930)
+- [TerminateInstances](Actions/EC2Wrapper.cs#L971)
+
+### Scenarios
+
+Code examples that show you how to accomplish a specific task by calling multiple
+functions within the same service.
+
+- [Build and manage a resilient service](../cross-service/ResilientService/ResilientServiceWorkflow/ResilientServiceWorkflow.cs)
+
+
+
+
+
+## Run the examples
+
+### Instructions
+
+For general instructions to run the examples, see the
+[README](../README.md#building-and-running-the-code-examples) in the `dotnetv3` folder.
+
+Some projects might include a settings.json file. Before compiling the project,
+you can change these values to match your own account and resources. Alternatively,
+add a settings.local.json file with your local settings, which will be loaded automatically
+when the application runs.
+
+After the example compiles, you can run it from the command line. To do so, navigate to
+the folder that contains the .csproj file and run the following command:
+
+```
+dotnet run
+```
+
+Alternatively, you can run the example from within your IDE.
+
+
+
+
+
+#### Hello Amazon EC2
+
+This example shows you how to get started using Amazon EC2.
+
+
+#### Learn the basics
+
+This example shows you how to do the following:
+
+- Create a key pair and security group.
+- Select an Amazon Machine Image (AMI) and compatible instance type, then create an instance.
+- Stop and restart the instance.
+- Associate an Elastic IP address with your instance.
+- Connect to your instance with SSH, then clean up resources.
+
+
+
+
+
+
+
+
+
+#### Build and manage a resilient service
+
+This example shows you how to create a load-balanced web service that returns book, movie, and song recommendations. The example shows how the service responds to failures, and how to restructure the service for more resilience when failures occur.
+
+- Use an Amazon EC2 Auto Scaling group to create Amazon Elastic Compute Cloud (Amazon EC2) instances based on a launch template and to keep the number of instances in a specified range.
+- Handle and distribute HTTP requests with Elastic Load Balancing.
+- Monitor the health of instances in an Auto Scaling group and forward requests only to healthy instances.
+- Run a Python web server on each EC2 instance to handle HTTP requests. The web server responds with recommendations and health checks.
+- Simulate a recommendation service with an Amazon DynamoDB table.
+- Control web server response to requests and health checks by updating AWS Systems Manager parameters.
+
+
+
+
+
+
+
+
+### Tests
+
+⚠ Running tests might result in charges to your AWS account.
+
+
+To find instructions for running these tests, see the [README](../README.md#Tests)
+in the `dotnetv3` folder.
+
+
+
+
+
+
+## Additional resources
+
+- [Amazon EC2 User Guide](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/concepts.html)
+- [Amazon EC2 API Reference](https://docs.aws.amazon.com/AWSEC2/latest/APIReference/Welcome.html)
+- [SDK for .NET Amazon EC2 reference](https://docs.aws.amazon.com/sdkfornet/v3/apidocs/items/EC2/NEC2.html)
+
+
+
+
+---
+
+Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
+
+SPDX-License-Identifier: Apache-2.0
\ No newline at end of file
diff --git a/dotnetv4/EC2/Scenarios/EC2_Basics/Basics.csproj b/dotnetv4/EC2/Scenarios/EC2_Basics/Basics.csproj
new file mode 100644
index 00000000000..e6364057c95
--- /dev/null
+++ b/dotnetv4/EC2/Scenarios/EC2_Basics/Basics.csproj
@@ -0,0 +1,21 @@
+
+
+
+ Exe
+ net8.0
+ enable
+ enable
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/dotnetv4/EC2/Scenarios/EC2_Basics/EC2Basics.cs b/dotnetv4/EC2/Scenarios/EC2_Basics/EC2Basics.cs
new file mode 100644
index 00000000000..f17d062d5af
--- /dev/null
+++ b/dotnetv4/EC2/Scenarios/EC2_Basics/EC2Basics.cs
@@ -0,0 +1,326 @@
+// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
+// SPDX-License-Identifier: Apache-2.0
+
+using Ec2_Basics;
+using Microsoft.Extensions.Hosting;
+using Microsoft.Extensions.Logging;
+
+namespace Basics;
+
+// snippet-start:[EC2.dotnetv4.Main]
+///
+/// Show Amazon Elastic Compute Cloud (Amazon EC2) Basics actions.
+///
+public class EC2Basics
+{
+ public static ILogger _logger = null!;
+ public static EC2Wrapper _ec2Wrapper = null!;
+ public static SsmWrapper _ssmWrapper = null!;
+ public static UiMethods _uiMethods = null!;
+
+ public static string associationId = null!;
+ public static string allocationId = null!;
+ public static string instanceId = null!;
+ public static string keyPairName = null!;
+ public static string groupName = null!;
+ public static string tempFileName = null!;
+ public static string secGroupId = null!;
+ public static bool isInteractive = true;
+
+ ///
+ /// Perform the actions defined for the Amazon EC2 Basics scenario.
+ ///
+ /// Command line arguments.
+ /// A Task object.
+ public static async Task Main(string[] args)
+ {
+ // Set up dependency injection for Amazon EC2 and Amazon Simple Systems
+ // Management (Amazon SSM) Service.
+ using var host = Microsoft.Extensions.Hosting.Host.CreateDefaultBuilder(args)
+ .ConfigureServices((_, services) =>
+ services.AddAWSService()
+ .AddAWSService()
+ .AddTransient()
+ .AddTransient()
+ )
+ .Build();
+
+ SetUpServices(host);
+
+ var uniqueName = Guid.NewGuid().ToString();
+ keyPairName = "mvp-example-key-pair" + uniqueName;
+ groupName = "ec2-scenario-group" + uniqueName;
+ var groupDescription = "A security group created for the EC2 Basics scenario.";
+
+ try
+ {
+ // Start the scenario.
+ _uiMethods.DisplayOverview();
+ _uiMethods.PressEnter(isInteractive);
+
+ // Create the key pair.
+ _uiMethods.DisplayTitle("Create RSA key pair");
+ Console.Write("Let's create an RSA key pair that you can be use to ");
+ Console.WriteLine("securely connect to your EC2 instance.");
+ var keyPair = await _ec2Wrapper.CreateKeyPair(keyPairName);
+
+ // Save key pair information to a temporary file.
+ tempFileName = _ec2Wrapper.SaveKeyPair(keyPair);
+
+ Console.WriteLine(
+ $"Created the key pair: {keyPair.KeyName} and saved it to: {tempFileName}");
+ string? answer = "";
+ if (isInteractive)
+ {
+ do
+ {
+ Console.Write("Would you like to list your existing key pairs? ");
+ answer = Console.ReadLine();
+ } while (answer!.ToLower() != "y" && answer.ToLower() != "n");
+ }
+
+ if (!isInteractive || answer == "y")
+ {
+ // List existing key pairs.
+ _uiMethods.DisplayTitle("Existing key pairs");
+
+ // Passing an empty string to the DescribeKeyPairs method will return
+ // a list of all existing key pairs.
+ var keyPairs = await _ec2Wrapper.DescribeKeyPairs("");
+ keyPairs.ForEach(kp =>
+ {
+ Console.WriteLine(
+ $"{kp.KeyName} created at: {kp.CreateTime} Fingerprint: {kp.KeyFingerprint}");
+ });
+ }
+
+ _uiMethods.PressEnter(isInteractive);
+
+ // Create the security group.
+ Console.WriteLine(
+ "Let's create a security group to manage access to your instance.");
+ secGroupId = await _ec2Wrapper.CreateSecurityGroup(groupName, groupDescription);
+ Console.WriteLine(
+ "Let's add rules to allow all HTTP and HTTPS inbound traffic and to allow SSH only from your current IP address.");
+
+ _uiMethods.DisplayTitle("Security group information");
+ var secGroups = await _ec2Wrapper.DescribeSecurityGroups(secGroupId);
+
+ Console.WriteLine($"Created security group {groupName} in your default VPC.");
+ secGroups.ForEach(group =>
+ {
+ _ec2Wrapper.DisplaySecurityGroupInfoAsync(group);
+ });
+ _uiMethods.PressEnter(isInteractive);
+
+ Console.WriteLine(
+ "Now we'll authorize the security group we just created so that it can");
+ Console.WriteLine("access the EC2 instances you create.");
+ await _ec2Wrapper.AuthorizeSecurityGroupIngress(groupName);
+
+ secGroups = await _ec2Wrapper.DescribeSecurityGroups(secGroupId);
+ Console.WriteLine($"Now let's look at the permissions again.");
+ secGroups.ForEach(group =>
+ {
+ _ec2Wrapper.DisplaySecurityGroupInfoAsync(group);
+ });
+ _uiMethods.PressEnter(isInteractive);
+
+ // Get list of available Amazon Linux 2 Amazon Machine Images (AMIs).
+ var parameters =
+ await _ssmWrapper.GetParametersByPath(
+ "/aws/service/ami-amazon-linux-latest");
+
+ List imageIds = parameters.Select(param => param.Value).ToList();
+
+ var images = await _ec2Wrapper.DescribeImages(imageIds);
+
+ var i = 1;
+ images.ForEach(image =>
+ {
+ Console.WriteLine($"\t{i++}\t{image.Description}");
+ });
+
+ int choice = 1;
+ bool validNumber = false;
+ if (isInteractive)
+ {
+ do
+ {
+ Console.Write("Please select an image: ");
+ var selImage = Console.ReadLine();
+ validNumber = int.TryParse(selImage, out choice);
+ } while (!validNumber);
+ }
+
+ var selectedImage = images[choice - 1];
+
+ // Display available instance types.
+ _uiMethods.DisplayTitle("Instance Types");
+ var instanceTypes =
+ await _ec2Wrapper.DescribeInstanceTypes(selectedImage.Architecture);
+
+ i = 1;
+ instanceTypes.ForEach(instanceType =>
+ {
+ Console.WriteLine($"\t{i++}\t{instanceType.InstanceType}");
+ });
+ if (isInteractive)
+ {
+ do
+ {
+ Console.Write("Please select an instance type: ");
+ var selImage = Console.ReadLine();
+ validNumber = int.TryParse(selImage, out choice);
+ } while (!validNumber);
+ }
+
+ var selectedInstanceType = instanceTypes[choice - 1].InstanceType;
+
+ // Create an EC2 instance.
+ _uiMethods.DisplayTitle("Creating an EC2 Instance");
+ instanceId = await _ec2Wrapper.RunInstances(selectedImage.ImageId,
+ selectedInstanceType, keyPairName, secGroupId);
+
+ _uiMethods.PressEnter(isInteractive);
+
+ var instance = await _ec2Wrapper.DescribeInstance(instanceId);
+ _uiMethods.DisplayTitle("New Instance Information");
+ _ec2Wrapper.DisplayInstanceInformation(instance);
+
+ Console.WriteLine(
+ "\nYou can use SSH to connect to your instance. For example:");
+ Console.WriteLine(
+ $"\tssh -i {tempFileName} ec2-user@{instance.PublicIpAddress}");
+
+ _uiMethods.PressEnter(isInteractive);
+
+ Console.WriteLine(
+ "Now we'll stop the instance and then start it again to see what's changed.");
+
+ await _ec2Wrapper.StopInstances(instanceId);
+
+ Console.WriteLine("Now let's start it up again.");
+ await _ec2Wrapper.StartInstances(instanceId);
+
+ Console.WriteLine("\nLet's see what changed.");
+
+ instance = await _ec2Wrapper.DescribeInstance(instanceId);
+ _uiMethods.DisplayTitle("New Instance Information");
+ _ec2Wrapper.DisplayInstanceInformation(instance);
+
+ Console.WriteLine("\nNotice the change in the SSH information:");
+ Console.WriteLine(
+ $"\tssh -i {tempFileName} ec2-user@{instance.PublicIpAddress}");
+
+ _uiMethods.PressEnter(isInteractive);
+
+ Console.WriteLine(
+ "Now we will stop the instance again. Then we will create and associate an");
+ Console.WriteLine("Elastic IP address to use with our instance.");
+
+ await _ec2Wrapper.StopInstances(instanceId);
+ _uiMethods.PressEnter(isInteractive);
+
+ _uiMethods.DisplayTitle("Allocate Elastic IP address");
+ Console.WriteLine(
+ "You can allocate an Elastic IP address and associate it with your instance\nto keep a consistent IP address even when your instance restarts.");
+ var allocationResponse = await _ec2Wrapper.AllocateAddress();
+ allocationId = allocationResponse.AllocationId;
+ Console.WriteLine(
+ "Now we will associate the Elastic IP address with our instance.");
+ associationId = await _ec2Wrapper.AssociateAddress(allocationId, instanceId);
+
+ // Start the instance again.
+ Console.WriteLine("Now let's start the instance again.");
+ await _ec2Wrapper.StartInstances(instanceId);
+
+ Console.WriteLine("\nLet's see what changed.");
+
+ instance = await _ec2Wrapper.DescribeInstance(instanceId);
+ _uiMethods.DisplayTitle("Instance information");
+ _ec2Wrapper.DisplayInstanceInformation(instance);
+
+ Console.WriteLine("\nHere is the SSH information:");
+ Console.WriteLine(
+ $"\tssh -i {tempFileName} ec2-user@{instance.PublicIpAddress}");
+
+ Console.WriteLine("Let's stop and start the instance again.");
+ _uiMethods.PressEnter(isInteractive);
+
+ await _ec2Wrapper.StopInstances(instanceId);
+
+ Console.WriteLine("\nThe instance has stopped.");
+
+ Console.WriteLine("Now let's start it up again.");
+ await _ec2Wrapper.StartInstances(instanceId);
+
+ instance = await _ec2Wrapper.DescribeInstance(instanceId);
+ _uiMethods.DisplayTitle("New Instance Information");
+ _ec2Wrapper.DisplayInstanceInformation(instance);
+ Console.WriteLine("Note that the IP address did not change this time.");
+ _uiMethods.PressEnter(isInteractive);
+
+ await Cleanup();
+ }
+ catch (Exception ex)
+ {
+ _logger.LogError(ex, "There was a problem with the scenario, starting cleanup.");
+ await Cleanup();
+ }
+
+ _uiMethods.DisplayTitle("EC2 Basics Scenario completed.");
+ _uiMethods.PressEnter(isInteractive);
+ }
+
+ ///
+ /// Set up the services and logging.
+ ///
+ ///
+ public static void SetUpServices(IHost host)
+ {
+ var loggerFactory = LoggerFactory.Create(builder =>
+ {
+ builder.AddConsole();
+ });
+ _logger = new Logger(loggerFactory);
+
+ // Now the client is available for injection.
+ _ec2Wrapper = host.Services.GetRequiredService();
+ _ssmWrapper = host.Services.GetRequiredService();
+ _uiMethods = new UiMethods();
+ }
+
+ ///
+ /// Clean up any resources from the scenario.
+ ///
+ ///
+ public static async Task Cleanup()
+ {
+ _uiMethods.DisplayTitle("Clean up resources");
+ Console.WriteLine("Now let's clean up the resources we created.");
+
+ Console.WriteLine("Disassociate the Elastic IP address and release it.");
+ // Disassociate the Elastic IP address.
+ await _ec2Wrapper.DisassociateIp(associationId);
+
+ // Delete the Elastic IP address.
+ await _ec2Wrapper.ReleaseAddress(allocationId);
+
+ // Terminate the instance.
+ Console.WriteLine("Terminating the instance we created.");
+ await _ec2Wrapper.TerminateInstances(instanceId);
+
+ // Delete the security group.
+ Console.WriteLine($"Deleting the Security Group: {groupName}.");
+ await _ec2Wrapper.DeleteSecurityGroup(secGroupId);
+
+ // Delete the RSA key pair.
+ Console.WriteLine($"Deleting the key pair: {keyPairName}");
+ await _ec2Wrapper.DeleteKeyPair(keyPairName);
+ Console.WriteLine("Deleting the temporary file with the key information.");
+ _ec2Wrapper.DeleteTempFile(tempFileName);
+ _uiMethods.PressEnter(isInteractive);
+ }
+}
+// snippet-end:[EC2.dotnetv4.Main]
\ No newline at end of file
diff --git a/dotnetv4/EC2/Scenarios/EC2_Basics/UIMethods.cs b/dotnetv4/EC2/Scenarios/EC2_Basics/UIMethods.cs
new file mode 100644
index 00000000000..2c3d7f7f882
--- /dev/null
+++ b/dotnetv4/EC2/Scenarios/EC2_Basics/UIMethods.cs
@@ -0,0 +1,59 @@
+// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
+// SPDX-License-Identifier: Apache-2.0
+
+namespace Ec2_Basics;
+
+public class UiMethods
+{
+ public readonly string SepBar = new string('-', 88);
+
+ ///
+ /// Show information about the scenario.
+ ///
+ public void DisplayOverview()
+ {
+ DisplayTitle("Welcome to the Amazon Elastic Compute Cloud (Amazon EC2) get started with instances demo.");
+
+ Console.WriteLine("This example application does the following:");
+ Console.WriteLine("\t 1. Creates an RSA key pair.");
+ Console.WriteLine("\t 2. Saves the key pair to a file in a temporary folder.");
+ Console.WriteLine("\t 3. Creates a security group with an inbound rule allowing this computer to SSH to the security group.");
+ Console.WriteLine("\t 4. Displays information for the security group.");
+ Console.WriteLine("\t 5. Gets a list of Amazon Linux 2 Amazon Machine Images (AMIs)\n\t\tand selects one.");
+ Console.WriteLine("\t 6. Gets a list of instance types and selects one.");
+ Console.WriteLine("\t 8. Creates an EC2 instance.");
+ Console.WriteLine("\t 9. Waits for the instance to be ready and displays its information.");
+ Console.WriteLine("\t10. Displays the SSH connection information.");
+ Console.WriteLine("\t11. Stops the instance.");
+ Console.WriteLine("\t12. Starts the instance again.");
+ Console.WriteLine("\t13. Displays the SSH connection information again to show that it has changed.");
+ Console.WriteLine("\t14. Allocates an Elastic IP and associates it to the instance.");
+ Console.WriteLine("\t15. Displays the SSH connection information for the instance.");
+ Console.WriteLine("\t16. Disassociates the Elastic IP and deletes it.");
+ Console.WriteLine("\t17. Terminates the instance and waits for termination to be complete.");
+ Console.WriteLine("\t18. Deletes the security group.");
+ Console.WriteLine("\t19. Deletes the key pair.");
+ }
+
+ ///
+ /// Display a message and wait until the user presses enter.
+ ///
+ public void PressEnter(bool interactive)
+ {
+ Console.Write("\nPlease press to continue. ");
+ if (interactive)
+ _ = Console.ReadLine();
+ }
+
+ ///
+ /// Display a line of hyphens, the text of the title and another
+ /// line of hyphens.
+ ///
+ /// The string to be displayed.
+ public void DisplayTitle(string strTitle)
+ {
+ Console.WriteLine(SepBar);
+ Console.WriteLine(strTitle);
+ Console.WriteLine(SepBar);
+ }
+}
\ No newline at end of file
diff --git a/dotnetv4/EC2/Scenarios/EC2_Basics/Usings.cs b/dotnetv4/EC2/Scenarios/EC2_Basics/Usings.cs
new file mode 100644
index 00000000000..900533b5c09
--- /dev/null
+++ b/dotnetv4/EC2/Scenarios/EC2_Basics/Usings.cs
@@ -0,0 +1,7 @@
+// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
+// SPDX-License-Identifier: Apache-2.0
+
+global using Amazon.EC2;
+global using Amazon.SimpleSystemsManagement;
+global using EC2Actions;
+global using Microsoft.Extensions.DependencyInjection;
\ No newline at end of file
diff --git a/dotnetv4/EC2/Tests/EC2Tests.csproj b/dotnetv4/EC2/Tests/EC2Tests.csproj
new file mode 100644
index 00000000000..0f1dcab08e3
--- /dev/null
+++ b/dotnetv4/EC2/Tests/EC2Tests.csproj
@@ -0,0 +1,43 @@
+
+
+
+ net8.0
+ enable
+ enable
+
+ false
+
+
+
+
+
+
+
+
+
+
+ runtime; build; native; contentfiles; analyzers; buildtransitive
+ all
+
+
+ runtime; build; native; contentfiles; analyzers; buildtransitive
+ all
+
+
+
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+ testsettings.json
+
+
+
+
+
+
+
+
+
diff --git a/dotnetv4/EC2/Tests/EC2WrapperTests.cs b/dotnetv4/EC2/Tests/EC2WrapperTests.cs
new file mode 100644
index 00000000000..f915cebc8c8
--- /dev/null
+++ b/dotnetv4/EC2/Tests/EC2WrapperTests.cs
@@ -0,0 +1,79 @@
+// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
+// SPDX-License-Identifier: Apache-2.0
+
+using Basics;
+using Microsoft.Extensions.Logging;
+using Moq;
+
+namespace EC2Tests;
+///
+/// Integration tests for the Amazon Elastic Compute Cloud (Amazon EC2)
+/// Basics scenario.
+///
+public class EC2WrapperTests
+{
+ private AmazonEC2Client _client = null!;
+ private EC2Wrapper _ec2Wrapper = null!;
+ private SsmWrapper _ssmWrapper = null!;
+
+ ///
+ /// Verifies the scenario with an integration test. No errors should be logged.
+ ///
+ /// Async task.
+ [Fact]
+ [Trait("Category", "Integration")]
+ public async Task TestScenario()
+ {
+ // Arrange.
+ EC2Basics.isInteractive = false;
+ var loggerScenarioMock = new Mock>();
+ var loggerWrapperMock = new Mock>();
+
+ _client = new AmazonEC2Client();
+
+ _ec2Wrapper = new EC2Wrapper(_client, loggerWrapperMock.Object);
+ _ssmWrapper = new SsmWrapper(new AmazonSimpleSystemsManagementClient());
+
+ EC2Basics._ec2Wrapper = _ec2Wrapper;
+ EC2Basics._ssmWrapper = _ssmWrapper;
+ EC2Basics._logger = loggerScenarioMock.Object;
+
+ loggerScenarioMock.Setup(logger => logger.Log(
+ It.Is(logLevel => logLevel == LogLevel.Error),
+ It.IsAny(),
+ It.Is((@object, @type) => true),
+ It.IsAny(),
+ It.IsAny>()
+ ));
+
+ loggerWrapperMock.Setup(logger => logger.Log(
+ It.Is(logLevel => logLevel == LogLevel.Error),
+ It.IsAny(),
+ It.Is((@object, @type) => true),
+ It.IsAny(),
+ It.IsAny>()
+ ));
+
+ // Act.
+ await EC2Basics.Main(new string[] { "" });
+
+ // Assert no exceptions or errors logged.
+ loggerScenarioMock.Verify(
+ logger => logger.Log(
+ It.Is(logLevel => logLevel == LogLevel.Error),
+ It.IsAny(),
+ It.Is((@object, @type) => true),
+ It.IsAny(),
+ It.IsAny>()),
+ Times.Never);
+
+ loggerWrapperMock.Verify(
+ logger => logger.Log(
+ It.Is(logLevel => logLevel == LogLevel.Error),
+ It.IsAny(),
+ It.Is((@object, @type) => true),
+ It.IsAny(),
+ It.IsAny>()),
+ Times.Never);
+ }
+}
\ No newline at end of file
diff --git a/dotnetv4/EC2/Tests/Usings.cs b/dotnetv4/EC2/Tests/Usings.cs
new file mode 100644
index 00000000000..f71c1b687e9
--- /dev/null
+++ b/dotnetv4/EC2/Tests/Usings.cs
@@ -0,0 +1,11 @@
+// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
+// SPDX-License-Identifier: Apache-2.0
+
+global using Amazon.EC2;
+global using Amazon.SimpleSystemsManagement;
+global using EC2Actions;
+global using Microsoft.Extensions.Configuration;
+global using Xunit;
+
+// Optional.
+[assembly: CollectionBehavior(DisableTestParallelization = true)]
\ No newline at end of file
diff --git a/dotnetv4/EC2/Tests/testsettings.json b/dotnetv4/EC2/Tests/testsettings.json
new file mode 100644
index 00000000000..ffeb7fbf1cd
--- /dev/null
+++ b/dotnetv4/EC2/Tests/testsettings.json
@@ -0,0 +1,12 @@
+{
+ "GroupDescription": "This is a group created for integration tests.",
+ "GroupName": "test-group-name",
+ "ImageId": "ami-02dd04850de58599e",
+ "InstanceType": "t2.micro",
+ "KeyPairName": "test-example-key-pair",
+ "VpcId": "vpc-1a2b3c4d",
+ "BucketName": "",
+ "SubnetId": "subnet-012345678912345606",
+ "SecurityGroupId": "sg-012345678912345606",
+ "WaitTimeMilliseconds": 300000
+}
From f3d18fcb5395fd1d1b17c80e1cd12822f014bbca Mon Sep 17 00:00:00 2001
From: Rachel Hagerman <110480692+rlhagerm@users.noreply.github.com>
Date: Fri, 24 Jan 2025 11:34:32 -0600
Subject: [PATCH 2/5] Updates for EC2.
---
dotnetv4/EC2/Actions/EC2Wrapper.cs | 198 +++--------------------------
dotnetv4/EC2/Actions/HelloEC2.cs | 2 +-
dotnetv4/EC2/EC2Examples.sln | 12 --
dotnetv4/EC2/README.md | 39 +-----
dotnetv4/EC2/Tests/Usings.cs | 1 -
5 files changed, 20 insertions(+), 232 deletions(-)
diff --git a/dotnetv4/EC2/Actions/EC2Wrapper.cs b/dotnetv4/EC2/Actions/EC2Wrapper.cs
index 691ae97b6be..f96202407ce 100644
--- a/dotnetv4/EC2/Actions/EC2Wrapper.cs
+++ b/dotnetv4/EC2/Actions/EC2Wrapper.cs
@@ -288,34 +288,6 @@ public async Task CreateSecurityGroup(string groupName, string groupDesc
// snippet-end:[EC2.dotnetv4.CreateSecurityGroup]
- // snippet-start:[EC2.dotnetv4.CreateVPC]
- ///
- /// Create a new Amazon EC2 VPC.
- ///
- /// The CIDR block for the new security group.
- /// The VPC Id of the new VPC.
- public async Task 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;
- }
- }
- // snippet-end:[EC2.dotnetv4.CreateVPC]
-
// snippet-start:[EC2.dotnetv4.DeleteKeyPair]
///
/// Delete an Amazon EC2 key pair.
@@ -391,24 +363,6 @@ await _amazonEC2.DeleteSecurityGroupAsync(
}
// snippet-end:[EC2.dotnetv4.DeleteSecurityGroup]
- // snippet-start:[EC2.dotnetv4.DeleteVPC]
- ///
- /// Delete an Amazon EC2 VPC.
- ///
- /// A Boolean value indicating the success of the action.
- public async Task DeleteVpc(string vpcId)
- {
- var request = new DeleteVpcRequest
- {
- VpcId = vpcId,
- };
-
- var response = await _amazonEC2.DeleteVpcAsync(request);
-
- return response.HttpStatusCode == System.Net.HttpStatusCode.OK;
- }
- // snippet-end:[EC2.dotnetv4.DeleteVPC]
-
// snippet-start:[EC2.dotnetv4.DescribeImages]
///
/// Get information about existing Amazon EC2 images.
@@ -428,20 +382,6 @@ public async Task> DescribeImages(List? imageIds)
return response.Images;
}
- ///
- /// Display the information returned by DescribeImages.
- ///
- /// The list of image information to display.
- public void DisplayImageInfo(List images)
- {
- images.ForEach(image =>
- {
- Console.WriteLine($"{image.Name} Created on: {image.CreationDate}");
- });
-
- }
- // snippet-end:[EC2.dotnetv4.DescribeImages]
-
// snippet-start:[EC2.dotnetv4.DescribeInstance]
///
/// Get information about an Amazon EC2 instance.
@@ -471,62 +411,6 @@ public void DisplayInstanceInformation(Instance instance)
}
// snippet-end:[EC2.dotnetv4.DescribeInstance]
- // snippet-start:[EC2.dotnetv4.DescribeInstances]
- ///
- /// Get information about EC2 instances with a particular state.
- ///
- /// The name of the tag to filter on.
- /// The value of the tag to look for.
- /// True if successful.
- public async Task GetInstancesWithState(string state)
- {
- try
- {
- // Filters the results of the instance list.
- var filters = new List
- {
- new Filter
- {
- Name = $"instance-state-name",
- Values = new List { state, },
- },
- };
- var request = new DescribeInstancesRequest { Filters = filters, };
-
- Console.WriteLine($"\nShowing instances with state {state}");
- var paginator = _amazonEC2.Paginators.DescribeInstances(request);
-
- await foreach (var response in paginator.Responses)
- {
- foreach (var reservation in response.Reservations)
- {
- foreach (var instance in reservation.Instances)
- {
- Console.Write($"Instance ID: {instance.InstanceId} ");
- Console.WriteLine($"\tCurrent State: {instance.State.Name}");
- }
- }
- }
-
- return true;
- }
- catch (AmazonEC2Exception ec2Exception)
- {
- if (ec2Exception.ErrorCode == "InvalidParameterValue")
- {
- _logger.LogError(
- $"Invalid parameter value for filtering instances.");
- }
-
- return false;
- }
- catch (Exception ex)
- {
- Console.WriteLine($"Couldn't list instances because: {ex.Message}");
- return false;
- }
- }
- // snippet-end:[EC2.dotnetv4.DescribeInstances]
// snippet-start:[EC2.dotnetv4.DescribeInstanceTypes]
///
@@ -585,6 +469,7 @@ public async Task> DescribeKeyPairs(string keyPairName)
{
try
{
+ var pairs = new List();
var request = new DescribeKeyPairsRequest();
if (!string.IsNullOrEmpty(keyPairName))
{
@@ -595,7 +480,12 @@ public async Task> DescribeKeyPairs(string keyPairName)
}
var response = await _amazonEC2.DescribeKeyPairsAsync(request);
- return response.KeyPairs.ToList();
+ if (response.KeyPairs != null)
+ {
+ pairs = response.KeyPairs.ToList();
+ }
+ return pairs;
+
}
catch (AmazonEC2Exception ec2Exception)
{
@@ -674,36 +564,36 @@ public void DisplaySecurityGroupInfoAsync(SecurityGroup securityGroup)
{
Console.WriteLine($"{securityGroup.GroupName}");
Console.WriteLine("Ingress permissions:");
- securityGroup.IpPermissions.ForEach(permission =>
+ 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} "); });
+ permission.Ipv4Ranges?.ForEach(range => { Console.Write($"{range.CidrIp} "); });
Console.WriteLine($"\n\tIpv6Ranges:");
- permission.Ipv6Ranges.ForEach(range => { Console.Write($"{range.CidrIpv6} "); });
+ permission.Ipv6Ranges?.ForEach(range => { Console.Write($"{range.CidrIpv6} "); });
Console.Write($"\n\tPrefixListIds: ");
- permission.PrefixListIds.ForEach(id => Console.Write($"{id.Id} "));
+ permission.PrefixListIds?.ForEach(id => Console.Write($"{id.Id} "));
Console.WriteLine($"\n\tTo Port: {permission.ToPort}");
});
Console.WriteLine("Egress permissions:");
- securityGroup.IpPermissionsEgress.ForEach(permission =>
+ 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} "); });
+ permission.Ipv4Ranges?.ForEach(range => { Console.Write($"{range.CidrIp} "); });
Console.WriteLine($"\n\tIpv6Ranges:");
- permission.Ipv6Ranges.ForEach(range => { Console.Write($"{range.CidrIpv6} "); });
+ permission.Ipv6Ranges?.ForEach(range => { Console.Write($"{range.CidrIpv6} "); });
Console.Write($"\n\tPrefixListIds: ");
- permission.PrefixListIds.ForEach(id => Console.Write($"{id.Id} "));
+ permission.PrefixListIds?.ForEach(id => Console.Write($"{id.Id} "));
Console.WriteLine($"\n\tTo Port: {permission.ToPort}");
});
@@ -744,60 +634,6 @@ public async Task DisassociateIp(string associationId)
}
// snippet-end:[EC2.dotnetv4.DisassociateAddress]
- // snippet-start:[EC2.dotnetv4.GetAMIList]
- ///
- /// Retrieve a list of available Amazon Linux images.
- ///
- /// A list of image information.
- public async Task> GetEC2AmiList()
- {
- var filter = new Filter { Name = "architecture", Values = new List { "x86_64" } };
- var filters = new List { filter };
- var response = await _amazonEC2.DescribeImagesAsync(new DescribeImagesRequest { Filters = filters });
- return response.Images;
- }
- // snippet-end:[EC2.dotnetv4.GetAMIList]
-
- // snippet-start:[EC2.dotnetv4.RebootInstances]
- ///
- /// Reboot a specific EC2 instance.
- ///
- /// The instance Id of the instance that will be rebooted.
- /// Async Task.
- public async Task RebootInstances(string ec2InstanceId)
- {
- try
- {
- var request = new RebootInstancesRequest
- {
- InstanceIds = new List { ec2InstanceId },
- };
-
- await _amazonEC2.RebootInstancesAsync(request);
-
- // Wait for the instance to be running.
- Console.Write("Waiting for the instance to start.");
- await WaitForInstanceState(ec2InstanceId, InstanceStateName.Running);
-
- return true;
- }
- catch (AmazonEC2Exception ec2Exception)
- {
- if (ec2Exception.ErrorCode == "InvalidInstanceId")
- {
- _logger.LogError(
- $"InstanceId {ec2InstanceId} is invalid, unable to reboot. {ec2Exception.Message}");
- }
- return false;
- }
- catch (Exception ex)
- {
- _logger.LogError(
- $"An error occurred while rebooting the instance {ec2InstanceId}.: {ex.Message}");
- return false;
- }
- }
- // snippet-end:[EC2.dotnetv4.RebootInstances]
// snippet-start:[EC2.dotnetv4.ReleaseAddress]
///
@@ -812,8 +648,8 @@ public async Task ReleaseAddress(string allocationId)
{
var request = new ReleaseAddressRequest { AllocationId = allocationId };
- var response = await _amazonEC2.ReleaseAddressAsync(request);
- return response.HttpStatusCode == HttpStatusCode.OK;
+ await _amazonEC2.ReleaseAddressAsync(request);
+ return true;
}
catch (AmazonEC2Exception ec2Exception)
{
diff --git a/dotnetv4/EC2/Actions/HelloEC2.cs b/dotnetv4/EC2/Actions/HelloEC2.cs
index d6ae2731449..39ef7fc12ac 100644
--- a/dotnetv4/EC2/Actions/HelloEC2.cs
+++ b/dotnetv4/EC2/Actions/HelloEC2.cs
@@ -28,7 +28,7 @@ static async Task Main(string[] args)
try
{
// Retrieve information for up to 10 Amazon EC2 security groups.
- var request = new DescribeSecurityGroupsRequest { MaxResults = 10, };
+ var request = new DescribeSecurityGroupsRequest { MaxResults = 10 };
var securityGroups = new List();
var paginatorForSecurityGroups =
diff --git a/dotnetv4/EC2/EC2Examples.sln b/dotnetv4/EC2/EC2Examples.sln
index aee7d5c12ce..c089758acd4 100644
--- a/dotnetv4/EC2/EC2Examples.sln
+++ b/dotnetv4/EC2/EC2Examples.sln
@@ -12,10 +12,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Basics", "Scenarios\EC2_Bas
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "EC2Tests", "Tests\EC2Tests.csproj", "{6046A2FC-6A39-4C2D-8DD9-AA3740B17B88}"
EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CreateVPCExample", "Scenarios\CreateVPCExample\CreateVPCExample.csproj", "{A0B25DB2-E0BE-409F-950D-19E23FB76319}"
-EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CreateVPCforS3Example", "Scenarios\CreateVPCforS3Example\CreateVPCforS3Example.csproj", "{CB5A94D4-1BA1-41CE-B5AE-7CE906B4CA7D}"
-EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -34,14 +30,6 @@ Global
{6046A2FC-6A39-4C2D-8DD9-AA3740B17B88}.Debug|Any CPU.Build.0 = Debug|Any CPU
{6046A2FC-6A39-4C2D-8DD9-AA3740B17B88}.Release|Any CPU.ActiveCfg = Release|Any CPU
{6046A2FC-6A39-4C2D-8DD9-AA3740B17B88}.Release|Any CPU.Build.0 = Release|Any CPU
- {A0B25DB2-E0BE-409F-950D-19E23FB76319}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {A0B25DB2-E0BE-409F-950D-19E23FB76319}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {A0B25DB2-E0BE-409F-950D-19E23FB76319}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {A0B25DB2-E0BE-409F-950D-19E23FB76319}.Release|Any CPU.Build.0 = Release|Any CPU
- {CB5A94D4-1BA1-41CE-B5AE-7CE906B4CA7D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {CB5A94D4-1BA1-41CE-B5AE-7CE906B4CA7D}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {CB5A94D4-1BA1-41CE-B5AE-7CE906B4CA7D}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {CB5A94D4-1BA1-41CE-B5AE-7CE906B4CA7D}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
diff --git a/dotnetv4/EC2/README.md b/dotnetv4/EC2/README.md
index 06f267e087e..45de83bb0bd 100644
--- a/dotnetv4/EC2/README.md
+++ b/dotnetv4/EC2/README.md
@@ -49,36 +49,20 @@ Code excerpts that show you how to call individual service functions.
- [AssociateAddress](Actions/EC2Wrapper.cs#L64)
- [AuthorizeSecurityGroupIngress](Actions/EC2Wrapper.cs#L107)
- [CreateKeyPair](Actions/EC2Wrapper.cs#L170)
-- [CreateLaunchTemplate](../cross-service/ResilientService/AutoScalerActions/AutoScalerWrapper.cs#L263)
- [CreateSecurityGroup](Actions/EC2Wrapper.cs#L242)
- [DeleteKeyPair](Actions/EC2Wrapper.cs#L319)
-- [DeleteLaunchTemplate](../cross-service/ResilientService/AutoScalerActions/AutoScalerWrapper.cs#L470)
- [DeleteSecurityGroup](Actions/EC2Wrapper.cs#L361)
-- [DescribeAvailabilityZones](../cross-service/ResilientService/AutoScalerActions/AutoScalerWrapper.cs#L325)
-- [DescribeIamInstanceProfileAssociations](../cross-service/ResilientService/AutoScalerActions/AutoScalerWrapper.cs#L574)
- [DescribeInstanceTypes](Actions/EC2Wrapper.cs#L531)
- [DescribeInstances](Actions/EC2Wrapper.cs#L474)
- [DescribeKeyPairs](Actions/EC2Wrapper.cs#L578)
- [DescribeSecurityGroups](Actions/EC2Wrapper.cs#L620)
-- [DescribeSubnets](../cross-service/ResilientService/AutoScalerActions/AutoScalerWrapper.cs#L422)
-- [DescribeVpcs](../cross-service/ResilientService/AutoScalerActions/AutoScalerWrapper.cs#L386)
- [DisassociateAddress](Actions/EC2Wrapper.cs#L714)
-- [RebootInstances](Actions/EC2Wrapper.cs#L761)
- [ReleaseAddress](Actions/EC2Wrapper.cs#L802)
-- [ReplaceIamInstanceProfileAssociation](../cross-service/ResilientService/AutoScalerActions/AutoScalerWrapper.cs#L611)
- [RunInstances](Actions/EC2Wrapper.cs#L837)
- [StartInstances](Actions/EC2Wrapper.cs#L890)
- [StopInstances](Actions/EC2Wrapper.cs#L930)
- [TerminateInstances](Actions/EC2Wrapper.cs#L971)
-### Scenarios
-
-Code examples that show you how to accomplish a specific task by calling multiple
-functions within the same service.
-
-- [Build and manage a resilient service](../cross-service/ResilientService/ResilientServiceWorkflow/ResilientServiceWorkflow.cs)
-
-
@@ -87,7 +71,7 @@ functions within the same service.
### Instructions
For general instructions to run the examples, see the
-[README](../README.md#building-and-running-the-code-examples) in the `dotnetv3` folder.
+[README](../README.md#building-and-running-the-code-examples) in the `dotnetv4` folder.
Some projects might include a settings.json file. Before compiling the project,
you can change these values to match your own account and resources. Alternatively,
@@ -129,32 +113,13 @@ This example shows you how to do the following:
-
-#### Build and manage a resilient service
-
-This example shows you how to create a load-balanced web service that returns book, movie, and song recommendations. The example shows how the service responds to failures, and how to restructure the service for more resilience when failures occur.
-
-- Use an Amazon EC2 Auto Scaling group to create Amazon Elastic Compute Cloud (Amazon EC2) instances based on a launch template and to keep the number of instances in a specified range.
-- Handle and distribute HTTP requests with Elastic Load Balancing.
-- Monitor the health of instances in an Auto Scaling group and forward requests only to healthy instances.
-- Run a Python web server on each EC2 instance to handle HTTP requests. The web server responds with recommendations and health checks.
-- Simulate a recommendation service with an Amazon DynamoDB table.
-- Control web server response to requests and health checks by updating AWS Systems Manager parameters.
-
-
-
-
-
-
-
-
### Tests
⚠ Running tests might result in charges to your AWS account.
To find instructions for running these tests, see the [README](../README.md#Tests)
-in the `dotnetv3` folder.
+in the `dotnetv4` folder.
diff --git a/dotnetv4/EC2/Tests/Usings.cs b/dotnetv4/EC2/Tests/Usings.cs
index f71c1b687e9..ca8cbead821 100644
--- a/dotnetv4/EC2/Tests/Usings.cs
+++ b/dotnetv4/EC2/Tests/Usings.cs
@@ -4,7 +4,6 @@
global using Amazon.EC2;
global using Amazon.SimpleSystemsManagement;
global using EC2Actions;
-global using Microsoft.Extensions.Configuration;
global using Xunit;
// Optional.
From d6ed4a643208c6ec1737245b8dffde34930253c5 Mon Sep 17 00:00:00 2001
From: Rachel Hagerman <110480692+rlhagerm@users.noreply.github.com>
Date: Mon, 2 Dec 2024 09:59:56 -0600
Subject: [PATCH 3/5] Updates for EC2
---
dotnetv4/DotNetV4Examples.sln | 26 +
dotnetv4/EC2/Actions/EC2Actions.csproj | 19 +
dotnetv4/EC2/Actions/EC2Wrapper.cs | 1046 +++++++++++++++++
dotnetv4/EC2/Actions/HelloEC2.cs | 62 +
dotnetv4/EC2/Actions/SsmWrapper.cs | 47 +
dotnetv4/EC2/Actions/Usings.cs | 9 +
dotnetv4/EC2/EC2Examples.sln | 52 +
dotnetv4/EC2/README.md | 177 +++
.../EC2/Scenarios/EC2_Basics/Basics.csproj | 21 +
.../EC2/Scenarios/EC2_Basics/EC2Basics.cs | 326 +++++
.../EC2/Scenarios/EC2_Basics/UIMethods.cs | 59 +
dotnetv4/EC2/Scenarios/EC2_Basics/Usings.cs | 7 +
dotnetv4/EC2/Tests/EC2Tests.csproj | 43 +
dotnetv4/EC2/Tests/EC2WrapperTests.cs | 79 ++
dotnetv4/EC2/Tests/Usings.cs | 11 +
dotnetv4/EC2/Tests/testsettings.json | 12 +
16 files changed, 1996 insertions(+)
create mode 100644 dotnetv4/EC2/Actions/EC2Actions.csproj
create mode 100644 dotnetv4/EC2/Actions/EC2Wrapper.cs
create mode 100644 dotnetv4/EC2/Actions/HelloEC2.cs
create mode 100644 dotnetv4/EC2/Actions/SsmWrapper.cs
create mode 100644 dotnetv4/EC2/Actions/Usings.cs
create mode 100644 dotnetv4/EC2/EC2Examples.sln
create mode 100644 dotnetv4/EC2/README.md
create mode 100644 dotnetv4/EC2/Scenarios/EC2_Basics/Basics.csproj
create mode 100644 dotnetv4/EC2/Scenarios/EC2_Basics/EC2Basics.cs
create mode 100644 dotnetv4/EC2/Scenarios/EC2_Basics/UIMethods.cs
create mode 100644 dotnetv4/EC2/Scenarios/EC2_Basics/Usings.cs
create mode 100644 dotnetv4/EC2/Tests/EC2Tests.csproj
create mode 100644 dotnetv4/EC2/Tests/EC2WrapperTests.cs
create mode 100644 dotnetv4/EC2/Tests/Usings.cs
create mode 100644 dotnetv4/EC2/Tests/testsettings.json
diff --git a/dotnetv4/DotNetV4Examples.sln b/dotnetv4/DotNetV4Examples.sln
index da9f1814a05..ab7be69d4d9 100644
--- a/dotnetv4/DotNetV4Examples.sln
+++ b/dotnetv4/DotNetV4Examples.sln
@@ -109,6 +109,16 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CloudWatchScenario", "Cloud
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CloudWatchActions", "CloudWatch\Actions\CloudWatchActions.csproj", "{EAF4A3B8-5CD0-48ED-B848-0EA6D451B8D3}"
EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "EC2", "EC2", "{9424FB14-B6DE-44CE-B675-AC2B57EC1E69}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EC2Tests", "EC2\Tests\EC2Tests.csproj", "{C99A0F7C-9477-4985-90F6-8EED38ECAC10}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Scenarios", "Scenarios", "{6C167F25-F97F-4854-8CD8-A2D446B6799B}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Basics", "EC2\Scenarios\EC2_Basics\Basics.csproj", "{D95519CA-BD27-45AE-B83B-3FB02E7AE445}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EC2Actions", "EC2\Actions\EC2Actions.csproj", "{0633CB2B-3508-48E5-A8C2-427A83A5CA6E}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -271,6 +281,18 @@ Global
{EAF4A3B8-5CD0-48ED-B848-0EA6D451B8D3}.Debug|Any CPU.Build.0 = Debug|Any CPU
{EAF4A3B8-5CD0-48ED-B848-0EA6D451B8D3}.Release|Any CPU.ActiveCfg = Release|Any CPU
{EAF4A3B8-5CD0-48ED-B848-0EA6D451B8D3}.Release|Any CPU.Build.0 = Release|Any CPU
+ {C99A0F7C-9477-4985-90F6-8EED38ECAC10}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {C99A0F7C-9477-4985-90F6-8EED38ECAC10}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {C99A0F7C-9477-4985-90F6-8EED38ECAC10}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {C99A0F7C-9477-4985-90F6-8EED38ECAC10}.Release|Any CPU.Build.0 = Release|Any CPU
+ {D95519CA-BD27-45AE-B83B-3FB02E7AE445}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {D95519CA-BD27-45AE-B83B-3FB02E7AE445}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {D95519CA-BD27-45AE-B83B-3FB02E7AE445}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {D95519CA-BD27-45AE-B83B-3FB02E7AE445}.Release|Any CPU.Build.0 = Release|Any CPU
+ {0633CB2B-3508-48E5-A8C2-427A83A5CA6E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {0633CB2B-3508-48E5-A8C2-427A83A5CA6E}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {0633CB2B-3508-48E5-A8C2-427A83A5CA6E}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {0633CB2B-3508-48E5-A8C2-427A83A5CA6E}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -323,6 +345,10 @@ Global
{106FBE12-6FF7-40DC-9B3C-E5F67F335B32} = {CED87D19-7F82-4D67-8A30-3EE085D07E45}
{565A9701-3D9C-49F8-86B7-D256A1D9E074} = {CED87D19-7F82-4D67-8A30-3EE085D07E45}
{EAF4A3B8-5CD0-48ED-B848-0EA6D451B8D3} = {CED87D19-7F82-4D67-8A30-3EE085D07E45}
+ {C99A0F7C-9477-4985-90F6-8EED38ECAC10} = {9424FB14-B6DE-44CE-B675-AC2B57EC1E69}
+ {6C167F25-F97F-4854-8CD8-A2D446B6799B} = {9424FB14-B6DE-44CE-B675-AC2B57EC1E69}
+ {D95519CA-BD27-45AE-B83B-3FB02E7AE445} = {6C167F25-F97F-4854-8CD8-A2D446B6799B}
+ {0633CB2B-3508-48E5-A8C2-427A83A5CA6E} = {9424FB14-B6DE-44CE-B675-AC2B57EC1E69}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {08502818-E8E1-4A91-A51C-4C8C8D4FF9CA}
diff --git a/dotnetv4/EC2/Actions/EC2Actions.csproj b/dotnetv4/EC2/Actions/EC2Actions.csproj
new file mode 100644
index 00000000000..6840737b6e5
--- /dev/null
+++ b/dotnetv4/EC2/Actions/EC2Actions.csproj
@@ -0,0 +1,19 @@
+
+
+
+ Exe
+ net8.0
+ enable
+ enable
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/dotnetv4/EC2/Actions/EC2Wrapper.cs b/dotnetv4/EC2/Actions/EC2Wrapper.cs
new file mode 100644
index 00000000000..691ae97b6be
--- /dev/null
+++ b/dotnetv4/EC2/Actions/EC2Wrapper.cs
@@ -0,0 +1,1046 @@
+// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
+// SPDX-License-Identifier: Apache-2.0
+
+using Microsoft.Extensions.Logging;
+
+namespace EC2Actions;
+
+// snippet-start:[EC2.dotnetv4.EC2WrapperClass]
+///
+/// Methods of this class perform Amazon Elastic Compute Cloud (Amazon EC2).
+///
+public class EC2Wrapper
+{
+ private readonly IAmazonEC2 _amazonEC2;
+ private readonly ILogger _logger;
+
+ ///
+ /// Constructor for the EC2Wrapper class.
+ ///
+ /// The injected EC2 client.
+ /// The injected logger.
+ public EC2Wrapper(IAmazonEC2 amazonService, ILogger logger)
+ {
+ _amazonEC2 = amazonService;
+ _logger = logger;
+ }
+
+ // snippet-start:[EC2.dotnetv4.AllocateAddress]
+ ///
+ /// 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.
+ ///
+ /// The response object for the allocated address.
+ public async Task AllocateAddress()
+ {
+ var request = new AllocateAddressRequest();
+
+ try
+ {
+ var response = await _amazonEC2.AllocateAddressAsync(request);
+ Console.WriteLine($"Allocated IP: {response.PublicIp} with allocation ID {response.AllocationId}.");
+ return response;
+ }
+ catch (AmazonEC2Exception ec2Exception)
+ {
+ if (ec2Exception.ErrorCode == "AddressLimitExceeded")
+ {
+ // For more information on Elastic IP address quotas, see:
+ // https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/elastic-ip-addresses-eip.html#using-instance-addressing-limit
+ _logger.LogError($"Unable to allocate Elastic IP, address limit exceeded. {ec2Exception.Message}");
+ }
+
+ throw;
+ }
+ catch (Exception ex)
+ {
+ _logger.LogError($"An error occurred while allocating Elastic IP.: {ex.Message}");
+ throw;
+ }
+ }
+ // snippet-end:[EC2.dotnetv4.AllocateAddress]
+
+ // snippet-start:[EC2.dotnetv4.AssociateAddress]
+ ///
+ /// 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.
+ ///
+ /// The allocation Id of an Elastic IP address.
+ /// The instance Id of the EC2 instance to
+ /// associate the address with.
+ /// The association Id that represents
+ /// the association of the Elastic IP address with an instance.
+ public async Task AssociateAddress(string allocationId, string instanceId)
+ {
+ try
+ {
+ var request = new AssociateAddressRequest
+ {
+ AllocationId = allocationId,
+ InstanceId = instanceId
+ };
+
+ var response = await _amazonEC2.AssociateAddressAsync(request);
+ return response.AssociationId;
+ }
+ catch (AmazonEC2Exception ec2Exception)
+ {
+ if (ec2Exception.ErrorCode == "InvalidInstanceId")
+ {
+ _logger.LogError(
+ $"InstanceId is invalid, unable to associate address. {ec2Exception.Message}");
+ }
+
+ throw;
+ }
+ catch (Exception ex)
+ {
+ _logger.LogError(
+ $"An error occurred while associating the Elastic IP.: {ex.Message}");
+ throw;
+ }
+ }
+ // snippet-end:[EC2.dotnetv4.AssociateAddress]
+
+ // snippet-start:[EC2.dotnetv4.AuthorizeSecurityGroupIngress]
+ ///
+ /// Authorize the local computer ingress to EC2 instances associated
+ /// with the virtual private cloud (VPC) security group.
+ ///
+ /// The name of the security group.
+ /// A Boolean value indicating the success of the action.
+ public async Task AuthorizeSecurityGroupIngress(string groupName)
+ {
+ try
+ {
+ // Get the IP address for the local computer.
+ var ipAddress = await GetIpAddress();
+ Console.WriteLine($"Your IP address is: {ipAddress}");
+ var ipRanges =
+ new List { new IpRange { CidrIp = $"{ipAddress}/32" } };
+ var permission = new IpPermission
+ {
+ Ipv4Ranges = ipRanges,
+ IpProtocol = "tcp",
+ FromPort = 22,
+ ToPort = 22
+ };
+ var permissions = new List { permission };
+ var response = await _amazonEC2.AuthorizeSecurityGroupIngressAsync(
+ new AuthorizeSecurityGroupIngressRequest(groupName, permissions));
+ return response.HttpStatusCode == HttpStatusCode.OK;
+ }
+ catch (AmazonEC2Exception ec2Exception)
+ {
+ if (ec2Exception.ErrorCode == "InvalidPermission.Duplicate")
+ {
+ _logger.LogError(
+ $"The ingress rule already exists. {ec2Exception.Message}");
+ }
+
+ throw;
+ }
+ catch (Exception ex)
+ {
+ _logger.LogError(
+ $"An error occurred while authorizing ingress.: {ex.Message}");
+ throw;
+ }
+ }
+
+ ///
+ /// Authorize the local computer for ingress to
+ /// the Amazon EC2 SecurityGroup.
+ ///
+ /// The IPv4 address of the computer running the scenario.
+ private static async Task 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();
+ }
+ // snippet-end:[EC2.dotnetv4.AuthorizeSecurityGroupIngress]
+
+ // snippet-start:[EC2.dotnetv4.CreateKeyPair]
+ ///
+ /// Create an Amazon EC2 key pair with a specified name.
+ ///
+ /// The name for the new key pair.
+ /// The Amazon EC2 key pair created.
+ public async Task CreateKeyPair(string keyPairName)
+ {
+ try
+ {
+ var request = new CreateKeyPairRequest { KeyName = keyPairName, };
+
+ var response = await _amazonEC2.CreateKeyPairAsync(request);
+
+ var kp = response.KeyPair;
+ // Return the key pair so it can be saved if needed.
+
+ // Wait until the key pair exists.
+ int retries = 5;
+ while (retries-- > 0)
+ {
+ Console.WriteLine($"Checking for new KeyPair {keyPairName}...");
+ var keyPairs = await DescribeKeyPairs(keyPairName);
+ if (keyPairs.Any())
+ {
+ return kp;
+ }
+
+ Thread.Sleep(5000);
+ retries--;
+ }
+ _logger.LogError($"Unable to find newly created KeyPair {keyPairName}.");
+ throw new DoesNotExistException("KeyPair not found");
+ }
+ catch (AmazonEC2Exception ec2Exception)
+ {
+ if (ec2Exception.ErrorCode == "InvalidKeyPair.Duplicate")
+ {
+ _logger.LogError(
+ $"A key pair called {keyPairName} already exists.");
+ }
+
+ throw;
+ }
+ catch (Exception ex)
+ {
+ _logger.LogError(
+ $"An error occurred while creating the key pair.: {ex.Message}");
+ throw;
+ }
+ }
+
+ ///
+ /// Save KeyPair information to a temporary file.
+ ///
+ /// The name of the key pair.
+ /// The full path to the temporary file.
+ 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;
+ }
+ // snippet-end:[EC2.dotnetv4.CreateKeyPair]
+
+ // snippet-start:[EC2.dotnetv4.CreateSecurityGroup]
+ ///
+ /// Create an Amazon EC2 security group with a specified name and description.
+ ///
+ /// The name for the new security group.
+ /// A description of the new security group.
+ /// The group Id of the new security group.
+ public async Task CreateSecurityGroup(string groupName, string groupDescription)
+ {
+ try
+ {
+ var response = await _amazonEC2.CreateSecurityGroupAsync(
+ new CreateSecurityGroupRequest(groupName, groupDescription));
+
+ // Wait until the security group exists.
+ int retries = 5;
+ while (retries-- > 0)
+ {
+ var groups = await DescribeSecurityGroups(response.GroupId);
+ if (groups.Any())
+ {
+ return response.GroupId;
+ }
+
+ Thread.Sleep(5000);
+ retries--;
+ }
+ _logger.LogError($"Unable to find newly created group {groupName}.");
+ throw new DoesNotExistException("security group not found");
+ }
+ catch (AmazonEC2Exception ec2Exception)
+ {
+ if (ec2Exception.ErrorCode == "ResourceAlreadyExists")
+ {
+ _logger.LogError(
+ $"A security group with the name {groupName} already exists. {ec2Exception.Message}");
+ }
+ throw;
+ }
+ catch (Exception ex)
+ {
+ _logger.LogError(
+ $"An error occurred while creating the security group.: {ex.Message}");
+ throw;
+ }
+ }
+
+ // snippet-end:[EC2.dotnetv4.CreateSecurityGroup]
+
+ // snippet-start:[EC2.dotnetv4.CreateVPC]
+ ///
+ /// Create a new Amazon EC2 VPC.
+ ///
+ /// The CIDR block for the new security group.
+ /// The VPC Id of the new VPC.
+ public async Task 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;
+ }
+ }
+ // snippet-end:[EC2.dotnetv4.CreateVPC]
+
+ // snippet-start:[EC2.dotnetv4.DeleteKeyPair]
+ ///
+ /// Delete an Amazon EC2 key pair.
+ ///
+ /// The name of the key pair to delete.
+ /// A Boolean value indicating the success of the action.
+ public async Task DeleteKeyPair(string keyPairName)
+ {
+ try
+ {
+ await _amazonEC2.DeleteKeyPairAsync(new DeleteKeyPairRequest(keyPairName)).ConfigureAwait(false);
+ return true;
+ }
+ catch (AmazonEC2Exception ec2Exception)
+ {
+ if (ec2Exception.ErrorCode == "InvalidKeyPair.NotFound")
+ {
+ _logger.LogError($"KeyPair {keyPairName} does not exist and cannot be deleted. Please verify the key pair name and try again.");
+ }
+
+ return false;
+ }
+ catch (Exception ex)
+ {
+ Console.WriteLine($"Couldn't delete the key pair because: {ex.Message}");
+ return false;
+ }
+ }
+
+ ///
+ /// Delete the temporary file where the key pair information was saved.
+ ///
+ /// The path to the temporary file.
+ public void DeleteTempFile(string tempFileName)
+ {
+ if (File.Exists(tempFileName))
+ {
+ File.Delete(tempFileName);
+ }
+ }
+ // snippet-end:[EC2.dotnetv4.DeleteKeyPair]
+
+ // snippet-start:[EC2.dotnetv4.DeleteSecurityGroup]
+ ///
+ /// Delete an Amazon EC2 security group.
+ ///
+ /// The name of the group to delete.
+ /// A Boolean value indicating the success of the action.
+ public async Task DeleteSecurityGroup(string groupId)
+ {
+ try
+ {
+ var response =
+ await _amazonEC2.DeleteSecurityGroupAsync(
+ new DeleteSecurityGroupRequest { GroupId = groupId });
+ return response.HttpStatusCode == HttpStatusCode.OK;
+ }
+ catch (AmazonEC2Exception ec2Exception)
+ {
+ if (ec2Exception.ErrorCode == "InvalidGroup.NotFound")
+ {
+ _logger.LogError(
+ $"Security Group {groupId} does not exist and cannot be deleted. Please verify the ID and try again.");
+ }
+
+ return false;
+ }
+ catch (Exception ex)
+ {
+ Console.WriteLine($"Couldn't delete the security group because: {ex.Message}");
+ return false;
+ }
+ }
+ // snippet-end:[EC2.dotnetv4.DeleteSecurityGroup]
+
+ // snippet-start:[EC2.dotnetv4.DeleteVPC]
+ ///
+ /// Delete an Amazon EC2 VPC.
+ ///
+ /// A Boolean value indicating the success of the action.
+ public async Task DeleteVpc(string vpcId)
+ {
+ var request = new DeleteVpcRequest
+ {
+ VpcId = vpcId,
+ };
+
+ var response = await _amazonEC2.DeleteVpcAsync(request);
+
+ return response.HttpStatusCode == System.Net.HttpStatusCode.OK;
+ }
+ // snippet-end:[EC2.dotnetv4.DeleteVPC]
+
+ // snippet-start:[EC2.dotnetv4.DescribeImages]
+ ///
+ /// Get information about existing Amazon EC2 images.
+ ///
+ /// A list of image information.
+ public async Task> DescribeImages(List? 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;
+ }
+
+ ///
+ /// Display the information returned by DescribeImages.
+ ///
+ /// The list of image information to display.
+ public void DisplayImageInfo(List images)
+ {
+ images.ForEach(image =>
+ {
+ Console.WriteLine($"{image.Name} Created on: {image.CreationDate}");
+ });
+
+ }
+ // snippet-end:[EC2.dotnetv4.DescribeImages]
+
+ // snippet-start:[EC2.dotnetv4.DescribeInstance]
+ ///
+ /// Get information about an Amazon EC2 instance.
+ ///
+ /// The instance Id of the EC2 instance.
+ /// An EC2 instance.
+ public async Task DescribeInstance(string instanceId)
+ {
+ var response = await _amazonEC2.DescribeInstancesAsync(
+ new DescribeInstancesRequest { InstanceIds = new List { instanceId } });
+ return response.Reservations[0].Instances[0];
+ }
+
+ ///
+ /// Display EC2 instance information.
+ ///
+ /// The instance Id of the EC2 instance.
+ 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}");
+ }
+ // snippet-end:[EC2.dotnetv4.DescribeInstance]
+
+ // snippet-start:[EC2.dotnetv4.DescribeInstances]
+ ///
+ /// Get information about EC2 instances with a particular state.
+ ///
+ /// The name of the tag to filter on.
+ /// The value of the tag to look for.
+ /// True if successful.
+ public async Task GetInstancesWithState(string state)
+ {
+ try
+ {
+ // Filters the results of the instance list.
+ var filters = new List
+ {
+ new Filter
+ {
+ Name = $"instance-state-name",
+ Values = new List { state, },
+ },
+ };
+ var request = new DescribeInstancesRequest { Filters = filters, };
+
+ Console.WriteLine($"\nShowing instances with state {state}");
+ var paginator = _amazonEC2.Paginators.DescribeInstances(request);
+
+ await foreach (var response in paginator.Responses)
+ {
+ foreach (var reservation in response.Reservations)
+ {
+ foreach (var instance in reservation.Instances)
+ {
+ Console.Write($"Instance ID: {instance.InstanceId} ");
+ Console.WriteLine($"\tCurrent State: {instance.State.Name}");
+ }
+ }
+ }
+
+ return true;
+ }
+ catch (AmazonEC2Exception ec2Exception)
+ {
+ if (ec2Exception.ErrorCode == "InvalidParameterValue")
+ {
+ _logger.LogError(
+ $"Invalid parameter value for filtering instances.");
+ }
+
+ return false;
+ }
+ catch (Exception ex)
+ {
+ Console.WriteLine($"Couldn't list instances because: {ex.Message}");
+ return false;
+ }
+ }
+ // snippet-end:[EC2.dotnetv4.DescribeInstances]
+
+ // snippet-start:[EC2.dotnetv4.DescribeInstanceTypes]
+ ///
+ /// Describe the instance types available.
+ ///
+ /// A list of instance type information.
+ public async Task> DescribeInstanceTypes(ArchitectureValues architecture)
+ {
+ try
+ {
+ var request = new DescribeInstanceTypesRequest();
+
+ var filters = new List
+ {
+ new Filter("processor-info.supported-architecture",
+ new List { architecture.ToString() })
+ };
+ filters.Add(new Filter("instance-type", new() { "*.micro", "*.small" }));
+
+ request.Filters = filters;
+ var instanceTypes = new List();
+
+ var paginator = _amazonEC2.Paginators.DescribeInstanceTypes(request);
+ await foreach (var instanceType in paginator.InstanceTypes)
+ {
+ instanceTypes.Add(instanceType);
+ }
+
+ return instanceTypes;
+ }
+ catch (AmazonEC2Exception ec2Exception)
+ {
+ if (ec2Exception.ErrorCode == "InvalidParameterValue")
+ {
+ _logger.LogError(
+ $"Parameters are invalid. Ensure architecture and size strings conform to DescribeInstanceTypes API reference.");
+ }
+
+ throw;
+ }
+ catch (Exception ex)
+ {
+ Console.WriteLine($"Couldn't delete the security group because: {ex.Message}");
+ throw;
+ }
+ }
+ // snippet-end:[EC2.dotnetv4.DescribeInstanceTypes]
+
+ // snippet-start:[EC2.dotnetv4.DescribeKeyPairs]
+ ///
+ /// Get information about an Amazon EC2 key pair.
+ ///
+ /// The name of the key pair.
+ /// A list of key pair information.
+ public async Task> DescribeKeyPairs(string keyPairName)
+ {
+ try
+ {
+ var request = new DescribeKeyPairsRequest();
+ if (!string.IsNullOrEmpty(keyPairName))
+ {
+ request = new DescribeKeyPairsRequest
+ {
+ KeyNames = new List { keyPairName }
+ };
+ }
+
+ var response = await _amazonEC2.DescribeKeyPairsAsync(request);
+ return response.KeyPairs.ToList();
+ }
+ catch (AmazonEC2Exception ec2Exception)
+ {
+ if (ec2Exception.ErrorCode == "InvalidKeyPair.NotFound")
+ {
+ _logger.LogError(
+ $"A key pair called {keyPairName} does not exist.");
+ }
+
+ throw;
+ }
+ catch (Exception ex)
+ {
+ _logger.LogError(
+ $"An error occurred while describing the key pair.: {ex.Message}");
+ throw;
+ }
+ }
+
+ // snippet-end:[EC2.dotnetv4.DescribeKeyPairs]
+
+ // snippet-start:[EC2.dotnetv4.DescribeSecurityGroups]
+ ///
+ /// Retrieve information for one or all Amazon EC2 security group.
+ ///
+ /// The optional Id of a specific Amazon EC2 security group.
+ /// A list of security group information.
+ public async Task> DescribeSecurityGroups(string groupId)
+ {
+ try
+ {
+ var securityGroups = new List();
+ var request = new DescribeSecurityGroupsRequest();
+
+ if (!string.IsNullOrEmpty(groupId))
+ {
+ var groupIds = new List { groupId };
+ request.GroupIds = groupIds;
+ }
+
+ var paginatorForSecurityGroups =
+ _amazonEC2.Paginators.DescribeSecurityGroups(request);
+
+ await foreach (var securityGroup in paginatorForSecurityGroups.SecurityGroups)
+ {
+ securityGroups.Add(securityGroup);
+ }
+
+ return securityGroups;
+
+ }
+ catch (AmazonEC2Exception ec2Exception)
+ {
+ if (ec2Exception.ErrorCode == "InvalidGroup.NotFound")
+ {
+ _logger.LogError(
+ $"A security group {groupId} does not exist.");
+ }
+
+ throw;
+ }
+ catch (Exception ex)
+ {
+ _logger.LogError(
+ $"An error occurred while listing security groups. {ex.Message}");
+ throw;
+ }
+ }
+
+ ///
+ /// Display the information returned by the call to
+ /// DescribeSecurityGroupsAsync.
+ ///
+ /// A list of security group information.
+ 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}");
+ });
+ }
+
+ // snippet-end:[EC2.dotnetv4.DescribeSecurityGroups]
+
+ // snippet-start:[EC2.dotnetv4.DisassociateAddress]
+ ///
+ /// Disassociate an Elastic IP address from an EC2 instance.
+ ///
+ /// The association Id.
+ /// A Boolean value indicating the success of the action.
+ public async Task DisassociateIp(string associationId)
+ {
+ try
+ {
+ var response = await _amazonEC2.DisassociateAddressAsync(
+ new DisassociateAddressRequest { AssociationId = associationId });
+ return response.HttpStatusCode == HttpStatusCode.OK;
+ }
+ catch (AmazonEC2Exception ec2Exception)
+ {
+ if (ec2Exception.ErrorCode == "InvalidAssociationID.NotFound")
+ {
+ _logger.LogError(
+ $"AssociationId is invalid, unable to disassociate address. {ec2Exception.Message}");
+ }
+
+ return false;
+ }
+ catch (Exception ex)
+ {
+ _logger.LogError(
+ $"An error occurred while disassociating the Elastic IP.: {ex.Message}");
+ return false;
+ }
+ }
+ // snippet-end:[EC2.dotnetv4.DisassociateAddress]
+
+ // snippet-start:[EC2.dotnetv4.GetAMIList]
+ ///
+ /// Retrieve a list of available Amazon Linux images.
+ ///
+ /// A list of image information.
+ public async Task> GetEC2AmiList()
+ {
+ var filter = new Filter { Name = "architecture", Values = new List { "x86_64" } };
+ var filters = new List { filter };
+ var response = await _amazonEC2.DescribeImagesAsync(new DescribeImagesRequest { Filters = filters });
+ return response.Images;
+ }
+ // snippet-end:[EC2.dotnetv4.GetAMIList]
+
+ // snippet-start:[EC2.dotnetv4.RebootInstances]
+ ///
+ /// Reboot a specific EC2 instance.
+ ///
+ /// The instance Id of the instance that will be rebooted.
+ /// Async Task.
+ public async Task RebootInstances(string ec2InstanceId)
+ {
+ try
+ {
+ var request = new RebootInstancesRequest
+ {
+ InstanceIds = new List { ec2InstanceId },
+ };
+
+ await _amazonEC2.RebootInstancesAsync(request);
+
+ // Wait for the instance to be running.
+ Console.Write("Waiting for the instance to start.");
+ await WaitForInstanceState(ec2InstanceId, InstanceStateName.Running);
+
+ return true;
+ }
+ catch (AmazonEC2Exception ec2Exception)
+ {
+ if (ec2Exception.ErrorCode == "InvalidInstanceId")
+ {
+ _logger.LogError(
+ $"InstanceId {ec2InstanceId} is invalid, unable to reboot. {ec2Exception.Message}");
+ }
+ return false;
+ }
+ catch (Exception ex)
+ {
+ _logger.LogError(
+ $"An error occurred while rebooting the instance {ec2InstanceId}.: {ex.Message}");
+ return false;
+ }
+ }
+ // snippet-end:[EC2.dotnetv4.RebootInstances]
+
+ // snippet-start:[EC2.dotnetv4.ReleaseAddress]
+ ///
+ /// Release an Elastic IP address. After the Elastic IP address is released,
+ /// it can no longer be used.
+ ///
+ /// The allocation Id of the Elastic IP address.
+ /// True if successful.
+ public async Task ReleaseAddress(string allocationId)
+ {
+ try
+ {
+ var request = new ReleaseAddressRequest { AllocationId = allocationId };
+
+ var response = await _amazonEC2.ReleaseAddressAsync(request);
+ return response.HttpStatusCode == HttpStatusCode.OK;
+ }
+ catch (AmazonEC2Exception ec2Exception)
+ {
+ if (ec2Exception.ErrorCode == "InvalidAllocationID.NotFound")
+ {
+ _logger.LogError(
+ $"AllocationId {allocationId} was not found. {ec2Exception.Message}");
+ }
+
+ return false;
+ }
+ catch (Exception ex)
+ {
+ _logger.LogError(
+ $"An error occurred while releasing the AllocationId {allocationId}.: {ex.Message}");
+ return false;
+ }
+ }
+ // snippet-end:[EC2.dotnetv4.ReleaseAddress]
+
+ // snippet-start:[EC2.dotnetv4.RunInstances]
+ ///
+ /// Create and run an EC2 instance.
+ ///
+ /// The image Id of the image used as a basis for the
+ /// EC2 instance.
+ /// The instance type of the EC2 instance to create.
+ /// The name of the key pair to associate with the
+ /// instance.
+ /// The Id of the Amazon EC2 security group that will be
+ /// allowed to interact with the new EC2 instance.
+ /// The instance Id of the new EC2 instance.
+ public async Task RunInstances(string imageId, string instanceType, string keyName, string groupId)
+ {
+ try
+ {
+ var request = new RunInstancesRequest
+ {
+ ImageId = imageId,
+ InstanceType = instanceType,
+ KeyName = keyName,
+ MinCount = 1,
+ MaxCount = 1,
+ SecurityGroupIds = new List { groupId }
+ };
+ var response = await _amazonEC2.RunInstancesAsync(request);
+ var instanceId = response.Reservation.Instances[0].InstanceId;
+
+ Console.Write("Waiting for the instance to start.");
+ await WaitForInstanceState(instanceId, InstanceStateName.Running);
+
+ return instanceId;
+ }
+ catch (AmazonEC2Exception ec2Exception)
+ {
+ if (ec2Exception.ErrorCode == "InvalidGroupId.NotFound")
+ {
+ _logger.LogError(
+ $"GroupId {groupId} was not found. {ec2Exception.Message}");
+ }
+
+ throw;
+ }
+ catch (Exception ex)
+ {
+ _logger.LogError(
+ $"An error occurred while running the instance.: {ex.Message}");
+ throw;
+ }
+ }
+
+ // snippet-end:[EC2.dotnetv4.RunInstances]
+
+ // snippet-start:[EC2.dotnetv4.StartInstances]
+ ///
+ /// Start an EC2 instance.
+ ///
+ /// The instance Id of the Amazon EC2 instance
+ /// to start.
+ /// Async task.
+ public async Task StartInstances(string ec2InstanceId)
+ {
+ try
+ {
+ var request = new StartInstancesRequest
+ {
+ InstanceIds = new List { ec2InstanceId },
+ };
+
+ await _amazonEC2.StartInstancesAsync(request);
+
+ Console.Write("Waiting for instance to start. ");
+ await WaitForInstanceState(ec2InstanceId, InstanceStateName.Running);
+ }
+ catch (AmazonEC2Exception ec2Exception)
+ {
+ if (ec2Exception.ErrorCode == "InvalidInstanceId")
+ {
+ _logger.LogError(
+ $"InstanceId is invalid, unable to start. {ec2Exception.Message}");
+ }
+
+ throw;
+ }
+ catch (Exception ex)
+ {
+ _logger.LogError(
+ $"An error occurred while starting the instance.: {ex.Message}");
+ throw;
+ }
+ }
+ // snippet-end:[EC2.dotnetv4.StartInstances]
+
+ // snippet-start:[EC2.dotnetv4.StopInstances]
+ ///
+ /// Stop an EC2 instance.
+ ///
+ /// The instance Id of the EC2 instance to
+ /// stop.
+ /// Async task.
+ public async Task StopInstances(string ec2InstanceId)
+ {
+ try
+ {
+ var request = new StopInstancesRequest
+ {
+ InstanceIds = new List { ec2InstanceId },
+ };
+
+ await _amazonEC2.StopInstancesAsync(request);
+ Console.Write("Waiting for the instance to stop.");
+ await WaitForInstanceState(ec2InstanceId, InstanceStateName.Stopped);
+
+ Console.WriteLine("\nThe instance has stopped.");
+ }
+ catch (AmazonEC2Exception ec2Exception)
+ {
+ if (ec2Exception.ErrorCode == "InvalidInstanceId")
+ {
+ _logger.LogError(
+ $"InstanceId is invalid, unable to stop. {ec2Exception.Message}");
+ }
+
+ throw;
+ }
+ catch (Exception ex)
+ {
+ _logger.LogError(
+ $"An error occurred while stopping the instance.: {ex.Message}");
+ throw;
+ }
+ }
+ // snippet-end:[EC2.dotnetv4.StopInstances]
+
+ // snippet-start:[EC2.dotnetv4.TerminateInstances]
+ ///
+ /// Terminate an EC2 instance.
+ ///
+ /// The instance Id of the EC2 instance
+ /// to terminate.
+ /// Async task.
+ public async Task> TerminateInstances(string ec2InstanceId)
+ {
+ try
+ {
+ var request = new TerminateInstancesRequest
+ {
+ InstanceIds = new List { ec2InstanceId }
+ };
+
+ var response = await _amazonEC2.TerminateInstancesAsync(request);
+ Console.Write("Waiting for the instance to terminate.");
+ await WaitForInstanceState(ec2InstanceId, InstanceStateName.Terminated);
+
+ Console.WriteLine($"\nThe instance {ec2InstanceId} has been terminated.");
+ return response.TerminatingInstances;
+ }
+ catch (AmazonEC2Exception ec2Exception)
+ {
+ if (ec2Exception.ErrorCode == "InvalidInstanceId")
+ {
+ _logger.LogError(
+ $"InstanceId is invalid, unable to terminate. {ec2Exception.Message}");
+ }
+
+ throw;
+ }
+ catch (Exception ex)
+ {
+ _logger.LogError(
+ $"An error occurred while terminating the instance.: {ex.Message}");
+ throw;
+ }
+ }
+ // snippet-end:[EC2.dotnetv4.TerminateInstances]
+
+ // snippet-start:[EC2.dotnetv4.WaitForInstanceState]
+ ///
+ /// Wait until an EC2 instance is in a specified state.
+ ///
+ /// The instance Id.
+ /// The state to wait for.
+ /// A Boolean value indicating the success of the action.
+ public async Task WaitForInstanceState(string instanceId, InstanceStateName stateName)
+ {
+ var request = new DescribeInstancesRequest
+ {
+ InstanceIds = new List { instanceId }
+ };
+
+ // Wait until the instance is in the specified state.
+ var hasState = false;
+ do
+ {
+ // Wait 5 seconds.
+ Thread.Sleep(5000);
+
+ // Check for the desired state.
+ var response = await _amazonEC2.DescribeInstancesAsync(request);
+ var instance = response.Reservations[0].Instances[0];
+ hasState = instance.State.Name == stateName;
+ Console.Write(". ");
+ } while (!hasState);
+
+ return hasState;
+ }
+
+ // snippet-end:[EC2.dotnetv4.WaitForInstanceState]
+}
+// snippet-end:[EC2.dotnetv4.EC2WrapperClass]
\ No newline at end of file
diff --git a/dotnetv4/EC2/Actions/HelloEC2.cs b/dotnetv4/EC2/Actions/HelloEC2.cs
new file mode 100644
index 00000000000..d6ae2731449
--- /dev/null
+++ b/dotnetv4/EC2/Actions/HelloEC2.cs
@@ -0,0 +1,62 @@
+// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
+// SPDX-License-Identifier: Apache-2.0
+
+// snippet-start:[EC2.dotnetv4.HelloEc2]
+
+namespace EC2Actions;
+
+public class HelloEc2
+{
+ ///
+ /// HelloEc2 lists the existing security groups for the default users.
+ ///
+ /// Command line arguments
+ /// Async task.
+ static async Task Main(string[] args)
+ {
+ // Set up dependency injection for Amazon Elastic Compute Cloud (Amazon EC2).
+ using var host = Microsoft.Extensions.Hosting.Host.CreateDefaultBuilder(args)
+ .ConfigureServices((_, services) =>
+ services.AddAWSService()
+ .AddTransient()
+ )
+ .Build();
+
+ // Now the client is available for injection.
+ var ec2Client = host.Services.GetRequiredService();
+
+ try
+ {
+ // Retrieve information for up to 10 Amazon EC2 security groups.
+ var request = new DescribeSecurityGroupsRequest { MaxResults = 10, };
+ var securityGroups = new List();
+
+ var paginatorForSecurityGroups =
+ ec2Client.Paginators.DescribeSecurityGroups(request);
+
+ await foreach (var securityGroup in paginatorForSecurityGroups.SecurityGroups)
+ {
+ securityGroups.Add(securityGroup);
+ }
+
+ // Now print the security groups returned by the call to
+ // DescribeSecurityGroupsAsync.
+ Console.WriteLine("Welcome to the EC2 Hello Service example. " +
+ "\nLet's list your Security Groups:");
+ securityGroups.ForEach(group =>
+ {
+ Console.WriteLine(
+ $"Security group: {group.GroupName} ID: {group.GroupId}");
+ });
+ }
+ catch (AmazonEC2Exception ex)
+ {
+ Console.WriteLine($"An Amazon EC2 service error occurred while listing security groups. {ex.Message}");
+ }
+ catch (Exception ex)
+ {
+ Console.WriteLine($"An error occurred while listing security groups. {ex.Message}");
+ }
+ }
+}
+// snippet-end:[EC2.dotnetv4.HelloEc2]
\ No newline at end of file
diff --git a/dotnetv4/EC2/Actions/SsmWrapper.cs b/dotnetv4/EC2/Actions/SsmWrapper.cs
new file mode 100644
index 00000000000..fe36855451a
--- /dev/null
+++ b/dotnetv4/EC2/Actions/SsmWrapper.cs
@@ -0,0 +1,47 @@
+// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
+// SPDX-License-Identifier: Apache-2.0
+
+namespace EC2Actions;
+
+// snippet-start:[SSM.dotnetv4.SSMActionsClass]
+///
+/// Methods that perform actions from the Simple Systems Management Service.
+///
+public class SsmWrapper
+{
+ private readonly IAmazonSimpleSystemsManagement _amazonSSM;
+
+ public SsmWrapper(IAmazonSimpleSystemsManagement amazonService)
+ {
+ _amazonSSM = amazonService;
+ }
+
+ ///
+ /// Get a list of parameter values based on the service path.
+ ///
+ /// The path used to retrieve parameters.
+ /// Async task.
+ public async Task> GetParametersByPath(string path)
+ {
+ var parameters = new List();
+ var request = new GetParametersByPathRequest { Path = path };
+
+ // Get the whole list with a paginator.
+ var paginatedParametersByPath = _amazonSSM.Paginators.GetParametersByPath(request);
+
+ await foreach (var parametersPage in paginatedParametersByPath.Responses)
+ {
+ parameters.AddRange(parametersPage.Parameters);
+ }
+
+ // Filter out everything except items that
+ // have "amzn2" in the name property.
+ var paramList =
+ from parameter in parameters
+ where parameter.Name.Contains("amzn2")
+ select parameter;
+ return paramList.ToList();
+ }
+}
+
+// snippet-end:[SSM.dotnetv4.SSMActionsClass]
\ No newline at end of file
diff --git a/dotnetv4/EC2/Actions/Usings.cs b/dotnetv4/EC2/Actions/Usings.cs
new file mode 100644
index 00000000000..3125ba51a46
--- /dev/null
+++ b/dotnetv4/EC2/Actions/Usings.cs
@@ -0,0 +1,9 @@
+// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
+// SPDX-License-Identifier: Apache-2.0
+
+global using System.Net;
+global using Amazon.EC2;
+global using Amazon.EC2.Model;
+global using Amazon.SimpleSystemsManagement;
+global using Amazon.SimpleSystemsManagement.Model;
+global using Microsoft.Extensions.DependencyInjection;
\ No newline at end of file
diff --git a/dotnetv4/EC2/EC2Examples.sln b/dotnetv4/EC2/EC2Examples.sln
new file mode 100644
index 00000000000..aee7d5c12ce
--- /dev/null
+++ b/dotnetv4/EC2/EC2Examples.sln
@@ -0,0 +1,52 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio Version 17
+VisualStudioVersion = 17.2.32630.192
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "EC2Actions", "Actions\EC2Actions.csproj", "{796910FA-6E94-460B-8CB4-97DF01B9ADC8}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Basics", "Scenarios\EC2_Basics\Basics.csproj", "{B1731AE1-381F-4044-BEBE-269FF7E24B1F}"
+ ProjectSection(ProjectDependencies) = postProject
+ {796910FA-6E94-460B-8CB4-97DF01B9ADC8} = {796910FA-6E94-460B-8CB4-97DF01B9ADC8}
+ EndProjectSection
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "EC2Tests", "Tests\EC2Tests.csproj", "{6046A2FC-6A39-4C2D-8DD9-AA3740B17B88}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CreateVPCExample", "Scenarios\CreateVPCExample\CreateVPCExample.csproj", "{A0B25DB2-E0BE-409F-950D-19E23FB76319}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CreateVPCforS3Example", "Scenarios\CreateVPCforS3Example\CreateVPCforS3Example.csproj", "{CB5A94D4-1BA1-41CE-B5AE-7CE906B4CA7D}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Release|Any CPU = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {796910FA-6E94-460B-8CB4-97DF01B9ADC8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {796910FA-6E94-460B-8CB4-97DF01B9ADC8}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {796910FA-6E94-460B-8CB4-97DF01B9ADC8}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {796910FA-6E94-460B-8CB4-97DF01B9ADC8}.Release|Any CPU.Build.0 = Release|Any CPU
+ {B1731AE1-381F-4044-BEBE-269FF7E24B1F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {B1731AE1-381F-4044-BEBE-269FF7E24B1F}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {B1731AE1-381F-4044-BEBE-269FF7E24B1F}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {B1731AE1-381F-4044-BEBE-269FF7E24B1F}.Release|Any CPU.Build.0 = Release|Any CPU
+ {6046A2FC-6A39-4C2D-8DD9-AA3740B17B88}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {6046A2FC-6A39-4C2D-8DD9-AA3740B17B88}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {6046A2FC-6A39-4C2D-8DD9-AA3740B17B88}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {6046A2FC-6A39-4C2D-8DD9-AA3740B17B88}.Release|Any CPU.Build.0 = Release|Any CPU
+ {A0B25DB2-E0BE-409F-950D-19E23FB76319}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {A0B25DB2-E0BE-409F-950D-19E23FB76319}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {A0B25DB2-E0BE-409F-950D-19E23FB76319}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {A0B25DB2-E0BE-409F-950D-19E23FB76319}.Release|Any CPU.Build.0 = Release|Any CPU
+ {CB5A94D4-1BA1-41CE-B5AE-7CE906B4CA7D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {CB5A94D4-1BA1-41CE-B5AE-7CE906B4CA7D}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {CB5A94D4-1BA1-41CE-B5AE-7CE906B4CA7D}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {CB5A94D4-1BA1-41CE-B5AE-7CE906B4CA7D}.Release|Any CPU.Build.0 = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+ GlobalSection(ExtensibilityGlobals) = postSolution
+ SolutionGuid = {870D888D-5C8B-4057-8722-F73ECF38E513}
+ EndGlobalSection
+EndGlobal
diff --git a/dotnetv4/EC2/README.md b/dotnetv4/EC2/README.md
new file mode 100644
index 00000000000..06f267e087e
--- /dev/null
+++ b/dotnetv4/EC2/README.md
@@ -0,0 +1,177 @@
+# Amazon EC2 code examples for the SDK for .NET
+
+## Overview
+
+Shows how to use the AWS SDK for .NET to work with Amazon Elastic Compute Cloud (Amazon EC2).
+
+
+
+
+_Amazon EC2 is a web service that provides resizable computing capacity—literally, servers in Amazon's data centers—that you use to build and host your software systems._
+
+## ⚠ Important
+
+* Running this code might result in charges to your AWS account. For more details, see [AWS Pricing](https://aws.amazon.com/pricing/) and [Free Tier](https://aws.amazon.com/free/).
+* Running the tests might result in charges to your AWS account.
+* We recommend that you grant your code least privilege. At most, grant only the minimum permissions required to perform the task. For more information, see [Grant least privilege](https://docs.aws.amazon.com/IAM/latest/UserGuide/best-practices.html#grant-least-privilege).
+* This code is not tested in every AWS Region. For more information, see [AWS Regional Services](https://aws.amazon.com/about-aws/global-infrastructure/regional-product-services).
+
+
+
+
+## Code examples
+
+### Prerequisites
+
+For prerequisites, see the [README](../README.md#Prerequisites) in the `dotnetv3` folder.
+
+
+
+
+
+### Get started
+
+- [Hello Amazon EC2](Actions/HelloEC2.cs#L4) (`DescribeSecurityGroups`)
+
+
+### Basics
+
+Code examples that show you how to perform the essential operations within a service.
+
+- [Learn the basics](Scenarios/EC2_Basics/EC2Basics.cs)
+
+
+### Single actions
+
+Code excerpts that show you how to call individual service functions.
+
+- [AllocateAddress](Actions/EC2Wrapper.cs#L28)
+- [AssociateAddress](Actions/EC2Wrapper.cs#L64)
+- [AuthorizeSecurityGroupIngress](Actions/EC2Wrapper.cs#L107)
+- [CreateKeyPair](Actions/EC2Wrapper.cs#L170)
+- [CreateLaunchTemplate](../cross-service/ResilientService/AutoScalerActions/AutoScalerWrapper.cs#L263)
+- [CreateSecurityGroup](Actions/EC2Wrapper.cs#L242)
+- [DeleteKeyPair](Actions/EC2Wrapper.cs#L319)
+- [DeleteLaunchTemplate](../cross-service/ResilientService/AutoScalerActions/AutoScalerWrapper.cs#L470)
+- [DeleteSecurityGroup](Actions/EC2Wrapper.cs#L361)
+- [DescribeAvailabilityZones](../cross-service/ResilientService/AutoScalerActions/AutoScalerWrapper.cs#L325)
+- [DescribeIamInstanceProfileAssociations](../cross-service/ResilientService/AutoScalerActions/AutoScalerWrapper.cs#L574)
+- [DescribeInstanceTypes](Actions/EC2Wrapper.cs#L531)
+- [DescribeInstances](Actions/EC2Wrapper.cs#L474)
+- [DescribeKeyPairs](Actions/EC2Wrapper.cs#L578)
+- [DescribeSecurityGroups](Actions/EC2Wrapper.cs#L620)
+- [DescribeSubnets](../cross-service/ResilientService/AutoScalerActions/AutoScalerWrapper.cs#L422)
+- [DescribeVpcs](../cross-service/ResilientService/AutoScalerActions/AutoScalerWrapper.cs#L386)
+- [DisassociateAddress](Actions/EC2Wrapper.cs#L714)
+- [RebootInstances](Actions/EC2Wrapper.cs#L761)
+- [ReleaseAddress](Actions/EC2Wrapper.cs#L802)
+- [ReplaceIamInstanceProfileAssociation](../cross-service/ResilientService/AutoScalerActions/AutoScalerWrapper.cs#L611)
+- [RunInstances](Actions/EC2Wrapper.cs#L837)
+- [StartInstances](Actions/EC2Wrapper.cs#L890)
+- [StopInstances](Actions/EC2Wrapper.cs#L930)
+- [TerminateInstances](Actions/EC2Wrapper.cs#L971)
+
+### Scenarios
+
+Code examples that show you how to accomplish a specific task by calling multiple
+functions within the same service.
+
+- [Build and manage a resilient service](../cross-service/ResilientService/ResilientServiceWorkflow/ResilientServiceWorkflow.cs)
+
+
+
+
+
+## Run the examples
+
+### Instructions
+
+For general instructions to run the examples, see the
+[README](../README.md#building-and-running-the-code-examples) in the `dotnetv3` folder.
+
+Some projects might include a settings.json file. Before compiling the project,
+you can change these values to match your own account and resources. Alternatively,
+add a settings.local.json file with your local settings, which will be loaded automatically
+when the application runs.
+
+After the example compiles, you can run it from the command line. To do so, navigate to
+the folder that contains the .csproj file and run the following command:
+
+```
+dotnet run
+```
+
+Alternatively, you can run the example from within your IDE.
+
+
+
+
+
+#### Hello Amazon EC2
+
+This example shows you how to get started using Amazon EC2.
+
+
+#### Learn the basics
+
+This example shows you how to do the following:
+
+- Create a key pair and security group.
+- Select an Amazon Machine Image (AMI) and compatible instance type, then create an instance.
+- Stop and restart the instance.
+- Associate an Elastic IP address with your instance.
+- Connect to your instance with SSH, then clean up resources.
+
+
+
+
+
+
+
+
+
+#### Build and manage a resilient service
+
+This example shows you how to create a load-balanced web service that returns book, movie, and song recommendations. The example shows how the service responds to failures, and how to restructure the service for more resilience when failures occur.
+
+- Use an Amazon EC2 Auto Scaling group to create Amazon Elastic Compute Cloud (Amazon EC2) instances based on a launch template and to keep the number of instances in a specified range.
+- Handle and distribute HTTP requests with Elastic Load Balancing.
+- Monitor the health of instances in an Auto Scaling group and forward requests only to healthy instances.
+- Run a Python web server on each EC2 instance to handle HTTP requests. The web server responds with recommendations and health checks.
+- Simulate a recommendation service with an Amazon DynamoDB table.
+- Control web server response to requests and health checks by updating AWS Systems Manager parameters.
+
+
+
+
+
+
+
+
+### Tests
+
+⚠ Running tests might result in charges to your AWS account.
+
+
+To find instructions for running these tests, see the [README](../README.md#Tests)
+in the `dotnetv3` folder.
+
+
+
+
+
+
+## Additional resources
+
+- [Amazon EC2 User Guide](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/concepts.html)
+- [Amazon EC2 API Reference](https://docs.aws.amazon.com/AWSEC2/latest/APIReference/Welcome.html)
+- [SDK for .NET Amazon EC2 reference](https://docs.aws.amazon.com/sdkfornet/v3/apidocs/items/EC2/NEC2.html)
+
+
+
+
+---
+
+Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
+
+SPDX-License-Identifier: Apache-2.0
\ No newline at end of file
diff --git a/dotnetv4/EC2/Scenarios/EC2_Basics/Basics.csproj b/dotnetv4/EC2/Scenarios/EC2_Basics/Basics.csproj
new file mode 100644
index 00000000000..e6364057c95
--- /dev/null
+++ b/dotnetv4/EC2/Scenarios/EC2_Basics/Basics.csproj
@@ -0,0 +1,21 @@
+
+
+
+ Exe
+ net8.0
+ enable
+ enable
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/dotnetv4/EC2/Scenarios/EC2_Basics/EC2Basics.cs b/dotnetv4/EC2/Scenarios/EC2_Basics/EC2Basics.cs
new file mode 100644
index 00000000000..f17d062d5af
--- /dev/null
+++ b/dotnetv4/EC2/Scenarios/EC2_Basics/EC2Basics.cs
@@ -0,0 +1,326 @@
+// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
+// SPDX-License-Identifier: Apache-2.0
+
+using Ec2_Basics;
+using Microsoft.Extensions.Hosting;
+using Microsoft.Extensions.Logging;
+
+namespace Basics;
+
+// snippet-start:[EC2.dotnetv4.Main]
+///
+/// Show Amazon Elastic Compute Cloud (Amazon EC2) Basics actions.
+///
+public class EC2Basics
+{
+ public static ILogger _logger = null!;
+ public static EC2Wrapper _ec2Wrapper = null!;
+ public static SsmWrapper _ssmWrapper = null!;
+ public static UiMethods _uiMethods = null!;
+
+ public static string associationId = null!;
+ public static string allocationId = null!;
+ public static string instanceId = null!;
+ public static string keyPairName = null!;
+ public static string groupName = null!;
+ public static string tempFileName = null!;
+ public static string secGroupId = null!;
+ public static bool isInteractive = true;
+
+ ///
+ /// Perform the actions defined for the Amazon EC2 Basics scenario.
+ ///
+ /// Command line arguments.
+ /// A Task object.
+ public static async Task Main(string[] args)
+ {
+ // Set up dependency injection for Amazon EC2 and Amazon Simple Systems
+ // Management (Amazon SSM) Service.
+ using var host = Microsoft.Extensions.Hosting.Host.CreateDefaultBuilder(args)
+ .ConfigureServices((_, services) =>
+ services.AddAWSService()
+ .AddAWSService()
+ .AddTransient()
+ .AddTransient()
+ )
+ .Build();
+
+ SetUpServices(host);
+
+ var uniqueName = Guid.NewGuid().ToString();
+ keyPairName = "mvp-example-key-pair" + uniqueName;
+ groupName = "ec2-scenario-group" + uniqueName;
+ var groupDescription = "A security group created for the EC2 Basics scenario.";
+
+ try
+ {
+ // Start the scenario.
+ _uiMethods.DisplayOverview();
+ _uiMethods.PressEnter(isInteractive);
+
+ // Create the key pair.
+ _uiMethods.DisplayTitle("Create RSA key pair");
+ Console.Write("Let's create an RSA key pair that you can be use to ");
+ Console.WriteLine("securely connect to your EC2 instance.");
+ var keyPair = await _ec2Wrapper.CreateKeyPair(keyPairName);
+
+ // Save key pair information to a temporary file.
+ tempFileName = _ec2Wrapper.SaveKeyPair(keyPair);
+
+ Console.WriteLine(
+ $"Created the key pair: {keyPair.KeyName} and saved it to: {tempFileName}");
+ string? answer = "";
+ if (isInteractive)
+ {
+ do
+ {
+ Console.Write("Would you like to list your existing key pairs? ");
+ answer = Console.ReadLine();
+ } while (answer!.ToLower() != "y" && answer.ToLower() != "n");
+ }
+
+ if (!isInteractive || answer == "y")
+ {
+ // List existing key pairs.
+ _uiMethods.DisplayTitle("Existing key pairs");
+
+ // Passing an empty string to the DescribeKeyPairs method will return
+ // a list of all existing key pairs.
+ var keyPairs = await _ec2Wrapper.DescribeKeyPairs("");
+ keyPairs.ForEach(kp =>
+ {
+ Console.WriteLine(
+ $"{kp.KeyName} created at: {kp.CreateTime} Fingerprint: {kp.KeyFingerprint}");
+ });
+ }
+
+ _uiMethods.PressEnter(isInteractive);
+
+ // Create the security group.
+ Console.WriteLine(
+ "Let's create a security group to manage access to your instance.");
+ secGroupId = await _ec2Wrapper.CreateSecurityGroup(groupName, groupDescription);
+ Console.WriteLine(
+ "Let's add rules to allow all HTTP and HTTPS inbound traffic and to allow SSH only from your current IP address.");
+
+ _uiMethods.DisplayTitle("Security group information");
+ var secGroups = await _ec2Wrapper.DescribeSecurityGroups(secGroupId);
+
+ Console.WriteLine($"Created security group {groupName} in your default VPC.");
+ secGroups.ForEach(group =>
+ {
+ _ec2Wrapper.DisplaySecurityGroupInfoAsync(group);
+ });
+ _uiMethods.PressEnter(isInteractive);
+
+ Console.WriteLine(
+ "Now we'll authorize the security group we just created so that it can");
+ Console.WriteLine("access the EC2 instances you create.");
+ await _ec2Wrapper.AuthorizeSecurityGroupIngress(groupName);
+
+ secGroups = await _ec2Wrapper.DescribeSecurityGroups(secGroupId);
+ Console.WriteLine($"Now let's look at the permissions again.");
+ secGroups.ForEach(group =>
+ {
+ _ec2Wrapper.DisplaySecurityGroupInfoAsync(group);
+ });
+ _uiMethods.PressEnter(isInteractive);
+
+ // Get list of available Amazon Linux 2 Amazon Machine Images (AMIs).
+ var parameters =
+ await _ssmWrapper.GetParametersByPath(
+ "/aws/service/ami-amazon-linux-latest");
+
+ List imageIds = parameters.Select(param => param.Value).ToList();
+
+ var images = await _ec2Wrapper.DescribeImages(imageIds);
+
+ var i = 1;
+ images.ForEach(image =>
+ {
+ Console.WriteLine($"\t{i++}\t{image.Description}");
+ });
+
+ int choice = 1;
+ bool validNumber = false;
+ if (isInteractive)
+ {
+ do
+ {
+ Console.Write("Please select an image: ");
+ var selImage = Console.ReadLine();
+ validNumber = int.TryParse(selImage, out choice);
+ } while (!validNumber);
+ }
+
+ var selectedImage = images[choice - 1];
+
+ // Display available instance types.
+ _uiMethods.DisplayTitle("Instance Types");
+ var instanceTypes =
+ await _ec2Wrapper.DescribeInstanceTypes(selectedImage.Architecture);
+
+ i = 1;
+ instanceTypes.ForEach(instanceType =>
+ {
+ Console.WriteLine($"\t{i++}\t{instanceType.InstanceType}");
+ });
+ if (isInteractive)
+ {
+ do
+ {
+ Console.Write("Please select an instance type: ");
+ var selImage = Console.ReadLine();
+ validNumber = int.TryParse(selImage, out choice);
+ } while (!validNumber);
+ }
+
+ var selectedInstanceType = instanceTypes[choice - 1].InstanceType;
+
+ // Create an EC2 instance.
+ _uiMethods.DisplayTitle("Creating an EC2 Instance");
+ instanceId = await _ec2Wrapper.RunInstances(selectedImage.ImageId,
+ selectedInstanceType, keyPairName, secGroupId);
+
+ _uiMethods.PressEnter(isInteractive);
+
+ var instance = await _ec2Wrapper.DescribeInstance(instanceId);
+ _uiMethods.DisplayTitle("New Instance Information");
+ _ec2Wrapper.DisplayInstanceInformation(instance);
+
+ Console.WriteLine(
+ "\nYou can use SSH to connect to your instance. For example:");
+ Console.WriteLine(
+ $"\tssh -i {tempFileName} ec2-user@{instance.PublicIpAddress}");
+
+ _uiMethods.PressEnter(isInteractive);
+
+ Console.WriteLine(
+ "Now we'll stop the instance and then start it again to see what's changed.");
+
+ await _ec2Wrapper.StopInstances(instanceId);
+
+ Console.WriteLine("Now let's start it up again.");
+ await _ec2Wrapper.StartInstances(instanceId);
+
+ Console.WriteLine("\nLet's see what changed.");
+
+ instance = await _ec2Wrapper.DescribeInstance(instanceId);
+ _uiMethods.DisplayTitle("New Instance Information");
+ _ec2Wrapper.DisplayInstanceInformation(instance);
+
+ Console.WriteLine("\nNotice the change in the SSH information:");
+ Console.WriteLine(
+ $"\tssh -i {tempFileName} ec2-user@{instance.PublicIpAddress}");
+
+ _uiMethods.PressEnter(isInteractive);
+
+ Console.WriteLine(
+ "Now we will stop the instance again. Then we will create and associate an");
+ Console.WriteLine("Elastic IP address to use with our instance.");
+
+ await _ec2Wrapper.StopInstances(instanceId);
+ _uiMethods.PressEnter(isInteractive);
+
+ _uiMethods.DisplayTitle("Allocate Elastic IP address");
+ Console.WriteLine(
+ "You can allocate an Elastic IP address and associate it with your instance\nto keep a consistent IP address even when your instance restarts.");
+ var allocationResponse = await _ec2Wrapper.AllocateAddress();
+ allocationId = allocationResponse.AllocationId;
+ Console.WriteLine(
+ "Now we will associate the Elastic IP address with our instance.");
+ associationId = await _ec2Wrapper.AssociateAddress(allocationId, instanceId);
+
+ // Start the instance again.
+ Console.WriteLine("Now let's start the instance again.");
+ await _ec2Wrapper.StartInstances(instanceId);
+
+ Console.WriteLine("\nLet's see what changed.");
+
+ instance = await _ec2Wrapper.DescribeInstance(instanceId);
+ _uiMethods.DisplayTitle("Instance information");
+ _ec2Wrapper.DisplayInstanceInformation(instance);
+
+ Console.WriteLine("\nHere is the SSH information:");
+ Console.WriteLine(
+ $"\tssh -i {tempFileName} ec2-user@{instance.PublicIpAddress}");
+
+ Console.WriteLine("Let's stop and start the instance again.");
+ _uiMethods.PressEnter(isInteractive);
+
+ await _ec2Wrapper.StopInstances(instanceId);
+
+ Console.WriteLine("\nThe instance has stopped.");
+
+ Console.WriteLine("Now let's start it up again.");
+ await _ec2Wrapper.StartInstances(instanceId);
+
+ instance = await _ec2Wrapper.DescribeInstance(instanceId);
+ _uiMethods.DisplayTitle("New Instance Information");
+ _ec2Wrapper.DisplayInstanceInformation(instance);
+ Console.WriteLine("Note that the IP address did not change this time.");
+ _uiMethods.PressEnter(isInteractive);
+
+ await Cleanup();
+ }
+ catch (Exception ex)
+ {
+ _logger.LogError(ex, "There was a problem with the scenario, starting cleanup.");
+ await Cleanup();
+ }
+
+ _uiMethods.DisplayTitle("EC2 Basics Scenario completed.");
+ _uiMethods.PressEnter(isInteractive);
+ }
+
+ ///
+ /// Set up the services and logging.
+ ///
+ ///
+ public static void SetUpServices(IHost host)
+ {
+ var loggerFactory = LoggerFactory.Create(builder =>
+ {
+ builder.AddConsole();
+ });
+ _logger = new Logger(loggerFactory);
+
+ // Now the client is available for injection.
+ _ec2Wrapper = host.Services.GetRequiredService();
+ _ssmWrapper = host.Services.GetRequiredService();
+ _uiMethods = new UiMethods();
+ }
+
+ ///
+ /// Clean up any resources from the scenario.
+ ///
+ ///
+ public static async Task Cleanup()
+ {
+ _uiMethods.DisplayTitle("Clean up resources");
+ Console.WriteLine("Now let's clean up the resources we created.");
+
+ Console.WriteLine("Disassociate the Elastic IP address and release it.");
+ // Disassociate the Elastic IP address.
+ await _ec2Wrapper.DisassociateIp(associationId);
+
+ // Delete the Elastic IP address.
+ await _ec2Wrapper.ReleaseAddress(allocationId);
+
+ // Terminate the instance.
+ Console.WriteLine("Terminating the instance we created.");
+ await _ec2Wrapper.TerminateInstances(instanceId);
+
+ // Delete the security group.
+ Console.WriteLine($"Deleting the Security Group: {groupName}.");
+ await _ec2Wrapper.DeleteSecurityGroup(secGroupId);
+
+ // Delete the RSA key pair.
+ Console.WriteLine($"Deleting the key pair: {keyPairName}");
+ await _ec2Wrapper.DeleteKeyPair(keyPairName);
+ Console.WriteLine("Deleting the temporary file with the key information.");
+ _ec2Wrapper.DeleteTempFile(tempFileName);
+ _uiMethods.PressEnter(isInteractive);
+ }
+}
+// snippet-end:[EC2.dotnetv4.Main]
\ No newline at end of file
diff --git a/dotnetv4/EC2/Scenarios/EC2_Basics/UIMethods.cs b/dotnetv4/EC2/Scenarios/EC2_Basics/UIMethods.cs
new file mode 100644
index 00000000000..2c3d7f7f882
--- /dev/null
+++ b/dotnetv4/EC2/Scenarios/EC2_Basics/UIMethods.cs
@@ -0,0 +1,59 @@
+// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
+// SPDX-License-Identifier: Apache-2.0
+
+namespace Ec2_Basics;
+
+public class UiMethods
+{
+ public readonly string SepBar = new string('-', 88);
+
+ ///
+ /// Show information about the scenario.
+ ///
+ public void DisplayOverview()
+ {
+ DisplayTitle("Welcome to the Amazon Elastic Compute Cloud (Amazon EC2) get started with instances demo.");
+
+ Console.WriteLine("This example application does the following:");
+ Console.WriteLine("\t 1. Creates an RSA key pair.");
+ Console.WriteLine("\t 2. Saves the key pair to a file in a temporary folder.");
+ Console.WriteLine("\t 3. Creates a security group with an inbound rule allowing this computer to SSH to the security group.");
+ Console.WriteLine("\t 4. Displays information for the security group.");
+ Console.WriteLine("\t 5. Gets a list of Amazon Linux 2 Amazon Machine Images (AMIs)\n\t\tand selects one.");
+ Console.WriteLine("\t 6. Gets a list of instance types and selects one.");
+ Console.WriteLine("\t 8. Creates an EC2 instance.");
+ Console.WriteLine("\t 9. Waits for the instance to be ready and displays its information.");
+ Console.WriteLine("\t10. Displays the SSH connection information.");
+ Console.WriteLine("\t11. Stops the instance.");
+ Console.WriteLine("\t12. Starts the instance again.");
+ Console.WriteLine("\t13. Displays the SSH connection information again to show that it has changed.");
+ Console.WriteLine("\t14. Allocates an Elastic IP and associates it to the instance.");
+ Console.WriteLine("\t15. Displays the SSH connection information for the instance.");
+ Console.WriteLine("\t16. Disassociates the Elastic IP and deletes it.");
+ Console.WriteLine("\t17. Terminates the instance and waits for termination to be complete.");
+ Console.WriteLine("\t18. Deletes the security group.");
+ Console.WriteLine("\t19. Deletes the key pair.");
+ }
+
+ ///
+ /// Display a message and wait until the user presses enter.
+ ///
+ public void PressEnter(bool interactive)
+ {
+ Console.Write("\nPlease press to continue. ");
+ if (interactive)
+ _ = Console.ReadLine();
+ }
+
+ ///
+ /// Display a line of hyphens, the text of the title and another
+ /// line of hyphens.
+ ///
+ /// The string to be displayed.
+ public void DisplayTitle(string strTitle)
+ {
+ Console.WriteLine(SepBar);
+ Console.WriteLine(strTitle);
+ Console.WriteLine(SepBar);
+ }
+}
\ No newline at end of file
diff --git a/dotnetv4/EC2/Scenarios/EC2_Basics/Usings.cs b/dotnetv4/EC2/Scenarios/EC2_Basics/Usings.cs
new file mode 100644
index 00000000000..900533b5c09
--- /dev/null
+++ b/dotnetv4/EC2/Scenarios/EC2_Basics/Usings.cs
@@ -0,0 +1,7 @@
+// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
+// SPDX-License-Identifier: Apache-2.0
+
+global using Amazon.EC2;
+global using Amazon.SimpleSystemsManagement;
+global using EC2Actions;
+global using Microsoft.Extensions.DependencyInjection;
\ No newline at end of file
diff --git a/dotnetv4/EC2/Tests/EC2Tests.csproj b/dotnetv4/EC2/Tests/EC2Tests.csproj
new file mode 100644
index 00000000000..0f1dcab08e3
--- /dev/null
+++ b/dotnetv4/EC2/Tests/EC2Tests.csproj
@@ -0,0 +1,43 @@
+
+
+
+ net8.0
+ enable
+ enable
+
+ false
+
+
+
+
+
+
+
+
+
+
+ runtime; build; native; contentfiles; analyzers; buildtransitive
+ all
+
+
+ runtime; build; native; contentfiles; analyzers; buildtransitive
+ all
+
+
+
+
+
+ PreserveNewest
+
+
+ PreserveNewest
+ testsettings.json
+
+
+
+
+
+
+
+
+
diff --git a/dotnetv4/EC2/Tests/EC2WrapperTests.cs b/dotnetv4/EC2/Tests/EC2WrapperTests.cs
new file mode 100644
index 00000000000..f915cebc8c8
--- /dev/null
+++ b/dotnetv4/EC2/Tests/EC2WrapperTests.cs
@@ -0,0 +1,79 @@
+// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
+// SPDX-License-Identifier: Apache-2.0
+
+using Basics;
+using Microsoft.Extensions.Logging;
+using Moq;
+
+namespace EC2Tests;
+///
+/// Integration tests for the Amazon Elastic Compute Cloud (Amazon EC2)
+/// Basics scenario.
+///
+public class EC2WrapperTests
+{
+ private AmazonEC2Client _client = null!;
+ private EC2Wrapper _ec2Wrapper = null!;
+ private SsmWrapper _ssmWrapper = null!;
+
+ ///
+ /// Verifies the scenario with an integration test. No errors should be logged.
+ ///
+ /// Async task.
+ [Fact]
+ [Trait("Category", "Integration")]
+ public async Task TestScenario()
+ {
+ // Arrange.
+ EC2Basics.isInteractive = false;
+ var loggerScenarioMock = new Mock>();
+ var loggerWrapperMock = new Mock>();
+
+ _client = new AmazonEC2Client();
+
+ _ec2Wrapper = new EC2Wrapper(_client, loggerWrapperMock.Object);
+ _ssmWrapper = new SsmWrapper(new AmazonSimpleSystemsManagementClient());
+
+ EC2Basics._ec2Wrapper = _ec2Wrapper;
+ EC2Basics._ssmWrapper = _ssmWrapper;
+ EC2Basics._logger = loggerScenarioMock.Object;
+
+ loggerScenarioMock.Setup(logger => logger.Log(
+ It.Is(logLevel => logLevel == LogLevel.Error),
+ It.IsAny(),
+ It.Is((@object, @type) => true),
+ It.IsAny(),
+ It.IsAny>()
+ ));
+
+ loggerWrapperMock.Setup(logger => logger.Log(
+ It.Is(logLevel => logLevel == LogLevel.Error),
+ It.IsAny(),
+ It.Is((@object, @type) => true),
+ It.IsAny(),
+ It.IsAny>()
+ ));
+
+ // Act.
+ await EC2Basics.Main(new string[] { "" });
+
+ // Assert no exceptions or errors logged.
+ loggerScenarioMock.Verify(
+ logger => logger.Log(
+ It.Is(logLevel => logLevel == LogLevel.Error),
+ It.IsAny(),
+ It.Is((@object, @type) => true),
+ It.IsAny(),
+ It.IsAny>()),
+ Times.Never);
+
+ loggerWrapperMock.Verify(
+ logger => logger.Log(
+ It.Is(logLevel => logLevel == LogLevel.Error),
+ It.IsAny(),
+ It.Is((@object, @type) => true),
+ It.IsAny(),
+ It.IsAny>()),
+ Times.Never);
+ }
+}
\ No newline at end of file
diff --git a/dotnetv4/EC2/Tests/Usings.cs b/dotnetv4/EC2/Tests/Usings.cs
new file mode 100644
index 00000000000..f71c1b687e9
--- /dev/null
+++ b/dotnetv4/EC2/Tests/Usings.cs
@@ -0,0 +1,11 @@
+// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
+// SPDX-License-Identifier: Apache-2.0
+
+global using Amazon.EC2;
+global using Amazon.SimpleSystemsManagement;
+global using EC2Actions;
+global using Microsoft.Extensions.Configuration;
+global using Xunit;
+
+// Optional.
+[assembly: CollectionBehavior(DisableTestParallelization = true)]
\ No newline at end of file
diff --git a/dotnetv4/EC2/Tests/testsettings.json b/dotnetv4/EC2/Tests/testsettings.json
new file mode 100644
index 00000000000..ffeb7fbf1cd
--- /dev/null
+++ b/dotnetv4/EC2/Tests/testsettings.json
@@ -0,0 +1,12 @@
+{
+ "GroupDescription": "This is a group created for integration tests.",
+ "GroupName": "test-group-name",
+ "ImageId": "ami-02dd04850de58599e",
+ "InstanceType": "t2.micro",
+ "KeyPairName": "test-example-key-pair",
+ "VpcId": "vpc-1a2b3c4d",
+ "BucketName": "",
+ "SubnetId": "subnet-012345678912345606",
+ "SecurityGroupId": "sg-012345678912345606",
+ "WaitTimeMilliseconds": 300000
+}
From 93485bf7588b43296987786d20c105829eac70c7 Mon Sep 17 00:00:00 2001
From: Rachel Hagerman <110480692+rlhagerm@users.noreply.github.com>
Date: Fri, 24 Jan 2025 11:34:32 -0600
Subject: [PATCH 4/5] Updates for EC2.
---
dotnetv4/EC2/Actions/EC2Wrapper.cs | 198 +++--------------------------
dotnetv4/EC2/Actions/HelloEC2.cs | 2 +-
dotnetv4/EC2/EC2Examples.sln | 12 --
dotnetv4/EC2/README.md | 39 +-----
dotnetv4/EC2/Tests/Usings.cs | 1 -
5 files changed, 20 insertions(+), 232 deletions(-)
diff --git a/dotnetv4/EC2/Actions/EC2Wrapper.cs b/dotnetv4/EC2/Actions/EC2Wrapper.cs
index 691ae97b6be..f96202407ce 100644
--- a/dotnetv4/EC2/Actions/EC2Wrapper.cs
+++ b/dotnetv4/EC2/Actions/EC2Wrapper.cs
@@ -288,34 +288,6 @@ public async Task CreateSecurityGroup(string groupName, string groupDesc
// snippet-end:[EC2.dotnetv4.CreateSecurityGroup]
- // snippet-start:[EC2.dotnetv4.CreateVPC]
- ///
- /// Create a new Amazon EC2 VPC.
- ///
- /// The CIDR block for the new security group.
- /// The VPC Id of the new VPC.
- public async Task 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;
- }
- }
- // snippet-end:[EC2.dotnetv4.CreateVPC]
-
// snippet-start:[EC2.dotnetv4.DeleteKeyPair]
///
/// Delete an Amazon EC2 key pair.
@@ -391,24 +363,6 @@ await _amazonEC2.DeleteSecurityGroupAsync(
}
// snippet-end:[EC2.dotnetv4.DeleteSecurityGroup]
- // snippet-start:[EC2.dotnetv4.DeleteVPC]
- ///
- /// Delete an Amazon EC2 VPC.
- ///
- /// A Boolean value indicating the success of the action.
- public async Task DeleteVpc(string vpcId)
- {
- var request = new DeleteVpcRequest
- {
- VpcId = vpcId,
- };
-
- var response = await _amazonEC2.DeleteVpcAsync(request);
-
- return response.HttpStatusCode == System.Net.HttpStatusCode.OK;
- }
- // snippet-end:[EC2.dotnetv4.DeleteVPC]
-
// snippet-start:[EC2.dotnetv4.DescribeImages]
///
/// Get information about existing Amazon EC2 images.
@@ -428,20 +382,6 @@ public async Task> DescribeImages(List? imageIds)
return response.Images;
}
- ///
- /// Display the information returned by DescribeImages.
- ///
- /// The list of image information to display.
- public void DisplayImageInfo(List images)
- {
- images.ForEach(image =>
- {
- Console.WriteLine($"{image.Name} Created on: {image.CreationDate}");
- });
-
- }
- // snippet-end:[EC2.dotnetv4.DescribeImages]
-
// snippet-start:[EC2.dotnetv4.DescribeInstance]
///
/// Get information about an Amazon EC2 instance.
@@ -471,62 +411,6 @@ public void DisplayInstanceInformation(Instance instance)
}
// snippet-end:[EC2.dotnetv4.DescribeInstance]
- // snippet-start:[EC2.dotnetv4.DescribeInstances]
- ///
- /// Get information about EC2 instances with a particular state.
- ///
- /// The name of the tag to filter on.
- /// The value of the tag to look for.
- /// True if successful.
- public async Task GetInstancesWithState(string state)
- {
- try
- {
- // Filters the results of the instance list.
- var filters = new List
- {
- new Filter
- {
- Name = $"instance-state-name",
- Values = new List { state, },
- },
- };
- var request = new DescribeInstancesRequest { Filters = filters, };
-
- Console.WriteLine($"\nShowing instances with state {state}");
- var paginator = _amazonEC2.Paginators.DescribeInstances(request);
-
- await foreach (var response in paginator.Responses)
- {
- foreach (var reservation in response.Reservations)
- {
- foreach (var instance in reservation.Instances)
- {
- Console.Write($"Instance ID: {instance.InstanceId} ");
- Console.WriteLine($"\tCurrent State: {instance.State.Name}");
- }
- }
- }
-
- return true;
- }
- catch (AmazonEC2Exception ec2Exception)
- {
- if (ec2Exception.ErrorCode == "InvalidParameterValue")
- {
- _logger.LogError(
- $"Invalid parameter value for filtering instances.");
- }
-
- return false;
- }
- catch (Exception ex)
- {
- Console.WriteLine($"Couldn't list instances because: {ex.Message}");
- return false;
- }
- }
- // snippet-end:[EC2.dotnetv4.DescribeInstances]
// snippet-start:[EC2.dotnetv4.DescribeInstanceTypes]
///
@@ -585,6 +469,7 @@ public async Task> DescribeKeyPairs(string keyPairName)
{
try
{
+ var pairs = new List();
var request = new DescribeKeyPairsRequest();
if (!string.IsNullOrEmpty(keyPairName))
{
@@ -595,7 +480,12 @@ public async Task> DescribeKeyPairs(string keyPairName)
}
var response = await _amazonEC2.DescribeKeyPairsAsync(request);
- return response.KeyPairs.ToList();
+ if (response.KeyPairs != null)
+ {
+ pairs = response.KeyPairs.ToList();
+ }
+ return pairs;
+
}
catch (AmazonEC2Exception ec2Exception)
{
@@ -674,36 +564,36 @@ public void DisplaySecurityGroupInfoAsync(SecurityGroup securityGroup)
{
Console.WriteLine($"{securityGroup.GroupName}");
Console.WriteLine("Ingress permissions:");
- securityGroup.IpPermissions.ForEach(permission =>
+ 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} "); });
+ permission.Ipv4Ranges?.ForEach(range => { Console.Write($"{range.CidrIp} "); });
Console.WriteLine($"\n\tIpv6Ranges:");
- permission.Ipv6Ranges.ForEach(range => { Console.Write($"{range.CidrIpv6} "); });
+ permission.Ipv6Ranges?.ForEach(range => { Console.Write($"{range.CidrIpv6} "); });
Console.Write($"\n\tPrefixListIds: ");
- permission.PrefixListIds.ForEach(id => Console.Write($"{id.Id} "));
+ permission.PrefixListIds?.ForEach(id => Console.Write($"{id.Id} "));
Console.WriteLine($"\n\tTo Port: {permission.ToPort}");
});
Console.WriteLine("Egress permissions:");
- securityGroup.IpPermissionsEgress.ForEach(permission =>
+ 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} "); });
+ permission.Ipv4Ranges?.ForEach(range => { Console.Write($"{range.CidrIp} "); });
Console.WriteLine($"\n\tIpv6Ranges:");
- permission.Ipv6Ranges.ForEach(range => { Console.Write($"{range.CidrIpv6} "); });
+ permission.Ipv6Ranges?.ForEach(range => { Console.Write($"{range.CidrIpv6} "); });
Console.Write($"\n\tPrefixListIds: ");
- permission.PrefixListIds.ForEach(id => Console.Write($"{id.Id} "));
+ permission.PrefixListIds?.ForEach(id => Console.Write($"{id.Id} "));
Console.WriteLine($"\n\tTo Port: {permission.ToPort}");
});
@@ -744,60 +634,6 @@ public async Task DisassociateIp(string associationId)
}
// snippet-end:[EC2.dotnetv4.DisassociateAddress]
- // snippet-start:[EC2.dotnetv4.GetAMIList]
- ///
- /// Retrieve a list of available Amazon Linux images.
- ///
- /// A list of image information.
- public async Task> GetEC2AmiList()
- {
- var filter = new Filter { Name = "architecture", Values = new List { "x86_64" } };
- var filters = new List { filter };
- var response = await _amazonEC2.DescribeImagesAsync(new DescribeImagesRequest { Filters = filters });
- return response.Images;
- }
- // snippet-end:[EC2.dotnetv4.GetAMIList]
-
- // snippet-start:[EC2.dotnetv4.RebootInstances]
- ///
- /// Reboot a specific EC2 instance.
- ///
- /// The instance Id of the instance that will be rebooted.
- /// Async Task.
- public async Task RebootInstances(string ec2InstanceId)
- {
- try
- {
- var request = new RebootInstancesRequest
- {
- InstanceIds = new List { ec2InstanceId },
- };
-
- await _amazonEC2.RebootInstancesAsync(request);
-
- // Wait for the instance to be running.
- Console.Write("Waiting for the instance to start.");
- await WaitForInstanceState(ec2InstanceId, InstanceStateName.Running);
-
- return true;
- }
- catch (AmazonEC2Exception ec2Exception)
- {
- if (ec2Exception.ErrorCode == "InvalidInstanceId")
- {
- _logger.LogError(
- $"InstanceId {ec2InstanceId} is invalid, unable to reboot. {ec2Exception.Message}");
- }
- return false;
- }
- catch (Exception ex)
- {
- _logger.LogError(
- $"An error occurred while rebooting the instance {ec2InstanceId}.: {ex.Message}");
- return false;
- }
- }
- // snippet-end:[EC2.dotnetv4.RebootInstances]
// snippet-start:[EC2.dotnetv4.ReleaseAddress]
///
@@ -812,8 +648,8 @@ public async Task ReleaseAddress(string allocationId)
{
var request = new ReleaseAddressRequest { AllocationId = allocationId };
- var response = await _amazonEC2.ReleaseAddressAsync(request);
- return response.HttpStatusCode == HttpStatusCode.OK;
+ await _amazonEC2.ReleaseAddressAsync(request);
+ return true;
}
catch (AmazonEC2Exception ec2Exception)
{
diff --git a/dotnetv4/EC2/Actions/HelloEC2.cs b/dotnetv4/EC2/Actions/HelloEC2.cs
index d6ae2731449..39ef7fc12ac 100644
--- a/dotnetv4/EC2/Actions/HelloEC2.cs
+++ b/dotnetv4/EC2/Actions/HelloEC2.cs
@@ -28,7 +28,7 @@ static async Task Main(string[] args)
try
{
// Retrieve information for up to 10 Amazon EC2 security groups.
- var request = new DescribeSecurityGroupsRequest { MaxResults = 10, };
+ var request = new DescribeSecurityGroupsRequest { MaxResults = 10 };
var securityGroups = new List();
var paginatorForSecurityGroups =
diff --git a/dotnetv4/EC2/EC2Examples.sln b/dotnetv4/EC2/EC2Examples.sln
index aee7d5c12ce..c089758acd4 100644
--- a/dotnetv4/EC2/EC2Examples.sln
+++ b/dotnetv4/EC2/EC2Examples.sln
@@ -12,10 +12,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Basics", "Scenarios\EC2_Bas
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "EC2Tests", "Tests\EC2Tests.csproj", "{6046A2FC-6A39-4C2D-8DD9-AA3740B17B88}"
EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CreateVPCExample", "Scenarios\CreateVPCExample\CreateVPCExample.csproj", "{A0B25DB2-E0BE-409F-950D-19E23FB76319}"
-EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CreateVPCforS3Example", "Scenarios\CreateVPCforS3Example\CreateVPCforS3Example.csproj", "{CB5A94D4-1BA1-41CE-B5AE-7CE906B4CA7D}"
-EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -34,14 +30,6 @@ Global
{6046A2FC-6A39-4C2D-8DD9-AA3740B17B88}.Debug|Any CPU.Build.0 = Debug|Any CPU
{6046A2FC-6A39-4C2D-8DD9-AA3740B17B88}.Release|Any CPU.ActiveCfg = Release|Any CPU
{6046A2FC-6A39-4C2D-8DD9-AA3740B17B88}.Release|Any CPU.Build.0 = Release|Any CPU
- {A0B25DB2-E0BE-409F-950D-19E23FB76319}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {A0B25DB2-E0BE-409F-950D-19E23FB76319}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {A0B25DB2-E0BE-409F-950D-19E23FB76319}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {A0B25DB2-E0BE-409F-950D-19E23FB76319}.Release|Any CPU.Build.0 = Release|Any CPU
- {CB5A94D4-1BA1-41CE-B5AE-7CE906B4CA7D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {CB5A94D4-1BA1-41CE-B5AE-7CE906B4CA7D}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {CB5A94D4-1BA1-41CE-B5AE-7CE906B4CA7D}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {CB5A94D4-1BA1-41CE-B5AE-7CE906B4CA7D}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
diff --git a/dotnetv4/EC2/README.md b/dotnetv4/EC2/README.md
index 06f267e087e..45de83bb0bd 100644
--- a/dotnetv4/EC2/README.md
+++ b/dotnetv4/EC2/README.md
@@ -49,36 +49,20 @@ Code excerpts that show you how to call individual service functions.
- [AssociateAddress](Actions/EC2Wrapper.cs#L64)
- [AuthorizeSecurityGroupIngress](Actions/EC2Wrapper.cs#L107)
- [CreateKeyPair](Actions/EC2Wrapper.cs#L170)
-- [CreateLaunchTemplate](../cross-service/ResilientService/AutoScalerActions/AutoScalerWrapper.cs#L263)
- [CreateSecurityGroup](Actions/EC2Wrapper.cs#L242)
- [DeleteKeyPair](Actions/EC2Wrapper.cs#L319)
-- [DeleteLaunchTemplate](../cross-service/ResilientService/AutoScalerActions/AutoScalerWrapper.cs#L470)
- [DeleteSecurityGroup](Actions/EC2Wrapper.cs#L361)
-- [DescribeAvailabilityZones](../cross-service/ResilientService/AutoScalerActions/AutoScalerWrapper.cs#L325)
-- [DescribeIamInstanceProfileAssociations](../cross-service/ResilientService/AutoScalerActions/AutoScalerWrapper.cs#L574)
- [DescribeInstanceTypes](Actions/EC2Wrapper.cs#L531)
- [DescribeInstances](Actions/EC2Wrapper.cs#L474)
- [DescribeKeyPairs](Actions/EC2Wrapper.cs#L578)
- [DescribeSecurityGroups](Actions/EC2Wrapper.cs#L620)
-- [DescribeSubnets](../cross-service/ResilientService/AutoScalerActions/AutoScalerWrapper.cs#L422)
-- [DescribeVpcs](../cross-service/ResilientService/AutoScalerActions/AutoScalerWrapper.cs#L386)
- [DisassociateAddress](Actions/EC2Wrapper.cs#L714)
-- [RebootInstances](Actions/EC2Wrapper.cs#L761)
- [ReleaseAddress](Actions/EC2Wrapper.cs#L802)
-- [ReplaceIamInstanceProfileAssociation](../cross-service/ResilientService/AutoScalerActions/AutoScalerWrapper.cs#L611)
- [RunInstances](Actions/EC2Wrapper.cs#L837)
- [StartInstances](Actions/EC2Wrapper.cs#L890)
- [StopInstances](Actions/EC2Wrapper.cs#L930)
- [TerminateInstances](Actions/EC2Wrapper.cs#L971)
-### Scenarios
-
-Code examples that show you how to accomplish a specific task by calling multiple
-functions within the same service.
-
-- [Build and manage a resilient service](../cross-service/ResilientService/ResilientServiceWorkflow/ResilientServiceWorkflow.cs)
-
-
@@ -87,7 +71,7 @@ functions within the same service.
### Instructions
For general instructions to run the examples, see the
-[README](../README.md#building-and-running-the-code-examples) in the `dotnetv3` folder.
+[README](../README.md#building-and-running-the-code-examples) in the `dotnetv4` folder.
Some projects might include a settings.json file. Before compiling the project,
you can change these values to match your own account and resources. Alternatively,
@@ -129,32 +113,13 @@ This example shows you how to do the following:
-
-#### Build and manage a resilient service
-
-This example shows you how to create a load-balanced web service that returns book, movie, and song recommendations. The example shows how the service responds to failures, and how to restructure the service for more resilience when failures occur.
-
-- Use an Amazon EC2 Auto Scaling group to create Amazon Elastic Compute Cloud (Amazon EC2) instances based on a launch template and to keep the number of instances in a specified range.
-- Handle and distribute HTTP requests with Elastic Load Balancing.
-- Monitor the health of instances in an Auto Scaling group and forward requests only to healthy instances.
-- Run a Python web server on each EC2 instance to handle HTTP requests. The web server responds with recommendations and health checks.
-- Simulate a recommendation service with an Amazon DynamoDB table.
-- Control web server response to requests and health checks by updating AWS Systems Manager parameters.
-
-
-
-
-
-
-
-
### Tests
⚠ Running tests might result in charges to your AWS account.
To find instructions for running these tests, see the [README](../README.md#Tests)
-in the `dotnetv3` folder.
+in the `dotnetv4` folder.
diff --git a/dotnetv4/EC2/Tests/Usings.cs b/dotnetv4/EC2/Tests/Usings.cs
index f71c1b687e9..ca8cbead821 100644
--- a/dotnetv4/EC2/Tests/Usings.cs
+++ b/dotnetv4/EC2/Tests/Usings.cs
@@ -4,7 +4,6 @@
global using Amazon.EC2;
global using Amazon.SimpleSystemsManagement;
global using EC2Actions;
-global using Microsoft.Extensions.Configuration;
global using Xunit;
// Optional.
From 0f0dab744c3c3d7183cef18d25abb45d67275e73 Mon Sep 17 00:00:00 2001
From: Rachel Hagerman <110480692+rlhagerm@users.noreply.github.com>
Date: Fri, 24 Jan 2025 11:48:49 -0600
Subject: [PATCH 5/5] Add missing tag.
---
dotnetv4/EC2/Actions/EC2Wrapper.cs | 1 +
1 file changed, 1 insertion(+)
diff --git a/dotnetv4/EC2/Actions/EC2Wrapper.cs b/dotnetv4/EC2/Actions/EC2Wrapper.cs
index f96202407ce..125a6fe0a6f 100644
--- a/dotnetv4/EC2/Actions/EC2Wrapper.cs
+++ b/dotnetv4/EC2/Actions/EC2Wrapper.cs
@@ -381,6 +381,7 @@ public async Task> DescribeImages(List? imageIds)
var response = await _amazonEC2.DescribeImagesAsync(request);
return response.Images;
}
+ // snippet-end:[EC2.dotnetv4.DescribeImages]
// snippet-start:[EC2.dotnetv4.DescribeInstance]
///