diff --git a/modules/project/README.md b/modules/project/README.md index ee2ba18d..3753a5da 100644 --- a/modules/project/README.md +++ b/modules/project/README.md @@ -2,6 +2,23 @@ This module implements the creation and management of one GCP project including IAM, organization policies, Shared VPC host or service attachment, service API activation, and tag attachment. It also offers a convenient way to refer to managed service identities (aka robot service accounts) for APIs. +# Basic Project Creation + +```hcl +module "project" { + source = "./fabric/modules/project" + billing_account = "123456-123456-123456" + name = "myproject" + parent = "folders/1234567890" + prefix = "foo" + services = [ + "container.googleapis.com", + "stackdriver.googleapis.com" + ] +} +# tftest modules=1 resources=3 inventory=basic.yaml +``` + ## IAM Examples IAM is managed via several variables that implement different levels of control: @@ -36,7 +53,7 @@ module "project" { ] } } -# tftest modules=1 resources=4 +# tftest modules=1 resources=4 inventory=iam-authoritative.yaml ``` The `group_iam` variable uses group email addresses as keys and is a convenient way to assign roles to humans following Google's best practices. The end result is readable code that also serves as documentation. @@ -48,10 +65,6 @@ module "project" { name = "project-example" parent = "folders/1234567890" prefix = "foo" - services = [ - "container.googleapis.com", - "stackdriver.googleapis.com" - ] group_iam = { "gcp-security-admins@example.com" = [ "roles/cloudasset.owner", @@ -61,7 +74,7 @@ module "project" { ] } } -# tftest modules=1 resources=7 +# tftest modules=1 resources=5 inventory=iam-group.yaml ``` ### Additive IAM @@ -85,7 +98,22 @@ module "project" { ], } } -# tftest modules=1 resources=5 inventory=additive.yaml +# tftest modules=1 resources=5 inventory=iam-additive.yaml +``` + +### Additive IAM by members + +```hcl +module "project" { + source = "./fabric/modules/project" + name = "project-example" + iam_additive_members = { + "user:one@example.org" = ["roles/owner"] + "user:two@example.org" = ["roles/owner", "roles/editor"] + } + +} +# tftest modules=1 resources=4 inventory=iam-additive-members.yaml ``` ### Service Identities and authoritative IAM @@ -110,34 +138,27 @@ module "project" { # tftest modules=1 resources=2 ``` -## Shared VPC service +## Shared VPC The module allows managing Shared VPC status for both hosts and service projects, and includes a simple way of assigning Shared VPC roles to service identities. -### Host project - You can enable Shared VPC Host at the project level and manage project service association independently. ```hcl -module "project" { +module "host-project" { source = "./fabric/modules/project" - name = "project-example" + name = "my-host-project" shared_vpc_host_config = { enabled = true } } -# tftest modules=1 resources=2 -``` -### Service project - -```hcl -module "project" { +module "service-project" { source = "./fabric/modules/project" - name = "project-example" + name = "my-service-project" shared_vpc_service_config = { attach = true - host_project = "my-host-project" + host_project = module.host-project.project_id service_identity_iam = { "roles/compute.networkUser" = [ "cloudservices", "container-engine" @@ -151,7 +172,7 @@ module "project" { } } } -# tftest modules=1 resources=6 +# tftest modules=2 resources=8 inventory=shared-vpc.yaml ``` ## Organization policies @@ -165,10 +186,6 @@ module "project" { name = "project-example" parent = "folders/1234567890" prefix = "foo" - services = [ - "container.googleapis.com", - "stackdriver.googleapis.com" - ] org_policies = { "compute.disableGuestAttributesAccess" = { enforce = true @@ -208,7 +225,7 @@ module "project" { } } } -# tftest modules=1 resources=10 +# tftest modules=1 resources=8 inventory=org-policies.yaml ``` ### Organization policy factory @@ -220,63 +237,54 @@ Note that contraints defined via `org_policies` take precedence over those in `o The example below deploys a few organization policies split between two YAML files. ```hcl -module "folder" { - source = "./fabric/modules/folder" - parent = "organizations/1234567890" - name = "Folder name" +module "project" { + source = "./fabric/modules/project" + billing_account = "123456-123456-123456" + name = "project-example" + parent = "folders/1234567890" + prefix = "foo" org_policies_data_path = "configs/org-policies/" } -# tftest modules=1 resources=6 files=boolean,list +# tftest modules=1 resources=8 files=boolean,list inventory=org-policies.yaml ``` ```yaml # tftest-file id=boolean path=configs/org-policies/boolean.yaml +compute.disableGuestAttributesAccess: + enforce: true +constraints/compute.skipDefaultNetworkCreation: + enforce: true iam.disableServiceAccountKeyCreation: enforce: true - iam.disableServiceAccountKeyUpload: enforce: false rules: - - condition: - expression: resource.matchTagId("tagKeys/1234", "tagValues/1234") - title: condition - description: test condition - location: xxx - enforce: true + - condition: + description: test condition + expression: resource.matchTagId("tagKeys/1234", "tagValues/1234") + location: somewhere + title: condition + enforce: true ``` ```yaml # tftest-file id=list path=configs/org-policies/list.yaml -compute.vmExternalIpAccess: - deny: - all: true - -iam.allowedPolicyMemberDomains: +constraints/compute.trustedImageProjects: allow: values: - - C0xxxxxxx - - C0yyyyyyy - -compute.restrictLoadBalancerCreationForTypes: + - projects/my-project +constraints/compute.vmExternalIpAccess: deny: - values: ["in:EXTERNAL"] - rules: - - condition: - expression: resource.matchTagId("tagKeys/1234", "tagValues/1234") - title: condition - description: test condition - allow: - values: ["in:EXTERNAL"] - - condition: - expression: resource.matchTagId("tagKeys/12345", "tagValues/12345") - title: condition2 - description: test condition2 - allow: - all: true + all: true +constraints/iam.allowedPolicyMemberDomains: + allow: + values: + - C0xxxxxxx + - C0yyyyyyy ``` -## Logging Sinks (in same project) +## Logging Sinks ```hcl module "gcs" { @@ -339,49 +347,18 @@ module "project-host" { no-gce-instances = "resource.type=gce_instance" } } -# tftest modules=5 resources=14 +# tftest modules=5 resources=14 inventory=logging.yaml ``` -## Logging Sinks (in different project) - -When writing to destinations in a different project, set `unique_writer` to `true`. - -```hcl -module "gcs" { - source = "./fabric/modules/gcs" - project_id = "project-1" - name = "gcs_sink" - force_destroy = true -} - -module "project-host" { - source = "./fabric/modules/project" - name = "project-2" - billing_account = "123456-123456-123456" - parent = "folders/1234567890" - logging_sinks = { - warnings = { - destination = module.gcs.id - filter = "severity=WARNING" - unique_writer = true - type = "storage" - } - } -} -# tftest modules=2 resources=4 -``` - - ## Cloud KMS encryption keys The module offers a simple, centralized way to assign `roles/cloudkms.cryptoKeyEncrypterDecrypter` to service identities. ```hcl module "project" { - source = "./fabric/modules/project" - name = "my-project" - billing_account = "123456-123456-123456" - prefix = "foo" + source = "./fabric/modules/project" + name = "my-project" + prefix = "foo" services = [ "compute.googleapis.com", "storage.googleapis.com" @@ -448,7 +425,7 @@ module "project" { output "compute_robot" { value = module.project.service_accounts.robots.compute } -# tftest modules=1 resources=2 +# tftest modules=1 resources=2 inventory:outputs.yaml ``` diff --git a/tests/examples/variables.tf b/tests/examples/variables.tf index 76c3770d..3a5a3f75 100644 --- a/tests/examples/variables.tf +++ b/tests/examples/variables.tf @@ -19,7 +19,7 @@ variable "bucket" { } variable "billing_account_id" { - default = "billing_account_id" + default = "123456-123456-123456" } variable "kms_key" { diff --git a/tests/modules/organization/test_plan_org_policies.py b/tests/modules/organization/test_plan_org_policies.py index 1e041dbc..f5002523 100644 --- a/tests/modules/organization/test_plan_org_policies.py +++ b/tests/modules/organization/test_plan_org_policies.py @@ -12,8 +12,6 @@ # See the License for the specific language governing permissions and # limitations under the License. -import pathlib - import pytest _params = ['boolean', 'list'] diff --git a/tests/modules/project/common.tfvars b/tests/modules/project/common.tfvars new file mode 100644 index 00000000..9bd31b6f --- /dev/null +++ b/tests/modules/project/common.tfvars @@ -0,0 +1 @@ +name = "my-project" diff --git a/tests/modules/project/examples/basic.yaml b/tests/modules/project/examples/basic.yaml new file mode 100644 index 00000000..a6ae5af3 --- /dev/null +++ b/tests/modules/project/examples/basic.yaml @@ -0,0 +1,39 @@ +# Copyright 2022 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. + + +values: + module.project.google_project.project[0]: + auto_create_network: false + billing_account: 123456-123456-123456 + folder_id: '1234567890' + labels: null + name: foo-myproject + org_id: null + project_id: foo-myproject + skip_delete: false + module.project.google_project_service.project_services["container.googleapis.com"]: + disable_dependent_services: false + disable_on_destroy: false + project: foo-myproject + service: container.googleapis.com + module.project.google_project_service.project_services["stackdriver.googleapis.com"]: + disable_dependent_services: false + disable_on_destroy: false + project: foo-myproject + service: stackdriver.googleapis.com + +counts: + google_project: 1 + google_project_service: 2 diff --git a/tests/modules/project/examples/iam-additive-members.yaml b/tests/modules/project/examples/iam-additive-members.yaml new file mode 100644 index 00000000..5832e4dc --- /dev/null +++ b/tests/modules/project/examples/iam-additive-members.yaml @@ -0,0 +1,36 @@ +# Copyright 2022 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. + +values: + module.project.google_project.project[0]: + project_id: project-example + module.project.google_project_iam_member.additive["roles/editor-user:two@example.org"]: + condition: [] + member: user:two@example.org + project: project-example + role: roles/editor + module.project.google_project_iam_member.additive["roles/owner-user:one@example.org"]: + condition: [] + member: user:one@example.org + project: project-example + role: roles/owner + module.project.google_project_iam_member.additive["roles/owner-user:two@example.org"]: + condition: [] + member: user:two@example.org + project: project-example + role: roles/owner + +counts: + google_project: 1 + google_project_iam_member: 3 diff --git a/tests/modules/project/examples/iam-additive.yaml b/tests/modules/project/examples/iam-additive.yaml new file mode 100644 index 00000000..f07b0df6 --- /dev/null +++ b/tests/modules/project/examples/iam-additive.yaml @@ -0,0 +1,40 @@ +# Copyright 2022 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. + +values: + module.project.google_project.project[0]: {} + module.project.google_project_iam_member.additive["roles/owner-group:three@example.org"]: + condition: [] + member: group:three@example.org + project: project-example + role: roles/owner + module.project.google_project_iam_member.additive["roles/storage.objectAdmin-group:two@example.org"]: + condition: [] + member: group:two@example.org + project: project-example + role: roles/storage.objectAdmin + module.project.google_project_iam_member.additive["roles/viewer-group:one@example.org"]: + condition: [] + member: group:one@example.org + project: project-example + role: roles/viewer + module.project.google_project_iam_member.additive["roles/viewer-group:two@xample.org"]: + condition: [] + member: group:two@xample.org + project: project-example + role: roles/viewer + +counts: + google_project: 1 + google_project_iam_member: 4 diff --git a/tests/modules/project/examples/iam-authoritative.yaml b/tests/modules/project/examples/iam-authoritative.yaml new file mode 100644 index 00000000..f190a429 --- /dev/null +++ b/tests/modules/project/examples/iam-authoritative.yaml @@ -0,0 +1,39 @@ +# Copyright 2022 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. + +values: + module.project.google_project.project[0]: {} + module.project.google_project_iam_binding.authoritative["roles/container.hostServiceAgentUser"]: + condition: [] + members: + - serviceAccount:my_gke_service_account + project: foo-project-example + role: roles/container.hostServiceAgentUser + module.project.google_project_service.project_services["container.googleapis.com"]: + disable_dependent_services: false + disable_on_destroy: false + project: foo-project-example + service: container.googleapis.com + timeouts: null + module.project.google_project_service.project_services["stackdriver.googleapis.com"]: + disable_dependent_services: false + disable_on_destroy: false + project: foo-project-example + service: stackdriver.googleapis.com + timeouts: null + +counts: + google_project: 1 + google_project_iam_binding: 1 + google_project_service: 2 diff --git a/tests/modules/project/examples/iam-group.yaml b/tests/modules/project/examples/iam-group.yaml new file mode 100644 index 00000000..02728d01 --- /dev/null +++ b/tests/modules/project/examples/iam-group.yaml @@ -0,0 +1,44 @@ +# Copyright 2022 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. + +values: + module.project.google_project.project[0]: {} + module.project.google_project_iam_binding.authoritative["roles/cloudasset.owner"]: + condition: [] + members: + - group:gcp-security-admins@example.com + project: foo-project-example + role: roles/cloudasset.owner + module.project.google_project_iam_binding.authoritative["roles/cloudsupport.techSupportEditor"]: + condition: [] + members: + - group:gcp-security-admins@example.com + project: foo-project-example + role: roles/cloudsupport.techSupportEditor + module.project.google_project_iam_binding.authoritative["roles/iam.securityReviewer"]: + condition: [] + members: + - group:gcp-security-admins@example.com + project: foo-project-example + role: roles/iam.securityReviewer + module.project.google_project_iam_binding.authoritative["roles/logging.admin"]: + condition: [] + members: + - group:gcp-security-admins@example.com + project: foo-project-example + role: roles/logging.admin + +counts: + google_project: 1 + google_project_iam_binding: 4 diff --git a/tests/modules/project/examples/kms.yaml b/tests/modules/project/examples/kms.yaml new file mode 100644 index 00000000..b3981881 --- /dev/null +++ b/tests/modules/project/examples/kms.yaml @@ -0,0 +1,38 @@ +# Copyright 2022 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. + +values: + module.org.google_tags_tag_key.default["environment"]: + description: Environment specification. + parent: organizations/1122334455 + purpose: null + purpose_data: null + short_name: environment + module.org.google_tags_tag_value.default["environment/dev"]: + description: Managed by the Terraform organization module. + short_name: dev + module.org.google_tags_tag_value.default["environment/prod"]: + description: Managed by the Terraform organization module. + short_name: prod + module.project.google_project.project[0]: + project_id: test-project + module.project.google_tags_tag_binding.binding["env-prod"]: {} + module.project.google_tags_tag_binding.binding["foo"]: + tag_value: tagValues/12345678 + +counts: + google_project: 1 + google_tags_tag_binding: 2 + google_tags_tag_key: 1 + google_tags_tag_value: 2 diff --git a/tests/modules/project/examples/logging.yaml b/tests/modules/project/examples/logging.yaml new file mode 100644 index 00000000..9902c0ad --- /dev/null +++ b/tests/modules/project/examples/logging.yaml @@ -0,0 +1,94 @@ +# Copyright 2022 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. + +values: + module.project-host.google_bigquery_dataset_iam_member.bq-sinks-binding["info"]: + condition: [] + role: roles/bigquery.dataEditor + module.project-host.google_logging_project_exclusion.logging-exclusion["no-gce-instances"]: + description: no-gce-instances (Terraform-managed). + disabled: null + filter: resource.type=gce_instance + name: no-gce-instances + project: my-project + module.project-host.google_logging_project_sink.sink["debug"]: + description: debug (Terraform-managed). + disabled: false + exclusions: + - description: null + disabled: false + filter: logName:compute + name: no-compute + filter: severity=DEBUG + name: debug + project: my-project + unique_writer_identity: false + module.project-host.google_logging_project_sink.sink["info"]: + description: info (Terraform-managed). + disabled: false + exclusions: [] + filter: severity=INFO + name: info + project: my-project + unique_writer_identity: false + module.project-host.google_logging_project_sink.sink["notice"]: + description: notice (Terraform-managed). + disabled: false + exclusions: [] + filter: severity=NOTICE + name: notice + project: my-project + unique_writer_identity: false + module.project-host.google_logging_project_sink.sink["warnings"]: + description: warnings (Terraform-managed). + destination: storage.googleapis.com/gcs_sink + disabled: false + exclusions: [] + filter: severity=WARNING + name: warnings + project: my-project + unique_writer_identity: false + module.project-host.google_project.project[0]: + auto_create_network: false + billing_account: 123456-123456-123456 + folder_id: '1234567890' + labels: null + name: my-project + org_id: null + project_id: my-project + skip_delete: false + module.project-host.google_project_iam_member.bucket-sinks-binding["debug"]: + condition: + - title: debug bucket writer + role: roles/logging.bucketWriter + module.project-host.google_pubsub_topic_iam_member.pubsub-sinks-binding["notice"]: + condition: [] + role: roles/pubsub.publisher + module.project-host.google_storage_bucket_iam_member.gcs-sinks-binding["warnings"]: + bucket: gcs_sink + condition: [] + role: roles/storage.objectCreator + +counts: + google_bigquery_dataset: 1 + google_bigquery_dataset_iam_member: 1 + google_logging_project_bucket_config: 1 + google_logging_project_exclusion: 1 + google_logging_project_sink: 4 + google_project: 1 + google_project_iam_member: 1 + google_pubsub_topic: 1 + google_pubsub_topic_iam_member: 1 + google_storage_bucket: 1 + google_storage_bucket_iam_member: 1 diff --git a/tests/modules/project/examples/org-policies.yaml b/tests/modules/project/examples/org-policies.yaml new file mode 100644 index 00000000..38e0ba9b --- /dev/null +++ b/tests/modules/project/examples/org-policies.yaml @@ -0,0 +1,125 @@ +# Copyright 2022 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. + +values: + module.project.google_org_policy_policy.default["compute.disableGuestAttributesAccess"]: + name: projects/foo-project-example/policies/compute.disableGuestAttributesAccess + parent: projects/foo-project-example + spec: + - inherit_from_parent: null + reset: null + rules: + - allow_all: null + condition: [] + deny_all: null + enforce: 'TRUE' + values: [] + module.project.google_org_policy_policy.default["constraints/compute.skipDefaultNetworkCreation"]: + name: projects/foo-project-example/policies/constraints/compute.skipDefaultNetworkCreation + parent: projects/foo-project-example + spec: + - inherit_from_parent: null + reset: null + rules: + - allow_all: null + condition: [] + deny_all: null + enforce: 'TRUE' + values: [] + module.project.google_org_policy_policy.default["constraints/compute.trustedImageProjects"]: + name: projects/foo-project-example/policies/constraints/compute.trustedImageProjects + parent: projects/foo-project-example + spec: + - inherit_from_parent: null + reset: null + rules: + - allow_all: null + condition: [] + deny_all: null + enforce: null + values: + - allowed_values: + - projects/my-project + denied_values: null + module.project.google_org_policy_policy.default["constraints/compute.vmExternalIpAccess"]: + name: projects/foo-project-example/policies/constraints/compute.vmExternalIpAccess + parent: projects/foo-project-example + spec: + - inherit_from_parent: null + reset: null + rules: + - allow_all: null + condition: [] + deny_all: 'TRUE' + enforce: null + values: [] + module.project.google_org_policy_policy.default["constraints/iam.allowedPolicyMemberDomains"]: + name: projects/foo-project-example/policies/constraints/iam.allowedPolicyMemberDomains + parent: projects/foo-project-example + spec: + - inherit_from_parent: null + reset: null + rules: + - allow_all: null + condition: [] + deny_all: null + enforce: null + values: + - allowed_values: + - C0xxxxxxx + - C0yyyyyyy + denied_values: null + module.project.google_org_policy_policy.default["iam.disableServiceAccountKeyCreation"]: + name: projects/foo-project-example/policies/iam.disableServiceAccountKeyCreation + parent: projects/foo-project-example + spec: + - inherit_from_parent: null + reset: null + rules: + - allow_all: null + condition: [] + deny_all: null + enforce: 'TRUE' + values: [] + module.project.google_org_policy_policy.default["iam.disableServiceAccountKeyUpload"]: + name: projects/foo-project-example/policies/iam.disableServiceAccountKeyUpload + parent: projects/foo-project-example + spec: + - inherit_from_parent: null + reset: null + rules: + - allow_all: null + condition: [] + deny_all: null + enforce: 'FALSE' + values: [] + - allow_all: null + condition: + - description: test condition + expression: resource.matchTagId("tagKeys/1234", "tagValues/1234") + location: somewhere + title: condition + deny_all: null + enforce: 'TRUE' + values: [] + module.project.google_project.project[0]: + billing_account: 123456-123456-123456 + folder_id: '1234567890' + name: foo-project-example + org_id: null + project_id: foo-project-example + +counts: + google_org_policy_policy: 7 + google_project: 1 diff --git a/tests/modules/project/examples/outputs.yaml b/tests/modules/project/examples/outputs.yaml new file mode 100644 index 00000000..33989662 --- /dev/null +++ b/tests/modules/project/examples/outputs.yaml @@ -0,0 +1,27 @@ +# Copyright 2022 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. + +values: + module.project.google_project.project[0]: + project_id: project-example + module.project.google_project_service.project_services["compute.googleapis.com"]: + project: project-example + service: compute.googleapis.com + +counts: + google_project: 1 + google_project_service: 1 + +outputs: + compute_robot: __missing__ diff --git a/tests/modules/project/examples/shared-vpc.yaml b/tests/modules/project/examples/shared-vpc.yaml new file mode 100644 index 00000000..b03f220a --- /dev/null +++ b/tests/modules/project/examples/shared-vpc.yaml @@ -0,0 +1,46 @@ +# Copyright 2022 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. + +values: + module.host-project.google_compute_shared_vpc_host_project.shared_vpc_host[0]: + project: my-host-project + module.host-project.google_project.project[0]: + project_id: my-host-project + module.service-project.google_compute_shared_vpc_service_project.shared_vpc_service[0]: + host_project: my-host-project + service_project: my-service-project + module.service-project.google_project.project[0]: + project_id: my-service-project + module.service-project.google_project_iam_member.shared_vpc_host_robots["roles/compute.networkUser:cloudservices"]: + condition: [] + project: my-host-project + role: roles/compute.networkUser + module.service-project.google_project_iam_member.shared_vpc_host_robots["roles/compute.networkUser:container-engine"]: + condition: [] + project: my-host-project + role: roles/compute.networkUser + module.service-project.google_project_iam_member.shared_vpc_host_robots["roles/container.hostServiceAgentUser:container-engine"]: + condition: [] + project: my-host-project + role: roles/container.hostServiceAgentUser + module.service-project.google_project_iam_member.shared_vpc_host_robots["roles/vpcaccess.user:cloudrun"]: + condition: [] + project: my-host-project + role: roles/vpcaccess.user + +counts: + google_compute_shared_vpc_host_project: 1 + google_compute_shared_vpc_service_project: 1 + google_project: 2 + google_project_iam_member: 4 diff --git a/tests/modules/project/fixture/main.tf b/tests/modules/project/fixture/main.tf deleted file mode 100644 index 08cf49dc..00000000 --- a/tests/modules/project/fixture/main.tf +++ /dev/null @@ -1,65 +0,0 @@ -/** - * Copyright 2022 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. - */ - -module "test" { - source = "../../../../modules/project" - name = var.name - billing_account = var.billing_account - auto_create_network = var.auto_create_network - custom_roles = var.custom_roles - iam = var.iam - iam_additive = var.iam_additive - iam_additive_members = var.iam_additive_members - labels = var.labels - lien_reason = var.lien_reason - org_policies = var.org_policies - org_policies_data_path = var.org_policies_data_path - oslogin = var.oslogin - oslogin_admins = var.oslogin_admins - oslogin_users = var.oslogin_users - parent = var.parent - prefix = var.prefix - service_encryption_key_ids = var.service_encryption_key_ids - services = var.services - logging_sinks = var.logging_sinks - logging_exclusions = var.logging_exclusions - shared_vpc_host_config = var.shared_vpc_host_config -} - -module "test-svpc-service" { - source = "../../../../modules/project" - count = var._test_service_project ? 1 : 0 - name = "test-svc" - billing_account = var.billing_account - auto_create_network = false - parent = var.parent - services = var.services - shared_vpc_service_config = { - attach = true - host_project = module.test.project_id - service_identity_iam = { - "roles/compute.networkUser" = [ - "cloudservices", "container-engine" - ] - "roles/vpcaccess.user" = [ - "cloudrun" - ] - "roles/container.hostServiceAgentUser" = [ - "container-engine" - ] - } - } -} diff --git a/tests/modules/project/fixture/test.logging-sinks.tfvars b/tests/modules/project/fixture/test.logging-sinks.tfvars deleted file mode 100644 index 5c79cfb5..00000000 --- a/tests/modules/project/fixture/test.logging-sinks.tfvars +++ /dev/null @@ -1,29 +0,0 @@ -logging_sinks = { - warning = { - destination = "mybucket" - type = "storage" - filter = "severity=WARNING" - } - info = { - destination = "projects/myproject/datasets/mydataset" - type = "bigquery" - filter = "severity=INFO" - disabled = true - } - notice = { - destination = "projects/myproject/topics/mytopic" - type = "pubsub" - filter = "severity=NOTICE" - unique_writer = true - } - debug = { - destination = "projects/myproject/locations/global/buckets/mybucket" - type = "logging" - filter = "severity=DEBUG" - exclusions = { - no-compute = "logName:compute" - no-container = "logName:container" - } - unique_writer = true - } -} diff --git a/tests/modules/project/fixture/variables.tf b/tests/modules/project/fixture/variables.tf deleted file mode 100644 index 4c3474f0..00000000 --- a/tests/modules/project/fixture/variables.tf +++ /dev/null @@ -1,134 +0,0 @@ -/** - * Copyright 2022 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 "_test_service_project" { - type = bool - default = false -} - -variable "name" { - type = string - default = "my-project" -} - -variable "billing_account" { - type = string - default = "12345-12345-12345" -} - -variable "auto_create_network" { - type = bool - default = false -} - -variable "custom_roles" { - type = map(list(string)) - default = {} -} - -variable "iam" { - type = map(list(string)) - default = {} -} - -variable "iam_additive" { - type = map(list(string)) - default = {} -} - -variable "iam_additive_members" { - type = map(list(string)) - default = {} -} - -variable "labels" { - type = map(string) - default = {} -} - -variable "lien_reason" { - type = string - default = "" -} - -variable "org_policies" { - type = any - default = {} -} - -variable "org_policies_data_path" { - type = any - default = null -} - -variable "oslogin" { - type = bool - default = false -} - -variable "oslogin_admins" { - type = list(string) - default = [] -} - -variable "oslogin_users" { - type = list(string) - default = [] -} - -variable "parent" { - type = string - default = null -} - -variable "prefix" { - type = string - default = null -} - -variable "service_encryption_key_ids" { - type = map(list(string)) - default = {} -} - -variable "services" { - type = list(string) - default = [] -} - -variable "logging_sinks" { - type = any - default = {} -} - -variable "logging_exclusions" { - type = map(string) - default = {} -} - -variable "shared_vpc_host_config" { - type = object({ - enabled = bool - service_projects = list(string) - }) - default = { - enabled = true - service_projects = [ - "my-service-project-1", - "my-service-project-2" - ] - } -} diff --git a/tests/modules/project/no_parent.tfvars b/tests/modules/project/no_parent.tfvars new file mode 100644 index 00000000..8b954bde --- /dev/null +++ b/tests/modules/project/no_parent.tfvars @@ -0,0 +1 @@ +parent = null diff --git a/tests/modules/project/no_parent.yaml b/tests/modules/project/no_parent.yaml new file mode 100644 index 00000000..57f2fbd4 --- /dev/null +++ b/tests/modules/project/no_parent.yaml @@ -0,0 +1,22 @@ +# Copyright 2022 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. + +values: + google_project.project[0]: + project_id: my-project + folder_id: null + org_id: null + +counts: + google_project: 1 diff --git a/tests/modules/project/no_prefix.tfvars b/tests/modules/project/no_prefix.tfvars new file mode 100644 index 00000000..3d1b7ab3 --- /dev/null +++ b/tests/modules/project/no_prefix.tfvars @@ -0,0 +1 @@ +prefix = null diff --git a/tests/modules/project/no_prefix.yaml b/tests/modules/project/no_prefix.yaml new file mode 100644 index 00000000..6322ca9c --- /dev/null +++ b/tests/modules/project/no_prefix.yaml @@ -0,0 +1,20 @@ +# Copyright 2022 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. + +values: + google_project.project[0]: + project_id: my-project + +counts: + google_project: 1 diff --git a/tests/modules/project/fixture/test.orgpolicies-boolean.tfvars b/tests/modules/project/org_policies_boolean.tfvars similarity index 100% rename from tests/modules/project/fixture/test.orgpolicies-boolean.tfvars rename to tests/modules/project/org_policies_boolean.tfvars diff --git a/tests/modules/project/org_policies_boolean.yaml b/tests/modules/project/org_policies_boolean.yaml new file mode 100644 index 00000000..44cba34d --- /dev/null +++ b/tests/modules/project/org_policies_boolean.yaml @@ -0,0 +1,53 @@ +# Copyright 2022 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. + +values: + google_org_policy_policy.default["iam.disableServiceAccountKeyCreation"]: + name: projects/my-project/policies/iam.disableServiceAccountKeyCreation + parent: projects/my-project + spec: + - inherit_from_parent: null + reset: null + rules: + - allow_all: null + condition: [] + deny_all: null + enforce: 'TRUE' + values: [] + timeouts: null + google_org_policy_policy.default["iam.disableServiceAccountKeyUpload"]: + name: projects/my-project/policies/iam.disableServiceAccountKeyUpload + parent: projects/my-project + spec: + - inherit_from_parent: null + reset: null + rules: + - allow_all: null + condition: [] + deny_all: null + enforce: 'FALSE' + values: [] + - allow_all: null + condition: + - description: test condition + expression: resource.matchTagId(aa, bb) + location: xxx + title: condition + deny_all: null + enforce: 'TRUE' + values: [] + timeouts: null + +counts: + google_org_policy_policy: 2 diff --git a/tests/modules/project/fixture/test.orgpolicies-list.tfvars b/tests/modules/project/org_policies_list.tfvars similarity index 96% rename from tests/modules/project/fixture/test.orgpolicies-list.tfvars rename to tests/modules/project/org_policies_list.tfvars index 73807173..f9de8dba 100644 --- a/tests/modules/project/fixture/test.orgpolicies-list.tfvars +++ b/tests/modules/project/org_policies_list.tfvars @@ -3,6 +3,7 @@ org_policies = { deny = { all = true } } "iam.allowedPolicyMemberDomains" = { + inherit_from_parent = true allow = { values = ["C0xxxxxxx", "C0yyyyyyy"] } diff --git a/tests/modules/project/org_policies_list.yaml b/tests/modules/project/org_policies_list.yaml new file mode 100644 index 00000000..ab556a10 --- /dev/null +++ b/tests/modules/project/org_policies_list.yaml @@ -0,0 +1,85 @@ +# Copyright 2022 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. + +values: + google_org_policy_policy.default["compute.restrictLoadBalancerCreationForTypes"]: + name: projects/my-project/policies/compute.restrictLoadBalancerCreationForTypes + parent: projects/my-project + spec: + - inherit_from_parent: null + reset: null + rules: + - allow_all: null + condition: [] + deny_all: null + enforce: null + values: + - allowed_values: null + denied_values: + - in:EXTERNAL + - allow_all: null + condition: + - description: test condition + expression: resource.matchTagId(aa, bb) + location: xxx + title: condition + deny_all: null + enforce: null + values: + - allowed_values: + - EXTERNAL_1 + denied_values: null + - allow_all: 'TRUE' + condition: + - description: test condition2 + expression: resource.matchTagId(cc, dd) + location: xxx + title: condition2 + deny_all: null + enforce: null + values: [] + timeouts: null + google_org_policy_policy.default["compute.vmExternalIpAccess"]: + name: projects/my-project/policies/compute.vmExternalIpAccess + parent: projects/my-project + spec: + - inherit_from_parent: null + reset: null + rules: + - allow_all: null + condition: [] + deny_all: 'TRUE' + enforce: null + values: [] + timeouts: null + google_org_policy_policy.default["iam.allowedPolicyMemberDomains"]: + name: projects/my-project/policies/iam.allowedPolicyMemberDomains + parent: projects/my-project + spec: + - inherit_from_parent: true + reset: null + rules: + - allow_all: null + condition: [] + deny_all: null + enforce: null + values: + - allowed_values: + - C0xxxxxxx + - C0yyyyyyy + denied_values: null + timeouts: null + +counts: + google_org_policy_policy: 3 diff --git a/tests/modules/project/parent_folder.tfvars b/tests/modules/project/parent_folder.tfvars new file mode 100644 index 00000000..e7ce6acd --- /dev/null +++ b/tests/modules/project/parent_folder.tfvars @@ -0,0 +1 @@ +parent = "folders/12345678" diff --git a/tests/modules/project/parent_folder.yaml b/tests/modules/project/parent_folder.yaml new file mode 100644 index 00000000..684f94d8 --- /dev/null +++ b/tests/modules/project/parent_folder.yaml @@ -0,0 +1,22 @@ +# Copyright 2022 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. + +values: + google_project.project[0]: + project_id: my-project + folder_id: '12345678' + org_id: null + +counts: + google_project: 1 diff --git a/tests/modules/project/parent_org.tfvars b/tests/modules/project/parent_org.tfvars new file mode 100644 index 00000000..86ce1eec --- /dev/null +++ b/tests/modules/project/parent_org.tfvars @@ -0,0 +1 @@ +parent = "organizations/12345678" diff --git a/tests/modules/project/parent_org.yaml b/tests/modules/project/parent_org.yaml new file mode 100644 index 00000000..ded3f6f3 --- /dev/null +++ b/tests/modules/project/parent_org.yaml @@ -0,0 +1,22 @@ +# Copyright 2022 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. + +values: + google_project.project[0]: + project_id: my-project + folder_id: null + org_id: '12345678' + +counts: + google_project: 1 diff --git a/tests/modules/project/prefix.tfvars b/tests/modules/project/prefix.tfvars new file mode 100644 index 00000000..0031d561 --- /dev/null +++ b/tests/modules/project/prefix.tfvars @@ -0,0 +1 @@ +prefix = "foo" diff --git a/tests/modules/project/prefix.yaml b/tests/modules/project/prefix.yaml new file mode 100644 index 00000000..e5126e20 --- /dev/null +++ b/tests/modules/project/prefix.yaml @@ -0,0 +1,20 @@ +# Copyright 2022 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. + +values: + google_project.project[0]: + project_id: foo-my-project + +counts: + google_project: 1 diff --git a/tests/modules/project/service_encryption_keys.tfvars b/tests/modules/project/service_encryption_keys.tfvars new file mode 100644 index 00000000..f0dedc21 --- /dev/null +++ b/tests/modules/project/service_encryption_keys.tfvars @@ -0,0 +1,4 @@ +service_encryption_key_ids = { + compute = ["key1"], + storage = ["key1", "key2"] +} diff --git a/tests/modules/project/service_encryption_keys.yaml b/tests/modules/project/service_encryption_keys.yaml new file mode 100644 index 00000000..8e2bd823 --- /dev/null +++ b/tests/modules/project/service_encryption_keys.yaml @@ -0,0 +1,44 @@ +# Copyright 2022 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. + +values: + google_kms_crypto_key_iam_member.service_identity_cmek["compute.key1"]: + condition: [] + crypto_key_id: key1 + role: roles/cloudkms.cryptoKeyEncrypterDecrypter + google_kms_crypto_key_iam_member.service_identity_cmek["storage.key1"]: + condition: [] + crypto_key_id: key1 + role: roles/cloudkms.cryptoKeyEncrypterDecrypter + google_kms_crypto_key_iam_member.service_identity_cmek["storage.key2"]: + condition: [] + crypto_key_id: key2 + role: roles/cloudkms.cryptoKeyEncrypterDecrypter + google_project.project[0]: + auto_create_network: false + billing_account: null + folder_id: null + labels: null + name: my-project + org_id: null + project_id: my-project + skip_delete: false + timeouts: null + +counts: + google_kms_crypto_key_iam_member: 3 + google_project: 1 + +outputs: + name: my-project diff --git a/tests/modules/project/test_iam.py b/tests/modules/project/test_iam.py deleted file mode 100644 index 16581baa..00000000 --- a/tests/modules/project/test_iam.py +++ /dev/null @@ -1,58 +0,0 @@ -# Copyright 2022 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. - -def test_iam(plan_runner): - "Test IAM bindings." - iam = ( - '{"roles/owner" = ["user:one@example.org"],' - '"roles/viewer" = ["user:two@example.org", "user:three@example.org"]}' - ) - _, resources = plan_runner(iam=iam) - roles = dict((r['values']['role'], r['values']['members']) - for r in resources if r['type'] == 'google_project_iam_binding') - assert roles == { - 'roles/owner': ['user:one@example.org'], - 'roles/viewer': ['user:three@example.org', 'user:two@example.org']} - - -def test_iam_additive(plan_runner): - "Test IAM additive bindings." - iam = ( - '{"roles/owner" = ["user:one@example.org"],' - '"roles/viewer" = ["user:two@example.org", "user:three@example.org"]}' - ) - _, resources = plan_runner(iam_additive=iam) - roles = set((r['values']['role'], r['values']['member']) - for r in resources if r['type'] == 'google_project_iam_member') - assert roles == set([ - ('roles/owner', 'user:one@example.org'), - ('roles/viewer', 'user:three@example.org'), - ('roles/viewer', 'user:two@example.org') - ]) - - -def test_iam_additive_members(plan_runner): - "Test IAM additive members." - iam = ( - '{"user:one@example.org" = ["roles/owner"],' - '"user:two@example.org" = ["roles/owner", "roles/editor"]}' - ) - _, resources = plan_runner(iam_additive_members=iam) - roles = set((r['values']['role'], r['values']['member']) - for r in resources if r['type'] == 'google_project_iam_member') - assert roles == set([ - ('roles/owner', 'user:one@example.org'), - ('roles/owner', 'user:two@example.org'), - ('roles/editor', 'user:two@example.org') - ]) diff --git a/tests/modules/project/test_plan.py b/tests/modules/project/test_plan.py index 8d1bd538..50d50b3c 100644 --- a/tests/modules/project/test_plan.py +++ b/tests/modules/project/test_plan.py @@ -12,65 +12,17 @@ # See the License for the specific language governing permissions and # limitations under the License. -def test_prefix(plan_runner): - "Test project id prefix." - _, resources = plan_runner() - assert len(resources) == 4 - [project_resource] = [r for r in resources if r['address'] == 'module.test.google_project.project[0]'] - assert project_resource['values']['name'] == 'my-project' - _, resources = plan_runner(prefix='foo') - assert len(resources) == 4 - [project_resource] = [r for r in resources if r['address'] == 'module.test.google_project.project[0]'] - assert project_resource['values']['name'] == 'foo-my-project' - - -def test_parent(plan_runner): - "Test project parent." - _, resources = plan_runner(parent='folders/12345678') - assert len(resources) == 4 - [project_resource] = [r for r in resources if r['address'] == 'module.test.google_project.project[0]'] - assert project_resource['values']['folder_id'] == '12345678' - assert project_resource['values'].get('org_id') == None - _, resources = plan_runner(parent='organizations/12345678') - assert len(resources) == 4 - [project_resource] = [r for r in resources if r['address'] == 'module.test.google_project.project[0]'] - assert project_resource['values']['org_id'] == '12345678' - assert project_resource['values'].get('folder_id') == None - - -def test_no_parent(plan_runner): - "Test null project parent." - _, resources = plan_runner() - assert len(resources) == 4 - [project_resource] = [r for r in resources if r['address'] == 'module.test.google_project.project[0]'] - assert project_resource['values'].get('folder_id') == None - assert project_resource['values'].get('org_id') == None - - -def test_service_encryption_keys(plan_runner): - "Test service encryption keys with no dependencies." - _, resources = plan_runner(service_encryption_key_ids=( - '{compute=["key1"], storage=["key1", "key2"]}' - )) - key_bindings = [ - r['index'] for r in resources - if r['type'] == 'google_kms_crypto_key_iam_member' - ] - assert len(key_bindings), 3 - assert key_bindings == ['compute.key1', 'storage.key1', 'storage.key2'] - - -def test_service_encryption_key_dependencies(plan_runner): - "Test service encryption keys with dependencies." - _, resources = plan_runner(service_encryption_key_ids=( - '{compute=["key1"], dataflow=["key1", "key2"]}' - )) - key_bindings = [ - r['index'] for r in resources - if r['type'] == 'google_kms_crypto_key_iam_member' - ] - assert len(key_bindings), 3 - # compute.key1 cannot repeat or we'll get a duplicate key error in for_each - assert key_bindings == [ - 'compute.key1', 'compute.key2', 'dataflow.key1', 'dataflow.key2' - ] +# def test_service_encryption_key_dependencies(plan_runner): +# "Test service encryption keys with dependencies." +# _, resources = plan_runner(service_encryption_key_ids=( +# '{compute=["key1"], dataflow=["key1", "key2"]}')) +# key_bindings = [ +# r['index'] +# for r in resources +# if r['type'] == 'google_kms_crypto_key_iam_member' +# ] +# assert len(key_bindings), 3 +# # compute.key1 cannot repeat or we'll get a duplicate key error in for_each +# assert key_bindings == [ +# 'compute.key1', 'compute.key2', 'dataflow.key1', 'dataflow.key2' +# ] diff --git a/tests/modules/project/test_plan_logging.py b/tests/modules/project/test_plan_logging.py deleted file mode 100644 index 59c9179b..00000000 --- a/tests/modules/project/test_plan_logging.py +++ /dev/null @@ -1,126 +0,0 @@ -# Copyright 2022 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. - -from collections import Counter - - -def test_sinks(plan_runner): - "Test folder-level sinks." - tfvars = 'test.logging-sinks.tfvars' - _, resources = plan_runner(tf_var_file=tfvars) - assert len(resources) == 12 - - resource_types = Counter([r["type"] for r in resources]) - assert resource_types == { - "google_logging_project_sink": 4, - "google_bigquery_dataset_iam_member": 1, - "google_project": 1, - "google_project_iam_member": 1, - "google_pubsub_topic_iam_member": 1, - "google_storage_bucket_iam_member": 1, - "google_compute_shared_vpc_host_project": 1, - "google_compute_shared_vpc_service_project": 2 - } - - sinks = [r for r in resources if r["type"] == "google_logging_project_sink"] - assert sorted([r["index"] for r in sinks]) == [ - "debug", - "info", - "notice", - "warning", - ] - values = [( - r["index"], - r["values"]["filter"], - r["values"]["destination"], - r["values"]["unique_writer_identity"], - ) for r in sinks] - assert sorted(values) == [ - ( - "debug", - "severity=DEBUG", - "logging.googleapis.com/projects/myproject/locations/global/buckets/mybucket", - True, - ), - ( - "info", - "severity=INFO", - "bigquery.googleapis.com/projects/myproject/datasets/mydataset", - False, - ), - ( - "notice", - "severity=NOTICE", - "pubsub.googleapis.com/projects/myproject/topics/mytopic", - True, - ), - ("warning", "severity=WARNING", "storage.googleapis.com/mybucket", False), - ] - - bindings = [r for r in resources if "member" in r["type"]] - values = [(r["index"], r["type"], r["values"]["role"]) for r in bindings] - assert sorted(values) == [ - ("debug", "google_project_iam_member", "roles/logging.bucketWriter"), - ("info", "google_bigquery_dataset_iam_member", - "roles/bigquery.dataEditor"), - ("notice", "google_pubsub_topic_iam_member", "roles/pubsub.publisher"), - ("warning", "google_storage_bucket_iam_member", - "roles/storage.objectCreator"), - ] - - exclusions = [(r["index"], r["values"]["exclusions"]) for r in sinks] - assert sorted(exclusions) == [ - ( - "debug", - [ - { - "description": None, - "disabled": False, - "filter": "logName:compute", - "name": "no-compute", - }, - { - "description": None, - "disabled": False, - "filter": "logName:container", - "name": "no-container", - }, - ], - ), - ("info", []), - ("notice", []), - ("warning", []), - ] - - -def test_exclusions(plan_runner): - "Test folder-level logging exclusions." - logging_exclusions = ("{" - 'exclusion1 = "resource.type=gce_instance", ' - 'exclusion2 = "severity=NOTICE", ' - "}") - _, resources = plan_runner(logging_exclusions=logging_exclusions) - assert len(resources) == 6 - exclusions = [ - r for r in resources if r["type"] == "google_logging_project_exclusion" - ] - assert sorted([r["index"] for r in exclusions]) == [ - "exclusion1", - "exclusion2", - ] - values = [(r["index"], r["values"]["filter"]) for r in exclusions] - assert sorted(values) == [ - ("exclusion1", "resource.type=gce_instance"), - ("exclusion2", "severity=NOTICE"), - ] diff --git a/tests/modules/project/test_plan_org_policies.py b/tests/modules/project/test_plan_org_policies.py index 8463761e..fef2a8aa 100644 --- a/tests/modules/project/test_plan_org_policies.py +++ b/tests/modules/project/test_plan_org_policies.py @@ -12,33 +12,18 @@ # See the License for the specific language governing permissions and # limitations under the License. -from .validate_policies import validate_policy_boolean, validate_policy_list +import pytest + +_params = ['boolean', 'list'] -def test_policy_boolean(plan_runner): - "Test boolean org policy." - tfvars = 'test.orgpolicies-boolean.tfvars' - _, resources = plan_runner(tf_var_file=tfvars) - validate_policy_boolean(resources) - - -def test_policy_list(plan_runner): - "Test list org policy." - tfvars = 'test.orgpolicies-list.tfvars' - _, resources = plan_runner(tf_var_file=tfvars) - validate_policy_list(resources) - - -def test_factory_policy_boolean(plan_runner, tfvars_to_yaml, tmp_path): +@pytest.mark.parametrize('policy_type', _params) +def test_policy_factory(plan_summary, tfvars_to_yaml, tmp_path, policy_type): dest = tmp_path / 'policies.yaml' - tfvars_to_yaml('fixture/test.orgpolicies-boolean.tfvars', dest, - 'org_policies') - _, resources = plan_runner(org_policies_data_path=f'"{tmp_path}"') - validate_policy_boolean(resources) - - -def test_factory_policy_list(plan_runner, tfvars_to_yaml, tmp_path): - dest = tmp_path / 'policies.yaml' - tfvars_to_yaml('fixture/test.orgpolicies-list.tfvars', dest, 'org_policies') - _, resources = plan_runner(org_policies_data_path=f'"{tmp_path}"') - validate_policy_list(resources) + tfvars_to_yaml(f'org_policies_{policy_type}.tfvars', dest, 'org_policies') + tfvars_plan = plan_summary( + 'modules/project', + tf_var_files=['common.tfvars', f'org_policies_{policy_type}.tfvars']) + yaml_plan = plan_summary('modules/project', tf_var_files=['common.tfvars'], + org_policies_data_path=f'{tmp_path}') + assert tfvars_plan.values == yaml_plan.values diff --git a/tests/modules/project/test_plan_svpc.py b/tests/modules/project/test_plan_svpc.py deleted file mode 100644 index bd22131d..00000000 --- a/tests/modules/project/test_plan_svpc.py +++ /dev/null @@ -1,26 +0,0 @@ -# Copyright 2022 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. - -import os - -def test_svpc(_plan_runner): - "Test Shared VPC service project attachment." - fixture_path = os.path.join(os.path.dirname(__file__), 'fixture') - plan = _plan_runner(fixture_path=fixture_path, _test_service_project='true') - modules = [m for m in plan.root_module['child_modules']] - resources = [r for r in modules[0]['resources'] if r['address'] == 'module.test.google_compute_shared_vpc_host_project.shared_vpc_host[0]'] - assert len(resources) == 1 - print(modules[1]['resources']) - resources = [r for r in modules[1]['resources'] if r['address'] == 'module.test-svpc-service[0].google_compute_shared_vpc_service_project.shared_vpc_service[0]'] - assert len(resources) == 1 diff --git a/tests/modules/project/tftest.yaml b/tests/modules/project/tftest.yaml new file mode 100644 index 00000000..2fda31b7 --- /dev/null +++ b/tests/modules/project/tftest.yaml @@ -0,0 +1,28 @@ +# Copyright 2022 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. + +module: modules/project + +common_tfvars: + - common.tfvars + +tests: + prefix: + no_prefix: + parent_folder: + parent_org: + no_parent: + service_encryption_keys: + org_policies_list: + org_policies_boolean: diff --git a/tests/modules/project/validate_policies.py b/tests/modules/project/validate_policies.py deleted file mode 100644 index 0fd03837..00000000 --- a/tests/modules/project/validate_policies.py +++ /dev/null @@ -1,160 +0,0 @@ -# Copyright 2022 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. - - -def validate_policy_boolean(resources): - assert len(resources) == 6 - policies = [r for r in resources if r['type'] == 'google_org_policy_policy'] - assert len(policies) == 2 - assert all(x['values']['parent'] == 'projects/my-project' for x in policies) - - p1 = [ - r['values']['spec'][0] - for r in policies - if r['index'] == 'iam.disableServiceAccountKeyCreation' - ][0] - - assert p1['inherit_from_parent'] is None - assert p1['reset'] is None - assert p1['rules'] == [{ - 'allow_all': None, - 'condition': [], - 'deny_all': None, - 'enforce': 'TRUE', - 'values': [] - }] - - p2 = [ - r['values']['spec'][0] - for r in policies - if r['index'] == 'iam.disableServiceAccountKeyUpload' - ][0] - - assert p2['inherit_from_parent'] is None - assert p2['reset'] is None - assert len(p2['rules']) == 2 - assert p2['rules'][0] == { - 'allow_all': None, - 'condition': [], - 'deny_all': None, - 'enforce': 'FALSE', - 'values': [] - } - assert p2['rules'][1] == { - 'allow_all': None, - 'condition': [{ - 'description': 'test condition', - 'expression': 'resource.matchTagId(aa, bb)', - 'location': 'xxx', - 'title': 'condition' - }], - 'deny_all': None, - 'enforce': 'TRUE', - 'values': [] - } - - -def validate_policy_list(resources): - assert len(resources) == 7 - - policies = [r for r in resources if r['type'] == 'google_org_policy_policy'] - assert len(policies) == 3 - assert all(x['values']['parent'] == 'projects/my-project' for x in policies) - - p1 = [ - r['values']['spec'][0] - for r in policies - if r['index'] == 'compute.vmExternalIpAccess' - ][0] - assert p1['inherit_from_parent'] is None - assert p1['reset'] is None - assert p1['rules'] == [{ - 'allow_all': None, - 'condition': [], - 'deny_all': 'TRUE', - 'enforce': None, - 'values': [] - }] - - p2 = [ - r['values']['spec'][0] - for r in policies - if r['index'] == 'iam.allowedPolicyMemberDomains' - ][0] - assert p2['inherit_from_parent'] is None - assert p2['reset'] is None - assert p2['rules'] == [{ - 'allow_all': - None, - 'condition': [], - 'deny_all': - None, - 'enforce': - None, - 'values': [{ - 'allowed_values': [ - 'C0xxxxxxx', - 'C0yyyyyyy', - ], - 'denied_values': None - }] - }] - - p3 = [ - r['values']['spec'][0] - for r in policies - if r['index'] == 'compute.restrictLoadBalancerCreationForTypes' - ][0] - assert p3['inherit_from_parent'] is None - assert p3['reset'] is None - assert len(p3['rules']) == 3 - assert p3['rules'][0] == { - 'allow_all': None, - 'condition': [], - 'deny_all': None, - 'enforce': None, - 'values': [{ - 'allowed_values': None, - 'denied_values': ['in:EXTERNAL'] - }] - } - - assert p3['rules'][1] == { - 'allow_all': None, - 'condition': [{ - 'description': 'test condition', - 'expression': 'resource.matchTagId(aa, bb)', - 'location': 'xxx', - 'title': 'condition' - }], - 'deny_all': None, - 'enforce': None, - 'values': [{ - 'allowed_values': ['EXTERNAL_1'], - 'denied_values': None - }] - } - - assert p3['rules'][2] == { - 'allow_all': 'TRUE', - 'condition': [{ - 'description': 'test condition2', - 'expression': 'resource.matchTagId(cc, dd)', - 'location': 'xxx', - 'title': 'condition2' - }], - 'deny_all': None, - 'enforce': None, - 'values': [] - }