diff --git a/internal/services/mssqlmanagedinstance/mssql_managed_instance_resource.go b/internal/services/mssqlmanagedinstance/mssql_managed_instance_resource.go index 384496d8f812..761f6b2f8af1 100644 --- a/internal/services/mssqlmanagedinstance/mssql_managed_instance_resource.go +++ b/internal/services/mssqlmanagedinstance/mssql_managed_instance_resource.go @@ -63,6 +63,8 @@ type MsSqlManagedInstanceModel struct { AzureActiveDirectoryAdministrator []AzureActiveDirectoryAdministrator `tfschema:"azure_active_directory_administrator"` ZoneRedundantEnabled bool `tfschema:"zone_redundant_enabled"` Tags map[string]string `tfschema:"tags"` + DatabaseFormat string `tfschema:"database_format"` + HybridSecondaryUsage string `tfschema:"hybrid_secondary_usage"` } type AzureActiveDirectoryAdministrator struct { @@ -230,12 +232,26 @@ func (r MsSqlManagedInstanceResource) Arguments() map[string]*pluginsdk.Schema { ForceNew: true, }, + "database_format": { + Type: schema.TypeString, + Optional: true, + Default: managedinstances.ManagedInstanceDatabaseFormatSQLServerTwoZeroTwoTwo, + ValidateFunc: validation.StringInSlice(managedinstances.PossibleValuesForManagedInstanceDatabaseFormat(), false), + }, + "dns_zone_partner_id": { Type: schema.TypeString, Optional: true, ValidateFunc: validate.ManagedInstanceID, }, + "hybrid_secondary_usage": { + Type: schema.TypeString, + Optional: true, + Default: managedinstances.HybridSecondaryUsageActive, + ValidateFunc: validation.StringInSlice(managedinstances.PossibleValuesForHybridSecondaryUsage(), false), + }, + "identity": commonschema.SystemAssignedUserAssignedIdentityOptional(), "maintenance_configuration_name": { @@ -361,6 +377,13 @@ func (r MsSqlManagedInstanceResource) CustomizeDiff() sdk.ResourceFunc { } } + // Once the `AlwaysUpToDate` is enabled, you can't go back to `SQLServer2022` update policy. + if oldVal, newVal := rd.GetChange("database_format"); oldVal.(string) == string(managedinstances.ManagedInstanceDatabaseFormatAlwaysUpToDate) && newVal.(string) == string(managedinstances.ManagedInstanceDatabaseFormatSQLServerTwoZeroTwoTwo) { + if err := rd.ForceNew("database_format"); err != nil { + return err + } + } + _, aadAdminOk := rd.GetOk("azure_active_directory_administrator") authOnlyEnabled := rd.Get("azure_active_directory_administrator.0.azuread_authentication_only_enabled").(bool) _, loginOk := rd.GetOk("administrator_login") @@ -426,7 +449,9 @@ func (r MsSqlManagedInstanceResource) Create() sdk.ResourceFunc { VCores: pointer.To(model.VCores), ZoneRedundant: pointer.To(model.ZoneRedundantEnabled), // `Administrators` is only valid when specified during creation` - Administrators: expandMsSqlManagedInstanceExternalAdministrators(model.AzureActiveDirectoryAdministrator), + Administrators: expandMsSqlManagedInstanceExternalAdministrators(model.AzureActiveDirectoryAdministrator), + DatabaseFormat: pointer.To(managedinstances.ManagedInstanceDatabaseFormat(model.DatabaseFormat)), + HybridSecondaryUsage: pointer.To(managedinstances.HybridSecondaryUsage(model.HybridSecondaryUsage)), }, Tags: pointer.To(model.Tags), } @@ -583,7 +608,13 @@ func (r MsSqlManagedInstanceResource) Update() sdk.ResourceFunc { } } - metadata.Logger.Infof("Updating %s", *id) + if metadata.ResourceData.HasChange("database_format") { + properties.Properties.DatabaseFormat = pointer.To(managedinstances.ManagedInstanceDatabaseFormat(state.DatabaseFormat)) + } + + if metadata.ResourceData.HasChange("hybrid_secondary_usage") { + properties.Properties.HybridSecondaryUsage = pointer.To(managedinstances.HybridSecondaryUsage(state.HybridSecondaryUsage)) + } err = client.CreateOrUpdateThenPoll(ctx, *id, properties) if err != nil { @@ -674,6 +705,8 @@ func (r MsSqlManagedInstanceResource) Read() sdk.ResourceFunc { if props.ServicePrincipal != nil { model.ServicePrincipalType = string(pointer.From(props.ServicePrincipal.Type)) } + model.DatabaseFormat = string(pointer.From(props.DatabaseFormat)) + model.HybridSecondaryUsage = string(pointer.From(props.HybridSecondaryUsage)) } } return metadata.Encode(&model) diff --git a/internal/services/mssqlmanagedinstance/mssql_managed_instance_resource_test.go b/internal/services/mssqlmanagedinstance/mssql_managed_instance_resource_test.go index 96487701a7d7..bee8609f7247 100644 --- a/internal/services/mssqlmanagedinstance/mssql_managed_instance_resource_test.go +++ b/internal/services/mssqlmanagedinstance/mssql_managed_instance_resource_test.go @@ -64,6 +64,57 @@ func TestAccMsSqlManagedInstance_update(t *testing.T) { }) } +func TestAccMsSqlManagedInstance_databaseFormat(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_mssql_managed_instance", "test") + r := MsSqlManagedInstanceResource{} + + data.ResourceTest(t, r, []acceptance.TestStep{ + { + Config: r.databaseFormat(data, "SQLServer2022"), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep("administrator_login_password"), + { + Config: r.databaseFormat(data, "AlwaysUpToDate"), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep("administrator_login_password"), + }) +} + +func TestAccMsSqlManagedInstance_hybridSecondaryUsage(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_mssql_managed_instance", "test") + r := MsSqlManagedInstanceResource{} + + data.ResourceTest(t, r, []acceptance.TestStep{ + { + Config: r.hybridSecondaryUsage(data, "Passive"), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep("administrator_login_password"), + { + Config: r.hybridSecondaryUsage(data, "Active"), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep("administrator_login_password"), + { + Config: r.hybridSecondaryUsage(data, "Passive"), + Check: acceptance.ComposeTestCheckFunc( + check.That(data.ResourceName).ExistsInAzure(r), + ), + }, + data.ImportStep("administrator_login_password"), + }) +} + func TestAccMsSqlManagedInstance_premium(t *testing.T) { data := acceptance.BuildTestData(t, "azurerm_mssql_managed_instance", "test") r := MsSqlManagedInstanceResource{} @@ -394,6 +445,96 @@ resource "azurerm_mssql_managed_instance" "test" { `, r.template(data, data.Locations.Primary), data.RandomInteger) } +func (r MsSqlManagedInstanceResource) databaseFormat(data acceptance.TestData, databaseFormat string) string { + return fmt.Sprintf(` +%[1]s + +provider "azurerm" { + features { + resource_group { + /* Due to the creation of unmanaged Microsoft.Network/networkIntentPolicies in this service, + prevent_deletion_if_contains_resources has been added here to allow the test resources to be + deleted until this can be properly investigated + */ + prevent_deletion_if_contains_resources = false + } + } +} + +resource "azurerm_mssql_managed_instance" "test" { + name = "acctestsqlserver%[2]d" + resource_group_name = azurerm_resource_group.test.name + location = azurerm_resource_group.test.location + + license_type = "BasePrice" + sku_name = "GP_Gen5" + storage_size_in_gb = 32 + subnet_id = azurerm_subnet.test.id + vcores = 4 + + administrator_login = "missadministrator" + administrator_login_password = "NCC-1701-D" + + database_format = "%[3]s" + + depends_on = [ + azurerm_subnet_network_security_group_association.test, + azurerm_subnet_route_table_association.test, + ] + + tags = { + environment = "staging" + database = "test" + } +} +`, r.template(data, data.Locations.Primary), data.RandomInteger, databaseFormat) +} + +func (r MsSqlManagedInstanceResource) hybridSecondaryUsage(data acceptance.TestData, hybridSecondaryUsage string) string { + return fmt.Sprintf(` +%[1]s + +provider "azurerm" { + features { + resource_group { + /* Due to the creation of unmanaged Microsoft.Network/networkIntentPolicies in this service, + prevent_deletion_if_contains_resources has been added here to allow the test resources to be + deleted until this can be properly investigated + */ + prevent_deletion_if_contains_resources = false + } + } +} + +resource "azurerm_mssql_managed_instance" "test" { + name = "acctestsqlserver%[2]d" + resource_group_name = azurerm_resource_group.test.name + location = azurerm_resource_group.test.location + + license_type = "BasePrice" + sku_name = "GP_Gen5" + storage_size_in_gb = 32 + subnet_id = azurerm_subnet.test.id + vcores = 4 + + administrator_login = "missadministrator" + administrator_login_password = "NCC-1701-D" + + hybrid_secondary_usage = "%[3]s" + + depends_on = [ + azurerm_subnet_network_security_group_association.test, + azurerm_subnet_route_table_association.test, + ] + + tags = { + environment = "staging" + database = "test" + } +} +`, r.template(data, data.Locations.Primary), data.RandomInteger, hybridSecondaryUsage) +} + func (r MsSqlManagedInstanceResource) basicZRS(data acceptance.TestData) string { return fmt.Sprintf(` %[1]s diff --git a/website/docs/r/mssql_managed_instance.html.markdown b/website/docs/r/mssql_managed_instance.html.markdown index f6ced0678d88..320db6682673 100644 --- a/website/docs/r/mssql_managed_instance.html.markdown +++ b/website/docs/r/mssql_managed_instance.html.markdown @@ -233,8 +233,14 @@ The following arguments are supported: * `collation` - (Optional) Specifies how the SQL Managed Instance will be collated. Default value is `SQL_Latin1_General_CP1_CI_AS`. Changing this forces a new resource to be created. +* `database_format` - (Optional) Specifies the internal format of the SQL Managed Instance databases specific to the SQL engine version. Possible values are `AlwaysUpToDate` and `SQLServer2022`. Defaults to `SQLServer2022`. + +~> **NOTE:** Changing `database_format` from `AlwaysUpToDate` to `SQLServer2022` forces a new SQL Managed Instance to be created. + * `dns_zone_partner_id` - (Optional) The ID of the SQL Managed Instance which will share the DNS zone. This is a prerequisite for creating an `azurerm_sql_managed_instance_failover_group`. Setting this after creation forces a new resource to be created. +* `hybrid_secondary_usage` - (Optional) Specifies the hybrid secondary usage for disaster recovery of the SQL Managed Instance. Possible values are `Active` and `Passive`. Defaults to `Active`. + * `identity` - (Optional) An `identity` block as defined below. * `maintenance_configuration_name` - (Optional) The name of the Public Maintenance Configuration window to apply to the SQL Managed Instance. Valid values include `SQL_Default` or an Azure Location in the format `SQL_{Location}_MI_{Size}`(for example `SQL_EastUS_MI_1`). Defaults to `SQL_Default`.