Skip to content

Commit

Permalink
feat(policy): support storage policies within terraform
Browse files Browse the repository at this point in the history
  • Loading branch information
kheina committed Feb 12, 2024
1 parent 93e41ae commit de1f0ce
Show file tree
Hide file tree
Showing 8 changed files with 560 additions and 6 deletions.
33 changes: 33 additions & 0 deletions docs/resources/policy_storage.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
---
# generated by https://github.com/hashicorp/terraform-plugin-docs
page_title: "boundary_policy_storage Resource - terraform-provider-boundary"
subcategory: ""
description: |-
The storage policy resource allows you to configure a Boundary storage policy. Storage policies allow an admin to configure how long session recordings must be stored and when to delete them. Storage policies must be applied to the global scope or an org scope in order to take effect.
---

# boundary_policy_storage (Resource)

The storage policy resource allows you to configure a Boundary storage policy. Storage policies allow an admin to configure how long session recordings must be stored and when to delete them. Storage policies must be applied to the global scope or an org scope in order to take effect.



<!-- schema generated by tfplugindocs -->
## Schema

### Required

- `scope_id` (String) The scope for this policy.

### Optional

- `delete_after_days` (Number) The number of days after which a session recording will be automatically deleted. Defaults to 0: never automatically delete. However, delete_after_days and retain_for_days cannot both be 0.
- `delete_after_overridable` (Boolean) Whether or not the associated delete_after_days value can be overridden by org scopes. Note: if the associated delete_after_days value is 0, overridable is ignored
- `description` (String) The policy description.
- `name` (String) The policy name. Defaults to the resource name.
- `retain_for_days` (Number) The number of days a session recording is required to be stored. Defaults to 0: allow deletions at any time. However, retain_for_days and delete_after_days cannot both be 0.
- `retain_for_overridable` (Boolean) Whether or not the associated retain_for_days value can be overridden by org scopes. Note: if the associated retain_for_days value is 0, overridable is ignored.

### Read-Only

- `id` (String) The ID of the policy.
25 changes: 25 additions & 0 deletions docs/resources/scope_policy_attachment.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
---
# generated by https://github.com/hashicorp/terraform-plugin-docs
page_title: "boundary_scope_policy_attachment Resource - terraform-provider-boundary"
subcategory: ""
description: |-
---

# boundary_scope_policy_attachment (Resource)





<!-- schema generated by tfplugindocs -->
## Schema

### Required

- `policy_id` (String)
- `scope_id` (String)

### Read-Only

- `id` (String) The ID of this resource.
27 changes: 27 additions & 0 deletions examples/resources/boundary_policy/resource.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
resource "boundary_scope" "global" {
global_scope = true
scope_id = "global"
}

resource "boundary_scope" "org" {
name = "organization_one"
description = "My first scope!"
scope_id = boundary_scope.global.id
auto_create_admin_role = true
auto_create_default_role = true
}

resource "boundary_policy_storage" "foo" {
name = "foo"
description = "Foo policy"
scope_id = boundary_scope.org.id
retain_for_days = 10
retain_for_overridable = false
delete_after_days = 10
delete_after_overridable = true
}

resource "boundary_scope_policy_attachment" "foo_attachment" {
scope_id = boundary_scope.org.id
policy_id = boundary_policy_storage.foo.id
}
4 changes: 2 additions & 2 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ go 1.21.5
require (
github.com/YakDriver/regexache v0.23.0
github.com/hashicorp/boundary v0.15.0
github.com/hashicorp/boundary/api v0.0.45
github.com/hashicorp/boundary/sdk v0.0.42
github.com/hashicorp/boundary/api v0.0.46-0.20240209231500-1b1c8b3bf9a6
github.com/hashicorp/boundary/sdk v0.0.43-0.20240209231500-1b1c8b3bf9a6
github.com/hashicorp/cap v0.4.1
github.com/hashicorp/cap/ldap v0.0.0-20231012003312-273118a6e3b8
github.com/hashicorp/go-cty v1.4.1-0.20200723130312-85980079f637
Expand Down
8 changes: 4 additions & 4 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -233,10 +233,10 @@ github.com/grpc-ecosystem/grpc-gateway/v2 v2.18.0 h1:RtRsiaGvWxcwd8y3BiRZxsylPT8
github.com/grpc-ecosystem/grpc-gateway/v2 v2.18.0/go.mod h1:TzP6duP4Py2pHLVPPQp42aoYI92+PCrVotyR5e8Vqlk=
github.com/hashicorp/boundary v0.15.0 h1:np75Igvt9ICQN3ZXJsXw91P5QFsvGDsNYoBNMFGeRcw=
github.com/hashicorp/boundary v0.15.0/go.mod h1:CJxelVYlFu4fU85wtxi50swKrVg665/GiIpd1Cxez1I=
github.com/hashicorp/boundary/api v0.0.45 h1:N4ZIjsYf4cwVfcW5sv0WitWiFr8UyudjA/ozCRkCckw=
github.com/hashicorp/boundary/api v0.0.45/go.mod h1:UrsSgLDpFmG6Eq+OIdpC8BSwbHwklsxL/ZCWklr0g5A=
github.com/hashicorp/boundary/sdk v0.0.42 h1:iRDdnM6/6q9qLtBP7/cpq/FWQGf69TFSsBQFxgGK8Jw=
github.com/hashicorp/boundary/sdk v0.0.42/go.mod h1:QJhm4rky2YMtk9DS5Rj8QJhDKB/6zQbVrm3R0QgdYmQ=
github.com/hashicorp/boundary/api v0.0.46-0.20240209231500-1b1c8b3bf9a6 h1:6/GdZZDW8ChPduD9rFnr8VGZmTPGGvlMrtELWqmoz8I=
github.com/hashicorp/boundary/api v0.0.46-0.20240209231500-1b1c8b3bf9a6/go.mod h1:UrsSgLDpFmG6Eq+OIdpC8BSwbHwklsxL/ZCWklr0g5A=
github.com/hashicorp/boundary/sdk v0.0.43-0.20240209231500-1b1c8b3bf9a6 h1:DIfWtA4OOcCJlIL3LJUERF7M90ephrEgn16KWWDX2KU=
github.com/hashicorp/boundary/sdk v0.0.43-0.20240209231500-1b1c8b3bf9a6/go.mod h1:QJhm4rky2YMtk9DS5Rj8QJhDKB/6zQbVrm3R0QgdYmQ=
github.com/hashicorp/cap v0.4.1 h1:LVYrTLbPV8W6DPwIm/zC/fbc4UTpCQ7nJhCAPshLuG4=
github.com/hashicorp/cap v0.4.1/go.mod h1:oOoohCNd2JAgfvLz2NpFJTZiZ6CqH9dW8dZ2js52lGA=
github.com/hashicorp/cap/ldap v0.0.0-20231012003312-273118a6e3b8 h1:IKSRg1IONHl9ouMwx/Un1OVwdnlqVGWXRjUEg6VQZuY=
Expand Down
2 changes: 2 additions & 0 deletions internal/provider/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,8 @@ func New() *schema.Provider {
"boundary_host_set": resourceHostSet(),
"boundary_host_set_static": resourceHostSetStatic(),
"boundary_host_set_plugin": resourceHostSetPlugin(),
"boundary_policy_storage": resourcePolicyStorage(),
"boundary_scope_policy_attachment": resourcePolicyAttachment(),
"boundary_role": resourceRole(),
"boundary_scope": resourceScope(),
"boundary_storage_bucket": resourceStorageBucket(),
Expand Down
164 changes: 164 additions & 0 deletions internal/provider/resource_policy_attachment.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,164 @@
// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: MPL-2.0

package provider

import (
"context"
"fmt"
"net/http"
"strings"

"github.com/hashicorp/boundary/api"
"github.com/hashicorp/boundary/api/scopes"
"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
)

const (
policyIdKey = "policy_id"
storagePolicyPrefix = "pst_"
)

func resourcePolicyAttachment() *schema.Resource {
return &schema.Resource{
CreateContext: resourcePolicyAttachmentCreate,
ReadWithoutTimeout: resourcePolicyAttachmentRead,
DeleteWithoutTimeout: resourcePolicyAttachmentDelete,

Importer: &schema.ResourceImporter{
StateContext: schema.ImportStatePassthroughContext,
},

Schema: map[string]*schema.Schema{
ScopeIdKey: {
Type: schema.TypeString,
Required: true,
ForceNew: true,
},
policyIdKey: {
Type: schema.TypeString,
ForceNew: true,
Required: true,
},
},
}
}

func resourcePolicyAttachmentCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
md := meta.(*metaData)
scp := scopes.NewClient(md.client)
opts := []scopes.Option{scopes.WithAutomaticVersioning(true)}

var scopeId string
if scopeIdVal, ok := d.GetOk(ScopeIdKey); ok {
scopeId = scopeIdVal.(string)
} else {
return diag.Errorf("no scope ID provided")
}

if err := d.Set(ScopeIdKey, scopeId); err != nil {
return diag.FromErr(err)
}

var policyId string
if policyIdVal, ok := d.GetOk(policyIdKey); ok {
policyId = policyIdVal.(string)
} else {
return diag.Errorf("no policy ID provided")
}

// other policy types may be added here and in other switches with this file
switch {
case strings.HasPrefix(policyId, storagePolicyPrefix):
if _, err := scp.AttachStoragePolicy(ctx, scopeId, 0, policyId, opts...); err != nil {
return diag.FromErr(err)
}
default:
return diag.Errorf("unknown policy type provided.")
}

if err := d.Set(policyIdKey, policyId); err != nil {
return diag.FromErr(err)
}

d.SetId(fmt.Sprintf("%s:%s", policyId, scopeId))

return nil
}

func resourcePolicyAttachmentRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
md := meta.(*metaData)
scp := scopes.NewClient(md.client)

var scopeId string
if scopeIdVal, ok := d.GetOk(ScopeIdKey); ok {
scopeId = scopeIdVal.(string)
} else {
return diag.Errorf("no scope ID provided")
}

var policyId string
if policyIdVal, ok := d.GetOk(policyIdKey); ok {
policyId = policyIdVal.(string)
} else {
return diag.Errorf("no policy ID provided")
}

s, err := scp.Read(ctx, scopeId)
if err != nil {
if apiErr := api.AsServerError(err); apiErr != nil && apiErr.Response().StatusCode() == http.StatusNotFound {
// the scope is gone, destroy this resource
d.SetId("")
return nil
}
return diag.Errorf("error reading scope: %v", err)
}
if s == nil {
return diag.Errorf("scope nil after read")
}

switch {
case strings.HasPrefix(policyId, storagePolicyPrefix):
if policyId != s.GetItem().StoragePolicyId {
// this policy is no longer attached to the scope, destroy this resource
d.SetId("")
return nil
}
default:
return diag.Errorf("unknown policy type provided.")
}

return nil
}

func resourcePolicyAttachmentDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
md := meta.(*metaData)
scp := scopes.NewClient(md.client)
opts := []scopes.Option{scopes.WithAutomaticVersioning(true)}

var scopeId string
if scopeIdVal, ok := d.GetOk(ScopeIdKey); ok {
scopeId = scopeIdVal.(string)
} else {
return diag.Errorf("no scope ID provided")
}

var policyId string
if policyIdVal, ok := d.GetOk(policyIdKey); ok {
policyId = policyIdVal.(string)
} else {
return diag.Errorf("no policy ID provided")
}

switch {
case strings.HasPrefix(policyId, storagePolicyPrefix):
if _, err := scp.DetachStoragePolicy(ctx, scopeId, 0, opts...); err != nil {
return diag.FromErr(err)
}
default:
return diag.Errorf("unknown policy type provided.")
}

return nil
}
Loading

0 comments on commit de1f0ce

Please sign in to comment.