terraform/builtin/providers/aws/config.go

275 lines
8.2 KiB
Go
Raw Normal View History

2014-06-24 04:01:57 +02:00
package aws
import (
2014-07-30 00:22:37 +02:00
"fmt"
"log"
2015-04-20 00:54:42 +02:00
"strings"
2014-06-24 04:01:57 +02:00
"github.com/hashicorp/go-multierror"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/awserr"
"github.com/aws/aws-sdk-go/aws/credentials"
"github.com/aws/aws-sdk-go/service/autoscaling"
"github.com/aws/aws-sdk-go/service/cloudwatch"
2015-09-16 23:02:28 +02:00
"github.com/aws/aws-sdk-go/service/cloudwatchlogs"
2015-06-04 02:12:41 +02:00
"github.com/aws/aws-sdk-go/service/dynamodb"
"github.com/aws/aws-sdk-go/service/ec2"
"github.com/aws/aws-sdk-go/service/ecs"
2015-06-02 22:19:50 +02:00
"github.com/aws/aws-sdk-go/service/efs"
"github.com/aws/aws-sdk-go/service/elasticache"
elasticsearch "github.com/aws/aws-sdk-go/service/elasticsearchservice"
"github.com/aws/aws-sdk-go/service/elb"
"github.com/aws/aws-sdk-go/service/iam"
"github.com/aws/aws-sdk-go/service/kinesis"
2015-06-09 21:12:47 +02:00
"github.com/aws/aws-sdk-go/service/lambda"
"github.com/aws/aws-sdk-go/service/opsworks"
"github.com/aws/aws-sdk-go/service/rds"
"github.com/aws/aws-sdk-go/service/route53"
"github.com/aws/aws-sdk-go/service/s3"
"github.com/aws/aws-sdk-go/service/sns"
"github.com/aws/aws-sdk-go/service/sqs"
2014-06-24 04:01:57 +02:00
)
type Config struct {
AccessKey string
SecretKey string
Token string
Region string
MaxRetries int
2015-04-20 00:54:42 +02:00
AllowedAccountIds []interface{}
ForbiddenAccountIds []interface{}
DynamoDBEndpoint string
}
type AWSClient struct {
2015-09-16 23:02:28 +02:00
cloudwatchconn *cloudwatch.CloudWatch
cloudwatchlogsconn *cloudwatchlogs.CloudWatchLogs
dynamodbconn *dynamodb.DynamoDB
ec2conn *ec2.EC2
ecsconn *ecs.ECS
2015-06-02 22:19:50 +02:00
efsconn *efs.EFS
2015-09-16 23:02:28 +02:00
elbconn *elb.ELB
esconn *elasticsearch.ElasticsearchService
2015-09-16 23:02:28 +02:00
autoscalingconn *autoscaling.AutoScaling
s3conn *s3.S3
sqsconn *sqs.SQS
snsconn *sns.SNS
r53conn *route53.Route53
region string
rdsconn *rds.RDS
iamconn *iam.IAM
kinesisconn *kinesis.Kinesis
elasticacheconn *elasticache.ElastiCache
lambdaconn *lambda.Lambda
2015-10-07 22:35:06 +02:00
opsworksconn *opsworks.OpsWorks
}
// Client configures and returns a fully initialized AWSClient
func (c *Config) Client() (interface{}, error) {
var client AWSClient
// Get the auth and region. This can fail if keys/regions were not
// specified and we're attempting to use the environment.
var errs []error
log.Println("[INFO] Building AWS region structure")
2015-03-13 15:42:50 +01:00
err := c.ValidateRegion()
if err != nil {
errs = append(errs, err)
}
if len(errs) == 0 {
// store AWS region in client struct, for region specific operations such as
// bucket storage in S3
client.region = c.Region
2015-02-20 15:55:54 +01:00
2015-03-13 15:42:50 +01:00
log.Println("[INFO] Building AWS auth structure")
2015-06-29 07:46:49 +02:00
// We fetched all credential sources in Provider. If they are
// available, they'll already be in c. See Provider definition.
creds := credentials.NewStaticCredentials(c.AccessKey, c.SecretKey, c.Token)
awsConfig := &aws.Config{
Credentials: creds,
Region: aws.String(c.Region),
MaxRetries: aws.Int(c.MaxRetries),
}
log.Println("[INFO] Initializing IAM Connection")
client.iamconn = iam.New(awsConfig)
err := c.ValidateCredentials(client.iamconn)
if err != nil {
errs = append(errs, err)
}
awsDynamoDBConfig := &aws.Config{
Credentials: creds,
Region: aws.String(c.Region),
MaxRetries: aws.Int(c.MaxRetries),
Endpoint: aws.String(c.DynamoDBEndpoint),
}
// Some services exist only in us-east-1, e.g. because they manage
// resources that can span across multiple regions, or because
// signature format v4 requires region to be us-east-1 for global
// endpoints:
// http://docs.aws.amazon.com/general/latest/gr/sigv4_changes.html
usEast1AwsConfig := &aws.Config{
Credentials: creds,
Region: aws.String("us-east-1"),
MaxRetries: aws.Int(c.MaxRetries),
}
2015-06-04 02:05:02 +02:00
log.Println("[INFO] Initializing DynamoDB connection")
client.dynamodbconn = dynamodb.New(awsDynamoDBConfig)
2015-06-04 02:05:02 +02:00
log.Println("[INFO] Initializing ELB connection")
client.elbconn = elb.New(awsConfig)
log.Println("[INFO] Initializing S3 connection")
client.s3conn = s3.New(awsConfig)
2015-02-20 15:55:54 +01:00
2015-05-12 23:34:10 +02:00
log.Println("[INFO] Initializing SQS connection")
client.sqsconn = sqs.New(awsConfig)
2015-05-15 01:17:18 +02:00
log.Println("[INFO] Initializing SNS connection")
client.snsconn = sns.New(awsConfig)
log.Println("[INFO] Initializing RDS Connection")
client.rdsconn = rds.New(awsConfig)
log.Println("[INFO] Initializing Kinesis Connection")
client.kinesisconn = kinesis.New(awsConfig)
authErr := c.ValidateAccountId(client.iamconn)
if authErr != nil {
errs = append(errs, authErr)
2015-04-20 00:54:42 +02:00
}
log.Println("[INFO] Initializing AutoScaling connection")
client.autoscalingconn = autoscaling.New(awsConfig)
log.Println("[INFO] Initializing EC2 Connection")
client.ec2conn = ec2.New(awsConfig)
log.Println("[INFO] Initializing ECS Connection")
client.ecsconn = ecs.New(awsConfig)
2015-06-02 22:19:50 +02:00
log.Println("[INFO] Initializing EFS Connection")
client.efsconn = efs.New(awsConfig)
log.Println("[INFO] Initializing ElasticSearch Connection")
client.esconn = elasticsearch.New(awsConfig)
2015-04-16 22:18:01 +02:00
log.Println("[INFO] Initializing Route 53 connection")
client.r53conn = route53.New(usEast1AwsConfig)
2015-04-26 03:53:21 +02:00
log.Println("[INFO] Initializing Elasticache Connection")
client.elasticacheconn = elasticache.New(awsConfig)
log.Println("[INFO] Initializing Lambda Connection")
client.lambdaconn = lambda.New(awsConfig)
log.Println("[INFO] Initializing CloudWatch SDK connection")
client.cloudwatchconn = cloudwatch.New(awsConfig)
2015-09-16 23:02:28 +02:00
log.Println("[INFO] Initializing CloudWatch Logs connection")
client.cloudwatchlogsconn = cloudwatchlogs.New(awsConfig)
log.Println("[INFO] Initializing OpsWorks Connection")
client.opsworksconn = opsworks.New(usEast1AwsConfig)
}
if len(errs) > 0 {
return nil, &multierror.Error{Errors: errs}
}
return &client, nil
2014-06-24 04:01:57 +02:00
}
// ValidateRegion returns an error if the configured region is not a
// valid aws region and nil otherwise.
2015-03-13 15:42:50 +01:00
func (c *Config) ValidateRegion() error {
var regions = [11]string{"us-east-1", "us-west-2", "us-west-1", "eu-west-1",
"eu-central-1", "ap-southeast-1", "ap-southeast-2", "ap-northeast-1",
"sa-east-1", "cn-north-1", "us-gov-west-1"}
2014-07-30 00:22:37 +02:00
for _, valid := range regions {
if c.Region == valid {
2015-03-13 15:42:50 +01:00
return nil
2014-07-30 00:22:37 +02:00
}
}
2015-03-13 15:42:50 +01:00
return fmt.Errorf("Not a valid region: %s", c.Region)
2014-06-24 04:01:57 +02:00
}
2015-04-20 00:54:42 +02:00
// Validate credentials early and fail before we do any graph walking.
// In the case of an IAM role/profile with insuffecient privileges, fail
// silently
func (c *Config) ValidateCredentials(iamconn *iam.IAM) error {
_, err := iamconn.GetUser(nil)
2015-07-21 15:57:59 +02:00
if awsErr, ok := err.(awserr.Error); ok {
if awsErr.Code() == "AccessDenied" || awsErr.Code() == "ValidationError" {
log.Printf("[WARN] AccessDenied Error with iam.GetUser, assuming IAM profile")
// User may be an IAM instance profile, or otherwise IAM role without the
// GetUser permissions, so fail silently
return nil
}
2015-07-21 15:57:59 +02:00
if awsErr.Code() == "SignatureDoesNotMatch" {
return fmt.Errorf("Failed authenticating with AWS: please verify credentials")
}
}
2015-07-21 15:57:59 +02:00
return err
}
// ValidateAccountId returns a context-specific error if the configured account
// id is explicitly forbidden or not authorised; and nil if it is authorised.
2015-04-20 00:54:42 +02:00
func (c *Config) ValidateAccountId(iamconn *iam.IAM) error {
if c.AllowedAccountIds == nil && c.ForbiddenAccountIds == nil {
return nil
}
log.Printf("[INFO] Validating account ID")
out, err := iamconn.GetUser(nil)
2015-04-20 00:54:42 +02:00
if err != nil {
2015-08-17 02:13:23 +02:00
awsErr, _ := err.(awserr.Error)
if awsErr.Code() == "ValidationError" {
log.Printf("[WARN] ValidationError with iam.GetUser, assuming its an IAM profile")
// User may be an IAM instance profile, so fail silently.
// If it is an IAM instance profile
// validating account might be superfluous
2015-09-24 01:41:48 +02:00
return nil
2015-08-17 02:13:23 +02:00
} else {
return fmt.Errorf("Failed getting account ID from IAM: %s", err)
// return error if the account id is explicitly not authorised
}
2015-04-20 00:54:42 +02:00
}
account_id := strings.Split(*out.User.Arn, ":")[4]
2015-04-20 00:54:42 +02:00
if c.ForbiddenAccountIds != nil {
for _, id := range c.ForbiddenAccountIds {
if id == account_id {
return fmt.Errorf("Forbidden account ID (%s)", id)
}
}
}
if c.AllowedAccountIds != nil {
for _, id := range c.AllowedAccountIds {
if id == account_id {
return nil
}
}
return fmt.Errorf("Account ID not allowed (%s)", account_id)
}
return nil
}