diff --git a/docs/data-sources/resourcemanager_project.md b/docs/data-sources/resourcemanager_project.md
index e02fa572..b2cd5e88 100644
--- a/docs/data-sources/resourcemanager_project.md
+++ b/docs/data-sources/resourcemanager_project.md
@@ -20,28 +20,18 @@ data "stackit_resourcemanager_project" "example" {
```
+
## Schema
### Optional
- `container_id` (String) Project container ID. Globally unique, user-friendly identifier.
-- `owner_email` (String, Deprecated) Email address of the owner of the project. This value is only considered during creation. Changing it afterwards will have no effect.
-
-!> The "owner_email" field has been deprecated in favor of the "members" field. Please use the "members" field to assign the owner role to a user, by setting the "role" field to `owner`.
+- `owner_email` (String) Email address of the owner of the project. This value is only considered during creation. Changing it afterwards will have no effect.
- `project_id` (String) Project UUID identifier. This is the ID that can be used in most of the other resources to identify the project.
### Read-Only
- `id` (String) Terraform's internal data source. ID. It is structured as "`container_id`".
- `labels` (Map of String) Labels are key-value string pairs which can be attached to a resource container. A label key must match the regex [A-ZÄÜÖa-zäüöß0-9_-]{1,64}. A label value must match the regex ^$|[A-ZÄÜÖa-zäüöß0-9_-]{1,64}
-- `members` (Attributes List) The members assigned to the project. At least one subject needs to be a user, and not a client or service account. (see [below for nested schema](#nestedatt--members))
- `name` (String) Project name.
- `parent_container_id` (String) Parent resource identifier. Both container ID (user-friendly) and UUID are supported
-
-
-### Nested Schema for `members`
-
-Read-Only:
-
-- `role` (String) The role of the member in the project. Legacy roles (`project.admin`, `project.auditor`, `project.member`, `project.owner`) are not supported.
-- `subject` (String) Unique identifier of the user, service account or client. This is usually the email address for users or service accounts, and the name in case of clients.
diff --git a/docs/resources/resourcemanager_project.md b/docs/resources/resourcemanager_project.md
index d240e864..dd17c357 100644
--- a/docs/resources/resourcemanager_project.md
+++ b/docs/resources/resourcemanager_project.md
@@ -19,20 +19,12 @@ resource "stackit_resourcemanager_project" "example" {
labels = {
"Label 1" = "foo"
}
- members = [
- {
- "subject" : "john.doe@stackit.cloud"
- "role" : "owner",
- },
- {
- "subject" : "some.engineer@stackit.cloud"
- "role" : "reader",
- },
- ]
+ owner_email = "john.doe@stackit.cloud"
}
```
+
## Schema
### Required
@@ -43,21 +35,10 @@ resource "stackit_resourcemanager_project" "example" {
### Optional
- `labels` (Map of String) Labels are key-value string pairs which can be attached to a resource container. A label key must match the regex [A-ZÄÜÖa-zäüöß0-9_-]{1,64}. A label value must match the regex ^$|[A-ZÄÜÖa-zäüöß0-9_-]{1,64}
-- `members` (Attributes List) The members assigned to the project. At least one subject needs to be a user, and not a client or service account. (see [below for nested schema](#nestedatt--members))
-- `owner_email` (String, Deprecated) Email address of the owner of the project. This value is only considered during creation. Changing it afterwards will have no effect.
-
-!> The "owner_email" field has been deprecated in favor of the "members" field. Please use the "members" field to assign the owner role to a user, by setting the "role" field to `owner`.
+- `owner_email` (String) Email address of the owner of the project. This value is only considered during creation. Changing it afterwards will have no effect.
### Read-Only
- `container_id` (String) Project container ID. Globally unique, user-friendly identifier.
- `id` (String) Terraform's internal resource ID. It is structured as "`container_id`".
- `project_id` (String) Project UUID identifier. This is the ID that can be used in most of the other resources to identify the project.
-
-
-### Nested Schema for `members`
-
-Required:
-
-- `role` (String) The role of the member in the project. Possible values include, but are not limited to: `owner`, `editor`, `reader`. Legacy roles (`project.admin`, `project.auditor`, `project.member`, `project.owner`) are not supported.
-- `subject` (String) Unique identifier of the user, service account or client. This is usually the email address for users or service accounts, and the name in case of clients.
diff --git a/examples/resources/stackit_resourcemanager_project/resource.tf b/examples/resources/stackit_resourcemanager_project/resource.tf
index dd9a4717..518d4eaa 100644
--- a/examples/resources/stackit_resourcemanager_project/resource.tf
+++ b/examples/resources/stackit_resourcemanager_project/resource.tf
@@ -4,14 +4,5 @@ resource "stackit_resourcemanager_project" "example" {
labels = {
"Label 1" = "foo"
}
- members = [
- {
- "subject" : "john.doe@stackit.cloud"
- "role" : "owner",
- },
- {
- "subject" : "some.engineer@stackit.cloud"
- "role" : "reader",
- },
- ]
+ owner_email = "john.doe@stackit.cloud"
}
diff --git a/stackit/internal/services/resourcemanager/project/datasource.go b/stackit/internal/services/resourcemanager/project/datasource.go
index 5fec6612..665ae8b5 100644
--- a/stackit/internal/services/resourcemanager/project/datasource.go
+++ b/stackit/internal/services/resourcemanager/project/datasource.go
@@ -102,18 +102,17 @@ func (d *projectDataSource) Configure(ctx context.Context, req datasource.Config
// Schema defines the schema for the data source.
func (d *projectDataSource) Schema(_ context.Context, _ datasource.SchemaRequest, resp *datasource.SchemaResponse) {
descriptions := map[string]string{
- "main": "Resource Manager project data source schema. To identify the project, you need to provider either project_id or container_id. If you provide both, project_id will be used.",
- "id": "Terraform's internal data source. ID. It is structured as \"`container_id`\".",
- "project_id": "Project UUID identifier. This is the ID that can be used in most of the other resources to identify the project.",
- "container_id": "Project container ID. Globally unique, user-friendly identifier.",
- "parent_container_id": "Parent resource identifier. Both container ID (user-friendly) and UUID are supported",
- "name": "Project name.",
- "labels": `Labels are key-value string pairs which can be attached to a resource container. A label key must match the regex [A-ZÄÜÖa-zäüöß0-9_-]{1,64}. A label value must match the regex ^$|[A-ZÄÜÖa-zäüöß0-9_-]{1,64}`,
- "owner_email": "Email address of the owner of the project. This value is only considered during creation. Changing it afterwards will have no effect.",
- "owner_email_deprecation_message": "The \"owner_email\" field has been deprecated in favor of the \"members\" field. Please use the \"members\" field to assign the owner role to a user, by setting the \"role\" field to `owner`.",
- "members": "The members assigned to the project. At least one subject needs to be a user, and not a client or service account.",
- "members.role": fmt.Sprintf("The role of the member in the project. Legacy roles (%s) are not supported.", strings.Join(utils.QuoteValues(utils.LegacyProjectRoles), ", ")),
- "members.subject": "Unique identifier of the user, service account or client. This is usually the email address for users or service accounts, and the name in case of clients.",
+ "main": "Resource Manager project data source schema. To identify the project, you need to provider either project_id or container_id. If you provide both, project_id will be used.",
+ "id": "Terraform's internal data source. ID. It is structured as \"`container_id`\".",
+ "project_id": "Project UUID identifier. This is the ID that can be used in most of the other resources to identify the project.",
+ "container_id": "Project container ID. Globally unique, user-friendly identifier.",
+ "parent_container_id": "Parent resource identifier. Both container ID (user-friendly) and UUID are supported",
+ "name": "Project name.",
+ "labels": `Labels are key-value string pairs which can be attached to a resource container. A label key must match the regex [A-ZÄÜÖa-zäüöß0-9_-]{1,64}. A label value must match the regex ^$|[A-ZÄÜÖa-zäüöß0-9_-]{1,64}`,
+ "owner_email": "Email address of the owner of the project. This value is only considered during creation. Changing it afterwards will have no effect.",
+ "members": "The members assigned to the project. At least one subject needs to be a user, and not a client or service account.",
+ "members.role": fmt.Sprintf("The role of the member in the project. Legacy roles (%s) are not supported.", strings.Join(utils.QuoteValues(utils.LegacyProjectRoles), ", ")),
+ "members.subject": "Unique identifier of the user, service account or client. This is usually the email address for users or service accounts, and the name in case of clients.",
}
resp.Schema = schema.Schema{
@@ -170,10 +169,8 @@ func (d *projectDataSource) Schema(_ context.Context, _ datasource.SchemaRequest
},
},
"owner_email": schema.StringAttribute{
- Description: descriptions["owner_email"],
- DeprecationMessage: descriptions["owner_email_deprecation_message"],
- MarkdownDescription: fmt.Sprintf("%s\n\n!> %s", descriptions["owner_email"], descriptions["owner_email_deprecation_message"]),
- Optional: true,
+ Description: descriptions["owner_email"],
+ Optional: true,
},
"members": schema.ListNestedAttribute{
Description: descriptions["members"],
diff --git a/stackit/internal/services/resourcemanager/project/resource.go b/stackit/internal/services/resourcemanager/project/resource.go
index c7507775..dd5754f1 100644
--- a/stackit/internal/services/resourcemanager/project/resource.go
+++ b/stackit/internal/services/resourcemanager/project/resource.go
@@ -147,18 +147,17 @@ func (r *projectResource) Configure(ctx context.Context, req resource.ConfigureR
// Schema defines the schema for the resource.
func (r *projectResource) Schema(_ context.Context, _ resource.SchemaRequest, resp *resource.SchemaResponse) {
descriptions := map[string]string{
- "main": "Resource Manager project resource schema. To use this resource, it is required that you set the service account email in the provider configuration.",
- "id": "Terraform's internal resource ID. It is structured as \"`container_id`\".",
- "project_id": "Project UUID identifier. This is the ID that can be used in most of the other resources to identify the project.",
- "container_id": "Project container ID. Globally unique, user-friendly identifier.",
- "parent_container_id": "Parent resource identifier. Both container ID (user-friendly) and UUID are supported",
- "name": "Project name.",
- "labels": "Labels are key-value string pairs which can be attached to a resource container. A label key must match the regex [A-ZÄÜÖa-zäüöß0-9_-]{1,64}. A label value must match the regex ^$|[A-ZÄÜÖa-zäüöß0-9_-]{1,64}",
- "owner_email": "Email address of the owner of the project. This value is only considered during creation. Changing it afterwards will have no effect.",
- "owner_email_deprecation_message": "The \"owner_email\" field has been deprecated in favor of the \"members\" field. Please use the \"members\" field to assign the owner role to a user, by setting the \"role\" field to `owner`.",
- "members": "The members assigned to the project. At least one subject needs to be a user, and not a client or service account.",
- "members.role": fmt.Sprintf("The role of the member in the project. Possible values include, but are not limited to: `owner`, `editor`, `reader`. Legacy roles (%s) are not supported.", strings.Join(utils.QuoteValues(utils.LegacyProjectRoles), ", ")),
- "members.subject": "Unique identifier of the user, service account or client. This is usually the email address for users or service accounts, and the name in case of clients.",
+ "main": "Resource Manager project resource schema. To use this resource, it is required that you set the service account email in the provider configuration.",
+ "id": "Terraform's internal resource ID. It is structured as \"`container_id`\".",
+ "project_id": "Project UUID identifier. This is the ID that can be used in most of the other resources to identify the project.",
+ "container_id": "Project container ID. Globally unique, user-friendly identifier.",
+ "parent_container_id": "Parent resource identifier. Both container ID (user-friendly) and UUID are supported",
+ "name": "Project name.",
+ "labels": "Labels are key-value string pairs which can be attached to a resource container. A label key must match the regex [A-ZÄÜÖa-zäüöß0-9_-]{1,64}. A label value must match the regex ^$|[A-ZÄÜÖa-zäüöß0-9_-]{1,64}",
+ "owner_email": "Email address of the owner of the project. This value is only considered during creation. Changing it afterwards will have no effect.",
+ "members": "The members assigned to the project. At least one subject needs to be a user, and not a client or service account.",
+ "members.role": fmt.Sprintf("The role of the member in the project. Possible values include, but are not limited to: `owner`, `editor`, `reader`. Legacy roles (%s) are not supported.", strings.Join(utils.QuoteValues(utils.LegacyProjectRoles), ", ")),
+ "members.subject": "Unique identifier of the user, service account or client. This is usually the email address for users or service accounts, and the name in case of clients.",
}
resp.Schema = schema.Schema{
@@ -224,16 +223,13 @@ func (r *projectResource) Schema(_ context.Context, _ resource.SchemaRequest, re
},
},
"owner_email": schema.StringAttribute{
- Description: descriptions["owner_email"],
- DeprecationMessage: descriptions["owner_email_deprecation_message"],
- MarkdownDescription: fmt.Sprintf("%s\n\n!> %s", descriptions["owner_email"], descriptions["owner_email_deprecation_message"]),
+ Description: descriptions["owner_email"],
// When removing the owner_email field, we should mark the members field as required and add a listvalidator.SizeAtLeast(1) validator to it
Optional: true,
},
"members": schema.ListNestedAttribute{
Description: descriptions["members"],
Optional: true,
- // Computed: true,
NestedObject: schema.NestedAttributeObject{
Attributes: map[string]schema.Attribute{
"role": schema.StringAttribute{
@@ -254,6 +250,39 @@ func (r *projectResource) Schema(_ context.Context, _ resource.SchemaRequest, re
}
}
+// ModifyPlan will be called in the Plan phase and will check if the members field is set
+func (r *projectResource) ModifyPlan(ctx context.Context, req resource.ModifyPlanRequest, resp *resource.ModifyPlanResponse) { // nolint:gocritic // function signature required by Terraform
+ var model Model
+ diags := req.Plan.Get(ctx, &model)
+ if diags.HasError() {
+ resp.Diagnostics.Append(diags...)
+ return
+ }
+
+ membersResp, err := r.authorizationClient.ListMembersExecute(ctx, projectResourceType, model.ProjectId.ValueString())
+ if err != nil {
+ core.LogAndAddError(ctx, &resp.Diagnostics, "Error creating project", fmt.Sprintf("Reading members: %v", err))
+ return
+ }
+
+ members := []string{}
+ for _, m := range *membersResp.Members {
+ if utils.IsLegacyProjectRole(*m.Role) {
+ continue
+ }
+ members = append(members, fmt.Sprintf(" - %s (%s)", *m.Subject, *m.Role))
+ }
+
+ if !(model.Members.IsNull() || model.Members.IsUnknown()) {
+ core.LogAndAddWarning(ctx, &resp.Diagnostics, "The members set in the \"members\" field will override the current members in your project",
+ fmt.Sprintf("%s\n%s\n%s\n\n%s",
+ "The current members in your project will be removed and replaced with the members set in the \"members\" field.",
+ "This might not be represented in the Terraform plan if you are migrating from the \"owner_email\" field, since the current members are not yet set in the state.",
+ "Please make sure that the members in the \"members\" field are correct and complete.",
+ fmt.Sprintf("Current members in your project:\n%v", strings.Join(members, "\n"))))
+ }
+}
+
// ConfigValidators validates the resource configuration
func (r *projectResource) ConfigValidators(_ context.Context) []resource.ConfigValidator {
return []resource.ConfigValidator{