Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

mssql_db: add support for transparent data encryption #13748

Merged
merged 14 commits into from
Nov 29, 2021
5 changes: 5 additions & 0 deletions internal/services/mssql/client/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ type Client struct {
ServerSecurityAlertPoliciesClient *sql.ServerSecurityAlertPoliciesClient
ServerVulnerabilityAssessmentsClient *sql.ServerVulnerabilityAssessmentsClient
ServersClient *sql.ServersClient
TransparentDataEncryptionsClient *sql.TransparentDataEncryptionsClient
VirtualMachinesClient *sqlvirtualmachine.SQLVirtualMachinesClient
VirtualNetworkRulesClient *sql.VirtualNetworkRulesClient
GeoBackupPoliciesClient *sql.GeoBackupPoliciesClient
Expand Down Expand Up @@ -110,6 +111,9 @@ func NewClient(o *common.ClientOptions) *Client {
sqlServerKeysClient := sql.NewServerKeysClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId)
o.ConfigureClient(&sqlServerKeysClient.Client, o.ResourceManagerAuthorizer)

sqlTransparentDataEncryptionsClient := sql.NewTransparentDataEncryptionsClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId)
o.ConfigureClient(&sqlTransparentDataEncryptionsClient.Client, o.ResourceManagerAuthorizer)

return &Client{
LongTermRetentionPoliciesClient: &LongTermRetentionPoliciesClient,
BackupShortTermRetentionPoliciesClient: &BackupShortTermRetentionPoliciesClient,
Expand All @@ -132,6 +136,7 @@ func NewClient(o *common.ClientOptions) *Client {
ServerConnectionPoliciesClient: &serverConnectionPoliciesClient,
ServerSecurityAlertPoliciesClient: &serverSecurityAlertPoliciesClient,
ServerVulnerabilityAssessmentsClient: &serverVulnerabilityAssessmentsClient,
TransparentDataEncryptionsClient: &sqlTransparentDataEncryptionsClient,
VirtualMachinesClient: &sqlVirtualMachinesClient,
VirtualNetworkRulesClient: &sqlVirtualNetworkRulesClient,
GeoBackupPoliciesClient: &geoBackupPoliciesClient,
Expand Down
76 changes: 72 additions & 4 deletions internal/services/mssql/mssql_database_resource.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,12 @@ import (

"github.com/Azure/azure-sdk-for-go/services/preview/sql/mgmt/v5.0/sql"
"github.com/Azure/go-autorest/autorest/date"

"github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
"github.com/hashicorp/terraform-provider-azurerm/helpers/tf"
azValidate "github.com/hashicorp/terraform-provider-azurerm/helpers/validate"
"github.com/hashicorp/terraform-provider-azurerm/internal/clients"
"github.com/hashicorp/terraform-provider-azurerm/internal/features"
"github.com/hashicorp/terraform-provider-azurerm/internal/locks"
"github.com/hashicorp/terraform-provider-azurerm/internal/services/mssql/helper"
"github.com/hashicorp/terraform-provider-azurerm/internal/services/mssql/migration"
Expand All @@ -27,7 +29,7 @@ import (
)

func resourceMsSqlDatabase() *pluginsdk.Resource {
return &pluginsdk.Resource{
resourceData := &pluginsdk.Resource{
Create: resourceMsSqlDatabaseCreateUpdate,
Read: resourceMsSqlDatabaseRead,
Update: resourceMsSqlDatabaseCreateUpdate,
Expand Down Expand Up @@ -304,7 +306,6 @@ func resourceMsSqlDatabase() *pluginsdk.Resource {
Optional: true,
Default: true,
},

"tags": tags.Schema(),
},

Expand All @@ -313,8 +314,30 @@ func resourceMsSqlDatabase() *pluginsdk.Resource {
// "hyperscale can not change to other sku
return strings.HasPrefix(old.(string), "HS") && !strings.HasPrefix(new.(string), "HS")
}),
),
func(ctx context.Context, d *schema.ResourceDiff, meta interface{}) error {
if !features.ThreePointOh() {
return nil
}
sku := d.Get("sku_name").(string)
if !strings.HasPrefix(sku, "DW") && !d.Get("transparent_data_encryption_enabled").(bool) {
return fmt.Errorf("transparent data encryption can only be disabled on Data Warehouse SKUs")
}
return nil
}),
}
if features.ThreePointOh() {
// TODO: Update docs with the following text:
//
// * `transparent_data_encryption_enabled` - If set to true, Transparent Data Encryption will be enabled on the database.
// -> **NOTE:** TDE cannot be disabled on servers with SKUs other than ones starting with DW.

resourceData.Schema["transparent_data_encryption_enabled"] = &pluginsdk.Schema{
Type: pluginsdk.TypeBool,
Optional: true,
Default: true,
}
}
return resourceData
}

func resourceMsSqlDatabaseImporter(ctx context.Context, d *pluginsdk.ResourceData, meta interface{}) ([]*pluginsdk.ResourceData, error) {
Expand Down Expand Up @@ -361,6 +384,7 @@ func resourceMsSqlDatabaseCreateUpdate(d *pluginsdk.ResourceData, meta interface
geoBackupPoliciesClient := meta.(*clients.Client).MSSQL.GeoBackupPoliciesClient
replicationLinksClient := meta.(*clients.Client).MSSQL.ReplicationLinksClient
resourcesClient := meta.(*clients.Client).Resource.ResourcesClient
transparentEncryptionClient := meta.(*clients.Client).MSSQL.TransparentDataEncryptionsClient

ctx, cancel := timeouts.ForCreateUpdate(meta.(*clients.Client).StopContext, d)
defer cancel()
Expand Down Expand Up @@ -558,6 +582,37 @@ func resourceMsSqlDatabaseCreateUpdate(d *pluginsdk.ResourceData, meta interface
return fmt.Errorf("waiting for create/update of %s: %+v", id, err)
}

if features.ThreePointOh() {
statusProperty := sql.TransparentDataEncryptionStatusDisabled
encryptionStatus := d.Get("transparent_data_encryption_enabled").(bool)
if encryptionStatus {
statusProperty = sql.TransparentDataEncryptionStatusEnabled
}
_, err := transparentEncryptionClient.CreateOrUpdate(ctx, id.ResourceGroup, id.ServerName, id.Name, sql.TransparentDataEncryption{
tombuildsstuff marked this conversation as resolved.
Show resolved Hide resolved
TransparentDataEncryptionProperties: &sql.TransparentDataEncryptionProperties{
Status: statusProperty,
},
})
if err != nil {
return fmt.Errorf("while enabling Transparent Data Encryption for %q: %+v", id.String(), err)
}

if err = pluginsdk.Retry(d.Timeout(pluginsdk.TimeoutCreate), func() *pluginsdk.RetryError {
koikonom marked this conversation as resolved.
Show resolved Hide resolved
c, err := client.Get(ctx, id.ResourceGroup, id.ServerName, id.Name)
if err != nil {
return resource.NonRetryableError(fmt.Errorf("while polling cluster %s for status: %+v", id.String(), err))
}
if c.DatabaseProperties.Status == sql.DatabaseStatusScaling {
return resource.RetryableError(fmt.Errorf("database %s is still scaling", id.String()))
}

return nil
}); err != nil {
return nil
}

}

d.SetId(id.ID())

// For datawarehouse SKUs only
Expand Down Expand Up @@ -651,6 +706,7 @@ func resourceMsSqlDatabaseRead(d *pluginsdk.ResourceData, meta interface{}) erro
longTermRetentionClient := meta.(*clients.Client).MSSQL.LongTermRetentionPoliciesClient
shortTermRetentionClient := meta.(*clients.Client).MSSQL.BackupShortTermRetentionPoliciesClient
geoBackupPoliciesClient := meta.(*clients.Client).MSSQL.GeoBackupPoliciesClient
transparentEncryptionClient := meta.(*clients.Client).MSSQL.TransparentDataEncryptionsClient

ctx, cancel := timeouts.ForRead(meta.(*clients.Client).StopContext, d)
defer cancel()
Expand Down Expand Up @@ -761,6 +817,18 @@ func resourceMsSqlDatabaseRead(d *pluginsdk.ResourceData, meta interface{}) erro
return fmt.Errorf("setting `geo_backup_enabled`: %+v", err)
}

if features.ThreePointOh() {
tde, err := transparentEncryptionClient.Get(ctx, id.ResourceGroup, id.ServerName, id.Name)
if err != nil {
return fmt.Errorf("while retrieving Transparent Data Encryption status of %q: %+v", id.String(), err)
}
tdeStatus := false
if tde.TransparentDataEncryptionProperties != nil && tde.TransparentDataEncryptionProperties.Status == sql.TransparentDataEncryptionStatusEnabled {
tdeStatus = true
}
d.Set("transparent_data_encryption_enabled", tdeStatus)
}

return tags.FlattenAndSet(d, resp.Tags)
}

Expand Down
74 changes: 74 additions & 0 deletions internal/services/mssql/mssql_database_resource_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,12 @@ package mssql_test
import (
"context"
"fmt"
"regexp"
"testing"
"time"

"github.com/hashicorp/terraform-provider-azurerm/internal/features"

"github.com/hashicorp/terraform-provider-azurerm/internal/acceptance"
"github.com/hashicorp/terraform-provider-azurerm/internal/acceptance/check"
"github.com/hashicorp/terraform-provider-azurerm/internal/clients"
Expand Down Expand Up @@ -695,6 +698,50 @@ func TestAccMsSqlDatabase_geoBackupPolicy(t *testing.T) {
})
}

func TestAccMsSqlDatabase_transitDataEncryption(t *testing.T) {
if !features.ThreePointOh() {
t.Skipf("This test runs only on 3.0")
}

data := acceptance.BuildTestData(t, "azurerm_mssql_database", "test")
r := MsSqlDatabaseResource{}

data.ResourceTest(t, r, []acceptance.TestStep{
{
Config: r.withTransitDataEncryptionOnDwSku(data, true),
Check: acceptance.ComposeTestCheckFunc(
check.That(data.ResourceName).ExistsInAzure(r),
check.That(data.ResourceName).Key("transparent_data_encryption_enabled").HasValue("true"),
),
},
data.ImportStep(),
{
Config: r.withTransitDataEncryptionOnDwSku(data, false),
Check: acceptance.ComposeTestCheckFunc(
check.That(data.ResourceName).ExistsInAzure(r),
check.That(data.ResourceName).Key("transparent_data_encryption_enabled").HasValue("false"),
),
},
data.ImportStep(),
})
}

func TestAccMsSqlDatabase_errorOnDisabledEncryption(t *testing.T) {
if !features.ThreePointOh() {
t.Skipf("This test runs only on 3.0")
}

data := acceptance.BuildTestData(t, "azurerm_mssql_database", "test")
r := MsSqlDatabaseResource{}

data.ResourceTest(t, r, []acceptance.TestStep{
{
Config: r.errorOnDisabledEncryption(data),
ExpectError: regexp.MustCompile("transparent data encryption can only be disabled on Data Warehouse SKUs"),
},
})
}

func (MsSqlDatabaseResource) Exists(ctx context.Context, client *clients.Client, state *pluginsdk.InstanceState) (*bool, error) {
id, err := parse.DatabaseID(state.ID)
if err != nil {
Expand Down Expand Up @@ -1678,3 +1725,30 @@ resource "azurerm_mssql_database" "test" {
}
`, r.template(data), data.RandomIntOfLength(15), data.RandomInteger)
}

func (r MsSqlDatabaseResource) withTransitDataEncryptionOnDwSku(data acceptance.TestData, state bool) string {
return fmt.Sprintf(`
%s

resource "azurerm_mssql_database" "test" {
name = "acctest-db-%d"
server_id = azurerm_mssql_server.test.id
sku_name = "DW100c"
transparent_data_encryption_enabled = %t
}

`, r.template(data), data.RandomInteger, state)
}

func (r MsSqlDatabaseResource) errorOnDisabledEncryption(data acceptance.TestData) string {
return fmt.Sprintf(`
%s

resource "azurerm_mssql_database" "test" {
name = "acctest-db-%d"
server_id = azurerm_mssql_server.test.id
transparent_data_encryption_enabled = false
}

`, r.template(data), data.RandomInteger)
}