diff --git a/internal/services/streamanalytics/registration.go b/internal/services/streamanalytics/registration.go index 445dd1b4fcc6..5cb9684327c1 100644 --- a/internal/services/streamanalytics/registration.go +++ b/internal/services/streamanalytics/registration.go @@ -52,6 +52,7 @@ func (r Registration) SupportedDataSources() map[string]*pluginsdk.Resource { func (r Registration) SupportedResources() map[string]*pluginsdk.Resource { return map[string]*pluginsdk.Resource{ "azurerm_stream_analytics_job": resourceStreamAnalyticsJob(), + "azurerm_stream_analytics_function_javascript_uda": resourceStreamAnalyticsFunctionUDA(), "azurerm_stream_analytics_function_javascript_udf": resourceStreamAnalyticsFunctionUDF(), "azurerm_stream_analytics_output_blob": resourceStreamAnalyticsOutputBlob(), "azurerm_stream_analytics_output_mssql": resourceStreamAnalyticsOutputSql(), diff --git a/internal/services/streamanalytics/stream_analytics_function_javascript_uda_resource.go b/internal/services/streamanalytics/stream_analytics_function_javascript_uda_resource.go new file mode 100644 index 000000000000..b392d3d0e896 --- /dev/null +++ b/internal/services/streamanalytics/stream_analytics_function_javascript_uda_resource.go @@ -0,0 +1,322 @@ +package streamanalytics + +import ( + "fmt" + "log" + "time" + + "github.com/Azure/azure-sdk-for-go/services/preview/streamanalytics/mgmt/2020-03-01-preview/streamanalytics" + "github.com/hashicorp/go-azure-helpers/lang/response" + "github.com/hashicorp/terraform-provider-azurerm/helpers/tf" + "github.com/hashicorp/terraform-provider-azurerm/internal/clients" + "github.com/hashicorp/terraform-provider-azurerm/internal/services/streamanalytics/parse" + "github.com/hashicorp/terraform-provider-azurerm/internal/services/streamanalytics/validate" + "github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk" + "github.com/hashicorp/terraform-provider-azurerm/internal/tf/validation" + "github.com/hashicorp/terraform-provider-azurerm/internal/timeouts" + "github.com/hashicorp/terraform-provider-azurerm/utils" +) + +func resourceStreamAnalyticsFunctionUDA() *pluginsdk.Resource { + return &pluginsdk.Resource{ + Create: resourceStreamAnalyticsFunctionUDACreate, + Read: resourceStreamAnalyticsFunctionUDARead, + Update: resourceStreamAnalyticsFunctionUDAUpdate, + Delete: resourceStreamAnalyticsFunctionUDADelete, + + Importer: pluginsdk.ImporterValidatingResourceId(func(id string) error { + _, err := parse.FunctionID(id) + return err + }), + + Timeouts: &pluginsdk.ResourceTimeout{ + Create: pluginsdk.DefaultTimeout(30 * time.Minute), + Read: pluginsdk.DefaultTimeout(5 * time.Minute), + Update: pluginsdk.DefaultTimeout(30 * time.Minute), + Delete: pluginsdk.DefaultTimeout(30 * time.Minute), + }, + + Schema: map[string]*pluginsdk.Schema{ + "name": { + Type: pluginsdk.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validate.FunctionName, + }, + + "stream_analytics_job_id": { + Type: pluginsdk.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validate.StreamingJobID, + }, + + "input": { + Type: pluginsdk.TypeList, + Required: true, + MinItems: 1, + Elem: &pluginsdk.Resource{ + Schema: map[string]*pluginsdk.Schema{ + "type": { + Type: pluginsdk.TypeString, + Required: true, + ValidateFunc: validation.StringInSlice([]string{ + "any", + "array", + "bigint", + "datetime", + "float", + "nvarchar(max)", + "record", + }, false), + }, + }, + }, + }, + + "output": { + Type: pluginsdk.TypeList, + Required: true, + MaxItems: 1, + Elem: &pluginsdk.Resource{ + Schema: map[string]*pluginsdk.Schema{ + "type": { + Type: pluginsdk.TypeString, + Required: true, + ValidateFunc: validation.StringInSlice([]string{ + "any", + "array", + "bigint", + "datetime", + "float", + "nvarchar(max)", + "record", + }, false), + }, + }, + }, + }, + + "script": { + Type: pluginsdk.TypeString, + Required: true, + ValidateFunc: validation.StringIsNotEmpty, + }, + }, + } +} + +func resourceStreamAnalyticsFunctionUDACreate(d *pluginsdk.ResourceData, meta interface{}) error { + client := meta.(*clients.Client).StreamAnalytics.FunctionsClient + subscriptionId := meta.(*clients.Client).Account.SubscriptionId + ctx, cancel := timeouts.ForCreateUpdate(meta.(*clients.Client).StopContext, d) + defer cancel() + + jobId, err := parse.StreamingJobID(d.Get("stream_analytics_job_id").(string)) + if err != nil { + return err + } + + id := parse.NewFunctionID(subscriptionId, jobId.ResourceGroup, jobId.Name, d.Get("name").(string)) + + existing, err := client.Get(ctx, id.ResourceGroup, id.StreamingjobName, id.Name) + if err != nil { + if !utils.ResponseWasNotFound(existing.Response) { + return fmt.Errorf("checking for presence of existing %s: %+v", id, err) + } + } + + if !utils.ResponseWasNotFound(existing.Response) { + return tf.ImportAsExistsError("azurerm_stream_analytics_function_javascript_uda", id.ID()) + } + + props := streamanalytics.Function{ + Properties: &streamanalytics.AggregateFunctionProperties{ + Type: streamanalytics.TypeAggregate, + FunctionConfiguration: &streamanalytics.FunctionConfiguration{ + Binding: &streamanalytics.JavaScriptFunctionBinding{ + Type: streamanalytics.TypeMicrosoftStreamAnalyticsJavascriptUdf, + JavaScriptFunctionBindingProperties: &streamanalytics.JavaScriptFunctionBindingProperties{ + Script: utils.String(d.Get("script").(string)), + }, + }, + Inputs: expandStreamAnalyticsFunctionUDAInputs(d.Get("input").([]interface{})), + Output: expandStreamAnalyticsFunctionUDAOutput(d.Get("output").([]interface{})), + }, + }, + } + + if _, err := client.CreateOrReplace(ctx, props, id.ResourceGroup, id.StreamingjobName, id.Name, "", ""); err != nil { + return fmt.Errorf("creating %s: %+v", id, err) + } + + d.SetId(id.ID()) + + return resourceStreamAnalyticsFunctionUDARead(d, meta) +} + +func resourceStreamAnalyticsFunctionUDARead(d *pluginsdk.ResourceData, meta interface{}) error { + client := meta.(*clients.Client).StreamAnalytics.FunctionsClient + ctx, cancel := timeouts.ForRead(meta.(*clients.Client).StopContext, d) + defer cancel() + + id, err := parse.FunctionID(d.Id()) + if err != nil { + return err + } + + resp, err := client.Get(ctx, id.ResourceGroup, id.StreamingjobName, id.Name) + if err != nil { + if utils.ResponseWasNotFound(resp.Response) { + log.Printf("[DEBUG] %q was not found - removing from state!", *id) + d.SetId("") + return nil + } + + return fmt.Errorf("retrieving %s: %+v", *id, err) + } + + d.Set("name", id.Name) + + jobId := parse.NewStreamingJobID(id.SubscriptionId, id.ResourceGroup, id.StreamingjobName) + d.Set("stream_analytics_job_id", jobId.ID()) + + if props := resp.Properties; props != nil { + aggregateProps, ok := props.AsAggregateFunctionProperties() + if !ok { + return fmt.Errorf("converting Props to a Aggregate Function") + } + + binding, ok := aggregateProps.Binding.AsJavaScriptFunctionBinding() + if !ok { + return fmt.Errorf("converting Binding to a JavaScript Function Binding") + } + + if bindingProps := binding.JavaScriptFunctionBindingProperties; bindingProps != nil { + d.Set("script", bindingProps.Script) + } + + if err := d.Set("input", flattenStreamAnalyticsFunctionUDAInputs(aggregateProps.Inputs)); err != nil { + return fmt.Errorf("flattening `input`: %+v", err) + } + + if err := d.Set("output", flattenStreamAnalyticsFunctionUDAOutput(aggregateProps.Output)); err != nil { + return fmt.Errorf("flattening `output`: %+v", err) + } + } + + return nil +} + +func resourceStreamAnalyticsFunctionUDAUpdate(d *pluginsdk.ResourceData, meta interface{}) error { + client := meta.(*clients.Client).StreamAnalytics.FunctionsClient + ctx, cancel := timeouts.ForCreateUpdate(meta.(*clients.Client).StopContext, d) + defer cancel() + + id, err := parse.FunctionID(d.Id()) + if err != nil { + return err + } + + props := streamanalytics.Function{ + Properties: &streamanalytics.AggregateFunctionProperties{ + Type: streamanalytics.TypeAggregate, + FunctionConfiguration: &streamanalytics.FunctionConfiguration{ + Binding: &streamanalytics.JavaScriptFunctionBinding{ + Type: streamanalytics.TypeMicrosoftStreamAnalyticsJavascriptUdf, + JavaScriptFunctionBindingProperties: &streamanalytics.JavaScriptFunctionBindingProperties{ + Script: utils.String(d.Get("script").(string)), + }, + }, + Inputs: expandStreamAnalyticsFunctionUDAInputs(d.Get("input").([]interface{})), + Output: expandStreamAnalyticsFunctionUDAOutput(d.Get("output").([]interface{})), + }, + }, + } + + if _, err := client.Update(ctx, props, id.ResourceGroup, id.StreamingjobName, id.Name, ""); err != nil { + return fmt.Errorf("updating %s: %+v", id, err) + } + + return resourceStreamAnalyticsFunctionUDARead(d, meta) +} + +func resourceStreamAnalyticsFunctionUDADelete(d *pluginsdk.ResourceData, meta interface{}) error { + client := meta.(*clients.Client).StreamAnalytics.FunctionsClient + ctx, cancel := timeouts.ForDelete(meta.(*clients.Client).StopContext, d) + defer cancel() + + id, err := parse.FunctionID(d.Id()) + if err != nil { + return err + } + + if resp, err := client.Delete(ctx, id.ResourceGroup, id.StreamingjobName, id.Name); err != nil { + if !response.WasNotFound(resp.Response) { + return fmt.Errorf("deleting %s: %+v", *id, err) + } + } + + return nil +} + +func expandStreamAnalyticsFunctionUDAInputs(input []interface{}) *[]streamanalytics.FunctionInput { + outputs := make([]streamanalytics.FunctionInput, 0) + + for _, raw := range input { + v := raw.(map[string]interface{}) + variableType := v["type"].(string) + outputs = append(outputs, streamanalytics.FunctionInput{ + DataType: utils.String(variableType), + }) + } + + return &outputs +} + +func flattenStreamAnalyticsFunctionUDAInputs(input *[]streamanalytics.FunctionInput) []interface{} { + if input == nil { + return []interface{}{} + } + + outputs := make([]interface{}, 0) + + for _, v := range *input { + var variableType string + if v.DataType != nil { + variableType = *v.DataType + } + + outputs = append(outputs, map[string]interface{}{ + "type": variableType, + }) + } + + return outputs +} + +func expandStreamAnalyticsFunctionUDAOutput(input []interface{}) *streamanalytics.FunctionOutput { + output := input[0].(map[string]interface{}) + + dataType := output["type"].(string) + return &streamanalytics.FunctionOutput{ + DataType: utils.String(dataType), + } +} + +func flattenStreamAnalyticsFunctionUDAOutput(input *streamanalytics.FunctionOutput) []interface{} { + if input == nil { + return []interface{}{} + } + + var variableType string + if input.DataType != nil { + variableType = *input.DataType + } + + return []interface{}{ + map[string]interface{}{ + "type": variableType, + }, + } +} diff --git a/internal/services/streamanalytics/stream_analytics_function_javascript_uda_resource_test.go b/internal/services/streamanalytics/stream_analytics_function_javascript_uda_resource_test.go new file mode 100644 index 000000000000..dde194e5fbaa --- /dev/null +++ b/internal/services/streamanalytics/stream_analytics_function_javascript_uda_resource_test.go @@ -0,0 +1,279 @@ +package streamanalytics_test + +import ( + "context" + "fmt" + "testing" + + "github.com/hashicorp/terraform-provider-azurerm/internal/acceptance" + "github.com/hashicorp/terraform-provider-azurerm/internal/acceptance/check" + "github.com/hashicorp/terraform-provider-azurerm/internal/clients" + "github.com/hashicorp/terraform-provider-azurerm/internal/services/streamanalytics/parse" + "github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk" + "github.com/hashicorp/terraform-provider-azurerm/utils" +) + +type StreamAnalyticsFunctionJavaScriptUDAResource struct{} + +func TestAccStreamAnalyticsFunctionJavaScriptUDA_basic(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_stream_analytics_function_javascript_uda", "test") + r := StreamAnalyticsFunctionJavaScriptUDAResource{} + + data.ResourceTest(t, r, []acceptance.TestStep{ + { + Config: r.basic(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep(), + }) +} + +func TestAccStreamAnalyticsFunctionJavaScriptUDA_requiresImport(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_stream_analytics_function_javascript_uda", "test") + r := StreamAnalyticsFunctionJavaScriptUDAResource{} + + data.ResourceTest(t, r, []acceptance.TestStep{ + { + Config: r.basic(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.RequiresImportErrorStep(r.requiresImport), + }) +} + +func TestAccStreamAnalyticsFunctionJavaScriptUDA_inputs(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_stream_analytics_function_javascript_uda", "test") + r := StreamAnalyticsFunctionJavaScriptUDAResource{} + + data.ResourceTest(t, r, []acceptance.TestStep{ + { + Config: r.basic(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep(), + { + Config: r.complete(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep(), + { + Config: r.basic(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep(), + }) +} + +func TestAccStreamAnalyticsFunctionJavaScriptUDA_update(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_stream_analytics_function_javascript_uda", "test") + r := StreamAnalyticsFunctionJavaScriptUDAResource{} + + data.ResourceTest(t, r, []acceptance.TestStep{ + { + Config: r.basic(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep(), + { + Config: r.update(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep(), + }) +} + +func (r StreamAnalyticsFunctionJavaScriptUDAResource) Exists(ctx context.Context, client *clients.Client, state *pluginsdk.InstanceState) (*bool, error) { + id, err := parse.FunctionID(state.ID) + if err != nil { + return nil, err + } + + resp, err := client.StreamAnalytics.FunctionsClient.Get(ctx, id.ResourceGroup, id.StreamingjobName, id.Name) + if err != nil { + if utils.ResponseWasNotFound(resp.Response) { + return utils.Bool(false), nil + } + return nil, fmt.Errorf("retrieving %s : %+v", *id, err) + } + + return utils.Bool(true), nil +} + +func (r StreamAnalyticsFunctionJavaScriptUDAResource) basic(data acceptance.TestData) string { + return fmt.Sprintf(` +%s + +resource "azurerm_stream_analytics_function_javascript_uda" "test" { + name = "acctestinput-%d" + stream_analytics_job_id = azurerm_stream_analytics_job.test.id + + script = <<SCRIPT +function main() { + this.init = function () { + this.state = 0; + } + + this.accumulate = function (value, timestamp) { + this.state += value; + } + + this.computeResult = function () { + return this.state; + } +} +SCRIPT + + + input { + type = "bigint" + } + + output { + type = "bigint" + } +} +`, r.template(data), data.RandomInteger) +} + +func (r StreamAnalyticsFunctionJavaScriptUDAResource) requiresImport(data acceptance.TestData) string { + return fmt.Sprintf(` +%s + +resource "azurerm_stream_analytics_function_javascript_uda" "import" { + name = azurerm_stream_analytics_function_javascript_uda.test.name + stream_analytics_job_id = azurerm_stream_analytics_function_javascript_uda.test.stream_analytics_job_id + script = azurerm_stream_analytics_function_javascript_uda.test.script + + input { + type = azurerm_stream_analytics_function_javascript_uda.test.input.0.type + } + + output { + type = azurerm_stream_analytics_function_javascript_uda.test.output.0.type + } +} +`, r.basic(data)) +} + +func (r StreamAnalyticsFunctionJavaScriptUDAResource) complete(data acceptance.TestData) string { + return fmt.Sprintf(` +%s + +resource "azurerm_stream_analytics_function_javascript_uda" "test" { + name = "acctestinput-%d" + stream_analytics_job_id = azurerm_stream_analytics_job.test.id + + script = <<SCRIPT +function main() { + this.init = function () { + this.state = 0; + } + + this.accumulate = function (value, timestamp) { + this.state += value; + } + + this.computeResult = function () { + return this.state; + } +} +SCRIPT + + + input { + type = "bigint" + } + + input { + type = "float" + } + + output { + type = "bigint" + } +} +`, r.template(data), data.RandomInteger) +} + +func (r StreamAnalyticsFunctionJavaScriptUDAResource) update(data acceptance.TestData) string { + return fmt.Sprintf(` +%s + +resource "azurerm_stream_analytics_function_javascript_uda" "test" { + name = "acctestinput-%d" + stream_analytics_job_id = azurerm_stream_analytics_job.test.id + + script = <<SCRIPT +function main() { + this.init = function () { + this.state = 0; + } + + this.accumulate = function (value) { + this.state += value; + } + + this.computeResult = function () { + return this.state; + } +} +SCRIPT + + + input { + type = "float" + } + + output { + type = "float" + } +} +`, r.template(data), data.RandomInteger) +} + +func (r StreamAnalyticsFunctionJavaScriptUDAResource) template(data acceptance.TestData) string { + return fmt.Sprintf(` +provider "azurerm" { + features {} +} + +resource "azurerm_resource_group" "test" { + name = "acctestRG-%d" + location = "%s" +} + +resource "azurerm_stream_analytics_job" "test" { + name = "acctestjob-%d" + resource_group_name = azurerm_resource_group.test.name + location = azurerm_resource_group.test.location + compatibility_level = "1.0" + data_locale = "en-GB" + events_late_arrival_max_delay_in_seconds = 60 + events_out_of_order_max_delay_in_seconds = 50 + events_out_of_order_policy = "Adjust" + output_error_policy = "Drop" + streaming_units = 3 + + transformation_query = <<QUERY + SELECT * + INTO [YourOutputAlias] + FROM [YourInputAlias] +QUERY + +} +`, data.RandomInteger, data.Locations.Primary, data.RandomInteger) +} diff --git a/internal/services/streamanalytics/validate/function_name.go b/internal/services/streamanalytics/validate/function_name.go new file mode 100644 index 000000000000..0b14f5d137ec --- /dev/null +++ b/internal/services/streamanalytics/validate/function_name.go @@ -0,0 +1,16 @@ +package validate + +import ( + "fmt" + "regexp" +) + +func FunctionName(v interface{}, _ string) (warnings []string, errors []error) { + input := v.(string) + + if !regexp.MustCompile(`^[a-zA-Z0-9-]{3,63}$`).MatchString(input) { + errors = append(errors, fmt.Errorf("%s contain only letters, numbers and hyphens. The value must be between 3 and 63 characters long", input)) + } + + return warnings, errors +} diff --git a/internal/services/streamanalytics/validate/function_name_test.go b/internal/services/streamanalytics/validate/function_name_test.go new file mode 100644 index 000000000000..db696806a474 --- /dev/null +++ b/internal/services/streamanalytics/validate/function_name_test.go @@ -0,0 +1,32 @@ +package validate + +import ( + "strings" + "testing" +) + +func TestFunctionName(t *testing.T) { + testCases := []struct { + input string + shouldError bool + }{ + {"", true}, + {"ABC", false}, + {"abc", false}, + {"a-b", false}, + {"ab-", false}, + {"ab-1", false}, + {"ab#-1", false}, + {strings.Repeat("s", 3), false}, + {strings.Repeat("s", 63), false}, + {strings.Repeat("s", 64), true}, + } + + for _, test := range testCases { + _, es := FunctionName(test.input, "name") + + if test.shouldError && len(es) == 0 { + t.Fatalf("Expected validating name %q to fail", test.input) + } + } +} diff --git a/website/docs/r/stream_analytics_function_javascript_uda.html.markdown b/website/docs/r/stream_analytics_function_javascript_uda.html.markdown new file mode 100644 index 000000000000..9d27ff3edcdd --- /dev/null +++ b/website/docs/r/stream_analytics_function_javascript_uda.html.markdown @@ -0,0 +1,102 @@ +--- +subcategory: "Stream Analytics" +layout: "azurerm" +page_title: "Azure Resource Manager: azurerm_stream_analytics_function_javascript_uda" +description: |- + Manages a JavaScript UDA Function within a Stream Analytics Streaming Job. +--- + +# azurerm_stream_analytics_function_javascript_uda + +Manages a JavaScript UDA Function within a Stream Analytics Streaming Job. + +## Example Usage + +```hcl +data "azurerm_resource_group" "example" { + name = "example-resources" +} + +data "azurerm_stream_analytics_job" "example" { + name = "example-job" + resource_group_name = azurerm_resource_group.example.name +} + +resource "azurerm_stream_analytics_function_javascript_uda" "example" { + name = "example-javascript-function" + stream_analytics_job_id = data.azurerm_stream_analytics_job.example.id + + script = <<SCRIPT +function main() { + this.init = function () { + this.state = 0; + } + + this.accumulate = function (value, timestamp) { + this.state += value; + } + + this.computeResult = function () { + return this.state; + } +} +SCRIPT + + input { + type = "bigint" + } + + output { + type = "bigint" + } +} +``` + +## Argument Reference + +The following arguments are supported: + +* `name` - (Required) The name of the JavaScript UDA Function. Changing this forces a new resource to be created. + +* `stream_analytics_job_id` - (Required) The resource ID of the Stream Analytics Job where this Function should be created. Changing this forces a new resource to be created. + +* `input` - (Required) One or more `input` blocks as defined below. + +* `output` - (Required) An `output` block as defined below. + +* `script` - (Required) The JavaScript of this UDA Function. + +--- + +An `input` block supports the following: + +* `type` - The input data type of this JavaScript Function. Possible values include `any`, `array`, `bigint`, `datetime`, `float`, `nvarchar(max)` and `record`. + +--- + +An `output` block supports the following: + +* `type` - The output data type from this JavaScript Function. Possible values include `any`, `array`, `bigint`, `datetime`, `float`, `nvarchar(max)` and `record`. + +## Attributes Reference + +The following attributes are exported in addition to the arguments listed above: + +* `id` - The ID of the Stream Analytics JavaScript UDA Function. + +## Timeouts + +The `timeouts` block allows you to specify [timeouts](https://www.terraform.io/docs/configuration/resources.html#timeouts) for certain actions: + +* `create` - (Defaults to 30 minutes) Used when creating the Stream Analytics JavaScript UDA Function. +* `update` - (Defaults to 30 minutes) Used when updating the Stream Analytics JavaScript UDA Function. +* `read` - (Defaults to 5 minutes) Used when retrieving the Stream Analytics JavaScript UDA Function. +* `delete` - (Defaults to 30 minutes) Used when deleting the Stream Analytics JavaScript UDA Function. + +## Import + +Stream Analytics JavaScript UDA Functions can be imported using the `resource id`, e.g. + +```shell +terraform import azurerm_stream_analytics_function_javascript_uda.example /subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/group1/providers/Microsoft.StreamAnalytics/streamingjobs/job1/functions/func1 +```