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