diff --git a/azurerm/helpers/validate/database.go b/azurerm/helpers/validate/database.go index 6e8c95377337..d531faca7937 100644 --- a/azurerm/helpers/validate/database.go +++ b/azurerm/helpers/validate/database.go @@ -21,20 +21,3 @@ func MariaDatabaseCollation(i interface{}, k string) (warnings []string, errors return warnings, errors } - -func PostgresDatabaseCollation(i interface{}, k string) (warnings []string, errors []error) { - v, ok := i.(string) - if !ok { - errors = append(errors, fmt.Errorf("expected type of %s to be string", k)) - return - } - - matched, _ := regexp.MatchString(`^[-A-Za-z0-9_. ]+$`, v) - - if !matched { - errors = append(errors, fmt.Errorf("%s contains invalid characters, only alphanumeric, underscore, space or hyphen characters are supported, got %s", k, v)) - return - } - - return warnings, errors -} diff --git a/azurerm/helpers/validate/database_test.go b/azurerm/helpers/validate/database_test.go deleted file mode 100644 index c8f9de2de9e7..000000000000 --- a/azurerm/helpers/validate/database_test.go +++ /dev/null @@ -1,38 +0,0 @@ -package validate - -import "testing" - -func TestDatabaseCollation(t *testing.T) { - cases := []struct { - Value string - Errors int - }{ - { - Value: "en@US", - Errors: 1, - }, - { - Value: "en-US", - Errors: 0, - }, - { - Value: "en_US", - Errors: 0, - }, - { - Value: "en US", - Errors: 0, - }, - { - Value: "English_United States.1252", - Errors: 0, - }, - } - - for _, tc := range cases { - _, errors := PostgresDatabaseCollation(tc.Value, "collation") - if len(errors) != tc.Errors { - t.Fatalf("Expected DatabaseCollation to trigger '%d' errors for '%s' - got '%d'", tc.Errors, tc.Value, len(errors)) - } - } -} diff --git a/azurerm/internal/services/postgres/parse/postgres.go b/azurerm/internal/services/postgres/parse/postgres.go new file mode 100644 index 000000000000..6a0238398055 --- /dev/null +++ b/azurerm/internal/services/postgres/parse/postgres.go @@ -0,0 +1,33 @@ +package parse + +import ( + "fmt" + + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/azure" +) + +type PostgresServerServerId struct { + ResourceGroup string + Name string +} + +func PostgresServerServerID(input string) (*PostgresServerServerId, error) { + id, err := azure.ParseAzureResourceID(input) + if err != nil { + return nil, fmt.Errorf("[ERROR] Unable to parse Postgres Server ID %q: %+v", input, err) + } + + server := PostgresServerServerId{ + 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/postgres/parse/postgres_test.go b/azurerm/internal/services/postgres/parse/postgres_test.go new file mode 100644 index 000000000000..b3b2dcc1fb6b --- /dev/null +++ b/azurerm/internal/services/postgres/parse/postgres_test.go @@ -0,0 +1,73 @@ +package parse + +import ( + "testing" +) + +func TestAnalysisServicesServerId(t *testing.T) { + testData := []struct { + Name string + Input string + Expected *PostgresServerServerId + }{ + { + Name: "Empty", + Input: "", + Expected: nil, + }, + { + Name: "No Resource Groups Segment", + Input: "/subscriptions/00000000-0000-0000-0000-000000000000", + Expected: nil, + }, + { + Name: "No Resource Groups Value", + Input: "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/", + Expected: nil, + }, + { + Name: "Resource Group ID", + Input: "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/foo/", + Expected: nil, + }, + { + Name: "Missing Servers Value", + Input: "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/resGroup1/providers/Microsoft.Web/servers/", + Expected: nil, + }, + { + Name: "Postgres Server ID", + Input: "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/resGroup1/providers/Microsoft.DBforPostgreSQL/servers/Server1", + Expected: &PostgresServerServerId{ + Name: "Server1", + ResourceGroup: "resGroup1", + }, + }, + { + Name: "Wrong Casing", + Input: "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/resGroup1/providers/Microsoft.DBforPostgreSQL/Servers/", + Expected: nil, + }, + } + + for _, v := range testData { + t.Logf("[DEBUG] Testing %q", v.Name) + + actual, err := PostgresServerServerID(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/postgres/resource_arm_postgresql_configuration.go b/azurerm/internal/services/postgres/resource_arm_postgresql_configuration.go index f7720a3b116b..bc95107b44b4 100644 --- a/azurerm/internal/services/postgres/resource_arm_postgresql_configuration.go +++ b/azurerm/internal/services/postgres/resource_arm_postgresql_configuration.go @@ -10,6 +10,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/postgres/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 resourceArmPostgreSQLConfiguration() *schema.Resource { Type: schema.TypeString, Required: true, ForceNew: true, - ValidateFunc: ValidatePSQLServerName, + ValidateFunc: validate.PostgresServerServerName, }, "value": { diff --git a/azurerm/internal/services/postgres/resource_arm_postgresql_database.go b/azurerm/internal/services/postgres/resource_arm_postgresql_database.go index 17107273427e..bf9bb90e12de 100644 --- a/azurerm/internal/services/postgres/resource_arm_postgresql_database.go +++ b/azurerm/internal/services/postgres/resource_arm_postgresql_database.go @@ -11,9 +11,9 @@ import ( "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/azure" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/suppress" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" - "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/postgres/validate" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/timeouts" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" ) @@ -47,7 +47,7 @@ func resourceArmPostgreSQLDatabase() *schema.Resource { Type: schema.TypeString, Required: true, ForceNew: true, - ValidateFunc: ValidatePSQLServerName, + ValidateFunc: validate.PostgresServerServerName, }, "charset": { diff --git a/azurerm/internal/services/postgres/resource_arm_postgresql_firewall_rule.go b/azurerm/internal/services/postgres/resource_arm_postgresql_firewall_rule.go index bb5c0e2242aa..a276e79ca8c0 100644 --- a/azurerm/internal/services/postgres/resource_arm_postgresql_firewall_rule.go +++ b/azurerm/internal/services/postgres/resource_arm_postgresql_firewall_rule.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/postgres/validate" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/timeouts" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" ) @@ -45,7 +46,7 @@ func resourceArmPostgreSQLFirewallRule() *schema.Resource { Type: schema.TypeString, Required: true, ForceNew: true, - ValidateFunc: ValidatePSQLServerName, + ValidateFunc: validate.PostgresServerServerName, }, "start_ip_address": { diff --git a/azurerm/internal/services/postgres/resource_arm_postgresql_server.go b/azurerm/internal/services/postgres/resource_arm_postgresql_server.go index 4889b3e4f82e..7f8cc2c45208 100644 --- a/azurerm/internal/services/postgres/resource_arm_postgresql_server.go +++ b/azurerm/internal/services/postgres/resource_arm_postgresql_server.go @@ -8,36 +8,42 @@ import ( "time" "github.com/Azure/azure-sdk-for-go/services/postgresql/mgmt/2017-12-01/postgresql" + "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" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/suppress" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf" - "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/postgres/parse" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/services/postgres/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" ) -func ValidatePSQLServerName(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 at most 63 characters", k)) - } - - return nil, nil -} - func resourceArmPostgreSQLServer() *schema.Resource { return &schema.Resource{ Create: resourceArmPostgreSQLServerCreate, Read: resourceArmPostgreSQLServerRead, Update: resourceArmPostgreSQLServerUpdate, Delete: resourceArmPostgreSQLServerDelete, + Importer: &schema.ResourceImporter{ - State: schema.ImportStatePassthrough, + State: func(d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) { + if _, err := parse.PostgresServerServerID(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{ @@ -52,7 +58,7 @@ func resourceArmPostgreSQLServer() *schema.Resource { Type: schema.TypeString, Required: true, ForceNew: true, - ValidateFunc: ValidatePSQLServerName, + ValidateFunc: validate.PostgresServerServerName, }, "location": azure.SchemaLocation(), @@ -86,18 +92,6 @@ func resourceArmPostgreSQLServer() *schema.Resource { }, false), }, - "administrator_login": { - Type: schema.TypeString, - Required: true, - ForceNew: true, - }, - - "administrator_login_password": { - Type: schema.TypeString, - Required: true, - Sensitive: true, - }, - "version": { Type: schema.TypeString, Required: true, @@ -109,18 +103,22 @@ func resourceArmPostgreSQLServer() *schema.Resource { string(postgresql.OneZero), string(postgresql.OneZeroFullStopZero), }, true), - DiffSuppressFunc: suppress.CaseDifference, + DiffSuppressFunc: suppress.CaseDifference, // TODO: make case sensitive in 3.0 }, "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 move 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, + 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), @@ -128,36 +126,150 @@ func resourceArmPostgreSQLServer() *schema.Resource { }, "backup_retention_days": { - Type: schema.TypeInt, - Optional: true, - ValidateFunc: validation.IntBetween(7, 35), + Type: schema.TypeInt, + Optional: true, + Default: 7, + 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), + }, + + "auto_grow": { + Type: schema.TypeString, + Optional: true, + Computed: true, + ConflictsWith: []string{"auto_grow_enabled"}, + Deprecated: "this has been moved to the top level and will be removed in version 3.0 of the provider.", + ValidateFunc: validation.StringInSlice([]string{ + string(postgresql.StorageAutogrowEnabled), + string(postgresql.StorageAutogrowDisabled), + }, false), }, "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 and will be removed in version 3.0 of the provider.", ValidateFunc: validation.StringInSlice([]string{ "Enabled", "Disabled", }, true), DiffSuppressFunc: suppress.CaseDifference, }, - "auto_grow": { - Type: schema.TypeString, - Optional: true, - Default: string(postgresql.StorageAutogrowEnabled), - ValidateFunc: validation.StringInSlice([]string{ - string(postgresql.StorageAutogrowEnabled), - string(postgresql.StorageAutogrowDisabled), - }, false), - }, }, }, }, - "ssl_enforcement": { + "administrator_login": { + Type: schema.TypeString, + Optional: true, + Computed: true, + ForceNew: true, + ValidateFunc: validation.StringIsNotWhiteSpace, + }, + + "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", "storage_profile.0.auto_grow"}, + }, + + "backup_retention_days": { + Type: schema.TypeInt, + Optional: true, + Computed: true, + ConflictsWith: []string{"storage_profile", "storage_profile.0.backup_retention_days"}, + ValidateFunc: validation.IntBetween(7, 35), + }, + + "geo_redundant_backup_enabled": { + Type: schema.TypeBool, + Optional: true, + Computed: true, // TODO: remove in 2.0 and default to false + ConflictsWith: []string{"storage_profile", "storage_profile.0.geo_redundant_backup"}, + }, + + "create_mode": { Type: schema.TypeString, - Required: true, + Optional: true, + Default: string(postgresql.CreateModeDefault), + ValidateFunc: validation.StringInSlice([]string{ + string(postgresql.CreateModeDefault), + string(postgresql.CreateModeGeoRestore), + string(postgresql.CreateModePointInTimeRestore), + string(postgresql.CreateModeReplica), + }, false), + }, + + "creation_source_server_id": { + Type: schema.TypeString, + Optional: true, + ValidateFunc: validate.PostgresServerServerID, + }, + + "infrastructure_encryption_enabled": { + Type: schema.TypeBool, + Optional: true, + ForceNew: true, + }, + + "public_network_access_enabled": { + Type: schema.TypeBool, + Optional: true, + Default: true, + }, + + "restore_point_in_time": { + Type: schema.TypeString, + Optional: true, + ValidateFunc: validation.IsRFC3339Time, + }, + + "storage_mb": { + Type: schema.TypeInt, + Optional: true, + Computed: true, + ConflictsWith: []string{"storage_profile", "storage_profile.0.storage_mb"}, + ValidateFunc: validation.All( + validation.IntBetween(5120, 4194304), + validation.IntDivisibleBy(1024), + ), + }, + + "ssl_minimal_tls_version_enforced": { + Type: schema.TypeString, + Optional: true, + Default: string(postgresql.TLSEnforcementDisabled), + ValidateFunc: validation.StringInSlice([]string{ + string(postgresql.TLSEnforcementDisabled), + string(postgresql.TLS10), + string(postgresql.TLS11), + string(postgresql.TLS12), + }, false), + }, + + "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"}, + }, + + "ssl_enforcement": { + Type: schema.TypeString, + Optional: true, + Computed: true, + Deprecated: "this has been renamed to the boolean `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(postgresql.SslEnforcementEnumDisabled), string(postgresql.SslEnforcementEnumEnabled), @@ -190,7 +302,7 @@ func resourceArmPostgreSQLServerCreate(d *schema.ResourceData, meta interface{}) existing, err := client.Get(ctx, resourceGroup, name) if err != nil { if !utils.ResponseWasNotFound(existing.Response) { - return fmt.Errorf("Error checking for presence of existing PostgreSQL Server %q (Resource Group %q): %+v", name, resourceGroup, err) + return fmt.Errorf("checking for presence of existing PostgreSQL Server %q (Resource Group %q): %+v", name, resourceGroup, err) } } @@ -199,37 +311,127 @@ func resourceArmPostgreSQLServerCreate(d *schema.ResourceData, meta interface{}) } } + mode := postgresql.CreateMode(d.Get("create_mode").(string)) + tlsMin := postgresql.MinimalTLSVersionEnum(d.Get("ssl_minimal_tls_version_enforced").(string)) + source := d.Get("creation_source_server_id").(string) + version := postgresql.ServerVersion(d.Get("version").(string)) + sku, err := expandServerSkuName(d.Get("sku_name").(string)) if err != nil { - return fmt.Errorf("error expanding `sku_name` for PostgreSQL Server %s (Resource Group %q): %v", name, resourceGroup, err) + return fmt.Errorf("expanding `sku_name` for PostgreSQL Server %s (Resource Group %q): %v", name, resourceGroup, err) } - properties := postgresql.ServerForCreate{ - Location: &location, - Properties: &postgresql.ServerPropertiesForDefaultCreate{ - AdministratorLogin: utils.String(d.Get("administrator_login").(string)), - AdministratorLoginPassword: utils.String(d.Get("administrator_login_password").(string)), - Version: postgresql.ServerVersion(d.Get("version").(string)), - SslEnforcement: postgresql.SslEnforcementEnum(d.Get("ssl_enforcement").(string)), - StorageProfile: expandAzureRmPostgreSQLStorageProfile(d), - CreateMode: postgresql.CreateMode("Default"), - }, - Sku: sku, - Tags: tags.Expand(d.Get("tags").(map[string]interface{})), + infraEncrypt := postgresql.InfrastructureEncryptionEnabled + if v := d.Get("infrastructure_encryption_enabled"); !v.(bool) { + infraEncrypt = postgresql.InfrastructureEncryptionDisabled + } + + publicAccess := postgresql.PublicNetworkAccessEnumEnabled + if v := d.Get("public_network_access_enabled"); !v.(bool) { + publicAccess = postgresql.PublicNetworkAccessEnumDisabled + } + + ssl := postgresql.SslEnforcementEnumEnabled + if v, ok := d.GetOk("ssl_enforcement"); ok && strings.EqualFold(v.(string), string(postgresql.SslEnforcementEnumDisabled)) { + ssl = postgresql.SslEnforcementEnumDisabled + } + if v, ok := d.GetOkExists("ssl_enforcement_enabled"); ok && !v.(bool) { + ssl = postgresql.SslEnforcementEnumDisabled + } + + storage := expandAzureRmPostgreSQLStorageProfile(d) + + var props postgresql.BasicServerPropertiesForCreate + switch mode { + case postgresql.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 = &postgresql.ServerPropertiesForDefaultCreate{ + AdministratorLogin: &admin, + AdministratorLoginPassword: &pass, + CreateMode: mode, + InfrastructureEncryption: infraEncrypt, + PublicNetworkAccess: publicAccess, + MinimalTLSVersion: tlsMin, + SslEnforcement: ssl, + StorageProfile: storage, + Version: version, + } + case postgresql.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 = &postgresql.ServerPropertiesForRestore{ + CreateMode: mode, + SourceServerID: &source, + RestorePointInTime: &date.Time{ + Time: time, + }, + InfrastructureEncryption: infraEncrypt, + PublicNetworkAccess: publicAccess, + MinimalTLSVersion: tlsMin, + SslEnforcement: ssl, + StorageProfile: storage, + Version: version, + } + case postgresql.CreateModeGeoRestore: + props = &postgresql.ServerPropertiesForGeoRestore{ + CreateMode: mode, + SourceServerID: &source, + InfrastructureEncryption: infraEncrypt, + PublicNetworkAccess: publicAccess, + MinimalTLSVersion: tlsMin, + SslEnforcement: ssl, + StorageProfile: storage, + Version: version, + } + case postgresql.CreateModeReplica: + props = &postgresql.ServerPropertiesForReplica{ + CreateMode: mode, + SourceServerID: &source, + InfrastructureEncryption: infraEncrypt, + PublicNetworkAccess: publicAccess, + MinimalTLSVersion: tlsMin, + SslEnforcement: ssl, + Version: version, + } + } + + server := postgresql.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 PostgreSQL Server %q (Resource Group %q): %+v", name, resourceGroup, err) + return fmt.Errorf("creating PostgreSQL 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 PostgreSQL Server %q (Resource Group %q): %+v", name, resourceGroup, err) + return fmt.Errorf("waiting for creation of PostgreSQL Server %q (Resource Group %q): %+v", name, resourceGroup, err) } read, err := client.Get(ctx, resourceGroup, name) if err != nil { - return fmt.Errorf("Error retrieving PostgreSQL Server %q (Resource Group %q): %+v", name, resourceGroup, err) + return fmt.Errorf("retrieving PostgreSQL Server %q (Resource Group %q): %+v", name, resourceGroup, err) } if read.ID == nil { @@ -248,40 +450,56 @@ func resourceArmPostgreSQLServerUpdate(d *schema.ResourceData, meta interface{}) log.Printf("[INFO] preparing arguments for AzureRM PostgreSQL Server update.") - name := d.Get("name").(string) - resourceGroup := d.Get("resource_group_name").(string) + id, err := parse.PostgresServerServerID(d.Id()) + if err != nil { + return fmt.Errorf("parsing Postgres Server ID : %v", err) + } sku, err := expandServerSkuName(d.Get("sku_name").(string)) if err != nil { - return fmt.Errorf("error expanding `sku_name` for PostgreSQL Server %s (Resource Group %q): %v", name, resourceGroup, err) + return fmt.Errorf("expanding `sku_name` for PostgreSQL Server %s (Resource Group %q): %v", id.Name, id.ResourceGroup, err) + } + + publicAccess := postgresql.PublicNetworkAccessEnumEnabled + if v := d.Get("public_network_access_enabled"); !v.(bool) { + publicAccess = postgresql.PublicNetworkAccessEnumDisabled + } + + ssl := postgresql.SslEnforcementEnumEnabled + if v := d.Get("ssl_enforcement"); strings.EqualFold(v.(string), string(postgresql.SslEnforcementEnumDisabled)) { + ssl = postgresql.SslEnforcementEnumDisabled + } + if v := d.Get("ssl_enforcement_enabled"); !v.(bool) { + ssl = postgresql.SslEnforcementEnumDisabled } properties := postgresql.ServerUpdateParameters{ ServerUpdateParametersProperties: &postgresql.ServerUpdateParametersProperties{ AdministratorLoginPassword: utils.String(d.Get("administrator_login_password").(string)), - Version: postgresql.ServerVersion(d.Get("version").(string)), - SslEnforcement: postgresql.SslEnforcementEnum(d.Get("ssl_enforcement").(string)), + PublicNetworkAccess: publicAccess, + SslEnforcement: ssl, StorageProfile: expandAzureRmPostgreSQLStorageProfile(d), + Version: postgresql.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 PostgreSQL Server %q (Resource Group %q): %+v", name, resourceGroup, err) + return fmt.Errorf("updating PostgreSQL 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 update of PostgreSQL Server %q (Resource Group %q): %+v", name, resourceGroup, err) + return fmt.Errorf("waiting for update of PostgreSQL Server %q (Resource Group %q): %+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 PostgreSQL Server %q (Resource Group %q): %+v", name, resourceGroup, err) + return fmt.Errorf("retrieving PostgreSQL Server %q (Resource Group %q): %+v", id.Name, id.ResourceGroup, err) } if read.ID == nil { - return fmt.Errorf("Cannot read PostgreSQL Server %s (resource group %s) ID", name, resourceGroup) + return fmt.Errorf("Cannot read PostgreSQL Server %s (resource group %s) ID", id.Name, id.ResourceGroup) } d.SetId(*read.ID) @@ -294,46 +512,57 @@ func resourceArmPostgreSQLServerRead(d *schema.ResourceData, meta interface{}) e ctx, cancel := timeouts.ForRead(meta.(*clients.Client).StopContext, d) defer cancel() - id, err := azure.ParseAzureResourceID(d.Id()) + id, err := parse.PostgresServerServerID(d.Id()) if err != nil { - return err + return fmt.Errorf("parsing Postgres 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] PostgreSQL Server %q was not found (resource group %q)", name, resourceGroup) + log.Printf("[WARN] PostgreSQL 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 PostgreSQL Server %q (Resource Group %q): %+v", name, resourceGroup, err) + return fmt.Errorf("making Read request on Azure PostgreSQL Server %q (Resource Group %q): %+v", id.Name, id.ResourceGroup, err) } d.Set("name", resp.Name) - d.Set("resource_group_name", resourceGroup) - - if location := resp.Location; location != nil { - d.Set("location", azure.NormalizeLocation(*location)) - } - - d.Set("administrator_login", resp.AdministratorLogin) - d.Set("version", string(resp.Version)) - d.Set("ssl_enforcement", string(resp.SslEnforcement)) + d.Set("resource_group_name", id.ResourceGroup) if sku := resp.Sku; sku != nil { d.Set("sku_name", sku.Name) } - if err := d.Set("storage_profile", flattenPostgreSQLStorageProfile(resp.StorageProfile)); err != nil { - return fmt.Errorf("Error setting `storage_profile`: %+v", err) - } + 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) + d.Set("version", string(props.Version)) - // Computed - d.Set("fqdn", resp.FullyQualifiedDomainName) + d.Set("infrastructure_encryption_enabled", props.InfrastructureEncryption == postgresql.InfrastructureEncryptionEnabled) + d.Set("public_network_access_enabled", props.PublicNetworkAccess == postgresql.PublicNetworkAccessEnumEnabled) + d.Set("ssl_enforcement_enabled", props.SslEnforcement == postgresql.SslEnforcementEnumEnabled) + if err := d.Set("storage_profile", flattenPostgreSQLStorageProfile(props.StorageProfile)); err != nil { + return fmt.Errorf("setting `storage_profile`: %+v", err) + } + + if storage := props.StorageProfile; storage != nil { + d.Set("storage_mb", storage.StorageMB) + d.Set("backup_retention_days", storage.BackupRetentionDays) + d.Set("auto_grow_enabled", storage.StorageAutogrow == postgresql.StorageAutogrowEnabled) + d.Set("geo_redundant_backup_enabled", storage.GeoRedundantBackup == postgresql.Enabled) + } + + // Computed + d.Set("fqdn", props.FullyQualifiedDomainName) + } return tags.FlattenAndSet(d, resp.Tags) } @@ -342,20 +571,18 @@ func resourceArmPostgreSQLServerDelete(d *schema.ResourceData, meta interface{}) ctx, cancel := timeouts.ForDelete(meta.(*clients.Client).StopContext, d) defer cancel() - id, err := azure.ParseAzureResourceID(d.Id()) + id, err := parse.PostgresServerServerID(d.Id()) if err != nil { - return err + return fmt.Errorf("parsing Postgres 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 { if response.WasNotFound(future.Response()) { return nil } - return fmt.Errorf("Error deleting PostgreSQL Server %q (Resource Group %q): %+v", name, resourceGroup, err) + return fmt.Errorf("deleting PostgreSQL Server %q (Resource Group %q): %+v", id.Name, id.ResourceGroup, err) } if err = future.WaitForCompletionRef(ctx, client.Client); err != nil { @@ -363,7 +590,7 @@ func resourceArmPostgreSQLServerDelete(d *schema.ResourceData, meta interface{}) return nil } - return fmt.Errorf("Error waiting for deletion of PostgreSQL Server %q (Resource Group %q): %+v", name, resourceGroup, err) + return fmt.Errorf("waiting for deletion of PostgreSQL Server %q (Resource Group %q): %+v", id.Name, id.ResourceGroup, err) } return nil @@ -401,35 +628,56 @@ func expandServerSkuName(skuName string) (*postgresql.Sku, error) { } func expandAzureRmPostgreSQLStorageProfile(d *schema.ResourceData) *postgresql.StorageProfile { - storageprofiles := d.Get("storage_profile").([]interface{}) - storageprofile := storageprofiles[0].(map[string]interface{}) + storage := postgresql.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.StorageMB = utils.Int32(int32(storageprofile["storage_mb"].(int))) + storage.StorageAutogrow = postgresql.StorageAutogrow(storageprofile["auto_grow"].(string)) + storage.GeoRedundantBackup = postgresql.GeoRedundantBackup(storageprofile["geo_redundant_backup"].(string)) + } - backupRetentionDays := storageprofile["backup_retention_days"].(int) - geoRedundantBackup := storageprofile["geo_redundant_backup"].(string) - storageMB := storageprofile["storage_mb"].(int) - autoGrow := storageprofile["auto_grow"].(string) + // 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 = postgresql.StorageAutogrowDisabled + if v.(bool) { + storage.StorageAutogrow = postgresql.StorageAutogrowEnabled + } + } - return &postgresql.StorageProfile{ - BackupRetentionDays: utils.Int32(int32(backupRetentionDays)), - GeoRedundantBackup: postgresql.GeoRedundantBackup(geoRedundantBackup), - StorageMB: utils.Int32(int32(storageMB)), - StorageAutogrow: postgresql.StorageAutogrow(autoGrow), + if v, ok := d.GetOk("backup_retention_days"); ok { + storage.BackupRetentionDays = utils.Int32(int32(v.(int))) } + + if v, ok := d.GetOk("geo_redundant_backup_enabled"); ok { + storage.GeoRedundantBackup = postgresql.Disabled + if v.(bool) { + storage.GeoRedundantBackup = postgresql.Enabled + } + } + + if v, ok := d.GetOk("storage_mb"); ok { + storage.StorageMB = utils.Int32(int32(v.(int))) + } + + return &storage } func flattenPostgreSQLStorageProfile(resp *postgresql.StorageProfile) []interface{} { values := map[string]interface{}{} + values["storage_mb"] = nil 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["auto_grow"] = string(resp.StorageAutogrow) values["geo_redundant_backup"] = string(resp.GeoRedundantBackup) return []interface{}{values} diff --git a/azurerm/internal/services/postgres/resource_arm_postgresql_virtual_network_rule.go b/azurerm/internal/services/postgres/resource_arm_postgresql_virtual_network_rule.go index 807775b8b0e7..cf3e6413a122 100644 --- a/azurerm/internal/services/postgres/resource_arm_postgresql_virtual_network_rule.go +++ b/azurerm/internal/services/postgres/resource_arm_postgresql_virtual_network_rule.go @@ -12,9 +12,10 @@ 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/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/postgres/validate" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/timeouts" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils" ) @@ -41,7 +42,7 @@ func resourceArmPostgreSQLVirtualNetworkRule() *schema.Resource { Type: schema.TypeString, Required: true, ForceNew: true, - ValidateFunc: validate.VirtualNetworkRuleName, + ValidateFunc: azValidate.VirtualNetworkRuleName, }, "resource_group_name": azure.SchemaResourceGroupName(), @@ -50,7 +51,7 @@ func resourceArmPostgreSQLVirtualNetworkRule() *schema.Resource { Type: schema.TypeString, Required: true, ForceNew: true, - ValidateFunc: ValidatePSQLServerName, + ValidateFunc: validate.PostgresServerServerName, }, "subnet_id": { diff --git a/azurerm/internal/services/postgres/tests/resource_arm_postgresql_server_test.go b/azurerm/internal/services/postgres/tests/resource_arm_postgresql_server_test.go index ce802d23e433..039481108944 100644 --- a/azurerm/internal/services/postgres/tests/resource_arm_postgresql_server_test.go +++ b/azurerm/internal/services/postgres/tests/resource_arm_postgresql_server_test.go @@ -3,12 +3,12 @@ package tests import ( "fmt" "testing" + "time" "github.com/hashicorp/terraform-plugin-sdk/helper/resource" "github.com/hashicorp/terraform-plugin-sdk/terraform" "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/acceptance" "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/utils" ) @@ -20,12 +20,34 @@ func TestAccAzureRMPostgreSQLServer_basicNinePointFive(t *testing.T) { CheckDestroy: testCheckAzureRMPostgreSQLServerDestroy, Steps: []resource.TestStep{ { - Config: testAccAzureRMPostgreSQLServer_basicNinePointFive(data), + Config: testAccAzureRMPostgreSQLServer_basic(data, "9.5"), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMPostgreSQLServerExists(data.ResourceName), + ), + }, + data.ImportStep("administrator_login_password"), + }, + }) +} + +func TestAccAzureRMPostgreSQLServer_basicNinePointFiveDeprecated(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_postgresql_server", "test") + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acceptance.PreCheck(t) }, + Providers: acceptance.SupportedProviders, + CheckDestroy: testCheckAzureRMPostgreSQLServerDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAzureRMPostgreSQLServer_basicDeprecated(data, "9.5"), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMPostgreSQLServerExists(data.ResourceName), + ), + }, + data.ImportStep("administrator_login_password"), + { + Config: testAccAzureRMPostgreSQLServer_basic(data, "9.5"), Check: resource.ComposeTestCheckFunc( testCheckAzureRMPostgreSQLServerExists(data.ResourceName), - resource.TestCheckResourceAttr(data.ResourceName, "administrator_login", "acctestun"), - resource.TestCheckResourceAttr(data.ResourceName, "version", "9.5"), - resource.TestCheckResourceAttr(data.ResourceName, "ssl_enforcement", "Enabled"), ), }, data.ImportStep("administrator_login_password"), @@ -41,12 +63,9 @@ func TestAccAzureRMPostgreSQLServer_basicNinePointSix(t *testing.T) { CheckDestroy: testCheckAzureRMPostgreSQLServerDestroy, Steps: []resource.TestStep{ { - Config: testAccAzureRMPostgreSQLServer_basicNinePointSix(data), + Config: testAccAzureRMPostgreSQLServer_basic(data, "9.6"), Check: resource.ComposeTestCheckFunc( testCheckAzureRMPostgreSQLServerExists(data.ResourceName), - resource.TestCheckResourceAttr(data.ResourceName, "administrator_login", "acctestun"), - resource.TestCheckResourceAttr(data.ResourceName, "version", "9.6"), - resource.TestCheckResourceAttr(data.ResourceName, "ssl_enforcement", "Enabled"), ), }, data.ImportStep("administrator_login_password"), @@ -62,12 +81,9 @@ func TestAccAzureRMPostgreSQLServer_basicTenPointZero(t *testing.T) { CheckDestroy: testCheckAzureRMPostgreSQLServerDestroy, Steps: []resource.TestStep{ { - Config: testAccAzureRMPostgreSQLServer_basicTenPointZero(data), + Config: testAccAzureRMPostgreSQLServer_basic(data, "10.0"), Check: resource.ComposeTestCheckFunc( testCheckAzureRMPostgreSQLServerExists(data.ResourceName), - resource.TestCheckResourceAttr(data.ResourceName, "administrator_login", "acctestun"), - resource.TestCheckResourceAttr(data.ResourceName, "version", "10.0"), - resource.TestCheckResourceAttr(data.ResourceName, "ssl_enforcement", "Enabled"), ), }, data.ImportStep("administrator_login_password"), @@ -83,12 +99,9 @@ func TestAccAzureRMPostgreSQLServer_basicEleven(t *testing.T) { CheckDestroy: testCheckAzureRMPostgreSQLServerDestroy, Steps: []resource.TestStep{ { - Config: testAccAzureRMPostgreSQLServer_basicEleven(data), + Config: testAccAzureRMPostgreSQLServer_basic(data, "11"), Check: resource.ComposeTestCheckFunc( testCheckAzureRMPostgreSQLServerExists(data.ResourceName), - resource.TestCheckResourceAttr(data.ResourceName, "administrator_login", "acctestun"), - resource.TestCheckResourceAttr(data.ResourceName, "version", "11"), - resource.TestCheckResourceAttr(data.ResourceName, "ssl_enforcement", "Enabled"), ), }, data.ImportStep("administrator_login_password"), @@ -96,25 +109,43 @@ func TestAccAzureRMPostgreSQLServer_basicEleven(t *testing.T) { }) } -func TestAccAzureRMPostgreSQLServer_requiresImport(t *testing.T) { - if !features.ShouldResourcesBeImported() { - t.Skip("Skipping since resources aren't required to be imported") - return - } +func TestAccAzureRMPostgreSQLServer_autogrowOnly(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_postgresql_server", "test") + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acceptance.PreCheck(t) }, + Providers: acceptance.SupportedProviders, + CheckDestroy: testCheckAzureRMPostgreSQLServerDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAzureRMPostgreSQLServer_autogrow(data, "11"), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMPostgreSQLServerExists(data.ResourceName), + ), + }, + data.ImportStep("administrator_login_password"), + { + Config: testAccAzureRMPostgreSQLServer_basic(data, "11"), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMPostgreSQLServerExists(data.ResourceName), + ), + }, + data.ImportStep("administrator_login_password"), + }, + }) +} +func TestAccAzureRMPostgreSQLServer_requiresImport(t *testing.T) { data := acceptance.BuildTestData(t, "azurerm_postgresql_server", "test") + resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { acceptance.PreCheck(t) }, Providers: acceptance.SupportedProviders, CheckDestroy: testCheckAzureRMPostgreSQLServerDestroy, Steps: []resource.TestStep{ { - Config: testAccAzureRMPostgreSQLServer_basicTenPointZero(data), + Config: testAccAzureRMPostgreSQLServer_basic(data, "10.0"), Check: resource.ComposeTestCheckFunc( testCheckAzureRMPostgreSQLServerExists(data.ResourceName), - resource.TestCheckResourceAttr(data.ResourceName, "administrator_login", "acctestun"), - resource.TestCheckResourceAttr(data.ResourceName, "version", "10.0"), - resource.TestCheckResourceAttr(data.ResourceName, "ssl_enforcement", "Enabled"), ), }, data.RequiresImportErrorStep(testAccAzureRMPostgreSQLServer_requiresImport), @@ -122,7 +153,7 @@ func TestAccAzureRMPostgreSQLServer_requiresImport(t *testing.T) { }) } -func TestAccAzureRMPostgreSQLServer_basicMaxStorage(t *testing.T) { +func TestAccAzureRMPostgreSQLServer_complete(t *testing.T) { data := acceptance.BuildTestData(t, "azurerm_postgresql_server", "test") resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { acceptance.PreCheck(t) }, @@ -130,12 +161,9 @@ func TestAccAzureRMPostgreSQLServer_basicMaxStorage(t *testing.T) { CheckDestroy: testCheckAzureRMPostgreSQLServerDestroy, Steps: []resource.TestStep{ { - Config: testAccAzureRMPostgreSQLServer_basicMaxStorage(data), + Config: testAccAzureRMPostgreSQLServer_complete(data, "9.6"), Check: resource.ComposeTestCheckFunc( testCheckAzureRMPostgreSQLServerExists(data.ResourceName), - resource.TestCheckResourceAttr(data.ResourceName, "administrator_login", "acctestun"), - resource.TestCheckResourceAttr(data.ResourceName, "version", "9.6"), - resource.TestCheckResourceAttr(data.ResourceName, "ssl_enforcement", "Enabled"), ), }, data.ImportStep("administrator_login_password"), @@ -143,7 +171,7 @@ func TestAccAzureRMPostgreSQLServer_basicMaxStorage(t *testing.T) { }) } -func TestAccAzureRMPostgreSQLServer_generalPurpose(t *testing.T) { +func TestAccAzureRMPostgreSQLServer_updatedDeprecated(t *testing.T) { data := acceptance.BuildTestData(t, "azurerm_postgresql_server", "test") resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { acceptance.PreCheck(t) }, @@ -151,7 +179,21 @@ func TestAccAzureRMPostgreSQLServer_generalPurpose(t *testing.T) { CheckDestroy: testCheckAzureRMPostgreSQLServerDestroy, Steps: []resource.TestStep{ { - Config: testAccAzureRMPostgreSQLServer_generalPurpose(data), + Config: testAccAzureRMPostgreSQLServer_basicDeprecated(data, "9.6"), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMPostgreSQLServerExists(data.ResourceName), + ), + }, + data.ImportStep("administrator_login_password"), + { + Config: testAccAzureRMPostgreSQLServer_completeDeprecated(data, "9.6"), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMPostgreSQLServerExists(data.ResourceName), + ), + }, + data.ImportStep("administrator_login_password"), + { + Config: testAccAzureRMPostgreSQLServer_basicDeprecated(data, "9.6"), Check: resource.ComposeTestCheckFunc( testCheckAzureRMPostgreSQLServerExists(data.ResourceName), ), @@ -161,7 +203,7 @@ func TestAccAzureRMPostgreSQLServer_generalPurpose(t *testing.T) { }) } -func TestAccAzureRMPostgreSQLServer_memoryOptimized(t *testing.T) { +func TestAccAzureRMPostgreSQLServer_updated(t *testing.T) { data := acceptance.BuildTestData(t, "azurerm_postgresql_server", "test") resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { acceptance.PreCheck(t) }, @@ -169,7 +211,21 @@ func TestAccAzureRMPostgreSQLServer_memoryOptimized(t *testing.T) { CheckDestroy: testCheckAzureRMPostgreSQLServerDestroy, Steps: []resource.TestStep{ { - Config: testAccAzureRMPostgreSQLServer_memoryOptimizedGeoRedundant(data), + Config: testAccAzureRMPostgreSQLServer_basic(data, "9.6"), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMPostgreSQLServerExists(data.ResourceName), + ), + }, + data.ImportStep("administrator_login_password"), + { + Config: testAccAzureRMPostgreSQLServer_complete(data, "9.6"), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMPostgreSQLServerExists(data.ResourceName), + ), + }, + data.ImportStep("administrator_login_password"), + { + Config: testAccAzureRMPostgreSQLServer_basic(data, "9.6"), Check: resource.ComposeTestCheckFunc( testCheckAzureRMPostgreSQLServerExists(data.ResourceName), ), @@ -179,7 +235,7 @@ func TestAccAzureRMPostgreSQLServer_memoryOptimized(t *testing.T) { }) } -func TestAccAzureRMPostgreSQLServer_updatePassword(t *testing.T) { +func TestAccAzureRMPostgreSQLServer_completeDeprecatedUpdate(t *testing.T) { data := acceptance.BuildTestData(t, "azurerm_postgresql_server", "test") resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { acceptance.PreCheck(t) }, @@ -187,22 +243,24 @@ func TestAccAzureRMPostgreSQLServer_updatePassword(t *testing.T) { CheckDestroy: testCheckAzureRMPostgreSQLServerDestroy, Steps: []resource.TestStep{ { - Config: testAccAzureRMPostgreSQLServer_basicNinePointSix(data), + Config: testAccAzureRMPostgreSQLServer_completeDeprecated(data, "9.6"), Check: resource.ComposeTestCheckFunc( testCheckAzureRMPostgreSQLServerExists(data.ResourceName), ), }, + data.ImportStep("administrator_login_password"), { - Config: testAccAzureRMPostgreSQLServer_basicNinePointSixUpdatedPassword(data), + Config: testAccAzureRMPostgreSQLServer_complete(data, "9.6"), Check: resource.ComposeTestCheckFunc( testCheckAzureRMPostgreSQLServerExists(data.ResourceName), ), }, + data.ImportStep("administrator_login_password"), }, }) } -func TestAccAzureRMPostgreSQLServer_updated(t *testing.T) { +func TestAccAzureRMPostgreSQLServer_updateSKU(t *testing.T) { data := acceptance.BuildTestData(t, "azurerm_postgresql_server", "test") resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { acceptance.PreCheck(t) }, @@ -210,32 +268,24 @@ func TestAccAzureRMPostgreSQLServer_updated(t *testing.T) { CheckDestroy: testCheckAzureRMPostgreSQLServerDestroy, Steps: []resource.TestStep{ { - Config: testAccAzureRMPostgreSQLServer_basicNinePointSix(data), + Config: testAccAzureRMPostgreSQLServer_sku(data, "10.0", "GP_Gen5_2"), Check: resource.ComposeTestCheckFunc( testCheckAzureRMPostgreSQLServerExists(data.ResourceName), - resource.TestCheckResourceAttr(data.ResourceName, "sku_name", "GP_Gen5_2"), - resource.TestCheckResourceAttr(data.ResourceName, "version", "9.6"), - resource.TestCheckResourceAttr(data.ResourceName, "storage_profile.0.storage_mb", "51200"), - resource.TestCheckResourceAttr(data.ResourceName, "storage_profile.0.auto_grow", "Disabled"), - resource.TestCheckResourceAttr(data.ResourceName, "administrator_login", "acctestun"), ), }, + data.ImportStep("administrator_login_password"), { - Config: testAccAzureRMPostgreSQLServer_basicNinePointSixUpdated(data), + Config: testAccAzureRMPostgreSQLServer_sku(data, "10.0", "MO_Gen5_16"), Check: resource.ComposeTestCheckFunc( testCheckAzureRMPostgreSQLServerExists(data.ResourceName), - resource.TestCheckResourceAttr(data.ResourceName, "sku_name", "GP_Gen5_4"), - resource.TestCheckResourceAttr(data.ResourceName, "version", "9.6"), - resource.TestCheckResourceAttr(data.ResourceName, "storage_profile.0.storage_mb", "640000"), - resource.TestCheckResourceAttr(data.ResourceName, "storage_profile.0.auto_grow", "Enabled"), - resource.TestCheckResourceAttr(data.ResourceName, "administrator_login", "acctestun"), ), }, + data.ImportStep("administrator_login_password"), }, }) } -func TestAccAzureRMPostgreSQLServer_updateSKU(t *testing.T) { +func TestAccAzureRMPostgreSQLServer_createReplica(t *testing.T) { data := acceptance.BuildTestData(t, "azurerm_postgresql_server", "test") resource.ParallelTest(t, resource.TestCase{ PreCheck: func() { acceptance.PreCheck(t) }, @@ -243,28 +293,52 @@ func TestAccAzureRMPostgreSQLServer_updateSKU(t *testing.T) { CheckDestroy: testCheckAzureRMPostgreSQLServerDestroy, Steps: []resource.TestStep{ { - Config: testAccAzureRMPostgreSQLServer_generalPurpose(data), + Config: testAccAzureRMPostgreSQLServer_basic(data, "11"), Check: resource.ComposeTestCheckFunc( testCheckAzureRMPostgreSQLServerExists(data.ResourceName), - resource.TestCheckResourceAttr(data.ResourceName, "sku_name", "GP_Gen5_32"), - resource.TestCheckResourceAttr(data.ResourceName, "storage_profile.0.storage_mb", "640000"), - resource.TestCheckResourceAttr(data.ResourceName, "administrator_login", "acctestun"), ), }, + data.ImportStep("administrator_login_password"), { - Config: testAccAzureRMPostgreSQLServer_memoryOptimized(data), + Config: testAccAzureRMPostgreSQLServer_createReplica(data, "11"), Check: resource.ComposeTestCheckFunc( testCheckAzureRMPostgreSQLServerExists(data.ResourceName), - resource.TestCheckResourceAttr(data.ResourceName, "sku_name", "MO_Gen5_16"), - resource.TestCheckResourceAttr(data.ResourceName, "storage_profile.0.storage_mb", "4194304"), - resource.TestCheckResourceAttr(data.ResourceName, "administrator_login", "acctestun"), + testCheckAzureRMPostgreSQLServerExists("azurerm_postgresql_server.replica"), ), }, + data.ImportStep("administrator_login_password"), }, }) } -// +func TestAccAzureRMPostgreSQLServer_createPointInTimeRestore(t *testing.T) { + data := acceptance.BuildTestData(t, "azurerm_postgresql_server", "test") + restoreTime := time.Now().Add(11 * time.Minute) + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { acceptance.PreCheck(t) }, + Providers: acceptance.SupportedProviders, + CheckDestroy: testCheckAzureRMPostgreSQLServerDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAzureRMPostgreSQLServer_basic(data, "11"), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMPostgreSQLServerExists(data.ResourceName), + ), + }, + data.ImportStep("administrator_login_password"), + { + PreConfig: func() { time.Sleep(restoreTime.Sub(time.Now().Add(-7 * time.Minute))) }, + Config: testAccAzureRMPostgreSQLServer_createPointInTimeRestore(data, "11", restoreTime.Format(time.RFC3339)), + Check: resource.ComposeTestCheckFunc( + testCheckAzureRMPostgreSQLServerExists(data.ResourceName), + testCheckAzureRMPostgreSQLServerExists("azurerm_postgresql_server.restore"), + ), + }, + data.ImportStep("administrator_login_password"), + }, + }) +} func testCheckAzureRMPostgreSQLServerExists(resourceName string) resource.TestCheckFunc { return func(s *terraform.State) error { @@ -340,66 +414,19 @@ resource "azurerm_postgresql_server" "test" { 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" - auto_grow = "Disabled" - } - administrator_login = "acctestun" administrator_login_password = "H@Sh1CoR3!" - version = "%s" - ssl_enforcement = "Enabled" -} -`, data.RandomInteger, data.Locations.Primary, data.RandomInteger, version) -} -func testAccAzureRMPostgreSQLServer_basicNinePointFive(data acceptance.TestData) string { - return testAccAzureRMPostgreSQLServer_basic(data, "9.5") -} - -func testAccAzureRMPostgreSQLServer_basicNinePointSix(data acceptance.TestData) string { - return testAccAzureRMPostgreSQLServer_basic(data, "9.6") -} - -func testAccAzureRMPostgreSQLServer_basicTenPointZero(data acceptance.TestData) string { - return testAccAzureRMPostgreSQLServer_basic(data, "10.0") -} - -func testAccAzureRMPostgreSQLServer_basicEleven(data acceptance.TestData) string { - return testAccAzureRMPostgreSQLServer_basic(data, "11") -} - -func testAccAzureRMPostgreSQLServer_requiresImport(data acceptance.TestData) string { - template := testAccAzureRMPostgreSQLServer_basicTenPointZero(data) - return fmt.Sprintf(` -%s - -resource "azurerm_postgresql_server" "import" { - name = azurerm_postgresql_server.test.name - location = azurerm_postgresql_server.test.location - resource_group_name = azurerm_postgresql_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 = "%s" + storage_mb = 51200 - administrator_login = "acctestun" - administrator_login_password = "H@Sh1CoR3!" - version = "10.0" - ssl_enforcement = "Enabled" + ssl_enforcement_enabled = true } -`, template) +`, data.RandomInteger, data.Locations.Primary, data.RandomInteger, version) } -func testAccAzureRMPostgreSQLServer_basicNinePointSixUpdatedPassword(data acceptance.TestData) string { +func testAccAzureRMPostgreSQLServer_autogrow(data acceptance.TestData, version string) string { return fmt.Sprintf(` provider "azurerm" { features {} @@ -415,23 +442,19 @@ resource "azurerm_postgresql_server" "test" { 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!" - storage_profile { - storage_mb = 51200 - backup_retention_days = 7 - geo_redundant_backup = "Disabled" - } + sku_name = "GP_Gen5_2" + version = "%s" + auto_grow_enabled = true - administrator_login = "acctestun" - administrator_login_password = "R3dH0TCh1l1P3pp3rs!" - version = "9.6" - ssl_enforcement = "Disabled" + ssl_enforcement_enabled = true } -`, data.RandomInteger, data.Locations.Primary, data.RandomInteger) +`, data.RandomInteger, data.Locations.Primary, data.RandomInteger, version) } -func testAccAzureRMPostgreSQLServer_basicNinePointSixUpdated(data acceptance.TestData) string { +func testAccAzureRMPostgreSQLServer_basicDeprecated(data acceptance.TestData, version string) string { return fmt.Sprintf(` provider "azurerm" { features {} @@ -447,23 +470,44 @@ resource "azurerm_postgresql_server" "test" { location = azurerm_resource_group.test.location resource_group_name = azurerm_resource_group.test.name - sku_name = "GP_Gen5_4" + administrator_login = "acctestun" + administrator_login_password = "H@Sh1CoR3!" + + sku_name = "GP_Gen5_2" + version = "%s" storage_profile { - storage_mb = 640000 - backup_retention_days = 7 - geo_redundant_backup = "Disabled" + storage_mb = 51200 } - administrator_login = "acctestun" - administrator_login_password = "H@Sh1CoR3!" - version = "9.6" - ssl_enforcement = "Enabled" + ssl_enforcement_enabled = true +} +`, data.RandomInteger, data.Locations.Primary, data.RandomInteger, version) +} + +func testAccAzureRMPostgreSQLServer_requiresImport(data acceptance.TestData) string { + template := testAccAzureRMPostgreSQLServer_basic(data, "10.0") + return fmt.Sprintf(` +%s + +resource "azurerm_postgresql_server" "import" { + name = azurerm_postgresql_server.test.name + location = azurerm_postgresql_server.test.location + resource_group_name = azurerm_postgresql_server.test.resource_group_name + + administrator_login = azurerm_postgresql_server.test.administrator_login + administrator_login_password = azurerm_postgresql_server.test.administrator_login_password + + sku_name = azurerm_postgresql_server.test.sku_name + version = azurerm_postgresql_server.test.version + storage_mb = azurerm_postgresql_server.test.storage_profile.storage_mb + + ssl_enforcement_enabled = azurerm_postgresql_server.test.ssl_enforcement_enabled } -`, data.RandomInteger, data.Locations.Primary, data.RandomInteger) +`, template) } -func testAccAzureRMPostgreSQLServer_basicMaxStorage(data acceptance.TestData) string { +func testAccAzureRMPostgreSQLServer_completeDeprecated(data acceptance.TestData, version string) string { return fmt.Sprintf(` provider "azurerm" { features {} @@ -479,24 +523,29 @@ resource "azurerm_postgresql_server" "test" { 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_4" + + administrator_login = "acctestun" + administrator_login_password = "H@Sh1CoR3!" + + infrastructure_encryption_enabled = true + public_network_access_enabled = false + ssl_minimal_tls_version_enforced = "TLS1_2" + + ssl_enforcement = "Enabled" storage_profile { - storage_mb = 947200 + storage_mb = 640000 backup_retention_days = 7 - geo_redundant_backup = "Disabled" + geo_redundant_backup = "Enabled" auto_grow = "Enabled" } - - administrator_login = "acctestun" - administrator_login_password = "H@Sh1CoR3!" - version = "9.6" - ssl_enforcement = "Enabled" } -`, data.RandomInteger, data.Locations.Primary, data.RandomInteger) +`, data.RandomInteger, data.Locations.Primary, data.RandomInteger, version) } -func testAccAzureRMPostgreSQLServer_generalPurpose(data acceptance.TestData) string { +func testAccAzureRMPostgreSQLServer_complete(data acceptance.TestData, version string) string { return fmt.Sprintf(` provider "azurerm" { features {} @@ -512,23 +561,26 @@ resource "azurerm_postgresql_server" "test" { location = azurerm_resource_group.test.location resource_group_name = azurerm_resource_group.test.name - sku_name = "GP_Gen5_32" + administrator_login = "acctestun" + administrator_login_password = "H@Sh1CoR3!updated" - storage_profile { - storage_mb = 640000 - backup_retention_days = 7 - geo_redundant_backup = "Disabled" - } + sku_name = "GP_Gen5_4" + version = "%s" + storage_mb = 640000 - administrator_login = "acctestun" - administrator_login_password = "H@Sh1CoR3!" - version = "9.6" - ssl_enforcement = "Enabled" + backup_retention_days = 7 + geo_redundant_backup_enabled = true + auto_grow_enabled = true + + infrastructure_encryption_enabled = true + public_network_access_enabled = false + ssl_enforcement_enabled = true + ssl_minimal_tls_version_enforced = "TLS1_2" } -`, data.RandomInteger, data.Locations.Primary, data.RandomInteger) +`, data.RandomInteger, data.Locations.Primary, data.RandomInteger, version) } -func testAccAzureRMPostgreSQLServer_memoryOptimized(data acceptance.TestData) string { +func testAccAzureRMPostgreSQLServer_sku(data acceptance.TestData, version, sku string) string { return fmt.Sprintf(` provider "azurerm" { features {} @@ -544,49 +596,56 @@ resource "azurerm_postgresql_server" "test" { 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 = "Disabled" - } - administrator_login = "acctestun" administrator_login_password = "H@Sh1CoR3!" - version = "9.6" - ssl_enforcement = "Enabled" + + sku_name = "%s" + storage_mb = 51200 + version = "%s" + + ssl_enforcement_enabled = true } -`, data.RandomInteger, data.Locations.Primary, data.RandomInteger) +`, data.RandomInteger, data.Locations.Primary, data.RandomInteger, sku, version) } -func testAccAzureRMPostgreSQLServer_memoryOptimizedGeoRedundant(data acceptance.TestData) string { +func testAccAzureRMPostgreSQLServer_createReplica(data acceptance.TestData, version string) string { return fmt.Sprintf(` -provider "azurerm" { - features {} -} +%[1]s -resource "azurerm_resource_group" "test" { - name = "acctestRG-psql-%d" - location = "%s" +resource "azurerm_postgresql_server" "replica" { + name = "acctest-psql-server-%[2]d-replica" + location = azurerm_resource_group.test.location + resource_group_name = azurerm_resource_group.test.name + + sku_name = "GP_Gen5_2" + version = "%[3]s" + + create_mode = "Replica" + creation_source_server_id = azurerm_postgresql_server.test.id + + ssl_enforcement_enabled = true +} +`, testAccAzureRMPostgreSQLServer_basic(data, version), data.RandomInteger, version) } -resource "azurerm_postgresql_server" "test" { - name = "acctest-psql-server-%d" +func testAccAzureRMPostgreSQLServer_createPointInTimeRestore(data acceptance.TestData, version, restoreTime string) string { + return fmt.Sprintf(` +%[1]s + +resource "azurerm_postgresql_server" "restore" { + name = "acctest-psql-server-%[2]d-restore" 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 = "%[4]s" + storage_mb = 51200 - administrator_login = "acctestun" - administrator_login_password = "H@Sh1CoR3!" - version = "9.6" - ssl_enforcement = "Enabled" + create_mode = "PointInTimeRestore" + creation_source_server_id = azurerm_postgresql_server.test.id + restore_point_in_time = "%[3]s" + + ssl_enforcement_enabled = true } -`, data.RandomInteger, data.Locations.Primary, data.RandomInteger) +`, testAccAzureRMPostgreSQLServer_basic(data, version), data.RandomInteger, restoreTime, version) } diff --git a/azurerm/internal/services/postgres/validate/postgres.go b/azurerm/internal/services/postgres/validate/postgres.go new file mode 100644 index 000000000000..5563c06b431c --- /dev/null +++ b/azurerm/internal/services/postgres/validate/postgres.go @@ -0,0 +1,48 @@ +package validate + +import ( + "fmt" + "regexp" + + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/validate" + "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/services/postgres/parse" +) + +func PostgresServerServerName(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 at most 63 characters", k)) + } + + return nil, nil +} + +func PostgresDatabaseCollation(i interface{}, k string) (warnings []string, errors []error) { + v, ok := i.(string) + if !ok { + errors = append(errors, fmt.Errorf("expected type of %s to be string", k)) + return + } + + matched, _ := regexp.MatchString(`^[-A-Za-z0-9_. ]+$`, v) + + if !matched { + errors = append(errors, fmt.Errorf("%s contains invalid characters, only alphanumeric, underscore, space or hyphen characters are supported, got %s", k, v)) + return + } + + return warnings, errors +} + +func PostgresServerServerID(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.PostgresServerServerID(v); err != nil { + errors = append(errors, fmt.Errorf("Can not parse %q as a Postgres Server resource id: %v", k, err)) + } + + return warnings, errors +} diff --git a/azurerm/internal/services/postgres/validation_test.go b/azurerm/internal/services/postgres/validate/postgres_test.go similarity index 89% rename from azurerm/internal/services/postgres/validation_test.go rename to azurerm/internal/services/postgres/validate/postgres_test.go index f465936116c7..4a5b1940f081 100644 --- a/azurerm/internal/services/postgres/validation_test.go +++ b/azurerm/internal/services/postgres/validate/postgres_test.go @@ -1,10 +1,10 @@ -package postgres +package validate import ( "testing" ) -func TestValidatePSQLServerName(t *testing.T) { +func TestValidatePostgresServerServerName(t *testing.T) { testData := []struct { input string expected bool @@ -59,7 +59,7 @@ func TestValidatePSQLServerName(t *testing.T) { for _, v := range testData { t.Logf("[DEBUG] Testing %q..", v.input) - _, errors := ValidatePSQLServerName(v.input, "name") + _, errors := PostgresServerServerName(v.input, "name") actual := len(errors) == 0 if v.expected != actual { t.Fatalf("Expected %t but got %t", v.expected, actual) diff --git a/website/docs/r/postgresql_server.html.markdown b/website/docs/r/postgresql_server.html.markdown index a4fa40007cc0..b5678303af86 100644 --- a/website/docs/r/postgresql_server.html.markdown +++ b/website/docs/r/postgresql_server.html.markdown @@ -51,29 +51,35 @@ The following arguments are supported: * `sku_name` - (Required) Specifies the SKU Name for this PostgreSQL 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/postgresql/servers/create#sku). -* `storage_profile` - (Required) A `storage_profile` block as defined below. +* `ssl_enforcement` - (Required) Specifies if SSL should be enforced on connections. Possible values are `Enabled` and `Disabled`. -* `administrator_login` - (Required) The Administrator Login for the PostgreSQL Server. Changing this forces a new resource to be created. +* `administrator_login` - (Optional) The Administrator Login for the PostgreSQL Server. Required when `create_mode` is `Default`. Changing this forces a new resource to be created. -* `administrator_login_password` - (Required) The Password associated with the `administrator_login` for the PostgreSQL Server. +* `administrator_login_password` - (Optional) The Password associated with the `administrator_login` for the PostgreSQL Server. Required when `create_mode` is `Default`. -* `version` - (Required) Specifies the version of PostgreSQL to use. Valid values are `9.5`, `9.6`, `10`, `10.0`, and `11`. Changing this forces a new resource to be created. +* `version` - (Optional) Specifies the version of PostgreSQL to use. Valid values are `9.5`, `9.6`, `10`, `10.0`, and `11`. Changing this forces a new resource to be created. -* `ssl_enforcement` - (Required) Specifies if SSL should be enforced on connections. Possible values are `Enabled` and `Disabled`. +* `auto_grow_enbled` - (Optional) Enable/Disable auto-growing of the storage. Valid values for this property are `Enabled` or `Disabled`. 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`. + +* `backup_retention_days` - (Optional) Backup retention days for the server, supported values are between `7` and `35` days. -* `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 support for the Basic tier. ---- +* `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.` -`storage_profile` supports the following: +* `creation_source_server_id` - (Optional) For creation modes other then default the source server ID to use. -* `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/postgresql/servers/create#StorageProfile). +* `infrastructure_encryption_enabled` - (Optional) Whether or not infrastructure is encrypted for this server. Defaults to `false`. + +* `public_network_access_enabled` - (Optional) Whether or not public network access is allowed for this server. Defaults to `true`. -* `backup_retention_days` - (Optional) Backup retention days for the server, supported values are between `7` and `35` days. +* `restore_point_in_time` - (Optional) When `create_mode` is `PointInTimeRestore` the point in time to restore from `creation_source_server_id`. -* `geo_redundant_backup` - (Optional) Enable/Disable Geo-redundant for server backup. Valid values for this property are `Enabled` or `Disabled`, not supported for the `basic` tier. 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. The Basic tier only offers locally redundant backup storage. +* `ssl_minimal_tls_version_enforced` - (Optional) The mimimun TLS version to support on the sever. Possible values are `TLSEnforcementDisabled`, `TLS1_0`, `TLS1_1`, and `TLS1_2`. Defaults to `TLSEnforcementDisabled`. + +* `storage_mb` - (Optional) 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/postgresql/servers/create#StorageProfile). -* `auto_grow` - (Optional) Enable/Disable auto-growing of the storage. Valid values for this property are `Enabled` or `Disabled`. 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 `Enabled`. +* `tags` - (Optional) A mapping of tags to assign to the resource. ## Attributes Reference