diff --git a/docs/data-sources/sqlserverflex_flavors.md b/docs/data-sources/sqlserverflex_flavors.md
new file mode 100644
index 000000000..de13eed00
--- /dev/null
+++ b/docs/data-sources/sqlserverflex_flavors.md
@@ -0,0 +1,75 @@
+---
+# generated by https://github.com/hashicorp/terraform-plugin-docs
+page_title: "stackit_sqlserverflex_flavors Data Source - stackit"
+subcategory: ""
+description: |-
+ SqlserverFlex flavors data source schema
+---
+
+# stackit_sqlserverflex_flavors (Data Source)
+
+SqlserverFlex flavors data source schema
+
+## Example Usage
+
+```terraform
+data "stackit_sqlserverflex_flavors" "example" {
+ project_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
+ // region taken from provider
+}
+
+// example usage with instance
+resource "stackit_sqlserverflex_instance" "example" {
+ project_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
+ name = "example"
+ flavor_id = one([for f in data.stackit_sqlserverflex_flavors.example.flavors : f.id if f.cpu == 4 && f.memory == 16])
+}
+```
+
+
+## Schema
+
+### Required
+
+- `project_id` (String) The project ID.
+
+### Optional
+
+- `region` (String) SqlserverFlex flavors data source region. If undefined the providers region is used.
+- `timeouts` (Attributes) (see [below for nested schema](#nestedatt--timeouts))
+
+### Read-Only
+
+- `flavors` (Attributes List) A list of available flavors. (see [below for nested schema](#nestedatt--flavors))
+- `id` (String) Terraform's internal data source ID, structured as "`project_id`,`region`"
+
+
+### Nested Schema for `timeouts`
+
+Optional:
+
+- `read` (String) A string that can be [parsed as a duration](https://pkg.go.dev/time#ParseDuration) consisting of numbers and unit suffixes, such as "30s" or "2h45m". Valid time units are "s" (seconds), "m" (minutes), "h" (hours).
+
+
+
+### Nested Schema for `flavors`
+
+Read-Only:
+
+- `cpu` (Number) The CPU count of the instance.
+- `description` (String) Description of the flavor.
+- `id` (String) ID of the flavor.
+- `max_gb` (Number) Maximum storage, which can be ordered for the flavor in Gigabyte.
+- `memory` (Number) The memory (Gibibyte) of the instance.
+- `min_gb` (Number) Minimum storage, which is required to order in Gigabyte.
+- `node_type` (String) Defines the node type (either single or HA).
+- `storage_classes` (Attributes List) ⚠️TODO, also nested attr descriptions are freestyled (see [below for nested schema](#nestedatt--flavors--storage_classes))
+
+
+### Nested Schema for `flavors.storage_classes`
+
+Read-Only:
+
+- `class` (String) Class of the instance.
+- `max_io_per_sec` (Number) Maximum I/O per second.
+- `max_through_in_mb` (Number) Maximum throughput in Megabyte.
diff --git a/examples/data-sources/stackit_sqlserverflex_flavors/data-source.tf b/examples/data-sources/stackit_sqlserverflex_flavors/data-source.tf
new file mode 100644
index 000000000..0e4d09b19
--- /dev/null
+++ b/examples/data-sources/stackit_sqlserverflex_flavors/data-source.tf
@@ -0,0 +1,12 @@
+data "stackit_sqlserverflex_flavors" "example" {
+ project_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
+ // region taken from provider
+}
+
+// example usage with instance
+resource "stackit_sqlserverflex_instance" "example" {
+ project_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
+ name = "example"
+ flavor_id = one([for f in data.stackit_sqlserverflex_flavors.example.flavors : f.id if f.cpu == 4 && f.memory == 16])
+}
+
diff --git a/stackit/internal/services/sqlserverflex/flavors/datasource.go b/stackit/internal/services/sqlserverflex/flavors/datasource.go
new file mode 100644
index 000000000..20f40bd3a
--- /dev/null
+++ b/stackit/internal/services/sqlserverflex/flavors/datasource.go
@@ -0,0 +1,257 @@
+package flavors
+
+import (
+ "context"
+ "fmt"
+ "slices"
+ "strings"
+
+ "github.com/hashicorp/terraform-plugin-framework-timeouts/datasource/timeouts"
+ "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-log/tflog"
+ sqlserverflex "github.com/stackitcloud/stackit-sdk-go/services/sqlserverflex/v3api"
+
+ "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/conversion"
+ "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/core"
+ sqlserverflexUtils "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/services/sqlserverflex/utils"
+ "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/utils"
+ "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/validate"
+)
+
+var (
+ _ datasource.DataSource = new(flavors)
+ _ datasource.DataSourceWithConfigure = new(flavors)
+)
+
+type model struct {
+ ID types.String `tfsdk:"id"`
+ ProjectId types.String `tfsdk:"project_id"`
+ Region types.String `tfsdk:"region"`
+ Flavors []flavor `tfsdk:"flavors"`
+ Timeouts timeouts.Value `tfsdk:"timeouts"`
+}
+
+type flavor struct {
+ Id types.String `tfsdk:"id"`
+ Description types.String `tfsdk:"description"`
+ CPU types.Int64 `tfsdk:"cpu"`
+ Memory types.Int64 `tfsdk:"memory"`
+ MinGB types.Int32 `tfsdk:"min_gb"`
+ MaxGB types.Int32 `tfsdk:"max_gb"`
+ NodeType types.String `tfsdk:"node_type"`
+ StorageClasses []storageClass `tfsdk:"storage_classes"`
+}
+
+type storageClass struct {
+ Class types.String `tfsdk:"class"`
+ MaxIOPerSec types.Int32 `tfsdk:"max_io_per_sec"`
+ MaxThroughInMB types.Int32 `tfsdk:"max_through_in_mb"`
+}
+
+type flavors struct {
+ client *sqlserverflex.APIClient
+ providerData core.ProviderData
+}
+
+func NewFlavorsDataSource() datasource.DataSource {
+ return new(flavors)
+}
+
+func (f *flavors) Metadata(_ context.Context, req datasource.MetadataRequest, resp *datasource.MetadataResponse) {
+ resp.TypeName = req.ProviderTypeName + "_sqlserverflex_flavors"
+}
+
+func (f *flavors) Configure(ctx context.Context, req datasource.ConfigureRequest, resp *datasource.ConfigureResponse) {
+ var ok bool
+ f.providerData, ok = conversion.ParseProviderData(ctx, req.ProviderData, &resp.Diagnostics)
+ if !ok {
+ return
+ }
+
+ apiClient := sqlserverflexUtils.ConfigureClient(ctx, &f.providerData, &resp.Diagnostics)
+ if resp.Diagnostics.HasError() {
+ return
+ }
+ f.client = apiClient
+ tflog.Info(ctx, "SqlserverFlex client configured")
+}
+
+func (f *flavors) Schema(ctx context.Context, _ datasource.SchemaRequest, resp *datasource.SchemaResponse) {
+ resp.Schema = schema.Schema{
+ Description: "SqlserverFlex flavors data source schema",
+ Attributes: map[string]schema.Attribute{
+ "id": schema.StringAttribute{
+ Description: "Terraform's internal data source ID, structured as \"`project_id`,`region`\"",
+ Computed: true,
+ },
+ "project_id": schema.StringAttribute{
+ Description: "The project ID.",
+ Required: true,
+ Validators: []validator.String{
+ validate.UUID(),
+ validate.NoSeparator(),
+ },
+ },
+ "region": schema.StringAttribute{
+ Description: "SqlserverFlex flavors data source region. If undefined the providers region is used.",
+ Optional: true,
+ Computed: true,
+ },
+ "timeouts": timeouts.Attributes(ctx),
+ "flavors": schema.ListNestedAttribute{
+ Description: "A list of available flavors.",
+ Computed: true,
+ NestedObject: schema.NestedAttributeObject{
+ Attributes: map[string]schema.Attribute{
+ "id": schema.StringAttribute{
+ Description: "ID of the flavor.",
+ Computed: true,
+ },
+ "description": schema.StringAttribute{
+ Description: "Description of the flavor.",
+ Computed: true,
+ },
+ "cpu": schema.Int64Attribute{
+ Description: "The CPU count of the instance.",
+ Computed: true,
+ },
+ "memory": schema.Int64Attribute{
+ Description: "The memory (Gibibyte) of the instance.",
+ Computed: true,
+ },
+ "min_gb": schema.Int64Attribute{
+ Description: "Minimum storage, which is required to order in Gigabyte.",
+ Computed: true,
+ },
+ "max_gb": schema.Int64Attribute{
+ Description: "Maximum storage, which can be ordered for the flavor in Gigabyte.",
+ Computed: true,
+ },
+ "node_type": schema.StringAttribute{
+ Description: "Defines the node type (either single or HA).",
+ Computed: true,
+ },
+ "storage_classes": schema.ListNestedAttribute{
+ Description: "⚠️TODO, also nested attr descriptions are freestyled",
+ Computed: true,
+ NestedObject: schema.NestedAttributeObject{
+ Attributes: map[string]schema.Attribute{
+ "class": schema.StringAttribute{
+ Description: "Class of the instance.",
+ Computed: true,
+ },
+ "max_io_per_sec": schema.Int32Attribute{
+ Description: "Maximum I/O per second.",
+ Computed: true,
+ },
+ "max_through_in_mb": schema.Int32Attribute{
+ Description: "Maximum throughput in Megabyte.",
+ Computed: true,
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ }
+}
+
+func (f *flavors) 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
+ }
+
+ readTimeout, diags := model.Timeouts.Read(ctx, core.DefaultOperationTimeout)
+ resp.Diagnostics.Append(diags...)
+ if resp.Diagnostics.HasError() {
+ return
+ }
+ ctx, cancel := context.WithTimeout(ctx, readTimeout)
+ defer cancel()
+
+ projectId := model.ProjectId.ValueString()
+ region := f.providerData.GetRegionWithOverride(model.Region)
+ ctx = utils.SetAndLogStateFields(ctx, &resp.Diagnostics, &resp.State, map[string]any{
+ "project_id": projectId,
+ "region": region,
+ })
+
+ ctx = core.InitProviderContext(ctx)
+
+ const pageSize = 100
+ flavorsResp, err := f.client.DefaultAPI.ListFlavors(ctx, projectId, region).Size(pageSize).Execute()
+ if err != nil {
+ core.LogAndAddError(ctx, &resp.Diagnostics, "Reading flavors", fmt.Sprintf("Error calling ListFlavors: %v", err))
+ return
+ }
+ if flavorsResp.Pagination.TotalRows > pageSize {
+ core.LogAndAddWarning(ctx, &resp.Diagnostics,
+ "Truncated results",
+ fmt.Sprintf("Due to API limitations we currently do not support more than %d flavors, but %d exist. The result is truncated", pageSize, flavorsResp.Pagination.TotalRows),
+ )
+ }
+
+ ctx = core.LogResponse(ctx)
+
+ err = mapFields(flavorsResp, &model)
+ if err != nil {
+ core.LogAndAddError(ctx, &resp.Diagnostics, "Reading Flavors", fmt.Sprintf("Processing API payload: %v", err))
+ return
+ }
+
+ resp.Diagnostics.Append(resp.State.Set(ctx, model)...)
+ if resp.Diagnostics.HasError() {
+ return
+ }
+ tflog.Info(ctx, "SqlserverFlex flavor read")
+}
+
+func mapFields(resp *sqlserverflex.ListFlavorsResponse, m *model) error {
+ if resp == nil {
+ return fmt.Errorf("nil response")
+ }
+ if m == nil {
+ return fmt.Errorf("nil model")
+ }
+
+ m.ID = utils.BuildInternalTerraformId(m.ProjectId.ValueString(), m.Region.ValueString())
+
+ slices.SortFunc(resp.Flavors, func(a, b sqlserverflex.ListFlavors) int {
+ return strings.Compare(a.Id, b.Id)
+ })
+
+ for _, respFlavor := range resp.Flavors {
+ modelFlavor := flavor{
+ Id: types.StringValue(respFlavor.Id),
+ Description: types.StringValue(respFlavor.Description),
+ CPU: types.Int64Value(respFlavor.Cpu),
+ Memory: types.Int64Value(respFlavor.Memory),
+ MinGB: types.Int32Value(respFlavor.MinGB),
+ MaxGB: types.Int32Value(respFlavor.MaxGB),
+ NodeType: types.StringValue(respFlavor.NodeType),
+ }
+
+ slices.SortFunc(respFlavor.StorageClasses, func(a, b sqlserverflex.FlavorStorageClassesStorageClass) int {
+ return strings.Compare(a.Class, b.Class)
+ })
+
+ for _, respSC := range respFlavor.StorageClasses {
+ modelSC := storageClass{
+ Class: types.StringValue(respSC.Class),
+ MaxIOPerSec: types.Int32Value(respSC.MaxIoPerSec),
+ MaxThroughInMB: types.Int32Value(respSC.MaxThroughInMb),
+ }
+ modelFlavor.StorageClasses = append(modelFlavor.StorageClasses, modelSC)
+ }
+ m.Flavors = append(m.Flavors, modelFlavor)
+ }
+ return nil
+}
diff --git a/stackit/internal/services/sqlserverflex/flavors/datasource_test.go b/stackit/internal/services/sqlserverflex/flavors/datasource_test.go
new file mode 100644
index 000000000..44bb1f245
--- /dev/null
+++ b/stackit/internal/services/sqlserverflex/flavors/datasource_test.go
@@ -0,0 +1,165 @@
+package flavors
+
+import (
+ "testing"
+
+ "github.com/google/go-cmp/cmp"
+ "github.com/hashicorp/terraform-plugin-framework/types"
+ sqlserverflex "github.com/stackitcloud/stackit-sdk-go/services/sqlserverflex/v3api"
+)
+
+func TestMapFields(t *testing.T) {
+ tests := []struct {
+ description string
+ input *sqlserverflex.ListFlavorsResponse
+ state *model
+ expected *model
+ isValid bool
+ }{
+ {
+ description: "default_values_and_sorting",
+ input: &sqlserverflex.ListFlavorsResponse{
+ Flavors: []sqlserverflex.ListFlavors{
+ {
+ Id: "id2",
+ Description: "desc2",
+ Cpu: 4,
+ Memory: 8,
+ MinGB: 20,
+ MaxGB: 200,
+ NodeType: "ha",
+ StorageClasses: []sqlserverflex.FlavorStorageClassesStorageClass{
+ {
+ Class: "class2",
+ MaxIoPerSec: 2000,
+ MaxThroughInMb: 200,
+ },
+ {
+ Class: "class1",
+ MaxIoPerSec: 1000,
+ MaxThroughInMb: 100,
+ },
+ },
+ },
+ {
+ Id: "id1",
+ Description: "desc1",
+ Cpu: 2,
+ Memory: 4,
+ MinGB: 10,
+ MaxGB: 100,
+ NodeType: "single",
+ StorageClasses: []sqlserverflex.FlavorStorageClassesStorageClass{
+ {
+ Class: "class3",
+ MaxIoPerSec: 3000,
+ MaxThroughInMb: 300,
+ },
+ },
+ },
+ },
+ },
+ state: &model{
+ ProjectId: types.StringValue("project_id"),
+ Region: types.StringValue("region"),
+ },
+ expected: &model{
+ ID: types.StringValue("project_id,region"),
+ ProjectId: types.StringValue("project_id"),
+ Region: types.StringValue("region"),
+ Flavors: []flavor{
+ {
+ Id: types.StringValue("id1"),
+ Description: types.StringValue("desc1"),
+ CPU: types.Int64Value(2),
+ Memory: types.Int64Value(4),
+ MinGB: types.Int32Value(10),
+ MaxGB: types.Int32Value(100),
+ NodeType: types.StringValue("single"),
+ StorageClasses: []storageClass{
+ {
+ Class: types.StringValue("class3"),
+ MaxIOPerSec: types.Int32Value(3000),
+ MaxThroughInMB: types.Int32Value(300),
+ },
+ },
+ },
+ {
+ Id: types.StringValue("id2"),
+ Description: types.StringValue("desc2"),
+ CPU: types.Int64Value(4),
+ Memory: types.Int64Value(8),
+ MinGB: types.Int32Value(20),
+ MaxGB: types.Int32Value(200),
+ NodeType: types.StringValue("ha"),
+ StorageClasses: []storageClass{
+ {
+ Class: types.StringValue("class1"),
+ MaxIOPerSec: types.Int32Value(1000),
+ MaxThroughInMB: types.Int32Value(100),
+ },
+ {
+ Class: types.StringValue("class2"),
+ MaxIOPerSec: types.Int32Value(2000),
+ MaxThroughInMB: types.Int32Value(200),
+ },
+ },
+ },
+ },
+ },
+ isValid: true,
+ },
+ {
+ description: "empty_response",
+ input: &sqlserverflex.ListFlavorsResponse{},
+ state: &model{
+ ProjectId: types.StringValue("project_id"),
+ Region: types.StringValue("region"),
+ },
+ expected: &model{
+ ID: types.StringValue("project_id,region"),
+ ProjectId: types.StringValue("project_id"),
+ Region: types.StringValue("region"),
+ },
+ isValid: true,
+ },
+ {
+ description: "nil_response",
+ input: nil,
+ state: &model{
+ ProjectId: types.StringValue("project_id"),
+ Region: types.StringValue("region"),
+ },
+ expected: &model{
+ ProjectId: types.StringValue("project_id"),
+ Region: types.StringValue("region"),
+ },
+ isValid: false,
+ },
+ {
+ description: "nil_model",
+ input: &sqlserverflex.ListFlavorsResponse{},
+ state: nil,
+ expected: nil,
+ isValid: false,
+ },
+ }
+
+ for _, tt := range tests {
+ t.Run(tt.description, func(t *testing.T) {
+ err := mapFields(tt.input, tt.state)
+ if !tt.isValid && err == nil {
+ t.Fatalf("Should have failed")
+ }
+ if tt.isValid && err != nil {
+ t.Fatalf("Should not have failed: %v", err)
+ }
+ if tt.isValid {
+ diff := cmp.Diff(tt.expected, tt.state)
+ if diff != "" {
+ t.Fatalf("Data does not match: %s", diff)
+ }
+ }
+ })
+ }
+}
diff --git a/stackit/internal/services/sqlserverflex/sqlserverflex_acc_test.go b/stackit/internal/services/sqlserverflex/sqlserverflex_acc_test.go
index ad7f44ccd..9dadf38f2 100644
--- a/stackit/internal/services/sqlserverflex/sqlserverflex_acc_test.go
+++ b/stackit/internal/services/sqlserverflex/sqlserverflex_acc_test.go
@@ -418,6 +418,39 @@ func TestAccSQLServerFlexMaxResource(t *testing.T) {
})
}
+func TestAccSqlServerFlexFlavorsDatasource(t *testing.T) {
+ resource.Test(t, resource.TestCase{
+ ProtoV6ProviderFactories: testutil.TestAccProtoV6ProviderFactories,
+ Steps: []resource.TestStep{
+ {
+ ConfigVariables: config.Variables{
+ "project_id": config.StringVariable(testutil.ProjectId),
+ },
+ Config: fmt.Sprintf(`
+ %s
+
+ variable "project_id" {}
+
+ data "stackit_sqlserverflex_flavors" "datasource" {
+ project_id = var.project_id
+ }`, testutil.NewConfigBuilder().BuildProviderConfig()),
+ Check: resource.ComposeAggregateTestCheckFunc(
+ resource.TestCheckResourceAttrSet("data.stackit_sqlserverflex_flavors.datasource", "flavors.0.id"),
+ resource.TestCheckResourceAttrSet("data.stackit_sqlserverflex_flavors.datasource", "flavors.0.description"),
+ resource.TestCheckResourceAttrSet("data.stackit_sqlserverflex_flavors.datasource", "flavors.0.cpu"),
+ resource.TestCheckResourceAttrSet("data.stackit_sqlserverflex_flavors.datasource", "flavors.0.memory"),
+ resource.TestCheckResourceAttrSet("data.stackit_sqlserverflex_flavors.datasource", "flavors.0.min_gb"),
+ resource.TestCheckResourceAttrSet("data.stackit_sqlserverflex_flavors.datasource", "flavors.0.max_gb"),
+ resource.TestCheckResourceAttrSet("data.stackit_sqlserverflex_flavors.datasource", "flavors.0.node_type"),
+ resource.TestCheckResourceAttrSet("data.stackit_sqlserverflex_flavors.datasource", "flavors.0.storage_classes.0.class"),
+ resource.TestCheckResourceAttrSet("data.stackit_sqlserverflex_flavors.datasource", "flavors.0.storage_classes.0.max_io_per_sec"),
+ resource.TestCheckResourceAttrSet("data.stackit_sqlserverflex_flavors.datasource", "flavors.0.storage_classes.0.max_through_in_mb"),
+ ),
+ },
+ },
+ })
+}
+
func testAccChecksqlserverflexDestroy(s *terraform.State) error {
ctx := context.Background()
client, err := sqlserverflex.NewAPIClient(testutil.NewConfigBuilder().BuildClientOptions(testutil.SQLServerFlexCustomEndpoint, false)...)
diff --git a/stackit/provider.go b/stackit/provider.go
index f99c5eb3c..c985a2ada 100644
--- a/stackit/provider.go
+++ b/stackit/provider.go
@@ -129,6 +129,7 @@ import (
skeKubeconfig "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/services/ske/kubeconfig"
skeKubernetesVersion "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/services/ske/provideroptions/kubernetesversions"
skeMachineImages "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/services/ske/provideroptions/machineimages"
+ sqlServerFlexFlavors "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/services/sqlserverflex/flavors"
sqlServerFlexInstance "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/services/sqlserverflex/instance"
sqlServerFlexUser "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/services/sqlserverflex/user"
telemetryLink "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/services/telemetrylink/link"
@@ -756,6 +757,7 @@ func (p *Provider) DataSources(_ context.Context) []func() datasource.DataSource
secretsManagerUser.NewUserDataSource,
sqlServerFlexInstance.NewInstanceDataSource,
sqlServerFlexUser.NewUserDataSource,
+ sqlServerFlexFlavors.NewFlavorsDataSource,
serverBackupSchedule.NewScheduleDataSource,
serverBackupSchedule.NewSchedulesDataSource,
serverUpdateSchedule.NewScheduleDataSource,