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