diff --git a/azurerm/internal/clients/client.go b/azurerm/internal/clients/client.go index 7dd87e94adc1e..53aa62e8a6096 100644 --- a/azurerm/internal/clients/client.go +++ b/azurerm/internal/clients/client.go @@ -34,6 +34,7 @@ import ( kusto "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/services/kusto/client" loganalytics "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/services/loganalytics/client" logic "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/services/logic/client" + machinelearning "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/services/machinelearning/client" managementgroup "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/services/managementgroup/client" maps "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/services/maps/client" mariadb "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/services/mariadb/client" @@ -103,6 +104,7 @@ type Client struct { Kusto *kusto.Client LogAnalytics *loganalytics.Client Logic *logic.Client + MachineLearning *machinelearning.Client ManagementGroups *managementgroup.Client Maps *maps.Client MariaDB *mariadb.Client @@ -171,6 +173,7 @@ func (client *Client) Build(ctx context.Context, o *common.ClientOptions) error client.Kusto = kusto.NewClient(o) client.LogAnalytics = loganalytics.NewClient(o) client.Logic = logic.NewClient(o) + client.MachineLearning = machinelearning.NewClient(o) client.ManagementGroups = managementgroup.NewClient(o) client.Maps = maps.NewClient(o) client.MariaDB = mariadb.NewClient(o) diff --git a/azurerm/internal/services/machinelearning/client.go b/azurerm/internal/services/machinelearning/client/client.go similarity index 95% rename from azurerm/internal/services/machinelearning/client.go rename to azurerm/internal/services/machinelearning/client/client.go index 362d7388bd1f5..095b6df00a24e 100644 --- a/azurerm/internal/services/machinelearning/client.go +++ b/azurerm/internal/services/machinelearning/client/client.go @@ -1,4 +1,4 @@ -package machinelearning +package client import ( "github.com/Azure/azure-sdk-for-go/services/machinelearningservices/mgmt/2019-11-01/machinelearningservices" @@ -12,7 +12,7 @@ type Client struct { UsagesClient *machinelearningservices.UsagesClient } -func BuildClient(o *common.ClientOptions) *Client { +func NewClient(o *common.ClientOptions) *Client { WorkspacesClient := machinelearningservices.NewWorkspacesClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId) o.ConfigureClient(&WorkspacesClient.Client, o.ResourceManagerAuthorizer) diff --git a/azurerm/internal/services/machinelearning/registration.go b/azurerm/internal/services/machinelearning/registration.go new file mode 100644 index 0000000000000..292cc925b4d61 --- /dev/null +++ b/azurerm/internal/services/machinelearning/registration.go @@ -0,0 +1,20 @@ +package machinelearning + +import "github.com/hashicorp/terraform-plugin-sdk/helper/schema" + +type Registration struct{} + +// Name is the name of this Service +func (r Registration) Name() string { + return "Machine Learning" +} + +// SupportedDataSources returns the supported Data Sources supported by this Service +func (r Registration) SupportedDataSources() map[string]*schema.Resource { + return 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{} +} diff --git a/azurerm/resource_arm_machine_learning_workspace.go b/azurerm/resource_arm_machine_learning_workspace.go index 37072b31f2a42..0a6eb562781d5 100644 --- a/azurerm/resource_arm_machine_learning_workspace.go +++ b/azurerm/resource_arm_machine_learning_workspace.go @@ -4,7 +4,9 @@ import ( "fmt" "github.com/hashicorp/terraform-plugin-sdk/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/helper/validation" - "reflect" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/clients" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/timeouts" + "time" "github.com/Azure/azure-sdk-for-go/services/machinelearningservices/mgmt/2019-11-01/machinelearningservices" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/azure" @@ -21,6 +23,13 @@ func resourceArmAmlWorkspace() *schema.Resource { Update: resourceArmAmlWorkspaceCreateUpdate, Delete: resourceArmAmlWorkspaceDelete, + 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), + }, + Schema: map[string]*schema.Schema{ "name": { Type: schema.TypeString, @@ -34,37 +43,41 @@ func resourceArmAmlWorkspace() *schema.Resource { "description": { Type: schema.TypeString, - Required: true, + Optional: true, }, "friendly_name": { Type: schema.TypeString, - Required: true, + Optional: true, }, - "key_vault": { - Type: schema.TypeString, - Required: true, + "key_vault_id": { + Type: schema.TypeString, + Required: true, + ValidateFunc: azure.ValidateResourceID, }, - "application_insights": { - Type: schema.TypeString, - Required: true, + "application_insights_id": { + Type: schema.TypeString, + Required: true, + ValidateFunc: azure.ValidateResourceID, }, - "container_registry": { - Type: schema.TypeString, - Required: true, + "container_registry_id": { + Type: schema.TypeString, + Optional: true, + ValidateFunc: azure.ValidateResourceID, }, - "storage_account": { - Type: schema.TypeString, - Required: true, + "storage_account_id": { + Type: schema.TypeString, + Required: true, + ValidateFunc: azure.ValidateResourceID, }, "discovery_url": { Type: schema.TypeString, - Required: true, + Optional: true, }, "tags": tags.Schema(), @@ -88,14 +101,9 @@ func resourceArmAmlWorkspace() *schema.Resource { Type: schema.TypeString, Computed: true, }, - "identity_ids": { - Type: schema.TypeList, - Optional: true, - MinItems: 1, - Elem: &schema.Schema{ - Type: schema.TypeString, - ValidateFunc: validation.NoZeroValues, - }, + "tenant_id": { + Type: schema.TypeString, + Computed: true, }, }, }, @@ -105,18 +113,19 @@ func resourceArmAmlWorkspace() *schema.Resource { } func resourceArmAmlWorkspaceCreateUpdate(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).MachineLearning.WorkspacesClient - ctx := meta.(*ArmClient).StopContext + client := meta.(*clients.Client).MachineLearning.WorkspacesClient + ctx, cancel := timeouts.ForCreateUpdate(meta.(*clients.Client).StopContext, d) + defer cancel() name := d.Get("name").(string) resGroup := d.Get("resource_group_name").(string) location := azure.NormalizeLocation(d.Get("location").(string)) description := d.Get("description").(string) friendlyName := d.Get("friendly_name").(string) - storageAccount := d.Get("storage_account").(string) - keyVault := d.Get("key_vault").(string) - containerRegistry := d.Get("container_registry").(string) - applicationInsights := d.Get("application_insights").(string) + storageAccount := d.Get("storage_account_id").(string) + keyVault := d.Get("key_vault_id").(string) + containerRegistry := d.Get("container_registry_id").(string) + applicationInsights := d.Get("application_insights_id").(string) discoveryUrl := d.Get("discovery_url").(string) t := d.Get("tags").(map[string]interface{}) @@ -131,6 +140,7 @@ func resourceArmAmlWorkspaceCreateUpdate(d *schema.ResourceData, meta interface{ } } + // TODO -- should validate container registry enable_admin is enabled. workspace := machinelearningservices.Workspace{ Name: &name, Location: &location, @@ -144,7 +154,7 @@ func resourceArmAmlWorkspaceCreateUpdate(d *schema.ResourceData, meta interface{ ApplicationInsights: &applicationInsights, KeyVault: &keyVault, }, - Identity: &machinelearningservices.Identity{Type: machinelearningservices.SystemAssigned}, + Identity: expandAmlIdentity(d), } result, err := client.CreateOrUpdate(ctx, resGroup, name, workspace) @@ -169,8 +179,9 @@ func resourceArmAmlWorkspaceCreateUpdate(d *schema.ResourceData, meta interface{ } func resourceArmAmlWorkspaceRead(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).MachineLearning.WorkspacesClient - ctx := meta.(*ArmClient).StopContext + client := meta.(*clients.Client).MachineLearning.WorkspacesClient + ctx, cancel := timeouts.ForRead(meta.(*clients.Client).StopContext, d) + defer cancel() id, err := azure.ParseAzureResourceID(d.Id()) if err != nil { @@ -198,11 +209,11 @@ func resourceArmAmlWorkspaceRead(d *schema.ResourceData, meta interface{}) error if props := resp.WorkspaceProperties; props != nil { d.Set("description", props.Description) d.Set("friendly_name", props.FriendlyName) - d.Set("storage_account", props.StorageAccount) + d.Set("storage_account_id", props.StorageAccount) d.Set("discovery_url", props.DiscoveryURL) - d.Set("container_registry", props.ContainerRegistry) - d.Set("application_insights", props.ApplicationInsights) - d.Set("key_vault", props.KeyVault) + d.Set("container_registry_id", props.ContainerRegistry) + d.Set("application_insights_id", props.ApplicationInsights) + d.Set("key_vault_id", props.KeyVault) } if err := d.Set("identity", flattenAmlIdentity(resp.Identity)); err != nil { return fmt.Errorf("Error flattening identity on Workspace %q (Resource Group %q): %+v", name, resGroup, err) @@ -212,8 +223,8 @@ func resourceArmAmlWorkspaceRead(d *schema.ResourceData, meta interface{}) error } func resourceArmAmlWorkspaceDelete(d *schema.ResourceData, meta interface{}) error { - client := meta.(*ArmClient).MachineLearning.WorkspacesClient - ctx := meta.(*ArmClient).StopContext + client := meta.(*clients.Client).MachineLearning.WorkspacesClient + ctx := meta.(*clients.Client).StopContext id, err := azure.ParseAzureResourceID(d.Id()) if err != nil { @@ -248,6 +259,10 @@ func flattenAmlIdentity(identity *machinelearningservices.Identity) interface{} func expandAmlIdentity(d *schema.ResourceData) *machinelearningservices.Identity { v := d.Get("identity") + // Rest api will return an error if you did not set the identity field. + if v == nil { + return &machinelearningservices.Identity{Type: machinelearningservices.SystemAssigned} + } identities := v.([]interface{}) identity := identities[0].(map[string]interface{}) identityType := machinelearningservices.ResourceIdentityType(identity["type"].(string)) diff --git a/azurerm/resource_arm_machine_learning_workspace_test.go b/azurerm/resource_arm_machine_learning_workspace_test.go new file mode 100644 index 0000000000000..0d43fdd5a3ecb --- /dev/null +++ b/azurerm/resource_arm_machine_learning_workspace_test.go @@ -0,0 +1,133 @@ +package azurerm + +import ( + "fmt" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/terraform" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/acceptance" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/clients" +) + +func TestAccAzureRMMachineLearningWorkspace_basic(t *testing.T) { + resourceName := "azurerm_machine_learning_workspace.test" + ri := tf.AccRandTimeInt() + location := acceptance.Location() + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acceptance.PreCheck(t) }, + Providers: acceptance.SupportedProviders, + CheckDestroy: testCheckAzureRMMachineLearningWorkspaceDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAzureRMMachineLearningWorkspace_basic(ri, location), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMMachineLearningWorkspaceExists(resourceName), + resource.TestCheckResourceAttrSet(resourceName, "key_vault_id"), + resource.TestCheckResourceAttrSet(resourceName, "application_insights_id"), + resource.TestCheckResourceAttrSet(resourceName, "storage_account_id"), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func testCheckAzureRMMachineLearningWorkspaceExists(resourceName string) resource.TestCheckFunc { + return func(s *terraform.State) error { + rs, ok := s.RootModule().Resources[resourceName] + if !ok { + return fmt.Errorf("Machine Learning Workspace not found: %s", resourceName) + } + + name := rs.Primary.Attributes["name"] + resourceGroup := rs.Primary.Attributes["resource_group_name"] + + client := acceptance.AzureProvider.Meta().(*clients.Client).MachineLearning.WorkspacesClient + ctx := acceptance.AzureProvider.Meta().(*clients.Client).StopContext + + if resp, err := client.Get(ctx, resourceGroup, name); err != nil { + if utils.ResponseWasNotFound(resp.Response) { + return fmt.Errorf("Bad: Machine Learning Workspace %q (Resource Group %q) does not exist", name, resourceGroup) + } + return fmt.Errorf("Bad: Get on machinelearningservices.WorkspacesClient: %+v", err) + } + + return nil + } +} + +func testCheckAzureRMMachineLearningWorkspaceDestroy(s *terraform.State) error { + client := acceptance.AzureProvider.Meta().(*clients.Client).MachineLearning.WorkspacesClient + ctx := acceptance.AzureProvider.Meta().(*clients.Client).StopContext + + for _, rs := range s.RootModule().Resources { + if rs.Type != "azurerm_machine_learning_workspace" { + continue + } + + name := rs.Primary.Attributes["name"] + resourceGroup := rs.Primary.Attributes["resource_group_name"] + + if resp, err := client.Get(ctx, resourceGroup, name); err != nil { + if !utils.ResponseWasNotFound(resp.Response) { + return fmt.Errorf("Bad: Get on machinelearningservices.WorkspacesClient: %+v", err) + } + } + + return nil + } + + return nil +} + +func testAccAzureRMMachineLearningWorkspace_basic(rInt int, location string) string { + return fmt.Sprintf(` +data "azurerm_client_config" "current" {} + +resource "azurerm_resource_group" "test" { + name = "acctestRG-mlservices-%d" + location = "%s" +} + +resource "azurerm_key_vault" "test" { + name = "acctestvault%d" + location = "${azurerm_resource_group.test.location}" + resource_group_name = "${azurerm_resource_group.test.name}" + tenant_id = "${data.azurerm_client_config.current.tenant_id}" + + sku_name = "premium" +} + +resource "azurerm_storage_account" "sa" { + name = "acctestsa%d" + location = "${azurerm_resource_group.rg.location}" + resource_group_name = "${azurerm_resource_group.rg.name}" + account_tier = "Premium" + account_replication_type = "LRS" +} + +resource "azurerm_application_insights" "test" { + name = "acctestai-%d" + location = "${azurerm_resource_group.rg.location}" + resource_group_name = "${azurerm_resource_group.rg.name}" + application_type = "web" +} + +resource "azurerm_machine_learning_workspace" "test" { + name = "acctestworkspace-%d" + location = "${azurerm_resource_group.test.location}" + resource_group_name = "${azurerm_resource_group.test.name}" + key_vault_id = "${azurerm_key_vault.test.id}" + storage_account_id = "${azurerm_storage_account.test.id}" + application_insights_id = "${azurerm_application_insights.test.id}" +} +`, rInt, location, rInt, rInt, rInt, rInt) +}