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