github.com/gmlexx/terraform@v0.9.6-0.20170514090029-1532b2a67680/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/codebuild" 28 "github.com/aws/aws-sdk-go/service/codecommit" 29 "github.com/aws/aws-sdk-go/service/codedeploy" 30 "github.com/aws/aws-sdk-go/service/codepipeline" 31 "github.com/aws/aws-sdk-go/service/cognitoidentity" 32 "github.com/aws/aws-sdk-go/service/configservice" 33 "github.com/aws/aws-sdk-go/service/databasemigrationservice" 34 "github.com/aws/aws-sdk-go/service/devicefarm" 35 "github.com/aws/aws-sdk-go/service/directoryservice" 36 "github.com/aws/aws-sdk-go/service/dynamodb" 37 "github.com/aws/aws-sdk-go/service/ec2" 38 "github.com/aws/aws-sdk-go/service/ecr" 39 "github.com/aws/aws-sdk-go/service/ecs" 40 "github.com/aws/aws-sdk-go/service/efs" 41 "github.com/aws/aws-sdk-go/service/elasticache" 42 "github.com/aws/aws-sdk-go/service/elasticbeanstalk" 43 elasticsearch "github.com/aws/aws-sdk-go/service/elasticsearchservice" 44 "github.com/aws/aws-sdk-go/service/elastictranscoder" 45 "github.com/aws/aws-sdk-go/service/elb" 46 "github.com/aws/aws-sdk-go/service/elbv2" 47 "github.com/aws/aws-sdk-go/service/emr" 48 "github.com/aws/aws-sdk-go/service/firehose" 49 "github.com/aws/aws-sdk-go/service/glacier" 50 "github.com/aws/aws-sdk-go/service/iam" 51 "github.com/aws/aws-sdk-go/service/inspector" 52 "github.com/aws/aws-sdk-go/service/kinesis" 53 "github.com/aws/aws-sdk-go/service/kms" 54 "github.com/aws/aws-sdk-go/service/lambda" 55 "github.com/aws/aws-sdk-go/service/lightsail" 56 "github.com/aws/aws-sdk-go/service/opsworks" 57 "github.com/aws/aws-sdk-go/service/rds" 58 "github.com/aws/aws-sdk-go/service/redshift" 59 "github.com/aws/aws-sdk-go/service/route53" 60 "github.com/aws/aws-sdk-go/service/s3" 61 "github.com/aws/aws-sdk-go/service/ses" 62 "github.com/aws/aws-sdk-go/service/sfn" 63 "github.com/aws/aws-sdk-go/service/simpledb" 64 "github.com/aws/aws-sdk-go/service/sns" 65 "github.com/aws/aws-sdk-go/service/sqs" 66 "github.com/aws/aws-sdk-go/service/ssm" 67 "github.com/aws/aws-sdk-go/service/sts" 68 "github.com/aws/aws-sdk-go/service/waf" 69 "github.com/davecgh/go-spew/spew" 70 "github.com/hashicorp/errwrap" 71 "github.com/hashicorp/go-cleanhttp" 72 "github.com/hashicorp/terraform/helper/logging" 73 "github.com/hashicorp/terraform/terraform" 74 ) 75 76 type Config struct { 77 AccessKey string 78 SecretKey string 79 CredsFilename string 80 Profile string 81 Token string 82 Region string 83 MaxRetries int 84 85 AssumeRoleARN string 86 AssumeRoleExternalID string 87 AssumeRoleSessionName string 88 AssumeRolePolicy string 89 90 AllowedAccountIds []interface{} 91 ForbiddenAccountIds []interface{} 92 93 CloudFormationEndpoint string 94 CloudWatchEndpoint string 95 CloudWatchEventsEndpoint string 96 CloudWatchLogsEndpoint string 97 DynamoDBEndpoint string 98 DeviceFarmEndpoint string 99 Ec2Endpoint string 100 ElbEndpoint string 101 IamEndpoint string 102 KinesisEndpoint string 103 KmsEndpoint string 104 RdsEndpoint string 105 S3Endpoint string 106 SnsEndpoint string 107 SqsEndpoint string 108 Insecure bool 109 110 SkipCredsValidation bool 111 SkipGetEC2Platforms bool 112 SkipRegionValidation bool 113 SkipRequestingAccountId bool 114 SkipMetadataApiCheck bool 115 S3ForcePathStyle bool 116 } 117 118 type AWSClient struct { 119 cfconn *cloudformation.CloudFormation 120 cloudfrontconn *cloudfront.CloudFront 121 cloudtrailconn *cloudtrail.CloudTrail 122 cloudwatchconn *cloudwatch.CloudWatch 123 cloudwatchlogsconn *cloudwatchlogs.CloudWatchLogs 124 cloudwatcheventsconn *cloudwatchevents.CloudWatchEvents 125 cognitoconn *cognitoidentity.CognitoIdentity 126 configconn *configservice.ConfigService 127 devicefarmconn *devicefarm.DeviceFarm 128 dmsconn *databasemigrationservice.DatabaseMigrationService 129 dsconn *directoryservice.DirectoryService 130 dynamodbconn *dynamodb.DynamoDB 131 ec2conn *ec2.EC2 132 ecrconn *ecr.ECR 133 ecsconn *ecs.ECS 134 efsconn *efs.EFS 135 elbconn *elb.ELB 136 elbv2conn *elbv2.ELBV2 137 emrconn *emr.EMR 138 esconn *elasticsearch.ElasticsearchService 139 acmconn *acm.ACM 140 apigateway *apigateway.APIGateway 141 appautoscalingconn *applicationautoscaling.ApplicationAutoScaling 142 autoscalingconn *autoscaling.AutoScaling 143 s3conn *s3.S3 144 sesConn *ses.SES 145 simpledbconn *simpledb.SimpleDB 146 sqsconn *sqs.SQS 147 snsconn *sns.SNS 148 stsconn *sts.STS 149 redshiftconn *redshift.Redshift 150 r53conn *route53.Route53 151 partition string 152 accountid string 153 supportedplatforms []string 154 region string 155 rdsconn *rds.RDS 156 iamconn *iam.IAM 157 kinesisconn *kinesis.Kinesis 158 kmsconn *kms.KMS 159 firehoseconn *firehose.Firehose 160 inspectorconn *inspector.Inspector 161 elasticacheconn *elasticache.ElastiCache 162 elasticbeanstalkconn *elasticbeanstalk.ElasticBeanstalk 163 elastictranscoderconn *elastictranscoder.ElasticTranscoder 164 lambdaconn *lambda.Lambda 165 lightsailconn *lightsail.Lightsail 166 opsworksconn *opsworks.OpsWorks 167 glacierconn *glacier.Glacier 168 codebuildconn *codebuild.CodeBuild 169 codedeployconn *codedeploy.CodeDeploy 170 codecommitconn *codecommit.CodeCommit 171 codepipelineconn *codepipeline.CodePipeline 172 sfnconn *sfn.SFN 173 ssmconn *ssm.SSM 174 wafconn *waf.WAF 175 } 176 177 func (c *AWSClient) S3() *s3.S3 { 178 return c.s3conn 179 } 180 181 func (c *AWSClient) DynamoDB() *dynamodb.DynamoDB { 182 return c.dynamodbconn 183 } 184 185 func (c *AWSClient) IsGovCloud() bool { 186 if c.region == "us-gov-west-1" { 187 return true 188 } 189 return false 190 } 191 192 func (c *AWSClient) IsChinaCloud() bool { 193 if c.region == "cn-north-1" { 194 return true 195 } 196 return false 197 } 198 199 // Client configures and returns a fully initialized AWSClient 200 func (c *Config) Client() (interface{}, error) { 201 // Get the auth and region. This can fail if keys/regions were not 202 // specified and we're attempting to use the environment. 203 if c.SkipRegionValidation { 204 log.Println("[INFO] Skipping region validation") 205 } else { 206 log.Println("[INFO] Building AWS region structure") 207 err := c.ValidateRegion() 208 if err != nil { 209 return nil, err 210 } 211 } 212 213 var client AWSClient 214 // store AWS region in client struct, for region specific operations such as 215 // bucket storage in S3 216 client.region = c.Region 217 218 log.Println("[INFO] Building AWS auth structure") 219 creds, err := GetCredentials(c) 220 if err != nil { 221 return nil, err 222 } 223 // Call Get to check for credential provider. If nothing found, we'll get an 224 // error, and we can present it nicely to the user 225 cp, err := creds.Get() 226 if err != nil { 227 if awsErr, ok := err.(awserr.Error); ok && awsErr.Code() == "NoCredentialProviders" { 228 return nil, errors.New(`No valid credential sources found for AWS Provider. 229 Please see https://terraform.io/docs/providers/aws/index.html for more information on 230 providing credentials for the AWS Provider`) 231 } 232 233 return nil, fmt.Errorf("Error loading credentials for AWS Provider: %s", err) 234 } 235 236 log.Printf("[INFO] AWS Auth provider used: %q", cp.ProviderName) 237 238 awsConfig := &aws.Config{ 239 Credentials: creds, 240 Region: aws.String(c.Region), 241 MaxRetries: aws.Int(c.MaxRetries), 242 HTTPClient: cleanhttp.DefaultClient(), 243 S3ForcePathStyle: aws.Bool(c.S3ForcePathStyle), 244 } 245 246 if logging.IsDebugOrHigher() { 247 awsConfig.LogLevel = aws.LogLevel(aws.LogDebugWithHTTPBody) 248 awsConfig.Logger = awsLogger{} 249 } 250 251 if c.Insecure { 252 transport := awsConfig.HTTPClient.Transport.(*http.Transport) 253 transport.TLSClientConfig = &tls.Config{ 254 InsecureSkipVerify: true, 255 } 256 } 257 258 // Set up base session 259 sess, err := session.NewSession(awsConfig) 260 if err != nil { 261 return nil, errwrap.Wrapf("Error creating AWS session: {{err}}", err) 262 } 263 264 sess.Handlers.Build.PushBackNamed(addTerraformVersionToUserAgent) 265 266 if extraDebug := os.Getenv("TERRAFORM_AWS_AUTHFAILURE_DEBUG"); extraDebug != "" { 267 sess.Handlers.UnmarshalError.PushFrontNamed(debugAuthFailure) 268 } 269 270 // Some services exist only in us-east-1, e.g. because they manage 271 // resources that can span across multiple regions, or because 272 // signature format v4 requires region to be us-east-1 for global 273 // endpoints: 274 // http://docs.aws.amazon.com/general/latest/gr/sigv4_changes.html 275 usEast1Sess := sess.Copy(&aws.Config{Region: aws.String("us-east-1")}) 276 277 // Some services have user-configurable endpoints 278 awsCfSess := sess.Copy(&aws.Config{Endpoint: aws.String(c.CloudFormationEndpoint)}) 279 awsCwSess := sess.Copy(&aws.Config{Endpoint: aws.String(c.CloudWatchEndpoint)}) 280 awsCweSess := sess.Copy(&aws.Config{Endpoint: aws.String(c.CloudWatchEventsEndpoint)}) 281 awsCwlSess := sess.Copy(&aws.Config{Endpoint: aws.String(c.CloudWatchLogsEndpoint)}) 282 awsDynamoSess := sess.Copy(&aws.Config{Endpoint: aws.String(c.DynamoDBEndpoint)}) 283 awsEc2Sess := sess.Copy(&aws.Config{Endpoint: aws.String(c.Ec2Endpoint)}) 284 awsElbSess := sess.Copy(&aws.Config{Endpoint: aws.String(c.ElbEndpoint)}) 285 awsIamSess := sess.Copy(&aws.Config{Endpoint: aws.String(c.IamEndpoint)}) 286 awsKinesisSess := sess.Copy(&aws.Config{Endpoint: aws.String(c.KinesisEndpoint)}) 287 awsKmsSess := sess.Copy(&aws.Config{Endpoint: aws.String(c.KmsEndpoint)}) 288 awsRdsSess := sess.Copy(&aws.Config{Endpoint: aws.String(c.RdsEndpoint)}) 289 awsS3Sess := sess.Copy(&aws.Config{Endpoint: aws.String(c.S3Endpoint)}) 290 awsSnsSess := sess.Copy(&aws.Config{Endpoint: aws.String(c.SnsEndpoint)}) 291 awsSqsSess := sess.Copy(&aws.Config{Endpoint: aws.String(c.SqsEndpoint)}) 292 awsDeviceFarmSess := sess.Copy(&aws.Config{Endpoint: aws.String(c.DeviceFarmEndpoint)}) 293 294 log.Println("[INFO] Initializing DeviceFarm SDK connection") 295 client.devicefarmconn = devicefarm.New(awsDeviceFarmSess) 296 297 // These two services need to be set up early so we can check on AccountID 298 client.iamconn = iam.New(awsIamSess) 299 client.stsconn = sts.New(sess) 300 301 if !c.SkipCredsValidation { 302 err = c.ValidateCredentials(client.stsconn) 303 if err != nil { 304 return nil, err 305 } 306 } 307 308 if !c.SkipRequestingAccountId { 309 partition, accountId, err := GetAccountInfo(client.iamconn, client.stsconn, cp.ProviderName) 310 if err == nil { 311 client.partition = partition 312 client.accountid = accountId 313 } 314 } 315 316 authErr := c.ValidateAccountId(client.accountid) 317 if authErr != nil { 318 return nil, authErr 319 } 320 321 client.ec2conn = ec2.New(awsEc2Sess) 322 323 if !c.SkipGetEC2Platforms { 324 supportedPlatforms, err := GetSupportedEC2Platforms(client.ec2conn) 325 if err != nil { 326 // We intentionally fail *silently* because there's a chance 327 // user just doesn't have ec2:DescribeAccountAttributes permissions 328 log.Printf("[WARN] Unable to get supported EC2 platforms: %s", err) 329 } else { 330 client.supportedplatforms = supportedPlatforms 331 } 332 } 333 334 client.acmconn = acm.New(sess) 335 client.apigateway = apigateway.New(sess) 336 client.appautoscalingconn = applicationautoscaling.New(sess) 337 client.autoscalingconn = autoscaling.New(sess) 338 client.cfconn = cloudformation.New(awsCfSess) 339 client.cloudfrontconn = cloudfront.New(sess) 340 client.cloudtrailconn = cloudtrail.New(sess) 341 client.cloudwatchconn = cloudwatch.New(awsCwSess) 342 client.cloudwatcheventsconn = cloudwatchevents.New(awsCweSess) 343 client.cloudwatchlogsconn = cloudwatchlogs.New(awsCwlSess) 344 client.codecommitconn = codecommit.New(sess) 345 client.codebuildconn = codebuild.New(sess) 346 client.codedeployconn = codedeploy.New(sess) 347 client.configconn = configservice.New(sess) 348 client.cognitoconn = cognitoidentity.New(sess) 349 client.dmsconn = databasemigrationservice.New(sess) 350 client.codepipelineconn = codepipeline.New(sess) 351 client.dsconn = directoryservice.New(sess) 352 client.dynamodbconn = dynamodb.New(awsDynamoSess) 353 client.ecrconn = ecr.New(sess) 354 client.ecsconn = ecs.New(sess) 355 client.efsconn = efs.New(sess) 356 client.elasticacheconn = elasticache.New(sess) 357 client.elasticbeanstalkconn = elasticbeanstalk.New(sess) 358 client.elastictranscoderconn = elastictranscoder.New(sess) 359 client.elbconn = elb.New(awsElbSess) 360 client.elbv2conn = elbv2.New(awsElbSess) 361 client.emrconn = emr.New(sess) 362 client.esconn = elasticsearch.New(sess) 363 client.firehoseconn = firehose.New(sess) 364 client.inspectorconn = inspector.New(sess) 365 client.glacierconn = glacier.New(sess) 366 client.kinesisconn = kinesis.New(awsKinesisSess) 367 client.kmsconn = kms.New(awsKmsSess) 368 client.lambdaconn = lambda.New(sess) 369 client.lightsailconn = lightsail.New(usEast1Sess) 370 client.opsworksconn = opsworks.New(sess) 371 client.r53conn = route53.New(usEast1Sess) 372 client.rdsconn = rds.New(awsRdsSess) 373 client.redshiftconn = redshift.New(sess) 374 client.simpledbconn = simpledb.New(sess) 375 client.s3conn = s3.New(awsS3Sess) 376 client.sesConn = ses.New(sess) 377 client.sfnconn = sfn.New(sess) 378 client.snsconn = sns.New(awsSnsSess) 379 client.sqsconn = sqs.New(awsSqsSess) 380 client.ssmconn = ssm.New(sess) 381 client.wafconn = waf.New(sess) 382 383 return &client, nil 384 } 385 386 // ValidateRegion returns an error if the configured region is not a 387 // valid aws region and nil otherwise. 388 func (c *Config) ValidateRegion() error { 389 var regions = []string{ 390 "ap-northeast-1", 391 "ap-northeast-2", 392 "ap-south-1", 393 "ap-southeast-1", 394 "ap-southeast-2", 395 "ca-central-1", 396 "cn-north-1", 397 "eu-central-1", 398 "eu-west-1", 399 "eu-west-2", 400 "sa-east-1", 401 "us-east-1", 402 "us-east-2", 403 "us-gov-west-1", 404 "us-west-1", 405 "us-west-2", 406 } 407 408 for _, valid := range regions { 409 if c.Region == valid { 410 return nil 411 } 412 } 413 return fmt.Errorf("Not a valid region: %s", c.Region) 414 } 415 416 // Validate credentials early and fail before we do any graph walking. 417 func (c *Config) ValidateCredentials(stsconn *sts.STS) error { 418 _, err := stsconn.GetCallerIdentity(&sts.GetCallerIdentityInput{}) 419 return err 420 } 421 422 // ValidateAccountId returns a context-specific error if the configured account 423 // id is explicitly forbidden or not authorised; and nil if it is authorised. 424 func (c *Config) ValidateAccountId(accountId string) error { 425 if c.AllowedAccountIds == nil && c.ForbiddenAccountIds == nil { 426 return nil 427 } 428 429 log.Println("[INFO] Validating account ID") 430 431 if c.ForbiddenAccountIds != nil { 432 for _, id := range c.ForbiddenAccountIds { 433 if id == accountId { 434 return fmt.Errorf("Forbidden account ID (%s)", id) 435 } 436 } 437 } 438 439 if c.AllowedAccountIds != nil { 440 for _, id := range c.AllowedAccountIds { 441 if id == accountId { 442 return nil 443 } 444 } 445 return fmt.Errorf("Account ID not allowed (%s)", accountId) 446 } 447 448 return nil 449 } 450 451 func GetSupportedEC2Platforms(conn *ec2.EC2) ([]string, error) { 452 attrName := "supported-platforms" 453 454 input := ec2.DescribeAccountAttributesInput{ 455 AttributeNames: []*string{aws.String(attrName)}, 456 } 457 attributes, err := conn.DescribeAccountAttributes(&input) 458 if err != nil { 459 return nil, err 460 } 461 462 var platforms []string 463 for _, attr := range attributes.AccountAttributes { 464 if *attr.AttributeName == attrName { 465 for _, v := range attr.AttributeValues { 466 platforms = append(platforms, *v.AttributeValue) 467 } 468 break 469 } 470 } 471 472 if len(platforms) == 0 { 473 return nil, fmt.Errorf("No EC2 platforms detected") 474 } 475 476 return platforms, nil 477 } 478 479 // addTerraformVersionToUserAgent is a named handler that will add Terraform's 480 // version information to requests made by the AWS SDK. 481 var addTerraformVersionToUserAgent = request.NamedHandler{ 482 Name: "terraform.TerraformVersionUserAgentHandler", 483 Fn: request.MakeAddToUserAgentHandler( 484 "APN/1.0 HashiCorp/1.0 Terraform", terraform.VersionString()), 485 } 486 487 var debugAuthFailure = request.NamedHandler{ 488 Name: "terraform.AuthFailureAdditionalDebugHandler", 489 Fn: func(req *request.Request) { 490 if isAWSErr(req.Error, "AuthFailure", "AWS was not able to validate the provided access credentials") { 491 log.Printf("[INFO] Additional AuthFailure Debugging Context") 492 log.Printf("[INFO] Current system UTC time: %s", time.Now().UTC()) 493 log.Printf("[INFO] Request object: %s", spew.Sdump(req)) 494 } 495 }, 496 } 497 498 type awsLogger struct{} 499 500 func (l awsLogger) Log(args ...interface{}) { 501 tokens := make([]string, 0, len(args)) 502 for _, arg := range args { 503 if token, ok := arg.(string); ok { 504 tokens = append(tokens, token) 505 } 506 } 507 log.Printf("[DEBUG] [aws-sdk-go] %s", strings.Join(tokens, " ")) 508 }