diff --git a/docs/data-sources/vpn_gateway_status.md b/docs/data-sources/vpn_gateway_status.md
new file mode 100644
index 000000000..fa50f30c7
--- /dev/null
+++ b/docs/data-sources/vpn_gateway_status.md
@@ -0,0 +1,55 @@
+---
+# generated by https://github.com/hashicorp/terraform-plugin-docs
+page_title: "stackit_vpn_gateway_status Data Source - stackit"
+subcategory: ""
+description: |-
+ VPN Gateway Status data source schema. Uses the default_region specified in the provider configuration as a fallback in case no region is defined on datasource level.
+---
+
+# stackit_vpn_gateway_status (Data Source)
+
+VPN Gateway Status data source schema. Uses the `default_region` specified in the provider configuration as a fallback in case no `region` is defined on datasource level.
+
+## Example Usage
+
+```terraform
+data "stackit_vpn_gateway_status" "example" {
+ project_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
+ gateway_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
+}
+```
+
+
+## Schema
+
+### Required
+
+- `gateway_id` (String) The server-generated UUID of the VPN gateway.
+- `project_id` (String) STACKIT project ID associated with the VPN gateway.
+
+### Read-Only
+
+- `connections` (Attributes List) List of connections in the VPN gateway. (see [below for nested schema](#nestedatt--connections))
+- `display_name` (String) A user-friendly name for the VPN gateway.
+- `id` (String) Terraform's internal resource identifier. Structured as "`project_id`,`region`,`gateway_id`".
+- `region` (String) STACKIT region name the resource is located in. If not defined, the provider region is used.
+- `tunnels` (Attributes List) List of the VPN tunnels in the gateway. (see [below for nested schema](#nestedatt--tunnels))
+
+
+### Nested Schema for `connections`
+
+Read-Only:
+
+- `connection_id` (String) ID of the VPN connection.
+- `display_name` (String) Display name of the VPN connection.
+- `enabled` (Boolean) Wether the VPN connection is enabled or not.
+
+
+
+### Nested Schema for `tunnels`
+
+Read-Only:
+
+- `internal_next_hop_ip` (String) The IPv4 address of the endpoint in the SNA.
+- `name` (String) The name of the VPN tunnel. Possible values are: `tunnel1`, `tunnel2`.
+- `public_ip` (String) The public IPv4 address of this endpoint.
diff --git a/examples/data-sources/stackit_vpn_gateway_status/data-source.tf b/examples/data-sources/stackit_vpn_gateway_status/data-source.tf
new file mode 100644
index 000000000..cd4559e44
--- /dev/null
+++ b/examples/data-sources/stackit_vpn_gateway_status/data-source.tf
@@ -0,0 +1,4 @@
+data "stackit_vpn_gateway_status" "example" {
+ project_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
+ gateway_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
+}
diff --git a/stackit/internal/services/vpn/gateway_status/datasource.go b/stackit/internal/services/vpn/gateway_status/datasource.go
new file mode 100644
index 000000000..f56134633
--- /dev/null
+++ b/stackit/internal/services/vpn/gateway_status/datasource.go
@@ -0,0 +1,338 @@
+package gateway_status
+
+import (
+ "context"
+ "errors"
+ "fmt"
+ "net/http"
+
+ "github.com/hashicorp/terraform-plugin-framework/attr"
+ "github.com/hashicorp/terraform-plugin-framework/datasource"
+ "github.com/hashicorp/terraform-plugin-framework/datasource/schema"
+ "github.com/hashicorp/terraform-plugin-framework/schema/validator"
+ "github.com/hashicorp/terraform-plugin-framework/types"
+ "github.com/hashicorp/terraform-plugin-framework/types/basetypes"
+ "github.com/hashicorp/terraform-plugin-log/tflog"
+
+ "github.com/stackitcloud/stackit-sdk-go/core/oapierror"
+ sdkUtils "github.com/stackitcloud/stackit-sdk-go/core/utils"
+ vpn "github.com/stackitcloud/stackit-sdk-go/services/vpn/v1api"
+
+ "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/core"
+ "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/services/vpn/utils"
+
+ "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/conversion"
+ tfutils "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/utils"
+ "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/validate"
+)
+
+var (
+ _ datasource.DataSource = &vpnGatewayStatusDataSource{}
+ _ datasource.DataSourceWithConfigure = &vpnGatewayStatusDataSource{}
+)
+
+type vpnGatewayStatusDataSource struct {
+ client *vpn.APIClient
+ providerData core.ProviderData
+}
+
+type Model struct {
+ Id types.String `tfsdk:"id"` // needed by TF
+ GatewayId types.String `tfsdk:"gateway_id"`
+ ProjectId types.String `tfsdk:"project_id"`
+ Region types.String `tfsdk:"region"`
+ Connections types.List `tfsdk:"connections"`
+ DisplayName types.String `tfsdk:"display_name"`
+ Tunnels types.List `tfsdk:"tunnels"`
+}
+
+type Connection struct {
+ DisplayName types.String `tfsdk:"display_name"`
+ Enabled types.Bool `tfsdk:"enabled"`
+ Id types.String `tfsdk:"connection_id"`
+}
+
+type Tunnel struct {
+ InternalNextHopIP types.String `tfsdk:"internal_next_hop_ip"`
+ Name types.String `tfsdk:"name"`
+ PublicIP types.String `tfsdk:"public_ip"`
+}
+
+var connectionType = map[string]attr.Type{
+ "display_name": basetypes.StringType{},
+ "enabled": basetypes.BoolType{},
+ "connection_id": basetypes.StringType{},
+}
+
+var tunnelType = map[string]attr.Type{
+ "internal_next_hop_ip": basetypes.StringType{},
+ "name": basetypes.StringType{},
+ "public_ip": basetypes.StringType{},
+}
+
+func NewVPNGatewayStatusDataSource() datasource.DataSource {
+ return &vpnGatewayStatusDataSource{}
+}
+
+func (d *vpnGatewayStatusDataSource) Configure(ctx context.Context, req datasource.ConfigureRequest, resp *datasource.ConfigureResponse) {
+ var ok bool
+ d.providerData, ok = conversion.ParseProviderData(ctx, req.ProviderData, &resp.Diagnostics)
+ if !ok {
+ return
+ }
+
+ d.client = utils.ConfigureClient(ctx, &d.providerData, &resp.Diagnostics)
+ if resp.Diagnostics.HasError() {
+ return
+ }
+ tflog.Info(ctx, "VPN client configured")
+}
+
+func (d *vpnGatewayStatusDataSource) Metadata(_ context.Context, req datasource.MetadataRequest, resp *datasource.MetadataResponse) {
+ resp.TypeName = req.ProviderTypeName + "_vpn_gateway_status"
+}
+
+var schemaDescriptions = map[string]string{
+ "id": "Terraform's internal resource identifier. Structured as \"`project_id`,`region`,`gateway_id`\".",
+ "gateway_id": "The server-generated UUID of the VPN gateway.",
+ "project_id": "STACKIT project ID associated with the VPN gateway.",
+ "region": "STACKIT region name the resource is located in. If not defined, the provider region is used.",
+ "connections": "List of connections in the VPN gateway.",
+ "connection_display_name": "Display name of the VPN connection.",
+ "connection_enabled": "Wether the VPN connection is enabled or not.",
+ "connection_id": "ID of the VPN connection.",
+ "display_name": "A user-friendly name for the VPN gateway.",
+ "tunnels": "List of the VPN tunnels in the gateway.",
+ "tunnel_internal_next_hop_ip": "The IPv4 address of the endpoint in the SNA.",
+ "tunnel_name": fmt.Sprintf("The name of the VPN tunnel. %s", tfutils.FormatPossibleValues(sdkUtils.EnumSliceToStringSlice(vpn.AllowedVPNTunnelsNameEnumValues)...)),
+ "tunnel_public_ip": "The public IPv4 address of this endpoint.",
+}
+
+func (d *vpnGatewayStatusDataSource) Schema(_ context.Context, _ datasource.SchemaRequest, resp *datasource.SchemaResponse) {
+ resp.Schema = schema.Schema{
+ Description: fmt.Sprintf("VPN Gateway Status data source schema. %s", core.DatasourceRegionFallbackDocstring),
+ Attributes: map[string]schema.Attribute{
+ "id": schema.StringAttribute{
+ Description: schemaDescriptions["id"],
+ Computed: true,
+ },
+ "project_id": schema.StringAttribute{
+ Description: schemaDescriptions["project_id"],
+ Required: true,
+ Validators: []validator.String{
+ validate.UUID(),
+ validate.NoSeparator(),
+ },
+ },
+ "region": schema.StringAttribute{
+ Description: schemaDescriptions["region"],
+ Computed: true,
+ },
+ "gateway_id": schema.StringAttribute{
+ Description: schemaDescriptions["gateway_id"],
+ Required: true,
+ Validators: []validator.String{
+ validate.UUID(),
+ validate.NoSeparator(),
+ },
+ },
+ "connections": schema.ListNestedAttribute{
+ Description: schemaDescriptions["connections"],
+ Computed: true,
+ NestedObject: schema.NestedAttributeObject{
+ Attributes: map[string]schema.Attribute{
+ "display_name": schema.StringAttribute{
+ Description: schemaDescriptions["connection_display_name"],
+ Computed: true,
+ },
+ "enabled": schema.BoolAttribute{
+ Description: schemaDescriptions["connection_enabled"],
+ Computed: true,
+ },
+ "connection_id": schema.StringAttribute{
+ Description: schemaDescriptions["connection_id"],
+ Computed: true,
+ },
+ },
+ },
+ },
+ "display_name": schema.StringAttribute{
+ Description: schemaDescriptions["display_name"],
+ Computed: true,
+ },
+ "tunnels": schema.ListNestedAttribute{
+ Description: schemaDescriptions["tunnels"],
+ Computed: true,
+ NestedObject: schema.NestedAttributeObject{
+ Attributes: map[string]schema.Attribute{
+ "internal_next_hop_ip": schema.StringAttribute{
+ Description: schemaDescriptions["tunnel_internal_next_hop_ip"],
+ Computed: true,
+ },
+ "name": schema.StringAttribute{
+ Description: schemaDescriptions["tunnel_name"],
+ Computed: true,
+ },
+ "public_ip": schema.StringAttribute{
+ Description: schemaDescriptions["tunnel_public_ip"],
+ Computed: true,
+ },
+ },
+ },
+ },
+ },
+ }
+}
+
+func (d *vpnGatewayStatusDataSource) Read(ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse) { // nolint:gocritic // function signature required by Terraform
+ var model Model
+ diags := req.Config.Get(ctx, &model)
+ resp.Diagnostics.Append(diags...)
+ if resp.Diagnostics.HasError() {
+ return
+ }
+
+ ctx = core.InitProviderContext(ctx)
+
+ projectId := model.ProjectId.ValueString()
+ region := d.providerData.GetRegionWithOverride(model.Region)
+ gatewayId := model.GatewayId.ValueString()
+
+ ctx = tflog.SetField(ctx, "project_id", projectId)
+ ctx = tflog.SetField(ctx, "region", region)
+ ctx = tflog.SetField(ctx, "gateway_id", gatewayId)
+
+ gatewayResponse, err := d.client.DefaultAPI.GetGatewayStatus(ctx, projectId, region, gatewayId).Execute()
+ if err != nil {
+ var oapiErr *oapierror.GenericOpenAPIError
+ ok := errors.As(err, &oapiErr)
+ if ok && oapiErr.StatusCode == http.StatusNotFound {
+ resp.State.RemoveResource(ctx)
+ return
+ }
+ core.LogAndAddError(ctx, &resp.Diagnostics, "Error reading VPN gateway", fmt.Sprintf("Calling API: %v", err))
+ return
+ }
+ ctx = core.LogResponse(ctx)
+
+ err = mapFields(ctx, gatewayResponse, &model, region)
+ if err != nil {
+ core.LogAndAddError(ctx, &resp.Diagnostics, "Error reading VPN gateway", fmt.Sprintf("Processing response: %v", err))
+ return
+ }
+
+ diags = resp.State.Set(ctx, model)
+ resp.Diagnostics.Append(diags...)
+ if resp.Diagnostics.HasError() {
+ return
+ }
+ tflog.Info(ctx, "VPN gateway read", map[string]any{
+ "gateway_id": gatewayId,
+ })
+}
+
+func mapFields(ctx context.Context, gatewayStatus *vpn.GatewayStatusResponse, model *Model, region string) error {
+ if gatewayStatus == nil {
+ return fmt.Errorf("response input is nil")
+ }
+ if model == nil {
+ return fmt.Errorf("model input is nil")
+ }
+
+ var gatewayId string
+ if model.GatewayId.ValueString() != "" {
+ gatewayId = model.GatewayId.ValueString()
+ } else if gatewayStatus.Id != nil {
+ gatewayId = *gatewayStatus.Id
+ } else {
+ return fmt.Errorf("gateway id not present")
+ }
+
+ model.Id = tfutils.BuildInternalTerraformId(model.ProjectId.ValueString(), region, gatewayId)
+ model.GatewayId = types.StringValue(gatewayId)
+ model.Region = types.StringValue(region)
+
+ if gatewayStatus.DisplayName != nil {
+ model.DisplayName = types.StringValue(*gatewayStatus.DisplayName)
+ }
+
+ tfConnections, err := mapConnections(ctx, gatewayStatus.Connections)
+ if err != nil {
+ return fmt.Errorf("map tunnels: %w", err)
+ } else if tfConnections != nil {
+ model.Connections = *tfConnections
+ }
+
+ tfTunnels, err := mapTunnels(ctx, gatewayStatus.Tunnels)
+ if err != nil {
+ return fmt.Errorf("map tunnels: %w", err)
+ } else if tfTunnels != nil {
+ model.Tunnels = *tfTunnels
+ }
+
+ return nil
+}
+
+func mapConnections(ctx context.Context, vpnConnections []vpn.ConnectionStatusResponse) (*basetypes.ListValue, error) {
+ connections := []attr.Value{}
+
+ for _, connectionItem := range vpnConnections {
+ connection := Connection{}
+
+ if connectionItem.DisplayName != nil {
+ connection.DisplayName = types.StringValue(string(*connectionItem.DisplayName))
+ }
+ if connectionItem.Enabled != nil {
+ connection.Enabled = types.BoolValue(*connectionItem.Enabled)
+ }
+ if connectionItem.Id != nil {
+ connection.Id = types.StringValue(string(*connectionItem.Id))
+ }
+
+ connectionValue, diags := types.ObjectValueFrom(ctx, connectionType, connection)
+ if diags.HasError() {
+ return nil, fmt.Errorf("mapping connection: %w", core.DiagsToError(diags))
+ }
+
+ connections = append(connections, connectionValue)
+ }
+
+ tfConnections, diags := types.ListValueFrom(ctx, types.ObjectType{AttrTypes: connectionType}, connections)
+ if diags.HasError() {
+ return nil, fmt.Errorf("mapping connections: %w", core.DiagsToError(diags))
+ }
+
+ return &tfConnections, nil
+}
+
+func mapTunnels(ctx context.Context, vpnTunnels []vpn.VPNTunnels) (*basetypes.ListValue, error) {
+ tunnels := []attr.Value{}
+
+ for _, tunnelItem := range vpnTunnels {
+ tunnel := Tunnel{}
+
+ if tunnelItem.InternalNextHopIP != nil {
+ tunnel.InternalNextHopIP = types.StringValue(string(*tunnelItem.InternalNextHopIP))
+ }
+ if tunnelItem.Name != nil {
+ tunnel.Name = types.StringValue(string(*tunnelItem.Name))
+ }
+ if tunnelItem.PublicIP != nil {
+ tunnel.PublicIP = types.StringValue(string(*tunnelItem.PublicIP))
+ }
+
+ tunnelValue, diags := types.ObjectValueFrom(ctx, tunnelType, tunnel)
+ if diags.HasError() {
+ return nil, fmt.Errorf("mapping tunnel: %w", core.DiagsToError(diags))
+ }
+
+ tunnels = append(tunnels, tunnelValue)
+ }
+
+ tfTunnels, diags := types.ListValueFrom(ctx, types.ObjectType{AttrTypes: tunnelType}, tunnels)
+ if diags.HasError() {
+ return nil, fmt.Errorf("mapping tunnels: %w", core.DiagsToError(diags))
+ }
+
+ return &tfTunnels, nil
+}
diff --git a/stackit/internal/services/vpn/gateway_status/datasource_test.go b/stackit/internal/services/vpn/gateway_status/datasource_test.go
new file mode 100644
index 000000000..c0a35a1cd
--- /dev/null
+++ b/stackit/internal/services/vpn/gateway_status/datasource_test.go
@@ -0,0 +1,242 @@
+package gateway_status
+
+import (
+ "context"
+ "reflect"
+ "testing"
+
+ "github.com/google/go-cmp/cmp"
+ "github.com/google/uuid"
+ "github.com/hashicorp/terraform-plugin-framework/attr"
+ "github.com/hashicorp/terraform-plugin-framework/types"
+ "github.com/hashicorp/terraform-plugin-framework/types/basetypes"
+ vpn "github.com/stackitcloud/stackit-sdk-go/services/vpn/v1api"
+)
+
+const (
+ testRegion = "eu01"
+ testDisplayName = "Gateway"
+ testTunnel1InternalNextHopIP = "123.45.67.89"
+ testTunnel1PublicIP = "98.76.54.32"
+ testTunnel2InternalNextHopIP = "123.45.67.89"
+ testTunnel2PublicIP = "98.76.54.32"
+)
+
+var (
+ testProjectId = uuid.NewString()
+ testGatewayId = uuid.NewString()
+ testId = testProjectId + "," + testRegion + "," + testGatewayId
+)
+
+func fixtureInput(mods ...func(m *vpn.GatewayStatusResponse)) *vpn.GatewayStatusResponse {
+ resp := &vpn.GatewayStatusResponse{
+ Id: new(testGatewayId),
+ Connections: []vpn.ConnectionStatusResponse{
+ {
+ DisplayName: new("Conn1"),
+ Enabled: new(true),
+ Id: new("foo"),
+ },
+ {
+ DisplayName: new("Conn2"),
+ Enabled: new(false),
+ Id: new("bar"),
+ },
+ },
+ DisplayName: new(testDisplayName),
+ Tunnels: []vpn.VPNTunnels{
+ {
+ InternalNextHopIP: new(testTunnel1InternalNextHopIP),
+ Name: vpn.VPNTUNNELSNAME_TUNNEL1.Ptr(),
+ PublicIP: new(testTunnel1PublicIP),
+ },
+ {
+ InternalNextHopIP: new(testTunnel2InternalNextHopIP),
+ Name: vpn.VPNTUNNELSNAME_TUNNEL2.Ptr(),
+ PublicIP: new(testTunnel2PublicIP),
+ },
+ },
+ }
+ for _, mod := range mods {
+ mod(resp)
+ }
+ return resp
+}
+
+func fixtureModel(mods ...func(m *Model)) *Model {
+ resp := &Model{
+ ProjectId: types.StringValue(testProjectId),
+ Region: types.StringValue(testRegion),
+ Id: types.StringValue(testId),
+ GatewayId: types.StringValue(testGatewayId),
+ Connections: types.ListValueMust(types.ObjectType{AttrTypes: connectionType}, []attr.Value{
+ types.ObjectValueMust(connectionType, map[string]attr.Value{
+ "display_name": types.StringValue("Conn1"),
+ "enabled": types.BoolValue(true),
+ "connection_id": types.StringValue("foo"),
+ }),
+ types.ObjectValueMust(connectionType, map[string]attr.Value{
+ "display_name": types.StringValue("Conn2"),
+ "enabled": types.BoolValue(false),
+ "connection_id": types.StringValue("bar"),
+ }),
+ }),
+ DisplayName: types.StringValue(testDisplayName),
+ Tunnels: types.ListValueMust(types.ObjectType{AttrTypes: tunnelType}, []attr.Value{
+ types.ObjectValueMust(tunnelType, map[string]attr.Value{
+ "internal_next_hop_ip": types.StringValue(testTunnel1InternalNextHopIP),
+ "name": types.StringValue(string(vpn.VPNTUNNELSNAME_TUNNEL1)),
+ "public_ip": types.StringValue(testTunnel1PublicIP),
+ }),
+ types.ObjectValueMust(tunnelType, map[string]attr.Value{
+ "internal_next_hop_ip": types.StringValue(testTunnel2InternalNextHopIP),
+ "name": types.StringValue(string(vpn.VPNTUNNELSNAME_TUNNEL2)),
+ "public_ip": types.StringValue(testTunnel2PublicIP),
+ }),
+ }),
+ }
+ for _, mod := range mods {
+ mod(resp)
+ }
+ return resp
+}
+
+func TestMapDatasourceFields(t *testing.T) {
+ tests := []struct {
+ name string
+ region string
+ state *Model
+ input *vpn.GatewayStatusResponse
+ expected *Model
+ isValid bool
+ }{
+ {
+ name: "default_values",
+ region: "eu01",
+ state: &Model{
+ ProjectId: types.StringValue(testProjectId),
+ },
+ input: fixtureInput(),
+ expected: fixtureModel(),
+ isValid: true,
+ },
+ {
+ name: "no_input",
+ region: "eu01",
+ state: &Model{
+ ProjectId: types.StringValue(testProjectId),
+ GatewayId: types.StringValue(testGatewayId),
+ },
+ input: nil,
+ expected: nil,
+ isValid: false,
+ },
+ {
+ name: "no_model",
+ region: "eu01",
+ state: nil,
+ input: &vpn.GatewayStatusResponse{},
+ expected: nil,
+ isValid: false,
+ },
+ {
+ name: "no_gateway_id",
+ region: "eu01",
+ state: &Model{
+ ProjectId: types.StringValue(testProjectId),
+ },
+ input: &vpn.GatewayStatusResponse{},
+ expected: nil,
+ isValid: false,
+ },
+ {
+ name: "empty_input",
+ region: "eu01",
+ state: &Model{
+ ProjectId: types.StringValue(testProjectId),
+ GatewayId: types.StringValue(testGatewayId),
+ },
+ input: &vpn.GatewayStatusResponse{},
+ expected: &Model{
+ Id: types.StringValue(testId),
+ ProjectId: types.StringValue(testProjectId),
+ GatewayId: types.StringValue(testGatewayId),
+ Region: types.StringValue(testRegion),
+ Connections: types.ListValueMust(types.ObjectType{AttrTypes: connectionType}, []attr.Value{}),
+ Tunnels: types.ListValueMust(types.ObjectType{AttrTypes: tunnelType}, []attr.Value{}),
+ },
+ isValid: true,
+ },
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ ctx := context.Background()
+ if err := mapFields(ctx, tt.input, tt.state, tt.region); (err == nil) != tt.isValid {
+ t.Errorf("unexpected error: %s", err)
+ }
+ if tt.isValid {
+ if diff := cmp.Diff(tt.state, tt.expected); diff != "" {
+ t.Fatalf("Data does not match: %s", diff)
+ }
+ }
+ })
+ }
+}
+
+func TestMapTunnels(t *testing.T) {
+ tests := []struct {
+ name string
+ input []vpn.VPNTunnels
+ expected *basetypes.ListValue
+ isValid bool
+ }{
+ {
+ name: "default_values",
+ input: []vpn.VPNTunnels{
+ {
+ InternalNextHopIP: new(testTunnel1InternalNextHopIP),
+ Name: vpn.VPNTUNNELSNAME_TUNNEL1.Ptr(),
+ PublicIP: new(testTunnel1PublicIP),
+ },
+ {
+ InternalNextHopIP: new(testTunnel2InternalNextHopIP),
+ Name: vpn.VPNTUNNELSNAME_TUNNEL2.Ptr(),
+ PublicIP: new(testTunnel2PublicIP),
+ },
+ },
+ expected: new(types.ListValueMust(types.ObjectType{AttrTypes: tunnelType}, []attr.Value{
+ types.ObjectValueMust(tunnelType, map[string]attr.Value{
+ "internal_next_hop_ip": types.StringValue(testTunnel1InternalNextHopIP),
+ "name": types.StringValue(string(vpn.VPNTUNNELSNAME_TUNNEL1)),
+ "public_ip": types.StringValue(testTunnel1PublicIP),
+ }),
+ types.ObjectValueMust(tunnelType, map[string]attr.Value{
+ "internal_next_hop_ip": types.StringValue(testTunnel2InternalNextHopIP),
+ "name": types.StringValue(string(vpn.VPNTUNNELSNAME_TUNNEL2)),
+ "public_ip": types.StringValue(testTunnel2PublicIP),
+ }),
+ })),
+ isValid: true,
+ },
+ {
+ name: "empty",
+ input: []vpn.VPNTunnels{},
+ expected: new(types.ListValueMust(types.ObjectType{AttrTypes: tunnelType}, []attr.Value{})),
+ isValid: true,
+ },
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ ctx := context.Background()
+ tfTunnels, err := mapTunnels(ctx, tt.input)
+ if (err == nil) != tt.isValid {
+ t.Errorf("unexpected error: %s", err)
+ }
+ if tt.isValid {
+ if !reflect.DeepEqual(tfTunnels, tt.expected) {
+ t.Errorf("ParseProviderData() got = %v, want %v", tfTunnels, tt.expected)
+ }
+ }
+ })
+ }
+}
diff --git a/stackit/internal/services/vpn/vpn_acc_test.go b/stackit/internal/services/vpn/vpn_acc_test.go
index 535e2061e..387a5ed1f 100644
--- a/stackit/internal/services/vpn/vpn_acc_test.go
+++ b/stackit/internal/services/vpn/vpn_acc_test.go
@@ -127,6 +127,38 @@ func TestAccVpnGatewayResourceMin(t *testing.T) {
resource.TestCheckResourceAttrPair("data.stackit_vpn_gateway.gateway", "gateway_id", "stackit_vpn_gateway.gateway", "gateway_id"),
),
},
+ // Status data source
+ {
+ ConfigVariables: gatewayMinVars,
+ Config: fmt.Sprintf(`
+ %s
+ %s
+
+ data "stackit_vpn_gateway_status" "gateway" {
+ project_id = stackit_vpn_gateway.gateway.project_id
+ gateway_id = stackit_vpn_gateway.gateway.gateway_id
+ }
+ `,
+ testutil.NewConfigBuilder().BuildProviderConfig(), gatewayMinConfig,
+ ),
+ Check: resource.ComposeAggregateTestCheckFunc(
+ resource.TestCheckResourceAttrPair("data.stackit_vpn_gateway_status.gateway", "gateway_id", "stackit_vpn_gateway.gateway", "gateway_id"),
+ resource.TestCheckResourceAttr("data.stackit_vpn_gateway_status.gateway", "project_id", testutil.ProjectId),
+ resource.TestCheckResourceAttr("data.stackit_vpn_gateway_status.gateway", "region", testutil.Region),
+ resource.TestCheckResourceAttr("data.stackit_vpn_gateway_status.gateway", "display_name", testutil.ConvertConfigVariable(gatewayMinVars["display_name"])),
+
+ resource.TestCheckResourceAttr("data.stackit_vpn_gateway_status.gateway", "tunnels.#", "2"),
+ resource.TestCheckResourceAttr("data.stackit_vpn_gateway_status.gateway", "tunnels.0.name", string(vpn.VPNTUNNELSNAME_TUNNEL1)),
+ resource.TestCheckResourceAttrSet("data.stackit_vpn_gateway_status.gateway", "tunnels.0.internal_next_hop_ip"),
+ resource.TestCheckResourceAttrSet("data.stackit_vpn_gateway_status.gateway", "tunnels.0.public_ip"),
+
+ resource.TestCheckResourceAttr("data.stackit_vpn_gateway_status.gateway", "tunnels.1.name", string(vpn.VPNTUNNELSNAME_TUNNEL2)),
+ resource.TestCheckResourceAttrSet("data.stackit_vpn_gateway_status.gateway", "tunnels.1.internal_next_hop_ip"),
+ resource.TestCheckResourceAttrSet("data.stackit_vpn_gateway_status.gateway", "tunnels.1.public_ip"),
+
+ resource.TestCheckResourceAttr("data.stackit_vpn_gateway_status.gateway", "connections.#", "0"),
+ ),
+ },
// Update
{
ConfigVariables: gatewayMinVarsUpdated,
@@ -226,6 +258,38 @@ func TestAccVpnGatewayResourceMax(t *testing.T) {
resource.TestCheckResourceAttrPair("data.stackit_vpn_gateway.gateway", "gateway_id", "stackit_vpn_gateway.gateway", "gateway_id"),
),
},
+ // Status data source
+ {
+ ConfigVariables: gatewayMaxVars,
+ Config: fmt.Sprintf(`
+ %s
+ %s
+
+ data "stackit_vpn_gateway_status" "gateway" {
+ project_id = stackit_vpn_gateway.gateway.project_id
+ gateway_id = stackit_vpn_gateway.gateway.gateway_id
+ }
+ `,
+ testutil.NewConfigBuilder().BuildProviderConfig(), gatewayMaxConfig,
+ ),
+ Check: resource.ComposeAggregateTestCheckFunc(
+ resource.TestCheckResourceAttrPair("data.stackit_vpn_gateway_status.gateway", "gateway_id", "stackit_vpn_gateway.gateway", "gateway_id"),
+ resource.TestCheckResourceAttr("data.stackit_vpn_gateway_status.gateway", "project_id", testutil.ProjectId),
+ resource.TestCheckResourceAttr("data.stackit_vpn_gateway_status.gateway", "region", testutil.Region),
+ resource.TestCheckResourceAttr("data.stackit_vpn_gateway_status.gateway", "display_name", testutil.ConvertConfigVariable(gatewayMaxVars["display_name"])),
+
+ resource.TestCheckResourceAttr("data.stackit_vpn_gateway_status.gateway", "tunnels.#", "2"),
+ resource.TestCheckResourceAttr("data.stackit_vpn_gateway_status.gateway", "tunnels.0.name", string(vpn.VPNTUNNELSNAME_TUNNEL1)),
+ resource.TestCheckResourceAttrSet("data.stackit_vpn_gateway_status.gateway", "tunnels.0.internal_next_hop_ip"),
+ resource.TestCheckResourceAttrSet("data.stackit_vpn_gateway_status.gateway", "tunnels.0.public_ip"),
+
+ resource.TestCheckResourceAttr("data.stackit_vpn_gateway_status.gateway", "tunnels.1.name", string(vpn.VPNTUNNELSNAME_TUNNEL2)),
+ resource.TestCheckResourceAttrSet("data.stackit_vpn_gateway_status.gateway", "tunnels.1.internal_next_hop_ip"),
+ resource.TestCheckResourceAttrSet("data.stackit_vpn_gateway_status.gateway", "tunnels.1.public_ip"),
+
+ resource.TestCheckResourceAttr("data.stackit_vpn_gateway_status.gateway", "connections.#", "0"),
+ ),
+ },
// Update
{
ConfigVariables: gatewayMaxVarsUpdated,
diff --git a/stackit/provider.go b/stackit/provider.go
index 0470af21a..7c653f474 100644
--- a/stackit/provider.go
+++ b/stackit/provider.go
@@ -129,6 +129,7 @@ import (
telemetryRouterDestination "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/services/telemetryrouter/destination"
telemetryRouterInstance "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/services/telemetryrouter/instance"
vpnGateway "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/services/vpn/gateway"
+ vpnGatewayStatus "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/services/vpn/gateway_status"
"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/utils"
)
@@ -757,6 +758,7 @@ func (p *Provider) DataSources(_ context.Context) []func() datasource.DataSource
telemetryRouterDestination.NewTelemetryRouterDestinationDataSource,
telemetryLink.NewTelemetryLinkDataSource,
vpnGateway.NewVPNGatewayDataSource,
+ vpnGatewayStatus.NewVPNGatewayStatusDataSource,
}
dataSources = append(dataSources, customRole.NewCustomRoleDataSources()...)
dataSources = append(dataSources, iamRoleBindingsV1.NewRoleBindingsDatasources()...)