Skip to content

Commit

Permalink
azurerm_postgres_server - support for threat_detection_policy (#6721)
Browse files Browse the repository at this point in the history
  • Loading branch information
katbyte committed May 14, 2020
1 parent 7a9a656 commit e1d4418
Show file tree
Hide file tree
Showing 6 changed files with 310 additions and 29 deletions.
Expand Up @@ -14,6 +14,8 @@ import (
"github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils"
)

// todo 3.0 - this may want to be put into the mssql_server resource now that it exists.

func resourceArmMssqlServerSecurityAlertPolicy() *schema.Resource {
return &schema.Resource{
Create: resourceArmMssqlServerSecurityAlertPolicyCreateUpdate,
Expand Down
Expand Up @@ -184,9 +184,9 @@ resource "azurerm_sql_server" "test" {
resource "azurerm_storage_account" "test" {
name = "accsa%d"
resource_group_name = azurerm_resource_group.test.name
location = "%s"
location = azurerm_resource_group.test.location
account_tier = "Standard"
account_replication_type = "GRS"
}
`, data.RandomInteger, data.Locations.Primary, data.RandomInteger, data.RandomInteger, data.Locations.Primary)
`, data.RandomInteger, data.Locations.Primary, data.RandomInteger, data.RandomInteger)
}
25 changes: 15 additions & 10 deletions azurerm/internal/services/postgres/client/client.go
Expand Up @@ -6,11 +6,12 @@ import (
)

type Client struct {
ConfigurationsClient *postgresql.ConfigurationsClient
DatabasesClient *postgresql.DatabasesClient
FirewallRulesClient *postgresql.FirewallRulesClient
ServersClient *postgresql.ServersClient
VirtualNetworkRulesClient *postgresql.VirtualNetworkRulesClient
ConfigurationsClient *postgresql.ConfigurationsClient
DatabasesClient *postgresql.DatabasesClient
FirewallRulesClient *postgresql.FirewallRulesClient
ServersClient *postgresql.ServersClient
ServerSecurityAlertPoliciesClient *postgresql.ServerSecurityAlertPoliciesClient
VirtualNetworkRulesClient *postgresql.VirtualNetworkRulesClient
}

func NewClient(o *common.ClientOptions) *Client {
Expand All @@ -26,14 +27,18 @@ func NewClient(o *common.ClientOptions) *Client {
serversClient := postgresql.NewServersClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId)
o.ConfigureClient(&serversClient.Client, o.ResourceManagerAuthorizer)

serverSecurityAlertPoliciesClient := postgresql.NewServerSecurityAlertPoliciesClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId)
o.ConfigureClient(&serverSecurityAlertPoliciesClient.Client, o.ResourceManagerAuthorizer)

virtualNetworkRulesClient := postgresql.NewVirtualNetworkRulesClientWithBaseURI(o.ResourceManagerEndpoint, o.SubscriptionId)
o.ConfigureClient(&virtualNetworkRulesClient.Client, o.ResourceManagerAuthorizer)

return &Client{
ConfigurationsClient: &configurationsClient,
DatabasesClient: &databasesClient,
FirewallRulesClient: &firewallRulesClient,
ServersClient: &serversClient,
VirtualNetworkRulesClient: &virtualNetworkRulesClient,
ConfigurationsClient: &configurationsClient,
DatabasesClient: &databasesClient,
FirewallRulesClient: &firewallRulesClient,
ServersClient: &serversClient,
ServerSecurityAlertPoliciesClient: &serverSecurityAlertPoliciesClient,
VirtualNetworkRulesClient: &virtualNetworkRulesClient,
}
}
204 changes: 193 additions & 11 deletions azurerm/internal/services/postgres/postgresql_server_resource.go
Expand Up @@ -277,6 +277,70 @@ func resourceArmPostgreSQLServer() *schema.Resource {
DiffSuppressFunc: suppress.CaseDifference,
},

"threat_detection_policy": {
Type: schema.TypeList,
Optional: true,
MaxItems: 1,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"enabled": {
Type: schema.TypeBool,
Optional: true,
},

"disabled_alerts": {
Type: schema.TypeSet,
Optional: true,
Set: schema.HashString,
Elem: &schema.Schema{
Type: schema.TypeString,
ValidateFunc: validation.StringInSlice([]string{
"Sql_Injection",
"Sql_Injection_Vulnerability",
"Access_Anomaly",
"Data_Exfiltration",
"Unsafe_Action",
}, false),
},
},

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

"email_addresses": {
Type: schema.TypeSet,
Optional: true,
Elem: &schema.Schema{
Type: schema.TypeString,
// todo email validation in code
},
Set: schema.HashString,
},

"retention_days": {
Type: schema.TypeInt,
Optional: true,
ValidateFunc: validation.IntAtLeast(0),
},

"storage_account_access_key": {
Type: schema.TypeString,
Optional: true,
Sensitive: true,
ValidateFunc: validation.StringIsNotEmpty,
},

"storage_endpoint": {
Type: schema.TypeString,
Optional: true,
ValidateFunc: validation.StringIsNotEmpty,
},
},
},
},

"fqdn": {
Type: schema.TypeString,
Computed: true,
Expand All @@ -289,6 +353,7 @@ func resourceArmPostgreSQLServer() *schema.Resource {

func resourceArmPostgreSQLServerCreate(d *schema.ResourceData, meta interface{}) error {
client := meta.(*clients.Client).Postgres.ServersClient
securityClient := meta.(*clients.Client).Postgres.ServerSecurityAlertPoliciesClient
ctx, cancel := timeouts.ForCreate(meta.(*clients.Client).StopContext, d)
defer cancel()

Expand Down Expand Up @@ -339,7 +404,7 @@ func resourceArmPostgreSQLServerCreate(d *schema.ResourceData, meta interface{})
ssl = postgresql.SslEnforcementEnumDisabled
}

storage := expandAzureRmPostgreSQLStorageProfile(d)
storage := expandPostgreSQLStorageProfile(d)

var props postgresql.BasicServerPropertiesForCreate
switch mode {
Expand Down Expand Up @@ -440,11 +505,26 @@ func resourceArmPostgreSQLServerCreate(d *schema.ResourceData, meta interface{})

d.SetId(*read.ID)

if v, ok := d.GetOk("threat_detection_policy"); ok {
alert := expandSecurityAlertPolicy(v)
if alert != nil {
future, err := securityClient.CreateOrUpdate(ctx, resourceGroup, name, *alert)
if err != nil {
return fmt.Errorf("error updataing postgres server security alert policy: %v", err)
}

if err = future.WaitForCompletionRef(ctx, client.Client); err != nil {
return fmt.Errorf("error waiting for creation/update of postgrest server security alert policy (server %q, resource group %q): %+v", name, resourceGroup, err)
}
}
}

return resourceArmPostgreSQLServerRead(d, meta)
}

func resourceArmPostgreSQLServerUpdate(d *schema.ResourceData, meta interface{}) error {
client := meta.(*clients.Client).Postgres.ServersClient
securityClient := meta.(*clients.Client).Postgres.ServerSecurityAlertPoliciesClient
ctx, cancel := timeouts.ForUpdate(meta.(*clients.Client).StopContext, d)
defer cancel()

Expand Down Expand Up @@ -478,7 +558,7 @@ func resourceArmPostgreSQLServerUpdate(d *schema.ResourceData, meta interface{})
AdministratorLoginPassword: utils.String(d.Get("administrator_login_password").(string)),
PublicNetworkAccess: publicAccess,
SslEnforcement: ssl,
StorageProfile: expandAzureRmPostgreSQLStorageProfile(d),
StorageProfile: expandPostgreSQLStorageProfile(d),
Version: postgresql.ServerVersion(d.Get("version").(string)),
},
Sku: sku,
Expand All @@ -494,21 +574,26 @@ func resourceArmPostgreSQLServerUpdate(d *schema.ResourceData, meta interface{})
return fmt.Errorf("waiting for update of PostgreSQL Server %q (Resource Group %q): %+v", id.Name, id.ResourceGroup, err)
}

read, err := client.Get(ctx, id.ResourceGroup, id.Name)
if err != nil {
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", id.Name, id.ResourceGroup)
}
if v, ok := d.GetOk("threat_detection_policy"); ok {
alert := expandSecurityAlertPolicy(v)
if alert != nil {
future, err := securityClient.CreateOrUpdate(ctx, id.ResourceGroup, id.Name, *alert)
if err != nil {
return fmt.Errorf("error updataing mssql server security alert policy: %v", err)
}

d.SetId(*read.ID)
if err = future.WaitForCompletionRef(ctx, client.Client); err != nil {
return fmt.Errorf("error waiting for creation/update of postgrest server security alert policy (server %q, resource group %q): %+v", id.Name, id.ResourceGroup, err)
}
}
}

return resourceArmPostgreSQLServerRead(d, meta)
}

func resourceArmPostgreSQLServerRead(d *schema.ResourceData, meta interface{}) error {
client := meta.(*clients.Client).Postgres.ServersClient
securityClient := meta.(*clients.Client).Postgres.ServerSecurityAlertPoliciesClient
ctx, cancel := timeouts.ForRead(meta.(*clients.Client).StopContext, d)
defer cancel()

Expand Down Expand Up @@ -563,6 +648,19 @@ func resourceArmPostgreSQLServerRead(d *schema.ResourceData, meta interface{}) e
// Computed
d.Set("fqdn", props.FullyQualifiedDomainName)
}

secResp, err := securityClient.Get(ctx, id.ResourceGroup, id.Name)
if err != nil && !utils.ResponseWasNotFound(secResp.Response) {
return fmt.Errorf("error making read request to postgres server security alert policy: %+v", err)
}

if !utils.ResponseWasNotFound(secResp.Response) {
block := flattenSecurityAlertPolicy(secResp.SecurityAlertPolicyProperties, d.Get("threat_detection_policy.0.storage_account_access_key").(string))
if err := d.Set("threat_detection_policy", block); err != nil {
return fmt.Errorf("setting `threat_detection_policy`: %+v", err)
}
}

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

Expand Down Expand Up @@ -627,7 +725,7 @@ func expandServerSkuName(skuName string) (*postgresql.Sku, error) {
}, nil
}

func expandAzureRmPostgreSQLStorageProfile(d *schema.ResourceData) *postgresql.StorageProfile {
func expandPostgreSQLStorageProfile(d *schema.ResourceData) *postgresql.StorageProfile {
storage := postgresql.StorageProfile{}
if v, ok := d.GetOk("storage_profile"); ok {
storageprofile := v.([]interface{})[0].(map[string]interface{})
Expand Down Expand Up @@ -682,3 +780,87 @@ func flattenPostgreSQLStorageProfile(resp *postgresql.StorageProfile) []interfac

return []interface{}{values}
}

func expandSecurityAlertPolicy(i interface{}) *postgresql.ServerSecurityAlertPolicy {
slice := i.([]interface{})
if len(slice) == 0 {
return nil
}

block := slice[0].(map[string]interface{})

state := postgresql.ServerSecurityAlertPolicyStateEnabled
if !block["enabled"].(bool) {
state = postgresql.ServerSecurityAlertPolicyStateDisabled
}

props := &postgresql.SecurityAlertPolicyProperties{
State: state,
}

if v, ok := block["disabled_alerts"]; ok {
props.DisabledAlerts = utils.ExpandStringSlice(v.(*schema.Set).List())
}

if v, ok := block["email_addresses"]; ok {
props.EmailAddresses = utils.ExpandStringSlice(v.(*schema.Set).List())
}

if v, ok := block["email_account_admins"]; ok {
props.EmailAccountAdmins = utils.Bool(v.(bool))
}

if v, ok := block["retention_days"]; ok {
props.RetentionDays = utils.Int32(int32(v.(int)))
}

if v, ok := block["storage_account_access_key"]; ok && v.(string) != "" {
props.StorageAccountAccessKey = utils.String(v.(string))
}

if v, ok := block["storage_endpoint"]; ok && v.(string) != "" {
props.StorageEndpoint = utils.String(v.(string))
}

return &postgresql.ServerSecurityAlertPolicy{
SecurityAlertPolicyProperties: props,
}
}

func flattenSecurityAlertPolicy(props *postgresql.SecurityAlertPolicyProperties, accessKey string) interface{} {
if props == nil {
return nil
}

// check if its an empty block as in its never been set before
if props.DisabledAlerts != nil && len(*props.DisabledAlerts) == 1 && (*props.DisabledAlerts)[0] == "" &&
props.EmailAddresses != nil && len(*props.EmailAddresses) == 1 && (*props.EmailAddresses)[0] == "" &&
props.StorageAccountAccessKey != nil && *props.StorageAccountAccessKey == "" &&
props.StorageEndpoint != nil && *props.StorageEndpoint == "" &&
props.RetentionDays != nil && *props.RetentionDays == 0 &&
props.EmailAccountAdmins != nil && !*props.EmailAccountAdmins &&
props.State == postgresql.ServerSecurityAlertPolicyStateDisabled {
return nil
}

block := map[string]interface{}{}

block["enabled"] = props.State == postgresql.ServerSecurityAlertPolicyStateEnabled

block["disabled_alerts"] = utils.FlattenStringSlice(props.DisabledAlerts)
block["email_addresses"] = utils.FlattenStringSlice(props.EmailAddresses)

if v := props.EmailAccountAdmins; v != nil {
block["email_account_admins"] = *v
}
if v := props.RetentionDays; v != nil {
block["retention_days"] = *v
}
if v := props.StorageEndpoint; v != nil {
block["storage_endpoint"] = *v
}

block["storage_account_access_key"] = accessKey

return []interface{}{block}
}

0 comments on commit e1d4418

Please sign in to comment.