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