Skip to content

Commit

Permalink
azurerm_synapse_sql_pool - add support for `geo_backup_policy_enabl…
Browse files Browse the repository at this point in the history
…ed` and fix `recovery_database_id` (hashicorp#20010)

* add recoverableDatabaseId

* add recoverableDatabaseId

* set recovery_database_id and add geo_backup_policy_enabled property

* simplify error message

* remove recovery_database_id from read and add docs

* docs
  • Loading branch information
catriona-m authored Jan 16, 2023
1 parent dbdefff commit 48a2dd2
Show file tree
Hide file tree
Showing 9 changed files with 449 additions and 6 deletions.
5 changes: 5 additions & 0 deletions internal/services/synapse/client/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ type Client struct {
SparkPoolClient *synapse.BigDataPoolsClient
SqlPoolClient *synapse.SQLPoolsClient
SqlPoolExtendedBlobAuditingPoliciesClient *synapse.ExtendedSQLPoolBlobAuditingPoliciesClient
SqlPoolGeoBackupPoliciesClient *synapse.SQLPoolGeoBackupPoliciesClient
SqlPoolSecurityAlertPolicyClient *synapse.SQLPoolSecurityAlertPoliciesClient
SqlPoolTransparentDataEncryptionClient *synapse.SQLPoolTransparentDataEncryptionsClient
SqlPoolVulnerabilityAssessmentsClient *synapse.SQLPoolVulnerabilityAssessmentsClient
Expand Down Expand Up @@ -63,6 +64,9 @@ func NewClient(o *common.ClientOptions) *Client {
sqlPoolExtendedBlobAuditingPoliciesClient := synapse.NewExtendedSQLPoolBlobAuditingPoliciesClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId)
o.ConfigureClient(&sqlPoolExtendedBlobAuditingPoliciesClient.Client, o.ResourceManagerAuthorizer)

sqlPoolGeoBackupPoliciesClient := synapse.NewSQLPoolGeoBackupPoliciesClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId)
o.ConfigureClient(&sqlPoolGeoBackupPoliciesClient.Client, o.ResourceManagerAuthorizer)

sqlPoolSecurityAlertPolicyClient := synapse.NewSQLPoolSecurityAlertPoliciesClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId)
o.ConfigureClient(&sqlPoolSecurityAlertPolicyClient.Client, o.ResourceManagerAuthorizer)

Expand Down Expand Up @@ -111,6 +115,7 @@ func NewClient(o *common.ClientOptions) *Client {
SparkPoolClient: &sparkPoolClient,
SqlPoolClient: &sqlPoolClient,
SqlPoolExtendedBlobAuditingPoliciesClient: &sqlPoolExtendedBlobAuditingPoliciesClient,
SqlPoolGeoBackupPoliciesClient: &sqlPoolGeoBackupPoliciesClient,
SqlPoolSecurityAlertPolicyClient: &sqlPoolSecurityAlertPolicyClient,
SqlPoolTransparentDataEncryptionClient: &sqlPoolTransparentDataEncryptionClient,
SqlPoolVulnerabilityAssessmentsClient: &sqlPoolVulnerabilityAssessmentsClient,
Expand Down
75 changes: 75 additions & 0 deletions internal/services/synapse/parse/sql_pool_recoverable_database.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
package parse

// NOTE: this file is generated via 'go:generate' - manual changes will be overwritten

import (
"fmt"
"strings"

"github.com/hashicorp/go-azure-helpers/resourcemanager/resourceids"
)

type SqlPoolRecoverableDatabaseId struct {
SubscriptionId string
ResourceGroup string
WorkspaceName string
RecoverableDatabaseName string
}

func NewSqlPoolRecoverableDatabaseID(subscriptionId, resourceGroup, workspaceName, recoverableDatabaseName string) SqlPoolRecoverableDatabaseId {
return SqlPoolRecoverableDatabaseId{
SubscriptionId: subscriptionId,
ResourceGroup: resourceGroup,
WorkspaceName: workspaceName,
RecoverableDatabaseName: recoverableDatabaseName,
}
}

func (id SqlPoolRecoverableDatabaseId) String() string {
segments := []string{
fmt.Sprintf("Recoverable Database Name %q", id.RecoverableDatabaseName),
fmt.Sprintf("Workspace Name %q", id.WorkspaceName),
fmt.Sprintf("Resource Group %q", id.ResourceGroup),
}
segmentsStr := strings.Join(segments, " / ")
return fmt.Sprintf("%s: (%s)", "Sql Pool Recoverable Database", segmentsStr)
}

func (id SqlPoolRecoverableDatabaseId) ID() string {
fmtString := "/subscriptions/%s/resourceGroups/%s/providers/Microsoft.Synapse/workspaces/%s/recoverableDatabases/%s"
return fmt.Sprintf(fmtString, id.SubscriptionId, id.ResourceGroup, id.WorkspaceName, id.RecoverableDatabaseName)
}

// SqlPoolRecoverableDatabaseID parses a SqlPoolRecoverableDatabase ID into an SqlPoolRecoverableDatabaseId struct
func SqlPoolRecoverableDatabaseID(input string) (*SqlPoolRecoverableDatabaseId, error) {
id, err := resourceids.ParseAzureResourceID(input)
if err != nil {
return nil, err
}

resourceId := SqlPoolRecoverableDatabaseId{
SubscriptionId: id.SubscriptionID,
ResourceGroup: id.ResourceGroup,
}

if resourceId.SubscriptionId == "" {
return nil, fmt.Errorf("ID was missing the 'subscriptions' element")
}

if resourceId.ResourceGroup == "" {
return nil, fmt.Errorf("ID was missing the 'resourceGroups' element")
}

if resourceId.WorkspaceName, err = id.PopSegment("workspaces"); err != nil {
return nil, err
}
if resourceId.RecoverableDatabaseName, err = id.PopSegment("recoverableDatabases"); err != nil {
return nil, err
}

if err := id.ValidateNoEmptySegments(input); err != nil {
return nil, err
}

return &resourceId, nil
}
128 changes: 128 additions & 0 deletions internal/services/synapse/parse/sql_pool_recoverable_database_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
package parse

// NOTE: this file is generated via 'go:generate' - manual changes will be overwritten

import (
"testing"

"github.com/hashicorp/go-azure-helpers/resourcemanager/resourceids"
)

var _ resourceids.Id = SqlPoolRecoverableDatabaseId{}

func TestSqlPoolRecoverableDatabaseIDFormatter(t *testing.T) {
actual := NewSqlPoolRecoverableDatabaseID("12345678-1234-9876-4563-123456789012", "resGroup1", "workspace1", "database").ID()
expected := "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Synapse/workspaces/workspace1/recoverableDatabases/database"
if actual != expected {
t.Fatalf("Expected %q but got %q", expected, actual)
}
}

func TestSqlPoolRecoverableDatabaseID(t *testing.T) {
testData := []struct {
Input string
Error bool
Expected *SqlPoolRecoverableDatabaseId
}{

{
// empty
Input: "",
Error: true,
},

{
// missing SubscriptionId
Input: "/",
Error: true,
},

{
// missing value for SubscriptionId
Input: "/subscriptions/",
Error: true,
},

{
// missing ResourceGroup
Input: "/subscriptions/12345678-1234-9876-4563-123456789012/",
Error: true,
},

{
// missing value for ResourceGroup
Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/",
Error: true,
},

{
// missing WorkspaceName
Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Synapse/",
Error: true,
},

{
// missing value for WorkspaceName
Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Synapse/workspaces/",
Error: true,
},

{
// missing RecoverableDatabaseName
Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Synapse/workspaces/workspace1/",
Error: true,
},

{
// missing value for RecoverableDatabaseName
Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Synapse/workspaces/workspace1/recoverableDatabases/",
Error: true,
},

{
// valid
Input: "/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Synapse/workspaces/workspace1/recoverableDatabases/database",
Expected: &SqlPoolRecoverableDatabaseId{
SubscriptionId: "12345678-1234-9876-4563-123456789012",
ResourceGroup: "resGroup1",
WorkspaceName: "workspace1",
RecoverableDatabaseName: "database",
},
},

{
// upper-cased
Input: "/SUBSCRIPTIONS/12345678-1234-9876-4563-123456789012/RESOURCEGROUPS/RESGROUP1/PROVIDERS/MICROSOFT.SYNAPSE/WORKSPACES/WORKSPACE1/RECOVERABLEDATABASES/DATABASE",
Error: true,
},
}

for _, v := range testData {
t.Logf("[DEBUG] Testing %q", v.Input)

actual, err := SqlPoolRecoverableDatabaseID(v.Input)
if err != nil {
if v.Error {
continue
}

t.Fatalf("Expect a value but got an error: %s", err)
}
if v.Error {
t.Fatal("Expect an error but didn't get one")
}

if actual.SubscriptionId != v.Expected.SubscriptionId {
t.Fatalf("Expected %q but got %q for SubscriptionId", v.Expected.SubscriptionId, actual.SubscriptionId)
}
if actual.ResourceGroup != v.Expected.ResourceGroup {
t.Fatalf("Expected %q but got %q for ResourceGroup", v.Expected.ResourceGroup, actual.ResourceGroup)
}
if actual.WorkspaceName != v.Expected.WorkspaceName {
t.Fatalf("Expected %q but got %q for WorkspaceName", v.Expected.WorkspaceName, actual.WorkspaceName)
}
if actual.RecoverableDatabaseName != v.Expected.RecoverableDatabaseName {
t.Fatalf("Expected %q but got %q for RecoverableDatabaseName", v.Expected.RecoverableDatabaseName, actual.RecoverableDatabaseName)
}
}
}
1 change: 1 addition & 0 deletions internal/services/synapse/resourceids.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ package synapse
//go:generate go run ../../tools/generator-resource-id/main.go -path=./ -name=SparkPool -id=/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Synapse/workspaces/workspace1/bigDataPools/bigDataPool1
//go:generate go run ../../tools/generator-resource-id/main.go -path=./ -name=SqlPool -id=/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Synapse/workspaces/workspace1/sqlPools/sqlPool1
//go:generate go run ../../tools/generator-resource-id/main.go -path=./ -name=SqlPoolExtendedAuditingPolicy -id=/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Synapse/workspaces/workspace1/sqlPools/sqlPool1/extendedAuditingSettings/default
//go:generate go run ../../tools/generator-resource-id/main.go -path=./ -name=SqlPoolRecoverableDatabase -id=/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Synapse/workspaces/workspace1/recoverableDatabases/database
//go:generate go run ../../tools/generator-resource-id/main.go -path=./ -name=SqlPoolSecurityAlertPolicy -id=/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Synapse/workspaces/workspace1/sqlPools/sqlPool1/securityAlertPolicies/Default
//go:generate go run ../../tools/generator-resource-id/main.go -path=./ -name=SqlPoolVulnerabilityAssessment -id=/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Synapse/workspaces/workspace1/sqlPools/sqlPool1/vulnerabilityAssessments/default
//go:generate go run ../../tools/generator-resource-id/main.go -path=./ -name=SqlPoolWorkloadClassifier -id=/subscriptions/12345678-1234-9876-4563-123456789012/resourceGroups/resGroup1/providers/Microsoft.Synapse/workspaces/workspace1/sqlPools/sqlPool1/workloadGroups/workloadGroup1/workloadClassifiers/workloadClassifier1
Expand Down
63 changes: 57 additions & 6 deletions internal/services/synapse/synapse_sql_pool_resource.go
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,9 @@ func resourceSynapseSqlPool() *pluginsdk.Resource {
ConflictsWith: []string{"restore"},
ValidateFunc: validation.Any(
validate.SqlPoolID,
validate.SqlPoolRecoverableDatabaseID,
mssqlValidate.DatabaseID,
mssqlValidate.RecoverableDatabaseID,
),
},

Expand Down Expand Up @@ -155,6 +157,12 @@ func resourceSynapseSqlPool() *pluginsdk.Resource {
Optional: true,
},

"geo_backup_policy_enabled": {
Type: pluginsdk.TypeBool,
Default: true,
Optional: true,
},

"tags": tags.Schema(),
},
}
Expand All @@ -164,6 +172,7 @@ func resourceSynapseSqlPoolCreate(d *pluginsdk.ResourceData, meta interface{}) e
sqlClient := meta.(*clients.Client).Synapse.SqlPoolClient
sqlPTDEClient := meta.(*clients.Client).Synapse.SqlPoolTransparentDataEncryptionClient
workspaceClient := meta.(*clients.Client).Synapse.WorkspaceClient
geoBackUpClient := meta.(*clients.Client).Synapse.SqlPoolGeoBackupPoliciesClient
ctx, cancel := timeouts.ForCreate(meta.(*clients.Client).StopContext, d)
defer cancel()

Expand All @@ -185,7 +194,7 @@ func resourceSynapseSqlPoolCreate(d *pluginsdk.ResourceData, meta interface{}) e

workspace, err := workspaceClient.Get(ctx, workspaceId.ResourceGroup, workspaceId.Name)
if err != nil {
return fmt.Errorf("retrieving Synapse Workspace %q (Resource Group %q): %+v", workspaceId.Name, workspaceId.ResourceGroup, err)
return fmt.Errorf("retrieving %s: %+v", workspaceId, err)
}

mode := d.Get("create_mode").(string)
Expand Down Expand Up @@ -243,13 +252,26 @@ func resourceSynapseSqlPoolCreate(d *pluginsdk.ResourceData, meta interface{}) e
}
}

if !d.Get("geo_backup_policy_enabled").(bool) {
geoBackupParams := synapse.GeoBackupPolicy{
GeoBackupPolicyProperties: &synapse.GeoBackupPolicyProperties{
State: synapse.GeoBackupPolicyStateDisabled,
},
}

if _, err := geoBackUpClient.CreateOrUpdate(ctx, id.ResourceGroup, id.WorkspaceName, id.Name, geoBackupParams); err != nil {
return fmt.Errorf("setting `geo_backup_policy_enabled`: %+v", err)
}
}

d.SetId(id.ID())
return resourceSynapseSqlPoolRead(d, meta)
}

func resourceSynapseSqlPoolUpdate(d *pluginsdk.ResourceData, meta interface{}) error {
sqlClient := meta.(*clients.Client).Synapse.SqlPoolClient
sqlPTDEClient := meta.(*clients.Client).Synapse.SqlPoolTransparentDataEncryptionClient
geoBackUpClient := meta.(*clients.Client).Synapse.SqlPoolGeoBackupPoliciesClient
ctx, cancel := timeouts.ForUpdate(meta.(*clients.Client).StopContext, d)
defer cancel()

Expand All @@ -274,6 +296,22 @@ func resourceSynapseSqlPoolUpdate(d *pluginsdk.ResourceData, meta interface{}) e
}
}

if d.HasChange("geo_backup_policy_enabled") {
state := synapse.GeoBackupPolicyStateEnabled
if !d.Get("geo_backup_policy_enabled").(bool) {
state = synapse.GeoBackupPolicyStateDisabled
}

geoBackupParams := synapse.GeoBackupPolicy{
GeoBackupPolicyProperties: &synapse.GeoBackupPolicyProperties{
State: state,
},
}
if _, err := geoBackUpClient.CreateOrUpdate(ctx, id.ResourceGroup, id.WorkspaceName, id.Name, geoBackupParams); err != nil {
return fmt.Errorf("updating `geo_backup_policy_enabled`: %+v", err)
}
}

if d.HasChanges("sku_name", "tags") {
sqlPoolInfo := synapse.SQLPoolPatchInfo{
Sku: &synapse.Sku{
Expand Down Expand Up @@ -316,6 +354,7 @@ func resourceSynapseSqlPoolUpdate(d *pluginsdk.ResourceData, meta interface{}) e
func resourceSynapseSqlPoolRead(d *pluginsdk.ResourceData, meta interface{}) error {
sqlClient := meta.(*clients.Client).Synapse.SqlPoolClient
sqlPTDEClient := meta.(*clients.Client).Synapse.SqlPoolTransparentDataEncryptionClient
geoBackUpClient := meta.(*clients.Client).Synapse.SqlPoolGeoBackupPoliciesClient
ctx, cancel := timeouts.ForRead(meta.(*clients.Client).StopContext, d)
defer cancel()

Expand All @@ -337,7 +376,12 @@ func resourceSynapseSqlPoolRead(d *pluginsdk.ResourceData, meta interface{}) err

transparentDataEncryption, err := sqlPTDEClient.Get(ctx, id.ResourceGroup, id.WorkspaceName, id.Name)
if err != nil {
return fmt.Errorf("retrieving Transparent Data Encryption settings of Synapse SqlPool %q (Workspace %q / Resource Group %q): %+v", id.Name, id.WorkspaceName, id.ResourceGroup, err)
return fmt.Errorf("retrieving Transparent Data Encryption settings of %s: %+v", *id, err)
}

geoBackupPolicy, err := geoBackUpClient.Get(ctx, id.ResourceGroup, id.WorkspaceName, id.Name)
if err != nil {
return fmt.Errorf("retrieving Geo Backup Policy of %s: %+v", *id, err)
}

workspaceId := parse.NewWorkspaceID(id.SubscriptionId, id.ResourceGroup, id.WorkspaceName).ID()
Expand All @@ -349,8 +393,15 @@ func resourceSynapseSqlPoolRead(d *pluginsdk.ResourceData, meta interface{}) err
if props := resp.SQLPoolResourceProperties; props != nil {
d.Set("collation", props.Collation)
}
if props := transparentDataEncryption.TransparentDataEncryptionProperties; props != nil {
d.Set("data_encrypted", props.Status == synapse.TransparentDataEncryptionStatusEnabled)

geoBackupEnabled := true
if geoBackupProps := geoBackupPolicy.GeoBackupPolicyProperties; geoBackupProps != nil {
geoBackupEnabled = geoBackupProps.State == synapse.GeoBackupPolicyStateEnabled
}
d.Set("geo_backup_policy_enabled", geoBackupEnabled)

if tdeProps := transparentDataEncryption.TransparentDataEncryptionProperties; tdeProps != nil {
d.Set("data_encrypted", tdeProps.Status == synapse.TransparentDataEncryptionStatusEnabled)
}

// whole "restore" block is not returned. to avoid conflict, so set it from the old state
Expand All @@ -371,11 +422,11 @@ func resourceSynapseSqlPoolDelete(d *pluginsdk.ResourceData, meta interface{}) e

future, err := sqlClient.Delete(ctx, id.ResourceGroup, id.WorkspaceName, id.Name)
if err != nil {
return fmt.Errorf("deleting Synapse Sql Pool %q (Workspace %q / Resource Group %q): %+v", id.Name, id.WorkspaceName, id.ResourceGroup, err)
return fmt.Errorf("deleting %s: %+v", *id, err)
}

if err = future.WaitForCompletionRef(ctx, sqlClient.Client); err != nil {
return fmt.Errorf("waiting for deletion of Synapse Sql Pool %q (Workspace %q / Resource Group %q): %+v", id.Name, id.WorkspaceName, id.ResourceGroup, err)
return fmt.Errorf("waiting for deletion of %s: %+v", *id, err)
}
return nil
}
Expand Down
Loading

0 comments on commit 48a2dd2

Please sign in to comment.