using System; | |
using System.Collections.Generic; | |
using System.Text.Json; | |
using System.Threading.Tasks; | |
using Amazon; | |
using Amazon.EC2; | |
using Amazon.EC2.Model; | |
using Amazon.Runtime; | |
using Amazon.SSO; | |
using Amazon.SSO.Model; | |
namespace AWSRetrieveEC2Instances_SSO | |
{ | |
// Class used to group the information of interest for us in the EC2 instances | |
class EC2InstanceLimitedData | |
{ | |
public string InstanceId { get; set; } | |
public string InstanceType { get; set; } | |
public string PrivateIpAddresses { get; set; } | |
public string PublicIpAddresses { get; set; } | |
public string AWSAccountName { get; set; } | |
public string AWSAccountId { get; set; } | |
} | |
class Program | |
{ | |
static async Task Main(string[] args) | |
{ | |
// ! Update these variables using the instructions at | |
// https://mihai-albert.com/2020/12/13/get-the-list-of-all-aws-ec2-instances-with-all-their-private-and-public-ips/#code-for-retrieving-ec2-instances-using-sso-users | |
string providedAccessToken = | |
""; | |
string ssoRoleName = ""; | |
// Connect to the SSO service. This is where we'll get all the | |
// AWS accounts the access token's owner has access to, and the respective roles within | |
// ! Make sure to use the correct zone here, as per | |
// https://mihai-albert.com/2020/12/13/get-the-list-of-all-aws-ec2-instances-with-all-their-private-and-public-ips/#getting-a-token | |
using (AmazonSSOClient amazonSSOClient = | |
new AmazonSSOClient(new AnonymousAWSCredentials(), RegionEndpoint.USEast2)) | |
{ | |
// The list that will contain all the EC2 instances retrieved from all the accounts | |
List<EC2InstanceLimitedData> ec2Instances = new List<EC2InstanceLimitedData>(); | |
// Cycle through all the AWS accounts where the user has permissions | |
foreach (var awsAccount in await GetAWSAccounts(amazonSSOClient, providedAccessToken)) | |
{ | |
Console.WriteLine($"In account {awsAccount.AccountId}"); | |
var credentialsForRole = await GetEC2ReadAccessRoleCredentials(amazonSSOClient, | |
providedAccessToken, awsAccount.AccountId, ssoRoleName); | |
var enabledRegions = await GetEC2EnabledRegions(credentialsForRole); | |
foreach (var enabledRegion in enabledRegions) | |
{ | |
// Build the EC2 client based on the temporary credentials handed back | |
// by STS when assuming the role in the current account | |
using (var ec2Client = new AmazonEC2Client(new SessionAWSCredentials( | |
credentialsForRole.AccessKeyId, credentialsForRole.SecretAccessKey, | |
credentialsForRole.SessionToken), | |
RegionEndpoint.GetBySystemName(enabledRegion.RegionName))) | |
{ | |
var ec2InstancesForCurrentAccountAndRegion = | |
await GetEC2InstancesForAccountAndRegion(ec2Client, awsAccount); | |
Console.WriteLine( | |
$"{ec2InstancesForCurrentAccountAndRegion.Count} instances found in region {ec2Client.Config.RegionEndpoint.DisplayName}"); | |
ec2Instances.AddRange(ec2InstancesForCurrentAccountAndRegion); | |
} | |
} | |
} | |
Console.WriteLine($"{ec2Instances.Count} EC2 instances retrieved overall"); | |
// Serialize the list of EC2 instance data to json | |
var ec2InstancesAsJson = JsonSerializer.Serialize(ec2Instances, | |
new JsonSerializerOptions() | |
{ | |
WriteIndented = true | |
}); | |
System.IO.File.WriteAllText("awsEC2Instances.json", ec2InstancesAsJson); | |
} | |
} | |
private static async Task<List<AccountInfo>> GetAWSAccounts(AmazonSSOClient amazonSSOClient, | |
string providedAccessToken) | |
{ | |
List<AccountInfo> awsAccounts = new List<AccountInfo>(); | |
string nextToken = null; | |
do | |
{ | |
var listAccountsResponse = await amazonSSOClient.ListAccountsAsync( | |
new ListAccountsRequest | |
{ | |
AccessToken = providedAccessToken, | |
NextToken = nextToken | |
}); | |
awsAccounts.AddRange(listAccountsResponse.AccountList); | |
nextToken = listAccountsResponse.NextToken; | |
} while (nextToken != null); | |
return awsAccounts; | |
} | |
private static async Task<RoleCredentials> GetEC2ReadAccessRoleCredentials(AmazonSSOClient amazonSSOClient, | |
string providedAccessToken, string awsAccountId, string ssoRoleName) | |
{ | |
// Assume the designated role in the current AWS account. The result is that | |
// short-term security credentials are provided back from the STS service | |
var getRoleCredentialsResponse = await amazonSSOClient.GetRoleCredentialsAsync( | |
new GetRoleCredentialsRequest() | |
{ | |
AccessToken = providedAccessToken, | |
AccountId = awsAccountId, | |
RoleName = ssoRoleName | |
}); | |
// Extract the credentials from the response | |
return getRoleCredentialsResponse.RoleCredentials; | |
} | |
private static async Task<List<Region>> GetEC2EnabledRegions(RoleCredentials credentialsForTargetRole) | |
{ | |
// Build a throwaway AmazonEC2Client object just to get the enabled regions. Use | |
// a region that is always enabled (us-east-1) | |
var enabledRegionsResponse = await (new AmazonEC2Client(new SessionAWSCredentials( | |
credentialsForTargetRole.AccessKeyId, credentialsForTargetRole.SecretAccessKey, | |
credentialsForTargetRole.SessionToken), | |
RegionEndpoint.USEast1)).DescribeRegionsAsync(); | |
return enabledRegionsResponse.Regions; | |
} | |
public static async Task<List<EC2InstanceLimitedData>> GetEC2InstancesForAccountAndRegion( | |
AmazonEC2Client ec2Client, AccountInfo awsAccount) | |
{ | |
// Don't get fooled by the fact that the AWS account gets passed | |
// through as parameter. It doesn't act as a "filter" for the EC2 | |
// instance data retrieved, as that's done based off the AmazonEC2Client | |
// object, which is also passed as a parameter (and was previously | |
// built against a a specific account using a specific set of | |
// credentials, and also against a specific region). Its sole purpose | |
// is to be able to add information about the account with each | |
// EC2 instance element in our final list, as extracting the account | |
// info directly from the AmazonEC2Client doesn't look possible | |
// In the EC2 world there are reservations (do not confuse with Reserved Instances) | |
// that refer to a launch event. Within a reservation there can be one or more EC2 | |
// instances (the actual VMs); if a launch fired up multiple instances, then all | |
// those instances will belong to the reservation corresponding to the launch. | |
List<EC2InstanceLimitedData> ec2InstancesPerAccountAndRegion = new List<EC2InstanceLimitedData>(); | |
string nextToken = null; | |
do | |
{ | |
var describeInstancesResult = await ec2Client.DescribeInstancesAsync(new DescribeInstancesRequest() | |
{ | |
NextToken = nextToken | |
}); | |
nextToken = describeInstancesResult.NextToken; | |
// Cycle through all the EC2 instances in the current response | |
foreach (var reservation in describeInstancesResult.Reservations) | |
{ | |
foreach (var instance in reservation.Instances) | |
{ | |
Console.WriteLine($"name={instance.InstanceId} (reservation id= {reservation.ReservationId})"); | |
// Add all the private IPs along with their corresponding public IPs; | |
// iterate through network adapters, then one level down through | |
// each private IP | |
List<string> currentPrivateIPsList = new List<string>(); | |
List<string> currentPublicIPsList = new List<string>(); | |
foreach (var eni in instance.NetworkInterfaces) | |
{ | |
foreach (var privateIP in eni.PrivateIpAddresses) | |
{ | |
currentPrivateIPsList.Add(privateIP.PrivateIpAddress); | |
if (privateIP.Association != null) | |
currentPublicIPsList.Add(privateIP.Association.PublicIp); | |
} | |
} | |
var currentPrivateIPsString = String.Join(",", currentPrivateIPsList.ToArray()); | |
var currentPublicIPsString = String.Join(",", currentPublicIPsList.ToArray()); | |
ec2InstancesPerAccountAndRegion.Add( | |
new EC2InstanceLimitedData() | |
{ | |
AWSAccountId = awsAccount.AccountId, | |
AWSAccountName = awsAccount.AccountName, | |
InstanceId = instance.InstanceId, | |
InstanceType = instance.InstanceType, | |
PrivateIpAddresses = currentPrivateIPsString, | |
PublicIpAddresses = currentPublicIPsString | |
}); | |
} | |
} | |
} while (nextToken != null); | |
return ec2InstancesPerAccountAndRegion; | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment