diff --git a/modules/net-vpc-firewall-yaml/README.md b/modules/net-vpc-firewall-yaml/README.md
new file mode 100644
index 00000000..95508092
--- /dev/null
+++ b/modules/net-vpc-firewall-yaml/README.md
@@ -0,0 +1,147 @@
+# Google Cloud VPC Firewall - Yaml
+
+This module allows creation and management of different types of firewall rules by defining them in well formatted `yaml` files.
+
+Yaml abstraction for FW rules can simplify users onboarding and also makes rules definition simpler and clearer comparing to HCL.
+
+Nested folder structure for yaml configurations is supported, which allows better and structured code management.
+
+## Example
+
+### Terraform code
+
+```hcl
+module "prod-firewall" {
+ source = "./modules/net-vpc-firewall-yaml"
+ project_id = "my-prod-project"
+ network = "my-prod-network"
+ config_path = "./prod"
+ log_config = {
+ metadata = "INCLUDE_ALL_METADATA"
+ }
+}
+
+module "dev-firewall" {
+ source = "./modules/net-vpc-firewall-yaml"
+ project_id = "my-dev-project"
+ network = "my-dev-network"
+ config_path = "./dev"
+}
+# tftest:skip
+```
+
+### Configuration Structure
+
+```bash
+├── dev
+│ ├── core
+│ │ └── common-rules.yaml
+│ ├── team-a
+│ │ ├── databases.yaml
+│ │ └── webb-app-a.yaml
+│ └── team-b
+│ ├── backend.yaml
+│ └── frontend.yaml
+└── prod
+ ├── core
+ │ └── common-rules.yaml
+ ├── team-a
+ │ ├── databases.yaml
+ │ └── webb-app-a.yaml
+ └── team-b
+ ├── backend.yaml
+ └── frontend.yaml
+```
+
+### Rule definition format and structure
+
+Firewall rules configuration should be placed in a set of yaml files in a folder/s. Firewall rule entry structure is following:
+
+```yaml
+rule-name: # descriptive name, naming convention is adjusted by the module
+ allow: # `allow` or `deny`
+ - ports: ['443', '80'] # ports for a specific protocol, keep empty list `[]` for all ports
+ protocol: tcp # protocol, put `all` for any protocol
+ direction: EGRESS # EGRESS or INGRESS
+ disabled: false # `false` or `true`, FW rule is disabled when `true`, default value is `true`
+ priority: 1000 # rule priority value, default value is 1000
+ source_ranges: # list of source ranges, should be specified only for `INGRESS` rule
+ - 0.0.0.0/0
+ destination_ranges: # list of destination ranges, should be specified only for `EGRESS` rule
+ - 0.0.0.0/0
+ source_tags: ['some-tag'] # list of source tags, should be specified only for `INGRESS` rule
+ source_service_accounts: # list of source service accounts, should be specified only for `INGRESS` rule, can not be specified together with `source_tags` or `target_tags`
+ - myapp@myproject-id.iam.gserviceaccount.com
+ target_tags: ['some-tag'] # list of target tags
+ target_service_accounts: # list of target service accounts, , can not be specified together with `source_tags` or `target_tags`
+ - myapp@myproject-id.iam.gserviceaccount.com
+```
+
+
+Firewall rules example yaml configuration
+
+```bash
+cat ./prod/core-network/common-rules.yaml
+# allow ingress from GCLB to all instances in the network
+lb-health-checks:
+ allow:
+ - ports: []
+ protocol: tcp
+ direction: INGRESS
+ priority: 1001
+ source_ranges:
+ - 35.191.0.0/16
+ - 130.211.0.0/22
+
+# deny all egress
+deny-all:
+ deny:
+ - ports: []
+ protocol: all
+ direction: EGRESS
+ priority: 65535
+ destination_ranges:
+ - 0.0.0.0/0
+
+cat ./dev/team-a/web-app-a.yaml
+# Myapp egress
+web-app-a-egress:
+ allow:
+ - ports: [443]
+ protocol: tcp
+ direction: EGRESS
+ destination_ranges:
+ - 192.168.0.0/24
+ target_service_accounts:
+ - myapp@myproject-id.iam.gserviceaccount.com
+# Myapp ingress
+web-app-a-ingress:
+ allow:
+ - ports: [1234]
+ protocol: tcp
+ direction: INGRESS
+ source_service_accounts:
+ - frontend-sa@myproject-id.iam.gserviceaccount.com
+ target_service_accounts:
+ - web-app-a@myproject-id.iam.gserviceaccount.com
+```
+
+
+## Variables
+
+| name | description | type | required | default |
+|---|---|:---: |:---:|:---:|
+| config_path | Path to a folder where firewall configs are stored in yaml format. Folder may include subfolders with configuration files. Files suffix must be `.yaml` | string
| ✓ | |
+| network | Name of the network this set of firewall rules applies to. | string
| ✓ | |
+| project_id | Project Id. | string
| ✓ | |
+| *log_config* | Log configuration. Possible values for `metadata` are `EXCLUDE_ALL_METADATA` and `INCLUDE_ALL_METADATA`. Set to `null` for disabling firewall logging. | object({...})
| | null
|
+
+## Outputs
+
+| name | description | sensitive |
+|---|---|:---:|
+| egress_allow_rules | Egress rules with allow blocks. | |
+| egress_deny_rules | Egress rules with allow blocks. | |
+| ingress_allow_rules | Ingress rules with allow blocks. | |
+| ingress_deny_rules | Ingress rules with deny blocks. | |
+
diff --git a/modules/net-vpc-firewall-yaml/main.tf b/modules/net-vpc-firewall-yaml/main.tf
new file mode 100644
index 00000000..e401f3b9
--- /dev/null
+++ b/modules/net-vpc-firewall-yaml/main.tf
@@ -0,0 +1,100 @@
+/**
+ * Copyright 2021 Google LLC
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+locals {
+ firewall_rules = merge(
+ [
+ for config_file in fileset("${path.root}/${var.config_path}", "**/*.yaml") :
+ try(yamldecode(file("${path.root}/${var.config_path}/${config_file}")), {})
+ ]...
+ )
+}
+
+resource "time_static" "timestamp" {
+ for_each = local.firewall_rules
+ triggers = {
+ name = md5(jsonencode(each.value))
+ }
+}
+
+resource "google_compute_firewall" "rules" {
+ for_each = local.firewall_rules
+ project = var.project_id
+ name = format(
+ "fwr-%s-%s-%s-%s",
+ var.network,
+ (try(each.value.target_service_accounts, null) != null ? "sac" : try(each.value.target_tags, null) != null ? "vpc" : "all"),
+ substr(lower(each.value.direction), 0, 1),
+ each.key
+ )
+ description = format(
+ "%s rule in network %s for %s created at %s",
+ each.value.direction,
+ var.network,
+ each.key,
+ time_static.timestamp[each.key].rfc3339
+ )
+
+ network = var.network
+ direction = each.value.direction
+ priority = try(each.value.priority, 1000)
+ disabled = try(each.value.disabled, null)
+
+ source_ranges = try(each.value.source_ranges, each.value.direction == "INGRESS" ? [] : null)
+ source_tags = try(each.value.source_tags, null)
+ source_service_accounts = try(each.value.source_service_accounts, null)
+
+ destination_ranges = try(each.value.destination_ranges, each.value.direction == "EGRESS" ? [] : null)
+ target_tags = try(each.value.target_tags, null)
+ target_service_accounts = try(each.value.target_service_accounts, null)
+
+ dynamic "allow" {
+ for_each = { for block in try(each.value.allow, []) :
+ "${block.protocol}-${join("-", block.ports)}" => {
+ ports = [for port in block.ports : tostring(port)]
+ protocol = block.protocol
+ }
+ }
+ content {
+ protocol = allow.value.protocol
+ ports = allow.value.ports
+ }
+ }
+
+ dynamic "deny" {
+ for_each = { for block in try(each.value.deny, []) :
+ "${block.protocol}-${join("-", block.ports)}" => {
+ ports = [for port in block.ports : tostring(port)]
+ protocol = block.protocol
+ }
+ }
+ content {
+ protocol = deny.value.protocol
+ ports = deny.value.ports
+ }
+ }
+
+ dynamic "log_config" {
+ for_each = var.log_config != null ? [""] : []
+ content {
+ metadata = var.log_config.metadata
+ }
+ }
+
+ lifecycle {
+ create_before_destroy = true
+ }
+}
diff --git a/modules/net-vpc-firewall-yaml/outputs.tf b/modules/net-vpc-firewall-yaml/outputs.tf
new file mode 100644
index 00000000..63c3d0c8
--- /dev/null
+++ b/modules/net-vpc-firewall-yaml/outputs.tf
@@ -0,0 +1,47 @@
+/**
+ * Copyright 2021 Google LLC
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+output "ingress_allow_rules" {
+ description = "Ingress rules with allow blocks."
+ value = [
+ for rule in google_compute_firewall.rules :
+ rule.name if rule.direction == "INGRESS" && length(rule.allow) > 0
+ ]
+}
+
+output "ingress_deny_rules" {
+ description = "Ingress rules with deny blocks."
+ value = [
+ for rule in google_compute_firewall.rules :
+ rule.name if rule.direction == "INGRESS" && length(rule.deny) > 0
+ ]
+}
+
+output "egress_allow_rules" {
+ description = "Egress rules with allow blocks."
+ value = [
+ for rule in google_compute_firewall.rules :
+ rule.name if rule.direction == "EGRESS" && length(rule.allow) > 0
+ ]
+}
+
+output "egress_deny_rules" {
+ description = "Egress rules with allow blocks."
+ value = [
+ for rule in google_compute_firewall.rules :
+ rule.name if rule.direction == "EGRESS" && length(rule.deny) > 0
+ ]
+}
diff --git a/modules/net-vpc-firewall-yaml/variables.tf b/modules/net-vpc-firewall-yaml/variables.tf
new file mode 100644
index 00000000..0d5d4da3
--- /dev/null
+++ b/modules/net-vpc-firewall-yaml/variables.tf
@@ -0,0 +1,38 @@
+/**
+ * Copyright 2021 Google LLC
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+variable "network" {
+ description = "Name of the network this set of firewall rules applies to."
+ type = string
+}
+
+variable "project_id" {
+ description = "Project Id."
+ type = string
+}
+
+variable "config_path" {
+ description = "Path to a folder where firewall configs are stored in yaml format. Folder may include subfolders with configuration files. Files suffix must be `.yaml`"
+ type = string
+}
+
+variable "log_config" {
+ description = "Log configuration. Possible values for `metadata` are `EXCLUDE_ALL_METADATA` and `INCLUDE_ALL_METADATA`. Set to `null` for disabling firewall logging."
+ type = object({
+ metadata = string
+ })
+ default = null
+}
diff --git a/modules/net-vpc-firewall-yaml/versions.tf b/modules/net-vpc-firewall-yaml/versions.tf
new file mode 100644
index 00000000..ea8877ca
--- /dev/null
+++ b/modules/net-vpc-firewall-yaml/versions.tf
@@ -0,0 +1,20 @@
+/**
+ * Copyright 2021 Google LLC
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+terraform {
+ required_version = ">= 0.13.3"
+}