Skip to content

Commit 7cb81c7

Browse files
committed
feat: implement service account resource/datasource
1 parent 81f876a commit 7cb81c7

File tree

14 files changed

+974
-8
lines changed

14 files changed

+974
-8
lines changed
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
---
2+
# generated by https://github.com/hashicorp/terraform-plugin-docs
3+
page_title: "stackit_service_account Data Source - stackit"
4+
subcategory: ""
5+
description: |-
6+
Schema for a STACKIT service account resource.
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_service_account (Data Source)
11+
12+
Schema for a STACKIT service account resource.
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+
data "stackit_service_account" "sa" {
20+
project_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
21+
email = "sa01-8565oq1@sa.stackit.cloud"
22+
}
23+
```
24+
25+
<!-- schema generated by tfplugindocs -->
26+
## Schema
27+
28+
### Required
29+
30+
- `email` (String) Email of the service account.
31+
- `project_id` (String) STACKIT project ID to which the service account is associated.
32+
33+
### Read-Only
34+
35+
- `id` (String) Terraform's internal resource ID, structured as "project_id,email".
36+
- `name` (String) Name of the service account.

docs/index.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -174,6 +174,7 @@ Note: AWS specific checks must be skipped as they do not work on STACKIT. For de
174174
- `secretsmanager_custom_endpoint` (String) Custom endpoint for the Secrets Manager service
175175
- `server_backup_custom_endpoint` (String) Custom endpoint for the Server Backup service
176176
- `server_update_custom_endpoint` (String) Custom endpoint for the Server Update service
177+
- `service_account_custom_endpoint` (String) Custom endpoint for the Service Account service
177178
- `service_account_email` (String, Deprecated) Service account email. It can also be set using the environment variable STACKIT_SERVICE_ACCOUNT_EMAIL. It is required if you want to use the resource manager project resource.
178179
- `service_account_key` (String) Service account key used for authentication. If set, the key flow will be used to authenticate all operations.
179180
- `service_account_key_path` (String) Path for the service account key used for authentication. If set, the key flow will be used to authenticate all operations.

docs/resources/service_account.md

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
---
2+
# generated by https://github.com/hashicorp/terraform-plugin-docs
3+
page_title: "stackit_service_account Resource - stackit"
4+
subcategory: ""
5+
description: |-
6+
Schema for a STACKIT service account resource.
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_service_account (Resource)
11+
12+
Schema for a STACKIT service account resource.
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_service_account" "sa" {
20+
project_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
21+
name = "sa01"
22+
}
23+
```
24+
25+
<!-- schema generated by tfplugindocs -->
26+
## Schema
27+
28+
### Required
29+
30+
- `name` (String) Name of the service account.
31+
- `project_id` (String) STACKIT project ID to which the service account is associated.
32+
33+
### Read-Only
34+
35+
- `email` (String) Email of the service account.
36+
- `id` (String) Terraform's internal resource ID, structured as "project_id,email".
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
data "stackit_service_account" "sa" {
2+
project_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
3+
email = "sa01-8565oq1@sa.stackit.cloud"
4+
}
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
resource "stackit_service_account" "sa" {
2+
project_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
3+
name = "sa01"
4+
}

go.mod

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ require (
2929
github.com/stackitcloud/stackit-sdk-go/services/secretsmanager v0.11.0
3030
github.com/stackitcloud/stackit-sdk-go/services/serverbackup v0.6.0
3131
github.com/stackitcloud/stackit-sdk-go/services/serverupdate v0.5.0
32+
github.com/stackitcloud/stackit-sdk-go/services/serviceaccount v0.6.0
3233
github.com/stackitcloud/stackit-sdk-go/services/serviceenablement v0.5.0
3334
github.com/stackitcloud/stackit-sdk-go/services/ske v0.22.0
3435
github.com/stackitcloud/stackit-sdk-go/services/sqlserverflex v1.0.0

go.sum

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -189,6 +189,8 @@ github.com/stackitcloud/stackit-sdk-go/services/serverbackup v0.6.0 h1:cESGAkm0f
189189
github.com/stackitcloud/stackit-sdk-go/services/serverbackup v0.6.0/go.mod h1:aYPLsiImzWaYXEfYIZ0wJnV56PwcR+buy8Xu9jjbfGA=
190190
github.com/stackitcloud/stackit-sdk-go/services/serverupdate v0.5.0 h1:TMUxDh8XGgWUpnWo7GsawVq2ICDsy/r8dMlfC26MR5g=
191191
github.com/stackitcloud/stackit-sdk-go/services/serverupdate v0.5.0/go.mod h1:giHnHz3kHeLY8Av9MZLsyJlaTXYz+BuGqdP/SKB5Vo0=
192+
github.com/stackitcloud/stackit-sdk-go/services/serviceaccount v0.6.0 h1:y+XzJcntHJ7M+IWWvAUkiVFA8op+jZxwHs3ktW2aLoA=
193+
github.com/stackitcloud/stackit-sdk-go/services/serviceaccount v0.6.0/go.mod h1:J/Wa67cbDI1wAyxib9PiEbNqGfIoFUH+DSLueVazQx8=
192194
github.com/stackitcloud/stackit-sdk-go/services/serviceenablement v0.5.0 h1:QG+rGBHsyXOlJ3ZIeOgExGqu9PoTlGY1rltW/VpG6lw=
193195
github.com/stackitcloud/stackit-sdk-go/services/serviceenablement v0.5.0/go.mod h1:16dOVT052cMuHhUJ3NIcPuY7TrpCr9QlxmvvfjLZubA=
194196
github.com/stackitcloud/stackit-sdk-go/services/ske v0.22.0 h1:3KUVls8zXsbT2tOYRSHyp3/l0Kpjl4f3INmQKYTe65Y=

stackit/internal/core/core.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ type ProviderData struct {
3838
ServerUpdateCustomEndpoint string
3939
SKECustomEndpoint string
4040
ServiceEnablementCustomEndpoint string
41+
ServiceAccountCustomEndpoint string
4142
EnableBetaResources bool
4243
}
4344

Lines changed: 171 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,171 @@
1+
package account
2+
3+
import (
4+
"context"
5+
"fmt"
6+
7+
"github.com/hashicorp/terraform-plugin-framework/datasource"
8+
"github.com/hashicorp/terraform-plugin-framework/datasource/schema"
9+
"github.com/hashicorp/terraform-plugin-framework/schema/validator"
10+
"github.com/hashicorp/terraform-plugin-framework/types"
11+
"github.com/hashicorp/terraform-plugin-log/tflog"
12+
"github.com/stackitcloud/stackit-sdk-go/core/config"
13+
"github.com/stackitcloud/stackit-sdk-go/services/serviceaccount"
14+
"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/core"
15+
"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/features"
16+
"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/validate"
17+
)
18+
19+
// Ensure the implementation satisfies the expected interfaces.
20+
var (
21+
_ datasource.DataSource = &serviceAccountDataSource{}
22+
)
23+
24+
// NewServiceAccountDataSource creates a new instance of the serviceAccountDataSource.
25+
func NewServiceAccountDataSource() datasource.DataSource {
26+
return &serviceAccountDataSource{}
27+
}
28+
29+
// serviceAccountDataSource is the datasource implementation for service accounts.
30+
type serviceAccountDataSource struct {
31+
client *serviceaccount.APIClient
32+
}
33+
34+
// Configure initializes the serviceAccountDataSource with the provided provider data.
35+
func (r *serviceAccountDataSource) Configure(ctx context.Context, req datasource.ConfigureRequest, resp *datasource.ConfigureResponse) {
36+
// Prevent panic if the provider has not been configured correctly.
37+
if req.ProviderData == nil {
38+
return
39+
}
40+
41+
providerData, ok := req.ProviderData.(core.ProviderData)
42+
if !ok {
43+
core.LogAndAddError(ctx, &resp.Diagnostics, "Error configuring API client", fmt.Sprintf("Expected configure type stackit.ProviderData, got %T", req.ProviderData))
44+
return
45+
}
46+
47+
if !resourceBetaCheckDone {
48+
features.CheckBetaResourcesEnabled(ctx, &providerData, &resp.Diagnostics, "stackit_service_account", "datasource")
49+
if resp.Diagnostics.HasError() {
50+
return
51+
}
52+
resourceBetaCheckDone = true
53+
}
54+
55+
var apiClient *serviceaccount.APIClient
56+
var err error
57+
if providerData.ServiceAccountCustomEndpoint != "" {
58+
apiClient, err = serviceaccount.NewAPIClient(
59+
config.WithCustomAuth(providerData.RoundTripper),
60+
config.WithEndpoint(providerData.ServiceAccountCustomEndpoint),
61+
)
62+
} else {
63+
apiClient, err = serviceaccount.NewAPIClient(
64+
config.WithCustomAuth(providerData.RoundTripper),
65+
)
66+
}
67+
68+
if err != nil {
69+
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))
70+
return
71+
}
72+
73+
r.client = apiClient
74+
tflog.Info(ctx, "Service Account client configured")
75+
}
76+
77+
// Metadata provides metadata for the service account datasource.
78+
func (r *serviceAccountDataSource) Metadata(_ context.Context, req datasource.MetadataRequest, resp *datasource.MetadataResponse) {
79+
resp.TypeName = req.ProviderTypeName + "_service_account"
80+
}
81+
82+
// Schema defines the schema for the service account data source.
83+
func (r *serviceAccountDataSource) Schema(_ context.Context, _ datasource.SchemaRequest, resp *datasource.SchemaResponse) {
84+
descriptions := map[string]string{
85+
"id": "Terraform's internal resource ID, structured as \"project_id,email\".",
86+
"project_id": "STACKIT project ID to which the service account is associated.",
87+
"name": "Name of the service account.",
88+
"email": "Email of the service account.",
89+
}
90+
91+
// Define the schema with validation rules and descriptions for each attribute.
92+
// The datasource schema differs slightly from the resource schema.
93+
// In this case, the email attribute is required to read the service account data from the API.
94+
resp.Schema = schema.Schema{
95+
MarkdownDescription: features.AddBetaDescription("Schema for a STACKIT service account resource."),
96+
Description: "Schema for a STACKIT service account resource.",
97+
Attributes: map[string]schema.Attribute{
98+
"id": schema.StringAttribute{
99+
Description: descriptions["id"],
100+
Computed: true,
101+
},
102+
"project_id": schema.StringAttribute{
103+
Description: descriptions["project_id"],
104+
Required: true,
105+
Validators: []validator.String{
106+
validate.UUID(),
107+
validate.NoSeparator(),
108+
},
109+
},
110+
"email": schema.StringAttribute{
111+
Description: descriptions["email"],
112+
Required: true,
113+
},
114+
"name": schema.StringAttribute{
115+
Description: descriptions["name"],
116+
Computed: true,
117+
},
118+
},
119+
}
120+
}
121+
122+
// Read reads all service accounts from the API and updates the state with the latest information.
123+
func (r *serviceAccountDataSource) Read(ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse) { // nolint:gocritic // function signature required by Terraform
124+
var model Model
125+
diags := req.Config.Get(ctx, &model)
126+
resp.Diagnostics.Append(diags...)
127+
if resp.Diagnostics.HasError() {
128+
return
129+
}
130+
131+
// Extract the project ID from the model configuration
132+
projectId := model.ProjectId.ValueString()
133+
134+
// Call the API to list service accounts in the specified project
135+
listSaResp, err := r.client.ListServiceAccounts(ctx, projectId).Execute()
136+
if err != nil {
137+
core.LogAndAddError(ctx, &resp.Diagnostics, "Error reading service account", fmt.Sprintf("Error calling API: %v", err))
138+
return
139+
}
140+
141+
// Iterate over the service accounts returned by the API to find the one matching the email
142+
serviceAccounts := *listSaResp.Items
143+
for i := range serviceAccounts {
144+
// Skip if the service account email does not match
145+
if *serviceAccounts[i].Email != model.Email.ValueString() {
146+
continue
147+
}
148+
149+
// Map the API response to the model, updating its fields with the service account data
150+
err = mapCreateOrListResponse(&serviceAccounts[i], &model)
151+
if err != nil {
152+
core.LogAndAddError(ctx, &resp.Diagnostics, "Error reading service account", fmt.Sprintf("Error processing API response: %v", err))
153+
return
154+
}
155+
156+
// Try to parse the name from the provided email address
157+
name, err := parseNameFromEmail(model.Email.ValueString())
158+
if name != "" && err == nil {
159+
model.Name = types.StringValue(name)
160+
}
161+
162+
// Update the state with the service account model
163+
diags = resp.State.Set(ctx, &model)
164+
resp.Diagnostics.Append(diags...)
165+
return
166+
}
167+
168+
// If no matching service account is found, remove the resource from the state
169+
core.LogAndAddError(ctx, &resp.Diagnostics, "Service account not found", "")
170+
resp.State.RemoveResource(ctx)
171+
}

0 commit comments

Comments
 (0)