Skip to content

Commit 3ebc58f

Browse files
feat(keymanager): add RotateKeyAction (#3517)
* feat(keymanager): add RotateKeyAction * test(keymanager): use key datasource in checks * lint
1 parent 195ce65 commit 3ebc58f

File tree

6 files changed

+649
-0
lines changed

6 files changed

+649
-0
lines changed
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
---
2+
subcategory: "Key Manager"
3+
page_title: "Scaleway: scaleway_key_manager_key_rotate_action"
4+
---
5+
6+
# scaleway_key_manager_key_rotate_action (Action)
7+
8+
<!-- action schema generated by tfplugindocs -->
9+
## Schema
10+
11+
### Required
12+
13+
- `key_id` (String) ID of the key to rotate (UUID format)
14+
15+
### Optional
16+
17+
- `region` (String) Region of the key. If not set, the region is derived from the key_id when possible or from the provider configuration.
18+
19+
Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
package keymanager
2+
3+
import (
4+
"context"
5+
"fmt"
6+
7+
"github.com/hashicorp/terraform-plugin-framework/action"
8+
"github.com/hashicorp/terraform-plugin-framework/action/schema"
9+
"github.com/hashicorp/terraform-plugin-framework/types"
10+
key_manager "github.com/scaleway/scaleway-sdk-go/api/key_manager/v1alpha1"
11+
"github.com/scaleway/scaleway-sdk-go/scw"
12+
"github.com/scaleway/terraform-provider-scaleway/v2/internal/locality"
13+
"github.com/scaleway/terraform-provider-scaleway/v2/internal/locality/regional"
14+
"github.com/scaleway/terraform-provider-scaleway/v2/internal/meta"
15+
)
16+
17+
var (
18+
_ action.Action = (*RotateKeyAction)(nil)
19+
_ action.ActionWithConfigure = (*RotateKeyAction)(nil)
20+
)
21+
22+
type RotateKeyAction struct {
23+
keyManagerAPI *key_manager.API
24+
meta *meta.Meta
25+
}
26+
27+
func (a *RotateKeyAction) Configure(ctx context.Context, req action.ConfigureRequest, resp *action.ConfigureResponse) {
28+
if req.ProviderData == nil {
29+
return
30+
}
31+
32+
m, ok := req.ProviderData.(*meta.Meta)
33+
if !ok {
34+
resp.Diagnostics.AddError(
35+
"Unexpected Action Configure Type",
36+
fmt.Sprintf("Expected *scw.Client, got: %T. Please report this issue to the provider developers.", req.ProviderData),
37+
)
38+
39+
return
40+
}
41+
42+
client := m.ScwClient()
43+
a.keyManagerAPI = key_manager.NewAPI(client)
44+
a.meta = m
45+
}
46+
47+
func (a *RotateKeyAction) Metadata(ctx context.Context, req action.MetadataRequest, resp *action.MetadataResponse) {
48+
resp.TypeName = req.ProviderTypeName + "_key_manager_key_rotate_action"
49+
}
50+
51+
type RotateKeyActionModel struct {
52+
Region types.String `tfsdk:"region"`
53+
KeyID types.String `tfsdk:"key_id"`
54+
}
55+
56+
func NewRotateKeyAction() action.Action {
57+
return &RotateKeyAction{}
58+
}
59+
60+
func (a *RotateKeyAction) Schema(ctx context.Context, req action.SchemaRequest, resp *action.SchemaResponse) {
61+
resp.Schema = schema.Schema{
62+
Attributes: map[string]schema.Attribute{
63+
"region": schema.StringAttribute{
64+
Optional: true,
65+
Description: "Region of the key. If not set, the region is derived from the key_id when possible or from the provider configuration.",
66+
},
67+
"key_id": schema.StringAttribute{
68+
Required: true,
69+
Description: "ID of the key to rotate (UUID format)",
70+
},
71+
},
72+
}
73+
}
74+
75+
func (a *RotateKeyAction) Invoke(ctx context.Context, req action.InvokeRequest, resp *action.InvokeResponse) {
76+
var data RotateKeyActionModel
77+
// Read action config data into the model
78+
resp.Diagnostics.Append(req.Config.Get(ctx, &data)...)
79+
80+
if resp.Diagnostics.HasError() {
81+
return
82+
}
83+
84+
if a.keyManagerAPI == nil {
85+
resp.Diagnostics.AddError(
86+
"Unconfigured keymanagerAPI",
87+
"The action was not properly configured. The Scaleway client is missing. "+
88+
"This is usually a bug in the provider. Please report it to the maintainers.",
89+
)
90+
91+
return
92+
}
93+
94+
keyID := locality.ExpandID(data.KeyID.ValueString())
95+
96+
var (
97+
region scw.Region
98+
err error
99+
)
100+
101+
if !data.Region.IsNull() && data.Region.ValueString() != "" {
102+
region = scw.Region(data.Region.ValueString())
103+
} else {
104+
// Try to derive region from the job_definition_id if it is a regional ID.
105+
if derivedRegion, id, parseErr := regional.ParseID(data.KeyID.ValueString()); parseErr == nil {
106+
region = derivedRegion
107+
keyID = id
108+
} else {
109+
// Use default region from provider configuration
110+
defaultRegion, exists := a.meta.ScwClient().GetDefaultRegion()
111+
if !exists {
112+
resp.Diagnostics.AddError(
113+
"Missing region",
114+
"The region attribute is required to rotate a key. Please provide it explicitly or configure a default region in the provider.",
115+
)
116+
117+
return
118+
}
119+
120+
region = defaultRegion
121+
}
122+
}
123+
124+
rotateReq := &key_manager.RotateKeyRequest{
125+
Region: region,
126+
KeyID: keyID,
127+
}
128+
129+
_, err = a.keyManagerAPI.RotateKey(rotateReq)
130+
if err != nil {
131+
resp.Diagnostics.AddError(
132+
"Error executing Key Manager RotateKey action",
133+
fmt.Sprintf("%s", err))
134+
}
135+
}
Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
package keymanager_test
2+
3+
import (
4+
"testing"
5+
6+
"github.com/hashicorp/terraform-plugin-testing/helper/resource"
7+
"github.com/scaleway/terraform-provider-scaleway/v2/internal/acctest"
8+
)
9+
10+
func TestAccActionRotateKey_Basic(t *testing.T) {
11+
if acctest.IsRunningOpenTofu() {
12+
t.Skip("Skipping TestAccActionRotateKey_Basic because actions are not yet supported on OpenTofu")
13+
}
14+
15+
tt := acctest.NewTestTools(t)
16+
defer tt.Cleanup()
17+
18+
resource.ParallelTest(t, resource.TestCase{
19+
ProtoV6ProviderFactories: tt.ProviderFactories,
20+
CheckDestroy: IsKeyManagerKeyDestroyed(tt),
21+
Steps: []resource.TestStep{
22+
{
23+
Config: `
24+
locals {
25+
region = "fr-par"
26+
}
27+
28+
resource "scaleway_key_manager_key" "main" {
29+
name = "tf-test-kms-key-rotation-action"
30+
region = local.region
31+
usage = "symmetric_encryption"
32+
algorithm = "aes_256_gcm"
33+
description = "Test key"
34+
tags = ["tf", "test"]
35+
unprotected = true
36+
lifecycle {
37+
action_trigger {
38+
events = [after_create]
39+
actions = [action.scaleway_key_manager_key_rotate_action.main]
40+
}
41+
}
42+
}
43+
44+
action "scaleway_key_manager_key_rotate_action" "main" {
45+
config {
46+
key_id = scaleway_key_manager_key.main.id
47+
region = local.region
48+
}
49+
}
50+
`,
51+
},
52+
{
53+
Config: `
54+
locals {
55+
region = "fr-par"
56+
}
57+
58+
resource "scaleway_key_manager_key" "main" {
59+
name = "tf-test-kms-key-rotation-action"
60+
region = local.region
61+
usage = "symmetric_encryption"
62+
algorithm = "aes_256_gcm"
63+
description = "Test key"
64+
tags = ["tf", "test"]
65+
unprotected = true
66+
lifecycle {
67+
action_trigger {
68+
events = [after_create]
69+
actions = [action.scaleway_key_manager_key_rotate_action.main]
70+
}
71+
}
72+
}
73+
74+
action "scaleway_key_manager_key_rotate_action" "main" {
75+
config {
76+
key_id = scaleway_key_manager_key.main.id
77+
region = local.region
78+
}
79+
}
80+
81+
data "scaleway_key_manager_key" "main" {
82+
key_id = scaleway_key_manager_key.main.id
83+
depends_on = [scaleway_key_manager_key.main]
84+
}
85+
`,
86+
Check: resource.ComposeTestCheckFunc(
87+
resource.TestCheckResourceAttr("data.scaleway_key_manager_key.main", "name", "tf-test-kms-key-rotation-action"),
88+
resource.TestCheckResourceAttr("data.scaleway_key_manager_key.main", "rotation_count", "2"),
89+
),
90+
},
91+
},
92+
})
93+
}

0 commit comments

Comments
 (0)