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