Skip to content
Open
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
3 changes: 2 additions & 1 deletion docs/data-sources/loadbalancer.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,11 +37,12 @@ data "stackit_loadbalancer" "example" {
- `external_address` (String) External Load Balancer IP address where this Load Balancer is exposed.
- `id` (String) Terraform's internal resource ID. It is structured as "`project_id`","region","`name`".
- `listeners` (Attributes List) List of all listeners which will accept traffic. Limited to 20. (see [below for nested schema](#nestedatt--listeners))
- `load_balancer_security_group_id` (String) The ID of the egress security group assigned to the Load Balancer's internal machines. This ID is essential for allowing traffic from the Load Balancer to targets in different networks or STACKIT Network areas (SNA). To enable this, create a security group rule for your target VMs and set the `remote_security_group_id` of that rule to this value. This is typically used when `disable_security_group_assignment` is set to `true`.
- `networks` (Attributes List) List of networks that listeners and targets reside in. (see [below for nested schema](#nestedatt--networks))
- `options` (Attributes) Defines any optional functionality you want to have enabled on your load balancer. (see [below for nested schema](#nestedatt--options))
- `plan_id` (String) The service plan ID. If not defined, the default service plan is `p10`. Possible values are: `p10`, `p50`, `p250`, `p750`.
- `private_address` (String) Transient private Load Balancer IP address. It can change any time.
- `security_group_id` (String) The ID of the egress security group assigned to the Load Balancer's internal machines. This ID is essential for allowing traffic from the Load Balancer to targets in different networks or STACKIT Network areas (SNA). To enable this, create a security group rule for your target VMs and set the `remote_security_group_id` of that rule to this value. This is typically used when `disable_security_group_assignment` is set to `true`.
- `security_group_id` (String) The ID of the backend security group
- `target_pools` (Attributes List) List of all target pools which will be used in the Load Balancer. Limited to 20. (see [below for nested schema](#nestedatt--target_pools))

<a id="nestedatt--listeners"></a>
Expand Down
16 changes: 11 additions & 5 deletions docs/resources/loadbalancer.md
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,7 @@ resource "stackit_network" "lb_network" {
resource "stackit_network" "target_network" {
project_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
name = "target-network-example"
routed = true
ipv4_prefix = "192.168.10.0/25"
ipv4_nameservers = ["8.8.8.8"]
}
Expand Down Expand Up @@ -181,7 +182,7 @@ resource "stackit_security_group_rule" "allow_lb_ingress" {
}

# This is the crucial link: it allows traffic from the LB's security group.
remote_security_group_id = stackit_loadbalancer.example.security_group_id
remote_security_group_id = stackit_loadbalancer.example.load_balancer_security_group_id

port_range = {
min = 80
Expand All @@ -201,15 +202,19 @@ resource "stackit_server" "example" {
size = 10
}

network_interfaces = [
stackit_network_interface.nic.network_interface_id
]
network_interfaces = [stackit_network_interface.nic.network_interface_id]

}

resource "stackit_network_interface" "nic" {
project_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
network_id = stackit_network.target_network.network_id
security_group_ids = [stackit_security_group.target_sg.security_group_id]
lifecycle {
ignore_changes = [
security_group_ids,
]
}
}
# End of advanced example

Expand Down Expand Up @@ -242,8 +247,9 @@ import {
### Read-Only

- `id` (String) Terraform's internal resource ID. It is structured as "`project_id`","region","`name`".
- `load_balancer_security_group_id` (String) The ID of the egress security group assigned to the Load Balancer's internal machines. This ID is essential for allowing traffic from the Load Balancer to targets in different networks or STACKIT network areas (SNA). To enable this, create a security group rule for your target VMs and set the `remote_security_group_id` of that rule to this value. This is typically used when `disable_security_group_assignment` is set to `true`.
- `private_address` (String) Transient private Load Balancer IP address. It can change any time.
- `security_group_id` (String) The ID of the egress security group assigned to the Load Balancer's internal machines. This ID is essential for allowing traffic from the Load Balancer to targets in different networks or STACKIT network areas (SNA). To enable this, create a security group rule for your target VMs and set the `remote_security_group_id` of that rule to this value. This is typically used when `disable_security_group_assignment` is set to `true`.
- `security_group_id` (String) The ID of the backend security group

<a id="nestedatt--listeners"></a>
### Nested Schema for `listeners`
Expand Down
13 changes: 9 additions & 4 deletions examples/resources/stackit_loadbalancer/resource.tf
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,7 @@ resource "stackit_network" "lb_network" {
resource "stackit_network" "target_network" {
project_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
name = "target-network-example"
routed = true
ipv4_prefix = "192.168.10.0/25"
ipv4_nameservers = ["8.8.8.8"]
}
Expand Down Expand Up @@ -162,7 +163,7 @@ resource "stackit_security_group_rule" "allow_lb_ingress" {
}

# This is the crucial link: it allows traffic from the LB's security group.
remote_security_group_id = stackit_loadbalancer.example.security_group_id
remote_security_group_id = stackit_loadbalancer.example.load_balancer_security_group_id

port_range = {
min = 80
Expand All @@ -182,15 +183,19 @@ resource "stackit_server" "example" {
size = 10
}

network_interfaces = [
stackit_network_interface.nic.network_interface_id
]
network_interfaces = [stackit_network_interface.nic.network_interface_id]

}

resource "stackit_network_interface" "nic" {
project_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
network_id = stackit_network.target_network.network_id
security_group_ids = [stackit_security_group.target_sg.security_group_id]
lifecycle {
ignore_changes = [
security_group_ids,
]
}
}
# End of advanced example

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,8 @@ func (r *loadBalancerDataSource) Schema(_ context.Context, _ datasource.SchemaRe
"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.",
"disable_security_group_assignment": "If set to true, this will disable the automatic assignment of a security group to the load balancer's targets. This option is primarily used to allow targets that are not within the load balancer's own network or SNA (STACKIT Network area). When this is enabled, you are fully responsible for ensuring network connectivity to the targets, including managing all routing and security group rules manually. This setting cannot be changed after the load balancer is created.",
"security_group_id": "The ID of the egress security group assigned to the Load Balancer's internal machines. This ID is essential for allowing traffic from the Load Balancer to targets in different networks or STACKIT Network areas (SNA). To enable this, create a security group rule for your target VMs and set the `remote_security_group_id` of that rule to this value. This is typically used when `disable_security_group_assignment` is set to `true`.",
"load_balancer_security_group_id": "The ID of the egress security group assigned to the Load Balancer's internal machines. This ID is essential for allowing traffic from the Load Balancer to targets in different networks or STACKIT Network areas (SNA). To enable this, create a security group rule for your target VMs and set the `remote_security_group_id` of that rule to this value. This is typically used when `disable_security_group_assignment` is set to `true`.",
"security_group_id": "The ID of the backend security group",
"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.",
Expand Down Expand Up @@ -349,6 +350,10 @@ func (r *loadBalancerDataSource) Schema(_ context.Context, _ datasource.SchemaRe
Description: descriptions["security_group_id"],
Computed: true,
},
"load_balancer_security_group_id": schema.StringAttribute{
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So you came here 2 weeks ago and introduced the security_group field to this resource (#986). Now you want to introduce a second one.

I don't quite get it yet. What's the other security group id for now?

Copy link
Contributor Author

@bm-stackit bm-stackit Sep 25, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since I took this one over from a colleague that is no longer in our team we realized a bit too late, that he missed adding the other crucial security group attribute that is on the loadbalancer vm itself which is needed to do the actual routing. The Load_balancer_security_group_id is the security group of the LB VM and the other security_group_id is the one that we create but do not assign. Load_balancer_security_group_id is being put into the remote_security_group_id of the backend security group which in return allows communication of the LB and the Backend target.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You were so speeding with your last PR, now we have the mess. Anyways, lets break it down:


We have the new load_balancer_security_group_id field now which relates to this field in the API docs right?

https://docs.api.stackit.cloud/documentation/load-balancer/version/v2#tag/Load-Balancer/operation/APIService_GetLoadBalancer

image

And then there is the "old" security_group_id attribute which relates to this field in the API docs, right?

image

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Your observations are correct. The Load_balancer_security_group_id(loadBalanerSecurityGroup) is important for loadbalancers across different Projects in 1 SNA. And the security_group_id (targetSecurityGroup) is useful for load balancers with targets in the same project but different networks.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So I would say the security_group_id field be named target_security_group_id field instead. Which isn't possible now that easily because we have a deprecation time of 6 months...

Copy link
Contributor Author

@bm-stackit bm-stackit Sep 25, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That is not a problem, leave it as is.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That is not a problem, leave it as is.

Maybe for you but I care about our users. IMO the security_group_id field must be deprecated and a new field target_security_group_id should be added.

Description: descriptions["load_balancer_security_group_id"],
Computed: true,
},
},
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ type Model struct {
TargetPools types.List `tfsdk:"target_pools"`
Region types.String `tfsdk:"region"`
SecurityGroupId types.String `tfsdk:"security_group_id"`
LoadBalancerSecurityGroupId types.String `tfsdk:"load_balancer_security_group_id"`
}

// Struct corresponding to Model.Listeners[i]
Expand Down Expand Up @@ -344,7 +345,8 @@ func (r *loadBalancerResource) Schema(_ context.Context, _ resource.SchemaReques
"targets.display_name": "Target display name",
"ip": "Target IP",
"region": "The resource region. If not defined, the provider region is used.",
"security_group_id": "The ID of the egress security group assigned to the Load Balancer's internal machines. This ID is essential for allowing traffic from the Load Balancer to targets in different networks or STACKIT network areas (SNA). To enable this, create a security group rule for your target VMs and set the `remote_security_group_id` of that rule to this value. This is typically used when `disable_security_group_assignment` is set to `true`.",
"security_group_id": "The ID of the backend security group",
"load_balancer_security_group_id": "The ID of the egress security group assigned to the Load Balancer's internal machines. This ID is essential for allowing traffic from the Load Balancer to targets in different networks or STACKIT network areas (SNA). To enable this, create a security group rule for your target VMs and set the `remote_security_group_id` of that rule to this value. This is typically used when `disable_security_group_assignment` is set to `true`.",
}

resp.Schema = schema.Schema{
Expand Down Expand Up @@ -692,6 +694,13 @@ The example below creates the supporting infrastructure using the STACKIT Terraf
stringplanmodifier.UseStateForUnknown(),
},
},
"load_balancer_security_group_id": schema.StringAttribute{
Description: descriptions["load_balancer_security_group_id"],
Computed: true,
PlanModifiers: []planmodifier.String{
stringplanmodifier.UseStateForUnknown(),
},
},
},
}
}
Expand Down Expand Up @@ -1247,6 +1256,12 @@ func mapFields(ctx context.Context, lb *loadbalancer.LoadBalancer, m *Model, reg
m.PrivateAddress = types.StringPointerValue(lb.PrivateAddress)
m.DisableSecurityGroupAssignment = types.BoolPointerValue(lb.DisableTargetSecurityGroupAssignment)

if lb.LoadBalancerSecurityGroup != nil {
m.LoadBalancerSecurityGroupId = types.StringPointerValue(lb.LoadBalancerSecurityGroup.Id)
} else {
m.LoadBalancerSecurityGroupId = types.StringNull()
}

if lb.TargetSecurityGroup != nil {
m.SecurityGroupId = types.StringPointerValue(lb.TargetSecurityGroup.Id)
} else {
Expand Down
12 changes: 12 additions & 0 deletions stackit/internal/services/loadbalancer/loadbalancer_acc_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,7 @@ func TestAccLoadBalancerResourceMin(t *testing.T) {
resource.TestCheckNoResourceAttr("stackit_loadbalancer.loadbalancer", "options.observability.metrics.credentials_ref"),
resource.TestCheckNoResourceAttr("stackit_loadbalancer.loadbalancer", "options.observability.metrics.push_url"),
resource.TestCheckResourceAttrSet("stackit_loadbalancer.loadbalancer", "security_group_id"),
resource.TestCheckResourceAttrSet("stackit_loadbalancer.loadbalancer", "load_balancer_security_group_id"),

// Loadbalancer observability credentials resource
resource.TestCheckResourceAttr("stackit_loadbalancer_observability_credential.obs_credential", "project_id", testutil.ConvertConfigVariable(testConfigVarsMin["project_id"])),
Expand Down Expand Up @@ -182,6 +183,11 @@ func TestAccLoadBalancerResourceMin(t *testing.T) {
"stackit_loadbalancer.loadbalancer", "security_group_id",
"data.stackit_loadbalancer.loadbalancer", "security_group_id",
),
resource.TestCheckResourceAttrSet("data.stackit_loadbalancer.loadbalancer", "load_balancer_security_group_id"),
resource.TestCheckResourceAttrPair(
"stackit_loadbalancer.loadbalancer", "load_balancer_security_group_id",
"data.stackit_loadbalancer.loadbalancer", "load_balancer_security_group_id",
),
)},
// Import
{
Expand Down Expand Up @@ -249,6 +255,7 @@ func TestAccLoadBalancerResourceMax(t *testing.T) {
resource.TestCheckResourceAttrSet("stackit_loadbalancer.loadbalancer", "external_address"),
resource.TestCheckResourceAttr("stackit_loadbalancer.loadbalancer", "disable_security_group_assignment", testutil.ConvertConfigVariable(testConfigVarsMax["disable_security_group_assignment"])),
resource.TestCheckResourceAttrSet("stackit_loadbalancer.loadbalancer", "security_group_id"),
resource.TestCheckResourceAttrSet("stackit_loadbalancer.loadbalancer", "load_balancer_security_group_id"),
resource.TestCheckResourceAttr("stackit_loadbalancer.loadbalancer", "target_pools.0.active_health_check.healthy_threshold", testutil.ConvertConfigVariable(testConfigVarsMax["healthy_threshold"])),
resource.TestCheckResourceAttr("stackit_loadbalancer.loadbalancer", "target_pools.0.active_health_check.interval", testutil.ConvertConfigVariable(testConfigVarsMax["health_interval"])),
resource.TestCheckResourceAttr("stackit_loadbalancer.loadbalancer", "target_pools.0.active_health_check.interval_jitter", testutil.ConvertConfigVariable(testConfigVarsMax["health_interval_jitter"])),
Expand Down Expand Up @@ -320,6 +327,7 @@ func TestAccLoadBalancerResourceMax(t *testing.T) {
resource.TestCheckResourceAttrSet("data.stackit_loadbalancer.loadbalancer", "external_address"),
resource.TestCheckResourceAttr("data.stackit_loadbalancer.loadbalancer", "disable_security_group_assignment", testutil.ConvertConfigVariable(testConfigVarsMax["disable_security_group_assignment"])),
resource.TestCheckResourceAttrSet("stackit_loadbalancer.loadbalancer", "security_group_id"),
resource.TestCheckResourceAttrSet("stackit_loadbalancer.loadbalancer", "load_balancer_security_group_id"),
resource.TestCheckResourceAttr("data.stackit_loadbalancer.loadbalancer", "target_pools.0.active_health_check.healthy_threshold", testutil.ConvertConfigVariable(testConfigVarsMax["healthy_threshold"])),
resource.TestCheckResourceAttr("data.stackit_loadbalancer.loadbalancer", "target_pools.0.active_health_check.interval", testutil.ConvertConfigVariable(testConfigVarsMax["health_interval"])),
resource.TestCheckResourceAttr("data.stackit_loadbalancer.loadbalancer", "target_pools.0.active_health_check.interval_jitter", testutil.ConvertConfigVariable(testConfigVarsMax["health_interval_jitter"])),
Expand All @@ -337,6 +345,10 @@ func TestAccLoadBalancerResourceMax(t *testing.T) {
"stackit_loadbalancer.loadbalancer", "security_group_id",
"data.stackit_loadbalancer.loadbalancer", "security_group_id",
),
resource.TestCheckResourceAttrPair(
"stackit_loadbalancer.loadbalancer", "load_balancer_security_group_id",
"data.stackit_loadbalancer.loadbalancer", "load_balancer_security_group_id",
),
)},
// Import
{
Expand Down
Loading