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