From d207eaa1ec2cb521198a7a679072b8c67ba03bd7 Mon Sep 17 00:00:00 2001 From: Tom Bamford Date: Thu, 14 May 2020 04:46:53 +0100 Subject: [PATCH] mysql_server: New attributes, flatten `storage_profile` block (#6833) create_mode, creation_source_server_id, infrastructure_encryption_enabled, ssl_minimal_tls_version_enforced Support replicas and point-in-time restores Use booleans for toggle properties, renaming auto_grow -> auto_grow_enabled geo_redundant_backup -> geo_redundant_backup_enabled ssl_enforcement -> ssl_enforcement_enabled Remove redundant storage_profile block and move all properties within to top level Fixes: #4756 Related: #6459 --- azurerm/helpers/azure/mysql.go | 15 - .../mysql/mysql_configuration_resource.go | 3 +- .../services/mysql/mysql_database_resource.go | 3 +- .../mysql/mysql_firewall_rule_resource.go | 3 +- .../services/mysql/mysql_server_resource.go | 521 +++++++++++++----- .../mysql_virtual_network_rule_resource.go | 26 +- .../internal/services/mysql/parse/mysql.go | 33 ++ .../services/mysql/parse/mysql_test.go | 68 +++ .../internal/services/mysql/registration.go | 2 +- .../mysql/tests/mysql_server_resource_test.go | 378 ++++++++----- ...ysql_virtual_network_rule_resource_test.go | 80 +-- .../internal/services/mysql/validate/mysql.go | 30 + .../services/mysql/validate/mysql_test.go | 106 ++++ .../services/postgres/parse/postgres_test.go | 2 +- .../postgres/postgresql_server_resource.go | 8 +- website/docs/r/mysql_server.html.markdown | 60 +- 16 files changed, 960 insertions(+), 378 deletions(-) delete mode 100644 azurerm/helpers/azure/mysql.go create mode 100644 azurerm/internal/services/mysql/parse/mysql.go create mode 100644 azurerm/internal/services/mysql/parse/mysql_test.go create mode 100644 azurerm/internal/services/mysql/validate/mysql.go create mode 100644 azurerm/internal/services/mysql/validate/mysql_test.go diff --git a/azurerm/helpers/azure/mysql.go b/azurerm/helpers/azure/mysql.go deleted file mode 100644 index 7d133e333be3..000000000000 --- a/azurerm/helpers/azure/mysql.go +++ /dev/null @@ -1,15 +0,0 @@ -package azure - -import ( - "fmt" - - "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/validate" -) - -func ValidateMySqlServerName(i interface{}, k string) (_ []string, errors []error) { - if m, regexErrs := validate.RegExHelper(i, k, `^[0-9a-z]([-0-9a-z]{0,61}[0-9a-z])?$`); !m { - return nil, append(regexErrs, fmt.Errorf("%q can contain only lowercase letters, numbers, and '-', but can't start or end with '-' or have more than 63 characters.", k)) - } - - return nil, nil -} diff --git a/azurerm/internal/services/mysql/mysql_configuration_resource.go b/azurerm/internal/services/mysql/mysql_configuration_resource.go index ea1cd6e80ad2..e7c1485a3b00 100644 --- a/azurerm/internal/services/mysql/mysql_configuration_resource.go +++ b/azurerm/internal/services/mysql/mysql_configuration_resource.go @@ -9,6 +9,7 @@ import ( "github.com/hashicorp/terraform-plugin-sdk/helper/schema" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/azure" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/clients" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/services/mysql/validate" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/timeouts" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" ) @@ -43,7 +44,7 @@ func resourceArmMySQLConfiguration() *schema.Resource { Type: schema.TypeString, Required: true, ForceNew: true, - ValidateFunc: azure.ValidateMySqlServerName, + ValidateFunc: validate.MysqlServerServerName, }, "value": { diff --git a/azurerm/internal/services/mysql/mysql_database_resource.go b/azurerm/internal/services/mysql/mysql_database_resource.go index f1f96e5ad6b8..1c7b8398cd55 100644 --- a/azurerm/internal/services/mysql/mysql_database_resource.go +++ b/azurerm/internal/services/mysql/mysql_database_resource.go @@ -12,6 +12,7 @@ import ( "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/features" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/services/mysql/validate" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/timeouts" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" ) @@ -46,7 +47,7 @@ func resourceArmMySqlDatabase() *schema.Resource { Type: schema.TypeString, Required: true, ForceNew: true, - ValidateFunc: azure.ValidateMySqlServerName, + ValidateFunc: validate.MysqlServerServerName, }, "charset": { diff --git a/azurerm/internal/services/mysql/mysql_firewall_rule_resource.go b/azurerm/internal/services/mysql/mysql_firewall_rule_resource.go index cc21cf9c0f48..b2435ced5e46 100644 --- a/azurerm/internal/services/mysql/mysql_firewall_rule_resource.go +++ b/azurerm/internal/services/mysql/mysql_firewall_rule_resource.go @@ -11,6 +11,7 @@ import ( "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/features" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/services/mysql/validate" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/timeouts" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" ) @@ -46,7 +47,7 @@ func resourceArmMySqlFirewallRule() *schema.Resource { Type: schema.TypeString, Required: true, ForceNew: true, - ValidateFunc: azure.ValidateMySqlServerName, + ValidateFunc: validate.MysqlServerServerName, }, "start_ip_address": { diff --git a/azurerm/internal/services/mysql/mysql_server_resource.go b/azurerm/internal/services/mysql/mysql_server_resource.go index 54bab650e0f6..7cc3d8364401 100644 --- a/azurerm/internal/services/mysql/mysql_server_resource.go +++ b/azurerm/internal/services/mysql/mysql_server_resource.go @@ -8,6 +8,8 @@ import ( "time" "github.com/Azure/azure-sdk-for-go/services/mysql/mgmt/2017-12-01/mysql" + "github.com/Azure/go-autorest/autorest/date" + "github.com/hashicorp/go-azure-helpers/response" "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/azure" @@ -15,6 +17,8 @@ import ( "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/features" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/services/mysql/parse" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/services/mysql/validate" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/tags" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/timeouts" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" @@ -28,7 +32,18 @@ func resourceArmMySqlServer() *schema.Resource { Delete: resourceArmMySqlServerDelete, Importer: &schema.ResourceImporter{ - State: schema.ImportStatePassthrough, + State: func(d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) { + if _, err := parse.MysqlServerServerID(d.Id()); err != nil { + return []*schema.ResourceData{d}, err + } + + d.Set("create_mode", "Default") + if v, ok := d.GetOk("create_mode"); ok && v.(string) != "" { + d.Set("create_mode", v) + } + + return []*schema.ResourceData{d}, nil + }, }, Timeouts: &schema.ResourceTimeout{ @@ -43,13 +58,89 @@ func resourceArmMySqlServer() *schema.Resource { Type: schema.TypeString, Required: true, ForceNew: true, - ValidateFunc: azure.ValidateMySqlServerName, + ValidateFunc: validate.MysqlServerServerName, + }, + + "administrator_login": { + Type: schema.TypeString, + Optional: true, + Computed: true, + ForceNew: true, + }, + + "administrator_login_password": { + Type: schema.TypeString, + Optional: true, + Sensitive: true, + }, + + "auto_grow_enabled": { + Type: schema.TypeBool, + Optional: true, + Computed: true, // TODO: remove in 3.0 and default to true + ConflictsWith: []string{"storage_profile.0.auto_grow"}, + }, + + "backup_retention_days": { + Type: schema.TypeInt, + Optional: true, + Computed: true, + ConflictsWith: []string{"storage_profile.0.backup_retention_days"}, + ValidateFunc: validation.IntBetween(7, 35), + }, + + "create_mode": { + Type: schema.TypeString, + Optional: true, + Default: string(mysql.CreateModeDefault), + ValidateFunc: validation.StringInSlice([]string{ + string(mysql.CreateModeDefault), + string(mysql.CreateModeGeoRestore), + string(mysql.CreateModePointInTimeRestore), + string(mysql.CreateModeReplica), + }, false), + }, + + "creation_source_server_id": { + Type: schema.TypeString, + Optional: true, + ValidateFunc: validate.MysqlServerServerID, + }, + + "fqdn": { + Type: schema.TypeString, + Computed: true, + }, + + "geo_redundant_backup_enabled": { + Type: schema.TypeBool, + Optional: true, + Computed: true, + ConflictsWith: []string{"storage_profile.0.geo_redundant_backup"}, + }, + + "infrastructure_encryption_enabled": { + Type: schema.TypeBool, + Optional: true, + ForceNew: true, }, "location": azure.SchemaLocation(), + "public_network_access_enabled": { + Type: schema.TypeBool, + Optional: true, + Default: true, + }, + "resource_group_name": azure.SchemaResourceGroupName(), + "restore_point_in_time": { + Type: schema.TypeString, + Optional: true, + ValidateFunc: validation.IsRFC3339Time, + }, + "sku_name": { Type: schema.TypeString, Required: true, @@ -77,100 +168,129 @@ func resourceArmMySqlServer() *schema.Resource { }, false), }, - "administrator_login": { - Type: schema.TypeString, - Required: true, - ForceNew: true, + "ssl_enforcement": { + Type: schema.TypeString, + Optional: true, + Computed: true, + Deprecated: "this has been moved to the boolean attribute `ssl_enforcement_enabled` and will be removed in version 3.0 of the provider.", + ExactlyOneOf: []string{"ssl_enforcement", "ssl_enforcement_enabled"}, + ValidateFunc: validation.StringInSlice([]string{ + string(mysql.SslEnforcementEnumDisabled), + string(mysql.SslEnforcementEnumEnabled), + }, true), + DiffSuppressFunc: suppress.CaseDifference, }, - "administrator_login_password": { - Type: schema.TypeString, - Required: true, - Sensitive: true, + "ssl_enforcement_enabled": { + Type: schema.TypeBool, + Optional: true, // required in 3.0 + Computed: true, // remove computed in 3.0 + ExactlyOneOf: []string{"ssl_enforcement", "ssl_enforcement_enabled"}, }, - "version": { + "ssl_minimal_tls_version_enforced": { Type: schema.TypeString, - Required: true, + Optional: true, + Default: string(mysql.TLSEnforcementDisabled), ValidateFunc: validation.StringInSlice([]string{ - string(mysql.FiveFullStopSix), - string(mysql.FiveFullStopSeven), - string(mysql.EightFullStopZero), - }, true), - DiffSuppressFunc: suppress.CaseDifference, - ForceNew: true, + string(mysql.TLSEnforcementDisabled), + string(mysql.TLS10), + string(mysql.TLS11), + string(mysql.TLS12), + }, false), + }, + + "storage_mb": { + Type: schema.TypeInt, + Optional: true, + Computed: true, + ExactlyOneOf: []string{"storage_profile.0.storage_mb"}, + ValidateFunc: validation.All( + validation.IntBetween(5120, 4194304), + validation.IntDivisibleBy(1024), + ), }, "storage_profile": { - Type: schema.TypeList, - Required: true, - MaxItems: 1, + Type: schema.TypeList, + Optional: true, + Computed: true, + MaxItems: 1, + Deprecated: "all storage_profile properties have been moved to the top level. This block will be removed in version 3.0 of the provider.", Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ - "storage_mb": { - Type: schema.TypeInt, - Required: true, - ValidateFunc: validation.All( - validation.IntBetween(5120, 4194304), - validation.IntDivisibleBy(1024), - ), + "auto_grow": { + Type: schema.TypeString, + Optional: true, + Computed: true, + ConflictsWith: []string{"auto_grow_enabled"}, + Deprecated: "this has been moved to the top level boolean attribute `auto_grow_enabled` and will be removed in version 3.0 of the provider.", + DiffSuppressFunc: suppress.CaseDifference, + ValidateFunc: validation.StringInSlice([]string{ + string(mysql.StorageAutogrowEnabled), + string(mysql.StorageAutogrowDisabled), + }, false), }, "backup_retention_days": { - Type: schema.TypeInt, - Optional: true, - ValidateFunc: validation.IntBetween(7, 35), + Type: schema.TypeInt, + Optional: true, + Computed: true, + ConflictsWith: []string{"backup_retention_days"}, + Deprecated: "this has been moved to the top level and will be removed in version 3.0 of the provider.", + ValidateFunc: validation.IntBetween(7, 35), }, "geo_redundant_backup": { - Type: schema.TypeString, - Optional: true, + Type: schema.TypeString, + Optional: true, + Computed: true, + ConflictsWith: []string{"geo_redundant_backup_enabled"}, + Deprecated: "this has been moved to the top level boolean attribute `geo_redundant_backup_enabled` and will be removed in version 3.0 of the provider.", + DiffSuppressFunc: suppress.CaseDifference, ValidateFunc: validation.StringInSlice([]string{ "Enabled", "Disabled", }, true), - DiffSuppressFunc: suppress.CaseDifference, }, - "auto_grow": { - Type: schema.TypeString, - Optional: true, - Default: string(mysql.StorageAutogrowEnabled), - ValidateFunc: validation.StringInSlice([]string{ - string(mysql.StorageAutogrowEnabled), - string(mysql.StorageAutogrowDisabled), - }, false), + "storage_mb": { + Type: schema.TypeInt, + Optional: true, + ConflictsWith: []string{"storage_mb"}, + Deprecated: "this has been moved to the top level and will be removed in version 3.0 of the provider.", + ValidateFunc: validation.All( + validation.IntBetween(5120, 4194304), + validation.IntDivisibleBy(1024), + ), }, }, }, }, - "ssl_enforcement": { + "tags": tags.Schema(), + + "version": { Type: schema.TypeString, Required: true, ValidateFunc: validation.StringInSlice([]string{ - string(mysql.SslEnforcementEnumDisabled), - string(mysql.SslEnforcementEnumEnabled), + string(mysql.FiveFullStopSix), + string(mysql.FiveFullStopSeven), + string(mysql.EightFullStopZero), }, true), DiffSuppressFunc: suppress.CaseDifference, + ForceNew: true, }, - - "public_network_access_enabled": { - Type: schema.TypeBool, - Optional: true, - Default: true, - }, - - "fqdn": { - Type: schema.TypeString, - Computed: true, - }, - - "tags": tags.Schema(), }, CustomizeDiff: func(diff *schema.ResourceDiff, v interface{}) error { tier, _ := diff.GetOk("sku_name") - storageMB, _ := diff.GetOk("storage_profile.0.storage_mb") - if strings.HasPrefix(tier.(string), "B_") && storageMB.(int) > 1048576 { + var storageMB int + if v, ok := diff.GetOk("storage_mb"); ok { + storageMB = v.(int) + } else if v, ok := diff.GetOk("storage_profile.0.storage_mb"); ok { + storageMB = v.(int) + } + + if strings.HasPrefix(tier.(string), "B_") && storageMB > 1048576 { return fmt.Errorf("basic pricing tier only supports upto 1,048,576 MB (1TB) of storage") } @@ -190,16 +310,11 @@ func resourceArmMySqlServerCreate(d *schema.ResourceData, meta interface{}) erro location := azure.NormalizeLocation(d.Get("location").(string)) resourceGroup := d.Get("resource_group_name").(string) - publicAccess := mysql.PublicNetworkAccessEnumEnabled - if v := d.Get("public_network_access_enabled").(bool); !v { - publicAccess = mysql.PublicNetworkAccessEnumDisabled - } - if features.ShouldResourcesBeImported() && d.IsNewResource() { existing, err := client.Get(ctx, resourceGroup, name) if err != nil { if !utils.ResponseWasNotFound(existing.Response) { - return fmt.Errorf("Error checking for presence of existing MySQL Server %q (Resource Group %q): %+v", name, resourceGroup, err) + return fmt.Errorf("checking for presence of existing MySQL Server %q (Resource Group %q): %+v", name, resourceGroup, err) } } @@ -208,42 +323,131 @@ func resourceArmMySqlServerCreate(d *schema.ResourceData, meta interface{}) erro } } + mode := mysql.CreateMode(d.Get("create_mode").(string)) + tlsMin := mysql.MinimalTLSVersionEnum(d.Get("ssl_minimal_tls_version_enforced").(string)) + source := d.Get("creation_source_server_id").(string) + version := mysql.ServerVersion(d.Get("version").(string)) + sku, err := expandServerSkuName(d.Get("sku_name").(string)) if err != nil { - return fmt.Errorf("error expanding sku_name for MySQL Server %q (Resource Group %q): %v", name, resourceGroup, err) + return fmt.Errorf("expanding sku_name for MySQL Server %q (Resource Group %q): %v", name, resourceGroup, err) } - properties := mysql.ServerForCreate{ - Location: &location, - Properties: &mysql.ServerPropertiesForDefaultCreate{ - AdministratorLogin: utils.String(d.Get("administrator_login").(string)), - AdministratorLoginPassword: utils.String(d.Get("administrator_login_password").(string)), - Version: mysql.ServerVersion(d.Get("version").(string)), - SslEnforcement: mysql.SslEnforcementEnum(d.Get("ssl_enforcement").(string)), - StorageProfile: expandMySQLStorageProfile(d), - CreateMode: mysql.CreateMode("Default"), + infraEncrypt := mysql.InfrastructureEncryptionEnabled + if v := d.Get("infrastructure_encryption_enabled"); !v.(bool) { + infraEncrypt = mysql.InfrastructureEncryptionDisabled + } + + publicAccess := mysql.PublicNetworkAccessEnumEnabled + if v := d.Get("public_network_access_enabled"); !v.(bool) { + publicAccess = mysql.PublicNetworkAccessEnumDisabled + } + + ssl := mysql.SslEnforcementEnumEnabled + if v, ok := d.GetOk("ssl_enforcement"); ok && strings.EqualFold(v.(string), string(mysql.SslEnforcementEnumDisabled)) { + ssl = mysql.SslEnforcementEnumDisabled + } + if v, ok := d.GetOkExists("ssl_enforcement_enabled"); ok && !v.(bool) { + ssl = mysql.SslEnforcementEnumDisabled + } + + storage := expandMySQLStorageProfile(d) + + var props mysql.BasicServerPropertiesForCreate + switch mode { + case mysql.CreateModeDefault: + admin := d.Get("administrator_login").(string) + pass := d.Get("administrator_login_password").(string) + + if admin == "" { + return fmt.Errorf("`administrator_login` must not be empty when `create_mode` is `default`") + } + if pass == "" { + return fmt.Errorf("`administrator_login_password` must not be empty when `create_mode` is `default`") + } + + if _, ok := d.GetOk("restore_point_in_time"); ok { + return fmt.Errorf("`restore_point_in_time` cannot be set when `create_mode` is `default`") + } + + // check admin + props = &mysql.ServerPropertiesForDefaultCreate{ + AdministratorLogin: &admin, + AdministratorLoginPassword: &pass, + CreateMode: mode, + InfrastructureEncryption: infraEncrypt, PublicNetworkAccess: publicAccess, - }, - Sku: sku, - Tags: tags.Expand(d.Get("tags").(map[string]interface{})), + MinimalTLSVersion: tlsMin, + SslEnforcement: ssl, + StorageProfile: storage, + Version: version, + } + case mysql.CreateModePointInTimeRestore: + v, ok := d.GetOk("restore_point_in_time") + if !ok || v.(string) == "" { + return fmt.Errorf("restore_point_in_time must be set when create_mode is PointInTimeRestore") + } + time, _ := time.Parse(time.RFC3339, v.(string)) // should be validated by the schema + + props = &mysql.ServerPropertiesForRestore{ + CreateMode: mode, + SourceServerID: &source, + RestorePointInTime: &date.Time{ + Time: time, + }, + InfrastructureEncryption: infraEncrypt, + PublicNetworkAccess: publicAccess, + MinimalTLSVersion: tlsMin, + SslEnforcement: ssl, + StorageProfile: storage, + Version: version, + } + case mysql.CreateModeGeoRestore: + props = &mysql.ServerPropertiesForGeoRestore{ + CreateMode: mode, + SourceServerID: &source, + InfrastructureEncryption: infraEncrypt, + PublicNetworkAccess: publicAccess, + MinimalTLSVersion: tlsMin, + SslEnforcement: ssl, + StorageProfile: storage, + Version: version, + } + case mysql.CreateModeReplica: + props = &mysql.ServerPropertiesForReplica{ + CreateMode: mode, + SourceServerID: &source, + InfrastructureEncryption: infraEncrypt, + PublicNetworkAccess: publicAccess, + MinimalTLSVersion: tlsMin, + SslEnforcement: ssl, + Version: version, + } + } + + server := mysql.ServerForCreate{ + Location: &location, + Properties: props, + Sku: sku, + Tags: tags.Expand(d.Get("tags").(map[string]interface{})), } - future, err := client.Create(ctx, resourceGroup, name, properties) + future, err := client.Create(ctx, resourceGroup, name, server) if err != nil { - return fmt.Errorf("Error creating MySQL Server %q (Resource Group %q): %+v", name, resourceGroup, err) + return fmt.Errorf("creating MySQL Server %q (Resource Group %q): %+v", name, resourceGroup, err) } if err = future.WaitForCompletionRef(ctx, client.Client); err != nil { - return fmt.Errorf("Error waiting for creation of MySQL Server %q (Resource Group %q): %+v", name, resourceGroup, err) + return fmt.Errorf("waiting for creation of MySQL Server %q (Resource Group %q): %+v", name, resourceGroup, err) } read, err := client.Get(ctx, resourceGroup, name) if err != nil { - return fmt.Errorf("Error retrieving MySQL Server %q (Resource Group %q): %+v", name, resourceGroup, err) + return fmt.Errorf("retrieving MySQL Server %q (Resource Group %q): %+v", name, resourceGroup, err) } if read.ID == nil { - return fmt.Errorf("Cannot read MySQL Server %q (Resource Group %q) ID", name, resourceGroup) + return fmt.Errorf("cannot read MySQL Server %q (Resource Group %q) ID", name, resourceGroup) } d.SetId(*read.ID) @@ -258,12 +462,14 @@ func resourceArmMySqlServerUpdate(d *schema.ResourceData, meta interface{}) erro log.Printf("[INFO] preparing arguments for AzureRM MySQL Server update.") - name := d.Get("name").(string) - resourceGroup := d.Get("resource_group_name").(string) + id, err := parse.MysqlServerServerID(d.Id()) + if err != nil { + return fmt.Errorf("parsing MySQL Server ID : %v", err) + } sku, err := expandServerSkuName(d.Get("sku_name").(string)) if err != nil { - return fmt.Errorf("error expanding sku_name for MySQL Server %q (Resource Group %q): %v", name, resourceGroup, err) + return fmt.Errorf("expanding sku_name for MySQL Server %q (Resource Group %q): %v", id.Name, id.ResourceGroup, err) } publicAccess := mysql.PublicNetworkAccessEnumEnabled @@ -271,34 +477,44 @@ func resourceArmMySqlServerUpdate(d *schema.ResourceData, meta interface{}) erro publicAccess = mysql.PublicNetworkAccessEnumDisabled } + ssl := mysql.SslEnforcementEnumEnabled + if v := d.Get("ssl_enforcement"); strings.EqualFold(v.(string), string(mysql.SslEnforcementEnumDisabled)) { + ssl = mysql.SslEnforcementEnumDisabled + } + if v := d.Get("ssl_enforcement_enabled").(bool); !v { + ssl = mysql.SslEnforcementEnumDisabled + } + + storageProfile := expandMySQLStorageProfile(d) + properties := mysql.ServerUpdateParameters{ ServerUpdateParametersProperties: &mysql.ServerUpdateParametersProperties{ - StorageProfile: expandMySQLStorageProfile(d), AdministratorLoginPassword: utils.String(d.Get("administrator_login_password").(string)), - Version: mysql.ServerVersion(d.Get("version").(string)), - SslEnforcement: mysql.SslEnforcementEnum(d.Get("ssl_enforcement").(string)), PublicNetworkAccess: publicAccess, + SslEnforcement: ssl, + StorageProfile: storageProfile, + Version: mysql.ServerVersion(d.Get("version").(string)), }, Sku: sku, Tags: tags.Expand(d.Get("tags").(map[string]interface{})), } - future, err := client.Update(ctx, resourceGroup, name, properties) + future, err := client.Update(ctx, id.ResourceGroup, id.Name, properties) if err != nil { - return fmt.Errorf("Error updating MySQL Server %q (Resource Group %q): %+v", name, resourceGroup, err) + return fmt.Errorf("updating MySQL Server %q (Resource Group %q): %+v", id.Name, id.ResourceGroup, err) } if err = future.WaitForCompletionRef(ctx, client.Client); err != nil { - return fmt.Errorf("Error waiting for MySQL Server %q (Resource Group %q) to finish updating: %+v", name, resourceGroup, err) + return fmt.Errorf("waiting for MySQL Server %q (Resource Group %q) to finish updating: %+v", id.Name, id.ResourceGroup, err) } - read, err := client.Get(ctx, resourceGroup, name) + read, err := client.Get(ctx, id.ResourceGroup, id.Name) if err != nil { - return fmt.Errorf("Error retrieving MySQL Server %q (Resource Group %q): %+v", name, resourceGroup, err) + return fmt.Errorf("retrieving MySQL Server %q (Resource Group %q): %+v", id.Name, id.ResourceGroup, err) } if read.ID == nil { - return fmt.Errorf("Cannot read MySQL Server %q (Resource Group %q) ID", name, resourceGroup) + return fmt.Errorf("cannot read MySQL Server %q (Resource Group %q) ID", id.Name, id.ResourceGroup) } d.SetId(*read.ID) @@ -311,25 +527,24 @@ func resourceArmMySqlServerRead(d *schema.ResourceData, meta interface{}) error ctx, cancel := timeouts.ForRead(meta.(*clients.Client).StopContext, d) defer cancel() - id, err := azure.ParseAzureResourceID(d.Id()) + id, err := parse.MysqlServerServerID(d.Id()) if err != nil { - return err + return fmt.Errorf("parsing MySQL Server ID : %v", err) } - resourceGroup := id.ResourceGroup - name := id.Path["servers"] - resp, err := client.Get(ctx, resourceGroup, name) + resp, err := client.Get(ctx, id.ResourceGroup, id.Name) if err != nil { if utils.ResponseWasNotFound(resp.Response) { + log.Printf("[WARN] MySQL Server %q was not found (Resource Group %q)", id.Name, id.ResourceGroup) d.SetId("") return nil } - return fmt.Errorf("Error making Read request on Azure MySQL Server %q (Resource Group %q): %+v", name, resourceGroup, err) + return fmt.Errorf("making Read request on Azure MySQL Server %q (Resource Group %q): %+v", id.Name, id.ResourceGroup, err) } d.Set("name", resp.Name) - d.Set("resource_group_name", resourceGroup) + d.Set("resource_group_name", id.ResourceGroup) if location := resp.Location; location != nil { d.Set("location", azure.NormalizeLocation(*location)) @@ -339,17 +554,29 @@ func resourceArmMySqlServerRead(d *schema.ResourceData, meta interface{}) error d.Set("sku_name", sku.Name) } - d.Set("administrator_login", resp.AdministratorLogin) - d.Set("version", string(resp.Version)) - d.Set("ssl_enforcement", string(resp.SslEnforcement)) - d.Set("public_network_access_enabled", resp.PublicNetworkAccess != mysql.PublicNetworkAccessEnumDisabled) + if props := resp.ServerProperties; props != nil { + d.Set("administrator_login", props.AdministratorLogin) + d.Set("infrastructure_encryption_enabled", props.InfrastructureEncryption == mysql.InfrastructureEncryptionEnabled) + d.Set("public_network_access_enabled", props.PublicNetworkAccess == mysql.PublicNetworkAccessEnumEnabled) + d.Set("ssl_enforcement", string(props.SslEnforcement)) + d.Set("ssl_enforcement_enabled", props.SslEnforcement == mysql.SslEnforcementEnumEnabled) + d.Set("ssl_minimal_tls_version_enforced", props.MinimalTLSVersion) + d.Set("version", string(props.Version)) + + if err := d.Set("storage_profile", flattenMySQLStorageProfile(resp.StorageProfile)); err != nil { + return fmt.Errorf("setting `storage_profile`: %+v", err) + } - if err := d.Set("storage_profile", flattenMySQLStorageProfile(resp.StorageProfile)); err != nil { - return fmt.Errorf("Error setting `storage_profile`: %+v", err) - } + if storage := props.StorageProfile; storage != nil { + d.Set("auto_grow_enabled", storage.StorageAutogrow == mysql.StorageAutogrowEnabled) + d.Set("backup_retention_days", storage.BackupRetentionDays) + d.Set("geo_redundant_backup_enabled", storage.GeoRedundantBackup == mysql.Enabled) + d.Set("storage_mb", storage.StorageMB) + } - // Computed - d.Set("fqdn", resp.FullyQualifiedDomainName) + // Computed + d.Set("fqdn", props.FullyQualifiedDomainName) + } return tags.FlattenAndSet(d, resp.Tags) } @@ -359,20 +586,24 @@ func resourceArmMySqlServerDelete(d *schema.ResourceData, meta interface{}) erro ctx, cancel := timeouts.ForDelete(meta.(*clients.Client).StopContext, d) defer cancel() - id, err := azure.ParseAzureResourceID(d.Id()) + id, err := parse.MysqlServerServerID(d.Id()) if err != nil { - return err + return fmt.Errorf("parsing MySQL Server ID : %v", err) } - resourceGroup := id.ResourceGroup - name := id.Path["servers"] - future, err := client.Delete(ctx, resourceGroup, name) + future, err := client.Delete(ctx, id.ResourceGroup, id.Name) if err != nil { - return fmt.Errorf("Error deleting MySQL Server %q (Resource Group %q): %+v", name, resourceGroup, err) + if response.WasNotFound(future.Response()) { + return nil + } + return fmt.Errorf("deleting MySQL Server %q (Resource Group %q): %+v", id.Name, id.ResourceGroup, err) } if err = future.WaitForCompletionRef(ctx, client.Client); err != nil { - return fmt.Errorf("Error waiting for deletion of MySQL Server %q (Resource Group %q): %+v", name, resourceGroup, err) + if response.WasNotFound(future.Response()) { + return nil + } + return fmt.Errorf("waiting for deletion of MySQL Server %q (Resource Group %q): %+v", id.Name, id.ResourceGroup, err) } return nil @@ -410,36 +641,58 @@ func expandServerSkuName(skuName string) (*mysql.Sku, error) { } func expandMySQLStorageProfile(d *schema.ResourceData) *mysql.StorageProfile { - storageprofiles := d.Get("storage_profile").([]interface{}) - storageprofile := storageprofiles[0].(map[string]interface{}) + storage := mysql.StorageProfile{} + if v, ok := d.GetOk("storage_profile"); ok { + storageprofile := v.([]interface{})[0].(map[string]interface{}) + + storage.BackupRetentionDays = utils.Int32(int32(storageprofile["backup_retention_days"].(int))) + storage.GeoRedundantBackup = mysql.GeoRedundantBackup(storageprofile["geo_redundant_backup"].(string)) + storage.StorageAutogrow = mysql.StorageAutogrow(storageprofile["auto_grow"].(string)) + storage.StorageMB = utils.Int32(int32(storageprofile["storage_mb"].(int))) + } + + // now override whatever we may have from the block with the top level properties + if v, ok := d.GetOk("auto_grow_enabled"); ok { + storage.StorageAutogrow = mysql.StorageAutogrowDisabled + if v.(bool) { + storage.StorageAutogrow = mysql.StorageAutogrowEnabled + } + } + + if v, ok := d.GetOk("backup_retention_days"); ok { + storage.BackupRetentionDays = utils.Int32(int32(v.(int))) + } - backupRetentionDays := storageprofile["backup_retention_days"].(int) - geoRedundantBackup := storageprofile["geo_redundant_backup"].(string) - storageMB := storageprofile["storage_mb"].(int) - autoGrow := storageprofile["auto_grow"].(string) + if v, ok := d.GetOk("geo_redundant_backup_enabled"); ok { + storage.GeoRedundantBackup = mysql.Disabled + if v.(bool) { + storage.GeoRedundantBackup = mysql.Enabled + } + } - return &mysql.StorageProfile{ - BackupRetentionDays: utils.Int32(int32(backupRetentionDays)), - GeoRedundantBackup: mysql.GeoRedundantBackup(geoRedundantBackup), - StorageMB: utils.Int32(int32(storageMB)), - StorageAutogrow: mysql.StorageAutogrow(autoGrow), + if v, ok := d.GetOk("storage_mb"); ok { + storage.StorageMB = utils.Int32(int32(v.(int))) } + + return &storage } func flattenMySQLStorageProfile(resp *mysql.StorageProfile) []interface{} { values := map[string]interface{}{} - if storageMB := resp.StorageMB; storageMB != nil { - values["storage_mb"] = *storageMB - } + values["auto_grow"] = string(resp.StorageAutogrow) + values["backup_retention_days"] = nil if backupRetentionDays := resp.BackupRetentionDays; backupRetentionDays != nil { values["backup_retention_days"] = *backupRetentionDays } values["geo_redundant_backup"] = string(resp.GeoRedundantBackup) - values["auto_grow"] = string(resp.StorageAutogrow) + values["storage_mb"] = nil + if storageMB := resp.StorageMB; storageMB != nil { + values["storage_mb"] = *storageMB + } return []interface{}{values} } diff --git a/azurerm/internal/services/mysql/mysql_virtual_network_rule_resource.go b/azurerm/internal/services/mysql/mysql_virtual_network_rule_resource.go index 21a520afca60..7399af29bc5e 100644 --- a/azurerm/internal/services/mysql/mysql_virtual_network_rule_resource.go +++ b/azurerm/internal/services/mysql/mysql_virtual_network_rule_resource.go @@ -10,22 +10,22 @@ import ( "github.com/hashicorp/go-azure-helpers/response" "github.com/hashicorp/terraform-plugin-sdk/helper/resource" "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/azure" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" - "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/validate" + azValidate "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/validate" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/clients" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/features" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/services/mysql/validate" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/timeouts" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" ) -func resourceArmMySqlVirtualNetworkRule() *schema.Resource { +func resourceArmMySSQLVirtualNetworkRule() *schema.Resource { return &schema.Resource{ - Create: resourceArmMySqlVirtualNetworkRuleCreateUpdate, - Read: resourceArmMySqlVirtualNetworkRuleRead, - Update: resourceArmMySqlVirtualNetworkRuleCreateUpdate, - Delete: resourceArmMySqlVirtualNetworkRuleDelete, + Create: resourceArmMySSQLVirtualNetworkRuleCreateUpdate, + Read: resourceArmMySSQLVirtualNetworkRuleRead, + Update: resourceArmMySSQLVirtualNetworkRuleCreateUpdate, + Delete: resourceArmMySSQLVirtualNetworkRuleDelete, Importer: &schema.ResourceImporter{ State: schema.ImportStatePassthrough, }, @@ -42,7 +42,7 @@ func resourceArmMySqlVirtualNetworkRule() *schema.Resource { Type: schema.TypeString, Required: true, ForceNew: true, - ValidateFunc: validate.VirtualNetworkRuleName, + ValidateFunc: azValidate.VirtualNetworkRuleName, }, "resource_group_name": azure.SchemaResourceGroupName(), @@ -51,7 +51,7 @@ func resourceArmMySqlVirtualNetworkRule() *schema.Resource { Type: schema.TypeString, Required: true, ForceNew: true, - ValidateFunc: validation.StringIsNotEmpty, + ValidateFunc: validate.MysqlServerServerName, }, "subnet_id": { @@ -63,7 +63,7 @@ func resourceArmMySqlVirtualNetworkRule() *schema.Resource { } } -func resourceArmMySqlVirtualNetworkRuleCreateUpdate(d *schema.ResourceData, meta interface{}) error { +func resourceArmMySSQLVirtualNetworkRuleCreateUpdate(d *schema.ResourceData, meta interface{}) error { client := meta.(*clients.Client).MySQL.VirtualNetworkRulesClient ctx, cancel := timeouts.ForCreateUpdate(meta.(*clients.Client).StopContext, d) defer cancel() @@ -123,10 +123,10 @@ func resourceArmMySqlVirtualNetworkRuleCreateUpdate(d *schema.ResourceData, meta d.SetId(*resp.ID) - return resourceArmMySqlVirtualNetworkRuleRead(d, meta) + return resourceArmMySSQLVirtualNetworkRuleRead(d, meta) } -func resourceArmMySqlVirtualNetworkRuleRead(d *schema.ResourceData, meta interface{}) error { +func resourceArmMySSQLVirtualNetworkRuleRead(d *schema.ResourceData, meta interface{}) error { client := meta.(*clients.Client).MySQL.VirtualNetworkRulesClient ctx, cancel := timeouts.ForRead(meta.(*clients.Client).StopContext, d) defer cancel() @@ -162,7 +162,7 @@ func resourceArmMySqlVirtualNetworkRuleRead(d *schema.ResourceData, meta interfa return nil } -func resourceArmMySqlVirtualNetworkRuleDelete(d *schema.ResourceData, meta interface{}) error { +func resourceArmMySSQLVirtualNetworkRuleDelete(d *schema.ResourceData, meta interface{}) error { client := meta.(*clients.Client).MySQL.VirtualNetworkRulesClient ctx, cancel := timeouts.ForDelete(meta.(*clients.Client).StopContext, d) defer cancel() diff --git a/azurerm/internal/services/mysql/parse/mysql.go b/azurerm/internal/services/mysql/parse/mysql.go new file mode 100644 index 000000000000..a8a370133682 --- /dev/null +++ b/azurerm/internal/services/mysql/parse/mysql.go @@ -0,0 +1,33 @@ +package parse + +import ( + "fmt" + + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/azure" +) + +type MysqlServerServerId struct { + ResourceGroup string + Name string +} + +func MysqlServerServerID(input string) (*MysqlServerServerId, error) { + id, err := azure.ParseAzureResourceID(input) + if err != nil { + return nil, fmt.Errorf("[ERROR] Unable to parse MySQL Server ID %q: %+v", input, err) + } + + server := MysqlServerServerId{ + ResourceGroup: id.ResourceGroup, + } + + if server.Name, err = id.PopSegment("servers"); err != nil { + return nil, err + } + + if err := id.ValidateNoEmptySegments(input); err != nil { + return nil, err + } + + return &server, nil +} diff --git a/azurerm/internal/services/mysql/parse/mysql_test.go b/azurerm/internal/services/mysql/parse/mysql_test.go new file mode 100644 index 000000000000..c3ccf38972ea --- /dev/null +++ b/azurerm/internal/services/mysql/parse/mysql_test.go @@ -0,0 +1,68 @@ +package parse + +import ( + "testing" +) + +func TestValidateMysqlServerServerID(t *testing.T) { + testData := []struct { + Name string + Input string + Expected *MysqlServerServerId + }{ + { + Name: "Empty resource ID", + Input: "", + Expected: nil, + }, + { + Name: "No resourceGroups segment", + Input: "/subscriptions/00000000-0000-0000-0000-000000000000", + Expected: nil, + }, + { + Name: "No resource group name", + Input: "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/", + Expected: nil, + }, + { + Name: "Resource group", + Input: "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/test-rg/", + Expected: nil, + }, + { + Name: "Missing server name", + Input: "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/test-rg/providers/Microsoft.DBforMySQL/servers/", + Expected: nil, + }, + { + Name: "Valid", + Input: "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/test-rg/providers/Microsoft.DBforMySQL/servers/test-mysql", + Expected: &MysqlServerServerId{ + Name: "test-mysql", + ResourceGroup: "test-rg", + }, + }, + } + + for _, v := range testData { + t.Logf("[DEBUG] Testing %q", v.Name) + + actual, err := MysqlServerServerID(v.Input) + if err != nil { + if v.Expected == nil { + continue + } + + t.Fatalf("Expected a value but got an error: %s", err) + } + + if actual.Name != v.Expected.Name { + t.Fatalf("Expected %q but got %q for Name", v.Expected.Name, actual.Name) + } + + if actual.ResourceGroup != v.Expected.ResourceGroup { + t.Fatalf("Expected %q but got %q for Resource Group", v.Expected.ResourceGroup, actual.ResourceGroup) + } + } +} diff --git a/azurerm/internal/services/mysql/registration.go b/azurerm/internal/services/mysql/registration.go index cf8e58ead01b..01c7dc617d45 100644 --- a/azurerm/internal/services/mysql/registration.go +++ b/azurerm/internal/services/mysql/registration.go @@ -30,5 +30,5 @@ func (r Registration) SupportedResources() map[string]*schema.Resource { "azurerm_mysql_database": resourceArmMySqlDatabase(), "azurerm_mysql_firewall_rule": resourceArmMySqlFirewallRule(), "azurerm_mysql_server": resourceArmMySqlServer(), - "azurerm_mysql_virtual_network_rule": resourceArmMySqlVirtualNetworkRule()} + "azurerm_mysql_virtual_network_rule": resourceArmMySSQLVirtualNetworkRule()} } diff --git a/azurerm/internal/services/mysql/tests/mysql_server_resource_test.go b/azurerm/internal/services/mysql/tests/mysql_server_resource_test.go index 833e6aa6ea71..645cd296f6a7 100644 --- a/azurerm/internal/services/mysql/tests/mysql_server_resource_test.go +++ b/azurerm/internal/services/mysql/tests/mysql_server_resource_test.go @@ -3,6 +3,7 @@ package tests import ( "fmt" "testing" + "time" "github.com/hashicorp/terraform-plugin-sdk/helper/resource" "github.com/hashicorp/terraform-plugin-sdk/terraform" @@ -20,7 +21,7 @@ func TestAccAzureRMMySQLServer_basicFiveSix(t *testing.T) { CheckDestroy: testCheckAzureRMMySQLServerDestroy, Steps: []resource.TestStep{ { - Config: testAccAzureRMMySQLServer_basicFiveSix(data), + Config: testAccAzureRMMySQLServer_basic(data, "5.6"), Check: resource.ComposeTestCheckFunc( testCheckAzureRMMySQLServerExists(data.ResourceName), ), @@ -30,7 +31,7 @@ func TestAccAzureRMMySQLServer_basicFiveSix(t *testing.T) { }) } -func TestAccAzureRMMySQLServer_disablePublicNetworkAccess(t *testing.T) { +func TestAccAzureRMMySQLServer_basicFiveSixDeprecated(t *testing.T) { // remove in v3.0 data := acceptance.BuildTestData(t, "azurerm_mysql_server", "test") resource.ParallelTest(t, resource.TestCase{ @@ -39,7 +40,7 @@ func TestAccAzureRMMySQLServer_disablePublicNetworkAccess(t *testing.T) { CheckDestroy: testCheckAzureRMMySQLServerDestroy, Steps: []resource.TestStep{ { - Config: testAccAzureRMMySQLServer_disablePublicNetworkAccess(data), + Config: testAccAzureRMMySQLServer_basicDeprecated(data, "5.6"), Check: resource.ComposeTestCheckFunc( testCheckAzureRMMySQLServerExists(data.ResourceName), ), @@ -49,7 +50,7 @@ func TestAccAzureRMMySQLServer_disablePublicNetworkAccess(t *testing.T) { }) } -func TestAccAzureRMMySQLServer_requiresImport(t *testing.T) { +func TestAccAzureRMMySQLServer_basicFiveSeven(t *testing.T) { data := acceptance.BuildTestData(t, "azurerm_mysql_server", "test") resource.ParallelTest(t, resource.TestCase{ @@ -58,21 +59,38 @@ func TestAccAzureRMMySQLServer_requiresImport(t *testing.T) { CheckDestroy: testCheckAzureRMMySQLServerDestroy, Steps: []resource.TestStep{ { - Config: testAccAzureRMMySQLServer_basicFiveSevenUpdated(data), + Config: testAccAzureRMMySQLServer_basic(data, "5.7"), Check: resource.ComposeTestCheckFunc( testCheckAzureRMMySQLServerExists(data.ResourceName), ), }, + data.ImportStep("administrator_login_password"), // not returned as sensitive + }, + }) +} + +func TestAccAzureRMMySQLServer_basicEightZero(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_mysql_server", "test") + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acceptance.PreCheck(t) }, + Providers: acceptance.SupportedProviders, + CheckDestroy: testCheckAzureRMMySQLServerDestroy, + Steps: []resource.TestStep{ { - Config: testAccAzureRMMySQLServer_requiresImport(data), - ExpectError: acceptance.RequiresImportError("azurerm_mysql_server"), + Config: testAccAzureRMMySQLServer_basic(data, "8.0"), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMMySQLServerExists(data.ResourceName), + ), }, + data.ImportStep("administrator_login_password"), // not returned as sensitive }, }) } -func TestAccAzureRMMySQLServer_basicFiveSeven(t *testing.T) { +func TestAccAzureRMMySQLServer_autogrowOnly(t *testing.T) { data := acceptance.BuildTestData(t, "azurerm_mysql_server", "test") + mysqlVersion := "5.7" resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { acceptance.PreCheck(t) }, @@ -80,7 +98,14 @@ func TestAccAzureRMMySQLServer_basicFiveSeven(t *testing.T) { CheckDestroy: testCheckAzureRMMySQLServerDestroy, Steps: []resource.TestStep{ { - Config: testAccAzureRMMySQLServer_basicFiveSeven(data), + Config: testAccAzureRMMySQLServer_autogrow(data, mysqlVersion), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMMySQLServerExists(data.ResourceName), + ), + }, + data.ImportStep("administrator_login_password"), // not returned as sensitive + { + Config: testAccAzureRMMySQLServer_basic(data, mysqlVersion), Check: resource.ComposeTestCheckFunc( testCheckAzureRMMySQLServerExists(data.ResourceName), ), @@ -90,8 +115,47 @@ func TestAccAzureRMMySQLServer_basicFiveSeven(t *testing.T) { }) } -func TestAccAzureRMMySQLServer_basicEightZero(t *testing.T) { +func TestAccAzureRMMySQLServer_requiresImport(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_mysql_server", "test") + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acceptance.PreCheck(t) }, + Providers: acceptance.SupportedProviders, + CheckDestroy: testCheckAzureRMMySQLServerDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAzureRMMySQLServer_basic(data, "5.7"), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMMySQLServerExists(data.ResourceName), + ), + }, + data.RequiresImportErrorStep(testAccAzureRMMySQLServer_requiresImport), + }, + }) +} + +func TestAccAzureRMMySQLServer_complete(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_mysql_server", "test") + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acceptance.PreCheck(t) }, + Providers: acceptance.SupportedProviders, + CheckDestroy: testCheckAzureRMMySQLServerDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAzureRMMySQLServer_complete(data, "8.0"), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMMySQLServerExists(data.ResourceName), + ), + }, + data.ImportStep("administrator_login_password"), // not returned as sensitive + }, + }) +} + +func TestAccAzureRMMySQLServer_update(t *testing.T) { data := acceptance.BuildTestData(t, "azurerm_mysql_server", "test") + mysqlVersion := "8.0" resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { acceptance.PreCheck(t) }, @@ -99,7 +163,21 @@ func TestAccAzureRMMySQLServer_basicEightZero(t *testing.T) { CheckDestroy: testCheckAzureRMMySQLServerDestroy, Steps: []resource.TestStep{ { - Config: testAccAzureRMMySQLServer_basicEightZero(data), + Config: testAccAzureRMMySQLServer_basic(data, mysqlVersion), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMMySQLServerExists(data.ResourceName), + ), + }, + data.ImportStep("administrator_login_password"), // not returned as sensitive + { + Config: testAccAzureRMMySQLServer_complete(data, mysqlVersion), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMMySQLServerExists(data.ResourceName), + ), + }, + data.ImportStep("administrator_login_password"), // not returned as sensitive + { + Config: testAccAzureRMMySQLServer_basic(data, mysqlVersion), Check: resource.ComposeTestCheckFunc( testCheckAzureRMMySQLServerExists(data.ResourceName), ), @@ -109,8 +187,9 @@ func TestAccAzureRMMySQLServer_basicEightZero(t *testing.T) { }) } -func TestAccAzureRMMySqlServer_memoryOptimized(t *testing.T) { +func TestAccAzureRMMySQLServer_completeDeprecatedMigrate(t *testing.T) { // remove in v3.0 data := acceptance.BuildTestData(t, "azurerm_mysql_server", "test") + mysqlVersion := "5.6" resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { acceptance.PreCheck(t) }, @@ -118,17 +197,26 @@ func TestAccAzureRMMySqlServer_memoryOptimized(t *testing.T) { CheckDestroy: testCheckAzureRMMySQLServerDestroy, Steps: []resource.TestStep{ { - Config: testAccAzureRMMySQLServer_memoryOptimizedGeoRedundant(data), + Config: testAccAzureRMMySQLServer_completeDeprecated(data, mysqlVersion), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMMySQLServerExists(data.ResourceName), + ), + }, + data.ImportStep("administrator_login_password"), // not returned as sensitive + { + Config: testAccAzureRMMySQLServer_complete(data, mysqlVersion), Check: resource.ComposeTestCheckFunc( testCheckAzureRMMySQLServerExists(data.ResourceName), ), - }, data.ImportStep("administrator_login_password"), // not returned as sensitive + }, + data.ImportStep("administrator_login_password"), // not returned as sensitive }, }) } -func TestAccAzureRMMySQLServer_basicFiveSevenUpdated(t *testing.T) { +func TestAccAzureRMMySQLServer_updateDeprecated(t *testing.T) { // remove in v3.0 data := acceptance.BuildTestData(t, "azurerm_mysql_server", "test") + mysqlVersion := "5.6" resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { acceptance.PreCheck(t) }, @@ -136,23 +224,23 @@ func TestAccAzureRMMySQLServer_basicFiveSevenUpdated(t *testing.T) { CheckDestroy: testCheckAzureRMMySQLServerDestroy, Steps: []resource.TestStep{ { - Config: testAccAzureRMMySQLServer_basicFiveSeven(data), + Config: testAccAzureRMMySQLServer_basicDeprecated(data, mysqlVersion), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMMySQLServerExists(data.ResourceName), + ), + }, + data.ImportStep("administrator_login_password"), // not returned as sensitive + { + Config: testAccAzureRMMySQLServer_completeDeprecated(data, mysqlVersion), Check: resource.ComposeTestCheckFunc( testCheckAzureRMMySQLServerExists(data.ResourceName), - resource.TestCheckResourceAttr(data.ResourceName, "sku_name", "GP_Gen5_2"), - resource.TestCheckResourceAttr(data.ResourceName, "version", "5.7"), - resource.TestCheckResourceAttr(data.ResourceName, "storage_profile.0.storage_mb", "51200"), - resource.TestCheckResourceAttr(data.ResourceName, "administrator_login", "acctestun"), ), }, + data.ImportStep("administrator_login_password"), // not returned as sensitive { - Config: testAccAzureRMMySQLServer_basicFiveSevenUpdated(data), + Config: testAccAzureRMMySQLServer_basicDeprecated(data, mysqlVersion), Check: resource.ComposeTestCheckFunc( testCheckAzureRMMySQLServerExists(data.ResourceName), - resource.TestCheckResourceAttr(data.ResourceName, "sku_name", "GP_Gen5_4"), - resource.TestCheckResourceAttr(data.ResourceName, "version", "5.7"), - resource.TestCheckResourceAttr(data.ResourceName, "storage_profile.0.storage_mb", "640000"), - resource.TestCheckResourceAttr(data.ResourceName, "administrator_login", "acctestun"), ), }, data.ImportStep("administrator_login_password"), // not returned as sensitive @@ -169,29 +257,26 @@ func TestAccAzureRMMySQLServer_updateSKU(t *testing.T) { CheckDestroy: testCheckAzureRMMySQLServerDestroy, Steps: []resource.TestStep{ { - Config: testAccAzureRMMySQLServer_basicEightZero(data), + Config: testAccAzureRMMySQLServer_sku(data, "GP_Gen5_2"), Check: resource.ComposeTestCheckFunc( testCheckAzureRMMySQLServerExists(data.ResourceName), - resource.TestCheckResourceAttr(data.ResourceName, "sku_name", "GP_Gen5_2"), - resource.TestCheckResourceAttr(data.ResourceName, "storage_profile.0.storage_mb", "51200"), - resource.TestCheckResourceAttr(data.ResourceName, "administrator_login", "acctestun"), ), }, + data.ImportStep("administrator_login_password"), // not returned as sensitive { - Config: testAccAzureRMMySQLServer_basicFiveSevenUpdated(data), + Config: testAccAzureRMMySQLServer_sku(data, "MO_Gen5_16"), Check: resource.ComposeTestCheckFunc( testCheckAzureRMMySQLServerExists(data.ResourceName), - resource.TestCheckResourceAttr(data.ResourceName, "sku_name", "GP_Gen5_4"), - resource.TestCheckResourceAttr(data.ResourceName, "storage_profile.0.storage_mb", "4194304"), - resource.TestCheckResourceAttr(data.ResourceName, "administrator_login", "acctestun"), ), }, + data.ImportStep("administrator_login_password"), // not returned as sensitive }, }) } -func TestAccAzureRMMySQLServer_storageAutogrow(t *testing.T) { +func TestAccAzureRMMySQLServer_createReplica(t *testing.T) { data := acceptance.BuildTestData(t, "azurerm_mysql_server", "test") + mysqlVersion := "8.0" resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { acceptance.PreCheck(t) }, @@ -199,24 +284,53 @@ func TestAccAzureRMMySQLServer_storageAutogrow(t *testing.T) { CheckDestroy: testCheckAzureRMMySQLServerDestroy, Steps: []resource.TestStep{ { - Config: testAccAzureRMMySQLServer_basicFiveSeven(data), + Config: testAccAzureRMMySQLServer_basic(data, mysqlVersion), Check: resource.ComposeTestCheckFunc( testCheckAzureRMMySQLServerExists(data.ResourceName), - resource.TestCheckResourceAttr(data.ResourceName, "storage_profile.0.auto_grow", "Enabled"), ), }, + data.ImportStep("administrator_login_password"), { - Config: testAccAzureRMMySQLServer_autogrow(data), + Config: testAccAzureRMMySQLServer_createReplica(data, mysqlVersion), Check: resource.ComposeTestCheckFunc( testCheckAzureRMMySQLServerExists(data.ResourceName), - resource.TestCheckResourceAttr(data.ResourceName, "storage_profile.0.auto_grow", "Disabled"), + testCheckAzureRMMySQLServerExists("azurerm_mysql_server.replica"), ), }, + data.ImportStep("administrator_login_password"), }, }) } -// +func TestAccAzureRMMySQLServer_createPointInTimeRestore(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_mysql_server", "test") + restoreTime := time.Now().Add(11 * time.Minute) + mysqlVersion := "8.0" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acceptance.PreCheck(t) }, + Providers: acceptance.SupportedProviders, + CheckDestroy: testCheckAzureRMMySQLServerDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAzureRMMySQLServer_basic(data, mysqlVersion), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMMySQLServerExists(data.ResourceName), + ), + }, + data.ImportStep("administrator_login_password"), + { + PreConfig: func() { time.Sleep(restoreTime.Sub(time.Now().Add(-7 * time.Minute))) }, + Config: testAccAzureRMMySQLServer_createPointInTimeRestore(data, mysqlVersion, restoreTime.Format(time.RFC3339)), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMMySQLServerExists(data.ResourceName), + testCheckAzureRMMySQLServerExists("azurerm_mysql_server.restore"), + ), + }, + data.ImportStep("administrator_login_password"), + }, + }) +} func testCheckAzureRMMySQLServerExists(resourceName string) resource.TestCheckFunc { return func(s *terraform.State) error { @@ -273,7 +387,7 @@ func testCheckAzureRMMySQLServerDestroy(s *terraform.State) error { return nil } -func testAccAzureRMMySQLServer_basicFiveSix(data acceptance.TestData) string { +func testAccAzureRMMySQLServer_basic(data acceptance.TestData, version string) string { return fmt.Sprintf(` provider "azurerm" { features {} @@ -285,27 +399,20 @@ resource "azurerm_resource_group" "test" { } resource "azurerm_mysql_server" "test" { - name = "acctestmysqlsvr-%d" - location = azurerm_resource_group.test.location - resource_group_name = azurerm_resource_group.test.name - - sku_name = "GP_Gen5_2" - - storage_profile { - storage_mb = 51200 - backup_retention_days = 7 - geo_redundant_backup = "Disabled" - } - + name = "acctestmysqlsvr-%d" + location = azurerm_resource_group.test.location + resource_group_name = azurerm_resource_group.test.name + sku_name = "GP_Gen5_2" administrator_login = "acctestun" administrator_login_password = "H@Sh1CoR3!" - version = "5.6" - ssl_enforcement = "Enabled" + ssl_enforcement_enabled = true + storage_mb = 51200 + version = "%s" } -`, data.RandomInteger, data.Locations.Primary, data.RandomInteger) +`, data.RandomInteger, data.Locations.Primary, data.RandomInteger, version) } -func testAccAzureRMMySQLServer_disablePublicNetworkAccess(data acceptance.TestData) string { +func testAccAzureRMMySQLServer_basicDeprecated(data acceptance.TestData, version string) string { // remove in v3.0 return fmt.Sprintf(` provider "azurerm" { features {} @@ -324,21 +431,18 @@ resource "azurerm_mysql_server" "test" { sku_name = "GP_Gen5_2" storage_profile { - storage_mb = 51200 - backup_retention_days = 7 - geo_redundant_backup = "Disabled" + storage_mb = 51200 } - administrator_login = "acctestun" - administrator_login_password = "H@Sh1CoR3!" - version = "5.6" - ssl_enforcement = "Enabled" - public_network_access_enabled = false + administrator_login = "acctestun" + administrator_login_password = "H@Sh1CoR3!" + version = "%s" + ssl_enforcement = "Enabled" } -`, data.RandomInteger, data.Locations.Primary, data.RandomInteger) +`, data.RandomInteger, data.Locations.Primary, data.RandomInteger, version) } -func testAccAzureRMMySQLServer_basicFiveSeven(data acceptance.TestData) string { +func testAccAzureRMMySQLServer_complete(data acceptance.TestData, version string) string { return fmt.Sprintf(` provider "azurerm" { features {} @@ -350,27 +454,24 @@ resource "azurerm_resource_group" "test" { } resource "azurerm_mysql_server" "test" { - name = "acctestmysqlsvr-%d" - location = azurerm_resource_group.test.location - resource_group_name = azurerm_resource_group.test.name - - sku_name = "GP_Gen5_2" - - storage_profile { - storage_mb = 51200 - backup_retention_days = 7 - geo_redundant_backup = "Disabled" - } - + name = "acctestmysqlsvr-%d" + location = azurerm_resource_group.test.location + resource_group_name = azurerm_resource_group.test.name + sku_name = "GP_Gen5_2" administrator_login = "acctestun" administrator_login_password = "H@Sh1CoR3!" - version = "5.7" - ssl_enforcement = "Enabled" + auto_grow_enabled = true + backup_retention_days = 7 + create_mode = "Default" + geo_redundant_backup_enabled = false + ssl_enforcement_enabled = true + storage_mb = 51200 + version = "%s" } -`, data.RandomInteger, data.Locations.Primary, data.RandomInteger) +`, data.RandomInteger, data.Locations.Primary, data.RandomInteger, version) } -func testAccAzureRMMySQLServer_basicEightZero(data acceptance.TestData) string { +func testAccAzureRMMySQLServer_completeDeprecated(data acceptance.TestData, version string) string { // remove in v3.0 return fmt.Sprintf(` provider "azurerm" { features {} @@ -392,14 +493,15 @@ resource "azurerm_mysql_server" "test" { storage_mb = 51200 backup_retention_days = 7 geo_redundant_backup = "Disabled" + auto_grow = "Enabled" } administrator_login = "acctestun" administrator_login_password = "H@Sh1CoR3!" - version = "8.0" + version = "%s" ssl_enforcement = "Enabled" } -`, data.RandomInteger, data.Locations.Primary, data.RandomInteger) +`, data.RandomInteger, data.Locations.Primary, data.RandomInteger, version) } func testAccAzureRMMySQLServer_requiresImport(data acceptance.TestData) string { @@ -410,24 +512,20 @@ resource "azurerm_mysql_server" "import" { name = azurerm_mysql_server.test.name location = azurerm_mysql_server.test.location resource_group_name = azurerm_mysql_server.test.resource_group_name - - sku_name = "GP_Gen5_2" - - storage_profile { - storage_mb = 51200 - backup_retention_days = 7 - geo_redundant_backup = "Disabled" - } + sku_name = "GP_Gen5_2" + version = "5.7" administrator_login = "acctestun" administrator_login_password = "H@Sh1CoR3!" - version = "5.7" - ssl_enforcement = "Enabled" + backup_retention_days = 7 + geo_redundant_backup_enabled = false + ssl_enforcement_enabled = true + storage_mb = 51200 } -`, testAccAzureRMMySQLServer_basicFiveSevenUpdated(data)) +`, testAccAzureRMMySQLServer_basic(data, "5.7")) } -func testAccAzureRMMySQLServer_basicFiveSevenUpdated(data acceptance.TestData) string { +func testAccAzureRMMySQLServer_sku(data acceptance.TestData, sku string) string { return fmt.Sprintf(` provider "azurerm" { features {} @@ -442,24 +540,18 @@ resource "azurerm_mysql_server" "test" { name = "acctestmysqlsvr-%d" location = azurerm_resource_group.test.location resource_group_name = azurerm_resource_group.test.name + sku_name = "%s" + version = "5.7" - sku_name = "GP_Gen5_4" - - storage_profile { - storage_mb = 640000 - backup_retention_days = 7 - geo_redundant_backup = "Disabled" - } - + storage_mb = 4194304 administrator_login = "acctestun" administrator_login_password = "H@Sh1CoR3!" - version = "5.7" - ssl_enforcement = "Enabled" + ssl_enforcement_enabled = true } -`, data.RandomInteger, data.Locations.Primary, data.RandomInteger) +`, data.RandomInteger, data.Locations.Primary, data.RandomInteger, sku) } -func testAccAzureRMMySQLServer_memoryOptimizedGeoRedundant(data acceptance.TestData) string { +func testAccAzureRMMySQLServer_autogrow(data acceptance.TestData, version string) string { return fmt.Sprintf(` provider "azurerm" { features {} @@ -474,52 +566,54 @@ resource "azurerm_mysql_server" "test" { name = "acctestmysqlsvr-%d" location = azurerm_resource_group.test.location resource_group_name = azurerm_resource_group.test.name - - sku_name = "MO_Gen5_16" - - storage_profile { - storage_mb = 4194304 - backup_retention_days = 7 - geo_redundant_backup = "Enabled" - } + sku_name = "GP_Gen5_2" + version = "%s" administrator_login = "acctestun" administrator_login_password = "H@Sh1CoR3!" - version = "5.7" - ssl_enforcement = "Enabled" + auto_grow_enabled = true + backup_retention_days = 7 + geo_redundant_backup_enabled = false + ssl_enforcement_enabled = true + storage_mb = 51200 } -`, data.RandomInteger, data.Locations.Primary, data.RandomInteger) +`, data.RandomInteger, data.Locations.Primary, data.RandomInteger, version) } -func testAccAzureRMMySQLServer_autogrow(data acceptance.TestData) string { +func testAccAzureRMMySQLServer_createReplica(data acceptance.TestData, version string) string { return fmt.Sprintf(` -provider "azurerm" { - features {} -} - -resource "azurerm_resource_group" "test" { - name = "acctestRG-%d" - location = "%s" -} +%s -resource "azurerm_mysql_server" "test" { - name = "acctestmysqlsvr-%d" +resource "azurerm_mysql_server" "replica" { + name = "acctestmysqlsvr-%d-replica" location = azurerm_resource_group.test.location resource_group_name = azurerm_resource_group.test.name + sku_name = "GP_Gen5_2" + version = "%s" - sku_name = "GP_Gen5_2" + create_mode = "Replica" + creation_source_server_id = azurerm_mysql_server.test.id + ssl_enforcement_enabled = true +} +`, testAccAzureRMMySQLServer_basic(data, version), data.RandomInteger, version) +} - storage_profile { - storage_mb = 51200 - backup_retention_days = 7 - geo_redundant_backup = "Disabled" - auto_grow = "Disabled" - } +func testAccAzureRMMySQLServer_createPointInTimeRestore(data acceptance.TestData, version, restoreTime string) string { + return fmt.Sprintf(` +%s - administrator_login = "acctestun" - administrator_login_password = "H@Sh1CoR3!" - version = "5.7" - ssl_enforcement = "Enabled" +resource "azurerm_mysql_server" "restore" { + name = "acctestmysqlsvr-%d-restore" + location = azurerm_resource_group.test.location + resource_group_name = azurerm_resource_group.test.name + sku_name = "GP_Gen5_2" + version = "%s" + + create_mode = "PointInTimeRestore" + creation_source_server_id = azurerm_mysql_server.test.id + restore_point_in_time = "%s" + ssl_enforcement_enabled = true + storage_mb = 51200 } -`, data.RandomInteger, data.Locations.Primary, data.RandomInteger) +`, testAccAzureRMMySQLServer_basic(data, version), data.RandomInteger, version, restoreTime) } diff --git a/azurerm/internal/services/mysql/tests/mysql_virtual_network_rule_resource_test.go b/azurerm/internal/services/mysql/tests/mysql_virtual_network_rule_resource_test.go index 67824d6c074d..797aea463362 100644 --- a/azurerm/internal/services/mysql/tests/mysql_virtual_network_rule_resource_test.go +++ b/azurerm/internal/services/mysql/tests/mysql_virtual_network_rule_resource_test.go @@ -13,66 +13,66 @@ import ( "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" ) -func TestAccAzureRMMySqlVirtualNetworkRule_basic(t *testing.T) { +func TestAccAzureRMMySSQLVirtualNetworkRule_basic(t *testing.T) { data := acceptance.BuildTestData(t, "azurerm_mysql_virtual_network_rule", "test") resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { acceptance.PreCheck(t) }, Providers: acceptance.SupportedProviders, - CheckDestroy: testCheckAzureRMMySqlVirtualNetworkRuleDestroy, + CheckDestroy: testCheckAzureRMMySSQLVirtualNetworkRuleDestroy, Steps: []resource.TestStep{ { - Config: testAccAzureRMMySqlVirtualNetworkRule_basic(data), + Config: testAccAzureRMMySSQLVirtualNetworkRule_basic(data), Check: resource.ComposeTestCheckFunc( - testCheckAzureRMMySqlVirtualNetworkRuleExists(data.ResourceName), + testCheckAzureRMMySSQLVirtualNetworkRuleExists(data.ResourceName), ), }, }, }) } -func TestAccAzureRMMySqlVirtualNetworkRule_badsubnet(t *testing.T) { +func TestAccAzureRMMySSQLVirtualNetworkRule_badsubnet(t *testing.T) { data := acceptance.BuildTestData(t, "azurerm_mysql_virtual_network_rule", "test") resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { acceptance.PreCheck(t) }, Providers: acceptance.SupportedProviders, - CheckDestroy: testCheckAzureRMMySqlVirtualNetworkRuleDestroy, + CheckDestroy: testCheckAzureRMMySSQLVirtualNetworkRuleDestroy, Steps: []resource.TestStep{ { - Config: testAccAzureRMMySqlVirtualNetworkRule_badsubnet(data), + Config: testAccAzureRMMySSQLVirtualNetworkRule_badsubnet(data), /* Check: resource.ComposeTestCheckFunc( - testCheckAzureRMMySqlVirtualNetworkRuleExists(data.ResourceName), + testCheckAzureRMMySSQLVirtualNetworkRuleExists(data.ResourceName), ),*/ }, }, }) } -func TestAccAzureRMMySqlVirtualNetworkRule_requiresImport(t *testing.T) { +func TestAccAzureRMMySSQLVirtualNetworkRule_requiresImport(t *testing.T) { data := acceptance.BuildTestData(t, "azurerm_mysql_virtual_network_rule", "test") resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { acceptance.PreCheck(t) }, Providers: acceptance.SupportedProviders, - CheckDestroy: testCheckAzureRMMySqlVirtualNetworkRuleDestroy, + CheckDestroy: testCheckAzureRMMySSQLVirtualNetworkRuleDestroy, Steps: []resource.TestStep{ { - Config: testAccAzureRMMySqlVirtualNetworkRule_basic(data), + Config: testAccAzureRMMySSQLVirtualNetworkRule_basic(data), Check: resource.ComposeTestCheckFunc( - testCheckAzureRMMySqlVirtualNetworkRuleExists(data.ResourceName), + testCheckAzureRMMySSQLVirtualNetworkRuleExists(data.ResourceName), ), }, { - Config: testAccAzureRMMySqlVirtualNetworkRule_requiresImport(data), + Config: testAccAzureRMMySSQLVirtualNetworkRule_requiresImport(data), ExpectError: acceptance.RequiresImportError("azurerm_mysql_virtual_network_rule"), }, }, }) } -func TestAccAzureRMMySqlVirtualNetworkRule_switchSubnets(t *testing.T) { +func TestAccAzureRMMySSQLVirtualNetworkRule_switchSubnets(t *testing.T) { data := acceptance.BuildTestData(t, "azurerm_mysql_virtual_network_rule", "test") // Create regex strings that will ensure that one subnet name exists, but not the other @@ -82,19 +82,19 @@ func TestAccAzureRMMySqlVirtualNetworkRule_switchSubnets(t *testing.T) { resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { acceptance.PreCheck(t) }, Providers: acceptance.SupportedProviders, - CheckDestroy: testCheckAzureRMMySqlVirtualNetworkRuleDestroy, + CheckDestroy: testCheckAzureRMMySSQLVirtualNetworkRuleDestroy, Steps: []resource.TestStep{ { - Config: testAccAzureRMMySqlVirtualNetworkRule_subnetSwitchPre(data), + Config: testAccAzureRMMySSQLVirtualNetworkRule_subnetSwitchPre(data), Check: resource.ComposeTestCheckFunc( - testCheckAzureRMMySqlVirtualNetworkRuleExists(data.ResourceName), + testCheckAzureRMMySSQLVirtualNetworkRuleExists(data.ResourceName), resource.TestMatchResourceAttr(data.ResourceName, "subnet_id", preConfigRegex), ), }, { - Config: testAccAzureRMMySqlVirtualNetworkRule_subnetSwitchPost(data), + Config: testAccAzureRMMySSQLVirtualNetworkRule_subnetSwitchPost(data), Check: resource.ComposeTestCheckFunc( - testCheckAzureRMMySqlVirtualNetworkRuleExists(data.ResourceName), + testCheckAzureRMMySSQLVirtualNetworkRuleExists(data.ResourceName), resource.TestMatchResourceAttr(data.ResourceName, "subnet_id", postConfigRegex), ), }, @@ -102,19 +102,19 @@ func TestAccAzureRMMySqlVirtualNetworkRule_switchSubnets(t *testing.T) { }) } -func TestAccAzureRMMySqlVirtualNetworkRule_disappears(t *testing.T) { +func TestAccAzureRMMySSQLVirtualNetworkRule_disappears(t *testing.T) { data := acceptance.BuildTestData(t, "azurerm_mysql_virtual_network_rule", "test") resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { acceptance.PreCheck(t) }, Providers: acceptance.SupportedProviders, - CheckDestroy: testCheckAzureRMMySqlVirtualNetworkRuleDestroy, + CheckDestroy: testCheckAzureRMMySSQLVirtualNetworkRuleDestroy, Steps: []resource.TestStep{ { - Config: testAccAzureRMMySqlVirtualNetworkRule_basic(data), + Config: testAccAzureRMMySSQLVirtualNetworkRule_basic(data), Check: resource.ComposeTestCheckFunc( - testCheckAzureRMMySqlVirtualNetworkRuleExists(data.ResourceName), - testCheckAzureRMMySqlVirtualNetworkRuleDisappears(data.ResourceName), + testCheckAzureRMMySSQLVirtualNetworkRuleExists(data.ResourceName), + testCheckAzureRMMySSQLVirtualNetworkRuleDisappears(data.ResourceName), ), ExpectNonEmptyPlan: true, }, @@ -122,7 +122,7 @@ func TestAccAzureRMMySqlVirtualNetworkRule_disappears(t *testing.T) { }) } -func TestAccAzureRMMySqlVirtualNetworkRule_multipleSubnets(t *testing.T) { +func TestAccAzureRMMySSQLVirtualNetworkRule_multipleSubnets(t *testing.T) { data := acceptance.BuildTestData(t, "azurerm_mysql_virtual_network_rule", "rule1") resourceName2 := "azurerm_mysql_virtual_network_rule.rule2" @@ -131,21 +131,21 @@ func TestAccAzureRMMySqlVirtualNetworkRule_multipleSubnets(t *testing.T) { resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { acceptance.PreCheck(t) }, Providers: acceptance.SupportedProviders, - CheckDestroy: testCheckAzureRMMySqlVirtualNetworkRuleDestroy, + CheckDestroy: testCheckAzureRMMySSQLVirtualNetworkRuleDestroy, Steps: []resource.TestStep{ { - Config: testAccAzureRMMySqlVirtualNetworkRule_multipleSubnets(data), + Config: testAccAzureRMMySSQLVirtualNetworkRule_multipleSubnets(data), Check: resource.ComposeTestCheckFunc( - testCheckAzureRMMySqlVirtualNetworkRuleExists(data.ResourceName), - testCheckAzureRMMySqlVirtualNetworkRuleExists(resourceName2), - testCheckAzureRMMySqlVirtualNetworkRuleExists(resourceName3), + testCheckAzureRMMySSQLVirtualNetworkRuleExists(data.ResourceName), + testCheckAzureRMMySSQLVirtualNetworkRuleExists(resourceName2), + testCheckAzureRMMySSQLVirtualNetworkRuleExists(resourceName3), ), }, }, }) } -func testCheckAzureRMMySqlVirtualNetworkRuleExists(resourceName string) resource.TestCheckFunc { +func testCheckAzureRMMySSQLVirtualNetworkRuleExists(resourceName string) resource.TestCheckFunc { return func(s *terraform.State) error { client := acceptance.AzureProvider.Meta().(*clients.Client).MySQL.VirtualNetworkRulesClient ctx := acceptance.AzureProvider.Meta().(*clients.Client).StopContext @@ -172,7 +172,7 @@ func testCheckAzureRMMySqlVirtualNetworkRuleExists(resourceName string) resource } } -func testCheckAzureRMMySqlVirtualNetworkRuleDestroy(s *terraform.State) error { +func testCheckAzureRMMySSQLVirtualNetworkRuleDestroy(s *terraform.State) error { client := acceptance.AzureProvider.Meta().(*clients.Client).MySQL.VirtualNetworkRulesClient ctx := acceptance.AzureProvider.Meta().(*clients.Client).StopContext @@ -200,7 +200,7 @@ func testCheckAzureRMMySqlVirtualNetworkRuleDestroy(s *terraform.State) error { return nil } -func testCheckAzureRMMySqlVirtualNetworkRuleDisappears(resourceName string) resource.TestCheckFunc { +func testCheckAzureRMMySSQLVirtualNetworkRuleDisappears(resourceName string) resource.TestCheckFunc { return func(s *terraform.State) error { client := acceptance.AzureProvider.Meta().(*clients.Client).MySQL.VirtualNetworkRulesClient ctx := acceptance.AzureProvider.Meta().(*clients.Client).StopContext @@ -239,7 +239,7 @@ func testCheckAzureRMMySqlVirtualNetworkRuleDisappears(resourceName string) reso } } -func testAccAzureRMMySqlVirtualNetworkRule_basic(data acceptance.TestData) string { +func testAccAzureRMMySSQLVirtualNetworkRule_basic(data acceptance.TestData) string { return fmt.Sprintf(` provider "azurerm" { features {} @@ -292,7 +292,7 @@ resource "azurerm_mysql_virtual_network_rule" "test" { `, data.RandomInteger, data.Locations.Primary, data.RandomInteger, data.RandomInteger, data.RandomInteger, data.RandomInteger) } -func testAccAzureRMMySqlVirtualNetworkRule_badsubnet(data acceptance.TestData) string { +func testAccAzureRMMySSQLVirtualNetworkRule_badsubnet(data acceptance.TestData) string { return fmt.Sprintf(` provider "azurerm" { features {} @@ -345,7 +345,7 @@ resource "azurerm_mysql_virtual_network_rule" "test" { `, data.RandomInteger, data.Locations.Primary, data.RandomInteger, data.RandomInteger, data.RandomInteger, data.RandomInteger) } -func testAccAzureRMMySqlVirtualNetworkRule_requiresImport(data acceptance.TestData) string { +func testAccAzureRMMySSQLVirtualNetworkRule_requiresImport(data acceptance.TestData) string { return fmt.Sprintf(` %s @@ -355,10 +355,10 @@ resource "azurerm_mysql_virtual_network_rule" "import" { server_name = azurerm_mysql_virtual_network_rule.test.server_name subnet_id = azurerm_mysql_virtual_network_rule.test.subnet_id } -`, testAccAzureRMMySqlVirtualNetworkRule_basic(data)) +`, testAccAzureRMMySSQLVirtualNetworkRule_basic(data)) } -func testAccAzureRMMySqlVirtualNetworkRule_subnetSwitchPre(data acceptance.TestData) string { +func testAccAzureRMMySSQLVirtualNetworkRule_subnetSwitchPre(data acceptance.TestData) string { return fmt.Sprintf(` provider "azurerm" { features {} @@ -419,7 +419,7 @@ resource "azurerm_mysql_virtual_network_rule" "test" { `, data.RandomInteger, data.Locations.Primary, data.RandomInteger, data.RandomInteger, data.RandomInteger, data.RandomInteger, data.RandomInteger) } -func testAccAzureRMMySqlVirtualNetworkRule_subnetSwitchPost(data acceptance.TestData) string { +func testAccAzureRMMySSQLVirtualNetworkRule_subnetSwitchPost(data acceptance.TestData) string { return fmt.Sprintf(` provider "azurerm" { features {} @@ -480,7 +480,7 @@ resource "azurerm_mysql_virtual_network_rule" "test" { `, data.RandomInteger, data.Locations.Primary, data.RandomInteger, data.RandomInteger, data.RandomInteger, data.RandomInteger, data.RandomInteger) } -func testAccAzureRMMySqlVirtualNetworkRule_multipleSubnets(data acceptance.TestData) string { +func testAccAzureRMMySSQLVirtualNetworkRule_multipleSubnets(data acceptance.TestData) string { return fmt.Sprintf(` provider "azurerm" { features {} diff --git a/azurerm/internal/services/mysql/validate/mysql.go b/azurerm/internal/services/mysql/validate/mysql.go new file mode 100644 index 000000000000..e5abee8309ba --- /dev/null +++ b/azurerm/internal/services/mysql/validate/mysql.go @@ -0,0 +1,30 @@ +package validate + +import ( + "fmt" + + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/validate" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/services/mysql/parse" +) + +func MysqlServerServerID(i interface{}, k string) (warnings []string, errors []error) { + v, ok := i.(string) + if !ok { + errors = append(errors, fmt.Errorf("expected type of %q to be string", k)) + return warnings, errors + } + + if _, err := parse.MysqlServerServerID(v); err != nil { + errors = append(errors, fmt.Errorf("Can not parse %q as a MySQL Server resource id: %v", k, err)) + } + + return warnings, errors +} + +func MysqlServerServerName(i interface{}, k string) (_ []string, errors []error) { + if m, regexErrs := validate.RegExHelper(i, k, `^[0-9a-z][-0-9a-z]{1,61}[0-9a-z]$`); !m { + return nil, append(regexErrs, fmt.Errorf("%q can contain only lowercase letters, numbers, and '-', but can't start or end with '-', and must be at least 3 characters and no more than 63 characters long.", k)) + } + + return nil, nil +} diff --git a/azurerm/internal/services/mysql/validate/mysql_test.go b/azurerm/internal/services/mysql/validate/mysql_test.go new file mode 100644 index 000000000000..3e919164fb37 --- /dev/null +++ b/azurerm/internal/services/mysql/validate/mysql_test.go @@ -0,0 +1,106 @@ +package validate + +import ( + "testing" +) + +func TestValidateMysqlServerServerID(t *testing.T) { + testData := []struct { + input string + expected bool + }{ + { + // empty + input: "", + expected: false, + }, + { + // invalid + input: "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/test-rg", + expected: false, + }, + { + // valid + input: "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/test-rg/providers/Microsoft.DBforMySQL/servers/test-mysql", + expected: true, + }, + } + + for _, v := range testData { + t.Logf("[DEBUG] Testing %q..", v.input) + + _, errors := MysqlServerServerID(v.input, "name") + actual := len(errors) == 0 + if v.expected != actual { + t.Fatalf("Expected %t but got %t", v.expected, actual) + } + } +} + +func TestValidateMysqlServerServerName(t *testing.T) { + testData := []struct { + input string + expected bool + }{ + { + // empty + input: "", + expected: false, + }, + { + // basic example + input: "ab-c", + expected: true, + }, + { + // can't contain upper case letter + input: "AbcD", + expected: false, + }, + { + // can't start with a hyphen + input: "-abc", + expected: false, + }, + { + // can't contain underscore + input: "ab_c", + expected: false, + }, + { + // can't end with hyphen + input: "abc-", + expected: false, + }, + { + // can not be shorter than 3 characters + input: "ab", + expected: false, + }, + { + // can not be shorter than 3 characters (catching bad regex) + input: "a", + expected: false, + }, + { + // 63 chars + input: "abcdefghijklmnopqrstuvwxyzabcdefabcdefghijklmnopqrstuvwxyzabcde", + expected: true, + }, + { + // 64 chars + input: "abcdefghijklmnopqrstuvwxyzabcdefabcdefghijklmnopqrstuvwxyzabcdef", + expected: false, + }, + } + + for _, v := range testData { + t.Logf("[DEBUG] Testing %q..", v.input) + + _, errors := MysqlServerServerName(v.input, "name") + actual := len(errors) == 0 + if v.expected != actual { + t.Fatalf("Expected %t but got %t", v.expected, actual) + } + } +} diff --git a/azurerm/internal/services/postgres/parse/postgres_test.go b/azurerm/internal/services/postgres/parse/postgres_test.go index b3b2dcc1fb6b..ae2dfa439205 100644 --- a/azurerm/internal/services/postgres/parse/postgres_test.go +++ b/azurerm/internal/services/postgres/parse/postgres_test.go @@ -32,7 +32,7 @@ func TestAnalysisServicesServerId(t *testing.T) { }, { Name: "Missing Servers Value", - Input: "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/resGroup1/providers/Microsoft.Web/servers/", + Input: "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/resGroup1/providers/Microsoft.DBforPostgreSQL/servers/", Expected: nil, }, { diff --git a/azurerm/internal/services/postgres/postgresql_server_resource.go b/azurerm/internal/services/postgres/postgresql_server_resource.go index 7f8cc2c45208..f9d1eeb3e24f 100644 --- a/azurerm/internal/services/postgres/postgresql_server_resource.go +++ b/azurerm/internal/services/postgres/postgresql_server_resource.go @@ -531,15 +531,15 @@ func resourceArmPostgreSQLServerRead(d *schema.ResourceData, meta interface{}) e d.Set("name", resp.Name) d.Set("resource_group_name", id.ResourceGroup) + if location := resp.Location; location != nil { + d.Set("location", azure.NormalizeLocation(*location)) + } + if sku := resp.Sku; sku != nil { d.Set("sku_name", sku.Name) } if props := resp.ServerProperties; props != nil { - if location := resp.Location; location != nil { - d.Set("location", azure.NormalizeLocation(*location)) - } - d.Set("administrator_login", props.AdministratorLogin) d.Set("ssl_enforcement", string(props.SslEnforcement)) d.Set("ssl_minimal_tls_version_enforced", props.MinimalTLSVersion) diff --git a/website/docs/r/mysql_server.html.markdown b/website/docs/r/mysql_server.html.markdown index 725ee963c2c6..c205512f5a12 100644 --- a/website/docs/r/mysql_server.html.markdown +++ b/website/docs/r/mysql_server.html.markdown @@ -14,28 +14,34 @@ Manages a MySQL Server. ## Example Usage ```hcl +provider "azurerm" { + features {} +} + resource "azurerm_resource_group" "example" { - name = "api-rg-pro" + name = "example-resources" location = "West Europe" } resource "azurerm_mysql_server" "example" { - name = "mysql-server-1" + name = "example-mysqlserver" location = azurerm_resource_group.example.location resource_group_name = azurerm_resource_group.example.name - sku_name = "B_Gen5_2" - - storage_profile { - storage_mb = 5120 - backup_retention_days = 7 - geo_redundant_backup = "Disabled" - } - administrator_login = "mysqladminun" administrator_login_password = "H@Sh1CoR3!" - version = "5.7" - ssl_enforcement = "Enabled" + + sku_name = "B_Gen5_2" + storage_mb = 5120 + version = "5.7" + + auto_grow_enabled = true + backup_retention_days = 7 + geo_redundant_backup_enabled = true + infrastructure_encryption_enabled = true + public_network_access_enabled = false + ssl_enforcement_enabled = true + ssl_minimal_tls_version_enforced = "TLS1_2" } ``` @@ -51,31 +57,35 @@ The following arguments are supported: * `sku_name` - (Required) Specifies the SKU Name for this MySQL Server. The name of the SKU, follows the `tier` + `family` + `cores` pattern (e.g. `B_Gen4_1`, `GP_Gen5_8`). For more information see the [product documentation](https://docs.microsoft.com/en-us/rest/api/mysql/servers/create#sku). -* `storage_profile` - (Required) A `storage_profile` block as defined below. +* `version` - (Required) Specifies the version of MySQL to use. Valid values are `5.6`, `5.7`, and `8.0`. Changing this forces a new resource to be created. + +* `administrator_login` - (Optional) The Administrator Login for the MySQL Server. Required when `create_mode` is `Default`. Changing this forces a new resource to be created. -* `administrator_login` - (Required) The Administrator Login for the MySQL Server. Changing this forces a new resource to be created. +* `administrator_login_password` - (Optional) The Password associated with the `administrator_login` for the MySQL Server. Required when `create_mode` is `Default`. -* `administrator_login_password` - (Required) The Password associated with the `administrator_login` for the MySQL Server. +* `auto_grow_enabled` - (Optional) Enable/Disable auto-growing of the storage. Storage auto-grow prevents your server from running out of storage and becoming read-only. If storage auto grow is enabled, the storage automatically grows without impacting the workload. The default value if not explicitly specified is `true`. -* `version` - (Required) Specifies the version of MySQL to use. Valid values are `5.6`, `5.7`, and `8.0`. Changing this forces a new resource to be created. +* `backup_retention_days` - (Optional) Backup retention days for the server, supported values are between `7` and `35` days. -* `ssl_enforcement` - (Required) Specifies if SSL should be enforced on connections. Possible values are `Enabled` and `Disabled`. +* `create_mode` - (Optional) The creation mode. Can be used to restore or replicate existing servers. Possible values are `Default`, `Replica`, `GeoRestore`, and `PointInTimeRestore`. Defaults to `Default`. -* `public_network_access_enabled` - (Optional) Should public network access be allowed for this server? Defaults to `true`. +* `creation_source_server_id` - (Optional) For creation modes other than `Default`, the source server ID to use. -* `tags` - (Optional) A mapping of tags to assign to the resource. +* `geo_redundant_backup_enabled` - (Optional) Turn Geo-redundant server backups on/off. This allows you to choose between locally redundant or geo-redundant backup storage in the General Purpose and Memory Optimized tiers. When the backups are stored in geo-redundant backup storage, they are not only stored within the region in which your server is hosted, but are also replicated to a paired data center. This provides better protection and ability to restore your server in a different region in the event of a disaster. This is not supported for the Basic tier. ---- +* `infrastructure_encryption_enabled` - (Optional) Whether or not infrastructure is encrypted for this server. Defaults to `false`. Changing this forces a new resource to be created. -`storage_profile` supports the following: +* `public_network_access_enabled` - (Optional) Whether or not public network access is allowed for this server. Defaults to `true`. -* `storage_mb` - (Required) Max storage allowed for a server. Possible values are between `5120` MB(5GB) and `1048576` MB(1TB) for the Basic SKU and between `5120` MB(5GB) and `4194304` MB(4TB) for General Purpose/Memory Optimized SKUs. For more information see the [product documentation](https://docs.microsoft.com/en-us/rest/api/mysql/servers/create#StorageProfile). +* `restore_point_in_time` - (Optional) When `create_mode` is `PointInTimeRestore`, specifies the point in time to restore from `creation_source_server_id`. -* `backup_retention_days` - (Optional) Backup retention days for the server, supported values are between `7` and `35` days. +* `ssl_enforcement_enabled` - (Required) Specifies if SSL should be enforced on connections. Possible values are `true` and `false`. + +* `ssl_minimal_tls_version_enforced` - (Optional) The minimum TLS version to support on the sever. Possible values are `TLSEnforcementDisabled`, `TLS1_0`, `TLS1_1`, and `TLS1_2`. Defaults to `TLSEnforcementDisabled`. -* `geo_redundant_backup` - (Optional) Enable Geo-redundant or not for server backup. Valid values for this property are `Enabled` or `Disabled`, not supported for the `basic` tier. +* `storage_mb` - (Required) Max storage allowed for a server. Possible values are between `5120` MB(5GB) and `1048576` MB(1TB) for the Basic SKU and between `5120` MB(5GB) and `4194304` MB(4TB) for General Purpose/Memory Optimized SKUs. For more information see the [product documentation](https://docs.microsoft.com/en-us/rest/api/mysql/servers/create#StorageProfile). -* `auto_grow` - (Optional) Defines whether autogrow is enabled or disabled for the storage. Valid values are `Enabled` or `Disabled`. +* `tags` - (Optional) A mapping of tags to assign to the resource. ## Attributes Reference