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

New resource: azurerm_sentinel_alert_rule_fusion #9829

Merged
merged 4 commits into from
Dec 30, 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.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions azurerm/internal/services/sentinel/registration.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ func (r Registration) SupportedDataSources() map[string]*schema.Resource {
// SupportedResources returns the supported Resources supported by this Service
func (r Registration) SupportedResources() map[string]*schema.Resource {
return map[string]*schema.Resource{
"azurerm_sentinel_alert_rule_fusion": resourceSentinelAlertRuleFusion(),
"azurerm_sentinel_alert_rule_ms_security_incident": resourceSentinelAlertRuleMsSecurityIncident(),
"azurerm_sentinel_alert_rule_scheduled": resourceSentinelAlertRuleScheduled(),
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,189 @@
package sentinel

import (
"fmt"
"log"
"time"

"github.com/Azure/azure-sdk-for-go/services/preview/securityinsight/mgmt/2019-01-01-preview/securityinsight"
"github.com/hashicorp/terraform-plugin-sdk/helper/schema"
"github.com/hashicorp/terraform-plugin-sdk/helper/validation"
"github.com/terraform-providers/terraform-provider-azurerm/azurerm/helpers/tf"
"github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/clients"
loganalyticsParse "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/services/loganalytics/parse"
loganalyticsValidate "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/services/loganalytics/validate"
"github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/services/sentinel/parse"
azSchema "github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/tf/schema"
"github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/timeouts"
"github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils"
)

func resourceSentinelAlertRuleFusion() *schema.Resource {
return &schema.Resource{
Create: resourceSentinelAlertRuleFusionCreateUpdate,
Read: resourceSentinelAlertRuleFusionRead,
Update: resourceSentinelAlertRuleFusionCreateUpdate,
Delete: resourceSentinelAlertRuleFusionDelete,

Importer: azSchema.ValidateResourceIDPriorToImportThen(func(id string) error {
_, err := parse.SentinelAlertRuleID(id)
return err
}, importSentinelAlertRule(securityinsight.Fusion)),

Timeouts: &schema.ResourceTimeout{
Create: schema.DefaultTimeout(30 * time.Minute),
Read: schema.DefaultTimeout(5 * time.Minute),
Update: schema.DefaultTimeout(30 * time.Minute),
Delete: schema.DefaultTimeout(30 * time.Minute),
},

Schema: map[string]*schema.Schema{
"name": {
Type: schema.TypeString,
Required: true,
ForceNew: true,
ValidateFunc: validation.StringIsNotEmpty,
},

"log_analytics_workspace_id": {
Type: schema.TypeString,
Required: true,
ForceNew: true,
ValidateFunc: loganalyticsValidate.LogAnalyticsWorkspaceID,
},

"alert_rule_template_guid": {
Type: schema.TypeString,
Required: true,
ForceNew: true,
ValidateFunc: validation.IsUUID,
},

"enabled": {
Type: schema.TypeBool,
Optional: true,
Default: true,
},
},
}
}

func resourceSentinelAlertRuleFusionCreateUpdate(d *schema.ResourceData, meta interface{}) error {
client := meta.(*clients.Client).Sentinel.AlertRulesClient
ctx, cancel := timeouts.ForCreateUpdate(meta.(*clients.Client).StopContext, d)
defer cancel()

name := d.Get("name").(string)

workspaceID, err := loganalyticsParse.LogAnalyticsWorkspaceID(d.Get("log_analytics_workspace_id").(string))
if err != nil {
return err
}

if d.IsNewResource() {
resp, err := client.Get(ctx, workspaceID.ResourceGroup, "Microsoft.OperationalInsights", workspaceID.WorkspaceName, name)
if err != nil {
if !utils.ResponseWasNotFound(resp.Response) {
return fmt.Errorf("checking for existing Sentinel Alert Rule Fusion %q (Resource Group %q): %+v", name, workspaceID.ResourceGroup, err)
}
}

id := alertRuleID(resp.Value)
if id != nil && *id != "" {
return tf.ImportAsExistsError("azurerm_sentinel_alert_rule_fusion", *id)
}
}

params := securityinsight.FusionAlertRule{
Kind: securityinsight.KindFusion,
FusionAlertRuleProperties: &securityinsight.FusionAlertRuleProperties{
AlertRuleTemplateName: utils.String(d.Get("alert_rule_template_guid").(string)),
Enabled: utils.Bool(d.Get("enabled").(bool)),
},
}

// Service avoid concurrent update of this resource via checking the "etag" to guarantee it is the same value as last Read.
if !d.IsNewResource() {
resp, err := client.Get(ctx, workspaceID.ResourceGroup, "Microsoft.OperationalInsights", workspaceID.WorkspaceName, name)
if err != nil {
return fmt.Errorf("retrieving Sentinel Alert Rule Fusion %q (Resource Group %q / Workspace: %q): %+v", name, workspaceID.ResourceGroup, workspaceID.WorkspaceName, err)
}

if err := assertAlertRuleKind(resp.Value, securityinsight.Fusion); err != nil {
return fmt.Errorf("asserting alert rule of %q (Resource Group %q / Workspace: %q): %+v", name, workspaceID.ResourceGroup, workspaceID.WorkspaceName, err)
}
params.Etag = resp.Value.(securityinsight.FusionAlertRule).Etag
}

if _, err := client.CreateOrUpdate(ctx, workspaceID.ResourceGroup, "Microsoft.OperationalInsights", workspaceID.WorkspaceName, name, params); err != nil {
return fmt.Errorf("creating Sentinel Alert Rule Fusion %q (Resource Group %q / Workspace: %q): %+v", name, workspaceID.ResourceGroup, workspaceID.WorkspaceName, err)
}

resp, err := client.Get(ctx, workspaceID.ResourceGroup, "Microsoft.OperationalInsights", workspaceID.WorkspaceName, name)
if err != nil {
return fmt.Errorf("retrieving Sentinel Alert Rule Fusion %q (Resource Group %q / Workspace: %q): %+v", name, workspaceID.ResourceGroup, workspaceID.WorkspaceName, err)
}
id := alertRuleID(resp.Value)
if id == nil || *id == "" {
return fmt.Errorf("empty or nil ID returned for Sentinel Alert Rule Fusion %q (Resource Group %q / Workspace: %q) ID", name, workspaceID.ResourceGroup, workspaceID.WorkspaceName)
}
d.SetId(*id)

return resourceSentinelAlertRuleFusionRead(d, meta)
}

func resourceSentinelAlertRuleFusionRead(d *schema.ResourceData, meta interface{}) error {
client := meta.(*clients.Client).Sentinel.AlertRulesClient
ctx, cancel := timeouts.ForRead(meta.(*clients.Client).StopContext, d)
defer cancel()

id, err := parse.SentinelAlertRuleID(d.Id())
if err != nil {
return err
}

resp, err := client.Get(ctx, id.ResourceGroup, "Microsoft.OperationalInsights", id.Workspace, id.Name)
if err != nil {
if utils.ResponseWasNotFound(resp.Response) {
log.Printf("[DEBUG] Sentinel Alert Rule Fusion %q was not found in Workspace: %q in Resource Group %q - removing from state!", id.Name, id.Workspace, id.ResourceGroup)
d.SetId("")
return nil
}

return fmt.Errorf("retrieving Sentinel Alert Rule Fusion %q (Resource Group %q / Workspace: %q): %+v", id.Name, id.ResourceGroup, id.Workspace, err)
}

if err := assertAlertRuleKind(resp.Value, securityinsight.Fusion); err != nil {
return fmt.Errorf("asserting alert rule of %q (Resource Group %q / Workspace: %q): %+v", id.Name, id.ResourceGroup, id.Workspace, err)
}
rule := resp.Value.(securityinsight.FusionAlertRule)

d.Set("name", id.Name)

workspaceId := loganalyticsParse.NewLogAnalyticsWorkspaceID(id.SubscriptionId, id.ResourceGroup, id.Workspace)
d.Set("log_analytics_workspace_id", workspaceId.ID())

if prop := rule.FusionAlertRuleProperties; prop != nil {
d.Set("enabled", prop.Enabled)
d.Set("alert_rule_template_guid", prop.AlertRuleTemplateName)
}

return nil
}

func resourceSentinelAlertRuleFusionDelete(d *schema.ResourceData, meta interface{}) error {
client := meta.(*clients.Client).Sentinel.AlertRulesClient
ctx, cancel := timeouts.ForDelete(meta.(*clients.Client).StopContext, d)
defer cancel()

id, err := parse.SentinelAlertRuleID(d.Id())
if err != nil {
return err
}

if _, err := client.Delete(ctx, id.ResourceGroup, "Microsoft.OperationalInsights", id.Workspace, id.Name); err != nil {
return fmt.Errorf("deleting Sentinel Alert Rule Fusion %q (Resource Group %q / Workspace: %q): %+v", id.Name, id.ResourceGroup, id.Workspace, err)
}

return nil
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,167 @@
package sentinel_test

import (
"context"
"fmt"
"testing"

"github.com/hashicorp/terraform-plugin-sdk/helper/resource"
"github.com/hashicorp/terraform-plugin-sdk/terraform"
"github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/acceptance"
"github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/acceptance/check"
"github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/clients"
"github.com/terraform-providers/terraform-provider-azurerm/azurerm/internal/services/sentinel/parse"
"github.com/terraform-providers/terraform-provider-azurerm/azurerm/utils"
)

type SentinelAlertRuleFusionResource struct{}

func TestAccSentinelAlertRuleFusion_basic(t *testing.T) {
data := acceptance.BuildTestData(t, "azurerm_sentinel_alert_rule_fusion", "test")
r := SentinelAlertRuleFusionResource{}

data.ResourceTest(t, r, []resource.TestStep{
{
Config: r.basic(data),
Check: resource.ComposeTestCheckFunc(
check.That(data.ResourceName).ExistsInAzure(r),
),
},
data.ImportStep(),
})
}

func TestAccSentinelAlertRuleFusion_complete(t *testing.T) {
data := acceptance.BuildTestData(t, "azurerm_sentinel_alert_rule_fusion", "test")
r := SentinelAlertRuleFusionResource{}

data.ResourceTest(t, r, []resource.TestStep{
{
Config: r.complete(data),
Check: resource.ComposeTestCheckFunc(
check.That(data.ResourceName).ExistsInAzure(r),
),
},
data.ImportStep(),
})
}

func TestAccSentinelAlertRuleFusion_update(t *testing.T) {
data := acceptance.BuildTestData(t, "azurerm_sentinel_alert_rule_fusion", "test")
r := SentinelAlertRuleFusionResource{}

data.ResourceTest(t, r, []resource.TestStep{
{
Config: r.basic(data),
Check: resource.ComposeTestCheckFunc(
check.That(data.ResourceName).ExistsInAzure(r),
),
},
data.ImportStep(),
{
Config: r.complete(data),
Check: resource.ComposeTestCheckFunc(
check.That(data.ResourceName).ExistsInAzure(r),
),
},
data.ImportStep(),
{
Config: r.basic(data),
Check: resource.ComposeTestCheckFunc(
check.That(data.ResourceName).ExistsInAzure(r),
),
},
data.ImportStep(),
})
}

func TestAccSentinelAlertRuleFusion_requiresImport(t *testing.T) {
data := acceptance.BuildTestData(t, "azurerm_sentinel_alert_rule_fusion", "test")
r := SentinelAlertRuleFusionResource{}

data.ResourceTest(t, r, []resource.TestStep{
{
Config: r.basic(data),
Check: resource.ComposeTestCheckFunc(
check.That(data.ResourceName).ExistsInAzure(r),
),
},
data.RequiresImportErrorStep(r.requiresImport),
})
}

func (r SentinelAlertRuleFusionResource) Exists(ctx context.Context, client *clients.Client, state *terraform.InstanceState) (*bool, error) {
alertRuleClient := client.Sentinel.AlertRulesClient
id, err := parse.SentinelAlertRuleID(state.ID)
if err != nil {
return nil, err
}

resp, err := alertRuleClient.Get(ctx, id.ResourceGroup, "Microsoft.OperationalInsights", id.Workspace, id.Name)
if err != nil {
if utils.ResponseWasNotFound(resp.Response) {
return utils.Bool(false), nil
}

return nil, fmt.Errorf("retrieving Sentinel Alert Rule Fusion (%q): %+v", state.String(), err)
}

return utils.Bool(resp.Value != nil), nil
}

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

resource "azurerm_sentinel_alert_rule_fusion" "test" {
name = "acctest-SentinelAlertRule-Fusion-%d"
log_analytics_workspace_id = azurerm_log_analytics_workspace.test.id
alert_rule_template_guid = "f71aba3d-28fb-450b-b192-4e76a83015c8"
}
`, r.template(data), data.RandomInteger)
}

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

resource "azurerm_sentinel_alert_rule_fusion" "test" {
name = "acctest-SentinelAlertRule-Fusion-%d"
log_analytics_workspace_id = azurerm_log_analytics_workspace.test.id
alert_rule_template_guid = "f71aba3d-28fb-450b-b192-4e76a83015c8"
enabled = false
}
`, r.template(data), data.RandomInteger)
}

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

resource "azurerm_sentinel_alert_rule_fusion" "import" {
name = azurerm_sentinel_alert_rule_fusion.test.name
log_analytics_workspace_id = azurerm_sentinel_alert_rule_fusion.test.log_analytics_workspace_id
alert_rule_template_guid = azurerm_sentinel_alert_rule_fusion.test.alert_rule_template_guid
}
`, r.basic(data))
}

func (r SentinelAlertRuleFusionResource) template(data acceptance.TestData) string {
return fmt.Sprintf(`
provider "azurerm" {
features {}
}

resource "azurerm_resource_group" "test" {
name = "acctestRG-sentinel-%d"
location = "%s"
}

resource "azurerm_log_analytics_workspace" "test" {
name = "acctestLAW-%d"
location = azurerm_resource_group.test.location
resource_group_name = azurerm_resource_group.test.name
sku = "PerGB2018"
}
`, data.RandomInteger, data.Locations.Primary, data.RandomInteger)
}
4 changes: 4 additions & 0 deletions website/azurerm.erb
Original file line number Diff line number Diff line change
Expand Up @@ -2781,6 +2781,10 @@
<li>
<a href="#">Sentinel Resources</a>
<ul class="nav">
<li>
<a href="/docs/providers/azurerm/r/sentinel_alert_rule_fusion.html">azurerm_sentinel_alert_rule_fusion</a>
</li>

<li>
<a href="/docs/providers/azurerm/r/sentinel_alert_rule_ms_security_incident.html">azurerm_sentinel_alert_rule_ms_security_incident</a>
</li>
Expand Down
Loading