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