From 888456e822600cdcd11ee74a8bbf52dd1e3ce891 Mon Sep 17 00:00:00 2001 From: Catriona Date: Wed, 8 Dec 2021 16:51:33 +0000 Subject: [PATCH 1/2] Add cloud_to_device block for iothub --- internal/services/iothub/iothub_resource.go | 128 ++++++++++++++++++ .../services/iothub/iothub_resource_test.go | 107 +++++++++++++++ website/docs/r/iothub.html.markdown | 32 +++++ 3 files changed, 267 insertions(+) diff --git a/internal/services/iothub/iothub_resource.go b/internal/services/iothub/iothub_resource.go index 23045c70d9e5..c1854237c2b0 100644 --- a/internal/services/iothub/iothub_resource.go +++ b/internal/services/iothub/iothub_resource.go @@ -441,6 +441,55 @@ func resourceIotHub() *pluginsdk.Resource { }, }, + "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, @@ -548,6 +597,11 @@ func resourceIotHubCreateUpdate(d *pluginsdk.ResourceData, meta interface{}) err return fmt.Errorf("expanding `file_upload`: %+v", err) } + cloudToDeviceProperties := &devices.CloudToDeviceProperties{} + if _, ok := d.GetOk("cloud_to_device"); ok { + cloudToDeviceProperties = expandIoTHubCloudToDevice(d) + } + props := devices.IotHubDescription{ Name: utils.String(id.Name), Location: utils.String(azure.NormalizeLocation(d.Get("location").(string))), @@ -558,6 +612,7 @@ func resourceIotHubCreateUpdate(d *pluginsdk.ResourceData, meta interface{}) err StorageEndpoints: storageEndpoints, MessagingEndpoints: messagingEndpoints, EnableFileUploadNotifications: &enableFileUploadNotifications, + CloudToDevice: cloudToDeviceProperties, }, Tags: tags.Expand(d.Get("tags").(map[string]interface{})), } @@ -698,6 +753,11 @@ func resourceIotHubRead(d *pluginsdk.ResourceData, meta interface{}) error { d.Set("public_network_access_enabled", enabled == devices.PublicNetworkAccessEnabled) } + cloudToDevice := flattenIoTHubCloudToDevice(properties.CloudToDevice) + if err := d.Set("cloud_to_device", cloudToDevice); err != nil { + return fmt.Errorf("setting `cloudToDevice` in IoTHub %q: %+v", id.Name, err) + } + d.Set("min_tls_version", properties.MinTLSVersion) } @@ -993,6 +1053,36 @@ func expandIoTHubSku(d *pluginsdk.ResourceData) *devices.IotHubSkuInfo { } } +func expandIoTHubCloudToDevice(d *pluginsdk.ResourceData) *devices.CloudToDeviceProperties { + ctdList := d.Get("cloud_to_device").([]interface{}) + if len(ctdList) == 0 { + return nil + } + cloudToDevice := devices.CloudToDeviceProperties{} + ctdMap := ctdList[0].(map[string]interface{}) + defaultTimeToLive := ctdMap["default_ttl"].(string) + + cloudToDevice.DefaultTTLAsIso8601 = &defaultTimeToLive + cloudToDevice.MaxDeliveryCount = utils.Int32(int32(ctdMap["max_delivery_count"].(int))) + feedback := ctdMap["feedback"].([]interface{}) + + cloudToDeviceFeedback := devices.FeedbackProperties{} + if len(feedback) > 0 { + feedbackMap := feedback[0].(map[string]interface{}) + + lockDuration := feedbackMap["lock_duration"].(string) + timeToLive := feedbackMap["time_to_live"].(string) + + cloudToDeviceFeedback.TTLAsIso8601 = &timeToLive + cloudToDeviceFeedback.LockDurationAsIso8601 = &lockDuration + cloudToDeviceFeedback.MaxDeliveryCount = utils.Int32(int32(feedbackMap["max_delivery_count"].(int))) + } + + cloudToDevice.Feedback = &cloudToDeviceFeedback + + return &cloudToDevice +} + func flattenIoTHubSku(input *devices.IotHubSkuInfo) []interface{} { output := make(map[string]interface{}) @@ -1244,6 +1334,44 @@ func flattenIoTHubFallbackRoute(input *devices.RoutingProperties) []interface{} return []interface{}{output} } +func flattenIoTHubCloudToDevice(input *devices.CloudToDeviceProperties) []interface{} { + if input == nil { + return []interface{}{} + } + + output := make(map[string]interface{}) + + if maxDeliveryCount := input.MaxDeliveryCount; maxDeliveryCount != nil { + output["max_delivery_count"] = *maxDeliveryCount + } + if defaultTimeToLive := input.DefaultTTLAsIso8601; defaultTimeToLive != nil { + output["default_ttl"] = *defaultTimeToLive + } + + output["feedback"] = flattenIoTHubCloudToDeviceFeedback(input.Feedback) + + return []interface{}{output} +} + +func flattenIoTHubCloudToDeviceFeedback(input *devices.FeedbackProperties) []interface{} { + if input == nil { + return []interface{}{} + } + + feedback := make(map[string]interface{}) + if feedbackMaxDeliveryCount := input.MaxDeliveryCount; feedbackMaxDeliveryCount != nil { + feedback["max_delivery_count"] = *feedbackMaxDeliveryCount + } + if feedbackTimeToLive := input.TTLAsIso8601; feedbackTimeToLive != nil { + feedback["time_to_live"] = *feedbackTimeToLive + } + if feedbackLockDuration := input.LockDurationAsIso8601; feedbackLockDuration != nil { + feedback["lock_duration"] = *feedbackLockDuration + } + + return []interface{}{feedback} +} + func expandIPFilterRules(d *pluginsdk.ResourceData) *[]devices.IPFilterRule { ipFilterRuleList := d.Get("ip_filter_rule").([]interface{}) if len(ipFilterRuleList) == 0 { diff --git a/internal/services/iothub/iothub_resource_test.go b/internal/services/iothub/iothub_resource_test.go index 98db2beda0c2..0ea9a06f54f9 100644 --- a/internal/services/iothub/iothub_resource_test.go +++ b/internal/services/iothub/iothub_resource_test.go @@ -247,6 +247,37 @@ func TestAccIotHub_minTLSVersion(t *testing.T) { }) } +func TestAccIotHub_cloudToDevice(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_iothub", "test") + r := IotHubResource{} + + data.ResourceTest(t, r, []acceptance.TestStep{ + { + Config: r.cloudToDevice(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + check.That(data.ResourceName).Key("cloud_to_device.0.max_delivery_count").HasValue("20"), + check.That(data.ResourceName).Key("cloud_to_device.0.default_ttl").HasValue("PT1H30M"), + check.That(data.ResourceName).Key("cloud_to_device.0.feedback.0.time_to_live").HasValue("PT1H15M"), + check.That(data.ResourceName).Key("cloud_to_device.0.feedback.0.max_delivery_count").HasValue("25"), + check.That(data.ResourceName).Key("cloud_to_device.0.feedback.0.lock_duration").HasValue("PT55S"), + ), + }, + { + Config: r.cloudToDeviceUpdated(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + check.That(data.ResourceName).Key("cloud_to_device.0.max_delivery_count").HasValue("30"), + check.That(data.ResourceName).Key("cloud_to_device.0.default_ttl").HasValue("PT1H"), + check.That(data.ResourceName).Key("cloud_to_device.0.feedback.0.time_to_live").HasValue("PT1H10M"), + check.That(data.ResourceName).Key("cloud_to_device.0.feedback.0.max_delivery_count").HasValue("15"), + check.That(data.ResourceName).Key("cloud_to_device.0.feedback.0.lock_duration").HasValue("PT30S"), + ), + }, + data.ImportStep(), + }) +} + func (t IotHubResource) Exists(ctx context.Context, clients *clients.Client, state *pluginsdk.InstanceState) (*bool, error) { id, err := parse.IotHubID(state.ID) if err != nil { @@ -977,3 +1008,79 @@ resource "azurerm_iothub" "test" { } `, data.RandomInteger, "eastus", data.RandomInteger) } + +func (IotHubResource) cloudToDevice(data acceptance.TestData) string { + return fmt.Sprintf(` +provider "azurerm" { + features {} +} + +resource "azurerm_resource_group" "test" { + name = "acctestRG-iothub-%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 = "B1" + capacity = "1" + } + + cloud_to_device { + max_delivery_count = 20 + default_ttl = "PT1H30M" + feedback { + time_to_live = "PT1H15M" + max_delivery_count = 25 + lock_duration = "PT55S" + } + } + + tags = { + purpose = "testing" + } +} +`, data.RandomInteger, data.Locations.Primary, data.RandomInteger) +} + +func (IotHubResource) cloudToDeviceUpdated(data acceptance.TestData) string { + return fmt.Sprintf(` +provider "azurerm" { + features {} +} + +resource "azurerm_resource_group" "test" { + name = "acctestRG-iothub-%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 = "B1" + capacity = "1" + } + + cloud_to_device { + max_delivery_count = 30 + default_ttl = "PT1H" + feedback { + time_to_live = "PT1H10M" + max_delivery_count = 15 + lock_duration = "PT30S" + } + } + + tags = { + purpose = "testing" + } +} +`, data.RandomInteger, data.Locations.Primary, data.RandomInteger) +} diff --git a/website/docs/r/iothub.html.markdown b/website/docs/r/iothub.html.markdown index aee680f9729c..baea5c845e61 100644 --- a/website/docs/r/iothub.html.markdown +++ b/website/docs/r/iothub.html.markdown @@ -112,6 +112,16 @@ resource "azurerm_iothub" "example" { endpoint_names = ["export", "export2"] } + cloud_to_device { + max_delivery_count = 30 + default_ttl = "PT1H" + feedback { + time_to_live = "PT1H10M" + max_delivery_count = 15 + lock_duration = "PT30S" + } + } + tags = { purpose = "testing" } @@ -148,6 +158,8 @@ The following arguments are supported: * `enrichment` - (Optional) A `enrichment` block as defined below. +* `cloud_to_device` - (Optional) A `cloud_to_device` block as defined below. + * `public_network_access_enabled` - (Optional) Is the IotHub resource accessible from a public network? * `min_tls_version` - (Optional) Specifies the minimum TLS version to support for this hub. The only valid value is `1.2`. Changing this forces a new resource to be created. @@ -250,6 +262,26 @@ A `file_upload` block supports the following: * `max_delivery_count` - (Optional) The number of times the IoT hub attempts to deliver a file upload notification message. It evaluates to 10 by default. +--- + +A `cloud_to_device` block supports the following: + +* `max_delivery_count` - (Optional) The maximum delivery count for cloud-to-device per-device queues. This value must be between 1 and 100, and evaluates to 10 by default. + +* `default_ttl` - (Optional) The default time to live for cloud-to-device messages, specified as an [ISO 8601 timespan duration](https://en.wikipedia.org/wiki/ISO_8601#Durations). This value must be between 1 minute and 48 hours, and evaluates to 'PT1H' by default. + +* `feedback` - (Optional) A `feedback` block as defined below. + +--- + +A `feedback` block supports the following: + +* `time_to_live` - (Optional) The retention time for service-bound feedback messages, specified as an [ISO 8601 timespan duration](https://en.wikipedia.org/wiki/ISO_8601#Durations). This value must be between 1 minute and 48 hours, and evaluates to 'PT1H' by default. + +* `max_delivery_count` - (Optional) The maximum delivery count for the feedback queue. This value must be between 1 and 100, and evaluates to 10 by default. + +* `lock_duration` - (Optional) The lock duration for the feedback queue, specified as an [ISO 8601 timespan duration](https://en.wikipedia.org/wiki/ISO_8601#Durations). This value must be between 5 and 300 seconds, and evaluates to 'PT60S' by default. + ## Attributes Reference The following attributes are exported: From 385f10add433e28e72694bd7b4ebb32c4ac547ad Mon Sep 17 00:00:00 2001 From: Catriona Date: Mon, 13 Dec 2021 14:09:49 +0000 Subject: [PATCH 2/2] highlight property values --- website/docs/r/iothub.html.markdown | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/website/docs/r/iothub.html.markdown b/website/docs/r/iothub.html.markdown index baea5c845e61..509d2e0ef0a4 100644 --- a/website/docs/r/iothub.html.markdown +++ b/website/docs/r/iothub.html.markdown @@ -252,23 +252,23 @@ A `file_upload` block supports the following: * `container_name` - (Required) The name of the root container where you upload files. The container need not exist but should be creatable using the connection_string specified. -* `sas_ttl` - (Optional) The period of time for which the SAS URI generated by IoT Hub for file upload is valid, specified as an [ISO 8601 timespan duration](https://en.wikipedia.org/wiki/ISO_8601#Durations). This value must be between 1 minute and 24 hours, and evaluates to 'PT1H' by default. +* `sas_ttl` - (Optional) The period of time for which the SAS URI generated by IoT Hub for file upload is valid, specified as an [ISO 8601 timespan duration](https://en.wikipedia.org/wiki/ISO_8601#Durations). This value must be between 1 minute and 24 hours, and evaluates to `PT1H` by default. * `notifications` - (Optional) Used to specify whether file notifications are sent to IoT Hub on upload. It evaluates to false by default. -* `lock_duration` - (Optional) The lock duration for the file upload notifications queue, specified as an [ISO 8601 timespan duration](https://en.wikipedia.org/wiki/ISO_8601#Durations). This value must be between 5 and 300 seconds, and evaluates to 'PT1M' by default. +* `lock_duration` - (Optional) The lock duration for the file upload notifications queue, specified as an [ISO 8601 timespan duration](https://en.wikipedia.org/wiki/ISO_8601#Durations). This value must be between 5 and 300 seconds, and evaluates to `PT1M` by default. -* `default_ttl` - (Optional) The period of time for which a file upload notification message is available to consume before it is expired by the IoT hub, specified as an [ISO 8601 timespan duration](https://en.wikipedia.org/wiki/ISO_8601#Durations). This value must be between 1 minute and 48 hours, and evaluates to 'PT1H' by default. +* `default_ttl` - (Optional) The period of time for which a file upload notification message is available to consume before it is expired by the IoT hub, specified as an [ISO 8601 timespan duration](https://en.wikipedia.org/wiki/ISO_8601#Durations). This value must be between 1 minute and 48 hours, and evaluates to `PT1H` by default. -* `max_delivery_count` - (Optional) The number of times the IoT hub attempts to deliver a file upload notification message. It evaluates to 10 by default. +* `max_delivery_count` - (Optional) The number of times the IoT hub attempts to deliver a file upload notification message. It evaluates to `10` by default. --- A `cloud_to_device` block supports the following: -* `max_delivery_count` - (Optional) The maximum delivery count for cloud-to-device per-device queues. This value must be between 1 and 100, and evaluates to 10 by default. +* `max_delivery_count` - (Optional) The maximum delivery count for cloud-to-device per-device queues. This value must be between `1` and `100`, and evaluates to `10` by default. -* `default_ttl` - (Optional) The default time to live for cloud-to-device messages, specified as an [ISO 8601 timespan duration](https://en.wikipedia.org/wiki/ISO_8601#Durations). This value must be between 1 minute and 48 hours, and evaluates to 'PT1H' by default. +* `default_ttl` - (Optional) The default time to live for cloud-to-device messages, specified as an [ISO 8601 timespan duration](https://en.wikipedia.org/wiki/ISO_8601#Durations). This value must be between 1 minute and 48 hours, and evaluates to `PT1H` by default. * `feedback` - (Optional) A `feedback` block as defined below. @@ -276,11 +276,11 @@ A `cloud_to_device` block supports the following: A `feedback` block supports the following: -* `time_to_live` - (Optional) The retention time for service-bound feedback messages, specified as an [ISO 8601 timespan duration](https://en.wikipedia.org/wiki/ISO_8601#Durations). This value must be between 1 minute and 48 hours, and evaluates to 'PT1H' by default. +* `time_to_live` - (Optional) The retention time for service-bound feedback messages, specified as an [ISO 8601 timespan duration](https://en.wikipedia.org/wiki/ISO_8601#Durations). This value must be between 1 minute and 48 hours, and evaluates to `PT1H` by default. -* `max_delivery_count` - (Optional) The maximum delivery count for the feedback queue. This value must be between 1 and 100, and evaluates to 10 by default. +* `max_delivery_count` - (Optional) The maximum delivery count for the feedback queue. This value must be between `1` and `100`, and evaluates to `10` by default. -* `lock_duration` - (Optional) The lock duration for the feedback queue, specified as an [ISO 8601 timespan duration](https://en.wikipedia.org/wiki/ISO_8601#Durations). This value must be between 5 and 300 seconds, and evaluates to 'PT60S' by default. +* `lock_duration` - (Optional) The lock duration for the feedback queue, specified as an [ISO 8601 timespan duration](https://en.wikipedia.org/wiki/ISO_8601#Durations). This value must be between 5 and 300 seconds, and evaluates to `PT60S` by default. ## Attributes Reference