Skip to content

Commit 68f288e

Browse files
committed
feat: allow explicit algorithm selection in Key Manager keys
1 parent 92ec184 commit 68f288e

10 files changed

+733
-328
lines changed

internal/services/keymanager/helpers.go

Lines changed: 24 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -55,28 +55,39 @@ func NewKeyManagerAPIWithRegionAndID(m any, id string) (*key_manager.API, scw.Re
5555
return client, region, keyID, nil
5656
}
5757

58-
func ExpandKeyUsage(usage string, algorithm string) *key_manager.KeyUsage {
58+
func ExpandKeyUsageFromFields(d *schema.ResourceData) *key_manager.KeyUsage {
59+
if v, ok := d.GetOk("usage_symmetric_encryption"); ok {
60+
alg := key_manager.KeyAlgorithmSymmetricEncryption(v.(string))
61+
return &key_manager.KeyUsage{SymmetricEncryption: &alg}
62+
}
63+
64+
if v, ok := d.GetOk("usage_asymmetric_encryption"); ok {
65+
alg := key_manager.KeyAlgorithmAsymmetricEncryption(v.(string))
66+
return &key_manager.KeyUsage{AsymmetricEncryption: &alg}
67+
}
68+
69+
if v, ok := d.GetOk("usage_asymmetric_signing"); ok {
70+
alg := key_manager.KeyAlgorithmAsymmetricSigning(v.(string))
71+
return &key_manager.KeyUsage{AsymmetricSigning: &alg}
72+
}
73+
74+
if v, ok := d.GetOk("usage"); ok {
75+
return ExpandKeyUsageLegacy(v.(string))
76+
}
77+
78+
return nil
79+
}
80+
81+
func ExpandKeyUsageLegacy(usage string) *key_manager.KeyUsage {
5982
switch usage {
6083
case "symmetric_encryption":
6184
alg := key_manager.KeyAlgorithmSymmetricEncryptionAes256Gcm
62-
if algorithm != "" {
63-
alg = key_manager.KeyAlgorithmSymmetricEncryption(algorithm)
64-
}
65-
6685
return &key_manager.KeyUsage{SymmetricEncryption: &alg}
6786
case "asymmetric_encryption":
6887
alg := key_manager.KeyAlgorithmAsymmetricEncryptionRsaOaep3072Sha256
69-
if algorithm != "" {
70-
alg = key_manager.KeyAlgorithmAsymmetricEncryption(algorithm)
71-
}
72-
7388
return &key_manager.KeyUsage{AsymmetricEncryption: &alg}
7489
case "asymmetric_signing":
7590
alg := key_manager.KeyAlgorithmAsymmetricSigningEcP256Sha256
76-
if algorithm != "" {
77-
alg = key_manager.KeyAlgorithmAsymmetricSigning(algorithm)
78-
}
79-
8091
return &key_manager.KeyUsage{AsymmetricSigning: &alg}
8192
default:
8293
return nil
@@ -128,7 +139,6 @@ func ExpandKeyRotationPolicy(v any) (*key_manager.KeyRotationPolicy, error) {
128139
RotationPeriod: scw.NewDurationFromTimeDuration(period),
129140
}
130141

131-
// Handle next_rotation_at if provided
132142
if nextRotationStr, ok := m["next_rotation_at"].(string); ok && nextRotationStr != "" {
133143
nextRotation, err := time.Parse(time.RFC3339, nextRotationStr)
134144
if err != nil {

internal/services/keymanager/key_resource.go

Lines changed: 75 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -29,17 +29,65 @@ func ResourceKeyManagerKey() *schema.Resource {
2929
"region": regional.Schema(),
3030
"usage": {
3131
Type: schema.TypeString,
32-
Required: true,
32+
Optional: true,
33+
Computed: true,
3334
ValidateFunc: validation.StringInSlice([]string{
3435
"symmetric_encryption", "asymmetric_encryption", "asymmetric_signing",
3536
}, false),
36-
Description: "Key usage. Keys with a usage set to 'symmetric_encryption' can encrypt and decrypt data using the AES-256-GCM key algorithm. Possible values: symmetric_encryption, asymmetric_encryption, asymmetric_signing.",
37+
Deprecated: "Use usage_symmetric_encryption, usage_asymmetric_encryption, or usage_asymmetric_signing instead",
38+
Description: "DEPRECATED: Use usage_symmetric_encryption, usage_asymmetric_encryption, or usage_asymmetric_signing instead. Key usage. Possible values: symmetric_encryption, asymmetric_encryption, asymmetric_signing.",
39+
ExactlyOneOf: []string{
40+
"usage",
41+
"usage_symmetric_encryption",
42+
"usage_asymmetric_encryption",
43+
"usage_asymmetric_signing",
44+
},
3745
},
38-
"algorithm": {
39-
Type: schema.TypeString,
40-
Optional: true,
41-
Computed: true,
42-
Description: "Algorithm for the key. If not specified, a default algorithm is chosen based on usage. See Key Manager documentation for supported algorithms.",
46+
"usage_symmetric_encryption": {
47+
Type: schema.TypeString,
48+
Optional: true,
49+
ValidateFunc: validation.StringInSlice([]string{
50+
"aes_256_gcm",
51+
}, false),
52+
Description: "Algorithm for symmetric encryption. Possible values: aes_256_gcm",
53+
ExactlyOneOf: []string{
54+
"usage",
55+
"usage_symmetric_encryption",
56+
"usage_asymmetric_encryption",
57+
"usage_asymmetric_signing",
58+
},
59+
},
60+
"usage_asymmetric_encryption": {
61+
Type: schema.TypeString,
62+
Optional: true,
63+
ValidateFunc: validation.StringInSlice([]string{
64+
"rsa_oaep_2048_sha256",
65+
"rsa_oaep_3072_sha256",
66+
"rsa_oaep_4096_sha256",
67+
}, false),
68+
Description: "Algorithm for asymmetric encryption. Possible values: rsa_oaep_2048_sha256, rsa_oaep_3072_sha256, rsa_oaep_4096_sha256",
69+
ExactlyOneOf: []string{
70+
"usage",
71+
"usage_symmetric_encryption",
72+
"usage_asymmetric_encryption",
73+
"usage_asymmetric_signing",
74+
},
75+
},
76+
"usage_asymmetric_signing": {
77+
Type: schema.TypeString,
78+
Optional: true,
79+
ValidateFunc: validation.StringInSlice([]string{
80+
"ec_p256_sha256",
81+
"rsa_pss_2048_sha256",
82+
"rsa_pkcs1_2048_sha256",
83+
}, false),
84+
Description: "Algorithm for asymmetric signing. Possible values: ec_p256_sha256, rsa_pss_2048_sha256, rsa_pkcs1_2048_sha256",
85+
ExactlyOneOf: []string{
86+
"usage",
87+
"usage_symmetric_encryption",
88+
"usage_asymmetric_encryption",
89+
"usage_asymmetric_signing",
90+
},
4391
},
4492
"description": {
4593
Type: schema.TypeString,
@@ -122,8 +170,7 @@ func resourceKeyManagerKeyCreate(ctx context.Context, d *schema.ResourceData, m
122170
createReq.Origin = key_manager.KeyOrigin(v.(string))
123171
}
124172

125-
algorithm := d.Get("algorithm").(string)
126-
createReq.Usage = ExpandKeyUsage(d.Get("usage").(string), algorithm)
173+
createReq.Usage = ExpandKeyUsageFromFields(d)
127174

128175
key, err := api.CreateKey(createReq)
129176
if err != nil {
@@ -152,8 +199,25 @@ func resourceKeyManagerKeyRead(ctx context.Context, d *schema.ResourceData, m an
152199
_ = d.Set("name", key.Name)
153200
_ = d.Set("project_id", key.ProjectID)
154201
_ = d.Set("region", key.Region.String())
155-
_ = d.Set("usage", UsageToString(key.Usage))
156-
_ = d.Set("algorithm", AlgorithmFromKeyUsage(key.Usage))
202+
203+
usageType := UsageToString(key.Usage)
204+
algorithm := AlgorithmFromKeyUsage(key.Usage)
205+
206+
_ = d.Set("usage", usageType)
207+
208+
_, usesLegacy := d.GetOk("usage")
209+
210+
if !usesLegacy {
211+
switch usageType {
212+
case "symmetric_encryption":
213+
_ = d.Set("usage_symmetric_encryption", algorithm)
214+
case "asymmetric_encryption":
215+
_ = d.Set("usage_asymmetric_encryption", algorithm)
216+
case "asymmetric_signing":
217+
_ = d.Set("usage_asymmetric_signing", algorithm)
218+
}
219+
}
220+
157221
_ = d.Set("description", key.Description)
158222
_ = d.Set("tags", key.Tags)
159223
_ = d.Set("rotation_count", int(key.RotationCount))

internal/services/keymanager/key_resource_test.go

Lines changed: 69 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -24,18 +24,19 @@ 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"
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 = "aes_256_gcm"
30+
description = "Test key"
31+
tags = ["tf", "test"]
32+
unprotected = true
3333
}
3434
`,
3535
Check: resource.ComposeTestCheckFunc(
3636
resource.TestCheckResourceAttr("scaleway_key_manager_key.main", "name", "tf-test-kms-key-unprotected-a"),
3737
resource.TestCheckResourceAttr("scaleway_key_manager_key.main", "region", "fr-par"),
3838
resource.TestCheckResourceAttr("scaleway_key_manager_key.main", "usage", "symmetric_encryption"),
39+
resource.TestCheckResourceAttr("scaleway_key_manager_key.main", "usage_symmetric_encryption", "aes_256_gcm"),
3940
resource.TestCheckResourceAttr("scaleway_key_manager_key.main", "description", "Test key"),
4041
resource.TestCheckResourceAttr("scaleway_key_manager_key.main", "tags.0", "tf"),
4142
resource.TestCheckResourceAttr("scaleway_key_manager_key.main", "tags.1", "test"),
@@ -57,34 +58,36 @@ func TestAccKeyManagerKey_Update(t *testing.T) {
5758
{
5859
Config: `
5960
resource "scaleway_key_manager_key" "main" {
60-
name = "tf-test-kms-key-update"
61-
region = "fr-par"
62-
usage = "symmetric_encryption"
63-
description = "Test key"
64-
tags = ["tf", "test"]
65-
unprotected = true
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
6667
}
6768
`,
6869
Check: resource.ComposeTestCheckFunc(
6970
resource.TestCheckResourceAttr("scaleway_key_manager_key.main", "name", "tf-test-kms-key-update"),
7071
resource.TestCheckResourceAttr("scaleway_key_manager_key.main", "description", "Test key"),
72+
resource.TestCheckResourceAttr("scaleway_key_manager_key.main", "usage_symmetric_encryption", "aes_256_gcm"),
7173
),
7274
},
7375
{
7476
Config: `
7577
resource "scaleway_key_manager_key" "main" {
76-
name = "tf-test-kms-key-updated"
77-
region = "fr-par"
78-
usage = "symmetric_encryption"
79-
description = "Test key updated"
80-
tags = ["tf", "updated"]
81-
unprotected = true
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
8284
}
8385
`,
8486
Check: resource.ComposeTestCheckFunc(
8587
resource.TestCheckResourceAttr("scaleway_key_manager_key.main", "name", "tf-test-kms-key-updated"),
8688
resource.TestCheckResourceAttr("scaleway_key_manager_key.main", "description", "Test key updated"),
8789
resource.TestCheckResourceAttr("scaleway_key_manager_key.main", "tags.1", "updated"),
90+
resource.TestCheckResourceAttr("scaleway_key_manager_key.main", "usage_symmetric_encryption", "aes_256_gcm"),
8891
),
8992
},
9093
},
@@ -136,11 +139,11 @@ func TestAccKeyManagerKey_WithRotationPolicy(t *testing.T) {
136139
{
137140
Config: `
138141
resource "scaleway_key_manager_key" "main" {
139-
name = "tf-test-kms-key-rotation"
140-
region = "fr-par"
141-
usage = "symmetric_encryption"
142-
description = "Test key with rotation policy"
143-
unprotected = true
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
144147
145148
rotation_policy {
146149
rotation_period = "720h"
@@ -151,6 +154,7 @@ func TestAccKeyManagerKey_WithRotationPolicy(t *testing.T) {
151154
Check: resource.ComposeTestCheckFunc(
152155
resource.TestCheckResourceAttr("scaleway_key_manager_key.main", "name", "tf-test-kms-key-rotation"),
153156
resource.TestCheckResourceAttr("scaleway_key_manager_key.main", "usage", "symmetric_encryption"),
157+
resource.TestCheckResourceAttr("scaleway_key_manager_key.main", "usage_symmetric_encryption", "aes_256_gcm"),
154158
resource.TestCheckResourceAttr("scaleway_key_manager_key.main", "description", "Test key with rotation policy"),
155159
resource.TestCheckResourceAttr("scaleway_key_manager_key.main", "rotation_policy.0.rotation_period", "720h0m0s"),
156160
),
@@ -171,18 +175,17 @@ func TestAccKeyManagerKey_WithCustomAlgorithm(t *testing.T) {
171175
{
172176
Config: `
173177
resource "scaleway_key_manager_key" "rsa_4096" {
174-
name = "tf-test-kms-key-rsa4096"
175-
region = "fr-par"
176-
usage = "asymmetric_encryption"
177-
algorithm = "rsa_oaep_4096_sha256"
178-
description = "Test key with RSA-4096 algorithm"
179-
unprotected = true
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
180183
}
181184
`,
182185
Check: resource.ComposeTestCheckFunc(
183186
resource.TestCheckResourceAttr("scaleway_key_manager_key.rsa_4096", "name", "tf-test-kms-key-rsa4096"),
184187
resource.TestCheckResourceAttr("scaleway_key_manager_key.rsa_4096", "usage", "asymmetric_encryption"),
185-
resource.TestCheckResourceAttr("scaleway_key_manager_key.rsa_4096", "algorithm", "rsa_oaep_4096_sha256"),
188+
resource.TestCheckResourceAttr("scaleway_key_manager_key.rsa_4096", "usage_asymmetric_encryption", "rsa_oaep_4096_sha256"),
186189
resource.TestCheckResourceAttr("scaleway_key_manager_key.rsa_4096", "description", "Test key with RSA-4096 algorithm"),
187190
),
188191
},
@@ -202,19 +205,47 @@ func TestAccKeyManagerKey_DefaultAlgorithm(t *testing.T) {
202205
{
203206
Config: `
204207
resource "scaleway_key_manager_key" "default_alg" {
205-
name = "tf-test-kms-key-default-alg"
206-
region = "fr-par"
207-
usage = "asymmetric_encryption"
208-
description = "Test key with default algorithm"
209-
unprotected = true
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
210213
}
211214
`,
212215
Check: resource.ComposeTestCheckFunc(
213216
resource.TestCheckResourceAttr("scaleway_key_manager_key.default_alg", "name", "tf-test-kms-key-default-alg"),
214217
resource.TestCheckResourceAttr("scaleway_key_manager_key.default_alg", "usage", "asymmetric_encryption"),
215-
// Verify default algorithm is set (RSA-OAEP-3072-SHA256)
216-
resource.TestCheckResourceAttr("scaleway_key_manager_key.default_alg", "algorithm", "rsa_oaep_3072_sha256"),
217-
resource.TestCheckResourceAttr("scaleway_key_manager_key.default_alg", "description", "Test key with default algorithm"),
218+
resource.TestCheckResourceAttr("scaleway_key_manager_key.default_alg", "usage_asymmetric_encryption", "rsa_oaep_3072_sha256"),
219+
resource.TestCheckResourceAttr("scaleway_key_manager_key.default_alg", "description", "Test key with RSA-3072 algorithm"),
220+
),
221+
},
222+
},
223+
})
224+
}
225+
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"),
218249
),
219250
},
220251
},

0 commit comments

Comments
 (0)