-
Notifications
You must be signed in to change notification settings - Fork 33
feat(resourcemanager): add folder resource/datasource #975
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
bda182c
f520b72
e1f0456
0fee44e
1643494
fe258cf
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
--- | ||
# generated by https://github.com/hashicorp/terraform-plugin-docs | ||
page_title: "stackit_resourcemanager_folder Data Source - stackit" | ||
subcategory: "" | ||
description: |- | ||
Resource Manager folder data source schema. To identify the folder, you need to provider the container_id. | ||
--- | ||
|
||
# stackit_resourcemanager_folder (Data Source) | ||
|
||
Resource Manager folder data source schema. To identify the folder, you need to provider the container_id. | ||
|
||
## Example Usage | ||
|
||
```terraform | ||
data "stackit_resourcemanager_folder" "example" { | ||
container_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" | ||
} | ||
``` | ||
|
||
<!-- schema generated by tfplugindocs --> | ||
## Schema | ||
|
||
### Required | ||
|
||
- `container_id` (String) Folder container ID. Globally unique, user-friendly identifier. | ||
|
||
### Read-Only | ||
|
||
- `creation_time` (String) Date-time at which the folder was created. | ||
- `folder_id` (String) Folder UUID identifier. Globally unique folder identifier | ||
- `id` (String) Terraform's internal resource 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}. | ||
- `name` (String) The name of the folder. | ||
- `parent_container_id` (String) Parent resource identifier. Both container ID (user-friendly) and UUID are supported. | ||
- `update_time` (String) Date-time at which the folder was last modified. |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
--- | ||
# generated by https://github.com/hashicorp/terraform-plugin-docs | ||
page_title: "stackit_resourcemanager_folder Resource - stackit" | ||
subcategory: "" | ||
description: |- | ||
Resource Manager folder resource schema. | ||
~> This resource is in beta and may be subject to breaking changes in the future. Use with caution. See our guide https://registry.terraform.io/providers/stackitcloud/stackit/latest/docs/guides/opting_into_beta_resources for how to opt-in to use beta resources. | ||
--- | ||
|
||
# stackit_resourcemanager_folder (Resource) | ||
|
||
Resource Manager folder resource schema. | ||
|
||
~> This resource is in beta and may be subject to breaking changes in the future. Use with caution. See our [guide](https://registry.terraform.io/providers/stackitcloud/stackit/latest/docs/guides/opting_into_beta_resources) for how to opt-in to use beta resources. | ||
|
||
## Example Usage | ||
|
||
```terraform | ||
resource "stackit_resourcemanager_folder" "example" { | ||
name = "example-folder" | ||
owner_email = "[email protected]" | ||
parent_container_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" | ||
} | ||
|
||
# Note: | ||
# You can add projects under folders. | ||
# However, when deleting a project, be aware: | ||
# - Projects may remain "invisible" for up to 7 days after deletion | ||
# - During this time, deleting the parent folder may fail because the project is still technically linked | ||
resource "stackit_resourcemanager_project" "example_project" { | ||
name = "example-project" | ||
owner_email = "[email protected]" | ||
parent_container_id = stackit_resourcemanager_folder.example.container_id | ||
} | ||
``` | ||
|
||
<!-- schema generated by tfplugindocs --> | ||
## Schema | ||
|
||
### Required | ||
|
||
- `name` (String) The name of the folder. | ||
- `owner_email` (String) Email address of the owner of the folder. This value is only considered during creation. Changing it afterwards will have no effect. | ||
- `parent_container_id` (String) Parent resource identifier. Both container ID (user-friendly) and UUID are supported. | ||
|
||
### 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}. | ||
|
||
### Read-Only | ||
|
||
- `container_id` (String) Folder container ID. Globally unique, user-friendly identifier. | ||
- `creation_time` (String) Date-time at which the folder was created. | ||
- `folder_id` (String) Folder UUID identifier. Globally unique folder identifier | ||
- `id` (String) Terraform's internal resource ID. It is structured as "`container_id`". | ||
- `update_time` (String) Date-time at which the folder was last modified. |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
data "stackit_resourcemanager_folder" "example" { | ||
container_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
resource "stackit_resourcemanager_folder" "example" { | ||
name = "example-folder" | ||
owner_email = "[email protected]" | ||
parent_container_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" | ||
} | ||
|
||
# Note: | ||
# You can add projects under folders. | ||
# However, when deleting a project, be aware: | ||
# - Projects may remain "invisible" for up to 7 days after deletion | ||
# - During this time, deleting the parent folder may fail because the project is still technically linked | ||
resource "stackit_resourcemanager_project" "example_project" { | ||
name = "example-project" | ||
owner_email = "[email protected]" | ||
parent_container_id = stackit_resourcemanager_folder.example.container_id | ||
} | ||
Original file line number | Diff line number | Diff line change | ||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|
@@ -0,0 +1,184 @@ | ||||||||||||
package folder | ||||||||||||
|
||||||||||||
import ( | ||||||||||||
"context" | ||||||||||||
"fmt" | ||||||||||||
"net/http" | ||||||||||||
"regexp" | ||||||||||||
|
||||||||||||
"github.com/hashicorp/terraform-plugin-framework-validators/mapvalidator" | ||||||||||||
"github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator" | ||||||||||||
"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" | ||||||||||||
"github.com/stackitcloud/stackit-sdk-go/services/resourcemanager" | ||||||||||||
"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/conversion" | ||||||||||||
"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/core" | ||||||||||||
"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/features" | ||||||||||||
resourcemanagerUtils "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/services/resourcemanager/utils" | ||||||||||||
"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/utils" | ||||||||||||
"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/validate" | ||||||||||||
) | ||||||||||||
|
||||||||||||
// Ensure the implementation satisfies the expected interfaces. | ||||||||||||
var ( | ||||||||||||
_ datasource.DataSource = &folderDataSource{} | ||||||||||||
Comment on lines
+26
to
+27
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nitpick. to ensure the Configure method is always implemented and not changed accidentally
Suggested change
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can be adjusted for the project datasource as well |
||||||||||||
) | ||||||||||||
|
||||||||||||
// NewFolderDataSource is a helper function to simplify the provider implementation. | ||||||||||||
func NewFolderDataSource() datasource.DataSource { | ||||||||||||
return &folderDataSource{} | ||||||||||||
} | ||||||||||||
|
||||||||||||
// folderDataSource is the data source implementation. | ||||||||||||
type folderDataSource struct { | ||||||||||||
client *resourcemanager.APIClient | ||||||||||||
} | ||||||||||||
|
||||||||||||
// Metadata returns the data source type name. | ||||||||||||
func (d *folderDataSource) Metadata(_ context.Context, req datasource.MetadataRequest, resp *datasource.MetadataResponse) { | ||||||||||||
resp.TypeName = req.ProviderTypeName + "_resourcemanager_folder" | ||||||||||||
} | ||||||||||||
|
||||||||||||
func (d *folderDataSource) Configure(ctx context.Context, req datasource.ConfigureRequest, resp *datasource.ConfigureResponse) { | ||||||||||||
providerData, ok := conversion.ParseProviderData(ctx, req.ProviderData, &resp.Diagnostics) | ||||||||||||
if !ok { | ||||||||||||
return | ||||||||||||
} | ||||||||||||
|
||||||||||||
features.CheckBetaResourcesEnabled(ctx, &providerData, &resp.Diagnostics, "stackit_resourcemanager_folder", "datasource") | ||||||||||||
if resp.Diagnostics.HasError() { | ||||||||||||
return | ||||||||||||
} | ||||||||||||
|
||||||||||||
apiClient := resourcemanagerUtils.ConfigureClient(ctx, &providerData, &resp.Diagnostics) | ||||||||||||
if resp.Diagnostics.HasError() { | ||||||||||||
return | ||||||||||||
} | ||||||||||||
d.client = apiClient | ||||||||||||
tflog.Info(ctx, "Resource Manager client configured") | ||||||||||||
} | ||||||||||||
|
||||||||||||
// Schema defines the schema for the data source. | ||||||||||||
func (d *folderDataSource) Schema(_ context.Context, _ datasource.SchemaRequest, resp *datasource.SchemaResponse) { | ||||||||||||
descriptions := map[string]string{ | ||||||||||||
"main": "Resource Manager folder data source schema. To identify the folder, you need to provider the container_id.", | ||||||||||||
"id": "Terraform's internal resource ID. It is structured as \"`container_id`\".", | ||||||||||||
"container_id": "Folder container ID. Globally unique, user-friendly identifier.", | ||||||||||||
"folder_id": "Folder UUID identifier. Globally unique folder identifier", | ||||||||||||
"parent_container_id": "Parent resource identifier. Both container ID (user-friendly) and UUID are supported.", | ||||||||||||
"name": "The name of the folder.", | ||||||||||||
"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 folder. This value is only considered during creation. Changing it afterwards will have no effect.", | ||||||||||||
"creation_time": "Date-time at which the folder was created.", | ||||||||||||
"update_time": "Date-time at which the folder was last modified.", | ||||||||||||
} | ||||||||||||
|
||||||||||||
resp.Schema = schema.Schema{ | ||||||||||||
Description: descriptions["main"], | ||||||||||||
Attributes: map[string]schema.Attribute{ | ||||||||||||
"id": schema.StringAttribute{ | ||||||||||||
Description: descriptions["id"], | ||||||||||||
Computed: true, | ||||||||||||
}, | ||||||||||||
"container_id": schema.StringAttribute{ | ||||||||||||
Description: descriptions["container_id"], | ||||||||||||
Validators: []validator.String{ | ||||||||||||
validate.NoSeparator(), | ||||||||||||
}, | ||||||||||||
Required: true, | ||||||||||||
}, | ||||||||||||
"folder_id": schema.StringAttribute{ | ||||||||||||
Description: descriptions["folder_id"], | ||||||||||||
Computed: true, | ||||||||||||
Validators: []validator.String{ | ||||||||||||
validate.UUID(), | ||||||||||||
}, | ||||||||||||
}, | ||||||||||||
"parent_container_id": schema.StringAttribute{ | ||||||||||||
Description: descriptions["parent_container_id"], | ||||||||||||
Computed: true, | ||||||||||||
Validators: []validator.String{ | ||||||||||||
validate.NoSeparator(), | ||||||||||||
}, | ||||||||||||
}, | ||||||||||||
"name": schema.StringAttribute{ | ||||||||||||
Description: descriptions["name"], | ||||||||||||
Computed: true, | ||||||||||||
Validators: []validator.String{ | ||||||||||||
stringvalidator.LengthAtLeast(1), | ||||||||||||
stringvalidator.LengthAtMost(63), | ||||||||||||
}, | ||||||||||||
}, | ||||||||||||
"labels": schema.MapAttribute{ | ||||||||||||
Description: descriptions["labels"], | ||||||||||||
ElementType: types.StringType, | ||||||||||||
Computed: true, | ||||||||||||
Validators: []validator.Map{ | ||||||||||||
mapvalidator.KeysAre( | ||||||||||||
stringvalidator.RegexMatches( | ||||||||||||
regexp.MustCompile(`[A-ZÄÜÖa-zäüöß0-9_-]{1,64}`), | ||||||||||||
"must match expression"), | ||||||||||||
), | ||||||||||||
mapvalidator.ValueStringsAre( | ||||||||||||
stringvalidator.RegexMatches( | ||||||||||||
regexp.MustCompile(`[A-ZÄÜÖa-zäüöß0-9_-]{1,64}`), | ||||||||||||
"must match expression"), | ||||||||||||
), | ||||||||||||
}, | ||||||||||||
}, | ||||||||||||
"creation_time": schema.StringAttribute{ | ||||||||||||
Description: descriptions["creation_time"], | ||||||||||||
Computed: true, | ||||||||||||
}, | ||||||||||||
"update_time": schema.StringAttribute{ | ||||||||||||
Description: descriptions["update_time"], | ||||||||||||
Computed: true, | ||||||||||||
}, | ||||||||||||
}, | ||||||||||||
} | ||||||||||||
} | ||||||||||||
|
||||||||||||
// Read refreshes the Terraform state with the latest data. | ||||||||||||
func (d *folderDataSource) 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 | ||||||||||||
} | ||||||||||||
|
||||||||||||
containerId := model.ContainerId.ValueString() | ||||||||||||
ctx = tflog.SetField(ctx, "container_id", containerId) | ||||||||||||
|
||||||||||||
folderResp, err := d.client.GetFolderDetails(ctx, containerId).Execute() | ||||||||||||
if err != nil { | ||||||||||||
utils.LogError( | ||||||||||||
ctx, | ||||||||||||
&resp.Diagnostics, | ||||||||||||
err, | ||||||||||||
"Reading folder", | ||||||||||||
fmt.Sprintf("folder with ID %q does not exist.", containerId), | ||||||||||||
map[int]string{ | ||||||||||||
http.StatusForbidden: fmt.Sprintf("folder with ID %q not found or forbidden access", containerId), | ||||||||||||
}, | ||||||||||||
) | ||||||||||||
resp.State.RemoveResource(ctx) | ||||||||||||
return | ||||||||||||
} | ||||||||||||
|
||||||||||||
err = mapFolderDetailsFields(ctx, folderResp, &model, &resp.State) | ||||||||||||
if err != nil { | ||||||||||||
core.LogAndAddError(ctx, &resp.Diagnostics, "Error reading folder", fmt.Sprintf("Processing API response: %v", err)) | ||||||||||||
return | ||||||||||||
} | ||||||||||||
|
||||||||||||
diags = resp.State.Set(ctx, &model) | ||||||||||||
resp.Diagnostics.Append(diags...) | ||||||||||||
if resp.Diagnostics.HasError() { | ||||||||||||
return | ||||||||||||
} | ||||||||||||
tflog.Info(ctx, "Resource Manager folder read") | ||||||||||||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you add an example, how to import a folder like we have it for other resources:
terraform-provider-stackit/examples/resources/stackit_resourcemanager_project/resource.tf
Lines 11 to 17 in fe258cf