Skip to content

Commit

Permalink
Add support for source/target service accounts to `google_compute_fir…
Browse files Browse the repository at this point in the history
…ewall` (#681)

* add source/target service accounts to firewalls

* firewall service account docs
  • Loading branch information
danawillow authored Nov 6, 2017
1 parent b74e1cd commit 4a499b2
Show file tree
Hide file tree
Showing 3 changed files with 121 additions and 14 deletions.
50 changes: 36 additions & 14 deletions google/resource_compute_firewall.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ var FirewallVersionedFeatures = []Feature{
Feature{Version: v0beta, Item: "direction"},
Feature{Version: v0beta, Item: "destination_ranges"},
Feature{Version: v0beta, Item: "priority", DefaultValue: COMPUTE_FIREWALL_PRIORITY_DEFAULT},
Feature{Version: v0beta, Item: "source_service_accounts"},
Feature{Version: v0beta, Item: "target_service_accounts"},
}

func resourceComputeFirewall() *schema.Resource {
Expand Down Expand Up @@ -159,6 +161,22 @@ func resourceComputeFirewall() *schema.Resource {
Elem: &schema.Schema{Type: schema.TypeString},
Set: schema.HashString,
},

"source_service_accounts": {
Type: schema.TypeSet,
Optional: true,
Elem: &schema.Schema{Type: schema.TypeString},
ForceNew: true,
ConflictsWith: []string{"source_tags", "target_tags"},
},

"target_service_accounts": {
Type: schema.TypeSet,
Optional: true,
Elem: &schema.Schema{Type: schema.TypeString},
ForceNew: true,
ConflictsWith: []string{"source_tags", "target_tags"},
},
},
}
}
Expand Down Expand Up @@ -191,7 +209,7 @@ func resourceComputeFirewallCreate(d *schema.ResourceData, meta interface{}) err
return err
}

firewall, err := resourceFirewall(d, meta, computeApiVersion)
firewall, err := resourceFirewall(d, meta)
if err != nil {
return err
}
Expand Down Expand Up @@ -314,6 +332,8 @@ func resourceComputeFirewallRead(d *schema.ResourceData, meta interface{}) error
d.Set("allow", flattenAllowed(firewall.Allowed))
d.Set("deny", flattenDenied(firewall.Denied))
d.Set("priority", int(firewall.Priority))
d.Set("source_service_accounts", firewall.SourceServiceAccounts)
d.Set("target_service_accounts", firewall.TargetServiceAccounts)
return nil
}

Expand All @@ -328,7 +348,7 @@ func resourceComputeFirewallUpdate(d *schema.ResourceData, meta interface{}) err

d.Partial(true)

firewall, err := resourceFirewall(d, meta, computeApiVersion)
firewall, err := resourceFirewall(d, meta)
if err != nil {
return err
}
Expand Down Expand Up @@ -402,7 +422,7 @@ func resourceComputeFirewallDelete(d *schema.ResourceData, meta interface{}) err
return nil
}

func resourceFirewall(d *schema.ResourceData, meta interface{}, computeApiVersion ComputeApiVersion) (*computeBeta.Firewall, error) {
func resourceFirewall(d *schema.ResourceData, meta interface{}) (*computeBeta.Firewall, error) {
config := meta.(*Config)

network, err := ParseNetworkFieldValue(d.Get("network").(string), d, config)
Expand Down Expand Up @@ -473,16 +493,18 @@ func resourceFirewall(d *schema.ResourceData, meta interface{}, computeApiVersio

// Build the firewall parameter
return &computeBeta.Firewall{
Name: d.Get("name").(string),
Description: d.Get("description").(string),
Direction: d.Get("direction").(string),
Network: network.RelativeLink(),
Allowed: allowed,
Denied: denied,
SourceRanges: sourceRanges,
SourceTags: sourceTags,
DestinationRanges: destinationRanges,
TargetTags: targetTags,
Priority: int64(d.Get("priority").(int)),
Name: d.Get("name").(string),
Description: d.Get("description").(string),
Direction: d.Get("direction").(string),
Network: network.RelativeLink(),
Allowed: allowed,
Denied: denied,
SourceRanges: sourceRanges,
SourceTags: sourceTags,
DestinationRanges: destinationRanges,
TargetTags: targetTags,
Priority: int64(d.Get("priority").(int)),
SourceServiceAccounts: convertStringSet(d.Get("source_service_accounts").(*schema.Set)),
TargetServiceAccounts: convertStringSet(d.Get("target_service_accounts").(*schema.Set)),
}, nil
}
72 changes: 72 additions & 0 deletions google/resource_compute_firewall_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package google

import (
"fmt"
"os"
"testing"

"github.com/hashicorp/terraform/helper/acctest"
Expand Down Expand Up @@ -167,6 +168,36 @@ func TestAccComputeFirewall_egress(t *testing.T) {
})
}

func TestAccComputeFirewall_serviceAccounts(t *testing.T) {
t.Parallel()

var firewall computeBeta.Firewall
networkName := fmt.Sprintf("firewall-test-%s", acctest.RandString(10))
firewallName := fmt.Sprintf("firewall-test-%s", acctest.RandString(10))

sourceSa := fmt.Sprintf("firewall-test-%s", acctest.RandString(10))
targetSa := fmt.Sprintf("firewall-test-%s", acctest.RandString(10))
project := os.Getenv("GOOGLE_PROJECT")
sourceSaEmail := fmt.Sprintf("%s@%s.iam.gserviceaccount.com", sourceSa, project)
targetSaEmail := fmt.Sprintf("%s@%s.iam.gserviceaccount.com", targetSa, project)

resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckComputeFirewallDestroy,
Steps: []resource.TestStep{
resource.TestStep{
Config: testAccComputeFirewall_serviceAccounts(sourceSa, targetSa, networkName, firewallName),
Check: resource.ComposeTestCheckFunc(
testAccCheckComputeBetaFirewallExists("google_compute_firewall.foobar", &firewall),
testAccCheckComputeBetaFirewallServiceAccounts(sourceSaEmail, targetSaEmail, &firewall),
testAccCheckComputeFirewallBetaApiVersion(&firewall),
),
},
},
})
}

func testAccCheckComputeFirewallDestroy(s *terraform.State) error {
config := testAccProvider.Meta().(*Config)

Expand Down Expand Up @@ -291,6 +322,19 @@ func testAccCheckComputeBetaFirewallEgress(firewall *computeBeta.Firewall) resou
}
}

func testAccCheckComputeBetaFirewallServiceAccounts(sourceSa, targetSa string, firewall *computeBeta.Firewall) resource.TestCheckFunc {
return func(s *terraform.State) error {
if len(firewall.SourceServiceAccounts) != 1 || firewall.SourceServiceAccounts[0] != sourceSa {
return fmt.Errorf("Expected sourceServiceAccount of %s, got %v", sourceSa, firewall.SourceServiceAccounts)
}
if len(firewall.TargetServiceAccounts) != 1 || firewall.TargetServiceAccounts[0] != targetSa {
return fmt.Errorf("Expected targetServiceAccount of %s, got %v", targetSa, firewall.TargetServiceAccounts)
}

return nil
}
}

func testAccCheckComputeFirewallBetaApiVersion(firewall *computeBeta.Firewall) resource.TestCheckFunc {
return func(s *terraform.State) error {
// The self-link of the network field is used to determine which API was used when fetching
Expand Down Expand Up @@ -426,3 +470,31 @@ func testAccComputeFirewall_egress(network, firewall string) string {
}
}`, network, firewall)
}

func testAccComputeFirewall_serviceAccounts(sourceSa, targetSa, network, firewall string) string {
return fmt.Sprintf(`
resource "google_service_account" "source" {
account_id = "%s"
}
resource "google_service_account" "target" {
account_id = "%s"
}
resource "google_compute_network" "foobar" {
name = "%s"
}
resource "google_compute_firewall" "foobar" {
name = "firewall-test-%s"
description = "Resource created for Terraform acceptance testing"
network = "${google_compute_network.foobar.name}"
allow {
protocol = "icmp"
}
source_service_accounts = ["${google_service_account.source.email}"]
target_service_accounts = ["${google_service_account.target.email}"]
}`, sourceSa, targetSa, network, firewall)
}
13 changes: 13 additions & 0 deletions website/docs/r/compute_firewall.html.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,19 @@ The following arguments are supported:
* `destination_ranges` - (Optional, [Beta](/docs/providers/google/index.html#beta-features)) A list of destination CIDR ranges that this
firewall applies to. Can't be used for `INGRESS`.

* `source_service_accounts` - (Optional, [Beta](/docs/providers/google/index.html#beta-features)) A list of service accounts such that
the firewall will apply only to traffic originating from an instance with a service account in this list. Source service accounts
cannot be used to control traffic to an instance's external IP address because service accounts are associated with an instance, not
an IP address. `source_ranges` can be set at the same time as `source_service_accounts`. If both are set, the firewall will apply to
traffic that has source IP address within `source_ranges` OR the source IP belongs to an instance with service account listed in
`source_service_accounts`. The connection does not need to match both properties for the firewall to apply. `source_service_accounts`
cannot be used at the same time as `source_tags` or `target_tags`.

* `target_service_accounts` - (Optional, [Beta](/docs/providers/google/index.html#beta-features)) A list of service accounts indicating
sets of instances located in the network that may make network connections as specified in `allow`. `target_service_accounts` cannot
be used at the same time as `source_tags` or `target_tags`. If neither `target_service_accounts` nor `target_tags` are specified, the
firewall rule applies to all instances on the specified network.

The `allow` block supports:

* `protocol` - (Required) The name of the protocol to allow.
Expand Down

0 comments on commit 4a499b2

Please sign in to comment.