Skip to content

Commit 813b8c0

Browse files
authored
feat(resourcemanager): add folder resource/datasource (#975)
* feat(resourcemanager): add folder resource/datasource * feat(resourcemanager): add created_at and updated_at attributes to resourcemanager project/folder --------- Signed-off-by: Mauritz Uphoff <[email protected]>
1 parent 27e4ef0 commit 813b8c0

File tree

17 files changed

+1844
-171
lines changed

17 files changed

+1844
-171
lines changed
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
---
2+
# generated by https://github.com/hashicorp/terraform-plugin-docs
3+
page_title: "stackit_resourcemanager_folder Data Source - stackit"
4+
subcategory: ""
5+
description: |-
6+
Resource Manager folder data source schema. To identify the folder, you need to provide the container_id.
7+
~> This datasource 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.
8+
---
9+
10+
# stackit_resourcemanager_folder (Data Source)
11+
12+
Resource Manager folder data source schema. To identify the folder, you need to provide the container_id.
13+
14+
~> This datasource 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.
15+
16+
## Example Usage
17+
18+
```terraform
19+
data "stackit_resourcemanager_folder" "example" {
20+
container_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
21+
}
22+
```
23+
24+
<!-- schema generated by tfplugindocs -->
25+
## Schema
26+
27+
### Required
28+
29+
- `container_id` (String) Folder container ID. Globally unique, user-friendly identifier.
30+
31+
### Read-Only
32+
33+
- `creation_time` (String) Date-time at which the folder was created.
34+
- `folder_id` (String) Folder UUID identifier. Globally unique folder identifier
35+
- `id` (String) Terraform's internal resource ID. It is structured as "`container_id`".
36+
- `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}.
37+
- `name` (String) The name of the folder.
38+
- `parent_container_id` (String) Parent resource identifier. Both container ID (user-friendly) and UUID are supported.
39+
- `update_time` (String) Date-time at which the folder was last modified.

docs/data-sources/resourcemanager_project.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,9 @@ data "stackit_resourcemanager_project" "example" {
2929

3030
### Read-Only
3131

32+
- `creation_time` (String) Date-time at which the project was created.
3233
- `id` (String) Terraform's internal data source. ID. It is structured as "`container_id`".
3334
- `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}
3435
- `name` (String) Project name.
3536
- `parent_container_id` (String) Parent resource identifier. Both container ID (user-friendly) and UUID are supported
37+
- `update_time` (String) Date-time at which the project was last modified.
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
---
2+
# generated by https://github.com/hashicorp/terraform-plugin-docs
3+
page_title: "stackit_resourcemanager_folder Resource - stackit"
4+
subcategory: ""
5+
description: |-
6+
Resource Manager folder resource schema.
7+
~> 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.
8+
---
9+
10+
# stackit_resourcemanager_folder (Resource)
11+
12+
Resource Manager folder resource schema.
13+
14+
~> 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.
15+
16+
## Example Usage
17+
18+
```terraform
19+
resource "stackit_resourcemanager_folder" "example" {
20+
name = "example-folder"
21+
owner_email = "[email protected]"
22+
parent_container_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
23+
}
24+
25+
# Note:
26+
# You can add projects under folders.
27+
# However, when deleting a project, be aware:
28+
# - Projects may remain "invisible" for up to 7 days after deletion
29+
# - During this time, deleting the parent folder may fail because the project is still technically linked
30+
resource "stackit_resourcemanager_project" "example_project" {
31+
name = "example-project"
32+
owner_email = "[email protected]"
33+
parent_container_id = stackit_resourcemanager_folder.example.container_id
34+
}
35+
36+
# Only use the import statement, if you want to import an existing resourcemanager folder
37+
# Note: There will be a conflict which needs to be resolved manually.
38+
# Must set a configuration value for the owner_email attribute as the provider has marked it as required.
39+
import {
40+
to = stackit_resourcemanager_folder.import-example
41+
id = var.container_id
42+
}
43+
```
44+
45+
<!-- schema generated by tfplugindocs -->
46+
## Schema
47+
48+
### Required
49+
50+
- `name` (String) The name of the folder.
51+
- `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.
52+
- `parent_container_id` (String) Parent resource identifier. Both container ID (user-friendly) and UUID are supported.
53+
54+
### Optional
55+
56+
- `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}.
57+
58+
### Read-Only
59+
60+
- `container_id` (String) Folder container ID. Globally unique, user-friendly identifier.
61+
- `creation_time` (String) Date-time at which the folder was created.
62+
- `folder_id` (String) Folder UUID identifier. Globally unique folder identifier
63+
- `id` (String) Terraform's internal resource ID. It is structured as "`container_id`".
64+
- `update_time` (String) Date-time at which the folder was last modified.

docs/resources/resourcemanager_project.md

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,13 @@
33
page_title: "stackit_resourcemanager_project Resource - stackit"
44
subcategory: ""
55
description: |-
6-
Resource Manager project resource schema. To use this resource, it is required that you set the service account email in the provider configuration.
6+
Resource Manager project resource schema.
77
-> In case you're getting started with an empty STACKIT organization and want to use this resource to create projects in it, check out this guide https://registry.terraform.io/providers/stackitcloud/stackit/latest/docs/guides/stackit_org_service_account for how to create a service account which you can use for authentication in the STACKIT Terraform provider.
88
---
99

1010
# stackit_resourcemanager_project (Resource)
1111

12-
Resource Manager project resource schema. To use this resource, it is required that you set the service account email in the provider configuration.
12+
Resource Manager project resource schema.
1313

1414
-> In case you're getting started with an empty STACKIT organization and want to use this resource to create projects in it, check out [this guide](https://registry.terraform.io/providers/stackitcloud/stackit/latest/docs/guides/stackit_org_service_account) for how to create a service account which you can use for authentication in the STACKIT Terraform provider.
1515

@@ -52,5 +52,7 @@ To create a project within a STACKIT Network Area, setting the label `networkAre
5252
### Read-Only
5353

5454
- `container_id` (String) Project container ID. Globally unique, user-friendly identifier.
55+
- `creation_time` (String) Date-time at which the project was created.
5556
- `id` (String) Terraform's internal resource ID. It is structured as "`container_id`".
5657
- `project_id` (String) Project UUID identifier. This is the ID that can be used in most of the other resources to identify the project.
58+
- `update_time` (String) Date-time at which the project was last modified.
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
data "stackit_resourcemanager_folder" "example" {
2+
container_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
3+
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
resource "stackit_resourcemanager_folder" "example" {
2+
name = "example-folder"
3+
owner_email = "[email protected]"
4+
parent_container_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
5+
}
6+
7+
# Note:
8+
# You can add projects under folders.
9+
# However, when deleting a project, be aware:
10+
# - Projects may remain "invisible" for up to 7 days after deletion
11+
# - During this time, deleting the parent folder may fail because the project is still technically linked
12+
resource "stackit_resourcemanager_project" "example_project" {
13+
name = "example-project"
14+
owner_email = "[email protected]"
15+
parent_container_id = stackit_resourcemanager_folder.example.container_id
16+
}
17+
18+
# Only use the import statement, if you want to import an existing resourcemanager folder
19+
# Note: There will be a conflict which needs to be resolved manually.
20+
# Must set a configuration value for the owner_email attribute as the provider has marked it as required.
21+
import {
22+
to = stackit_resourcemanager_folder.import-example
23+
id = var.container_id
24+
}
Lines changed: 185 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,185 @@
1+
package folder
2+
3+
import (
4+
"context"
5+
"fmt"
6+
"net/http"
7+
"regexp"
8+
9+
"github.com/hashicorp/terraform-plugin-framework-validators/mapvalidator"
10+
"github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator"
11+
"github.com/hashicorp/terraform-plugin-framework/datasource"
12+
"github.com/hashicorp/terraform-plugin-framework/datasource/schema"
13+
"github.com/hashicorp/terraform-plugin-framework/schema/validator"
14+
"github.com/hashicorp/terraform-plugin-framework/types"
15+
"github.com/hashicorp/terraform-plugin-log/tflog"
16+
"github.com/stackitcloud/stackit-sdk-go/services/resourcemanager"
17+
"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/conversion"
18+
"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/core"
19+
"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/features"
20+
resourcemanagerUtils "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/services/resourcemanager/utils"
21+
"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/utils"
22+
"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/validate"
23+
)
24+
25+
// Ensure the implementation satisfies the expected interfaces.
26+
var (
27+
_ datasource.DataSource = &folderDataSource{}
28+
_ datasource.DataSourceWithConfigure = &folderDataSource{}
29+
)
30+
31+
// NewFolderDataSource is a helper function to simplify the provider implementation.
32+
func NewFolderDataSource() datasource.DataSource {
33+
return &folderDataSource{}
34+
}
35+
36+
// folderDataSource is the data source implementation.
37+
type folderDataSource struct {
38+
client *resourcemanager.APIClient
39+
}
40+
41+
// Metadata returns the data source type name.
42+
func (d *folderDataSource) Metadata(_ context.Context, req datasource.MetadataRequest, resp *datasource.MetadataResponse) {
43+
resp.TypeName = req.ProviderTypeName + "_resourcemanager_folder"
44+
}
45+
46+
func (d *folderDataSource) Configure(ctx context.Context, req datasource.ConfigureRequest, resp *datasource.ConfigureResponse) {
47+
providerData, ok := conversion.ParseProviderData(ctx, req.ProviderData, &resp.Diagnostics)
48+
if !ok {
49+
return
50+
}
51+
52+
features.CheckBetaResourcesEnabled(ctx, &providerData, &resp.Diagnostics, "stackit_resourcemanager_folder", "datasource")
53+
if resp.Diagnostics.HasError() {
54+
return
55+
}
56+
57+
apiClient := resourcemanagerUtils.ConfigureClient(ctx, &providerData, &resp.Diagnostics)
58+
if resp.Diagnostics.HasError() {
59+
return
60+
}
61+
d.client = apiClient
62+
tflog.Info(ctx, "Resource Manager client configured")
63+
}
64+
65+
// Schema defines the schema for the data source.
66+
func (d *folderDataSource) Schema(_ context.Context, _ datasource.SchemaRequest, resp *datasource.SchemaResponse) {
67+
descriptions := map[string]string{
68+
"main": "Resource Manager folder data source schema. To identify the folder, you need to provide the container_id.",
69+
"id": "Terraform's internal resource ID. It is structured as \"`container_id`\".",
70+
"container_id": "Folder container ID. Globally unique, user-friendly identifier.",
71+
"folder_id": "Folder UUID identifier. Globally unique folder identifier",
72+
"parent_container_id": "Parent resource identifier. Both container ID (user-friendly) and UUID are supported.",
73+
"name": "The name of the folder.",
74+
"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}.",
75+
"owner_email": "Email address of the owner of the folder. This value is only considered during creation. Changing it afterwards will have no effect.",
76+
"creation_time": "Date-time at which the folder was created.",
77+
"update_time": "Date-time at which the folder was last modified.",
78+
}
79+
80+
resp.Schema = schema.Schema{
81+
Description: features.AddBetaDescription(descriptions["main"], core.Datasource),
82+
Attributes: map[string]schema.Attribute{
83+
"id": schema.StringAttribute{
84+
Description: descriptions["id"],
85+
Computed: true,
86+
},
87+
"container_id": schema.StringAttribute{
88+
Description: descriptions["container_id"],
89+
Validators: []validator.String{
90+
validate.NoSeparator(),
91+
},
92+
Required: true,
93+
},
94+
"folder_id": schema.StringAttribute{
95+
Description: descriptions["folder_id"],
96+
Computed: true,
97+
Validators: []validator.String{
98+
validate.UUID(),
99+
},
100+
},
101+
"parent_container_id": schema.StringAttribute{
102+
Description: descriptions["parent_container_id"],
103+
Computed: true,
104+
Validators: []validator.String{
105+
validate.NoSeparator(),
106+
},
107+
},
108+
"name": schema.StringAttribute{
109+
Description: descriptions["name"],
110+
Computed: true,
111+
Validators: []validator.String{
112+
stringvalidator.LengthAtLeast(1),
113+
stringvalidator.LengthAtMost(63),
114+
},
115+
},
116+
"labels": schema.MapAttribute{
117+
Description: descriptions["labels"],
118+
ElementType: types.StringType,
119+
Computed: true,
120+
Validators: []validator.Map{
121+
mapvalidator.KeysAre(
122+
stringvalidator.RegexMatches(
123+
regexp.MustCompile(`[A-ZÄÜÖa-zäüöß0-9_-]{1,64}`),
124+
"must match expression"),
125+
),
126+
mapvalidator.ValueStringsAre(
127+
stringvalidator.RegexMatches(
128+
regexp.MustCompile(`[A-ZÄÜÖa-zäüöß0-9_-]{1,64}`),
129+
"must match expression"),
130+
),
131+
},
132+
},
133+
"creation_time": schema.StringAttribute{
134+
Description: descriptions["creation_time"],
135+
Computed: true,
136+
},
137+
"update_time": schema.StringAttribute{
138+
Description: descriptions["update_time"],
139+
Computed: true,
140+
},
141+
},
142+
}
143+
}
144+
145+
// Read refreshes the Terraform state with the latest data.
146+
func (d *folderDataSource) Read(ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse) { // nolint:gocritic // function signature required by Terraform
147+
var model Model
148+
diags := req.Config.Get(ctx, &model)
149+
resp.Diagnostics.Append(diags...)
150+
if resp.Diagnostics.HasError() {
151+
return
152+
}
153+
154+
containerId := model.ContainerId.ValueString()
155+
ctx = tflog.SetField(ctx, "container_id", containerId)
156+
157+
folderResp, err := d.client.GetFolderDetails(ctx, containerId).Execute()
158+
if err != nil {
159+
utils.LogError(
160+
ctx,
161+
&resp.Diagnostics,
162+
err,
163+
"Reading folder",
164+
fmt.Sprintf("folder with ID %q does not exist.", containerId),
165+
map[int]string{
166+
http.StatusForbidden: fmt.Sprintf("folder with ID %q not found or forbidden access", containerId),
167+
},
168+
)
169+
resp.State.RemoveResource(ctx)
170+
return
171+
}
172+
173+
err = mapFolderFields(ctx, folderResp, &model, &resp.State)
174+
if err != nil {
175+
core.LogAndAddError(ctx, &resp.Diagnostics, "Error reading folder", fmt.Sprintf("Processing API response: %v", err))
176+
return
177+
}
178+
179+
diags = resp.State.Set(ctx, &model)
180+
resp.Diagnostics.Append(diags...)
181+
if resp.Diagnostics.HasError() {
182+
return
183+
}
184+
tflog.Info(ctx, "Resource Manager folder read")
185+
}

0 commit comments

Comments
 (0)