Skip to content

Commit

Permalink
azurerm_sentinel_alert_rule_fusion - remove existing check for buil…
Browse files Browse the repository at this point in the history
…t in rule (hashicorp#27653)

* `azurerm_sentinel_alert_rule_fusion` - remove existing check for built rule

* divide create and update function

* update workaround

* update per comment

* deprecate `name`

* update documents

* remove `name` from document

* update per comments

* update error message & document
  • Loading branch information
ziyeqf authored Nov 7, 2024
1 parent 313d2ac commit b70594c
Show file tree
Hide file tree
Showing 4 changed files with 109 additions and 114 deletions.
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() {
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

0 comments on commit b70594c

Please sign in to comment.