Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
## [Unreleased]

- Add `ignore_missing_component_templates` to `elasticstack_elasticsearch_index_template` ([#1206](https://github.com/elastic/terraform-provider-elasticstack/pull/1206))
- Migrate `elasticstack_elasticsearch_enrich_policy` resource and data source to Terraform Plugin Framework ([#1220](https://github.com/elastic/terraform-provider-elasticstack/pull/1220))
- Prevent provider panic when a script exists in state, but not in Elasticsearch ([#1218](https://github.com/elastic/terraform-provider-elasticstack/pull/1218))

## [0.11.17] - 2025-07-21
Expand Down
26 changes: 25 additions & 1 deletion docs/data-sources/elasticsearch_enrich_policy.md
Original file line number Diff line number Diff line change
Expand Up @@ -74,11 +74,35 @@ output "query" {

- `name` (String) The name of the policy.

### Optional

- `elasticsearch_connection` (Block List, Deprecated) Elasticsearch connection configuration block. (see [below for nested schema](#nestedblock--elasticsearch_connection))

### Read-Only

- `enrich_fields` (Set of String) Fields to add to matching incoming documents. These fields must be present in the source indices.
- `id` (String) Internal identifier of the resource
- `indices` (Set of String) Array of one or more source indices used to create the enrich index.
- `match_field` (String) Field in source indices used to match incoming documents.
- `match_field` (String) Field from the source indices used to match incoming documents.
- `policy_type` (String) The type of enrich policy, can be one of geo_match, match, range.
- `query` (String) Query used to filter documents in the enrich index. The policy only uses documents matching this query to enrich incoming documents. Defaults to a match_all query.

<a id="nestedblock--elasticsearch_connection"></a>
### Nested Schema for `elasticsearch_connection`

Optional:

- `api_key` (String, Sensitive) API Key to use for authentication to Elasticsearch
- `bearer_token` (String, Sensitive) Bearer Token to use for authentication to Elasticsearch
- `ca_data` (String) PEM-encoded custom Certificate Authority certificate
- `ca_file` (String) Path to a custom Certificate Authority certificate
- `cert_data` (String) PEM encoded certificate for client auth
- `cert_file` (String) Path to a file containing the PEM encoded certificate for client auth
- `endpoints` (List of String, Sensitive) A list of endpoints where the terraform provider will point to, this must include the http(s) schema and port number.
- `es_client_authentication` (String, Sensitive) ES Client Authentication field to be used with the JWT token
- `headers` (Map of String, Sensitive) A list of headers to be sent with each request to Elasticsearch.
- `insecure` (Boolean) Disable TLS certificate validation
- `key_data` (String, Sensitive) PEM encoded private key for client auth
- `key_file` (String) Path to a file containing the PEM encoded private key for client auth
- `password` (String, Sensitive) Password to use for API authentication to Elasticsearch.
- `username` (String) Username to use for API authentication to Elasticsearch.
4 changes: 2 additions & 2 deletions docs/resources/elasticsearch_enrich_policy.md
Original file line number Diff line number Diff line change
Expand Up @@ -58,13 +58,13 @@ resource "elasticstack_elasticsearch_enrich_policy" "policy1" {

### Optional

- `elasticsearch_connection` (Block List, Max: 1, Deprecated) Elasticsearch connection configuration block. This property will be removed in a future provider version. Configure the Elasticsearch connection via the provider configuration instead. (see [below for nested schema](#nestedblock--elasticsearch_connection))
- `elasticsearch_connection` (Block List, Deprecated) Elasticsearch connection configuration block. (see [below for nested schema](#nestedblock--elasticsearch_connection))
- `execute` (Boolean) Whether to call the execute API function in order to create the enrich index.
- `query` (String) Query used to filter documents in the enrich index. The policy only uses documents matching this query to enrich incoming documents. Defaults to a match_all query.

### Read-Only

- `id` (String) The ID of this resource.
- `id` (String) Internal identifier of the resource

<a id="nestedblock--elasticsearch_connection"></a>
### Nested Schema for `elasticsearch_connection`
Expand Down
200 changes: 200 additions & 0 deletions internal/elasticsearch/enrich/acc_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,200 @@
package enrich_test

import (
"encoding/json"
"fmt"
"net/http"
"testing"

"github.com/elastic/terraform-provider-elasticstack/internal/acctest"
"github.com/elastic/terraform-provider-elasticstack/internal/clients"
sdkacctest "github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource"
"github.com/hashicorp/terraform-plugin-sdk/v2/terraform"
)

func TestAccResourceEnrichPolicyFW(t *testing.T) {
name := sdkacctest.RandStringFromCharSet(10, sdkacctest.CharSetAlphaNum)
resource.Test(t, resource.TestCase{
PreCheck: func() { acctest.PreCheck(t) },
CheckDestroy: checkEnrichPolicyDestroyFW(name),
ProtoV6ProviderFactories: acctest.Providers,
Steps: []resource.TestStep{
{
Config: testAccEnrichPolicyFW(name),
Check: resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttr("elasticstack_elasticsearch_enrich_policy.policy", "name", name),
resource.TestCheckResourceAttr("elasticstack_elasticsearch_enrich_policy.policy", "policy_type", "match"),
resource.TestCheckResourceAttr("elasticstack_elasticsearch_enrich_policy.policy", "match_field", `email`),
resource.TestCheckResourceAttr("elasticstack_elasticsearch_enrich_policy.policy", "indices.0", name),
resource.TestCheckResourceAttr("elasticstack_elasticsearch_enrich_policy.policy", "enrich_fields.0", "first_name"),
resource.TestCheckResourceAttr("elasticstack_elasticsearch_enrich_policy.policy", "enrich_fields.1", "last_name"),
resource.TestCheckResourceAttr("elasticstack_elasticsearch_enrich_policy.policy", "query", "{\"match_all\": {}}\n"),
resource.TestCheckResourceAttr("elasticstack_elasticsearch_enrich_policy.policy", "execute", "true"),
),
},
},
})
}

func TestAccDataSourceEnrichPolicyFW(t *testing.T) {
name := sdkacctest.RandStringFromCharSet(10, sdkacctest.CharSetAlphaNum)
resource.Test(t, resource.TestCase{
PreCheck: func() { acctest.PreCheck(t) },
ProtoV6ProviderFactories: acctest.Providers,
Steps: []resource.TestStep{
{
Config: testAccEnrichPolicyDataSourceFW(name),
Check: resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttr("data.elasticstack_elasticsearch_enrich_policy.test", "name", name),
resource.TestCheckResourceAttr("data.elasticstack_elasticsearch_enrich_policy.test", "policy_type", "match"),
resource.TestCheckResourceAttr("data.elasticstack_elasticsearch_enrich_policy.test", "match_field", "email"),
resource.TestCheckResourceAttr("data.elasticstack_elasticsearch_enrich_policy.test", "indices.0", name),
resource.TestCheckResourceAttr("data.elasticstack_elasticsearch_enrich_policy.test", "enrich_fields.0", "first_name"),
resource.TestCheckResourceAttr("data.elasticstack_elasticsearch_enrich_policy.test", "enrich_fields.1", "last_name"),
resource.TestCheckResourceAttr("data.elasticstack_elasticsearch_enrich_policy.test", "query", "{\"match_all\":{}}"),
),
},
},
})
}

func TestAccResourceEnrichPolicyFromSDK(t *testing.T) {
name := sdkacctest.RandStringFromCharSet(10, sdkacctest.CharSetAlphaNum)
resource.Test(t, resource.TestCase{
PreCheck: func() { acctest.PreCheck(t) },
Steps: []resource.TestStep{
{
// Create the enrich policy with the last provider version where the enrich policy resource was built on the SDK
ExternalProviders: map[string]resource.ExternalProvider{
"elasticstack": {
Source: "elastic/elasticstack",
VersionConstraint: "0.11.17",
},
},
Config: testAccEnrichPolicyFW(name),
Check: resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttr("elasticstack_elasticsearch_enrich_policy.policy", "name", name),
resource.TestCheckResourceAttr("elasticstack_elasticsearch_enrich_policy.policy", "policy_type", "match"),
resource.TestCheckResourceAttr("elasticstack_elasticsearch_enrich_policy.policy", "execute", "true"),
),
},
{
ProtoV6ProviderFactories: acctest.Providers,
Config: testAccEnrichPolicyFW(name),
Check: resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttr("elasticstack_elasticsearch_enrich_policy.policy", "name", name),
resource.TestCheckResourceAttr("elasticstack_elasticsearch_enrich_policy.policy", "policy_type", "match"),
resource.TestCheckResourceAttr("elasticstack_elasticsearch_enrich_policy.policy", "execute", "true"),
),
},
},
})
}

func testAccEnrichPolicyFW(name string) string {
return fmt.Sprintf(`
provider "elasticstack" {
elasticsearch {}
}

resource "elasticstack_elasticsearch_index" "my_index" {
name = "%s"

mappings = jsonencode({
properties = {
email = { type = "text" }
first_name = { type = "text" }
last_name = { type = "text" }
}
})
deletion_protection = false
}

resource "elasticstack_elasticsearch_enrich_policy" "policy" {
name = "%s"
policy_type = "match"
indices = [elasticstack_elasticsearch_index.my_index.name]
match_field = "email"
enrich_fields = ["first_name", "last_name"]
query = <<-EOD
{"match_all": {}}
EOD
}
`, name, name)
}

func testAccEnrichPolicyDataSourceFW(name string) string {
return fmt.Sprintf(`
provider "elasticstack" {
elasticsearch {}
}

resource "elasticstack_elasticsearch_index" "my_index" {
name = "%s"

mappings = jsonencode({
properties = {
email = { type = "text" }
first_name = { type = "text" }
last_name = { type = "text" }
}
})
deletion_protection = false
}

resource "elasticstack_elasticsearch_enrich_policy" "policy" {
name = "%s"
policy_type = "match"
indices = [elasticstack_elasticsearch_index.my_index.name]
match_field = "email"
enrich_fields = ["first_name", "last_name"]
query = <<-EOD
{"match_all": {}}
EOD
}

data "elasticstack_elasticsearch_enrich_policy" "test" {
name = elasticstack_elasticsearch_enrich_policy.policy.name
}
`, name, name)
}

func checkEnrichPolicyDestroyFW(name string) func(s *terraform.State) error {
return func(s *terraform.State) error {
client, err := clients.NewAcceptanceTestingClient()
if err != nil {
return err
}

for _, rs := range s.RootModule().Resources {
if rs.Type != "elasticstack_elasticsearch_enrich_policy" {
continue
}
compId, _ := clients.CompositeIdFromStr(rs.Primary.ID)
if compId.ResourceId != name {
return fmt.Errorf("Found unexpectedly enrich policy: %s", compId.ResourceId)
}
esClient, err := client.GetESClient()
if err != nil {
return err
}
req := esClient.EnrichGetPolicy.WithName(compId.ResourceId)
res, err := esClient.EnrichGetPolicy(req)
if err != nil {
return err
}
defer res.Body.Close()
if res.StatusCode == http.StatusFound {
var policiesResponse map[string]any
if err := json.NewDecoder(res.Body).Decode(&policiesResponse); err != nil {
return err
}
if len(policiesResponse["policies"].([]any)) != 0 {
return fmt.Errorf("Enrich policy (%s) still exists", compId.ResourceId)
}
}
}
return nil
}
}
94 changes: 94 additions & 0 deletions internal/elasticsearch/enrich/create.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
package enrich

import (
"context"

"github.com/elastic/terraform-provider-elasticstack/internal/clients"
"github.com/elastic/terraform-provider-elasticstack/internal/clients/elasticsearch"
"github.com/elastic/terraform-provider-elasticstack/internal/models"
"github.com/elastic/terraform-provider-elasticstack/internal/utils"
"github.com/hashicorp/terraform-plugin-framework/diag"
"github.com/hashicorp/terraform-plugin-framework/path"
"github.com/hashicorp/terraform-plugin-framework/resource"
"github.com/hashicorp/terraform-plugin-framework/tfsdk"
"github.com/hashicorp/terraform-plugin-framework/types"
)

func (r *enrichPolicyResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) {
diags := r.upsert(ctx, req.Plan, &resp.State)
resp.Diagnostics.Append(diags...)
if resp.Diagnostics.HasError() {
return
}
}

func (r *enrichPolicyResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) {
diags := r.upsert(ctx, req.Plan, &resp.State)
resp.Diagnostics.Append(diags...)
if resp.Diagnostics.HasError() {
return
}
}

func (r *enrichPolicyResource) upsert(ctx context.Context, plan tfsdk.Plan, state *tfsdk.State) diag.Diagnostics {
var data EnrichPolicyDataWithExecute
var diags diag.Diagnostics
diags.Append(plan.Get(ctx, &data)...)
if diags.HasError() {
return diags
}

policyName := data.Name.ValueString()
id, sdkDiags := r.client.ID(ctx, policyName)
diags.Append(utils.FrameworkDiagsFromSDK(sdkDiags)...)
if diags.HasError() {
return diags
}

client, diags := clients.MaybeNewApiClientFromFrameworkResource(ctx, data.ElasticsearchConnection, r.client)
diags.Append(diags...)
if diags.HasError() {
return diags
}

// Convert framework types to model
indices := utils.SetTypeAs[string](ctx, data.Indices, path.Empty(), &diags)
if diags.HasError() {
return diags
}

enrichFields := utils.SetTypeAs[string](ctx, data.EnrichFields, path.Empty(), &diags)
if diags.HasError() {
return diags
}

policy := &models.EnrichPolicy{
Type: data.PolicyType.ValueString(),
Name: policyName,
Indices: indices,
MatchField: data.MatchField.ValueString(),
EnrichFields: enrichFields,
}

if !data.Query.IsNull() && !data.Query.IsUnknown() {
policy.Query = data.Query.ValueString()
}

if sdkDiags := elasticsearch.PutEnrichPolicy(ctx, client, policy); sdkDiags.HasError() {
diags.Append(utils.FrameworkDiagsFromSDK(sdkDiags)...)
return diags
}

data.Id = types.StringValue(id.String())

// Execute policy if requested
if !data.Execute.IsNull() && !data.Execute.IsUnknown() && data.Execute.ValueBool() {
if sdkDiags := elasticsearch.ExecuteEnrichPolicy(ctx, client, policyName); sdkDiags.HasError() {
diags.Append(utils.FrameworkDiagsFromSDK(sdkDiags)...)
return diags
}
}

diags.Append(state.Set(ctx, &data)...)
return diags
}
Loading
Loading