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

Fix azurerm_sql_database blob auditing error under online secondary create mode #6402

Merged
merged 6 commits into from Apr 26, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
7 changes: 6 additions & 1 deletion azurerm/internal/services/mssql/client/client.go
Expand Up @@ -8,6 +8,7 @@ import (

type Client struct {
DatabasesClient *sql.DatabasesClient
DatabaseExtendedBlobAuditingPoliciesClient *sql.ExtendedDatabaseBlobAuditingPoliciesClient
DatabaseThreatDetectionPoliciesClient *sql.DatabaseThreatDetectionPoliciesClient
ElasticPoolsClient *sql.ElasticPoolsClient
DatabaseVulnerabilityAssessmentRuleBaselinesClient *sql.DatabaseVulnerabilityAssessmentRuleBaselinesClient
Expand All @@ -21,6 +22,9 @@ func NewClient(o *common.ClientOptions) *Client {
databasesClient := sql.NewDatabasesClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId)
o.ConfigureClient(&databasesClient.Client, o.ResourceManagerAuthorizer)

databaseExtendedBlobAuditingPoliciesClient := sql.NewExtendedDatabaseBlobAuditingPoliciesClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId)
o.ConfigureClient(&databaseExtendedBlobAuditingPoliciesClient.Client, o.ResourceManagerAuthorizer)

databaseThreatDetectionPoliciesClient := sql.NewDatabaseThreatDetectionPoliciesClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId)
o.ConfigureClient(&databaseThreatDetectionPoliciesClient.Client, o.ResourceManagerAuthorizer)

Expand All @@ -43,7 +47,8 @@ func NewClient(o *common.ClientOptions) *Client {
o.ConfigureClient(&sqlVirtualMachinesClient.Client, o.ResourceManagerAuthorizer)

return &Client{
DatabasesClient: &databasesClient,
DatabasesClient: &databasesClient,
DatabaseExtendedBlobAuditingPoliciesClient: &databaseExtendedBlobAuditingPoliciesClient,
DatabaseThreatDetectionPoliciesClient: &databaseThreatDetectionPoliciesClient,
DatabaseVulnerabilityAssessmentRuleBaselinesClient: &databaseVulnerabilityAssessmentRuleBaselinesClient,
ElasticPoolsClient: &elasticPoolsClient,
Expand Down
98 changes: 98 additions & 0 deletions azurerm/internal/services/mssql/helper/sql_extended_auditing.go
@@ -0,0 +1,98 @@
package helper

import (
"github.com/Azure/azure-sdk-for-go/services/preview/sql/mgmt/v3.0/sql"
"github.com/hashicorp/terraform-plugin-sdk/helper/schema"
"github.com/hashicorp/terraform-plugin-sdk/helper/validation"
"github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils"
)

func ExtendedAuditingSchema() *schema.Schema {
return &schema.Schema{
Type: schema.TypeList,
Optional: true,
MaxItems: 1,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"storage_account_access_key": {
Type: schema.TypeString,
Required: true,
Sensitive: true,
ValidateFunc: validation.StringIsNotEmpty,
},

"storage_endpoint": {
Type: schema.TypeString,
Required: true,
ValidateFunc: validation.IsURLWithHTTPS,
},

"storage_account_access_key_is_secondary": {
Type: schema.TypeBool,
Optional: true,
},

"retention_in_days": {
Type: schema.TypeInt,
Optional: true,
ValidateFunc: validation.IntBetween(0, 3285),
},
},
},
}
}

func ExpandAzureRmMsSqlDBBlobAuditingPolicies(input []interface{}) *sql.ExtendedDatabaseBlobAuditingPolicyProperties {
if len(input) == 0 {
return &sql.ExtendedDatabaseBlobAuditingPolicyProperties{
State: sql.BlobAuditingPolicyStateDisabled,
}
}
dbBlobAuditingPolicies := input[0].(map[string]interface{})

ExtendedDatabaseBlobAuditingPolicyProperties := sql.ExtendedDatabaseBlobAuditingPolicyProperties{
State: sql.BlobAuditingPolicyStateEnabled,
StorageAccountAccessKey: utils.String(dbBlobAuditingPolicies["storage_account_access_key"].(string)),
StorageEndpoint: utils.String(dbBlobAuditingPolicies["storage_endpoint"].(string)),
}
if v, ok := dbBlobAuditingPolicies["storage_account_access_key_is_secondary"]; ok {
ExtendedDatabaseBlobAuditingPolicyProperties.IsStorageSecondaryKeyInUse = utils.Bool(v.(bool))
}
if v, ok := dbBlobAuditingPolicies["retention_in_days"]; ok {
ExtendedDatabaseBlobAuditingPolicyProperties.RetentionDays = utils.Int32(int32(v.(int)))
}

return &ExtendedDatabaseBlobAuditingPolicyProperties
}

func FlattenAzureRmMsSqlDBBlobAuditingPolicies(extendedDatabaseBlobAuditingPolicy *sql.ExtendedDatabaseBlobAuditingPolicy, d *schema.ResourceData) []interface{} {
if extendedDatabaseBlobAuditingPolicy == nil || extendedDatabaseBlobAuditingPolicy.State == sql.BlobAuditingPolicyStateDisabled {
return []interface{}{}
}
var storageAccessKey, storageEndpoint string
// storage_account_access_key will not be returned, so we transfer the schema value
if v, ok := d.GetOk("extended_auditing_policy.0.storage_account_access_key"); ok {
storageAccessKey = v.(string)
}

if extendedDatabaseBlobAuditingPolicy.StorageEndpoint != nil {
storageEndpoint = *extendedDatabaseBlobAuditingPolicy.StorageEndpoint
}
var secondKeyInUse bool
if extendedDatabaseBlobAuditingPolicy.IsStorageSecondaryKeyInUse != nil {
secondKeyInUse = *extendedDatabaseBlobAuditingPolicy.IsStorageSecondaryKeyInUse
}
var retentionDays int32
if extendedDatabaseBlobAuditingPolicy.RetentionDays != nil {
retentionDays = *extendedDatabaseBlobAuditingPolicy.RetentionDays
}

return []interface{}{
map[string]interface{}{
"storage_account_access_key": storageAccessKey,
"storage_endpoint": storageEndpoint,
"storage_account_access_key_is_secondary": secondKeyInUse,
"retention_in_days": retentionDays,
},
}
}
38 changes: 33 additions & 5 deletions azurerm/internal/services/mssql/resource_arm_mssql_database.go
Expand Up @@ -17,6 +17,7 @@ import (
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/mssql/helper"
"github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/services/mssql/parse"
"github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/services/mssql/validate"
"github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/tags"
Expand Down Expand Up @@ -99,6 +100,8 @@ func resourceArmMsSqlDatabase() *schema.Resource {
ValidateFunc: validate.MsSqlElasticPoolID,
},

"extended_auditing_policy": helper.ExtendedAuditingSchema(),

"license_type": {
Type: schema.TypeString,
Optional: true,
Expand Down Expand Up @@ -270,6 +273,7 @@ func resourceArmMsSqlDatabase() *schema.Resource {

func resourceArmMsSqlDatabaseCreateUpdate(d *schema.ResourceData, meta interface{}) error {
client := meta.(*clients.Client).MSSQL.DatabasesClient
auditingClient := meta.(*clients.Client).MSSQL.DatabaseExtendedBlobAuditingPoliciesClient
serverClient := meta.(*clients.Client).MSSQL.ServersClient
threatClient := meta.(*clients.Client).MSSQL.DatabaseThreatDetectionPoliciesClient
ctx, cancel := timeouts.ForCreateUpdate(meta.(*clients.Client).StopContext, d)
Expand Down Expand Up @@ -321,11 +325,15 @@ func resourceArmMsSqlDatabaseCreateUpdate(d *schema.ResourceData, meta interface
Tags: tags.Expand(d.Get("tags").(map[string]interface{})),
}

if v, ok := d.GetOk("create_mode"); ok {
if _, ok := d.GetOk("creation_source_database_id"); (v.(string) == string(sql.CreateModeCopy) || v.(string) == string(sql.CreateModePointInTimeRestore) || v.(string) == string(sql.CreateModeRestore) || v.(string) == string(sql.CreateModeSecondary)) && !ok {
return fmt.Errorf("'creation_source_database_id' is required for create_mode %s", v.(string))
}
params.DatabaseProperties.CreateMode = sql.CreateMode(v.(string))
createMode, ok := d.GetOk("create_mode")
if _, dbok := d.GetOk("creation_source_database_id"); ok && (createMode.(string) == string(sql.CreateModeCopy) || createMode.(string) == string(sql.CreateModePointInTimeRestore) || createMode.(string) == string(sql.CreateModeRestore) || createMode.(string) == string(sql.CreateModeSecondary)) && !dbok {
return fmt.Errorf("'creation_source_database_id' is required for create_mode %s", createMode.(string))
}
params.DatabaseProperties.CreateMode = sql.CreateMode(createMode.(string))

auditingPolicies := d.Get("extended_auditing_policy").([]interface{})
if (createMode == string(sql.CreateModeOnlineSecondary) || createMode == string(sql.Secondary)) && len(auditingPolicies) > 0 {
return fmt.Errorf("could not configure auditing policies on SQL Database %q (Resource Group %q, Server %q) in secondary create mode", name, serverId.ResourceGroup, serverId.Name)
}

if v, ok := d.GetOk("max_size_gb"); ok {
Expand Down Expand Up @@ -382,12 +390,22 @@ func resourceArmMsSqlDatabaseCreateUpdate(d *schema.ResourceData, meta interface
return fmt.Errorf("setting database threat detection policy: %+v", err)
}

if createMode != string(sql.CreateModeOnlineSecondary) && createMode != string(sql.CreateModeSecondary) {
auditingProps := sql.ExtendedDatabaseBlobAuditingPolicy{
ExtendedDatabaseBlobAuditingPolicyProperties: helper.ExpandAzureRmMsSqlDBBlobAuditingPolicies(auditingPolicies),
}
if _, err = auditingClient.CreateOrUpdate(ctx, serverId.ResourceGroup, serverId.Name, name, auditingProps); err != nil {
return fmt.Errorf("failure in issuing create/update request for SQL Database %q Blob Auditing Policies(SQL Server %q/ Resource Group %q): %+v", name, serverId.Name, serverId.ResourceGroup, err)
}
}

return resourceArmMsSqlDatabaseRead(d, meta)
}

func resourceArmMsSqlDatabaseRead(d *schema.ResourceData, meta interface{}) error {
client := meta.(*clients.Client).MSSQL.DatabasesClient
threatClient := meta.(*clients.Client).MSSQL.DatabaseThreatDetectionPoliciesClient
auditingClient := meta.(*clients.Client).MSSQL.DatabaseExtendedBlobAuditingPoliciesClient
ctx, cancel := timeouts.ForRead(meta.(*clients.Client).StopContext, d)
defer cancel()

Expand Down Expand Up @@ -441,6 +459,16 @@ func resourceArmMsSqlDatabaseRead(d *schema.ResourceData, meta interface{}) erro
}
}

auditingResp, err := auditingClient.Get(ctx, id.ResourceGroup, id.MsSqlServer, id.Name)
if err != nil {
return fmt.Errorf("failure in reading SQL Database %q: %v Blob Auditing Policies", id.Name, err)
}

flattenBlobAuditing := helper.FlattenAzureRmMsSqlDBBlobAuditingPolicies(&auditingResp, d)
if err := d.Set("extended_auditing_policy", flattenBlobAuditing); err != nil {
return fmt.Errorf("failure in setting `extended_auditing_policy`: %+v", err)
}

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

Expand Down
Expand Up @@ -333,6 +333,46 @@ func TestAccAzureRMMsSqlDatabase_threatDetectionPolicy(t *testing.T) {
})
}

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

resource.ParallelTest(t, resource.TestCase{
PreCheck: func() { acceptance.PreCheck(t) },
Providers: acceptance.SupportedProviders,
CheckDestroy: testCheckAzureRMMsSqlDatabaseDestroy,
Steps: []resource.TestStep{
{
Config: testAccAzureRMMsSqlDatabase_basic(data),
Check: resource.ComposeTestCheckFunc(
testCheckAzureRMMsSqlDatabaseExists(data.ResourceName),
),
},
data.ImportStep(),
{
Config: testAccAzureRMMsSqlDatabase_withBlobAuditingPolices(data),
Check: resource.ComposeTestCheckFunc(
testCheckAzureRMMsSqlDatabaseExists(data.ResourceName),
),
},
data.ImportStep("extended_auditing_policy.0.storage_account_access_key"),
{
Config: testAccAzureRMMsSqlDatabase_withBlobAuditingPolicesUpdated(data),
Check: resource.ComposeTestCheckFunc(
testCheckAzureRMMsSqlDatabaseExists(data.ResourceName),
),
},
data.ImportStep("extended_auditing_policy.0.storage_account_access_key"),
{
Config: testAccAzureRMMsSqlDatabase_basic(data),
Check: resource.ComposeTestCheckFunc(
testCheckAzureRMMsSqlDatabaseExists(data.ResourceName),
),
},
data.ImportStep(),
},
})
}

func testCheckAzureRMMsSqlDatabaseExists(resourceName string) resource.TestCheckFunc {
return func(s *terraform.State) error {
client := acceptance.AzureProvider.Meta().(*clients.Client).MSSQL.DatabasesClient
Expand Down Expand Up @@ -704,3 +744,71 @@ resource "azurerm_mssql_database" "test" {
}
`, template, data.RandomInteger, state)
}

func testAccAzureRMMsSqlDatabase_withBlobAuditingPolices(data acceptance.TestData) string {
template := testAccAzureRMMsSqlDatabase_template(data)
return fmt.Sprintf(`
%s

resource "azurerm_storage_account" "test" {
name = "acctest%[2]d"
resource_group_name = azurerm_resource_group.test.name
location = azurerm_resource_group.test.location
account_tier = "Standard"
account_replication_type = "LRS"
}

resource "azurerm_storage_account" "test2" {
name = "acctest2%[2]d"
resource_group_name = azurerm_resource_group.test.name
location = azurerm_resource_group.test.location
account_tier = "Standard"
account_replication_type = "LRS"
}

resource "azurerm_mssql_database" "test" {
name = "acctest-db-%[3]d"
server_id = azurerm_sql_server.test.id
extended_auditing_policy {
storage_endpoint = azurerm_storage_account.test.primary_blob_endpoint
storage_account_access_key = azurerm_storage_account.test.primary_access_key
storage_account_access_key_is_secondary = true
retention_in_days = 6
}
}
`, template, data.RandomIntOfLength(15), data.RandomInteger)
}

func testAccAzureRMMsSqlDatabase_withBlobAuditingPolicesUpdated(data acceptance.TestData) string {
template := testAccAzureRMMsSqlDatabase_template(data)
return fmt.Sprintf(`
%s

resource "azurerm_storage_account" "test" {
name = "acctest%[2]d"
resource_group_name = azurerm_resource_group.test.name
location = azurerm_resource_group.test.location
account_tier = "Standard"
account_replication_type = "LRS"
}

resource "azurerm_storage_account" "test2" {
name = "acctest2%[2]d"
resource_group_name = azurerm_resource_group.test.name
location = azurerm_resource_group.test.location
account_tier = "Standard"
account_replication_type = "LRS"
}

resource "azurerm_mssql_database" "test" {
name = "acctest-db-%[3]d"
server_id = azurerm_sql_server.test.id
extended_auditing_policy {
storage_endpoint = azurerm_storage_account.test2.primary_blob_endpoint
storage_account_access_key = azurerm_storage_account.test2.primary_access_key
storage_account_access_key_is_secondary = false
retention_in_days = 3
}
}
`, template, data.RandomIntOfLength(15), data.RandomInteger)
}
24 changes: 16 additions & 8 deletions azurerm/internal/services/sql/resource_arm_sql_database.go
Expand Up @@ -365,6 +365,12 @@ func resourceArmSqlDatabaseCreateUpdate(d *schema.ResourceData, meta interface{}
resourceGroup := d.Get("resource_group_name").(string)
location := azure.NormalizeLocation(d.Get("location").(string))
createMode := d.Get("create_mode").(string)
auditingPolicies := d.Get("extended_auditing_policy").([]interface{})

if createMode == string(sql.CreateModeOnlineSecondary) && len(auditingPolicies) > 0 {
return fmt.Errorf("could not configure auditing policies on SQL Database %q (Resource Group %q, Server %q) in online secondary create mode", name, resourceGroup, serverName)
}

zoneRedundant := d.Get("zone_redundant").(bool)
t := d.Get("tags").(map[string]interface{})

Expand Down Expand Up @@ -500,12 +506,14 @@ func resourceArmSqlDatabaseCreateUpdate(d *schema.ResourceData, meta interface{}
return fmt.Errorf("Error setting database threat detection policy: %+v", err)
}

auditingClient := meta.(*clients.Client).Sql.DatabaseExtendedBlobAuditingPoliciesClient
auditingProps := sql.ExtendedDatabaseBlobAuditingPolicy{
ExtendedDatabaseBlobAuditingPolicyProperties: helper.ExpandAzureRmSqlDBBlobAuditingPolicies(d.Get("extended_auditing_policy").([]interface{})),
}
if _, err = auditingClient.CreateOrUpdate(ctx, resourceGroup, serverName, name, auditingProps); err != nil {
return fmt.Errorf("Error issuing create/update request for SQL Database %q Blob Auditing Policies(SQL Server %q/ Resource Group %q): %+v", name, serverName, resourceGroup, err)
if createMode != string(sql.CreateModeOnlineSecondary) {
auditingClient := meta.(*clients.Client).Sql.DatabaseExtendedBlobAuditingPoliciesClient
auditingProps := sql.ExtendedDatabaseBlobAuditingPolicy{
ExtendedDatabaseBlobAuditingPolicyProperties: helper.ExpandAzureRmSqlDBBlobAuditingPolicies(auditingPolicies),
}
if _, err = auditingClient.CreateOrUpdate(ctx, resourceGroup, serverName, name, auditingProps); err != nil {
return fmt.Errorf("failure in issuing create/update request for SQL Database %q Blob Auditing Policies(SQL Server %q/ Resource Group %q): %+v", name, serverName, resourceGroup, err)
}
}

return resourceArmSqlDatabaseRead(d, meta)
Expand Down Expand Up @@ -594,12 +602,12 @@ func resourceArmSqlDatabaseRead(d *schema.ResourceData, meta interface{}) error
auditingClient := meta.(*clients.Client).Sql.DatabaseExtendedBlobAuditingPoliciesClient
auditingResp, err := auditingClient.Get(ctx, resourceGroup, serverName, name)
if err != nil {
return fmt.Errorf("Error reading SQL Database %q: %v Blob Auditing Policies", name, err)
return fmt.Errorf("failure in reading SQL Database %q: %v Blob Auditing Policies", name, err)
}

flattenBlobAuditing := helper.FlattenAzureRmSqlDBBlobAuditingPolicies(&auditingResp, d)
if err := d.Set("extended_auditing_policy", flattenBlobAuditing); err != nil {
return fmt.Errorf("Error setting `extended_auditing_policy`: %+v", err)
return fmt.Errorf("failure in setting `extended_auditing_policy`: %+v", err)
}

return tags.FlattenAndSet(d, resp.Tags)
Expand Down