diff --git a/.gitignore b/.gitignore
index c588a0f1..1dac2ea3 100644
--- a/.gitignore
+++ b/.gitignore
@@ -46,3 +46,5 @@ dist
pkg_gen
/release/
+.env
+**/.env
diff --git a/docs/data-sources/sqlserverflexalpha_database.md b/docs/data-sources/sqlserverflexalpha_database.md
index 20e8a653..df66ffb7 100644
--- a/docs/data-sources/sqlserverflexalpha_database.md
+++ b/docs/data-sources/sqlserverflexalpha_database.md
@@ -26,7 +26,7 @@ description: |-
- `collation_name` (String) The collation of the database. This database collation should match the *collation_name* of one of the collations given by the **Get database collation list** endpoint.
- `compatibility_level` (Number) CompatibilityLevel of the Database.
-- `id` (String) Terraform's internal resource ID. It is structured as \"`project_id`,`region`,`instance_id`,`database_id`\".",
+- `id` (String) The terraform internal identifier.
- `name` (String) The name of the database.
- `owner` (String) The owner of the database.
- `tf_original_api_id` (Number) The id of the database.
diff --git a/docs/data-sources/sqlserverflexalpha_flavor.md b/docs/data-sources/sqlserverflexalpha_flavor.md
index 0dfc1fd2..7a03ecfb 100644
--- a/docs/data-sources/sqlserverflexalpha_flavor.md
+++ b/docs/data-sources/sqlserverflexalpha_flavor.md
@@ -29,20 +29,20 @@ data "stackitprivatepreview_sqlserverflexalpha_flavor" "flavor" {
### Required
- `cpu` (Number) The cpu count of the instance.
-- `node_type` (String) defines the nodeType it can be either single or replica
-- `project_id` (String) The cpu count of the instance.
+- `node_type` (String) defines the nodeType it can be either single or HA
+- `project_id` (String) The project ID of the flavor.
- `ram` (Number) The memory of the instance in Gibibyte.
-- `region` (String) The flavor description.
+- `region` (String) The region of the flavor.
- `storage_class` (String) The memory of the instance in Gibibyte.
### Read-Only
- `description` (String) The flavor description.
-- `flavor_id` (String) The flavor id of the instance flavor.
-- `id` (String) The terraform id of the instance flavor.
+- `flavor_id` (String) The id of the instance flavor.
+- `id` (String) The id of the instance flavor.
- `max_gb` (Number) maximum storage which can be ordered for the flavor in Gigabyte.
- `min_gb` (Number) minimum storage which is required to order in Gigabyte.
-- `storage_classes` (Attributes List) (see [below for nested schema](#nestedatt--storage_classes))
+- `storage_classes` (Attributes List) maximum storage which can be ordered for the flavor in Gigabyte. (see [below for nested schema](#nestedatt--storage_classes))
### Nested Schema for `storage_classes`
diff --git a/docs/data-sources/sqlserverflexalpha_instance.md b/docs/data-sources/sqlserverflexalpha_instance.md
index f209c424..b05d7b8e 100644
--- a/docs/data-sources/sqlserverflexalpha_instance.md
+++ b/docs/data-sources/sqlserverflexalpha_instance.md
@@ -34,7 +34,6 @@ data "stackitprivatepreview_sqlserverflexalpha_instance" "example" {
- `edition` (String) Edition of the MSSQL server instance
- `encryption` (Attributes) this defines which key to use for storage encryption (see [below for nested schema](#nestedatt--encryption))
- `flavor_id` (String) The id of the instance flavor.
-- `id` (String) Terraform's internal resource ID. It is structured as \"`project_id`,`region`,`instance_id`\".
- `is_deletable` (Boolean) Whether the instance can be deleted or not.
- `name` (String) The name of the instance.
- `network` (Attributes) The access configuration of the instance (see [below for nested schema](#nestedatt--network))
diff --git a/docs/data-sources/sqlserverflexalpha_user.md b/docs/data-sources/sqlserverflexalpha_user.md
index b0b15341..63526135 100644
--- a/docs/data-sources/sqlserverflexalpha_user.md
+++ b/docs/data-sources/sqlserverflexalpha_user.md
@@ -3,12 +3,12 @@
page_title: "stackitprivatepreview_sqlserverflexalpha_user Data Source - stackitprivatepreview"
subcategory: ""
description: |-
- SQLServer Flex user data source schema. Must have a region specified in the provider configuration.
+
---
# stackitprivatepreview_sqlserverflexalpha_user (Data Source)
-SQLServer Flex user data source schema. Must have a `region` specified in the provider configuration.
+
## Example Usage
@@ -25,20 +25,38 @@ data "stackitprivatepreview_sqlserverflexalpha_user" "example" {
### Required
-- `instance_id` (String) ID of the SQLServer Flex instance.
-- `project_id` (String) STACKIT project ID to which the instance is associated.
-- `user_id` (Number) User ID.
+- `instance_id` (String) The ID of the instance.
+- `project_id` (String) The STACKIT project ID.
+- `region` (String) The region which should be addressed
### Optional
-- `region` (String) The resource region. If not defined, the provider region is used.
+- `page` (Number) Number of the page of items list to be returned.
+- `size` (Number) Number of items to be returned on each page.
+- `sort` (String) Sorting of the users to be returned on each page.
### Read-Only
-- `default_database` (String)
-- `host` (String)
-- `id` (String) Terraform's internal data source. ID. It is structured as "`project_id`,`region`,`instance_id`,`user_id`".
-- `port` (Number)
-- `roles` (Set of String) Database access levels for the user.
-- `status` (String)
-- `username` (String) Username of the SQLServer Flex instance.
+- `pagination` (Attributes) (see [below for nested schema](#nestedatt--pagination))
+- `users` (Attributes List) List of all users inside an instance (see [below for nested schema](#nestedatt--users))
+
+
+### Nested Schema for `pagination`
+
+Read-Only:
+
+- `page` (Number)
+- `size` (Number)
+- `sort` (String)
+- `total_pages` (Number)
+- `total_rows` (Number)
+
+
+
+### Nested Schema for `users`
+
+Read-Only:
+
+- `status` (String) The current status of the user.
+- `tf_original_api_id` (Number) The ID of the user.
+- `username` (String) The name of the user.
diff --git a/docs/resources/postgresflexalpha_user.md b/docs/resources/postgresflexalpha_user.md
index d68b920c..d0b9c500 100644
--- a/docs/resources/postgresflexalpha_user.md
+++ b/docs/resources/postgresflexalpha_user.md
@@ -44,7 +44,6 @@ import {
### Read-Only
-- `connection_string` (String) The connection string for the user to the instance.
- `id` (Number) The ID of the user.
- `password` (String) The password for the user.
- `status` (String) The current status of the user.
diff --git a/sample/postgres/postresql.tf b/sample/postgres/postresql.tf
index fa2f49e8..531b17e2 100644
--- a/sample/postgres/postresql.tf
+++ b/sample/postgres/postresql.tf
@@ -65,15 +65,15 @@ resource "stackitprivatepreview_postgresflexalpha_instance" "msh-sna-pe-example2
resource "stackitprivatepreview_postgresflexalpha_user" "ptlsdbadminuser" {
project_id = var.project_id
instance_id = stackitprivatepreview_postgresflexalpha_instance.msh-sna-pe-example.instance_id
- username = var.db_admin_username
- roles = ["createdb", "login"]
+ name = var.db_admin_username
+ roles = ["createdb", "login", "login"]
# roles = ["createdb", "login", "createrole"]
}
resource "stackitprivatepreview_postgresflexalpha_user" "ptlsdbadminuser2" {
project_id = var.project_id
instance_id = stackitprivatepreview_postgresflexalpha_instance.msh-sna-pe-example2.instance_id
- username = var.db_admin_username
+ name = var.db_admin_username
roles = ["createdb", "login"]
# roles = ["createdb", "login", "createrole"]
}
@@ -81,7 +81,7 @@ resource "stackitprivatepreview_postgresflexalpha_user" "ptlsdbadminuser2" {
resource "stackitprivatepreview_postgresflexalpha_user" "ptlsdbuser" {
project_id = var.project_id
instance_id = stackitprivatepreview_postgresflexalpha_instance.msh-sna-pe-example.instance_id
- username = var.db_username
+ name = var.db_name
roles = ["login"]
# roles = ["createdb", "login", "createrole"]
}
diff --git a/stackit/internal/services/postgresflexalpha/user/resource.go b/stackit/internal/services/postgresflexalpha/user/resource.go
index 4adbaff0..cf9fd389 100644
--- a/stackit/internal/services/postgresflexalpha/user/resource.go
+++ b/stackit/internal/services/postgresflexalpha/user/resource.go
@@ -5,6 +5,7 @@ import (
_ "embed"
"fmt"
"math"
+ "slices"
"strconv"
"strings"
"time"
@@ -29,11 +30,12 @@ import (
var (
// Ensure the implementation satisfies the expected interfaces.
- _ resource.Resource = &userResource{}
- _ resource.ResourceWithConfigure = &userResource{}
- _ resource.ResourceWithImportState = &userResource{}
- _ resource.ResourceWithModifyPlan = &userResource{}
- _ resource.ResourceWithIdentity = &userResource{}
+ _ resource.Resource = &userResource{}
+ _ resource.ResourceWithConfigure = &userResource{}
+ _ resource.ResourceWithImportState = &userResource{}
+ _ resource.ResourceWithModifyPlan = &userResource{}
+ _ resource.ResourceWithIdentity = &userResource{}
+ _ resource.ResourceWithValidateConfig = &userResource{}
// Error message constants
extractErrorSummary = "extracting failed"
@@ -138,6 +140,39 @@ func (r *userResource) Schema(ctx context.Context, _ resource.SchemaRequest, res
resp.Schema = s
}
+func (r *userResource) ValidateConfig(
+ ctx context.Context,
+ req resource.ValidateConfigRequest,
+ resp *resource.ValidateConfigResponse,
+) {
+ var data resourceModel
+
+ resp.Diagnostics.Append(req.Config.Get(ctx, &data)...)
+ if resp.Diagnostics.HasError() {
+ return
+ }
+
+ var roles []string
+ diags := data.Roles.ElementsAs(ctx, &roles, false)
+ resp.Diagnostics.Append(diags...)
+ if diags.HasError() {
+ return
+ }
+
+ var resRoles []string
+ for _, role := range roles {
+ if slices.Contains(resRoles, role) {
+ resp.Diagnostics.AddAttributeError(
+ path.Root("roles"),
+ "Attribute Configuration Error",
+ "defined roles MUST NOT contain duplicates",
+ )
+ return
+ }
+ resRoles = append(resRoles, role)
+ }
+}
+
// Create creates the resource and sets the initial Terraform state.
func (r *userResource) Create(
ctx context.Context,
@@ -217,7 +252,7 @@ func (r *userResource) Create(
model.UserId = types.Int64Value(id)
model.Password = types.StringValue(userResp.GetPassword())
model.Status = types.StringValue(userResp.GetStatus())
- model.ConnectionString = types.StringValue(userResp.GetConnectionString())
+ //model.ConnectionString = types.StringValue(userResp.GetConnectionString())
waitResp, err := postgresflexalphaWait.GetUserByIdWaitHandler(
ctx,
@@ -712,5 +747,6 @@ func (r *userResource) expandRoles(ctx context.Context, rolesSet types.List, dia
}
var roles []string
diags.Append(rolesSet.ElementsAs(ctx, &roles, false)...)
+ slices.Sort(roles)
return roles
}
diff --git a/stackit/internal/services/postgresflexalpha/user/resources_gen/user_resource_gen.go b/stackit/internal/services/postgresflexalpha/user/resources_gen/user_resource_gen.go
index f07ab701..f96d8d93 100644
--- a/stackit/internal/services/postgresflexalpha/user/resources_gen/user_resource_gen.go
+++ b/stackit/internal/services/postgresflexalpha/user/resources_gen/user_resource_gen.go
@@ -14,11 +14,6 @@ import (
func UserResourceSchema(ctx context.Context) schema.Schema {
return schema.Schema{
Attributes: map[string]schema.Attribute{
- "connection_string": schema.StringAttribute{
- Computed: true,
- Description: "The connection string for the user to the instance.",
- MarkdownDescription: "The connection string for the user to the instance.",
- },
"id": schema.Int64Attribute{
Computed: true,
Description: "The ID of the user.",
@@ -80,14 +75,13 @@ func UserResourceSchema(ctx context.Context) schema.Schema {
}
type UserModel struct {
- ConnectionString types.String `tfsdk:"connection_string"`
- Id types.Int64 `tfsdk:"id"`
- InstanceId types.String `tfsdk:"instance_id"`
- Name types.String `tfsdk:"name"`
- Password types.String `tfsdk:"password"`
- ProjectId types.String `tfsdk:"project_id"`
- Region types.String `tfsdk:"region"`
- Roles types.List `tfsdk:"roles"`
- Status types.String `tfsdk:"status"`
- UserId types.Int64 `tfsdk:"user_id"`
+ Id types.Int64 `tfsdk:"id"`
+ InstanceId types.String `tfsdk:"instance_id"`
+ Name types.String `tfsdk:"name"`
+ Password types.String `tfsdk:"password"`
+ ProjectId types.String `tfsdk:"project_id"`
+ Region types.String `tfsdk:"region"`
+ Roles types.List `tfsdk:"roles"`
+ Status types.String `tfsdk:"status"`
+ UserId types.Int64 `tfsdk:"user_id"`
}
diff --git a/stackit/internal/services/sqlserverflexalpha/user/mapper.go b/stackit/internal/services/sqlserverflexalpha/user/mapper.go
index 515c9ddc..8e522d59 100644
--- a/stackit/internal/services/sqlserverflexalpha/user/mapper.go
+++ b/stackit/internal/services/sqlserverflexalpha/user/mapper.go
@@ -2,6 +2,7 @@ package sqlserverflexalpha
import (
"fmt"
+ "slices"
"strconv"
"github.com/hashicorp/terraform-plugin-framework/attr"
@@ -44,8 +45,11 @@ func mapDataSourceFields(userResp *sqlserverflexalpha.GetUserResponse, model *da
if user.Roles == nil {
model.Roles = types.List(types.SetNull(types.StringType))
} else {
+ resRoles := *user.Roles
+ slices.Sort(resRoles)
+
var roles []attr.Value
- for _, role := range *user.Roles {
+ for _, role := range resRoles {
roles = append(roles, types.StringValue(string(role)))
}
rolesSet, diags := types.SetValue(types.StringType, roles)
@@ -92,8 +96,11 @@ func mapFields(userResp *sqlserverflexalpha.GetUserResponse, model *resourceMode
// Map roles
if user.Roles != nil {
+ resRoles := *user.Roles
+ slices.Sort(resRoles)
+
var roles []attr.Value
- for _, role := range *user.Roles {
+ for _, role := range resRoles {
roles = append(roles, types.StringValue(string(role)))
}
rolesSet, diags := types.SetValue(types.StringType, roles)
@@ -139,8 +146,11 @@ func mapFieldsCreate(userResp *sqlserverflexalpha.CreateUserResponse, model *res
model.Password = types.StringValue(*user.Password)
if user.Roles != nil {
+ resRoles := *user.Roles
+ slices.Sort(resRoles)
+
var roles []attr.Value
- for _, role := range *user.Roles {
+ for _, role := range resRoles {
roles = append(roles, types.StringValue(string(role)))
}
rolesSet, diags := types.SetValue(types.StringType, roles)
diff --git a/stackit/internal/services/sqlserverflexalpha/user/resource.go b/stackit/internal/services/sqlserverflexalpha/user/resource.go
index 2b98c94c..8a0c1df7 100644
--- a/stackit/internal/services/sqlserverflexalpha/user/resource.go
+++ b/stackit/internal/services/sqlserverflexalpha/user/resource.go
@@ -6,6 +6,7 @@ import (
"errors"
"fmt"
"net/http"
+ "slices"
"strconv"
"strings"
"time"
@@ -30,11 +31,12 @@ import (
)
var (
- _ resource.Resource = &userResource{}
- _ resource.ResourceWithConfigure = &userResource{}
- _ resource.ResourceWithImportState = &userResource{}
- _ resource.ResourceWithModifyPlan = &userResource{}
- _ resource.ResourceWithIdentity = &userResource{}
+ _ resource.Resource = &userResource{}
+ _ resource.ResourceWithConfigure = &userResource{}
+ _ resource.ResourceWithImportState = &userResource{}
+ _ resource.ResourceWithModifyPlan = &userResource{}
+ _ resource.ResourceWithIdentity = &userResource{}
+ _ resource.ResourceWithValidateConfig = &userResource{}
)
func NewUserResource() resource.Resource {
@@ -156,6 +158,39 @@ func (r *userResource) IdentitySchema(
}
}
+func (r *userResource) ValidateConfig(
+ ctx context.Context,
+ req resource.ValidateConfigRequest,
+ resp *resource.ValidateConfigResponse,
+) {
+ var data resourceModel
+
+ resp.Diagnostics.Append(req.Config.Get(ctx, &data)...)
+ if resp.Diagnostics.HasError() {
+ return
+ }
+
+ var roles []string
+ diags := data.Roles.ElementsAs(ctx, &roles, false)
+ resp.Diagnostics.Append(diags...)
+ if diags.HasError() {
+ return
+ }
+
+ var resRoles []string
+ for _, role := range roles {
+ if slices.Contains(resRoles, role) {
+ resp.Diagnostics.AddAttributeError(
+ path.Root("roles"),
+ "Attribute Configuration Error",
+ "defined roles MUST NOT contain duplicates",
+ )
+ return
+ }
+ resRoles = append(resRoles, role)
+ }
+}
+
// Create creates the resource and sets the initial Terraform state.
func (r *userResource) Create(
ctx context.Context,
@@ -186,6 +221,8 @@ func (r *userResource) Create(
if resp.Diagnostics.HasError() {
return
}
+
+ slices.Sort(roles)
}
// Generate API request body from model
diff --git a/stackit/internal/services/sqlserverflexbeta/user/mapper.go b/stackit/internal/services/sqlserverflexbeta/user/mapper.go
index 9115906f..3674ac0b 100644
--- a/stackit/internal/services/sqlserverflexbeta/user/mapper.go
+++ b/stackit/internal/services/sqlserverflexbeta/user/mapper.go
@@ -2,6 +2,7 @@ package sqlserverflexbeta
import (
"fmt"
+ "slices"
"strconv"
"github.com/hashicorp/terraform-plugin-framework/attr"
@@ -92,10 +93,14 @@ func mapFields(userResp *sqlserverflexbeta.GetUserResponse, model *resourceModel
// Map roles
if user.Roles != nil {
+ resRoles := *user.Roles
+ slices.Sort(resRoles)
+
var roles []attr.Value
- for _, role := range *user.Roles {
+ for _, role := range resRoles {
roles = append(roles, types.StringValue(string(role)))
}
+
rolesSet, diags := types.SetValue(types.StringType, roles)
if diags.HasError() {
return fmt.Errorf("failed to map roles: %w", core.DiagsToError(diags))
@@ -139,8 +144,11 @@ func mapFieldsCreate(userResp *sqlserverflexbeta.CreateUserResponse, model *reso
model.Password = types.StringValue(*user.Password)
if user.Roles != nil {
+ resRoles := *user.Roles
+ slices.Sort(resRoles)
+
var roles []attr.Value
- for _, role := range *user.Roles {
+ for _, role := range resRoles {
roles = append(roles, types.StringValue(string(role)))
}
rolesSet, diags := types.SetValue(types.StringType, roles)
diff --git a/stackit/internal/services/sqlserverflexbeta/user/resource.go b/stackit/internal/services/sqlserverflexbeta/user/resource.go
index 9508c743..8f19eb61 100644
--- a/stackit/internal/services/sqlserverflexbeta/user/resource.go
+++ b/stackit/internal/services/sqlserverflexbeta/user/resource.go
@@ -6,6 +6,7 @@ import (
"errors"
"fmt"
"net/http"
+ "slices"
"strconv"
"strings"
"time"
@@ -30,11 +31,12 @@ import (
)
var (
- _ resource.Resource = &userResource{}
- _ resource.ResourceWithConfigure = &userResource{}
- _ resource.ResourceWithImportState = &userResource{}
- _ resource.ResourceWithModifyPlan = &userResource{}
- _ resource.ResourceWithIdentity = &userResource{}
+ _ resource.Resource = &userResource{}
+ _ resource.ResourceWithConfigure = &userResource{}
+ _ resource.ResourceWithImportState = &userResource{}
+ _ resource.ResourceWithModifyPlan = &userResource{}
+ _ resource.ResourceWithIdentity = &userResource{}
+ _ resource.ResourceWithValidateConfig = &userResource{}
)
func NewUserResource() resource.Resource {
@@ -156,6 +158,39 @@ func (r *userResource) IdentitySchema(
}
}
+func (r *userResource) ValidateConfig(
+ ctx context.Context,
+ req resource.ValidateConfigRequest,
+ resp *resource.ValidateConfigResponse,
+) {
+ var data resourceModel
+
+ resp.Diagnostics.Append(req.Config.Get(ctx, &data)...)
+ if resp.Diagnostics.HasError() {
+ return
+ }
+
+ var roles []string
+ diags := data.Roles.ElementsAs(ctx, &roles, false)
+ resp.Diagnostics.Append(diags...)
+ if diags.HasError() {
+ return
+ }
+
+ var resRoles []string
+ for _, role := range roles {
+ if slices.Contains(resRoles, role) {
+ resp.Diagnostics.AddAttributeError(
+ path.Root("roles"),
+ "Attribute Configuration Error",
+ "defined roles MUST NOT contain duplicates",
+ )
+ return
+ }
+ resRoles = append(resRoles, role)
+ }
+}
+
// Create creates the resource and sets the initial Terraform state.
func (r *userResource) Create(
ctx context.Context,
@@ -186,6 +221,7 @@ func (r *userResource) Create(
if resp.Diagnostics.HasError() {
return
}
+ slices.Sort(roles)
}
// Generate API request body from model
@@ -379,7 +415,7 @@ func (r *userResource) Update(
resp *resource.UpdateResponse,
) { // nolint:gocritic // function signature required by Terraform
// Update shouldn't be called
- core.LogAndAddError(ctx, &resp.Diagnostics, "Error updating user", "User can't be updated")
+ core.LogAndAddError(ctx, &resp.Diagnostics, "Error updating user", "an SQL server user can not be updated, only created")
}
// Delete deletes the resource and removes the Terraform state on success.