Skip to content

Commit df995c0

Browse files
orangecolatobio
andauthored
update integration_policy to include agent_policy_ids (#1311)
* update integration_policy to include agent_policy_ids * fix tests * edit upgrade * Add feature flag for policy_ids * applied suggestions * remove test that contains both policyid and policyids * update changelog.md * Fixup kb client build --------- Co-authored-by: Toby Brain <[email protected]>
1 parent 1ae6c3e commit df995c0

File tree

11 files changed

+10664
-718
lines changed

11 files changed

+10664
-718
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
- Add support for `unenrollment_timeout` in `elasticstack_fleet_agent_policy` ([#1169](https://github.com/elastic/terraform-provider-elasticstack/issues/1169))
2424
- Handle default value for `allow_restricted_indices` in `elasticstack_elasticsearch_security_api_key` ([#1315](https://github.com/elastic/terraform-provider-elasticstack/pull/1315))
2525
- Fixed `nil` reference in kibana synthetics API client in case of response errors ([#1320](https://github.com/elastic/terraform-provider-elasticstack/pull/1320))
26+
- Add support for `agent_policy_ids` in `elasticstack_fleet_integration_policy` ([#1131](https://github.com/elastic/terraform-provider-elasticstack/pull/1311))
2627

2728
## [0.11.17] - 2025-07-21
2829

docs/resources/fleet_integration_policy.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,14 +90,15 @@ resource "elasticstack_fleet_integration_policy" "sample" {
9090

9191
### Required
9292

93-
- `agent_policy_id` (String) ID of the agent policy.
9493
- `integration_name` (String) The name of the integration package.
9594
- `integration_version` (String) The version of the integration package.
9695
- `name` (String) The name of the integration policy.
9796
- `namespace` (String) The namespace of the integration policy.
9897

9998
### Optional
10099

100+
- `agent_policy_id` (String) ID of the agent policy.
101+
- `agent_policy_ids` (List of String) List of agent policy IDs.
101102
- `description` (String) The description of the integration policy.
102103
- `enabled` (Boolean) Enable the integration policy.
103104
- `force` (Boolean) Force operations, such as creation and deletion, to occur.

generated/kbapi/kibana.gen.go

Lines changed: 10376 additions & 669 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

generated/kbapi/transform_schema.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -566,6 +566,7 @@ var transformers = []TransformFunc{
566566
fixPutSecurityRoleName,
567567
fixGetSpacesParams,
568568
fixGetSyntheticsMonitorsParams,
569+
fixGetMaintenanceWindowFindParams,
569570
transformRemoveExamples,
570571
transformRemoveUnusedComponents,
571572
transformOmitEmptyNullable,
@@ -909,6 +910,10 @@ func fixGetSyntheticsMonitorsParams(schema *Schema) {
909910
schema.MustGetPath("/api/synthetics/monitors").MustGetEndpoint("get").Move("parameters.12.schema.oneOf.1", "parameters.12.schema")
910911
}
911912

913+
func fixGetMaintenanceWindowFindParams(schema *Schema) {
914+
schema.MustGetPath("/api/maintenance_window/_find").MustGetEndpoint("get").Move("parameters.2.schema.anyOf.1", "parameters.2.schema")
915+
}
916+
912917
// transformFleetPaths fixes the fleet paths.
913918
func transformFleetPaths(schema *Schema) {
914919
// Agent policies

internal/fleet/integration_policy/create.go

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,13 @@ func (r *integrationPolicyResource) Create(ctx context.Context, req resource.Cre
2222
return
2323
}
2424

25-
body, diags := planModel.toAPIModel(ctx, false)
25+
feat, diags := r.buildFeatures(ctx)
26+
resp.Diagnostics.Append(diags...)
27+
if resp.Diagnostics.HasError() {
28+
return
29+
}
30+
31+
body, diags := planModel.toAPIModel(ctx, false, feat)
2632
resp.Diagnostics.Append(diags...)
2733
if resp.Diagnostics.HasError() {
2834
return

internal/fleet/integration_policy/models.go

Lines changed: 69 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package integration_policy
22

33
import (
44
"context"
5+
"fmt"
56
"sort"
67

78
"github.com/elastic/terraform-provider-elasticstack/generated/kbapi"
@@ -12,12 +13,17 @@ import (
1213
"github.com/hashicorp/terraform-plugin-framework/types"
1314
)
1415

16+
type features struct {
17+
SupportsPolicyIds bool
18+
}
19+
1520
type integrationPolicyModel struct {
1621
ID types.String `tfsdk:"id"`
1722
PolicyID types.String `tfsdk:"policy_id"`
1823
Name types.String `tfsdk:"name"`
1924
Namespace types.String `tfsdk:"namespace"`
2025
AgentPolicyID types.String `tfsdk:"agent_policy_id"`
26+
AgentPolicyIDs types.List `tfsdk:"agent_policy_ids"`
2127
Description types.String `tfsdk:"description"`
2228
Enabled types.Bool `tfsdk:"enabled"`
2329
Force types.Bool `tfsdk:"force"`
@@ -45,7 +51,40 @@ func (model *integrationPolicyModel) populateFromAPI(ctx context.Context, data *
4551
model.PolicyID = types.StringValue(data.Id)
4652
model.Name = types.StringValue(data.Name)
4753
model.Namespace = types.StringPointerValue(data.Namespace)
48-
model.AgentPolicyID = types.StringPointerValue(data.PolicyId)
54+
55+
// Only populate the agent policy field that was originally configured
56+
// to avoid Terraform detecting inconsistent state
57+
58+
originallyUsedAgentPolicyID := utils.IsKnown(model.AgentPolicyID)
59+
originallyUsedAgentPolicyIDs := utils.IsKnown(model.AgentPolicyIDs)
60+
61+
if originallyUsedAgentPolicyID {
62+
model.AgentPolicyID = types.StringPointerValue(data.PolicyId)
63+
}
64+
if originallyUsedAgentPolicyIDs {
65+
if data.PolicyIds != nil {
66+
agentPolicyIDs, d := types.ListValueFrom(ctx, types.StringType, *data.PolicyIds)
67+
diags.Append(d...)
68+
model.AgentPolicyIDs = agentPolicyIDs
69+
} else {
70+
model.AgentPolicyIDs = types.ListNull(types.StringType)
71+
}
72+
}
73+
74+
if !originallyUsedAgentPolicyID && !originallyUsedAgentPolicyIDs {
75+
// Handle edge cases: both fields configured or neither configured
76+
// Default to the behavior based on API response structure
77+
if data.PolicyIds != nil && len(*data.PolicyIds) > 1 {
78+
// Multiple policy IDs, use agent_policy_ids
79+
agentPolicyIDs, d := types.ListValueFrom(ctx, types.StringType, *data.PolicyIds)
80+
diags.Append(d...)
81+
model.AgentPolicyIDs = agentPolicyIDs
82+
} else if data.PolicyId != nil {
83+
// Single policy ID, use agent_policy_id
84+
model.AgentPolicyID = types.StringPointerValue(data.PolicyId)
85+
}
86+
}
87+
4988
model.Description = types.StringPointerValue(data.Description)
5089
model.Enabled = types.BoolValue(data.Enabled)
5190
model.IntegrationName = types.StringValue(data.Package.Name)
@@ -81,9 +120,22 @@ func (model *integrationPolicyModel) populateInputFromAPI(ctx context.Context, i
81120
}
82121
}
83122

84-
func (model integrationPolicyModel) toAPIModel(ctx context.Context, isUpdate bool) (kbapi.PackagePolicyRequest, diag.Diagnostics) {
123+
func (model integrationPolicyModel) toAPIModel(ctx context.Context, isUpdate bool, feat features) (kbapi.PackagePolicyRequest, diag.Diagnostics) {
85124
var diags diag.Diagnostics
86125

126+
// Check if agent_policy_ids is configured and version supports it
127+
if utils.IsKnown(model.AgentPolicyIDs) {
128+
if !feat.SupportsPolicyIds {
129+
return kbapi.PackagePolicyRequest{}, diag.Diagnostics{
130+
diag.NewAttributeErrorDiagnostic(
131+
path.Root("agent_policy_ids"),
132+
"Unsupported Elasticsearch version",
133+
fmt.Sprintf("Agent policy IDs are only supported in Elastic Stack %s and above", MinVersionPolicyIds),
134+
),
135+
}
136+
}
137+
}
138+
87139
body := kbapi.PackagePolicyRequest{
88140
Description: model.Description.ValueStringPointer(),
89141
Force: model.Force.ValueBoolPointer(),
@@ -94,7 +146,21 @@ func (model integrationPolicyModel) toAPIModel(ctx context.Context, isUpdate boo
94146
Version: model.IntegrationVersion.ValueString(),
95147
},
96148
PolicyId: model.AgentPolicyID.ValueStringPointer(),
97-
Vars: utils.MapRef(utils.NormalizedTypeToMap[any](model.VarsJson, path.Root("vars_json"), &diags)),
149+
PolicyIds: func() *[]string {
150+
if !model.AgentPolicyIDs.IsNull() && !model.AgentPolicyIDs.IsUnknown() {
151+
var policyIDs []string
152+
d := model.AgentPolicyIDs.ElementsAs(ctx, &policyIDs, false)
153+
diags.Append(d...)
154+
return &policyIDs
155+
}
156+
// Only return empty array for 8.15+ when agent_policy_ids is not defined
157+
if feat.SupportsPolicyIds {
158+
emptyArray := []string{}
159+
return &emptyArray
160+
}
161+
return nil
162+
}(),
163+
Vars: utils.MapRef(utils.NormalizedTypeToMap[any](model.VarsJson, path.Root("vars_json"), &diags)),
98164
}
99165

100166
if isUpdate {

internal/fleet/integration_policy/resource.go

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,9 @@ import (
55
"fmt"
66

77
"github.com/elastic/terraform-provider-elasticstack/internal/clients"
8+
"github.com/elastic/terraform-provider-elasticstack/internal/diagutil"
9+
"github.com/hashicorp/go-version"
10+
"github.com/hashicorp/terraform-plugin-framework/diag"
811
"github.com/hashicorp/terraform-plugin-framework/path"
912
"github.com/hashicorp/terraform-plugin-framework/resource"
1013
)
@@ -16,6 +19,10 @@ var (
1619
_ resource.ResourceWithUpgradeState = &integrationPolicyResource{}
1720
)
1821

22+
var (
23+
MinVersionPolicyIds = version.Must(version.NewVersion("8.15.0"))
24+
)
25+
1926
// NewResource is a helper function to simplify the provider implementation.
2027
func NewResource() resource.Resource {
2128
return &integrationPolicyResource{}
@@ -44,3 +51,14 @@ func (r *integrationPolicyResource) UpgradeState(context.Context) map[int64]reso
4451
0: {PriorSchema: getSchemaV0(), StateUpgrader: upgradeV0},
4552
}
4653
}
54+
55+
func (r *integrationPolicyResource) buildFeatures(ctx context.Context) (features, diag.Diagnostics) {
56+
supportsPolicyIds, diags := r.client.EnforceMinVersion(ctx, MinVersionPolicyIds)
57+
if diags.HasError() {
58+
return features{}, diagutil.FrameworkDiagsFromSDK(diags)
59+
}
60+
61+
return features{
62+
SupportsPolicyIds: supportsPolicyIds,
63+
}, nil
64+
}

internal/fleet/integration_policy/resource_test.go

Lines changed: 84 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,9 @@ import (
2121
)
2222

2323
var (
24-
minVersionIntegrationPolicy = version.Must(version.NewVersion("8.10.0"))
25-
minVersionSqlIntegration = version.Must(version.NewVersion("9.1.0"))
24+
minVersionIntegrationPolicy = version.Must(version.NewVersion("8.10.0"))
25+
minVersionIntegrationPolicyIds = version.Must(version.NewVersion("8.15.0"))
26+
minVersionSqlIntegration = version.Must(version.NewVersion("9.1.0"))
2627
)
2728

2829
func TestJsonTypes(t *testing.T) {
@@ -33,6 +34,29 @@ func TestJsonTypes(t *testing.T) {
3334
require.False(t, equal)
3435
}
3536

37+
func TestAccResourceIntegrationPolicyMultipleAgentPolicies(t *testing.T) {
38+
policyName := sdkacctest.RandStringFromCharSet(22, sdkacctest.CharSetAlphaNum)
39+
40+
resource.Test(t, resource.TestCase{
41+
PreCheck: func() { acctest.PreCheck(t) },
42+
CheckDestroy: checkResourceIntegrationPolicyDestroy,
43+
ProtoV6ProviderFactories: acctest.Providers,
44+
Steps: []resource.TestStep{
45+
{
46+
SkipFunc: versionutils.CheckIfVersionIsUnsupported(minVersionIntegrationPolicyIds),
47+
Config: testAccResourceIntegrationPolicyCreateMultipleAgentPolicies(policyName),
48+
Check: resource.ComposeTestCheckFunc(
49+
resource.TestCheckResourceAttr("elasticstack_fleet_integration_policy.test_policy", "name", policyName),
50+
resource.TestCheckResourceAttr("elasticstack_fleet_integration_policy.test_policy", "description", "IntegrationPolicyTest Policy"),
51+
resource.TestCheckResourceAttr("elasticstack_fleet_integration_policy.test_policy", "integration_name", "tcp"),
52+
resource.TestCheckResourceAttr("elasticstack_fleet_integration_policy.test_policy", "integration_version", "1.16.0"),
53+
resource.TestCheckResourceAttr("elasticstack_fleet_integration_policy.test_policy", "agent_policy_ids.#", "2"),
54+
),
55+
},
56+
},
57+
})
58+
}
59+
3660
func TestAccResourceIntegrationPolicy(t *testing.T) {
3761
policyName := sdkacctest.RandStringFromCharSet(22, sdkacctest.CharSetAlphaNum)
3862

@@ -494,6 +518,64 @@ resource "elasticstack_fleet_integration_policy" "test_policy" {
494518
`, common, id, key, id)
495519
}
496520

521+
func testAccResourceIntegrationPolicyCreateMultipleAgentPolicies(id string) string {
522+
return fmt.Sprintf(`
523+
provider "elasticstack" {
524+
elasticsearch {}
525+
kibana {}
526+
}
527+
resource "elasticstack_fleet_integration" "test_policy" {
528+
name = "tcp"
529+
version = "1.16.0"
530+
force = true
531+
}
532+
resource "elasticstack_fleet_agent_policy" "test_policy_1" {
533+
name = "%s Agent Policy 1"
534+
namespace = "default"
535+
description = "IntegrationPolicyTest Agent Policy 1"
536+
monitor_logs = true
537+
monitor_metrics = true
538+
skip_destroy = false
539+
}
540+
resource "elasticstack_fleet_agent_policy" "test_policy_2" {
541+
name = "%s Agent Policy 2"
542+
namespace = "default"
543+
description = "IntegrationPolicyTest Agent Policy 2"
544+
monitor_logs = true
545+
monitor_metrics = true
546+
skip_destroy = false
547+
}
548+
resource "elasticstack_fleet_integration_policy" "test_policy" {
549+
name = "%s"
550+
namespace = "default"
551+
description = "IntegrationPolicyTest Policy"
552+
agent_policy_ids = [
553+
elasticstack_fleet_agent_policy.test_policy_1.policy_id,
554+
elasticstack_fleet_agent_policy.test_policy_2.policy_id
555+
]
556+
integration_name = elasticstack_fleet_integration.test_policy.name
557+
integration_version = elasticstack_fleet_integration.test_policy.version
558+
input {
559+
input_id = "tcp-tcp"
560+
streams_json = jsonencode({
561+
"tcp.generic": {
562+
"enabled": true
563+
"vars": {
564+
"listen_address": "localhost"
565+
"listen_port": 8080
566+
"data_stream.dataset": "tcp.generic"
567+
"tags": []
568+
"syslog_options": "field: message"
569+
"ssl": ""
570+
"custom": ""
571+
}
572+
}
573+
})
574+
}
575+
}
576+
`, id, id, id)
577+
}
578+
497579
func testAccResourceIntegrationPolicySecretsIds(id string, key string) string {
498580
common := testAccResourceIntegrationPolicyCommon(id, "sql", "1.1.0")
499581
return fmt.Sprintf(`

internal/fleet/integration_policy/schema.go

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,17 @@ import (
55
_ "embed"
66

77
"github.com/hashicorp/terraform-plugin-framework-jsontypes/jsontypes"
8+
"github.com/hashicorp/terraform-plugin-framework-validators/listvalidator"
9+
"github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator"
810
"github.com/hashicorp/terraform-plugin-framework/attr"
11+
"github.com/hashicorp/terraform-plugin-framework/path"
912
"github.com/hashicorp/terraform-plugin-framework/resource"
1013
"github.com/hashicorp/terraform-plugin-framework/resource/schema"
1114
"github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault"
1215
"github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier"
1316
"github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier"
17+
"github.com/hashicorp/terraform-plugin-framework/schema/validator"
18+
"github.com/hashicorp/terraform-plugin-framework/types"
1419
)
1520

1621
//go:embed resource-description.md
@@ -51,7 +56,19 @@ func getSchemaV1() schema.Schema {
5156
},
5257
"agent_policy_id": schema.StringAttribute{
5358
Description: "ID of the agent policy.",
54-
Required: true,
59+
Optional: true,
60+
Validators: []validator.String{
61+
stringvalidator.ConflictsWith(path.Root("agent_policy_ids").Expression()),
62+
},
63+
},
64+
"agent_policy_ids": schema.ListAttribute{
65+
Description: "List of agent policy IDs.",
66+
ElementType: types.StringType,
67+
Optional: true,
68+
Validators: []validator.List{
69+
listvalidator.ConflictsWith(path.Root("agent_policy_id").Expression()),
70+
listvalidator.SizeAtLeast(1),
71+
},
5572
},
5673
"description": schema.StringAttribute{
5774
Description: "The description of the integration policy.",

internal/fleet/integration_policy/update.go

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,13 @@ func (r *integrationPolicyResource) Update(ctx context.Context, req resource.Upd
2222
return
2323
}
2424

25-
body, diags := planModel.toAPIModel(ctx, true)
25+
feat, diags := r.buildFeatures(ctx)
26+
resp.Diagnostics.Append(diags...)
27+
if resp.Diagnostics.HasError() {
28+
return
29+
}
30+
31+
body, diags := planModel.toAPIModel(ctx, true, feat)
2632
resp.Diagnostics.Append(diags...)
2733
if resp.Diagnostics.HasError() {
2834
return

0 commit comments

Comments
 (0)