diff --git a/azurerm/internal/services/eventgrid/eventgrid_event_subscription_resource.go b/azurerm/internal/services/eventgrid/eventgrid_event_subscription_resource.go index b133082a6639..7162eaef445d 100644 --- a/azurerm/internal/services/eventgrid/eventgrid_event_subscription_resource.go +++ b/azurerm/internal/services/eventgrid/eventgrid_event_subscription_resource.go @@ -3,6 +3,8 @@ package eventgrid import ( "fmt" "log" + "strconv" + "strings" "time" "github.com/Azure/azure-sdk-for-go/services/preview/eventgrid/mgmt/2020-04-01-preview/eventgrid" @@ -228,6 +230,52 @@ func resourceArmEventGridEventSubscription() *schema.Resource { }, }, + "advanced_filter": { + Type: schema.TypeList, + Optional: true, + Computed: true, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "key": { + Type: schema.TypeString, + Optional: true, + ValidateFunc: validation.StringIsNotEmpty, + }, + "value": { + Type: schema.TypeString, + Optional: true, + ValidateFunc: validation.StringIsNotEmpty, + }, + "values": { + Type: schema.TypeList, + Optional: true, + Elem: &schema.Schema{ + Type: schema.TypeString, + ValidateFunc: validation.StringIsNotEmpty, + }, + }, + "operator_type": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.StringInSlice([]string{ + string(eventgrid.OperatorTypeBoolEquals), + string(eventgrid.OperatorTypeNumberGreaterThan), + string(eventgrid.OperatorTypeNumberGreaterThanOrEquals), + string(eventgrid.OperatorTypeNumberIn), + string(eventgrid.OperatorTypeNumberLessThan), + string(eventgrid.OperatorTypeNumberLessThanOrEquals), + string(eventgrid.OperatorTypeNumberNotIn), + string(eventgrid.OperatorTypeStringBeginsWith), + string(eventgrid.OperatorTypeStringContains), + string(eventgrid.OperatorTypeStringEndsWith), + string(eventgrid.OperatorTypeStringIn), + string(eventgrid.OperatorTypeStringNotIn), + }, false), + }, + }, + }, + }, + "storage_blob_dead_letter_destination": { Type: schema.TypeList, MaxItems: 1, @@ -308,6 +356,14 @@ func resourceArmEventGridEventSubscriptionCreateUpdate(d *schema.ResourceData, m filter := expandEventGridEventSubscriptionFilter(d) + advancedFilters, err := expandEventGridEventSubscriptionAdvancedFilter(d) + if err != nil { + return fmt.Errorf("Error creating/updating EventGrid Event Subscription %q (Scope %q): %s Advanced Filters", name, scope, err) + } + if advancedFilters != nil { + filter.AdvancedFilters = advancedFilters + } + expirationTime, err := expandEventGridExpirationTime(d) if err != nil { return fmt.Errorf("Error creating/updating EventGrid Event Subscription %q (Scope %q): %s", name, scope, err) @@ -434,6 +490,10 @@ func resourceArmEventGridEventSubscriptionRead(d *schema.ResourceData, meta inte if err := d.Set("subject_filter", flattenEventGridEventSubscriptionSubjectFilter(filter)); err != nil { 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", flattenEventGridEventSubscriptionAdvancedFilter(filter)); err != nil { + return fmt.Errorf("Error setting `advanced_filter` for EventGrid Event Subscription %q (Scope %q): %s", id.Name, id.Scope, err) + } + } if props.DeadLetterDestination != nil { @@ -626,6 +686,144 @@ func expandEventGridEventSubscriptionFilter(d *schema.ResourceData) *eventgrid.E return filter } +func expandEventGridEventSubscriptionAdvancedFilter(d *schema.ResourceData) (*[]eventgrid.BasicAdvancedFilter, error) { + advFilters := d.Get("advanced_filter").([]interface{}) + advancedFilters := make([]eventgrid.BasicAdvancedFilter, 0, len(advFilters)) + + for _, advFilter := range advFilters { + advfilterconfig := advFilter.(map[string]interface{}) + key := advfilterconfig["key"].(string) + operatorType := advfilterconfig["operator_type"].(string) + value := advfilterconfig["value"].(string) + values := utils.ExpandStringSlice(advfilterconfig["values"].([]interface{})) + + if strings.Compare(operatorType, string(eventgrid.OperatorTypeBoolEquals)) == 0 { + boolEquals := &eventgrid.BoolEqualsAdvancedFilter{} + boolValue, err := strconv.ParseBool(value) + if err != nil { + return nil, err + } + boolEquals.Value = &boolValue + boolEquals.Key = &key + boolEquals.OperatorType = eventgrid.OperatorTypeBoolEquals + advancedFilters = append(advancedFilters, boolEquals) + } + + if strings.Compare(operatorType, string(eventgrid.OperatorTypeNumberGreaterThan)) == 0 { + numberGreaterThan := &eventgrid.NumberGreaterThanAdvancedFilter{} + numberValue, err := strconv.ParseFloat(value, 64) + if err != nil { + return nil, err + } + numberGreaterThan.Value = &numberValue + numberGreaterThan.Key = &key + numberGreaterThan.OperatorType = eventgrid.OperatorTypeNumberGreaterThan + advancedFilters = append(advancedFilters, numberGreaterThan) + } + + if strings.Compare(operatorType, string(eventgrid.OperatorTypeNumberGreaterThanOrEquals)) == 0 { + numberGreaterThanEquals := &eventgrid.NumberGreaterThanOrEqualsAdvancedFilter{} + numberValue, err := strconv.ParseFloat(value, 64) + if err != nil { + return nil, err + } + numberGreaterThanEquals.Value = &numberValue + numberGreaterThanEquals.Key = &key + numberGreaterThanEquals.OperatorType = eventgrid.OperatorTypeNumberGreaterThanOrEquals + advancedFilters = append(advancedFilters, numberGreaterThanEquals) + } + + if strings.Compare(operatorType, string(eventgrid.OperatorTypeNumberIn)) == 0 { + numberIn := &eventgrid.NumberInAdvancedFilter{} + floatValues, err := sliceAtof(*values) + if err != nil { + return nil, err + } + numberIn.Values = &floatValues + numberIn.Key = &key + numberIn.OperatorType = eventgrid.OperatorTypeNumberIn + advancedFilters = append(advancedFilters, numberIn) + } + + if strings.Compare(operatorType, string(eventgrid.OperatorTypeNumberLessThan)) == 0 { + numberLessThan := &eventgrid.NumberLessThanAdvancedFilter{} + numberValue, err := strconv.ParseFloat(value, 64) + if err != nil { + return nil, err + } + numberLessThan.Value = &numberValue + numberLessThan.Key = &key + numberLessThan.OperatorType = eventgrid.OperatorTypeNumberLessThan + advancedFilters = append(advancedFilters, numberLessThan) + } + + if strings.Compare(operatorType, string(eventgrid.OperatorTypeNumberLessThanOrEquals)) == 0 { + numberLessThanEquals := &eventgrid.NumberLessThanOrEqualsAdvancedFilter{} + numberValue, err := strconv.ParseFloat(value, 64) + if err != nil { + return nil, err + } + numberLessThanEquals.Value = &numberValue + numberLessThanEquals.Key = &key + numberLessThanEquals.OperatorType = eventgrid.OperatorTypeNumberLessThanOrEquals + advancedFilters = append(advancedFilters, numberLessThanEquals) + } + + if strings.Compare(operatorType, string(eventgrid.OperatorTypeNumberNotIn)) == 0 { + numberNotIn := &eventgrid.NumberNotInAdvancedFilter{} + floatValues, err := sliceAtof(*values) + if err != nil { + return nil, err + } + numberNotIn.Values = &floatValues + numberNotIn.Key = &key + numberNotIn.OperatorType = eventgrid.OperatorTypeNumberNotIn + advancedFilters = append(advancedFilters, numberNotIn) + } + + if strings.Compare(operatorType, string(eventgrid.OperatorTypeStringBeginsWith)) == 0 { + stringBeginsWith := &eventgrid.StringBeginsWithAdvancedFilter{} + stringBeginsWith.Values = values + stringBeginsWith.Key = &key + stringBeginsWith.OperatorType = eventgrid.OperatorTypeStringBeginsWith + advancedFilters = append(advancedFilters, stringBeginsWith) + } + + if strings.Compare(operatorType, string(eventgrid.OperatorTypeStringContains)) == 0 { + stringContains := &eventgrid.StringContainsAdvancedFilter{} + stringContains.Values = values + stringContains.Key = &key + stringContains.OperatorType = eventgrid.OperatorTypeStringContains + advancedFilters = append(advancedFilters, stringContains) + } + + if strings.Compare(operatorType, string(eventgrid.OperatorTypeStringEndsWith)) == 0 { + stringEndsWith := &eventgrid.StringEndsWithAdvancedFilter{} + stringEndsWith.Values = values + stringEndsWith.Key = &key + stringEndsWith.OperatorType = eventgrid.OperatorTypeStringEndsWith + advancedFilters = append(advancedFilters, stringEndsWith) + } + + if strings.Compare(operatorType, string(eventgrid.OperatorTypeStringIn)) == 0 { + stringIn := &eventgrid.StringEndsWithAdvancedFilter{} + stringIn.Values = values + stringIn.Key = &key + stringIn.OperatorType = eventgrid.OperatorTypeStringIn + advancedFilters = append(advancedFilters, stringIn) + } + + if strings.Compare(operatorType, string(eventgrid.OperatorTypeStringNotIn)) == 0 { + stringNotIn := &eventgrid.StringEndsWithAdvancedFilter{} + stringNotIn.Values = values + stringNotIn.Key = &key + stringNotIn.OperatorType = eventgrid.OperatorTypeStringNotIn + advancedFilters = append(advancedFilters, stringNotIn) + } + } + return &advancedFilters, nil +} + func expandEventGridEventSubscriptionStorageBlobDeadLetterDestination(d *schema.ResourceData) eventgrid.BasicDeadLetterDestination { if v, ok := d.GetOk("storage_blob_dead_letter_destination"); ok { dest := v.([]interface{})[0].(map[string]interface{}) @@ -726,11 +924,87 @@ func flattenEventGridEventSubscriptionSubjectFilter(filter *eventgrid.EventSubsc result["subject_ends_with"] = *filter.SubjectEndsWith } - if filter.IsSubjectCaseSensitive != nil { - result["case_sensitive"] = *filter.IsSubjectCaseSensitive + return []interface{}{result} +} + +func flattenEventGridEventSubscriptionAdvancedFilter(filter *eventgrid.EventSubscriptionFilter) []interface{} { + if filter.AdvancedFilters == nil { + return nil } - return []interface{}{result} + filterResult := make([]interface{}, 0, len(*filter.AdvancedFilters)) + for _, advancedFilter := range *filter.AdvancedFilters { + advFilter := make(map[string]interface{}) + if boolEqualsFilter, _ := advancedFilter.AsBoolEqualsAdvancedFilter(); boolEqualsFilter != nil { + advFilter["key"] = boolEqualsFilter.Key + advFilter["operator_type"] = boolEqualsFilter.OperatorType + advFilter["value"] = strconv.FormatBool(*boolEqualsFilter.Value) + } + + if numberGreaterThanFilter, _ := advancedFilter.AsNumberGreaterThanAdvancedFilter(); numberGreaterThanFilter != nil { + advFilter["key"] = numberGreaterThanFilter.Key + advFilter["operator_type"] = numberGreaterThanFilter.OperatorType + advFilter["value"] = strconv.FormatFloat(*numberGreaterThanFilter.Value, 'f', 0, 64) + } + + if numberGreaterThanOrEqualsFilter, _ := advancedFilter.AsNumberGreaterThanOrEqualsAdvancedFilter(); numberGreaterThanOrEqualsFilter != nil { + advFilter["key"] = numberGreaterThanOrEqualsFilter.Key + advFilter["operator_type"] = numberGreaterThanOrEqualsFilter.OperatorType + advFilter["value"] = strconv.FormatFloat(*numberGreaterThanOrEqualsFilter.Value, 'f', 0, 64) + } + + if numberInFilter, _ := advancedFilter.AsNumberInAdvancedFilter(); numberInFilter != nil { + advFilter["key"] = numberInFilter.Key + advFilter["operator_type"] = numberInFilter.OperatorType + advFilter["values"] = sliceFtoa(*numberInFilter.Values) + } + + if numberLessThanFilter, _ := advancedFilter.AsNumberLessThanAdvancedFilter(); numberLessThanFilter != nil { + advFilter["key"] = numberLessThanFilter.Key + advFilter["operator_type"] = numberLessThanFilter.OperatorType + advFilter["value"] = strconv.FormatFloat(*numberLessThanFilter.Value, 'f', 0, 64) + } + + if numberLessThanOrEqualsFilter, _ := advancedFilter.AsNumberLessThanOrEqualsAdvancedFilter(); numberLessThanOrEqualsFilter != nil { + advFilter["key"] = numberLessThanOrEqualsFilter.Key + advFilter["operator_type"] = numberLessThanOrEqualsFilter.OperatorType + advFilter["value"] = strconv.FormatFloat(*numberLessThanOrEqualsFilter.Value, 'f', 0, 64) + } + + if numberNotInFilter, _ := advancedFilter.AsNumberNotInAdvancedFilter(); numberNotInFilter != nil { + advFilter["key"] = numberNotInFilter.Key + advFilter["operator_type"] = numberNotInFilter.OperatorType + advFilter["values"] = sliceFtoa(*numberNotInFilter.Values) + } + + if stringBeginsWithFilter, _ := advancedFilter.AsStringBeginsWithAdvancedFilter(); stringBeginsWithFilter != nil { + advFilter["key"] = stringBeginsWithFilter.Key + advFilter["operator_type"] = stringBeginsWithFilter.OperatorType + advFilter["values"] = stringBeginsWithFilter.Values + } + + if stringContainsFilter, _ := advancedFilter.AsStringContainsAdvancedFilter(); stringContainsFilter != nil { + advFilter["key"] = stringContainsFilter.Key + advFilter["operator_type"] = stringContainsFilter.OperatorType + advFilter["values"] = stringContainsFilter.Values + } + + if stringEndsWithFilter, _ := advancedFilter.AsStringEndsWithAdvancedFilter(); stringEndsWithFilter != nil { + advFilter["key"] = stringEndsWithFilter.Key + advFilter["operator_type"] = stringEndsWithFilter.OperatorType + advFilter["values"] = stringEndsWithFilter.Values + } + + if stringInFilter, _ := advancedFilter.AsStringInAdvancedFilter(); stringInFilter != nil { + advFilter["key"] = stringInFilter.Key + advFilter["operator_type"] = stringInFilter.OperatorType + advFilter["values"] = stringInFilter.Values + } + + filterResult = append(filterResult, advFilter) + } + + return filterResult } func flattenEventGridEventSubscriptionStorageBlobDeadLetterDestination(dest *eventgrid.StorageBlobDeadLetterDestination) []interface{} { @@ -763,3 +1037,25 @@ func flattenEventGridEventSubscriptionRetryPolicy(retryPolicy *eventgrid.RetryPo return []interface{}{result} } + +func sliceAtof(strvalues []string) ([]float64, error) { + floatvalues := make([]float64, 0, len(strvalues)) + for _, a := range strvalues { + i, err := strconv.ParseFloat(a, 64) + if err != nil { + return floatvalues, err + } + floatvalues = append(floatvalues, i) + } + return floatvalues, nil +} + +func sliceFtoa(floatvalues []float64) []string { + valuesText := []string{} + + for _, number := range floatvalues { + text := strconv.FormatFloat(number, 'f', 0, 64) + valuesText = append(valuesText, text) + } + return valuesText +} diff --git a/azurerm/internal/services/eventgrid/tests/eventgrid_event_subscription_resource_test.go b/azurerm/internal/services/eventgrid/tests/eventgrid_event_subscription_resource_test.go index dea09dffd18b..3c64cc2748c4 100644 --- a/azurerm/internal/services/eventgrid/tests/eventgrid_event_subscription_resource_test.go +++ b/azurerm/internal/services/eventgrid/tests/eventgrid_event_subscription_resource_test.go @@ -180,6 +180,30 @@ func TestAccAzureRMEventGridEventSubscription_filter(t *testing.T) { }) } +func TestAccAzureRMEventGridEventSubscription_advancedFilters(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_advancedFilter(data), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMEventGridEventSubscriptionExists(data.ResourceName), + resource.TestCheckResourceAttr(data.ResourceName, "included_event_types.0", "Microsoft.Storage.BlobCreated"), + resource.TestCheckResourceAttr(data.ResourceName, "included_event_types.1", "Microsoft.Storage.BlobDeleted"), + resource.TestCheckResourceAttr(data.ResourceName, "subject_filter.0.subject_ends_with", ".jpg"), + resource.TestCheckResourceAttr(data.ResourceName, "subject_filter.0.subject_begins_with", "test/test"), + resource.TestCheckResourceAttr(data.ResourceName, "advanced_filter.0.values.0", "contains"), + resource.TestCheckResourceAttr(data.ResourceName, "advanced_filter.0.values.1", "contains2"), + ), + }, + data.ImportStep(), + }, + }) +} func testCheckAzureRMEventGridEventSubscriptionDestroy(s *terraform.State) error { client := acceptance.AzureProvider.Meta().(*clients.Client).EventGrid.EventSubscriptionsClient ctx := acceptance.AzureProvider.Meta().(*clients.Client).StopContext @@ -536,3 +560,112 @@ resource "azurerm_eventgrid_event_subscription" "test" { } `, data.RandomInteger, data.Locations.Primary, data.RandomString, data.RandomInteger, data.RandomInteger) } + +func testAccAzureRMEventGridEventSubscription_advancedFilter(data acceptance.TestData) string { + return fmt.Sprintf(` +provider "azurerm" { + features {} +} + +resource "azurerm_resource_group" "test" { + name = "acctestRG-eg-%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_storage_queue" "test" { + name = "mysamplequeue-%d" + storage_account_name = "${azurerm_storage_account.test.name}" +} + +resource "azurerm_eventgrid_event_subscription" "test" { + name = "acctest-eg-%d" + scope = "${azurerm_resource_group.test.id}" + + storage_queue_endpoint { + storage_account_id = "${azurerm_storage_account.test.id}" + queue_name = "${azurerm_storage_queue.test.name}" + } + + included_event_types = ["Microsoft.Storage.BlobCreated", "Microsoft.Storage.BlobDeleted"] + + subject_filter { + subject_begins_with = "test/test" + subject_ends_with = ".jpg" + subject_contains = ["contains", "contains2"] + } + + advanced_filter { + key = "Subject" + operator_type = "StringContains" + values = ["contains", "contains2"] + } + + advanced_filter { + key = "Subject" + operator_type = "BoolEquals" + value = "true" + } + + advanced_filter { + key = "Subject" + operator_type = "NumberGreaterThan" + value = "50" + } + advanced_filter { + key = "Subject" + operator_type = "NumberGreaterThanOrEquals" + value = "50" + } + advanced_filter { + key = "Subject" + operator_type = "NumberLessThan" + value = "50" + } + advanced_filter { + key = "Subject" + operator_type = "NumberLessThanOrEquals" + value = "50" + } + + advanced_filter { + key = "Subject" + operator_type = "NumberIn" + values = ["60", "70"] + } + + advanced_filter { + key = "Subject" + operator_type = "StringBeginsWith" + values = ["contains", "contains2"] + } + + advanced_filter { + key = "Subject" + operator_type = "StringEndsWith" + values = ["contains", "contains2"] + } + advanced_filter { + key = "Subject" + operator_type = "StringIn" + values = ["contains", "contains2"] + } + advanced_filter { + key = "Subject" + operator_type = "StringNotIn" + values = ["xyz", "abc"] + } +} +`, data.RandomInteger, data.Locations.Primary, data.RandomString, data.RandomInteger, data.RandomInteger) +}