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