github.com/vtorhonen/terraform@v0.9.0-beta2.0.20170307220345-5d894e4ffda7/builtin/providers/aws/resource_aws_s3_bucket_notification.go (about) 1 package aws 2 3 import ( 4 "fmt" 5 "log" 6 "strings" 7 "time" 8 9 "github.com/hashicorp/terraform/helper/resource" 10 "github.com/hashicorp/terraform/helper/schema" 11 12 "github.com/aws/aws-sdk-go/aws" 13 "github.com/aws/aws-sdk-go/aws/awserr" 14 "github.com/aws/aws-sdk-go/service/s3" 15 ) 16 17 func resourceAwsS3BucketNotification() *schema.Resource { 18 return &schema.Resource{ 19 Create: resourceAwsS3BucketNotificationPut, 20 Read: resourceAwsS3BucketNotificationRead, 21 Update: resourceAwsS3BucketNotificationPut, 22 Delete: resourceAwsS3BucketNotificationDelete, 23 Importer: &schema.ResourceImporter{ 24 State: schema.ImportStatePassthrough, 25 }, 26 27 Schema: map[string]*schema.Schema{ 28 "bucket": &schema.Schema{ 29 Type: schema.TypeString, 30 Required: true, 31 ForceNew: true, 32 }, 33 34 "topic": &schema.Schema{ 35 Type: schema.TypeList, 36 Optional: true, 37 Elem: &schema.Resource{ 38 Schema: map[string]*schema.Schema{ 39 "id": &schema.Schema{ 40 Type: schema.TypeString, 41 Optional: true, 42 Computed: true, 43 }, 44 "filter_prefix": &schema.Schema{ 45 Type: schema.TypeString, 46 Optional: true, 47 }, 48 "filter_suffix": &schema.Schema{ 49 Type: schema.TypeString, 50 Optional: true, 51 }, 52 "topic_arn": &schema.Schema{ 53 Type: schema.TypeString, 54 Required: true, 55 }, 56 "events": &schema.Schema{ 57 Type: schema.TypeSet, 58 Required: true, 59 Elem: &schema.Schema{Type: schema.TypeString}, 60 Set: schema.HashString, 61 }, 62 }, 63 }, 64 }, 65 66 "queue": &schema.Schema{ 67 Type: schema.TypeList, 68 Optional: true, 69 Elem: &schema.Resource{ 70 Schema: map[string]*schema.Schema{ 71 "id": &schema.Schema{ 72 Type: schema.TypeString, 73 Optional: true, 74 Computed: true, 75 }, 76 "filter_prefix": &schema.Schema{ 77 Type: schema.TypeString, 78 Optional: true, 79 }, 80 "filter_suffix": &schema.Schema{ 81 Type: schema.TypeString, 82 Optional: true, 83 }, 84 "queue_arn": &schema.Schema{ 85 Type: schema.TypeString, 86 Required: true, 87 }, 88 "events": &schema.Schema{ 89 Type: schema.TypeSet, 90 Required: true, 91 Elem: &schema.Schema{Type: schema.TypeString}, 92 Set: schema.HashString, 93 }, 94 }, 95 }, 96 }, 97 98 "lambda_function": &schema.Schema{ 99 Type: schema.TypeList, 100 Optional: true, 101 Elem: &schema.Resource{ 102 Schema: map[string]*schema.Schema{ 103 "id": &schema.Schema{ 104 Type: schema.TypeString, 105 Optional: true, 106 Computed: true, 107 }, 108 "filter_prefix": &schema.Schema{ 109 Type: schema.TypeString, 110 Optional: true, 111 }, 112 "filter_suffix": &schema.Schema{ 113 Type: schema.TypeString, 114 Optional: true, 115 }, 116 "lambda_function_arn": &schema.Schema{ 117 Type: schema.TypeString, 118 Optional: true, 119 }, 120 "events": &schema.Schema{ 121 Type: schema.TypeSet, 122 Required: true, 123 Elem: &schema.Schema{Type: schema.TypeString}, 124 Set: schema.HashString, 125 }, 126 }, 127 }, 128 }, 129 }, 130 } 131 } 132 133 func resourceAwsS3BucketNotificationPut(d *schema.ResourceData, meta interface{}) error { 134 s3conn := meta.(*AWSClient).s3conn 135 bucket := d.Get("bucket").(string) 136 137 // TopicNotifications 138 topicNotifications := d.Get("topic").([]interface{}) 139 topicConfigs := make([]*s3.TopicConfiguration, 0, len(topicNotifications)) 140 for i, c := range topicNotifications { 141 tc := &s3.TopicConfiguration{} 142 143 c := c.(map[string]interface{}) 144 145 // Id 146 if val, ok := c["id"].(string); ok && val != "" { 147 tc.Id = aws.String(val) 148 } else { 149 tc.Id = aws.String(resource.PrefixedUniqueId("tf-s3-topic-")) 150 } 151 152 // TopicArn 153 if val, ok := c["topic_arn"].(string); ok { 154 tc.TopicArn = aws.String(val) 155 } 156 157 // Events 158 events := d.Get(fmt.Sprintf("topic.%d.events", i)).(*schema.Set).List() 159 tc.Events = make([]*string, 0, len(events)) 160 for _, e := range events { 161 tc.Events = append(tc.Events, aws.String(e.(string))) 162 } 163 164 // Filter 165 filterRules := make([]*s3.FilterRule, 0, 2) 166 if val, ok := c["filter_prefix"].(string); ok && val != "" { 167 filterRule := &s3.FilterRule{ 168 Name: aws.String("prefix"), 169 Value: aws.String(val), 170 } 171 filterRules = append(filterRules, filterRule) 172 } 173 if val, ok := c["filter_suffix"].(string); ok && val != "" { 174 filterRule := &s3.FilterRule{ 175 Name: aws.String("suffix"), 176 Value: aws.String(val), 177 } 178 filterRules = append(filterRules, filterRule) 179 } 180 if len(filterRules) > 0 { 181 tc.Filter = &s3.NotificationConfigurationFilter{ 182 Key: &s3.KeyFilter{ 183 FilterRules: filterRules, 184 }, 185 } 186 } 187 topicConfigs = append(topicConfigs, tc) 188 } 189 190 // SQS 191 queueNotifications := d.Get("queue").([]interface{}) 192 queueConfigs := make([]*s3.QueueConfiguration, 0, len(queueNotifications)) 193 for i, c := range queueNotifications { 194 qc := &s3.QueueConfiguration{} 195 196 c := c.(map[string]interface{}) 197 198 // Id 199 if val, ok := c["id"].(string); ok && val != "" { 200 qc.Id = aws.String(val) 201 } else { 202 qc.Id = aws.String(resource.PrefixedUniqueId("tf-s3-queue-")) 203 } 204 205 // QueueArn 206 if val, ok := c["queue_arn"].(string); ok { 207 qc.QueueArn = aws.String(val) 208 } 209 210 // Events 211 events := d.Get(fmt.Sprintf("queue.%d.events", i)).(*schema.Set).List() 212 qc.Events = make([]*string, 0, len(events)) 213 for _, e := range events { 214 qc.Events = append(qc.Events, aws.String(e.(string))) 215 } 216 217 // Filter 218 filterRules := make([]*s3.FilterRule, 0, 2) 219 if val, ok := c["filter_prefix"].(string); ok && val != "" { 220 filterRule := &s3.FilterRule{ 221 Name: aws.String("prefix"), 222 Value: aws.String(val), 223 } 224 filterRules = append(filterRules, filterRule) 225 } 226 if val, ok := c["filter_suffix"].(string); ok && val != "" { 227 filterRule := &s3.FilterRule{ 228 Name: aws.String("suffix"), 229 Value: aws.String(val), 230 } 231 filterRules = append(filterRules, filterRule) 232 } 233 if len(filterRules) > 0 { 234 qc.Filter = &s3.NotificationConfigurationFilter{ 235 Key: &s3.KeyFilter{ 236 FilterRules: filterRules, 237 }, 238 } 239 } 240 queueConfigs = append(queueConfigs, qc) 241 } 242 243 // Lambda 244 lambdaFunctionNotifications := d.Get("lambda_function").([]interface{}) 245 lambdaConfigs := make([]*s3.LambdaFunctionConfiguration, 0, len(lambdaFunctionNotifications)) 246 for i, c := range lambdaFunctionNotifications { 247 lc := &s3.LambdaFunctionConfiguration{} 248 249 c := c.(map[string]interface{}) 250 251 // Id 252 if val, ok := c["id"].(string); ok && val != "" { 253 lc.Id = aws.String(val) 254 } else { 255 lc.Id = aws.String(resource.PrefixedUniqueId("tf-s3-lambda-")) 256 } 257 258 // LambdaFunctionArn 259 if val, ok := c["lambda_function_arn"].(string); ok { 260 lc.LambdaFunctionArn = aws.String(val) 261 } 262 263 // Events 264 events := d.Get(fmt.Sprintf("lambda_function.%d.events", i)).(*schema.Set).List() 265 lc.Events = make([]*string, 0, len(events)) 266 for _, e := range events { 267 lc.Events = append(lc.Events, aws.String(e.(string))) 268 } 269 270 // Filter 271 filterRules := make([]*s3.FilterRule, 0, 2) 272 if val, ok := c["filter_prefix"].(string); ok && val != "" { 273 filterRule := &s3.FilterRule{ 274 Name: aws.String("prefix"), 275 Value: aws.String(val), 276 } 277 filterRules = append(filterRules, filterRule) 278 } 279 if val, ok := c["filter_suffix"].(string); ok && val != "" { 280 filterRule := &s3.FilterRule{ 281 Name: aws.String("suffix"), 282 Value: aws.String(val), 283 } 284 filterRules = append(filterRules, filterRule) 285 } 286 if len(filterRules) > 0 { 287 lc.Filter = &s3.NotificationConfigurationFilter{ 288 Key: &s3.KeyFilter{ 289 FilterRules: filterRules, 290 }, 291 } 292 } 293 lambdaConfigs = append(lambdaConfigs, lc) 294 } 295 296 notificationConfiguration := &s3.NotificationConfiguration{} 297 if len(lambdaConfigs) > 0 { 298 notificationConfiguration.LambdaFunctionConfigurations = lambdaConfigs 299 } 300 if len(queueConfigs) > 0 { 301 notificationConfiguration.QueueConfigurations = queueConfigs 302 } 303 if len(topicConfigs) > 0 { 304 notificationConfiguration.TopicConfigurations = topicConfigs 305 } 306 i := &s3.PutBucketNotificationConfigurationInput{ 307 Bucket: aws.String(bucket), 308 NotificationConfiguration: notificationConfiguration, 309 } 310 311 log.Printf("[DEBUG] S3 bucket: %s, Putting notification: %v", bucket, i) 312 err := resource.Retry(1*time.Minute, func() *resource.RetryError { 313 if _, err := s3conn.PutBucketNotificationConfiguration(i); err != nil { 314 if awserr, ok := err.(awserr.Error); ok { 315 switch awserr.Message() { 316 case "Unable to validate the following destination configurations": 317 return resource.RetryableError(awserr) 318 } 319 } 320 // Didn't recognize the error, so shouldn't retry. 321 return resource.NonRetryableError(err) 322 } 323 // Successful put configuration 324 return nil 325 }) 326 if err != nil { 327 return fmt.Errorf("Error putting S3 notification configuration: %s", err) 328 } 329 330 d.SetId(bucket) 331 332 return resourceAwsS3BucketNotificationRead(d, meta) 333 } 334 335 func resourceAwsS3BucketNotificationDelete(d *schema.ResourceData, meta interface{}) error { 336 s3conn := meta.(*AWSClient).s3conn 337 338 i := &s3.PutBucketNotificationConfigurationInput{ 339 Bucket: aws.String(d.Id()), 340 NotificationConfiguration: &s3.NotificationConfiguration{}, 341 } 342 343 log.Printf("[DEBUG] S3 bucket: %s, Deleting notification: %v", d.Id(), i) 344 _, err := s3conn.PutBucketNotificationConfiguration(i) 345 if err != nil { 346 return fmt.Errorf("Error deleting S3 notification configuration: %s", err) 347 } 348 349 d.SetId("") 350 351 return nil 352 } 353 354 func resourceAwsS3BucketNotificationRead(d *schema.ResourceData, meta interface{}) error { 355 s3conn := meta.(*AWSClient).s3conn 356 357 var err error 358 _, err = s3conn.HeadBucket(&s3.HeadBucketInput{ 359 Bucket: aws.String(d.Id()), 360 }) 361 if err != nil { 362 if awsError, ok := err.(awserr.RequestFailure); ok && awsError.StatusCode() == 404 { 363 log.Printf("[WARN] S3 Bucket (%s) not found, error code (404)", d.Id()) 364 d.SetId("") 365 return nil 366 } else { 367 // some of the AWS SDK's errors can be empty strings, so let's add 368 // some additional context. 369 return fmt.Errorf("error reading S3 bucket \"%s\": %s", d.Id(), err) 370 } 371 } 372 373 // Read the notification configuration 374 notificationConfigs, err := s3conn.GetBucketNotificationConfiguration(&s3.GetBucketNotificationConfigurationRequest{ 375 Bucket: aws.String(d.Id()), 376 }) 377 if err != nil { 378 return err 379 } 380 log.Printf("[DEBUG] S3 Bucket: %s, get notification: %v", d.Id(), notificationConfigs) 381 // Topic Notification 382 if err := d.Set("topic", flattenTopicConfigurations(notificationConfigs.TopicConfigurations)); err != nil { 383 return fmt.Errorf("error reading S3 bucket \"%s\" topic notification: %s", d.Id(), err) 384 } 385 386 // SQS Notification 387 if err := d.Set("queue", flattenQueueConfigurations(notificationConfigs.QueueConfigurations)); err != nil { 388 return fmt.Errorf("error reading S3 bucket \"%s\" queue notification: %s", d.Id(), err) 389 } 390 391 // Lambda Notification 392 if err := d.Set("lambda_function", flattenLambdaFunctionConfigurations(notificationConfigs.LambdaFunctionConfigurations)); err != nil { 393 return fmt.Errorf("error reading S3 bucket \"%s\" lambda function notification: %s", d.Id(), err) 394 } 395 396 return nil 397 } 398 399 func flattenNotificationConfigurationFilter(filter *s3.NotificationConfigurationFilter) map[string]interface{} { 400 filterRules := map[string]interface{}{} 401 for _, f := range filter.Key.FilterRules { 402 if strings.ToLower(*f.Name) == s3.FilterRuleNamePrefix { 403 filterRules["filter_prefix"] = *f.Value 404 } 405 if strings.ToLower(*f.Name) == s3.FilterRuleNameSuffix { 406 filterRules["filter_suffix"] = *f.Value 407 } 408 } 409 return filterRules 410 } 411 412 func flattenTopicConfigurations(configs []*s3.TopicConfiguration) []map[string]interface{} { 413 topicNotifications := make([]map[string]interface{}, 0, len(configs)) 414 for _, notification := range configs { 415 var conf map[string]interface{} 416 if filter := notification.Filter; filter != nil { 417 conf = flattenNotificationConfigurationFilter(filter) 418 } else { 419 conf = map[string]interface{}{} 420 } 421 422 conf["id"] = *notification.Id 423 conf["events"] = schema.NewSet(schema.HashString, flattenStringList(notification.Events)) 424 conf["topic_arn"] = *notification.TopicArn 425 topicNotifications = append(topicNotifications, conf) 426 } 427 428 return topicNotifications 429 } 430 431 func flattenQueueConfigurations(configs []*s3.QueueConfiguration) []map[string]interface{} { 432 queueNotifications := make([]map[string]interface{}, 0, len(configs)) 433 for _, notification := range configs { 434 var conf map[string]interface{} 435 if filter := notification.Filter; filter != nil { 436 conf = flattenNotificationConfigurationFilter(filter) 437 } else { 438 conf = map[string]interface{}{} 439 } 440 441 conf["id"] = *notification.Id 442 conf["events"] = schema.NewSet(schema.HashString, flattenStringList(notification.Events)) 443 conf["queue_arn"] = *notification.QueueArn 444 queueNotifications = append(queueNotifications, conf) 445 } 446 447 return queueNotifications 448 } 449 450 func flattenLambdaFunctionConfigurations(configs []*s3.LambdaFunctionConfiguration) []map[string]interface{} { 451 lambdaFunctionNotifications := make([]map[string]interface{}, 0, len(configs)) 452 for _, notification := range configs { 453 var conf map[string]interface{} 454 if filter := notification.Filter; filter != nil { 455 conf = flattenNotificationConfigurationFilter(filter) 456 } else { 457 conf = map[string]interface{}{} 458 } 459 460 conf["id"] = *notification.Id 461 conf["events"] = schema.NewSet(schema.HashString, flattenStringList(notification.Events)) 462 conf["lambda_function_arn"] = *notification.LambdaFunctionArn 463 lambdaFunctionNotifications = append(lambdaFunctionNotifications, conf) 464 } 465 466 return lambdaFunctionNotifications 467 }