Skip to content
Closed
Show file tree
Hide file tree
Changes from 8 commits
Commits
Show all changes
67 commits
Select commit Hold shift + click to select a range
bf64470
Adds the new `disable_security_group_assignment` boolean attribute to…
msk-stackit Jul 21, 2025
b0e1e52
Exposes the ID of the Load Balancer's egress security group.
msk-stackit Jul 21, 2025
19e167f
Exposes the ID of the Load Balancer's egress security group.
msk-stackit Jul 21, 2025
065cd04
change order of imports
msk-stackit Jul 21, 2025
288cf75
added a example for the new feature
msk-stackit Jul 22, 2025
8bafad8
update docs
msk-stackit Jul 22, 2025
c22bacd
terraform fmt
msk-stackit Jul 25, 2025
3b4ae4b
Merge branch 'main' into main
msk-stackit Jul 29, 2025
895ac47
add PR feedback
msk-stackit Jul 31, 2025
3b4b190
Merge branch 'main' into main
msk-stackit Aug 8, 2025
4f4aa53
update stackit_loadbalancer resource to no longer wait until LB becom…
msk-stackit Aug 8, 2025
d803bc7
cleanup
msk-stackit Aug 8, 2025
545aabb
update docs and example
msk-stackit Aug 8, 2025
a30cead
update docs and example
msk-stackit Aug 8, 2025
e6d3654
update docs and example
msk-stackit Aug 8, 2025
180aa11
update docs and example
msk-stackit Aug 8, 2025
b4a9a7d
update docs and example
msk-stackit Aug 8, 2025
b9bc3aa
revert to simpler function to allow the LB to become active before pr…
msk-stackit Aug 15, 2025
2858cb1
update description
msk-stackit Aug 15, 2025
77dc880
update description
msk-stackit Aug 15, 2025
7517389
update description
msk-stackit Aug 15, 2025
e63bded
small correction of unnecessary var
msk-stackit Aug 15, 2025
8e0ebfe
update example
msk-stackit Aug 15, 2025
e30d554
Adds the new `disable_security_group_assignment` boolean attribute to…
msk-stackit Jul 21, 2025
7ab6beb
Exposes the ID of the Load Balancer's egress security group.
msk-stackit Jul 21, 2025
e73871a
Exposes the ID of the Load Balancer's egress security group.
msk-stackit Jul 21, 2025
9e8d943
change order of imports
msk-stackit Jul 21, 2025
ac2a715
update example
msk-stackit Aug 15, 2025
53e5f80
terraform fmt
msk-stackit Jul 25, 2025
52e6da9
add PR feedback
msk-stackit Jul 31, 2025
588d00f
update stackit_loadbalancer resource to no longer wait until LB becom…
msk-stackit Aug 8, 2025
795c64a
cleanup
msk-stackit Aug 8, 2025
0e51e07
update docs and example
msk-stackit Aug 8, 2025
2c9f1f2
update docs and example
msk-stackit Aug 8, 2025
fbc2293
update docs and example
msk-stackit Aug 8, 2025
41951d2
update docs and example
msk-stackit Aug 8, 2025
854044a
update docs and example
msk-stackit Aug 8, 2025
da756fc
revert to simpler function to allow the LB to become active before pr…
msk-stackit Aug 15, 2025
5f6e84e
update description
msk-stackit Aug 15, 2025
2c7a446
update description
msk-stackit Aug 15, 2025
f1e7cf7
update description
msk-stackit Aug 15, 2025
c507bdf
small correction of unnecessary var
msk-stackit Aug 15, 2025
bd862ce
update example
msk-stackit Aug 15, 2025
ca736a5
Merge remote-tracking branch 'origin/main'
msk-stackit Aug 15, 2025
dbd035e
Adds the new `disable_security_group_assignment` boolean attribute to…
msk-stackit Jul 21, 2025
3c9d065
Exposes the ID of the Load Balancer's egress security group.
msk-stackit Jul 21, 2025
fa164d7
Exposes the ID of the Load Balancer's egress security group.
msk-stackit Jul 21, 2025
aaa1aa3
change order of imports
msk-stackit Jul 21, 2025
616d202
added a example for the new feature
msk-stackit Jul 22, 2025
0dd3fc3
update docs
msk-stackit Jul 22, 2025
61b4856
terraform fmt
msk-stackit Jul 25, 2025
f778676
add PR feedback
msk-stackit Jul 31, 2025
6396efa
update stackit_loadbalancer resource to no longer wait until LB becom…
msk-stackit Aug 8, 2025
c8a768a
cleanup
msk-stackit Aug 8, 2025
b1e18a4
update docs and example
msk-stackit Aug 8, 2025
ae39a48
update docs and example
msk-stackit Aug 8, 2025
00c9c32
update docs and example
msk-stackit Aug 8, 2025
93d704e
update docs and example
msk-stackit Aug 8, 2025
f1dc94e
update docs and example
msk-stackit Aug 8, 2025
e93b07d
revert to simpler function to allow the LB to become active before pr…
msk-stackit Aug 15, 2025
e7f8579
update description
msk-stackit Aug 15, 2025
e968326
update description
msk-stackit Aug 15, 2025
88fa6d0
update description
msk-stackit Aug 15, 2025
860ae7b
small correction of unnecessary var
msk-stackit Aug 15, 2025
86da55e
update example
msk-stackit Aug 15, 2025
3e33e85
Merge remote-tracking branch 'origin/main'
msk-stackit Aug 19, 2025
7139285
update tests
msk-stackit Aug 19, 2025
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
2 changes: 2 additions & 0 deletions docs/data-sources/loadbalancer.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,13 +33,15 @@ data "stackit_loadbalancer" "example" {

### Read-Only

- `disable_security_group_assignment` (Boolean) Disables the target security group assignment.
- `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))
- `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 security group automatically assigned to the load balancer's targets.
- `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
94 changes: 94 additions & 0 deletions docs/resources/loadbalancer.md
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,98 @@ resource "stackit_loadbalancer" "example" {
private_network_only = false
}
}

# This example shows an advanced setup where the load balancer is in one
# network and the target server is in another. This requires manual
# security group configuration.

# 1. Create a network for the Load Balancer
resource "stackit_network" "lb_network" {
project_id = var.project_id
name = "lb-network"
ipv4_prefix = "192.168.1.0/24"
}

# 2. Create a separate network for the Target Server
resource "stackit_network" "target_network" {
project_id = var.project_id
name = "target-network"
ipv4_prefix = "192.168.2.0/24"
}

# 3. Create the Load Balancer and disable automatic security groups
resource "stackit_loadbalancer" "example_advanced" {
project_id = var.project_id
name = "advanced-lb"

# This is the key setting for manual mode
disable_security_group_assignment = true

networks = [{
network_id = stackit_network.lb_network.id
role = "ROLE_LISTENERS_AND_TARGETS"
}]

target_pools = [{
name = "cross-network-pool"
target_port = 80
targets = [{
display_name = "remote-target-server"
ip = stackit_server.target_server.network_interfaces[0].ipv4
}]
}]

listeners = [{
port = 80
protocol = "PROTOCOL_TCP"
target_pool = "cross-network-pool"
}]
}

# 4. Create a new security group for the target server
resource "stackit_security_group" "target_sg" {
project_id = var.project_id
name = "target-sg-for-lb-access"
description = "Allows ingress traffic from the advanced load balancer."
}

# 5. Create a rule to allow traffic FROM the load balancer
# This is the core of the manual setup.
resource "stackit_security_group_rule" "allow_lb_ingress" {
security_group_id = stackit_security_group.target_sg.id
direction = "ingress"
protocol = "tcp"

# Use the computed security_group_id from the load balancer
remote_security_group_id = stackit_loadbalancer.example_advanced.security_group_id

port_range = {
min = 80
max = 80
}
}

# 6. Create the target server and assign the new security group to it
resource "stackit_server" "target_server" {
project_id = var.project_id
name = "remote-target-server"
machine_type = "c1.1"
availability_zone = "eu01-1"

boot_volume = {
source_type = "image"
source_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" // e.g., an Ubuntu image ID
size = 10
}

network_interfaces = [{
network_id = stackit_network.target_network.id
security_groups = [stackit_security_group.target_sg.id]
}]

# Ensure the rule is created before the server
depends_on = [stackit_security_group_rule.allow_lb_ingress]
}
```

<!-- schema generated by tfplugindocs -->
Expand All @@ -124,6 +216,7 @@ resource "stackit_loadbalancer" "example" {

### Optional

- `disable_security_group_assignment` (Boolean) 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. 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.
- `external_address` (String) External Load Balancer IP address where this Load Balancer is exposed.
- `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`.
Expand All @@ -133,6 +226,7 @@ resource "stackit_loadbalancer" "example" {

- `id` (String) Terraform's internal resource ID. It is structured as "`project_id`","region","`name`".
- `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`.

<a id="nestedatt--listeners"></a>
### Nested Schema for `listeners`
Expand Down
92 changes: 92 additions & 0 deletions examples/resources/stackit_loadbalancer/resource.tf
Original file line number Diff line number Diff line change
Expand Up @@ -89,4 +89,96 @@ resource "stackit_loadbalancer" "example" {
options = {
private_network_only = false
}
}

# This example shows an advanced setup where the load balancer is in one
# network and the target server is in another. This requires manual
# security group configuration.

# 1. Create a network for the Load Balancer
resource "stackit_network" "lb_network" {
project_id = var.project_id
name = "lb-network"
ipv4_prefix = "192.168.1.0/24"
}

# 2. Create a separate network for the Target Server
resource "stackit_network" "target_network" {
project_id = var.project_id
name = "target-network"
ipv4_prefix = "192.168.2.0/24"
}

# 3. Create the Load Balancer and disable automatic security groups
resource "stackit_loadbalancer" "example_advanced" {
project_id = var.project_id
name = "advanced-lb"

# This is the key setting for manual mode
disable_security_group_assignment = true

networks = [{
network_id = stackit_network.lb_network.id
role = "ROLE_LISTENERS_AND_TARGETS"
}]

target_pools = [{
name = "cross-network-pool"
target_port = 80
targets = [{
display_name = "remote-target-server"
ip = stackit_server.target_server.network_interfaces[0].ipv4
}]
}]

listeners = [{
port = 80
protocol = "PROTOCOL_TCP"
target_pool = "cross-network-pool"
}]
}

# 4. Create a new security group for the target server
resource "stackit_security_group" "target_sg" {
project_id = var.project_id
name = "target-sg-for-lb-access"
description = "Allows ingress traffic from the advanced load balancer."
}

# 5. Create a rule to allow traffic FROM the load balancer
# This is the core of the manual setup.
resource "stackit_security_group_rule" "allow_lb_ingress" {
security_group_id = stackit_security_group.target_sg.id
direction = "ingress"
protocol = "tcp"

# Use the computed security_group_id from the load balancer
remote_security_group_id = stackit_loadbalancer.example_advanced.security_group_id

port_range = {
min = 80
max = 80
}
}

# 6. Create the target server and assign the new security group to it
resource "stackit_server" "target_server" {
project_id = var.project_id
name = "remote-target-server"
machine_type = "c1.1"
availability_zone = "eu01-1"

boot_volume = {
source_type = "image"
source_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" // e.g., an Ubuntu image ID
size = 10
}

network_interfaces = [{
network_id = stackit_network.target_network.id
security_groups = [stackit_security_group.target_sg.id]
}]

# Ensure the rule is created before the server
depends_on = [stackit_security_group_rule.allow_lb_ingress]
}
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ func (r *loadBalancerDataSource) Schema(_ context.Context, _ datasource.SchemaRe
"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.",
"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. 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.",
"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 @@ -125,6 +126,10 @@ func (r *loadBalancerDataSource) Schema(_ context.Context, _ datasource.SchemaRe
Description: descriptions["external_address"],
Computed: true,
},
"disable_security_group_assignment": schema.BoolAttribute{
Description: "Disables the target security group assignment.",
Computed: true,
},
"plan_id": schema.StringAttribute{
Description: descriptions["plan_id"],
Computed: true,
Expand Down Expand Up @@ -339,6 +344,10 @@ func (r *loadBalancerDataSource) Schema(_ context.Context, _ datasource.SchemaRe
Optional: true,
Description: descriptions["region"],
},
"security_group_id": schema.StringAttribute{
Description: "The ID of the security group automatically assigned to the load balancer's targets.",
Computed: true,
},
},
}
}
Expand Down
64 changes: 46 additions & 18 deletions stackit/internal/services/loadbalancer/loadbalancer/resource.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import (
"github.com/hashicorp/terraform-plugin-framework/path"
"github.com/hashicorp/terraform-plugin-framework/resource"
"github.com/hashicorp/terraform-plugin-framework/resource/schema"
"github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault"
"github.com/hashicorp/terraform-plugin-framework/resource/schema/boolplanmodifier"
"github.com/hashicorp/terraform-plugin-framework/resource/schema/int64planmodifier"
"github.com/hashicorp/terraform-plugin-framework/resource/schema/listplanmodifier"
Expand Down Expand Up @@ -48,17 +49,19 @@ var (
)

type Model struct {
Id types.String `tfsdk:"id"` // needed by TF
ProjectId types.String `tfsdk:"project_id"`
ExternalAddress types.String `tfsdk:"external_address"`
Listeners types.List `tfsdk:"listeners"`
Name types.String `tfsdk:"name"`
PlanId types.String `tfsdk:"plan_id"`
Networks types.List `tfsdk:"networks"`
Options types.Object `tfsdk:"options"`
PrivateAddress types.String `tfsdk:"private_address"`
TargetPools types.List `tfsdk:"target_pools"`
Region types.String `tfsdk:"region"`
Id types.String `tfsdk:"id"` // needed by TF
ProjectId types.String `tfsdk:"project_id"`
ExternalAddress types.String `tfsdk:"external_address"`
DisableSecurityGroupAssignment types.Bool `tfsdk:"disable_security_group_assignment"`
Listeners types.List `tfsdk:"listeners"`
Name types.String `tfsdk:"name"`
PlanId types.String `tfsdk:"plan_id"`
Networks types.List `tfsdk:"networks"`
Options types.Object `tfsdk:"options"`
PrivateAddress types.String `tfsdk:"private_address"`
TargetPools types.List `tfsdk:"target_pools"`
Region types.String `tfsdk:"region"`
SecurityGroupId types.String `tfsdk:"security_group_id"`
}

// Struct corresponding to Model.Listeners[i]
Expand Down Expand Up @@ -303,6 +306,7 @@ func (r *loadBalancerResource) Schema(_ context.Context, _ resource.SchemaReques
"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.",
"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. 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.",
"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),
Expand Down Expand Up @@ -373,6 +377,16 @@ The example below creates the supporting infrastructure using the STACKIT Terraf
stringplanmodifier.RequiresReplace(),
},
},
"disable_security_group_assignment": schema.BoolAttribute{
Description: descriptions["disable_security_group_assignment"],
Optional: true,
Computed: true,
Default: booldefault.StaticBool(false),
PlanModifiers: []planmodifier.Bool{
boolplanmodifier.RequiresReplace(),
boolplanmodifier.UseStateForUnknown(),
},
},
"plan_id": schema.StringAttribute{
Description: descriptions["plan_id"],
Optional: true,
Expand Down Expand Up @@ -669,6 +683,13 @@ The example below creates the supporting infrastructure using the STACKIT Terraf
stringplanmodifier.RequiresReplace(),
},
},
"security_group_id": schema.StringAttribute{
Description: "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`.",
Computed: true,
PlanModifiers: []planmodifier.String{
stringplanmodifier.UseStateForUnknown(),
},
},
},
}
}
Expand Down Expand Up @@ -907,13 +928,14 @@ func toCreatePayload(ctx context.Context, model *Model) (*loadbalancer.CreateLoa
}

return &loadbalancer.CreateLoadBalancerPayload{
ExternalAddress: conversion.StringValueToPointer(model.ExternalAddress),
Listeners: listenersPayload,
Name: conversion.StringValueToPointer(model.Name),
PlanId: conversion.StringValueToPointer(model.PlanId),
Networks: networksPayload,
Options: optionsPayload,
TargetPools: targetPoolsPayload,
ExternalAddress: conversion.StringValueToPointer(model.ExternalAddress),
DisableTargetSecurityGroupAssignment: conversion.BoolValueToPointer(model.DisableSecurityGroupAssignment),
Listeners: listenersPayload,
Name: conversion.StringValueToPointer(model.Name),
PlanId: conversion.StringValueToPointer(model.PlanId),
Networks: networksPayload,
Options: optionsPayload,
TargetPools: targetPoolsPayload,
}, nil
}

Expand Down Expand Up @@ -1221,7 +1243,13 @@ func mapFields(ctx context.Context, lb *loadbalancer.LoadBalancer, m *Model, reg
m.PlanId = types.StringPointerValue(lb.PlanId)
m.ExternalAddress = types.StringPointerValue(lb.ExternalAddress)
m.PrivateAddress = types.StringPointerValue(lb.PrivateAddress)
m.DisableSecurityGroupAssignment = types.BoolPointerValue(lb.DisableTargetSecurityGroupAssignment)

if lb.TargetSecurityGroup != nil {
m.SecurityGroupId = types.StringPointerValue(lb.TargetSecurityGroup.Id)
} else {
m.SecurityGroupId = types.StringNull()
}
err := mapListeners(lb, m)
if err != nil {
return fmt.Errorf("mapping listeners: %w", err)
Expand Down
Loading