github.com/danp/terraform@v0.9.5-0.20170426144147-39d740081351/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  }