diff --git a/google-beta/resource_compute_security_policy.go b/google-beta/resource_compute_security_policy.go index 03948c1978..e198374c12 100644 --- a/google-beta/resource_compute_security_policy.go +++ b/google-beta/resource_compute_security_policy.go @@ -9,7 +9,7 @@ import ( "github.com/hashicorp/errwrap" "github.com/hashicorp/terraform-plugin-sdk/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/helper/validation" - "google.golang.org/api/compute/v0.beta" + compute "google.golang.org/api/compute/v0.beta" ) func resourceComputeSecurityPolicy() *schema.Resource { @@ -73,7 +73,7 @@ func resourceComputeSecurityPolicy() *schema.Resource { Schema: map[string]*schema.Schema{ "config": { Type: schema.TypeList, - Required: true, + Optional: true, MaxItems: 1, Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ @@ -90,9 +90,36 @@ func resourceComputeSecurityPolicy() *schema.Resource { "versioned_expr": { Type: schema.TypeString, - Required: true, + Optional: true, ValidateFunc: validation.StringInSlice([]string{"SRC_IPS_V1"}, false), }, + + "expr": { + Type: schema.TypeList, + Optional: true, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "expression": { + Type: schema.TypeString, + Required: true, + }, + // These fields are not yet supported (Issue terraform-providers/terraform-provider-google#4497: mbang) + // "title": { + // Type: schema.TypeString, + // Optional: true, + // }, + // "description": { + // Type: schema.TypeString, + // Optional: true, + // }, + // "location": { + // Type: schema.TypeString, + // Optional: true, + // }, + }, + }, + }, }, }, }, @@ -330,6 +357,7 @@ func expandSecurityPolicyMatch(configured []interface{}) *compute.SecurityPolicy return &compute.SecurityPolicyRuleMatcher{ VersionedExpr: data["versioned_expr"].(string), Config: expandSecurityPolicyMatchConfig(data["config"].([]interface{})), + Expr: expandSecurityPolicyMatchExpr(data["expr"].([]interface{})), } } @@ -344,6 +372,21 @@ func expandSecurityPolicyMatchConfig(configured []interface{}) *compute.Security } } +func expandSecurityPolicyMatchExpr(expr []interface{}) *compute.Expr { + if len(expr) == 0 || expr[0] == nil { + return nil + } + + data := expr[0].(map[string]interface{}) + return &compute.Expr{ + Expression: data["expression"].(string), + // These fields are not yet supported (Issue terraform-providers/terraform-provider-google#4497: mbang) + // Title: data["title"].(string), + // Description: data["description"].(string), + // Location: data["location"].(string), + } +} + func flattenSecurityPolicyRules(rules []*compute.SecurityPolicyRule) []map[string]interface{} { rulesSchema := make([]map[string]interface{}, 0, len(rules)) for _, rule := range rules { @@ -352,16 +395,7 @@ func flattenSecurityPolicyRules(rules []*compute.SecurityPolicyRule) []map[strin "priority": rule.Priority, "action": rule.Action, "preview": rule.Preview, - "match": []map[string]interface{}{ - { - "versioned_expr": rule.Match.VersionedExpr, - "config": []map[string]interface{}{ - { - "src_ip_ranges": schema.NewSet(schema.HashString, convertStringArrToInterface(rule.Match.Config.SrcIpRanges)), - }, - }, - }, - }, + "match": flattenMatch(rule.Match), } rulesSchema = append(rulesSchema, data) @@ -369,6 +403,48 @@ func flattenSecurityPolicyRules(rules []*compute.SecurityPolicyRule) []map[strin return rulesSchema } +func flattenMatch(match *compute.SecurityPolicyRuleMatcher) []map[string]interface{} { + if match == nil { + return nil + } + + data := map[string]interface{}{ + "versioned_expr": match.VersionedExpr, + "config": flattenMatchConfig(match.Config), + "expr": flattenMatchExpr(match), + } + + return []map[string]interface{}{data} +} + +func flattenMatchConfig(conf *compute.SecurityPolicyRuleMatcherConfig) []map[string]interface{} { + if conf == nil { + return nil + } + + data := map[string]interface{}{ + "src_ip_ranges": schema.NewSet(schema.HashString, convertStringArrToInterface(conf.SrcIpRanges)), + } + + return []map[string]interface{}{data} +} + +func flattenMatchExpr(match *compute.SecurityPolicyRuleMatcher) []map[string]interface{} { + if match.Expr == nil { + return nil + } + + data := map[string]interface{}{ + "expression": match.Expr.Expression, + // These fields are not yet supported (Issue terraform-providers/terraform-provider-google#4497: mbang) + // "title": match.Expr.Title, + // "description": match.Expr.Description, + // "location": match.Expr.Location, + } + + return []map[string]interface{}{data} +} + func resourceSecurityPolicyStateImporter(d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) { config := meta.(*Config) if err := parseImportId([]string{"projects/(?P[^/]+)/global/securityPolicies/(?P[^/]+)", "(?P[^/]+)/(?P[^/]+)", "(?P[^/]+)"}, d, config); err != nil { diff --git a/google-beta/resource_compute_security_policy_test.go b/google-beta/resource_compute_security_policy_test.go index f99136796d..2058341036 100644 --- a/google-beta/resource_compute_security_policy_test.go +++ b/google-beta/resource_compute_security_policy_test.go @@ -53,6 +53,28 @@ func TestAccComputeSecurityPolicy_withRule(t *testing.T) { }) } +func TestAccComputeSecurityPolicy_withRuleExpr(t *testing.T) { + t.Parallel() + + spName := fmt.Sprintf("tf-test-%s", acctest.RandString(10)) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckComputeSecurityPolicyDestroy, + Steps: []resource.TestStep{ + { + Config: testAccComputeSecurityPolicy_withRuleExpr(spName), + }, + { + ResourceName: "google_compute_security_policy.policy", + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + func TestAccComputeSecurityPolicy_update(t *testing.T) { t.Parallel() @@ -200,3 +222,37 @@ resource "google_compute_security_policy" "policy" { } `, spName) } + +func testAccComputeSecurityPolicy_withRuleExpr(spName string) string { + return fmt.Sprintf(` +resource "google_compute_security_policy" "policy" { + name = "%s" + + rule { + action = "allow" + priority = "2147483647" + match { + versioned_expr = "SRC_IPS_V1" + config { + src_ip_ranges = ["*"] + } + } + description = "default rule" + } + + rule { + action = "allow" + priority = "2000" + match { + expr { + // These fields are not yet supported (Issue terraform-providers/terraform-provider-google#4497: mbang) + // title = "Has User" + // description = "Determines whether the request has a user account" + expression = "evaluatePreconfiguredExpr('xss-canary')" + } + } + preview = true + } +} +`, spName) +} diff --git a/website/docs/r/compute_security_policy.html.markdown b/website/docs/r/compute_security_policy.html.markdown index 88ea7b09f2..0e3f1e411e 100644 --- a/website/docs/r/compute_security_policy.html.markdown +++ b/website/docs/r/compute_security_policy.html.markdown @@ -83,18 +83,29 @@ The `rule` block supports: The `match` block supports: -* `config` - (Required) The configuration options available when specifying `versioned_expr`. +* `config` - (Optional) The configuration options available when specifying `versioned_expr`. + This field must be specified if `versioned_expr` is specified and cannot be specified if `versioned_expr` is not specified. Structure is documented below. -* `versioned_expr` - (Required) Predefined rule expression. Available options: +* `versioned_expr` - (Optional) Predefined rule expression. If this field is specified, `config` must also be specified. + Available options: * SRC_IPS_V1: Must specify the corresponding `src_ip_ranges` field in `config`. +* `expr` - (Optional) User defined CEVAL expression. A CEVAL expression is used to specify match criteria + such as origin.ip, source.region_code and contents in the request header. + Structure is documented below. + The `config` block supports: * `src_ip_ranges` - (Required) Set of IP addresses or ranges (IPV4 or IPV6) in CIDR notation to match against inbound traffic. There is a limit of 5 IP ranges per rule. A value of '\*' matches all IPs (can be used to override the default behavior). +The `expr` block supports: + +* `expression` - (Required) Textual representation of an expression in Common Expression Language syntax. + The application context of the containing message determines which well-known feature set of CEL is supported. + ## Attributes Reference In addition to the arguments listed above, the following computed attributes are