Skip to content

Commit 209596d

Browse files
committed
feat: refactor Key Manager to use usage + algorithm (AWS-like approach)
1 parent b21dcfa commit 209596d

9 files changed

+298
-615
lines changed

internal/services/keymanager/key_resource.go

Lines changed: 50 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -2,16 +2,17 @@ package keymanager
22

33
import (
44
"context"
5+
"fmt"
56

67
"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
8+
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/customdiff"
79
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
810
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation"
911
key_manager "github.com/scaleway/scaleway-sdk-go/api/key_manager/v1alpha1"
1012
"github.com/scaleway/terraform-provider-scaleway/v2/internal/dsf"
1113
"github.com/scaleway/terraform-provider-scaleway/v2/internal/locality/regional"
1214
"github.com/scaleway/terraform-provider-scaleway/v2/internal/services/account"
1315
"github.com/scaleway/terraform-provider-scaleway/v2/internal/types"
14-
"github.com/scaleway/terraform-provider-scaleway/v2/internal/verify"
1516
)
1617

1718
func ResourceKeyManagerKey() *schema.Resource {
@@ -20,6 +21,9 @@ func ResourceKeyManagerKey() *schema.Resource {
2021
ReadContext: resourceKeyManagerKeyRead,
2122
UpdateContext: resourceKeyManagerKeyUpdate,
2223
DeleteContext: resourceKeyManagerKeyDelete,
24+
CustomizeDiff: customdiff.All(
25+
validateUsageAlgorithmCombination(),
26+
),
2327
Schema: map[string]*schema.Schema{
2428
"name": {
2529
Type: schema.TypeString,
@@ -30,56 +34,17 @@ func ResourceKeyManagerKey() *schema.Resource {
3034
"region": regional.Schema(),
3135
"usage": {
3236
Type: schema.TypeString,
33-
Optional: true,
34-
Computed: true,
37+
Required: true,
3538
ValidateFunc: validation.StringInSlice([]string{
3639
"symmetric_encryption", "asymmetric_encryption", "asymmetric_signing",
3740
}, false),
38-
Deprecated: "Use usage_symmetric_encryption, usage_asymmetric_encryption, or usage_asymmetric_signing instead",
39-
Description: "DEPRECATED: Use usage_symmetric_encryption, usage_asymmetric_encryption, or usage_asymmetric_signing instead. Key usage. Possible values: symmetric_encryption, asymmetric_encryption, asymmetric_signing.",
40-
ExactlyOneOf: []string{
41-
"usage",
42-
"usage_symmetric_encryption",
43-
"usage_asymmetric_encryption",
44-
"usage_asymmetric_signing",
45-
},
46-
},
47-
"usage_symmetric_encryption": {
48-
Type: schema.TypeString,
49-
Optional: true,
50-
ValidateDiagFunc: verify.ValidateEnum[key_manager.KeyAlgorithmSymmetricEncryption](),
51-
Description: "Algorithm for symmetric encryption",
52-
ExactlyOneOf: []string{
53-
"usage",
54-
"usage_symmetric_encryption",
55-
"usage_asymmetric_encryption",
56-
"usage_asymmetric_signing",
57-
},
58-
},
59-
"usage_asymmetric_encryption": {
60-
Type: schema.TypeString,
61-
Optional: true,
62-
ValidateDiagFunc: verify.ValidateEnum[key_manager.KeyAlgorithmAsymmetricEncryption](),
63-
Description: "Algorithm for asymmetric encryption",
64-
ExactlyOneOf: []string{
65-
"usage",
66-
"usage_symmetric_encryption",
67-
"usage_asymmetric_encryption",
68-
"usage_asymmetric_signing",
69-
},
70-
},
71-
"usage_asymmetric_signing": {
72-
Type: schema.TypeString,
73-
Optional: true,
74-
ValidateDiagFunc: verify.ValidateEnum[key_manager.KeyAlgorithmAsymmetricSigning](),
75-
Description: "Algorithm for asymmetric signing",
76-
ExactlyOneOf: []string{
77-
"usage",
78-
"usage_symmetric_encryption",
79-
"usage_asymmetric_encryption",
80-
"usage_asymmetric_signing",
81-
},
41+
Description: "Key usage type. Possible values: symmetric_encryption, asymmetric_encryption, asymmetric_signing.",
8242
},
43+
"algorithm": {
44+
Type: schema.TypeString,
45+
Required: true,
46+
Description: "Algorithm to use for the key. The valid algorithms depend on the usage type.",
47+
},
8348
"description": {
8449
Type: schema.TypeString,
8550
Optional: true,
@@ -161,7 +126,13 @@ func resourceKeyManagerKeyCreate(ctx context.Context, d *schema.ResourceData, m
161126
createReq.Origin = key_manager.KeyOrigin(v.(string))
162127
}
163128

164-
createReq.Usage = ExpandKeyUsageFromFields(d)
129+
usage := d.Get("usage").(string)
130+
algorithm := d.Get("algorithm").(string)
131+
keyUsage, err := expandUsageAlgorithm(usage, algorithm)
132+
if err != nil {
133+
return diag.FromErr(err)
134+
}
135+
createReq.Usage = keyUsage
165136

166137
key, err := api.CreateKey(createReq)
167138
if err != nil {
@@ -195,19 +166,7 @@ func resourceKeyManagerKeyRead(ctx context.Context, d *schema.ResourceData, m an
195166
algorithm := AlgorithmFromKeyUsage(key.Usage)
196167

197168
_ = d.Set("usage", usageType)
198-
199-
_, usesLegacy := d.GetOk("usage")
200-
201-
if !usesLegacy {
202-
switch usageType {
203-
case usageSymmetricEncryption:
204-
_ = d.Set("usage_symmetric_encryption", algorithm)
205-
case usageAsymmetricEncryption:
206-
_ = d.Set("usage_asymmetric_encryption", algorithm)
207-
case usageAsymmetricSigning:
208-
_ = d.Set("usage_asymmetric_signing", algorithm)
209-
}
210-
}
169+
_ = d.Set("algorithm", algorithm)
211170

212171
_ = d.Set("description", key.Description)
213172
_ = d.Set("tags", key.Tags)
@@ -285,3 +244,33 @@ func resourceKeyManagerKeyDelete(ctx context.Context, d *schema.ResourceData, m
285244

286245
return nil
287246
}
247+
248+
func validateUsageAlgorithmCombination() schema.CustomizeDiffFunc {
249+
return func(ctx context.Context, diff *schema.ResourceDiff, _ any) error {
250+
// No strict validation here - we let the API validate the algorithm
251+
// This prevents the provider from being a bottleneck when new algorithms are added
252+
return nil
253+
}
254+
}
255+
256+
func expandUsageAlgorithm(usage, algorithm string) (*key_manager.KeyUsage, error) {
257+
switch usage {
258+
case usageSymmetricEncryption:
259+
// Accept any algorithm for symmetric encryption - let API validate
260+
typedAlgo := key_manager.KeyAlgorithmSymmetricEncryption(algorithm)
261+
return &key_manager.KeyUsage{SymmetricEncryption: &typedAlgo}, nil
262+
263+
case usageAsymmetricEncryption:
264+
// Accept any algorithm for asymmetric encryption - let API validate
265+
typedAlgo := key_manager.KeyAlgorithmAsymmetricEncryption(algorithm)
266+
return &key_manager.KeyUsage{AsymmetricEncryption: &typedAlgo}, nil
267+
268+
case usageAsymmetricSigning:
269+
// Accept any algorithm for asymmetric signing - let API validate
270+
typedAlgo := key_manager.KeyAlgorithmAsymmetricSigning(algorithm)
271+
return &key_manager.KeyUsage{AsymmetricSigning: &typedAlgo}, nil
272+
273+
default:
274+
return nil, fmt.Errorf("unknown usage type: %s", usage)
275+
}
276+
}

internal/services/keymanager/key_resource_test.go

Lines changed: 45 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -24,19 +24,20 @@ func TestAccKeyManagerKey_Basic(t *testing.T) {
2424
{
2525
Config: `
2626
resource "scaleway_key_manager_key" "main" {
27-
name = "tf-test-kms-key-unprotected-a"
28-
region = "fr-par"
29-
usage_symmetric_encryption = "aes_256_gcm"
30-
description = "Test key"
31-
tags = ["tf", "test"]
32-
unprotected = true
27+
name = "tf-test-kms-key-unprotected-a"
28+
region = "fr-par"
29+
usage = "symmetric_encryption"
30+
algorithm = "aes_256_gcm"
31+
description = "Test key"
32+
tags = ["tf", "test"]
33+
unprotected = true
3334
}
3435
`,
3536
Check: resource.ComposeTestCheckFunc(
3637
resource.TestCheckResourceAttr("scaleway_key_manager_key.main", "name", "tf-test-kms-key-unprotected-a"),
3738
resource.TestCheckResourceAttr("scaleway_key_manager_key.main", "region", "fr-par"),
3839
resource.TestCheckResourceAttr("scaleway_key_manager_key.main", "usage", "symmetric_encryption"),
39-
resource.TestCheckResourceAttr("scaleway_key_manager_key.main", "usage_symmetric_encryption", "aes_256_gcm"),
40+
resource.TestCheckResourceAttr("scaleway_key_manager_key.main", "algorithm", "aes_256_gcm"),
4041
resource.TestCheckResourceAttr("scaleway_key_manager_key.main", "description", "Test key"),
4142
resource.TestCheckResourceAttr("scaleway_key_manager_key.main", "tags.0", "tf"),
4243
resource.TestCheckResourceAttr("scaleway_key_manager_key.main", "tags.1", "test"),
@@ -58,36 +59,38 @@ func TestAccKeyManagerKey_Update(t *testing.T) {
5859
{
5960
Config: `
6061
resource "scaleway_key_manager_key" "main" {
61-
name = "tf-test-kms-key-update"
62-
region = "fr-par"
63-
usage_symmetric_encryption = "aes_256_gcm"
64-
description = "Test key"
65-
tags = ["tf", "test"]
66-
unprotected = true
62+
name = "tf-test-kms-key-update"
63+
region = "fr-par"
64+
usage = "symmetric_encryption"
65+
algorithm = "aes_256_gcm"
66+
description = "Test key"
67+
tags = ["tf", "test"]
68+
unprotected = true
6769
}
6870
`,
6971
Check: resource.ComposeTestCheckFunc(
7072
resource.TestCheckResourceAttr("scaleway_key_manager_key.main", "name", "tf-test-kms-key-update"),
7173
resource.TestCheckResourceAttr("scaleway_key_manager_key.main", "description", "Test key"),
72-
resource.TestCheckResourceAttr("scaleway_key_manager_key.main", "usage_symmetric_encryption", "aes_256_gcm"),
74+
resource.TestCheckResourceAttr("scaleway_key_manager_key.main", "algorithm", "aes_256_gcm"),
7375
),
7476
},
7577
{
7678
Config: `
7779
resource "scaleway_key_manager_key" "main" {
78-
name = "tf-test-kms-key-updated"
79-
region = "fr-par"
80-
usage_symmetric_encryption = "aes_256_gcm"
81-
description = "Test key updated"
82-
tags = ["tf", "updated"]
83-
unprotected = true
80+
name = "tf-test-kms-key-updated"
81+
region = "fr-par"
82+
usage = "symmetric_encryption"
83+
algorithm = "aes_256_gcm"
84+
description = "Test key updated"
85+
tags = ["tf", "updated"]
86+
unprotected = true
8487
}
8588
`,
8689
Check: resource.ComposeTestCheckFunc(
8790
resource.TestCheckResourceAttr("scaleway_key_manager_key.main", "name", "tf-test-kms-key-updated"),
8891
resource.TestCheckResourceAttr("scaleway_key_manager_key.main", "description", "Test key updated"),
8992
resource.TestCheckResourceAttr("scaleway_key_manager_key.main", "tags.1", "updated"),
90-
resource.TestCheckResourceAttr("scaleway_key_manager_key.main", "usage_symmetric_encryption", "aes_256_gcm"),
93+
resource.TestCheckResourceAttr("scaleway_key_manager_key.main", "algorithm", "aes_256_gcm"),
9194
),
9295
},
9396
},
@@ -139,11 +142,12 @@ func TestAccKeyManagerKey_WithRotationPolicy(t *testing.T) {
139142
{
140143
Config: `
141144
resource "scaleway_key_manager_key" "main" {
142-
name = "tf-test-kms-key-rotation"
143-
region = "fr-par"
144-
usage_symmetric_encryption = "aes_256_gcm"
145-
description = "Test key with rotation policy"
146-
unprotected = true
145+
name = "tf-test-kms-key-rotation"
146+
region = "fr-par"
147+
usage = "symmetric_encryption"
148+
algorithm = "aes_256_gcm"
149+
description = "Test key with rotation policy"
150+
unprotected = true
147151
148152
rotation_policy {
149153
rotation_period = "720h"
@@ -154,7 +158,7 @@ func TestAccKeyManagerKey_WithRotationPolicy(t *testing.T) {
154158
Check: resource.ComposeTestCheckFunc(
155159
resource.TestCheckResourceAttr("scaleway_key_manager_key.main", "name", "tf-test-kms-key-rotation"),
156160
resource.TestCheckResourceAttr("scaleway_key_manager_key.main", "usage", "symmetric_encryption"),
157-
resource.TestCheckResourceAttr("scaleway_key_manager_key.main", "usage_symmetric_encryption", "aes_256_gcm"),
161+
resource.TestCheckResourceAttr("scaleway_key_manager_key.main", "algorithm", "aes_256_gcm"),
158162
resource.TestCheckResourceAttr("scaleway_key_manager_key.main", "description", "Test key with rotation policy"),
159163
resource.TestCheckResourceAttr("scaleway_key_manager_key.main", "rotation_policy.0.rotation_period", "720h0m0s"),
160164
),
@@ -175,17 +179,18 @@ func TestAccKeyManagerKey_WithCustomAlgorithm(t *testing.T) {
175179
{
176180
Config: `
177181
resource "scaleway_key_manager_key" "rsa_4096" {
178-
name = "tf-test-kms-key-rsa4096"
179-
region = "fr-par"
180-
usage_asymmetric_encryption = "rsa_oaep_4096_sha256"
181-
description = "Test key with RSA-4096 algorithm"
182-
unprotected = true
182+
name = "tf-test-kms-key-rsa4096"
183+
region = "fr-par"
184+
usage = "asymmetric_encryption"
185+
algorithm = "rsa_oaep_4096_sha256"
186+
description = "Test key with RSA-4096 algorithm"
187+
unprotected = true
183188
}
184189
`,
185190
Check: resource.ComposeTestCheckFunc(
186191
resource.TestCheckResourceAttr("scaleway_key_manager_key.rsa_4096", "name", "tf-test-kms-key-rsa4096"),
187192
resource.TestCheckResourceAttr("scaleway_key_manager_key.rsa_4096", "usage", "asymmetric_encryption"),
188-
resource.TestCheckResourceAttr("scaleway_key_manager_key.rsa_4096", "usage_asymmetric_encryption", "rsa_oaep_4096_sha256"),
193+
resource.TestCheckResourceAttr("scaleway_key_manager_key.rsa_4096", "algorithm", "rsa_oaep_4096_sha256"),
189194
resource.TestCheckResourceAttr("scaleway_key_manager_key.rsa_4096", "description", "Test key with RSA-4096 algorithm"),
190195
),
191196
},
@@ -205,49 +210,22 @@ func TestAccKeyManagerKey_DefaultAlgorithm(t *testing.T) {
205210
{
206211
Config: `
207212
resource "scaleway_key_manager_key" "default_alg" {
208-
name = "tf-test-kms-key-default-alg"
209-
region = "fr-par"
210-
usage_asymmetric_encryption = "rsa_oaep_3072_sha256"
211-
description = "Test key with RSA-3072 algorithm"
212-
unprotected = true
213+
name = "tf-test-kms-key-default-alg"
214+
region = "fr-par"
215+
usage = "asymmetric_encryption"
216+
algorithm = "rsa_oaep_3072_sha256"
217+
description = "Test key with RSA-3072 algorithm"
218+
unprotected = true
213219
}
214220
`,
215221
Check: resource.ComposeTestCheckFunc(
216222
resource.TestCheckResourceAttr("scaleway_key_manager_key.default_alg", "name", "tf-test-kms-key-default-alg"),
217223
resource.TestCheckResourceAttr("scaleway_key_manager_key.default_alg", "usage", "asymmetric_encryption"),
218-
resource.TestCheckResourceAttr("scaleway_key_manager_key.default_alg", "usage_asymmetric_encryption", "rsa_oaep_3072_sha256"),
224+
resource.TestCheckResourceAttr("scaleway_key_manager_key.default_alg", "algorithm", "rsa_oaep_3072_sha256"),
219225
resource.TestCheckResourceAttr("scaleway_key_manager_key.default_alg", "description", "Test key with RSA-3072 algorithm"),
220226
),
221227
},
222228
},
223229
})
224230
}
225231

226-
func TestAccKeyManagerKey_LegacyUsage(t *testing.T) {
227-
tt := acctest.NewTestTools(t)
228-
defer tt.Cleanup()
229-
230-
resource.ParallelTest(t, resource.TestCase{
231-
PreCheck: func() { acctest.PreCheck(t) },
232-
ProtoV6ProviderFactories: tt.ProviderFactories,
233-
CheckDestroy: IsKeyManagerKeyDestroyed(tt),
234-
Steps: []resource.TestStep{
235-
{
236-
Config: `
237-
resource "scaleway_key_manager_key" "legacy" {
238-
name = "tf-test-kms-key-legacy"
239-
region = "fr-par"
240-
usage = "symmetric_encryption"
241-
description = "Test key with deprecated usage field"
242-
unprotected = true
243-
}
244-
`,
245-
Check: resource.ComposeTestCheckFunc(
246-
resource.TestCheckResourceAttr("scaleway_key_manager_key.legacy", "name", "tf-test-kms-key-legacy"),
247-
resource.TestCheckResourceAttr("scaleway_key_manager_key.legacy", "usage", "symmetric_encryption"),
248-
resource.TestCheckResourceAttr("scaleway_key_manager_key.legacy", "description", "Test key with deprecated usage field"),
249-
),
250-
},
251-
},
252-
})
253-
}

0 commit comments

Comments
 (0)