Skip to content

Commit 6591ad4

Browse files
authored
Add Security List Data Stream Resource (#1525)
1 parent ccd0484 commit 6591ad4

File tree

49 files changed

+2267
-127
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

49 files changed

+2267
-127
lines changed

CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,12 @@ alias = [
5151
- Add `elasticstack_elasticsearch_alias` resource ([#1343](https://github.com/elastic/terraform-provider-elasticstack/pull/1343))
5252
- Add `mapping_total_fields_limit` to `elasticstack_elasticsearch_index` ([#1494](https://github.com/elastic/terraform-provider-elasticstack/pull/1494))
5353
- Add `elasticstack_kibana_default_data_view` resource ([#1379](https://github.com/elastic/terraform-provider-elasticstack/pull/1379))
54+
- Add support for [Security Exceptions](https://github.com/elastic/terraform-provider-elasticstack/issues/1332)
55+
- Add `elasticstack_kibana_security_exception_item` resource ([#1496](https://github.com/elastic/terraform-provider-elasticstack/pull/1496))
56+
- Add `elasticstack_kibana_security_exception_list` resource ([#1495](https://github.com/elastic/terraform-provider-elasticstack/pull/1495))
57+
- Add `elasticstack_kibana_security_list` resource ([#1489](https://github.com/elastic/terraform-provider-elasticstack/pull/1489))
58+
- Add `elasticstack_kibana_security_list_item` resource ([#1492](https://github.com/elastic/terraform-provider-elasticstack/pull/1492))
59+
- Add `elasticstack_kibana_security_list_data_streams` resource ([#1525](https://github.com/elastic/terraform-provider-elasticstack/pull/1525))
5460

5561
## [0.12.2] - 2025-11-19
5662
- Fix `elasticstack_elasticsearch_snapshot_lifecycle` metadata type conversion causing terraform apply to fail ([#1409](https://github.com/elastic/terraform-provider-elasticstack/issues/1409))
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
# Create list data streams in the default space
2+
resource "elasticstack_kibana_security_list_data_streams" "default" {
3+
}
4+
5+
# Create list data streams in a custom space
6+
resource "elasticstack_kibana_security_list_data_streams" "custom" {
7+
space_id = "my-space"
8+
}

internal/clients/kibana_oapi/security_lists.go

Lines changed: 48 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,15 +12,62 @@ import (
1212

1313
// CreateListIndex creates the .lists and .items data streams for a space if they don't exist.
1414
// This is required before any list operations can be performed.
15-
func CreateListIndex(ctx context.Context, client *Client, spaceId string) diag.Diagnostics {
15+
// Returns true if acknowledged, and diagnostics if there was an error.
16+
func CreateListIndex(ctx context.Context, client *Client, spaceId string) (bool, diag.Diagnostics) {
1617
resp, err := client.API.CreateListIndexWithResponse(ctx, kbapi.SpaceId(spaceId))
18+
if err != nil {
19+
return false, diagutil.FrameworkDiagFromError(err)
20+
}
21+
22+
switch resp.StatusCode() {
23+
case http.StatusOK:
24+
if resp.JSON200 != nil {
25+
return resp.JSON200.Acknowledged, nil
26+
}
27+
return true, nil
28+
case http.StatusConflict:
29+
// Data streams already exist ([docs](https://www.elastic.co/docs/api/doc/kibana/operation/operation-createlistindex#operation-createlistindex-409))
30+
return true, nil
31+
default:
32+
return false, reportUnknownError(resp.StatusCode(), resp.Body)
33+
}
34+
}
35+
36+
// ReadListIndex reads the status of .lists and .items data streams for a space.
37+
// Returns the status of list_index and list_item_index separately, and diagnostics on error.
38+
func ReadListIndex(ctx context.Context, client *Client, spaceId string) (listIndex bool, listItemIndex bool, diags diag.Diagnostics) {
39+
resp, err := client.API.ReadListIndexWithResponse(ctx, kbapi.SpaceId(spaceId))
40+
if err != nil {
41+
return false, false, diagutil.FrameworkDiagFromError(err)
42+
}
43+
44+
switch resp.StatusCode() {
45+
case http.StatusOK:
46+
if resp.JSON200 != nil {
47+
return resp.JSON200.ListIndex, resp.JSON200.ListItemIndex, nil
48+
}
49+
return false, false, nil
50+
case http.StatusNotFound:
51+
// Data streams don't exist
52+
return false, false, nil
53+
default:
54+
return false, false, reportUnknownError(resp.StatusCode(), resp.Body)
55+
}
56+
}
57+
58+
// DeleteListIndex deletes the .lists and .items data streams for a space.
59+
// Returns diagnostics if there was an error.
60+
func DeleteListIndex(ctx context.Context, client *Client, spaceId string) diag.Diagnostics {
61+
resp, err := client.API.DeleteListIndexWithResponse(ctx, kbapi.SpaceId(spaceId))
1762
if err != nil {
1863
return diagutil.FrameworkDiagFromError(err)
1964
}
2065

2166
switch resp.StatusCode() {
2267
case http.StatusOK:
2368
return nil
69+
case http.StatusNotFound:
70+
return nil
2471
default:
2572
return reportUnknownError(resp.StatusCode(), resp.Body)
2673
}

internal/kibana/security_exception_item/acc_test.go

Lines changed: 356 additions & 5 deletions
Large diffs are not rendered by default.

internal/kibana/security_exception_item/schema.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import (
77
"github.com/elastic/terraform-provider-elasticstack/internal/utils/validators"
88
"github.com/hashicorp/terraform-plugin-framework-jsontypes/jsontypes"
99
"github.com/hashicorp/terraform-plugin-framework-timetypes/timetypes"
10+
"github.com/hashicorp/terraform-plugin-framework-validators/listvalidator"
1011
"github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator"
1112
"github.com/hashicorp/terraform-plugin-framework/path"
1213
"github.com/hashicorp/terraform-plugin-framework/resource"
@@ -105,6 +106,9 @@ func (r *ExceptionItemResource) Schema(_ context.Context, _ resource.SchemaReque
105106
"entries": schema.ListNestedAttribute{
106107
MarkdownDescription: "The exception item entries. This defines the conditions under which the exception applies.",
107108
Required: true,
109+
Validators: []validator.List{
110+
listvalidator.SizeAtLeast(1),
111+
},
108112
NestedObject: schema.NestedAttributeObject{
109113
Attributes: map[string]schema.Attribute{
110114
"type": schema.StringAttribute{
@@ -151,6 +155,9 @@ func (r *ExceptionItemResource) Schema(_ context.Context, _ resource.SchemaReque
151155
"type": schema.StringAttribute{
152156
MarkdownDescription: "The value list type (e.g., `keyword`, `ip`, `ip_range`).",
153157
Required: true,
158+
Validators: []validator.String{
159+
stringvalidator.OneOf("keyword", "ip", "ip_range"),
160+
},
154161
},
155162
},
156163
},
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
variable "list_id" {
2+
description = "The exception list ID"
3+
type = string
4+
}
5+
6+
variable "item_id" {
7+
description = "The exception item ID"
8+
type = string
9+
}
10+
11+
provider "elasticstack" {
12+
elasticsearch {}
13+
kibana {}
14+
}
15+
16+
resource "elasticstack_kibana_security_exception_list" "test" {
17+
list_id = var.list_id
18+
name = "Test Exception List for Exists Entry"
19+
description = "Test exception list for exists entry type"
20+
type = "detection"
21+
namespace_type = "single"
22+
}
23+
24+
resource "elasticstack_kibana_security_exception_item" "test" {
25+
list_id = elasticstack_kibana_security_exception_list.test.list_id
26+
item_id = var.item_id
27+
name = "Test Exception Item - Exists Entry Multiple"
28+
description = "Test exception item with multiple exists entries"
29+
type = "simple"
30+
namespace_type = "single"
31+
entries = [
32+
{
33+
type = "exists"
34+
field = "file.hash.sha256"
35+
operator = "included"
36+
},
37+
{
38+
type = "exists"
39+
field = "process.code_signature.trusted"
40+
operator = "included"
41+
},
42+
{
43+
type = "exists"
44+
field = "network.protocol"
45+
operator = "excluded"
46+
}
47+
]
48+
tags = ["test", "exists", "multiple"]
49+
}

internal/kibana/security_exception_item/testdata/TestAccResourceExceptionItemEntryType_List/list/exception_item.tf

Lines changed: 40 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,8 @@
1+
variable "space_id" {
2+
description = "The space ID"
3+
type = string
4+
}
5+
16
variable "exception_list_id" {
27
description = "The exception list ID"
38
type = string
@@ -22,31 +27,53 @@ provider "elasticstack" {
2227
kibana {}
2328
}
2429

30+
resource "elasticstack_kibana_space" "test" {
31+
space_id = var.space_id
32+
name = "Test Space for List Entry"
33+
}
34+
35+
resource "elasticstack_kibana_security_list_data_streams" "test" {
36+
space_id = elasticstack_kibana_space.test.space_id
37+
}
38+
2539
resource "elasticstack_kibana_security_exception_list" "test" {
40+
space_id = elasticstack_kibana_space.test.space_id
2641
list_id = var.exception_list_id
27-
name = "Test Exception List for List Entry"
28-
description = "Test exception list for list entry type"
42+
name = "Test Exception List for List Entry - IP"
43+
description = "Test exception list for list entry type with ip"
2944
type = "detection"
3045
namespace_type = "single"
3146
}
32-
resource "elasticstack_kibana_security_list_item" "test-item" {
33-
list_id = elasticstack_kibana_security_list.test.list_id
34-
value = var.value_list_value
35-
}
3647

3748
# Create a value list to reference in the exception item
38-
resource "elasticstack_kibana_security_list" "test" {
49+
resource "elasticstack_kibana_security_list" "test-ip" {
50+
space_id = elasticstack_kibana_space.test.space_id
3951
list_id = var.value_list_id
40-
name = "Test Value List"
41-
description = "Test value list for list entry type"
52+
name = "Test Value List - IP"
53+
description = "Test value list for list entry type with ip"
4254
type = "ip"
55+
56+
depends_on = [elasticstack_kibana_security_list_data_streams.test]
57+
58+
lifecycle {
59+
create_before_destroy = true
60+
}
61+
}
62+
63+
resource "elasticstack_kibana_security_list_item" "test-item" {
64+
space_id = elasticstack_kibana_space.test.space_id
65+
list_id = elasticstack_kibana_security_list.test-ip.list_id
66+
value = var.value_list_value
67+
68+
depends_on = [elasticstack_kibana_security_list_data_streams.test]
4369
}
4470

4571
resource "elasticstack_kibana_security_exception_item" "test" {
72+
space_id = elasticstack_kibana_space.test.space_id
4673
list_id = elasticstack_kibana_security_exception_list.test.list_id
4774
item_id = var.item_id
48-
name = "Test Exception Item - List Entry"
49-
description = "Test exception item with list entry type"
75+
name = "Test Exception Item - List Entry IP"
76+
description = "Test exception item with list entry type using ip"
5077
type = "simple"
5178
namespace_type = "single"
5279
entries = [
@@ -55,10 +82,10 @@ resource "elasticstack_kibana_security_exception_item" "test" {
5582
field = "source.ip"
5683
operator = "included"
5784
list = {
58-
id = elasticstack_kibana_security_list.test.list_id
85+
id = elasticstack_kibana_security_list.test-ip.list_id
5986
type = "ip"
6087
}
6188
}
6289
]
63-
tags = ["test", "list"]
90+
tags = ["test", "list", "ip"]
6491
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
variable "space_id" {
2+
description = "The space ID"
3+
type = string
4+
}
5+
6+
variable "exception_list_id" {
7+
description = "The exception list ID"
8+
type = string
9+
}
10+
11+
variable "item_id" {
12+
description = "The exception item ID"
13+
type = string
14+
}
15+
16+
variable "value_list_id" {
17+
description = "The value list ID"
18+
type = string
19+
}
20+
variable "value_list_value" {
21+
description = "The value list value"
22+
type = string
23+
}
24+
25+
provider "elasticstack" {
26+
elasticsearch {}
27+
kibana {}
28+
}
29+
30+
resource "elasticstack_kibana_space" "test" {
31+
space_id = var.space_id
32+
name = "Test Space for List Entry"
33+
}
34+
35+
resource "elasticstack_kibana_security_list_data_streams" "test" {
36+
space_id = elasticstack_kibana_space.test.space_id
37+
}
38+
39+
resource "elasticstack_kibana_security_exception_list" "test" {
40+
space_id = elasticstack_kibana_space.test.space_id
41+
list_id = var.exception_list_id
42+
name = "Test Exception List for List Entry - IP Range"
43+
description = "Test exception list for list entry type with ip_range"
44+
type = "detection"
45+
namespace_type = "single"
46+
}
47+
48+
# Create a value list to reference in the exception item
49+
resource "elasticstack_kibana_security_list" "test-ip-range" {
50+
space_id = elasticstack_kibana_space.test.space_id
51+
list_id = var.value_list_id
52+
name = "Test Value List - IP Range"
53+
description = "Test value list for list entry type with ip_range"
54+
type = "ip_range"
55+
56+
depends_on = [elasticstack_kibana_security_list_data_streams.test]
57+
58+
lifecycle {
59+
create_before_destroy = true
60+
}
61+
}
62+
63+
resource "elasticstack_kibana_security_list_item" "test-item" {
64+
space_id = elasticstack_kibana_space.test.space_id
65+
list_id = elasticstack_kibana_security_list.test-ip-range.list_id
66+
value = var.value_list_value
67+
68+
depends_on = [elasticstack_kibana_security_list_data_streams.test]
69+
}
70+
71+
resource "elasticstack_kibana_security_exception_item" "test" {
72+
space_id = elasticstack_kibana_space.test.space_id
73+
list_id = elasticstack_kibana_security_exception_list.test.list_id
74+
item_id = var.item_id
75+
name = "Test Exception Item - List Entry IP Range"
76+
description = "Test exception item with list entry type using ip_range"
77+
type = "simple"
78+
namespace_type = "single"
79+
entries = [
80+
{
81+
type = "list"
82+
field = "destination.ip"
83+
operator = "included"
84+
list = {
85+
id = elasticstack_kibana_security_list.test-ip-range.list_id
86+
type = "ip_range"
87+
}
88+
}
89+
]
90+
tags = ["test", "list", "ip_range"]
91+
}

0 commit comments

Comments
 (0)