Skip to content

Commit d6965c4

Browse files
authored
feat(secret): add ephemeral policy configuration (#2691)
* feat(secret): add ephemeral policy configuration * add resource doc * update doc * set ttl to optional and test its removal * remove extra code
1 parent 13becfc commit d6965c4

File tree

5 files changed

+972
-0
lines changed

5 files changed

+972
-0
lines changed

docs/resources/secret.md

+17
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,19 @@ resource "scaleway_secret" "main" {
2020
}
2121
```
2222

23+
### Ephemeral Policy
24+
25+
```terraform
26+
resource "scaleway_secret" "ephemeral" {
27+
name = "foo"
28+
ephemeral_policy {
29+
ttl = "24h"
30+
expires_once_accessed = true
31+
action = "disable"
32+
}
33+
}
34+
```
35+
2336
## Argument Reference
2437

2538
The following arguments are supported:
@@ -29,6 +42,10 @@ The following arguments are supported:
2942
- `protected` - (Optional) True if secret protection is enabled on the secret. A protected secret cannot be deleted, terraform will fail to destroy unless this is set to false.
3043
- `description` - (Optional) Description of the secret (e.g. `my-new-description`).
3144
- `tags` - (Optional) Tags of the secret (e.g. `["tag", "secret"]`).
45+
- `ephemeral_policy` - (Optional) Ephemeral policy of the secret. Policy that defines whether/when a secret's versions expire. By default, the policy is applied to all the secret's versions.
46+
- `ttl` - (Optional) Time frame, from one second and up to one year, during which the secret's versions are valid. Has to be specified in [Go Duration format](https://pkg.go.dev/time#ParseDuration) (ex: "30m", "24h").
47+
- `expires_once_accessed` - (Optional) True if the secret version expires after a single user access.
48+
- `action` - (Required) Action to perform when the version of a secret expires. Available values can be found in [SDK constants](https://pkg.go.dev/github.com/scaleway/scaleway-sdk-go@master/api/secret/v1beta1#pkg-constants).
3249
- `region` - (Defaults to [provider](../index.md#region) `region`) The [region](../guides/regions_and_zones.md#regions)
3350
in which the resource exists.
3451
- `project_id` - (Optional) The project ID containing is the secret.

internal/services/secret/helpers.go

+41
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import (
1212
"github.com/scaleway/terraform-provider-scaleway/v2/internal/locality"
1313
"github.com/scaleway/terraform-provider-scaleway/v2/internal/locality/regional"
1414
"github.com/scaleway/terraform-provider-scaleway/v2/internal/meta"
15+
"github.com/scaleway/terraform-provider-scaleway/v2/internal/types"
1516
)
1617

1718
const (
@@ -126,3 +127,43 @@ func updateSecretProtection(api *secret.API, region scw.Region, secretID string,
126127

127128
return nil
128129
}
130+
131+
func expandEphemeralPolicy(rawSchemaPolicy any) (*secret.EphemeralPolicy, error) {
132+
rawList := rawSchemaPolicy.([]interface{})
133+
if len(rawList) != 1 {
134+
return nil, fmt.Errorf("expected 1 policy, found %d", len(rawList))
135+
}
136+
rawPolicy := rawList[0].(map[string]interface{})
137+
138+
ttl, err := types.ExpandDuration(rawPolicy["ttl"])
139+
if err != nil {
140+
return nil, fmt.Errorf("error parsing ttl: %s", err)
141+
}
142+
143+
policy := &secret.EphemeralPolicy{
144+
ExpiresOnceAccessed: types.ExpandBoolPtr(rawPolicy["expires_once_accessed"]),
145+
Action: secret.EphemeralPolicyAction(rawPolicy["action"].(string)),
146+
}
147+
148+
if ttl != nil {
149+
policy.TimeToLive = scw.NewDurationFromTimeDuration(*ttl)
150+
}
151+
152+
return policy, nil
153+
}
154+
155+
func flattenEphemeralPolicy(policy *secret.EphemeralPolicy) []map[string]interface{} {
156+
if policy == nil {
157+
return nil
158+
}
159+
policyElem := map[string]interface{}{}
160+
if policy.TimeToLive != nil {
161+
policyElem["ttl"] = types.FlattenDuration(policy.TimeToLive.ToTimeDuration())
162+
}
163+
if policy.ExpiresOnceAccessed != nil {
164+
policyElem["expires_once_accessed"] = types.FlattenBoolPtr(policy.ExpiresOnceAccessed)
165+
}
166+
policyElem["action"] = policy.Action
167+
168+
return []map[string]interface{}{policyElem}
169+
}

internal/services/secret/secret.go

+45
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,12 @@ import (
88
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
99
secret "github.com/scaleway/scaleway-sdk-go/api/secret/v1beta1"
1010
"github.com/scaleway/scaleway-sdk-go/scw"
11+
"github.com/scaleway/terraform-provider-scaleway/v2/internal/dsf"
1112
"github.com/scaleway/terraform-provider-scaleway/v2/internal/httperrors"
1213
"github.com/scaleway/terraform-provider-scaleway/v2/internal/locality/regional"
1314
"github.com/scaleway/terraform-provider-scaleway/v2/internal/services/account"
1415
"github.com/scaleway/terraform-provider-scaleway/v2/internal/types"
16+
"github.com/scaleway/terraform-provider-scaleway/v2/internal/verify"
1517
)
1618

1719
func ResourceSecret() *schema.Resource {
@@ -80,6 +82,32 @@ func ResourceSecret() *schema.Resource {
8082
Optional: true,
8183
Description: "True if secret protection is enabled on a given secret. A protected secret cannot be deleted.",
8284
},
85+
"ephemeral_policy": {
86+
Type: schema.TypeList,
87+
Optional: true,
88+
Elem: &schema.Resource{
89+
Schema: map[string]*schema.Schema{
90+
"ttl": {
91+
Optional: true,
92+
Type: schema.TypeString,
93+
DiffSuppressFunc: dsf.Duration,
94+
ValidateFunc: verify.IsDuration(),
95+
Description: "Time frame, from one second and up to one year, during which the secret's versions are valid. Has to be specified in Go Duration format",
96+
},
97+
"expires_once_accessed": {
98+
Type: schema.TypeBool,
99+
Optional: true,
100+
Description: "True if the secret version expires after a single user access.",
101+
},
102+
"action": {
103+
Type: schema.TypeString,
104+
Required: true,
105+
ValidateDiagFunc: verify.ValidateEnum[secret.EphemeralPolicyAction](),
106+
Description: "Action to perform when the version of a secret expires.",
107+
},
108+
},
109+
},
110+
},
83111
"region": regional.Schema(),
84112
"project_id": account.ProjectIDSchema(),
85113
},
@@ -114,6 +142,14 @@ func ResourceSecretCreate(ctx context.Context, d *schema.ResourceData, m interfa
114142
secretCreateRequest.Path = types.ExpandStringPtr(rawPath)
115143
}
116144

145+
rawEphemeralPolicy, policyExists := d.GetOk("ephemeral_policy")
146+
if policyExists {
147+
secretCreateRequest.EphemeralPolicy, err = expandEphemeralPolicy(rawEphemeralPolicy)
148+
if err != nil {
149+
return diag.FromErr(err)
150+
}
151+
}
152+
117153
secretResponse, err := api.CreateSecret(secretCreateRequest, scw.WithContext(ctx))
118154
if err != nil {
119155
return diag.FromErr(err)
@@ -156,6 +192,7 @@ func ResourceSecretRead(ctx context.Context, d *schema.ResourceData, m interface
156192
_ = d.Set("project_id", secretResponse.ProjectID)
157193
_ = d.Set("path", secretResponse.Path)
158194
_ = d.Set("protected", secretResponse.Protected)
195+
_ = d.Set("ephemeral_policy", flattenEphemeralPolicy(secretResponse.EphemeralPolicy))
159196

160197
return nil
161198
}
@@ -193,6 +230,14 @@ func ResourceSecretUpdate(ctx context.Context, d *schema.ResourceData, m interfa
193230
hasChanged = true
194231
}
195232

233+
if d.HasChange("ephemeral_policy") {
234+
updateRequest.EphemeralPolicy, err = expandEphemeralPolicy(d.Get("ephemeral_policy"))
235+
if err != nil {
236+
return diag.FromErr(err)
237+
}
238+
hasChanged = true
239+
}
240+
196241
if hasChanged {
197242
_, err := api.UpdateSecret(updateRequest, scw.WithContext(ctx))
198243
if err != nil {

internal/services/secret/secret_test.go

+78
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ func TestAccSecret_Basic(t *testing.T) {
4242
resource.TestCheckResourceAttr("scaleway_secret.main", "tags.1", "provider"),
4343
resource.TestCheckResourceAttr("scaleway_secret.main", "tags.2", "terraform"),
4444
resource.TestCheckResourceAttr("scaleway_secret.main", "tags.#", "3"),
45+
resource.TestCheckResourceAttr("scaleway_secret.main", "ephemeral_policy.#", "0"),
4546
resource.TestCheckResourceAttrSet("scaleway_secret.main", "updated_at"),
4647
resource.TestCheckResourceAttrSet("scaleway_secret.main", "created_at"),
4748
acctest.CheckResourceAttrUUID("scaleway_secret.main", "id"),
@@ -211,6 +212,83 @@ func TestAccSecret_Protected(t *testing.T) {
211212
})
212213
}
213214

215+
func TestAccSecret_EphemeralPolicy(t *testing.T) {
216+
tt := acctest.NewTestTools(t)
217+
defer tt.Cleanup()
218+
219+
resource.ParallelTest(t, resource.TestCase{
220+
PreCheck: func() { acctest.PreCheck(t) },
221+
ProviderFactories: tt.ProviderFactories,
222+
CheckDestroy: testAccCheckSecretDestroy(tt),
223+
Steps: []resource.TestStep{
224+
{
225+
Config: `
226+
resource "scaleway_secret" "main" {
227+
name = "test-secret-policy-secret"
228+
ephemeral_policy {
229+
ttl = "30m"
230+
action = "disable"
231+
}
232+
}
233+
`,
234+
Check: resource.ComposeTestCheckFunc(
235+
testAccCheckSecretExists(tt, "scaleway_secret.main"),
236+
resource.TestCheckResourceAttr("scaleway_secret.main", "name", "test-secret-policy-secret"),
237+
resource.TestCheckResourceAttr("scaleway_secret.main", "path", "/"),
238+
resource.TestCheckResourceAttr("scaleway_secret.main", "ephemeral_policy.#", "1"),
239+
resource.TestCheckResourceAttr("scaleway_secret.main", "ephemeral_policy.0.ttl", "30m0s"),
240+
resource.TestCheckResourceAttr("scaleway_secret.main", "ephemeral_policy.0.action", "disable"),
241+
resource.TestCheckResourceAttr("scaleway_secret.main", "ephemeral_policy.0.expires_once_accessed", "false"),
242+
acctest.CheckResourceAttrUUID("scaleway_secret.main", "id"),
243+
),
244+
},
245+
{
246+
Config: `
247+
resource "scaleway_secret" "main" {
248+
name = "test-secret-policy-secret"
249+
ephemeral_policy {
250+
ttl = "5h"
251+
action = "delete"
252+
expires_once_accessed = true
253+
}
254+
}
255+
`,
256+
Check: resource.ComposeTestCheckFunc(
257+
testAccCheckSecretExists(tt, "scaleway_secret.main"),
258+
resource.TestCheckResourceAttr("scaleway_secret.main", "name", "test-secret-policy-secret"),
259+
resource.TestCheckResourceAttr("scaleway_secret.main", "path", "/"),
260+
resource.TestCheckResourceAttr("scaleway_secret.main", "ephemeral_policy.#", "1"),
261+
resource.TestCheckResourceAttr("scaleway_secret.main", "ephemeral_policy.0.ttl", "5h0m0s"),
262+
resource.TestCheckResourceAttr("scaleway_secret.main", "ephemeral_policy.0.action", "delete"),
263+
resource.TestCheckResourceAttr("scaleway_secret.main", "ephemeral_policy.0.expires_once_accessed", "true"),
264+
acctest.CheckResourceAttrUUID("scaleway_secret.main", "id"),
265+
),
266+
},
267+
{
268+
Config: `
269+
resource "scaleway_secret" "main" {
270+
name = "test-secret-policy-secret"
271+
ephemeral_policy {
272+
action = "delete"
273+
expires_once_accessed = true
274+
}
275+
}
276+
`,
277+
Check: resource.ComposeTestCheckFunc(
278+
testAccCheckSecretExists(tt, "scaleway_secret.main"),
279+
resource.TestCheckResourceAttr("scaleway_secret.main", "name", "test-secret-policy-secret"),
280+
resource.TestCheckResourceAttr("scaleway_secret.main", "path", "/"),
281+
resource.TestCheckResourceAttr("scaleway_secret.main", "ephemeral_policy.#", "1"),
282+
resource.TestCheckResourceAttr("scaleway_secret.main", "ephemeral_policy.0.ttl", ""),
283+
resource.TestCheckResourceAttr("scaleway_secret.main", "ephemeral_policy.0.action", "delete"),
284+
resource.TestCheckResourceAttr("scaleway_secret.main", "ephemeral_policy.0.expires_once_accessed", "true"),
285+
acctest.CheckResourceAttrUUID("scaleway_secret.main", "id"),
286+
),
287+
},
288+
},
289+
})
290+
}
291+
214292
func testAccCheckSecretExists(tt *acctest.TestTools, n string) resource.TestCheckFunc {
215293
return func(state *terraform.State) error {
216294
rs, ok := state.RootModule().Resources[n]

0 commit comments

Comments
 (0)