Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions sysdig/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ const (
SchemaTargetKey = "target"
SchemaZonesKey = "zones"
SchemaZonesIDsKey = "zone_ids"
SchemaZoneIDKey = "zone_id"
SchemaAllZones = "all_zones"
SchemaScopeKey = "scope"
SchemaScopesKey = "scopes"
Expand Down
1 change: 1 addition & 0 deletions sysdig/internal/client/v2/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ type SecureCommon interface {
PostureVulnerabilityAcceptRiskInterface
ZoneInterface
ZoneV2Interface
ZonePolicyAssignmentInterface
}

type Requester interface {
Expand Down
9 changes: 9 additions & 0 deletions sysdig/internal/client/v2/model.go
Original file line number Diff line number Diff line change
Expand Up @@ -1123,6 +1123,15 @@ type PostureZoneResponse struct {
Data PostureZone `json:"data"`
}

type ZonePolicyAssignment struct {
ZoneID int `json:"zoneId"`
PolicyIDs []int `json:"policyIds"`
}

type ZonePolicyAssignmentRequest struct {
PolicyIDs []int `json:"policyIds"`
}

type IdentityContext struct {
IdentityType string `json:"identityType"`
CustomerID int `json:"customerId"`
Expand Down
103 changes: 103 additions & 0 deletions sysdig/internal/client/v2/zone_policy_assignment.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
package v2

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

const zonePolicyAssignmentPath = "%s/api/cspm/v1/zones/%d/policies"

type ZonePolicyAssignmentInterface interface {
Base
GetZonePolicyAssignment(ctx context.Context, zoneID int) (*ZonePolicyAssignment, error)
CreateZonePolicyAssignment(ctx context.Context, zoneID int, req *ZonePolicyAssignmentRequest) (*ZonePolicyAssignment, error)
UpdateZonePolicyAssignment(ctx context.Context, zoneID int, req *ZonePolicyAssignmentRequest) (*ZonePolicyAssignment, error)
DeleteZonePolicyAssignment(ctx context.Context, zoneID int) error
}

func (c *Client) GetZonePolicyAssignment(ctx context.Context, zoneID int) (result *ZonePolicyAssignment, err error) {
response, err := c.requester.Request(ctx, http.MethodGet, c.getZonePolicyAssignmentURL(zoneID), nil)
if err != nil {
return nil, err
}
defer func() {
if dErr := response.Body.Close(); dErr != nil {
err = fmt.Errorf("unable to close response body: %w", dErr)
}
}()

if response.StatusCode != http.StatusOK {
return nil, c.APIErrorFromResponse(response)
}

return Unmarshal[*ZonePolicyAssignment](response.Body)
}

func (c *Client) CreateZonePolicyAssignment(ctx context.Context, zoneID int, req *ZonePolicyAssignmentRequest) (result *ZonePolicyAssignment, err error) {
payload, err := Marshal(req)
if err != nil {
return nil, err
}

response, err := c.requester.Request(ctx, http.MethodPost, c.getZonePolicyAssignmentURL(zoneID), payload)
if err != nil {
return nil, err
}
defer func() {
if dErr := response.Body.Close(); dErr != nil {
err = fmt.Errorf("unable to close response body: %w", dErr)
}
}()

if response.StatusCode != http.StatusOK && response.StatusCode != http.StatusCreated {
return nil, c.APIErrorFromResponse(response)
}

return Unmarshal[*ZonePolicyAssignment](response.Body)
}

func (c *Client) UpdateZonePolicyAssignment(ctx context.Context, zoneID int, req *ZonePolicyAssignmentRequest) (result *ZonePolicyAssignment, err error) {
payload, err := Marshal(req)
if err != nil {
return nil, err
}

response, err := c.requester.Request(ctx, http.MethodPut, c.getZonePolicyAssignmentURL(zoneID), payload)
if err != nil {
return nil, err
}
defer func() {
if dErr := response.Body.Close(); dErr != nil {
err = fmt.Errorf("unable to close response body: %w", dErr)
}
}()

if response.StatusCode != http.StatusOK && response.StatusCode != http.StatusCreated {
return nil, c.APIErrorFromResponse(response)
}

return Unmarshal[*ZonePolicyAssignment](response.Body)
}

func (c *Client) DeleteZonePolicyAssignment(ctx context.Context, zoneID int) (err error) {
response, err := c.requester.Request(ctx, http.MethodDelete, c.getZonePolicyAssignmentURL(zoneID), nil)
if err != nil {
return err
}
defer func() {
if dErr := response.Body.Close(); dErr != nil {
err = fmt.Errorf("unable to close response body: %w", dErr)
}
}()

if response.StatusCode != http.StatusNoContent && response.StatusCode != http.StatusOK && response.StatusCode != http.StatusNotFound {
return c.APIErrorFromResponse(response)
}

return nil
}

func (c *Client) getZonePolicyAssignmentURL(zoneID int) string {
return fmt.Sprintf(zonePolicyAssignmentPath, c.config.url, zoneID)
}
1 change: 1 addition & 0 deletions sysdig/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,7 @@ func (p *SysdigProvider) Provider() *schema.Provider {
"sysdig_secure_vulnerability_policy": resourceSysdigSecureVulnerabilityPolicy(),
"sysdig_secure_vulnerability_rule_bundle": resourceSysdigSecureVulnerabilityRuleBundle(),
"sysdig_secure_zone": resourceSysdigSecureZone(),
"sysdig_secure_zone_posture_policy_assignment": resourceSysdigSecureZonePosturePolicyAssignment(),
},
DataSourcesMap: map[string]*schema.Resource{
"sysdig_agent_access_key": dataSourceSysdigAgentAccessKey(),
Expand Down
154 changes: 154 additions & 0 deletions sysdig/resource_sysdig_secure_zone_posture_policy_assignment.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
package sysdig

import (
"context"
"fmt"
"strconv"
"time"

v2 "github.com/draios/terraform-provider-sysdig/sysdig/internal/client/v2"
"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
)

func resourceSysdigSecureZonePosturePolicyAssignment() *schema.Resource {
timeout := 5 * time.Minute

return &schema.Resource{
CreateContext: resourceSysdigSecureZonePosturePolicyAssignmentCreate,
ReadContext: resourceSysdigSecureZonePosturePolicyAssignmentRead,
UpdateContext: resourceSysdigSecureZonePosturePolicyAssignmentUpdate,
DeleteContext: resourceSysdigSecureZonePosturePolicyAssignmentDelete,
Importer: &schema.ResourceImporter{
StateContext: schema.ImportStatePassthroughContext,
},
Timeouts: &schema.ResourceTimeout{
Create: schema.DefaultTimeout(timeout),
Update: schema.DefaultTimeout(timeout),
Read: schema.DefaultTimeout(timeout),
Delete: schema.DefaultTimeout(timeout),
},
Schema: map[string]*schema.Schema{
SchemaZoneIDKey: {
Type: schema.TypeInt,
Required: true,
ForceNew: true,
},
SchemaPolicyIDsKey: {
Type: schema.TypeSet,
Required: true,
Elem: &schema.Schema{Type: schema.TypeInt},
},
},
}
}

func getZonePolicyAssignmentClient(clients SysdigClients) (v2.ZonePolicyAssignmentInterface, error) {
var client v2.ZonePolicyAssignmentInterface
var err error
switch clients.GetClientType() {
case IBMSecure:
client, err = clients.ibmSecureClient()
default:
client, err = clients.sysdigSecureClientV2()
}
if err != nil {
return nil, err
}
return client, nil
}

func resourceSysdigSecureZonePosturePolicyAssignmentCreate(ctx context.Context, d *schema.ResourceData, m any) diag.Diagnostics {
client, err := getZonePolicyAssignmentClient(m.(SysdigClients))
if err != nil {
return diag.FromErr(err)
}

zoneID := d.Get(SchemaZoneIDKey).(int)
req := &v2.ZonePolicyAssignmentRequest{
PolicyIDs: expandIntSet(d.Get(SchemaPolicyIDsKey).(*schema.Set)),
}

_, err = client.CreateZonePolicyAssignment(ctx, zoneID, req)
if err != nil {
return diag.FromErr(fmt.Errorf("error creating zone policy assignment: %w", err))
}

d.SetId(strconv.Itoa(zoneID))
return resourceSysdigSecureZonePosturePolicyAssignmentRead(ctx, d, m)
}

func resourceSysdigSecureZonePosturePolicyAssignmentRead(ctx context.Context, d *schema.ResourceData, m any) diag.Diagnostics {
client, err := getZonePolicyAssignmentClient(m.(SysdigClients))
if err != nil {
return diag.FromErr(err)
}

zoneID, err := strconv.Atoi(d.Id())
if err != nil {
return diag.FromErr(fmt.Errorf("invalid zone id %q: %w", d.Id(), err))
}

assignment, err := client.GetZonePolicyAssignment(ctx, zoneID)
if err != nil {
if isNotFound(err) {
d.SetId("")
return nil
}
return diag.FromErr(fmt.Errorf("error reading zone policy assignment for zone %d: %w", zoneID, err))
}

_ = d.Set(SchemaZoneIDKey, zoneID)
_ = d.Set(SchemaPolicyIDsKey, assignment.PolicyIDs)
return nil
}

func resourceSysdigSecureZonePosturePolicyAssignmentUpdate(ctx context.Context, d *schema.ResourceData, m any) diag.Diagnostics {
client, err := getZonePolicyAssignmentClient(m.(SysdigClients))
if err != nil {
return diag.FromErr(err)
}

zoneID, err := strconv.Atoi(d.Id())
if err != nil {
return diag.FromErr(fmt.Errorf("invalid zone id %q: %w", d.Id(), err))
}

req := &v2.ZonePolicyAssignmentRequest{
PolicyIDs: expandIntSet(d.Get(SchemaPolicyIDsKey).(*schema.Set)),
}

_, err = client.UpdateZonePolicyAssignment(ctx, zoneID, req)
if err != nil {
return diag.FromErr(fmt.Errorf("error updating zone policy assignment for zone %d: %w", zoneID, err))
}

return resourceSysdigSecureZonePosturePolicyAssignmentRead(ctx, d, m)
}

func resourceSysdigSecureZonePosturePolicyAssignmentDelete(ctx context.Context, d *schema.ResourceData, m any) diag.Diagnostics {
client, err := getZonePolicyAssignmentClient(m.(SysdigClients))
if err != nil {
return diag.FromErr(err)
}

zoneID, err := strconv.Atoi(d.Id())
if err != nil {
return diag.FromErr(fmt.Errorf("invalid zone id %q: %w", d.Id(), err))
}

if err := client.DeleteZonePolicyAssignment(ctx, zoneID); err != nil {
return diag.FromErr(fmt.Errorf("error deleting zone policy assignment for zone %d: %w", zoneID, err))
}

d.SetId("")
return nil
}

func expandIntSet(set *schema.Set) []int {
result := make([]int, 0, set.Len())
for _, v := range set.List() {
result = append(result, v.(int))
}
return result
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
//go:build tf_acc_sysdig_secure || tf_acc_ibm_secure

package sysdig_test

import (
"fmt"
"testing"

"github.com/draios/terraform-provider-sysdig/sysdig"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
)

func TestAccSecureZonePosturePolicyAssignment_basic(t *testing.T) {
zoneName := "ZonePolicyAssign_TF_" + randomText(5)

resource.ParallelTest(t, resource.TestCase{
PreCheck: preCheckAnyEnv(t, SysdigSecureApiTokenEnv, SysdigIBMSecureAPIKeyEnv),
ProviderFactories: map[string]func() (*schema.Provider, error){
"sysdig": func() (*schema.Provider, error) {
return sysdig.Provider(), nil
},
},
Steps: []resource.TestStep{
// Step 1: Create zone + assignment with 1 policy
{
Config: testAccZonePolicyAssignmentWith1Policy(zoneName),
Check: resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttrSet("sysdig_secure_zone_posture_policy_assignment.test", "zone_id"),
resource.TestCheckResourceAttr("sysdig_secure_zone_posture_policy_assignment.test", "policy_ids.#", "1"),
),
},
// Step 2: Update to 2 policies
{
Config: testAccZonePolicyAssignmentWith2Policies(zoneName),
Check: resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttr("sysdig_secure_zone_posture_policy_assignment.test", "policy_ids.#", "2"),
),
},
// Step 3: Import by zone_id
{
ResourceName: "sysdig_secure_zone_posture_policy_assignment.test",
ImportState: true,
ImportStateVerify: true,
},
},
})
}

func testAccZonePolicyAssignmentWith1Policy(zoneName string) string {
return fmt.Sprintf(`
resource "sysdig_secure_zone" "test" {
name = "%s"
scope {
target_type = "aws"
rules = "account in (\"111111111111\")"
}
}

data "sysdig_secure_posture_policy" "p1" {
name = "Sysdig Kubernetes"
}

resource "sysdig_secure_zone_posture_policy_assignment" "test" {
zone_id = sysdig_secure_zone.test.id
policy_ids = [data.sysdig_secure_posture_policy.p1.id]
}
`, zoneName)
}

func testAccZonePolicyAssignmentWith2Policies(zoneName string) string {
return fmt.Sprintf(`
resource "sysdig_secure_zone" "test" {
name = "%s"
scope {
target_type = "aws"
rules = "account in (\"111111111111\")"
}
}

data "sysdig_secure_posture_policy" "p1" {
name = "Sysdig Kubernetes"
}

data "sysdig_secure_posture_policy" "p2" {
id = 1
}

resource "sysdig_secure_zone_posture_policy_assignment" "test" {
zone_id = sysdig_secure_zone.test.id
policy_ids = [
data.sysdig_secure_posture_policy.p1.id,
data.sysdig_secure_posture_policy.p2.id,
]
}
`, zoneName)
}
Loading
Loading