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