Skip to content

Instantly share code, notes, and snippets.

@luckerby
Created December 14, 2020 00:39
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save luckerby/c214680ea51eaf310a724496e74cda88 to your computer and use it in GitHub Desktop.
Save luckerby/c214680ea51eaf310a724496e74cda88 to your computer and use it in GitHub Desktop.
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