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