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