Skip to content

Commit b5e157a

Browse files
add firewall logging controls (#3780) (#2310)
* add firewall logging controls * make backward compatible * check enable_logging in expand * update docs * update expand logic to fix failing test Signed-off-by: Modular Magician <[email protected]>
1 parent e9eaf87 commit b5e157a

File tree

4 files changed

+114
-48
lines changed

4 files changed

+114
-48
lines changed

.changelog/3780.txt

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
```release-note:enhancement
2+
compute: added `logConfig.metadata` to `google_compute_firewall`, defining this will enable logging.
3+
```
4+
```release-note:deprecation
5+
compute: deprecated `enableLogging` on `google_compute_firewall`, define `logConfig.metadata` to enable logging instead.
6+
```

google-beta/resource_compute_firewall.go

Lines changed: 75 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,6 @@ import (
2727
"github.com/hashicorp/terraform-plugin-sdk/helper/hashcode"
2828
"github.com/hashicorp/terraform-plugin-sdk/helper/schema"
2929
"github.com/hashicorp/terraform-plugin-sdk/helper/validation"
30-
"google.golang.org/api/googleapi"
3130
)
3231

3332
func resourceComputeFirewallRuleHash(v interface{}) int {
@@ -53,6 +52,30 @@ func compareCaseInsensitive(k, old, new string, d *schema.ResourceData) bool {
5352
return strings.ToLower(old) == strings.ToLower(new)
5453
}
5554

55+
func diffSuppressEnableLogging(k, old, new string, d *schema.ResourceData) bool {
56+
if k == "log_config.#" {
57+
if new == "0" && d.Get("enable_logging").(bool) {
58+
return true
59+
}
60+
}
61+
62+
return false
63+
}
64+
65+
func resourceComputeFirewallEnableLoggingCustomizeDiff(diff *schema.ResourceDiff, v interface{}) error {
66+
enableLogging, enableExists := diff.GetOkExists("enable_logging")
67+
if !enableExists {
68+
return nil
69+
}
70+
71+
logConfigExists := diff.Get("log_config.#").(int) != 0
72+
if logConfigExists && enableLogging == false {
73+
return fmt.Errorf("log_config cannot be defined when enable_logging is false")
74+
}
75+
76+
return nil
77+
}
78+
5679
func resourceComputeFirewall() *schema.Resource {
5780
return &schema.Resource{
5881
Create: resourceComputeFirewallCreate,
@@ -72,6 +95,7 @@ func resourceComputeFirewall() *schema.Resource {
7295

7396
SchemaVersion: 1,
7497
MigrateState: resourceComputeFirewallMigrateState,
98+
CustomizeDiff: resourceComputeFirewallEnableLoggingCustomizeDiff,
7599

76100
Schema: map[string]*schema.Schema{
77101
"name": {
@@ -150,14 +174,24 @@ network it is associated with. When set to true, the firewall rule is
150174
not enforced and the network behaves as if it did not exist. If this
151175
is unspecified, the firewall rule will be enabled.`,
152176
},
153-
"enable_logging": {
154-
Type: schema.TypeBool,
155-
Optional: true,
156-
Description: `This field denotes whether to enable logging for a particular
157-
firewall rule. If logging is enabled, logs will be exported to
158-
Stackdriver.`,
177+
"log_config": {
178+
Type: schema.TypeList,
179+
Optional: true,
180+
DiffSuppressFunc: diffSuppressEnableLogging,
181+
Description: `This field denotes the logging options for a particular firewall rule.
182+
If defined, logging is enabled, and logs will be exported to Cloud Logging.`,
183+
MaxItems: 1,
184+
Elem: &schema.Resource{
185+
Schema: map[string]*schema.Schema{
186+
"metadata": {
187+
Type: schema.TypeString,
188+
Required: true,
189+
ValidateFunc: validation.StringInSlice([]string{"EXCLUDE_ALL_METADATA", "INCLUDE_ALL_METADATA"}, false),
190+
Description: `This field denotes whether to include or exclude metadata for firewall logs. Possible values: ["EXCLUDE_ALL_METADATA", "INCLUDE_ALL_METADATA"]`,
191+
},
192+
},
193+
},
159194
},
160-
161195
"priority": {
162196
Type: schema.TypeInt,
163197
Optional: true,
@@ -261,6 +295,13 @@ instances on the specified network.`,
261295
Computed: true,
262296
Description: `Creation timestamp in RFC3339 text format.`,
263297
},
298+
"enable_logging": {
299+
Type: schema.TypeBool,
300+
Optional: true,
301+
Computed: true,
302+
Deprecated: "Deprecated in favor of log_config",
303+
Description: "This field denotes whether to enable logging for a particular firewall rule. If logging is enabled, logs will be exported to Stackdriver.",
304+
},
264305
"project": {
265306
Type: schema.TypeString,
266307
Optional: true,
@@ -375,10 +416,10 @@ func resourceComputeFirewallCreate(d *schema.ResourceData, meta interface{}) err
375416
} else if v, ok := d.GetOkExists("disabled"); ok || !reflect.DeepEqual(v, disabledProp) {
376417
obj["disabled"] = disabledProp
377418
}
378-
logConfigProp, err := expandComputeFirewallLogConfig(nil, d, config)
419+
logConfigProp, err := expandComputeFirewallLogConfig(d.Get("log_config"), d, config)
379420
if err != nil {
380421
return err
381-
} else if !isEmptyValue(reflect.ValueOf(logConfigProp)) {
422+
} else if v, ok := d.GetOkExists("log_config"); ok || !reflect.DeepEqual(v, logConfigProp) {
382423
obj["logConfig"] = logConfigProp
383424
}
384425
nameProp, err := expandComputeFirewallName(d.Get("name"), d, config)
@@ -509,18 +550,8 @@ func resourceComputeFirewallRead(d *schema.ResourceData, meta interface{}) error
509550
if err := d.Set("disabled", flattenComputeFirewallDisabled(res["disabled"], d, config)); err != nil {
510551
return fmt.Errorf("Error reading Firewall: %s", err)
511552
}
512-
// Terraform must set the top level schema field, but since this object contains collapsed properties
513-
// it's difficult to know what the top level should be. Instead we just loop over the map returned from flatten.
514-
if flattenedProp := flattenComputeFirewallLogConfig(res["logConfig"], d, config); flattenedProp != nil {
515-
if gerr, ok := flattenedProp.(*googleapi.Error); ok {
516-
return fmt.Errorf("Error reading Firewall: %s", gerr)
517-
}
518-
casted := flattenedProp.([]interface{})[0]
519-
if casted != nil {
520-
for k, v := range casted.(map[string]interface{}) {
521-
d.Set(k, v)
522-
}
523-
}
553+
if err := d.Set("log_config", flattenComputeFirewallLogConfig(res["logConfig"], d, config)); err != nil {
554+
return fmt.Errorf("Error reading Firewall: %s", err)
524555
}
525556
if err := d.Set("name", flattenComputeFirewallName(res["name"], d, config)); err != nil {
526557
return fmt.Errorf("Error reading Firewall: %s", err)
@@ -592,10 +623,10 @@ func resourceComputeFirewallUpdate(d *schema.ResourceData, meta interface{}) err
592623
} else if v, ok := d.GetOkExists("disabled"); ok || !reflect.DeepEqual(v, disabledProp) {
593624
obj["disabled"] = disabledProp
594625
}
595-
logConfigProp, err := expandComputeFirewallLogConfig(nil, d, config)
626+
logConfigProp, err := expandComputeFirewallLogConfig(d.Get("log_config"), d, config)
596627
if err != nil {
597628
return err
598-
} else if !isEmptyValue(reflect.ValueOf(logConfigProp)) {
629+
} else if v, ok := d.GetOkExists("log_config"); ok || !reflect.DeepEqual(v, logConfigProp) {
599630
obj["logConfig"] = logConfigProp
600631
}
601632
networkProp, err := expandComputeFirewallNetwork(d.Get("network"), d, config)
@@ -802,14 +833,16 @@ func flattenComputeFirewallLogConfig(v interface{}, d *schema.ResourceData, conf
802833
if len(original) == 0 {
803834
return nil
804835
}
836+
837+
v, ok := original["enable"]
838+
if ok && !v.(bool) {
839+
return nil
840+
}
841+
805842
transformed := make(map[string]interface{})
806-
transformed["enable_logging"] =
807-
flattenComputeFirewallLogConfigEnableLogging(original["enable"], d, config)
843+
transformed["metadata"] = original["metadata"]
808844
return []interface{}{transformed}
809845
}
810-
func flattenComputeFirewallLogConfigEnableLogging(v interface{}, d *schema.ResourceData, config *Config) interface{} {
811-
return v
812-
}
813846

814847
func flattenComputeFirewallName(v interface{}, d *schema.ResourceData, config *Config) interface{} {
815848
return v
@@ -968,19 +1001,23 @@ func expandComputeFirewallDisabled(v interface{}, d TerraformResourceData, confi
9681001
}
9691002

9701003
func expandComputeFirewallLogConfig(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) {
1004+
l := v.([]interface{})
9711005
transformed := make(map[string]interface{})
972-
transformedEnableLogging, err := expandComputeFirewallLogConfigEnableLogging(d.Get("enable_logging"), d, config)
973-
if err != nil {
974-
return nil, err
975-
} else {
976-
transformed["enable"] = transformedEnableLogging
1006+
1007+
if len(l) == 0 || l[0] == nil {
1008+
// send enable = enable_logging value to ensure correct logging status if there is no config
1009+
transformed["enable"] = d.Get("enable_logging").(bool)
1010+
return transformed, nil
9771011
}
9781012

979-
return transformed, nil
980-
}
1013+
raw := l[0]
1014+
original := raw.(map[string]interface{})
9811015

982-
func expandComputeFirewallLogConfigEnableLogging(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) {
983-
return v, nil
1016+
// The log_config block is specified, so logging should be enabled
1017+
transformed["enable"] = true
1018+
transformed["metadata"] = original["metadata"]
1019+
1020+
return transformed, nil
9841021
}
9851022

9861023
func expandComputeFirewallName(v interface{}, d TerraformResourceData, config *Config) (interface{}, error) {

google-beta/resource_compute_firewall_test.go

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -207,23 +207,31 @@ func TestAccComputeFirewall_enableLogging(t *testing.T) {
207207
CheckDestroy: testAccCheckComputeFirewallDestroyProducer(t),
208208
Steps: []resource.TestStep{
209209
{
210-
Config: testAccComputeFirewall_enableLogging(networkName, firewallName, false),
210+
Config: testAccComputeFirewall_enableLogging(networkName, firewallName, ""),
211211
},
212212
{
213213
ResourceName: "google_compute_firewall.foobar",
214214
ImportState: true,
215215
ImportStateVerify: true,
216216
},
217217
{
218-
Config: testAccComputeFirewall_enableLogging(networkName, firewallName, true),
218+
Config: testAccComputeFirewall_enableLogging(networkName, firewallName, "INCLUDE_ALL_METADATA"),
219219
},
220220
{
221221
ResourceName: "google_compute_firewall.foobar",
222222
ImportState: true,
223223
ImportStateVerify: true,
224224
},
225225
{
226-
Config: testAccComputeFirewall_enableLogging(networkName, firewallName, false),
226+
Config: testAccComputeFirewall_enableLogging(networkName, firewallName, "EXCLUDE_ALL_METADATA"),
227+
},
228+
{
229+
ResourceName: "google_compute_firewall.foobar",
230+
ImportState: true,
231+
ImportStateVerify: true,
232+
},
233+
{
234+
Config: testAccComputeFirewall_enableLogging(networkName, firewallName, ""),
227235
},
228236
{
229237
ResourceName: "google_compute_firewall.foobar",
@@ -411,10 +419,13 @@ resource "google_compute_firewall" "foobar" {
411419
`, network, firewall)
412420
}
413421

414-
func testAccComputeFirewall_enableLogging(network, firewall string, enableLogging bool) string {
422+
func testAccComputeFirewall_enableLogging(network, firewall, logging string) string {
415423
enableLoggingCfg := ""
416-
if enableLogging {
417-
enableLoggingCfg = "enable_logging= true"
424+
if logging != "" {
425+
enableLoggingCfg = fmt.Sprintf(`log_config {
426+
metadata = "%s"
427+
}
428+
`, logging)
418429
}
419430
return fmt.Sprintf(`
420431
resource "google_compute_network" "foobar" {

website/docs/r/compute_firewall.html.markdown

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -136,11 +136,10 @@ The following arguments are supported:
136136
not enforced and the network behaves as if it did not exist. If this
137137
is unspecified, the firewall rule will be enabled.
138138

139-
* `enable_logging` -
139+
* `log_config` -
140140
(Optional)
141-
This field denotes whether to enable logging for a particular
142-
firewall rule. If logging is enabled, logs will be exported to
143-
Stackdriver.
141+
This field denotes the logging options for a particular firewall rule.
142+
If defined, logging is enabled, and logs will be exported to Cloud Logging. Structure is documented below.
144143

145144
* `priority` -
146145
(Optional)
@@ -208,6 +207,9 @@ The following arguments are supported:
208207
If it is not provided, the provider project is used.
209208

210209

210+
* `enable_logging` - (Optional, Deprecated) This field denotes whether to enable logging for a particular firewall rule.
211+
If logging is enabled, logs will be exported to Stackdriver. Deprecated in favor of `log_config`
212+
211213
The `allow` block supports:
212214

213215
* `protocol` -
@@ -244,6 +246,16 @@ The `deny` block supports:
244246
Example inputs include: ["22"], ["80","443"], and
245247
["12345-12349"].
246248

249+
The `log_config` block supports:
250+
251+
* `metadata` -
252+
(Required)
253+
This field denotes whether to include or exclude metadata for firewall logs.
254+
255+
Possible values are:
256+
* `EXCLUDE_ALL_METADATA`
257+
* `INCLUDE_ALL_METADATA`
258+
247259
## Attributes Reference
248260

249261
In addition to the arguments listed above, the following computed attributes are exported:

0 commit comments

Comments
 (0)