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