diff --git a/azurerm/internal/services/mssql/helper/sql_virtual_machine_storage_settings.go b/azurerm/internal/services/mssql/helper/sql_virtual_machine_storage_settings.go new file mode 100644 index 000000000000..ed98253d7d94 --- /dev/null +++ b/azurerm/internal/services/mssql/helper/sql_virtual_machine_storage_settings.go @@ -0,0 +1,30 @@ +package helper + +import ( + "github.com/hashicorp/terraform-plugin-sdk/helper/schema" + "github.com/hashicorp/terraform-plugin-sdk/helper/validation" +) + +func StorageSettingSchema() *schema.Schema { + return &schema.Schema{ + Type: schema.TypeList, + Optional: true, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "luns": { + Type: schema.TypeList, + Required: true, + Elem: &schema.Schema{ + Type: schema.TypeInt, + }, + }, + "default_file_path": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.StringIsNotEmpty, + }, + }, + }, + } +} diff --git a/azurerm/internal/services/mssql/mssql_virtual_machine_resource.go b/azurerm/internal/services/mssql/mssql_virtual_machine_resource.go index 8357ae001e9a..04409e5c4b83 100644 --- a/azurerm/internal/services/mssql/mssql_virtual_machine_resource.go +++ b/azurerm/internal/services/mssql/mssql_virtual_machine_resource.go @@ -13,6 +13,7 @@ import ( "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/clients" parseCompute "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/services/compute/parse" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/services/mssql/helper" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/services/mssql/parse" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/services/mssql/validate" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/tags" @@ -171,6 +172,37 @@ func resourceArmMsSqlVirtualMachine() *schema.Resource { ValidateFunc: validate.MsSqlVMLoginUserName, }, + "storage_configuration": { + Type: schema.TypeList, + Optional: true, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "disk_type": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.StringInSlice([]string{ + string(sqlvirtualmachine.NEW), + string(sqlvirtualmachine.EXTEND), + string(sqlvirtualmachine.ADD), + }, false), + }, + "storage_workload_type": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validation.StringInSlice([]string{ + string(sqlvirtualmachine.GENERAL), + string(sqlvirtualmachine.OLTP), + string(sqlvirtualmachine.DW), + }, false), + }, + "data_settings": helper.StorageSettingSchema(), + "log_settings": helper.StorageSettingSchema(), + "temp_db_settings": helper.StorageSettingSchema(), + }, + }, + }, + "tags": tags.Schema(), }, } @@ -229,6 +261,7 @@ func resourceArmMsSqlVirtualMachineCreateUpdate(d *schema.ResourceData, meta int SQLAuthUpdateUserName: utils.String(d.Get("sql_connectivity_update_username").(string)), }, }, + StorageConfigurationSettings: expandArmSqlVirtualMachineStorageConfigurationSettings(d.Get("storage_configuration").([]interface{})), }, Tags: tags.Expand(d.Get("tags").(map[string]interface{})), } @@ -293,6 +326,17 @@ func resourceArmMsSqlVirtualMachineRead(d *schema.ResourceData, meta interface{} d.Set("sql_connectivity_type", mgmtSettings.SQLConnectivityUpdateSettings.ConnectivityType) } } + + // `storage_configuration.0.storage_workload_type` is in a different spot than the rest of the `storage_configuration` + // so we'll grab that here and pass it along + storageWorkloadType := "" + if props.ServerConfigurationsManagementSettings != nil && props.ServerConfigurationsManagementSettings.SQLWorkloadTypeUpdateSettings != nil { + storageWorkloadType = string(props.ServerConfigurationsManagementSettings.SQLWorkloadTypeUpdateSettings.SQLWorkloadType) + } + + if err := d.Set("storage_configuration", flattenArmSqlVirtualMachineStorageConfigurationSettings(props.StorageConfigurationSettings, storageWorkloadType)); err != nil { + return fmt.Errorf("error setting `storage_configuration`: %+v", err) + } } return tags.FlattenAndSet(d, resp.Tags) } @@ -419,3 +463,74 @@ func mssqlVMCredentialNameDiffSuppressFunc(_, old, new string, _ *schema.Resourc } return false } + +func expandArmSqlVirtualMachineStorageConfigurationSettings(input []interface{}) *sqlvirtualmachine.StorageConfigurationSettings { + if len(input) == 0 || input[0] == nil { + return nil + } + storageSettings := input[0].(map[string]interface{}) + + return &sqlvirtualmachine.StorageConfigurationSettings{ + DiskConfigurationType: sqlvirtualmachine.DiskConfigurationType(storageSettings["disk_type"].(string)), + StorageWorkloadType: sqlvirtualmachine.StorageWorkloadType(storageSettings["storage_workload_type"].(string)), + SQLDataSettings: expandArmSqlVirtualMachineDataStorageSettings(storageSettings["data_settings"].([]interface{})), + SQLLogSettings: expandArmSqlVirtualMachineDataStorageSettings(storageSettings["log_settings"].([]interface{})), + SQLTempDbSettings: expandArmSqlVirtualMachineDataStorageSettings(storageSettings["temp_db_settings"].([]interface{})), + } +} + +func flattenArmSqlVirtualMachineStorageConfigurationSettings(input *sqlvirtualmachine.StorageConfigurationSettings, storageWorkloadType string) []interface{} { + if input == nil { + return []interface{}{} + } + + return []interface{}{ + map[string]interface{}{ + "disk_type": string(input.DiskConfigurationType), + "storage_workload_type": storageWorkloadType, + "data_settings": flattenArmSqlVirtualMachineStorageSettings(input.SQLDataSettings), + "log_settings": flattenArmSqlVirtualMachineStorageSettings(input.SQLLogSettings), + "temp_db_settings": flattenArmSqlVirtualMachineStorageSettings(input.SQLTempDbSettings), + }, + } +} + +func expandArmSqlVirtualMachineDataStorageSettings(input []interface{}) *sqlvirtualmachine.SQLStorageSettings { + if len(input) == 0 || input[0] == nil { + return nil + } + dataStorageSettings := input[0].(map[string]interface{}) + + return &sqlvirtualmachine.SQLStorageSettings{ + Luns: expandArmSqlVirtualMachineStorageSettingsLuns(dataStorageSettings["luns"].([]interface{})), + DefaultFilePath: utils.String(dataStorageSettings["default_file_path"].(string)), + } +} + +func expandArmSqlVirtualMachineStorageSettingsLuns(input []interface{}) *[]int32 { + expandedLuns := make([]int32, len(input)) + for i := range input { + if input[i] != nil { + expandedLuns[i] = int32(input[i].(int)) + } + } + + return &expandedLuns +} + +func flattenArmSqlVirtualMachineStorageSettings(input *sqlvirtualmachine.SQLStorageSettings) []interface{} { + if input == nil || input.Luns == nil { + return []interface{}{} + } + attrs := make(map[string]interface{}) + + if input.Luns != nil { + attrs["luns"] = *input.Luns + } + + if input.DefaultFilePath != nil { + attrs["default_file_path"] = *input.DefaultFilePath + } + + return []interface{}{attrs} +} diff --git a/azurerm/internal/services/mssql/tests/mssql_virtual_machine_resource_test.go b/azurerm/internal/services/mssql/tests/mssql_virtual_machine_resource_test.go index 4fd0cbedd06d..7d9b9f0721d6 100644 --- a/azurerm/internal/services/mssql/tests/mssql_virtual_machine_resource_test.go +++ b/azurerm/internal/services/mssql/tests/mssql_virtual_machine_resource_test.go @@ -148,6 +148,32 @@ func TestAccAzureRMMsSqlVirtualMachine_updateKeyVault(t *testing.T) { }) } +func TestAccAzureRMMsSqlVirtualMachine_storageConfigurationSettings(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_mssql_virtual_machine", "test") + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acceptance.PreCheck(t) }, + Providers: acceptance.SupportedProviders, + CheckDestroy: testCheckAzureRMMsSqlVirtualMachineDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAzureRMMsSqlVirtualMachine_storageConfigurationSettings(data), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMMsSqlVirtualMachineExists(data.ResourceName), + ), + }, + data.ImportStep(), + { + Config: testAccAzureRMMsSqlVirtualMachine_basic(data), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMMsSqlVirtualMachineExists(data.ResourceName), + ), + }, + data.ImportStep(), + }, + }) +} + func testCheckAzureRMMsSqlVirtualMachineExists(resourceName string) resource.TestCheckFunc { return func(s *terraform.State) error { rs, ok := s.RootModule().Resources[resourceName] @@ -575,3 +601,51 @@ resource "azurerm_mssql_virtual_machine" "test" { } `, vmconfig, data.RandomInteger, value) } + +func testAccAzureRMMsSqlVirtualMachine_storageConfigurationSettings(data acceptance.TestData) string { + vmconfig := testAccAzureRMVirtualMachine_template(data) + return fmt.Sprintf(` +%s + +resource "azurerm_managed_disk" "test" { + name = "accmd-sqlvm-%[2]d" + location = azurerm_resource_group.test.location + resource_group_name = azurerm_resource_group.test.name + storage_account_type = "Standard_LRS" + create_option = "Empty" + disk_size_gb = 10 +} + +resource "azurerm_virtual_machine_data_disk_attachment" "test" { + managed_disk_id = azurerm_managed_disk.test.id + virtual_machine_id = azurerm_virtual_machine.test.id + lun = "0" + caching = "None" +} + +resource "azurerm_mssql_virtual_machine" "test" { + virtual_machine_id = azurerm_virtual_machine.test.id + sql_license_type = "PAYG" + + storage_configuration { + disk_type = "NEW" + storage_workload_type = "OLTP" + + data_settings { + luns = [0] + default_file_path = "F:\\SQLData" + } + + log_settings { + luns = [0] + default_file_path = "F:\\SQLLog" + } + + temp_db_settings { + luns = [0] + default_file_path = "F:\\SQLTemp" + } + } +} +`, vmconfig, data.RandomInteger) +} diff --git a/website/docs/r/mssql_virtual_machine.html.markdown b/website/docs/r/mssql_virtual_machine.html.markdown index 8a4688a376e7..e17c3ea9b734 100644 --- a/website/docs/r/mssql_virtual_machine.html.markdown +++ b/website/docs/r/mssql_virtual_machine.html.markdown @@ -60,6 +60,8 @@ The following arguments are supported: * `sql_connectivity_update_username` - (Optional) The SQL Server sysadmin login to create. +* `storage_configuration` - (Optional) An `storage_configuration` block as defined below. + * `tags` - (Optional) A mapping of tags to assign to the resource. --- @@ -84,6 +86,28 @@ The `key_vault_credential` block supports the following: * `service_principal_secret` - (Required) The service principal name secret to access key vault. Changing this forces a new resource to be created. +--- + +The `storage_configuration` block supports the following: + +* `disk_type` - (Required) The type of disk configuration to apply to the SQL Server. Valid values include `NEW`, `EXTEND`, or `ADD`. + +* `storage_workload_type` - (Required) The type of storage workload. Valid values include `GENERAL`, `OLTP`, or `DW`. + +* `data_settings` - (Optional) An `storage_settings` as defined below. + +* `log_settings` - (Optional) An `storage_settings` as defined below. + +* `temp_db_settings` - (Optional) An `storage_settings` as defined below. + +--- + +The `storage_settings` block supports the following: + +* `default_file_path` - (Required) The SQL Server default path + +* `luns` - (Required) A list of Logical Unit Numbers for the disks. + ## Attributes Reference The following attributes are exported: