Skip to content

Commit a7adf49

Browse files
authored
Merge branch 'main' into kb_update
2 parents 031d7bc + 697a7f4 commit a7adf49

File tree

11 files changed

+759
-302
lines changed

11 files changed

+759
-302
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,10 @@
1313
- Migrate `elasticstack_kibana_action_connector` to the Terraform plugin framework ([#1269](https://github.com/elastic/terraform-provider-elasticstack/pull/1269))
1414
- Migrate `elasticstack_elasticsearch_security_role_mapping` resource and data source to Terraform Plugin Framework ([#1279](https://github.com/elastic/terraform-provider-elasticstack/pull/1279))
1515
- Add support for `inactivity_timeout` in `elasticstack_fleet_agent_policy` ([#641](https://github.com/elastic/terraform-provider-elasticstack/issues/641))
16+
- Add support for `prevent_initial_backfill` to `elasticstack_kibana_slo` ([#1071](https://github.com/elastic/terraform-provider-elasticstack/pull/1071))
17+
- [Refactor] Regenerate the SLO client using the current OpenAPI spec ([#1303](https://github.com/elastic/terraform-provider-elasticstack/pull/1303))
18+
- Add support for `data_view_id` in the `elasticstack_kibana_slo` resource ([#1305](https://github.com/elastic/terraform-provider-elasticstack/pull/1305))
19+
- Add support for `unenrollment_timeout` in `elasticstack_fleet_agent_policy` ([#1169](https://github.com/elastic/terraform-provider-elasticstack/issues/1169))
1620

1721
## [0.11.17] - 2025-07-21
1822

docs/resources/fleet_agent_policy.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ resource "elasticstack_fleet_agent_policy" "test_policy" {
5959
- `skip_destroy` (Boolean) Set to true if you do not wish the agent policy to be deleted at destroy time, and instead just remove the agent policy from the Terraform state.
6060
- `supports_agentless` (Boolean) Set to true to enable agentless data collection.
6161
- `sys_monitoring` (Boolean) Enable collection of system logs and metrics.
62+
- `unenrollment_timeout` (String) The unenrollment timeout for the agent policy. If an agent is inactive for this period, it will be automatically unenrolled. Supports duration strings (e.g., '30s', '2m', '1h').
6263

6364
### Read-Only
6465

docs/resources/kibana_slo.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -323,6 +323,7 @@ Required:
323323

324324
Optional:
325325

326+
- `data_view_id` (String) Optional data view id to use for this indicator.
326327
- `filter` (String)
327328
- `timestamp_field` (String)
328329

@@ -366,6 +367,7 @@ Required:
366367

367368
Optional:
368369

370+
- `data_view_id` (String) Optional data view id to use for this indicator.
369371
- `filter` (String)
370372
- `good` (String)
371373
- `timestamp_field` (String)
@@ -383,6 +385,7 @@ Required:
383385

384386
Optional:
385387

388+
- `data_view_id` (String) Optional data view id to use for this indicator.
386389
- `filter` (String)
387390
- `timestamp_field` (String)
388391

@@ -439,6 +442,7 @@ Optional:
439442
Optional:
440443

441444
- `frequency` (String)
445+
- `prevent_initial_backfill` (Boolean) Prevents the underlying ES transform from attempting to backfill data on start, which can sometimes be resource-intensive or time-consuming and unnecessary
442446
- `sync_delay` (String)
443447

444448

@@ -453,6 +457,7 @@ Required:
453457

454458
Optional:
455459

460+
- `data_view_id` (String) Optional data view id to use for this indicator.
456461
- `filter` (String)
457462

458463
<a id="nestedblock--timeslice_metric_indicator--metric"></a>

internal/clients/kibana/slo.go

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,9 @@ func GetSlo(ctx context.Context, apiClient *clients.ApiClient, id, spaceID strin
2828
return nil, nil
2929
}
3030
if err != nil {
31-
return nil, diag.FromErr(err)
31+
diags := diag.FromErr(err)
32+
diags = append(diags, utils.CheckHttpError(res, "unable to create slo with id "+id)...)
33+
return nil, diags
3234
}
3335

3436
defer res.Body.Close()
@@ -46,7 +48,9 @@ func DeleteSlo(ctx context.Context, apiClient *clients.ApiClient, sloId string,
4648
req := client.DeleteSloOp(ctxWithAuth, sloId, spaceId).KbnXsrf("true")
4749
res, err := req.Execute()
4850
if err != nil && res == nil {
49-
return diag.FromErr(err)
51+
diags := diag.FromErr(err)
52+
diags = append(diags, utils.CheckHttpError(res, "unable to create slo with id "+sloId)...)
53+
return diags
5054
}
5155

5256
defer res.Body.Close()
@@ -80,7 +84,9 @@ func UpdateSlo(ctx context.Context, apiClient *clients.ApiClient, s models.Slo,
8084
_, res, err := req.Execute()
8185

8286
if err != nil {
83-
return nil, diag.FromErr(err)
87+
diags := diag.FromErr(err)
88+
diags = append(diags, utils.CheckHttpError(res, "unable to create slo with id "+s.SloID)...)
89+
return nil, diags
8490
}
8591

8692
defer res.Body.Close()
@@ -122,7 +128,9 @@ func CreateSlo(ctx context.Context, apiClient *clients.ApiClient, s models.Slo,
122128
req := client.CreateSloOp(ctxWithAuth, s.SpaceID).KbnXsrf("true").CreateSloRequest(reqModel)
123129
sloRes, res, err := req.Execute()
124130
if err != nil {
125-
return nil, diag.FromErr(err)
131+
diags := diag.FromErr(err)
132+
diags = append(diags, utils.CheckHttpError(res, "unable to create slo with id "+s.SloID)...)
133+
return nil, diags
126134
}
127135
defer res.Body.Close()
128136

internal/fleet/agent_policy/models.go

Lines changed: 68 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,10 @@ import (
1717
)
1818

1919
type features struct {
20-
SupportsGlobalDataTags bool
21-
SupportsSupportsAgentless bool
22-
SupportsInactivityTimeout bool
20+
SupportsGlobalDataTags bool
21+
SupportsSupportsAgentless bool
22+
SupportsInactivityTimeout bool
23+
SupportsUnenrollmentTimeout bool
2324
}
2425

2526
type globalDataTagsItemModel struct {
@@ -28,22 +29,23 @@ type globalDataTagsItemModel struct {
2829
}
2930

3031
type agentPolicyModel struct {
31-
ID types.String `tfsdk:"id"`
32-
PolicyID types.String `tfsdk:"policy_id"`
33-
Name types.String `tfsdk:"name"`
34-
Namespace types.String `tfsdk:"namespace"`
35-
Description types.String `tfsdk:"description"`
36-
DataOutputId types.String `tfsdk:"data_output_id"`
37-
MonitoringOutputId types.String `tfsdk:"monitoring_output_id"`
38-
FleetServerHostId types.String `tfsdk:"fleet_server_host_id"`
39-
DownloadSourceId types.String `tfsdk:"download_source_id"`
40-
MonitorLogs types.Bool `tfsdk:"monitor_logs"`
41-
MonitorMetrics types.Bool `tfsdk:"monitor_metrics"`
42-
SysMonitoring types.Bool `tfsdk:"sys_monitoring"`
43-
SkipDestroy types.Bool `tfsdk:"skip_destroy"`
44-
SupportsAgentless types.Bool `tfsdk:"supports_agentless"`
45-
InactivityTimeout customtypes.Duration `tfsdk:"inactivity_timeout"`
46-
GlobalDataTags types.Map `tfsdk:"global_data_tags"` //> globalDataTagsModel
32+
ID types.String `tfsdk:"id"`
33+
PolicyID types.String `tfsdk:"policy_id"`
34+
Name types.String `tfsdk:"name"`
35+
Namespace types.String `tfsdk:"namespace"`
36+
Description types.String `tfsdk:"description"`
37+
DataOutputId types.String `tfsdk:"data_output_id"`
38+
MonitoringOutputId types.String `tfsdk:"monitoring_output_id"`
39+
FleetServerHostId types.String `tfsdk:"fleet_server_host_id"`
40+
DownloadSourceId types.String `tfsdk:"download_source_id"`
41+
MonitorLogs types.Bool `tfsdk:"monitor_logs"`
42+
MonitorMetrics types.Bool `tfsdk:"monitor_metrics"`
43+
SysMonitoring types.Bool `tfsdk:"sys_monitoring"`
44+
SkipDestroy types.Bool `tfsdk:"skip_destroy"`
45+
SupportsAgentless types.Bool `tfsdk:"supports_agentless"`
46+
InactivityTimeout customtypes.Duration `tfsdk:"inactivity_timeout"`
47+
UnenrollmentTimeout customtypes.Duration `tfsdk:"unenrollment_timeout"`
48+
GlobalDataTags types.Map `tfsdk:"global_data_tags"` //> globalDataTagsModel
4749
}
4850

4951
func (model *agentPolicyModel) populateFromAPI(ctx context.Context, data *kbapi.AgentPolicy) diag.Diagnostics {
@@ -79,11 +81,20 @@ func (model *agentPolicyModel) populateFromAPI(ctx context.Context, data *kbapi.
7981
model.SupportsAgentless = types.BoolPointerValue(data.SupportsAgentless)
8082
if data.InactivityTimeout != nil {
8183
// Convert seconds to duration string
82-
d := time.Duration(*data.InactivityTimeout * float32(time.Second)).Truncate(time.Second)
83-
model.InactivityTimeout = customtypes.NewDurationValue(d.String())
84+
seconds := int64(*data.InactivityTimeout)
85+
d := time.Duration(seconds) * time.Second
86+
model.InactivityTimeout = customtypes.NewDurationValue(d.Truncate(time.Second).String())
8487
} else {
8588
model.InactivityTimeout = customtypes.NewDurationNull()
8689
}
90+
if data.UnenrollTimeout != nil {
91+
// Convert seconds to duration string
92+
seconds := int64(*data.UnenrollTimeout)
93+
d := time.Duration(seconds) * time.Second
94+
model.UnenrollmentTimeout = customtypes.NewDurationValue(d.Truncate(time.Second).String())
95+
} else {
96+
model.UnenrollmentTimeout = customtypes.NewDurationNull()
97+
}
8798
if utils.Deref(data.GlobalDataTags) != nil {
8899
diags := diag.Diagnostics{}
89100
var map0 = make(map[string]globalDataTagsItemModel)
@@ -216,6 +227,24 @@ func (model *agentPolicyModel) toAPICreateModel(ctx context.Context, feat featur
216227
body.InactivityTimeout = &seconds
217228
}
218229

230+
if utils.IsKnown(model.UnenrollmentTimeout) {
231+
if !feat.SupportsUnenrollmentTimeout {
232+
return kbapi.PostFleetAgentPoliciesJSONRequestBody{}, diag.Diagnostics{
233+
diag.NewAttributeErrorDiagnostic(
234+
path.Root("unenrollment_timeout"),
235+
"Unsupported Elasticsearch version",
236+
fmt.Sprintf("Unenrollment timeout is only supported in Elastic Stack %s and above", MinVersionUnenrollmentTimeout),
237+
),
238+
}
239+
}
240+
duration, diags := model.UnenrollmentTimeout.Parse()
241+
if diags.HasError() {
242+
return kbapi.PostFleetAgentPoliciesJSONRequestBody{}, diags
243+
}
244+
seconds := float32(duration.Seconds())
245+
body.UnenrollTimeout = &seconds
246+
}
247+
219248
tags, diags := model.convertGlobalDataTags(ctx, feat)
220249
if diags.HasError() {
221250
return kbapi.PostFleetAgentPoliciesJSONRequestBody{}, diags
@@ -276,6 +305,24 @@ func (model *agentPolicyModel) toAPIUpdateModel(ctx context.Context, feat featur
276305
body.InactivityTimeout = &seconds
277306
}
278307

308+
if utils.IsKnown(model.UnenrollmentTimeout) {
309+
if !feat.SupportsUnenrollmentTimeout {
310+
return kbapi.PutFleetAgentPoliciesAgentpolicyidJSONRequestBody{}, diag.Diagnostics{
311+
diag.NewAttributeErrorDiagnostic(
312+
path.Root("unenrollment_timeout"),
313+
"Unsupported Elasticsearch version",
314+
fmt.Sprintf("Unenrollment timeout is only supported in Elastic Stack %s and above", MinVersionUnenrollmentTimeout),
315+
),
316+
}
317+
}
318+
duration, diags := model.UnenrollmentTimeout.Parse()
319+
if diags.HasError() {
320+
return kbapi.PutFleetAgentPoliciesAgentpolicyidJSONRequestBody{}, diags
321+
}
322+
seconds := float32(duration.Seconds())
323+
body.UnenrollTimeout = &seconds
324+
}
325+
279326
tags, diags := model.convertGlobalDataTags(ctx, feat)
280327
if diags.HasError() {
281328
return kbapi.PutFleetAgentPoliciesAgentpolicyidJSONRequestBody{}, diags

internal/fleet/agent_policy/resource.go

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,10 @@ var (
1919
)
2020

2121
var (
22-
MinVersionGlobalDataTags = version.Must(version.NewVersion("8.15.0"))
23-
MinSupportsAgentlessVersion = version.Must(version.NewVersion("8.15.0"))
24-
MinVersionInactivityTimeout = version.Must(version.NewVersion("8.7.0"))
22+
MinVersionGlobalDataTags = version.Must(version.NewVersion("8.15.0"))
23+
MinSupportsAgentlessVersion = version.Must(version.NewVersion("8.15.0"))
24+
MinVersionInactivityTimeout = version.Must(version.NewVersion("8.7.0"))
25+
MinVersionUnenrollmentTimeout = version.Must(version.NewVersion("8.15.0"))
2526
)
2627

2728
// NewResource is a helper function to simplify the provider implementation.
@@ -63,9 +64,15 @@ func (r *agentPolicyResource) buildFeatures(ctx context.Context) (features, diag
6364
return features{}, utils.FrameworkDiagsFromSDK(diags)
6465
}
6566

67+
supportsUnenrollmentTimeout, diags := r.client.EnforceMinVersion(ctx, MinVersionUnenrollmentTimeout)
68+
if diags.HasError() {
69+
return features{}, utils.FrameworkDiagsFromSDK(diags)
70+
}
71+
6672
return features{
67-
SupportsGlobalDataTags: supportsGDT,
68-
SupportsSupportsAgentless: supportsSupportsAgentless,
69-
SupportsInactivityTimeout: supportsInactivityTimeout,
73+
SupportsGlobalDataTags: supportsGDT,
74+
SupportsSupportsAgentless: supportsSupportsAgentless,
75+
SupportsInactivityTimeout: supportsInactivityTimeout,
76+
SupportsUnenrollmentTimeout: supportsUnenrollmentTimeout,
7077
}, nil
7178
}

internal/fleet/agent_policy/resource_test.go

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,33 @@ func TestAccResourceAgentPolicy(t *testing.T) {
156156
resource.TestCheckResourceAttr("elasticstack_fleet_agent_policy.test_policy", "inactivity_timeout", "2m"),
157157
),
158158
},
159+
{
160+
SkipFunc: versionutils.CheckIfVersionIsUnsupported(agent_policy.MinVersionUnenrollmentTimeout),
161+
Config: testAccResourceAgentPolicyCreateWithUnenrollmentTimeout(policyName),
162+
Check: resource.ComposeTestCheckFunc(
163+
resource.TestCheckResourceAttr("elasticstack_fleet_agent_policy.test_policy", "name", fmt.Sprintf("Policy %s", policyName)),
164+
resource.TestCheckResourceAttr("elasticstack_fleet_agent_policy.test_policy", "namespace", "default"),
165+
resource.TestCheckResourceAttr("elasticstack_fleet_agent_policy.test_policy", "description", "Test Agent Policy with Unenrollment Timeout"),
166+
resource.TestCheckResourceAttr("elasticstack_fleet_agent_policy.test_policy", "monitor_logs", "true"),
167+
resource.TestCheckResourceAttr("elasticstack_fleet_agent_policy.test_policy", "monitor_metrics", "false"),
168+
resource.TestCheckResourceAttr("elasticstack_fleet_agent_policy.test_policy", "skip_destroy", "false"),
169+
resource.TestCheckResourceAttr("elasticstack_fleet_agent_policy.test_policy", "unenrollment_timeout", "300s"),
170+
),
171+
},
172+
{
173+
SkipFunc: versionutils.CheckIfVersionIsUnsupported(agent_policy.MinVersionUnenrollmentTimeout),
174+
Config: testAccResourceAgentPolicyUpdateWithTimeouts(policyName),
175+
Check: resource.ComposeTestCheckFunc(
176+
resource.TestCheckResourceAttr("elasticstack_fleet_agent_policy.test_policy", "name", fmt.Sprintf("Updated Policy %s", policyName)),
177+
resource.TestCheckResourceAttr("elasticstack_fleet_agent_policy.test_policy", "namespace", "default"),
178+
resource.TestCheckResourceAttr("elasticstack_fleet_agent_policy.test_policy", "description", "Test Agent Policy with Both Timeouts"),
179+
resource.TestCheckResourceAttr("elasticstack_fleet_agent_policy.test_policy", "monitor_logs", "false"),
180+
resource.TestCheckResourceAttr("elasticstack_fleet_agent_policy.test_policy", "monitor_metrics", "true"),
181+
resource.TestCheckResourceAttr("elasticstack_fleet_agent_policy.test_policy", "skip_destroy", "false"),
182+
resource.TestCheckResourceAttr("elasticstack_fleet_agent_policy.test_policy", "inactivity_timeout", "120s"),
183+
resource.TestCheckResourceAttr("elasticstack_fleet_agent_policy.test_policy", "unenrollment_timeout", "900s"),
184+
),
185+
},
159186
{
160187
SkipFunc: versionutils.CheckIfVersionIsUnsupported(agent_policy.MinVersionGlobalDataTags),
161188
Config: testAccResourceAgentPolicyCreateWithGlobalDataTags(policyNameGlobalDataTags, false),
@@ -332,6 +359,30 @@ data "elasticstack_fleet_enrollment_tokens" "test_policy" {
332359
`, fmt.Sprintf("Policy %s", id))
333360
}
334361

362+
func testAccResourceAgentPolicyCreateWithUnenrollmentTimeout(id string) string {
363+
return fmt.Sprintf(`
364+
provider "elasticstack" {
365+
elasticsearch {}
366+
kibana {}
367+
}
368+
369+
resource "elasticstack_fleet_agent_policy" "test_policy" {
370+
name = "%s"
371+
namespace = "default"
372+
description = "Test Agent Policy with Unenrollment Timeout"
373+
monitor_logs = true
374+
monitor_metrics = false
375+
skip_destroy = false
376+
unenrollment_timeout = "300s"
377+
}
378+
379+
data "elasticstack_fleet_enrollment_tokens" "test_policy" {
380+
policy_id = elasticstack_fleet_agent_policy.test_policy.policy_id
381+
}
382+
383+
`, fmt.Sprintf("Policy %s", id))
384+
}
385+
335386
func testAccResourceAgentPolicyCreateWithBadGlobalDataTags(id string, skipDestroy bool) string {
336387
return fmt.Sprintf(`
337388
provider "elasticstack" {
@@ -509,3 +560,28 @@ func checkResourceAgentPolicySkipDestroy(s *terraform.State) error {
509560
}
510561
return nil
511562
}
563+
564+
func testAccResourceAgentPolicyUpdateWithTimeouts(id string) string {
565+
return fmt.Sprintf(`
566+
provider "elasticstack" {
567+
elasticsearch {}
568+
kibana {}
569+
}
570+
571+
resource "elasticstack_fleet_agent_policy" "test_policy" {
572+
name = "%s"
573+
namespace = "default"
574+
description = "Test Agent Policy with Both Timeouts"
575+
monitor_logs = false
576+
monitor_metrics = true
577+
skip_destroy = false
578+
inactivity_timeout = "120s"
579+
unenrollment_timeout = "900s"
580+
}
581+
582+
data "elasticstack_fleet_enrollment_tokens" "test_policy" {
583+
policy_id = elasticstack_fleet_agent_policy.test_policy.policy_id
584+
}
585+
586+
`, fmt.Sprintf("Updated Policy %s", id))
587+
}

internal/fleet/agent_policy/schema.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,12 @@ func getSchema() schema.Schema {
104104
Optional: true,
105105
CustomType: customtypes.DurationType{},
106106
},
107+
"unenrollment_timeout": schema.StringAttribute{
108+
Description: "The unenrollment timeout for the agent policy. If an agent is inactive for this period, it will be automatically unenrolled. Supports duration strings (e.g., '30s', '2m', '1h').",
109+
Computed: true,
110+
Optional: true,
111+
CustomType: customtypes.DurationType{},
112+
},
107113
"global_data_tags": schema.MapNestedAttribute{
108114
Description: "User-defined data tags to apply to all inputs. Values can be strings (string_value) or numbers (number_value) but not both. Example -- key1 = {string_value = value1}, key2 = {number_value = 42}",
109115
NestedObject: schema.NestedAttributeObject{

0 commit comments

Comments
 (0)