Skip to content

Commit

Permalink
Including #24412
Browse files Browse the repository at this point in the history
* Added new fields to `azurerm_mssql_database` and
 `azurerm_mssql_server`

* Fixed tests

* Changed `auto_key_rotation_enabled` field into `transparent_data_encryption_key_automatic_rotation_enabled`

* Changed comment
  • Loading branch information
dkuzmenok authored Jan 11, 2024
1 parent 6826acf commit f5837dc
Show file tree
Hide file tree
Showing 8 changed files with 409 additions and 20 deletions.
41 changes: 41 additions & 0 deletions internal/services/mssql/mssql_database_data_source.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,10 @@ import (
"github.com/hashicorp/go-azure-helpers/lang/response"
"github.com/hashicorp/go-azure-helpers/resourcemanager/commonids"
"github.com/hashicorp/go-azure-helpers/resourcemanager/commonschema"
"github.com/hashicorp/go-azure-helpers/resourcemanager/identity"
"github.com/hashicorp/go-azure-helpers/resourcemanager/tags"
"github.com/hashicorp/go-azure-sdk/resource-manager/sql/2023-02-01-preview/databases"
"github.com/hashicorp/go-azure-sdk/resource-manager/sql/2023-02-01-preview/transparentdataencryptions"
"github.com/hashicorp/terraform-provider-azurerm/internal/clients"
"github.com/hashicorp/terraform-provider-azurerm/internal/services/mssql/validate"
"github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk"
Expand Down Expand Up @@ -90,13 +92,31 @@ func dataSourceMsSqlDatabase() *pluginsdk.Resource {
Computed: true,
},

"identity": commonschema.UserAssignedIdentityComputed(),

"transparent_data_encryption_enabled": {
Type: pluginsdk.TypeBool,
Computed: true,
},

"transparent_data_encryption_key_vault_key_id": {
Type: pluginsdk.TypeString,
Computed: true,
},

"transparent_data_encryption_key_automatic_rotation_enabled": {
Type: pluginsdk.TypeBool,
Computed: true,
},

"tags": commonschema.TagsDataSource(),
},
}
}

func dataSourceMsSqlDatabaseRead(d *pluginsdk.ResourceData, meta interface{}) error {
client := meta.(*clients.Client).MSSQL.DatabasesClient
transparentEncryptionClient := meta.(*clients.Client).MSSQL.TransparentDataEncryptionsClient
ctx, cancel := timeouts.ForRead(meta.(*clients.Client).StopContext, d)
defer cancel()

Expand Down Expand Up @@ -130,6 +150,8 @@ func dataSourceMsSqlDatabaseRead(d *pluginsdk.ResourceData, meta interface{}) er
d.Set("read_replica_count", props.HighAvailabilityReplicaCount)
d.Set("sku_name", props.CurrentServiceObjectiveName)
d.Set("zone_redundant", props.ZoneRedundant)
d.Set("transparent_data_encryption_key_vault_key_id", props.EncryptionProtector)
d.Set("transparent_data_encryption_key_automatic_rotation_enabled", props.EncryptionProtectorAutoRotation)

maxSizeGb := int64(0)
if props.MaxSizeBytes != nil {
Expand All @@ -156,6 +178,25 @@ func dataSourceMsSqlDatabaseRead(d *pluginsdk.ResourceData, meta interface{}) er
d.Set("storage_account_type", storageAccountType)
}

identity, err := identity.FlattenUserAssignedMap(model.Identity)
if err != nil {
return fmt.Errorf("setting `identity`: %+v", err)
}

if err := d.Set("identity", identity); err != nil {
return fmt.Errorf("setting `identity`: %+v", err)
}

tde, err := transparentEncryptionClient.Get(ctx, databaseId)
if err != nil {
return fmt.Errorf("while retrieving Transparent Data Encryption state for %s: %+v", databaseId, err)
}
if model := tde.Model; model != nil {
if props := model.Properties; props != nil {
d.Set("transparent_data_encryption_enabled", props.State == transparentdataencryptions.TransparentDataEncryptionStateEnabled)
}
}

if err := tags.FlattenAndSet(d, model.Tags); err != nil {
return err
}
Expand Down
26 changes: 26 additions & 0 deletions internal/services/mssql/mssql_database_data_source_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,21 @@ func TestAccDataSourceMsSqlDatabase_complete(t *testing.T) {
})
}

func TestAccDataSourceMsSqlDatabase_transparentDataEncryptionKey(t *testing.T) {
data := acceptance.BuildTestData(t, "data.azurerm_mssql_database", "test")

data.DataSourceTest(t, []acceptance.TestStep{
{
Config: MsSqlDatabaseDataSource{}.transparentDataEncryptionKey(data),
Check: acceptance.ComposeTestCheckFunc(
check.That(data.ResourceName).Key("name").HasValue(fmt.Sprintf("acctest-db-%d", data.RandomInteger)),
check.That(data.ResourceName).Key("server_id").Exists(),
check.That(data.ResourceName).Key("identity.0.identity_ids.#").HasValue("1"),
),
},
})
}

func (MsSqlDatabaseDataSource) basic(data acceptance.TestData) string {
return fmt.Sprintf(`
%[1]s
Expand All @@ -70,3 +85,14 @@ data "azurerm_mssql_database" "test" {
}
`, MsSqlDatabaseResource{}.complete(data))
}

func (MsSqlDatabaseDataSource) transparentDataEncryptionKey(data acceptance.TestData) string {
return fmt.Sprintf(`
%[1]s
data "azurerm_mssql_database" "test" {
name = azurerm_mssql_database.test.name
server_id = azurerm_mssql_server.test.id
}
`, MsSqlDatabaseResource{}.transparentDataEncryptionKey(data))
}
129 changes: 109 additions & 20 deletions internal/services/mssql/mssql_database_resource.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import (
"github.com/hashicorp/go-azure-helpers/lang/response"
"github.com/hashicorp/go-azure-helpers/resourcemanager/commonids"
"github.com/hashicorp/go-azure-helpers/resourcemanager/commonschema"
"github.com/hashicorp/go-azure-helpers/resourcemanager/identity"
"github.com/hashicorp/go-azure-helpers/resourcemanager/tags"
"github.com/hashicorp/go-azure-sdk/resource-manager/maintenance/2022-07-01-preview/publicmaintenanceconfigurations"
"github.com/hashicorp/go-azure-sdk/resource-manager/sql/2023-02-01-preview/backupshorttermretentionpolicies"
Expand All @@ -29,6 +30,8 @@ import (
"github.com/hashicorp/terraform-provider-azurerm/helpers/tf"
"github.com/hashicorp/terraform-provider-azurerm/internal/clients"
"github.com/hashicorp/terraform-provider-azurerm/internal/locks"
keyVaultParser "github.com/hashicorp/terraform-provider-azurerm/internal/services/keyvault/parse"
keyVaultValidate "github.com/hashicorp/terraform-provider-azurerm/internal/services/keyvault/validate"
"github.com/hashicorp/terraform-provider-azurerm/internal/services/mssql/helper"
"github.com/hashicorp/terraform-provider-azurerm/internal/services/mssql/migration"
"github.com/hashicorp/terraform-provider-azurerm/internal/services/mssql/validate"
Expand Down Expand Up @@ -280,6 +283,7 @@ func resourceMsSqlDatabaseCreate(d *pluginsdk.ResourceData, meta interface{}) er
RequestedBackupStorageRedundancy: pointer.To(databases.BackupStorageRedundancy(d.Get("storage_account_type").(string))),
ZoneRedundant: pointer.To(d.Get("zone_redundant").(bool)),
IsLedgerOn: pointer.To(ledgerEnabled),
EncryptionProtectorAutoRotation: pointer.To(d.Get("transparent_data_encryption_key_automatic_rotation_enabled").(bool)),
},

Tags: tags.Expand(d.Get("tags").(map[string]interface{})),
Expand Down Expand Up @@ -377,6 +381,25 @@ func resourceMsSqlDatabaseCreate(d *pluginsdk.ResourceData, meta interface{}) er
input.Properties.RestorableDroppedDatabaseId = pointer.To(v.(string))
}

if v, ok := d.GetOk("identity"); ok {
expandedIdentity, err := identity.ExpandUserAssignedMap(v.([]interface{}))
if err != nil {
return fmt.Errorf("expanding `identity`: %+v", err)
}
input.Identity = expandedIdentity
}

if v, ok := d.GetOk("transparent_data_encryption_key_vault_key_id"); ok {
keyVaultKeyId := v.(string)

keyId, err := keyVaultParser.ParseNestedItemID(keyVaultKeyId)
if err != nil {
return fmt.Errorf("unable to parse key: %q: %+v", keyVaultKeyId, err)
}

input.Properties.EncryptionProtector = pointer.To(keyId.ID())
}

err = client.CreateOrUpdateThenPoll(ctx, id, input)
if err != nil {
return fmt.Errorf("creating %s: %+v", id, err)
Expand Down Expand Up @@ -431,34 +454,51 @@ func resourceMsSqlDatabaseCreate(d *pluginsdk.ResourceData, meta interface{}) er
state = transparentdataencryptions.TransparentDataEncryptionStateEnabled
}

input := transparentdataencryptions.LogicalDatabaseTransparentDataEncryption{
Properties: &transparentdataencryptions.TransparentDataEncryptionProperties{
State: state,
},
tde, err := transparentEncryptionClient.Get(ctx, id)
if err != nil {
return fmt.Errorf("while retrieving Transparent Data Encryption state for %s: %+v", id, err)
}

err := transparentEncryptionClient.CreateOrUpdateThenPoll(ctx, id, input)
if err != nil {
return fmt.Errorf("while enabling Transparent Data Encryption for %q: %+v", id.String(), err)
currentState := transparentdataencryptions.TransparentDataEncryptionStateDisabled
if model := tde.Model; model != nil {
if props := model.Properties; props != nil {
currentState = props.State
}
}

// NOTE: Internal x-ref, this is another case of hashicorp/go-azure-sdk#307 so this can be removed once that's fixed
if err = pluginsdk.Retry(d.Timeout(pluginsdk.TimeoutCreate), func() *pluginsdk.RetryError {
c, err := client.Get(ctx, id, databases.DefaultGetOperationOptions())
// Submit TDE state only when state is being changed, otherwise it can cause unwanted detection of state changes from the cloud side
if !strings.EqualFold(string(currentState), string(state)) {
input := transparentdataencryptions.LogicalDatabaseTransparentDataEncryption{
Properties: &transparentdataencryptions.TransparentDataEncryptionProperties{
State: state,
},
}

err := transparentEncryptionClient.CreateOrUpdateThenPoll(ctx, id, input)
if err != nil {
return pluginsdk.NonRetryableError(fmt.Errorf("while polling %s for status: %+v", id.String(), err))
return fmt.Errorf("while enabling Transparent Data Encryption for %q: %+v", id.String(), err)
}
if c.Model != nil && c.Model.Properties != nil && c.Model.Properties.Status != nil {
if c.Model.Properties.Status == pointer.To(databases.DatabaseStatusScaling) {
return pluginsdk.RetryableError(fmt.Errorf("database %s is still scaling", id.String()))

// NOTE: Internal x-ref, this is another case of hashicorp/go-azure-sdk#307 so this can be removed once that's fixed
if err = pluginsdk.Retry(d.Timeout(pluginsdk.TimeoutCreate), func() *pluginsdk.RetryError {
c, err := client.Get(ctx, id, databases.DefaultGetOperationOptions())
if err != nil {
return pluginsdk.NonRetryableError(fmt.Errorf("while polling %s for status: %+v", id.String(), err))
}
if c.Model != nil && c.Model.Properties != nil && c.Model.Properties.Status != nil {
if c.Model.Properties.Status == pointer.To(databases.DatabaseStatusScaling) {
return pluginsdk.RetryableError(fmt.Errorf("database %s is still scaling", id.String()))
}
} else {
return pluginsdk.RetryableError(fmt.Errorf("retrieving database status %s: Model, Properties or Status is nil", id.String()))
}
} else {
return pluginsdk.RetryableError(fmt.Errorf("retrieving database status %s: Model, Properties or Status is nil", id.String()))
}

return nil
}); err != nil {
return nil
return nil
}); err != nil {
return nil
}
} else {
log.Print("[DEBUG] Skipping re-writing of Transparent Data Encryption, since encryption state is not changing ...")
}
}

Expand Down Expand Up @@ -639,6 +679,17 @@ func resourceMsSqlDatabaseRead(d *pluginsdk.ResourceData, meta interface{}) erro
d.Set("maintenance_configuration_name", configurationName)
d.Set("ledger_enabled", ledgerEnabled)
d.Set("enclave_type", enclaveType)
d.Set("transparent_data_encryption_key_vault_key_id", props.EncryptionProtector)
d.Set("transparent_data_encryption_key_automatic_rotation_enabled", pointer.From(props.EncryptionProtectorAutoRotation))

identity, err := identity.FlattenUserAssignedMap(model.Identity)
if err != nil {
return fmt.Errorf("setting `identity`: %+v", err)
}

if err := d.Set("identity", identity); err != nil {
return fmt.Errorf("setting `identity`: %+v", err)
}

if err := tags.FlattenAndSet(d, model.Tags); err != nil {
return err
Expand Down Expand Up @@ -961,6 +1012,29 @@ func resourceMsSqlDatabaseUpdate(d *pluginsdk.ResourceData, meta interface{}) er
payload.Tags = tags.Expand(d.Get("tags").(map[string]interface{}))
}

if d.HasChange("identity") {
expanded, err := identity.ExpandUserAssignedMap(d.Get("identity").([]interface{}))
if err != nil {
return fmt.Errorf("expanding `identity`: %+v", err)
}
payload.Identity = expanded
}

if d.HasChange("transparent_data_encryption_key_vault_key_id") {
keyVaultKeyId := d.Get(("transparent_data_encryption_key_vault_key_id")).(string)

keyId, err := keyVaultParser.ParseNestedItemID(keyVaultKeyId)
if err != nil {
return fmt.Errorf("unable to parse key: %q: %+v", keyVaultKeyId, err)
}

props.EncryptionProtector = pointer.To(keyId.ID())
}

if d.HasChange("transparent_data_encryption_key_automatic_rotation_enabled") {
props.EncryptionProtectorAutoRotation = pointer.To(d.Get("transparent_data_encryption_key_automatic_rotation_enabled").(bool))
}

payload.Properties = pointer.To(props)
err = client.UpdateThenPoll(ctx, id, payload)
if err != nil {
Expand Down Expand Up @@ -1601,12 +1675,27 @@ func resourceMsSqlDatabaseSchema() map[string]*pluginsdk.Schema {
ForceNew: true,
},

"identity": commonschema.UserAssignedIdentityOptional(),

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

"transparent_data_encryption_key_vault_key_id": {
Type: pluginsdk.TypeString,
Optional: true,
ValidateFunc: keyVaultValidate.NestedItemId,
},

"transparent_data_encryption_key_automatic_rotation_enabled": {
Type: pluginsdk.TypeBool,
Optional: true,
Default: false,
RequiredWith: []string{"transparent_data_encryption_key_vault_key_id"},
},

"tags": commonschema.Tags(),
}
}
Loading

0 comments on commit f5837dc

Please sign in to comment.