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