Skip to content
Merged
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
- Add `slo_id` validation to `elasticstack_kibana_slo` ([#1221](https://github.com/elastic/terraform-provider-elasticstack/pull/1221))
- Add `ignore_missing_component_templates` to `elasticstack_elasticsearch_index_template` ([#1206](https://github.com/elastic/terraform-provider-elasticstack/pull/1206))
- Prevent provider panic when a script exists in state, but not in Elasticsearch ([#1218](https://github.com/elastic/terraform-provider-elasticstack/pull/1218))
- Fix `namespace` usage in synthetic monitors. Separate Kibana `space_id` the monitor is saved in from the `namespace`, the data stream namespace for Fleet. ([#1247](https://github.com/elastic/terraform-provider-elasticstack/pull/1247))

## [0.11.17] - 2025-07-21

Expand Down
3 changes: 2 additions & 1 deletion docs/resources/kibana_synthetics_monitor.md
Original file line number Diff line number Diff line change
Expand Up @@ -72,12 +72,13 @@ resource "elasticstack_kibana_synthetics_monitor" "my_monitor" {
- `http` (Attributes) HTTP Monitor specific fields (see [below for nested schema](#nestedatt--http))
- `icmp` (Attributes) ICMP Monitor specific fields (see [below for nested schema](#nestedatt--icmp))
- `locations` (List of String) Where to deploy the monitor. Monitors can be deployed in multiple locations so that you can detect differences in availability and response times across those locations.
- `namespace` (String) Fleet namespace. The `namespace` field should be lowercase and not contain spaces. The namespace must not include any of the following characters: *, \, /, ?, ", <, >, |, whitespace, ,, #, :, or -. Default: `default`
- `params` (String) Monitor parameters. Raw JSON object, use `jsonencode` function to represent JSON
- `private_locations` (List of String) These Private Locations refer to locations hosted and managed by you, whereas locations are hosted by Elastic. You can specify a Private Location using the location’s name.
- `retest_on_failure` (Boolean) Enable or disable retesting when a monitor fails. By default, monitors are automatically retested if the monitor goes from "up" to "down". If the result of the retest is also "down", an error will be created, and if configured, an alert sent. Then the monitor will resume running according to the defined schedule. Using retest_on_failure can reduce noise related to transient problems. Default: `true`.
- `schedule` (Number) The monitor’s schedule in minutes. Supported values are 1, 3, 5, 10, 15, 30, 60, 120 and 240.
- `service_name` (String) The APM service name.
- `space_id` (String) The namespace field should be lowercase and not contain spaces. The namespace must not include any of the following characters: *, \, /, ?, ", <, >, |, whitespace, ,, #, :, or -. Default: `default`
- `space_id` (String) Kibana space. The space ID that is part of the Kibana URL when inside the space. Space IDs are limited to lowercase alphanumeric, underscore, and hyphen characters (a-z, 0-9, _, and -). You are cannot change the ID with the update operation.
- `tags` (List of String) An array of tags.
- `tcp` (Attributes) TCP Monitor specific fields (see [below for nested schema](#nestedatt--tcp))
- `timeout` (Number) The monitor timeout in seconds, monitor will fail if it doesn’t complete within this time. Default: `16`
Expand Down
30 changes: 24 additions & 6 deletions internal/kibana/synthetics/acc_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ resource "elasticstack_kibana_synthetics_monitor" "%s" {
resource "elasticstack_kibana_synthetics_monitor" "%s" {
name = "TestHttpMonitorResource - %s"
space_id = "testacc"
namespace = "test_namespace"
schedule = 5
private_locations = [elasticstack_kibana_synthetics_private_location.%s.label]
enabled = true
Expand Down Expand Up @@ -164,6 +165,7 @@ resource "elasticstack_kibana_synthetics_monitor" "%s" {
resource "elasticstack_kibana_synthetics_monitor" "%s" {
name = "TestTcpMonitorResource - %s"
space_id = "testacc"
namespace = "testacc_test"
schedule = 5
private_locations = [elasticstack_kibana_synthetics_private_location.%s.label]
enabled = true
Expand Down Expand Up @@ -230,6 +232,7 @@ resource "elasticstack_kibana_synthetics_monitor" "%s" {
resource "elasticstack_kibana_synthetics_monitor" "%s" {
name = "TestIcmpMonitorResource - %s"
space_id = "testacc"
namespace = "testacc_namespace"
schedule = 5
private_locations = [elasticstack_kibana_synthetics_private_location.%s.label]
enabled = true
Expand Down Expand Up @@ -279,6 +282,7 @@ resource "elasticstack_kibana_synthetics_monitor" "%s" {
resource "elasticstack_kibana_synthetics_monitor" "%s" {
name = "TestBrowserMonitorResource - %s"
space_id = "testacc"
namespace = "testacc_ns"
schedule = 5
private_locations = [elasticstack_kibana_synthetics_private_location.%s.label]
enabled = true
Expand Down Expand Up @@ -363,7 +367,8 @@ func TestSyntheticMonitorHTTPResource(t *testing.T) {
Check: resource.ComposeAggregateTestCheckFunc(
resource.TestCheckResourceAttrSet(bmMonitorId, "id"),
resource.TestCheckResourceAttr(bmMonitorId, "name", "TestHttpMonitorResource - "+bmName),
resource.TestCheckResourceAttr(bmMonitorId, "space_id", "default"),
resource.TestCheckResourceAttr(bmMonitorId, "space_id", ""),
resource.TestCheckResourceAttr(bmMonitorId, "namespace", "default"),
resource.TestCheckResourceAttr(bmMonitorId, "alert.status.enabled", "true"),
resource.TestCheckResourceAttr(bmMonitorId, "alert.tls.enabled", "true"),
resource.TestCheckResourceAttr(bmMonitorId, "http.url", "http://localhost:5601"),
Expand All @@ -376,7 +381,8 @@ func TestSyntheticMonitorHTTPResource(t *testing.T) {
Check: resource.ComposeAggregateTestCheckFunc(
resource.TestCheckResourceAttrSet(sslHttpMonitorId, "id"),
resource.TestCheckResourceAttr(sslHttpMonitorId, "name", "TestHttpMonitorResource - "+sslName),
resource.TestCheckResourceAttr(sslHttpMonitorId, "space_id", "default"),
resource.TestCheckResourceAttr(sslHttpMonitorId, "space_id", ""),
resource.TestCheckResourceAttr(sslHttpMonitorId, "namespace", "default"),
resource.TestCheckResourceAttr(sslHttpMonitorId, "http.url", "http://localhost:5601"),
resource.TestCheckResourceAttr(sslHttpMonitorId, "http.ssl_verification_mode", "full"),
resource.TestCheckResourceAttr(sslHttpMonitorId, "http.ssl_supported_protocols.#", "1"),
Expand Down Expand Up @@ -405,6 +411,7 @@ func TestSyntheticMonitorHTTPResource(t *testing.T) {
resource.TestCheckResourceAttrSet(httpMonitorId, "id"),
resource.TestCheckResourceAttr(httpMonitorId, "name", "TestHttpMonitorResource - "+name),
resource.TestCheckResourceAttr(httpMonitorId, "space_id", "testacc"),
resource.TestCheckResourceAttr(httpMonitorId, "namespace", "test_namespace"),
resource.TestCheckResourceAttr(httpMonitorId, "schedule", "5"),
resource.TestCheckResourceAttr(httpMonitorId, "private_locations.#", "1"),
resource.TestCheckResourceAttrSet(httpMonitorId, "private_locations.0"),
Expand Down Expand Up @@ -446,6 +453,7 @@ func TestSyntheticMonitorHTTPResource(t *testing.T) {
resource.TestCheckResourceAttrSet(httpMonitorId, "id"),
resource.TestCheckResourceAttr(httpMonitorId, "name", "TestHttpMonitorResource Updated - "+name),
resource.TestCheckResourceAttr(httpMonitorId, "space_id", "testacc"),
resource.TestCheckResourceAttr(httpMonitorId, "namespace", "test_namespace"),
resource.TestCheckResourceAttr(httpMonitorId, "schedule", "10"),
resource.TestCheckResourceAttr(httpMonitorId, "private_locations.#", "1"),
resource.TestCheckResourceAttrSet(httpMonitorId, "private_locations.0"),
Expand Down Expand Up @@ -509,7 +517,8 @@ func TestSyntheticMonitorTCPResource(t *testing.T) {
Check: resource.ComposeAggregateTestCheckFunc(
resource.TestCheckResourceAttrSet(bmMonitorId, "id"),
resource.TestCheckResourceAttr(bmMonitorId, "name", "TestTcpMonitorResource - "+bmName),
resource.TestCheckResourceAttr(bmMonitorId, "space_id", "default"),
resource.TestCheckResourceAttr(bmMonitorId, "space_id", ""),
resource.TestCheckResourceAttr(bmMonitorId, "namespace", "default"),
resource.TestCheckResourceAttr(bmMonitorId, "tcp.host", "http://localhost:5601"),
resource.TestCheckResourceAttr(bmMonitorId, "alert.status.enabled", "true"),
resource.TestCheckResourceAttr(bmMonitorId, "alert.tls.enabled", "true"),
Expand All @@ -523,7 +532,8 @@ func TestSyntheticMonitorTCPResource(t *testing.T) {
Check: resource.ComposeAggregateTestCheckFunc(
resource.TestCheckResourceAttrSet(sslTcpMonitorId, "id"),
resource.TestCheckResourceAttr(sslTcpMonitorId, "name", "TestHttpMonitorResource - "+sslName),
resource.TestCheckResourceAttr(sslTcpMonitorId, "space_id", "default"),
resource.TestCheckResourceAttr(sslTcpMonitorId, "space_id", ""),
resource.TestCheckResourceAttr(sslTcpMonitorId, "namespace", "default"),
resource.TestCheckResourceAttr(sslTcpMonitorId, "tcp.host", "http://localhost:5601"),
resource.TestCheckResourceAttr(sslTcpMonitorId, "tcp.ssl_verification_mode", "full"),
resource.TestCheckResourceAttr(sslTcpMonitorId, "tcp.ssl_supported_protocols.#", "1"),
Expand Down Expand Up @@ -552,6 +562,7 @@ func TestSyntheticMonitorTCPResource(t *testing.T) {
resource.TestCheckResourceAttrSet(tcpMonitorId, "id"),
resource.TestCheckResourceAttr(tcpMonitorId, "name", "TestTcpMonitorResource - "+name),
resource.TestCheckResourceAttr(tcpMonitorId, "space_id", "testacc"),
resource.TestCheckResourceAttr(tcpMonitorId, "namespace", "testacc_test"),
resource.TestCheckResourceAttr(tcpMonitorId, "schedule", "5"),
resource.TestCheckResourceAttr(tcpMonitorId, "private_locations.#", "1"),
resource.TestCheckResourceAttrSet(tcpMonitorId, "private_locations.0"),
Expand Down Expand Up @@ -590,6 +601,7 @@ func TestSyntheticMonitorTCPResource(t *testing.T) {
resource.TestCheckResourceAttrSet(tcpMonitorId, "id"),
resource.TestCheckResourceAttr(tcpMonitorId, "name", "TestTcpMonitorResource Updated - "+name),
resource.TestCheckResourceAttr(tcpMonitorId, "space_id", "testacc"),
resource.TestCheckResourceAttr(tcpMonitorId, "namespace", "testacc_test"),
resource.TestCheckResourceAttr(tcpMonitorId, "schedule", "10"),
resource.TestCheckResourceAttr(tcpMonitorId, "private_locations.#", "1"),
resource.TestCheckResourceAttrSet(tcpMonitorId, "private_locations.0"),
Expand Down Expand Up @@ -643,7 +655,8 @@ func TestSyntheticMonitorICMPResource(t *testing.T) {
Check: resource.ComposeAggregateTestCheckFunc(
resource.TestCheckResourceAttrSet(bmMonitorId, "id"),
resource.TestCheckResourceAttr(bmMonitorId, "name", "TestIcmpMonitorResource - "+bmName),
resource.TestCheckResourceAttr(bmMonitorId, "space_id", "default"),
resource.TestCheckResourceAttr(bmMonitorId, "space_id", ""),
resource.TestCheckResourceAttr(bmMonitorId, "namespace", "default"),
resource.TestCheckResourceAttr(bmMonitorId, "icmp.host", "localhost"),
resource.TestCheckResourceAttr(bmMonitorId, "alert.status.enabled", "true"),
resource.TestCheckResourceAttr(bmMonitorId, "alert.tls.enabled", "true"),
Expand All @@ -658,6 +671,7 @@ func TestSyntheticMonitorICMPResource(t *testing.T) {
resource.TestCheckResourceAttrSet(icmpMonitorId, "id"),
resource.TestCheckResourceAttr(icmpMonitorId, "name", "TestIcmpMonitorResource - "+name),
resource.TestCheckResourceAttr(icmpMonitorId, "space_id", "testacc"),
resource.TestCheckResourceAttr(icmpMonitorId, "namespace", "testacc_namespace"),
resource.TestCheckResourceAttr(icmpMonitorId, "schedule", "5"),
resource.TestCheckResourceAttr(icmpMonitorId, "private_locations.#", "1"),
resource.TestCheckResourceAttrSet(icmpMonitorId, "private_locations.0"),
Expand Down Expand Up @@ -689,6 +703,7 @@ func TestSyntheticMonitorICMPResource(t *testing.T) {
resource.TestCheckResourceAttrSet(icmpMonitorId, "id"),
resource.TestCheckResourceAttr(icmpMonitorId, "name", "TestIcmpMonitorResource Updated - "+name),
resource.TestCheckResourceAttr(icmpMonitorId, "space_id", "testacc"),
resource.TestCheckResourceAttr(icmpMonitorId, "namespace", "testacc_namespace"),
resource.TestCheckResourceAttr(icmpMonitorId, "schedule", "10"),
resource.TestCheckResourceAttr(icmpMonitorId, "private_locations.#", "1"),
resource.TestCheckResourceAttrSet(icmpMonitorId, "private_locations.0"),
Expand Down Expand Up @@ -735,7 +750,8 @@ func TestSyntheticMonitorBrowserResource(t *testing.T) {
Check: resource.ComposeAggregateTestCheckFunc(
resource.TestCheckResourceAttrSet(bmMonitorId, "id"),
resource.TestCheckResourceAttr(bmMonitorId, "name", "TestBrowserMonitorResource - "+bmName),
resource.TestCheckResourceAttr(bmMonitorId, "space_id", "default"),
resource.TestCheckResourceAttr(bmMonitorId, "space_id", ""),
resource.TestCheckResourceAttr(bmMonitorId, "namespace", "default"),
resource.TestCheckResourceAttr(bmMonitorId, "browser.inline_script", "step('Go to https://google.com.co', () => page.goto('https://www.google.com'))"),
resource.TestCheckResourceAttr(bmMonitorId, "alert.status.enabled", "true"),
resource.TestCheckResourceAttr(bmMonitorId, "alert.tls.enabled", "true"),
Expand All @@ -749,6 +765,7 @@ func TestSyntheticMonitorBrowserResource(t *testing.T) {
resource.TestCheckResourceAttrSet(browserMonitorId, "id"),
resource.TestCheckResourceAttr(browserMonitorId, "name", "TestBrowserMonitorResource - "+name),
resource.TestCheckResourceAttr(browserMonitorId, "space_id", "testacc"),
resource.TestCheckResourceAttr(browserMonitorId, "namespace", "testacc_ns"),
resource.TestCheckResourceAttr(browserMonitorId, "schedule", "5"),
resource.TestCheckResourceAttr(browserMonitorId, "private_locations.#", "1"),
resource.TestCheckResourceAttrSet(browserMonitorId, "private_locations.0"),
Expand Down Expand Up @@ -780,6 +797,7 @@ func TestSyntheticMonitorBrowserResource(t *testing.T) {
resource.TestCheckResourceAttrSet(browserMonitorId, "id"),
resource.TestCheckResourceAttr(browserMonitorId, "name", "TestBrowserMonitorResource Updated - "+name),
resource.TestCheckResourceAttr(browserMonitorId, "space_id", "testacc"),
resource.TestCheckResourceAttr(browserMonitorId, "namespace", "testacc_ns"),
resource.TestCheckResourceAttr(browserMonitorId, "schedule", "10"),
resource.TestCheckResourceAttr(browserMonitorId, "private_locations.#", "1"),
resource.TestCheckResourceAttrSet(browserMonitorId, "private_locations.0"),
Expand Down
9 changes: 5 additions & 4 deletions internal/kibana/synthetics/create.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package synthetics
import (
"context"
"fmt"

"github.com/hashicorp/terraform-plugin-framework/resource"
)

Expand All @@ -26,14 +27,14 @@ func (r *Resource) Create(ctx context.Context, request resource.CreateRequest, r
return
}

namespace := plan.SpaceID.ValueString()
result, err := kibanaClient.KibanaSynthetics.Monitor.Add(ctx, input.config, input.fields, namespace)
spaceId := plan.SpaceID.ValueString()
result, err := kibanaClient.KibanaSynthetics.Monitor.Add(ctx, input.config, input.fields, spaceId)
if err != nil {
response.Diagnostics.AddError(fmt.Sprintf("Failed to create Kibana monitor `%s`, namespace %s", input.config.Name, namespace), err.Error())
response.Diagnostics.AddError(fmt.Sprintf("Failed to create Kibana monitor `%s`, space %s", input.config.Name, spaceId), err.Error())
return
}

plan, diags = plan.toModelV0(ctx, result)
plan, diags = plan.toModelV0(ctx, result, spaceId)
response.Diagnostics.Append(diags...)
if response.Diagnostics.HasError() {
return
Expand Down
9 changes: 5 additions & 4 deletions internal/kibana/synthetics/read.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"context"
"errors"
"fmt"

"github.com/disaster37/go-kibana-rest/v8/kbapi"
"github.com/hashicorp/terraform-plugin-framework/resource"
)
Expand All @@ -28,21 +29,21 @@ func (r *Resource) Read(ctx context.Context, request resource.ReadRequest, respo
return
}

namespace := compositeId.ClusterId
spaceId := compositeId.ClusterId
monitorId := kbapi.MonitorID(compositeId.ResourceId)
result, err := kibanaClient.KibanaSynthetics.Monitor.Get(ctx, monitorId, namespace)
result, err := kibanaClient.KibanaSynthetics.Monitor.Get(ctx, monitorId, spaceId)
if err != nil {
var apiError *kbapi.APIError
if errors.As(err, &apiError) && apiError.Code == 404 {
response.State.RemoveResource(ctx)
return
}

response.Diagnostics.AddError(fmt.Sprintf("Failed to get monitor `%s`, namespace %s", monitorId, namespace), err.Error())
response.Diagnostics.AddError(fmt.Sprintf("Failed to get monitor `%s`, space %s", monitorId, spaceId), err.Error())
return
}

state, diags = state.toModelV0(ctx, result)
state, diags = state.toModelV0(ctx, result, spaceId)
response.Diagnostics.Append(diags...)
if response.Diagnostics.HasError() {
return
Expand Down
21 changes: 16 additions & 5 deletions internal/kibana/synthetics/schema.go
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,7 @@ type tfModelV0 struct {
ID types.String `tfsdk:"id"`
Name types.String `tfsdk:"name"`
SpaceID types.String `tfsdk:"space_id"`
Namespace types.String `tfsdk:"namespace"`
Schedule types.Int64 `tfsdk:"schedule"`
Locations []types.String `tfsdk:"locations"`
PrivateLocations []types.String `tfsdk:"private_locations"`
Expand Down Expand Up @@ -143,7 +144,16 @@ func monitorConfigSchema() schema.Schema {
MarkdownDescription: "The monitor’s name.",
},
"space_id": schema.StringAttribute{
MarkdownDescription: "The namespace field should be lowercase and not contain spaces. The namespace must not include any of the following characters: *, \\, /, ?, \", <, >, |, whitespace, ,, #, :, or -. Default: `default`",
MarkdownDescription: "Kibana space. The space ID that is part of the Kibana URL when inside the space. Space IDs are limited to lowercase alphanumeric, underscore, and hyphen characters (a-z, 0-9, _, and -). You are cannot change the ID with the update operation.",
Optional: true,
PlanModifiers: []planmodifier.String{
stringplanmodifier.UseStateForUnknown(),
stringplanmodifier.RequiresReplace(),
},
Computed: true,
},
"namespace": schema.StringAttribute{
MarkdownDescription: "Fleet namespace. The `namespace` field should be lowercase and not contain spaces. The namespace must not include any of the following characters: *, \\, /, ?, \", <, >, |, whitespace, ,, #, :, or -. Default: `default`",
Optional: true,
PlanModifiers: []planmodifier.String{
stringplanmodifier.UseStateForUnknown(),
Expand Down Expand Up @@ -566,7 +576,7 @@ func stringToInt64(v string) (int64, error) {
return res, err
}

func (v *tfModelV0) toModelV0(ctx context.Context, api *kbapi.SyntheticsMonitor) (*tfModelV0, diag.Diagnostics) {
func (v *tfModelV0) toModelV0(ctx context.Context, api *kbapi.SyntheticsMonitor, space string) (*tfModelV0, diag.Diagnostics) {
var schedule int64
var err error
dg := diag.Diagnostics{}
Expand Down Expand Up @@ -640,7 +650,7 @@ func (v *tfModelV0) toModelV0(ctx context.Context, api *kbapi.SyntheticsMonitor)
}

resourceID := clients.CompositeId{
ClusterId: api.Namespace,
ClusterId: space,
ResourceId: string(api.Id),
}

Expand All @@ -652,7 +662,8 @@ func (v *tfModelV0) toModelV0(ctx context.Context, api *kbapi.SyntheticsMonitor)
return &tfModelV0{
ID: types.StringValue(resourceID.String()),
Name: types.StringValue(api.Name),
SpaceID: types.StringValue(api.Namespace),
SpaceID: types.StringValue(space),
Namespace: types.StringValue(api.Namespace),
Schedule: types.Int64Value(schedule),
Locations: v.Locations,
PrivateLocations: StringSliceValue(privateLocLabels),
Expand Down Expand Up @@ -883,7 +894,7 @@ func (v *tfModelV0) toSyntheticsMonitorConfig(ctx context.Context) (*kbapi.Synth
Alert: toTFAlertConfig(ctx, v.Alert),
APMServiceName: v.APMServiceName.ValueString(),
TimeoutSeconds: int(v.TimeoutSeconds.ValueInt64()),
Namespace: v.SpaceID.ValueString(),
Namespace: v.Namespace.ValueString(),
Params: params,
RetestOnFailure: v.RetestOnFailure.ValueBoolPointer(),
}, diag.Diagnostics{} //dg
Expand Down
Loading
Loading