github.com/anuaimi/terraform@v0.6.4-0.20150904235404-2bf9aec61da8/builtin/providers/aws/config.go (about) 1 package aws 2 3 import ( 4 "fmt" 5 "log" 6 "strings" 7 8 "github.com/hashicorp/terraform/helper/multierror" 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/credentials" 13 "github.com/aws/aws-sdk-go/service/autoscaling" 14 "github.com/aws/aws-sdk-go/service/cloudwatch" 15 "github.com/aws/aws-sdk-go/service/dynamodb" 16 "github.com/aws/aws-sdk-go/service/ec2" 17 "github.com/aws/aws-sdk-go/service/ecs" 18 "github.com/aws/aws-sdk-go/service/elasticache" 19 "github.com/aws/aws-sdk-go/service/elb" 20 "github.com/aws/aws-sdk-go/service/iam" 21 "github.com/aws/aws-sdk-go/service/kinesis" 22 "github.com/aws/aws-sdk-go/service/lambda" 23 "github.com/aws/aws-sdk-go/service/rds" 24 "github.com/aws/aws-sdk-go/service/route53" 25 "github.com/aws/aws-sdk-go/service/s3" 26 "github.com/aws/aws-sdk-go/service/sns" 27 "github.com/aws/aws-sdk-go/service/sqs" 28 ) 29 30 type Config struct { 31 AccessKey string 32 SecretKey string 33 Token string 34 Region string 35 MaxRetries int 36 37 AllowedAccountIds []interface{} 38 ForbiddenAccountIds []interface{} 39 40 DynamoDBEndpoint string 41 } 42 43 type AWSClient struct { 44 cloudwatchconn *cloudwatch.CloudWatch 45 dynamodbconn *dynamodb.DynamoDB 46 ec2conn *ec2.EC2 47 ecsconn *ecs.ECS 48 elbconn *elb.ELB 49 autoscalingconn *autoscaling.AutoScaling 50 s3conn *s3.S3 51 sqsconn *sqs.SQS 52 snsconn *sns.SNS 53 r53conn *route53.Route53 54 region string 55 rdsconn *rds.RDS 56 iamconn *iam.IAM 57 kinesisconn *kinesis.Kinesis 58 elasticacheconn *elasticache.ElastiCache 59 lambdaconn *lambda.Lambda 60 } 61 62 // Client configures and returns a fully initialized AWSClient 63 func (c *Config) Client() (interface{}, error) { 64 var client AWSClient 65 66 // Get the auth and region. This can fail if keys/regions were not 67 // specified and we're attempting to use the environment. 68 var errs []error 69 70 log.Println("[INFO] Building AWS region structure") 71 err := c.ValidateRegion() 72 if err != nil { 73 errs = append(errs, err) 74 } 75 76 if len(errs) == 0 { 77 // store AWS region in client struct, for region specific operations such as 78 // bucket storage in S3 79 client.region = c.Region 80 81 log.Println("[INFO] Building AWS auth structure") 82 // We fetched all credential sources in Provider. If they are 83 // available, they'll already be in c. See Provider definition. 84 creds := credentials.NewStaticCredentials(c.AccessKey, c.SecretKey, c.Token) 85 awsConfig := &aws.Config{ 86 Credentials: creds, 87 Region: aws.String(c.Region), 88 MaxRetries: aws.Int(c.MaxRetries), 89 } 90 91 log.Println("[INFO] Initializing IAM Connection") 92 client.iamconn = iam.New(awsConfig) 93 94 err := c.ValidateCredentials(client.iamconn) 95 if err != nil { 96 errs = append(errs, err) 97 } 98 99 awsDynamoDBConfig := &aws.Config{ 100 Credentials: creds, 101 Region: aws.String(c.Region), 102 MaxRetries: aws.Int(c.MaxRetries), 103 Endpoint: aws.String(c.DynamoDBEndpoint), 104 } 105 106 log.Println("[INFO] Initializing DynamoDB connection") 107 client.dynamodbconn = dynamodb.New(awsDynamoDBConfig) 108 109 log.Println("[INFO] Initializing ELB connection") 110 client.elbconn = elb.New(awsConfig) 111 112 log.Println("[INFO] Initializing S3 connection") 113 client.s3conn = s3.New(awsConfig) 114 115 log.Println("[INFO] Initializing SQS connection") 116 client.sqsconn = sqs.New(awsConfig) 117 118 log.Println("[INFO] Initializing SNS connection") 119 client.snsconn = sns.New(awsConfig) 120 121 log.Println("[INFO] Initializing RDS Connection") 122 client.rdsconn = rds.New(awsConfig) 123 124 log.Println("[INFO] Initializing Kinesis Connection") 125 client.kinesisconn = kinesis.New(awsConfig) 126 127 authErr := c.ValidateAccountId(client.iamconn) 128 if authErr != nil { 129 errs = append(errs, authErr) 130 } 131 132 log.Println("[INFO] Initializing AutoScaling connection") 133 client.autoscalingconn = autoscaling.New(awsConfig) 134 135 log.Println("[INFO] Initializing EC2 Connection") 136 client.ec2conn = ec2.New(awsConfig) 137 138 log.Println("[INFO] Initializing ECS Connection") 139 client.ecsconn = ecs.New(awsConfig) 140 141 // aws-sdk-go uses v4 for signing requests, which requires all global 142 // endpoints to use 'us-east-1'. 143 // See http://docs.aws.amazon.com/general/latest/gr/sigv4_changes.html 144 log.Println("[INFO] Initializing Route 53 connection") 145 client.r53conn = route53.New(&aws.Config{ 146 Credentials: creds, 147 Region: aws.String("us-east-1"), 148 MaxRetries: aws.Int(c.MaxRetries), 149 }) 150 151 log.Println("[INFO] Initializing Elasticache Connection") 152 client.elasticacheconn = elasticache.New(awsConfig) 153 154 log.Println("[INFO] Initializing Lambda Connection") 155 client.lambdaconn = lambda.New(awsConfig) 156 157 log.Println("[INFO] Initializing CloudWatch SDK connection") 158 client.cloudwatchconn = cloudwatch.New(awsConfig) 159 } 160 161 if len(errs) > 0 { 162 return nil, &multierror.Error{Errors: errs} 163 } 164 165 return &client, nil 166 } 167 168 // ValidateRegion returns an error if the configured region is not a 169 // valid aws region and nil otherwise. 170 func (c *Config) ValidateRegion() error { 171 var regions = [11]string{"us-east-1", "us-west-2", "us-west-1", "eu-west-1", 172 "eu-central-1", "ap-southeast-1", "ap-southeast-2", "ap-northeast-1", 173 "sa-east-1", "cn-north-1", "us-gov-west-1"} 174 175 for _, valid := range regions { 176 if c.Region == valid { 177 return nil 178 } 179 } 180 return fmt.Errorf("Not a valid region: %s", c.Region) 181 } 182 183 // Validate credentials early and fail before we do any graph walking. 184 // In the case of an IAM role/profile with insuffecient privileges, fail 185 // silently 186 func (c *Config) ValidateCredentials(iamconn *iam.IAM) error { 187 _, err := iamconn.GetUser(nil) 188 189 if awsErr, ok := err.(awserr.Error); ok { 190 191 if awsErr.Code() == "AccessDenied" || awsErr.Code() == "ValidationError" { 192 log.Printf("[WARN] AccessDenied Error with iam.GetUser, assuming IAM profile") 193 // User may be an IAM instance profile, or otherwise IAM role without the 194 // GetUser permissions, so fail silently 195 return nil 196 } 197 198 if awsErr.Code() == "SignatureDoesNotMatch" { 199 return fmt.Errorf("Failed authenticating with AWS: please verify credentials") 200 } 201 } 202 203 return err 204 } 205 206 // ValidateAccountId returns a context-specific error if the configured account 207 // id is explicitly forbidden or not authorised; and nil if it is authorised. 208 func (c *Config) ValidateAccountId(iamconn *iam.IAM) error { 209 if c.AllowedAccountIds == nil && c.ForbiddenAccountIds == nil { 210 return nil 211 } 212 213 log.Printf("[INFO] Validating account ID") 214 215 out, err := iamconn.GetUser(nil) 216 if err != nil { 217 return fmt.Errorf("Failed getting account ID from IAM: %s", err) 218 } 219 220 account_id := strings.Split(*out.User.Arn, ":")[4] 221 222 if c.ForbiddenAccountIds != nil { 223 for _, id := range c.ForbiddenAccountIds { 224 if id == account_id { 225 return fmt.Errorf("Forbidden account ID (%s)", id) 226 } 227 } 228 } 229 230 if c.AllowedAccountIds != nil { 231 for _, id := range c.AllowedAccountIds { 232 if id == account_id { 233 return nil 234 } 235 } 236 return fmt.Errorf("Account ID not allowed (%s)", account_id) 237 } 238 239 return nil 240 }