diff --git a/azurerm/internal/services/digitaltwins/digital_twins_endpoint_servicebus_resource.go b/azurerm/internal/services/digitaltwins/digital_twins_endpoint_servicebus_resource.go new file mode 100644 index 000000000000..e230de3be5cb --- /dev/null +++ b/azurerm/internal/services/digitaltwins/digital_twins_endpoint_servicebus_resource.go @@ -0,0 +1,179 @@ +package digitaltwins + +import ( + "fmt" + "log" + "time" + + "github.com/Azure/azure-sdk-for-go/services/digitaltwins/mgmt/2020-10-31/digitaltwins" + "github.com/hashicorp/terraform-plugin-sdk/helper/schema" + "github.com/hashicorp/terraform-plugin-sdk/helper/validation" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/clients" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/services/digitaltwins/parse" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/services/digitaltwins/validate" + azSchema "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/tf/schema" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/timeouts" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" +) + +func resourceDigitalTwinsEndpointServiceBus() *schema.Resource { + return &schema.Resource{ + Create: resourceDigitalTwinsEndpointServiceBusCreateUpdate, + Read: resourceDigitalTwinsEndpointServiceBusRead, + Update: resourceDigitalTwinsEndpointServiceBusCreateUpdate, + Delete: resourceDigitalTwinsEndpointServiceBusDelete, + + Timeouts: &schema.ResourceTimeout{ + Create: schema.DefaultTimeout(30 * time.Minute), + Read: schema.DefaultTimeout(5 * time.Minute), + Update: schema.DefaultTimeout(30 * time.Minute), + Delete: schema.DefaultTimeout(30 * time.Minute), + }, + + Importer: azSchema.ValidateResourceIDPriorToImport(func(id string) error { + _, err := parse.DigitalTwinsEndpointID(id) + return err + }), + + Schema: map[string]*schema.Schema{ + "name": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validate.DigitalTwinsInstanceName, + }, + + "digital_twins_id": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validate.DigitalTwinsInstanceID, + }, + + "servicebus_primary_connection_string": { + Type: schema.TypeString, + Required: true, + Sensitive: true, + ValidateFunc: validation.StringIsNotEmpty, + }, + + "servicebus_secondary_connection_string": { + Type: schema.TypeString, + Required: true, + Sensitive: true, + ValidateFunc: validation.StringIsNotEmpty, + }, + + "dead_letter_storage_secret": { + Type: schema.TypeString, + Optional: true, + Sensitive: true, + ValidateFunc: validation.StringIsNotEmpty, + }, + }, + } +} +func resourceDigitalTwinsEndpointServiceBusCreateUpdate(d *schema.ResourceData, meta interface{}) error { + subscriptionId := meta.(*clients.Client).Account.SubscriptionId + client := meta.(*clients.Client).DigitalTwins.EndpointClient + ctx, cancel := timeouts.ForCreateUpdate(meta.(*clients.Client).StopContext, d) + defer cancel() + + name := d.Get("name").(string) + digitalTwinsId, err := parse.DigitalTwinsInstanceID(d.Get("digital_twins_id").(string)) + if err != nil { + return err + } + + id := parse.NewDigitalTwinsEndpointID(subscriptionId, digitalTwinsId.ResourceGroup, digitalTwinsId.Name, name).ID("") + + if d.IsNewResource() { + existing, err := client.Get(ctx, digitalTwinsId.ResourceGroup, digitalTwinsId.Name, name) + if err != nil { + if !utils.ResponseWasNotFound(existing.Response) { + return fmt.Errorf("checking for present of existing Digital Twins Endpoint %q (Resource Group %q / Instance %q): %+v", name, digitalTwinsId.ResourceGroup, digitalTwinsId.Name, err) + } + } + if !utils.ResponseWasNotFound(existing.Response) { + return tf.ImportAsExistsError("azurerm_digital_twins_endpoint_servicebus", id) + } + } + + properties := digitaltwins.EndpointResource{ + Properties: &digitaltwins.ServiceBus{ + EndpointType: digitaltwins.EndpointTypeServiceBus, + PrimaryConnectionString: utils.String(d.Get("servicebus_primary_connection_string").(string)), + SecondaryConnectionString: utils.String(d.Get("servicebus_secondary_connection_string").(string)), + DeadLetterSecret: utils.String(d.Get("dead_letter_storage_secret").(string)), + }, + } + + future, err := client.CreateOrUpdate(ctx, digitalTwinsId.ResourceGroup, digitalTwinsId.Name, name, properties) + if err != nil { + return fmt.Errorf("creating/updating Digital Twins Endpoint ServiceBus %q (Resource Group %q / Instance %q): %+v", name, digitalTwinsId.ResourceGroup, digitalTwinsId.Name, err) + } + + if err := future.WaitForCompletionRef(ctx, client.Client); err != nil { + return fmt.Errorf("waiting for creation/update of the Digital Twins Endpoint ServiceBus %q (Resource Group %q / Instance %q): %+v", name, digitalTwinsId.ResourceGroup, digitalTwinsId.Name, err) + } + + if _, err := client.Get(ctx, digitalTwinsId.ResourceGroup, digitalTwinsId.Name, name); err != nil { + return fmt.Errorf("retrieving Digital Twins Endpoint ServiceBus %q (Resource Group %q / Instance %q): %+v", name, digitalTwinsId.ResourceGroup, digitalTwinsId.Name, err) + } + + d.SetId(id) + + return resourceDigitalTwinsEndpointServiceBusRead(d, meta) +} + +func resourceDigitalTwinsEndpointServiceBusRead(d *schema.ResourceData, meta interface{}) error { + subscriptionId := meta.(*clients.Client).Account.SubscriptionId + client := meta.(*clients.Client).DigitalTwins.EndpointClient + ctx, cancel := timeouts.ForRead(meta.(*clients.Client).StopContext, d) + defer cancel() + + id, err := parse.DigitalTwinsEndpointID(d.Id()) + if err != nil { + return err + } + + resp, err := client.Get(ctx, id.ResourceGroup, id.DigitalTwinsInstanceName, id.EndpointName) + if err != nil { + if utils.ResponseWasNotFound(resp.Response) { + log.Printf("[INFO] Digital Twins ServiceBus Endpoint %q does not exist - removing from state", d.Id()) + d.SetId("") + return nil + } + return fmt.Errorf("retrieving Digital Twins Endpoint ServiceBus %q (Resource Group %q / Instance %q): %+v", id.EndpointName, id.ResourceGroup, id.DigitalTwinsInstanceName, err) + } + d.Set("name", id.EndpointName) + d.Set("digital_twins_id", parse.NewDigitalTwinsInstanceID(subscriptionId, id.ResourceGroup, id.DigitalTwinsInstanceName).ID("")) + if resp.Properties != nil { + if _, ok := resp.Properties.AsServiceBus(); !ok { + return fmt.Errorf("retrieving Digital Twins Endpoint %q (Resource Group %q / Instance %q) is not type ServiceBus", id.EndpointName, id.ResourceGroup, id.DigitalTwinsInstanceName) + } + } + return nil +} + +func resourceDigitalTwinsEndpointServiceBusDelete(d *schema.ResourceData, meta interface{}) error { + client := meta.(*clients.Client).DigitalTwins.EndpointClient + ctx, cancel := timeouts.ForDelete(meta.(*clients.Client).StopContext, d) + defer cancel() + + id, err := parse.DigitalTwinsEndpointID(d.Id()) + if err != nil { + return err + } + + future, err := client.Delete(ctx, id.ResourceGroup, id.DigitalTwinsInstanceName, id.EndpointName) + if err != nil { + return fmt.Errorf("deleting Digital Twins Endpoint ServiceBus %q (Resource Group %q / Instance %q): %+v", id.EndpointName, id.ResourceGroup, id.DigitalTwinsInstanceName, err) + } + + if err := future.WaitForCompletionRef(ctx, client.Client); err != nil { + return fmt.Errorf("waiting for deletion of the Digital Twins Endpoint ServiceBus %q (Resource Group %q / Instance %q): %+v", id.EndpointName, id.ResourceGroup, id.DigitalTwinsInstanceName, err) + } + return nil +} diff --git a/azurerm/internal/services/digitaltwins/digital_twins_endpoint_servicebus_resource_test.go b/azurerm/internal/services/digitaltwins/digital_twins_endpoint_servicebus_resource_test.go new file mode 100644 index 000000000000..057fa7dfac96 --- /dev/null +++ b/azurerm/internal/services/digitaltwins/digital_twins_endpoint_servicebus_resource_test.go @@ -0,0 +1,308 @@ +package digitaltwins_test + +import ( + "context" + "fmt" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/terraform" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/acceptance" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/acceptance/check" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/clients" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/services/digitaltwins/parse" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" +) + +type DigitalTwinsEndpointServiceBusResource struct{} + +func TestAccDigitalTwinsEndpointServicebus_basic(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_digital_twins_endpoint_servicebus", "test") + r := DigitalTwinsEndpointServiceBusResource{} + + data.ResourceTest(t, r, []resource.TestStep{ + { + Config: r.basic(data), + Check: resource.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep("servicebus_primary_connection_string", "servicebus_secondary_connection_string"), + }) +} + +func TestAccDigitalTwinsEndpointServicebus_requiresImport(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_digital_twins_endpoint_servicebus", "test") + r := DigitalTwinsEndpointServiceBusResource{} + + data.ResourceTest(t, r, []resource.TestStep{ + { + Config: r.basic(data), + Check: resource.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.RequiresImportErrorStep(r.requiresImport), + }) +} + +func TestAccDigitalTwinsEndpointServicebus_updateServiceBus(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_digital_twins_endpoint_servicebus", "test") + r := DigitalTwinsEndpointServiceBusResource{} + + data.ResourceTest(t, r, []resource.TestStep{ + { + Config: r.basic(data), + Check: resource.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep("servicebus_primary_connection_string", "servicebus_secondary_connection_string"), + { + Config: r.updateServiceBus(data), + Check: resource.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep("servicebus_primary_connection_string", "servicebus_secondary_connection_string"), + { + Config: r.updateServiceBusRestore(data), + Check: resource.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep("servicebus_primary_connection_string", "servicebus_secondary_connection_string"), + }) +} + +func TestAccDigitalTwinsEndpointServicebus_updateDeadLetter(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_digital_twins_endpoint_servicebus", "test") + r := DigitalTwinsEndpointServiceBusResource{} + + data.ResourceTest(t, r, []resource.TestStep{ + { + Config: r.basic(data), + Check: resource.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep("servicebus_primary_connection_string", "servicebus_secondary_connection_string"), + { + Config: r.updateDeadLetter(data), + Check: resource.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep("servicebus_primary_connection_string", "servicebus_secondary_connection_string", "dead_letter_storage_secret"), + { + Config: r.updateDeadLetterRestore(data), + Check: resource.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep("servicebus_primary_connection_string", "servicebus_secondary_connection_string", "dead_letter_storage_secret"), + }) +} + +func (r DigitalTwinsEndpointServiceBusResource) Exists(ctx context.Context, client *clients.Client, state *terraform.InstanceState) (*bool, error) { + id, err := parse.DigitalTwinsEndpointID(state.ID) + if err != nil { + return nil, err + } + resp, err := client.DigitalTwins.EndpointClient.Get(ctx, id.ResourceGroup, id.DigitalTwinsInstanceName, id.EndpointName) + if err != nil { + if utils.ResponseWasNotFound(resp.Response) { + return utils.Bool(false), nil + } + return nil, fmt.Errorf("retrieving Digital Twins Service Bus Endpoint %q (Resource Group %q / Digital Twins Instance Name %q): %+v", id.EndpointName, id.ResourceGroup, id.DigitalTwinsInstanceName, err) + } + + return utils.Bool(true), nil +} + +func (r DigitalTwinsEndpointServiceBusResource) template(data acceptance.TestData) string { + iR := DigitalTwinsInstanceResource{} + digitalTwinsInstance := iR.basic(data) + return fmt.Sprintf(` +%[1]s + +resource "azurerm_servicebus_namespace" "test" { + name = "acctestservicebusnamespace-%[2]d" + location = azurerm_resource_group.test.location + resource_group_name = azurerm_resource_group.test.name + sku = "Standard" +} + +resource "azurerm_servicebus_topic" "test" { + name = "acctestservicebustopic-%[2]d" + namespace_name = azurerm_servicebus_namespace.test.name + resource_group_name = azurerm_resource_group.test.name +} + +resource "azurerm_servicebus_topic_authorization_rule" "test" { + name = "acctest-rule-%[2]d" + namespace_name = azurerm_servicebus_namespace.test.name + resource_group_name = azurerm_resource_group.test.name + topic_name = azurerm_servicebus_topic.test.name + + listen = false + send = true + manage = false +} +`, digitalTwinsInstance, data.RandomInteger) +} + +func (r DigitalTwinsEndpointServiceBusResource) basic(data acceptance.TestData) string { + return fmt.Sprintf(` +%s + +resource "azurerm_digital_twins_endpoint_servicebus" "test" { + name = "acctest-EndpointSB-%d" + digital_twins_id = azurerm_digital_twins_instance.test.id + servicebus_primary_connection_string = azurerm_servicebus_topic_authorization_rule.test.primary_connection_string + servicebus_secondary_connection_string = azurerm_servicebus_topic_authorization_rule.test.secondary_connection_string +} +`, r.template(data), data.RandomInteger) +} + +func (r DigitalTwinsEndpointServiceBusResource) requiresImport(data acceptance.TestData) string { + return fmt.Sprintf(` +%s + +resource "azurerm_digital_twins_endpoint_servicebus" "import" { + name = azurerm_digital_twins_endpoint_servicebus.test.name + digital_twins_id = azurerm_digital_twins_endpoint_servicebus.test.digital_twins_id + servicebus_primary_connection_string = azurerm_digital_twins_endpoint_servicebus.test.servicebus_primary_connection_string + servicebus_secondary_connection_string = azurerm_digital_twins_endpoint_servicebus.test.servicebus_secondary_connection_string +} +`, r.basic(data)) +} + +func (r DigitalTwinsEndpointServiceBusResource) updateServiceBus(data acceptance.TestData) string { + return fmt.Sprintf(` +%[1]s + +resource "azurerm_servicebus_namespace" "test_alt" { + name = "acctestservicebusnamespace-alt-%[2]d" + location = azurerm_resource_group.test.location + resource_group_name = azurerm_resource_group.test.name + sku = "basic" +} + +resource "azurerm_servicebus_topic" "test_alt" { + name = "acctestservicebustopic-alt-%[2]d" + namespace_name = azurerm_servicebus_namespace.test.name + resource_group_name = azurerm_resource_group.test.name +} + +resource "azurerm_servicebus_topic_authorization_rule" "test_alt" { + name = "acctest-rule-alt-%[2]d" + namespace_name = azurerm_servicebus_namespace.test.name + resource_group_name = azurerm_resource_group.test.name + topic_name = azurerm_servicebus_topic.test.name + + listen = false + send = true + manage = false +} + +resource "azurerm_digital_twins_endpoint_servicebus" "test" { + name = "acctest-EndpointSB-%[2]d" + digital_twins_id = azurerm_digital_twins_instance.test.id + servicebus_primary_connection_string = azurerm_servicebus_topic_authorization_rule.test_alt.primary_connection_string + servicebus_secondary_connection_string = azurerm_servicebus_topic_authorization_rule.test_alt.secondary_connection_string +} +`, r.template(data), data.RandomInteger) +} + +func (r DigitalTwinsEndpointServiceBusResource) updateServiceBusRestore(data acceptance.TestData) string { + return fmt.Sprintf(` +%[1]s + +resource "azurerm_servicebus_namespace" "test_alt" { + name = "acctestservicebusnamespace-alt-%[2]d" + location = azurerm_resource_group.test.location + resource_group_name = azurerm_resource_group.test.name + sku = "basic" +} + +resource "azurerm_servicebus_topic" "test_alt" { + name = "acctestservicebustopic-alt-%[2]d" + namespace_name = azurerm_servicebus_namespace.test.name + resource_group_name = azurerm_resource_group.test.name +} + +resource "azurerm_servicebus_topic_authorization_rule" "test_alt" { + name = "acctest-rule-alt-%[2]d" + namespace_name = azurerm_servicebus_namespace.test.name + resource_group_name = azurerm_resource_group.test.name + topic_name = azurerm_servicebus_topic.test.name + + listen = false + send = true + manage = false +} + +resource "azurerm_digital_twins_endpoint_servicebus" "test" { + name = "acctest-EndpointSB-%[2]d" + digital_twins_id = azurerm_digital_twins_instance.test.id + servicebus_primary_connection_string = azurerm_servicebus_topic_authorization_rule.test.primary_connection_string + servicebus_secondary_connection_string = azurerm_servicebus_topic_authorization_rule.test.secondary_connection_string +} +`, r.template(data), data.RandomInteger) +} + +func (r DigitalTwinsEndpointServiceBusResource) updateDeadLetter(data acceptance.TestData) string { + return fmt.Sprintf(` +%[1]s + +resource "azurerm_storage_account" "test" { + name = "acctestacc%[2]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 = "vhds" + storage_account_name = azurerm_storage_account.test.name + container_access_type = "private" +} + +resource "azurerm_digital_twins_endpoint_servicebus" "test" { + name = "acctest-EndpointSB-%[3]d" + digital_twins_id = azurerm_digital_twins_instance.test.id + servicebus_primary_connection_string = azurerm_servicebus_topic_authorization_rule.test.primary_connection_string + servicebus_secondary_connection_string = azurerm_servicebus_topic_authorization_rule.test.secondary_connection_string + dead_letter_storage_secret = "${azurerm_storage_container.test.id}?${azurerm_storage_account.test.primary_access_key}" +} +`, r.template(data), data.RandomString, data.RandomInteger) +} + +func (r DigitalTwinsEndpointServiceBusResource) updateDeadLetterRestore(data acceptance.TestData) string { + return fmt.Sprintf(` +%[1]s + +resource "azurerm_storage_account" "test" { + name = "acctestacc%[2]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 = "vhds" + storage_account_name = azurerm_storage_account.test.name + container_access_type = "private" +} + +resource "azurerm_digital_twins_endpoint_servicebus" "test" { + name = "acctest-EndpointSB-%[3]d" + digital_twins_id = azurerm_digital_twins_instance.test.id + servicebus_primary_connection_string = azurerm_servicebus_topic_authorization_rule.test.primary_connection_string + servicebus_secondary_connection_string = azurerm_servicebus_topic_authorization_rule.test.secondary_connection_string +} +`, r.template(data), data.RandomString, data.RandomInteger) +} diff --git a/azurerm/internal/services/digitaltwins/registration.go b/azurerm/internal/services/digitaltwins/registration.go index 9a7a2351b77a..e0c9bf61387c 100644 --- a/azurerm/internal/services/digitaltwins/registration.go +++ b/azurerm/internal/services/digitaltwins/registration.go @@ -28,8 +28,9 @@ func (r Registration) SupportedDataSources() map[string]*schema.Resource { // SupportedResources returns the supported Resources supported by this Service func (r Registration) SupportedResources() map[string]*schema.Resource { return map[string]*schema.Resource{ - "azurerm_digital_twins_instance": resourceDigitalTwinsInstance(), - "azurerm_digital_twins_endpoint_eventgrid": resourceDigitalTwinsEndpointEventGrid(), - "azurerm_digital_twins_endpoint_eventhub": resourceDigitalTwinsEndpointEventHub(), + "azurerm_digital_twins_instance": resourceDigitalTwinsInstance(), + "azurerm_digital_twins_endpoint_eventgrid": resourceDigitalTwinsEndpointEventGrid(), + "azurerm_digital_twins_endpoint_eventhub": resourceDigitalTwinsEndpointEventHub(), + "azurerm_digital_twins_endpoint_servicebus": resourceDigitalTwinsEndpointServiceBus(), } } diff --git a/website/azurerm.erb b/website/azurerm.erb index 642b9d5c6d60..f5af900e393c 100644 --- a/website/azurerm.erb +++ b/website/azurerm.erb @@ -1836,6 +1836,9 @@