Skip to content

Commit

Permalink
support multiple aws accounts with credentials
Browse files Browse the repository at this point in the history
  • Loading branch information
Bryan Fang committed Mar 15, 2024
1 parent e7d41d3 commit 39862e0
Show file tree
Hide file tree
Showing 5 changed files with 697 additions and 50 deletions.
11 changes: 11 additions & 0 deletions cmd/aws_credentials.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package cmd

type Account struct {
AwsAccessKeyID string `mapstructure:"aws_access_key_id"`
AwsSecretAccessKey string `mapstructure:"aws_secret_access_key"`
Regions []string `yaml:"regions"`
}

type AWSCredentials struct {
Accounts []Account `yaml:"accounts"`
}
34 changes: 34 additions & 0 deletions cmd/helper.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,11 @@ import (
"context"
"fmt"
"log/slog"
"reflect"

"github.com/aws/aws-sdk-go-v2/aws"
"github.com/aws/aws-sdk-go-v2/config"
"github.com/aws/aws-sdk-go-v2/credentials"
"github.com/aws/aws-sdk-go-v2/credentials/stscreds"
"github.com/aws/aws-sdk-go-v2/feature/ec2/imds"
"github.com/aws/aws-sdk-go-v2/service/sts"
Expand Down Expand Up @@ -54,3 +56,35 @@ func getAWSSessionInformation(cfg aws.Config) (string, string, error) {

return *output.Account, cfg.Region, nil
}

func getAWSConfigurationByCredentials(logger *slog.Logger, configuration exporterConfig) ([]aws.Config, error) {
var configs []aws.Config
accountsFromYaml := configuration.AwsCredentials
if reflect.ValueOf(accountsFromYaml).IsZero() {
logger.Error("AWS accounts not configured in yaml")
return nil, nil
} else {
accounts := accountsFromYaml.Accounts
for _, c := range accounts {
aws_access_key_id := c.AwsAccessKeyID
aws_secret_access_key := c.AwsSecretAccessKey
staticProvider := credentials.NewStaticCredentialsProvider(
aws_access_key_id,
aws_secret_access_key,
"",
)
cfg, err := config.LoadDefaultConfig(
context.Background(),
config.WithCredentialsProvider(staticProvider),
)
if err != nil {
return nil, err
}
for _, region := range c.Regions {
cfg.Region = region
configs = append(configs, cfg)
}
}
}
return configs, nil
}
84 changes: 59 additions & 25 deletions cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ const (
)

var cfgFile string
var actRegionClient []exporter.AccountRegionClients

type exporterConfig struct {
Debug bool `mapstructure:"debug"`
Expand All @@ -46,6 +47,7 @@ type exporterConfig struct {
CollectQuotas bool `mapstructure:"collect-quotas"`
CollectUsages bool `mapstructure:"collect-usages"`
OTELTracesEnabled bool `mapstructure:"enable-otel-traces"`
AwsCredentials AWSCredentials
}

func run(configuration exporterConfig) {
Expand All @@ -54,24 +56,6 @@ func run(configuration exporterConfig) {
fmt.Println("ERROR: Fail to initialize logger: %w", err)
panic(err)
}

cfg, err := getAWSConfiguration(logger, configuration.AWSAssumeRoleArn, configuration.AWSAssumeRoleSession)
if err != nil {
logger.Error("can't initialize AWS configuration", "reason", err)
os.Exit(awsErrorExitCode)
}

awsAccountID, awsRegion, err := getAWSSessionInformation(cfg)
if err != nil {
logger.Error("can't identify AWS account and/or region", "reason", err)
os.Exit(awsErrorExitCode)
}

rdsClient := rds.NewFromConfig(cfg)
ec2Client := ec2.NewFromConfig(cfg)
cloudWatchClient := cloudwatch.NewFromConfig(cfg)
servicequotasClient := servicequotas.NewFromConfig(cfg)

collectorConfiguration := exporter.Configuration{
CollectInstanceMetrics: configuration.CollectInstanceMetrics,
CollectInstanceTypes: configuration.CollectInstanceTypes,
Expand All @@ -82,16 +66,65 @@ func run(configuration exporterConfig) {
CollectUsages: configuration.CollectUsages,
}

collector := exporter.NewCollector(*logger, collectorConfiguration, awsAccountID, awsRegion, rdsClient, ec2Client, cloudWatchClient, servicequotasClient)
cfgs, err := getAWSConfigurationByCredentials(logger, configuration)
if err != nil {
logger.Error("can't initialize AWS configuration", "reason", err)
os.Exit(awsErrorExitCode)
}
if cfgs == nil {
logger.Info("Didn't configure aws IAM User credentials in configuration file, will use default aws configuration")
cfg, err := getAWSConfiguration(logger, configuration.AWSAssumeRoleArn, configuration.AWSAssumeRoleSession)
if err != nil {
logger.Error("can't initialize AWS configuration", "reason", err)
os.Exit(awsErrorExitCode)
}
awsAccountID, awsRegion, err := getAWSSessionInformation(cfg)
if err != nil {
logger.Error("can't identify AWS account and/or region", "reason", err)
os.Exit(awsErrorExitCode)
}

rdsClient := rds.NewFromConfig(cfg)
ec2Client := ec2.NewFromConfig(cfg)
cloudWatchClient := cloudwatch.NewFromConfig(cfg)
servicequotasClient := servicequotas.NewFromConfig(cfg)

collector := exporter.NewCollector(*logger, collectorConfiguration, awsAccountID, awsRegion, rdsClient, ec2Client, cloudWatchClient, servicequotasClient)

prometheus.MustRegister(collector)

prometheus.MustRegister(collector)
} else {
for _, cfg := range cfgs {
awsAccountID, awsRegion, err := getAWSSessionInformation(cfg)
if err != nil {
logger.Error("can't identify AWS account and/or region", "reason", err)
os.Exit(awsErrorExitCode)
}

rdsClient := rds.NewFromConfig(cfg)
ec2Client := ec2.NewFromConfig(cfg)
cloudWatchClient := cloudwatch.NewFromConfig(cfg)
servicequotasClient := servicequotas.NewFromConfig(cfg)

var accountRegionClients exporter.AccountRegionClients
accountRegionClients.AwsAccountID = awsAccountID
accountRegionClients.AwsRegion = awsRegion
accountRegionClients.RdsClient = rdsClient
accountRegionClients.Ec2Client = ec2Client
accountRegionClients.CloudWatchClient = cloudWatchClient
accountRegionClients.ServicequotasClient = servicequotasClient
actRegionClient = append(actRegionClient, accountRegionClients)
}
collector := exporter.NewMultiCollector(*logger, collectorConfiguration, actRegionClient)
prometheus.MustRegister(collector)
}

// http configurations for exporter service
serverConfiguration := http.Config{
ListenAddress: configuration.ListenAddress,
MetricPath: configuration.MetricPath,
TLSCertPath: configuration.TLSCertPath,
TLSKeyPath: configuration.TLSKeyPath,
OTELTracesEnabled: configuration.OTELTracesEnabled,
ListenAddress: configuration.ListenAddress,
MetricPath: configuration.MetricPath,
TLSCertPath: configuration.TLSCertPath,
TLSKeyPath: configuration.TLSKeyPath,
}

server := http.New(*logger, serverConfiguration)
Expand All @@ -118,6 +151,7 @@ func NewRootCommand() (*cobra.Command, error) {

return
}
viper.UnmarshalKey("accounts", &c.AwsCredentials.Accounts)
run(c)
},
}
Expand Down
7 changes: 7 additions & 0 deletions configs/prometheus-rds-exporter/prometheus-rds-exporter.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,13 @@
#
# AWS credentials
#
# accounts:
# - aws_access_key_id: <placeholder>
# aws_secret_access_key: <placeholder>
# regions:
# - ap-northeast-1
# - eu-central-1
# - us-east-1

# AWS IAM ARN role to assume to fetch metrics
# aws-assume-role-arn: arn:aws:iam::000000000000:role/prometheus-rds-exporter
Expand Down
Loading

0 comments on commit 39862e0

Please sign in to comment.