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