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