github.com/anuaimi/terraform@v0.6.4-0.20150904235404-2bf9aec61da8/builtin/providers/aws/config.go (about)

     1  package aws
     2  
     3  import (
     4  	"fmt"
     5  	"log"
     6  	"strings"
     7  
     8  	"github.com/hashicorp/terraform/helper/multierror"
     9  
    10  	"github.com/aws/aws-sdk-go/aws"
    11  	"github.com/aws/aws-sdk-go/aws/awserr"
    12  	"github.com/aws/aws-sdk-go/aws/credentials"
    13  	"github.com/aws/aws-sdk-go/service/autoscaling"
    14  	"github.com/aws/aws-sdk-go/service/cloudwatch"
    15  	"github.com/aws/aws-sdk-go/service/dynamodb"
    16  	"github.com/aws/aws-sdk-go/service/ec2"
    17  	"github.com/aws/aws-sdk-go/service/ecs"
    18  	"github.com/aws/aws-sdk-go/service/elasticache"
    19  	"github.com/aws/aws-sdk-go/service/elb"
    20  	"github.com/aws/aws-sdk-go/service/iam"
    21  	"github.com/aws/aws-sdk-go/service/kinesis"
    22  	"github.com/aws/aws-sdk-go/service/lambda"
    23  	"github.com/aws/aws-sdk-go/service/rds"
    24  	"github.com/aws/aws-sdk-go/service/route53"
    25  	"github.com/aws/aws-sdk-go/service/s3"
    26  	"github.com/aws/aws-sdk-go/service/sns"
    27  	"github.com/aws/aws-sdk-go/service/sqs"
    28  )
    29  
    30  type Config struct {
    31  	AccessKey  string
    32  	SecretKey  string
    33  	Token      string
    34  	Region     string
    35  	MaxRetries int
    36  
    37  	AllowedAccountIds   []interface{}
    38  	ForbiddenAccountIds []interface{}
    39  
    40  	DynamoDBEndpoint string
    41  }
    42  
    43  type AWSClient struct {
    44  	cloudwatchconn  *cloudwatch.CloudWatch
    45  	dynamodbconn    *dynamodb.DynamoDB
    46  	ec2conn         *ec2.EC2
    47  	ecsconn         *ecs.ECS
    48  	elbconn         *elb.ELB
    49  	autoscalingconn *autoscaling.AutoScaling
    50  	s3conn          *s3.S3
    51  	sqsconn         *sqs.SQS
    52  	snsconn         *sns.SNS
    53  	r53conn         *route53.Route53
    54  	region          string
    55  	rdsconn         *rds.RDS
    56  	iamconn         *iam.IAM
    57  	kinesisconn     *kinesis.Kinesis
    58  	elasticacheconn *elasticache.ElastiCache
    59  	lambdaconn      *lambda.Lambda
    60  }
    61  
    62  // Client configures and returns a fully initialized AWSClient
    63  func (c *Config) Client() (interface{}, error) {
    64  	var client AWSClient
    65  
    66  	// Get the auth and region. This can fail if keys/regions were not
    67  	// specified and we're attempting to use the environment.
    68  	var errs []error
    69  
    70  	log.Println("[INFO] Building AWS region structure")
    71  	err := c.ValidateRegion()
    72  	if err != nil {
    73  		errs = append(errs, err)
    74  	}
    75  
    76  	if len(errs) == 0 {
    77  		// store AWS region in client struct, for region specific operations such as
    78  		// bucket storage in S3
    79  		client.region = c.Region
    80  
    81  		log.Println("[INFO] Building AWS auth structure")
    82  		// We fetched all credential sources in Provider. If they are
    83  		// available, they'll already be in c. See Provider definition.
    84  		creds := credentials.NewStaticCredentials(c.AccessKey, c.SecretKey, c.Token)
    85  		awsConfig := &aws.Config{
    86  			Credentials: creds,
    87  			Region:      aws.String(c.Region),
    88  			MaxRetries:  aws.Int(c.MaxRetries),
    89  		}
    90  
    91  		log.Println("[INFO] Initializing IAM Connection")
    92  		client.iamconn = iam.New(awsConfig)
    93  
    94  		err := c.ValidateCredentials(client.iamconn)
    95  		if err != nil {
    96  			errs = append(errs, err)
    97  		}
    98  
    99  		awsDynamoDBConfig := &aws.Config{
   100  			Credentials: creds,
   101  			Region:      aws.String(c.Region),
   102  			MaxRetries:  aws.Int(c.MaxRetries),
   103  			Endpoint:    aws.String(c.DynamoDBEndpoint),
   104  		}
   105  
   106  		log.Println("[INFO] Initializing DynamoDB connection")
   107  		client.dynamodbconn = dynamodb.New(awsDynamoDBConfig)
   108  
   109  		log.Println("[INFO] Initializing ELB connection")
   110  		client.elbconn = elb.New(awsConfig)
   111  
   112  		log.Println("[INFO] Initializing S3 connection")
   113  		client.s3conn = s3.New(awsConfig)
   114  
   115  		log.Println("[INFO] Initializing SQS connection")
   116  		client.sqsconn = sqs.New(awsConfig)
   117  
   118  		log.Println("[INFO] Initializing SNS connection")
   119  		client.snsconn = sns.New(awsConfig)
   120  
   121  		log.Println("[INFO] Initializing RDS Connection")
   122  		client.rdsconn = rds.New(awsConfig)
   123  
   124  		log.Println("[INFO] Initializing Kinesis Connection")
   125  		client.kinesisconn = kinesis.New(awsConfig)
   126  
   127  		authErr := c.ValidateAccountId(client.iamconn)
   128  		if authErr != nil {
   129  			errs = append(errs, authErr)
   130  		}
   131  
   132  		log.Println("[INFO] Initializing AutoScaling connection")
   133  		client.autoscalingconn = autoscaling.New(awsConfig)
   134  
   135  		log.Println("[INFO] Initializing EC2 Connection")
   136  		client.ec2conn = ec2.New(awsConfig)
   137  
   138  		log.Println("[INFO] Initializing ECS Connection")
   139  		client.ecsconn = ecs.New(awsConfig)
   140  
   141  		// aws-sdk-go uses v4 for signing requests, which requires all global
   142  		// endpoints to use 'us-east-1'.
   143  		// See http://docs.aws.amazon.com/general/latest/gr/sigv4_changes.html
   144  		log.Println("[INFO] Initializing Route 53 connection")
   145  		client.r53conn = route53.New(&aws.Config{
   146  			Credentials: creds,
   147  			Region:      aws.String("us-east-1"),
   148  			MaxRetries:  aws.Int(c.MaxRetries),
   149  		})
   150  
   151  		log.Println("[INFO] Initializing Elasticache Connection")
   152  		client.elasticacheconn = elasticache.New(awsConfig)
   153  
   154  		log.Println("[INFO] Initializing Lambda Connection")
   155  		client.lambdaconn = lambda.New(awsConfig)
   156  
   157  		log.Println("[INFO] Initializing CloudWatch SDK connection")
   158  		client.cloudwatchconn = cloudwatch.New(awsConfig)
   159  	}
   160  
   161  	if len(errs) > 0 {
   162  		return nil, &multierror.Error{Errors: errs}
   163  	}
   164  
   165  	return &client, nil
   166  }
   167  
   168  // ValidateRegion returns an error if the configured region is not a
   169  // valid aws region and nil otherwise.
   170  func (c *Config) ValidateRegion() error {
   171  	var regions = [11]string{"us-east-1", "us-west-2", "us-west-1", "eu-west-1",
   172  		"eu-central-1", "ap-southeast-1", "ap-southeast-2", "ap-northeast-1",
   173  		"sa-east-1", "cn-north-1", "us-gov-west-1"}
   174  
   175  	for _, valid := range regions {
   176  		if c.Region == valid {
   177  			return nil
   178  		}
   179  	}
   180  	return fmt.Errorf("Not a valid region: %s", c.Region)
   181  }
   182  
   183  // Validate credentials early and fail before we do any graph walking.
   184  // In the case of an IAM role/profile with insuffecient privileges, fail
   185  // silently
   186  func (c *Config) ValidateCredentials(iamconn *iam.IAM) error {
   187  	_, err := iamconn.GetUser(nil)
   188  
   189  	if awsErr, ok := err.(awserr.Error); ok {
   190  
   191  		if awsErr.Code() == "AccessDenied" || awsErr.Code() == "ValidationError" {
   192  			log.Printf("[WARN] AccessDenied Error with iam.GetUser, assuming IAM profile")
   193  			// User may be an IAM instance profile, or otherwise IAM role without the
   194  			// GetUser permissions, so fail silently
   195  			return nil
   196  		}
   197  
   198  		if awsErr.Code() == "SignatureDoesNotMatch" {
   199  			return fmt.Errorf("Failed authenticating with AWS: please verify credentials")
   200  		}
   201  	}
   202  
   203  	return err
   204  }
   205  
   206  // ValidateAccountId returns a context-specific error if the configured account
   207  // id is explicitly forbidden or not authorised; and nil if it is authorised.
   208  func (c *Config) ValidateAccountId(iamconn *iam.IAM) error {
   209  	if c.AllowedAccountIds == nil && c.ForbiddenAccountIds == nil {
   210  		return nil
   211  	}
   212  
   213  	log.Printf("[INFO] Validating account ID")
   214  
   215  	out, err := iamconn.GetUser(nil)
   216  	if err != nil {
   217  		return fmt.Errorf("Failed getting account ID from IAM: %s", err)
   218  	}
   219  
   220  	account_id := strings.Split(*out.User.Arn, ":")[4]
   221  
   222  	if c.ForbiddenAccountIds != nil {
   223  		for _, id := range c.ForbiddenAccountIds {
   224  			if id == account_id {
   225  				return fmt.Errorf("Forbidden account ID (%s)", id)
   226  			}
   227  		}
   228  	}
   229  
   230  	if c.AllowedAccountIds != nil {
   231  		for _, id := range c.AllowedAccountIds {
   232  			if id == account_id {
   233  				return nil
   234  			}
   235  		}
   236  		return fmt.Errorf("Account ID not allowed (%s)", account_id)
   237  	}
   238  
   239  	return nil
   240  }