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] - support for the file_upload property #3735

Merged
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
141 changes: 141 additions & 0 deletions azurerm/resource_arm_iothub.go
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,63 @@ func resourceArmIotHub() *schema.Resource {
},
},

"file_upload": {
Type: schema.TypeList,
Optional: true,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"connection_string": {
Type: schema.TypeString,
Required: true,
DiffSuppressFunc: func(k, old, new string, d *schema.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,
},
"container_name": {
Type: schema.TypeString,
Required: true,
},
"notifications": {
Type: schema.TypeBool,
Optional: true,
Default: false,
},
"max_delivery_count": {
Type: schema.TypeInt,
Optional: true,
Default: 10,
ValidateFunc: validation.IntBetween(1, 100),
},
"sas_ttl": {
Type: schema.TypeString,
Optional: true,
Default: "PT1H",
ValidateFunc: validateIso8601Duration(),
},
"default_ttl": {
Type: schema.TypeString,
Optional: true,
Default: "PT1H",
ValidateFunc: validateIso8601Duration(),
},
"lock_duration": {
Type: schema.TypeString,
Optional: true,
Default: "PT1M",
ValidateFunc: validateIso8601Duration(),
},
},
},
},

"endpoint": {
Type: schema.TypeList,
Optional: true,
Expand Down Expand Up @@ -407,6 +464,11 @@ func resourceArmIotHubCreateUpdate(d *schema.ResourceData, meta interface{}) err
return fmt.Errorf("Error expanding `endpoint`: %+v", err)
}

storageEndpoints, messagingEndpoints, enableFileUploadNotifications, err := expandIoTHubFileUpload(d)
if err != nil {
return fmt.Errorf("Error expanding `file_upload`: %+v", err)
}

routes := expandIoTHubRoutes(d)
ipFilterRules := expandIPFilterRules(d)

Expand All @@ -421,6 +483,9 @@ func resourceArmIotHubCreateUpdate(d *schema.ResourceData, meta interface{}) err
Routes: routes,
FallbackRoute: fallbackRoute,
},
StorageEndpoints: storageEndpoints,
MessagingEndpoints: messagingEndpoints,
EnableFileUploadNotifications: &enableFileUploadNotifications,
},
Tags: expandTags(tags),
}
Expand Down Expand Up @@ -517,6 +582,10 @@ func resourceArmIotHubRead(d *schema.ResourceData, meta interface{}) error {
return fmt.Errorf("Error setting `ip_filter_rule` in IoTHub %q: %+v", name, err)
}

fileUpload := flattenIoTHubFileUpload(properties.StorageEndpoints, properties.MessagingEndpoints, properties.EnableFileUploadNotifications)
if err := d.Set("file_upload", fileUpload); err != nil {
return fmt.Errorf("Error setting `file_upload` in IoTHub %q: %+v", name, err)
}
}

d.Set("name", name)
Expand Down Expand Up @@ -621,6 +690,44 @@ func expandIoTHubRoutes(d *schema.ResourceData) *[]devices.RouteProperties {
return &routeProperties
}

func expandIoTHubFileUpload(d *schema.ResourceData) (map[string]*devices.StorageEndpointProperties, map[string]*devices.MessagingEndpointProperties, bool, error) {
fileUploadList := d.Get("file_upload").([]interface{})
jhosteny marked this conversation as resolved.
Show resolved Hide resolved

storageEndpointProperties := make(map[string]*devices.StorageEndpointProperties)
messagingEndpointProperties := make(map[string]*devices.MessagingEndpointProperties)
notifications := false

if len(fileUploadList) > 1 {
return storageEndpointProperties, messagingEndpointProperties, notifications, fmt.Errorf("more than one file_upload found")
}

for _, fileUploadRaw := range fileUploadList {
fileUpload := fileUploadRaw.(map[string]interface{})

connectionStr := fileUpload["connection_string"].(string)
containerName := fileUpload["container_name"].(string)
notifications = fileUpload["notifications"].(bool)
maxDeliveryCount := int32(fileUpload["max_delivery_count"].(int))
sasTTL := fileUpload["sas_ttl"].(string)
defaultTTL := fileUpload["default_ttl"].(string)
lockDuration := fileUpload["lock_duration"].(string)

storageEndpointProperties["$default"] = &devices.StorageEndpointProperties{
SasTTLAsIso8601: &sasTTL,
ConnectionString: &connectionStr,
ContainerName: &containerName,
}

messagingEndpointProperties["fileNotifications"] = &devices.MessagingEndpointProperties{
LockDurationAsIso8601: &lockDuration,
TTLAsIso8601: &defaultTTL,
MaxDeliveryCount: &maxDeliveryCount,
}
}

return storageEndpointProperties, messagingEndpointProperties, notifications, nil
}

func expandIoTHubEndpoints(d *schema.ResourceData, subscriptionId string) (*devices.RoutingEndpoints, error) {
routeEndpointList := d.Get("endpoint").([]interface{})

Expand Down Expand Up @@ -770,6 +877,40 @@ func flattenIoTHubSharedAccessPolicy(input *[]devices.SharedAccessSignatureAutho
return results
}

func flattenIoTHubFileUpload(storageEndpoints map[string]*devices.StorageEndpointProperties, messagingEndpoints map[string]*devices.MessagingEndpointProperties, enableFileUploadNotifications *bool) []interface{} {
results := make([]interface{}, 0)
output := make(map[string]interface{})

if storageEndpointProperties, ok := storageEndpoints["$default"]; ok {
if connString := storageEndpointProperties.ConnectionString; connString != nil {
output["connection_string"] = *connString
}
if containerName := storageEndpointProperties.ContainerName; containerName != nil {
output["container_name"] = *containerName
}
if sasTTLAsIso8601 := storageEndpointProperties.SasTTLAsIso8601; sasTTLAsIso8601 != nil {
output["sas_ttl"] = *sasTTLAsIso8601
}
}

if messagingEndpointProperties, ok := messagingEndpoints["fileNotifications"]; ok {
if lockDurationAsIso8601 := messagingEndpointProperties.LockDurationAsIso8601; lockDurationAsIso8601 != nil {
output["lock_duration"] = *lockDurationAsIso8601
}
if ttlAsIso8601 := messagingEndpointProperties.TTLAsIso8601; ttlAsIso8601 != nil {
output["default_ttl"] = *ttlAsIso8601
}
if maxDeliveryCount := messagingEndpointProperties.MaxDeliveryCount; maxDeliveryCount != nil {
output["max_delivery_count"] = *maxDeliveryCount
}
}

output["notifications"] = *enableFileUploadNotifications
results = append(results, output)

return results
}

func flattenIoTHubEndpoint(input *devices.RoutingProperties) []interface{} {
results := make([]interface{}, 0)

Expand Down
77 changes: 75 additions & 2 deletions azurerm/resource_arm_iothub_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,33 @@ func TestAccAzureRMIotHub_customRoutes(t *testing.T) {
})
}

func TestAccAzureRMIotHub_fileUpload(t *testing.T) {
resourceName := "azurerm_iothub.test"
rInt := tf.AccRandTimeInt()
rStr := acctest.RandString(5)

resource.ParallelTest(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testCheckAzureRMIotHubDestroy,
Steps: []resource.TestStep{
{
Config: testAccAzureRMIotHub_fileUpload(rInt, rStr, testLocation()),
Check: resource.ComposeTestCheckFunc(
testCheckAzureRMIotHubExists(resourceName),
resource.TestCheckResourceAttr(resourceName, "file_upload.#", "1"),
resource.TestCheckResourceAttr(resourceName, "file_upload.0.lock_duration", "PT1M"),
),
},
{
ResourceName: resourceName,
ImportState: true,
ImportStateVerify: true,
},
},
})
}

func TestAccAzureRMIotHub_fallbackRoute(t *testing.T) {
resourceName := "azurerm_iothub.test"
rInt := tf.AccRandTimeInt()
Expand Down Expand Up @@ -312,7 +339,7 @@ resource "azurerm_iothub" "test" {
tier = "Standard"
capacity = "1"
}

ip_filter_rule {
name = "test"
ip_mask = "10.0.0.0/31"
Expand Down Expand Up @@ -368,7 +395,7 @@ resource "azurerm_eventhub_authorization_rule" "test" {
namespace_name = "${azurerm_eventhub_namespace.test.name}"
eventhub_name = "${azurerm_eventhub.test.name}"
name = "acctest"
send = true
send = true
}

resource "azurerm_iothub" "test" {
Expand Down Expand Up @@ -452,3 +479,49 @@ resource "azurerm_iothub" "test" {
}
`, rInt, location, rInt)
}

func testAccAzureRMIotHub_fileUpload(rInt int, rStr string, location string) string {
return fmt.Sprintf(`
resource "azurerm_resource_group" "test" {
name = "acctestRG-%d"
location = "%s"
}

resource "azurerm_storage_account" "test" {
name = "acctestsa%s"
resource_group_name = "${azurerm_resource_group.test.name}"
location = "${azurerm_resource_group.test.location}"
account_tier = "Standard"
account_replication_type = "LRS"
}

resource "azurerm_storage_container" "test" {
name = "test"
resource_group_name = "${azurerm_resource_group.test.name}"
storage_account_name = "${azurerm_storage_account.test.name}"
container_access_type = "private"
}

resource "azurerm_iothub" "test" {
name = "acctestIoTHub-%d"
resource_group_name = "${azurerm_resource_group.test.name}"
location = "${azurerm_resource_group.test.location}"

sku {
name = "S1"
tier = "Standard"
capacity = "1"
}

file_upload {
connection_string = "${azurerm_storage_account.test.primary_blob_connection_string}"
container_name = "${azurerm_storage_container.test.name}"
notifications = true
max_delivery_count = 10
sas_ttl = "PT1H"
default_ttl = "PT1H"
lock_duration = "PT1M"
}
}
`, rInt, location, rStr, rInt)
}
31 changes: 31 additions & 0 deletions website/docs/r/iothub.html.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,16 @@ resource "azurerm_iothub" "test" {
enabled = true
}

file_upload {
connection_string = "${azurerm_storage_account.test.primary_blob_connection_string}"
container_name = "${azurerm_storage_container.test.name}"
sas_ttl = "PT1H"
notifications = true
lock_duration = "PT1M"
default_ttl = "PT1H"
max_delivery_count = 10
}

tags = {
purpose = "testing"
}
Expand All @@ -93,6 +103,8 @@ The following arguments are supported:

* `fallback_route` - (Optional) A `fallback_route` block as defined below. If the fallback route is enabled, messages that don't match any of the supplied routes are automatically sent to this route. Defaults to messages/events.

* `file_upload` - (Optional) A `file_upload` block as defined below.

* `tags` - (Optional) A mapping of tags to assign to the resource.

---
Expand Down Expand Up @@ -163,6 +175,25 @@ A `fallback_route` block supports the following:

* `enabled` - (Optional) Used to specify whether the fallback route is enabled.

---

A `file_upload` block supports the following:

* `connection_string` - (Required) The connection string for the Azure Storage account to which files are uploaded.

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

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

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


## Attributes Reference

The following attributes are exported:
Expand Down