Implement Secrets Manager User, change ACL to Set (#94)

* Implement secrets manager user

* Add user tests

* Add secrets manager user

* Fix typo

* Change ACL to set

* Fix field name

* Change ACLs to set

* Fix typo

* Fix formatting

* Fix update not using existing password

* Add repeating ACLs to test case

* Fix signature

* Add user checks

* Reorder list

---------

Co-authored-by: Henrique Santos <henrique.santos@freiheit.com>
This commit is contained in:
Henrique Santos 2023-10-19 13:56:24 +01:00 committed by GitHub
parent e1265578ce
commit 7a7f28a306
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 1159 additions and 45 deletions

View file

@ -110,7 +110,7 @@ func (r *instanceDataSource) Schema(_ context.Context, _ datasource.SchemaReques
Description: descriptions["name"], Description: descriptions["name"],
Computed: true, Computed: true,
}, },
"acls": schema.ListAttribute{ "acls": schema.SetAttribute{
Description: descriptions["acls"], Description: descriptions["acls"],
ElementType: types.StringType, ElementType: types.StringType,
Computed: true, Computed: true,

View file

@ -5,7 +5,7 @@ import (
"fmt" "fmt"
"strings" "strings"
"github.com/hashicorp/terraform-plugin-framework-validators/listvalidator" "github.com/hashicorp/terraform-plugin-framework-validators/setvalidator"
"github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator" "github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator"
"github.com/hashicorp/terraform-plugin-framework/attr" "github.com/hashicorp/terraform-plugin-framework/attr"
"github.com/hashicorp/terraform-plugin-framework/schema/validator" "github.com/hashicorp/terraform-plugin-framework/schema/validator"
@ -36,7 +36,7 @@ type Model struct {
InstanceId types.String `tfsdk:"instance_id"` InstanceId types.String `tfsdk:"instance_id"`
ProjectId types.String `tfsdk:"project_id"` ProjectId types.String `tfsdk:"project_id"`
Name types.String `tfsdk:"name"` Name types.String `tfsdk:"name"`
ACLs types.List `tfsdk:"acls"` ACLs types.Set `tfsdk:"acls"`
} }
// NewInstanceResource is a helper function to simplify the provider implementation. // NewInstanceResource is a helper function to simplify the provider implementation.
@ -143,13 +143,12 @@ func (r *instanceResource) Schema(_ context.Context, _ resource.SchemaRequest, r
stringvalidator.LengthAtLeast(1), stringvalidator.LengthAtLeast(1),
}, },
}, },
"acls": schema.ListAttribute{ "acls": schema.SetAttribute{
Description: descriptions["acls"], Description: descriptions["acls"],
ElementType: types.StringType, ElementType: types.StringType,
Optional: true, Optional: true,
Validators: []validator.List{ Validators: []validator.Set{
listvalidator.UniqueValues(), setvalidator.ValueStringsAre(
listvalidator.ValueStringsAre(
validate.CIDR(), validate.CIDR(),
), ),
}, },
@ -397,7 +396,7 @@ func mapACLs(aclList *secretsmanager.AclList, model *Model) error {
return fmt.Errorf("nil ACL list") return fmt.Errorf("nil ACL list")
} }
if aclList.Acls == nil || len(*aclList.Acls) == 0 { if aclList.Acls == nil || len(*aclList.Acls) == 0 {
model.ACLs = types.ListNull(types.StringType) model.ACLs = types.SetNull(types.StringType)
return nil return nil
} }
@ -405,7 +404,7 @@ func mapACLs(aclList *secretsmanager.AclList, model *Model) error {
for _, acl := range *aclList.Acls { for _, acl := range *aclList.Acls {
acls = append(acls, types.StringValue(*acl.Cidr)) acls = append(acls, types.StringValue(*acl.Cidr))
} }
aclsList, diags := types.ListValue(types.StringType, acls) aclsList, diags := types.SetValue(types.StringType, acls)
if diags.HasError() { if diags.HasError() {
return fmt.Errorf("mapping ACLs: %w", core.DiagsToError(diags)) return fmt.Errorf("mapping ACLs: %w", core.DiagsToError(diags))
} }

View file

@ -36,7 +36,7 @@ func TestMapFields(t *testing.T) {
InstanceId: types.StringValue("iid"), InstanceId: types.StringValue("iid"),
ProjectId: types.StringValue("pid"), ProjectId: types.StringValue("pid"),
Name: types.StringNull(), Name: types.StringNull(),
ACLs: types.ListNull(types.StringType), ACLs: types.SetNull(types.StringType),
}, },
true, true,
}, },
@ -66,7 +66,7 @@ func TestMapFields(t *testing.T) {
InstanceId: types.StringValue("iid"), InstanceId: types.StringValue("iid"),
ProjectId: types.StringValue("pid"), ProjectId: types.StringValue("pid"),
Name: types.StringValue("name"), Name: types.StringValue("name"),
ACLs: types.ListValueMust(types.StringType, []attr.Value{ ACLs: types.SetValueMust(types.StringType, []attr.Value{
types.StringValue("cidr-1"), types.StringValue("cidr-1"),
types.StringValue("cidr-2"), types.StringValue("cidr-2"),
types.StringValue("cidr-3"), types.StringValue("cidr-3"),

View file

@ -25,7 +25,14 @@ var instanceResource = map[string]string{
"acl-1-updated": "111.222.111.222/22", "acl-1-updated": "111.222.111.222/22",
} }
func resourceConfig(acls *string) string { // User resource data
var userResource = map[string]string{
"description": testutil.ResourceNameWithDateTime("secretsmanager"),
"write_enabled": "false",
"write_enabled_updated": "true",
}
func resourceConfig(acls *string, writeEnabled string) string {
if acls == nil { if acls == nil {
return fmt.Sprintf(` return fmt.Sprintf(`
%s %s
@ -34,10 +41,19 @@ func resourceConfig(acls *string) string {
project_id = "%s" project_id = "%s"
name = "%s" name = "%s"
} }
resource "stackit_secretsmanager_user" "user" {
project_id = stackit_secretsmanager_instance.instance.project_id
instance_id = stackit_secretsmanager_instance.instance.instance_id
description = "%s"
write_enabled = %s
}
`, `,
testutil.SecretsManagerProviderConfig(), testutil.SecretsManagerProviderConfig(),
instanceResource["project_id"], instanceResource["project_id"],
instanceResource["name"], instanceResource["name"],
userResource["description"],
writeEnabled,
) )
} }
@ -49,11 +65,20 @@ func resourceConfig(acls *string) string {
name = "%s" name = "%s"
acls = %s acls = %s
} }
resource "stackit_secretsmanager_user" "user" {
project_id = stackit_secretsmanager_instance.instance.project_id
instance_id = stackit_secretsmanager_instance.instance.instance_id
description = "%s"
write_enabled = %s
}
`, `,
testutil.SecretsManagerProviderConfig(), testutil.SecretsManagerProviderConfig(),
instanceResource["project_id"], instanceResource["project_id"],
instanceResource["name"], instanceResource["name"],
*acls, *acls,
userResource["description"],
writeEnabled,
) )
} }
@ -65,37 +90,66 @@ func TestAccSecretsManager(t *testing.T) {
// Creation // Creation
{ {
Config: resourceConfig(utils.Ptr(fmt.Sprintf( Config: resourceConfig(
"[%q, %q]", utils.Ptr(fmt.Sprintf(
instanceResource["acl-0"], "[%q, %q, %q]",
instanceResource["acl-1"], instanceResource["acl-0"],
))), instanceResource["acl-1"],
instanceResource["acl-1"],
)),
userResource["write_enabled"],
),
Check: resource.ComposeAggregateTestCheckFunc( Check: resource.ComposeAggregateTestCheckFunc(
// Instance data // Instance
resource.TestCheckResourceAttr("stackit_secretsmanager_instance.instance", "project_id", instanceResource["project_id"]), resource.TestCheckResourceAttr("stackit_secretsmanager_instance.instance", "project_id", instanceResource["project_id"]),
resource.TestCheckResourceAttrSet("stackit_secretsmanager_instance.instance", "instance_id"), resource.TestCheckResourceAttrSet("stackit_secretsmanager_instance.instance", "instance_id"),
resource.TestCheckResourceAttr("stackit_secretsmanager_instance.instance", "name", instanceResource["name"]), resource.TestCheckResourceAttr("stackit_secretsmanager_instance.instance", "name", instanceResource["name"]),
resource.TestCheckResourceAttr("stackit_secretsmanager_instance.instance", "acls.#", "2"), resource.TestCheckResourceAttr("stackit_secretsmanager_instance.instance", "acls.#", "2"),
resource.TestCheckResourceAttr("stackit_secretsmanager_instance.instance", "acls.0", instanceResource["acl-0"]), resource.TestCheckResourceAttr("stackit_secretsmanager_instance.instance", "acls.0", instanceResource["acl-0"]),
resource.TestCheckResourceAttr("stackit_secretsmanager_instance.instance", "acls.1", instanceResource["acl-1"]), resource.TestCheckResourceAttr("stackit_secretsmanager_instance.instance", "acls.1", instanceResource["acl-1"]),
// User
resource.TestCheckResourceAttrPair(
"stackit_secretsmanager_user.user", "project_id",
"stackit_secretsmanager_instance.instance", "project_id",
),
resource.TestCheckResourceAttrPair(
"stackit_secretsmanager_user.user", "instance_id",
"stackit_secretsmanager_instance.instance", "instance_id",
),
resource.TestCheckResourceAttrSet("stackit_secretsmanager_user.user", "user_id"),
resource.TestCheckResourceAttr("stackit_secretsmanager_user.user", "description", userResource["description"]),
resource.TestCheckResourceAttr("stackit_secretsmanager_user.user", "write_enabled", userResource["write_enabled"]),
resource.TestCheckResourceAttrSet("stackit_secretsmanager_user.user", "username"),
resource.TestCheckResourceAttrSet("stackit_secretsmanager_user.user", "password"),
), ),
}, },
{ // Data source // Data source
{
Config: fmt.Sprintf(` Config: fmt.Sprintf(`
%s %s
data "stackit_secretsmanager_instance" "instance" { data "stackit_secretsmanager_instance" "instance" {
project_id = stackit_secretsmanager_instance.instance.project_id project_id = stackit_secretsmanager_instance.instance.project_id
instance_id = stackit_secretsmanager_instance.instance.instance_id instance_id = stackit_secretsmanager_instance.instance.instance_id
}
data "stackit_secretsmanager_user" "user" {
project_id = stackit_secretsmanager_user.user.project_id
instance_id = stackit_secretsmanager_user.user.instance_id
user_id = stackit_secretsmanager_user.user.user_id
}`, }`,
resourceConfig(utils.Ptr(fmt.Sprintf( resourceConfig(
"[%q, %q]", utils.Ptr(fmt.Sprintf(
instanceResource["acl-0"], "[%q, %q]",
instanceResource["acl-1"], instanceResource["acl-0"],
))), instanceResource["acl-1"],
)),
userResource["write_enabled"],
),
), ),
Check: resource.ComposeAggregateTestCheckFunc( Check: resource.ComposeAggregateTestCheckFunc(
// Instance data // Instance
resource.TestCheckResourceAttr("data.stackit_secretsmanager_instance.instance", "project_id", instanceResource["project_id"]), resource.TestCheckResourceAttr("data.stackit_secretsmanager_instance.instance", "project_id", instanceResource["project_id"]),
resource.TestCheckResourceAttrPair( resource.TestCheckResourceAttrPair(
"stackit_secretsmanager_instance.instance", "instance_id", "stackit_secretsmanager_instance.instance", "instance_id",
@ -104,6 +158,26 @@ func TestAccSecretsManager(t *testing.T) {
resource.TestCheckResourceAttr("data.stackit_secretsmanager_instance.instance", "name", instanceResource["name"]), resource.TestCheckResourceAttr("data.stackit_secretsmanager_instance.instance", "name", instanceResource["name"]),
resource.TestCheckResourceAttr("data.stackit_secretsmanager_instance.instance", "acls.0", instanceResource["acl-0"]), resource.TestCheckResourceAttr("data.stackit_secretsmanager_instance.instance", "acls.0", instanceResource["acl-0"]),
resource.TestCheckResourceAttr("data.stackit_secretsmanager_instance.instance", "acls.1", instanceResource["acl-1"]), resource.TestCheckResourceAttr("data.stackit_secretsmanager_instance.instance", "acls.1", instanceResource["acl-1"]),
// User
resource.TestCheckResourceAttrPair(
"stackit_secretsmanager_user.user", "project_id",
"data.stackit_secretsmanager_user.user", "project_id",
),
resource.TestCheckResourceAttrPair(
"stackit_secretsmanager_user.user", "instance_id",
"data.stackit_secretsmanager_user.user", "instance_id",
),
resource.TestCheckResourceAttrPair(
"stackit_secretsmanager_user.user", "user_id",
"data.stackit_secretsmanager_user.user", "user_id",
),
resource.TestCheckResourceAttr("data.stackit_secretsmanager_user.user", "description", userResource["description"]),
resource.TestCheckResourceAttr("data.stackit_secretsmanager_user.user", "write_enabled", userResource["write_enabled"]),
resource.TestCheckResourceAttrPair(
"stackit_secretsmanager_user.user", "username",
"data.stackit_secretsmanager_user.user", "username",
),
), ),
}, },
// Import // Import
@ -123,32 +197,88 @@ func TestAccSecretsManager(t *testing.T) {
ImportState: true, ImportState: true,
ImportStateVerify: true, ImportStateVerify: true,
}, },
{
ResourceName: "stackit_secretsmanager_user.user",
ImportStateIdFunc: func(s *terraform.State) (string, error) {
r, ok := s.RootModule().Resources["stackit_secretsmanager_user.user"]
if !ok {
return "", fmt.Errorf("couldn't find resource stackit_secretsmanager_user.user")
}
instanceId, ok := r.Primary.Attributes["instance_id"]
if !ok {
return "", fmt.Errorf("couldn't find attribute instance_id")
}
userId, ok := r.Primary.Attributes["user_id"]
if !ok {
return "", fmt.Errorf("couldn't find attribute user_id")
}
return fmt.Sprintf("%s,%s,%s", testutil.ProjectId, instanceId, userId), nil
},
ImportState: true,
ImportStateVerify: true,
ImportStateVerifyIgnore: []string{"password"},
Check: resource.TestCheckNoResourceAttr("stackit_secretsmanager_user.user", "password"),
},
// Update // Update
{ {
Config: resourceConfig(utils.Ptr(fmt.Sprintf( Config: resourceConfig(
"[%q, %q]", utils.Ptr(fmt.Sprintf(
instanceResource["acl-0"], "[%q, %q]",
instanceResource["acl-1-updated"], instanceResource["acl-0"],
))), instanceResource["acl-1-updated"],
)),
userResource["write_enabled_updated"],
),
Check: resource.ComposeAggregateTestCheckFunc( Check: resource.ComposeAggregateTestCheckFunc(
// Instance data // Instance
resource.TestCheckResourceAttr("stackit_secretsmanager_instance.instance", "project_id", instanceResource["project_id"]), resource.TestCheckResourceAttr("stackit_secretsmanager_instance.instance", "project_id", instanceResource["project_id"]),
resource.TestCheckResourceAttrSet("stackit_secretsmanager_instance.instance", "instance_id"), resource.TestCheckResourceAttrSet("stackit_secretsmanager_instance.instance", "instance_id"),
resource.TestCheckResourceAttr("stackit_secretsmanager_instance.instance", "name", instanceResource["name"]), resource.TestCheckResourceAttr("stackit_secretsmanager_instance.instance", "name", instanceResource["name"]),
resource.TestCheckResourceAttr("stackit_secretsmanager_instance.instance", "acls.#", "2"), resource.TestCheckResourceAttr("stackit_secretsmanager_instance.instance", "acls.#", "2"),
resource.TestCheckResourceAttr("stackit_secretsmanager_instance.instance", "acls.0", instanceResource["acl-0"]), resource.TestCheckResourceAttr("stackit_secretsmanager_instance.instance", "acls.0", instanceResource["acl-0"]),
resource.TestCheckResourceAttr("stackit_secretsmanager_instance.instance", "acls.1", instanceResource["acl-1-updated"]), resource.TestCheckResourceAttr("stackit_secretsmanager_instance.instance", "acls.1", instanceResource["acl-1-updated"]),
// User
resource.TestCheckResourceAttrPair(
"stackit_secretsmanager_user.user", "project_id",
"stackit_secretsmanager_instance.instance", "project_id",
),
resource.TestCheckResourceAttrPair(
"stackit_secretsmanager_user.user", "instance_id",
"stackit_secretsmanager_instance.instance", "instance_id",
),
resource.TestCheckResourceAttrSet("stackit_secretsmanager_user.user", "user_id"),
resource.TestCheckResourceAttr("stackit_secretsmanager_user.user", "description", userResource["description"]),
resource.TestCheckResourceAttr("stackit_secretsmanager_user.user", "write_enabled", userResource["write_enabled_updated"]),
resource.TestCheckResourceAttrSet("stackit_secretsmanager_user.user", "username"),
resource.TestCheckResourceAttrSet("stackit_secretsmanager_user.user", "password"),
), ),
}, },
// Update, no ACLs // Update, no ACLs
{ {
Config: resourceConfig(nil), Config: resourceConfig(nil, userResource["write_enabled_updated"]),
Check: resource.ComposeAggregateTestCheckFunc( Check: resource.ComposeAggregateTestCheckFunc(
// Instance data // Instance data
resource.TestCheckResourceAttr("stackit_secretsmanager_instance.instance", "project_id", instanceResource["project_id"]), resource.TestCheckResourceAttr("stackit_secretsmanager_instance.instance", "project_id", instanceResource["project_id"]),
resource.TestCheckResourceAttrSet("stackit_secretsmanager_instance.instance", "instance_id"), resource.TestCheckResourceAttrSet("stackit_secretsmanager_instance.instance", "instance_id"),
resource.TestCheckResourceAttr("stackit_secretsmanager_instance.instance", "name", instanceResource["name"]), resource.TestCheckResourceAttr("stackit_secretsmanager_instance.instance", "name", instanceResource["name"]),
resource.TestCheckResourceAttr("stackit_secretsmanager_instance.instance", "acls.#", "0"), resource.TestCheckResourceAttr("stackit_secretsmanager_instance.instance", "acls.#", "0"),
// User
resource.TestCheckResourceAttrPair(
"stackit_secretsmanager_user.user", "project_id",
"stackit_secretsmanager_instance.instance", "project_id",
),
resource.TestCheckResourceAttrPair(
"stackit_secretsmanager_user.user", "instance_id",
"stackit_secretsmanager_instance.instance", "instance_id",
),
resource.TestCheckResourceAttrSet("stackit_secretsmanager_user.user", "user_id"),
resource.TestCheckResourceAttr("stackit_secretsmanager_user.user", "description", userResource["description"]),
resource.TestCheckResourceAttr("stackit_secretsmanager_user.user", "write_enabled", userResource["write_enabled_updated"]),
resource.TestCheckResourceAttrSet("stackit_secretsmanager_user.user", "username"),
resource.TestCheckResourceAttrSet("stackit_secretsmanager_user.user", "password"),
), ),
}, },
// Deletion is done by the framework implicitly // Deletion is done by the framework implicitly

View file

@ -0,0 +1,213 @@
package secretsmanager
import (
"context"
"fmt"
"strings"
"github.com/hashicorp/terraform-plugin-framework/datasource"
"github.com/hashicorp/terraform-plugin-framework/schema/validator"
"github.com/hashicorp/terraform-plugin-log/tflog"
"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/core"
"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/validate"
"github.com/hashicorp/terraform-plugin-framework/datasource/schema"
"github.com/hashicorp/terraform-plugin-framework/types"
"github.com/stackitcloud/stackit-sdk-go/core/config"
"github.com/stackitcloud/stackit-sdk-go/services/secretsmanager"
)
// Ensure the implementation satisfies the expected interfaces.
var (
_ datasource.DataSource = &userDataSource{}
)
type DataSourceModel struct {
Id types.String `tfsdk:"id"` // needed by TF
UserId types.String `tfsdk:"user_id"`
InstanceId types.String `tfsdk:"instance_id"`
ProjectId types.String `tfsdk:"project_id"`
Description types.String `tfsdk:"description"`
WriteEnabled types.Bool `tfsdk:"write_enabled"`
Username types.String `tfsdk:"username"`
}
// NewUserDataSource is a helper function to simplify the provider implementation.
func NewUserDataSource() datasource.DataSource {
return &userDataSource{}
}
// userDataSource is the data source implementation.
type userDataSource struct {
client *secretsmanager.APIClient
}
// Metadata returns the data source type name.
func (r *userDataSource) Metadata(_ context.Context, req datasource.MetadataRequest, resp *datasource.MetadataResponse) {
resp.TypeName = req.ProviderTypeName + "_secretsmanager_user"
}
// Configure adds the provider configured client to the data source.
func (r *userDataSource) Configure(ctx context.Context, req datasource.ConfigureRequest, resp *datasource.ConfigureResponse) {
// Prevent panic if the provider has not been configured.
if req.ProviderData == nil {
return
}
providerData, ok := req.ProviderData.(core.ProviderData)
if !ok {
core.LogAndAddError(ctx, &resp.Diagnostics, "Error configuring API client", fmt.Sprintf("Expected configure type stackit.ProviderData, got %T", req.ProviderData))
return
}
var apiClient *secretsmanager.APIClient
var err error
if providerData.SecretsManagerCustomEndpoint != "" {
apiClient, err = secretsmanager.NewAPIClient(
config.WithCustomAuth(providerData.RoundTripper),
config.WithEndpoint(providerData.SecretsManagerCustomEndpoint),
)
} else {
apiClient, err = secretsmanager.NewAPIClient(
config.WithCustomAuth(providerData.RoundTripper),
config.WithRegion(providerData.Region),
)
}
if err != nil {
core.LogAndAddError(ctx, &resp.Diagnostics, "Error configuring API client", fmt.Sprintf("Configuring client: %v. This is an error related to the provider configuration, not to the data source configuration", err))
return
}
r.client = apiClient
tflog.Info(ctx, "Secrets Manager user client configured")
}
// Schema defines the schema for the data source.
func (r *userDataSource) Schema(_ context.Context, _ datasource.SchemaRequest, resp *datasource.SchemaResponse) {
descriptions := map[string]string{
"main": "Secrets Manager user data source schema. Must have a `region` specified in the provider configuration.",
"id": "Terraform's internal data source identifier. It is structured as \"`project_id`,`instance_id`,`user_id`\".",
"user_id": "The user's ID.",
"instance_id": "ID of the Secrets Manager instance.",
"project_id": "STACKIT Project ID to which the instance is associated.",
"description": "A user chosen description to differentiate between multiple users. Can't be changed after creation.",
"write_enabled": "If true, the user has writeaccess to the secrets engine.",
"username": "An auto-generated user name.",
}
resp.Schema = schema.Schema{
Description: descriptions["main"],
Attributes: map[string]schema.Attribute{
"id": schema.StringAttribute{
Description: descriptions["id"],
Computed: true,
},
"user_id": schema.StringAttribute{
Description: descriptions["user_id"],
Required: true,
Validators: []validator.String{
validate.UUID(),
validate.NoSeparator(),
},
},
"instance_id": schema.StringAttribute{
Description: descriptions["instance_id"],
Required: true,
Validators: []validator.String{
validate.UUID(),
validate.NoSeparator(),
},
},
"project_id": schema.StringAttribute{
Description: descriptions["project_id"],
Required: true,
Validators: []validator.String{
validate.UUID(),
validate.NoSeparator(),
},
},
"description": schema.StringAttribute{
Description: descriptions["description"],
Computed: true,
},
"write_enabled": schema.BoolAttribute{
Description: descriptions["write_enabled"],
Computed: true,
},
"username": schema.StringAttribute{
Description: descriptions["username"],
Computed: true,
},
},
}
}
// Read refreshes the Terraform state with the latest data.
func (r *userDataSource) Read(ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse) { // nolint:gocritic // function signature required by Terraform
var model DataSourceModel
diags := req.Config.Get(ctx, &model)
resp.Diagnostics.Append(diags...)
if resp.Diagnostics.HasError() {
return
}
projectId := model.ProjectId.ValueString()
instanceId := model.InstanceId.ValueString()
userId := model.UserId.ValueString()
ctx = tflog.SetField(ctx, "project_id", projectId)
ctx = tflog.SetField(ctx, "instance_id", instanceId)
ctx = tflog.SetField(ctx, "user_id", userId)
userResp, err := r.client.GetUser(ctx, projectId, instanceId, userId).Execute()
if err != nil {
core.LogAndAddError(ctx, &resp.Diagnostics, "Error reading user", fmt.Sprintf("Calling API: %v", err))
return
}
// Map response body to schema and populate Computed attribute values
err = mapDataSourceFields(userResp, &model)
if err != nil {
core.LogAndAddError(ctx, &resp.Diagnostics, "Error reading user", fmt.Sprintf("Processing API payload: %v", err))
return
}
// Set refreshed state
diags = resp.State.Set(ctx, model)
resp.Diagnostics.Append(diags...)
if resp.Diagnostics.HasError() {
return
}
tflog.Info(ctx, "Secrets Manager user read")
}
func mapDataSourceFields(user *secretsmanager.User, model *DataSourceModel) error {
if user == nil {
return fmt.Errorf("response input is nil")
}
if model == nil {
return fmt.Errorf("model input is nil")
}
var userId string
if model.UserId.ValueString() != "" {
userId = model.UserId.ValueString()
} else if user.Id != nil {
userId = *user.Id
} else {
return fmt.Errorf("user id not present")
}
idParts := []string{
model.ProjectId.ValueString(),
model.InstanceId.ValueString(),
userId,
}
model.Id = types.StringValue(
strings.Join(idParts, core.Separator),
)
model.UserId = types.StringValue(userId)
model.Description = types.StringPointerValue(user.Description)
model.WriteEnabled = types.BoolPointerValue(user.Write)
model.Username = types.StringPointerValue(user.Username)
return nil
}

View file

@ -0,0 +1,88 @@
package secretsmanager
import (
"testing"
"github.com/google/go-cmp/cmp"
"github.com/hashicorp/terraform-plugin-framework/types"
"github.com/stackitcloud/stackit-sdk-go/core/utils"
"github.com/stackitcloud/stackit-sdk-go/services/secretsmanager"
)
func TestMapDataSourceFields(t *testing.T) {
tests := []struct {
description string
input *secretsmanager.User
expected DataSourceModel
isValid bool
}{
{
"default_values",
&secretsmanager.User{
Id: utils.Ptr("uid"),
},
DataSourceModel{
Id: types.StringValue("pid,iid,uid"),
UserId: types.StringValue("uid"),
InstanceId: types.StringValue("iid"),
ProjectId: types.StringValue("pid"),
Description: types.StringNull(),
WriteEnabled: types.BoolNull(),
Username: types.StringNull(),
},
true,
},
{
"simple_values",
&secretsmanager.User{
Id: utils.Ptr("uid"),
Description: utils.Ptr("description"),
Write: utils.Ptr(false),
Username: utils.Ptr("username"),
},
DataSourceModel{
Id: types.StringValue("pid,iid,uid"),
UserId: types.StringValue("uid"),
InstanceId: types.StringValue("iid"),
ProjectId: types.StringValue("pid"),
Description: types.StringValue("description"),
WriteEnabled: types.BoolValue(false),
Username: types.StringValue("username"),
},
true,
},
{
"nil_response",
nil,
DataSourceModel{},
false,
},
{
"no_resource_id",
&secretsmanager.User{},
DataSourceModel{},
false,
},
}
for _, tt := range tests {
t.Run(tt.description, func(t *testing.T) {
state := &DataSourceModel{
ProjectId: tt.expected.ProjectId,
InstanceId: tt.expected.InstanceId,
}
err := mapDataSourceFields(tt.input, 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(state, &tt.expected)
if diff != "" {
t.Fatalf("Data does not match: %s", diff)
}
}
})
}
}

View file

@ -0,0 +1,410 @@
package secretsmanager
import (
"context"
"fmt"
"strings"
"github.com/hashicorp/terraform-plugin-framework/schema/validator"
"github.com/hashicorp/terraform-plugin-log/tflog"
"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/core"
"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/validate"
"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/planmodifier"
"github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier"
"github.com/hashicorp/terraform-plugin-framework/types"
"github.com/stackitcloud/stackit-sdk-go/core/config"
"github.com/stackitcloud/stackit-sdk-go/services/secretsmanager"
)
// Ensure the implementation satisfies the expected interfaces.
var (
_ resource.Resource = &userResource{}
_ resource.ResourceWithConfigure = &userResource{}
_ resource.ResourceWithImportState = &userResource{}
)
type Model struct {
Id types.String `tfsdk:"id"` // needed by TF
UserId types.String `tfsdk:"user_id"`
InstanceId types.String `tfsdk:"instance_id"`
ProjectId types.String `tfsdk:"project_id"`
Description types.String `tfsdk:"description"`
WriteEnabled types.Bool `tfsdk:"write_enabled"`
Username types.String `tfsdk:"username"`
Password types.String `tfsdk:"password"`
}
// NewUserResource is a helper function to simplify the provider implementation.
func NewUserResource() resource.Resource {
return &userResource{}
}
// userResource is the resource implementation.
type userResource struct {
client *secretsmanager.APIClient
}
// Metadata returns the resource type name.
func (r *userResource) Metadata(_ context.Context, req resource.MetadataRequest, resp *resource.MetadataResponse) {
resp.TypeName = req.ProviderTypeName + "_secretsmanager_user"
}
// Configure adds the provider configured client to the resource.
func (r *userResource) Configure(ctx context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) {
// Prevent panic if the provider has not been configured.
if req.ProviderData == nil {
return
}
providerData, ok := req.ProviderData.(core.ProviderData)
if !ok {
core.LogAndAddError(ctx, &resp.Diagnostics, "Error configuring API client", fmt.Sprintf("Expected configure type stackit.ProviderData, got %T", req.ProviderData))
return
}
var apiClient *secretsmanager.APIClient
var err error
if providerData.SecretsManagerCustomEndpoint != "" {
apiClient, err = secretsmanager.NewAPIClient(
config.WithCustomAuth(providerData.RoundTripper),
config.WithEndpoint(providerData.SecretsManagerCustomEndpoint),
)
} else {
apiClient, err = secretsmanager.NewAPIClient(
config.WithCustomAuth(providerData.RoundTripper),
config.WithRegion(providerData.Region),
)
}
if err != nil {
core.LogAndAddError(ctx, &resp.Diagnostics, "Error configuring API client", fmt.Sprintf("Configuring client: %v. This is an error related to the provider configuration, not to the resource configuration", err))
return
}
r.client = apiClient
tflog.Info(ctx, "Secrets Manager user client configured")
}
// Schema defines the schema for the resource.
func (r *userResource) Schema(_ context.Context, _ resource.SchemaRequest, resp *resource.SchemaResponse) {
descriptions := map[string]string{
"main": "Secrets Manager user resource schema. Must have a `region` specified in the provider configuration.",
"id": "Terraform's internal resource identifier. It is structured as \"`project_id`,`instance_id`,`user_id`\".",
"user_id": "The user's ID.",
"instance_id": "ID of the Secrets Manager instance.",
"project_id": "STACKIT Project ID to which the instance is associated.",
"description": "A user chosen description to differentiate between multiple users. Can't be changed after creation.",
"write_enabled": "If true, the user has writeaccess to the secrets engine.",
"username": "An auto-generated user name.",
"password": "An auto-generated password.",
}
resp.Schema = schema.Schema{
Description: descriptions["main"],
Attributes: map[string]schema.Attribute{
"id": schema.StringAttribute{
Description: descriptions["id"],
Computed: true,
PlanModifiers: []planmodifier.String{
stringplanmodifier.UseStateForUnknown(),
},
},
"user_id": schema.StringAttribute{
Description: descriptions["user_id"],
Computed: true,
PlanModifiers: []planmodifier.String{
stringplanmodifier.UseStateForUnknown(),
},
Validators: []validator.String{
validate.UUID(),
validate.NoSeparator(),
},
},
"instance_id": schema.StringAttribute{
Description: descriptions["instance_id"],
Required: true,
PlanModifiers: []planmodifier.String{
stringplanmodifier.RequiresReplace(),
},
Validators: []validator.String{
validate.UUID(),
validate.NoSeparator(),
},
},
"project_id": schema.StringAttribute{
Description: descriptions["project_id"],
Required: true,
PlanModifiers: []planmodifier.String{
stringplanmodifier.RequiresReplace(),
},
Validators: []validator.String{
validate.UUID(),
validate.NoSeparator(),
},
},
"description": schema.StringAttribute{
Description: descriptions["description"],
Required: true,
PlanModifiers: []planmodifier.String{
stringplanmodifier.RequiresReplace(),
},
},
"write_enabled": schema.BoolAttribute{
Description: descriptions["write_enabled"],
Required: true,
},
"username": schema.StringAttribute{
Description: descriptions["username"],
Computed: true,
},
"password": schema.StringAttribute{
Description: descriptions["password"],
Computed: true,
Sensitive: true,
},
},
}
}
// Create creates the resource and sets the initial Terraform state.
func (r *userResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) { // nolint:gocritic // function signature required by Terraform
var model Model
diags := req.Plan.Get(ctx, &model)
resp.Diagnostics.Append(diags...)
if resp.Diagnostics.HasError() {
return
}
projectId := model.ProjectId.ValueString()
instanceId := model.InstanceId.ValueString()
ctx = tflog.SetField(ctx, "project_id", projectId)
ctx = tflog.SetField(ctx, "instance_id", instanceId)
// Generate API request body from model
payload, err := toCreatePayload(&model)
if err != nil {
core.LogAndAddError(ctx, &resp.Diagnostics, "Error creating user", fmt.Sprintf("Creating API payload: %v", err))
return
}
// Create new user
userResp, err := r.client.CreateUser(ctx, projectId, instanceId).CreateUserPayload(*payload).Execute()
if err != nil {
core.LogAndAddError(ctx, &resp.Diagnostics, "Error creating user", fmt.Sprintf("Calling API: %v", err))
return
}
if userResp.Id == nil {
core.LogAndAddError(ctx, &resp.Diagnostics, "Error creating user", "Got empty user id")
return
}
userId := *userResp.Id
ctx = tflog.SetField(ctx, "user_id", userId)
// Map response body to schema
err = mapFields(userResp, &model)
if err != nil {
core.LogAndAddError(ctx, &resp.Diagnostics, "Error creating user", fmt.Sprintf("Processing API payload: %v", err))
return
}
diags = resp.State.Set(ctx, model)
resp.Diagnostics.Append(diags...)
if resp.Diagnostics.HasError() {
return
}
tflog.Info(ctx, "Secrets Manager user created")
}
// Read refreshes the Terraform state with the latest data.
func (r *userResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) { // nolint:gocritic // function signature required by Terraform
var model Model
diags := req.State.Get(ctx, &model)
resp.Diagnostics.Append(diags...)
if resp.Diagnostics.HasError() {
return
}
projectId := model.ProjectId.ValueString()
instanceId := model.InstanceId.ValueString()
userId := model.UserId.ValueString()
ctx = tflog.SetField(ctx, "project_id", projectId)
ctx = tflog.SetField(ctx, "instance_id", instanceId)
ctx = tflog.SetField(ctx, "user_id", userId)
userResp, err := r.client.GetUser(ctx, projectId, instanceId, userId).Execute()
if err != nil {
core.LogAndAddError(ctx, &resp.Diagnostics, "Error reading user", fmt.Sprintf("Calling API: %v", err))
return
}
// Map response body to schema
err = mapFields(userResp, &model)
if err != nil {
core.LogAndAddError(ctx, &resp.Diagnostics, "Error reading user", fmt.Sprintf("Processing API payload: %v", err))
return
}
// Set refreshed state
diags = resp.State.Set(ctx, model)
resp.Diagnostics.Append(diags...)
if resp.Diagnostics.HasError() {
return
}
tflog.Info(ctx, "Secrets Manager user read")
}
// Update updates the resource and sets the updated Terraform state on success.
func (r *userResource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) { // nolint:gocritic // function signature required by Terraform
// Retrieve values from plan
var model Model
diags := req.Plan.Get(ctx, &model)
resp.Diagnostics.Append(diags...)
if resp.Diagnostics.HasError() {
return
}
projectId := model.ProjectId.ValueString()
instanceId := model.InstanceId.ValueString()
userId := model.UserId.ValueString()
ctx = tflog.SetField(ctx, "project_id", projectId)
ctx = tflog.SetField(ctx, "instance_id", instanceId)
ctx = tflog.SetField(ctx, "user_id", userId)
// Generate API request body from model
payload, err := toUpdatePayload(&model)
if err != nil {
core.LogAndAddError(ctx, &resp.Diagnostics, "Error updating user", fmt.Sprintf("Creating API payload: %v", err))
return
}
// Update existing user
err = r.client.UpdateUser(ctx, projectId, instanceId, userId).UpdateUserPayload(*payload).Execute()
if err != nil {
core.LogAndAddError(ctx, &resp.Diagnostics, "Error updating user", err.Error())
return
}
user, err := r.client.GetUser(ctx, projectId, instanceId, userId).Execute()
if err != nil {
core.LogAndAddError(ctx, &resp.Diagnostics, "Error updating user", fmt.Sprintf("Calling API to get user's current state: %v", err))
return
}
// Get existing state
diags = req.State.Get(ctx, &model)
resp.Diagnostics.Append(diags...)
if resp.Diagnostics.HasError() {
return
}
// Map response body to schema
err = mapFields(user, &model)
if err != nil {
core.LogAndAddError(ctx, &resp.Diagnostics, "Error updating user", fmt.Sprintf("Processing API payload: %v", err))
return
}
diags = resp.State.Set(ctx, model)
resp.Diagnostics.Append(diags...)
if resp.Diagnostics.HasError() {
return
}
tflog.Info(ctx, "Secrets Manager user updated")
}
// Delete deletes the resource and removes the Terraform state on success.
func (r *userResource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) { // nolint:gocritic // function signature required by Terraform
var model Model
diags := req.State.Get(ctx, &model)
resp.Diagnostics.Append(diags...)
if resp.Diagnostics.HasError() {
return
}
projectId := model.ProjectId.ValueString()
instanceId := model.InstanceId.ValueString()
userId := model.UserId.ValueString()
ctx = tflog.SetField(ctx, "project_id", projectId)
ctx = tflog.SetField(ctx, "instance_id", instanceId)
ctx = tflog.SetField(ctx, "user_id", userId)
// Delete existing user
err := r.client.DeleteUser(ctx, projectId, instanceId, userId).Execute()
if err != nil {
core.LogAndAddError(ctx, &resp.Diagnostics, "Error deleting user", fmt.Sprintf("Calling API: %v", err))
}
tflog.Info(ctx, "Secrets Manager user deleted")
}
// ImportState imports a resource into the Terraform state on success.
// The expected format of the resource import identifier is: project_id,instance_id,user_id
func (r *userResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) {
idParts := strings.Split(req.ID, core.Separator)
if len(idParts) != 3 || idParts[0] == "" || idParts[1] == "" || idParts[2] == "" {
core.LogAndAddError(ctx, &resp.Diagnostics,
"Error importing credential",
fmt.Sprintf("Expected import identifier with format [project_id],[instance_id],[user_id], got %q", req.ID),
)
return
}
resp.Diagnostics.Append(resp.State.SetAttribute(ctx, path.Root("project_id"), idParts[0])...)
resp.Diagnostics.Append(resp.State.SetAttribute(ctx, path.Root("instance_id"), idParts[1])...)
resp.Diagnostics.Append(resp.State.SetAttribute(ctx, path.Root("user_id"), idParts[2])...)
core.LogAndAddWarning(ctx, &resp.Diagnostics,
"Secrets Manager user imported with empty password",
"The user password is not imported as it is only available upon creation of a new user. The password field will be empty.",
)
tflog.Info(ctx, "Secrets Manager user state imported")
}
func toCreatePayload(model *Model) (*secretsmanager.CreateUserPayload, error) {
if model == nil {
return nil, fmt.Errorf("nil model")
}
return &secretsmanager.CreateUserPayload{
Description: model.Description.ValueStringPointer(),
Write: model.WriteEnabled.ValueBoolPointer(),
}, nil
}
func toUpdatePayload(model *Model) (*secretsmanager.UpdateUserPayload, error) {
if model == nil {
return nil, fmt.Errorf("nil model")
}
return &secretsmanager.UpdateUserPayload{
Write: model.WriteEnabled.ValueBoolPointer(),
}, nil
}
func mapFields(user *secretsmanager.User, model *Model) error {
if user == nil {
return fmt.Errorf("response input is nil")
}
if model == nil {
return fmt.Errorf("model input is nil")
}
var userId string
if model.UserId.ValueString() != "" {
userId = model.UserId.ValueString()
} else if user.Id != nil {
userId = *user.Id
} else {
return fmt.Errorf("user id not present")
}
idParts := []string{
model.ProjectId.ValueString(),
model.InstanceId.ValueString(),
userId,
}
model.Id = types.StringValue(
strings.Join(idParts, core.Separator),
)
model.UserId = types.StringValue(userId)
model.Description = types.StringPointerValue(user.Description)
model.WriteEnabled = types.BoolPointerValue(user.Write)
model.Username = types.StringPointerValue(user.Username)
// Password only sent in creation response, responses after that have it as ""
if user.Password != nil && *user.Password != "" {
model.Password = types.StringPointerValue(user.Password)
}
return nil
}

View file

@ -0,0 +1,271 @@
package secretsmanager
import (
"testing"
"github.com/google/go-cmp/cmp"
"github.com/hashicorp/terraform-plugin-framework/types"
"github.com/stackitcloud/stackit-sdk-go/core/utils"
"github.com/stackitcloud/stackit-sdk-go/services/secretsmanager"
)
func TestMapFields(t *testing.T) {
tests := []struct {
description string
input *secretsmanager.User
modelPassword *string
expected Model
isValid bool
}{
{
"default_values",
&secretsmanager.User{
Id: utils.Ptr("uid"),
},
nil,
Model{
Id: types.StringValue("pid,iid,uid"),
UserId: types.StringValue("uid"),
InstanceId: types.StringValue("iid"),
ProjectId: types.StringValue("pid"),
Description: types.StringNull(),
WriteEnabled: types.BoolNull(),
Username: types.StringNull(),
Password: types.StringNull(),
},
true,
},
{
"simple_values",
&secretsmanager.User{
Id: utils.Ptr("uid"),
Description: utils.Ptr("description"),
Write: utils.Ptr(false),
Username: utils.Ptr("username"),
Password: utils.Ptr("password"),
},
nil,
Model{
Id: types.StringValue("pid,iid,uid"),
UserId: types.StringValue("uid"),
InstanceId: types.StringValue("iid"),
ProjectId: types.StringValue("pid"),
Description: types.StringValue("description"),
WriteEnabled: types.BoolValue(false),
Username: types.StringValue("username"),
Password: types.StringValue("password"),
},
true,
},
{
"nil_response",
nil,
nil,
Model{},
false,
},
{
"no_resource_id",
&secretsmanager.User{},
nil,
Model{},
false,
},
{
"no_password_in_response_1",
&secretsmanager.User{
Id: utils.Ptr("uid"),
Description: utils.Ptr("description"),
Write: utils.Ptr(false),
Username: utils.Ptr("username"),
},
utils.Ptr("password"),
Model{
Id: types.StringValue("pid,iid,uid"),
UserId: types.StringValue("uid"),
InstanceId: types.StringValue("iid"),
ProjectId: types.StringValue("pid"),
Description: types.StringValue("description"),
WriteEnabled: types.BoolValue(false),
Username: types.StringValue("username"),
Password: types.StringValue("password"),
},
true,
},
{
"no_password_in_response_2",
&secretsmanager.User{
Id: utils.Ptr("uid"),
Description: utils.Ptr("description"),
Write: utils.Ptr(false),
Username: utils.Ptr("username"),
Password: utils.Ptr(""),
},
utils.Ptr("password"),
Model{
Id: types.StringValue("pid,iid,uid"),
UserId: types.StringValue("uid"),
InstanceId: types.StringValue("iid"),
ProjectId: types.StringValue("pid"),
Description: types.StringValue("description"),
WriteEnabled: types.BoolValue(false),
Username: types.StringValue("username"),
Password: types.StringValue("password"),
},
true,
},
}
for _, tt := range tests {
t.Run(tt.description, func(t *testing.T) {
state := &Model{
ProjectId: tt.expected.ProjectId,
InstanceId: tt.expected.InstanceId,
}
if tt.modelPassword != nil {
state.Password = types.StringPointerValue(tt.modelPassword)
}
err := mapFields(tt.input, 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(state, &tt.expected)
if diff != "" {
t.Fatalf("Data does not match: %s", diff)
}
}
})
}
}
func TestToCreatePayload(t *testing.T) {
tests := []struct {
description string
input *Model
expected *secretsmanager.CreateUserPayload
isValid bool
}{
{
"default_values",
&Model{},
&secretsmanager.CreateUserPayload{
Description: nil,
Write: nil,
},
true,
},
{
"simple_values",
&Model{
Description: types.StringValue("description"),
WriteEnabled: types.BoolValue(false),
},
&secretsmanager.CreateUserPayload{
Description: utils.Ptr("description"),
Write: utils.Ptr(false),
},
true,
},
{
"null_fields",
&Model{
Description: types.StringNull(),
WriteEnabled: types.BoolNull(),
},
&secretsmanager.CreateUserPayload{
Description: nil,
Write: nil,
},
true,
},
{
"empty_fields",
&Model{
Description: types.StringValue(""),
WriteEnabled: types.BoolNull(),
},
&secretsmanager.CreateUserPayload{
Description: utils.Ptr(""),
Write: nil,
},
true,
},
{
"nil_model",
nil,
nil,
false,
},
}
for _, tt := range tests {
t.Run(tt.description, func(t *testing.T) {
output, err := toCreatePayload(tt.input)
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(output, tt.expected)
if diff != "" {
t.Fatalf("Data does not match: %s", diff)
}
}
})
}
}
func TestToUpdatePayload(t *testing.T) {
tests := []struct {
description string
input *Model
expected *secretsmanager.UpdateUserPayload
isValid bool
}{
{
"default_values",
&Model{},
&secretsmanager.UpdateUserPayload{
Write: nil,
},
true,
},
{
"simple_values",
&Model{
WriteEnabled: types.BoolValue(false),
},
&secretsmanager.UpdateUserPayload{
Write: utils.Ptr(false),
},
true,
},
{
"nil_model",
nil,
nil,
false,
},
}
for _, tt := range tests {
t.Run(tt.description, func(t *testing.T) {
output, err := toUpdatePayload(tt.input)
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(output, tt.expected)
if diff != "" {
t.Fatalf("Data does not match: %s", diff)
}
}
})
}
}

View file

@ -35,6 +35,7 @@ import (
redisInstance "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/services/redis/instance" redisInstance "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/services/redis/instance"
resourceManagerProject "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/services/resourcemanager/project" resourceManagerProject "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/services/resourcemanager/project"
secretsManagerInstance "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/services/secretsmanager/instance" secretsManagerInstance "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/services/secretsmanager/instance"
secretsManagerUser "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/services/secretsmanager/user"
skeCluster "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/services/ske/cluster" skeCluster "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/services/ske/cluster"
skeProject "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/services/ske/project" skeProject "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/services/ske/project"
@ -327,10 +328,10 @@ func (p *Provider) Configure(ctx context.Context, req provider.ConfigureRequest,
// DataSources defines the data sources implemented in the provider. // DataSources defines the data sources implemented in the provider.
func (p *Provider) DataSources(_ context.Context) []func() datasource.DataSource { func (p *Provider) DataSources(_ context.Context) []func() datasource.DataSource {
return []func() datasource.DataSource{ return []func() datasource.DataSource{
argusInstance.NewInstanceDataSource,
argusScrapeConfig.NewScrapeConfigDataSource,
dnsZone.NewZoneDataSource, dnsZone.NewZoneDataSource,
dnsRecordSet.NewRecordSetDataSource, dnsRecordSet.NewRecordSetDataSource,
postgresInstance.NewInstanceDataSource,
postgresCredential.NewCredentialDataSource,
logMeInstance.NewInstanceDataSource, logMeInstance.NewInstanceDataSource,
logMeCredential.NewCredentialDataSource, logMeCredential.NewCredentialDataSource,
mariaDBInstance.NewInstanceDataSource, mariaDBInstance.NewInstanceDataSource,
@ -342,28 +343,30 @@ func (p *Provider) DataSources(_ context.Context) []func() datasource.DataSource
objecStorageCredential.NewCredentialDataSource, objecStorageCredential.NewCredentialDataSource,
openSearchInstance.NewInstanceDataSource, openSearchInstance.NewInstanceDataSource,
openSearchCredential.NewCredentialDataSource, openSearchCredential.NewCredentialDataSource,
postgresFlexInstance.NewInstanceDataSource,
postgresFlexUser.NewUserDataSource,
postgresInstance.NewInstanceDataSource,
postgresCredential.NewCredentialDataSource,
rabbitMQInstance.NewInstanceDataSource, rabbitMQInstance.NewInstanceDataSource,
rabbitMQCredential.NewCredentialDataSource, rabbitMQCredential.NewCredentialDataSource,
redisInstance.NewInstanceDataSource, redisInstance.NewInstanceDataSource,
redisCredential.NewCredentialDataSource, redisCredential.NewCredentialDataSource,
argusInstance.NewInstanceDataSource,
argusScrapeConfig.NewScrapeConfigDataSource,
resourceManagerProject.NewProjectDataSource, resourceManagerProject.NewProjectDataSource,
secretsManagerInstance.NewInstanceDataSource, secretsManagerInstance.NewInstanceDataSource,
secretsManagerUser.NewUserDataSource,
skeProject.NewProjectDataSource, skeProject.NewProjectDataSource,
skeCluster.NewClusterDataSource, skeCluster.NewClusterDataSource,
postgresFlexInstance.NewInstanceDataSource,
postgresFlexUser.NewUserDataSource,
} }
} }
// Resources defines the resources implemented in the provider. // Resources defines the resources implemented in the provider.
func (p *Provider) Resources(_ context.Context) []func() resource.Resource { func (p *Provider) Resources(_ context.Context) []func() resource.Resource {
return []func() resource.Resource{ return []func() resource.Resource{
argusCredential.NewCredentialResource,
argusInstance.NewInstanceResource,
argusScrapeConfig.NewScrapeConfigResource,
dnsZone.NewZoneResource, dnsZone.NewZoneResource,
dnsRecordSet.NewRecordSetResource, dnsRecordSet.NewRecordSetResource,
postgresInstance.NewInstanceResource,
postgresCredential.NewCredentialResource,
logMeInstance.NewInstanceResource, logMeInstance.NewInstanceResource,
logMeCredential.NewCredentialResource, logMeCredential.NewCredentialResource,
mariaDBInstance.NewInstanceResource, mariaDBInstance.NewInstanceResource,
@ -375,18 +378,18 @@ func (p *Provider) Resources(_ context.Context) []func() resource.Resource {
objecStorageCredential.NewCredentialResource, objecStorageCredential.NewCredentialResource,
openSearchInstance.NewInstanceResource, openSearchInstance.NewInstanceResource,
openSearchCredential.NewCredentialResource, openSearchCredential.NewCredentialResource,
postgresFlexInstance.NewInstanceResource,
postgresFlexUser.NewUserResource,
postgresInstance.NewInstanceResource,
postgresCredential.NewCredentialResource,
rabbitMQInstance.NewInstanceResource, rabbitMQInstance.NewInstanceResource,
rabbitMQCredential.NewCredentialResource, rabbitMQCredential.NewCredentialResource,
redisInstance.NewInstanceResource, redisInstance.NewInstanceResource,
redisCredential.NewCredentialResource, redisCredential.NewCredentialResource,
argusInstance.NewInstanceResource,
argusScrapeConfig.NewScrapeConfigResource,
resourceManagerProject.NewProjectResource, resourceManagerProject.NewProjectResource,
argusCredential.NewCredentialResource,
secretsManagerInstance.NewInstanceResource, secretsManagerInstance.NewInstanceResource,
secretsManagerUser.NewUserResource,
skeProject.NewProjectResource, skeProject.NewProjectResource,
skeCluster.NewClusterResource, skeCluster.NewClusterResource,
postgresFlexInstance.NewInstanceResource,
postgresFlexUser.NewUserResource,
} }
} }