Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

azurerm_iothub - Add support for cloud_to_device block #14546

Merged
merged 2 commits into from
Dec 16, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
128 changes: 128 additions & 0 deletions internal/services/iothub/iothub_resource.go
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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))),
Expand All @@ -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{})),
}
Expand Down Expand Up @@ -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)
}

Expand Down Expand Up @@ -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{})

Expand Down Expand Up @@ -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 {
Expand Down
107 changes: 107 additions & 0 deletions internal/services/iothub/iothub_resource_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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)
}
40 changes: 36 additions & 4 deletions website/docs/r/iothub.html.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -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"
}
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -240,15 +252,35 @@ 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.

* `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.

* `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 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 number of times the IoT hub attempts to deliver a file upload notification message. It 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

Expand Down