Skip to content

Commit 9d06d7f

Browse files
committed
feat: implement sa access token resource
1 parent a4654bb commit 9d06d7f

File tree

5 files changed

+24
-28
lines changed

5 files changed

+24
-28
lines changed

docs/resources/service_account_access_token.md

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -17,14 +17,11 @@ description: |-
1717
rotation_days = 80
1818
}
1919
20-
// The access token is valid for 180 days but is configured to rotate every 80 days
21-
// when a Terraform apply is triggered.
2220
resource "stackit_service_account_access_token" "sa1" {
2321
project_id = var.stackit_project_id
2422
service_account_email = stackit_service_account.sa.email
2523
ttl_days = 180
2624
27-
// Trigger token rotation based on time_rotating changes.
2825
rotate_when_changed = {
2926
rotation = time_rotating.rotate.id
3027
}
@@ -50,14 +47,11 @@ resource "time_rotating" "rotate" {
5047
rotation_days = 80
5148
}
5249
53-
// The access token is valid for 180 days but is configured to rotate every 80 days
54-
// when a Terraform apply is triggered.
5550
resource "stackit_service_account_access_token" "sa1" {
5651
project_id = var.stackit_project_id
5752
service_account_email = stackit_service_account.sa.email
5853
ttl_days = 180
5954
60-
// Trigger token rotation based on time_rotating changes.
6155
rotate_when_changed = {
6256
rotation = time_rotating.rotate.id
6357
}
@@ -83,6 +77,7 @@ resource "stackit_service_account_access_token" "sa1" {
8377
### Read-Only
8478

8579
- `access_token_id` (String) Identifier for the access token linked to the service account.
80+
- `active` (Boolean) Indicate whether the token is currently active or inactive
8681
- `created_at` (String) Timestamp indicating when the access token was created.
8782
- `id` (String) Unique internal resource ID for Terraform, formatted as "project_id,access_token_id".
8883
- `token` (String, Sensitive) JWT access token for API authentication. Prefixed by 'Bearer' and should be stored securely as it is irretrievable once lost.

stackit/internal/services/serviceaccount/serviceaccount_acc_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ func TestServiceAccount(t *testing.T) {
6060
ProtoV6ProviderFactories: testutil.TestAccProtoV6ProviderFactories,
6161
CheckDestroy: testAccCheckServiceAccountDestroy,
6262
Steps: []resource.TestStep{
63-
// Create
63+
// Creation
6464
{
6565
Config: inputServiceAccountResourceConfig(serviceAccountResource["name01"]),
6666
Check: resource.ComposeAggregateTestCheckFunc(

stackit/internal/services/serviceaccount/token/const.go

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,14 +16,11 @@ resource "time_rotating" "rotate" {
1616
rotation_days = 80
1717
}
1818
19-
// The access token is valid for 180 days but is configured to rotate every 80 days
20-
// when a Terraform apply is triggered.
2119
resource "stackit_service_account_access_token" "sa1" {
2220
project_id = var.stackit_project_id
2321
service_account_email = stackit_service_account.sa.email
2422
ttl_days = 180
2523
26-
// Trigger token rotation based on time_rotating changes.
2724
rotate_when_changed = {
2825
rotation = time_rotating.rotate.id
2926
}

stackit/internal/services/serviceaccount/token/resource.go

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,9 @@ package token
22

33
import (
44
"context"
5+
"errors"
56
"fmt"
7+
"net/http"
68
"strings"
79
"time"
810

@@ -19,6 +21,7 @@ import (
1921
"github.com/hashicorp/terraform-plugin-framework/types/basetypes"
2022
"github.com/hashicorp/terraform-plugin-log/tflog"
2123
"github.com/stackitcloud/stackit-sdk-go/core/config"
24+
"github.com/stackitcloud/stackit-sdk-go/core/oapierror"
2225
"github.com/stackitcloud/stackit-sdk-go/services/serviceaccount"
2326
"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/conversion"
2427
"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/core"
@@ -46,6 +49,7 @@ type Model struct {
4649
TtlDays types.Int64 `tfsdk:"ttl_days"`
4750
RotateWhenChanged types.Map `tfsdk:"rotate_when_changed"`
4851
Token types.String `tfsdk:"token"`
52+
Active types.Bool `tfsdk:"active"`
4953
CreatedAt types.String `tfsdk:"created_at"`
5054
ValidUntil types.String `tfsdk:"valid_until"`
5155
}
@@ -122,6 +126,7 @@ func (r *serviceAccountTokenResource) Schema(_ context.Context, _ resource.Schem
122126
"rotate_when_changed": "A map of arbitrary key/value pairs that will force recreation of the token when they change, enabling token rotation based on external conditions such as a rotating timestamp. Changing this forces a new resource to be created.",
123127
"access_token_id": "Identifier for the access token linked to the service account.",
124128
"token": "JWT access token for API authentication. Prefixed by 'Bearer' and should be stored securely as it is irretrievable once lost.",
129+
"active": "Indicate whether the token is currently active or inactive",
125130
"created_at": "Timestamp indicating when the access token was created.",
126131
"valid_until": "Estimated expiration timestamp of the access token. For precise validity, check the JWT details.",
127132
}
@@ -182,7 +187,10 @@ func (r *serviceAccountTokenResource) Schema(_ context.Context, _ resource.Schem
182187
Computed: true,
183188
Sensitive: true,
184189
},
185-
190+
"active": schema.BoolAttribute{
191+
Description: descriptions["active"],
192+
Computed: true,
193+
},
186194
"created_at": schema.StringAttribute{
187195
Description: descriptions["created_at"],
188196
Computed: true,
@@ -259,6 +267,12 @@ func (r *serviceAccountTokenResource) Read(ctx context.Context, req resource.Rea
259267
// Fetch the list of service account tokens from the API.
260268
listSaTokensResp, err := r.client.ListAccessTokens(ctx, projectId, serviceAccountEmail).Execute()
261269
if err != nil {
270+
var oapiErr *oapierror.GenericOpenAPIError
271+
ok := errors.As(err, &oapiErr) //nolint:errorlint //complaining that error.As should be used to catch wrapped errors, but this error should not be wrapped
272+
if ok && oapiErr.StatusCode == http.StatusNotFound || oapiErr.StatusCode == http.StatusForbidden {
273+
resp.State.RemoveResource(ctx)
274+
return
275+
}
262276
core.LogAndAddError(ctx, &resp.Diagnostics, "Error reading service account tokens", fmt.Sprintf("Error calling API: %v", err))
263277
return
264278
}
@@ -270,6 +284,12 @@ func (r *serviceAccountTokenResource) Read(ctx context.Context, req resource.Rea
270284
continue
271285
}
272286

287+
if !*saTokens[i].Active {
288+
tflog.Info(ctx, fmt.Sprintf("Service account access token with id %s is not active", model.AccessTokenId.ValueString()))
289+
resp.State.RemoveResource(ctx)
290+
return
291+
}
292+
273293
err = mapListResponse(&saTokens[i], &model)
274294
if err != nil {
275295
core.LogAndAddError(ctx, &resp.Diagnostics, "Error reading service account", fmt.Sprintf("Error processing API response: %v", err))
@@ -365,6 +385,7 @@ func mapCreateResponse(resp *serviceaccount.AccessToken, model *Model) error {
365385
model.Id = types.StringValue(strings.Join(idParts, core.Separator))
366386
model.AccessTokenId = types.StringPointerValue(resp.Id)
367387
model.Token = types.StringPointerValue(resp.Token)
388+
model.Active = types.BoolPointerValue(resp.Active)
368389
model.CreatedAt = createdAt
369390
model.ValidUntil = validUntil
370391

stackit/internal/testutil/testutil.go

Lines changed: 0 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -404,23 +404,6 @@ func AuthorizationProviderConfig() string {
404404
)
405405
}
406406

407-
func ServiceAccountProviderConfig() string {
408-
if ServiceAccountCustomEndpoint == "" {
409-
return `
410-
provider "stackit" {
411-
region = "eu01"
412-
enable_beta_resources = true
413-
}`
414-
}
415-
return fmt.Sprintf(`
416-
provider "stackit" {
417-
service_account_custom_endpoint = "%s"
418-
enable_beta_resources = true
419-
}`,
420-
ServiceAccountCustomEndpoint,
421-
)
422-
}
423-
424407
func ResourceNameWithDateTime(name string) string {
425408
dateTime := time.Now().Format(time.RFC3339)
426409
// Remove timezone to have a smaller datetime

0 commit comments

Comments
 (0)