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