From 7a6d900a772710a52fab6cd1e12aa4169206e615 Mon Sep 17 00:00:00 2001 From: Catriona Date: Thu, 24 Feb 2022 15:37:38 +0000 Subject: [PATCH 1/5] deprecate ip_filter_rule in favour of network_rule_set --- internal/services/iothub/iothub_resource.go | 134 +++++++++++++++++- .../services/iothub/iothub_resource_test.go | 114 +++++++++++++++ website/docs/r/iothub.html.markdown | 24 ++++ 3 files changed, 266 insertions(+), 6 deletions(-) diff --git a/internal/services/iothub/iothub_resource.go b/internal/services/iothub/iothub_resource.go index 65108acf8c38..65a5346f93ca 100644 --- a/internal/services/iothub/iothub_resource.go +++ b/internal/services/iothub/iothub_resource.go @@ -469,9 +469,12 @@ func resourceIotHub() *pluginsdk.Resource { }, }, + // TODO remove in 3.0 "ip_filter_rule": { - Type: pluginsdk.TypeList, - Optional: true, + Type: pluginsdk.TypeList, + Optional: true, + ConflictsWith: []string{"network_rule_set"}, + Deprecated: "This property block is deprecated in favour of `network_rule_set` and will be removed in version 3.0 of the provider.", Elem: &pluginsdk.Resource{ Schema: map[string]*pluginsdk.Schema{ "name": { @@ -496,6 +499,56 @@ func resourceIotHub() *pluginsdk.Resource { }, }, + "network_rule_set": { + Type: pluginsdk.TypeList, + Optional: true, + ConflictsWith: []string{"ip_filter_rule"}, + Elem: &pluginsdk.Resource{ + Schema: map[string]*pluginsdk.Schema{ + "default_action": { + Type: pluginsdk.TypeString, + Optional: true, + Default: string(devices.DefaultActionDeny), + ValidateFunc: validation.StringInSlice([]string{ + string(devices.DefaultActionAllow), + string(devices.DefaultActionDeny), + }, false), + }, + "apply_to_builtin_eventhub_endpoint": { + Type: pluginsdk.TypeBool, + Optional: true, + Default: false, + }, + "ip_rule": { + Type: pluginsdk.TypeList, + Optional: true, + Elem: &pluginsdk.Resource{ + Schema: map[string]*pluginsdk.Schema{ + "name": { + Type: pluginsdk.TypeString, + Required: true, + ValidateFunc: validation.StringIsNotEmpty, + }, + "ip_mask": { + Type: pluginsdk.TypeString, + Required: true, + ValidateFunc: validate.CIDR, + }, + "action": { + Type: pluginsdk.TypeString, + Required: true, + Default: string(devices.NetworkRuleIPActionAllow), + ValidateFunc: validation.StringInSlice([]string{ + string(devices.NetworkRuleIPActionAllow), + }, false), + }, + }, + }, + }, + }, + }, + }, + "cloud_to_device": { Type: pluginsdk.TypeList, Optional: true, @@ -684,7 +737,6 @@ func resourceIotHubCreateUpdate(d *pluginsdk.ResourceData, meta interface{}) err Location: utils.String(azure.NormalizeLocation(d.Get("location").(string))), Sku: expandIoTHubSku(d), Properties: &devices.IotHubProperties{ - IPFilterRules: expandIPFilterRules(d), Routing: &routingProperties, StorageEndpoints: storageEndpoints, MessagingEndpoints: messagingEndpoints, @@ -695,6 +747,14 @@ func resourceIotHubCreateUpdate(d *pluginsdk.ResourceData, meta interface{}) err Tags: tags.Expand(d.Get("tags").(map[string]interface{})), } + if _, ok := d.GetOk("ip_filter_rule"); ok { + props.Properties.IPFilterRules = expandIPFilterRules(d) + } + + if _, ok := d.GetOk("network_rule_set"); ok { + props.Properties.NetworkRuleSets = expandNetworkRuleSetProperties(d) + } + // nolint staticcheck if v, ok := d.GetOkExists("public_network_access_enabled"); ok { enabled := devices.PublicNetworkAccessDisabled @@ -825,9 +885,16 @@ func resourceIotHubRead(d *pluginsdk.ResourceData, meta interface{}) error { return fmt.Errorf("setting `fallbackRoute` in IoTHub %q: %+v", id.Name, err) } - ipFilterRules := flattenIPFilterRules(properties.IPFilterRules) - if err := d.Set("ip_filter_rule", ipFilterRules); err != nil { - return fmt.Errorf("setting `ip_filter_rule` in IoTHub %q: %+v", id.Name, err) + networkRuleSet := flattenNetworkRuleSetProperties(properties.NetworkRuleSets) + if err := d.Set("network_rule_set", networkRuleSet); err != nil { + return fmt.Errorf("setting `network_rule_set` in IoTHub %q: %+v", id.Name, err) + } + + if len(networkRuleSet) == 0 { + ipFilterRules := flattenIPFilterRules(properties.IPFilterRules) + if err := d.Set("ip_filter_rule", ipFilterRules); err != nil { + return fmt.Errorf("setting `ip_filter_rule` in IoTHub %q: %+v", id.Name, err) + } } fileUpload := flattenIoTHubFileUpload(properties.StorageEndpoints, properties.MessagingEndpoints, properties.EnableFileUploadNotifications) @@ -1671,6 +1738,61 @@ func flattenIPFilterRules(in *[]devices.IPFilterRule) []interface{} { return rules } +func expandNetworkRuleSetProperties(d *pluginsdk.ResourceData) *devices.NetworkRuleSetProperties { + networkRuleSet := d.Get("network_rule_set").([]interface{}) + networkRuleSetProps := devices.NetworkRuleSetProperties{} + nrsMap := networkRuleSet[0].(map[string]interface{}) + + networkRuleSetProps.DefaultAction = devices.DefaultAction(nrsMap["default_action"].(string)) + networkRuleSetProps.ApplyToBuiltInEventHubEndpoint = utils.Bool(nrsMap["apply_to_builtin_eventhub_endpoint"].(bool)) + ipRules := nrsMap["ip_rule"].([]interface{}) + + if len(ipRules) != 0 { + rules := make([]devices.NetworkRuleSetIPRule, 0) + + for _, r := range ipRules { + rawRule := r.(map[string]interface{}) + rule := &devices.NetworkRuleSetIPRule{ + FilterName: utils.String(rawRule["name"].(string)), + Action: devices.NetworkRuleIPAction(rawRule["action"].(string)), + IPMask: utils.String(rawRule["ip_mask"].(string)), + } + rules = append(rules, *rule) + } + networkRuleSetProps.IPRules = &rules + } + return &networkRuleSetProps +} + +func flattenNetworkRuleSetProperties(input *devices.NetworkRuleSetProperties) []interface{} { + if input == nil { + return []interface{}{} + } + + output := make(map[string]interface{}) + output["default_action"] = input.DefaultAction + output["apply_to_builtin_eventhub_endpoint"] = input.ApplyToBuiltInEventHubEndpoint + rules := make([]interface{}, 0) + + for _, r := range *input.IPRules { + rawRule := make(map[string]interface{}) + + if r.FilterName != nil { + rawRule["name"] = *r.FilterName + } + + rawRule["action"] = string(r.Action) + + if r.IPMask != nil { + rawRule["ip_mask"] = *r.IPMask + } + rules = append(rules, rawRule) + } + + output["ip_rule"] = rules + return []interface{}{output} +} + func expandIotHubIdentity(input []interface{}) (*devices.ArmIdentity, error) { config, err := identity.ExpandSystemAndUserAssignedMap(input) if err != nil { diff --git a/internal/services/iothub/iothub_resource_test.go b/internal/services/iothub/iothub_resource_test.go index bbd6999430c8..f04f4cf323b0 100644 --- a/internal/services/iothub/iothub_resource_test.go +++ b/internal/services/iothub/iothub_resource_test.go @@ -52,6 +52,28 @@ func TestAccIotHub_ipFilterRules(t *testing.T) { }) } +func TestAccIotHub_networkRulesSet(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_iothub", "test") + r := IotHubResource{} + + data.ResourceTest(t, r, []acceptance.TestStep{ + { + Config: r.networkRuleSet(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep(), + { + Config: r.networkRuleSetUpdated(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep(), + }) +} + func TestAccIotHub_requiresImport(t *testing.T) { data := acceptance.BuildTestData(t, "azurerm_iothub", "test") r := IotHubResource{} @@ -616,6 +638,98 @@ resource "azurerm_iothub" "test" { `, data.RandomInteger, data.Locations.Primary, data.RandomInteger) } +func (IotHubResource) networkRuleSet(data acceptance.TestData) string { + return fmt.Sprintf(` +provider "azurerm" { + features {} +} + +resource "azurerm_resource_group" "test" { + name = "acctestRG-%d" + location = "%s" +} + +resource "azurerm_iothub" "test" { + name = "acctestIoTHub-%d" + resource_group_name = azurerm_resource_group.test.name + location = azurerm_resource_group.test.location + + sku { + name = "S1" + capacity = "1" + } + + network_rule_set { + default_action = "Allow" + apply_to_builtin_eventhub_endpoint = true + + ip_rule { + name = "test" + ip_mask = "10.0.1.0/31" + action = "Allow" + } + + ip_rule { + name = "test2" + ip_mask = "10.0.3.0/31" + action = "Allow" + } + + } + + tags = { + purpose = "testing" + } +} +`, data.RandomInteger, data.Locations.Primary, data.RandomInteger) +} + +func (IotHubResource) networkRuleSetUpdated(data acceptance.TestData) string { + return fmt.Sprintf(` +provider "azurerm" { + features {} +} + +resource "azurerm_resource_group" "test" { + name = "acctestRG-%d" + location = "%s" +} + +resource "azurerm_iothub" "test" { + name = "acctestIoTHub-%d" + resource_group_name = azurerm_resource_group.test.name + location = azurerm_resource_group.test.location + + sku { + name = "S1" + capacity = "1" + } + + network_rule_set { + default_action = "Allow" + apply_to_builtin_eventhub_endpoint = true + + ip_rule { + name = "test" + ip_mask = "10.0.0.0/31" + action = "Allow" + } + + ip_rule { + name = "test2" + ip_mask = "10.0.2.0/31" + action = "Allow" + } + + } + + tags = { + purpose = "testing" + } +} +`, data.RandomInteger, data.Locations.Primary, data.RandomInteger) +} + func (IotHubResource) customRoutes(data acceptance.TestData) string { return fmt.Sprintf(` provider "azurerm" { diff --git a/website/docs/r/iothub.html.markdown b/website/docs/r/iothub.html.markdown index 5de5eadac33d..249ebfb6be01 100644 --- a/website/docs/r/iothub.html.markdown +++ b/website/docs/r/iothub.html.markdown @@ -156,6 +156,10 @@ The following arguments are supported: * `ip_filter_rule` - (Optional) One or more `ip_filter_rule` blocks as defined below. +~> **NOTE:** The `ip_filter_rule` property block has been deprecated in favour of the `network_rule_set` property and will be removed in version 3.0 of the provider. + +* `network_rule_set` - (Optional) A `network_rule_set` block as defined below. + * `route` - (Optional) A `route` block as defined below. * `enrichment` - (Optional) A `enrichment` block as defined below. @@ -232,6 +236,26 @@ An `ip_filter_rule` block supports the following: --- +A `network_rule_set` block supports the following: + +* `default_action` - (Optional) Default Action for Network Rule Set. Possible values are `DefaultActionDeny`, `DefaultActionAllow`. Defaults to `DefaultActionDeny`. + +* `apply_to_builtin_eventhub_endpoint` - (Optional) Determines if Network Rule Set is also applied to the BuiltIn EventHub EndPoint of the IotHub. Defaults to `false`. + +* `ip_rule` - (Optional) One or more `ip_rule` blocks as defined below. + +--- + +A `ip_rule` block supports the following: + +* `name` - (Required) The name of the ip rule. + +* `ip_mask` - (Required) The IP address range in CIDR notation for the ip rule. + +* `action` - (Required) The desired action for requests captured by this rule. Possible values are `Allow`. + +--- + A `route` block supports the following: * `name` - (Required) The name of the route. From 669d9d095613dc3c4b609549b7e1531f0389384b Mon Sep 17 00:00:00 2001 From: Catriona Date: Thu, 24 Feb 2022 16:05:22 +0000 Subject: [PATCH 2/5] deprecate ip_filter_rule in favour of network_rule_set --- internal/services/iothub/iothub_resource.go | 2 +- website/docs/r/iothub.html.markdown | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/internal/services/iothub/iothub_resource.go b/internal/services/iothub/iothub_resource.go index 65a5346f93ca..8d51a8965599 100644 --- a/internal/services/iothub/iothub_resource.go +++ b/internal/services/iothub/iothub_resource.go @@ -536,7 +536,7 @@ func resourceIotHub() *pluginsdk.Resource { }, "action": { Type: pluginsdk.TypeString, - Required: true, + Optional: true, Default: string(devices.NetworkRuleIPActionAllow), ValidateFunc: validation.StringInSlice([]string{ string(devices.NetworkRuleIPActionAllow), diff --git a/website/docs/r/iothub.html.markdown b/website/docs/r/iothub.html.markdown index 249ebfb6be01..f49368b7ea7e 100644 --- a/website/docs/r/iothub.html.markdown +++ b/website/docs/r/iothub.html.markdown @@ -252,7 +252,7 @@ A `ip_rule` block supports the following: * `ip_mask` - (Required) The IP address range in CIDR notation for the ip rule. -* `action` - (Required) The desired action for requests captured by this rule. Possible values are `Allow`. +* `action` - (Optional) The desired action for requests captured by this rule. Possible values are `Allow`. Defaults to `Allow`. --- From 34ed818ab61508ddd26132dd97f76c22c163da01 Mon Sep 17 00:00:00 2001 From: Catriona Date: Thu, 24 Feb 2022 16:23:02 +0000 Subject: [PATCH 3/5] docs correction --- website/docs/r/iothub.html.markdown | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/docs/r/iothub.html.markdown b/website/docs/r/iothub.html.markdown index f49368b7ea7e..dccd6b8cca94 100644 --- a/website/docs/r/iothub.html.markdown +++ b/website/docs/r/iothub.html.markdown @@ -156,7 +156,7 @@ The following arguments are supported: * `ip_filter_rule` - (Optional) One or more `ip_filter_rule` blocks as defined below. -~> **NOTE:** The `ip_filter_rule` property block has been deprecated in favour of the `network_rule_set` property and will be removed in version 3.0 of the provider. +~> **NOTE:** The `ip_filter_rule` property block has been deprecated in favour of the `network_rule_set` block and will be removed in version 3.0 of the provider. * `network_rule_set` - (Optional) A `network_rule_set` block as defined below. From 8f6a5e1dc9043b0eb0b59db4bf59f4ca58e58fad Mon Sep 17 00:00:00 2001 From: Catriona Date: Fri, 25 Feb 2022 14:12:54 +0000 Subject: [PATCH 4/5] add beta flag --- internal/services/iothub/iothub_resource.go | 1035 ++++++++++--------- 1 file changed, 521 insertions(+), 514 deletions(-) diff --git a/internal/services/iothub/iothub_resource.go b/internal/services/iothub/iothub_resource.go index 8d51a8965599..867901de1c5a 100644 --- a/internal/services/iothub/iothub_resource.go +++ b/internal/services/iothub/iothub_resource.go @@ -14,6 +14,7 @@ import ( "github.com/Azure/azure-sdk-for-go/services/iothub/mgmt/2021-07-02/devices" "github.com/hashicorp/go-azure-helpers/resourcemanager/commonschema" "github.com/hashicorp/go-azure-helpers/resourcemanager/identity" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-provider-azurerm/helpers/azure" "github.com/hashicorp/terraform-provider-azurerm/helpers/tf" "github.com/hashicorp/terraform-provider-azurerm/helpers/validate" @@ -86,568 +87,572 @@ func resourceIotHub() *pluginsdk.Resource { Delete: pluginsdk.DefaultTimeout(30 * time.Minute), }, - Schema: map[string]*pluginsdk.Schema{ - "name": { - Type: pluginsdk.TypeString, - Required: true, - ForceNew: true, - ValidateFunc: iothubValidate.IoTHubName, - }, - - "location": azure.SchemaLocation(), - - "resource_group_name": azure.SchemaResourceGroupName(), - - "sku": { - Type: pluginsdk.TypeList, - MaxItems: 1, - Required: true, - Elem: &pluginsdk.Resource{ - Schema: map[string]*pluginsdk.Schema{ - "name": { - Type: pluginsdk.TypeString, - Required: true, - DiffSuppressFunc: suppress.CaseDifference, - ValidateFunc: validation.StringInSlice([]string{ - string(devices.IotHubSkuB1), - string(devices.IotHubSkuB2), - string(devices.IotHubSkuB3), - string(devices.IotHubSkuF1), - string(devices.IotHubSkuS1), - string(devices.IotHubSkuS2), - string(devices.IotHubSkuS3), - }, false), - }, + Schema: func() map[string]*pluginsdk.Schema { + s := map[string]*pluginsdk.Schema{ + "name": { + Type: pluginsdk.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: iothubValidate.IoTHubName, + }, - "capacity": { - Type: pluginsdk.TypeInt, - Required: true, - ValidateFunc: validation.IntBetween(1, 200), + "location": azure.SchemaLocation(), + + "resource_group_name": azure.SchemaResourceGroupName(), + + "sku": { + Type: pluginsdk.TypeList, + MaxItems: 1, + Required: true, + Elem: &pluginsdk.Resource{ + Schema: map[string]*pluginsdk.Schema{ + "name": { + Type: pluginsdk.TypeString, + Required: true, + DiffSuppressFunc: suppress.CaseDifference, + ValidateFunc: validation.StringInSlice([]string{ + string(devices.IotHubSkuB1), + string(devices.IotHubSkuB2), + string(devices.IotHubSkuB3), + string(devices.IotHubSkuF1), + string(devices.IotHubSkuS1), + string(devices.IotHubSkuS2), + string(devices.IotHubSkuS3), + }, false), + }, + + "capacity": { + Type: pluginsdk.TypeInt, + Required: true, + ValidateFunc: validation.IntBetween(1, 200), + }, }, }, }, - }, - - "shared_access_policy": { - Type: pluginsdk.TypeList, - Computed: true, - Elem: &pluginsdk.Resource{ - Schema: map[string]*pluginsdk.Schema{ - "key_name": { - Type: pluginsdk.TypeString, - Computed: true, - }, - "primary_key": { - Type: pluginsdk.TypeString, - Computed: true, - Sensitive: true, - }, - "secondary_key": { - Type: pluginsdk.TypeString, - Computed: true, - Sensitive: true, - }, - "permissions": { - Type: pluginsdk.TypeString, - Computed: true, + + "shared_access_policy": { + Type: pluginsdk.TypeList, + Computed: true, + Elem: &pluginsdk.Resource{ + Schema: map[string]*pluginsdk.Schema{ + "key_name": { + Type: pluginsdk.TypeString, + Computed: true, + }, + "primary_key": { + Type: pluginsdk.TypeString, + Computed: true, + Sensitive: true, + }, + "secondary_key": { + Type: pluginsdk.TypeString, + Computed: true, + Sensitive: true, + }, + "permissions": { + Type: pluginsdk.TypeString, + Computed: true, + }, }, }, }, - }, - - "event_hub_partition_count": { - Type: pluginsdk.TypeInt, - Optional: true, - Computed: true, - ValidateFunc: validation.IntBetween(2, 128), - }, - "event_hub_retention_in_days": { - Type: pluginsdk.TypeInt, - Optional: true, - Computed: true, - ValidateFunc: validation.IntBetween(1, 7), - }, - - "file_upload": { - Type: pluginsdk.TypeList, - MaxItems: 1, - Optional: true, - Elem: &pluginsdk.Resource{ - Schema: map[string]*pluginsdk.Schema{ - "connection_string": { - Type: pluginsdk.TypeString, - Required: true, - DiffSuppressFunc: fileUploadConnectionStringDiffSuppress, - Sensitive: true, - }, - "container_name": { - Type: pluginsdk.TypeString, - Required: true, - }, - "notifications": { - Type: pluginsdk.TypeBool, - Optional: true, - Default: false, - }, - "max_delivery_count": { - Type: pluginsdk.TypeInt, - Optional: true, - Default: 10, - ValidateFunc: validation.IntBetween(1, 100), - }, - "sas_ttl": { - Type: pluginsdk.TypeString, - Optional: true, - Computed: true, - ValidateFunc: validate.ISO8601Duration, - }, - "default_ttl": { - Type: pluginsdk.TypeString, - Optional: true, - Computed: true, - ValidateFunc: validate.ISO8601Duration, - }, - "lock_duration": { - Type: pluginsdk.TypeString, - Optional: true, - Computed: true, - ValidateFunc: validate.ISO8601Duration, + + "event_hub_partition_count": { + Type: pluginsdk.TypeInt, + Optional: true, + Computed: true, + ValidateFunc: validation.IntBetween(2, 128), + }, + "event_hub_retention_in_days": { + Type: pluginsdk.TypeInt, + Optional: true, + Computed: true, + ValidateFunc: validation.IntBetween(1, 7), + }, + + "file_upload": { + Type: pluginsdk.TypeList, + MaxItems: 1, + Optional: true, + Elem: &pluginsdk.Resource{ + Schema: map[string]*pluginsdk.Schema{ + "connection_string": { + Type: pluginsdk.TypeString, + Required: true, + DiffSuppressFunc: fileUploadConnectionStringDiffSuppress, + Sensitive: true, + }, + "container_name": { + Type: pluginsdk.TypeString, + Required: true, + }, + "notifications": { + Type: pluginsdk.TypeBool, + Optional: true, + Default: false, + }, + "max_delivery_count": { + Type: pluginsdk.TypeInt, + Optional: true, + Default: 10, + ValidateFunc: validation.IntBetween(1, 100), + }, + "sas_ttl": { + Type: pluginsdk.TypeString, + Optional: true, + Computed: true, + ValidateFunc: validate.ISO8601Duration, + }, + "default_ttl": { + Type: pluginsdk.TypeString, + Optional: true, + Computed: true, + ValidateFunc: validate.ISO8601Duration, + }, + "lock_duration": { + Type: pluginsdk.TypeString, + Optional: true, + Computed: true, + ValidateFunc: validate.ISO8601Duration, + }, }, }, }, - }, - - "endpoint": { - Type: pluginsdk.TypeList, - Optional: true, - Computed: true, - ConfigMode: pluginsdk.SchemaConfigModeAttr, - Elem: &pluginsdk.Resource{ - Schema: map[string]*pluginsdk.Schema{ - "type": { - Type: pluginsdk.TypeString, - Required: true, - ValidateFunc: validation.StringInSlice([]string{ - "AzureIotHub.StorageContainer", - "AzureIotHub.ServiceBusQueue", - "AzureIotHub.ServiceBusTopic", - "AzureIotHub.EventHub", - }, false), - }, - "authentication_type": { - Type: pluginsdk.TypeString, - Optional: true, - Default: string(devices.AuthenticationTypeKeyBased), - ValidateFunc: validation.StringInSlice([]string{ - string(devices.AuthenticationTypeKeyBased), - string(devices.AuthenticationTypeIdentityBased), - }, false), - }, + "endpoint": { + Type: pluginsdk.TypeList, + Optional: true, + Computed: true, + ConfigMode: pluginsdk.SchemaConfigModeAttr, + Elem: &pluginsdk.Resource{ + Schema: map[string]*pluginsdk.Schema{ + "type": { + Type: pluginsdk.TypeString, + Required: true, + ValidateFunc: validation.StringInSlice([]string{ + "AzureIotHub.StorageContainer", + "AzureIotHub.ServiceBusQueue", + "AzureIotHub.ServiceBusTopic", + "AzureIotHub.EventHub", + }, false), + }, - "identity_id": { - Type: pluginsdk.TypeString, - Optional: true, - ValidateFunc: msivalidate.UserAssignedIdentityID, - }, + "authentication_type": { + Type: pluginsdk.TypeString, + Optional: true, + Default: string(devices.AuthenticationTypeKeyBased), + ValidateFunc: validation.StringInSlice([]string{ + string(devices.AuthenticationTypeKeyBased), + string(devices.AuthenticationTypeIdentityBased), + }, false), + }, - "endpoint_uri": { - Type: pluginsdk.TypeString, - Optional: true, - ValidateFunc: validation.StringIsNotEmpty, - }, + "identity_id": { + Type: pluginsdk.TypeString, + Optional: true, + ValidateFunc: msivalidate.UserAssignedIdentityID, + }, - "entity_path": { - Type: pluginsdk.TypeString, - Optional: true, - DiffSuppressFunc: suppressIfTypeIs("AzureIotHub.StorageContainer"), - ValidateFunc: validation.Any( - servicebusValidate.QueueName(), - servicebusValidate.TopicName(), - eventhubValidate.ValidateEventHubName(), - ), - }, + "endpoint_uri": { + Type: pluginsdk.TypeString, + Optional: true, + ValidateFunc: validation.StringIsNotEmpty, + }, - "connection_string": { - Type: pluginsdk.TypeString, - Optional: true, - DiffSuppressFunc: func(k, old, new string, d *pluginsdk.ResourceData) bool { - secretKeyRegex := regexp.MustCompile("(SharedAccessKey|AccountKey)=[^;]+") - sbProtocolRegex := regexp.MustCompile("sb://([^:]+)(:5671)?/;") - - // Azure will always mask the Access Keys and will include the port number in the GET response - // 5671 is the default port for Azure Service Bus connections - maskedNew := sbProtocolRegex.ReplaceAllString(new, "sb://$1:5671/;") - maskedNew = secretKeyRegex.ReplaceAllString(maskedNew, "$1=****") - return (new == d.Get(k).(string)) && (maskedNew == old) + "entity_path": { + Type: pluginsdk.TypeString, + Optional: true, + DiffSuppressFunc: suppressIfTypeIs("AzureIotHub.StorageContainer"), + ValidateFunc: validation.Any( + servicebusValidate.QueueName(), + servicebusValidate.TopicName(), + eventhubValidate.ValidateEventHubName(), + ), }, - Sensitive: true, - }, - "name": { - Type: pluginsdk.TypeString, - Required: true, - ValidateFunc: iothubValidate.IoTHubEndpointName, - }, + "connection_string": { + Type: pluginsdk.TypeString, + Optional: true, + DiffSuppressFunc: func(k, old, new string, d *pluginsdk.ResourceData) bool { + secretKeyRegex := regexp.MustCompile("(SharedAccessKey|AccountKey)=[^;]+") + sbProtocolRegex := regexp.MustCompile("sb://([^:]+)(:5671)?/;") + + // Azure will always mask the Access Keys and will include the port number in the GET response + // 5671 is the default port for Azure Service Bus connections + maskedNew := sbProtocolRegex.ReplaceAllString(new, "sb://$1:5671/;") + maskedNew = secretKeyRegex.ReplaceAllString(maskedNew, "$1=****") + return (new == d.Get(k).(string)) && (maskedNew == old) + }, + Sensitive: true, + }, - "batch_frequency_in_seconds": { - Type: pluginsdk.TypeInt, - Optional: true, - Default: 300, - DiffSuppressFunc: suppressIfTypeIsNot("AzureIotHub.StorageContainer"), - ValidateFunc: validation.IntBetween(60, 720), - }, + "name": { + Type: pluginsdk.TypeString, + Required: true, + ValidateFunc: iothubValidate.IoTHubEndpointName, + }, - "max_chunk_size_in_bytes": { - Type: pluginsdk.TypeInt, - Optional: true, - Default: 314572800, - DiffSuppressFunc: suppressIfTypeIsNot("AzureIotHub.StorageContainer"), - ValidateFunc: validation.IntBetween(10485760, 524288000), - }, + "batch_frequency_in_seconds": { + Type: pluginsdk.TypeInt, + Optional: true, + Default: 300, + DiffSuppressFunc: suppressIfTypeIsNot("AzureIotHub.StorageContainer"), + ValidateFunc: validation.IntBetween(60, 720), + }, - "container_name": { - Type: pluginsdk.TypeString, - Optional: true, - DiffSuppressFunc: suppressIfTypeIsNot("AzureIotHub.StorageContainer"), - }, + "max_chunk_size_in_bytes": { + Type: pluginsdk.TypeInt, + Optional: true, + Default: 314572800, + DiffSuppressFunc: suppressIfTypeIsNot("AzureIotHub.StorageContainer"), + ValidateFunc: validation.IntBetween(10485760, 524288000), + }, - // encoding should be case-sensitive but kept case-insensitive for backward compatibility. - // todo remove suppress.CaseDifference, make encoding case-sensitive and normalize it with pandora in 3.0 or 4.0 - "encoding": { - Type: pluginsdk.TypeString, - Optional: true, - ForceNew: true, - Default: string(devices.EncodingAvro), - DiffSuppressFunc: suppressWhenAny( - suppressIfTypeIsNot("AzureIotHub.StorageContainer"), - suppress.CaseDifferenceV2Only), - ValidateFunc: validation.StringInSlice([]string{ - string(devices.EncodingAvro), - string(devices.EncodingAvroDeflate), - string(devices.EncodingJSON), - }, !features.ThreePointOh()), - }, + "container_name": { + Type: pluginsdk.TypeString, + Optional: true, + DiffSuppressFunc: suppressIfTypeIsNot("AzureIotHub.StorageContainer"), + }, - "file_name_format": { - Type: pluginsdk.TypeString, - Optional: true, - Default: "{iothub}/{partition}/{YYYY}/{MM}/{DD}/{HH}/{mm}", - DiffSuppressFunc: suppressIfTypeIsNot("AzureIotHub.StorageContainer"), - ValidateFunc: iothubValidate.FileNameFormat, - }, + // encoding should be case-sensitive but kept case-insensitive for backward compatibility. + // todo remove suppress.CaseDifference, make encoding case-sensitive and normalize it with pandora in 3.0 or 4.0 + "encoding": { + Type: pluginsdk.TypeString, + Optional: true, + ForceNew: true, + Default: string(devices.EncodingAvro), + DiffSuppressFunc: suppressWhenAny( + suppressIfTypeIsNot("AzureIotHub.StorageContainer"), + suppress.CaseDifferenceV2Only), + ValidateFunc: validation.StringInSlice([]string{ + string(devices.EncodingAvro), + string(devices.EncodingAvroDeflate), + string(devices.EncodingJSON), + }, !features.ThreePointOh()), + }, - "resource_group_name": commonschema.ResourceGroupNameOptional(), - }, - }, - }, - - "route": { - Type: pluginsdk.TypeList, - Optional: true, - Computed: true, - ConfigMode: pluginsdk.SchemaConfigModeAttr, - Elem: &pluginsdk.Resource{ - Schema: map[string]*pluginsdk.Schema{ - "name": { - Type: pluginsdk.TypeString, - Required: true, - ValidateFunc: validation.StringMatch( - regexp.MustCompile("^[-_.a-zA-Z0-9]{1,64}$"), - "Route Name name can only include alphanumeric characters, periods, underscores, hyphens, has a maximum length of 64 characters, and must be unique.", - ), - }, - "source": { - Type: pluginsdk.TypeString, - Required: true, - ValidateFunc: validation.StringInSlice([]string{ - "DeviceConnectionStateEvents", - "DeviceJobLifecycleEvents", - "DeviceLifecycleEvents", - "DeviceMessages", - "Invalid", - "TwinChangeEvents", - }, false), - }, - "condition": { - // The condition is a string value representing device-to-cloud message routes query expression - // https://docs.microsoft.com/en-us/azure/iot-hub/iot-hub-devguide-query-language#device-to-cloud-message-routes-query-expressions - Type: pluginsdk.TypeString, - Optional: true, - Default: "true", - }, - "endpoint_names": { - Type: pluginsdk.TypeList, - Elem: &pluginsdk.Schema{ - Type: pluginsdk.TypeString, + "file_name_format": { + Type: pluginsdk.TypeString, + Optional: true, + Default: "{iothub}/{partition}/{YYYY}/{MM}/{DD}/{HH}/{mm}", + DiffSuppressFunc: suppressIfTypeIsNot("AzureIotHub.StorageContainer"), + ValidateFunc: iothubValidate.FileNameFormat, }, - Required: true, - }, - "enabled": { - Type: pluginsdk.TypeBool, - Required: true, + + "resource_group_name": commonschema.ResourceGroupNameOptional(), }, }, }, - }, - - "enrichment": { - Type: pluginsdk.TypeList, - // Currently only 10 enrichments is allowed for standard or basic tier, 2 for Free tier. - MaxItems: 10, - Optional: true, - Computed: true, - ConfigMode: pluginsdk.SchemaConfigModeAttr, - Elem: &pluginsdk.Resource{ - Schema: map[string]*pluginsdk.Schema{ - "key": { - Type: pluginsdk.TypeString, - Required: true, - ValidateFunc: validation.StringMatch( - regexp.MustCompile("^[-_.a-zA-Z0-9]{1,64}$"), - "Enrichment Key name can only include alphanumeric characters, periods, underscores, hyphens, has a maximum length of 64 characters, and must be unique.", - ), - }, - "value": { - Type: pluginsdk.TypeString, - Required: true, - ValidateFunc: validation.StringIsNotEmpty, - }, - "endpoint_names": { - Type: pluginsdk.TypeList, - Elem: &pluginsdk.Schema{ - Type: pluginsdk.TypeString, + + "route": { + Type: pluginsdk.TypeList, + Optional: true, + Computed: true, + ConfigMode: pluginsdk.SchemaConfigModeAttr, + Elem: &pluginsdk.Resource{ + Schema: map[string]*pluginsdk.Schema{ + "name": { + Type: pluginsdk.TypeString, + Required: true, + ValidateFunc: validation.StringMatch( + regexp.MustCompile("^[-_.a-zA-Z0-9]{1,64}$"), + "Route Name name can only include alphanumeric characters, periods, underscores, hyphens, has a maximum length of 64 characters, and must be unique.", + ), + }, + "source": { + Type: pluginsdk.TypeString, + Required: true, + ValidateFunc: validation.StringInSlice([]string{ + "DeviceConnectionStateEvents", + "DeviceJobLifecycleEvents", + "DeviceLifecycleEvents", + "DeviceMessages", + "Invalid", + "TwinChangeEvents", + }, false), + }, + "condition": { + // The condition is a string value representing device-to-cloud message routes query expression + // https://docs.microsoft.com/en-us/azure/iot-hub/iot-hub-devguide-query-language#device-to-cloud-message-routes-query-expressions + Type: pluginsdk.TypeString, + Optional: true, + Default: "true", + }, + "endpoint_names": { + Type: pluginsdk.TypeList, + Elem: &pluginsdk.Schema{ + Type: pluginsdk.TypeString, + }, + Required: true, + }, + "enabled": { + Type: pluginsdk.TypeBool, + Required: true, }, - Required: true, }, }, }, - }, - - "fallback_route": { - Type: pluginsdk.TypeList, - MaxItems: 1, - Optional: true, - Computed: true, - Elem: &pluginsdk.Resource{ - Schema: map[string]*pluginsdk.Schema{ - "source": { - Type: pluginsdk.TypeString, - Optional: true, - Default: string(devices.RoutingSourceDeviceMessages), - ValidateFunc: validation.StringInSlice([]string{ - string(devices.RoutingSourceDeviceConnectionStateEvents), - string(devices.RoutingSourceDeviceJobLifecycleEvents), - string(devices.RoutingSourceDeviceLifecycleEvents), - string(devices.RoutingSourceDeviceMessages), - string(devices.RoutingSourceInvalid), - string(devices.RoutingSourceTwinChangeEvents), - }, false), - }, - "condition": { - // The condition is a string value representing device-to-cloud message routes query expression - // https://docs.microsoft.com/en-us/azure/iot-hub/iot-hub-devguide-query-language#device-to-cloud-message-routes-query-expressions - Type: pluginsdk.TypeString, - Optional: true, - Default: "true", - }, - "endpoint_names": { - Type: pluginsdk.TypeList, - Optional: true, - Computed: true, - Elem: &pluginsdk.Schema{ + + "enrichment": { + Type: pluginsdk.TypeList, + // Currently only 10 enrichments is allowed for standard or basic tier, 2 for Free tier. + MaxItems: 10, + Optional: true, + Computed: true, + ConfigMode: pluginsdk.SchemaConfigModeAttr, + Elem: &pluginsdk.Resource{ + Schema: map[string]*pluginsdk.Schema{ + "key": { + Type: pluginsdk.TypeString, + Required: true, + ValidateFunc: validation.StringMatch( + regexp.MustCompile("^[-_.a-zA-Z0-9]{1,64}$"), + "Enrichment Key name can only include alphanumeric characters, periods, underscores, hyphens, has a maximum length of 64 characters, and must be unique.", + ), + }, + "value": { Type: pluginsdk.TypeString, - ValidateFunc: validation.StringLenBetween(0, 64), + Required: true, + ValidateFunc: validation.StringIsNotEmpty, + }, + "endpoint_names": { + Type: pluginsdk.TypeList, + Elem: &pluginsdk.Schema{ + Type: pluginsdk.TypeString, + }, + Required: true, }, - }, - "enabled": { - Type: pluginsdk.TypeBool, - Optional: true, - Computed: true, }, }, }, - }, - - // TODO remove in 3.0 - "ip_filter_rule": { - Type: pluginsdk.TypeList, - Optional: true, - ConflictsWith: []string{"network_rule_set"}, - Deprecated: "This property block is deprecated in favour of `network_rule_set` and will be removed in version 3.0 of the provider.", - Elem: &pluginsdk.Resource{ - Schema: map[string]*pluginsdk.Schema{ - "name": { - Type: pluginsdk.TypeString, - Required: true, - ValidateFunc: validation.StringIsNotEmpty, - }, - "ip_mask": { - Type: pluginsdk.TypeString, - Required: true, - ValidateFunc: validate.CIDR, - }, - "action": { - Type: pluginsdk.TypeString, - Required: true, - ValidateFunc: validation.StringInSlice([]string{ - string(devices.IPFilterActionTypeAccept), - string(devices.IPFilterActionTypeReject), - }, false), + + "fallback_route": { + Type: pluginsdk.TypeList, + MaxItems: 1, + Optional: true, + Computed: true, + Elem: &pluginsdk.Resource{ + Schema: map[string]*pluginsdk.Schema{ + "source": { + Type: pluginsdk.TypeString, + Optional: true, + Default: string(devices.RoutingSourceDeviceMessages), + ValidateFunc: validation.StringInSlice([]string{ + string(devices.RoutingSourceDeviceConnectionStateEvents), + string(devices.RoutingSourceDeviceJobLifecycleEvents), + string(devices.RoutingSourceDeviceLifecycleEvents), + string(devices.RoutingSourceDeviceMessages), + string(devices.RoutingSourceInvalid), + string(devices.RoutingSourceTwinChangeEvents), + }, false), + }, + "condition": { + // The condition is a string value representing device-to-cloud message routes query expression + // https://docs.microsoft.com/en-us/azure/iot-hub/iot-hub-devguide-query-language#device-to-cloud-message-routes-query-expressions + Type: pluginsdk.TypeString, + Optional: true, + Default: "true", + }, + "endpoint_names": { + Type: pluginsdk.TypeList, + Optional: true, + Computed: true, + Elem: &pluginsdk.Schema{ + Type: pluginsdk.TypeString, + ValidateFunc: validation.StringLenBetween(0, 64), + }, + }, + "enabled": { + Type: pluginsdk.TypeBool, + Optional: true, + Computed: true, + }, }, }, }, - }, - - "network_rule_set": { - Type: pluginsdk.TypeList, - Optional: true, - ConflictsWith: []string{"ip_filter_rule"}, - Elem: &pluginsdk.Resource{ - Schema: map[string]*pluginsdk.Schema{ - "default_action": { - Type: pluginsdk.TypeString, - Optional: true, - Default: string(devices.DefaultActionDeny), - ValidateFunc: validation.StringInSlice([]string{ - string(devices.DefaultActionAllow), - string(devices.DefaultActionDeny), - }, false), - }, - "apply_to_builtin_eventhub_endpoint": { - Type: pluginsdk.TypeBool, - Optional: true, - Default: false, - }, - "ip_rule": { - Type: pluginsdk.TypeList, - Optional: true, - Elem: &pluginsdk.Resource{ - Schema: map[string]*pluginsdk.Schema{ - "name": { - Type: pluginsdk.TypeString, - Required: true, - ValidateFunc: validation.StringIsNotEmpty, - }, - "ip_mask": { - Type: pluginsdk.TypeString, - Required: true, - ValidateFunc: validate.CIDR, - }, - "action": { - Type: pluginsdk.TypeString, - Optional: true, - Default: string(devices.NetworkRuleIPActionAllow), - ValidateFunc: validation.StringInSlice([]string{ - string(devices.NetworkRuleIPActionAllow), - }, false), + + "network_rule_set": { + Type: pluginsdk.TypeList, + Optional: true, + ConflictsWith: []string{"ip_filter_rule"}, + Elem: &pluginsdk.Resource{ + Schema: map[string]*pluginsdk.Schema{ + "default_action": { + Type: pluginsdk.TypeString, + Optional: true, + Default: string(devices.DefaultActionDeny), + ValidateFunc: validation.StringInSlice([]string{ + string(devices.DefaultActionAllow), + string(devices.DefaultActionDeny), + }, false), + }, + "apply_to_builtin_eventhub_endpoint": { + Type: pluginsdk.TypeBool, + Optional: true, + Default: false, + }, + "ip_rule": { + Type: pluginsdk.TypeList, + Optional: true, + Elem: &pluginsdk.Resource{ + Schema: map[string]*pluginsdk.Schema{ + "name": { + Type: pluginsdk.TypeString, + Required: true, + ValidateFunc: validation.StringIsNotEmpty, + }, + "ip_mask": { + Type: pluginsdk.TypeString, + Required: true, + ValidateFunc: validate.CIDR, + }, + "action": { + Type: pluginsdk.TypeString, + Optional: true, + Default: string(devices.NetworkRuleIPActionAllow), + ValidateFunc: validation.StringInSlice([]string{ + string(devices.NetworkRuleIPActionAllow), + }, false), + }, }, }, }, }, }, }, - }, - - "cloud_to_device": { - Type: pluginsdk.TypeList, - Optional: true, - MaxItems: 1, - Computed: true, - Elem: &pluginsdk.Resource{ - Schema: map[string]*pluginsdk.Schema{ - "max_delivery_count": { - Type: pluginsdk.TypeInt, - Optional: true, - Default: 10, - ValidateFunc: validation.IntBetween(1, 100), - }, - "default_ttl": { - Type: pluginsdk.TypeString, - Optional: true, - Default: "PT1H", - ValidateFunc: validate.ISO8601DurationBetween("PT15M", "P2D"), - }, - "feedback": { - Type: pluginsdk.TypeList, - Optional: true, - Elem: &pluginsdk.Resource{ - Schema: map[string]*pluginsdk.Schema{ - "time_to_live": { - Type: pluginsdk.TypeString, - Optional: true, - Default: "PT1H", - ValidateFunc: validate.ISO8601DurationBetween("PT15M", "P2D"), - }, - "max_delivery_count": { - Type: pluginsdk.TypeInt, - Optional: true, - Default: 10, - ValidateFunc: validation.IntBetween(1, 100), - }, - "lock_duration": { - Type: pluginsdk.TypeString, - Optional: true, - Default: "PT60S", - ValidateFunc: validate.ISO8601DurationBetween("PT5S", "PT300S"), + + "cloud_to_device": { + Type: pluginsdk.TypeList, + Optional: true, + MaxItems: 1, + Computed: true, + Elem: &pluginsdk.Resource{ + Schema: map[string]*pluginsdk.Schema{ + "max_delivery_count": { + Type: pluginsdk.TypeInt, + Optional: true, + Default: 10, + ValidateFunc: validation.IntBetween(1, 100), + }, + "default_ttl": { + Type: pluginsdk.TypeString, + Optional: true, + Default: "PT1H", + ValidateFunc: validate.ISO8601DurationBetween("PT15M", "P2D"), + }, + "feedback": { + Type: pluginsdk.TypeList, + Optional: true, + Elem: &pluginsdk.Resource{ + Schema: map[string]*pluginsdk.Schema{ + "time_to_live": { + Type: pluginsdk.TypeString, + Optional: true, + Default: "PT1H", + ValidateFunc: validate.ISO8601DurationBetween("PT15M", "P2D"), + }, + "max_delivery_count": { + Type: pluginsdk.TypeInt, + Optional: true, + Default: 10, + ValidateFunc: validation.IntBetween(1, 100), + }, + "lock_duration": { + Type: pluginsdk.TypeString, + Optional: true, + Default: "PT60S", + ValidateFunc: validate.ISO8601DurationBetween("PT5S", "PT300S"), + }, }, }, }, }, }, }, - }, - - "min_tls_version": { - Type: pluginsdk.TypeString, - Optional: true, - ForceNew: true, - ValidateFunc: validation.StringInSlice([]string{ - "1.2", - }, false), - }, - - "public_network_access_enabled": { - Type: pluginsdk.TypeBool, - Optional: true, - }, - - "type": { - Type: pluginsdk.TypeString, - Computed: true, - }, - - "hostname": { - Type: pluginsdk.TypeString, - Computed: true, - }, - - "event_hub_events_endpoint": { - Type: pluginsdk.TypeString, - Computed: true, - }, - "event_hub_events_namespace": { - Type: pluginsdk.TypeString, - Computed: true, - }, - "event_hub_operations_endpoint": { - Type: pluginsdk.TypeString, - Computed: true, - }, - - "event_hub_events_path": { - Type: pluginsdk.TypeString, - Computed: true, - }, - "event_hub_operations_path": { - Type: pluginsdk.TypeString, - Computed: true, - }, - - "identity": commonschema.SystemAssignedUserAssignedIdentityOptional(), - - "tags": tags.Schema(), - }, + + "min_tls_version": { + Type: pluginsdk.TypeString, + Optional: true, + ForceNew: true, + ValidateFunc: validation.StringInSlice([]string{ + "1.2", + }, false), + }, + + "public_network_access_enabled": { + Type: pluginsdk.TypeBool, + Optional: true, + }, + + "type": { + Type: pluginsdk.TypeString, + Computed: true, + }, + + "hostname": { + Type: pluginsdk.TypeString, + Computed: true, + }, + + "event_hub_events_endpoint": { + Type: pluginsdk.TypeString, + Computed: true, + }, + "event_hub_events_namespace": { + Type: pluginsdk.TypeString, + Computed: true, + }, + "event_hub_operations_endpoint": { + Type: pluginsdk.TypeString, + Computed: true, + }, + + "event_hub_events_path": { + Type: pluginsdk.TypeString, + Computed: true, + }, + "event_hub_operations_path": { + Type: pluginsdk.TypeString, + Computed: true, + }, + + "identity": commonschema.SystemAssignedUserAssignedIdentityOptional(), + + "tags": tags.Schema(), + } + + if !features.ThreePointOhBeta() { + s["ip_filter_rule"] = &schema.Schema{ + Type: pluginsdk.TypeList, + Optional: true, + ConflictsWith: []string{"network_rule_set"}, + Deprecated: "This property block is deprecated in favour of `network_rule_set` and will be removed in version 3.0 of the provider.", + Elem: &pluginsdk.Resource{ + Schema: map[string]*pluginsdk.Schema{ + "name": { + Type: pluginsdk.TypeString, + Required: true, + ValidateFunc: validation.StringIsNotEmpty, + }, + "ip_mask": { + Type: pluginsdk.TypeString, + Required: true, + ValidateFunc: validate.CIDR, + }, + "action": { + Type: pluginsdk.TypeString, + Required: true, + ValidateFunc: validation.StringInSlice([]string{ + string(devices.IPFilterActionTypeAccept), + string(devices.IPFilterActionTypeReject), + }, false), + }, + }, + }, + } + } + return s + }(), } } @@ -747,8 +752,10 @@ func resourceIotHubCreateUpdate(d *pluginsdk.ResourceData, meta interface{}) err Tags: tags.Expand(d.Get("tags").(map[string]interface{})), } - if _, ok := d.GetOk("ip_filter_rule"); ok { - props.Properties.IPFilterRules = expandIPFilterRules(d) + if !features.ThreePointOhBeta() { + if _, ok := d.GetOk("ip_filter_rule"); ok { + props.Properties.IPFilterRules = expandIPFilterRules(d) + } } if _, ok := d.GetOk("network_rule_set"); ok { @@ -890,7 +897,7 @@ func resourceIotHubRead(d *pluginsdk.ResourceData, meta interface{}) error { return fmt.Errorf("setting `network_rule_set` in IoTHub %q: %+v", id.Name, err) } - if len(networkRuleSet) == 0 { + if !features.ThreePointOhBeta() && len(networkRuleSet) == 0 { ipFilterRules := flattenIPFilterRules(properties.IPFilterRules) if err := d.Set("ip_filter_rule", ipFilterRules); err != nil { return fmt.Errorf("setting `ip_filter_rule` in IoTHub %q: %+v", id.Name, err) From 432843cc0813c8b4977f089fb839f170d8bbc528 Mon Sep 17 00:00:00 2001 From: Catriona Date: Tue, 1 Mar 2022 11:06:34 +0000 Subject: [PATCH 5/5] fix network_rule_set schema --- internal/services/iothub/iothub_resource.go | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/internal/services/iothub/iothub_resource.go b/internal/services/iothub/iothub_resource.go index 867901de1c5a..181789fc7616 100644 --- a/internal/services/iothub/iothub_resource.go +++ b/internal/services/iothub/iothub_resource.go @@ -472,9 +472,8 @@ func resourceIotHub() *pluginsdk.Resource { }, "network_rule_set": { - Type: pluginsdk.TypeList, - Optional: true, - ConflictsWith: []string{"ip_filter_rule"}, + Type: pluginsdk.TypeList, + Optional: true, Elem: &pluginsdk.Resource{ Schema: map[string]*pluginsdk.Schema{ "default_action": {