├── .dockerignore ├── .github ├── conventional-commit-lint.yaml ├── release-please.yml ├── renovate.json ├── trusted-contribution.yml └── workflows │ ├── lint.yaml │ └── stale.yml ├── .gitignore ├── .kitchen.yml ├── CHANGELOG.md ├── CODEOWNERS ├── CONTRIBUTING.md ├── LICENSE ├── Makefile ├── README.md ├── build ├── int.cloudbuild.yaml └── lint.cloudbuild.yaml ├── docs ├── upgrading_to_v4.0.md └── upgrading_to_v6.0.md ├── examples ├── access_level_vpc_ip │ ├── README.md │ ├── main.tf │ ├── outputs.tf │ └── variables.tf ├── automatic_folder │ ├── .gitignore │ ├── README.md │ ├── backend.tf.sample │ ├── diagram.png │ ├── main.py │ ├── main.tf │ ├── outputs.tf │ ├── variables.tf │ └── watcher.tf ├── bq-exfil-demo │ ├── .gitignore │ ├── README.md │ ├── assets │ │ └── arch.png │ ├── fixtures │ │ ├── cars.csv │ │ └── schema.json │ ├── main.tf │ ├── org.tf │ ├── outputs.tf │ ├── projects.tf │ ├── storage.tf │ ├── terraform.tfvars.sample │ └── variables.tf ├── onprem_demo │ ├── README.md │ ├── main.tf │ ├── onprem_project │ │ ├── main.tf │ │ ├── outputs.tf │ │ └── variables.tf │ ├── outputs.tf │ ├── variables.tf │ └── vpc_sc_project │ │ ├── main.tf │ │ ├── outputs.tf │ │ └── variables.tf ├── scoped_example_with_egress_rule │ ├── README.md │ ├── main.tf │ ├── outputs.tf │ └── variables.tf ├── scoped_example_with_ingress_rule │ ├── README.md │ ├── main.tf │ ├── outputs.tf │ └── variables.tf ├── simple_example │ ├── README.md │ ├── main.tf │ ├── outputs.tf │ ├── sample_bq_schema.json │ └── variables.tf ├── simple_example_access_level │ ├── README.md │ ├── main.tf │ ├── outputs.tf │ └── variables.tf ├── simple_example_access_level_dry_run │ ├── README.md │ ├── main.tf │ ├── outputs.tf │ └── variables.tf ├── simple_example_bridge │ ├── README.md │ ├── main.tf │ ├── outputs.tf │ └── variables.tf └── simple_example_dynamic │ ├── README.md │ ├── main.tf │ ├── outputs.tf │ ├── projects.tf │ └── variables.tf ├── main.tf ├── modules ├── access_level │ ├── README.md │ ├── main.tf │ ├── outputs.tf │ ├── variables.tf │ └── versions.tf ├── bridge_service_perimeter │ ├── README.md │ ├── main.tf │ ├── outputs.tf │ ├── variables.tf │ └── versions.tf └── regular_service_perimeter │ ├── README.md │ ├── main.tf │ ├── outputs.tf │ ├── variables.tf │ ├── versions.tf │ └── vpc-sc-policies.tf ├── outputs.tf ├── test ├── .gitignore ├── fixtures │ ├── scoped_example_with_egress_rule │ │ ├── main.tf │ │ ├── outputs.tf │ │ └── variables.tf │ ├── scoped_example_with_ingress_rule │ │ ├── main.tf │ │ ├── outputs.tf │ │ └── variables.tf │ ├── shared │ │ ├── outputs.tf │ │ └── variables.tf │ ├── simple_example │ │ ├── main.tf │ │ ├── outputs.tf │ │ ├── sample_bq_schema.json │ │ └── variables.tf │ └── simple_example_bridge │ │ ├── main.tf │ │ ├── outputs.tf │ │ └── variables.tf ├── integration │ ├── discover_test.go │ ├── go.mod │ ├── go.sum │ ├── scoped_example_with_egress_rule │ │ └── scoped_example_with_egress_rule_test.go │ ├── scoped_example_with_ingress_rule │ │ └── scoped_example_with_ingress_rule_test.go │ ├── simple_example │ │ ├── controls │ │ │ └── gcloud.rb │ │ └── inspec.yml │ └── simple_example_bridge │ │ ├── controls │ │ └── gcloud.rb │ │ └── inspec.yml ├── setup │ ├── .gitignore │ ├── iam.tf │ ├── main.tf │ ├── make_source.sh │ ├── outputs.tf │ ├── variables.tf │ └── versions.tf └── task_helper_functions.sh ├── variables.tf └── versions.tf /.dockerignore: -------------------------------------------------------------------------------- 1 | .git 2 | .terraform 3 | .terraform.d 4 | .kitchen 5 | terraform.tfstate.d 6 | examples/.kitchen 7 | examples/*/.terraform 8 | examples/*/terraform.tfstate.d 9 | -------------------------------------------------------------------------------- /.github/conventional-commit-lint.yaml: -------------------------------------------------------------------------------- 1 | # Copyright 2022-2025 Google LLC 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | # NOTE: This file is automatically generated from: 16 | # https://github.com/GoogleCloudPlatform/cloud-foundation-toolkit/blob/main/infra/terraform/test-org/github 17 | 18 | enabled: true 19 | always_check_pr_title: true 20 | -------------------------------------------------------------------------------- /.github/release-please.yml: -------------------------------------------------------------------------------- 1 | # Copyright 2021 Google LLC 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | releaseType: terraform-module 16 | handleGHRelease: true 17 | -------------------------------------------------------------------------------- /.github/renovate.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://docs.renovatebot.com/renovate-schema.json", 3 | "extends": ["github>GoogleCloudPlatform/cloud-foundation-toolkit//infra/terraform/test-org/github/resources/renovate"] 4 | } 5 | -------------------------------------------------------------------------------- /.github/trusted-contribution.yml: -------------------------------------------------------------------------------- 1 | # Copyright 2023-2025 Google LLC 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | # NOTE: This file is automatically generated from: 16 | # https://github.com/GoogleCloudPlatform/cloud-foundation-toolkit/blob/main/infra/terraform/test-org/github 17 | 18 | annotations: 19 | - type: comment 20 | text: "/gcbrun" 21 | trustedContributors: 22 | - release-please[bot] 23 | - renovate[bot] 24 | - renovate-bot 25 | - forking-renovate[bot] 26 | - dependabot[bot] 27 | -------------------------------------------------------------------------------- /.github/workflows/lint.yaml: -------------------------------------------------------------------------------- 1 | # Copyright 2023-2025 Google LLC 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | # NOTE: This file is automatically generated from values at: 16 | # https://github.com/GoogleCloudPlatform/cloud-foundation-toolkit/blob/main/infra/terraform/test-org/org/locals.tf 17 | 18 | name: 'lint' 19 | 20 | on: 21 | workflow_dispatch: 22 | pull_request: 23 | branches: 24 | - main 25 | 26 | concurrency: 27 | group: '${{ github.workflow }}-${{ github.head_ref || github.ref }}' 28 | cancel-in-progress: true 29 | 30 | jobs: 31 | lint: 32 | name: 'lint' 33 | runs-on: 'ubuntu-latest' 34 | steps: 35 | - uses: 'actions/checkout@v4' 36 | - id: variables 37 | run: | 38 | MAKEFILE=$(find . -name Makefile -print -quit) 39 | if [ -z "$MAKEFILE" ]; then 40 | echo dev-tools=gcr.io/cloud-foundation-cicd/cft/developer-tools:1 >> "$GITHUB_OUTPUT" 41 | else 42 | VERSION=$(grep "DOCKER_TAG_VERSION_DEVELOPER_TOOLS := " $MAKEFILE | cut -d\ -f3) 43 | IMAGE=$(grep "DOCKER_IMAGE_DEVELOPER_TOOLS := " $MAKEFILE | cut -d\ -f3) 44 | REGISTRY=$(grep "REGISTRY_URL := " $MAKEFILE | cut -d\ -f3) 45 | echo dev-tools=${REGISTRY}/${IMAGE}:${VERSION} >> "$GITHUB_OUTPUT" 46 | fi 47 | - run: docker run --rm -v ${{ github.workspace }}:/workspace ${{ steps.variables.outputs.dev-tools }} module-swapper 48 | - run: docker run --rm -v ${{ github.workspace }}:/workspace ${{ steps.variables.outputs.dev-tools }} /usr/local/bin/test_lint.sh 49 | -------------------------------------------------------------------------------- /.github/workflows/stale.yml: -------------------------------------------------------------------------------- 1 | # Copyright 2022-2025 Google LLC 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | # NOTE: This file is automatically generated from: 16 | # https://github.com/GoogleCloudPlatform/cloud-foundation-toolkit/blob/main/infra/terraform/test-org/github 17 | 18 | name: "Close stale issues" 19 | on: 20 | schedule: 21 | - cron: "0 23 * * *" 22 | 23 | jobs: 24 | stale: 25 | if: github.repository_owner == 'GoogleCloudPlatform' || github.repository_owner == 'terraform-google-modules' 26 | runs-on: ubuntu-latest 27 | steps: 28 | - uses: actions/stale@v9 29 | with: 30 | repo-token: ${{ secrets.GITHUB_TOKEN }} 31 | stale-issue-message: 'This issue is stale because it has been open 60 days with no activity. Remove stale label or comment or this will be closed in 7 days' 32 | stale-pr-message: 'This PR is stale because it has been open 60 days with no activity. Remove stale label or comment or this will be closed in 7 days' 33 | exempt-issue-labels: 'triaged' 34 | exempt-pr-labels: 'dependencies,autorelease: pending' 35 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # OSX leaves these everywhere on SMB shares 2 | ._* 3 | 4 | # OSX trash 5 | .DS_Store 6 | 7 | # Python 8 | *.pyc 9 | 10 | # Emacs save files 11 | *~ 12 | \#*\# 13 | .\#* 14 | 15 | # Vim-related files 16 | [._]*.s[a-w][a-z] 17 | [._]s[a-w][a-z] 18 | *.un~ 19 | Session.vim 20 | .netrwhist 21 | 22 | ### https://raw.github.com/github/gitignore/90f149de451a5433aebd94d02d11b0e28843a1af/Terraform.gitignore 23 | 24 | # Local .terraform directories 25 | **/.terraform/* 26 | 27 | # .tfstate files 28 | *.tfstate 29 | *.tfstate.* 30 | 31 | # Crash log files 32 | crash.log 33 | 34 | # Kitchen files 35 | **/inspec.lock 36 | **/.kitchen 37 | **/.kitchen.local.yml 38 | **/Gemfile.lock 39 | 40 | # Ignore any .tfvars files that are generated automatically for each Terraform run. Most 41 | # .tfvars files are managed as part of configuration and so should be included in 42 | # version control. 43 | # 44 | # example.tfvars 45 | test/fixtures/shared/terraform.tfvars 46 | test/fixtures/simple_example/terraform.tfvars 47 | examples/simple_example/terraform.tfvars 48 | examples/simple_example_bridge/terraform.tfvars 49 | examples/simple_example_access_level/terraform.tfvars 50 | terraform.tfvars 51 | 52 | credentials.json 53 | 54 | examples/automatic_folder.zip 55 | 56 | node_modules 57 | yarn.lock 58 | 59 | # tf lock file 60 | .terraform.lock.hcl 61 | -------------------------------------------------------------------------------- /.kitchen.yml: -------------------------------------------------------------------------------- 1 | # Copyright 2024 Google LLC 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | --- 16 | driver: 17 | name: "terraform" 18 | command_timeout: 1800 19 | verify_version: false 20 | 21 | provisioner: 22 | name: "terraform" 23 | 24 | platforms: 25 | - name: local 26 | 27 | suites: 28 | - name: "simple_example" 29 | driver: 30 | name: "terraform" 31 | command_timeout: 1800 32 | root_module_directory: test/fixtures/simple_example/ 33 | verifier: 34 | name: terraform 35 | color: false 36 | systems: 37 | - name: simple_example 38 | backend: local 39 | controls: 40 | - big_query_vpc_positive_test 41 | - big_query_vpc_negative_test 42 | - access_level_regions_test 43 | provisioner: 44 | name: terraform 45 | - name: "simple_example_bridge" 46 | driver: 47 | name: "terraform" 48 | command_timeout: 1800 49 | root_module_directory: test/fixtures/simple_example_bridge/ 50 | verifier: 51 | name: terraform 52 | color: false 53 | systems: 54 | - name: simple_example_bridge 55 | backend: local 56 | controls: 57 | - bridge_policy_test 58 | provisioner: 59 | name: terraform 60 | -------------------------------------------------------------------------------- /CODEOWNERS: -------------------------------------------------------------------------------- 1 | # NOTE: This file is automatically generated from values at: 2 | # https://github.com/GoogleCloudPlatform/cloud-foundation-toolkit/blob/main/infra/terraform/test-org/org/locals.tf 3 | 4 | * @terraform-google-modules/cft-admins @imrannayer 5 | 6 | # NOTE: GitHub CODEOWNERS locations: 7 | # https://docs.github.com/en/repositories/managing-your-repositorys-settings-and-features/customizing-your-repository/about-code-owners#codeowners-and-branch-protection 8 | 9 | CODEOWNERS @terraform-google-modules/cft-admins 10 | .github/CODEOWNERS @terraform-google-modules/cft-admins 11 | docs/CODEOWNERS @terraform-google-modules/cft-admins 12 | 13 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | This document provides guidelines for contributing to the module VPC service controls. 4 | 5 | ## Dependencies 6 | 7 | The following dependencies must be installed on the development system: 8 | 9 | - [Docker Engine][docker-engine] 10 | - [Google Cloud SDK][google-cloud-sdk] 11 | - [make] 12 | 13 | ## Generating Documentation for Inputs and Outputs 14 | 15 | The Inputs and Outputs tables in the READMEs of the root module, 16 | submodules, and example modules are automatically generated based on 17 | the `variables` and `outputs` of the respective modules. These tables 18 | must be refreshed if the module interfaces are changed. 19 | 20 | ### Execution 21 | 22 | Run `make generate_docs` to generate new Inputs and Outputs tables. 23 | 24 | ## Integration Testing 25 | 26 | Integration tests are used to verify the behaviour of the root module, 27 | submodules, and example modules. Additions, changes, and fixes should 28 | be accompanied with tests. 29 | 30 | The integration tests are run using [Kitchen][kitchen], 31 | [Kitchen-Terraform][kitchen-terraform], and [InSpec][inspec]. These 32 | tools are packaged within a Docker image for convenience. 33 | 34 | The general strategy for these tests is to verify the behaviour of the 35 | [example modules](./examples/), thus ensuring that the root module, 36 | submodules, and example modules are all functionally correct. 37 | 38 | ### Test Environment 39 | The easiest way to test the module is in an isolated test project. The setup for such a project is defined in [test/setup](./test/setup/) directory. 40 | 41 | To use this setup, you need a service account with Project Creator access on a folder. Export the Service Account credentials to your environment like so: 42 | 43 | ``` 44 | export SERVICE_ACCOUNT_JSON=$(< credentials.json) 45 | ``` 46 | 47 | You will also need to set a few environment variables: 48 | ``` 49 | export TF_VAR_org_id="your_org_id" 50 | export TF_VAR_folder_id="your_folder_id" 51 | export TF_VAR_billing_account="your_billing_account_id" 52 | ``` 53 | 54 | With these settings in place, you can prepare a test project using Docker: 55 | ``` 56 | make docker_test_prepare 57 | ``` 58 | 59 | To remove Access Policy, if any exists, run funcion inside Docker: 60 | ``` 61 | remove_gcloud_org_accesspolicy 62 | ``` 63 | 64 | ### Noninteractive Execution 65 | 66 | Run `make docker_test_integration` to test all of the example modules 67 | noninteractively, using the prepared test project. 68 | 69 | ### Interactive Execution 70 | 71 | 1. Run `make docker_run` to start the testing Docker container in 72 | interactive mode. 73 | 74 | 1. Run `kitchen_do create ` to initialize the working 75 | directory for an example module. 76 | 77 | 1. Run `kitchen_do converge ` to apply the example module. 78 | 79 | 1. Run `activate_gcloud_sa && kitchen_do verify ` to test the example module. 80 | 81 | 1. Run `kitchen_do destroy ` to destroy the example module 82 | state. 83 | 84 | ## Linting and Formatting 85 | 86 | Many of the files in the repository can be linted or formatted to 87 | maintain a standard of quality. 88 | 89 | ### Execution 90 | 91 | Run `make docker_test_lint`. 92 | 93 | [docker-engine]: https://www.docker.com/products/docker-engine 94 | [flake8]: http://flake8.pycqa.org/en/latest/ 95 | [gofmt]: https://golang.org/cmd/gofmt/ 96 | [google-cloud-sdk]: https://cloud.google.com/sdk/install 97 | [hadolint]: https://github.com/hadolint/hadolint 98 | [inspec]: https://inspec.io/ 99 | [kitchen-terraform]: https://github.com/newcontext-oss/kitchen-terraform 100 | [kitchen]: https://kitchen.ci/ 101 | [make]: https://en.wikipedia.org/wiki/Make_(software) 102 | [shellcheck]: https://www.shellcheck.net/ 103 | [terraform-docs]: https://github.com/segmentio/terraform-docs 104 | [terraform]: https://terraform.io/ 105 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # Copyright 2024 Google LLC 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # https://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | # Please note that this file was generated from [terraform-google-module-template](https://github.com/terraform-google-modules/terraform-google-module-template). 16 | # Please make sure to contribute relevant changes upstream! 17 | 18 | # Make will use bash instead of sh 19 | SHELL := /usr/bin/env bash 20 | 21 | DOCKER_TAG_VERSION_DEVELOPER_TOOLS := 1.24 22 | DOCKER_IMAGE_DEVELOPER_TOOLS := cft/developer-tools 23 | REGISTRY_URL := gcr.io/cloud-foundation-cicd 24 | 25 | # Enter docker container for local development 26 | .PHONY: docker_run 27 | docker_run: 28 | docker run --rm -it \ 29 | -e SERVICE_ACCOUNT_JSON \ 30 | -v $(CURDIR):/workspace \ 31 | $(REGISTRY_URL)/${DOCKER_IMAGE_DEVELOPER_TOOLS}:${DOCKER_TAG_VERSION_DEVELOPER_TOOLS} \ 32 | /bin/bash 33 | 34 | # Execute prepare tests within the docker container 35 | .PHONY: docker_test_prepare 36 | docker_test_prepare: 37 | docker run --rm -it \ 38 | -e SERVICE_ACCOUNT_JSON \ 39 | -e TF_VAR_org_id \ 40 | -e TF_VAR_folder_id \ 41 | -e TF_VAR_billing_account \ 42 | -v $(CURDIR):/workspace \ 43 | $(REGISTRY_URL)/${DOCKER_IMAGE_DEVELOPER_TOOLS}:${DOCKER_TAG_VERSION_DEVELOPER_TOOLS} \ 44 | /usr/local/bin/execute_with_credentials.sh prepare_environment 45 | 46 | # Clean up test environment within the docker container 47 | .PHONY: docker_test_cleanup 48 | docker_test_cleanup: 49 | docker run --rm -it \ 50 | -e SERVICE_ACCOUNT_JSON \ 51 | -e TF_VAR_org_id \ 52 | -e TF_VAR_folder_id \ 53 | -e TF_VAR_billing_account \ 54 | -v $(CURDIR):/workspace \ 55 | $(REGISTRY_URL)/${DOCKER_IMAGE_DEVELOPER_TOOLS}:${DOCKER_TAG_VERSION_DEVELOPER_TOOLS} \ 56 | /usr/local/bin/execute_with_credentials.sh cleanup_environment 57 | 58 | # Execute integration tests within the docker container 59 | .PHONY: docker_test_integration 60 | docker_test_integration: 61 | docker run --rm -it \ 62 | -e SERVICE_ACCOUNT_JSON \ 63 | -v $(CURDIR):/workspace \ 64 | $(REGISTRY_URL)/${DOCKER_IMAGE_DEVELOPER_TOOLS}:${DOCKER_TAG_VERSION_DEVELOPER_TOOLS} \ 65 | /usr/local/bin/test_integration.sh 66 | 67 | # Execute lint tests within the docker container 68 | .PHONY: docker_test_lint 69 | docker_test_lint: 70 | docker run --rm -it \ 71 | -v $(CURDIR):/workspace \ 72 | $(REGISTRY_URL)/${DOCKER_IMAGE_DEVELOPER_TOOLS}:${DOCKER_TAG_VERSION_DEVELOPER_TOOLS} \ 73 | /usr/local/bin/test_lint.sh 74 | 75 | # Generate documentation 76 | .PHONY: docker_generate_docs 77 | docker_generate_docs: 78 | docker run --rm -it \ 79 | -v $(CURDIR):/workspace \ 80 | $(REGISTRY_URL)/${DOCKER_IMAGE_DEVELOPER_TOOLS}:${DOCKER_TAG_VERSION_DEVELOPER_TOOLS} \ 81 | /bin/bash -c 'source /usr/local/bin/task_helper_functions.sh && generate_docs' 82 | 83 | # Alias for backwards compatibility 84 | .PHONY: generate_docs 85 | generate_docs: docker_generate_docs 86 | -------------------------------------------------------------------------------- /build/lint.cloudbuild.yaml: -------------------------------------------------------------------------------- 1 | # Copyright 2019 Google LLC 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # https://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | steps: 16 | - id: 'lint' 17 | name: 'gcr.io/cloud-foundation-cicd/$_DOCKER_IMAGE_DEVELOPER_TOOLS:$_DOCKER_TAG_VERSION_DEVELOPER_TOOLS' 18 | args: ['/usr/local/bin/test_lint.sh'] 19 | tags: 20 | - 'ci' 21 | - 'lint' 22 | substitutions: 23 | _DOCKER_IMAGE_DEVELOPER_TOOLS: 'cft/developer-tools' 24 | _DOCKER_TAG_VERSION_DEVELOPER_TOOLS: '1.24' 25 | -------------------------------------------------------------------------------- /docs/upgrading_to_v4.0.md: -------------------------------------------------------------------------------- 1 | # Upgrading to v4.x 2 | 3 | The v4.x release is a backwards-incompatible release. 4 | 5 | The `resources` inside perimeters have been split into their [own Terraform resource](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/access_context_manager_service_perimeter_resource). 6 | This allows you to add resources (projects) to the perimeter from *outside* the module. 7 | 8 | However, this change has a few implications: 9 | 1. Resources added to the perimeter out-of-band will no longer be removed by Terraform. 10 | You will need to develop an alternative system for dealing with these. 11 | 2. The location of resources has moved in the state file. 12 | 3. Because resources are now created using `for_each`, if the underlying project is created in the **same** 13 | Terraform configuration as the perimeter, you will need to provide a `resource_keys` variable. 14 | 15 | ## Dynamic resources 16 | If resources are created inside the same configuration as the perimeter, you will received an error that their value cannot be determined until apply: 17 | ``` 18 | │ Error: Invalid for_each argument 19 | │ 20 | │ on ../../modules/regular_service_perimeter/main.tf line 195, in resource "google_access_context_manager_service_perimeter_resource" "service_perimeter_resource": 21 | │ 195: for_each = local.resources 22 | │ ├──────────────── 23 | │ │ local.resources will be known only after apply 24 | ``` 25 | 26 | To work around this, you need to provide a `resource_keys` variable input with keys for each resource. These keys are only used by Terraform and can be any alphanumeric string. 27 | 28 | ```diff 29 | module "regular_service_perimeter_2" { 30 | source = "terraform-google-modules/vpc-service-controls/google//modules/regular_service_perimeter" 31 | - version = "~> 3.0" 32 | + version = "~> 4.0" 33 | 34 | ... 35 | 36 | resources = [module.project_two.project_number, module.project_three.project_number] 37 | + resource_keys = ["two", "three"] 38 | } 39 | 40 | module "bridge" { 41 | source = "terraform-google-modules/vpc-service-controls/google//modules/bridge_service_perimeter" 42 | - version = "~> 3.0" 43 | + version = "~> 4.0" 44 | 45 | ... 46 | 47 | resources = concat( 48 | module.regular_service_perimeter_1.shared_resources["all"], 49 | module.regular_service_perimeter_2.shared_resources["all"], 50 | ) 51 | + resource_keys = ["one", "two", "three"] 52 | } 53 | ``` 54 | 55 | ## State migration 56 | 57 | If you run `terraform plan` on an upgraded module, you will notice that Terraform wants to add `service_perimeter` resources. 58 | 59 | ``` 60 | Terraform will perform the following actions: 61 | 62 | # module.bridge.google_access_context_manager_service_perimeter_resource.service_perimeter_resource["projects/34502780858"] will be created 63 | + resource "google_access_context_manager_service_perimeter_resource" "service_perimeter_resource" { 64 | + id = (known after apply) 65 | + perimeter_name = "accessPolicies/209696272439/servicePerimeters/bridge_perimeter_1" 66 | + resource = "projects/34502780858" 67 | } 68 | 69 | # module.bridge.google_access_context_manager_service_perimeter_resource.service_perimeter_resource["projects/843696391937"] will be created 70 | + resource "google_access_context_manager_service_perimeter_resource" "service_perimeter_resource" { 71 | + id = (known after apply) 72 | + perimeter_name = "accessPolicies/209696272439/servicePerimeters/bridge_perimeter_1" 73 | + resource = "projects/843696391937" 74 | } 75 | 76 | # module.regular_service_perimeter_1.google_access_context_manager_service_perimeter_resource.service_perimeter_resource["34502780858"] will be created 77 | + resource "google_access_context_manager_service_perimeter_resource" "service_perimeter_resource" { 78 | + id = (known after apply) 79 | + perimeter_name = "accessPolicies/209696272439/servicePerimeters/regular_perimeter_1" 80 | + resource = "projects/34502780858" 81 | } 82 | 83 | Plan: 3 to add, 0 to change, 0 to destroy. 84 | ``` 85 | 86 | For each resource, you will need to import it into the Terraform config: 87 | 88 | ``` 89 | terraform import 'module.bridge.google_access_context_manager_service_perimeter_resource.service_perimeter_resource["one"]' 'accessPolicies/209696272439/servicePerimeters/bridge_perimeter_1/projects/34502780858' 90 | terraform import 'module.bridge.google_access_context_manager_service_perimeter_resource.service_perimeter_resource["two"]' 'accessPolicies/209696272439/servicePerimeters/bridge_perimeter_1/projects/843696391937' 91 | terraform import 'module.regular_service_perimeter_1.google_access_context_manager_service_perimeter_resource.service_perimeter_resource["34502780858"]' 'accessPolicies/209696272439/servicePerimeters/regular_perimeter_1/projects/34502780858' 92 | ``` 93 | -------------------------------------------------------------------------------- /docs/upgrading_to_v6.0.md: -------------------------------------------------------------------------------- 1 | # Upgrading to v6.x 2 | 3 | The v6.x release contains backwards-incompatible changes. 4 | 5 | This update requires upgrading the minimum provider version to `5.4`. 6 | -------------------------------------------------------------------------------- /examples/access_level_vpc_ip/README.md: -------------------------------------------------------------------------------- 1 | # Simple Example Access Level 2 | 3 | This example illustrates how to use the `vpc-service-controls` module to configure an org policy and an access level 4 | 5 | # Requirements 6 | 1. Make sure you've gone through the root [Requirement Section](../../#requirements) 7 | 8 | 9 | 10 | 11 | ## Inputs 12 | 13 | | Name | Description | Type | Default | Required | 14 | |------|-------------|------|---------|:--------:| 15 | | parent\_id | The parent of this AccessPolicy in the Cloud Resource Hierarchy. As of now, only organization are accepted as parent. | `string` | n/a | yes | 16 | | project\_id | The ID of the project in which to provision network. | `string` | n/a | yes | 17 | 18 | ## Outputs 19 | 20 | | Name | Description | 21 | |------|-------------| 22 | | access\_level | n/a | 23 | 24 | 25 | -------------------------------------------------------------------------------- /examples/access_level_vpc_ip/main.tf: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2024 Google LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | resource "random_id" "random_suffix" { 18 | byte_length = 2 19 | } 20 | 21 | module "access_context_manager_policy" { 22 | source = "terraform-google-modules/vpc-service-controls/google" 23 | version = "~> 7.0" 24 | 25 | parent_id = var.parent_id 26 | policy_name = "int_test_vpc_sc_policy_${random_id.random_suffix.hex}" 27 | } 28 | 29 | #Create Network with a subnetwork and private service access for both netapp.servicenetworking.goog and servicenetworking.googleapis.com 30 | 31 | resource "google_compute_network" "network1" { 32 | name = "vpc-a" 33 | project = var.project_id 34 | auto_create_subnetworks = false 35 | description = "test network" 36 | } 37 | 38 | resource "google_compute_subnetwork" "network1_us_central1" { 39 | name = "vpc-a-us-central1" 40 | ip_cidr_range = "10.0.0.0/24" 41 | region = "us-central1" 42 | project = var.project_id 43 | network = google_compute_network.network1.self_link 44 | private_ip_google_access = true 45 | } 46 | 47 | resource "google_compute_subnetwork" "network1_us_east1" { 48 | name = "vpc-a-us-east1" 49 | ip_cidr_range = "10.0.1.0/24" 50 | region = "us-east1" 51 | project = var.project_id 52 | network = google_compute_network.network1.self_link 53 | private_ip_google_access = true 54 | } 55 | 56 | resource "google_compute_network" "network2" { 57 | name = "vpc-b" 58 | project = var.project_id 59 | auto_create_subnetworks = false 60 | description = "test network b" 61 | } 62 | 63 | resource "google_compute_subnetwork" "network2_us_central1" { 64 | name = "vpc-b-us-central1" 65 | ip_cidr_range = "10.0.10.0/24" 66 | region = "us-central1" 67 | project = var.project_id 68 | network = google_compute_network.network2.self_link 69 | private_ip_google_access = true 70 | } 71 | 72 | module "access_level_vpc_ranges" { 73 | source = "terraform-google-modules/vpc-service-controls/google//modules/access_level" 74 | version = "~> 7.0" 75 | 76 | policy = module.access_context_manager_policy.policy_id 77 | name = "vpc_ip_address_policy" 78 | description = "access level for vpc ip addresses" 79 | vpc_network_sources = { 80 | "vpc_a" = { 81 | network_id = google_compute_network.network1.id 82 | ip_address_ranges = [ 83 | "10.0.0.0/24", 84 | "192.169.0.0/16", 85 | ] 86 | } 87 | "vpc_b" = { 88 | network_id = google_compute_network.network2.id 89 | } 90 | } 91 | depends_on = [ 92 | google_compute_subnetwork.network1_us_central1, 93 | google_compute_subnetwork.network1_us_east1, 94 | google_compute_subnetwork.network2_us_central1, 95 | ] 96 | } 97 | -------------------------------------------------------------------------------- /examples/access_level_vpc_ip/outputs.tf: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2024 Google LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | output "access_level" { 18 | value = module.access_level_vpc_ranges.access_level 19 | } 20 | -------------------------------------------------------------------------------- /examples/access_level_vpc_ip/variables.tf: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2024 Google LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | variable "parent_id" { 18 | description = "The parent of this AccessPolicy in the Cloud Resource Hierarchy. As of now, only organization are accepted as parent." 19 | type = string 20 | } 21 | 22 | variable "project_id" { 23 | description = "The ID of the project in which to provision network." 24 | type = string 25 | } 26 | -------------------------------------------------------------------------------- /examples/automatic_folder/.gitignore: -------------------------------------------------------------------------------- 1 | /provider.tf 2 | /.terraform 3 | tfplan 4 | local.tfvars 5 | -------------------------------------------------------------------------------- /examples/automatic_folder/backend.tf.sample: -------------------------------------------------------------------------------- 1 | terraform { 2 | required_version = "~> 0.12.0" 3 | 4 | backend "gcs" { 5 | bucket = "" 6 | prefix = "terraform/vpc-service-controls" 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /examples/automatic_folder/diagram.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/terraform-google-modules/terraform-google-vpc-service-controls/8b14973fd700a4237944b25eedb1e2d0c292e3c3/examples/automatic_folder/diagram.png -------------------------------------------------------------------------------- /examples/automatic_folder/main.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | # Copyright 2018 Google LLC 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # https://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | # -*- coding: utf-8 -*- 17 | 18 | import subprocess 19 | import urllib.request 20 | import os 21 | from shutil import copytree, ignore_patterns, rmtree 22 | 23 | # Version of Terraform that we're using 24 | TERRAFORM_VERSION = '0.12.8' 25 | 26 | # Download URL for Terraform 27 | PLATFORM = 'linux' 28 | TERRAFORM_DOWNLOAD_URL = ( 29 | 'https://releases.hashicorp.com/terraform/%s/terraform_%s_%s_amd64.zip' 30 | % (TERRAFORM_VERSION, TERRAFORM_VERSION, PLATFORM)) 31 | 32 | # Paths where Terraform should be installed 33 | TERRAFORM_DIR = os.path.join('/tmp', 'terraform_%s' % TERRAFORM_VERSION) 34 | TERRAFORM_PATH = os.path.join(TERRAFORM_DIR, 'terraform') 35 | 36 | PROJECT_DIR = os.path.join('/tmp', 'project') 37 | 38 | 39 | def check_call(args, cwd=None, printOut=False): 40 | """Wrapper for subprocess that checks if a process runs correctly, 41 | and if not, prints stdout and stderr. 42 | """ 43 | proc = subprocess.Popen(args, 44 | stdout=subprocess.PIPE, 45 | stderr=subprocess.PIPE, 46 | cwd=cwd) 47 | stdout, stderr = proc.communicate() 48 | if proc.returncode != 0: 49 | print(stdout.decode()) 50 | print(stderr.decode()) 51 | raise subprocess.CalledProcessError( 52 | returncode=proc.returncode, 53 | cmd=args) 54 | if printOut: 55 | print(stdout.decode()) 56 | print(stderr.decode()) 57 | 58 | 59 | def install_terraform(): 60 | """Install Terraform.""" 61 | if os.path.exists(TERRAFORM_PATH): 62 | return 63 | 64 | print(TERRAFORM_PATH) 65 | 66 | urllib.request.urlretrieve(TERRAFORM_DOWNLOAD_URL, '/tmp/terraform.zip') 67 | 68 | check_call(['unzip', '-o', '/tmp/terraform.zip', '-d', 69 | TERRAFORM_DIR], '/tmp') 70 | 71 | check_call([TERRAFORM_PATH, '--version']) 72 | 73 | 74 | def handler(event, context): 75 | print(event) 76 | 77 | if os.path.exists(PROJECT_DIR): 78 | rmtree(PROJECT_DIR) 79 | 80 | copytree('.', PROJECT_DIR, ignore=ignore_patterns('.terraform', 81 | 'credentials.json')) 82 | 83 | install_terraform() 84 | 85 | check_call([TERRAFORM_PATH, 'init'], 86 | cwd=PROJECT_DIR, 87 | printOut=True) 88 | check_call([TERRAFORM_PATH, 'apply', 89 | '-target=module.service_perimeter', '-no-color', 90 | '-auto-approve', '-lock=false', 91 | '-lock-timeout=300s'], 92 | cwd=PROJECT_DIR, 93 | printOut=True) 94 | check_call([TERRAFORM_PATH, 'output', '-json'], 95 | cwd=PROJECT_DIR, 96 | printOut=True) 97 | -------------------------------------------------------------------------------- /examples/automatic_folder/main.tf: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2024 Google LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | data "google_projects" "in_perimeter_folder" { 18 | filter = "parent.id:${var.folder_id}" 19 | } 20 | 21 | data "google_project" "in_perimeter_folder" { 22 | count = length(data.google_projects.in_perimeter_folder.projects) 23 | 24 | project_id = data.google_projects.in_perimeter_folder.projects[count.index].project_id 25 | } 26 | 27 | locals { 28 | projects = compact(data.google_project.in_perimeter_folder[*].number) 29 | parent_id = var.org_id 30 | watcher_name = replace("${var.policy_name}-manager", "_", "-") 31 | } 32 | 33 | module "access_context_manager_policy" { 34 | source = "terraform-google-modules/vpc-service-controls/google" 35 | version = "~> 7.0" 36 | 37 | parent_id = local.parent_id 38 | policy_name = var.policy_name 39 | } 40 | 41 | module "access_level_members" { 42 | source = "terraform-google-modules/vpc-service-controls/google//modules/access_level" 43 | version = "~> 7.0" 44 | 45 | description = "${var.perimeter_name} Access Level" 46 | policy = module.access_context_manager_policy.policy_id 47 | name = "${var.perimeter_name}_members" 48 | members = var.members 49 | } 50 | 51 | module "service_perimeter" { 52 | source = "terraform-google-modules/vpc-service-controls/google//modules/regular_service_perimeter" 53 | version = "~> 7.0" 54 | 55 | policy = module.access_context_manager_policy.policy_id 56 | perimeter_name = var.perimeter_name 57 | 58 | description = "Perimeter ${var.perimeter_name}" 59 | resources = local.projects 60 | 61 | access_levels = [module.access_level_members.name] 62 | restricted_services = var.restricted_services 63 | 64 | shared_resources = { 65 | all = local.projects 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /examples/automatic_folder/outputs.tf: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2024 Google LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | output "policy_name" { 18 | description = "Name of the parent policy" 19 | value = var.policy_name 20 | } 21 | 22 | output "protected_project_ids" { 23 | description = "Project ids of the projects INSIDE the regular service perimeter" 24 | value = local.projects 25 | } 26 | 27 | output "function_service_account" { 28 | description = "Email of the watcher function's Service Account" 29 | value = google_service_account.watcher.email 30 | } 31 | 32 | output "organization_id" { 33 | description = "Organization ID hosting the perimeter" 34 | value = local.parent_id 35 | } 36 | 37 | output "project_id" { 38 | value = var.project_id 39 | description = "The ID of the project hosting the watcher function." 40 | } 41 | 42 | output "folder_id" { 43 | value = var.folder_id 44 | description = "The ID of the watched folder." 45 | } 46 | -------------------------------------------------------------------------------- /examples/automatic_folder/variables.tf: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2024 Google LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | variable "project_id" { 18 | type = string 19 | description = "The ID of the project to host the watcher function." 20 | } 21 | 22 | variable "org_id" { 23 | description = "The parent organization ID of this AccessPolicy in the Cloud Resource Hierarchy." 24 | type = string 25 | } 26 | 27 | variable "policy_name" { 28 | description = "The policy's name." 29 | type = string 30 | } 31 | 32 | variable "members" { 33 | description = "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}" 34 | type = list(string) 35 | } 36 | 37 | variable "folder_id" { 38 | description = "Folder ID to watch for projects." 39 | type = string 40 | } 41 | 42 | variable "perimeter_name" { 43 | description = "Name of perimeter." 44 | type = string 45 | default = "regular_perimeter" 46 | } 47 | 48 | variable "restricted_services" { 49 | description = "List of services to restrict." 50 | type = list(string) 51 | } 52 | 53 | variable "region" { 54 | type = string 55 | description = "The region in which resources will be applied." 56 | } 57 | -------------------------------------------------------------------------------- /examples/automatic_folder/watcher.tf: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2024 Google LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | module "event_folder_log_entry" { 18 | source = "terraform-google-modules/event-function/google//modules/event-folder-log-entry" 19 | version = "~> 5.0" 20 | 21 | filter = < --member=serviceAccount: --role=roles/storage.Admin` 14 | 15 | 1. Enable Storage API on the protected project. 16 | 2. If you want to run the integration tests for this example, repeat step #3 and #4 on the protected project. 17 | 18 | 19 | ## Inputs 20 | 21 | | Name | Description | Type | Default | Required | 22 | |------|-------------|------|---------|:--------:| 23 | | access\_level\_name | Access level name of the Access Policy. | `string` | `"terraform_members_e"` | no | 24 | | buckets\_names | Buckets Names as list of strings | `list(string)` |
[
"bucket1-e",
"bucket2-e"
]
| no | 25 | | buckets\_prefix | Bucket Prefix | `string` | `"test-bucket-e"` | no | 26 | | 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 | 27 | | parent\_id | The parent of this AccessPolicy in the Cloud Resource Hierarchy. As of now, only organization are accepted as parent. | `string` | n/a | yes | 28 | | perimeter\_name | Perimeter name of the Access Policy.. | `string` | `"regular_perimeter_e"` | no | 29 | | policy\_name | The policy's name. | `string` | n/a | yes | 30 | | protected\_project\_ids | Project id and number of the project INSIDE the regular service perimeter. This map variable expects an "id" for the project id and "number" key for the project number. | `object({ id = string, number = number })` | n/a | yes | 31 | | public\_project\_ids | Project id and number of the project OUTSIDE the regular service perimeter. This map variable expects an "id" for the project id and "number" key for the project number. | `object({ id = string, number = number })` | n/a | yes | 32 | | regions | The request must originate from one of the provided countries/regions. Format: A valid ISO 3166-1 alpha-2 code. | `list(string)` | `[]` | no | 33 | | scopes | Folder or project on which this policy is applicable. Format: 'folders/FOLDER\_ID' or 'projects/PROJECT\_NUMBER' | `list(string)` | `[]` | no | 34 | 35 | ## Outputs 36 | 37 | | Name | Description | 38 | |------|-------------| 39 | | policy\_id | Resource name of the AccessPolicy. | 40 | | policy\_name | Name of the parent policy | 41 | | protected\_project\_id | Project id of the project INSIDE the regular service perimeter | 42 | | service\_perimeter\_name | Service perimeter name | 43 | 44 | 45 | 46 | To provision this example, run the following from within this directory: 47 | - `terraform init` to get the plugins 48 | - `terraform plan` to see the infrastructure plan 49 | - `terraform apply` to apply the infrastructure build 50 | - `terraform destroy` to destroy the built infrastructure 51 | -------------------------------------------------------------------------------- /examples/scoped_example_with_egress_rule/main.tf: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2025 Google LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | module "access_context_manager_policy" { 18 | source = "terraform-google-modules/vpc-service-controls/google" 19 | version = "~> 7.1" 20 | 21 | parent_id = var.parent_id 22 | policy_name = var.policy_name 23 | scopes = var.scopes 24 | 25 | depends_on = [module.gcs_buckets] 26 | } 27 | 28 | module "access_level_members" { 29 | source = "terraform-google-modules/vpc-service-controls/google//modules/access_level" 30 | version = "~> 7.1" 31 | 32 | description = "Simple Example Access Level" 33 | policy = module.access_context_manager_policy.policy_id 34 | name = var.access_level_name 35 | members = var.members 36 | regions = var.regions 37 | } 38 | 39 | resource "time_sleep" "wait_for_members" { 40 | create_duration = "90s" 41 | destroy_duration = "90s" 42 | 43 | depends_on = [module.access_level_members] 44 | } 45 | 46 | module "regular_service_perimeter_1" { 47 | source = "terraform-google-modules/vpc-service-controls/google//modules/regular_service_perimeter" 48 | version = "~> 7.1" 49 | 50 | 51 | policy = module.access_context_manager_policy.policy_id 52 | perimeter_name = var.perimeter_name 53 | 54 | description = "Perimeter shielding bigquery project" 55 | resources = [var.protected_project_ids["number"]] 56 | access_levels = [module.access_level_members.name] 57 | 58 | restricted_services = ["bigquery.googleapis.com", "storage.googleapis.com"] 59 | 60 | egress_policies = [ 61 | { 62 | title = "Read outside buckets from project" 63 | from = { 64 | sources = { 65 | resources = ["projects/${var.protected_project_ids["number"]}"] 66 | }, 67 | identity_type = "ANY_SERVICE_ACCOUNT" 68 | } 69 | to = { 70 | resources = [ 71 | "projects/${var.public_project_ids["number"]}" 72 | ] 73 | operations = { 74 | "storage.googleapis.com" = { 75 | methods = [ 76 | "google.storage.objects.get", 77 | "google.storage.objects.list" 78 | ] 79 | } 80 | } 81 | } 82 | }, 83 | { 84 | title = "Use permissions for Big Query access" # See https://cloud.google.com/vpc-service-controls/docs/supported-method-restrictions 85 | from = { 86 | sources = { 87 | resources = ["projects/${var.protected_project_ids["number"]}"] 88 | }, 89 | identity_type = "ANY_SERVICE_ACCOUNT" 90 | } 91 | to = { 92 | resources = [ 93 | "projects/${var.public_project_ids["number"]}" 94 | ] 95 | operations = { 96 | "bigquery.googleapis.com" = { 97 | permissions = [ 98 | "bigquery.datasets.get", 99 | "bigquery.models.getData", 100 | "bigquery.models.getMetadata", 101 | "bigquery.models.list", 102 | "bigquery.tables.get", 103 | "bigquery.tables.getData", 104 | "bigquery.tables.list" 105 | ] 106 | } 107 | } 108 | } 109 | }, 110 | ] 111 | 112 | shared_resources = { 113 | all = [var.protected_project_ids["number"]] 114 | } 115 | 116 | depends_on = [ 117 | module.gcs_buckets, 118 | time_sleep.wait_for_members 119 | ] 120 | } 121 | 122 | module "gcs_buckets" { 123 | source = "terraform-google-modules/cloud-storage/google" 124 | version = "~> 10.0" 125 | project_id = var.public_project_ids["id"] 126 | names = var.buckets_names 127 | randomize_suffix = true 128 | prefix = var.buckets_prefix 129 | set_admin_roles = true 130 | admins = var.members 131 | } 132 | -------------------------------------------------------------------------------- /examples/scoped_example_with_egress_rule/outputs.tf: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2025 Google LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | output "policy_id" { 18 | description = "Resource name of the AccessPolicy." 19 | value = module.access_context_manager_policy.policy_id 20 | } 21 | 22 | output "policy_name" { 23 | description = "Name of the parent policy" 24 | value = var.policy_name 25 | } 26 | 27 | 28 | output "protected_project_id" { 29 | description = "Project id of the project INSIDE the regular service perimeter" 30 | value = var.protected_project_ids["id"] 31 | } 32 | 33 | output "service_perimeter_name" { 34 | description = "Service perimeter name" 35 | value = module.regular_service_perimeter_1.perimeter_name 36 | } 37 | -------------------------------------------------------------------------------- /examples/scoped_example_with_egress_rule/variables.tf: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2025 Google LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | variable "parent_id" { 18 | description = "The parent of this AccessPolicy in the Cloud Resource Hierarchy. As of now, only organization are accepted as parent." 19 | type = string 20 | } 21 | 22 | variable "policy_name" { 23 | description = "The policy's name." 24 | type = string 25 | } 26 | 27 | variable "protected_project_ids" { 28 | description = "Project id and number of the project INSIDE the regular service perimeter. This map variable expects an \"id\" for the project id and \"number\" key for the project number." 29 | type = object({ id = string, number = number }) 30 | } 31 | 32 | variable "public_project_ids" { 33 | description = "Project id and number of the project OUTSIDE the regular service perimeter. This map variable expects an \"id\" for the project id and \"number\" key for the project number." 34 | type = object({ id = string, number = number }) 35 | } 36 | 37 | variable "members" { 38 | description = "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}" 39 | type = list(string) 40 | } 41 | 42 | variable "regions" { 43 | description = "The request must originate from one of the provided countries/regions. Format: A valid ISO 3166-1 alpha-2 code." 44 | type = list(string) 45 | default = [] 46 | } 47 | 48 | variable "perimeter_name" { 49 | description = "Perimeter name of the Access Policy.." 50 | type = string 51 | default = "regular_perimeter_e" 52 | } 53 | 54 | variable "access_level_name" { 55 | description = "Access level name of the Access Policy." 56 | type = string 57 | default = "terraform_members_e" 58 | } 59 | 60 | variable "buckets_prefix" { 61 | description = "Bucket Prefix" 62 | type = string 63 | default = "test-bucket-e" 64 | } 65 | 66 | variable "buckets_names" { 67 | description = "Buckets Names as list of strings" 68 | type = list(string) 69 | default = ["bucket1-e", "bucket2-e"] 70 | } 71 | 72 | variable "scopes" { 73 | description = "Folder or project on which this policy is applicable. Format: 'folders/FOLDER_ID' or 'projects/PROJECT_NUMBER'" 74 | type = list(string) 75 | default = [] 76 | } 77 | -------------------------------------------------------------------------------- /examples/scoped_example_with_ingress_rule/README.md: -------------------------------------------------------------------------------- 1 | # Simple Example with Ingress Rule 2 | 3 | This example illustrates how to use the `vpc-service-controls` module to configure a scoped org policy, a regular perimeter with storage buckets that can be access in it from outside read only via ingress rule. 4 | 5 | # Requirements 6 | 7 | 1. Make sure you've gone through the root [Requirement Section](../../README.md#requirements) on any project in your organization. 8 | 2. If you need to run integration tests for this example, select a second project in your organization. The project you already configured will be referred as the protected project that will be inside of the regular service perimeter. The second project will be the public project, which will be outside of the regular service perimeter. 9 | 3. Grant the service account the following permissions on the protected project: 10 | - roles/storage.Admin 11 | 12 | You may use the following gcloud commands: 13 | `gcloud projects add-iam-policy-binding --member=serviceAccount: --role=roles/storage.Admin` 14 | 15 | 1. Enable Storage API on the protected project. 16 | 2. If you want to run the integration tests for this example, repeat step #3 and #4 on the public project. 17 | 18 | 19 | ## Inputs 20 | 21 | | Name | Description | Type | Default | Required | 22 | |------|-------------|------|---------|:--------:| 23 | | access\_level\_name | Access level name of the Access Policy. | `string` | `"terraform_members"` | no | 24 | | buckets\_names | Buckets Names as list of strings | `list(string)` |
[
"bucket1",
"bucket2"
]
| no | 25 | | buckets\_prefix | Bucket Prefix | `string` | `"test-bucket"` | no | 26 | | 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 | 27 | | parent\_id | The parent of this AccessPolicy in the Cloud Resource Hierarchy. As of now, only organization are accepted as parent. | `string` | n/a | yes | 28 | | perimeter\_name | Perimeter name of the Access Policy.. | `string` | `"regular_perimeter_1"` | no | 29 | | policy\_name | The policy's name. | `string` | n/a | yes | 30 | | protected\_project\_ids | Project id and number of the project INSIDE the regular service perimeter. This map variable expects an "id" for the project id and "number" key for the project number. | `object({ id = string, number = number })` | n/a | yes | 31 | | public\_project\_ids | Project id and number of the project OUTSIDE the regular service perimeter. This map variable expects an "id" for the project id and "number" key for the project number. | `object({ id = string, number = number })` | n/a | yes | 32 | | read\_bucket\_identities | List of all identities should get read access on bucket | `list(string)` | `[]` | no | 33 | | regions | The request must originate from one of the provided countries/regions. Format: A valid ISO 3166-1 alpha-2 code. | `list(string)` | `[]` | no | 34 | | scopes | Folder or project on which this policy is applicable. Format: 'folders/FOLDER\_ID' or 'projects/PROJECT\_NUMBER' | `list(string)` | `[]` | no | 35 | 36 | ## Outputs 37 | 38 | | Name | Description | 39 | |------|-------------| 40 | | policy\_id | Resource name of the AccessPolicy. | 41 | | policy\_name | Name of the parent policy | 42 | | protected\_project\_id | Project id of the project INSIDE the regular service perimeter | 43 | | service\_perimeter\_name | Service perimeter name | 44 | 45 | 46 | 47 | To provision this example, run the following from within this directory: 48 | - `terraform init` to get the plugins 49 | - `terraform plan` to see the infrastructure plan 50 | - `terraform apply` to apply the infrastructure build 51 | - `terraform destroy` to destroy the built infrastructure 52 | -------------------------------------------------------------------------------- /examples/scoped_example_with_ingress_rule/main.tf: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2024 Google LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | module "access_context_manager_policy" { 18 | source = "terraform-google-modules/vpc-service-controls/google" 19 | version = "~> 7.1" 20 | 21 | parent_id = var.parent_id 22 | policy_name = var.policy_name 23 | scopes = var.scopes 24 | 25 | depends_on = [module.gcs_buckets] 26 | } 27 | 28 | module "access_level_members" { 29 | source = "terraform-google-modules/vpc-service-controls/google//modules/access_level" 30 | version = "~> 7.1" 31 | 32 | description = "Simple Example Access Level" 33 | policy = module.access_context_manager_policy.policy_id 34 | name = var.access_level_name 35 | members = var.members 36 | regions = var.regions 37 | } 38 | 39 | resource "time_sleep" "wait_for_members" { 40 | create_duration = "90s" 41 | destroy_duration = "90s" 42 | 43 | depends_on = [module.access_level_members] 44 | } 45 | 46 | module "regular_service_perimeter_1" { 47 | source = "terraform-google-modules/vpc-service-controls/google//modules/regular_service_perimeter" 48 | version = "~> 7.1" 49 | 50 | policy = module.access_context_manager_policy.policy_id 51 | perimeter_name = var.perimeter_name 52 | 53 | description = "Perimeter shielding bigquery project" 54 | resources = [var.protected_project_ids["number"]] 55 | access_levels = [module.access_level_members.name] 56 | 57 | restricted_services = ["bigquery.googleapis.com", "storage.googleapis.com"] 58 | 59 | ingress_policies = [ 60 | { 61 | title = "Allow Access from everywhere" 62 | from = { 63 | sources = { 64 | access_levels = ["*"] # Allow Access from everywhere 65 | }, 66 | identities = var.read_bucket_identities 67 | 68 | } 69 | to = { 70 | resources = [ 71 | "*" 72 | ] 73 | operations = { 74 | "storage.googleapis.com" = { 75 | methods = [ 76 | "google.storage.objects.get", 77 | "google.storage.objects.list" 78 | ] 79 | } 80 | } 81 | } 82 | }, 83 | 84 | 85 | { 86 | title = "Allow Access from project" 87 | from = { 88 | sources = { 89 | resources = ["projects/${var.public_project_ids["number"]}"] # Allow Access from project 90 | }, 91 | identity_type = "ANY_SERVICE_ACCOUNT" 92 | 93 | } 94 | to = { 95 | resources = [ 96 | "*" 97 | ] 98 | operations = { 99 | "storage.googleapis.com" = { 100 | methods = [ 101 | "google.storage.objects.get", 102 | "google.storage.objects.list" 103 | ] 104 | } 105 | } 106 | } 107 | }, 108 | { 109 | title = "without from source" 110 | from = { 111 | identities = var.read_bucket_identities 112 | } 113 | to = { 114 | resources = [ 115 | "projects/${var.protected_project_ids["number"]}" 116 | ] 117 | operations = { 118 | "storage.googleapis.com" = { 119 | methods = [ 120 | "google.storage.objects.get", 121 | "google.storage.objects.list" 122 | ] 123 | } 124 | } 125 | } 126 | } 127 | ] 128 | 129 | shared_resources = { 130 | all = [var.protected_project_ids["number"]] 131 | } 132 | 133 | depends_on = [ 134 | module.gcs_buckets, 135 | time_sleep.wait_for_members 136 | ] 137 | } 138 | 139 | module "gcs_buckets" { 140 | source = "terraform-google-modules/cloud-storage/google" 141 | version = "~> 10.0" 142 | project_id = var.public_project_ids["id"] 143 | names = var.buckets_names 144 | randomize_suffix = true 145 | prefix = var.buckets_prefix 146 | set_admin_roles = true 147 | admins = var.members 148 | } 149 | -------------------------------------------------------------------------------- /examples/scoped_example_with_ingress_rule/outputs.tf: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2024 Google LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | output "policy_id" { 18 | description = "Resource name of the AccessPolicy." 19 | value = module.access_context_manager_policy.policy_id 20 | } 21 | 22 | output "policy_name" { 23 | description = "Name of the parent policy" 24 | value = var.policy_name 25 | } 26 | 27 | output "service_perimeter_name" { 28 | description = "Service perimeter name" 29 | value = module.regular_service_perimeter_1.perimeter_name 30 | } 31 | 32 | output "protected_project_id" { 33 | description = "Project id of the project INSIDE the regular service perimeter" 34 | value = var.protected_project_ids["id"] 35 | } 36 | -------------------------------------------------------------------------------- /examples/scoped_example_with_ingress_rule/variables.tf: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2024 Google LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | variable "parent_id" { 18 | description = "The parent of this AccessPolicy in the Cloud Resource Hierarchy. As of now, only organization are accepted as parent." 19 | type = string 20 | } 21 | 22 | variable "policy_name" { 23 | description = "The policy's name." 24 | type = string 25 | } 26 | 27 | variable "protected_project_ids" { 28 | description = "Project id and number of the project INSIDE the regular service perimeter. This map variable expects an \"id\" for the project id and \"number\" key for the project number." 29 | type = object({ id = string, number = number }) 30 | } 31 | 32 | variable "public_project_ids" { 33 | description = "Project id and number of the project OUTSIDE the regular service perimeter. This map variable expects an \"id\" for the project id and \"number\" key for the project number." 34 | type = object({ id = string, number = number }) 35 | } 36 | 37 | variable "members" { 38 | description = "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}" 39 | type = list(string) 40 | } 41 | 42 | variable "regions" { 43 | description = "The request must originate from one of the provided countries/regions. Format: A valid ISO 3166-1 alpha-2 code." 44 | type = list(string) 45 | default = [] 46 | } 47 | 48 | variable "perimeter_name" { 49 | description = "Perimeter name of the Access Policy.." 50 | type = string 51 | default = "regular_perimeter_1" 52 | } 53 | 54 | variable "access_level_name" { 55 | description = "Access level name of the Access Policy." 56 | type = string 57 | default = "terraform_members" 58 | } 59 | 60 | variable "read_bucket_identities" { 61 | description = "List of all identities should get read access on bucket" 62 | type = list(string) 63 | default = [] 64 | } 65 | 66 | variable "buckets_prefix" { 67 | description = "Bucket Prefix" 68 | type = string 69 | default = "test-bucket" 70 | } 71 | 72 | variable "buckets_names" { 73 | description = "Buckets Names as list of strings" 74 | type = list(string) 75 | default = ["bucket1", "bucket2"] 76 | } 77 | 78 | variable "scopes" { 79 | description = "Folder or project on which this policy is applicable. Format: 'folders/FOLDER_ID' or 'projects/PROJECT_NUMBER'" 80 | type = list(string) 81 | default = [] 82 | } 83 | -------------------------------------------------------------------------------- /examples/simple_example/README.md: -------------------------------------------------------------------------------- 1 | # Simple Example 2 | 3 | This example illustrates how to use the `vpc-service-controls` module to configure an org policy, an access level, a regular perimeter and a BigQuery resource inside the regular perimeter. 4 | 5 | # Requirements 6 | 7 | 1. Make sure you've gone through the root [Requirement Section](../../README.md#requirements) on any project in your organization. 8 | 2. If you need to run integration tests for this example, select a second project in your organization. The project you already configured will be referred as the protected project that will be inside of the regular service perimeter. The second project will be the public project, which will be outside of the regular service perimeter. 9 | 3. Grant the service account the following permissions on the protected project: 10 | - roles/bigquery.dataOwner 11 | - roles/bigquery.jobUser 12 | 13 | You may use the following gcloud commands: 14 | `gcloud projects add-iam-policy-binding --member=serviceAccount: --role=roles/bigquery.jobUser` 15 | `gcloud projects add-iam-policy-binding --member=serviceAccount: --role=roles/bigquery.dataOwner` 16 | 17 | 4. Enable BigQuery API on the protected project. 18 | 5. If you want to run the integration tests for this example, repeat step #3 and #4 on the public project. 19 | 20 | 21 | 22 | 23 | ## Inputs 24 | 25 | | Name | Description | Type | Default | Required | 26 | |------|-------------|------|---------|:--------:| 27 | | access\_level\_name | Access level name of the Access Policy. | `string` | `"terraform_members"` | no | 28 | | dataset\_id | Unique dataset ID/name that will be created. | `string` | `"sample_dataset"` | no | 29 | | 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 | 30 | | parent\_id | The parent of this AccessPolicy in the Cloud Resource Hierarchy. As of now, only organization are accepted as parent. | `string` | n/a | yes | 31 | | perimeter\_name | Perimeter name of the Access Policy.. | `string` | `"regular_perimeter_1"` | no | 32 | | policy\_name | The policy's name. | `string` | n/a | yes | 33 | | protected\_project\_ids | Project id and number of the project INSIDE the regular service perimeter. This map variable expects an "id" for the project id and "number" key for the project number. | `object({ id = string, number = number })` | n/a | yes | 34 | | regions | The request must originate from one of the provided countries/regions. Format: A valid ISO 3166-1 alpha-2 code. | `list(string)` | `[]` | no | 35 | 36 | ## Outputs 37 | 38 | | Name | Description | 39 | |------|-------------| 40 | | access\_level\_name | Access level name of the Access Policy. | 41 | | dataset\_id | Unique id for the BigQuery dataset being provisioned | 42 | | dataset\_name | Name of dataset being provisioned | 43 | | policy\_id | Resource name of the AccessPolicy. | 44 | | policy\_name | Name of the parent policy | 45 | | protected\_project\_id | Project id of the project INSIDE the regular service perimeter | 46 | | table\_id | Unique id for the BigQuery table being provisioned | 47 | 48 | 49 | 50 | To provision this example, run the following from within this directory: 51 | - `terraform init` to get the plugins 52 | - `terraform plan` to see the infrastructure plan 53 | - `terraform apply` to apply the infrastructure build 54 | - `terraform destroy` to destroy the built infrastructure 55 | -------------------------------------------------------------------------------- /examples/simple_example/main.tf: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2024 Google LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | module "access_context_manager_policy" { 18 | source = "terraform-google-modules/vpc-service-controls/google" 19 | version = "~> 7.0" 20 | 21 | parent_id = var.parent_id 22 | policy_name = var.policy_name 23 | } 24 | 25 | module "access_level_members" { 26 | source = "terraform-google-modules/vpc-service-controls/google//modules/access_level" 27 | version = "~> 7.0" 28 | 29 | description = "Simple Example Access Level" 30 | policy = module.access_context_manager_policy.policy_id 31 | name = var.access_level_name 32 | members = var.members 33 | regions = var.regions 34 | } 35 | 36 | resource "null_resource" "wait_for_members" { 37 | provisioner "local-exec" { 38 | command = "sleep 60" 39 | } 40 | 41 | depends_on = [module.access_level_members] 42 | } 43 | 44 | module "regular_service_perimeter_1" { 45 | source = "terraform-google-modules/vpc-service-controls/google//modules/regular_service_perimeter" 46 | version = "~> 7.0" 47 | 48 | policy = module.access_context_manager_policy.policy_id 49 | perimeter_name = var.perimeter_name 50 | 51 | description = "Perimeter shielding bigquery project ${null_resource.wait_for_members.id}" 52 | resources = [var.protected_project_ids["number"]] 53 | 54 | access_levels = [module.access_level_members.name] 55 | restricted_services = ["bigquery.googleapis.com", "storage.googleapis.com"] 56 | 57 | shared_resources = { 58 | all = [var.protected_project_ids["number"]] 59 | } 60 | } 61 | 62 | module "bigquery" { 63 | source = "terraform-google-modules/bigquery/google" 64 | version = "~> 10.0" 65 | dataset_id = var.dataset_id 66 | dataset_name = var.dataset_id 67 | description = "Dataset with a single table with one field" 68 | default_table_expiration_ms = "3600000" 69 | project_id = var.protected_project_ids["id"] 70 | location = "US" 71 | access = [] 72 | deletion_protection = false 73 | 74 | tables = [ 75 | { 76 | table_id = "example_table", 77 | schema = file("sample_bq_schema.json") 78 | time_partitioning = { 79 | type = "DAY", 80 | field = null, 81 | require_partition_filter = false, 82 | expiration_ms = null, 83 | }, 84 | range_partitioning = null, 85 | expiration_time = null, 86 | clustering = [], 87 | labels = { 88 | env = "dev" 89 | billable = "true" 90 | owner = "joedoe" 91 | }, 92 | } 93 | ] 94 | } 95 | -------------------------------------------------------------------------------- /examples/simple_example/outputs.tf: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2019 Google LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | output "policy_id" { 18 | description = "Resource name of the AccessPolicy." 19 | value = module.access_context_manager_policy.policy_id 20 | } 21 | 22 | output "policy_name" { 23 | description = "Name of the parent policy" 24 | value = var.policy_name 25 | } 26 | 27 | output "access_level_name" { 28 | description = "Access level name of the Access Policy." 29 | value = var.access_level_name 30 | } 31 | 32 | output "protected_project_id" { 33 | description = "Project id of the project INSIDE the regular service perimeter" 34 | value = var.protected_project_ids["id"] 35 | } 36 | 37 | output "dataset_id" { 38 | description = "Unique id for the BigQuery dataset being provisioned" 39 | value = module.bigquery.bigquery_dataset.dataset_id 40 | } 41 | 42 | output "table_id" { 43 | description = "Unique id for the BigQuery table being provisioned" 44 | value = module.bigquery.table_ids 45 | } 46 | 47 | output "dataset_name" { 48 | description = "Name of dataset being provisioned" 49 | value = module.bigquery.bigquery_dataset.dataset_id 50 | } 51 | -------------------------------------------------------------------------------- /examples/simple_example/sample_bq_schema.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "description": "Example Field", 4 | "mode": "NULLABLE", 5 | "name": "name", 6 | "type": "STRING" 7 | } 8 | ] 9 | 10 | -------------------------------------------------------------------------------- /examples/simple_example/variables.tf: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2019 Google LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | variable "parent_id" { 18 | description = "The parent of this AccessPolicy in the Cloud Resource Hierarchy. As of now, only organization are accepted as parent." 19 | type = string 20 | } 21 | 22 | variable "policy_name" { 23 | description = "The policy's name." 24 | type = string 25 | } 26 | 27 | variable "protected_project_ids" { 28 | description = "Project id and number of the project INSIDE the regular service perimeter. This map variable expects an \"id\" for the project id and \"number\" key for the project number." 29 | type = object({ id = string, number = number }) 30 | } 31 | 32 | variable "members" { 33 | description = "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}" 34 | type = list(string) 35 | } 36 | 37 | variable "regions" { 38 | description = "The request must originate from one of the provided countries/regions. Format: A valid ISO 3166-1 alpha-2 code." 39 | type = list(string) 40 | default = [] 41 | } 42 | 43 | variable "access_level_name" { 44 | description = "Access level name of the Access Policy." 45 | type = string 46 | default = "terraform_members" 47 | } 48 | 49 | variable "perimeter_name" { 50 | description = "Perimeter name of the Access Policy.." 51 | type = string 52 | default = "regular_perimeter_1" 53 | } 54 | variable "dataset_id" { 55 | description = "Unique dataset ID/name that will be created." 56 | type = string 57 | default = "sample_dataset" 58 | } 59 | -------------------------------------------------------------------------------- /examples/simple_example_access_level/README.md: -------------------------------------------------------------------------------- 1 | # Simple Example Access Level 2 | 3 | This example illustrates how to use the `vpc-service-controls` module to configure an org policy and an access level 4 | 5 | # Requirements 6 | 1. Make sure you've gone through the root [Requirement Section](../../#requirements) 7 | 8 | 9 | 10 | 11 | ## Inputs 12 | 13 | | Name | Description | Type | Default | Required | 14 | |------|-------------|------|---------|:--------:| 15 | | ip\_subnetworks | A list of CIDR block IP subnetwork specification. May be IPv4 or IPv6. Note that for a CIDR IP address block, the specified IP address portion must be properly truncated (i.e. all the host bits must be zero) or the input is considered malformed. For example, "192.0.2.0/24" is accepted but "192.0.2.1/24" is not. Similarly, for IPv6, "2001:db8::/32" is accepted whereas "2001:db8::1/32" is not. The originating IP of a request must be in one of the listed subnets in order for this Condition to be true. If empty, all IP addresses are allowed. | `list(string)` | n/a | yes | 16 | | parent\_id | The parent of this AccessPolicy in the Cloud Resource Hierarchy. As of now, only organization are accepted as parent. | `string` | n/a | yes | 17 | | policy\_name | The policy's name. | `string` | n/a | yes | 18 | | protected\_project\_id | Project number of the project INSIDE the regular service perimeter. | `number` | n/a | yes | 19 | 20 | ## Outputs 21 | 22 | | Name | Description | 23 | |------|-------------| 24 | | policy\_name | n/a | 25 | 26 | 27 | -------------------------------------------------------------------------------- /examples/simple_example_access_level/main.tf: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2024 Google LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | module "access_context_manager_policy" { 18 | source = "terraform-google-modules/vpc-service-controls/google" 19 | version = "~> 7.0" 20 | 21 | parent_id = var.parent_id 22 | policy_name = var.policy_name 23 | } 24 | 25 | module "access_level_1" { 26 | source = "terraform-google-modules/vpc-service-controls/google//modules/access_level" 27 | version = "~> 7.0" 28 | 29 | policy = module.access_context_manager_policy.policy_id 30 | name = "single_ip_policy" 31 | ip_subnetworks = var.ip_subnetworks 32 | description = "Some description" 33 | } 34 | 35 | module "regular_service_perimeter_1" { 36 | source = "terraform-google-modules/vpc-service-controls/google//modules/regular_service_perimeter" 37 | version = "~> 7.0" 38 | 39 | policy = module.access_context_manager_policy.policy_id 40 | perimeter_name = "regular_perimeter_1" 41 | description = "Some description" 42 | resources = [var.protected_project_id] 43 | 44 | restricted_services = ["bigquery.googleapis.com", "storage.googleapis.com"] 45 | 46 | access_levels = [module.access_level_1.name] 47 | 48 | shared_resources = { 49 | all = [var.protected_project_id] 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /examples/simple_example_access_level/outputs.tf: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2024 Google LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | output "policy_name" { 18 | value = var.policy_name 19 | } 20 | -------------------------------------------------------------------------------- /examples/simple_example_access_level/variables.tf: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2024 Google LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | variable "parent_id" { 18 | description = "The parent of this AccessPolicy in the Cloud Resource Hierarchy. As of now, only organization are accepted as parent." 19 | type = string 20 | } 21 | 22 | variable "policy_name" { 23 | description = "The policy's name." 24 | type = string 25 | } 26 | 27 | variable "protected_project_id" { 28 | description = "Project number of the project INSIDE the regular service perimeter." 29 | type = number 30 | } 31 | 32 | variable "ip_subnetworks" { 33 | description = "A list of CIDR block IP subnetwork specification. May be IPv4 or IPv6. Note that for a CIDR IP address block, the specified IP address portion must be properly truncated (i.e. all the host bits must be zero) or the input is considered malformed. For example, \"192.0.2.0/24\" is accepted but \"192.0.2.1/24\" is not. Similarly, for IPv6, \"2001:db8::/32\" is accepted whereas \"2001:db8::1/32\" is not. The originating IP of a request must be in one of the listed subnets in order for this Condition to be true. If empty, all IP addresses are allowed." 34 | type = list(string) 35 | } 36 | -------------------------------------------------------------------------------- /examples/simple_example_access_level_dry_run/README.md: -------------------------------------------------------------------------------- 1 | # Simple Example Access Level with Dry-run policy 2 | 3 | This example illustrates how to use the `vpc-service-controls` module to configure an org policy and an access level. 4 | 5 | # Requirements 6 | 1. Make sure you've gone through the root [Requirement Section](../../#requirements) 7 | 8 | 9 | 10 | 11 | ## Inputs 12 | 13 | | Name | Description | Type | Default | Required | 14 | |------|-------------|------|---------|:--------:| 15 | | ip\_subnetworks | A list of CIDR block IP subnetwork specification. May be IPv4 or IPv6. Note that for a CIDR IP address block, the specified IP address portion must be properly truncated (i.e. all the host bits must be zero) or the input is considered malformed. For example, "192.0.2.0/24" is accepted but "192.0.2.1/24" is not. Similarly, for IPv6, "2001:db8::/32" is accepted whereas "2001:db8::1/32" is not. The originating IP of a request must be in one of the listed subnets in order for this Condition to be true. If empty, all IP addresses are allowed. | `list(string)` | n/a | yes | 16 | | parent\_id | The parent of this AccessPolicy in the Cloud Resource Hierarchy. As of now, only organization are accepted as parent. | `string` | n/a | yes | 17 | | policy\_name | The policy's name. | `string` | n/a | yes | 18 | | protected\_project\_id | Project number of the project INSIDE the regular service perimeter. | `number` | n/a | yes | 19 | 20 | ## Outputs 21 | 22 | | Name | Description | 23 | |------|-------------| 24 | | policy\_name | n/a | 25 | 26 | 27 | -------------------------------------------------------------------------------- /examples/simple_example_access_level_dry_run/main.tf: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2024 Google LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | module "access_context_manager_policy" { 18 | source = "terraform-google-modules/vpc-service-controls/google" 19 | version = "~> 7.0" 20 | 21 | parent_id = var.parent_id 22 | policy_name = var.policy_name 23 | } 24 | 25 | module "access_level_1" { 26 | source = "terraform-google-modules/vpc-service-controls/google//modules/access_level" 27 | version = "~> 7.0" 28 | 29 | policy = module.access_context_manager_policy.policy_id 30 | name = "single_ip_policy" 31 | ip_subnetworks = var.ip_subnetworks 32 | description = "Some description" 33 | } 34 | 35 | module "access_level_2" { 36 | source = "terraform-google-modules/vpc-service-controls/google//modules/access_level" 37 | version = "~> 7.0" 38 | 39 | policy = module.access_context_manager_policy.policy_id 40 | name = "single_ip_policy_dry_run" 41 | ip_subnetworks = var.ip_subnetworks 42 | description = "Some description" 43 | } 44 | 45 | module "regular_service_perimeter_1" { 46 | source = "terraform-google-modules/vpc-service-controls/google//modules/regular_service_perimeter" 47 | version = "~> 7.0" 48 | 49 | policy = module.access_context_manager_policy.policy_id 50 | perimeter_name = "regular_perimeter_1" 51 | description = "Some description" 52 | resources = [var.protected_project_id] 53 | 54 | restricted_services = ["bigquery.googleapis.com", "storage.googleapis.com"] 55 | 56 | access_levels = [module.access_level_1.name] 57 | 58 | 59 | resources_dry_run = [var.protected_project_id] 60 | restricted_services_dry_run = ["storage.googleapis.com"] 61 | access_levels_dry_run = [module.access_level_2.name] 62 | 63 | shared_resources = { 64 | all = [var.protected_project_id] 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /examples/simple_example_access_level_dry_run/outputs.tf: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2024 Google LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | output "policy_name" { 18 | value = var.policy_name 19 | } 20 | -------------------------------------------------------------------------------- /examples/simple_example_access_level_dry_run/variables.tf: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2024 Google LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | variable "parent_id" { 18 | description = "The parent of this AccessPolicy in the Cloud Resource Hierarchy. As of now, only organization are accepted as parent." 19 | type = string 20 | } 21 | 22 | variable "policy_name" { 23 | description = "The policy's name." 24 | type = string 25 | } 26 | 27 | variable "protected_project_id" { 28 | description = "Project number of the project INSIDE the regular service perimeter." 29 | type = number 30 | } 31 | 32 | variable "ip_subnetworks" { 33 | description = "A list of CIDR block IP subnetwork specification. May be IPv4 or IPv6. Note that for a CIDR IP address block, the specified IP address portion must be properly truncated (i.e. all the host bits must be zero) or the input is considered malformed. For example, \"192.0.2.0/24\" is accepted but \"192.0.2.1/24\" is not. Similarly, for IPv6, \"2001:db8::/32\" is accepted whereas \"2001:db8::1/32\" is not. The originating IP of a request must be in one of the listed subnets in order for this Condition to be true. If empty, all IP addresses are allowed." 34 | type = list(string) 35 | } 36 | -------------------------------------------------------------------------------- /examples/simple_example_bridge/README.md: -------------------------------------------------------------------------------- 1 | # Simple Example Bridge 2 | 3 | This example illustrates how to use the `vpc-service-controls` module to configure an org policy, an access level, 2 regular perimeters, a bridge perimeter and a BigQuery resource inside the regular perimeter. 4 | 5 | # Requirements 6 | 1. Make sure you've gone through the root [Requirement Section](../../#requirements) 7 | 2. Select 2 projects in your organization that will part of 2 different regular service perimeters. 8 | 3. Enable the BigQuery API on both projects 9 | 4. Grant the service account the following permissions on both projects: 10 | - roles/bigquery.dataOwner 11 | - roles/bigquery.jobUser 12 | 13 | You may use the following gcloud commands: 14 | `gcloud projects add-iam-policy-binding --member=serviceAccount: --role=roles/bigquery.jobUser` 15 | `gcloud projects add-iam-policy-binding --member=serviceAccount: --role=roles/bigquery.dataOwner` 16 | 17 | 18 | ## Inputs 19 | 20 | | Name | Description | Type | Default | Required | 21 | |------|-------------|------|---------|:--------:| 22 | | parent\_id | The parent of this AccessPolicy in the Cloud Resource Hierarchy. As of now, only organization are accepted as parent. | `string` | n/a | yes | 23 | | policy\_name | The policy's name. | `string` | n/a | yes | 24 | | protected\_project\_ids | Project id and number of the project INSIDE the regular service perimeter. This map variable expects an "id" for the project id and "number" key for the project number. | `object({ id = string, number = number })` | n/a | yes | 25 | | public\_project\_ids | Project id and number of the project OUTSIDE of the regular service perimeter. This variable is only necessary for running integration tests. This map variable expects an "id" for the project id and "number" key for the project number. | `object({ id = string, number = number })` | n/a | yes | 26 | 27 | ## Outputs 28 | 29 | | Name | Description | 30 | |------|-------------| 31 | | policy\_name | n/a | 32 | 33 | 34 | 35 | To provision this example, run the following from within this directory: 36 | - `terraform init` to get the plugins 37 | - `terraform plan` to see the infrastructure plan 38 | - `terraform apply` to apply the infrastructure build 39 | - `terraform destroy` to destroy the built infrastructure 40 | -------------------------------------------------------------------------------- /examples/simple_example_bridge/main.tf: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2024 Google LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | module "access_context_manager_policy" { 18 | source = "terraform-google-modules/vpc-service-controls/google" 19 | version = "~> 7.0" 20 | 21 | parent_id = var.parent_id 22 | policy_name = var.policy_name 23 | } 24 | 25 | module "bridge_service_perimeter_1" { 26 | source = "terraform-google-modules/vpc-service-controls/google//modules/bridge_service_perimeter" 27 | version = "~> 7.0" 28 | 29 | policy = module.access_context_manager_policy.policy_id 30 | perimeter_name = "bridge_perimeter_1" 31 | description = "Some description" 32 | 33 | resources = concat( 34 | module.regular_service_perimeter_1.shared_resources["all"], 35 | module.regular_service_perimeter_2.shared_resources["all"], 36 | ) 37 | } 38 | 39 | module "regular_service_perimeter_1" { 40 | source = "terraform-google-modules/vpc-service-controls/google//modules/regular_service_perimeter" 41 | version = "~> 7.0" 42 | 43 | policy = module.access_context_manager_policy.policy_id 44 | perimeter_name = "regular_perimeter_1" 45 | description = "Some description" 46 | resources = [var.protected_project_ids["number"]] 47 | 48 | restricted_services = ["bigquery.googleapis.com", "storage.googleapis.com"] 49 | 50 | shared_resources = { 51 | all = [var.protected_project_ids["number"]] 52 | } 53 | } 54 | 55 | module "regular_service_perimeter_2" { 56 | source = "terraform-google-modules/vpc-service-controls/google//modules/regular_service_perimeter" 57 | version = "~> 7.0" 58 | 59 | policy = module.access_context_manager_policy.policy_id 60 | perimeter_name = "regular_perimeter_2" 61 | description = "Some description" 62 | resources = [var.public_project_ids["number"]] 63 | 64 | restricted_services = ["storage.googleapis.com"] 65 | 66 | shared_resources = { 67 | all = [var.public_project_ids["number"]] 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /examples/simple_example_bridge/outputs.tf: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2024 Google LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | output "policy_name" { 18 | value = var.policy_name 19 | } 20 | -------------------------------------------------------------------------------- /examples/simple_example_bridge/variables.tf: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2024 Google LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | variable "parent_id" { 18 | description = "The parent of this AccessPolicy in the Cloud Resource Hierarchy. As of now, only organization are accepted as parent." 19 | type = string 20 | } 21 | 22 | variable "policy_name" { 23 | description = "The policy's name." 24 | type = string 25 | } 26 | 27 | variable "protected_project_ids" { 28 | description = "Project id and number of the project INSIDE the regular service perimeter. This map variable expects an \"id\" for the project id and \"number\" key for the project number." 29 | type = object({ id = string, number = number }) 30 | } 31 | 32 | variable "public_project_ids" { 33 | description = "Project id and number of the project OUTSIDE of the regular service perimeter. This variable is only necessary for running integration tests. This map variable expects an \"id\" for the project id and \"number\" key for the project number." 34 | type = object({ id = string, number = number }) 35 | } 36 | -------------------------------------------------------------------------------- /examples/simple_example_dynamic/README.md: -------------------------------------------------------------------------------- 1 | # Simple Example Dynamic 2 | 3 | This example illustrates how to use the module to create two perimeters, with projects inside of them, and a bridge between the two. 4 | 5 | 6 | ## Inputs 7 | 8 | | Name | Description | Type | Default | Required | 9 | |------|-------------|------|---------|:--------:| 10 | | billing\_account | The billing account to use for creating projects | `string` | n/a | yes | 11 | | parent\_id | The parent of this AccessPolicy in the Cloud Resource Hierarchy. As of now, only organization are accepted as parent. | `string` | n/a | yes | 12 | | policy\_name | The policy's name. | `string` | `"vpc-sc"` | no | 13 | 14 | ## Outputs 15 | 16 | | Name | Description | 17 | |------|-------------| 18 | | policy\_id | The ID of the created policy | 19 | 20 | 21 | 22 | To provision this example, run the following from within this directory: 23 | - `terraform init` to get the plugins 24 | - `terraform plan` to see the infrastructure plan 25 | - `terraform apply` to apply the infrastructure build 26 | - `terraform destroy` to destroy the built infrastructure 27 | -------------------------------------------------------------------------------- /examples/simple_example_dynamic/main.tf: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2022 Google LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | module "access_context_manager_policy" { 18 | source = "terraform-google-modules/vpc-service-controls/google" 19 | version = "~> 7.0" 20 | 21 | parent_id = var.parent_id 22 | policy_name = var.policy_name 23 | } 24 | 25 | module "bridge" { 26 | source = "terraform-google-modules/vpc-service-controls/google//modules/bridge_service_perimeter" 27 | version = "~> 7.0" 28 | 29 | policy = module.access_context_manager_policy.policy_id 30 | perimeter_name = "bridge_perimeter_1" 31 | description = "Some description" 32 | 33 | resources = [module.project_one.project_number, module.project_two.project_number, module.project_three.project_number] 34 | resource_keys = ["one", "two", "three"] 35 | 36 | depends_on = [ 37 | module.regular_service_perimeter_1, 38 | module.regular_service_perimeter_2 39 | ] 40 | } 41 | 42 | module "regular_service_perimeter_1" { 43 | source = "terraform-google-modules/vpc-service-controls/google//modules/regular_service_perimeter" 44 | version = "~> 7.0" 45 | 46 | policy = module.access_context_manager_policy.policy_id 47 | perimeter_name = "regular_perimeter_1" 48 | description = "Some description" 49 | resources = [module.project_one.project_number] 50 | 51 | restricted_services = ["bigquery.googleapis.com", "storage.googleapis.com"] 52 | 53 | shared_resources = { 54 | all = [module.project_one.project_number] 55 | } 56 | } 57 | 58 | module "regular_service_perimeter_2" { 59 | source = "terraform-google-modules/vpc-service-controls/google//modules/regular_service_perimeter" 60 | version = "~> 7.0" 61 | 62 | policy = module.access_context_manager_policy.policy_id 63 | perimeter_name = "regular_perimeter_2" 64 | description = "Some description" 65 | resources = [module.project_two.project_number, module.project_three.project_number] 66 | resource_keys = ["two", "three"] 67 | 68 | restricted_services = ["storage.googleapis.com"] 69 | 70 | shared_resources = { 71 | all = [module.project_two.project_number, module.project_three.project_number] 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /examples/simple_example_dynamic/outputs.tf: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2022 Google LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | output "policy_id" { 18 | description = "The ID of the created policy" 19 | value = module.access_context_manager_policy.policy_id 20 | } 21 | -------------------------------------------------------------------------------- /examples/simple_example_dynamic/projects.tf: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2022 Google LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | module "project_one" { 18 | source = "terraform-google-modules/project-factory/google" 19 | version = "~> 18.0" 20 | 21 | name = "vpcsc-test-one" 22 | random_project_id = true 23 | org_id = var.parent_id 24 | billing_account = var.billing_account 25 | } 26 | 27 | module "project_two" { 28 | source = "terraform-google-modules/project-factory/google" 29 | version = "~> 18.0" 30 | 31 | name = "vpcsc-test-two" 32 | random_project_id = true 33 | org_id = var.parent_id 34 | billing_account = var.billing_account 35 | } 36 | 37 | module "project_three" { 38 | source = "terraform-google-modules/project-factory/google" 39 | version = "~> 18.0" 40 | 41 | name = "vpcsc-test-two" 42 | random_project_id = true 43 | org_id = var.parent_id 44 | billing_account = var.billing_account 45 | } 46 | -------------------------------------------------------------------------------- /examples/simple_example_dynamic/variables.tf: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2022 Google LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | variable "parent_id" { 18 | description = "The parent of this AccessPolicy in the Cloud Resource Hierarchy. As of now, only organization are accepted as parent." 19 | type = string 20 | } 21 | 22 | variable "policy_name" { 23 | description = "The policy's name." 24 | type = string 25 | default = "vpc-sc" 26 | } 27 | 28 | variable "billing_account" { 29 | description = "The billing account to use for creating projects" 30 | type = string 31 | } 32 | -------------------------------------------------------------------------------- /main.tf: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2024 Google LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | resource "google_access_context_manager_access_policy" "access_policy" { 18 | provider = google 19 | 20 | title = var.policy_name 21 | parent = "organizations/${var.parent_id}" 22 | scopes = var.scopes 23 | } 24 | -------------------------------------------------------------------------------- /modules/access_level/README.md: -------------------------------------------------------------------------------- 1 | # Access Level Submodule 2 | 3 | This module handles opiniated configuration and deployment of [access_context_manager_level](https://www.terraform.io/docs/providers/google/r/access_context_manager_access_level.html) resource. 4 | 5 | ## Usage 6 | ```hcl 7 | provider "google" { 8 | version = "~> 2.5.0" 9 | } 10 | 11 | module "org_policy" { 12 | source = "terraform-google-modules/vpc-service-controls/google" 13 | parent_id = var.parent_id 14 | policy_name = var.policy_name 15 | } 16 | 17 | module "access_level_members" { 18 | source = "terraform-google-modules/vpc-service-controls/google//modules/access_level" 19 | policy = module.org_policy.policy_id 20 | name = "terraform_members" 21 | members = ["serviceAccount:", "user:"] 22 | } 23 | ``` 24 | 25 | 26 | ## Inputs 27 | 28 | | Name | Description | Type | Default | Required | 29 | |------|-------------|------|---------|:--------:| 30 | | allowed\_device\_management\_levels | Condition - A list of allowed device management levels. An empty list allows all management levels. | `list(string)` | `[]` | no | 31 | | allowed\_encryption\_statuses | Condition - A list of allowed encryptions statuses. An empty list allows all statuses. | `list(string)` | `[]` | no | 32 | | combining\_function | How the conditions list should be combined to determine if a request is granted this AccessLevel. If AND is used, each Condition must be satisfied for the AccessLevel to be applied. If OR is used, at least one Condition must be satisfied for the AccessLevel to be applied. | `string` | `"AND"` | no | 33 | | description | Description of the access level | `string` | `""` | no | 34 | | ip\_subnetworks | Condition - A list of CIDR block IP subnetwork specification. May be IPv4 or IPv6. Note that for a CIDR IP address block, the specified IP address portion must be properly truncated (i.e. all the host bits must be zero) or the input is considered malformed. For example, "192.0.2.0/24" is accepted but "192.0.2.1/24" is not. Similarly, for IPv6, "2001:db8::/32" is accepted whereas "2001:db8::1/32" is not. The originating IP of a request must be in one of the listed subnets in order for this Condition to be true. If empty, all IP addresses are allowed. | `list(string)` | `[]` | no | 35 | | members | Condition - 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)` | `[]` | no | 36 | | minimum\_version | The minimum allowed OS version. If not set, any version of this OS satisfies the constraint. Format: "major.minor.patch" such as "10.5.301", "9.2.1". | `string` | `""` | no | 37 | | name | Description of the AccessLevel and its use. Does not affect behavior. | `string` | n/a | yes | 38 | | negate | Whether to negate the Condition. If true, the Condition becomes a NAND over its non-empty fields, each field must be false for the Condition overall to be satisfied. | `bool` | `false` | no | 39 | | os\_type | The operating system type of the device. | `string` | `"OS_UNSPECIFIED"` | no | 40 | | policy | Name of the parent policy | `string` | n/a | yes | 41 | | regions | Condition - The request must originate from one of the provided countries/regions. Format: A valid ISO 3166-1 alpha-2 code. | `list(string)` | `[]` | no | 42 | | require\_corp\_owned | Condition - Whether the device needs to be corp owned. | `bool` | `false` | no | 43 | | require\_screen\_lock | Condition - Whether or not screenlock is required for the DevicePolicy to be true. | `bool` | `false` | no | 44 | | required\_access\_levels | Condition - A list of other access levels defined in the same Policy, referenced by resource name. Referencing an AccessLevel which does not exist is an error. All access levels listed must be granted for the Condition to be true. | `list(string)` | `[]` | no | 45 | | vpc\_network\_sources | VPC network's id and list of subnet IP address prefixes . Empty ip\_address\_ranges means all subnets of the VPC. For on-prem IP address connected through VPN/Interconnect, provide the name of VPC which contains cloud router for the VPN/Interconnect and on-prem private IP address prefixes. Cannot specify this field together with ip\_subnetworks |
map(object({
network_id = string
ip_address_ranges = optional(list(string))
}))
| `{}` | no | 46 | 47 | ## Outputs 48 | 49 | | Name | Description | 50 | |------|-------------| 51 | | access\_level | access\_level created | 52 | | name | Description of the AccessLevel and its use. Does not affect behavior. | 53 | | name\_id | The fully-qualified name of the Access Level. Format: accessPolicies/{policy\_id}/accessLevels/{name} | 54 | 55 | 56 | -------------------------------------------------------------------------------- /modules/access_level/main.tf: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2024 Google LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | locals { 18 | output_name = google_access_context_manager_access_level.access_level.name 19 | } 20 | 21 | resource "google_access_context_manager_access_level" "access_level" { 22 | provider = google 23 | parent = "accessPolicies/${var.policy}" 24 | name = "accessPolicies/${var.policy}/accessLevels/${var.name}" 25 | title = var.name 26 | description = var.description 27 | 28 | basic { 29 | conditions { 30 | ip_subnetworks = var.ip_subnetworks 31 | required_access_levels = var.required_access_levels 32 | members = var.members 33 | negate = var.negate 34 | regions = var.regions 35 | 36 | dynamic "device_policy" { 37 | for_each = var.require_corp_owned || var.require_screen_lock || length(var.allowed_encryption_statuses) > 0 || length(var.allowed_device_management_levels) > 0 || var.minimum_version != "" || var.os_type != "OS_UNSPECIFIED" ? [{}] : [] 38 | 39 | content { 40 | require_screen_lock = var.require_screen_lock 41 | allowed_encryption_statuses = var.allowed_encryption_statuses 42 | allowed_device_management_levels = var.allowed_device_management_levels 43 | require_corp_owned = var.require_corp_owned 44 | 45 | os_constraints { 46 | minimum_version = var.minimum_version 47 | os_type = var.os_type 48 | } 49 | } 50 | } 51 | 52 | dynamic "vpc_network_sources" { 53 | for_each = var.vpc_network_sources 54 | content { 55 | vpc_subnetwork { 56 | network = "//compute.googleapis.com/${vpc_network_sources.value.network_id}" 57 | vpc_ip_subnetworks = vpc_network_sources.value.ip_address_ranges 58 | } 59 | } 60 | } 61 | } 62 | 63 | combining_function = var.combining_function 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /modules/access_level/outputs.tf: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2024 Google LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | output "name" { 18 | description = "Description of the AccessLevel and its use. Does not affect behavior." 19 | value = split("/", local.output_name)[3] 20 | } 21 | 22 | output "name_id" { 23 | description = "The fully-qualified name of the Access Level. Format: accessPolicies/{policy_id}/accessLevels/{name}" 24 | value = local.output_name 25 | } 26 | 27 | output "access_level" { 28 | description = "access_level created" 29 | value = google_access_context_manager_access_level.access_level 30 | } 31 | -------------------------------------------------------------------------------- /modules/access_level/variables.tf: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2024 Google LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | variable "name" { 18 | description = "Description of the AccessLevel and its use. Does not affect behavior." 19 | type = string 20 | } 21 | 22 | variable "policy" { 23 | description = "Name of the parent policy" 24 | type = string 25 | } 26 | 27 | variable "combining_function" { 28 | description = "How the conditions list should be combined to determine if a request is granted this AccessLevel. If AND is used, each Condition must be satisfied for the AccessLevel to be applied. If OR is used, at least one Condition must be satisfied for the AccessLevel to be applied." 29 | type = string 30 | default = "AND" 31 | } 32 | 33 | variable "description" { 34 | description = "Description of the access level" 35 | type = string 36 | default = "" 37 | } 38 | 39 | variable "ip_subnetworks" { 40 | description = "Condition - A list of CIDR block IP subnetwork specification. May be IPv4 or IPv6. Note that for a CIDR IP address block, the specified IP address portion must be properly truncated (i.e. all the host bits must be zero) or the input is considered malformed. For example, \"192.0.2.0/24\" is accepted but \"192.0.2.1/24\" is not. Similarly, for IPv6, \"2001:db8::/32\" is accepted whereas \"2001:db8::1/32\" is not. The originating IP of a request must be in one of the listed subnets in order for this Condition to be true. If empty, all IP addresses are allowed." 41 | type = list(string) 42 | default = [] 43 | } 44 | 45 | variable "vpc_network_sources" { 46 | description = "VPC network's id and list of subnet IP address prefixes . Empty ip_address_ranges means all subnets of the VPC. For on-prem IP address connected through VPN/Interconnect, provide the name of VPC which contains cloud router for the VPN/Interconnect and on-prem private IP address prefixes. Cannot specify this field together with ip_subnetworks" 47 | type = map(object({ 48 | network_id = string 49 | ip_address_ranges = optional(list(string)) 50 | })) 51 | default = {} 52 | } 53 | 54 | variable "required_access_levels" { 55 | description = "Condition - A list of other access levels defined in the same Policy, referenced by resource name. Referencing an AccessLevel which does not exist is an error. All access levels listed must be granted for the Condition to be true." 56 | type = list(string) 57 | default = [] 58 | } 59 | 60 | variable "members" { 61 | description = "Condition - 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}" 62 | type = list(string) 63 | default = [] 64 | } 65 | 66 | variable "regions" { 67 | description = "Condition - The request must originate from one of the provided countries/regions. Format: A valid ISO 3166-1 alpha-2 code." 68 | type = list(string) 69 | default = [] 70 | } 71 | 72 | variable "negate" { 73 | description = "Whether to negate the Condition. If true, the Condition becomes a NAND over its non-empty fields, each field must be false for the Condition overall to be satisfied." 74 | type = bool 75 | default = false 76 | } 77 | 78 | variable "require_screen_lock" { 79 | description = "Condition - Whether or not screenlock is required for the DevicePolicy to be true." 80 | type = bool 81 | default = false 82 | } 83 | 84 | variable "require_corp_owned" { 85 | description = "Condition - Whether the device needs to be corp owned." 86 | type = bool 87 | default = false 88 | } 89 | 90 | variable "allowed_encryption_statuses" { 91 | description = "Condition - A list of allowed encryptions statuses. An empty list allows all statuses." 92 | type = list(string) 93 | default = [] 94 | } 95 | 96 | variable "allowed_device_management_levels" { 97 | description = "Condition - A list of allowed device management levels. An empty list allows all management levels." 98 | type = list(string) 99 | default = [] 100 | } 101 | 102 | variable "minimum_version" { 103 | description = "The minimum allowed OS version. If not set, any version of this OS satisfies the constraint. Format: \"major.minor.patch\" such as \"10.5.301\", \"9.2.1\"." 104 | type = string 105 | default = "" 106 | } 107 | 108 | variable "os_type" { 109 | description = "The operating system type of the device." 110 | type = string 111 | default = "OS_UNSPECIFIED" 112 | } 113 | -------------------------------------------------------------------------------- /modules/access_level/versions.tf: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2024 Google LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | terraform { 18 | required_version = ">= 1.3" 19 | required_providers { 20 | 21 | google = { 22 | source = "hashicorp/google" 23 | version = ">= 5.4, < 7" 24 | } 25 | } 26 | 27 | provider_meta "google" { 28 | module_name = "blueprints/terraform/terraform-google-vpc-service-controls:access_level/v7.0.0" 29 | } 30 | 31 | } 32 | -------------------------------------------------------------------------------- /modules/bridge_service_perimeter/README.md: -------------------------------------------------------------------------------- 1 | # Access Bridge Submodule 2 | 3 | This module handles opiniated configuration and deployment of a [access_context_manager_service_perimeter](https://www.terraform.io/docs/providers/google/r/access_context_manager_service_perimeter.html) resource for bridge service perimeter types. 4 | 5 | ## Usage 6 | ```hcl 7 | provider "google" { 8 | version = "~> 2.5.0" 9 | } 10 | 11 | module "org_policy" { 12 | source = "terraform-google-modules/vpc-service-controls/google" 13 | parent_id = var.parent_id 14 | policy_name = var.policy_name 15 | } 16 | 17 | module "bridge_service_perimeter_1" { 18 | source = "terraform-google-modules/vpc-service-controls/google//modules/bridge_service_perimeter" 19 | policy = module.org_policy.policy_id 20 | perimeter_name = "bridge_perimeter_1" 21 | description = "Some description" 22 | 23 | resources = [ 24 | module.regular_service_perimeter_1.shared_resources["all"], 25 | module.regular_service_perimeter_2.shared_resources["all"], 26 | ] 27 | } 28 | 29 | module "regular_service_perimeter_1" { 30 | source = "terraform-google-modules/vpc-service-controls/google//modules/regular_service_perimeter" 31 | policy = module.org_policy.policy_id 32 | perimeter_name = "regular_perimeter_1" 33 | description = "Some description" 34 | resources = ["1111111111"] 35 | 36 | restricted_services = ["bigquery.googleapis.com", "storage.googleapis.com"] 37 | 38 | shared_resources = { 39 | all = ["1111111111"] 40 | } 41 | } 42 | 43 | module "regular_service_perimeter_2" { 44 | source = "terraform-google-modules/vpc-service-controls/google//modules/regular_service_perimeter" 45 | policy = module.org_policy.policy_id 46 | perimeter_name = "regular_perimeter_2" 47 | description = "Some description" 48 | resources = ["222222222222"] 49 | 50 | restricted_services = ["storage.googleapis.com"] 51 | 52 | shared_resources = { 53 | all = ["222222222222"] 54 | } 55 | } 56 | ``` 57 | 58 | 59 | ## Inputs 60 | 61 | | Name | Description | Type | Default | Required | 62 | |------|-------------|------|---------|:--------:| 63 | | description | Description of the bridge perimeter | `string` | `""` | no | 64 | | perimeter\_name | Name of the perimeter. Should be one unified string. Must only be letters, numbers and underscores | `string` | n/a | yes | 65 | | policy | Name of the parent policy | `string` | n/a | yes | 66 | | 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 | 67 | | resources | A list of GCP resources that are inside of the service perimeter. Currently only projects are allowed. | `list(string)` | n/a | yes | 68 | 69 | ## Outputs 70 | 71 | | Name | Description | 72 | |------|-------------| 73 | | resources | A list of GCP resources that are inside of the service perimeter. Currently only projects are allowed. | 74 | 75 | 76 | -------------------------------------------------------------------------------- /modules/bridge_service_perimeter/main.tf: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2019 Google LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | resource "google_access_context_manager_service_perimeter" "bridge_service_perimeter" { 18 | provider = google 19 | parent = "accessPolicies/${var.policy}" 20 | perimeter_type = "PERIMETER_TYPE_BRIDGE" 21 | name = "accessPolicies/${var.policy}/servicePerimeters/${var.perimeter_name}" 22 | title = var.perimeter_name 23 | description = var.description 24 | 25 | lifecycle { 26 | ignore_changes = [status] 27 | } 28 | } 29 | 30 | locals { 31 | resource_keys = var.resource_keys != null ? var.resource_keys : var.resources 32 | resources = { 33 | for rk in local.resource_keys : 34 | rk => var.resources[index(local.resource_keys, rk)] 35 | } 36 | } 37 | 38 | resource "google_access_context_manager_service_perimeter_resource" "service_perimeter_resource" { 39 | for_each = local.resources 40 | perimeter_name = google_access_context_manager_service_perimeter.bridge_service_perimeter.name 41 | resource = "projects/${each.value}" 42 | } 43 | -------------------------------------------------------------------------------- /modules/bridge_service_perimeter/outputs.tf: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2019 Google LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | output "resources" { 18 | description = "A list of GCP resources that are inside of the service perimeter. Currently only projects are allowed." 19 | value = var.resources 20 | depends_on = [ 21 | google_access_context_manager_service_perimeter.bridge_service_perimeter, 22 | google_access_context_manager_service_perimeter_resource.service_perimeter_resource 23 | ] 24 | } 25 | -------------------------------------------------------------------------------- /modules/bridge_service_perimeter/variables.tf: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2019 Google LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | variable "policy" { 18 | description = "Name of the parent policy" 19 | type = string 20 | } 21 | 22 | variable "description" { 23 | description = "Description of the bridge perimeter" 24 | type = string 25 | default = "" 26 | } 27 | 28 | variable "perimeter_name" { 29 | description = "Name of the perimeter. Should be one unified string. Must only be letters, numbers and underscores" 30 | type = string 31 | } 32 | 33 | variable "resources" { 34 | description = "A list of GCP resources that are inside of the service perimeter. Currently only projects are allowed." 35 | type = list(string) 36 | } 37 | 38 | variable "resource_keys" { 39 | description = "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." 40 | type = list(string) 41 | default = null 42 | } 43 | -------------------------------------------------------------------------------- /modules/bridge_service_perimeter/versions.tf: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2021 Google LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | terraform { 18 | required_version = ">= 1.3" 19 | required_providers { 20 | 21 | google = { 22 | source = "hashicorp/google" 23 | version = ">= 3.50, < 7" 24 | } 25 | } 26 | 27 | provider_meta "google" { 28 | module_name = "blueprints/terraform/terraform-google-vpc-service-controls:bridge_service_perimeter/v7.0.0" 29 | } 30 | 31 | } 32 | -------------------------------------------------------------------------------- /modules/regular_service_perimeter/main.tf: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2024 Google LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | 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, "*")) 19 | } 20 | 21 | resource "google_access_context_manager_service_perimeter" "regular_service_perimeter" { 22 | provider = google 23 | parent = "accessPolicies/${var.policy}" 24 | perimeter_type = "PERIMETER_TYPE_REGULAR" 25 | name = "accessPolicies/${var.policy}/servicePerimeters/${var.perimeter_name}" 26 | title = var.perimeter_name 27 | description = var.description 28 | 29 | status { 30 | restricted_services = var.restricted_services 31 | access_levels = formatlist( 32 | "accessPolicies/${var.policy}/accessLevels/%s", 33 | var.access_levels 34 | ) 35 | 36 | 37 | 38 | dynamic "vpc_accessible_services" { 39 | for_each = contains(var.vpc_accessible_services, "*") ? [] : [var.vpc_accessible_services] 40 | content { 41 | enable_restriction = true 42 | allowed_services = vpc_accessible_services.value 43 | } 44 | } 45 | } 46 | 47 | 48 | dynamic "spec" { 49 | for_each = local.dry_run ? ["dry-run"] : [] 50 | content { 51 | restricted_services = var.restricted_services_dry_run 52 | resources = [for item in var.resources_dry_run : can(regex("global/networks", item)) ? format("//compute.googleapis.com/%s", item) : format("projects/%s", item)] 53 | access_levels = formatlist( 54 | "accessPolicies/${var.policy}/accessLevels/%s", 55 | var.access_levels_dry_run 56 | ) 57 | 58 | 59 | 60 | dynamic "vpc_accessible_services" { 61 | for_each = contains(var.vpc_accessible_services_dry_run, "*") ? [] : [var.vpc_accessible_services_dry_run] 62 | content { 63 | enable_restriction = true 64 | allowed_services = vpc_accessible_services.value 65 | } 66 | } 67 | } 68 | } 69 | use_explicit_dry_run_spec = local.dry_run 70 | 71 | lifecycle { 72 | ignore_changes = [ 73 | status[0].resources, 74 | status[0].ingress_policies, # Allows ingress policies to be managed by google_access_context_manager_service_perimeter_ingress_policy resources 75 | status[0].egress_policies, # Allows egress policies to be managed by google_access_context_manager_service_perimeter_egress_policy resources 76 | spec[0].resources, 77 | spec[0].ingress_policies, # Allows dry-run ingress policies to be managed by google_access_context_manager_service_perimeter_ingress_policy resources 78 | spec[0].egress_policies # Allows dry-run egress policies to be managed by google_access_context_manager_service_perimeter_egress_policy resources 79 | ] 80 | } 81 | } 82 | 83 | locals { 84 | resource_keys = var.resource_keys != null ? var.resource_keys : var.resources 85 | resources = { 86 | for rk in local.resource_keys : 87 | rk => var.resources[index(local.resource_keys, rk)] 88 | } 89 | } 90 | 91 | resource "google_access_context_manager_service_perimeter_resource" "service_perimeter_resource" { 92 | for_each = local.resources 93 | perimeter_name = google_access_context_manager_service_perimeter.regular_service_perimeter.name 94 | resource = can(regex("global/networks", each.value)) ? "//compute.googleapis.com/${each.value}" : "projects/${each.value}" 95 | } 96 | -------------------------------------------------------------------------------- /modules/regular_service_perimeter/outputs.tf: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2024 Google LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | output "shared_resources" { 18 | description = "A map of lists of resources to share in a Bridge perimeter module. Each list should contain all or a subset of the perimeters resources" 19 | value = var.shared_resources 20 | depends_on = [ 21 | google_access_context_manager_service_perimeter.regular_service_perimeter, 22 | google_access_context_manager_service_perimeter_resource.service_perimeter_resource 23 | ] 24 | } 25 | 26 | output "resources" { 27 | description = "A list of GCP resources that are inside of the service perimeter. Currently only projects are allowed." 28 | value = var.resources 29 | depends_on = [ 30 | google_access_context_manager_service_perimeter.regular_service_perimeter, 31 | google_access_context_manager_service_perimeter_resource.service_perimeter_resource 32 | ] 33 | } 34 | 35 | output "perimeter_name" { 36 | description = "The perimeter's name." 37 | value = var.perimeter_name 38 | depends_on = [ 39 | google_access_context_manager_service_perimeter.regular_service_perimeter, 40 | google_access_context_manager_service_perimeter_resource.service_perimeter_resource 41 | ] 42 | } 43 | -------------------------------------------------------------------------------- /modules/regular_service_perimeter/versions.tf: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2024 Google LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | terraform { 18 | required_version = ">= 1.3" 19 | required_providers { 20 | 21 | google = { 22 | source = "hashicorp/google" 23 | version = ">= 6.21, < 7" 24 | } 25 | } 26 | 27 | provider_meta "google" { 28 | module_name = "blueprints/terraform/terraform-google-vpc-service-controls:regular_service_perimeter/v7.0.0" 29 | } 30 | 31 | } 32 | -------------------------------------------------------------------------------- /outputs.tf: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2024 Google LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | output "policy_id" { 18 | description = "Resource name of the AccessPolicy." 19 | value = google_access_context_manager_access_policy.access_policy.name 20 | } 21 | 22 | output "policy_name" { 23 | description = "The policy's name." 24 | value = var.policy_name 25 | } 26 | -------------------------------------------------------------------------------- /test/.gitignore: -------------------------------------------------------------------------------- 1 | source.sh 2 | -------------------------------------------------------------------------------- /test/fixtures/scoped_example_with_egress_rule/main.tf: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | * Copyright 2025 Google LLC 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | resource "random_string" "suffix" { 19 | length = 6 20 | special = false 21 | upper = false 22 | } 23 | 24 | module "example" { 25 | source = "../../../examples/scoped_example_with_egress_rule" 26 | 27 | parent_id = var.parent_id 28 | policy_name = "int_test_vpc_sc_rules_e_${random_string.suffix.result}" 29 | scopes = var.scopes 30 | 31 | protected_project_ids = var.protected_project_ids 32 | public_project_ids = var.public_project_ids 33 | members = var.members 34 | 35 | buckets_prefix = "egress" 36 | buckets_names = ["directional-rules"] 37 | regions = ["US"] 38 | } 39 | -------------------------------------------------------------------------------- /test/fixtures/scoped_example_with_egress_rule/outputs.tf: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2025 Google LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | output "policy_id" { 18 | description = "Resource name of the AccessPolicy." 19 | value = module.example.policy_id 20 | } 21 | 22 | output "policy_name" { 23 | description = "Name of the parent policy" 24 | value = module.example.policy_name 25 | } 26 | 27 | 28 | output "protected_project_id" { 29 | description = "Project id of the project INSIDE the regular service perimeter" 30 | value = module.example.protected_project_id 31 | } 32 | 33 | output "service_perimeter_name" { 34 | description = "Service perimeter name" 35 | value = module.example.service_perimeter_name 36 | } 37 | -------------------------------------------------------------------------------- /test/fixtures/scoped_example_with_egress_rule/variables.tf: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2025 Google LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | variable "parent_id" { 18 | description = "The parent of this AccessPolicy in the Cloud Resource Hierarchy. As of now, only organization are accepted as parent." 19 | type = string 20 | } 21 | 22 | variable "protected_project_ids" { 23 | description = "Project id and number of the project INSIDE the regular service perimeter. This map variable expects an \"id\" for the project id and \"number\" key for the project number." 24 | type = object({ id = string, number = number }) 25 | } 26 | 27 | variable "public_project_ids" { 28 | description = "Project id and number of the project OUTSIDE the regular service perimeter. This map variable expects an \"id\" for the project id and \"number\" key for the project number." 29 | type = object({ id = string, number = number }) 30 | } 31 | 32 | variable "members" { 33 | description = "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}" 34 | type = list(string) 35 | } 36 | 37 | variable "scopes" { 38 | description = "Folder or project on which this policy is applicable. Format: 'folders/FOLDER_ID' or 'projects/PROJECT_NUMBER'" 39 | type = list(string) 40 | default = [] 41 | } 42 | -------------------------------------------------------------------------------- /test/fixtures/scoped_example_with_ingress_rule/main.tf: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | * Copyright 2025 Google LLC 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | resource "random_string" "suffix" { 19 | length = 6 20 | special = false 21 | upper = false 22 | } 23 | 24 | module "example" { 25 | source = "../../../examples/scoped_example_with_ingress_rule" 26 | 27 | parent_id = var.parent_id 28 | policy_name = "int_test_vpc_sc_rules_i_${random_string.suffix.result}" 29 | scopes = var.scopes 30 | 31 | protected_project_ids = var.protected_project_ids 32 | public_project_ids = var.public_project_ids 33 | members = var.members 34 | 35 | read_bucket_identities = var.members 36 | buckets_prefix = "ingress" 37 | buckets_names = ["directional-rules"] 38 | regions = ["US"] 39 | } 40 | -------------------------------------------------------------------------------- /test/fixtures/scoped_example_with_ingress_rule/outputs.tf: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2025 Google LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | output "policy_id" { 18 | description = "Resource name of the AccessPolicy." 19 | value = module.example.policy_id 20 | } 21 | 22 | output "policy_name" { 23 | description = "Name of the parent policy" 24 | value = module.example.policy_name 25 | } 26 | 27 | output "service_perimeter_name" { 28 | description = "Service perimeter name" 29 | value = module.example.service_perimeter_name 30 | } 31 | 32 | output "protected_project_id" { 33 | description = "Project id of the project INSIDE the regular service perimeter" 34 | value = module.example.protected_project_id 35 | } 36 | -------------------------------------------------------------------------------- /test/fixtures/scoped_example_with_ingress_rule/variables.tf: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2025 Google LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | variable "parent_id" { 18 | description = "The parent of this AccessPolicy in the Cloud Resource Hierarchy. As of now, only organization are accepted as parent." 19 | type = string 20 | } 21 | 22 | variable "protected_project_ids" { 23 | description = "Project id and number of the project INSIDE the regular service perimeter. This map variable expects an \"id\" for the project id and \"number\" key for the project number." 24 | type = object({ id = string, number = number }) 25 | } 26 | 27 | variable "public_project_ids" { 28 | description = "Project id and number of the project OUTSIDE the regular service perimeter. This map variable expects an \"id\" for the project id and \"number\" key for the project number." 29 | type = object({ id = string, number = number }) 30 | } 31 | 32 | variable "members" { 33 | description = "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}" 34 | type = list(string) 35 | } 36 | 37 | variable "scopes" { 38 | description = "Folder or project on which this policy is applicable. Format: 'folders/FOLDER_ID' or 'projects/PROJECT_NUMBER'" 39 | type = list(string) 40 | default = [] 41 | } 42 | -------------------------------------------------------------------------------- /test/fixtures/shared/outputs.tf: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2019 Google LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | output "parent_id" { 18 | value = var.parent_id 19 | } 20 | 21 | output "policy_id" { 22 | value = module.example.policy_id 23 | } 24 | 25 | output "policy_name" { 26 | value = module.example.policy_name 27 | } 28 | 29 | output "access_level_name" { 30 | value = module.example.access_level_name 31 | } 32 | 33 | output "protected_project_id" { 34 | value = var.protected_project_ids["id"] 35 | } 36 | 37 | output "public_project_id" { 38 | value = var.public_project_ids["id"] 39 | } 40 | 41 | output "dataset_name" { 42 | value = module.example.dataset_name 43 | } 44 | -------------------------------------------------------------------------------- /test/fixtures/shared/variables.tf: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2019 Google LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | variable "parent_id" { 18 | description = "The parent of this AccessPolicy in the Cloud Resource Hierarchy. As of now, only organization are accepted as parent." 19 | type = string 20 | } 21 | 22 | variable "protected_project_ids" { 23 | description = "Project id and number of the project within the regular service perimeter" 24 | type = object({ id = string, number = number }) 25 | } 26 | 27 | variable "public_project_ids" { 28 | description = "Project id and number of the project OUTSIDE of the regular service perimeter. This variable is only necessary for running integration tests. This map variable expects an \"id\" for the project id and \"number\" key for the project number." 29 | type = object({ id = string, number = number }) 30 | } 31 | 32 | variable "members" { 33 | description = "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}" 34 | type = list(string) 35 | } 36 | -------------------------------------------------------------------------------- /test/fixtures/simple_example/main.tf: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2019 Google LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | resource "random_id" "random_suffix" { 18 | byte_length = 2 19 | } 20 | 21 | module "example" { 22 | source = "../../../examples/simple_example" 23 | parent_id = var.parent_id 24 | policy_name = "int_test_vpc_sc_policy_${random_id.random_suffix.hex}" 25 | protected_project_ids = var.protected_project_ids 26 | members = var.members 27 | regions = ["US", "CA", "DE"] 28 | access_level_name = "vpc_sc_members_test_${random_id.random_suffix.hex}" 29 | perimeter_name = "perimeter_vpc_sc_test_${random_id.random_suffix.hex}" 30 | dataset_id = "dataset_vpc_sc_test_${random_id.random_suffix.hex}" 31 | } 32 | -------------------------------------------------------------------------------- /test/fixtures/simple_example/outputs.tf: -------------------------------------------------------------------------------- 1 | ../shared/outputs.tf -------------------------------------------------------------------------------- /test/fixtures/simple_example/sample_bq_schema.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "description": "Example Field", 4 | "mode": "NULLABLE", 5 | "name": "name", 6 | "type": "STRING" 7 | } 8 | ] 9 | 10 | -------------------------------------------------------------------------------- /test/fixtures/simple_example/variables.tf: -------------------------------------------------------------------------------- 1 | ../shared/variables.tf -------------------------------------------------------------------------------- /test/fixtures/simple_example_bridge/main.tf: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | * Copyright 2019 Google LLC 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | resource "random_id" "random_suffix" { 19 | byte_length = 2 20 | } 21 | 22 | module "example_bridge" { 23 | source = "../../../examples/simple_example_bridge" 24 | parent_id = var.parent_id 25 | policy_name = "int_test_vpc_sc_bridge_policy_${random_id.random_suffix.hex}" 26 | protected_project_ids = var.protected_project_ids 27 | public_project_ids = var.public_project_ids 28 | } 29 | -------------------------------------------------------------------------------- /test/fixtures/simple_example_bridge/outputs.tf: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2019 Google LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | output "policy_name" { 18 | value = module.example_bridge.policy_name 19 | } 20 | 21 | output "parent_id" { 22 | value = var.parent_id 23 | } 24 | -------------------------------------------------------------------------------- /test/fixtures/simple_example_bridge/variables.tf: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2019 Google LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | variable "parent_id" { 18 | description = "The parent of this AccessPolicy in the Cloud Resource Hierarchy. As of now, only organization are accepted as parent." 19 | type = string 20 | } 21 | 22 | variable "protected_project_ids" { 23 | description = "Project id and number of the project INSIDE the regular service perimeter. This map variable expects an \"id\" for the project id and \"number\" key for the project number." 24 | type = object({ id = string, number = number }) 25 | } 26 | 27 | variable "public_project_ids" { 28 | description = "Project id and number of the project OUTSIDE of the regular service perimeter. This variable is only necessary for running integration tests. This map variable expects an \"id\" for the project id and \"number\" key for the project number." 29 | type = object({ id = string, number = number }) 30 | } 31 | -------------------------------------------------------------------------------- /test/integration/discover_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2023 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package test 16 | 17 | import ( 18 | "testing" 19 | 20 | "github.com/GoogleCloudPlatform/cloud-foundation-toolkit/infra/blueprint-test/pkg/tft" 21 | ) 22 | 23 | func TestAll(t *testing.T) { 24 | tft.AutoDiscoverAndTest(t) 25 | } 26 | -------------------------------------------------------------------------------- /test/integration/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/GoogleCloudPlatform/terraform-google-vpc-service-controls/test/integration 2 | 3 | go 1.23.0 4 | 5 | toolchain go1.23.9 6 | 7 | require github.com/GoogleCloudPlatform/cloud-foundation-toolkit/infra/blueprint-test v0.17.6 8 | 9 | require ( 10 | github.com/agext/levenshtein v1.2.3 // indirect 11 | github.com/alexflint/go-filemutex v1.3.0 // indirect 12 | github.com/apparentlymart/go-textseg/v15 v15.0.0 // indirect 13 | github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d // indirect 14 | github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect 15 | github.com/go-errors/errors v1.5.0 // indirect 16 | github.com/go-openapi/jsonpointer v0.21.0 // indirect 17 | github.com/go-openapi/jsonreference v0.20.2 // indirect 18 | github.com/go-openapi/swag v0.23.0 // indirect 19 | github.com/google/gnostic-models v0.6.9 // indirect 20 | github.com/google/go-cmp v0.6.0 // indirect 21 | github.com/gruntwork-io/terratest v0.48.2 // indirect 22 | github.com/hashicorp/errwrap v1.1.0 // indirect 23 | github.com/hashicorp/go-cleanhttp v0.5.2 // indirect 24 | github.com/hashicorp/go-getter/v2 v2.2.3 // indirect 25 | github.com/hashicorp/go-multierror v1.1.1 // indirect 26 | github.com/hashicorp/go-safetemp v1.0.0 // indirect 27 | github.com/hashicorp/go-version v1.7.0 // indirect 28 | github.com/hashicorp/hcl v0.0.0-20170504190234-a4b07c25de5f // indirect 29 | github.com/hashicorp/hcl/v2 v2.22.0 // indirect 30 | github.com/hashicorp/terraform-config-inspect v0.0.0-20250203082807-efaa306e97b4 // indirect 31 | github.com/hashicorp/terraform-json v0.24.0 // indirect 32 | github.com/jinzhu/copier v0.4.0 // indirect 33 | github.com/josharian/intern v1.0.0 // indirect 34 | github.com/klauspost/compress v1.16.7 // indirect 35 | github.com/mailru/easyjson v0.7.7 // indirect 36 | github.com/mattn/go-shellwords v1.0.12 // indirect 37 | github.com/mattn/go-zglob v0.0.4 // indirect 38 | github.com/mitchellh/go-homedir v1.1.0 // indirect 39 | github.com/mitchellh/go-testing-interface v1.14.2-0.20210821155943-2d9075ca8770 // indirect 40 | github.com/mitchellh/go-wordwrap v1.0.1 // indirect 41 | github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect 42 | github.com/stretchr/testify v1.10.0 // indirect 43 | github.com/tidwall/gjson v1.18.0 // indirect 44 | github.com/tidwall/match v1.1.1 // indirect 45 | github.com/tidwall/pretty v1.2.1 // indirect 46 | github.com/tidwall/sjson v1.2.5 // indirect 47 | github.com/tmccombs/hcl2json v0.6.4 // indirect 48 | github.com/ulikunitz/xz v0.5.11 // indirect 49 | github.com/zclconf/go-cty v1.15.1 // indirect 50 | golang.org/x/crypto v0.36.0 // indirect 51 | golang.org/x/mod v0.23.0 // indirect 52 | golang.org/x/net v0.38.0 // indirect 53 | golang.org/x/sync v0.12.0 // indirect 54 | golang.org/x/sys v0.31.0 // indirect 55 | golang.org/x/text v0.23.0 // indirect 56 | golang.org/x/tools v0.26.0 // indirect 57 | google.golang.org/protobuf v1.35.1 // indirect 58 | gopkg.in/yaml.v3 v3.0.1 // indirect 59 | k8s.io/kube-openapi v0.0.0-20241212222426-2c72e554b1e7 // indirect 60 | sigs.k8s.io/kustomize/kyaml v0.19.0 // indirect 61 | sigs.k8s.io/yaml v1.4.0 // indirect 62 | ) 63 | -------------------------------------------------------------------------------- /test/integration/scoped_example_with_egress_rule/scoped_example_with_egress_rule_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2025 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package scoped_example_with_egress_rule_test 16 | 17 | import ( 18 | "fmt" 19 | "testing" 20 | "time" 21 | 22 | "github.com/tidwall/gjson" 23 | 24 | "github.com/GoogleCloudPlatform/cloud-foundation-toolkit/infra/blueprint-test/pkg/gcloud" 25 | "github.com/GoogleCloudPlatform/cloud-foundation-toolkit/infra/blueprint-test/pkg/tft" 26 | "github.com/gruntwork-io/terratest/modules/terraform" 27 | "github.com/stretchr/testify/assert" 28 | ) 29 | 30 | var ( 31 | RetryableTransientErrors = map[string]string{ 32 | // Editing VPC Service Controls is eventually consistent. 33 | ".*Error 403.*Request is prohibited by organization's policy.*vpcServiceControlsUniqueIdentifier.*": "Request is prohibited by organization's policy.", 34 | } 35 | ) 36 | 37 | // getResultFieldStrSlice parses a field of a results list into a string slice 38 | func GetResultFieldStrSlice(rs []gjson.Result, field string) []string { 39 | s := make([]string, 0) 40 | for _, r := range rs { 41 | s = append(s, r.Get(field).String()) 42 | } 43 | return s 44 | } 45 | 46 | func TestScopedExampleWithEgressRule(t *testing.T) { 47 | 48 | expectedPermissions := []string{ 49 | "bigquery.datasets.get", 50 | "bigquery.models.getData", 51 | "bigquery.models.getMetadata", 52 | "bigquery.models.list", 53 | "bigquery.tables.get", 54 | "bigquery.tables.getData", 55 | "bigquery.tables.list", 56 | } 57 | 58 | expectedMethods := []string{ 59 | "google.storage.objects.get", 60 | "google.storage.objects.list", 61 | } 62 | 63 | setup := tft.NewTFBlueprintTest(t, 64 | tft.WithTFDir("../../setup"), 65 | ) 66 | protectedProjectNumber := terraform.OutputMap(t, setup.GetTFOptions(), "protected_project_ids")["number"] 67 | publicProjectNumber := terraform.OutputMap(t, setup.GetTFOptions(), "public_project_ids")["number"] 68 | 69 | bpt := tft.NewTFBlueprintTest(t, 70 | tft.WithRetryableTerraformErrors(RetryableTransientErrors, 6, 2*time.Minute), 71 | ) 72 | 73 | bpt.DefineVerify(func(assert *assert.Assertions) { 74 | bpt.DefaultVerify(assert) 75 | 76 | policyID := bpt.GetStringOutput("policy_id") 77 | scopedPolicy := gcloud.Runf(t, "access-context-manager policies describe %s", policyID) 78 | assert.Equal(fmt.Sprintf("projects/%s", protectedProjectNumber), scopedPolicy.Get("scopes").Array()[0].String(), "scoped project should be %s", protectedProjectNumber) 79 | 80 | servicePerimeterLink := fmt.Sprintf("accessPolicies/%s/servicePerimeters/%s", policyID, bpt.GetStringOutput("service_perimeter_name")) 81 | servicePerimeter := gcloud.Runf(t, "access-context-manager perimeters describe %s --policy %s", servicePerimeterLink, policyID) 82 | 83 | egressPolicies := servicePerimeter.Get("status.egressPolicies").Array() 84 | for _, rule := range egressPolicies { 85 | 86 | from := rule.Get("egressFrom") 87 | assert.Equal("ANY_SERVICE_ACCOUNT", from.Get("identityType").String(), "identityType should be ANY_SERVICE_ACCOUNT") 88 | assert.Equal("SOURCE_RESTRICTION_ENABLED", from.Get("sourceRestriction").String(), "source restriction should be enabled") 89 | assert.Equal(fmt.Sprintf("projects/%s", protectedProjectNumber), from.Get("sources").Array()[0].Get("resource").String(), "source project should be %s", protectedProjectNumber) 90 | 91 | to := rule.Get("egressTo") 92 | resource := to.Get("resources").Array()[0] 93 | assert.Equal(fmt.Sprintf("projects/%s", publicProjectNumber), resource.String(), "to public project should be %s", publicProjectNumber) 94 | operation := to.Get("operations").Array()[0] 95 | if operation.Get("serviceName").String() == "storage.googleapis.com" { 96 | methods := GetResultFieldStrSlice(operation.Get("methodSelectors").Array(), "method") 97 | for _, expected := range expectedMethods { 98 | assert.Contains(methods, expected) 99 | } 100 | assert.Equal("Read outside buckets from project", rule.Get("title").String(), "check title") 101 | } 102 | 103 | if operation.Get("serviceName").String() == "bigquery.googleapis.com" { 104 | permissions := GetResultFieldStrSlice(operation.Get("methodSelectors").Array(), "permission") 105 | for _, expected := range expectedPermissions { 106 | assert.Contains(permissions, expected) 107 | } 108 | assert.Equal("Use permissions for Big Query access", rule.Get("title").String(), "check title") 109 | } 110 | } 111 | }) 112 | bpt.Test() 113 | } 114 | -------------------------------------------------------------------------------- /test/integration/scoped_example_with_ingress_rule/scoped_example_with_ingress_rule_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2025 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package scoped_example_with_ingress_rule_test 16 | 17 | import ( 18 | "fmt" 19 | "testing" 20 | "time" 21 | 22 | "github.com/tidwall/gjson" 23 | 24 | "github.com/GoogleCloudPlatform/cloud-foundation-toolkit/infra/blueprint-test/pkg/gcloud" 25 | "github.com/GoogleCloudPlatform/cloud-foundation-toolkit/infra/blueprint-test/pkg/tft" 26 | "github.com/gruntwork-io/terratest/modules/terraform" 27 | "github.com/stretchr/testify/assert" 28 | ) 29 | 30 | var ( 31 | RetryableTransientErrors = map[string]string{ 32 | // Editing VPC Service Controls is eventually consistent. 33 | ".*Error 403.*Request is prohibited by organization's policy.*vpcServiceControlsUniqueIdentifier.*": "Request is prohibited by organization's policy.", 34 | ".*Error 400.*Invalid Directional Policies set in Perimeter.*Only resources protected by this Service Perimeter can be put in IngressTo.resources.*": "Only resources protected by this Service Perimeter can be put in IngressTo.resources.", 35 | } 36 | ) 37 | 38 | // getResultFieldStrSlice parses a field of a results list into a string slice 39 | func GetResultFieldStrSlice(rs []gjson.Result, field string) []string { 40 | s := make([]string, 0) 41 | for _, r := range rs { 42 | s = append(s, r.Get(field).String()) 43 | } 44 | return s 45 | } 46 | 47 | func TestScopedExampleWithIngressRule(t *testing.T) { 48 | 49 | expectedMethods := []string{ 50 | "google.storage.objects.get", 51 | "google.storage.objects.list", 52 | } 53 | 54 | setup := tft.NewTFBlueprintTest(t, 55 | tft.WithTFDir("../../setup"), 56 | ) 57 | protectedProjectNumber := terraform.OutputMap(t, setup.GetTFOptions(), "protected_project_ids")["number"] 58 | publicProjectNumber := terraform.OutputMap(t, setup.GetTFOptions(), "public_project_ids")["number"] 59 | 60 | bpt := tft.NewTFBlueprintTest(t, 61 | tft.WithRetryableTerraformErrors(RetryableTransientErrors, 6, 2*time.Minute), 62 | ) 63 | 64 | bpt.DefineVerify(func(assert *assert.Assertions) { 65 | bpt.DefaultVerify(assert) 66 | 67 | policyID := bpt.GetStringOutput("policy_id") 68 | scopedPolicy := gcloud.Runf(t, "access-context-manager policies describe %s", policyID) 69 | assert.Equal(fmt.Sprintf("projects/%s", protectedProjectNumber), scopedPolicy.Get("scopes").Array()[0].String(), "scoped project should be %s", protectedProjectNumber) 70 | 71 | servicePerimeterLink := fmt.Sprintf("accessPolicies/%s/servicePerimeters/%s", policyID, bpt.GetStringOutput("service_perimeter_name")) 72 | servicePerimeter := gcloud.Runf(t, "access-context-manager perimeters describe %s --policy %s", servicePerimeterLink, policyID) 73 | 74 | ingressPolicies := servicePerimeter.Get("status.ingressPolicies").Array() 75 | for _, rule := range ingressPolicies { 76 | 77 | from := rule.Get("ingressFrom") 78 | // Cases 79 | if rule.Get("title").String() == "Allow Access from everywhere" { 80 | assert.Equal("*", from.Get("sources").Array()[0].Get("accessLevel").String(), "accessLevel should be '*'") 81 | } 82 | if rule.Get("title").String() == "without from source" { 83 | assert.NotEmpty(from.Get("identities").Array()) 84 | } 85 | if rule.Get("title").String() == "Allow Access from project" { 86 | assert.Equal("ANY_SERVICE_ACCOUNT", from.Get("identityType").String(), "identityType should be ANY_SERVICE_ACCOUNT") 87 | assert.Equal(fmt.Sprintf("projects/%s", publicProjectNumber), from.Get("sources").Array()[0].Get("resource").String(), "source project should be %s", publicProjectNumber) 88 | } 89 | 90 | to := rule.Get("ingressTo") 91 | operation := to.Get("operations").Array()[0] 92 | assert.Equal("storage.googleapis.com", operation.Get("serviceName").String(), "service should be storage.googleapis.com") 93 | methods := GetResultFieldStrSlice(operation.Get("methodSelectors").Array(), "method") 94 | for _, expected := range expectedMethods { 95 | assert.Contains(methods, expected) 96 | } 97 | //cases 98 | resource := to.Get("resources").Array()[0] 99 | if rule.Get("title").String() == "Allow Access from everywhere" { 100 | assert.Equal("*", resource.String(), "should be all projects '*'") 101 | } 102 | if rule.Get("title").String() == "without from source" { 103 | assert.Equal(fmt.Sprintf("projects/%s", protectedProjectNumber), resource.String(), "to protected project should be %s", protectedProjectNumber) 104 | } 105 | if rule.Get("title").String() == "Allow Access from project" { 106 | assert.Equal("*", resource.String(), "should be all projects *") 107 | } 108 | } 109 | }) 110 | bpt.Test() 111 | } 112 | -------------------------------------------------------------------------------- /test/integration/simple_example/controls/gcloud.rb: -------------------------------------------------------------------------------- 1 | # Copyright 2019 Google LLC 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # https://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | protected_project_id = attribute('protected_project_id') 16 | dataset_name = attribute('dataset_name') 17 | table_id = attribute('table_id') 18 | public_project_id = attribute('public_project_id') 19 | policy_id = attribute('policy_id') 20 | access_level_name = attribute('access_level_name') 21 | regions_to_allow = [ 22 | 'US', 23 | 'CA' 24 | ] 25 | 26 | control "big_query_vpc_positive_test" do 27 | describe command("gcloud alpha bq tables show-rows --table=example_table --dataset=#{dataset_name} --limit=10 --project=#{protected_project_id}") do 28 | its(:exit_status) { should be 0 } 29 | its(:stderr) { should eq '' } 30 | end 31 | end 32 | 33 | control "big_query_vpc_negative_test" do 34 | describe command("bq query --use_legacy_sql=false --project_id=#{public_project_id} \'select * from `#{protected_project_id}.#{dataset_name}.example_table` limit 10\'" ) do 35 | 36 | # exit_status should be 1 as this is intentionally triggering an error by accessing the BigQuery data from outside the perimeter. 37 | its(:exit_status) { should be 1 } 38 | its(:stderr) { should eq '' } 39 | its(:stdout) { should include "Request is prohibited by organization's policy." } 40 | end 41 | end 42 | 43 | control "access_level_regions_test" do 44 | describe command("gcloud access-context-manager levels describe #{access_level_name} --policy #{policy_id} --format=json" ) do 45 | its(:exit_status) { should be 0 } 46 | 47 | let(:data) do 48 | if subject.exit_status.zero? 49 | JSON.parse(subject.stdout) 50 | else 51 | {} 52 | end 53 | end 54 | 55 | describe 'allowed regions' do 56 | regions_to_allow.each do |region| 57 | it "#{region} should be allowed" do 58 | expect(data['basic']['conditions'][0]['regions']).to include(region) 59 | end 60 | end 61 | end 62 | 63 | end 64 | end 65 | -------------------------------------------------------------------------------- /test/integration/simple_example/inspec.yml: -------------------------------------------------------------------------------- 1 | # Copyright 2021 Google LLC 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | name: simple_example 16 | attributes: 17 | - name: protected_project_id 18 | required: true 19 | type: string 20 | - name: policy_id 21 | required: true 22 | type: string 23 | - name: access_level_name 24 | required: true 25 | type: string 26 | - name: public_project_id 27 | required: true 28 | type: string 29 | - name: dataset_name 30 | required: true 31 | type: string 32 | - name: table_id 33 | required: true 34 | type: string 35 | -------------------------------------------------------------------------------- /test/integration/simple_example_bridge/controls/gcloud.rb: -------------------------------------------------------------------------------- 1 | # Copyright 2019 Google LLC 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # https://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | policy_name = attribute('policy_name') 16 | org_id = attribute('parent_id') 17 | 18 | control "bridge_policy_test" do 19 | title "Access policy test" 20 | describe command("gcloud access-context-manager policies list --organization=#{org_id} --format=json" ) do 21 | its(:exit_status) { should eq 0 } 22 | its(:stderr) { should eq '' } 23 | let!(:data) do 24 | if subject.exit_status == 0 25 | JSON.parse(subject.stdout) 26 | else 27 | {} 28 | end 29 | end 30 | describe "policy" do 31 | it "has correct title" do 32 | expect(data[0]["title"]).to eq policy_name 33 | end 34 | end 35 | end 36 | end 37 | -------------------------------------------------------------------------------- /test/integration/simple_example_bridge/inspec.yml: -------------------------------------------------------------------------------- 1 | # Copyright 2021 Google LLC 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | name: simple_example_bridge 16 | attributes: 17 | - name: policy_name 18 | required: true 19 | type: string 20 | - name: parent_id 21 | required: true 22 | type: string 23 | -------------------------------------------------------------------------------- /test/setup/.gitignore: -------------------------------------------------------------------------------- 1 | terraform.tfvars 2 | source.sh 3 | -------------------------------------------------------------------------------- /test/setup/iam.tf: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2019 Google LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | locals { 18 | int_required_project_roles = [ 19 | "roles/owner", 20 | ] 21 | 22 | int_required_org_roles = [ 23 | "roles/resourcemanager.organizationViewer", 24 | "roles/accesscontextmanager.policyAdmin", 25 | "roles/iam.securityAdmin", 26 | ] 27 | 28 | policy_sa_required_org_role = "roles/resourcemanager.organizationViewer" 29 | 30 | } 31 | 32 | resource "google_service_account" "int_test" { 33 | project = module.project-vpc-service-controls.project_id 34 | account_id = "vpc-sc-ci-testsi-${random_id.random_suffix.hex}" 35 | display_name = "vpc-service-controls-ci-tests" 36 | } 37 | 38 | resource "google_project_iam_member" "int_test" { 39 | count = length(local.int_required_project_roles) 40 | 41 | project = module.project-vpc-service-controls.project_id 42 | role = local.int_required_project_roles[count.index] 43 | member = "serviceAccount:${google_service_account.int_test.email}" 44 | } 45 | 46 | resource "google_project_iam_member" "bq_roles_0" { 47 | project = module.project-vpc-service-controls-policy-0.project_id 48 | role = "roles/bigquery.admin" 49 | member = "serviceAccount:${google_service_account.int_test.email}" 50 | } 51 | 52 | resource "google_project_iam_member" "bq_roles_1" { 53 | project = module.project-vpc-service-controls-policy-1.project_id 54 | role = "roles/bigquery.admin" 55 | member = "serviceAccount:${google_service_account.int_test.email}" 56 | } 57 | 58 | resource "google_project_iam_member" "storage_roles_0" { 59 | project = module.project-vpc-service-controls-policy-0.project_id 60 | role = "roles/storage.admin" 61 | member = "serviceAccount:${google_service_account.int_test.email}" 62 | } 63 | 64 | resource "google_project_iam_member" "storage_roles_1" { 65 | project = module.project-vpc-service-controls-policy-1.project_id 66 | role = "roles/storage.admin" 67 | member = "serviceAccount:${google_service_account.int_test.email}" 68 | } 69 | 70 | resource "google_service_account_key" "int_test" { 71 | service_account_id = google_service_account.int_test.id 72 | } 73 | 74 | resource "google_organization_iam_member" "int_test_org" { 75 | count = length(local.int_required_org_roles) 76 | 77 | org_id = var.org_id 78 | role = local.int_required_org_roles[count.index] 79 | member = "serviceAccount:${google_service_account.int_test.email}" 80 | } 81 | 82 | resource "google_organization_iam_member" "policy_sa_org" { 83 | count = length(google_service_account.test_policy) 84 | 85 | org_id = var.org_id 86 | role = local.policy_sa_required_org_role 87 | member = "serviceAccount:${google_service_account.test_policy[count.index].email}" 88 | } 89 | 90 | resource "google_service_account" "test_policy" { 91 | count = 2 92 | 93 | project = module.project-vpc-service-controls.project_id 94 | account_id = "test-vpc-sc-policy-${random_id.random_suffix.hex}-${count.index}" 95 | display_name = "test-vpc-sc-policy-${count.index}" 96 | } 97 | 98 | -------------------------------------------------------------------------------- /test/setup/main.tf: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2019 Google LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | resource "random_id" "random_suffix" { 18 | byte_length = 2 19 | } 20 | 21 | module "project-vpc-service-controls" { 22 | source = "terraform-google-modules/project-factory/google" 23 | version = "~> 18.0" 24 | 25 | name = "ci-vpc-sc" 26 | random_project_id = true 27 | org_id = var.org_id 28 | folder_id = var.folder_id 29 | billing_account = var.billing_account 30 | 31 | activate_apis = [ 32 | "cloudresourcemanager.googleapis.com", 33 | "iam.googleapis.com", 34 | "storage-api.googleapis.com", 35 | "serviceusage.googleapis.com", 36 | "accesscontextmanager.googleapis.com", 37 | "bigquery.googleapis.com", 38 | "bigquerystorage.googleapis.com", 39 | "compute.googleapis.com" 40 | ] 41 | } 42 | 43 | module "project-vpc-service-controls-policy-0" { 44 | source = "terraform-google-modules/project-factory/google" 45 | version = "~> 18.0" 46 | 47 | name = "ci-project-policy-test-0" 48 | random_project_id = true 49 | org_id = var.org_id 50 | folder_id = var.folder_id 51 | billing_account = var.billing_account 52 | 53 | activate_apis = [ 54 | "bigquery.googleapis.com", 55 | "bigquerystorage.googleapis.com", 56 | "storage.googleapis.com", 57 | ] 58 | } 59 | 60 | module "project-vpc-service-controls-policy-1" { 61 | source = "terraform-google-modules/project-factory/google" 62 | version = "~> 18.0" 63 | 64 | name = "ci-project-policy-test-1" 65 | random_project_id = true 66 | org_id = var.org_id 67 | folder_id = var.folder_id 68 | billing_account = var.billing_account 69 | 70 | activate_apis = [ 71 | "bigquery.googleapis.com", 72 | "bigquerystorage.googleapis.com", 73 | "storage.googleapis.com", 74 | ] 75 | } 76 | -------------------------------------------------------------------------------- /test/setup/make_source.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # Copyright 2018 Google LLC 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | 17 | echo "#!/usr/bin/env bash" > ../source.sh 18 | 19 | project_id=$(terraform output project_id) 20 | echo "export TF_VAR_project_id=$project_id" >> ../source.sh 21 | 22 | sa_json=$(terraform output sa_key) 23 | # shellcheck disable=SC2086 24 | echo "export SERVICE_ACCOUNT_JSON='$(echo $sa_json | base64 --decode -i)'" >> ../source.sh 25 | 26 | parent_id=$(terraform output parent_id) 27 | echo "export TF_VAR_parent_id=$parent_id" >> ../source.sh 28 | echo "export TF_VAR_org_id=$parent_id" >> ../source.sh 29 | 30 | protected_project_ids=$(terraform output protected_project_ids) 31 | echo "export TF_VAR_protected_project_ids='$protected_project_ids'" >> ../source.sh 32 | 33 | public_project_ids=$(terraform output public_project_ids) 34 | echo "export TF_VAR_public_project_ids='$public_project_ids'" >> ../source.sh 35 | 36 | members=$(terraform output members) 37 | echo "export TF_VAR_members='$members'" >> ../source.sh 38 | -------------------------------------------------------------------------------- /test/setup/outputs.tf: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2019 Google LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | output "project_id" { 18 | value = module.project-vpc-service-controls.project_id 19 | } 20 | 21 | output "sa_key" { 22 | value = google_service_account_key.int_test.private_key 23 | sensitive = true 24 | } 25 | 26 | output "sa_email" { 27 | value = google_service_account.int_test.email 28 | } 29 | 30 | output "parent_id" { 31 | value = var.org_id 32 | } 33 | 34 | output "protected_project_ids" { 35 | value = { 36 | id = module.project-vpc-service-controls-policy-0.project_id 37 | number = module.project-vpc-service-controls-policy-0.project_number 38 | } 39 | } 40 | 41 | output "public_project_ids" { 42 | value = { 43 | id = module.project-vpc-service-controls-policy-1.project_id 44 | number = module.project-vpc-service-controls-policy-1.project_number 45 | } 46 | } 47 | 48 | output "members" { 49 | value = [ 50 | "serviceAccount:${google_service_account.int_test.email}", 51 | "serviceAccount:${google_service_account.test_policy[0].email}", 52 | "serviceAccount:${google_service_account.test_policy[1].email}" 53 | ] 54 | } 55 | 56 | output "scopes" { 57 | value = ["projects/${module.project-vpc-service-controls-policy-0.project_number}"] 58 | } 59 | -------------------------------------------------------------------------------- /test/setup/variables.tf: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2019 Google LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | variable "org_id" { 17 | description = "The numeric organization id" 18 | } 19 | 20 | variable "folder_id" { 21 | description = "The folder to deploy in" 22 | } 23 | 24 | variable "billing_account" { 25 | description = "The billing account id associated with the project, e.g. XXXXXX-YYYYYY-ZZZZZZ" 26 | } 27 | -------------------------------------------------------------------------------- /test/setup/versions.tf: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2019 Google LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | terraform { 18 | required_version = ">= 0.13" 19 | required_providers { 20 | google = { 21 | source = "hashicorp/google" 22 | version = ">= 4.0" 23 | } 24 | google-beta = { 25 | source = "hashicorp/google-beta" 26 | version = ">= 4.0" 27 | } 28 | random = { 29 | source = "hashicorp/random" 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /test/task_helper_functions.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # Copyright 2019 Google LLC 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | 17 | # Delete any exisiting oragization AccessPolicy 18 | # shellcheck disable=SC2154 19 | remove_gcloud_org_accesspolicy() { 20 | source_test_env 21 | init_credentials 22 | 23 | local policy 24 | policy=$(gcloud access-context-manager policies list --organization="${TF_VAR_org_id}" | grep "${TF_VAR_org_id}" | awk '{print $1}') 25 | if [[ -n "${policy}" ]]; then 26 | echo "Removing Access Policy ${policy}" 27 | gcloud access-context-manager policies delete "${policy}" --quiet 28 | fi 29 | } 30 | -------------------------------------------------------------------------------- /variables.tf: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2024 Google LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | variable "parent_id" { 18 | description = "The parent of this AccessPolicy in the Cloud Resource Hierarchy. As of now, only organization are accepted as parent." 19 | type = string 20 | } 21 | 22 | variable "policy_name" { 23 | description = "The policy's name." 24 | type = string 25 | } 26 | 27 | variable "scopes" { 28 | description = "Folder or project on which this policy is applicable. Format: 'folders/FOLDER_ID' or 'projects/PROJECT_NUMBER'" 29 | type = list(string) 30 | default = [] 31 | } 32 | -------------------------------------------------------------------------------- /versions.tf: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2024 Google LLC 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | terraform { 18 | required_version = ">= 1.3" 19 | required_providers { 20 | 21 | google = { 22 | source = "hashicorp/google" 23 | version = ">= 3.62, < 7" 24 | } 25 | } 26 | 27 | provider_meta "google" { 28 | module_name = "blueprints/terraform/terraform-google-vpc-service-controls/v7.0.0" 29 | } 30 | 31 | } 32 | --------------------------------------------------------------------------------