From 031d7bc8414b2dbb68df97780cd94f3c7e272b74 Mon Sep 17 00:00:00 2001 From: Joel Teo Date: Mon, 15 Sep 2025 09:35:15 +0100 Subject: [PATCH 1/8] update integration_policy to include agent_policy_ids --- docs/resources/fleet_integration_policy.md | 3 +- generated/kbapi/kibana.gen.go | 85 ++++++++- internal/fleet/integration_policy/models.go | 47 ++++- .../fleet/integration_policy/resource_test.go | 177 ++++++++++++++++++ internal/fleet/integration_policy/schema.go | 11 +- 5 files changed, 313 insertions(+), 10 deletions(-) diff --git a/docs/resources/fleet_integration_policy.md b/docs/resources/fleet_integration_policy.md index 351cd8e5e..b5805d330 100644 --- a/docs/resources/fleet_integration_policy.md +++ b/docs/resources/fleet_integration_policy.md @@ -82,7 +82,6 @@ resource "elasticstack_fleet_integration_policy" "sample" { ### Required -- `agent_policy_id` (String) ID of the agent policy. - `integration_name` (String) The name of the integration package. - `integration_version` (String) The version of the integration package. - `name` (String) The name of the integration policy. @@ -90,6 +89,8 @@ resource "elasticstack_fleet_integration_policy" "sample" { ### Optional +- `agent_policy_id` (String, Deprecated) ID of the agent policy. +- `agent_policy_ids` (List of String) List of agent policy IDs. - `description` (String) The description of the integration policy. - `enabled` (Boolean) Enable the integration policy. - `force` (Boolean) Force operations, such as creation and deletion, to occur. diff --git a/generated/kbapi/kibana.gen.go b/generated/kbapi/kibana.gen.go index 97e146c28..642e6fdad 100644 --- a/generated/kbapi/kibana.gen.go +++ b/generated/kbapi/kibana.gen.go @@ -16565,6 +16565,15 @@ type SecurityEndpointManagementAPIKillProcessRouteResponse = map[string]interfac // SecurityEndpointManagementAPIKuery A KQL string. type SecurityEndpointManagementAPIKuery = string +// SecurityEndpointManagementAPIMDERunScriptParameters Parameters for Run Script response action against Microsoft Defender Endpoint agent type. +type SecurityEndpointManagementAPIMDERunScriptParameters struct { + // Args Optional command line arguments for the script. + Args *string `json:"args,omitempty"` + + // ScriptName The name of the script to execute from the cloud storage. + ScriptName string `json:"scriptName"` +} + // SecurityEndpointManagementAPIMetadataListResponse defines model for Security_Endpoint_Management_API_MetadataListResponse. type SecurityEndpointManagementAPIMetadataListResponse = map[string]interface{} @@ -17212,12 +17221,40 @@ type SecurityEntityAnalyticsAPIMonitoredUserDoc struct { } `json:"user,omitempty"` } +// SecurityEntityAnalyticsAPIMonitoredUserUpdateDoc defines model for Security_Entity_Analytics_API_MonitoredUserUpdateDoc. +type SecurityEntityAnalyticsAPIMonitoredUserUpdateDoc struct { + EntityAnalyticsMonitoring *struct { + Labels *[]struct { + Field *string `json:"field,omitempty"` + Source *string `json:"source,omitempty"` + Value *string `json:"value,omitempty"` + } `json:"labels,omitempty"` + } `json:"entity_analytics_monitoring,omitempty"` + Id *string `json:"id,omitempty"` + Labels *struct { + SourceIds *[]string `json:"source_ids,omitempty"` + SourceIntegrations *[]string `json:"source_integrations,omitempty"` + Sources *[]interface{} `json:"sources,omitempty"` + } `json:"labels,omitempty"` + User *struct { + // IsPrivileged Indicates if the user is privileged. + IsPrivileged *bool `json:"is_privileged,omitempty"` + Name *string `json:"name,omitempty"` + } `json:"user,omitempty"` +} + // SecurityEntityAnalyticsAPIMonitoringEngineDescriptor defines model for Security_Entity_Analytics_API_MonitoringEngineDescriptor. type SecurityEntityAnalyticsAPIMonitoringEngineDescriptor struct { + Error *struct { + // Message Error message typically only present if the engine is in error state + Message *string `json:"message,omitempty"` + } `json:"error,omitempty"` + + // Status The status of the Privilege Monitoring Engine Status SecurityEntityAnalyticsAPIPrivilegeMonitoringEngineStatus `json:"status"` } -// SecurityEntityAnalyticsAPIPrivilegeMonitoringEngineStatus defines model for Security_Entity_Analytics_API_PrivilegeMonitoringEngineStatus. +// SecurityEntityAnalyticsAPIPrivilegeMonitoringEngineStatus The status of the Privilege Monitoring Engine type SecurityEntityAnalyticsAPIPrivilegeMonitoringEngineStatus string // SecurityEntityAnalyticsAPIPrivmonUserCsvUploadErrorItem defines model for Security_Entity_Analytics_API_PrivmonUserCsvUploadErrorItem. @@ -38775,7 +38812,7 @@ type CreatePrivMonUserJSONRequestBody = SecurityEntityAnalyticsAPIUserName type PrivmonBulkUploadUsersCSVMultipartRequestBody PrivmonBulkUploadUsersCSVMultipartBody // UpdatePrivMonUserJSONRequestBody defines body for UpdatePrivMonUser for application/json ContentType. -type UpdatePrivMonUserJSONRequestBody = SecurityEntityAnalyticsAPIMonitoredUserDoc +type UpdatePrivMonUserJSONRequestBody = SecurityEntityAnalyticsAPIMonitoredUserUpdateDoc // InitEntityStoreJSONRequestBody defines body for InitEntityStore for application/json ContentType. type InitEntityStoreJSONRequestBody InitEntityStoreJSONBody @@ -51569,7 +51606,7 @@ func (t SLOsIndicatorPropertiesTimesliceMetric_Params_Metric_Metrics_Item) AsSLO // FromSLOsTimesliceMetricBasicMetricWithField overwrites any union data inside the SLOsIndicatorPropertiesTimesliceMetric_Params_Metric_Metrics_Item as the provided SLOsTimesliceMetricBasicMetricWithField func (t *SLOsIndicatorPropertiesTimesliceMetric_Params_Metric_Metrics_Item) FromSLOsTimesliceMetricBasicMetricWithField(v SLOsTimesliceMetricBasicMetricWithField) error { - v.Aggregation = "max" + v.Aggregation = "last_value" b, err := json.Marshal(v) t.union = b return err @@ -51577,7 +51614,7 @@ func (t *SLOsIndicatorPropertiesTimesliceMetric_Params_Metric_Metrics_Item) From // MergeSLOsTimesliceMetricBasicMetricWithField performs a merge with any union data inside the SLOsIndicatorPropertiesTimesliceMetric_Params_Metric_Metrics_Item, using the provided SLOsTimesliceMetricBasicMetricWithField func (t *SLOsIndicatorPropertiesTimesliceMetric_Params_Metric_Metrics_Item) MergeSLOsTimesliceMetricBasicMetricWithField(v SLOsTimesliceMetricBasicMetricWithField) error { - v.Aggregation = "max" + v.Aggregation = "last_value" b, err := json.Marshal(v) if err != nil { return err @@ -51660,7 +51697,7 @@ func (t SLOsIndicatorPropertiesTimesliceMetric_Params_Metric_Metrics_Item) Value switch discriminator { case "doc_count": return t.AsSLOsTimesliceMetricDocCountMetric() - case "max": + case "last_value": return t.AsSLOsTimesliceMetricBasicMetricWithField() case "percentile": return t.AsSLOsTimesliceMetricPercentileMetric() @@ -55329,6 +55366,32 @@ func (t *SecurityEndpointManagementAPIRunScriptRouteRequestBody_Parameters) Merg return err } +// AsSecurityEndpointManagementAPIMDERunScriptParameters returns the union data inside the SecurityEndpointManagementAPIRunScriptRouteRequestBody_Parameters as a SecurityEndpointManagementAPIMDERunScriptParameters +func (t SecurityEndpointManagementAPIRunScriptRouteRequestBody_Parameters) AsSecurityEndpointManagementAPIMDERunScriptParameters() (SecurityEndpointManagementAPIMDERunScriptParameters, error) { + var body SecurityEndpointManagementAPIMDERunScriptParameters + err := json.Unmarshal(t.union, &body) + return body, err +} + +// FromSecurityEndpointManagementAPIMDERunScriptParameters overwrites any union data inside the SecurityEndpointManagementAPIRunScriptRouteRequestBody_Parameters as the provided SecurityEndpointManagementAPIMDERunScriptParameters +func (t *SecurityEndpointManagementAPIRunScriptRouteRequestBody_Parameters) FromSecurityEndpointManagementAPIMDERunScriptParameters(v SecurityEndpointManagementAPIMDERunScriptParameters) error { + b, err := json.Marshal(v) + t.union = b + return err +} + +// MergeSecurityEndpointManagementAPIMDERunScriptParameters performs a merge with any union data inside the SecurityEndpointManagementAPIRunScriptRouteRequestBody_Parameters, using the provided SecurityEndpointManagementAPIMDERunScriptParameters +func (t *SecurityEndpointManagementAPIRunScriptRouteRequestBody_Parameters) MergeSecurityEndpointManagementAPIMDERunScriptParameters(v SecurityEndpointManagementAPIMDERunScriptParameters) error { + b, err := json.Marshal(v) + if err != nil { + return err + } + + merged, err := runtime.JSONMerge(t.union, b) + t.union = merged + return err +} + func (t SecurityEndpointManagementAPIRunScriptRouteRequestBody_Parameters) MarshalJSON() ([]byte, error) { b, err := t.union.MarshalJSON() return b, err @@ -105828,6 +105891,7 @@ type InitMonitoringEngineResponse struct { Body []byte HTTPResponse *http.Response JSON200 *SecurityEntityAnalyticsAPIMonitoringEngineDescriptor + JSON500 *SecurityEntityAnalyticsAPIMonitoringEngineDescriptor } // Status returns HTTPResponse.Status @@ -105882,6 +105946,8 @@ type PrivMonHealthResponse struct { Error *struct { Message *string `json:"message,omitempty"` } `json:"error,omitempty"` + + // Status The status of the Privilege Monitoring Engine Status SecurityEntityAnalyticsAPIPrivilegeMonitoringEngineStatus `json:"status"` } } @@ -130106,6 +130172,13 @@ func ParseInitMonitoringEngineResponse(rsp *http.Response) (*InitMonitoringEngin } response.JSON200 = &dest + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 500: + var dest SecurityEntityAnalyticsAPIMonitoringEngineDescriptor + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON500 = &dest + } return response, nil @@ -130169,6 +130242,8 @@ func ParsePrivMonHealthResponse(rsp *http.Response) (*PrivMonHealthResponse, err Error *struct { Message *string `json:"message,omitempty"` } `json:"error,omitempty"` + + // Status The status of the Privilege Monitoring Engine Status SecurityEntityAnalyticsAPIPrivilegeMonitoringEngineStatus `json:"status"` } if err := json.Unmarshal(bodyBytes, &dest); err != nil { diff --git a/internal/fleet/integration_policy/models.go b/internal/fleet/integration_policy/models.go index 0ffe6014e..20c009fda 100644 --- a/internal/fleet/integration_policy/models.go +++ b/internal/fleet/integration_policy/models.go @@ -18,6 +18,7 @@ type integrationPolicyModel struct { Name types.String `tfsdk:"name"` Namespace types.String `tfsdk:"namespace"` AgentPolicyID types.String `tfsdk:"agent_policy_id"` + AgentPolicyIDs types.List `tfsdk:"agent_policy_ids"` Description types.String `tfsdk:"description"` Enabled types.Bool `tfsdk:"enabled"` Force types.Bool `tfsdk:"force"` @@ -45,7 +46,38 @@ func (model *integrationPolicyModel) populateFromAPI(ctx context.Context, data * model.PolicyID = types.StringValue(data.Id) model.Name = types.StringValue(data.Name) model.Namespace = types.StringPointerValue(data.Namespace) - model.AgentPolicyID = types.StringPointerValue(data.PolicyId) + + // Only populate the agent policy field that was originally configured + // to avoid Terraform detecting inconsistent state + originallyUsedAgentPolicyID := !model.AgentPolicyID.IsNull() && !model.AgentPolicyID.IsUnknown() + originallyUsedAgentPolicyIDs := !model.AgentPolicyIDs.IsNull() && !model.AgentPolicyIDs.IsUnknown() + + if originallyUsedAgentPolicyID && !originallyUsedAgentPolicyIDs { + // Only set agent_policy_id if it was originally used + model.AgentPolicyID = types.StringPointerValue(data.PolicyId) + } else if originallyUsedAgentPolicyIDs && !originallyUsedAgentPolicyID { + // Only set agent_policy_ids if it was originally used + if data.PolicyIds != nil { + agentPolicyIDs, d := types.ListValueFrom(ctx, types.StringType, *data.PolicyIds) + diags.Append(d...) + model.AgentPolicyIDs = agentPolicyIDs + } else { + model.AgentPolicyIDs = types.ListNull(types.StringType) + } + } else { + // Handle edge cases: both fields configured or neither configured + // Default to the behavior based on API response structure + if data.PolicyIds != nil && len(*data.PolicyIds) > 1 { + // Multiple policy IDs, use agent_policy_ids + agentPolicyIDs, d := types.ListValueFrom(ctx, types.StringType, *data.PolicyIds) + diags.Append(d...) + model.AgentPolicyIDs = agentPolicyIDs + } else { + // Single policy ID, use agent_policy_id + model.AgentPolicyID = types.StringPointerValue(data.PolicyId) + } + } + model.Description = types.StringPointerValue(data.Description) model.Enabled = types.BoolValue(data.Enabled) model.IntegrationName = types.StringValue(data.Package.Name) @@ -94,7 +126,18 @@ func (model integrationPolicyModel) toAPIModel(ctx context.Context, isUpdate boo Version: model.IntegrationVersion.ValueString(), }, PolicyId: model.AgentPolicyID.ValueStringPointer(), - Vars: utils.MapRef(utils.NormalizedTypeToMap[any](model.VarsJson, path.Root("vars_json"), &diags)), + PolicyIds: func() *[]string { + if !model.AgentPolicyIDs.IsNull() && !model.AgentPolicyIDs.IsUnknown() { + var policyIDs []string + d := model.AgentPolicyIDs.ElementsAs(ctx, &policyIDs, false) + diags.Append(d...) + return &policyIDs + } + // Return empty array instead of nil when agent_policy_ids is not defined + emptyArray := []string{} + return &emptyArray + }(), + Vars: utils.MapRef(utils.NormalizedTypeToMap[any](model.VarsJson, path.Root("vars_json"), &diags)), } if isUpdate { diff --git a/internal/fleet/integration_policy/resource_test.go b/internal/fleet/integration_policy/resource_test.go index 70c9375d3..92db1d6db 100644 --- a/internal/fleet/integration_policy/resource_test.go +++ b/internal/fleet/integration_policy/resource_test.go @@ -22,6 +22,69 @@ import ( var minVersionIntegrationPolicy = version.Must(version.NewVersion("8.10.0")) +func TestAccResourceIntegrationPolicyMultipleAgentPolicies(t *testing.T) { + policyName := sdkacctest.RandStringFromCharSet(22, sdkacctest.CharSetAlphaNum) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(t) }, + CheckDestroy: checkResourceIntegrationPolicyDestroy, + ProtoV6ProviderFactories: acctest.Providers, + Steps: []resource.TestStep{ + { + SkipFunc: versionutils.CheckIfVersionIsUnsupported(minVersionIntegrationPolicy), + Config: testAccResourceIntegrationPolicyCreateMultipleAgentPolicies(policyName), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr("elasticstack_fleet_integration_policy.test_policy", "name", policyName), + resource.TestCheckResourceAttr("elasticstack_fleet_integration_policy.test_policy", "description", "IntegrationPolicyTest Policy"), + resource.TestCheckResourceAttr("elasticstack_fleet_integration_policy.test_policy", "integration_name", "tcp"), + resource.TestCheckResourceAttr("elasticstack_fleet_integration_policy.test_policy", "integration_version", "1.16.0"), + resource.TestCheckResourceAttr("elasticstack_fleet_integration_policy.test_policy", "agent_policy_ids.#", "2"), + ), + }, + }, + }) +} + +func TestAccResourceIntegrationPolicyValidation(t *testing.T) { + policyName := sdkacctest.RandStringFromCharSet(22, sdkacctest.CharSetAlphaNum) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(t) }, + ProtoV6ProviderFactories: acctest.Providers, + Steps: []resource.TestStep{ + { + SkipFunc: versionutils.CheckIfVersionIsUnsupported(minVersionIntegrationPolicy), + Config: testAccResourceIntegrationPolicyCreateWithNoAgentPolicyFields(policyName), + ExpectError: regexp.MustCompile("Either 'agent_policy_id' or 'agent_policy_ids' must be provided"), + }, + }, + }) +} + +func TestAccResourceIntegrationPolicyBothAgentPolicyFields(t *testing.T) { + policyName := sdkacctest.RandStringFromCharSet(22, sdkacctest.CharSetAlphaNum) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(t) }, + CheckDestroy: checkResourceIntegrationPolicyDestroy, + ProtoV6ProviderFactories: acctest.Providers, + Steps: []resource.TestStep{ + { + SkipFunc: versionutils.CheckIfVersionIsUnsupported(minVersionIntegrationPolicy), + Config: testAccResourceIntegrationPolicyCreateWithBothAgentPolicyFields(policyName), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr("elasticstack_fleet_integration_policy.test_policy", "name", policyName), + resource.TestCheckResourceAttr("elasticstack_fleet_integration_policy.test_policy", "description", "IntegrationPolicyTest Policy"), + resource.TestCheckResourceAttr("elasticstack_fleet_integration_policy.test_policy", "integration_name", "tcp"), + resource.TestCheckResourceAttr("elasticstack_fleet_integration_policy.test_policy", "integration_version", "1.16.0"), + resource.TestCheckResourceAttrSet("elasticstack_fleet_integration_policy.test_policy", "agent_policy_id"), + resource.TestCheckResourceAttr("elasticstack_fleet_integration_policy.test_policy", "agent_policy_ids.#", "1"), + ), + }, + }, + }) +} + func TestJsonTypes(t *testing.T) { mapBytes, err := json.Marshal(map[string]string{}) require.NoError(t, err) @@ -451,3 +514,117 @@ resource "elasticstack_fleet_integration_policy" "test_policy" { } `, common, id, key, id) } + +func testAccResourceIntegrationPolicyCreateMultipleAgentPolicies(id string) string { + return fmt.Sprintf(` +provider "elasticstack" { + elasticsearch {} + kibana {} +} +resource "elasticstack_fleet_integration" "test_policy" { + name = "tcp" + version = "1.16.0" + force = true +} +resource "elasticstack_fleet_agent_policy" "test_policy_1" { + name = "%s Agent Policy 1" + namespace = "default" + description = "IntegrationPolicyTest Agent Policy 1" + monitor_logs = true + monitor_metrics = true + skip_destroy = false +} +resource "elasticstack_fleet_agent_policy" "test_policy_2" { + name = "%s Agent Policy 2" + namespace = "default" + description = "IntegrationPolicyTest Agent Policy 2" + monitor_logs = true + monitor_metrics = true + skip_destroy = false +} +resource "elasticstack_fleet_integration_policy" "test_policy" { + name = "%s" + namespace = "default" + description = "IntegrationPolicyTest Policy" + agent_policy_ids = [ + elasticstack_fleet_agent_policy.test_policy_1.policy_id, + elasticstack_fleet_agent_policy.test_policy_2.policy_id + ] + integration_name = elasticstack_fleet_integration.test_policy.name + integration_version = elasticstack_fleet_integration.test_policy.version + input { + input_id = "tcp-tcp" + enabled = true + streams_json = jsonencode({ + "tcp.generic": { + "enabled": true + "vars": { + "listen_address": "localhost" + "listen_port": 8080 + } + } + }) + } +} +`, id, id, id) +} + +func testAccResourceIntegrationPolicyCreateWithBothAgentPolicyFields(id string) string { + return fmt.Sprintf(` +provider "elasticstack" { + elasticsearch {} + kibana {} +} +resource "elasticstack_fleet_integration" "test_policy" { + name = "tcp" + version = "1.16.0" + force = true +} +resource "elasticstack_fleet_agent_policy" "test_policy" { + name = "%s Agent Policy" + namespace = "default" + description = "IntegrationPolicyTest Agent Policy" + monitor_logs = true + monitor_metrics = true + skip_destroy = false +} +resource "elasticstack_fleet_integration_policy" "test_policy" { + name = "%s" + namespace = "default" + description = "IntegrationPolicyTest Policy" + agent_policy_id = elasticstack_fleet_agent_policy.test_policy.policy_id + agent_policy_ids = [elasticstack_fleet_agent_policy.test_policy.policy_id] + integration_name = elasticstack_fleet_integration.test_policy.name + integration_version = elasticstack_fleet_integration.test_policy.version + input { + input_id = "tcp-tcp" + enabled = true + } +} +`, id, id) +} + +func testAccResourceIntegrationPolicyCreateWithNoAgentPolicyFields(id string) string { + return fmt.Sprintf(` +provider "elasticstack" { + elasticsearch {} + kibana {} +} +resource "elasticstack_fleet_integration" "test_policy" { + name = "tcp" + version = "1.16.0" + force = true +} +resource "elasticstack_fleet_integration_policy" "test_policy" { + name = "%s" + namespace = "default" + description = "IntegrationPolicyTest Policy" + integration_name = elasticstack_fleet_integration.test_policy.name + integration_version = elasticstack_fleet_integration.test_policy.version + input { + input_id = "tcp-tcp" + enabled = true + } +} +`, id) +} diff --git a/internal/fleet/integration_policy/schema.go b/internal/fleet/integration_policy/schema.go index 055a70614..9261765b5 100644 --- a/internal/fleet/integration_policy/schema.go +++ b/internal/fleet/integration_policy/schema.go @@ -10,6 +10,7 @@ import ( "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" + "github.com/hashicorp/terraform-plugin-framework/types" ) func (r *integrationPolicyResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) { @@ -46,8 +47,14 @@ func getSchemaV1() schema.Schema { Required: true, }, "agent_policy_id": schema.StringAttribute{ - Description: "ID of the agent policy.", - Required: true, + Description: "ID of the agent policy.", + DeprecationMessage: "Use agent_policy_ids instead. This field will be removed in a future version.", + Optional: true, + }, + "agent_policy_ids": schema.ListAttribute{ + Description: "List of agent policy IDs.", + ElementType: types.StringType, + Optional: true, }, "description": schema.StringAttribute{ Description: "The description of the integration policy.", From f343977b298597670f0e73ab22e7d451c13747d9 Mon Sep 17 00:00:00 2001 From: Joel Teo Date: Thu, 18 Sep 2025 08:55:15 +0100 Subject: [PATCH 2/8] fix tests --- .../fleet/integration_policy/resource_test.go | 47 ++----------------- 1 file changed, 5 insertions(+), 42 deletions(-) diff --git a/internal/fleet/integration_policy/resource_test.go b/internal/fleet/integration_policy/resource_test.go index 92db1d6db..9cbd92f13 100644 --- a/internal/fleet/integration_policy/resource_test.go +++ b/internal/fleet/integration_policy/resource_test.go @@ -45,22 +45,6 @@ func TestAccResourceIntegrationPolicyMultipleAgentPolicies(t *testing.T) { }) } -func TestAccResourceIntegrationPolicyValidation(t *testing.T) { - policyName := sdkacctest.RandStringFromCharSet(22, sdkacctest.CharSetAlphaNum) - - resource.Test(t, resource.TestCase{ - PreCheck: func() { acctest.PreCheck(t) }, - ProtoV6ProviderFactories: acctest.Providers, - Steps: []resource.TestStep{ - { - SkipFunc: versionutils.CheckIfVersionIsUnsupported(minVersionIntegrationPolicy), - Config: testAccResourceIntegrationPolicyCreateWithNoAgentPolicyFields(policyName), - ExpectError: regexp.MustCompile("Either 'agent_policy_id' or 'agent_policy_ids' must be provided"), - }, - }, - }) -} - func TestAccResourceIntegrationPolicyBothAgentPolicyFields(t *testing.T) { policyName := sdkacctest.RandStringFromCharSet(22, sdkacctest.CharSetAlphaNum) @@ -554,13 +538,17 @@ resource "elasticstack_fleet_integration_policy" "test_policy" { integration_version = elasticstack_fleet_integration.test_policy.version input { input_id = "tcp-tcp" - enabled = true streams_json = jsonencode({ "tcp.generic": { "enabled": true "vars": { "listen_address": "localhost" "listen_port": 8080 + "data_stream.dataset": "tcp.generic" + "tags": [] + "syslog_options": "field: message" + "ssl": "" + "custom": "" } } }) @@ -603,28 +591,3 @@ resource "elasticstack_fleet_integration_policy" "test_policy" { } `, id, id) } - -func testAccResourceIntegrationPolicyCreateWithNoAgentPolicyFields(id string) string { - return fmt.Sprintf(` -provider "elasticstack" { - elasticsearch {} - kibana {} -} -resource "elasticstack_fleet_integration" "test_policy" { - name = "tcp" - version = "1.16.0" - force = true -} -resource "elasticstack_fleet_integration_policy" "test_policy" { - name = "%s" - namespace = "default" - description = "IntegrationPolicyTest Policy" - integration_name = elasticstack_fleet_integration.test_policy.name - integration_version = elasticstack_fleet_integration.test_policy.version - input { - input_id = "tcp-tcp" - enabled = true - } -} -`, id) -} From 6397ee1679b648856377373ba3cbf4123fc84a72 Mon Sep 17 00:00:00 2001 From: Joel Teo Date: Fri, 19 Sep 2025 04:50:24 +0100 Subject: [PATCH 3/8] edit upgrade --- internal/fleet/integration_policy/upgrade.go | 117 ++++++++++++------- 1 file changed, 77 insertions(+), 40 deletions(-) diff --git a/internal/fleet/integration_policy/upgrade.go b/internal/fleet/integration_policy/upgrade.go index 7992ce150..1cf0330ac 100644 --- a/internal/fleet/integration_policy/upgrade.go +++ b/internal/fleet/integration_policy/upgrade.go @@ -4,7 +4,7 @@ import ( "context" "github.com/elastic/terraform-provider-elasticstack/internal/utils" - "github.com/hashicorp/terraform-plugin-framework/attr" + "github.com/hashicorp/terraform-plugin-framework-jsontypes/jsontypes" "github.com/hashicorp/terraform-plugin-framework/path" "github.com/hashicorp/terraform-plugin-framework/resource" "github.com/hashicorp/terraform-plugin-framework/resource/schema" @@ -14,6 +14,29 @@ import ( "github.com/hashicorp/terraform-plugin-framework/types" ) +// V0 model structures - used regular string types for JSON fields +type integrationPolicyModelV0 struct { + ID types.String `tfsdk:"id"` + PolicyID types.String `tfsdk:"policy_id"` + Name types.String `tfsdk:"name"` + Namespace types.String `tfsdk:"namespace"` + AgentPolicyID types.String `tfsdk:"agent_policy_id"` + Description types.String `tfsdk:"description"` + Enabled types.Bool `tfsdk:"enabled"` + Force types.Bool `tfsdk:"force"` + IntegrationName types.String `tfsdk:"integration_name"` + IntegrationVersion types.String `tfsdk:"integration_version"` + Input types.List `tfsdk:"input"` //> integrationPolicyInputModelV0 + VarsJson types.String `tfsdk:"vars_json"` +} + +type integrationPolicyInputModelV0 struct { + InputID types.String `tfsdk:"input_id"` + Enabled types.Bool `tfsdk:"enabled"` + StreamsJson types.String `tfsdk:"streams_json"` + VarsJson types.String `tfsdk:"vars_json"` +} + func getSchemaV0() *schema.Schema { return &schema.Schema{ Version: 0, @@ -45,70 +68,84 @@ func getSchemaV0() *schema.Schema { } } -func getInputTypeV0() attr.Type { - return getSchemaV0().Blocks["input"].Type().(attr.TypeWithElementType).ElementType() -} - -type integrationPolicyModelV0 struct { - ID types.String `tfsdk:"id"` - PolicyID types.String `tfsdk:"policy_id"` - Name types.String `tfsdk:"name"` - Namespace types.String `tfsdk:"namespace"` - AgentPolicyID types.String `tfsdk:"agent_policy_id"` - Description types.String `tfsdk:"description"` - Enabled types.Bool `tfsdk:"enabled"` - Force types.Bool `tfsdk:"force"` - IntegrationName types.String `tfsdk:"integration_name"` - IntegrationVersion types.String `tfsdk:"integration_version"` - Input types.List `tfsdk:"input"` //> integrationPolicyInputModelV0 - VarsJson types.String `tfsdk:"vars_json"` -} - -type integrationPolicyInputModelV0 struct { - InputID types.String `tfsdk:"input_id"` - Enabled types.Bool `tfsdk:"enabled"` - StreamsJson types.String `tfsdk:"streams_json"` - VarsJson types.String `tfsdk:"vars_json"` -} - // The schema between V0 and V1 is mostly the same, however vars_json and // streams_json saved "" values to the state when null values were in the // config. jsontypes.Normalized correctly states this is invalid JSON. func upgradeV0(ctx context.Context, req resource.UpgradeStateRequest, resp *resource.UpgradeStateResponse) { - var stateModel integrationPolicyModelV0 + var stateModelV0 integrationPolicyModelV0 - diags := req.State.Get(ctx, &stateModel) + diags := req.State.Get(ctx, &stateModelV0) resp.Diagnostics.Append(diags...) if resp.Diagnostics.HasError() { return } - if varsJSON := stateModel.VarsJson.ValueStringPointer(); varsJSON != nil { + // Convert V0 model to V1 model + stateModelV1 := integrationPolicyModel{ + ID: stateModelV0.ID, + PolicyID: stateModelV0.PolicyID, + Name: stateModelV0.Name, + Namespace: stateModelV0.Namespace, + AgentPolicyID: stateModelV0.AgentPolicyID, + AgentPolicyIDs: types.ListNull(types.StringType), // V0 didn't have agent_policy_ids + Description: stateModelV0.Description, + Enabled: stateModelV0.Enabled, + Force: stateModelV0.Force, + IntegrationName: stateModelV0.IntegrationName, + IntegrationVersion: stateModelV0.IntegrationVersion, + } + + // Convert vars_json from string to normalized JSON type + if varsJSON := stateModelV0.VarsJson.ValueStringPointer(); varsJSON != nil { if *varsJSON == "" { - stateModel.VarsJson = types.StringNull() + stateModelV1.VarsJson = jsontypes.NewNormalizedNull() + } else { + stateModelV1.VarsJson = jsontypes.NewNormalizedValue(*varsJSON) } + } else { + stateModelV1.VarsJson = jsontypes.NewNormalizedNull() } - inputs := utils.ListTypeAs[integrationPolicyInputModelV0](ctx, stateModel.Input, path.Root("input"), &resp.Diagnostics) - for index, input := range inputs { - if varsJSON := input.VarsJson.ValueStringPointer(); varsJSON != nil { + // Convert inputs from V0 to V1 + inputsV0 := utils.ListTypeAs[integrationPolicyInputModelV0](ctx, stateModelV0.Input, path.Root("input"), &resp.Diagnostics) + var inputsV1 []integrationPolicyInputModel + + for _, inputV0 := range inputsV0 { + inputV1 := integrationPolicyInputModel{ + InputID: inputV0.InputID, + Enabled: inputV0.Enabled, + } + + // Convert vars_json + if varsJSON := inputV0.VarsJson.ValueStringPointer(); varsJSON != nil { if *varsJSON == "" { - input.VarsJson = types.StringNull() + inputV1.VarsJson = jsontypes.NewNormalizedNull() + } else { + inputV1.VarsJson = jsontypes.NewNormalizedValue(*varsJSON) } + } else { + inputV1.VarsJson = jsontypes.NewNormalizedNull() } - if streamsJSON := input.StreamsJson.ValueStringPointer(); streamsJSON != nil { + + // Convert streams_json + if streamsJSON := inputV0.StreamsJson.ValueStringPointer(); streamsJSON != nil { if *streamsJSON == "" { - input.StreamsJson = types.StringNull() + inputV1.StreamsJson = jsontypes.NewNormalizedNull() + } else { + inputV1.StreamsJson = jsontypes.NewNormalizedValue(*streamsJSON) } + } else { + inputV1.StreamsJson = jsontypes.NewNormalizedNull() } - inputs[index] = input + + inputsV1 = append(inputsV1, inputV1) } - stateModel.Input = utils.ListValueFrom(ctx, inputs, getInputTypeV0(), path.Root("input"), &resp.Diagnostics) + stateModelV1.Input = utils.ListValueFrom(ctx, inputsV1, getInputTypeV1(), path.Root("input"), &resp.Diagnostics) if resp.Diagnostics.HasError() { return } - diags = resp.State.Set(ctx, stateModel) + diags = resp.State.Set(ctx, stateModelV1) resp.Diagnostics.Append(diags...) } From c1f255ce92209e60162c68aa68439b57f11546ec Mon Sep 17 00:00:00 2001 From: Joel Teo Date: Mon, 22 Sep 2025 06:50:04 +0100 Subject: [PATCH 4/8] Add feature flag for policy_ids --- internal/fleet/integration_policy/create.go | 8 +++++++- internal/fleet/integration_policy/models.go | 15 +++++++++++---- internal/fleet/integration_policy/resource.go | 18 ++++++++++++++++++ .../fleet/integration_policy/resource_test.go | 5 +++-- internal/fleet/integration_policy/update.go | 8 +++++++- 5 files changed, 46 insertions(+), 8 deletions(-) diff --git a/internal/fleet/integration_policy/create.go b/internal/fleet/integration_policy/create.go index 2a47da9b5..d9024792d 100644 --- a/internal/fleet/integration_policy/create.go +++ b/internal/fleet/integration_policy/create.go @@ -22,7 +22,13 @@ func (r *integrationPolicyResource) Create(ctx context.Context, req resource.Cre return } - body, diags := planModel.toAPIModel(ctx, false) + feat, diags := r.buildFeatures(ctx) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + body, diags := planModel.toAPIModel(ctx, false, feat) resp.Diagnostics.Append(diags...) if resp.Diagnostics.HasError() { return diff --git a/internal/fleet/integration_policy/models.go b/internal/fleet/integration_policy/models.go index 20c009fda..bc2f44103 100644 --- a/internal/fleet/integration_policy/models.go +++ b/internal/fleet/integration_policy/models.go @@ -12,6 +12,10 @@ import ( "github.com/hashicorp/terraform-plugin-framework/types" ) +type features struct { + SupportsPolicyIds bool +} + type integrationPolicyModel struct { ID types.String `tfsdk:"id"` PolicyID types.String `tfsdk:"policy_id"` @@ -113,7 +117,7 @@ func (model *integrationPolicyModel) populateInputFromAPI(ctx context.Context, i } } -func (model integrationPolicyModel) toAPIModel(ctx context.Context, isUpdate bool) (kbapi.PackagePolicyRequest, diag.Diagnostics) { +func (model integrationPolicyModel) toAPIModel(ctx context.Context, isUpdate bool, feat features) (kbapi.PackagePolicyRequest, diag.Diagnostics) { var diags diag.Diagnostics body := kbapi.PackagePolicyRequest{ @@ -133,9 +137,12 @@ func (model integrationPolicyModel) toAPIModel(ctx context.Context, isUpdate boo diags.Append(d...) return &policyIDs } - // Return empty array instead of nil when agent_policy_ids is not defined - emptyArray := []string{} - return &emptyArray + // Only return empty array for 8.15+ when agent_policy_ids is not defined + if feat.SupportsPolicyIds { + emptyArray := []string{} + return &emptyArray + } + return nil }(), Vars: utils.MapRef(utils.NormalizedTypeToMap[any](model.VarsJson, path.Root("vars_json"), &diags)), } diff --git a/internal/fleet/integration_policy/resource.go b/internal/fleet/integration_policy/resource.go index b10e68a80..0343b4dca 100644 --- a/internal/fleet/integration_policy/resource.go +++ b/internal/fleet/integration_policy/resource.go @@ -5,6 +5,9 @@ import ( "fmt" "github.com/elastic/terraform-provider-elasticstack/internal/clients" + "github.com/elastic/terraform-provider-elasticstack/internal/diagutil" + "github.com/hashicorp/go-version" + "github.com/hashicorp/terraform-plugin-framework/diag" "github.com/hashicorp/terraform-plugin-framework/path" "github.com/hashicorp/terraform-plugin-framework/resource" ) @@ -16,6 +19,10 @@ var ( _ resource.ResourceWithUpgradeState = &integrationPolicyResource{} ) +var ( + MinVersionPolicyIds = version.Must(version.NewVersion("8.15.0")) +) + // NewResource is a helper function to simplify the provider implementation. func NewResource() resource.Resource { return &integrationPolicyResource{} @@ -44,3 +51,14 @@ func (r *integrationPolicyResource) UpgradeState(context.Context) map[int64]reso 0: {PriorSchema: getSchemaV0(), StateUpgrader: upgradeV0}, } } + +func (r *integrationPolicyResource) buildFeatures(ctx context.Context) (features, diag.Diagnostics) { + supportsPolicyIds, diags := r.client.EnforceMinVersion(ctx, MinVersionPolicyIds) + if diags.HasError() { + return features{}, diagutil.FrameworkDiagsFromSDK(diags) + } + + return features{ + SupportsPolicyIds: supportsPolicyIds, + }, nil +} diff --git a/internal/fleet/integration_policy/resource_test.go b/internal/fleet/integration_policy/resource_test.go index e1ccec0dd..e91115cf8 100644 --- a/internal/fleet/integration_policy/resource_test.go +++ b/internal/fleet/integration_policy/resource_test.go @@ -21,6 +21,7 @@ import ( ) var minVersionIntegrationPolicy = version.Must(version.NewVersion("8.10.0")) +var minVersionIntegrationPolicyIds = version.Must(version.NewVersion("8.15.0")) func TestAccResourceIntegrationPolicyMultipleAgentPolicies(t *testing.T) { policyName := sdkacctest.RandStringFromCharSet(22, sdkacctest.CharSetAlphaNum) @@ -31,7 +32,7 @@ func TestAccResourceIntegrationPolicyMultipleAgentPolicies(t *testing.T) { ProtoV6ProviderFactories: acctest.Providers, Steps: []resource.TestStep{ { - SkipFunc: versionutils.CheckIfVersionIsUnsupported(minVersionIntegrationPolicy), + SkipFunc: versionutils.CheckIfVersionIsUnsupported(minVersionIntegrationPolicyIds), Config: testAccResourceIntegrationPolicyCreateMultipleAgentPolicies(policyName), Check: resource.ComposeTestCheckFunc( resource.TestCheckResourceAttr("elasticstack_fleet_integration_policy.test_policy", "name", policyName), @@ -54,7 +55,7 @@ func TestAccResourceIntegrationPolicyBothAgentPolicyFields(t *testing.T) { ProtoV6ProviderFactories: acctest.Providers, Steps: []resource.TestStep{ { - SkipFunc: versionutils.CheckIfVersionIsUnsupported(minVersionIntegrationPolicy), + SkipFunc: versionutils.CheckIfVersionIsUnsupported(minVersionIntegrationPolicyIds), Config: testAccResourceIntegrationPolicyCreateWithBothAgentPolicyFields(policyName), Check: resource.ComposeTestCheckFunc( resource.TestCheckResourceAttr("elasticstack_fleet_integration_policy.test_policy", "name", policyName), diff --git a/internal/fleet/integration_policy/update.go b/internal/fleet/integration_policy/update.go index d094c2c49..82f70dc7a 100644 --- a/internal/fleet/integration_policy/update.go +++ b/internal/fleet/integration_policy/update.go @@ -22,7 +22,13 @@ func (r *integrationPolicyResource) Update(ctx context.Context, req resource.Upd return } - body, diags := planModel.toAPIModel(ctx, true) + feat, diags := r.buildFeatures(ctx) + resp.Diagnostics.Append(diags...) + if resp.Diagnostics.HasError() { + return + } + + body, diags := planModel.toAPIModel(ctx, true, feat) resp.Diagnostics.Append(diags...) if resp.Diagnostics.HasError() { return From a98c34e4b803ccc087fb63fad7d2672b560e698a Mon Sep 17 00:00:00 2001 From: Joel Teo Date: Tue, 23 Sep 2025 06:54:24 +0100 Subject: [PATCH 5/8] applied suggestions --- docs/resources/fleet_integration_policy.md | 2 +- internal/fleet/integration_policy/models.go | 32 +++++++++++++++------ internal/fleet/integration_policy/schema.go | 16 +++++++++-- 3 files changed, 38 insertions(+), 12 deletions(-) diff --git a/docs/resources/fleet_integration_policy.md b/docs/resources/fleet_integration_policy.md index a8c752f58..573459340 100644 --- a/docs/resources/fleet_integration_policy.md +++ b/docs/resources/fleet_integration_policy.md @@ -97,7 +97,7 @@ resource "elasticstack_fleet_integration_policy" "sample" { ### Optional -- `agent_policy_id` (String, Deprecated) ID of the agent policy. +- `agent_policy_id` (String) ID of the agent policy. - `agent_policy_ids` (List of String) List of agent policy IDs. - `description` (String) The description of the integration policy. - `enabled` (Boolean) Enable the integration policy. diff --git a/internal/fleet/integration_policy/models.go b/internal/fleet/integration_policy/models.go index bc2f44103..a76265d03 100644 --- a/internal/fleet/integration_policy/models.go +++ b/internal/fleet/integration_policy/models.go @@ -2,6 +2,7 @@ package integration_policy import ( "context" + "fmt" "sort" "github.com/elastic/terraform-provider-elasticstack/generated/kbapi" @@ -53,14 +54,14 @@ func (model *integrationPolicyModel) populateFromAPI(ctx context.Context, data * // Only populate the agent policy field that was originally configured // to avoid Terraform detecting inconsistent state - originallyUsedAgentPolicyID := !model.AgentPolicyID.IsNull() && !model.AgentPolicyID.IsUnknown() - originallyUsedAgentPolicyIDs := !model.AgentPolicyIDs.IsNull() && !model.AgentPolicyIDs.IsUnknown() - if originallyUsedAgentPolicyID && !originallyUsedAgentPolicyIDs { - // Only set agent_policy_id if it was originally used + originallyUsedAgentPolicyID := utils.IsKnown(model.AgentPolicyID) + originallyUsedAgentPolicyIDs := utils.IsKnown(model.AgentPolicyIDs) + + if originallyUsedAgentPolicyID { model.AgentPolicyID = types.StringPointerValue(data.PolicyId) - } else if originallyUsedAgentPolicyIDs && !originallyUsedAgentPolicyID { - // Only set agent_policy_ids if it was originally used + } + if originallyUsedAgentPolicyIDs { if data.PolicyIds != nil { agentPolicyIDs, d := types.ListValueFrom(ctx, types.StringType, *data.PolicyIds) diags.Append(d...) @@ -68,7 +69,9 @@ func (model *integrationPolicyModel) populateFromAPI(ctx context.Context, data * } else { model.AgentPolicyIDs = types.ListNull(types.StringType) } - } else { + } + + if !originallyUsedAgentPolicyID && !originallyUsedAgentPolicyIDs { // Handle edge cases: both fields configured or neither configured // Default to the behavior based on API response structure if data.PolicyIds != nil && len(*data.PolicyIds) > 1 { @@ -76,7 +79,7 @@ func (model *integrationPolicyModel) populateFromAPI(ctx context.Context, data * agentPolicyIDs, d := types.ListValueFrom(ctx, types.StringType, *data.PolicyIds) diags.Append(d...) model.AgentPolicyIDs = agentPolicyIDs - } else { + } else if data.PolicyId != nil { // Single policy ID, use agent_policy_id model.AgentPolicyID = types.StringPointerValue(data.PolicyId) } @@ -120,6 +123,19 @@ func (model *integrationPolicyModel) populateInputFromAPI(ctx context.Context, i func (model integrationPolicyModel) toAPIModel(ctx context.Context, isUpdate bool, feat features) (kbapi.PackagePolicyRequest, diag.Diagnostics) { var diags diag.Diagnostics + // Check if agent_policy_ids is configured and version supports it + if utils.IsKnown(model.AgentPolicyIDs) { + if !feat.SupportsPolicyIds { + return kbapi.PackagePolicyRequest{}, diag.Diagnostics{ + diag.NewAttributeErrorDiagnostic( + path.Root("agent_policy_ids"), + "Unsupported Elasticsearch version", + fmt.Sprintf("Agent policy IDs are only supported in Elastic Stack %s and above", MinVersionPolicyIds), + ), + } + } + } + body := kbapi.PackagePolicyRequest{ Description: model.Description.ValueStringPointer(), Force: model.Force.ValueBoolPointer(), diff --git a/internal/fleet/integration_policy/schema.go b/internal/fleet/integration_policy/schema.go index ae48d302c..c76477d25 100644 --- a/internal/fleet/integration_policy/schema.go +++ b/internal/fleet/integration_policy/schema.go @@ -5,12 +5,16 @@ import ( _ "embed" "github.com/hashicorp/terraform-plugin-framework-jsontypes/jsontypes" + "github.com/hashicorp/terraform-plugin-framework-validators/listvalidator" + "github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator" "github.com/hashicorp/terraform-plugin-framework/attr" + "github.com/hashicorp/terraform-plugin-framework/path" "github.com/hashicorp/terraform-plugin-framework/resource" "github.com/hashicorp/terraform-plugin-framework/resource/schema" "github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault" "github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier" "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" + "github.com/hashicorp/terraform-plugin-framework/schema/validator" "github.com/hashicorp/terraform-plugin-framework/types" ) @@ -51,14 +55,20 @@ func getSchemaV1() schema.Schema { Required: true, }, "agent_policy_id": schema.StringAttribute{ - Description: "ID of the agent policy.", - DeprecationMessage: "Use agent_policy_ids instead. This field will be removed in a future version.", - Optional: true, + Description: "ID of the agent policy.", + Optional: true, + Validators: []validator.String{ + stringvalidator.ConflictsWith(path.Root("agent_policy_ids").Expression()), + }, }, "agent_policy_ids": schema.ListAttribute{ Description: "List of agent policy IDs.", ElementType: types.StringType, Optional: true, + Validators: []validator.List{ + listvalidator.ConflictsWith(path.Root("agent_policy_ids").Expression()), + listvalidator.SizeAtLeast(1), + }, }, "description": schema.StringAttribute{ Description: "The description of the integration policy.", From e28708e7aaa203d6cfbdcf335688c1802fc9738e Mon Sep 17 00:00:00 2001 From: Joel Teo Date: Tue, 23 Sep 2025 08:38:03 +0100 Subject: [PATCH 6/8] remove test that contains both policyid and policyids --- .../fleet/integration_policy/resource_test.go | 59 ------------------- 1 file changed, 59 deletions(-) diff --git a/internal/fleet/integration_policy/resource_test.go b/internal/fleet/integration_policy/resource_test.go index e91115cf8..1a8bb2a07 100644 --- a/internal/fleet/integration_policy/resource_test.go +++ b/internal/fleet/integration_policy/resource_test.go @@ -46,30 +46,6 @@ func TestAccResourceIntegrationPolicyMultipleAgentPolicies(t *testing.T) { }) } -func TestAccResourceIntegrationPolicyBothAgentPolicyFields(t *testing.T) { - policyName := sdkacctest.RandStringFromCharSet(22, sdkacctest.CharSetAlphaNum) - - resource.Test(t, resource.TestCase{ - PreCheck: func() { acctest.PreCheck(t) }, - CheckDestroy: checkResourceIntegrationPolicyDestroy, - ProtoV6ProviderFactories: acctest.Providers, - Steps: []resource.TestStep{ - { - SkipFunc: versionutils.CheckIfVersionIsUnsupported(minVersionIntegrationPolicyIds), - Config: testAccResourceIntegrationPolicyCreateWithBothAgentPolicyFields(policyName), - Check: resource.ComposeTestCheckFunc( - resource.TestCheckResourceAttr("elasticstack_fleet_integration_policy.test_policy", "name", policyName), - resource.TestCheckResourceAttr("elasticstack_fleet_integration_policy.test_policy", "description", "IntegrationPolicyTest Policy"), - resource.TestCheckResourceAttr("elasticstack_fleet_integration_policy.test_policy", "integration_name", "tcp"), - resource.TestCheckResourceAttr("elasticstack_fleet_integration_policy.test_policy", "integration_version", "1.16.0"), - resource.TestCheckResourceAttrSet("elasticstack_fleet_integration_policy.test_policy", "agent_policy_id"), - resource.TestCheckResourceAttr("elasticstack_fleet_integration_policy.test_policy", "agent_policy_ids.#", "1"), - ), - }, - }, - }) -} - func TestJsonTypes(t *testing.T) { mapBytes, err := json.Marshal(map[string]string{}) require.NoError(t, err) @@ -557,38 +533,3 @@ resource "elasticstack_fleet_integration_policy" "test_policy" { } `, id, id, id) } - -func testAccResourceIntegrationPolicyCreateWithBothAgentPolicyFields(id string) string { - return fmt.Sprintf(` -provider "elasticstack" { - elasticsearch {} - kibana {} -} -resource "elasticstack_fleet_integration" "test_policy" { - name = "tcp" - version = "1.16.0" - force = true -} -resource "elasticstack_fleet_agent_policy" "test_policy" { - name = "%s Agent Policy" - namespace = "default" - description = "IntegrationPolicyTest Agent Policy" - monitor_logs = true - monitor_metrics = true - skip_destroy = false -} -resource "elasticstack_fleet_integration_policy" "test_policy" { - name = "%s" - namespace = "default" - description = "IntegrationPolicyTest Policy" - agent_policy_id = elasticstack_fleet_agent_policy.test_policy.policy_id - agent_policy_ids = [elasticstack_fleet_agent_policy.test_policy.policy_id] - integration_name = elasticstack_fleet_integration.test_policy.name - integration_version = elasticstack_fleet_integration.test_policy.version - input { - input_id = "tcp-tcp" - enabled = true - } -} -`, id, id) -} From a80c0f70310f1428223d1404c116ef5b02e7e2f6 Mon Sep 17 00:00:00 2001 From: Joel Teo Date: Thu, 25 Sep 2025 07:35:58 +0100 Subject: [PATCH 7/8] update changelog.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7ec23c0bc..3091150aa 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,6 +19,7 @@ - Add support for `data_view_id` in the `elasticstack_kibana_slo` resource ([#1305](https://github.com/elastic/terraform-provider-elasticstack/pull/1305)) - Add support for `unenrollment_timeout` in `elasticstack_fleet_agent_policy` ([#1169](https://github.com/elastic/terraform-provider-elasticstack/issues/1169)) - Handle default value for `allow_restricted_indices` in `elasticstack_elasticsearch_security_api_key` ([#1315](https://github.com/elastic/terraform-provider-elasticstack/pull/1315)) +- Add support for `agent_policy_ids` in `elasticstack_fleet_integration_policy` ([#1131](https://github.com/elastic/terraform-provider-elasticstack/pull/1311)) ## [0.11.17] - 2025-07-21 From a85cbfde022ea6df98e4f78ca47f35c02feaf846 Mon Sep 17 00:00:00 2001 From: Toby Brain Date: Tue, 7 Oct 2025 13:41:05 +1100 Subject: [PATCH 8/8] Fixup kb client build --- generated/kbapi/kibana.gen.go | 32 +++++++++++------------------ generated/kbapi/transform_schema.go | 5 +++++ 2 files changed, 17 insertions(+), 20 deletions(-) diff --git a/generated/kbapi/kibana.gen.go b/generated/kbapi/kibana.gen.go index 7017d5180..5031ad980 100644 --- a/generated/kbapi/kibana.gen.go +++ b/generated/kbapi/kibana.gen.go @@ -3451,12 +3451,12 @@ const ( ImportListItemsParamsRefreshWaitFor ImportListItemsParamsRefresh = "wait_for" ) -// Defines values for GetMaintenanceWindowFindParamsStatus0. +// Defines values for GetMaintenanceWindowFindParamsStatus. const ( - Archived GetMaintenanceWindowFindParamsStatus0 = "archived" - Finished GetMaintenanceWindowFindParamsStatus0 = "finished" - Running GetMaintenanceWindowFindParamsStatus0 = "running" - Upcoming GetMaintenanceWindowFindParamsStatus0 = "upcoming" + Archived GetMaintenanceWindowFindParamsStatus = "archived" + Finished GetMaintenanceWindowFindParamsStatus = "finished" + Running GetMaintenanceWindowFindParamsStatus = "running" + Upcoming GetMaintenanceWindowFindParamsStatus = "upcoming" ) // Defines values for PostSecurityRoleQueryJSONBodySortDirection. @@ -29604,9 +29604,7 @@ type GetMaintenanceWindowFindParams struct { CreatedBy *string `form:"created_by,omitempty" json:"created_by,omitempty"` // Status The status of the maintenance window. One of "running", "upcoming", "finished" or "archived". - Status *struct { - union json.RawMessage - } `form:"status,omitempty" json:"status,omitempty"` + Status *[]GetMaintenanceWindowFindParamsStatus `form:"status,omitempty" json:"status,omitempty"` // Page The page number to return. Page *float32 `form:"page,omitempty" json:"page,omitempty"` @@ -29615,14 +29613,8 @@ type GetMaintenanceWindowFindParams struct { PerPage *float32 `form:"per_page,omitempty" json:"per_page,omitempty"` } -// GetMaintenanceWindowFindParamsStatus0 defines parameters for GetMaintenanceWindowFind. -type GetMaintenanceWindowFindParamsStatus0 string - -// GetMaintenanceWindowFindParamsStatus1 defines parameters for GetMaintenanceWindowFind. -type GetMaintenanceWindowFindParamsStatus1 = []GetMaintenanceWindowFindParamsStatus1 - -// GetMaintenanceWindowFindParamsStatus1 defines parameters for GetMaintenanceWindowFind. -type GetMaintenanceWindowFindParamsStatus1 string +// GetMaintenanceWindowFindParamsStatus defines parameters for GetMaintenanceWindowFind. +type GetMaintenanceWindowFindParamsStatus string // MlSyncParams defines parameters for MlSync. type MlSyncParams struct { @@ -54717,7 +54709,7 @@ func (t SLOsIndicatorPropertiesTimesliceMetric_Params_Metric_Metrics_Item) AsSLO // FromSLOsTimesliceMetricBasicMetricWithField overwrites any union data inside the SLOsIndicatorPropertiesTimesliceMetric_Params_Metric_Metrics_Item as the provided SLOsTimesliceMetricBasicMetricWithField func (t *SLOsIndicatorPropertiesTimesliceMetric_Params_Metric_Metrics_Item) FromSLOsTimesliceMetricBasicMetricWithField(v SLOsTimesliceMetricBasicMetricWithField) error { - v.Aggregation = "avg" + v.Aggregation = "std_deviation" b, err := json.Marshal(v) t.union = b return err @@ -54725,7 +54717,7 @@ func (t *SLOsIndicatorPropertiesTimesliceMetric_Params_Metric_Metrics_Item) From // MergeSLOsTimesliceMetricBasicMetricWithField performs a merge with any union data inside the SLOsIndicatorPropertiesTimesliceMetric_Params_Metric_Metrics_Item, using the provided SLOsTimesliceMetricBasicMetricWithField func (t *SLOsIndicatorPropertiesTimesliceMetric_Params_Metric_Metrics_Item) MergeSLOsTimesliceMetricBasicMetricWithField(v SLOsTimesliceMetricBasicMetricWithField) error { - v.Aggregation = "avg" + v.Aggregation = "std_deviation" b, err := json.Marshal(v) if err != nil { return err @@ -54806,12 +54798,12 @@ func (t SLOsIndicatorPropertiesTimesliceMetric_Params_Metric_Metrics_Item) Value return nil, err } switch discriminator { - case "avg": - return t.AsSLOsTimesliceMetricBasicMetricWithField() case "doc_count": return t.AsSLOsTimesliceMetricDocCountMetric() case "percentile": return t.AsSLOsTimesliceMetricPercentileMetric() + case "std_deviation": + return t.AsSLOsTimesliceMetricBasicMetricWithField() default: return nil, errors.New("unknown discriminator value: " + discriminator) } diff --git a/generated/kbapi/transform_schema.go b/generated/kbapi/transform_schema.go index 82a841583..962151838 100644 --- a/generated/kbapi/transform_schema.go +++ b/generated/kbapi/transform_schema.go @@ -566,6 +566,7 @@ var transformers = []TransformFunc{ fixPutSecurityRoleName, fixGetSpacesParams, fixGetSyntheticsMonitorsParams, + fixGetMaintenanceWindowFindParams, transformRemoveExamples, transformRemoveUnusedComponents, transformOmitEmptyNullable, @@ -909,6 +910,10 @@ func fixGetSyntheticsMonitorsParams(schema *Schema) { schema.MustGetPath("/api/synthetics/monitors").MustGetEndpoint("get").Move("parameters.12.schema.oneOf.1", "parameters.12.schema") } +func fixGetMaintenanceWindowFindParams(schema *Schema) { + schema.MustGetPath("/api/maintenance_window/_find").MustGetEndpoint("get").Move("parameters.2.schema.anyOf.1", "parameters.2.schema") +} + // transformFleetPaths fixes the fleet paths. func transformFleetPaths(schema *Schema) { // Agent policies