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

azurerm_sentinel_alert_rule_fusion - remove existing check for built in rule #27653

Merged
merged 11 commits into from
Nov 7, 2024
142 changes: 92 additions & 50 deletions internal/services/sentinel/sentinel_alert_rule_fusion_resource.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,20 +8,23 @@ import (
"log"
"time"

"github.com/hashicorp/go-azure-helpers/lang/pointer"
"github.com/hashicorp/go-azure-helpers/lang/response"
"github.com/hashicorp/go-azure-sdk/resource-manager/securityinsights/2022-10-01-preview/alertrules"
"github.com/hashicorp/terraform-provider-azurerm/helpers/tf"
"github.com/hashicorp/terraform-provider-azurerm/internal/clients"
"github.com/hashicorp/terraform-provider-azurerm/internal/features"
"github.com/hashicorp/terraform-provider-azurerm/internal/tf/pluginsdk"
"github.com/hashicorp/terraform-provider-azurerm/internal/tf/validation"
"github.com/hashicorp/terraform-provider-azurerm/internal/timeouts"
)

const SentinelAlertRuleFusionName = "BuiltInFusion"

func resourceSentinelAlertRuleFusion() *pluginsdk.Resource {
return &pluginsdk.Resource{
Create: resourceSentinelAlertRuleFusionCreateUpdate,
resource := &pluginsdk.Resource{
Create: resourceSentinelAlertRuleFusionCreate,
Read: resourceSentinelAlertRuleFusionRead,
Update: resourceSentinelAlertRuleFusionCreateUpdate,
Update: resourceSentinelAlertRuleFusionUpdate,
Delete: resourceSentinelAlertRuleFusionDelete,

Importer: pluginsdk.ImporterValidatingResourceIdThen(func(id string) error {
Expand All @@ -37,13 +40,6 @@ func resourceSentinelAlertRuleFusion() *pluginsdk.Resource {
},

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

"log_analytics_workspace_id": {
Type: pluginsdk.TypeString,
Required: true,
Expand Down Expand Up @@ -122,34 +118,38 @@ func resourceSentinelAlertRuleFusion() *pluginsdk.Resource {
},
},
}

if !features.FivePointOhBeta() {
resource.Schema["name"] = &pluginsdk.Schema{
Deprecated: "the `name` is deprecated and will be removed in v5.0 version of the provider.",
Type: pluginsdk.TypeString,
Optional: true,
ForceNew: true,
Default: SentinelAlertRuleFusionName,
ValidateFunc: validation.StringIsNotEmpty,
}
}
return resource
}

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

name := d.Get("name").(string)
name := SentinelAlertRuleFusionName
if !features.FivePointOhBeta() {
name = d.Get("name").(string)
}

workspaceID, err := alertrules.ParseWorkspaceID(d.Get("log_analytics_workspace_id").(string))
if err != nil {
return err
}
id := alertrules.NewAlertRuleID(workspaceID.SubscriptionId, workspaceID.ResourceGroupName, workspaceID.WorkspaceName, name)

if d.IsNewResource() {
stephybun marked this conversation as resolved.
Show resolved Hide resolved
resp, err := client.Get(ctx, id)
if err != nil {
if !response.WasNotFound(resp.HttpResponse) {
return fmt.Errorf("checking for existing %q: %+v", id, err)
}
}

if !response.WasNotFound(resp.HttpResponse) {
return tf.ImportAsExistsError("azurerm_sentinel_alert_rule_fusion", id.ID())
}
}

// The only one fusion alert is enabled by default, so we do not do exisiting check here.
// https://learn.microsoft.com/en-us/azure/sentinel/configure-fusion-rules#configure-scheduled-analytics-rules-for-fusion-detections
params := alertrules.FusionAlertRule{
Properties: &alertrules.FusionAlertRuleProperties{
AlertRuleTemplateName: d.Get("alert_rule_template_guid").(string),
Expand All @@ -158,25 +158,65 @@ func resourceSentinelAlertRuleFusionCreateUpdate(d *pluginsdk.ResourceData, meta
},
}

if !d.IsNewResource() {
resp, err := client.Get(ctx, id)
if err != nil {
return fmt.Errorf("retrieving %q: %+v", id, err)
}
if _, err := client.CreateOrUpdate(ctx, id, params); err != nil {
return fmt.Errorf("creating %s: %+v", id, err)
}

if resp.Model == nil {
return fmt.Errorf("retrieving %q: model was nil", id)
}
if err = assertAlertRuleKind(resp.Model, alertrules.AlertRuleKindFusion); err != nil {
return fmt.Errorf("asserting alert rule of %q: %+v", id, err)
}
d.SetId(id.ID())

return resourceSentinelAlertRuleFusionRead(d, meta)
}

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

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

if _, err := client.CreateOrUpdate(ctx, id, params); err != nil {
return fmt.Errorf("creating Sentinel Alert Rule Fusion %q: %+v", id, err)
resp, err := client.Get(ctx, *id)
if err != nil {
return fmt.Errorf("retrieving %s: %+v", id, err)
}

d.SetId(id.ID())
if resp.Model == nil {
return fmt.Errorf("retrieving %s: `model` was nil", id)
}

payload, ok := resp.Model.(alertrules.FusionAlertRule)
if !ok {
return fmt.Errorf("retrieving %s: expected an alert rule of type `Fusion`, got %q", id, pointer.From(resp.Model.AlertRule().Type))
}

if payload.Properties == nil {
return fmt.Errorf("retrieving %s: `properties` was nil", id)
}

if d.HasChange("alert_rule_template_guid") {
payload.Properties.AlertRuleTemplateName = d.Get("alert_rule_template_guid").(string)
}

if d.HasChange("enabled") {
payload.Properties.Enabled = d.Get("enabled").(bool)
}

if d.HasChange("source") {
payload.Properties.SourceSettings = expandFusionSourceSettings(d.Get("source").([]interface{}))
}

// The `Description` is read-only but not specified on the Swagger, tracked on: https://github.com/Azure/azure-rest-api-specs/issues/31330
payload.Properties.Description = nil
payload.Properties.DisplayName = nil
payload.Properties.LastModifiedUtc = nil
payload.Properties.Severity = nil
payload.Properties.Tactics = nil

if _, err := client.CreateOrUpdate(ctx, *id, payload); err != nil {
return fmt.Errorf("updating %s: %+v", id, err)
}

return resourceSentinelAlertRuleFusionRead(d, meta)
}
Expand All @@ -194,25 +234,27 @@ func resourceSentinelAlertRuleFusionRead(d *pluginsdk.ResourceData, meta interfa
resp, err := client.Get(ctx, *id)
if err != nil {
if response.WasNotFound(resp.HttpResponse) {
log.Printf("[DEBUG] %q was not found - removing from state!", id)
log.Printf("[DEBUG] %s was not found - removing from state!", id)
d.SetId("")
return nil
}

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

workspaceId := alertrules.NewWorkspaceID(id.SubscriptionId, id.ResourceGroupName, id.WorkspaceName)

if !features.FivePointOhBeta() {
d.Set("name", id.RuleId)
}
d.Set("log_analytics_workspace_id", workspaceId.ID())

if model := resp.Model; model != nil {
if err := assertAlertRuleKind(resp.Model, alertrules.AlertRuleKindFusion); err != nil {
return fmt.Errorf("asserting alert rule of %q: %+v", id, err)
return fmt.Errorf("asserting alert rule of %s: %+v", id, err)
}

if rule, ok := model.(alertrules.FusionAlertRule); ok {
d.Set("name", id.RuleId)

workspaceId := alertrules.NewWorkspaceID(id.SubscriptionId, id.ResourceGroupName, id.WorkspaceName)
d.Set("log_analytics_workspace_id", workspaceId.ID())

if prop := rule.Properties; prop != nil {
d.Set("enabled", prop.Enabled)
d.Set("alert_rule_template_guid", prop.AlertRuleTemplateName)
Expand All @@ -237,7 +279,7 @@ func resourceSentinelAlertRuleFusionDelete(d *pluginsdk.ResourceData, meta inter
}

if _, err := client.Delete(ctx, *id); err != nil {
return fmt.Errorf("deleting Sentinel Alert Rule Fusion %q: %+v", id, err)
return fmt.Errorf("deleting %s: %+v", id, err)
}

return nil
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -84,21 +84,6 @@ func TestAccSentinelAlertRuleFusion_sourceSetting(t *testing.T) {
})
}

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

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

func (r SentinelAlertRuleFusionResource) Exists(ctx context.Context, client *clients.Client, state *pluginsdk.InstanceState) (*bool, error) {
alertRuleClient := client.Sentinel.AlertRulesClient
id, err := alertrules.ParseAlertRuleID(state.ID)
Expand Down Expand Up @@ -132,15 +117,14 @@ func (r SentinelAlertRuleFusionResource) basic(data acceptance.TestData) string

data "azurerm_sentinel_alert_rule_template" "test" {
display_name = "Advanced Multistage Attack Detection"
log_analytics_workspace_id = azurerm_log_analytics_solution.test.workspace_resource_id
log_analytics_workspace_id = azurerm_sentinel_log_analytics_workspace_onboarding.test.workspace_id
}

resource "azurerm_sentinel_alert_rule_fusion" "test" {
name = "acctest-SentinelAlertRule-Fusion-%d"
log_analytics_workspace_id = azurerm_log_analytics_solution.test.workspace_resource_id
log_analytics_workspace_id = azurerm_sentinel_log_analytics_workspace_onboarding.test.workspace_id
alert_rule_template_guid = data.azurerm_sentinel_alert_rule_template.test.name
}
`, r.template(data), data.RandomInteger)
`, r.template(data))
}

func (r SentinelAlertRuleFusionResource) disabled(data acceptance.TestData) string {
Expand All @@ -149,16 +133,15 @@ func (r SentinelAlertRuleFusionResource) disabled(data acceptance.TestData) stri

data "azurerm_sentinel_alert_rule_template" "test" {
display_name = "Advanced Multistage Attack Detection"
log_analytics_workspace_id = azurerm_log_analytics_solution.test.workspace_resource_id
log_analytics_workspace_id = azurerm_sentinel_log_analytics_workspace_onboarding.test.workspace_id
}

resource "azurerm_sentinel_alert_rule_fusion" "test" {
name = "acctest-SentinelAlertRule-Fusion-%d"
log_analytics_workspace_id = azurerm_log_analytics_solution.test.workspace_resource_id
log_analytics_workspace_id = azurerm_sentinel_log_analytics_workspace_onboarding.test.workspace_id
alert_rule_template_guid = data.azurerm_sentinel_alert_rule_template.test.name
enabled = false
}
`, r.template(data), data.RandomInteger)
`, r.template(data))
}

func (r SentinelAlertRuleFusionResource) sourceSetting(data acceptance.TestData, enabled bool) string {
Expand All @@ -167,12 +150,11 @@ func (r SentinelAlertRuleFusionResource) sourceSetting(data acceptance.TestData,

data "azurerm_sentinel_alert_rule_template" "test" {
display_name = "Advanced Multistage Attack Detection"
log_analytics_workspace_id = azurerm_log_analytics_solution.test.workspace_resource_id
log_analytics_workspace_id = azurerm_sentinel_log_analytics_workspace_onboarding.test.workspace_id
}

resource "azurerm_sentinel_alert_rule_fusion" "test" {
name = "acctest-SentinelAlertRule-Fusion-%[2]d"
log_analytics_workspace_id = azurerm_log_analytics_solution.test.workspace_resource_id
log_analytics_workspace_id = azurerm_sentinel_log_analytics_workspace_onboarding.test.workspace_id
alert_rule_template_guid = data.azurerm_sentinel_alert_rule_template.test.name
source {
name = "Anomalies"
Expand Down Expand Up @@ -236,18 +218,6 @@ resource "azurerm_sentinel_alert_rule_fusion" "test" {
`, r.template(data), data.RandomInteger, enabled)
}

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" {
Expand All @@ -266,17 +236,8 @@ resource "azurerm_log_analytics_workspace" "test" {
sku = "PerGB2018"
}

resource "azurerm_log_analytics_solution" "test" {
solution_name = "SecurityInsights"
location = azurerm_resource_group.test.location
resource_group_name = azurerm_resource_group.test.name
workspace_resource_id = azurerm_log_analytics_workspace.test.id
workspace_name = azurerm_log_analytics_workspace.test.name

plan {
publisher = "Microsoft"
product = "OMSGallery/SecurityInsights"
}
resource "azurerm_sentinel_log_analytics_workspace_onboarding" "test" {
workspace_id = azurerm_log_analytics_workspace.test.id
}
`, data.RandomInteger, data.Locations.Primary, data.RandomInteger)
}
4 changes: 4 additions & 0 deletions website/docs/5.0-upgrade-guide.html.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,10 @@ Please follow the format in the example below for listing breaking changes in re
* The `example_property_with_changed_default` property now defaults to `NewDefault`.
```

### `azurerm_sentinel_alert_rule_fusion`

* The deprecated `name` property has been removed.

### `azurerm_storage_account`

* The deprecated `queue_properties` block has been removed and superseded by the `azurerm_storage_account_queue_properties` resource.
Expand Down
18 changes: 3 additions & 15 deletions website/docs/r/sentinel_alert_rule_fusion.html.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -25,22 +25,12 @@ resource "azurerm_log_analytics_workspace" "example" {
sku = "PerGB2018"
}

resource "azurerm_log_analytics_solution" "example" {
solution_name = "SecurityInsights"
location = azurerm_resource_group.example.location
resource_group_name = azurerm_resource_group.example.name
workspace_resource_id = azurerm_log_analytics_workspace.example.id
workspace_name = azurerm_log_analytics_workspace.example.name

plan {
publisher = "Microsoft"
product = "OMSGallery/SecurityInsights"
}
resource "azurerm_sentinel_log_analytics_workspace_onboarding" "example" {
workspace_id = azurerm_log_analytics_workspace.example.id
}

resource "azurerm_sentinel_alert_rule_fusion" "example" {
name = "example-fusion-alert-rule"
log_analytics_workspace_id = azurerm_log_analytics_solution.example.workspace_resource_id
log_analytics_workspace_id = azurerm_sentinel_log_analytics_workspace_onboarding.example.workspace_id
alert_rule_template_guid = "f71aba3d-28fb-450b-b192-4e76a83015c8"
}
```
Expand All @@ -49,8 +39,6 @@ resource "azurerm_sentinel_alert_rule_fusion" "example" {

The following arguments are supported:

* `name` - (Required) The name which should be used for this Sentinel Fusion Alert Rule. Changing this forces a new Sentinel Fusion Alert Rule to be created.

* `log_analytics_workspace_id` - (Required) The ID of the Log Analytics Workspace this Sentinel Fusion Alert Rule belongs to. Changing this forces a new Sentinel Fusion Alert Rule to be created.

* `alert_rule_template_guid` - (Required) The GUID of the alert rule template which is used for this Sentinel Fusion Alert Rule. Changing this forces a new Sentinel Fusion Alert Rule to be created.
Expand Down
Loading