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