github.com/ns1/terraform@v0.7.10-0.20161109153551-8949419bef40/builtin/providers/aws/config.go (about)

     1  package aws
     2  
     3  import (
     4  	"crypto/tls"
     5  	"errors"
     6  	"fmt"
     7  	"log"
     8  	"net/http"
     9  	"os"
    10  	"strings"
    11  	"time"
    12  
    13  	"github.com/aws/aws-sdk-go/aws"
    14  	"github.com/aws/aws-sdk-go/aws/awserr"
    15  	"github.com/aws/aws-sdk-go/aws/request"
    16  	"github.com/aws/aws-sdk-go/aws/session"
    17  	"github.com/aws/aws-sdk-go/service/acm"
    18  	"github.com/aws/aws-sdk-go/service/apigateway"
    19  	"github.com/aws/aws-sdk-go/service/applicationautoscaling"
    20  	"github.com/aws/aws-sdk-go/service/autoscaling"
    21  	"github.com/aws/aws-sdk-go/service/cloudformation"
    22  	"github.com/aws/aws-sdk-go/service/cloudfront"
    23  	"github.com/aws/aws-sdk-go/service/cloudtrail"
    24  	"github.com/aws/aws-sdk-go/service/cloudwatch"
    25  	"github.com/aws/aws-sdk-go/service/cloudwatchevents"
    26  	"github.com/aws/aws-sdk-go/service/cloudwatchlogs"
    27  	"github.com/aws/aws-sdk-go/service/codecommit"
    28  	"github.com/aws/aws-sdk-go/service/codedeploy"
    29  	"github.com/aws/aws-sdk-go/service/directoryservice"
    30  	"github.com/aws/aws-sdk-go/service/dynamodb"
    31  	"github.com/aws/aws-sdk-go/service/ec2"
    32  	"github.com/aws/aws-sdk-go/service/ecr"
    33  	"github.com/aws/aws-sdk-go/service/ecs"
    34  	"github.com/aws/aws-sdk-go/service/efs"
    35  	"github.com/aws/aws-sdk-go/service/elasticache"
    36  	"github.com/aws/aws-sdk-go/service/elasticbeanstalk"
    37  	elasticsearch "github.com/aws/aws-sdk-go/service/elasticsearchservice"
    38  	"github.com/aws/aws-sdk-go/service/elastictranscoder"
    39  	"github.com/aws/aws-sdk-go/service/elb"
    40  	"github.com/aws/aws-sdk-go/service/elbv2"
    41  	"github.com/aws/aws-sdk-go/service/emr"
    42  	"github.com/aws/aws-sdk-go/service/firehose"
    43  	"github.com/aws/aws-sdk-go/service/glacier"
    44  	"github.com/aws/aws-sdk-go/service/iam"
    45  	"github.com/aws/aws-sdk-go/service/kinesis"
    46  	"github.com/aws/aws-sdk-go/service/kms"
    47  	"github.com/aws/aws-sdk-go/service/lambda"
    48  	"github.com/aws/aws-sdk-go/service/opsworks"
    49  	"github.com/aws/aws-sdk-go/service/rds"
    50  	"github.com/aws/aws-sdk-go/service/redshift"
    51  	"github.com/aws/aws-sdk-go/service/route53"
    52  	"github.com/aws/aws-sdk-go/service/s3"
    53  	"github.com/aws/aws-sdk-go/service/ses"
    54  	"github.com/aws/aws-sdk-go/service/simpledb"
    55  	"github.com/aws/aws-sdk-go/service/sns"
    56  	"github.com/aws/aws-sdk-go/service/sqs"
    57  	"github.com/aws/aws-sdk-go/service/ssm"
    58  	"github.com/aws/aws-sdk-go/service/sts"
    59  	"github.com/aws/aws-sdk-go/service/waf"
    60  	"github.com/davecgh/go-spew/spew"
    61  	"github.com/hashicorp/errwrap"
    62  	"github.com/hashicorp/go-cleanhttp"
    63  	"github.com/hashicorp/terraform/helper/logging"
    64  	"github.com/hashicorp/terraform/terraform"
    65  )
    66  
    67  type Config struct {
    68  	AccessKey     string
    69  	SecretKey     string
    70  	CredsFilename string
    71  	Profile       string
    72  	Token         string
    73  	Region        string
    74  	MaxRetries    int
    75  
    76  	AssumeRoleARN         string
    77  	AssumeRoleExternalID  string
    78  	AssumeRoleSessionName string
    79  
    80  	AllowedAccountIds   []interface{}
    81  	ForbiddenAccountIds []interface{}
    82  
    83  	DynamoDBEndpoint string
    84  	KinesisEndpoint  string
    85  	Ec2Endpoint      string
    86  	IamEndpoint      string
    87  	ElbEndpoint      string
    88  	S3Endpoint       string
    89  	Insecure         bool
    90  
    91  	SkipCredsValidation     bool
    92  	SkipRequestingAccountId bool
    93  	SkipMetadataApiCheck    bool
    94  	S3ForcePathStyle        bool
    95  }
    96  
    97  type AWSClient struct {
    98  	cfconn                *cloudformation.CloudFormation
    99  	cloudfrontconn        *cloudfront.CloudFront
   100  	cloudtrailconn        *cloudtrail.CloudTrail
   101  	cloudwatchconn        *cloudwatch.CloudWatch
   102  	cloudwatchlogsconn    *cloudwatchlogs.CloudWatchLogs
   103  	cloudwatcheventsconn  *cloudwatchevents.CloudWatchEvents
   104  	dsconn                *directoryservice.DirectoryService
   105  	dynamodbconn          *dynamodb.DynamoDB
   106  	ec2conn               *ec2.EC2
   107  	ecrconn               *ecr.ECR
   108  	ecsconn               *ecs.ECS
   109  	efsconn               *efs.EFS
   110  	elbconn               *elb.ELB
   111  	elbv2conn             *elbv2.ELBV2
   112  	emrconn               *emr.EMR
   113  	esconn                *elasticsearch.ElasticsearchService
   114  	acmconn               *acm.ACM
   115  	apigateway            *apigateway.APIGateway
   116  	appautoscalingconn    *applicationautoscaling.ApplicationAutoScaling
   117  	autoscalingconn       *autoscaling.AutoScaling
   118  	s3conn                *s3.S3
   119  	sesConn               *ses.SES
   120  	simpledbconn          *simpledb.SimpleDB
   121  	sqsconn               *sqs.SQS
   122  	snsconn               *sns.SNS
   123  	stsconn               *sts.STS
   124  	redshiftconn          *redshift.Redshift
   125  	r53conn               *route53.Route53
   126  	partition             string
   127  	accountid             string
   128  	region                string
   129  	rdsconn               *rds.RDS
   130  	iamconn               *iam.IAM
   131  	kinesisconn           *kinesis.Kinesis
   132  	kmsconn               *kms.KMS
   133  	firehoseconn          *firehose.Firehose
   134  	elasticacheconn       *elasticache.ElastiCache
   135  	elasticbeanstalkconn  *elasticbeanstalk.ElasticBeanstalk
   136  	elastictranscoderconn *elastictranscoder.ElasticTranscoder
   137  	lambdaconn            *lambda.Lambda
   138  	opsworksconn          *opsworks.OpsWorks
   139  	glacierconn           *glacier.Glacier
   140  	codedeployconn        *codedeploy.CodeDeploy
   141  	codecommitconn        *codecommit.CodeCommit
   142  	ssmconn               *ssm.SSM
   143  	wafconn               *waf.WAF
   144  }
   145  
   146  // Client configures and returns a fully initialized AWSClient
   147  func (c *Config) Client() (interface{}, error) {
   148  	// Get the auth and region. This can fail if keys/regions were not
   149  	// specified and we're attempting to use the environment.
   150  	log.Println("[INFO] Building AWS region structure")
   151  	err := c.ValidateRegion()
   152  	if err != nil {
   153  		return nil, err
   154  	}
   155  
   156  	var client AWSClient
   157  	// store AWS region in client struct, for region specific operations such as
   158  	// bucket storage in S3
   159  	client.region = c.Region
   160  
   161  	log.Println("[INFO] Building AWS auth structure")
   162  	creds, err := GetCredentials(c)
   163  	if err != nil {
   164  		return nil, err
   165  	}
   166  	// Call Get to check for credential provider. If nothing found, we'll get an
   167  	// error, and we can present it nicely to the user
   168  	cp, err := creds.Get()
   169  	if err != nil {
   170  		if awsErr, ok := err.(awserr.Error); ok && awsErr.Code() == "NoCredentialProviders" {
   171  			return nil, errors.New(`No valid credential sources found for AWS Provider.
   172    Please see https://terraform.io/docs/providers/aws/index.html for more information on
   173    providing credentials for the AWS Provider`)
   174  		}
   175  
   176  		return nil, fmt.Errorf("Error loading credentials for AWS Provider: %s", err)
   177  	}
   178  
   179  	log.Printf("[INFO] AWS Auth provider used: %q", cp.ProviderName)
   180  
   181  	awsConfig := &aws.Config{
   182  		Credentials:      creds,
   183  		Region:           aws.String(c.Region),
   184  		MaxRetries:       aws.Int(c.MaxRetries),
   185  		HTTPClient:       cleanhttp.DefaultClient(),
   186  		S3ForcePathStyle: aws.Bool(c.S3ForcePathStyle),
   187  	}
   188  
   189  	if logging.IsDebugOrHigher() {
   190  		awsConfig.LogLevel = aws.LogLevel(aws.LogDebugWithHTTPBody)
   191  		awsConfig.Logger = awsLogger{}
   192  	}
   193  
   194  	if c.Insecure {
   195  		transport := awsConfig.HTTPClient.Transport.(*http.Transport)
   196  		transport.TLSClientConfig = &tls.Config{
   197  			InsecureSkipVerify: true,
   198  		}
   199  	}
   200  
   201  	// Set up base session
   202  	sess, err := session.NewSession(awsConfig)
   203  	if err != nil {
   204  		return nil, errwrap.Wrapf("Error creating AWS session: {{err}}", err)
   205  	}
   206  
   207  	// Removes the SDK Version handler, so we only have the provider User-Agent
   208  	// Ex: "User-Agent: APN/1.0 HashiCorp/1.0 Terraform/0.7.9-dev"
   209  	sess.Handlers.Build.Remove(request.NamedHandler{Name: "core.SDKVersionUserAgentHandler"})
   210  	sess.Handlers.Build.PushFrontNamed(addTerraformVersionToUserAgent)
   211  
   212  	if extraDebug := os.Getenv("TERRAFORM_AWS_AUTHFAILURE_DEBUG"); extraDebug != "" {
   213  		sess.Handlers.UnmarshalError.PushFrontNamed(debugAuthFailure)
   214  	}
   215  
   216  	// Some services exist only in us-east-1, e.g. because they manage
   217  	// resources that can span across multiple regions, or because
   218  	// signature format v4 requires region to be us-east-1 for global
   219  	// endpoints:
   220  	// http://docs.aws.amazon.com/general/latest/gr/sigv4_changes.html
   221  	usEast1Sess := sess.Copy(&aws.Config{Region: aws.String("us-east-1")})
   222  
   223  	// Some services have user-configurable endpoints
   224  	awsEc2Sess := sess.Copy(&aws.Config{Endpoint: aws.String(c.Ec2Endpoint)})
   225  	awsElbSess := sess.Copy(&aws.Config{Endpoint: aws.String(c.ElbEndpoint)})
   226  	awsIamSess := sess.Copy(&aws.Config{Endpoint: aws.String(c.IamEndpoint)})
   227  	awsS3Sess := sess.Copy(&aws.Config{Endpoint: aws.String(c.S3Endpoint)})
   228  	dynamoSess := sess.Copy(&aws.Config{Endpoint: aws.String(c.DynamoDBEndpoint)})
   229  	kinesisSess := sess.Copy(&aws.Config{Endpoint: aws.String(c.KinesisEndpoint)})
   230  
   231  	// These two services need to be set up early so we can check on AccountID
   232  	client.iamconn = iam.New(awsIamSess)
   233  	client.stsconn = sts.New(sess)
   234  
   235  	if !c.SkipCredsValidation {
   236  		err = c.ValidateCredentials(client.stsconn)
   237  		if err != nil {
   238  			return nil, err
   239  		}
   240  	}
   241  
   242  	if !c.SkipRequestingAccountId {
   243  		partition, accountId, err := GetAccountInfo(client.iamconn, client.stsconn, cp.ProviderName)
   244  		if err == nil {
   245  			client.partition = partition
   246  			client.accountid = accountId
   247  		}
   248  	}
   249  
   250  	authErr := c.ValidateAccountId(client.accountid)
   251  	if authErr != nil {
   252  		return nil, authErr
   253  	}
   254  
   255  	client.acmconn = acm.New(sess)
   256  	client.apigateway = apigateway.New(sess)
   257  	client.appautoscalingconn = applicationautoscaling.New(sess)
   258  	client.autoscalingconn = autoscaling.New(sess)
   259  	client.cfconn = cloudformation.New(sess)
   260  	client.cloudfrontconn = cloudfront.New(sess)
   261  	client.cloudtrailconn = cloudtrail.New(sess)
   262  	client.cloudwatchconn = cloudwatch.New(sess)
   263  	client.cloudwatcheventsconn = cloudwatchevents.New(sess)
   264  	client.cloudwatchlogsconn = cloudwatchlogs.New(sess)
   265  	client.codecommitconn = codecommit.New(usEast1Sess)
   266  	client.codedeployconn = codedeploy.New(sess)
   267  	client.dsconn = directoryservice.New(sess)
   268  	client.dynamodbconn = dynamodb.New(dynamoSess)
   269  	client.ec2conn = ec2.New(awsEc2Sess)
   270  	client.ecrconn = ecr.New(sess)
   271  	client.ecsconn = ecs.New(sess)
   272  	client.efsconn = efs.New(sess)
   273  	client.elasticacheconn = elasticache.New(sess)
   274  	client.elasticbeanstalkconn = elasticbeanstalk.New(sess)
   275  	client.elastictranscoderconn = elastictranscoder.New(sess)
   276  	client.elbconn = elb.New(awsElbSess)
   277  	client.elbv2conn = elbv2.New(awsElbSess)
   278  	client.emrconn = emr.New(sess)
   279  	client.esconn = elasticsearch.New(sess)
   280  	client.firehoseconn = firehose.New(sess)
   281  	client.glacierconn = glacier.New(sess)
   282  	client.kinesisconn = kinesis.New(kinesisSess)
   283  	client.kmsconn = kms.New(sess)
   284  	client.lambdaconn = lambda.New(sess)
   285  	client.opsworksconn = opsworks.New(usEast1Sess)
   286  	client.r53conn = route53.New(usEast1Sess)
   287  	client.rdsconn = rds.New(sess)
   288  	client.redshiftconn = redshift.New(sess)
   289  	client.simpledbconn = simpledb.New(sess)
   290  	client.s3conn = s3.New(awsS3Sess)
   291  	client.sesConn = ses.New(sess)
   292  	client.snsconn = sns.New(sess)
   293  	client.sqsconn = sqs.New(sess)
   294  	client.ssmconn = ssm.New(sess)
   295  	client.wafconn = waf.New(sess)
   296  
   297  	return &client, nil
   298  }
   299  
   300  // ValidateRegion returns an error if the configured region is not a
   301  // valid aws region and nil otherwise.
   302  func (c *Config) ValidateRegion() error {
   303  	var regions = []string{
   304  		"ap-northeast-1",
   305  		"ap-northeast-2",
   306  		"ap-south-1",
   307  		"ap-southeast-1",
   308  		"ap-southeast-2",
   309  		"cn-north-1",
   310  		"eu-central-1",
   311  		"eu-west-1",
   312  		"sa-east-1",
   313  		"us-east-1",
   314  		"us-east-2",
   315  		"us-gov-west-1",
   316  		"us-west-1",
   317  		"us-west-2",
   318  	}
   319  
   320  	for _, valid := range regions {
   321  		if c.Region == valid {
   322  			return nil
   323  		}
   324  	}
   325  	return fmt.Errorf("Not a valid region: %s", c.Region)
   326  }
   327  
   328  // Validate credentials early and fail before we do any graph walking.
   329  func (c *Config) ValidateCredentials(stsconn *sts.STS) error {
   330  	_, err := stsconn.GetCallerIdentity(&sts.GetCallerIdentityInput{})
   331  	return err
   332  }
   333  
   334  // ValidateAccountId returns a context-specific error if the configured account
   335  // id is explicitly forbidden or not authorised; and nil if it is authorised.
   336  func (c *Config) ValidateAccountId(accountId string) error {
   337  	if c.AllowedAccountIds == nil && c.ForbiddenAccountIds == nil {
   338  		return nil
   339  	}
   340  
   341  	log.Println("[INFO] Validating account ID")
   342  
   343  	if c.ForbiddenAccountIds != nil {
   344  		for _, id := range c.ForbiddenAccountIds {
   345  			if id == accountId {
   346  				return fmt.Errorf("Forbidden account ID (%s)", id)
   347  			}
   348  		}
   349  	}
   350  
   351  	if c.AllowedAccountIds != nil {
   352  		for _, id := range c.AllowedAccountIds {
   353  			if id == accountId {
   354  				return nil
   355  			}
   356  		}
   357  		return fmt.Errorf("Account ID not allowed (%s)", accountId)
   358  	}
   359  
   360  	return nil
   361  }
   362  
   363  // addTerraformVersionToUserAgent is a named handler that will add Terraform's
   364  // version information to requests made by the AWS SDK.
   365  var addTerraformVersionToUserAgent = request.NamedHandler{
   366  	Name: "terraform.TerraformVersionUserAgentHandler",
   367  	Fn: request.MakeAddToUserAgentHandler(
   368  		"APN/1.0 HashiCorp/1.0 Terraform", terraform.VersionString()),
   369  }
   370  
   371  var debugAuthFailure = request.NamedHandler{
   372  	Name: "terraform.AuthFailureAdditionalDebugHandler",
   373  	Fn: func(req *request.Request) {
   374  		if isAWSErr(req.Error, "AuthFailure", "AWS was not able to validate the provided access credentials") {
   375  			log.Printf("[INFO] Additional AuthFailure Debugging Context")
   376  			log.Printf("[INFO] Current system UTC time: %s", time.Now().UTC())
   377  			log.Printf("[INFO] Request object: %s", spew.Sdump(req))
   378  		}
   379  	},
   380  }
   381  
   382  type awsLogger struct{}
   383  
   384  func (l awsLogger) Log(args ...interface{}) {
   385  	tokens := make([]string, 0, len(args))
   386  	for _, arg := range args {
   387  		if token, ok := arg.(string); ok {
   388  			tokens = append(tokens, token)
   389  		}
   390  	}
   391  	log.Printf("[DEBUG] [aws-sdk-go] %s", strings.Join(tokens, " "))
   392  }