Add new google_compute_security_policy resource (#1242)

* revendor compute/v0.beta

* add new `google_compute_security_policy` resource

* docs updates for security policy

* Add link to official docs
This commit is contained in:
Dana Hoffman 2018-03-21 16:52:23 -07:00 committed by GitHub
parent 98baa31a05
commit fde96ca9d9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 40608 additions and 33259 deletions

View File

@ -126,6 +126,7 @@ func Provider() terraform.ResourceProvider {
"google_compute_router": resourceComputeRouter(),
"google_compute_router_interface": resourceComputeRouterInterface(),
"google_compute_router_peer": resourceComputeRouterPeer(),
"google_compute_security_policy": resourceComputeSecurityPolicy(),
"google_compute_shared_vpc_host_project": resourceComputeSharedVpcHostProject(),
"google_compute_shared_vpc_service_project": resourceComputeSharedVpcServiceProject(),
"google_compute_ssl_certificate": resourceComputeSslCertificate(),

View File

@ -0,0 +1,363 @@
package google
import (
"fmt"
"log"
"time"
"github.com/hashicorp/errwrap"
"github.com/hashicorp/terraform/helper/schema"
"github.com/hashicorp/terraform/helper/validation"
"google.golang.org/api/compute/v0.beta"
)
func resourceComputeSecurityPolicy() *schema.Resource {
return &schema.Resource{
Create: resourceComputeSecurityPolicyCreate,
Read: resourceComputeSecurityPolicyRead,
Update: resourceComputeSecurityPolicyUpdate,
Delete: resourceComputeSecurityPolicyDelete,
Importer: &schema.ResourceImporter{
State: schema.ImportStatePassthrough,
},
Timeouts: &schema.ResourceTimeout{
Create: schema.DefaultTimeout(4 * time.Minute),
Update: schema.DefaultTimeout(4 * time.Minute),
Delete: schema.DefaultTimeout(4 * time.Minute),
},
Schema: map[string]*schema.Schema{
"name": &schema.Schema{
Type: schema.TypeString,
Required: true,
ForceNew: true,
ValidateFunc: validateGCPName,
},
"description": &schema.Schema{
Type: schema.TypeString,
Optional: true,
},
"project": &schema.Schema{
Type: schema.TypeString,
Optional: true,
Computed: true,
ForceNew: true,
},
"rule": &schema.Schema{
Type: schema.TypeSet,
Optional: true,
Computed: true, // If no rules are set, a default rule is added
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"action": &schema.Schema{
Type: schema.TypeString,
Required: true,
ValidateFunc: validation.StringInSlice([]string{"allow", "deny(403)", "deny(404)", "deny(502)"}, false),
},
"priority": &schema.Schema{
Type: schema.TypeInt,
Required: true,
},
"match": &schema.Schema{
Type: schema.TypeList,
Required: true,
MaxItems: 1,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"config": &schema.Schema{
Type: schema.TypeList,
Required: true,
MaxItems: 1,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"src_ip_ranges": &schema.Schema{
Type: schema.TypeSet,
Required: true,
MinItems: 1,
MaxItems: 5,
Elem: &schema.Schema{Type: schema.TypeString},
},
},
},
},
"versioned_expr": &schema.Schema{
Type: schema.TypeString,
Required: true,
ValidateFunc: validation.StringInSlice([]string{"SRC_IPS_V1"}, false),
},
},
},
},
"description": &schema.Schema{
Type: schema.TypeString,
Optional: true,
},
"preview": &schema.Schema{
Type: schema.TypeBool,
Optional: true,
},
},
},
},
"fingerprint": &schema.Schema{
Type: schema.TypeString,
Computed: true,
},
"self_link": &schema.Schema{
Type: schema.TypeString,
Computed: true,
},
},
}
}
func resourceComputeSecurityPolicyCreate(d *schema.ResourceData, meta interface{}) error {
config := meta.(*Config)
project, err := getProject(d, config)
if err != nil {
return err
}
sp := d.Get("name").(string)
securityPolicy := &compute.SecurityPolicy{
Name: sp,
Description: d.Get("description").(string),
}
if v, ok := d.GetOk("rule"); ok {
securityPolicy.Rules = expandSecurityPolicyRules(v.(*schema.Set).List())
}
log.Printf("[DEBUG] SecurityPolicy insert request: %#v", securityPolicy)
op, err := config.clientComputeBeta.SecurityPolicies.Insert(project, securityPolicy).Do()
if err != nil {
return errwrap.Wrapf("Error creating SecurityPolicy: {{err}}", err)
}
d.SetId(securityPolicy.Name)
err = computeSharedOperationWaitTime(config.clientCompute, op, project, int(d.Timeout(schema.TimeoutCreate).Minutes()), fmt.Sprintf("Creating SecurityPolicy %q", sp))
if err != nil {
return err
}
return resourceComputeSecurityPolicyRead(d, meta)
}
func resourceComputeSecurityPolicyRead(d *schema.ResourceData, meta interface{}) error {
config := meta.(*Config)
project, err := getProject(d, config)
if err != nil {
return err
}
securityPolicy, err := config.clientComputeBeta.SecurityPolicies.Get(project, d.Id()).Do()
if err != nil {
return handleNotFoundError(err, d, fmt.Sprintf("SecurityPolicy %q", d.Id()))
}
d.Set("name", securityPolicy.Name)
d.Set("description", securityPolicy.Description)
if err := d.Set("rule", flattenSecurityPolicyRules(securityPolicy.Rules)); err != nil {
return err
}
d.Set("fingerprint", securityPolicy.Fingerprint)
d.Set("project", project)
d.Set("self_link", ConvertSelfLinkToV1(securityPolicy.SelfLink))
return nil
}
func resourceComputeSecurityPolicyUpdate(d *schema.ResourceData, meta interface{}) error {
config := meta.(*Config)
project, err := getProject(d, config)
if err != nil {
return err
}
sp := d.Id()
if d.HasChange("description") {
securityPolicy := &compute.SecurityPolicy{
Description: d.Get("description").(string),
Fingerprint: d.Get("fingerprint").(string),
ForceSendFields: []string{"Description"},
}
op, err := config.clientComputeBeta.SecurityPolicies.Patch(project, sp, securityPolicy).Do()
if err != nil {
return errwrap.Wrapf(fmt.Sprintf("Error updating SecurityPolicy %q: {{err}}", sp), err)
}
err = computeSharedOperationWaitTime(config.clientCompute, op, project, int(d.Timeout(schema.TimeoutCreate).Minutes()), fmt.Sprintf("Updating SecurityPolicy %q", sp))
if err != nil {
return err
}
}
if d.HasChange("rule") {
o, n := d.GetChange("rule")
oSet := o.(*schema.Set)
nSet := n.(*schema.Set)
oPriorities := map[int64]bool{}
nPriorities := map[int64]bool{}
for _, rule := range oSet.List() {
oPriorities[int64(rule.(map[string]interface{})["priority"].(int))] = true
}
for _, rule := range nSet.List() {
priority := int64(rule.(map[string]interface{})["priority"].(int))
nPriorities[priority] = true
if !oPriorities[priority] {
// If the rule is in new and its priority does not exist in old, then add it.
op, err := config.clientComputeBeta.SecurityPolicies.AddRule(project, sp, expandSecurityPolicyRule(rule)).Do()
if err != nil {
return errwrap.Wrapf(fmt.Sprintf("Error updating SecurityPolicy %q: {{err}}", sp), err)
}
err = computeSharedOperationWaitTime(config.clientCompute, op, project, int(d.Timeout(schema.TimeoutCreate).Minutes()), fmt.Sprintf("Updating SecurityPolicy %q", sp))
if err != nil {
return err
}
} else if !oSet.Contains(rule) {
// If the rule is in new, and its priority is in old, but its hash is different than the one in old, update it.
op, err := config.clientComputeBeta.SecurityPolicies.PatchRule(project, sp, expandSecurityPolicyRule(rule)).Priority(priority).Do()
if err != nil {
return errwrap.Wrapf(fmt.Sprintf("Error updating SecurityPolicy %q: {{err}}", sp), err)
}
err = computeSharedOperationWaitTime(config.clientCompute, op, project, int(d.Timeout(schema.TimeoutCreate).Minutes()), fmt.Sprintf("Updating SecurityPolicy %q", sp))
if err != nil {
return err
}
}
}
for _, rule := range oSet.List() {
priority := int64(rule.(map[string]interface{})["priority"].(int))
if !nPriorities[priority] {
// If the rule's priority is in old but not new, remove it.
op, err := config.clientComputeBeta.SecurityPolicies.RemoveRule(project, sp).Priority(priority).Do()
if err != nil {
return errwrap.Wrapf(fmt.Sprintf("Error updating SecurityPolicy %q: {{err}}", sp), err)
}
err = computeSharedOperationWaitTime(config.clientCompute, op, project, int(d.Timeout(schema.TimeoutCreate).Minutes()), fmt.Sprintf("Updating SecurityPolicy %q", sp))
if err != nil {
return err
}
}
}
}
return resourceComputeSecurityPolicyRead(d, meta)
}
func resourceComputeSecurityPolicyDelete(d *schema.ResourceData, meta interface{}) error {
config := meta.(*Config)
project, err := getProject(d, config)
if err != nil {
return err
}
// Delete the SecurityPolicy
op, err := config.clientComputeBeta.SecurityPolicies.Delete(project, d.Id()).Do()
if err != nil {
return errwrap.Wrapf("Error deleting SecurityPolicy: {{err}}", err)
}
err = computeSharedOperationWaitTime(config.clientCompute, op, project, int(d.Timeout(schema.TimeoutDelete).Minutes()), "Deleting SecurityPolicy")
if err != nil {
return err
}
d.SetId("")
return nil
}
func expandSecurityPolicyRules(configured []interface{}) []*compute.SecurityPolicyRule {
rules := make([]*compute.SecurityPolicyRule, 0, len(configured))
for _, raw := range configured {
rules = append(rules, expandSecurityPolicyRule(raw))
}
return rules
}
func expandSecurityPolicyRule(raw interface{}) *compute.SecurityPolicyRule {
data := raw.(map[string]interface{})
return &compute.SecurityPolicyRule{
Description: data["description"].(string),
Priority: int64(data["priority"].(int)),
Action: data["action"].(string),
Preview: data["preview"].(bool),
Match: expandSecurityPolicyMatch(data["match"].([]interface{})),
ForceSendFields: []string{"Description", "Preview"},
}
}
func expandSecurityPolicyMatch(configured []interface{}) *compute.SecurityPolicyRuleMatcher {
if len(configured) == 0 {
return nil
}
data := configured[0].(map[string]interface{})
return &compute.SecurityPolicyRuleMatcher{
VersionedExpr: data["versioned_expr"].(string),
Config: expandSecurityPolicyMatchConfig(data["config"].([]interface{})),
}
}
func expandSecurityPolicyMatchConfig(configured []interface{}) *compute.SecurityPolicyRuleMatcherConfig {
if len(configured) == 0 {
return nil
}
data := configured[0].(map[string]interface{})
return &compute.SecurityPolicyRuleMatcherConfig{
SrcIpRanges: convertStringArr(data["src_ip_ranges"].(*schema.Set).List()),
}
}
func flattenSecurityPolicyRules(rules []*compute.SecurityPolicyRule) []map[string]interface{} {
rulesSchema := make([]map[string]interface{}, 0, len(rules))
for _, rule := range rules {
data := map[string]interface{}{
"description": rule.Description,
"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)),
},
},
},
},
}
rulesSchema = append(rulesSchema, data)
}
return rulesSchema
}

View File

@ -0,0 +1,202 @@
package google
import (
"fmt"
"testing"
"github.com/hashicorp/terraform/helper/acctest"
"github.com/hashicorp/terraform/helper/resource"
"github.com/hashicorp/terraform/terraform"
)
func TestAccComputeSecurityPolicy_basic(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{
resource.TestStep{
Config: testAccComputeSecurityPolicy_basic(spName),
},
resource.TestStep{
ResourceName: "google_compute_security_policy.policy",
ImportState: true,
ImportStateVerify: true,
},
},
})
}
func TestAccComputeSecurityPolicy_withRule(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{
resource.TestStep{
Config: testAccComputeSecurityPolicy_withRule(spName),
},
resource.TestStep{
ResourceName: "google_compute_security_policy.policy",
ImportState: true,
ImportStateVerify: true,
},
},
})
}
func TestAccComputeSecurityPolicy_update(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{
resource.TestStep{
Config: testAccComputeSecurityPolicy_withRule(spName),
},
resource.TestStep{
ResourceName: "google_compute_security_policy.policy",
ImportState: true,
ImportStateVerify: true,
},
resource.TestStep{
Config: testAccComputeSecurityPolicy_update(spName),
},
resource.TestStep{
ResourceName: "google_compute_security_policy.policy",
ImportState: true,
ImportStateVerify: true,
},
resource.TestStep{
Config: testAccComputeSecurityPolicy_withRule(spName),
},
resource.TestStep{
ResourceName: "google_compute_security_policy.policy",
ImportState: true,
ImportStateVerify: true,
},
},
})
}
func testAccCheckComputeSecurityPolicyDestroy(s *terraform.State) error {
config := testAccProvider.Meta().(*Config)
for _, rs := range s.RootModule().Resources {
if rs.Type != "google_compute_security_policy" {
continue
}
pol := rs.Primary.ID
_, err := config.clientComputeBeta.SecurityPolicies.Get(config.Project, pol).Do()
if err == nil {
return fmt.Errorf("Security policy %q still exists", pol)
}
}
return nil
}
func testAccComputeSecurityPolicy_basic(spName string) string {
return fmt.Sprintf(`
resource "google_compute_security_policy" "policy" {
name = "%s"
description = "basic security policy"
}
`, spName)
}
func testAccComputeSecurityPolicy_withRule(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 {
versioned_expr = "SRC_IPS_V1"
config {
src_ip_ranges = ["10.0.0.0/24"]
}
}
preview = true
}
}
`, spName)
}
func testAccComputeSecurityPolicy_update(spName string) string {
return fmt.Sprintf(`
resource "google_compute_security_policy" "policy" {
name = "%s"
description = "updated description"
// keep this
rule {
action = "allow"
priority = "2147483647"
match {
versioned_expr = "SRC_IPS_V1"
config {
src_ip_ranges = ["*"]
}
}
description = "default rule"
}
// add this
rule {
action = "deny(403)"
priority = "1000"
match {
versioned_expr = "SRC_IPS_V1"
config {
src_ip_ranges = ["10.0.1.0/24"]
}
}
}
// update this
rule {
action = "allow"
priority = "2000"
match {
versioned_expr = "SRC_IPS_V1"
config {
src_ip_ranges = ["10.0.0.0/24"]
}
}
description = "updated description"
preview = false
}
}
`, spName)
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

6
vendor/vendor.json vendored
View File

@ -1274,10 +1274,10 @@
"revisionTime": "2017-09-12T00:03:44Z"
},
{
"checksumSHA1": "xIL4sQObOZib3wMDNpK6ck29uuo=",
"checksumSHA1": "kasq8gGLFuCh5Li1MgvYSOr1C20=",
"path": "google.golang.org/api/compute/v0.beta",
"revision": "17b5f22a248d6d3913171c1a557552ace0d9c806",
"revisionTime": "2017-11-21T19:49:26Z"
"revision": "8dd9492f46214d3ecd264e970de1cf530c51c12e",
"revisionTime": "2018-03-07T00:03:37Z"
},
{
"checksumSHA1": "mAsa+xKdihWif0c4tJ6hOwpDuhk=",

View File

@ -0,0 +1,112 @@
---
layout: "google"
page_title: "Google: google_compute_security_policy"
sidebar_current: "docs-google-compute-security-policy"
description: |-
Creates a Security Policy resource for Google Compute Engine.
---
# google\_compute\_security\_policy
A Security Policy defines an IP blacklist or whitelist that protects load balanced Google Cloud services by denying or permitting traffic from specified IP ranges. For more information
see the [official documentation](https://cloud.google.com/armor/docs/configure-security-policies)
and the [API](https://cloud.google.com/compute/docs/reference/rest/beta/securityPolicies).
~> **Note:** This entire resource is in [Beta](/docs/providers/google/index.html#beta-features)
## Example Usage
```hcl
resource "google_compute_security_policy" "policy" {
name = "my-policy"
rule {
action = "deny(403)"
priority = "1000"
match {
versioned_expr = "SRC_IPS_V1"
config {
src_ip_ranges = ["9.9.9.9/32"]
}
}
description = "Deny access to IPs in 9.9.9.0/24"
}
rule {
action = "allow"
priority = "2147483647"
match {
versioned_expr = "SRC_IPS_V1"
config {
src_ip_ranges = ["*"]
}
}
description = "default rule"
}
}
```
## Argument Reference
The following arguments are supported:
* `name` - (Required) The name of the security policy.
- - -
* `description` - (Optional) An optional description of this security policy. Max size is 2048.
* `project` - (Optional) The project in which the resource belongs. If it
is not provided, the provider project is used.
* `rule` - (Optional) The set of rules that belong to this policy. There must always be a default
rule (rule with priority 2147483647 and match "\*"). If no rules are provided when creating a
security policy, a default rule with action "allow" will be added. Structure is documented below.
The `rule` block supports:
* `action` - (Required) Action to take when `match` matches the request. Valid values:
* "allow" : allow access to target
* "deny(status)" : deny access to target, returns the HTTP response code specified (valid values are 403, 404 and 502)
* `priority` - (Required) An unique positive integer indicating the priority of evaluation for a rule.
Rules are evaluated from highest priority (lowest numerically) to lowest priority (highest numerically) in order.
* `match` - (Required) A match condition that incoming traffic is evaluated against.
If it evaluates to true, the corresponding `action` is enforced. Structure is documented below.
* `description` - (Optional) An optional description of this rule. Max size is 64.
* `preview` - (Optional) When set to true, the `action` specified above is not enforced.
Stackdriver logs for requests that trigger a preview action are annotated as such.
The `match` block supports:
* `config` - (Required) The configuration options available when specifying `versioned_expr`.
Structure is documented below.
* `versioned_expr` - (Required) Predefined rule expression. Available options:
* SRC_IPS_V1: Must specify the corresponding `src_ip_ranges` field in `config`.
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).
## Attributes Reference
In addition to the arguments listed above, the following computed attributes are
exported:
* `fingerprint` - Fingerprint of this resource.
* `self_link` - The URI of the created resource.
## Import
Security policies can be imported using the `name`, e.g.
```
$ terraform import google_compute_security_policy.policy my-policy
```

View File

@ -323,6 +323,10 @@
<a href="/docs/providers/google/r/compute_router_peer.html">google_compute_router_peer</a>
</li>
<li<%= sidebar_current("docs-google-compute-security-policy") %>>
<a href="/docs/providers/google/r/compute_security_policy.html">google_compute_security_policy</a>
</li>
<li<%= sidebar_current("docs-google-compute-shared-vpc-host-project") %>>
<a href="/docs/providers/google/r/compute_shared_vpc_host_project.html">google_compute_shared_vpc_host_project</a>
</li>