Skip to content

Commit

Permalink
fix: add policy_exception field_values_map (#619)
Browse files Browse the repository at this point in the history
Deprecates field_value_map for field_values_map. The data type needs to
change for the exceptions to take effect. By deprecating field_value_map
instead of changing it we will not introduce a breaking change, corrupt
any current state, and avoid any required code changes due to breakage.
  • Loading branch information
ipcrm authored Apr 2, 2024
1 parent cb81202 commit 0f27c50
Show file tree
Hide file tree
Showing 5 changed files with 199 additions and 6 deletions.
35 changes: 34 additions & 1 deletion docs/resources/policy_exception.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,28 @@ resource "lacework_policy_exception" "example" {
}
```


Create a Lacework Policy Exception to exempt specified `resourceTags` from policy.

```hcl
resource "lacework_policy_exception" "example" {
policy_id = "lacework-global-73"
description = "Exception for resource tag example1 and example2"
constraint {
field_key = "resourceTags"
field_values_map {
key = "example_tag1"
value = ["example_value", "example_value1"]
}
field_values_map {
key = "example_tag2"
value = ["example_value", "example_value1"]
}
}
}
```

## Argument Reference

The following arguments are supported:
Expand All @@ -40,7 +62,18 @@ The following arguments are supported:
`constraint` supports the following arguments:

* `field_key` - (Required) The key of the constraint being applied. Example for Aws polices this could be `accountIds`.
* `field_values` - (Required) The values related to the constraint key.
* `field_values` - (Optional) The values related to the constraint key.
* `field_value_map` - (Optional, **Deprecated**) FieldValueMap. See[FieldValueMap](#FieldValueMap) below for details.
* `field_values_map` - (Optional) FieldValueMap. See[FieldValuesMap](#FieldValuesMap) below for details.

### FieldValueMap

`field_value_map` allows defining constraint values for the `resourceTags` field key. Where `field_value_map` key
property is the name a given resource tag, and `value` includes any value that should match this exception.
**Deprecated** Use `field_values_map` instead.

`field_values_map` allows defining constraint values for the `resourceTags` field key. Where `field_values_map` key
property is the name a given resource tag, and `value` includes a list of values that should match this exception.

## Import

Expand Down
50 changes: 50 additions & 0 deletions examples/resource_lacework_policy_exception/current/main.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
terraform {
required_providers {
lacework = {
source = "lacework/lacework"
}
}
}

resource "lacework_policy_exception" "example" {
policy_id = "lacework-global-39"
description = var.description
constraint {
field_key = var.field_key
field_values = ["*"]
}

constraint {
field_key = "resourceTags"
field_values_map {
key = "test"
value = ["test", "test"]
}
}
}

variable "policy_id" {
type = string
default = "lacework-global-46"
}
variable "field_key" {
type = string
default = "accountIds"
}
variable "field_values" {
type = list(string)
default = ["*"]
}

variable "description" {
type = string
default = "Policy Exception Created via Terraform"
}

output "description" {
value = lacework_policy_exception.example.description
}

output "policy_id" {
value = lacework_policy_exception.example.policy_id
}
90 changes: 87 additions & 3 deletions integration/resource_lacework_policy_exception_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,13 @@ import (
)

// TestPolicyExceptionCreate applies integration terraform:
// => '../examples/resource_lacework_policy_exception'
// => '../examples/resource_lacework_policy_exception/deprecated'
//
// It uses the go-sdk to verify the created policy exception,
// applies an update and destroys it
func TestPolicyExceptionCreate(t *testing.T) {
terraformOptions := terraform.WithDefaultRetryableErrors(t, &terraform.Options{
TerraformDir: "../examples/resource_lacework_policy_exception",
TerraformDir: "../examples/resource_lacework_policy_exception/current",
EnvVars: tokenEnvVar,
Vars: map[string]interface{}{
"policy_id": "lacework-global-46",
Expand Down Expand Up @@ -50,13 +50,97 @@ func TestPolicyExceptionCreate(t *testing.T) {

assert.Contains(t, "Policy Exception Created via Terraform Updated", updateProps.Data.Description)

for _, c := range updateProps.Data.Constraints {
if c.FieldKey == "resourceTags" {
for _, v := range c.FieldValues {
data, ok := v.(map[string]interface{})
assert.True(t, ok, "data in constraint should have been map[string]interface{}")

keyValue, ok := data["key"]
assert.True(t, ok, "test key in resourceTags should have existed")
assert.Equal(t, "test", keyValue)

dataValue, ok := data["value"]
assert.True(t, ok, "value key should have existed")

testDataValues, ok := dataValue.([]interface{})
assert.True(t, ok, "data values should have been []string")
assert.EqualValues(t, []interface{}{"test", "test"}, testDataValues)
}
}
}

assert.Equal(t, "Policy Exception Created via Terraform Updated", actualDescription)
}

// TestPolicyExceptionInvalidConstraint tests an invalid constraint returns an error and lists valid constraint fields
func TestPolicyExceptionInvalidConstraint(t *testing.T) {
terraformOptions := terraform.WithDefaultRetryableErrors(t, &terraform.Options{
TerraformDir: "../examples/resource_lacework_policy_exception",
TerraformDir: "../examples/resource_lacework_policy_exception/current",
EnvVars: tokenEnvVar,
Vars: map[string]interface{}{
"policy_id": "lacework-global-46",
"description": "Policy Exception Created via Terraform",
"field_key": "invalid",
"field_values": []string{"*"},
},
})
defer terraform.Destroy(t, terraformOptions)

_, err := terraform.InitAndApplyE(t, terraformOptions)
assert.ErrorContains(t, err, "[400] fieldKey: invalid is not applicable to policy lacework-global-39. Valid fieldKey are [accountIds, arns, resourceNames, resourceTags]")
}

// TestDeprecatedPolicyExceptionCreate applies integration terraform:
// => '../examples/resource_lacework_policy_exception/deprecated'
//
// It uses the go-sdk to verify the created policy exception,
// applies an update and destroys it
func TestDeprecatedPolicyExceptionCreate(t *testing.T) {
terraformOptions := terraform.WithDefaultRetryableErrors(t, &terraform.Options{
TerraformDir: "../examples/resource_lacework_policy_exception/deprecated",
EnvVars: tokenEnvVar,
Vars: map[string]interface{}{
"policy_id": "lacework-global-46",
"description": "Policy Exception Created via Terraform",
"field_key": "accountIds",
"field_values": []string{"*"},
},
})
defer terraform.Destroy(t, terraformOptions)

// Create new Policy Exception
create := terraform.InitAndApplyAndIdempotent(t, terraformOptions)
actualDescription := terraform.Output(t, terraformOptions, "description")
actualPolicyID := terraform.Output(t, terraformOptions, "policy_id")
createProps := GetPolicyExceptionProps(create, actualPolicyID)

assert.Contains(t, "Policy Exception Created via Terraform", createProps.Data.Description)

assert.Equal(t, "Policy Exception Created via Terraform", actualDescription)

// Update Policy Exception
terraformOptions.Vars = map[string]interface{}{
"policy_id": "lacework-global-46",
"description": "Policy Exception Created via Terraform Updated",
"field_key": "accountIds",
"field_values": []string{"*"},
}

update := terraform.ApplyAndIdempotent(t, terraformOptions)
updateProps := GetPolicyExceptionProps(update, actualPolicyID)

actualDescription = terraform.Output(t, terraformOptions, "description")

assert.Contains(t, "Policy Exception Created via Terraform Updated", updateProps.Data.Description)

assert.Equal(t, "Policy Exception Created via Terraform Updated", actualDescription)
}

// TestDeprecatedPolicyExceptionInvalidConstraint tests an invalid constraint returns an error and lists valid constraint fields
func TestDeprecatedPolicyExceptionInvalidConstraint(t *testing.T) {
terraformOptions := terraform.WithDefaultRetryableErrors(t, &terraform.Options{
TerraformDir: "../examples/resource_lacework_policy_exception/deprecated",
EnvVars: tokenEnvVar,
Vars: map[string]interface{}{
"policy_id": "lacework-global-46",
Expand Down
30 changes: 28 additions & 2 deletions lacework/resource_lacework_policy_exception.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,10 +57,36 @@ func resourceLaceworkPolicyException() *schema.Resource {
},
},
},
"field_values_map": {
Type: schema.TypeSet,
Optional: true,
Description: "A list of key values pairs to filter the policy exception",
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"key": {
Type: schema.TypeString,
Description: "The values map key",
Required: true,
},
"value": {
Type: schema.TypeList,
Description: "The values map value list",
Required: true,
Elem: &schema.Schema{
Type: schema.TypeString,
StateFunc: func(val interface{}) string {
return strings.TrimSpace(val.(string))
},
},
},
},
},
},
"field_value_map": {
Type: schema.TypeSet,
Optional: true,
Description: "A list of key value pairs to filter the policy exception",
Deprecated: "This attribute is deprecated and has been replaced by `field_values_map`",
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"key": {
Expand Down Expand Up @@ -247,7 +273,7 @@ func sanitizeConstraintKeys(itemMap map[string]any) map[string]any {
var newMap = make(map[string]any)
var constraintMapList []any
for k, v := range itemMap {
if k == "field_value_map" {
if k == "field_value_map" || k == "field_values_map" {
list := v.(*schema.Set).List()
if len(list) > 0 {
constraintMapList = append(constraintMapList, list...)
Expand All @@ -256,7 +282,7 @@ func sanitizeConstraintKeys(itemMap map[string]any) map[string]any {
}
}
newKey := strings.Replace(k, "_", "", -1)
if newKey != "fieldvaluemap" {
if newKey != "fieldvaluemap" && newKey != "fieldvaluesmap" {
newMap[newKey] = v
}
}
Expand Down

0 comments on commit 0f27c50

Please sign in to comment.