From 440c0141e29750ca487389d0f337db2282ff086a Mon Sep 17 00:00:00 2001 From: Xiaxin <92154856+xiaxyi@users.noreply.github.com> Date: Tue, 3 May 2022 05:06:23 +0800 Subject: [PATCH] `Dependency` New Resource/Data Source azurerm_healthcare_dicom_service (#15887) Co-authored-by: kt adding new resource: healthcare dicom service. Dependency of #15759 DICOM (Digital Imaging and Communications in Medicine) is the international standard to transmit, store, retrieve, print, process, and display medical imaging information, and is the primary medical imaging standard accepted across healthcare. The DICOM service is a managed service within Azure Health Data Services that ingests and persists DICOM objects at multiple thousands of images per second. --- internal/services/healthcare/client/client.go | 13 +- .../healthcare_dicom_data_source.go | 135 ++++++ .../healthcare_dicom_data_source_test.go | 35 ++ .../healthcare/healthcare_dicom_resource.go | 426 ++++++++++++++++++ .../healthcare_dicom_resource_test.go | 221 +++++++++ .../healthcare/parse/dicom_service.go | 75 +++ .../healthcare/parse/dicom_service_test.go | 128 ++++++ internal/services/healthcare/registration.go | 10 +- internal/services/healthcare/resourceids.go | 1 + .../healthcare/validate/dicom_name.go | 16 + .../healthcare/validate/dicom_service_id.go | 23 + .../validate/dicom_service_id_test.go | 88 ++++ website/docs/d/healthcare_dicom.html.markdown | 58 +++ website/docs/r/healthcare_dicom.html.markdown | 91 ++++ 14 files changed, 1312 insertions(+), 8 deletions(-) create mode 100644 internal/services/healthcare/healthcare_dicom_data_source.go create mode 100644 internal/services/healthcare/healthcare_dicom_data_source_test.go create mode 100644 internal/services/healthcare/healthcare_dicom_resource.go create mode 100644 internal/services/healthcare/healthcare_dicom_resource_test.go create mode 100644 internal/services/healthcare/parse/dicom_service.go create mode 100644 internal/services/healthcare/parse/dicom_service_test.go create mode 100644 internal/services/healthcare/validate/dicom_name.go create mode 100644 internal/services/healthcare/validate/dicom_service_id.go create mode 100644 internal/services/healthcare/validate/dicom_service_id_test.go create mode 100644 website/docs/d/healthcare_dicom.html.markdown create mode 100644 website/docs/r/healthcare_dicom.html.markdown diff --git a/internal/services/healthcare/client/client.go b/internal/services/healthcare/client/client.go index 6b6e37e26e4a..07984b15646f 100644 --- a/internal/services/healthcare/client/client.go +++ b/internal/services/healthcare/client/client.go @@ -6,8 +6,9 @@ import ( ) type Client struct { - HealthcareServiceClient *healthcareapis.ServicesClient - HealthcareWorkspaceClient *healthcareapis.WorkspacesClient + HealthcareServiceClient *healthcareapis.ServicesClient + HealthcareWorkspaceClient *healthcareapis.WorkspacesClient + HealthcareWorkspaceDicomServiceClient *healthcareapis.DicomServicesClient } func NewClient(o *common.ClientOptions) *Client { @@ -17,8 +18,12 @@ func NewClient(o *common.ClientOptions) *Client { HealthcareWorkspaceClient := healthcareapis.NewWorkspacesClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) o.ConfigureClient(&HealthcareWorkspaceClient.Client, o.ResourceManagerAuthorizer) + HealthcareWorkspaceDicomServiceClient := healthcareapis.NewDicomServicesClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) + o.ConfigureClient(&HealthcareWorkspaceDicomServiceClient.Client, o.ResourceManagerAuthorizer) + return &Client{ - HealthcareServiceClient: &HealthcareServiceClient, - HealthcareWorkspaceClient: &HealthcareWorkspaceClient, + HealthcareServiceClient: &HealthcareServiceClient, + HealthcareWorkspaceClient: &HealthcareWorkspaceClient, + HealthcareWorkspaceDicomServiceClient: &HealthcareWorkspaceDicomServiceClient, } } diff --git a/internal/services/healthcare/healthcare_dicom_data_source.go b/internal/services/healthcare/healthcare_dicom_data_source.go new file mode 100644 index 000000000000..2d082935892f --- /dev/null +++ b/internal/services/healthcare/healthcare_dicom_data_source.go @@ -0,0 +1,135 @@ +package healthcare + +import ( + "fmt" + "time" + + "github.com/hashicorp/go-azure-helpers/resourcemanager/commonschema" + "github.com/hashicorp/go-azure-helpers/resourcemanager/location" + "github.com/hashicorp/terraform-provider-azurerm/internal/clients" + "github.com/hashicorp/terraform-provider-azurerm/internal/services/healthcare/parse" + "github.com/hashicorp/terraform-provider-azurerm/internal/services/healthcare/validate" + "github.com/hashicorp/terraform-provider-azurerm/internal/tags" + "github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk" + "github.com/hashicorp/terraform-provider-azurerm/internal/timeouts" + "github.com/hashicorp/terraform-provider-azurerm/utils" +) + +func dataSourceHealthcareDicomService() *pluginsdk.Resource { + return &pluginsdk.Resource{ + Read: dataSourceHealthcareApisDicomServiceRead, + + Timeouts: &pluginsdk.ResourceTimeout{ + Read: pluginsdk.DefaultTimeout(5 * time.Minute), + }, + + Schema: map[string]*pluginsdk.Schema{ + "name": { + Type: pluginsdk.TypeString, + Required: true, + ValidateFunc: validate.DicomServiceName(), + }, + + "workspace_id": { + Type: pluginsdk.TypeString, + Required: true, + ValidateFunc: validate.WorkspaceID, + }, + + "location": commonschema.LocationComputed(), + + "identity": commonschema.SystemAssignedUserAssignedIdentityComputed(), + + "authentication": { + Type: pluginsdk.TypeList, + Computed: true, + Elem: &pluginsdk.Resource{ + Schema: map[string]*pluginsdk.Schema{ + "authority": { + Type: pluginsdk.TypeString, + Computed: true, + }, + "audience": { + Type: pluginsdk.TypeList, + Computed: true, + Elem: &pluginsdk.Schema{Type: pluginsdk.TypeString}, + }, + }, + }, + }, + + "private_endpoint": { + Type: pluginsdk.TypeList, + Computed: true, + Elem: &pluginsdk.Resource{ + Schema: map[string]*pluginsdk.Schema{ + "name": { + Type: pluginsdk.TypeString, + Computed: true, + }, + + "id": { + Type: pluginsdk.TypeString, + Computed: true, + }, + }, + }, + }, + + "service_url": { + Type: pluginsdk.TypeString, + Computed: true, + }, + + "tags": tags.SchemaDataSource(), + }, + } +} + +func dataSourceHealthcareApisDicomServiceRead(d *pluginsdk.ResourceData, meta interface{}) error { + client := meta.(*clients.Client).HealthCare.HealthcareWorkspaceDicomServiceClient + subscriptionId := meta.(*clients.Client).Account.SubscriptionId + ctx, cancel := timeouts.ForRead(meta.(*clients.Client).StopContext, d) + defer cancel() + + workspaceId, err := parse.WorkspaceID(d.Get("workspace_id").(string)) + if err != nil { + return fmt.Errorf("parsing workspace id error: %+v", err) + } + + id := parse.NewDicomServiceID(subscriptionId, workspaceId.ResourceGroup, workspaceId.Name, d.Get("name").(string)) + resp, err := client.Get(ctx, id.ResourceGroup, id.WorkspaceName, id.Name) + if err != nil { + if utils.ResponseWasNotFound(resp.Response) { + d.SetId("") + return nil + } + return fmt.Errorf("retrieving %s: %+v", id, err) + } + + d.SetId(id.ID()) + + d.Set("name", id.Name) + d.Set("workspace_id", workspaceId.ID()) + + if resp.Location != nil { + d.Set("location", location.Normalize(*resp.Location)) + } + + if props := resp.DicomServiceProperties; props != nil { + d.Set("authentication", flattenDicomAuthentication(props.AuthenticationConfiguration)) + d.Set("private_endpoint", flattenDicomServicePrivateEndpoint(props.PrivateEndpointConnections)) + d.Set("service_url", props.ServiceURL) + } + + identity, _ := flattenDicomManagedIdentity(resp.Identity) + if err := d.Set("identity", identity); err != nil { + return fmt.Errorf("setting `identity`: %+v", err) + } + + if err := tags.FlattenAndSet(d, resp.Tags); err != nil { + return err + } + + return nil +} diff --git a/internal/services/healthcare/healthcare_dicom_data_source_test.go b/internal/services/healthcare/healthcare_dicom_data_source_test.go new file mode 100644 index 000000000000..1e1fdbbf78ab --- /dev/null +++ b/internal/services/healthcare/healthcare_dicom_data_source_test.go @@ -0,0 +1,35 @@ +package healthcare_test + +import ( + "fmt" + "testing" + + "github.com/hashicorp/terraform-provider-azurerm/internal/acceptance" + "github.com/hashicorp/terraform-provider-azurerm/internal/acceptance/check" +) + +type HealthCareDicomServiceDataSource struct{} + +func TestAccHealthCareDicomDataSource_basic(t *testing.T) { + data := acceptance.BuildTestData(t, "data.azurerm_healthcare_dicom_service", "test") + r := HealthCareDicomServiceDataSource{} + + data.DataSourceTest(t, []acceptance.TestStep{ + { + Config: r.basic(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).Key("name").Exists()), + }, + }) +} + +func (r HealthCareDicomServiceDataSource) basic(data acceptance.TestData) string { + return fmt.Sprintf(` +%s + +data "azurerm_healthcare_dicom_service" "test" { + name = azurerm_healthcare_dicom_service.test.name + workspace_id = azurerm_healthcare_dicom_service.test.workspace_id +} +`, HealthCareDicomResource{}.basic(data)) +} diff --git a/internal/services/healthcare/healthcare_dicom_resource.go b/internal/services/healthcare/healthcare_dicom_resource.go new file mode 100644 index 000000000000..0b609725a8c1 --- /dev/null +++ b/internal/services/healthcare/healthcare_dicom_resource.go @@ -0,0 +1,426 @@ +package healthcare + +import ( + "context" + "fmt" + "log" + "time" + + "github.com/Azure/azure-sdk-for-go/services/healthcareapis/mgmt/2021-11-01/healthcareapis" + "github.com/hashicorp/go-azure-helpers/lang/response" + "github.com/hashicorp/go-azure-helpers/resourcemanager/commonschema" + "github.com/hashicorp/go-azure-helpers/resourcemanager/identity" + "github.com/hashicorp/go-azure-helpers/resourcemanager/location" + "github.com/hashicorp/terraform-provider-azurerm/helpers/tf" + "github.com/hashicorp/terraform-provider-azurerm/internal/clients" + "github.com/hashicorp/terraform-provider-azurerm/internal/services/healthcare/parse" + "github.com/hashicorp/terraform-provider-azurerm/internal/services/healthcare/validate" + "github.com/hashicorp/terraform-provider-azurerm/internal/tags" + "github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk" + "github.com/hashicorp/terraform-provider-azurerm/internal/timeouts" + "github.com/hashicorp/terraform-provider-azurerm/utils" +) + +func resourceHealthcareApisDicomService() *pluginsdk.Resource { + return &pluginsdk.Resource{ + Create: resourceHealthcareApisDicomServiceCreate, + Read: resourceHealthcareApisDicomServiceRead, + Update: resourceHealthcareApisDicomServiceUpdate, + Delete: resourceHealthcareApisDicomServiceDelete, + + 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), + }, + + Importer: pluginsdk.ImporterValidatingResourceId(func(id string) error { + _, err := parse.DicomServiceID(id) + return err + }), + + Schema: map[string]*pluginsdk.Schema{ + "name": { + Type: pluginsdk.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validate.DicomServiceName(), + }, + + "workspace_id": { + Type: pluginsdk.TypeString, + Required: true, + ForceNew: true, + ValidateFunc: validate.WorkspaceID, + }, + + "location": commonschema.Location(), + + "identity": commonschema.SystemAssignedUserAssignedIdentityOptional(), + + "authentication": { + Type: pluginsdk.TypeList, + Computed: true, + Elem: &pluginsdk.Resource{ + Schema: map[string]*pluginsdk.Schema{ + "authority": { + Type: pluginsdk.TypeString, + Computed: true, + }, + "audience": { + Type: pluginsdk.TypeList, + Computed: true, + Elem: &pluginsdk.Schema{Type: pluginsdk.TypeString}, + }, + }, + }, + }, + + "private_endpoint": { + Type: pluginsdk.TypeSet, + Computed: true, + Elem: &pluginsdk.Resource{ + Schema: map[string]*pluginsdk.Schema{ + "name": { + Type: pluginsdk.TypeString, + Computed: true, + }, + + "id": { + Type: pluginsdk.TypeString, + Computed: true, + }, + }, + }, + }, + + "service_url": { + Type: pluginsdk.TypeString, + Computed: true, + }, + + "public_network_access_enabled": { + Type: pluginsdk.TypeBool, + Optional: true, + Default: true, + }, + + "tags": commonschema.Tags(), + }, + } +} + +func resourceHealthcareApisDicomServiceCreate(d *pluginsdk.ResourceData, meta interface{}) error { + client := meta.(*clients.Client).HealthCare.HealthcareWorkspaceDicomServiceClient + ctx, cancel := timeouts.ForCreate(meta.(*clients.Client).StopContext, d) + defer cancel() + log.Printf("[INFO] preparing arguments for AzureRM Healthcare Dicom Service creation.") + + workspace, err := parse.WorkspaceID(d.Get("workspace_id").(string)) + if err != nil { + return fmt.Errorf("parsing healthcare workspace error: %+v", err) + } + + dicomServiceId := parse.NewDicomServiceID(workspace.SubscriptionId, workspace.ResourceGroup, workspace.Name, d.Get("name").(string)) + + if d.IsNewResource() { + existing, err := client.Get(ctx, dicomServiceId.ResourceGroup, dicomServiceId.WorkspaceName, dicomServiceId.Name) + if err != nil { + if !utils.ResponseWasNotFound(existing.Response) { + return fmt.Errorf("checking for presence of existing %s: %+v", dicomServiceId, err) + } + } + + if !utils.ResponseWasNotFound(existing.Response) { + return tf.ImportAsExistsError("azurerm_healthcare_dicom_service", dicomServiceId.ID()) + } + } + + identity, err := expandDicomManagedIdentity(d.Get("identity").([]interface{})) + if err != nil { + return fmt.Errorf("expanding `identity`: %+v", err) + } + + t := d.Get("tags").(map[string]interface{}) + + parameters := healthcareapis.DicomService{ + Identity: identity, + DicomServiceProperties: &healthcareapis.DicomServiceProperties{ + PublicNetworkAccess: healthcareapis.PublicNetworkAccessEnabled, + }, + Location: utils.String(location.Normalize(d.Get("location").(string))), + Tags: tags.Expand(t), + } + if enabled := d.Get("public_network_access_enabled").(bool); !enabled { + parameters.DicomServiceProperties.PublicNetworkAccess = healthcareapis.PublicNetworkAccessDisabled + } + + future, err := client.CreateOrUpdate(ctx, dicomServiceId.ResourceGroup, dicomServiceId.WorkspaceName, dicomServiceId.Name, parameters) + if err != nil { + return fmt.Errorf("creating/updating %s: %+v", dicomServiceId, err) + } + + if err = future.WaitForCompletionRef(ctx, client.Client); err != nil { + return fmt.Errorf("waiting for creation %s: %+v", dicomServiceId, err) + } + + d.SetId(dicomServiceId.ID()) + + return resourceHealthcareApisDicomServiceRead(d, meta) +} + +func resourceHealthcareApisDicomServiceRead(d *pluginsdk.ResourceData, meta interface{}) error { + client := meta.(*clients.Client).HealthCare.HealthcareWorkspaceDicomServiceClient + ctx, cancel := timeouts.ForRead(meta.(*clients.Client).StopContext, d) + defer cancel() + + id, err := parse.DicomServiceID(d.Id()) + if err != nil { + return fmt.Errorf("parsing Dicom service error: %+v", err) + } + + resp, err := client.Get(ctx, id.ResourceGroup, id.WorkspaceName, id.Name) + if err != nil { + if utils.ResponseWasNotFound(resp.Response) { + d.SetId("") + return nil + } + return fmt.Errorf("retrieving %s: %+v", *id, err) + } + + d.Set("name", id.Name) + workspaceId := parse.NewWorkspaceID(id.SubscriptionId, id.ResourceGroup, id.WorkspaceName) + d.Set("workspace_id", workspaceId.ID()) + + if resp.Location != nil { + d.Set("location", location.Normalize(*resp.Location)) + } + + if props := resp.DicomServiceProperties; props != nil { + d.Set("authentication", flattenDicomAuthentication(props.AuthenticationConfiguration)) + d.Set("private_endpoint", flattenDicomServicePrivateEndpoint(props.PrivateEndpointConnections)) + d.Set("service_url", props.ServiceURL) + + if props.PublicNetworkAccess != "" { + d.Set("public_network_access_enabled", props.PublicNetworkAccess == healthcareapis.PublicNetworkAccessEnabled) + } + } + + identity, _ := flattenDicomManagedIdentity(resp.Identity) + if err := d.Set("identity", identity); err != nil { + return fmt.Errorf("setting `identity`: %+v", err) + } + + if err := tags.FlattenAndSet(d, resp.Tags); err != nil { + return err + } + return nil +} + +func resourceHealthcareApisDicomServiceUpdate(d *pluginsdk.ResourceData, meta interface{}) error { + client := meta.(*clients.Client).HealthCare.HealthcareWorkspaceDicomServiceClient + ctx, cancel := timeouts.ForCreateUpdate(meta.(*clients.Client).StopContext, d) + defer cancel() + + id, err := parse.DicomServiceID(d.Id()) + if err != nil { + return err + } + + expandedIdentity, err := expandDicomManagedIdentity(d.Get("identity").([]interface{})) + if err != nil { + return fmt.Errorf("expanding `identity`: %+v", err) + } + + parameters := healthcareapis.DicomService{ + Location: utils.String(location.Normalize(d.Get("location").(string))), + DicomServiceProperties: &healthcareapis.DicomServiceProperties{ + PublicNetworkAccess: healthcareapis.PublicNetworkAccessEnabled, + }, + Identity: expandedIdentity, + } + + if enabled := d.Get("public_network_access_enabled").(bool); !enabled { + parameters.DicomServiceProperties.PublicNetworkAccess = healthcareapis.PublicNetworkAccessDisabled + } + + if d.HasChange("tags") { + if err := updateTags(d, meta); err != nil { + return fmt.Errorf("updating tags error: %+v", err) + } + } + + future, err := client.CreateOrUpdate(ctx, id.ResourceGroup, id.WorkspaceName, id.Name, parameters) + if err != nil { + return fmt.Errorf("updating %s: %+v", id, err) + } + + if err = future.WaitForCompletionRef(ctx, client.Client); err != nil { + return fmt.Errorf("waiting for update %s: %+v", id, err) + } + + d.SetId(id.ID()) + return resourceHealthcareApisDicomServiceRead(d, meta) +} + +func resourceHealthcareApisDicomServiceDelete(d *pluginsdk.ResourceData, meta interface{}) error { + client := meta.(*clients.Client).HealthCare.HealthcareWorkspaceDicomServiceClient + ctx, cancel := timeouts.ForDelete(meta.(*clients.Client).StopContext, d) + defer cancel() + + id, err := parse.DicomServiceID(d.Id()) + if err != nil { + return fmt.Errorf("parsing Dicom service error: %+v", err) + } + future, err := client.Delete(ctx, id.ResourceGroup, id.Name, id.WorkspaceName) + if err != nil { + if !response.WasNotFound(future.Response()) { + return fmt.Errorf("deleting %s: %+v", *id, err) + } + } + + log.Printf("[DEBUG] Waiting for %s to be deleted..", id) + stateConf := &pluginsdk.StateChangeConf{ + Pending: []string{"Pending"}, + Target: []string{"Deleted"}, + Refresh: dicomServiceStateStatusCodeRefreshFunc(ctx, client, *id), + Timeout: d.Timeout(pluginsdk.TimeoutDelete), + ContinuousTargetOccurence: 3, + PollInterval: 10 * time.Second, + } + + if _, err := stateConf.WaitForStateContext(ctx); err != nil { + return fmt.Errorf("waiting for %s to be deleted: %+v", id, err) + } + return nil +} + +func dicomServiceStateStatusCodeRefreshFunc(ctx context.Context, client *healthcareapis.DicomServicesClient, id parse.DicomServiceId) pluginsdk.StateRefreshFunc { + return func() (interface{}, string, error) { + res, err := client.Get(ctx, id.ResourceGroup, id.WorkspaceName, id.Name) + + if err != nil { + if utils.ResponseWasNotFound(res.Response) { + return res, "Deleted", nil + } + return nil, "Error", fmt.Errorf("polling for the status of %s: %+v", id, err) + } + + return res, "Pending", nil + } +} + +func updateTags(d *pluginsdk.ResourceData, meta interface{}) error { + client := meta.(*clients.Client).HealthCare.HealthcareWorkspaceDicomServiceClient + ctx, cancel := timeouts.ForUpdate(meta.(*clients.Client).StopContext, d) + defer cancel() + + id, err := parse.DicomServiceID(d.Id()) + if err != nil { + return err + } + + update := healthcareapis.DicomServicePatchResource{ + Tags: tags.Expand(d.Get("tags").(map[string]interface{})), + } + + future, err := client.Update(ctx, id.ResourceGroup, id.Name, id.WorkspaceName, update) + if err != nil { + return fmt.Errorf("updating %s: %+v", id, err) + } + + if err = future.WaitForCompletionRef(ctx, client.Client); err != nil { + return fmt.Errorf("waiting for update %s: %+v", id, err) + } + + return nil +} + +func flattenDicomAuthentication(input *healthcareapis.DicomServiceAuthenticationConfiguration) []interface{} { + if input == nil { + return []interface{}{} + } + + authBlock := make(map[string]interface{}) + if input.Authority != nil { + authBlock["authority"] = *input.Authority + } + + audience := make([]interface{}, 0) + if input.Audiences != nil { + for _, data := range *input.Audiences { + audience = append(audience, data) + } + } + authBlock["audience"] = audience + + return []interface{}{authBlock} +} + +func flattenDicomServicePrivateEndpoint(input *[]healthcareapis.PrivateEndpointConnection) []interface{} { + results := make([]interface{}, 0) + if input == nil { + return results + } + + for _, endpoint := range *input { + result := map[string]interface{}{} + if endpoint.Name != nil { + result["name"] = *endpoint.Name + } + + if endpoint.ID != nil { + result["id"] = *endpoint.ID + } + } + return results +} + +func expandDicomManagedIdentity(input []interface{}) (*healthcareapis.ServiceManagedIdentityIdentity, error) { + expanded, err := identity.ExpandSystemAndUserAssignedMap(input) + if err != nil { + return nil, err + } + + out := healthcareapis.ServiceManagedIdentityIdentity{ + Type: healthcareapis.ServiceManagedIdentityType(string(expanded.Type)), + } + + if expanded.Type == identity.TypeUserAssigned || expanded.Type == identity.TypeSystemAssignedUserAssigned { + out.UserAssignedIdentities = make(map[string]*healthcareapis.UserAssignedIdentity) + for k := range expanded.IdentityIds { + out.UserAssignedIdentities[k] = &healthcareapis.UserAssignedIdentity{ + // intentionally empty + } + } + } + return &out, nil +} + +func flattenDicomManagedIdentity(input *healthcareapis.ServiceManagedIdentityIdentity) (*[]interface{}, error) { + var config *identity.SystemAndUserAssignedList + + if input != nil { + config = &identity.SystemAndUserAssignedList{ + Type: identity.Type(string(input.Type)), + } + + if input.PrincipalID != nil { + id := *input.PrincipalID + config.PrincipalId = id.String() + } + if input.TenantID != nil { + tenantID := *input.TenantID + config.TenantId = tenantID.String() + } + + userAssignedIdentityIds := make([]string, 0) + for k := range input.UserAssignedIdentities { + userAssignedIdentityIds = append(userAssignedIdentityIds, k) + } + config.IdentityIds = userAssignedIdentityIds + } + + return identity.FlattenSystemAndUserAssignedList(config) +} diff --git a/internal/services/healthcare/healthcare_dicom_resource_test.go b/internal/services/healthcare/healthcare_dicom_resource_test.go new file mode 100644 index 000000000000..2c7b58bef31f --- /dev/null +++ b/internal/services/healthcare/healthcare_dicom_resource_test.go @@ -0,0 +1,221 @@ +package healthcare_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/healthcare/parse" + "github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk" + "github.com/hashicorp/terraform-provider-azurerm/utils" +) + +type HealthCareDicomResource struct{} + +func TestAccHealthCareDicomResource_basic(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_healthcare_dicom_service", "test") + r := HealthCareDicomResource{} + + data.ResourceTest(t, r, []acceptance.TestStep{ + { + Config: r.basic(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r)), + }, + data.ImportStep(), + }) +} + +func TestAccHealthCareDicomResource_complete(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_healthcare_dicom_service", "test") + r := HealthCareDicomResource{} + + data.ResourceTest(t, r, []acceptance.TestStep{ + { + Config: r.complete(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r)), + }, + data.ImportStep(), + }) +} + +func TestAccHealthCareDicomResource_update(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_healthcare_dicom_service", "test") + r := HealthCareDicomResource{} + + 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 TestAccHealthCareDicomResource_updateUserAssignedIdentity(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_healthcare_dicom_service", "test") + r := HealthCareDicomResource{} + + data.ResourceTest(t, r, []acceptance.TestStep{ + { + Config: r.complete(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r)), + }, + data.ImportStep(), + { + Config: r.userAssignedIdentity(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r)), + }, + data.ImportStep(), + }) +} + +func TestAccHealthCareDicomResource_requiresImport(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_healthcare_dicom_service", "test") + r := HealthCareDicomResource{} + + data.ResourceTest(t, r, []acceptance.TestStep{ + { + Config: r.basic(data), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r)), + }, + data.RequiresImportErrorStep(r.requiresImport), + }) +} + +func (HealthCareDicomResource) Exists(ctx context.Context, clients *clients.Client, state *pluginsdk.InstanceState) (*bool, error) { + id, err := parse.DicomServiceID(state.ID) + if err != nil { + return nil, err + } + + resp, err := clients.HealthCare.HealthcareWorkspaceDicomServiceClient.Get(ctx, id.ResourceGroup, id.WorkspaceName, id.Name) + if err != nil { + return nil, fmt.Errorf("retrieving %s, %+v", *id, err) + } + + return utils.Bool(resp.DicomServiceProperties != nil), nil +} + +func (r HealthCareDicomResource) basic(data acceptance.TestData) string { + return fmt.Sprintf(` +%s + +resource "azurerm_healthcare_dicom_service" "test" { + name = "dicom%d" + workspace_id = azurerm_healthcare_workspace.test.id + location = "%s" + depends_on = [azurerm_healthcare_workspace.test] +} +`, r.template(data), data.RandomIntOfLength(10), data.Locations.Primary) +} + +func (r HealthCareDicomResource) complete(data acceptance.TestData) string { + return fmt.Sprintf(` +%s + +resource "azurerm_healthcare_dicom_service" "test" { + name = "dicom%d" + workspace_id = azurerm_healthcare_workspace.test.id + location = "%s" + + identity { + type = "SystemAssigned" + } + + tags = { + environment = "None" + } + depends_on = [azurerm_healthcare_workspace.test] +} +`, r.template(data), data.RandomIntOfLength(10), data.Locations.Primary) +} + +func (r HealthCareDicomResource) update(data acceptance.TestData) string { + return fmt.Sprintf(` +%s + +resource "azurerm_healthcare_dicom_service" "test" { + name = "dicom%d" + workspace_id = azurerm_healthcare_workspace.test.id + location = "%s" + + tags = { + environment = "Prod" + } + depends_on = [azurerm_healthcare_workspace.test] +} +`, r.template(data), data.RandomIntOfLength(10), data.Locations.Primary) +} + +func (r HealthCareDicomResource) userAssignedIdentity(data acceptance.TestData) string { + return fmt.Sprintf(` +%s + +resource "azurerm_user_assigned_identity" "test" { + name = "acctest-uai-%d" + resource_group_name = azurerm_resource_group.test.name + location = azurerm_resource_group.test.location +} + +resource "azurerm_healthcare_dicom_service" "test" { + name = "dicom%d" + workspace_id = azurerm_healthcare_workspace.test.id + location = "%s" + + identity { + type = "UserAssigned" + identity_ids = [azurerm_user_assigned_identity.test.id] + } + + tags = { + environment = "None" + } + depends_on = [azurerm_healthcare_workspace.test, azurerm_user_assigned_identity.test] +} +`, r.template(data), data.RandomInteger, data.RandomIntOfLength(10), data.Locations.Primary) +} + +func (r HealthCareDicomResource) requiresImport(data acceptance.TestData) string { + return fmt.Sprintf(` +%s +resource "azurerm_healthcare_dicom_service" "import" { + name = azurerm_healthcare_dicom_service.test.name + workspace_id = azurerm_healthcare_dicom_service.test.workspace_id + location = azurerm_healthcare_dicom_service.test.location +} +`, r.basic(data)) +} + +func (HealthCareDicomResource) template(data acceptance.TestData) string { + return fmt.Sprintf(` +provider "azurerm" { + features {} +} + +resource "azurerm_resource_group" "test" { + name = "acctestRG-dicom-%d" + location = "%s" +} + +resource "azurerm_healthcare_workspace" "test" { + name = "wk%d" + resource_group_name = azurerm_resource_group.test.name + location = azurerm_resource_group.test.location +} +`, data.RandomInteger, data.Locations.Primary, data.RandomIntOfLength(10)) +} diff --git a/internal/services/healthcare/parse/dicom_service.go b/internal/services/healthcare/parse/dicom_service.go new file mode 100644 index 000000000000..6309633f0cc0 --- /dev/null +++ b/internal/services/healthcare/parse/dicom_service.go @@ -0,0 +1,75 @@ +package parse + +// NOTE: this file is generated via 'go:generate' - manual changes will be overwritten + +import ( + "fmt" + "strings" + + "github.com/hashicorp/go-azure-helpers/resourcemanager/resourceids" +) + +type DicomServiceId struct { + SubscriptionId string + ResourceGroup string + WorkspaceName string + Name string +} + +func NewDicomServiceID(subscriptionId, resourceGroup, workspaceName, name string) DicomServiceId { + return DicomServiceId{ + SubscriptionId: subscriptionId, + ResourceGroup: resourceGroup, + WorkspaceName: workspaceName, + Name: name, + } +} + +func (id DicomServiceId) String() string { + segments := []string{ + fmt.Sprintf("Name %q", id.Name), + fmt.Sprintf("Workspace Name %q", id.WorkspaceName), + fmt.Sprintf("Resource Group %q", id.ResourceGroup), + } + segmentsStr := strings.Join(segments, " / ") + return fmt.Sprintf("%s: (%s)", "Dicom Service", segmentsStr) +} + +func (id DicomServiceId) ID() string { + fmtString := "/subscriptions/%s/resourceGroups/%s/providers/Microsoft.HealthcareApis/workspaces/%s/dicomservices/%s" + return fmt.Sprintf(fmtString, id.SubscriptionId, id.ResourceGroup, id.WorkspaceName, id.Name) +} + +// DicomServiceID parses a DicomService ID into an DicomServiceId struct +func DicomServiceID(input string) (*DicomServiceId, error) { + id, err := resourceids.ParseAzureResourceID(input) + if err != nil { + return nil, err + } + + resourceId := DicomServiceId{ + SubscriptionId: id.SubscriptionID, + ResourceGroup: id.ResourceGroup, + } + + if resourceId.SubscriptionId == "" { + return nil, fmt.Errorf("ID was missing the 'subscriptions' element") + } + + if resourceId.ResourceGroup == "" { + return nil, fmt.Errorf("ID was missing the 'resourceGroups' element") + } + + if resourceId.WorkspaceName, err = id.PopSegment("workspaces"); err != nil { + return nil, err + } + if resourceId.Name, err = id.PopSegment("dicomservices"); err != nil { + return nil, err + } + + if err := id.ValidateNoEmptySegments(input); err != nil { + return nil, err + } + + return &resourceId, nil +} diff --git a/internal/services/healthcare/parse/dicom_service_test.go b/internal/services/healthcare/parse/dicom_service_test.go new file mode 100644 index 000000000000..473490011e54 --- /dev/null +++ b/internal/services/healthcare/parse/dicom_service_test.go @@ -0,0 +1,128 @@ +package parse + +// NOTE: this file is generated via 'go:generate' - manual changes will be overwritten + +import ( + "testing" + + "github.com/hashicorp/go-azure-helpers/resourcemanager/resourceids" +) + +var _ resourceids.Id = DicomServiceId{} + +func TestDicomServiceIDFormatter(t *testing.T) { + actual := NewDicomServiceID("12345678-1234-9876-4563-123456789012", "group1", "workspace1", "service1").ID() + expected := "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/group1/providers/Microsoft.HealthcareApis/workspaces/workspace1/dicomservices/service1" + if actual != expected { + t.Fatalf("Expected %q but got %q", expected, actual) + } +} + +func TestDicomServiceID(t *testing.T) { + testData := []struct { + Input string + Error bool + Expected *DicomServiceId + }{ + + { + // empty + Input: "", + Error: true, + }, + + { + // missing SubscriptionId + Input: "/", + Error: true, + }, + + { + // missing value for SubscriptionId + Input: "/subscriptions/", + Error: true, + }, + + { + // missing ResourceGroup + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/", + Error: true, + }, + + { + // missing value for ResourceGroup + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/", + Error: true, + }, + + { + // missing WorkspaceName + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/group1/providers/Microsoft.HealthcareApis/", + Error: true, + }, + + { + // missing value for WorkspaceName + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/group1/providers/Microsoft.HealthcareApis/workspaces/", + Error: true, + }, + + { + // missing Name + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/group1/providers/Microsoft.HealthcareApis/workspaces/workspace1/", + Error: true, + }, + + { + // missing value for Name + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/group1/providers/Microsoft.HealthcareApis/workspaces/workspace1/dicomservices/", + Error: true, + }, + + { + // valid + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/group1/providers/Microsoft.HealthcareApis/workspaces/workspace1/dicomservices/service1", + Expected: &DicomServiceId{ + SubscriptionId: "12345678-1234-9876-4563-123456789012", + ResourceGroup: "group1", + WorkspaceName: "workspace1", + Name: "service1", + }, + }, + + { + // upper-cased + Input: "/SUBSCRIPTIONS/12345678-1234-9876-4563-123456789012/RESOURCEGROUPS/GROUP1/PROVIDERS/MICROSOFT.HEALTHCAREAPIS/WORKSPACES/WORKSPACE1/DICOMSERVICES/SERVICE1", + Error: true, + }, + } + + for _, v := range testData { + t.Logf("[DEBUG] Testing %q", v.Input) + + actual, err := DicomServiceID(v.Input) + if err != nil { + if v.Error { + continue + } + + t.Fatalf("Expect a value but got an error: %s", err) + } + if v.Error { + t.Fatal("Expect an error but didn't get one") + } + + if actual.SubscriptionId != v.Expected.SubscriptionId { + t.Fatalf("Expected %q but got %q for SubscriptionId", v.Expected.SubscriptionId, actual.SubscriptionId) + } + if actual.ResourceGroup != v.Expected.ResourceGroup { + t.Fatalf("Expected %q but got %q for ResourceGroup", v.Expected.ResourceGroup, actual.ResourceGroup) + } + if actual.WorkspaceName != v.Expected.WorkspaceName { + t.Fatalf("Expected %q but got %q for WorkspaceName", v.Expected.WorkspaceName, actual.WorkspaceName) + } + if actual.Name != v.Expected.Name { + t.Fatalf("Expected %q but got %q for Name", v.Expected.Name, actual.Name) + } + } +} diff --git a/internal/services/healthcare/registration.go b/internal/services/healthcare/registration.go index 75f534108c4f..632233c1fb59 100644 --- a/internal/services/healthcare/registration.go +++ b/internal/services/healthcare/registration.go @@ -28,15 +28,17 @@ func (r Registration) WebsiteCategories() []string { // SupportedDataSources returns the supported Data Sources supported by this Service func (r Registration) SupportedDataSources() map[string]*pluginsdk.Resource { return map[string]*pluginsdk.Resource{ - "azurerm_healthcare_service": dataSourceHealthcareService(), - "azurerm_healthcare_workspace": dataSourceHealthcareWorkspace(), + "azurerm_healthcare_service": dataSourceHealthcareService(), + "azurerm_healthcare_workspace": dataSourceHealthcareWorkspace(), + "azurerm_healthcare_dicom_service": dataSourceHealthcareDicomService(), } } // SupportedResources returns the supported Resources supported by this Service func (r Registration) SupportedResources() map[string]*pluginsdk.Resource { return map[string]*pluginsdk.Resource{ - "azurerm_healthcare_service": resourceHealthcareService(), - "azurerm_healthcare_workspace": resourceHealthcareApisWorkspace(), + "azurerm_healthcare_service": resourceHealthcareService(), + "azurerm_healthcare_workspace": resourceHealthcareApisWorkspace(), + "azurerm_healthcare_dicom_service": resourceHealthcareApisDicomService(), } } diff --git a/internal/services/healthcare/resourceids.go b/internal/services/healthcare/resourceids.go index 0f0d65503ecb..b8b2510c7aa4 100644 --- a/internal/services/healthcare/resourceids.go +++ b/internal/services/healthcare/resourceids.go @@ -2,3 +2,4 @@ package healthcare //go:generate go run ../../tools/generator-resource-id/main.go -path=./ -name=Service -id=/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/group1/providers/Microsoft.HealthcareApis/services/service1 //go:generate go run ../../tools/generator-resource-id/main.go -path=./ -name=Workspace -id=/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/group1/providers/Microsoft.HealthcareApis/workspaces/workspace1 +//go:generate go run ../../tools/generator-resource-id/main.go -path=./ -name=DicomService -id=/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/group1/providers/Microsoft.HealthcareApis/workspaces/workspace1/dicomservices/service1 diff --git a/internal/services/healthcare/validate/dicom_name.go b/internal/services/healthcare/validate/dicom_name.go new file mode 100644 index 000000000000..201db25ff37f --- /dev/null +++ b/internal/services/healthcare/validate/dicom_name.go @@ -0,0 +1,16 @@ +package validate + +import ( + "regexp" + + "github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk" + "github.com/hashicorp/terraform-provider-azurerm/internal/tf/validation" +) + +// DicomServiceName validates Dicom Service names +func DicomServiceName() pluginsdk.SchemaValidateFunc { + return validation.StringMatch( + regexp.MustCompile(`^[0-9a-zA-Z][-0-9a-zA-Z]{1,22}[0-9a-zA-Z]$`), + `The service name must start with a letter or number. The account name can contain letters, numbers, and dashes. The final character must be a letter or a number. The service name length must be from 3 to 24 characters.`, + ) +} diff --git a/internal/services/healthcare/validate/dicom_service_id.go b/internal/services/healthcare/validate/dicom_service_id.go new file mode 100644 index 000000000000..74ed2ef78449 --- /dev/null +++ b/internal/services/healthcare/validate/dicom_service_id.go @@ -0,0 +1,23 @@ +package validate + +// NOTE: this file is generated via 'go:generate' - manual changes will be overwritten + +import ( + "fmt" + + "github.com/hashicorp/terraform-provider-azurerm/internal/services/healthcare/parse" +) + +func DicomServiceID(input interface{}, key string) (warnings []string, errors []error) { + v, ok := input.(string) + if !ok { + errors = append(errors, fmt.Errorf("expected %q to be a string", key)) + return + } + + if _, err := parse.DicomServiceID(v); err != nil { + errors = append(errors, err) + } + + return +} diff --git a/internal/services/healthcare/validate/dicom_service_id_test.go b/internal/services/healthcare/validate/dicom_service_id_test.go new file mode 100644 index 000000000000..606a234bb022 --- /dev/null +++ b/internal/services/healthcare/validate/dicom_service_id_test.go @@ -0,0 +1,88 @@ +package validate + +// NOTE: this file is generated via 'go:generate' - manual changes will be overwritten + +import "testing" + +func TestDicomServiceID(t *testing.T) { + cases := []struct { + Input string + Valid bool + }{ + + { + // empty + Input: "", + Valid: false, + }, + + { + // missing SubscriptionId + Input: "/", + Valid: false, + }, + + { + // missing value for SubscriptionId + Input: "/subscriptions/", + Valid: false, + }, + + { + // missing ResourceGroup + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/", + Valid: false, + }, + + { + // missing value for ResourceGroup + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/", + Valid: false, + }, + + { + // missing WorkspaceName + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/group1/providers/Microsoft.HealthcareApis/", + Valid: false, + }, + + { + // missing value for WorkspaceName + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/group1/providers/Microsoft.HealthcareApis/workspaces/", + Valid: false, + }, + + { + // missing Name + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/group1/providers/Microsoft.HealthcareApis/workspaces/workspace1/", + Valid: false, + }, + + { + // missing value for Name + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/group1/providers/Microsoft.HealthcareApis/workspaces/workspace1/dicomservices/", + Valid: false, + }, + + { + // valid + Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/group1/providers/Microsoft.HealthcareApis/workspaces/workspace1/dicomservices/service1", + Valid: true, + }, + + { + // upper-cased + Input: "/SUBSCRIPTIONS/12345678-1234-9876-4563-123456789012/RESOURCEGROUPS/GROUP1/PROVIDERS/MICROSOFT.HEALTHCAREAPIS/WORKSPACES/WORKSPACE1/DICOMSERVICES/SERVICE1", + Valid: false, + }, + } + for _, tc := range cases { + t.Logf("[DEBUG] Testing Value %s", tc.Input) + _, errors := DicomServiceID(tc.Input, "test") + valid := len(errors) == 0 + + if tc.Valid != valid { + t.Fatalf("Expected %t but got %t", tc.Valid, valid) + } + } +} diff --git a/website/docs/d/healthcare_dicom.html.markdown b/website/docs/d/healthcare_dicom.html.markdown new file mode 100644 index 000000000000..870218d21c5b --- /dev/null +++ b/website/docs/d/healthcare_dicom.html.markdown @@ -0,0 +1,58 @@ +--- +subcategory: "Healthcare" +layout: "azurerm" +page_title: "Azure Resource Manager: azurerm_healthcare_dicom_service" +description: |- + Get information about an existing Healthcare DICOM (Digital Imaging and Communications in Medicine) Service +--- + +# Data Source: azurerm_healthcare_dicom_service + +Use this data source to access information about an existing Healthcare DICOM Service + +## Example Usage + +```hcl +data "azurerm_healthcare_dicom_service" "example" { + name = "example-healthcare_dicom_service" + workspace_id = "example_healthcare_workspace" +} + +output "azurerm_healthcare_dicom_service" { + value = data.azurerm_healthcare_dicom_service.example.id +} +``` + +## Argument Reference + +* `name` - The name of the Healthcare DICOM Service + +* `workspace_id` - The name of the Healthcare Workspace in which the Healthcare DICOM Service exists. + +## Attributes Reference + +The following attributes are exported: + +* `id` - The ID of the Healthcare DICOM Service. + +* `location` - The Azure Region where the Healthcare DICOM Service is located. + +* `authentication` - The `authentication` block as defined below. + +* `service_url` - The url of the Healthcare DICOM Services. + +* `tags` - A map of tags assigned to the Healthcare DICOM Service. + +--- +An `authentication` supports the following: + +* `authority` - The Azure Active Directory (tenant) that serves as the authentication authority to access the service. The default authority is the Directory defined in the authentication scheme in use when running Terraform. + Authority must be registered to Azure AD and in the following format: https://{Azure-AD-endpoint}/{tenant-id}. + +* `audience` - The intended audience to receive authentication tokens for the service. The default value is https://dicom.azurehealthcareapis.azure.com + +## Timeouts + +The `timeouts` block allows you to specify [timeouts](https://www.terraform.io/docs/configuration/resources.html#timeouts) for certain actions: + +* `read` - (Defaults to 5 minutes) Used when retrieving the Healthcare DICOM Service. diff --git a/website/docs/r/healthcare_dicom.html.markdown b/website/docs/r/healthcare_dicom.html.markdown new file mode 100644 index 000000000000..58d7b288464b --- /dev/null +++ b/website/docs/r/healthcare_dicom.html.markdown @@ -0,0 +1,91 @@ +--- +subcategory: "Healthcare" +layout: "azurerm" +page_title: "Azure Resource Manager: azurerm_healthcare_dicom_service" +description: |- + Manages a Healthcare DICOM (Digital Imaging and Communications in Medicine) Service. +--- + +# azurerm_healthcare_dicom_service + +Manages a Healthcare DICOM Service + +## Example Usage + +```hcl +resource "azurerm_healthcare_workspace" "test" { + name = "tfexworkspace" + resource_group_name = "tfex-resource_group" + location = "east us" +} + +resource "azurerm_healthcare_dicom_service" "test" { + name = "tfexDicom" + workspace_id = azurerm_healthcare_workspace.test.id + location = "east us" + + identity { + type = "SystemAssigned" + } + + tags = { + environment = "None" + } +} +``` + +## Argument Reference + +The following arguments are supported: + +* `name` - (Required) Specifies the name of the Healthcare DICOM Service. Changing this forces a new Healthcare DICOM Service to be created. + +* `workspace_id` - (Required) Specifies the name of the Healthcare Workspace where the Healthcare DICOM Service should exist. Changing this forces a new Healthcare DICOM Service to be created. + +* `location` - (Required) Specifies the Azure Region where the Healthcare DICOM Service should be created. Changing this forces a new Healthcare DICOM Service to be created. + +* `identity` - (Optional) An `identity` block as defined below. + +* `public_network_access_enabled` - (Optional) Whether to enabled public networks when data plane traffic coming from public networks while private endpoint is enabled. + +--- + +An `identity` block supports the following: + +* `type` - (Required) The type of identity used for the Healthcare DICOM service. Possible values are `SystemAssigned` and `UserAssigned`. If `UserAssigned` is set, an `identity_ids` must be set as well. + +* `identity_ids` - (Optional) A list of User Assigned Identity IDs which should be assigned to this Healthcare DICOM service. + +## Attributes Reference + +The following attributes are exported: + +* `id` - The ID of the Healthcare DICOM Service. + +* `authentication` - The `authentication` block as defined below. + +* `service_url` - The url of the Healthcare DICOM Services. + +--- +An `authentication` supports the following: + +* `authority` - The Azure Active Directory (tenant) that serves as the authentication authority to access the service. The default authority is the Directory defined in the authentication scheme in use when running Terraform. + Authority must be registered to Azure AD and in the following format: https://{Azure-AD-endpoint}/{tenant-id}. + +* `audience` - The intended audience to receive authentication tokens for the service. The default value is https://dicom.azurehealthcareapis.azure.com + +## 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 Healthcare DICOM Service. +* `update` - (Defaults to 30 minutes) Used when updating the Healthcare DICOM Service. +* `read` - (Defaults to 5 minutes) Used when retrieving the Healthcare DICOM Service. +* `delete` - (Defaults to 30 minutes) Used when deleting the Healthcare DICOM Service. + +## Import + +Healthcare DICOM Service can be imported using the resource`id`, e.g. + +```shell +terraform import azurerm_healthcare_dicom_service.example /subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/group1/providers/Microsoft.HealthcareApis/workspaces/workspace1/dicomservices/service1 +```