github.com/inge4pres/terraform@v0.7.5-0.20160930053151-bd083f84f376/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 "strings" 10 11 "github.com/aws/aws-sdk-go/aws" 12 "github.com/aws/aws-sdk-go/aws/awserr" 13 "github.com/aws/aws-sdk-go/aws/request" 14 "github.com/aws/aws-sdk-go/aws/session" 15 "github.com/aws/aws-sdk-go/service/apigateway" 16 "github.com/aws/aws-sdk-go/service/applicationautoscaling" 17 "github.com/aws/aws-sdk-go/service/autoscaling" 18 "github.com/aws/aws-sdk-go/service/cloudformation" 19 "github.com/aws/aws-sdk-go/service/cloudfront" 20 "github.com/aws/aws-sdk-go/service/cloudtrail" 21 "github.com/aws/aws-sdk-go/service/cloudwatch" 22 "github.com/aws/aws-sdk-go/service/cloudwatchevents" 23 "github.com/aws/aws-sdk-go/service/cloudwatchlogs" 24 "github.com/aws/aws-sdk-go/service/codecommit" 25 "github.com/aws/aws-sdk-go/service/codedeploy" 26 "github.com/aws/aws-sdk-go/service/directoryservice" 27 "github.com/aws/aws-sdk-go/service/dynamodb" 28 "github.com/aws/aws-sdk-go/service/ec2" 29 "github.com/aws/aws-sdk-go/service/ecr" 30 "github.com/aws/aws-sdk-go/service/ecs" 31 "github.com/aws/aws-sdk-go/service/efs" 32 "github.com/aws/aws-sdk-go/service/elasticache" 33 "github.com/aws/aws-sdk-go/service/elasticbeanstalk" 34 elasticsearch "github.com/aws/aws-sdk-go/service/elasticsearchservice" 35 "github.com/aws/aws-sdk-go/service/elastictranscoder" 36 "github.com/aws/aws-sdk-go/service/elb" 37 "github.com/aws/aws-sdk-go/service/elbv2" 38 "github.com/aws/aws-sdk-go/service/emr" 39 "github.com/aws/aws-sdk-go/service/firehose" 40 "github.com/aws/aws-sdk-go/service/glacier" 41 "github.com/aws/aws-sdk-go/service/iam" 42 "github.com/aws/aws-sdk-go/service/kinesis" 43 "github.com/aws/aws-sdk-go/service/kms" 44 "github.com/aws/aws-sdk-go/service/lambda" 45 "github.com/aws/aws-sdk-go/service/opsworks" 46 "github.com/aws/aws-sdk-go/service/rds" 47 "github.com/aws/aws-sdk-go/service/redshift" 48 "github.com/aws/aws-sdk-go/service/route53" 49 "github.com/aws/aws-sdk-go/service/s3" 50 "github.com/aws/aws-sdk-go/service/ses" 51 "github.com/aws/aws-sdk-go/service/simpledb" 52 "github.com/aws/aws-sdk-go/service/sns" 53 "github.com/aws/aws-sdk-go/service/sqs" 54 "github.com/aws/aws-sdk-go/service/ssm" 55 "github.com/aws/aws-sdk-go/service/sts" 56 "github.com/aws/aws-sdk-go/service/waf" 57 "github.com/hashicorp/errwrap" 58 "github.com/hashicorp/go-cleanhttp" 59 "github.com/hashicorp/terraform/helper/logging" 60 "github.com/hashicorp/terraform/terraform" 61 ) 62 63 type Config struct { 64 AccessKey string 65 SecretKey string 66 CredsFilename string 67 Profile string 68 Token string 69 Region string 70 MaxRetries int 71 72 AssumeRoleARN string 73 AssumeRoleExternalID string 74 AssumeRoleSessionName string 75 76 AllowedAccountIds []interface{} 77 ForbiddenAccountIds []interface{} 78 79 DynamoDBEndpoint string 80 KinesisEndpoint string 81 Ec2Endpoint string 82 IamEndpoint string 83 ElbEndpoint string 84 S3Endpoint string 85 Insecure bool 86 87 SkipCredsValidation bool 88 SkipRequestingAccountId bool 89 SkipMetadataApiCheck bool 90 S3ForcePathStyle bool 91 } 92 93 type AWSClient struct { 94 cfconn *cloudformation.CloudFormation 95 cloudfrontconn *cloudfront.CloudFront 96 cloudtrailconn *cloudtrail.CloudTrail 97 cloudwatchconn *cloudwatch.CloudWatch 98 cloudwatchlogsconn *cloudwatchlogs.CloudWatchLogs 99 cloudwatcheventsconn *cloudwatchevents.CloudWatchEvents 100 dsconn *directoryservice.DirectoryService 101 dynamodbconn *dynamodb.DynamoDB 102 ec2conn *ec2.EC2 103 ecrconn *ecr.ECR 104 ecsconn *ecs.ECS 105 efsconn *efs.EFS 106 elbconn *elb.ELB 107 elbv2conn *elbv2.ELBV2 108 emrconn *emr.EMR 109 esconn *elasticsearch.ElasticsearchService 110 apigateway *apigateway.APIGateway 111 appautoscalingconn *applicationautoscaling.ApplicationAutoScaling 112 autoscalingconn *autoscaling.AutoScaling 113 s3conn *s3.S3 114 sesConn *ses.SES 115 simpledbconn *simpledb.SimpleDB 116 sqsconn *sqs.SQS 117 snsconn *sns.SNS 118 stsconn *sts.STS 119 redshiftconn *redshift.Redshift 120 r53conn *route53.Route53 121 accountid string 122 region string 123 rdsconn *rds.RDS 124 iamconn *iam.IAM 125 kinesisconn *kinesis.Kinesis 126 kmsconn *kms.KMS 127 firehoseconn *firehose.Firehose 128 elasticacheconn *elasticache.ElastiCache 129 elasticbeanstalkconn *elasticbeanstalk.ElasticBeanstalk 130 elastictranscoderconn *elastictranscoder.ElasticTranscoder 131 lambdaconn *lambda.Lambda 132 opsworksconn *opsworks.OpsWorks 133 glacierconn *glacier.Glacier 134 codedeployconn *codedeploy.CodeDeploy 135 codecommitconn *codecommit.CodeCommit 136 ssmconn *ssm.SSM 137 wafconn *waf.WAF 138 } 139 140 // Client configures and returns a fully initialized AWSClient 141 func (c *Config) Client() (interface{}, error) { 142 // Get the auth and region. This can fail if keys/regions were not 143 // specified and we're attempting to use the environment. 144 log.Println("[INFO] Building AWS region structure") 145 err := c.ValidateRegion() 146 if err != nil { 147 return nil, err 148 } 149 150 var client AWSClient 151 // store AWS region in client struct, for region specific operations such as 152 // bucket storage in S3 153 client.region = c.Region 154 155 log.Println("[INFO] Building AWS auth structure") 156 creds, err := GetCredentials(c) 157 if err != nil { 158 return nil, err 159 } 160 // Call Get to check for credential provider. If nothing found, we'll get an 161 // error, and we can present it nicely to the user 162 cp, err := creds.Get() 163 if err != nil { 164 if awsErr, ok := err.(awserr.Error); ok && awsErr.Code() == "NoCredentialProviders" { 165 return nil, errors.New(`No valid credential sources found for AWS Provider. 166 Please see https://terraform.io/docs/providers/aws/index.html for more information on 167 providing credentials for the AWS Provider`) 168 } 169 170 return nil, fmt.Errorf("Error loading credentials for AWS Provider: %s", err) 171 } 172 173 log.Printf("[INFO] AWS Auth provider used: %q", cp.ProviderName) 174 175 awsConfig := &aws.Config{ 176 Credentials: creds, 177 Region: aws.String(c.Region), 178 MaxRetries: aws.Int(c.MaxRetries), 179 HTTPClient: cleanhttp.DefaultClient(), 180 S3ForcePathStyle: aws.Bool(c.S3ForcePathStyle), 181 } 182 183 if logging.IsDebugOrHigher() { 184 awsConfig.LogLevel = aws.LogLevel(aws.LogDebugWithHTTPBody) 185 awsConfig.Logger = awsLogger{} 186 } 187 188 if c.Insecure { 189 transport := awsConfig.HTTPClient.Transport.(*http.Transport) 190 transport.TLSClientConfig = &tls.Config{ 191 InsecureSkipVerify: true, 192 } 193 } 194 195 // Set up base session 196 sess, err := session.NewSession(awsConfig) 197 if err != nil { 198 return nil, errwrap.Wrapf("Error creating AWS session: {{err}}", err) 199 } 200 sess.Handlers.Build.PushFrontNamed(addTerraformVersionToUserAgent) 201 202 // Some services exist only in us-east-1, e.g. because they manage 203 // resources that can span across multiple regions, or because 204 // signature format v4 requires region to be us-east-1 for global 205 // endpoints: 206 // http://docs.aws.amazon.com/general/latest/gr/sigv4_changes.html 207 usEast1Sess := sess.Copy(&aws.Config{Region: aws.String("us-east-1")}) 208 209 // Some services have user-configurable endpoints 210 awsEc2Sess := sess.Copy(&aws.Config{Endpoint: aws.String(c.Ec2Endpoint)}) 211 awsElbSess := sess.Copy(&aws.Config{Endpoint: aws.String(c.ElbEndpoint)}) 212 awsIamSess := sess.Copy(&aws.Config{Endpoint: aws.String(c.IamEndpoint)}) 213 awsS3Sess := sess.Copy(&aws.Config{Endpoint: aws.String(c.S3Endpoint)}) 214 dynamoSess := sess.Copy(&aws.Config{Endpoint: aws.String(c.DynamoDBEndpoint)}) 215 kinesisSess := sess.Copy(&aws.Config{Endpoint: aws.String(c.KinesisEndpoint)}) 216 217 // These two services need to be set up early so we can check on AccountID 218 client.iamconn = iam.New(awsIamSess) 219 client.stsconn = sts.New(sess) 220 221 if !c.SkipCredsValidation { 222 err = c.ValidateCredentials(client.stsconn) 223 if err != nil { 224 return nil, err 225 } 226 } 227 228 if !c.SkipRequestingAccountId { 229 accountId, err := GetAccountId(client.iamconn, client.stsconn, cp.ProviderName) 230 if err == nil { 231 client.accountid = accountId 232 } 233 } 234 235 authErr := c.ValidateAccountId(client.accountid) 236 if authErr != nil { 237 return nil, authErr 238 } 239 240 client.apigateway = apigateway.New(sess) 241 client.appautoscalingconn = applicationautoscaling.New(sess) 242 client.autoscalingconn = autoscaling.New(sess) 243 client.cfconn = cloudformation.New(sess) 244 client.cloudfrontconn = cloudfront.New(sess) 245 client.cloudtrailconn = cloudtrail.New(sess) 246 client.cloudwatchconn = cloudwatch.New(sess) 247 client.cloudwatcheventsconn = cloudwatchevents.New(sess) 248 client.cloudwatchlogsconn = cloudwatchlogs.New(sess) 249 client.codecommitconn = codecommit.New(usEast1Sess) 250 client.codedeployconn = codedeploy.New(sess) 251 client.dsconn = directoryservice.New(sess) 252 client.dynamodbconn = dynamodb.New(dynamoSess) 253 client.ec2conn = ec2.New(awsEc2Sess) 254 client.ecrconn = ecr.New(sess) 255 client.ecsconn = ecs.New(sess) 256 client.efsconn = efs.New(sess) 257 client.elasticacheconn = elasticache.New(sess) 258 client.elasticbeanstalkconn = elasticbeanstalk.New(sess) 259 client.elastictranscoderconn = elastictranscoder.New(sess) 260 client.elbconn = elb.New(awsElbSess) 261 client.elbv2conn = elbv2.New(awsElbSess) 262 client.emrconn = emr.New(sess) 263 client.esconn = elasticsearch.New(sess) 264 client.firehoseconn = firehose.New(sess) 265 client.glacierconn = glacier.New(sess) 266 client.kinesisconn = kinesis.New(kinesisSess) 267 client.kmsconn = kms.New(sess) 268 client.lambdaconn = lambda.New(sess) 269 client.opsworksconn = opsworks.New(usEast1Sess) 270 client.r53conn = route53.New(usEast1Sess) 271 client.rdsconn = rds.New(sess) 272 client.redshiftconn = redshift.New(sess) 273 client.simpledbconn = simpledb.New(sess) 274 client.s3conn = s3.New(awsS3Sess) 275 client.sesConn = ses.New(sess) 276 client.snsconn = sns.New(sess) 277 client.sqsconn = sqs.New(sess) 278 client.ssmconn = ssm.New(sess) 279 client.wafconn = waf.New(sess) 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.Println("[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 }