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