diff --git a/azurerm/internal/services/eventgrid/resource_arm_eventgrid_event_subscription.go b/azurerm/internal/services/eventgrid/resource_arm_eventgrid_event_subscription.go index 086e4129fd1f..d83857e87a25 100644 --- a/azurerm/internal/services/eventgrid/resource_arm_eventgrid_event_subscription.go +++ b/azurerm/internal/services/eventgrid/resource_arm_eventgrid_event_subscription.go @@ -21,10 +21,10 @@ import ( ) func getEnpointTypes() []string { - return []string{"service_bus_queue_endpoint", "eventhub_endpoint", "hybrid_connection_endpoint", "webhook_endpoint", "storage_queue_endpoint"} + return []string{"webhook_endpoint", "storage_queue_endpoint", "eventhub_endpoint", "hybrid_connection_endpoint", "service_bus_queue_endpoint", "service_bus_topic_endpoint", "azure_function_endpoint"} } -// RemoveFromStringArray ... +// RemoveFromStringArray removes all matching values from a string array func RemoveFromStringArray(elements []string, remove string) []string { for i, v := range elements { if v == remove { @@ -34,6 +34,32 @@ func RemoveFromStringArray(elements []string, remove string) []string { return elements } +// AdvancedFilterDiffSuppressFunc performs a type relaxed diff +func AdvancedFilterDiffSuppressFunc(k, old, new string, d *schema.ResourceData) bool { + if strings.EqualFold(old, new) { + return true + } else if o, err := strconv.ParseFloat(old, 64); err == nil { + n, err := strconv.ParseFloat(new, 64) + if err == nil { + return o == n + } + } else if o, err := strconv.ParseBool(old); err == nil { + n, err := strconv.ParseBool(new) + if err == nil { + return o == n + } + } + return false +} + +// ParseFloat tries to convert a string to a float64 value +func ParseFloat(value string) (*float64, error) { + if f, err := strconv.ParseFloat(value, 64); err == nil { + return &f, nil + } + return nil, fmt.Errorf("Value %q is not a float number", value) +} + func resourceArmEventGridEventSubscription() *schema.Resource { return &schema.Resource{ Create: resourceArmEventGridEventSubscriptionCreateUpdate, @@ -84,12 +110,6 @@ func resourceArmEventGridEventSubscription() *schema.Resource { ValidateFunc: validation.StringIsNotEmpty, }, - "topic_name": { - Type: schema.TypeString, - Optional: true, - Computed: true, - }, - "storage_queue_endpoint": { Type: schema.TypeList, MaxItems: 1, @@ -111,14 +131,14 @@ func resourceArmEventGridEventSubscription() *schema.Resource { }, }, - "service_bus_queue_endpoint": { + "eventhub_endpoint": { Type: schema.TypeList, MaxItems: 1, Optional: true, - ConflictsWith: RemoveFromStringArray(getEnpointTypes(), "service_bus_queue_endpoint"), + ConflictsWith: RemoveFromStringArray(getEnpointTypes(), "eventhub_endpoint"), Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ - "service_bus_queue_id": { + "eventhub_id": { Type: schema.TypeString, Required: true, ValidateFunc: azure.ValidateResourceID, @@ -127,14 +147,14 @@ func resourceArmEventGridEventSubscription() *schema.Resource { }, }, - "eventhub_endpoint": { + "hybrid_connection_endpoint": { Type: schema.TypeList, MaxItems: 1, Optional: true, - ConflictsWith: RemoveFromStringArray(getEnpointTypes(), "eventhub_endpoint"), + ConflictsWith: RemoveFromStringArray(getEnpointTypes(), "hybrid_connection_endpoint"), Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ - "eventhub_id": { + "hybrid_connection_id": { Type: schema.TypeString, Required: true, ValidateFunc: azure.ValidateResourceID, @@ -143,14 +163,30 @@ func resourceArmEventGridEventSubscription() *schema.Resource { }, }, - "hybrid_connection_endpoint": { + "webhook_endpoint": { Type: schema.TypeList, MaxItems: 1, Optional: true, ConflictsWith: RemoveFromStringArray(getEnpointTypes(), "hybrid_connection_endpoint"), Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ - "hybrid_connection_id": { + "url": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.IsURLWithHTTPS, + }, + }, + }, + }, + + "service_bus_queue_endpoint": { + Type: schema.TypeList, + MaxItems: 1, + Optional: true, + ConflictsWith: RemoveFromStringArray(getEnpointTypes(), "service_bus_queue_endpoint"), + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "service_bus_queue_id": { Type: schema.TypeString, Required: true, ValidateFunc: azure.ValidateResourceID, @@ -159,17 +195,33 @@ func resourceArmEventGridEventSubscription() *schema.Resource { }, }, - "webhook_endpoint": { + "service_bus_topic_endpoint": { Type: schema.TypeList, MaxItems: 1, Optional: true, - ConflictsWith: RemoveFromStringArray(getEnpointTypes(), "hybrid_connection_endpoint"), + ConflictsWith: RemoveFromStringArray(getEnpointTypes(), "service_bus_topic_endpoint"), Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ - "url": { + "service_bus_queue_id": { Type: schema.TypeString, Required: true, - ValidateFunc: validation.IsURLWithHTTPS, + ValidateFunc: azure.ValidateResourceID, + }, + }, + }, + }, + + "azure_function_endpoint": { + Type: schema.TypeList, + MaxItems: 1, + Optional: true, + ConflictsWith: RemoveFromStringArray(getEnpointTypes(), "azure_function_endpoint"), + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "azure_function_id": { + Type: schema.TypeString, + Required: true, + ValidateFunc: azure.ValidateResourceID, }, }, }, @@ -207,8 +259,9 @@ func resourceArmEventGridEventSubscription() *schema.Resource { }, }, - "advanced_filter_scalar": { + "advanced_filter": { Type: schema.TypeList, + MaxItems: 5, Optional: true, Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ @@ -227,46 +280,6 @@ func resourceArmEventGridEventSubscription() *schema.Resource { string(eventgrid.OperatorTypeNumberGreaterThanOrEquals), string(eventgrid.OperatorTypeNumberLessThan), string(eventgrid.OperatorTypeNumberLessThanOrEquals), - }, false), - }, - "value": { - Type: schema.TypeString, - Required: true, - DiffSuppressFunc: func(k, old, new string, d *schema.ResourceData) bool { - if strings.EqualFold(old, new) { - return true - } else if o, err := strconv.ParseFloat(old, 64); err == nil { - n, err := strconv.ParseFloat(new, 64) - if err == nil { - return o == n - } - } else if o, err := strconv.ParseBool(old); err == nil { - n, err := strconv.ParseBool(new) - if err == nil { - return o == n - } - } - return false - }, - }, - }, - }, - }, - - "advanced_filter_array": { - Type: schema.TypeList, - Optional: true, - Elem: &schema.Resource{ - Schema: map[string]*schema.Schema{ - "key": { - Type: schema.TypeString, - Required: true, - ValidateFunc: validation.StringIsNotEmpty, - }, - "operator_type": { - Type: schema.TypeString, - Required: true, - ValidateFunc: validation.StringInSlice([]string{ string(eventgrid.OperatorTypeNumberIn), string(eventgrid.OperatorTypeNumberNotIn), string(eventgrid.OperatorTypeStringBeginsWith), @@ -276,30 +289,22 @@ func resourceArmEventGridEventSubscription() *schema.Resource { string(eventgrid.OperatorTypeStringNotIn), }, false), }, + "value": { + Type: schema.TypeString, + Optional: true, + ConflictsWith: []string{"advanced_filter.values"}, + DiffSuppressFunc: AdvancedFilterDiffSuppressFunc, + }, "values": { - Type: schema.TypeList, - MinItems: 1, - MaxItems: 5, - Required: true, + Type: schema.TypeList, + MinItems: 1, + MaxItems: 5, + Optional: true, + ConflictsWith: []string{"advanced_filter.value"}, Elem: &schema.Schema{ - Type: schema.TypeString, - ValidateFunc: validation.StringIsNotEmpty, - DiffSuppressFunc: func(k, old, new string, d *schema.ResourceData) bool { - if strings.EqualFold(old, new) { - return true - } else if o, err := strconv.ParseFloat(old, 64); err == nil { - n, err := strconv.ParseFloat(new, 64) - if err == nil { - return o == n - } - } else if o, err := strconv.ParseBool(old); err == nil { - n, err := strconv.ParseBool(new) - if err == nil { - return o == n - } - } - return false - }, + Type: schema.TypeString, + ValidateFunc: validation.StringIsNotEmpty, + DiffSuppressFunc: AdvancedFilterDiffSuppressFunc, }, }, }, @@ -354,6 +359,12 @@ func resourceArmEventGridEventSubscription() *schema.Resource { Type: schema.TypeString, }, }, + + "topic_name": { + Type: schema.TypeString, + Optional: true, + Computed: true, + }, }, } } @@ -406,10 +417,6 @@ func resourceArmEventGridEventSubscriptionCreateUpdate(d *schema.ResourceData, m ExpirationTimeUtc: &expirationTime, } - if v, ok := d.GetOk("topic_name"); ok { - eventSubscriptionProperties.Topic = utils.String(v.(string)) - } - eventSubscription := eventgrid.EventSubscription{ EventSubscriptionProperties: &eventSubscriptionProperties, } @@ -474,11 +481,6 @@ func resourceArmEventGridEventSubscriptionRead(d *schema.ResourceData, meta inte return fmt.Errorf("Error setting `storage_queue_endpoint` for EventGrid Event Subscription %q (Scope %q): %s", id.Name, id.Scope, err) } } - if serviceBusQueueEndpoint, ok := props.Destination.AsServiceBusQueueEventSubscriptionDestination(); ok { - if err := d.Set("service_bus_queue_endpoint", flattenEventGridEventSubscriptionServiceBusQueueEndpoint(serviceBusQueueEndpoint)); err != nil { - return fmt.Errorf("Error setting `service_bus_queue_endpoint` for EventGrid Event Subscription %q (Scope %q): %s", name, scope, err) - } - } if eventHubEndpoint, ok := props.Destination.AsEventHubEventSubscriptionDestination(); ok { if err := d.Set("eventhub_endpoint", flattenEventGridEventSubscriptionEventHubEndpoint(eventHubEndpoint)); err != nil { return fmt.Errorf("Error setting `eventhub_endpoint` for EventGrid Event Subscription %q (Scope %q): %s", id.Name, id.Scope, err) @@ -489,6 +491,21 @@ func resourceArmEventGridEventSubscriptionRead(d *schema.ResourceData, meta inte return fmt.Errorf("Error setting `hybrid_connection_endpoint` for EventGrid Event Subscription %q (Scope %q): %s", id.Name, id.Scope, err) } } + if serviceBusQueueEndpoint, ok := props.Destination.AsServiceBusQueueEventSubscriptionDestination(); ok { + if err := d.Set("service_bus_queue_endpoint", flattenEventGridEventSubscriptionServiceBusQueueEndpoint(serviceBusQueueEndpoint)); err != nil { + return fmt.Errorf("Error setting `service_bus_queue_endpoint` for EventGrid Event Subscription %q (Scope %q): %s", name, scope, err) + } + } + if serviceBusTopicEndpoint, ok := props.Destination.AsServiceBusTopicEventSubscriptionDestination(); ok { + if err := d.Set("service_bus_topic_endpoint", flattenEventGridEventSubscriptionServiceBusTopicEndpoint(serviceBusTopicEndpoint)); err != nil { + return fmt.Errorf("Error setting `service_bus_topic_endpoint` for EventGrid Event Subscription %q (Scope %q): %s", name, scope, err) + } + } + if azureFunctionEndpoint, ok := props.Destination.AsAzureFunctionEventSubscriptionDestination(); ok { + if err := d.Set("azure_function_endpoint", flattenEventGridEventSubscriptionAzureFunctionEndpoint(azureFunctionEndpoint)); err != nil { + return fmt.Errorf("Error setting `azure_function_endpoint` for EventGrid Event Subscription %q (Scope %q): %s", name, scope, err) + } + } if _, ok := props.Destination.AsWebHookEventSubscriptionDestination(); ok { fullURL, err := client.GetFullURL(ctx, id.Scope, id.Name) if err != nil { @@ -505,13 +522,9 @@ func resourceArmEventGridEventSubscriptionRead(d *schema.ResourceData, meta inte return fmt.Errorf("Error setting `subject_filter` for EventGrid Event Subscription %q (Scope %q): %s", id.Name, id.Scope, err) } - if err := d.Set("advanced_filter_scalar", flattenEventGridEventSubscriptionScalarAdvancedFilter(filter.AdvancedFilters)); err != nil { + if err := d.Set("advanced_filter", flattenEventGridEventSubscriptionAdvancedFilter(filter.AdvancedFilters)); err != nil { return fmt.Errorf("Error setting `advanced_filter_scalar` for EventGrid Event Subscription %q (Scope %q): %s", name, scope, err) } - - if err := d.Set("advanced_filter_array", flattenEventGridEventSubscriptionArrayAdvancedFilter(filter.AdvancedFilters)); err != nil { - return fmt.Errorf("Error setting `advanced_filter_array` for EventGrid Event Subscription %q (Scope %q): %s", name, scope, err) - } } if props.DeadLetterDestination != nil { @@ -589,10 +602,6 @@ func expandEventGridEventSubscriptionDestination(d *schema.ResourceData) eventgr return expandEventGridEventSubscriptionStorageQueueEndpoint(d) } - if _, ok := d.GetOk("service_bus_queue_endpoint"); ok { - return expandEventGridEventSubscriptionServiceBusQueueEndpoint(d) - } - if _, ok := d.GetOk("eventhub_endpoint"); ok { return expandEventGridEventSubscriptionEventHubEndpoint(d) } @@ -605,6 +614,18 @@ func expandEventGridEventSubscriptionDestination(d *schema.ResourceData) eventgr return expandEventGridEventSubscriptionWebhookEndpoint(d) } + if _, ok := d.GetOk("service_bus_queue_endpoint"); ok { + return expandEventGridEventSubscriptionServiceBusQueueEndpoint(d) + } + + if _, ok := d.GetOk("service_bus_topic_endpoint"); ok { + return expandEventGridEventSubscriptionServiceBusTopicEndpoint(d) + } + + if _, ok := d.GetOk("azure_function"); ok { + return expandEventGridEventSubscriptionAzureFunctionEndpoint(d) + } + return nil } @@ -623,19 +644,6 @@ func expandEventGridEventSubscriptionStorageQueueEndpoint(d *schema.ResourceData return storageQueueEndpoint } -func expandEventGridEventSubscriptionServiceBusQueueEndpoint(d *schema.ResourceData) eventgrid.BasicEventSubscriptionDestination { - props := d.Get("service_bus_queue_endpoint").([]interface{})[0].(map[string]interface{}) - serviceBusQueueID := props["service_bus_queue_id"].(string) - - storageQueueEndpoint := eventgrid.ServiceBusQueueEventSubscriptionDestination{ - EndpointType: eventgrid.EndpointTypeServiceBusQueue, - ServiceBusQueueEventSubscriptionDestinationProperties: &eventgrid.ServiceBusQueueEventSubscriptionDestinationProperties{ - ResourceID: &serviceBusQueueID, - }, - } - return storageQueueEndpoint -} - func expandEventGridEventSubscriptionEventHubEndpoint(d *schema.ResourceData) eventgrid.BasicEventSubscriptionDestination { props := d.Get("eventhub_endpoint").([]interface{})[0].(map[string]interface{}) eventHubID := props["eventhub_id"].(string) @@ -675,6 +683,45 @@ func expandEventGridEventSubscriptionWebhookEndpoint(d *schema.ResourceData) eve return webhookEndpoint } +func expandEventGridEventSubscriptionServiceBusQueueEndpoint(d *schema.ResourceData) eventgrid.BasicEventSubscriptionDestination { + props := d.Get("service_bus_queue_endpoint").([]interface{})[0].(map[string]interface{}) + serviceBusQueueID := props["service_bus_queue_id"].(string) + + serviceBusQueueEndpoint := eventgrid.ServiceBusQueueEventSubscriptionDestination{ + EndpointType: eventgrid.EndpointTypeServiceBusQueue, + ServiceBusQueueEventSubscriptionDestinationProperties: &eventgrid.ServiceBusQueueEventSubscriptionDestinationProperties{ + ResourceID: &serviceBusQueueID, + }, + } + return serviceBusQueueEndpoint +} + +func expandEventGridEventSubscriptionServiceBusTopicEndpoint(d *schema.ResourceData) eventgrid.BasicEventSubscriptionDestination { + props := d.Get("service_bus_topic_endpoint").([]interface{})[0].(map[string]interface{}) + serviceBusTopicID := props["service_bus_topic_id"].(string) + + serviceBusTopicEndpoint := eventgrid.ServiceBusTopicEventSubscriptionDestination{ + EndpointType: eventgrid.EndpointTypeServiceBusTopic, + ServiceBusTopicEventSubscriptionDestinationProperties: &eventgrid.ServiceBusTopicEventSubscriptionDestinationProperties{ + ResourceID: &serviceBusTopicID, + }, + } + return serviceBusTopicEndpoint +} + +func expandEventGridEventSubscriptionAzureFunctionEndpoint(d *schema.ResourceData) eventgrid.BasicEventSubscriptionDestination { + props := d.Get("azure_function_endpoint").([]interface{})[0].(map[string]interface{}) + azureFunctionResourceID := props["azure_function_id"].(string) + + storageQueueEndpoint := eventgrid.AzureFunctionEventSubscriptionDestination{ + EndpointType: eventgrid.EndpointTypeAzureFunction, + AzureFunctionEventSubscriptionDestinationProperties: &eventgrid.AzureFunctionEventSubscriptionDestinationProperties{ + ResourceID: &azureFunctionResourceID, + }, + } + return storageQueueEndpoint +} + func expandEventGridEventSubscriptionFilter(d *schema.ResourceData) (*eventgrid.EventSubscriptionFilter, error) { filter := &eventgrid.EventSubscriptionFilter{} @@ -693,45 +740,61 @@ func expandEventGridEventSubscriptionFilter(d *schema.ResourceData) (*eventgrid. filter.IsSubjectCaseSensitive = &caseSensitive } - advancedFilters := make([]eventgrid.BasicAdvancedFilter, 0) - - if advancedFilterScalar, ok := d.GetOk("advanced_filter_scalar"); ok { - for _, v := range advancedFilterScalar.([]interface{}) { + if advancedFilter, ok := d.GetOk("advanced_filter"); ok { + advancedFilters := make([]eventgrid.BasicAdvancedFilter, 0) + for _, v := range advancedFilter.([]interface{}) { config := v.(map[string]interface{}) - key := config["key"].(string) - operatorType := config["operator_type"].(string) - value := config["value"].(string) - - filter, err := expandScalarAdvancedFilter(key, operatorType, value) - if err != nil { + if filter, err := expandAdvancedFilter(config); err == nil { + advancedFilters = append(advancedFilters, filter) + } else { return nil, err } - - advancedFilters = append(advancedFilters, filter) } + filter.AdvancedFilters = &advancedFilters } - if advancedFilterArray, ok := d.GetOk("advanced_filter_array"); ok { - for _, v := range advancedFilterArray.([]interface{}) { - config := v.(map[string]interface{}) - - key := config["key"].(string) - operatorType := config["operator_type"].(string) - values := utils.ExpandStringSlice(config["values"].([]interface{})) + return filter, nil +} - filter, err := expandArrayAdvancedFilter(key, operatorType, *values) - if err != nil { - return nil, err - } +func expandAdvancedFilter(config map[string]interface{}) (eventgrid.BasicAdvancedFilter, error) { + operatorType := config["operator_type"].(string) + key := config["key"].(string) + value := config["value"].(string) + values := utils.ExpandStringSlice(config["values"].([]interface{})) - advancedFilters = append(advancedFilters, filter) + switch operatorType { + case string(eventgrid.OperatorTypeAdvancedFilter), + string(eventgrid.OperatorTypeBoolEquals), + string(eventgrid.OperatorTypeNumberGreaterThan), + string(eventgrid.OperatorTypeNumberGreaterThanOrEquals), + string(eventgrid.OperatorTypeNumberLessThan), + string(eventgrid.OperatorTypeNumberLessThanOrEquals): + if values != nil && len(*values) > 0 { + return nil, fmt.Errorf("Conflicting field for `advanced_filter` (key=%s, operator_type=%s): values", key, operatorType) + } + if &value == nil || len(value) == 0 { + return nil, fmt.Errorf("Missing value for`advanced_filter` (key=%s, operator_type=%s, value=%q)", key, operatorType, value) + } + return expandScalarAdvancedFilter(key, operatorType, value) + case + string(eventgrid.OperatorTypeNumberIn), + string(eventgrid.OperatorTypeNumberNotIn), + string(eventgrid.OperatorTypeStringBeginsWith), + string(eventgrid.OperatorTypeStringContains), + string(eventgrid.OperatorTypeStringEndsWith), + string(eventgrid.OperatorTypeStringIn), + string(eventgrid.OperatorTypeStringNotIn): + if &value != nil && len(value) > 0 { + return nil, fmt.Errorf("Conflicting field for `advanced_filter` (key=%s, operator_type=%s): value", key, operatorType) + } + if len(*values) == 0 { + return nil, fmt.Errorf("Missing values for `advanced_filter` (key=%s, operator_type=%s, values=%q)", key, operatorType, values) } + return expandArrayAdvancedFilter(key, operatorType, *values) + default: + return nil, fmt.Errorf("Invalid `advanced_filter` operator_type %s used", operatorType) } - - filter.AdvancedFilters = &advancedFilters - - return filter, nil } func expandArrayAdvancedFilter(key string, operatorType string, values []string) (eventgrid.BasicAdvancedFilter, error) { @@ -739,21 +802,21 @@ func expandArrayAdvancedFilter(key string, operatorType string, values []string) case string(eventgrid.OperatorTypeNumberIn): var numbers = []float64{} for _, v := range values { - f, err := strconv.ParseFloat(v, 64) - if err != nil { - return nil, fmt.Errorf("Value %q is not a float number", v) + if f, err := ParseFloat(v); err == nil { + numbers = append(numbers, *f) + } else { + return nil, err } - numbers = append(numbers, f) } return eventgrid.NumberInAdvancedFilter{Key: &key, OperatorType: eventgrid.OperatorTypeNumberIn, Values: &numbers}, nil case string(eventgrid.OperatorTypeNumberNotIn): var numbers = []float64{} for _, v := range values { - f, err := strconv.ParseFloat(v, 64) - if err != nil { - return nil, fmt.Errorf("Value %q is not a float number", v) + if f, err := ParseFloat(v); err == nil { + numbers = append(numbers, *f) + } else { + return nil, err } - numbers = append(numbers, f) } return eventgrid.NumberNotInAdvancedFilter{Key: &key, OperatorType: eventgrid.OperatorTypeNumberIn, Values: &numbers}, nil case string(eventgrid.OperatorTypeStringIn): @@ -767,7 +830,7 @@ func expandArrayAdvancedFilter(key string, operatorType string, values []string) case string(eventgrid.OperatorTypeStringContains): return eventgrid.StringContainsAdvancedFilter{Key: &key, OperatorType: eventgrid.OperatorTypeStringContains, Values: &values}, nil default: - return eventgrid.AdvancedFilter{Key: &key, OperatorType: eventgrid.OperatorTypeAdvancedFilter}, nil + return nil, nil } } @@ -804,7 +867,7 @@ func expandScalarAdvancedFilter(key string, operatorType string, value string) ( } return nil, fmt.Errorf("Value %q is not a bool", value) default: - return eventgrid.AdvancedFilter{Key: &key, OperatorType: eventgrid.OperatorTypeAdvancedFilter}, nil + return nil, nil } } @@ -853,20 +916,20 @@ func flattenEventGridEventSubscriptionStorageQueueEndpoint(input *eventgrid.Stor return []interface{}{result} } -func flattenEventGridEventSubscriptionServiceBusQueueEndpoint(input *eventgrid.ServiceBusQueueEventSubscriptionDestination) []interface{} { +func flattenEventGridEventSubscriptionEventHubEndpoint(input *eventgrid.EventHubEventSubscriptionDestination) []interface{} { if input == nil { return nil } result := make(map[string]interface{}) if input.ResourceID != nil { - result["service_bus_queue_id"] = *input.ResourceID + result["eventhub_id"] = *input.ResourceID } return []interface{}{result} } -func flattenEventGridEventSubscriptionEventHubEndpoint(input *eventgrid.EventHubEventSubscriptionDestination) []interface{} { +func flattenEventGridEventSubscriptionHybridConnectionEndpoint(input *eventgrid.HybridConnectionEventSubscriptionDestination) []interface{} { if input == nil { return nil } @@ -879,27 +942,53 @@ func flattenEventGridEventSubscriptionEventHubEndpoint(input *eventgrid.EventHub return []interface{}{result} } -func flattenEventGridEventSubscriptionHybridConnectionEndpoint(input *eventgrid.HybridConnectionEventSubscriptionDestination) []interface{} { +func flattenEventGridEventSubscriptionWebhookEndpoint(input *eventgrid.EventSubscriptionFullURL) []interface{} { + if input == nil { + return nil + } + result := make(map[string]interface{}) + + if input.EndpointURL != nil { + result["url"] = *input.EndpointURL + } + + return []interface{}{result} +} + +func flattenEventGridEventSubscriptionServiceBusQueueEndpoint(input *eventgrid.ServiceBusQueueEventSubscriptionDestination) []interface{} { if input == nil { return nil } result := make(map[string]interface{}) if input.ResourceID != nil { - result["eventhub_id"] = *input.ResourceID + result["service_bus_queue_id"] = *input.ResourceID } return []interface{}{result} } -func flattenEventGridEventSubscriptionWebhookEndpoint(input *eventgrid.EventSubscriptionFullURL) []interface{} { +func flattenEventGridEventSubscriptionServiceBusTopicEndpoint(input *eventgrid.ServiceBusTopicEventSubscriptionDestination) []interface{} { if input == nil { return nil } result := make(map[string]interface{}) - if input.EndpointURL != nil { - result["url"] = *input.EndpointURL + if input.ResourceID != nil { + result["service_bus_topic_id"] = *input.ResourceID + } + + return []interface{}{result} +} + +func flattenEventGridEventSubscriptionAzureFunctionEndpoint(input *eventgrid.AzureFunctionEventSubscriptionDestination) []interface{} { + if input == nil { + return nil + } + result := make(map[string]interface{}) + + if input.ResourceID != nil { + result["azure_function_id"] = *input.ResourceID } return []interface{}{result} @@ -926,7 +1015,7 @@ func flattenEventGridEventSubscriptionSubjectFilter(filter *eventgrid.EventSubsc return []interface{}{result} } -func flattenEventGridEventSubscriptionScalarAdvancedFilter(input *[]eventgrid.BasicAdvancedFilter) []interface{} { +func flattenEventGridEventSubscriptionAdvancedFilter(input *[]eventgrid.BasicAdvancedFilter) []interface{} { results := make([]interface{}, 0) if input == nil { return results @@ -936,6 +1025,7 @@ func flattenEventGridEventSubscriptionScalarAdvancedFilter(input *[]eventgrid.Ba var key interface{} var operatorType eventgrid.OperatorType var value interface{} + var values interface{} switch f := item.(type) { case eventgrid.AdvancedFilter: @@ -957,37 +1047,6 @@ func flattenEventGridEventSubscriptionScalarAdvancedFilter(input *[]eventgrid.Ba key = *f.Key operatorType = f.OperatorType value = strconv.FormatFloat(*f.Value, 'f', -1, 64) - case eventgrid.NumberLessThanOrEqualsAdvancedFilter: - key = *f.Key - operatorType = f.OperatorType - value = strconv.FormatFloat(*f.Value, 'f', -1, 64) - } - - results = append(results, map[string]interface{}{ - "key": key, - "operator_type": operatorType, - "value": value, - }) - } - - return results -} - -func flattenEventGridEventSubscriptionArrayAdvancedFilter(input *[]eventgrid.BasicAdvancedFilter) []interface{} { - results := make([]interface{}, 0) - if input == nil { - return results - } - - for _, item := range *input { - var key interface{} - var operatorType eventgrid.OperatorType - var values []interface{} - - switch f := item.(type) { - case eventgrid.AdvancedFilter: - key = *f.Key - operatorType = f.OperatorType case eventgrid.NumberInAdvancedFilter: key = *f.Key operatorType = f.OperatorType @@ -1026,11 +1085,16 @@ func flattenEventGridEventSubscriptionArrayAdvancedFilter(input *[]eventgrid.Bas key = *f.Key operatorType = f.OperatorType values = utils.FlattenStringSlice(f.Values) + case eventgrid.NumberLessThanOrEqualsAdvancedFilter: + key = *f.Key + operatorType = f.OperatorType + value = strconv.FormatFloat(*f.Value, 'f', -1, 64) } results = append(results, map[string]interface{}{ "key": key, "operator_type": operatorType, + "value": value, "values": values, }) } diff --git a/azurerm/internal/services/eventgrid/tests/resource_arm_eventgrid_event_subscription_test.go b/azurerm/internal/services/eventgrid/tests/resource_arm_eventgrid_event_subscription_test.go index 695d018cb4d5..23348da42546 100644 --- a/azurerm/internal/services/eventgrid/tests/resource_arm_eventgrid_event_subscription_test.go +++ b/azurerm/internal/services/eventgrid/tests/resource_arm_eventgrid_event_subscription_test.go @@ -44,7 +44,7 @@ func TestAccAzureRMEventGridEventSubscription_eventhub(t *testing.T) { Config: testAccAzureRMEventGridEventSubscription_eventhub(data), Check: resource.ComposeTestCheckFunc( testCheckAzureRMEventGridEventSubscriptionExists(data.ResourceName), - resource.TestCheckResourceAttr(data.ResourceName, "event_delivery_schema", "CloudEventV01Schema"), + resource.TestCheckResourceAttr(data.ResourceName, "event_delivery_schema", "CloudEventSchemaV1_0"), resource.TestCheckResourceAttr(data.ResourceName, "eventhub_endpoint.#", "1"), ), }, @@ -65,7 +65,7 @@ func TestAccAzureRMEventGridEventSubscription_serviceBusQueue(t *testing.T) { Config: testAccAzureRMEventGridEventSubscription_serviceBusQueue(data), Check: resource.ComposeTestCheckFunc( testCheckAzureRMEventGridEventSubscriptionExists(data.ResourceName), - resource.TestCheckResourceAttr(data.ResourceName, "event_delivery_schema", "CloudEventV01Schema"), + resource.TestCheckResourceAttr(data.ResourceName, "event_delivery_schema", "CloudEventSchemaV1_0"), resource.TestCheckResourceAttr(data.ResourceName, "service_bus_queue_endpoint.#", "1"), ), }, @@ -74,6 +74,48 @@ func TestAccAzureRMEventGridEventSubscription_serviceBusQueue(t *testing.T) { }) } +func TestAccAzureRMEventGridEventSubscription_serviceBusTopic(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_eventgrid_event_subscription", "test") + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acceptance.PreCheck(t) }, + Providers: acceptance.SupportedProviders, + CheckDestroy: testCheckAzureRMEventGridEventSubscriptionDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAzureRMEventGridEventSubscription_serviceBusTopic(data), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMEventGridEventSubscriptionExists(data.ResourceName), + resource.TestCheckResourceAttr(data.ResourceName, "event_delivery_schema", "CloudEventSchemaV1_0"), + resource.TestCheckResourceAttr(data.ResourceName, "service_bus_topic_endpoint.#", "1"), + ), + }, + data.ImportStep(), + }, + }) +} + +func TestAccAzureRMEventGridEventSubscription_azureFunction(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_eventgrid_event_subscription", "test") + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acceptance.PreCheck(t) }, + Providers: acceptance.SupportedProviders, + CheckDestroy: testCheckAzureRMEventGridEventSubscriptionDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAzureRMEventGridEventSubscription_azureFunction(data), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMEventGridEventSubscriptionExists(data.ResourceName), + resource.TestCheckResourceAttr(data.ResourceName, "event_delivery_schema", "CloudEventSchemaV1_0"), + resource.TestCheckResourceAttr(data.ResourceName, "azure_function_endpoint.#", "1"), + ), + }, + data.ImportStep(), + }, + }) +} + func TestAccAzureRMEventGridEventSubscription_update(t *testing.T) { data := acceptance.BuildTestData(t, "azurerm_eventgrid_event_subscription", "test") @@ -149,9 +191,12 @@ func TestAccAzureRMEventGridEventSubscription_advancedFilter(t *testing.T) { Config: testAccAzureRMEventGridEventSubscription_advancedFilter(data), Check: resource.ComposeTestCheckFunc( testCheckAzureRMEventGridEventSubscriptionExists(data.ResourceName), - resource.TestCheckResourceAttr(data.ResourceName, "advanced_filter_array.0.key", "topic"), - resource.TestCheckResourceAttr(data.ResourceName, "advanced_filter_array.0.operator_type", "Microsoft.Storage.BlobDeleted"), - resource.TestCheckResourceAttr(data.ResourceName, "advanced_filter_array.0.value", "topic_prefix"), + resource.TestCheckResourceAttr(data.ResourceName, "advanced_filter_array.0.key", "data.filesite"), + resource.TestCheckResourceAttr(data.ResourceName, "advanced_filter_array.0.operator_type", "NumberLessThan"), + resource.TestCheckResourceAttr(data.ResourceName, "advanced_filter_array.0.value", "42.0"), + resource.TestCheckResourceAttr(data.ResourceName, "advanced_filter_array.1.key", "topic"), + resource.TestCheckResourceAttr(data.ResourceName, "advanced_filter_array.1.operator_type", "StringBeginsWith"), + resource.TestCheckResourceAttr(data.ResourceName, "advanced_filter_array.1.value", "topic_prefix"), ), }, data.ImportStep(), @@ -389,7 +434,7 @@ resource "azurerm_eventhub" "test" { resource "azurerm_eventgrid_event_subscription" "test" { name = "acctesteg-%d" scope = azurerm_resource_group.test.id - event_delivery_schema = "CloudEventV01Schema" + event_delivery_schema = "CloudEventSchemaV1_0" eventhub_endpoint { eventhub_id = azurerm_eventhub.test.id @@ -426,7 +471,7 @@ resource "azurerm_servicebus_queue" "test" { resource "azurerm_eventgrid_event_subscription" "test" { name = "acctesteg-%d" scope = azurerm_resource_group.test.id - event_delivery_schema = "CloudEventV01Schema" + event_delivery_schema = "CloudEventSchemaV1_0" service_bus_queue_endpoint { service_bus_queue_id = "${azurerm_servicebus_queue.test.id}" @@ -435,6 +480,97 @@ resource "azurerm_eventgrid_event_subscription" "test" { `, data.RandomInteger, data.Locations.Primary, data.RandomInteger, data.RandomInteger, data.RandomInteger) } +func testAccAzureRMEventGridEventSubscription_serviceBusTopic(data acceptance.TestData) string { + return fmt.Sprintf(` +provider "azurerm" { + features {} +} + +resource "azurerm_resource_group" "test" { + name = "acctestRG-%d" + location = "%s" +} + +resource "azurerm_servicebus_namespace" "example" { + name = "acctestservicebusnamespace-%d" + location = azurerm_resource_group.test.location + resource_group_name = azurerm_resource_group.test.name + sku = "Basic" +} + +resource "azurerm_servicebus_topic" "test" { + name = "acctestservicebustopic-%d" + resource_group_name = azurerm_resource_group.test.name + namespace_name = azurerm_servicebus_namespace.example.name + enable_partitioning = true +} + +resource "azurerm_eventgrid_event_subscription" "test" { + name = "acctesteg-%d" + scope = azurerm_resource_group.test.id + event_delivery_schema = "CloudEventSchemaV1_0" + + service_bus_topic_endpoint { + service_bus_topic_id = "${azurerm_servicebus_topic.test.id}" + } +} +`, data.RandomInteger, data.Locations.Primary, data.RandomInteger, data.RandomInteger, data.RandomInteger) +} + +func testAccAzureRMEventGridEventSubscription_azureFunction(data acceptance.TestData) string { + return fmt.Sprintf(` +provider "azurerm" { + features {} +} + +resource "azurerm_resource_group" "test" { + name = "acctestRG-%d" + location = "%s" +} + +resource "azurerm_storage_account" "test" { + name = "acctestacc%s" + resource_group_name = azurerm_resource_group.test.name + location = azurerm_resource_group.test.location + account_tier = "Standard" + account_replication_type = "LRS" + + tags = { + environment = "staging" + } +} + +resource "azurerm_app_service_plan" "test" { + name = "acctestASP-%d" + location = azurerm_resource_group.test.location + resource_group_name = azurerm_resource_group.test.name + + sku { + tier = "Standard" + size = "S1" + } +} + +resource "azurerm_function_app" "test" { + name = "acctest-%d-func" + location = azurerm_resource_group.test.location + resource_group_name = azurerm_resource_group.test.name + app_service_plan_id = azurerm_app_service_plan.test.id + storage_connection_string = azurerm_storage_account.test.primary_connection_string +} + +resource "azurerm_eventgrid_event_subscription" "test" { + name = "acctesteg-%d" + scope = azurerm_resource_group.test.id + event_delivery_schema = "CloudEventSchemaV1_0" + + azure_function_endpoint { + azure_function_id = "${azurerm_function_app.test.id}" + } +} +`, data.RandomInteger, data.Locations.Primary, data.RandomString, data.RandomInteger, data.RandomInteger, data.RandomInteger) +} + func testAccAzureRMEventGridEventSubscription_filter(data acceptance.TestData) string { return fmt.Sprintf(` provider "azurerm" { @@ -520,13 +656,13 @@ resource "azurerm_eventgrid_event_subscription" "test" { queue_name = "${azurerm_storage_queue.test.name}" } - advanced_filter_scalar { + advanced_filter { key = "data.filesize" operator_type = "NumberLessThan" value = 42.0 } - advanced_filter_array { + advanced_filter { key = "topic" operator_type = "StringBeginsWith" value = ["topic_prefix"] diff --git a/website/docs/r/eventgrid_event_subscription.html.markdown b/website/docs/r/eventgrid_event_subscription.html.markdown index 307a7aaa9629..4f0e2df129d0 100644 --- a/website/docs/r/eventgrid_event_subscription.html.markdown +++ b/website/docs/r/eventgrid_event_subscription.html.markdown @@ -56,13 +56,9 @@ The following arguments are supported: * `scope` - (Required) Specifies the scope at which the EventGrid Event Subscription should be created. Changing this forces a new resource to be created. -* `expiration_time_utc` - (Optional) Specifies the expiration time of the event subscription (RFC RFC3339). +* `expiration_time_utc` - (Optional) Specifies the expiration time of the event subscription (Datetime Format `RFC 3339`). -* `event_delivery_schema` - (Optional) Specifies the event delivery schema for the event subscription. Possible values include: `EventGridSchema`, `CloudEventV01Schema`, `CustomInputSchema`. Defaults to `EventGridSchema`. Changing this forces a new resource to be created. - -* `topic_name` - (Optional) Specifies the name of the topic to associate with the event subscription. - -* `service_bus_queue_endpoint` - (Optional) A `service_bus_queue_endpoint` block as defined below. +* `event_delivery_schema` - (Optional) Specifies the event delivery schema for the event subscription. Possible values include: `EventGridSchema`, `CloudEventSchemaV1_0`, `CustomInputSchema`. Defaults to `EventGridSchema`. Changing this forces a new resource to be created. * `storage_queue_endpoint` - (Optional) A `storage_queue_endpoint` block as defined below. @@ -72,15 +68,19 @@ The following arguments are supported: * `webhook_endpoint` - (Optional) A `webhook_endpoint` block as defined below. -~> **NOTE:** One of `storage_queue_endpoint`, `eventhub_endpoint`, `hybrid_connection_endpoint` or `webhook_endpoint` must be specified. +* `service_bus_queue_endpoint` - (Optional) A `service_bus_queue_endpoint` block as defined below. + +* `service_bus_topic_endpoint` - (Optional) A `service_bus_topic_endpoint` block as defined below. + +* `azure_function_endpoint` - (Optional) A `azure_function_endpoint` block as defined below. + +~> **NOTE:** One of `storage_queue_endpoint`, `eventhub_endpoint`, `hybrid_connection_endpoint`, `webhook_endpoint`, `service_bus_queue_endpoint`, `service_bus_topic_endpoint` or `azure_function_endpoint` must be specified. * `included_event_types` - (Optional) A list of applicable event types that need to be part of the event subscription. * `subject_filter` - (Optional) A `subject_filter` block as defined below. -* `advanced_filter_scalar` - (Optional) A `advanced_filter_scalar` block as defined below. - -* `advanced_filter_array` - (Optional) A `advanced_filter_array` block as defined below. +* `advanced_filter` - (Optional) A `advanced_filter` block as defined below. * `storage_blob_dead_letter_destination` - (Optional) A `storage_blob_dead_letter_destination` block as defined below. @@ -90,12 +90,6 @@ The following arguments are supported: --- -A `service_bus_queue_endpoint` supports the following: - -* `service_bus_queue_id` - (Required) Specifies the id where the service bus queue is located. - ---- - A `storage_queue_endpoint` supports the following: * `storage_account_id` - (Required) Specifies the id of the storage account id where the storage queue is located. @@ -114,39 +108,51 @@ A `hybrid_connection_endpoint` supports the following: * `hybrid_connection_id` - (Required) Specifies the id of the hybrid connection where the Event Subscription will receive events. +--- + A `webhook_endpoint` supports the following: * `url` - (Required) Specifies the url of the webhook where the Event Subscription will receive events. --- -A `subject_filter` supports the following: +A `service_bus_queue_endpoint` supports the following: -* `subject_begins_with` - (Optional) A string to filter events for an event subscription based on a resource path prefix. +* `service_bus_queue_id` - (Required) Specifies the id where the service bus queue is located. -* `subject_ends_with` - (Optional) A string to filter events for an event subscription based on a resource path suffix. +--- -* `case_sensitive` - (Optional) Specifies if `subject_begins_with` and `subject_ends_with` case sensitive. This value defaults to `false`. +A `service_bus_topic_endpoint` supports the following: + +* `service_bus_topic_id` - (Required) Specifies the id where the service bus topic is located. --- -A `advanced_filter_scalar` supports the following: +A `azure_function_endpoint` supports the following: -* `key` - (Required) Specifies the field/property in the event based on which you want to filter. +* `azure_function_id` - (Required) Specifies the id where the azure function is located. -* `operator_type` - (Required) Specifies the operator used for the filter. Must be one of the following operators: `NumberLessThan`, `NumberGreaterThan`, `NumberLessThanOrEquals`, `NumberGreaterThanOrEquals`, `BoolEquals`. +--- -* `value` - (Required) Specifies the filter value. +A `subject_filter` supports the following: + +* `subject_begins_with` - (Optional) A string to filter events for an event subscription based on a resource path prefix. + +* `subject_ends_with` - (Optional) A string to filter events for an event subscription based on a resource path suffix. + +* `case_sensitive` - (Optional) Specifies if `subject_begins_with` and `subject_ends_with` case sensitive. This value defaults to `false`. --- -A `advanced_filter_array` supports the following: +A `advanced_filter` supports the following: + +* `key` - (Required) Specifies the field in the event data that you're using for filtering. It can be a number, boolean, or string. -* `key` - (Required) Specifies the field/property in the event based on which you want to filter. +* `operator_type` - (Required) Specifies the type of comparison. Must be one of the following operators: `NumberLessThan`, `NumberGreaterThan`, `NumberLessThanOrEquals`, `NumberGreaterThanOrEquals`, `BoolEquals`, `NumberIn`, `NumberNotIn`, `StringIn`, `StringNotIn`, `StringBeginsWith`, `StringEndsWith`. `StringContains`. -* `operator_type` - (Required) Specifies the operator used for the filter. Must be one of the following operators: `NumberIn`, `NumberNotIn`, `StringIn`, `StringNotIn`, `StringBeginsWith`, `StringEndsWith`. `StringContains` +* `value` or `values` - (Required) Specifies the value or values to compare to the key. -* `values` - (Required) Specifies the set of filter values (min items: 0, max items: 5) +~> **NOTE:** A maximum of 5 advanced filters are allowed. --- @@ -170,9 +176,9 @@ The following attributes are exported: * `id` - The ID of the EventGrid Event Subscription. -## Timeouts - +* `topic_name` - (Optional) Specifies the name of the topic to associate with the event subscription. +## Timeouts The `timeouts` block allows you to specify [timeouts](https://www.terraform.io/docs/configuration/resources.html#timeouts) for certain actions: