diff --git a/docs/data-sources/loadbalancer.md b/docs/data-sources/loadbalancer.md
index 7b5d8bb5..047321ce 100644
--- a/docs/data-sources/loadbalancer.md
+++ b/docs/data-sources/loadbalancer.md
@@ -79,8 +79,36 @@ Read-Only:
Read-Only:
- `acl` (Set of String) Load Balancer is accessible only from an IP address in this range.
+- `observability` (Attributes) We offer Load Balancer metrics observability via ARGUS or external solutions. (see [below for nested schema](#nestedatt--options--observability))
- `private_network_only` (Boolean) If true, Load Balancer is accessible only via a private network IP address.
+
+### Nested Schema for `options.observability`
+
+Read-Only:
+
+- `logs` (Attributes) Observability logs configuration. (see [below for nested schema](#nestedatt--options--observability--logs))
+- `metrics` (Attributes) Observability metrics configuration. (see [below for nested schema](#nestedatt--options--observability--metrics))
+
+
+### Nested Schema for `options.observability.logs`
+
+Read-Only:
+
+- `credentials_ref` (String) Credentials reference for logs.
+- `push_url` (String) Credentials reference for logs.
+
+
+
+### Nested Schema for `options.observability.metrics`
+
+Read-Only:
+
+- `credentials_ref` (String) Credentials reference for metrics.
+- `push_url` (String) Credentials reference for metrics.
+
+
+
### Nested Schema for `target_pools`
diff --git a/docs/guides/using_loadbalancer_with_observability.md b/docs/guides/using_loadbalancer_with_observability.md
new file mode 100644
index 00000000..106d8797
--- /dev/null
+++ b/docs/guides/using_loadbalancer_with_observability.md
@@ -0,0 +1,167 @@
+---
+page_title: "Using the STACKIT Loadbalancer together with STACKIT Observability"
+---
+# Using the STACKIT Loadbalancer together with STACKIT Observability
+
+## Overview
+
+This guide explains how to configure the STACKIT Loadbalancer product to send metrics and logs to a STACKIT Observability instance.
+
+1. **Set Up Providers**
+
+ Begin by configuring the STACKIT provider to connect to the STACKIT services.
+
+ ```hcl
+ provider "stackit" {
+ default_region = "eu01"
+ }
+ ```
+
+2. **Create an Observability instance**
+
+ Establish a STACKIT Observability instance and its credentials.
+
+ ```hcl
+ resource "stackit_observability_instance" "observability01" {
+ project_id = var.project_id_prod
+ name = "example-instance"
+ plan_name = "Observability-Monitoring-Medium-EU01"
+ acl = ["0.0.0.0/0"]
+ metrics_retention_days = 30
+ metrics_retention_days_5m_downsampling = 10
+ metrics_retention_days_1h_downsampling = 5
+ }
+
+ resource "stackit_observability_credential" "observability01-credential" {
+ project_id = var.project_id_prod
+ instance_id = stackit_observability_instance.observability01.instance_id
+ }
+ ```
+
+3. **Create STACKIT Loadbalancer credentials reference**
+
+ Create a STACKIT Loadbalancer credentials which will be used in the STACKIT Loadbalancer resource as a reference.
+
+ ```hcl
+ resource "stackit_loadbalancer_observability_credential" "example" {
+ project_id = var.project_id_prod
+ display_name = "example-credentials"
+ username = stackit_observability_credential.observability01-credential.username
+ password = stackit_observability_credential.observability01-credential.password
+ }
+ ```
+
+4. **Create the STACKIT Loadbalancer**
+
+ ```hcl
+ # Create a network
+ resource "stackit_network" "example_network" {
+ project_id = var.project_id_prod
+ name = "example-network"
+ ipv4_nameservers = ["8.8.8.8"]
+ ipv4_prefix = "192.168.0.0/25"
+ labels = {
+ "key" = "value"
+ }
+ routed = true
+ }
+
+ # Create a network interface
+ resource "stackit_network_interface" "nic" {
+ project_id = var.project_id_prod
+ network_id = stackit_network.example_network.network_id
+ }
+
+ # Create a public IP for the load balancer
+ resource "stackit_public_ip" "public-ip" {
+ project_id = var.project_id_prod
+ lifecycle {
+ ignore_changes = [network_interface_id]
+ }
+ }
+
+ # Create a key pair for accessing the server instance
+ resource "stackit_key_pair" "keypair" {
+ name = "example-key-pair"
+ # set the path of your public key file here
+ public_key = chomp(file("/home/bob/.ssh/id_ed25519.pub"))
+ }
+
+ # Create a server instance
+ resource "stackit_server" "boot-from-image" {
+ project_id = var.project_id_prod
+ name = "example-server"
+ boot_volume = {
+ size = 64
+ source_type = "image"
+ source_id = "59838a89-51b1-4892-b57f-b3caf598ee2f" // Ubuntu 24.04
+ }
+ availability_zone = "eu01-1"
+ machine_type = "g1.1"
+ keypair_name = stackit_key_pair.keypair.name
+ }
+
+ # Attach the network interface to the server
+ resource "stackit_server_network_interface_attach" "nic-attachment" {
+ project_id = var.project_id_prod
+ server_id = stackit_server.boot-from-image.server_id
+ network_interface_id = stackit_network_interface.nic.network_interface_id
+ }
+
+ # Create a load balancer
+ resource "stackit_loadbalancer" "example" {
+ project_id = var.project_id_prod
+ name = "example-load-balancer"
+ target_pools = [
+ {
+ name = "example-target-pool"
+ target_port = 80
+ targets = [
+ {
+ display_name = stackit_server.boot-from-image.name
+ ip = stackit_network_interface.nic.ipv4
+ }
+ ]
+ active_health_check = {
+ healthy_threshold = 10
+ interval = "3s"
+ interval_jitter = "3s"
+ timeout = "3s"
+ unhealthy_threshold = 10
+ }
+ }
+ ]
+ listeners = [
+ {
+ display_name = "example-listener"
+ port = 80
+ protocol = "PROTOCOL_TCP"
+ target_pool = "example-target-pool"
+ }
+ ]
+ networks = [
+ {
+ network_id = stackit_network.example_network.network_id
+ role = "ROLE_LISTENERS_AND_TARGETS"
+ }
+ ]
+ external_address = stackit_public_ip.public-ip.ip
+ options = {
+ private_network_only = false
+ observability = {
+ logs = {
+ # uses the load balancer credential from the last step
+ credentials_ref = stackit_loadbalancer_observability_credential.example.credentials_ref
+ # uses the observability instance from step 1
+ push_url = stackit_observability_instance.observability01.logs_push_url
+ }
+ metrics = {
+ # uses the load balancer credential from the last step
+ credentials_ref = stackit_loadbalancer_observability_credential.example.credentials_ref
+ # uses the observability instance from step 1
+ push_url = stackit_observability_instance.observability01.metrics_push_url
+ }
+ }
+ }
+ }
+ ```
diff --git a/docs/resources/loadbalancer.md b/docs/resources/loadbalancer.md
index 7a6c9125..009f543f 100644
--- a/docs/resources/loadbalancer.md
+++ b/docs/resources/loadbalancer.md
@@ -218,4 +218,30 @@ Optional:
Optional:
- `acl` (Set of String) Load Balancer is accessible only from an IP address in this range.
+- `observability` (Attributes) We offer Load Balancer metrics observability via ARGUS or external solutions. Not changeable after creation. (see [below for nested schema](#nestedatt--options--observability))
- `private_network_only` (Boolean) If true, Load Balancer is accessible only via a private network IP address.
+
+
+### Nested Schema for `options.observability`
+
+Optional:
+
+- `logs` (Attributes) Observability logs configuration. Not changeable after creation. (see [below for nested schema](#nestedatt--options--observability--logs))
+- `metrics` (Attributes) Observability metrics configuration. Not changeable after creation. (see [below for nested schema](#nestedatt--options--observability--metrics))
+
+
+### Nested Schema for `options.observability.logs`
+
+Optional:
+
+- `credentials_ref` (String) Credentials reference for logs. Not changeable after creation.
+- `push_url` (String) Credentials reference for logs. Not changeable after creation.
+
+
+
+### Nested Schema for `options.observability.metrics`
+
+Optional:
+
+- `credentials_ref` (String) Credentials reference for metrics. Not changeable after creation.
+- `push_url` (String) Credentials reference for metrics. Not changeable after creation.
diff --git a/stackit/internal/services/loadbalancer/loadbalancer/datasource.go b/stackit/internal/services/loadbalancer/loadbalancer/datasource.go
index 33514471..6e8521d8 100644
--- a/stackit/internal/services/loadbalancer/loadbalancer/datasource.go
+++ b/stackit/internal/services/loadbalancer/loadbalancer/datasource.go
@@ -81,38 +81,45 @@ func (r *loadBalancerDataSource) Configure(ctx context.Context, req datasource.C
// Schema defines the schema for the data source.
func (r *loadBalancerDataSource) Schema(_ context.Context, _ datasource.SchemaRequest, resp *datasource.SchemaResponse) {
descriptions := map[string]string{
- "main": "Load Balancer data source schema. Must have a `region` specified in the provider configuration.",
- "id": "Terraform's internal resource ID. It is structured as \"`project_id`\",\"region\",\"`name`\".",
- "project_id": "STACKIT project ID to which the Load Balancer is associated.",
- "external_address": "External Load Balancer IP address where this Load Balancer is exposed.",
- "listeners": "List of all listeners which will accept traffic. Limited to 20.",
- "port": "Port number where we listen for traffic.",
- "protocol": "Protocol is the highest network protocol we understand to load balance.",
- "target_pool": "Reference target pool by target pool name.",
- "name": "Load balancer name.",
- "networks": "List of networks that listeners and targets reside in.",
- "network_id": "Openstack network ID.",
- "role": "The role defines how the load balancer is using the network.",
- "options": "Defines any optional functionality you want to have enabled on your load balancer.",
- "acl": "Load Balancer is accessible only from an IP address in this range.",
- "private_network_only": "If true, Load Balancer is accessible only via a private network IP address.",
- "session_persistence": "Here you can setup various session persistence options, so far only \"`use_source_ip_address`\" is supported.",
- "use_source_ip_address": "If true then all connections from one source IP address are redirected to the same target. This setting changes the load balancing algorithm to Maglev.",
- "server_name_indicators": "A list of domain names to match in order to pass TLS traffic to the target pool in the current listener",
- "server_name_indicators.name": "A domain name to match in order to pass TLS traffic to the target pool in the current listener",
- "private_address": "Transient private Load Balancer IP address. It can change any time.",
- "target_pools": "List of all target pools which will be used in the Load Balancer. Limited to 20.",
- "healthy_threshold": "Healthy threshold of the health checking.",
- "interval": "Interval duration of health checking in seconds.",
- "interval_jitter": "Interval duration threshold of the health checking in seconds.",
- "timeout": "Active health checking timeout duration in seconds.",
- "unhealthy_threshold": "Unhealthy threshold of the health checking.",
- "target_pools.name": "Target pool name.",
- "target_port": "Identical port number where each target listens for traffic.",
- "targets": "List of all targets which will be used in the pool. Limited to 1000.",
- "targets.display_name": "Target display name",
- "ip": "Target IP",
- "region": "The resource region. If not defined, the provider region is used.",
+ "main": "Load Balancer data source schema. Must have a `region` specified in the provider configuration.",
+ "id": "Terraform's internal resource ID. It is structured as \"`project_id`\",\"region\",\"`name`\".",
+ "project_id": "STACKIT project ID to which the Load Balancer is associated.",
+ "external_address": "External Load Balancer IP address where this Load Balancer is exposed.",
+ "listeners": "List of all listeners which will accept traffic. Limited to 20.",
+ "port": "Port number where we listen for traffic.",
+ "protocol": "Protocol is the highest network protocol we understand to load balance.",
+ "target_pool": "Reference target pool by target pool name.",
+ "name": "Load balancer name.",
+ "networks": "List of networks that listeners and targets reside in.",
+ "network_id": "Openstack network ID.",
+ "role": "The role defines how the load balancer is using the network.",
+ "observability": "We offer Load Balancer metrics observability via ARGUS or external solutions.",
+ "observability_logs": "Observability logs configuration.",
+ "observability_logs_credentials_ref": "Credentials reference for logs.",
+ "observability_logs_push_url": "The ARGUS/Loki remote write Push URL to ship the logs to.",
+ "observability_metrics": "Observability metrics configuration.",
+ "observability_metrics_credentials_ref": "Credentials reference for metrics.",
+ "observability_metrics_push_url": "The ARGUS/Prometheus remote write Push URL to ship the metrics to.",
+ "options": "Defines any optional functionality you want to have enabled on your load balancer.",
+ "acl": "Load Balancer is accessible only from an IP address in this range.",
+ "private_network_only": "If true, Load Balancer is accessible only via a private network IP address.",
+ "session_persistence": "Here you can setup various session persistence options, so far only \"`use_source_ip_address`\" is supported.",
+ "use_source_ip_address": "If true then all connections from one source IP address are redirected to the same target. This setting changes the load balancing algorithm to Maglev.",
+ "server_name_indicators": "A list of domain names to match in order to pass TLS traffic to the target pool in the current listener",
+ "server_name_indicators.name": "A domain name to match in order to pass TLS traffic to the target pool in the current listener",
+ "private_address": "Transient private Load Balancer IP address. It can change any time.",
+ "target_pools": "List of all target pools which will be used in the Load Balancer. Limited to 20.",
+ "healthy_threshold": "Healthy threshold of the health checking.",
+ "interval": "Interval duration of health checking in seconds.",
+ "interval_jitter": "Interval duration threshold of the health checking in seconds.",
+ "timeout": "Active health checking timeout duration in seconds.",
+ "unhealthy_threshold": "Unhealthy threshold of the health checking.",
+ "target_pools.name": "Target pool name.",
+ "target_port": "Identical port number where each target listens for traffic.",
+ "targets": "List of all targets which will be used in the pool. Limited to 1000.",
+ "targets.display_name": "Target display name",
+ "ip": "Target IP",
+ "region": "The resource region. If not defined, the provider region is used.",
}
resp.Schema = schema.Schema{
@@ -222,6 +229,40 @@ func (r *loadBalancerDataSource) Schema(_ context.Context, _ datasource.SchemaRe
Description: descriptions["private_network_only"],
Computed: true,
},
+ "observability": schema.SingleNestedAttribute{
+ Description: descriptions["observability"],
+ Computed: true,
+ Attributes: map[string]schema.Attribute{
+ "logs": schema.SingleNestedAttribute{
+ Description: descriptions["observability_logs"],
+ Computed: true,
+ Attributes: map[string]schema.Attribute{
+ "credentials_ref": schema.StringAttribute{
+ Description: descriptions["observability_logs_credentials_ref"],
+ Computed: true,
+ },
+ "push_url": schema.StringAttribute{
+ Description: descriptions["observability_logs_credentials_ref"],
+ Computed: true,
+ },
+ },
+ },
+ "metrics": schema.SingleNestedAttribute{
+ Description: descriptions["observability_metrics"],
+ Computed: true,
+ Attributes: map[string]schema.Attribute{
+ "credentials_ref": schema.StringAttribute{
+ Description: descriptions["observability_metrics_credentials_ref"],
+ Computed: true,
+ },
+ "push_url": schema.StringAttribute{
+ Description: descriptions["observability_metrics_credentials_ref"],
+ Computed: true,
+ },
+ },
+ },
+ },
+ },
},
},
"private_address": schema.StringAttribute{
diff --git a/stackit/internal/services/loadbalancer/loadbalancer/resource.go b/stackit/internal/services/loadbalancer/loadbalancer/resource.go
index 3496b709..9134e5d6 100644
--- a/stackit/internal/services/loadbalancer/loadbalancer/resource.go
+++ b/stackit/internal/services/loadbalancer/loadbalancer/resource.go
@@ -100,14 +100,36 @@ var networkTypes = map[string]attr.Type{
// Struct corresponding to Model.Options
type options struct {
- ACL types.Set `tfsdk:"acl"`
- PrivateNetworkOnly types.Bool `tfsdk:"private_network_only"`
+ ACL types.Set `tfsdk:"acl"`
+ PrivateNetworkOnly types.Bool `tfsdk:"private_network_only"`
+ Observability types.Object `tfsdk:"observability"`
}
// Types corresponding to options
var optionsTypes = map[string]attr.Type{
"acl": types.SetType{ElemType: types.StringType},
"private_network_only": types.BoolType,
+ "observability": types.ObjectType{AttrTypes: observabilityTypes},
+}
+
+type observability struct {
+ Logs types.Object `tfsdk:"logs"`
+ Metrics types.Object `tfsdk:"metrics"`
+}
+
+var observabilityTypes = map[string]attr.Type{
+ "logs": types.ObjectType{AttrTypes: observabilityOptionTypes},
+ "metrics": types.ObjectType{AttrTypes: observabilityOptionTypes},
+}
+
+type observabilityOption struct {
+ CredentialsRef types.String `tfsdk:"credentials_ref"`
+ PushUrl types.String `tfsdk:"push_url"`
+}
+
+var observabilityOptionTypes = map[string]attr.Type{
+ "credentials_ref": types.StringType,
+ "push_url": types.StringType,
}
// Struct corresponding to Model.TargetPools[i]
@@ -257,38 +279,45 @@ func (r *loadBalancerResource) Schema(_ context.Context, _ resource.SchemaReques
roleOptions := []string{"ROLE_UNSPECIFIED", "ROLE_LISTENERS_AND_TARGETS", "ROLE_LISTENERS", "ROLE_TARGETS"}
descriptions := map[string]string{
- "main": "Load Balancer resource schema.",
- "id": "Terraform's internal resource ID. It is structured as \"`project_id`\",\"region\",\"`name`\".",
- "project_id": "STACKIT project ID to which the Load Balancer is associated.",
- "external_address": "External Load Balancer IP address where this Load Balancer is exposed.",
- "listeners": "List of all listeners which will accept traffic. Limited to 20.",
- "port": "Port number where we listen for traffic.",
- "protocol": "Protocol is the highest network protocol we understand to load balance. " + utils.SupportedValuesDocumentation(protocolOptions),
- "target_pool": "Reference target pool by target pool name.",
- "name": "Load balancer name.",
- "networks": "List of networks that listeners and targets reside in.",
- "network_id": "Openstack network ID.",
- "role": "The role defines how the load balancer is using the network. " + utils.SupportedValuesDocumentation(roleOptions),
- "options": "Defines any optional functionality you want to have enabled on your load balancer.",
- "acl": "Load Balancer is accessible only from an IP address in this range.",
- "private_network_only": "If true, Load Balancer is accessible only via a private network IP address.",
- "session_persistence": "Here you can setup various session persistence options, so far only \"`use_source_ip_address`\" is supported.",
- "use_source_ip_address": "If true then all connections from one source IP address are redirected to the same target. This setting changes the load balancing algorithm to Maglev.",
- "server_name_indicators": "A list of domain names to match in order to pass TLS traffic to the target pool in the current listener",
- "server_name_indicators.name": "A domain name to match in order to pass TLS traffic to the target pool in the current listener",
- "private_address": "Transient private Load Balancer IP address. It can change any time.",
- "target_pools": "List of all target pools which will be used in the Load Balancer. Limited to 20.",
- "healthy_threshold": "Healthy threshold of the health checking.",
- "interval": "Interval duration of health checking in seconds.",
- "interval_jitter": "Interval duration threshold of the health checking in seconds.",
- "timeout": "Active health checking timeout duration in seconds.",
- "unhealthy_threshold": "Unhealthy threshold of the health checking.",
- "target_pools.name": "Target pool name.",
- "target_port": "Identical port number where each target listens for traffic.",
- "targets": "List of all targets which will be used in the pool. Limited to 1000.",
- "targets.display_name": "Target display name",
- "ip": "Target IP",
- "region": "The resource region. If not defined, the provider region is used.",
+ "main": "Load Balancer resource schema.",
+ "id": "Terraform's internal resource ID. It is structured as \"`project_id`\",\"region\",\"`name`\".",
+ "project_id": "STACKIT project ID to which the Load Balancer is associated.",
+ "external_address": "External Load Balancer IP address where this Load Balancer is exposed.",
+ "listeners": "List of all listeners which will accept traffic. Limited to 20.",
+ "port": "Port number where we listen for traffic.",
+ "protocol": "Protocol is the highest network protocol we understand to load balance. " + utils.SupportedValuesDocumentation(protocolOptions),
+ "target_pool": "Reference target pool by target pool name.",
+ "name": "Load balancer name.",
+ "networks": "List of networks that listeners and targets reside in.",
+ "network_id": "Openstack network ID.",
+ "role": "The role defines how the load balancer is using the network. " + utils.SupportedValuesDocumentation(roleOptions),
+ "observability": "We offer Load Balancer metrics observability via ARGUS or external solutions. Not changeable after creation.",
+ "observability_logs": "Observability logs configuration. Not changeable after creation.",
+ "observability_logs_credentials_ref": "Credentials reference for logs. Not changeable after creation.",
+ "observability_logs_push_url": "The ARGUS/Loki remote write Push URL to ship the logs to. Not changeable after creation.",
+ "observability_metrics": "Observability metrics configuration. Not changeable after creation.",
+ "observability_metrics_credentials_ref": "Credentials reference for metrics. Not changeable after creation.",
+ "observability_metrics_push_url": "The ARGUS/Prometheus remote write Push URL to ship the metrics to. Not changeable after creation.",
+ "options": "Defines any optional functionality you want to have enabled on your load balancer.",
+ "acl": "Load Balancer is accessible only from an IP address in this range.",
+ "private_network_only": "If true, Load Balancer is accessible only via a private network IP address.",
+ "session_persistence": "Here you can setup various session persistence options, so far only \"`use_source_ip_address`\" is supported.",
+ "use_source_ip_address": "If true then all connections from one source IP address are redirected to the same target. This setting changes the load balancing algorithm to Maglev.",
+ "server_name_indicators": "A list of domain names to match in order to pass TLS traffic to the target pool in the current listener",
+ "server_name_indicators.name": "A domain name to match in order to pass TLS traffic to the target pool in the current listener",
+ "private_address": "Transient private Load Balancer IP address. It can change any time.",
+ "target_pools": "List of all target pools which will be used in the Load Balancer. Limited to 20.",
+ "healthy_threshold": "Healthy threshold of the health checking.",
+ "interval": "Interval duration of health checking in seconds.",
+ "interval_jitter": "Interval duration threshold of the health checking in seconds.",
+ "timeout": "Active health checking timeout duration in seconds.",
+ "unhealthy_threshold": "Unhealthy threshold of the health checking.",
+ "target_pools.name": "Target pool name.",
+ "target_port": "Identical port number where each target listens for traffic.",
+ "targets": "List of all targets which will be used in the pool. Limited to 1000.",
+ "targets.display_name": "Target display name",
+ "ip": "Target IP",
+ "region": "The resource region. If not defined, the provider region is used.",
}
resp.Schema = schema.Schema{
@@ -466,6 +495,51 @@ The example below creates the supporting infrastructure using the STACKIT Terraf
boolplanmodifier.UseStateForUnknown(),
},
},
+ "observability": schema.SingleNestedAttribute{
+ Description: descriptions["observability"],
+ Optional: true,
+ Computed: true,
+ PlanModifiers: []planmodifier.Object{
+ // API docs says observability options are not changeable after creation
+ objectplanmodifier.RequiresReplace(),
+ },
+ Attributes: map[string]schema.Attribute{
+ "logs": schema.SingleNestedAttribute{
+ Description: descriptions["observability_logs"],
+ Optional: true,
+ Computed: true,
+ Attributes: map[string]schema.Attribute{
+ "credentials_ref": schema.StringAttribute{
+ Description: descriptions["observability_logs_credentials_ref"],
+ Optional: true,
+ Computed: true,
+ },
+ "push_url": schema.StringAttribute{
+ Description: descriptions["observability_logs_credentials_ref"],
+ Optional: true,
+ Computed: true,
+ },
+ },
+ },
+ "metrics": schema.SingleNestedAttribute{
+ Description: descriptions["observability_metrics"],
+ Optional: true,
+ Computed: true,
+ Attributes: map[string]schema.Attribute{
+ "credentials_ref": schema.StringAttribute{
+ Description: descriptions["observability_metrics_credentials_ref"],
+ Optional: true,
+ Computed: true,
+ },
+ "push_url": schema.StringAttribute{
+ Description: descriptions["observability_metrics_credentials_ref"],
+ Optional: true,
+ Computed: true,
+ },
+ },
+ },
+ },
+ },
},
},
"private_address": schema.StringAttribute{
@@ -903,6 +977,7 @@ func toOptionsPayload(ctx context.Context, model *Model) (*loadbalancer.LoadBala
if model.Options.IsNull() || model.Options.IsUnknown() {
return &loadbalancer.LoadBalancerOptions{
AccessControl: &loadbalancer.LoadbalancerOptionAccessControl{},
+ Observability: &loadbalancer.LoadbalancerOptionObservability{},
}, nil
}
@@ -922,8 +997,40 @@ func toOptionsPayload(ctx context.Context, model *Model) (*loadbalancer.LoadBala
accessControlPayload.AllowedSourceRanges = &aclModel
}
+ observabilityPayload := &loadbalancer.LoadbalancerOptionObservability{}
+ if !(optionsModel.Observability.IsNull() || optionsModel.Observability.IsUnknown()) {
+ observabilityModel := observability{}
+ diags := optionsModel.Observability.As(ctx, &observabilityModel, basetypes.ObjectAsOptions{})
+ if diags.HasError() {
+ return nil, fmt.Errorf("converting observability: %w", core.DiagsToError(diags))
+ }
+
+ // observability logs
+ observabilityLogsModel := observabilityOption{}
+ diags = observabilityModel.Logs.As(ctx, &observabilityLogsModel, basetypes.ObjectAsOptions{})
+ if diags.HasError() {
+ return nil, fmt.Errorf("converting observability logs: %w", core.DiagsToError(diags))
+ }
+ observabilityPayload.Logs = &loadbalancer.LoadbalancerOptionLogs{
+ CredentialsRef: observabilityLogsModel.CredentialsRef.ValueStringPointer(),
+ PushUrl: observabilityLogsModel.PushUrl.ValueStringPointer(),
+ }
+
+ // observability metrics
+ observabilityMetricsModel := observabilityOption{}
+ diags = observabilityModel.Metrics.As(ctx, &observabilityMetricsModel, basetypes.ObjectAsOptions{})
+ if diags.HasError() {
+ return nil, fmt.Errorf("converting observability metrics: %w", core.DiagsToError(diags))
+ }
+ observabilityPayload.Metrics = &loadbalancer.LoadbalancerOptionMetrics{
+ CredentialsRef: observabilityMetricsModel.CredentialsRef.ValueStringPointer(),
+ PushUrl: observabilityMetricsModel.PushUrl.ValueStringPointer(),
+ }
+ }
+
payload := loadbalancer.LoadBalancerOptions{
AccessControl: accessControlPayload,
+ Observability: observabilityPayload,
PrivateNetworkOnly: conversion.BoolValueToPointer(optionsModel.PrivateNetworkOnly),
}
@@ -1249,6 +1356,42 @@ func mapOptions(ctx context.Context, loadBalancerResp *loadbalancer.LoadBalancer
return fmt.Errorf("mapping field ACL: %w", err)
}
+ observabilityLogsMap := map[string]attr.Value{
+ "credentials_ref": types.StringNull(),
+ "push_url": types.StringNull(),
+ }
+ if loadBalancerResp.Options.HasObservability() && loadBalancerResp.Options.Observability.HasLogs() {
+ observabilityLogsMap["credentials_ref"] = types.StringPointerValue(loadBalancerResp.Options.Observability.Logs.CredentialsRef)
+ observabilityLogsMap["push_url"] = types.StringPointerValue(loadBalancerResp.Options.Observability.Logs.PushUrl)
+ }
+ observabilityLogsTF, diags := types.ObjectValue(observabilityOptionTypes, observabilityLogsMap)
+ if diags.HasError() {
+ return core.DiagsToError(diags)
+ }
+
+ observabilityMetricsMap := map[string]attr.Value{
+ "credentials_ref": types.StringNull(),
+ "push_url": types.StringNull(),
+ }
+ if loadBalancerResp.Options.HasObservability() && loadBalancerResp.Options.Observability.HasMetrics() {
+ observabilityMetricsMap["credentials_ref"] = types.StringPointerValue(loadBalancerResp.Options.Observability.Metrics.CredentialsRef)
+ observabilityMetricsMap["push_url"] = types.StringPointerValue(loadBalancerResp.Options.Observability.Metrics.PushUrl)
+ }
+ observabilityMetricsTF, diags := types.ObjectValue(observabilityOptionTypes, observabilityMetricsMap)
+ if diags.HasError() {
+ return core.DiagsToError(diags)
+ }
+
+ observabilityMap := map[string]attr.Value{
+ "logs": observabilityLogsTF,
+ "metrics": observabilityMetricsTF,
+ }
+ observabilityTF, diags := types.ObjectValue(observabilityTypes, observabilityMap)
+ if diags.HasError() {
+ return core.DiagsToError(diags)
+ }
+ optionsMap["observability"] = observabilityTF
+
optionsTF, diags := types.ObjectValue(optionsTypes, optionsMap)
if diags.HasError() {
return core.DiagsToError(diags)
diff --git a/stackit/internal/services/loadbalancer/loadbalancer/resource_test.go b/stackit/internal/services/loadbalancer/loadbalancer/resource_test.go
index 95a85018..cb41abce 100644
--- a/stackit/internal/services/loadbalancer/loadbalancer/resource_test.go
+++ b/stackit/internal/services/loadbalancer/loadbalancer/resource_test.go
@@ -32,6 +32,7 @@ func TestToCreatePayload(t *testing.T) {
AllowedSourceRanges: nil,
},
PrivateNetworkOnly: nil,
+ Observability: &loadbalancer.LoadbalancerOptionObservability{},
},
TargetPools: nil,
},
@@ -76,6 +77,16 @@ func TestToCreatePayload(t *testing.T) {
types.StringType,
[]attr.Value{types.StringValue("cidr")}),
"private_network_only": types.BoolValue(true),
+ "observability": types.ObjectValueMust(observabilityTypes, map[string]attr.Value{
+ "logs": types.ObjectValueMust(observabilityOptionTypes, map[string]attr.Value{
+ "credentials_ref": types.StringValue("logs-credentials_ref"),
+ "push_url": types.StringValue("logs-push_url"),
+ }),
+ "metrics": types.ObjectValueMust(observabilityOptionTypes, map[string]attr.Value{
+ "credentials_ref": types.StringValue("metrics-credentials_ref"),
+ "push_url": types.StringValue("metrics-push_url"),
+ }),
+ }),
},
),
TargetPools: types.ListValueMust(types.ObjectType{AttrTypes: targetPoolTypes}, []attr.Value{
@@ -132,6 +143,16 @@ func TestToCreatePayload(t *testing.T) {
AllowedSourceRanges: &[]string{"cidr"},
},
PrivateNetworkOnly: utils.Ptr(true),
+ Observability: &loadbalancer.LoadbalancerOptionObservability{
+ Logs: &loadbalancer.LoadbalancerOptionLogs{
+ CredentialsRef: utils.Ptr("logs-credentials_ref"),
+ PushUrl: utils.Ptr("logs-push_url"),
+ },
+ Metrics: &loadbalancer.LoadbalancerOptionMetrics{
+ CredentialsRef: utils.Ptr("metrics-credentials_ref"),
+ PushUrl: utils.Ptr("metrics-push_url"),
+ },
+ },
},
TargetPools: &[]loadbalancer.TargetPool{
{
@@ -290,6 +311,10 @@ func TestMapFields(t *testing.T) {
AllowedSourceRanges: nil,
},
PrivateNetworkOnly: nil,
+ Observability: &loadbalancer.LoadbalancerOptionObservability{
+ Logs: &loadbalancer.LoadbalancerOptionLogs{},
+ Metrics: &loadbalancer.LoadbalancerOptionMetrics{},
+ },
},
TargetPools: nil,
},
@@ -305,6 +330,16 @@ func TestMapFields(t *testing.T) {
Options: types.ObjectValueMust(optionsTypes, map[string]attr.Value{
"acl": types.SetNull(types.StringType),
"private_network_only": types.BoolNull(),
+ "observability": types.ObjectValueMust(observabilityTypes, map[string]attr.Value{
+ "logs": types.ObjectValueMust(observabilityOptionTypes, map[string]attr.Value{
+ "credentials_ref": types.StringNull(),
+ "push_url": types.StringNull(),
+ }),
+ "metrics": types.ObjectValueMust(observabilityOptionTypes, map[string]attr.Value{
+ "credentials_ref": types.StringNull(),
+ "push_url": types.StringNull(),
+ }),
+ }),
}),
PrivateAddress: types.StringNull(),
TargetPools: types.ListNull(types.ObjectType{AttrTypes: targetPoolTypes}),
@@ -342,6 +377,16 @@ func TestMapFields(t *testing.T) {
}),
Options: utils.Ptr(loadbalancer.LoadBalancerOptions{
PrivateNetworkOnly: utils.Ptr(true),
+ Observability: &loadbalancer.LoadbalancerOptionObservability{
+ Logs: &loadbalancer.LoadbalancerOptionLogs{
+ CredentialsRef: utils.Ptr("logs_credentials_ref"),
+ PushUrl: utils.Ptr("logs_push_url"),
+ },
+ Metrics: &loadbalancer.LoadbalancerOptionMetrics{
+ CredentialsRef: utils.Ptr("metrics_credentials_ref"),
+ PushUrl: utils.Ptr("metrics_push_url"),
+ },
+ },
}),
TargetPools: utils.Ptr([]loadbalancer.TargetPool{
{
@@ -405,6 +450,16 @@ func TestMapFields(t *testing.T) {
map[string]attr.Value{
"private_network_only": types.BoolValue(true),
"acl": types.SetNull(types.StringType),
+ "observability": types.ObjectValueMust(observabilityTypes, map[string]attr.Value{
+ "logs": types.ObjectValueMust(observabilityOptionTypes, map[string]attr.Value{
+ "credentials_ref": types.StringValue("logs_credentials_ref"),
+ "push_url": types.StringValue("logs_push_url"),
+ }),
+ "metrics": types.ObjectValueMust(observabilityOptionTypes, map[string]attr.Value{
+ "credentials_ref": types.StringValue("metrics_credentials_ref"),
+ "push_url": types.StringValue("metrics_push_url"),
+ }),
+ }),
},
),
TargetPools: types.ListValueMust(types.ObjectType{AttrTypes: targetPoolTypes}, []attr.Value{
@@ -466,6 +521,16 @@ func TestMapFields(t *testing.T) {
AllowedSourceRanges: utils.Ptr([]string{"cidr"}),
},
PrivateNetworkOnly: nil, // API sets this to nil if it's false in the request
+ Observability: &loadbalancer.LoadbalancerOptionObservability{
+ Logs: &loadbalancer.LoadbalancerOptionLogs{
+ CredentialsRef: utils.Ptr("logs_credentials_ref"),
+ PushUrl: utils.Ptr("logs_push_url"),
+ },
+ Metrics: &loadbalancer.LoadbalancerOptionMetrics{
+ CredentialsRef: utils.Ptr("metrics_credentials_ref"),
+ PushUrl: utils.Ptr("metrics_push_url"),
+ },
+ },
}),
TargetPools: utils.Ptr([]loadbalancer.TargetPool{
{
@@ -531,6 +596,16 @@ func TestMapFields(t *testing.T) {
types.StringType,
[]attr.Value{types.StringValue("cidr")}),
"private_network_only": types.BoolValue(false),
+ "observability": types.ObjectValueMust(observabilityTypes, map[string]attr.Value{
+ "logs": types.ObjectValueMust(observabilityOptionTypes, map[string]attr.Value{
+ "credentials_ref": types.StringValue("logs_credentials_ref"),
+ "push_url": types.StringValue("logs_push_url"),
+ }),
+ "metrics": types.ObjectValueMust(observabilityOptionTypes, map[string]attr.Value{
+ "credentials_ref": types.StringValue("metrics_credentials_ref"),
+ "push_url": types.StringValue("metrics_push_url"),
+ }),
+ }),
},
),
TargetPools: types.ListValueMust(types.ObjectType{AttrTypes: targetPoolTypes}, []attr.Value{
@@ -585,6 +660,16 @@ func TestMapFields(t *testing.T) {
model.Options = types.ObjectValueMust(optionsTypes, map[string]attr.Value{
"private_network_only": types.BoolValue(*tt.modelPrivateNetworkOnly),
"acl": types.SetNull(types.StringType),
+ "observability": types.ObjectValueMust(observabilityTypes, map[string]attr.Value{
+ "logs": types.ObjectValueMust(observabilityOptionTypes, map[string]attr.Value{
+ "credentials_ref": types.StringNull(),
+ "push_url": types.StringNull(),
+ }),
+ "metrics": types.ObjectValueMust(observabilityOptionTypes, map[string]attr.Value{
+ "credentials_ref": types.StringNull(),
+ "push_url": types.StringNull(),
+ }),
+ }),
})
}
err := mapFields(context.Background(), tt.input, model, tt.region)
diff --git a/stackit/internal/services/loadbalancer/loadbalancer_acc_test.go b/stackit/internal/services/loadbalancer/loadbalancer_acc_test.go
index d99854b7..4fe3d9e0 100644
--- a/stackit/internal/services/loadbalancer/loadbalancer_acc_test.go
+++ b/stackit/internal/services/loadbalancer/loadbalancer_acc_test.go
@@ -67,6 +67,15 @@ var testConfigVarsMax = config.Variables{
"use_source_ip_address": config.StringVariable("true"),
"private_network_only": config.StringVariable("false"),
"acl": config.StringVariable("192.168.0.0/24"),
+
+ "observability_logs_push_url": config.StringVariable("https://logs.observability.dummy.stackit.cloud"),
+ "observability_metrics_push_url": config.StringVariable("https://metrics.observability.dummy.stackit.cloud"),
+ "observability_credential_logs_name": config.StringVariable(fmt.Sprintf("tf-acc-l%s", acctest.RandStringFromCharSet(7, acctest.CharSetAlphaNum))),
+ "observability_credential_logs_username": config.StringVariable("obs-cred-logs-username"),
+ "observability_credential_logs_password": config.StringVariable("obs-cred-logs-password"),
+ "observability_credential_metrics_name": config.StringVariable(fmt.Sprintf("tf-acc-m%s", acctest.RandStringFromCharSet(7, acctest.CharSetAlphaNum))),
+ "observability_credential_metrics_username": config.StringVariable("obs-cred-metrics-username"),
+ "observability_credential_metrics_password": config.StringVariable("obs-cred-metrics-password"),
}
func configVarsMinUpdated() config.Variables {
@@ -93,7 +102,7 @@ func TestAccLoadBalancerResourceMin(t *testing.T) {
ConfigVariables: testConfigVarsMin,
Config: testutil.LoadBalancerProviderConfig() + resourceMinConfig,
Check: resource.ComposeAggregateTestCheckFunc(
- // Load balancer instance
+ // Load balancer instance resource
resource.TestCheckResourceAttr("stackit_loadbalancer.loadbalancer", "project_id", testutil.ConvertConfigVariable(testConfigVarsMin["project_id"])),
resource.TestCheckResourceAttr("stackit_loadbalancer.loadbalancer", "name", testutil.ConvertConfigVariable(testConfigVarsMin["loadbalancer_name"])),
resource.TestCheckResourceAttr("stackit_loadbalancer.loadbalancer", "target_pools.0.name", testutil.ConvertConfigVariable(testConfigVarsMin["target_pool_name"])),
@@ -107,12 +116,17 @@ func TestAccLoadBalancerResourceMin(t *testing.T) {
resource.TestCheckResourceAttrSet("stackit_loadbalancer.loadbalancer", "networks.0.network_id"),
resource.TestCheckResourceAttr("stackit_loadbalancer.loadbalancer", "networks.0.role", testutil.ConvertConfigVariable(testConfigVarsMin["network_role"])),
resource.TestCheckResourceAttrSet("stackit_loadbalancer.loadbalancer", "external_address"),
+ resource.TestCheckNoResourceAttr("stackit_loadbalancer.loadbalancer", "options.observability.logs.credentials_ref"),
+ resource.TestCheckNoResourceAttr("stackit_loadbalancer.loadbalancer", "options.observability.logs.push_url"),
+ resource.TestCheckNoResourceAttr("stackit_loadbalancer.loadbalancer", "options.observability.metrics.credentials_ref"),
+ resource.TestCheckNoResourceAttr("stackit_loadbalancer.loadbalancer", "options.observability.metrics.push_url"),
- // observability credentials
+ // Loadbalancer observability credentials resource
resource.TestCheckResourceAttr("stackit_loadbalancer_observability_credential.obs_credential", "project_id", testutil.ConvertConfigVariable(testConfigVarsMin["project_id"])),
resource.TestCheckResourceAttr("stackit_loadbalancer_observability_credential.obs_credential", "display_name", testutil.ConvertConfigVariable(testConfigVarsMin["obs_display_name"])),
resource.TestCheckResourceAttr("stackit_loadbalancer_observability_credential.obs_credential", "username", testutil.ConvertConfigVariable(testConfigVarsMin["obs_username"])),
resource.TestCheckResourceAttr("stackit_loadbalancer_observability_credential.obs_credential", "password", testutil.ConvertConfigVariable(testConfigVarsMin["obs_password"])),
+ resource.TestCheckResourceAttrSet("stackit_loadbalancer_observability_credential.obs_credential", "credentials_ref"),
),
},
// Data source
@@ -151,6 +165,10 @@ func TestAccLoadBalancerResourceMin(t *testing.T) {
resource.TestCheckResourceAttrSet("data.stackit_loadbalancer.loadbalancer", "networks.0.network_id"),
resource.TestCheckResourceAttr("data.stackit_loadbalancer.loadbalancer", "networks.0.role", testutil.ConvertConfigVariable(testConfigVarsMin["network_role"])),
resource.TestCheckResourceAttrSet("data.stackit_loadbalancer.loadbalancer", "external_address"),
+ resource.TestCheckNoResourceAttr("data.stackit_loadbalancer.loadbalancer", "options.observability.logs.credentials_ref"),
+ resource.TestCheckNoResourceAttr("data.stackit_loadbalancer.loadbalancer", "options.observability.logs.push_url"),
+ resource.TestCheckNoResourceAttr("data.stackit_loadbalancer.loadbalancer", "options.observability.metrics.credentials_ref"),
+ resource.TestCheckNoResourceAttr("data.stackit_loadbalancer.loadbalancer", "options.observability.metrics.push_url"),
)},
// Import
{
@@ -197,7 +215,7 @@ func TestAccLoadBalancerResourceMax(t *testing.T) {
ConfigVariables: testConfigVarsMax,
Config: testutil.LoadBalancerProviderConfig() + resourceMaxConfig,
Check: resource.ComposeAggregateTestCheckFunc(
- // Load balancer instance
+ // Load balancer instance resource
resource.TestCheckResourceAttr("stackit_loadbalancer.loadbalancer", "project_id", testutil.ConvertConfigVariable(testConfigVarsMax["project_id"])),
resource.TestCheckResourceAttr("stackit_loadbalancer.loadbalancer", "name", testutil.ConvertConfigVariable(testConfigVarsMax["loadbalancer_name"])),
resource.TestCheckResourceAttr("stackit_loadbalancer.loadbalancer", "target_pools.0.name", testutil.ConvertConfigVariable(testConfigVarsMax["target_pool_name"])),
@@ -222,6 +240,25 @@ func TestAccLoadBalancerResourceMax(t *testing.T) {
resource.TestCheckResourceAttr("stackit_loadbalancer.loadbalancer", "target_pools.0.session_persistence.use_source_ip_address", testutil.ConvertConfigVariable(testConfigVarsMax["use_source_ip_address"])),
resource.TestCheckResourceAttr("stackit_loadbalancer.loadbalancer", "options.private_network_only", testutil.ConvertConfigVariable(testConfigVarsMax["private_network_only"])),
resource.TestCheckResourceAttr("stackit_loadbalancer.loadbalancer", "options.acl.0", testutil.ConvertConfigVariable(testConfigVarsMax["acl"])),
+
+ resource.TestCheckResourceAttrSet("stackit_loadbalancer.loadbalancer", "options.observability.logs.credentials_ref"),
+ resource.TestCheckResourceAttrPair("stackit_loadbalancer_observability_credential.logs", "credentials_ref", "stackit_loadbalancer.loadbalancer", "options.observability.logs.credentials_ref"),
+ resource.TestCheckResourceAttr("stackit_loadbalancer.loadbalancer", "options.observability.logs.push_url", testutil.ConvertConfigVariable(testConfigVarsMax["observability_logs_push_url"])),
+ resource.TestCheckResourceAttrSet("stackit_loadbalancer.loadbalancer", "options.observability.metrics.credentials_ref"),
+ resource.TestCheckResourceAttrPair("stackit_loadbalancer_observability_credential.metrics", "credentials_ref", "stackit_loadbalancer.loadbalancer", "options.observability.metrics.credentials_ref"),
+ resource.TestCheckResourceAttr("stackit_loadbalancer.loadbalancer", "options.observability.metrics.push_url", testutil.ConvertConfigVariable(testConfigVarsMax["observability_metrics_push_url"])),
+
+ // Loadbalancer observability credential resource
+ resource.TestCheckResourceAttr("stackit_loadbalancer_observability_credential.logs", "project_id", testutil.ConvertConfigVariable(testConfigVarsMin["project_id"])),
+ resource.TestCheckResourceAttr("stackit_loadbalancer_observability_credential.logs", "display_name", testutil.ConvertConfigVariable(testConfigVarsMax["observability_credential_logs_name"])),
+ resource.TestCheckResourceAttr("stackit_loadbalancer_observability_credential.logs", "username", testutil.ConvertConfigVariable(testConfigVarsMax["observability_credential_logs_username"])),
+ resource.TestCheckResourceAttr("stackit_loadbalancer_observability_credential.logs", "password", testutil.ConvertConfigVariable(testConfigVarsMax["observability_credential_logs_password"])),
+ resource.TestCheckResourceAttrSet("stackit_loadbalancer_observability_credential.logs", "credentials_ref"),
+ resource.TestCheckResourceAttr("stackit_loadbalancer_observability_credential.metrics", "project_id", testutil.ConvertConfigVariable(testConfigVarsMin["project_id"])),
+ resource.TestCheckResourceAttr("stackit_loadbalancer_observability_credential.metrics", "display_name", testutil.ConvertConfigVariable(testConfigVarsMax["observability_credential_metrics_name"])),
+ resource.TestCheckResourceAttr("stackit_loadbalancer_observability_credential.metrics", "username", testutil.ConvertConfigVariable(testConfigVarsMax["observability_credential_metrics_username"])),
+ resource.TestCheckResourceAttr("stackit_loadbalancer_observability_credential.metrics", "password", testutil.ConvertConfigVariable(testConfigVarsMax["observability_credential_metrics_password"])),
+ resource.TestCheckResourceAttrSet("stackit_loadbalancer_observability_credential.metrics", "credentials_ref"),
),
},
// Data source
@@ -271,6 +308,13 @@ func TestAccLoadBalancerResourceMax(t *testing.T) {
resource.TestCheckResourceAttr("data.stackit_loadbalancer.loadbalancer", "target_pools.0.session_persistence.use_source_ip_address", testutil.ConvertConfigVariable(testConfigVarsMax["use_source_ip_address"])),
resource.TestCheckResourceAttr("data.stackit_loadbalancer.loadbalancer", "options.acl.0", testutil.ConvertConfigVariable(testConfigVarsMax["acl"])),
+
+ resource.TestCheckResourceAttrSet("data.stackit_loadbalancer.loadbalancer", "options.observability.logs.credentials_ref"),
+ resource.TestCheckResourceAttrPair("stackit_loadbalancer_observability_credential.logs", "credentials_ref", "data.stackit_loadbalancer.loadbalancer", "options.observability.logs.credentials_ref"),
+ resource.TestCheckResourceAttr("data.stackit_loadbalancer.loadbalancer", "options.observability.logs.push_url", testutil.ConvertConfigVariable(testConfigVarsMax["observability_logs_push_url"])),
+ resource.TestCheckResourceAttrSet("data.stackit_loadbalancer.loadbalancer", "options.observability.metrics.credentials_ref"),
+ resource.TestCheckResourceAttrPair("stackit_loadbalancer_observability_credential.metrics", "credentials_ref", "data.stackit_loadbalancer.loadbalancer", "options.observability.metrics.credentials_ref"),
+ resource.TestCheckResourceAttr("data.stackit_loadbalancer.loadbalancer", "options.observability.metrics.push_url", testutil.ConvertConfigVariable(testConfigVarsMax["observability_metrics_push_url"])),
)},
// Import
{
diff --git a/stackit/internal/services/loadbalancer/testfiles/resource-max.tf b/stackit/internal/services/loadbalancer/testfiles/resource-max.tf
index 4fc7aefb..2b9ba616 100644
--- a/stackit/internal/services/loadbalancer/testfiles/resource-max.tf
+++ b/stackit/internal/services/loadbalancer/testfiles/resource-max.tf
@@ -22,6 +22,15 @@ variable "use_source_ip_address" {}
variable "private_network_only" {}
variable "acl" {}
+variable "observability_logs_push_url" {}
+variable "observability_metrics_push_url" {}
+variable "observability_credential_logs_name" {}
+variable "observability_credential_logs_username" {}
+variable "observability_credential_logs_password" {}
+variable "observability_credential_metrics_name" {}
+variable "observability_credential_metrics_username" {}
+variable "observability_credential_metrics_password" {}
+
resource "stackit_network" "network" {
project_id = var.project_id
name = var.network_name
@@ -108,6 +117,31 @@ resource "stackit_loadbalancer" "loadbalancer" {
options = {
private_network_only = var.private_network_only
acl = [var.acl]
+ observability = {
+ logs = {
+ credentials_ref = stackit_loadbalancer_observability_credential.logs.credentials_ref
+ push_url = var.observability_logs_push_url
+ }
+ metrics = {
+ credentials_ref = stackit_loadbalancer_observability_credential.metrics.credentials_ref
+ push_url = var.observability_metrics_push_url
+ }
+ }
}
external_address = stackit_public_ip.public_ip.ip
}
+
+resource "stackit_loadbalancer_observability_credential" "logs" {
+ project_id = var.project_id
+ display_name = var.observability_credential_logs_name
+ username = var.observability_credential_logs_username
+ password = var.observability_credential_logs_password
+}
+
+resource "stackit_loadbalancer_observability_credential" "metrics" {
+ project_id = var.project_id
+ display_name = var.observability_credential_metrics_name
+ username = var.observability_credential_metrics_username
+ password = var.observability_credential_metrics_password
+}
+
diff --git a/templates/guides/using_loadbalancer_with_observability.md.tmpl b/templates/guides/using_loadbalancer_with_observability.md.tmpl
new file mode 100644
index 00000000..106d8797
--- /dev/null
+++ b/templates/guides/using_loadbalancer_with_observability.md.tmpl
@@ -0,0 +1,167 @@
+---
+page_title: "Using the STACKIT Loadbalancer together with STACKIT Observability"
+---
+# Using the STACKIT Loadbalancer together with STACKIT Observability
+
+## Overview
+
+This guide explains how to configure the STACKIT Loadbalancer product to send metrics and logs to a STACKIT Observability instance.
+
+1. **Set Up Providers**
+
+ Begin by configuring the STACKIT provider to connect to the STACKIT services.
+
+ ```hcl
+ provider "stackit" {
+ default_region = "eu01"
+ }
+ ```
+
+2. **Create an Observability instance**
+
+ Establish a STACKIT Observability instance and its credentials.
+
+ ```hcl
+ resource "stackit_observability_instance" "observability01" {
+ project_id = var.project_id_prod
+ name = "example-instance"
+ plan_name = "Observability-Monitoring-Medium-EU01"
+ acl = ["0.0.0.0/0"]
+ metrics_retention_days = 30
+ metrics_retention_days_5m_downsampling = 10
+ metrics_retention_days_1h_downsampling = 5
+ }
+
+ resource "stackit_observability_credential" "observability01-credential" {
+ project_id = var.project_id_prod
+ instance_id = stackit_observability_instance.observability01.instance_id
+ }
+ ```
+
+3. **Create STACKIT Loadbalancer credentials reference**
+
+ Create a STACKIT Loadbalancer credentials which will be used in the STACKIT Loadbalancer resource as a reference.
+
+ ```hcl
+ resource "stackit_loadbalancer_observability_credential" "example" {
+ project_id = var.project_id_prod
+ display_name = "example-credentials"
+ username = stackit_observability_credential.observability01-credential.username
+ password = stackit_observability_credential.observability01-credential.password
+ }
+ ```
+
+4. **Create the STACKIT Loadbalancer**
+
+ ```hcl
+ # Create a network
+ resource "stackit_network" "example_network" {
+ project_id = var.project_id_prod
+ name = "example-network"
+ ipv4_nameservers = ["8.8.8.8"]
+ ipv4_prefix = "192.168.0.0/25"
+ labels = {
+ "key" = "value"
+ }
+ routed = true
+ }
+
+ # Create a network interface
+ resource "stackit_network_interface" "nic" {
+ project_id = var.project_id_prod
+ network_id = stackit_network.example_network.network_id
+ }
+
+ # Create a public IP for the load balancer
+ resource "stackit_public_ip" "public-ip" {
+ project_id = var.project_id_prod
+ lifecycle {
+ ignore_changes = [network_interface_id]
+ }
+ }
+
+ # Create a key pair for accessing the server instance
+ resource "stackit_key_pair" "keypair" {
+ name = "example-key-pair"
+ # set the path of your public key file here
+ public_key = chomp(file("/home/bob/.ssh/id_ed25519.pub"))
+ }
+
+ # Create a server instance
+ resource "stackit_server" "boot-from-image" {
+ project_id = var.project_id_prod
+ name = "example-server"
+ boot_volume = {
+ size = 64
+ source_type = "image"
+ source_id = "59838a89-51b1-4892-b57f-b3caf598ee2f" // Ubuntu 24.04
+ }
+ availability_zone = "eu01-1"
+ machine_type = "g1.1"
+ keypair_name = stackit_key_pair.keypair.name
+ }
+
+ # Attach the network interface to the server
+ resource "stackit_server_network_interface_attach" "nic-attachment" {
+ project_id = var.project_id_prod
+ server_id = stackit_server.boot-from-image.server_id
+ network_interface_id = stackit_network_interface.nic.network_interface_id
+ }
+
+ # Create a load balancer
+ resource "stackit_loadbalancer" "example" {
+ project_id = var.project_id_prod
+ name = "example-load-balancer"
+ target_pools = [
+ {
+ name = "example-target-pool"
+ target_port = 80
+ targets = [
+ {
+ display_name = stackit_server.boot-from-image.name
+ ip = stackit_network_interface.nic.ipv4
+ }
+ ]
+ active_health_check = {
+ healthy_threshold = 10
+ interval = "3s"
+ interval_jitter = "3s"
+ timeout = "3s"
+ unhealthy_threshold = 10
+ }
+ }
+ ]
+ listeners = [
+ {
+ display_name = "example-listener"
+ port = 80
+ protocol = "PROTOCOL_TCP"
+ target_pool = "example-target-pool"
+ }
+ ]
+ networks = [
+ {
+ network_id = stackit_network.example_network.network_id
+ role = "ROLE_LISTENERS_AND_TARGETS"
+ }
+ ]
+ external_address = stackit_public_ip.public-ip.ip
+ options = {
+ private_network_only = false
+ observability = {
+ logs = {
+ # uses the load balancer credential from the last step
+ credentials_ref = stackit_loadbalancer_observability_credential.example.credentials_ref
+ # uses the observability instance from step 1
+ push_url = stackit_observability_instance.observability01.logs_push_url
+ }
+ metrics = {
+ # uses the load balancer credential from the last step
+ credentials_ref = stackit_loadbalancer_observability_credential.example.credentials_ref
+ # uses the observability instance from step 1
+ push_url = stackit_observability_instance.observability01.metrics_push_url
+ }
+ }
+ }
+ }
+ ```