Skip to content

Commit a40c3c0

Browse files
daniel-citapeabody
andauthored
fix: Directional rules after apply (#210)
Co-authored-by: Andrew Peabody <[email protected]>
1 parent cc68ba5 commit a40c3c0

File tree

13 files changed

+237
-24
lines changed

13 files changed

+237
-24
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,3 +58,4 @@ yarn.lock
5858

5959
# tf lock file
6060
.terraform.lock.hcl
61+
.terraform.lock

examples/scoped_example_with_egress_rule/README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ You may use the following gcloud commands:
2121
| Name | Description | Type | Default | Required |
2222
|------|-------------|------|---------|:--------:|
2323
| access\_level\_name | Access level name of the Access Policy. | `string` | `"terraform_members_e"` | no |
24+
| access\_level\_name\_dry\_run | Access level name of the Access Policy in Dry-run mode. | `string` | `"terraform_members_e_dry_run"` | no |
2425
| buckets\_names | Buckets Names as list of strings | `list(string)` | <pre>[<br> "bucket1-e",<br> "bucket2-e"<br>]</pre> | no |
2526
| buckets\_prefix | Bucket Prefix | `string` | `"test-bucket-e"` | no |
2627
| members | An allowed list of members (users, service accounts). The signed-in identity originating the request must be a part of one of the provided members. If not specified, a request may come from any user (logged in/not logged in, etc.). Formats: user:{emailid}, serviceAccount:{emailid} | `list(string)` | n/a | yes |

examples/scoped_example_with_egress_rule/main.tf

Lines changed: 53 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,34 @@
1414
* limitations under the License.
1515
*/
1616

17+
locals {
18+
egress_policies_dry_run = [
19+
{
20+
title = "dry-run"
21+
from = {
22+
sources = {
23+
access_levels = [module.access_level_members_dry_run.name]
24+
},
25+
identity_type = "ANY_SERVICE_ACCOUNT"
26+
}
27+
to = {
28+
resources = [
29+
"projects/${var.public_project_ids["number"]}"
30+
]
31+
operations = {
32+
for service in [
33+
"cloudresourcemanager.googleapis.com",
34+
"cloudfunctions.googleapis.com",
35+
] : service =>
36+
{
37+
methods = ["*"]
38+
}
39+
}
40+
}
41+
},
42+
]
43+
}
44+
1745
module "access_context_manager_policy" {
1846
source = "terraform-google-modules/vpc-service-controls/google"
1947
version = "~> 7.1"
@@ -36,24 +64,41 @@ module "access_level_members" {
3664
regions = var.regions
3765
}
3866

67+
module "access_level_members_dry_run" {
68+
source = "terraform-google-modules/vpc-service-controls/google//modules/access_level"
69+
version = "~> 7.1"
70+
71+
description = "Simple Example Access Level dry-run"
72+
policy = module.access_context_manager_policy.policy_id
73+
name = var.access_level_name_dry_run
74+
members = var.members
75+
regions = var.regions
76+
}
77+
78+
3979
resource "time_sleep" "wait_for_members" {
4080
create_duration = "90s"
4181
destroy_duration = "90s"
4282

43-
depends_on = [module.access_level_members]
83+
depends_on = [
84+
module.access_level_members,
85+
module.access_level_members_dry_run
86+
]
4487
}
4588

4689
module "regular_service_perimeter_1" {
4790
source = "terraform-google-modules/vpc-service-controls/google//modules/regular_service_perimeter"
4891
version = "~> 7.1"
4992

50-
5193
policy = module.access_context_manager_policy.policy_id
5294
perimeter_name = var.perimeter_name
5395

54-
description = "Perimeter shielding bigquery project"
55-
resources = [var.protected_project_ids["number"]]
56-
access_levels = [module.access_level_members.name]
96+
description = "Perimeter shielding bigquery project"
97+
resources = [var.protected_project_ids["number"]]
98+
resources_dry_run = [var.protected_project_ids["number"]]
99+
access_levels = [module.access_level_members.name]
100+
access_levels_dry_run = [module.access_level_members_dry_run.name]
101+
57102

58103
restricted_services = ["bigquery.googleapis.com", "storage.googleapis.com"]
59104

@@ -109,6 +154,9 @@ module "regular_service_perimeter_1" {
109154
},
110155
]
111156

157+
egress_policies_dry_run = distinct(tolist(local.egress_policies_dry_run))
158+
egress_policies_keys_dry_run = ["rule_one"]
159+
112160
shared_resources = {
113161
all = [var.protected_project_ids["number"]]
114162
}

examples/scoped_example_with_egress_rule/variables.tf

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,12 @@ variable "access_level_name" {
5757
default = "terraform_members_e"
5858
}
5959

60+
variable "access_level_name_dry_run" {
61+
description = "Access level name of the Access Policy in Dry-run mode."
62+
type = string
63+
default = "terraform_members_e_dry_run"
64+
}
65+
6066
variable "buckets_prefix" {
6167
description = "Bucket Prefix"
6268
type = string
@@ -74,3 +80,4 @@ variable "scopes" {
7480
type = list(string)
7581
default = []
7682
}
83+

examples/scoped_example_with_ingress_rule/README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ You may use the following gcloud commands:
2121
| Name | Description | Type | Default | Required |
2222
|------|-------------|------|---------|:--------:|
2323
| access\_level\_name | Access level name of the Access Policy. | `string` | `"terraform_members"` | no |
24+
| access\_level\_name\_dry\_run | Access level name of the Access Policy in Dry-run mode. | `string` | `"terraform_members_dry_run"` | no |
2425
| buckets\_names | Buckets Names as list of strings | `list(string)` | <pre>[<br> "bucket1",<br> "bucket2"<br>]</pre> | no |
2526
| buckets\_prefix | Bucket Prefix | `string` | `"test-bucket"` | no |
2627
| members | An allowed list of members (users, service accounts). The signed-in identity originating the request must be a part of one of the provided members. If not specified, a request may come from any user (logged in/not logged in, etc.). Formats: user:{emailid}, serviceAccount:{emailid} | `list(string)` | n/a | yes |

examples/scoped_example_with_ingress_rule/main.tf

Lines changed: 56 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,33 @@
1414
* limitations under the License.
1515
*/
1616

17+
locals {
18+
ingress_policies_dry_run = [
19+
{
20+
title = "dry-run"
21+
from = {
22+
identities = var.read_bucket_identities
23+
sources = {
24+
access_levels = [module.access_level_members_dry_run.name]
25+
},
26+
}
27+
to = {
28+
resources = [
29+
"*"
30+
]
31+
operations = {
32+
"storage.googleapis.com" = {
33+
methods = [
34+
"google.storage.objects.get",
35+
"google.storage.objects.list"
36+
]
37+
}
38+
}
39+
}
40+
}
41+
]
42+
}
43+
1744
module "access_context_manager_policy" {
1845
source = "terraform-google-modules/vpc-service-controls/google"
1946
version = "~> 7.1"
@@ -36,11 +63,25 @@ module "access_level_members" {
3663
regions = var.regions
3764
}
3865

66+
module "access_level_members_dry_run" {
67+
source = "terraform-google-modules/vpc-service-controls/google//modules/access_level"
68+
version = "~> 7.1"
69+
70+
description = "Simple Example Access Level dry-run"
71+
policy = module.access_context_manager_policy.policy_id
72+
name = var.access_level_name_dry_run
73+
members = var.members
74+
regions = var.regions
75+
}
76+
3977
resource "time_sleep" "wait_for_members" {
4078
create_duration = "90s"
4179
destroy_duration = "90s"
4280

43-
depends_on = [module.access_level_members]
81+
depends_on = [
82+
module.access_level_members,
83+
module.access_level_members_dry_run
84+
]
4485
}
4586

4687
module "regular_service_perimeter_1" {
@@ -50,21 +91,22 @@ module "regular_service_perimeter_1" {
5091
policy = module.access_context_manager_policy.policy_id
5192
perimeter_name = var.perimeter_name
5293

53-
description = "Perimeter shielding bigquery project"
54-
resources = [var.protected_project_ids["number"]]
55-
access_levels = [module.access_level_members.name]
94+
description = "Perimeter shielding bigquery project"
95+
resources = [var.protected_project_ids["number"]]
96+
resources_dry_run = [var.protected_project_ids["number"]]
97+
access_levels = [module.access_level_members.name]
98+
access_levels_dry_run = [module.access_level_members_dry_run.name]
5699

57100
restricted_services = ["bigquery.googleapis.com", "storage.googleapis.com"]
58101

59102
ingress_policies = [
60103
{
61104
title = "Allow Access from everywhere"
62105
from = {
106+
identities = var.read_bucket_identities
63107
sources = {
64108
access_levels = ["*"] # Allow Access from everywhere
65109
},
66-
identities = var.read_bucket_identities
67-
68110
}
69111
to = {
70112
resources = [
@@ -80,8 +122,6 @@ module "regular_service_perimeter_1" {
80122
}
81123
}
82124
},
83-
84-
85125
{
86126
title = "Allow Access from project"
87127
from = {
@@ -106,9 +146,12 @@ module "regular_service_perimeter_1" {
106146
}
107147
},
108148
{
109-
title = "without from source"
149+
title = "from bucket read identity"
110150
from = {
111151
identities = var.read_bucket_identities
152+
sources = {
153+
resources = ["projects/${var.public_project_ids["number"]}"]
154+
}
112155
}
113156
to = {
114157
resources = [
@@ -126,6 +169,10 @@ module "regular_service_perimeter_1" {
126169
}
127170
]
128171

172+
ingress_policies_dry_run = distinct(tolist(local.ingress_policies_dry_run))
173+
ingress_policies_keys_dry_run = ["rule_one"]
174+
175+
129176
shared_resources = {
130177
all = [var.protected_project_ids["number"]]
131178
}

examples/scoped_example_with_ingress_rule/variables.tf

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,12 @@ variable "access_level_name" {
5757
default = "terraform_members"
5858
}
5959

60+
variable "access_level_name_dry_run" {
61+
description = "Access level name of the Access Policy in Dry-run mode."
62+
type = string
63+
default = "terraform_members_dry_run"
64+
}
65+
6066
variable "read_bucket_identities" {
6167
description = "List of all identities should get read access on bucket"
6268
type = list(string)

modules/regular_service_perimeter/README.md

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -100,12 +100,16 @@ module "regular_service_perimeter_1" {
100100
| description | Description of the regular perimeter | `string` | n/a | yes |
101101
| egress\_policies | A list of all [egress policies](https://cloud.google.com/vpc-service-controls/docs/ingress-egress-rules#egress-rules-reference), each list object has a `from` and `to` value that describes egress\_from and egress\_to.<br><br>Example: `[{ from={ identities=[], identity_type="ID_TYPE" }, to={ resources=[], operations={ "SRV_NAME"={ OP_TYPE=[] }}}}]`<br><br>Valid Values:<br>`ID_TYPE` = `null` or `IDENTITY_TYPE_UNSPECIFIED` (only allow indentities from list); `ANY_IDENTITY`; `ANY_USER_ACCOUNT`; `ANY_SERVICE_ACCOUNT`<br>`SRV_NAME` = "`*`" (allow all services) or [Specific Services](https://cloud.google.com/vpc-service-controls/docs/supported-products#supported_products)<br>`OP_TYPE` = [methods](https://cloud.google.com/vpc-service-controls/docs/supported-method-restrictions) or [permissions](https://cloud.google.com/vpc-service-controls/docs/supported-method-restrictions) | <pre>list(object({<br> title = optional(string, null)<br> from = object({<br> sources = optional(object({<br> resources = optional(list(string), [])<br> access_levels = optional(list(string), [])<br> }), {}),<br> identity_type = optional(string, null)<br> identities = optional(list(string), null)<br> })<br> to = object({<br> operations = optional(map(object({<br> methods = optional(list(string), [])<br> permissions = optional(list(string), [])<br> })), {}),<br> roles = optional(list(string), null)<br> resources = optional(list(string), ["*"])<br> external_resources = optional(list(string), [])<br> })<br> }))</pre> | `[]` | no |
102102
| egress\_policies\_dry\_run | A list of all [egress policies](https://cloud.google.com/vpc-service-controls/docs/ingress-egress-rules#egress-rules-reference), each list object has a `from` and `to` value that describes egress\_from and egress\_to. Use same formatting as `egress_policies`. | <pre>list(object({<br> title = optional(string, null)<br> from = object({<br> sources = optional(object({<br> resources = optional(list(string), [])<br> access_levels = optional(list(string), [])<br> }), {}),<br> identity_type = optional(string, null)<br> identities = optional(list(string), null)<br> })<br> to = object({<br> operations = optional(map(object({<br> methods = optional(list(string), [])<br> permissions = optional(list(string), [])<br> })), {}),<br> roles = optional(list(string), null)<br> resources = optional(list(string), ["*"])<br> external_resources = optional(list(string), [])<br> })<br> }))</pre> | `[]` | no |
103+
| egress\_policies\_keys | A list of keys to use for the Terraform state. The order should correspond to var.egress\_policies and the keys must not be dynamically computed. If `null`, var.egress\_policies will be used as keys. | `list(string)` | `null` | no |
104+
| egress\_policies\_keys\_dry\_run | (Dry-run) A list of keys to use for the Terraform state. The order should correspond to var.egress\_policies\_dry\_run and the keys must not be dynamically computed. If `null`, var.egress\_policies\_dry\_run will be used as keys. | `list(string)` | `null` | no |
103105
| ingress\_policies | A list of all [ingress policies](https://cloud.google.com/vpc-service-controls/docs/ingress-egress-rules#ingress-rules-reference), each list object has a `from` and `to` value that describes ingress\_from and ingress\_to.<br><br>Example: `[{ from={ sources={ resources=[], access_levels=[] }, identities=[], identity_type="ID_TYPE" }, to={ resources=[], operations={ "SRV_NAME"={ OP_TYPE=[] }}}}]`<br><br>Valid Values:<br>`ID_TYPE` = `null` or `IDENTITY_TYPE_UNSPECIFIED` (only allow indentities from list); `ANY_IDENTITY`; `ANY_USER_ACCOUNT`; `ANY_SERVICE_ACCOUNT`<br>`SRV_NAME` = "`*`" (allow all services) or [Specific Services](https://cloud.google.com/vpc-service-controls/docs/supported-products#supported_products)<br>`OP_TYPE` = [methods](https://cloud.google.com/vpc-service-controls/docs/supported-method-restrictions) or [permissions](https://cloud.google.com/vpc-service-controls/docs/supported-method-restrictions) | <pre>list(object({<br> title = optional(string, null)<br> from = object({<br> sources = optional(object({<br> resources = optional(list(string), [])<br> access_levels = optional(list(string), [])<br> }), {}),<br> identity_type = optional(string, null)<br> identities = optional(list(string), null)<br> })<br> to = object({<br> operations = optional(map(object({<br> methods = optional(list(string), [])<br> permissions = optional(list(string), [])<br> })), {}),<br> roles = optional(list(string), null)<br> resources = optional(list(string), ["*"])<br> })<br> }))</pre> | `[]` | no |
104106
| ingress\_policies\_dry\_run | A list of all [ingress policies](https://cloud.google.com/vpc-service-controls/docs/ingress-egress-rules#ingress-rules-reference), each list object has a `from` and `to` value that describes ingress\_from and ingress\_to. Use same formatting as `ingress_policies`. | <pre>list(object({<br> title = optional(string, null)<br> from = object({<br> sources = optional(object({<br> resources = optional(list(string), [])<br> access_levels = optional(list(string), [])<br> }), {}),<br> identity_type = optional(string, null)<br> identities = optional(list(string), null)<br> })<br> to = object({<br> operations = optional(map(object({<br> methods = optional(list(string), [])<br> permissions = optional(list(string), [])<br> })), {}),<br> roles = optional(list(string), null)<br> resources = optional(list(string), ["*"])<br> })<br> }))</pre> | `[]` | no |
107+
| ingress\_policies\_keys | A list of keys to use for the Terraform state. The order should correspond to var.ingress\_policies and the keys must not be dynamically computed. If `null`, var.ingress\_policies will be used as keys. | `list(string)` | `null` | no |
108+
| ingress\_policies\_keys\_dry\_run | (Dry-run) A list of keys to use for the Terraform state. The order should correspond to var.ingress\_policies\_dry\_run and the keys must not be dynamically computed. If `null`, var.ingress\_policies\_dry\_run will be used as keys. | `list(string)` | `null` | no |
105109
| perimeter\_name | Name of the perimeter. Should be one unified string. Must only be letters, numbers and underscores | `string` | n/a | yes |
106110
| policy | Name of the parent policy | `string` | n/a | yes |
107111
| resource\_keys | A list of keys to use for the Terraform state. The order should correspond to var.resources and the keys must not be dynamically computed. If `null`, var.resources will be used as keys. | `list(string)` | `null` | no |
108-
| resource\_keys\_dry\_run | A list of keys to use for the Terraform state. The order should correspond to var.resources\_dry\_run and the keys must not be dynamically computed. If `null`, var.resources\_dry\_run will be used as keys. | `list(string)` | `null` | no |
112+
| resource\_keys\_dry\_run | (Dry-run) A list of keys to use for the Terraform state. The order should correspond to var.resources\_dry\_run and the keys must not be dynamically computed. If `null`, var.resources\_dry\_run will be used as keys. | `list(string)` | `null` | no |
109113
| resources | A list of GCP resources that are inside of the service perimeter. Currently only projects and VPC networks are allowed. | `list(string)` | `[]` | no |
110114
| resources\_dry\_run | (Dry-run) A list of GCP resources that are inside of the service perimeter. Currently only projects and VPC networks are allowed. If set, a dry-run policy will be set. | `list(string)` | `[]` | no |
111115
| restricted\_services | GCP services that are subject to the Service Perimeter restrictions. Must contain a list of services. For example, if storage.googleapis.com is specified, access to the storage buckets inside the perimeter must meet the perimeter's access restrictions. | `list(string)` | `[]` | no |

modules/regular_service_perimeter/main.tf

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
*/
1616

1717
locals {
18-
dry_run = (length(var.restricted_services_dry_run) > 0 || length(var.resources_dry_run) > 0 || length(var.access_levels_dry_run) > 0 || !contains(var.vpc_accessible_services_dry_run, "*"))
18+
dry_run = (length(var.restricted_services_dry_run) > 0 || length(var.resources_dry_run) > 0 || length(var.access_levels_dry_run) > 0 || length(var.egress_policies_dry_run) > 0 || length(var.ingress_policies_dry_run) > 0 || !contains(var.vpc_accessible_services_dry_run, "*"))
1919
}
2020

2121
resource "google_access_context_manager_service_perimeter" "regular_service_perimeter" {
@@ -80,13 +80,14 @@ resource "google_access_context_manager_service_perimeter" "regular_service_peri
8080
}
8181

8282
locals {
83+
# enforced
8384
resource_keys = var.resource_keys != null ? var.resource_keys : var.resources
8485
resources = {
8586
for rk in local.resource_keys :
8687
rk => var.resources[index(local.resource_keys, rk)]
8788
}
8889

89-
#dry-run
90+
# dry-run
9091
resource_keys_dry_run = var.resource_keys_dry_run != null ? var.resource_keys_dry_run : var.resources_dry_run
9192
resources_dry_run = {
9293
for rk in local.resource_keys_dry_run :

modules/regular_service_perimeter/variables.tf

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,31 @@ variable "resources_dry_run" {
6666
}
6767

6868
variable "resource_keys_dry_run" {
69-
description = "A list of keys to use for the Terraform state. The order should correspond to var.resources_dry_run and the keys must not be dynamically computed. If `null`, var.resources_dry_run will be used as keys."
69+
description = "(Dry-run) A list of keys to use for the Terraform state. The order should correspond to var.resources_dry_run and the keys must not be dynamically computed. If `null`, var.resources_dry_run will be used as keys."
70+
type = list(string)
71+
default = null
72+
}
73+
74+
variable "ingress_policies_keys" {
75+
description = "A list of keys to use for the Terraform state. The order should correspond to var.ingress_policies and the keys must not be dynamically computed. If `null`, var.ingress_policies will be used as keys."
76+
type = list(string)
77+
default = null
78+
}
79+
80+
variable "egress_policies_keys" {
81+
description = "A list of keys to use for the Terraform state. The order should correspond to var.egress_policies and the keys must not be dynamically computed. If `null`, var.egress_policies will be used as keys."
82+
type = list(string)
83+
default = null
84+
}
85+
86+
variable "ingress_policies_keys_dry_run" {
87+
description = "(Dry-run) A list of keys to use for the Terraform state. The order should correspond to var.ingress_policies_dry_run and the keys must not be dynamically computed. If `null`, var.ingress_policies_dry_run will be used as keys."
88+
type = list(string)
89+
default = null
90+
}
91+
92+
variable "egress_policies_keys_dry_run" {
93+
description = "(Dry-run) A list of keys to use for the Terraform state. The order should correspond to var.egress_policies_dry_run and the keys must not be dynamically computed. If `null`, var.egress_policies_dry_run will be used as keys."
7094
type = list(string)
7195
default = null
7296
}

0 commit comments

Comments
 (0)