Skip to content
Open
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 CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
## [Unreleased]

- Create `elasticstack_kibana_prebuilt_rule` resource ([#1296](https://github.com/elastic/terraform-provider-elasticstack/pull/1296))
- Create `elasticstack_kibana_maintenance_window` resource. ([#1224](https://github.com/elastic/terraform-provider-elasticstack/pull/1224))
- Add support for `solution` field in `elasticstack_kibana_space` resource and data source ([#1102](https://github.com/elastic/terraform-provider-elasticstack/issues/1102))
- Add `slo_id` validation to `elasticstack_kibana_slo` ([#1221](https://github.com/elastic/terraform-provider-elasticstack/pull/1221))
Expand Down
41 changes: 41 additions & 0 deletions docs/resources/kibana_install_prebuilt_rules.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
---
# generated by https://github.com/hashicorp/terraform-plugin-docs
page_title: "elasticstack_kibana_install_prebuilt_rules Resource - elasticstack"
subcategory: ""
description: |-
Manages Elastic prebuilt detection rules. This resource installs and updates Elastic prebuilt rules and timelines. See https://www.elastic.co/guide/en/security/current/prebuilt-rules.html
---

# elasticstack_kibana_install_prebuilt_rules (Resource)

Manages Elastic prebuilt detection rules. This resource installs and updates Elastic prebuilt rules and timelines. See https://www.elastic.co/guide/en/security/current/prebuilt-rules.html

## Example Usage

```terraform
provider "elasticstack" {
kibana {}
}


resource "elasticstack_kibana_install_prebuilt_rules" "example" {
space_id = "default"
}
```

<!-- schema generated by tfplugindocs -->
## Schema

### Optional

- `space_id` (String) An identifier for the space. If space_id is not provided, the default space is used.

### Read-Only

- `id` (String) The ID of this resource.
- `rules_installed` (Number) Number of prebuilt rules that are installed.
- `rules_not_installed` (Number) Number of prebuilt rules that are not installed.
- `rules_not_updated` (Number) Number of prebuilt rules that have updates available.
- `timelines_installed` (Number) Number of prebuilt timelines that are installed.
- `timelines_not_installed` (Number) Number of prebuilt timelines that are not installed.
- `timelines_not_updated` (Number) Number of prebuilt timelines that have updates available.
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
provider "elasticstack" {
kibana {}
}


resource "elasticstack_kibana_install_prebuilt_rules" "example" {
space_id = "default"
}
42 changes: 42 additions & 0 deletions internal/kibana/prebuilt_rules/acc_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package prebuilt_rules_test

import (
"testing"

"github.com/elastic/terraform-provider-elasticstack/internal/acctest"
"github.com/elastic/terraform-provider-elasticstack/internal/versionutils"
"github.com/hashicorp/go-version"
"github.com/hashicorp/terraform-plugin-testing/helper/resource"
)

var minVersionPrebuiltRules = version.Must(version.NewVersion("8.0.0"))

func TestAccResourcePrebuiltRules(t *testing.T) {
resource.Test(t, resource.TestCase{
PreCheck: func() { acctest.PreCheck(t) },
ProtoV6ProviderFactories: acctest.Providers,
Steps: []resource.TestStep{
{
SkipFunc: versionutils.CheckIfVersionIsUnsupported(minVersionPrebuiltRules),
Config: testAccPrebuiltRuleConfigBasic(),
Check: resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttr("elasticstack_kibana_install_prebuilt_rules.test", "space_id", "default"),
resource.TestCheckResourceAttrSet("elasticstack_kibana_install_prebuilt_rules.test", "rules_installed"),
resource.TestCheckResourceAttrSet("elasticstack_kibana_install_prebuilt_rules.test", "rules_not_installed"),
resource.TestCheckResourceAttrSet("elasticstack_kibana_install_prebuilt_rules.test", "rules_not_updated"),
resource.TestCheckResourceAttrSet("elasticstack_kibana_install_prebuilt_rules.test", "timelines_installed"),
resource.TestCheckResourceAttrSet("elasticstack_kibana_install_prebuilt_rules.test", "timelines_not_installed"),
resource.TestCheckResourceAttrSet("elasticstack_kibana_install_prebuilt_rules.test", "timelines_not_updated"),
),
},
},
})
}

func testAccPrebuiltRuleConfigBasic() string {
return `
resource "elasticstack_kibana_install_prebuilt_rules" "test" {
space_id = "default"
}
`
}
58 changes: 58 additions & 0 deletions internal/kibana/prebuilt_rules/create.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
package prebuilt_rules

import (
"context"

"github.com/elastic/terraform-provider-elasticstack/internal/utils"
"github.com/hashicorp/go-version"
"github.com/hashicorp/terraform-plugin-framework/resource"
)

func (r *PrebuiltRuleResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) {
var model prebuiltRuleModel

resp.Diagnostics.Append(req.Plan.Get(ctx, &model)...)
if resp.Diagnostics.HasError() {
return
}

serverVersion, sdkDiags := r.client.ServerVersion(ctx)
resp.Diagnostics.Append(utils.FrameworkDiagsFromSDK(sdkDiags)...)
if resp.Diagnostics.HasError() {
return
}

minVersion := version.Must(version.NewVersion("8.0.0"))
if serverVersion.LessThan(minVersion) {
resp.Diagnostics.AddError("Unsupported server version", "Prebuilt rules are not supported until Elastic Stack v8.0.0. Upgrade the target server to use this resource")
return
}

client, err := r.client.GetKibanaOapiClient()
if err != nil {
resp.Diagnostics.AddError(err.Error(), "")
return
}

spaceID := model.SpaceID.ValueString()

// Install/update prebuilt rules and timelines
resp.Diagnostics.Append(installPrebuiltRules(ctx, client, spaceID)...)
if resp.Diagnostics.HasError() {
return
}

// Set the resource ID to the space ID
model.ID = model.SpaceID

// Read the current status to populate computed attributes
status, statusDiags := getPrebuiltRulesStatus(ctx, client, spaceID)
resp.Diagnostics.Append(statusDiags...)
if resp.Diagnostics.HasError() {
return
}

model.populateFromStatus(ctx, status)

resp.Diagnostics.Append(resp.State.Set(ctx, model)...)
}
12 changes: 12 additions & 0 deletions internal/kibana/prebuilt_rules/delete.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package prebuilt_rules

import (
"context"

"github.com/hashicorp/terraform-plugin-framework/resource"
"github.com/hashicorp/terraform-plugin-log/tflog"
)

func (r *PrebuiltRuleResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) {
tflog.Info(ctx, "Delete isn't supported for elasticstack_kibana_install_prebuilt_rules")
}
79 changes: 79 additions & 0 deletions internal/kibana/prebuilt_rules/models.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
package prebuilt_rules

import (
"context"
"fmt"
"net/http"

"github.com/elastic/terraform-provider-elasticstack/generated/kbapi"
"github.com/elastic/terraform-provider-elasticstack/internal/clients/kibana_oapi"
"github.com/elastic/terraform-provider-elasticstack/internal/utils"
"github.com/hashicorp/terraform-plugin-framework/diag"
"github.com/hashicorp/terraform-plugin-framework/types"
)

type prebuiltRuleModel struct {
ID types.String `tfsdk:"id"`
SpaceID types.String `tfsdk:"space_id"`
RulesInstalled types.Int64 `tfsdk:"rules_installed"`
RulesNotInstalled types.Int64 `tfsdk:"rules_not_installed"`
RulesNotUpdated types.Int64 `tfsdk:"rules_not_updated"`
TimelinesInstalled types.Int64 `tfsdk:"timelines_installed"`
TimelinesNotInstalled types.Int64 `tfsdk:"timelines_not_installed"`
TimelinesNotUpdated types.Int64 `tfsdk:"timelines_not_updated"`
}

func (model *prebuiltRuleModel) populateFromStatus(ctx context.Context, status *kbapi.ReadPrebuiltRulesAndTimelinesStatusResponse) {
model.RulesInstalled = types.Int64Value(int64(status.JSON200.RulesInstalled))
model.RulesNotInstalled = types.Int64Value(int64(status.JSON200.RulesNotInstalled))
model.RulesNotUpdated = types.Int64Value(int64(status.JSON200.RulesNotUpdated))
model.TimelinesInstalled = types.Int64Value(int64(status.JSON200.TimelinesInstalled))
model.TimelinesNotInstalled = types.Int64Value(int64(status.JSON200.TimelinesNotInstalled))
model.TimelinesNotUpdated = types.Int64Value(int64(status.JSON200.TimelinesNotUpdated))
}

func getPrebuiltRulesStatus(ctx context.Context, client *kibana_oapi.Client, spaceID string) (*kbapi.ReadPrebuiltRulesAndTimelinesStatusResponse, diag.Diagnostics) {
resp, err := client.API.ReadPrebuiltRulesAndTimelinesStatusWithResponse(ctx, func(ctx context.Context, req *http.Request) error {
if spaceID != "default" {
req.Header.Set("kbn-space-id", spaceID)
}
return nil
})

if err != nil {
return nil, utils.FrameworkDiagFromError(err)
}

if resp.StatusCode() != 200 {
return nil, utils.FrameworkDiagFromError(fmt.Errorf("failed to get prebuilt rules status: %s", resp.Status()))
}

return resp, nil
}

func installPrebuiltRules(ctx context.Context, client *kibana_oapi.Client, spaceID string) diag.Diagnostics {
resp, err := client.API.InstallPrebuiltRulesAndTimelinesWithResponse(ctx, func(ctx context.Context, req *http.Request) error {
if spaceID != "default" {
req.Header.Set("kbn-space-id", spaceID)
}
return nil
})

if err != nil {
return utils.FrameworkDiagFromError(err)
}

if resp.StatusCode() != 200 {
return utils.FrameworkDiagFromError(fmt.Errorf("failed to install prebuilt rules: %s - %s", resp.Status(), string(resp.Body)))
}

return nil
}

func needsRuleUpdate(ctx context.Context, client *kibana_oapi.Client, spaceID string) bool {
status, diags := getPrebuiltRulesStatus(ctx, client, spaceID)
if diags.HasError() {
return true
}
return status.JSON200.RulesNotInstalled >= 1 || status.JSON200.RulesNotUpdated >= 1
}
56 changes: 56 additions & 0 deletions internal/kibana/prebuilt_rules/read.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
package prebuilt_rules

import (
"context"

"github.com/elastic/terraform-provider-elasticstack/internal/utils"
"github.com/hashicorp/go-version"
"github.com/hashicorp/terraform-plugin-framework/resource"
)

func (r *PrebuiltRuleResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) {
var model prebuiltRuleModel

resp.Diagnostics.Append(req.State.Get(ctx, &model)...)
if resp.Diagnostics.HasError() {
return
}

serverVersion, sdkDiags := r.client.ServerVersion(ctx)
resp.Diagnostics.Append(utils.FrameworkDiagsFromSDK(sdkDiags)...)
if resp.Diagnostics.HasError() {
return
}

minVersion := version.Must(version.NewVersion("8.0.0"))
if serverVersion.LessThan(minVersion) {
resp.Diagnostics.AddError("Unsupported server version", "Prebuilt rules are not supported until Elastic Stack v8.0.0. Upgrade the target server to use this resource")
return
}

client, err := r.client.GetKibanaOapiClient()
if err != nil {
resp.Diagnostics.AddError(err.Error(), "")
return
}

spaceID := model.ID.ValueString()

// Get current status
status, statusDiags := getPrebuiltRulesStatus(ctx, client, spaceID)
resp.Diagnostics.Append(statusDiags...)
if resp.Diagnostics.HasError() {
return
}

model.populateFromStatus(ctx, status)

if needsRuleUpdate(ctx, client, spaceID) {
resp.Diagnostics.Append(installPrebuiltRules(ctx, client, spaceID)...)
if resp.Diagnostics.HasError() {
return
}
}

resp.Diagnostics.Append(resp.State.Set(ctx, model)...)
}
33 changes: 33 additions & 0 deletions internal/kibana/prebuilt_rules/resource.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package prebuilt_rules

import (
"context"
"fmt"

"github.com/elastic/terraform-provider-elasticstack/internal/clients"
"github.com/hashicorp/terraform-plugin-framework/resource"
)

var (
_ resource.Resource = &PrebuiltRuleResource{}
_ resource.ResourceWithConfigure = &PrebuiltRuleResource{}
)

// NewResource is a helper function to simplify the provider implementation.
func NewResource() resource.Resource {
return &PrebuiltRuleResource{}
}

type PrebuiltRuleResource struct {
client *clients.ApiClient
}

func (r *PrebuiltRuleResource) Configure(ctx context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) {
client, diags := clients.ConvertProviderData(req.ProviderData)
resp.Diagnostics.Append(diags...)
r.client = client
}

func (r *PrebuiltRuleResource) Metadata(ctx context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) {
resp.TypeName = fmt.Sprintf("%s_%s", req.ProviderTypeName, "kibana_install_prebuilt_rules")
}
Loading