Merge branch 'master' into maunope/static_routes

This commit is contained in:
maunope 2022-11-11 09:39:47 +01:00 committed by GitHub
commit 13238be7ca
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
18 changed files with 752 additions and 842 deletions

View File

@ -5,6 +5,7 @@ All notable changes to this project will be documented in this file.
## [Unreleased] ## [Unreleased]
<!-- None < 2022-09-09 18:02:15+00:00 --> <!-- None < 2022-09-09 18:02:15+00:00 -->
- [[#913](https://github.com/GoogleCloudPlatform/cloud-foundation-fabric/pull/913)] Adding support for PSA ranges, starting with Redis instances. ([aurelienlegrand](https://github.com/aurelienlegrand)) <!-- 2022-11-09 11:07:41+00:00 -->
- [[#939](https://github.com/GoogleCloudPlatform/cloud-foundation-fabric/pull/939)] Temporarily duplicate cloud armor example ([ludoo](https://github.com/ludoo)) <!-- 2022-11-02 09:36:04+00:00 --> - [[#939](https://github.com/GoogleCloudPlatform/cloud-foundation-fabric/pull/939)] Temporarily duplicate cloud armor example ([ludoo](https://github.com/ludoo)) <!-- 2022-11-02 09:36:04+00:00 -->
### BLUEPRINTS ### BLUEPRINTS
@ -63,6 +64,7 @@ All notable changes to this project will be documented in this file.
### FAST ### FAST
- [[#963](https://github.com/GoogleCloudPlatform/cloud-foundation-fabric/pull/963)] **incompatible change:** Refactor vps-sc module for Terraform 1.3 ([ludoo](https://github.com/ludoo)) <!-- 2022-11-10 18:34:45+00:00 -->
- [[#956](https://github.com/GoogleCloudPlatform/cloud-foundation-fabric/pull/956)] FAST: bootstrap and extra stage CI/CD improvements and fixes ([ludoo](https://github.com/ludoo)) <!-- 2022-11-08 08:38:16+00:00 --> - [[#956](https://github.com/GoogleCloudPlatform/cloud-foundation-fabric/pull/956)] FAST: bootstrap and extra stage CI/CD improvements and fixes ([ludoo](https://github.com/ludoo)) <!-- 2022-11-08 08:38:16+00:00 -->
- [[#949](https://github.com/GoogleCloudPlatform/cloud-foundation-fabric/pull/949)] **incompatible change:** Refactor VPC firewall module for Terraform 1.3 ([ludoo](https://github.com/ludoo)) <!-- 2022-11-04 12:56:08+00:00 --> - [[#949](https://github.com/GoogleCloudPlatform/cloud-foundation-fabric/pull/949)] **incompatible change:** Refactor VPC firewall module for Terraform 1.3 ([ludoo](https://github.com/ludoo)) <!-- 2022-11-04 12:56:08+00:00 -->
- [[#943](https://github.com/GoogleCloudPlatform/cloud-foundation-fabric/pull/943)] Update bootstrap README.md with unique project id requirements ([KPRepos](https://github.com/KPRepos)) <!-- 2022-11-03 22:22:22+00:00 --> - [[#943](https://github.com/GoogleCloudPlatform/cloud-foundation-fabric/pull/943)] Update bootstrap README.md with unique project id requirements ([KPRepos](https://github.com/KPRepos)) <!-- 2022-11-03 22:22:22+00:00 -->
@ -92,6 +94,7 @@ All notable changes to this project will be documented in this file.
### MODULES ### MODULES
- [[#963](https://github.com/GoogleCloudPlatform/cloud-foundation-fabric/pull/963)] **incompatible change:** Refactor vps-sc module for Terraform 1.3 ([ludoo](https://github.com/ludoo)) <!-- 2022-11-10 18:34:45+00:00 -->
- [[#958](https://github.com/GoogleCloudPlatform/cloud-foundation-fabric/pull/958)] Add support for org policy custom constraints ([averbuks](https://github.com/averbuks)) <!-- 2022-11-09 09:07:46+00:00 --> - [[#958](https://github.com/GoogleCloudPlatform/cloud-foundation-fabric/pull/958)] Add support for org policy custom constraints ([averbuks](https://github.com/averbuks)) <!-- 2022-11-09 09:07:46+00:00 -->
- [[#960](https://github.com/GoogleCloudPlatform/cloud-foundation-fabric/pull/960)] Fix README typo in firewall module ([valeriobponza](https://github.com/valeriobponza)) <!-- 2022-11-08 23:25:34+00:00 --> - [[#960](https://github.com/GoogleCloudPlatform/cloud-foundation-fabric/pull/960)] Fix README typo in firewall module ([valeriobponza](https://github.com/valeriobponza)) <!-- 2022-11-08 23:25:34+00:00 -->
- [[#953](https://github.com/GoogleCloudPlatform/cloud-foundation-fabric/pull/953)] Added IAM Additive and converted some outputs to static ([muresan](https://github.com/muresan)) <!-- 2022-11-07 13:20:17+00:00 --> - [[#953](https://github.com/GoogleCloudPlatform/cloud-foundation-fabric/pull/953)] Added IAM Additive and converted some outputs to static ([muresan](https://github.com/muresan)) <!-- 2022-11-07 13:20:17+00:00 -->

View File

@ -81,7 +81,7 @@ jobs:
- id: tf-setup - id: tf-setup
name: Set up Terraform name: Set up Terraform
uses: hashicorp/setup-terraform@v1 uses: hashicorp/setup-terraform@v2.0.3
with: with:
terraform_version: $${{ env.TF_VERSION }} terraform_version: $${{ env.TF_VERSION }}
@ -99,14 +99,18 @@ jobs:
name: Terraform plan name: Terraform plan
continue-on-error: true continue-on-error: true
run: | run: |
terraform plan -input=false -out ../plan.out -no-color echo -e "## Plan Output\n\n\`\`\`hcl" >> $$GITHUB_STEP_SUMMARY
terraform plan -input=false -out ../plan.out -no-color |tee -a $$GITHUB_STEP_SUMMARY
echo "\`\`\`" >> $$GITHUB_STEP_SUMMARY
- id: tf-apply - id: tf-apply
if: github.event.pull_request.merged == true if: github.event.pull_request.merged == true
name: Terraform apply name: Terraform apply
continue-on-error: true continue-on-error: true
run: | run: |
terraform apply -input=false -auto-approve -no-color ../plan.out echo -e "## Apply Output\n\n\`\`\`hcl" >> $$GITHUB_STEP_SUMMARY
terraform apply -input=false -auto-approve -no-color ../plan.out |tee -a $$GITHUB_STEP_SUMMARY
echo "\`\`\`" >> $$GITHUB_STEP_SUMMARY
- id: pr-comment - id: pr-comment
name: Post comment to Pull Request name: Post comment to Pull Request
@ -116,8 +120,10 @@ jobs:
PLAN: terraform\n$${{ steps.tf-plan.outputs.stdout }} PLAN: terraform\n$${{ steps.tf-plan.outputs.stdout }}
with: with:
script: | script: |
const output = `#### Terraform Initialization ⚙️\`$${{ steps.tf-init.outcome }}\` const output = `### Terraform Initialization \`$${{ steps.tf-init.outcome }}\`
#### Terraform Validation 🤖\`$${{ steps.tf-validate.outcome }}\`
### Terraform Validation \`$${{ steps.tf-validate.outcome }}\`
<details><summary>Validation Output</summary> <details><summary>Validation Output</summary>
\`\`\`\n \`\`\`\n
@ -126,17 +132,17 @@ jobs:
</details> </details>
#### Terraform Plan 📖\`$${{ steps.tf-plan.outcome }}\` ### Terraform Plan \`$${{ steps.tf-plan.outcome }}\`
<details><summary>Show Plan</summary> <details><summary>Show Plan</summary>
\`\`\`\n \`\`\`\n
$${process.env.PLAN} $${process.env.PLAN.split('\n').filter(l => l.match(/^([A-Z\s].*|)$$/)).join('\n')}
\`\`\` \`\`\`
</details> </details>
#### Terraform Apply 📖\`$${{ steps.tf-apply.outcome }}\` ### Terraform Apply \`$${{ steps.tf-apply.outcome }}\`
*Pusher: @$${{ github.actor }}, Action: \`$${{ github.event_name }}\`, Working Directory: \`$${{ env.tf_actions_working_dir }}\`, Workflow: \`$${{ github.workflow }}\`*`; *Pusher: @$${{ github.actor }}, Action: \`$${{ github.event_name }}\`, Working Directory: \`$${{ env.tf_actions_working_dir }}\`, Workflow: \`$${{ github.workflow }}\`*`;

View File

@ -8,10 +8,10 @@ Legend: <code>+</code> additive, <code>•</code> conditional.
|---|---| |---|---|
|<b>GCP organization domain</b><br><small><i>domain</i></small>|[roles/browser](https://cloud.google.com/iam/docs/understanding-roles#browser) | |<b>GCP organization domain</b><br><small><i>domain</i></small>|[roles/browser](https://cloud.google.com/iam/docs/understanding-roles#browser) |
|<b>gcp-billing-admins</b><br><small><i>group</i></small>|[roles/billing.admin](https://cloud.google.com/iam/docs/understanding-roles#billing.admin) <code>+</code><br>[roles/billing.costsManager](https://cloud.google.com/iam/docs/understanding-roles#billing.costsManager) <code>+</code>| |<b>gcp-billing-admins</b><br><small><i>group</i></small>|[roles/billing.admin](https://cloud.google.com/iam/docs/understanding-roles#billing.admin) <code>+</code><br>[roles/billing.costsManager](https://cloud.google.com/iam/docs/understanding-roles#billing.costsManager) <code>+</code>|
|<b>gcp-devops</b><br><small><i>group</i></small>|[roles/cloudsupport.techSupportEditor](https://cloud.google.com/iam/docs/understanding-roles#cloudsupport.techSupportEditor) <br>[roles/logging.viewer](https://cloud.google.com/iam/docs/understanding-roles#logging.viewer) <br>[roles/monitoring.viewer](https://cloud.google.com/iam/docs/understanding-roles#monitoring.viewer) |
|<b>gcp-network-admins</b><br><small><i>group</i></small>|[roles/cloudasset.owner](https://cloud.google.com/iam/docs/understanding-roles#cloudasset.owner) <br>[roles/cloudsupport.techSupportEditor](https://cloud.google.com/iam/docs/understanding-roles#cloudsupport.techSupportEditor) <br>[roles/compute.orgFirewallPolicyAdmin](https://cloud.google.com/iam/docs/understanding-roles#compute.orgFirewallPolicyAdmin) <code>+</code><br>[roles/compute.xpnAdmin](https://cloud.google.com/iam/docs/understanding-roles#compute.xpnAdmin) <code>+</code>| |<b>gcp-network-admins</b><br><small><i>group</i></small>|[roles/cloudasset.owner](https://cloud.google.com/iam/docs/understanding-roles#cloudasset.owner) <br>[roles/cloudsupport.techSupportEditor](https://cloud.google.com/iam/docs/understanding-roles#cloudsupport.techSupportEditor) <br>[roles/compute.orgFirewallPolicyAdmin](https://cloud.google.com/iam/docs/understanding-roles#compute.orgFirewallPolicyAdmin) <code>+</code><br>[roles/compute.xpnAdmin](https://cloud.google.com/iam/docs/understanding-roles#compute.xpnAdmin) <code>+</code>|
|<b>gcp-organization-admins</b><br><small><i>group</i></small>|[roles/cloudasset.owner](https://cloud.google.com/iam/docs/understanding-roles#cloudasset.owner) <br>[roles/cloudsupport.admin](https://cloud.google.com/iam/docs/understanding-roles#cloudsupport.admin) <br>[roles/compute.osAdminLogin](https://cloud.google.com/iam/docs/understanding-roles#compute.osAdminLogin) <br>[roles/compute.osLoginExternalUser](https://cloud.google.com/iam/docs/understanding-roles#compute.osLoginExternalUser) <br>[roles/owner](https://cloud.google.com/iam/docs/understanding-roles#owner) <br>[roles/resourcemanager.folderAdmin](https://cloud.google.com/iam/docs/understanding-roles#resourcemanager.folderAdmin) <br>[roles/resourcemanager.organizationAdmin](https://cloud.google.com/iam/docs/understanding-roles#resourcemanager.organizationAdmin) <br>[roles/resourcemanager.projectCreator](https://cloud.google.com/iam/docs/understanding-roles#resourcemanager.projectCreator) <br>[roles/billing.admin](https://cloud.google.com/iam/docs/understanding-roles#billing.admin) <code>+</code><br>[roles/billing.costsManager](https://cloud.google.com/iam/docs/understanding-roles#billing.costsManager) <code>+</code><br>[roles/orgpolicy.policyAdmin](https://cloud.google.com/iam/docs/understanding-roles#orgpolicy.policyAdmin) <code>+</code>| |<b>gcp-organization-admins</b><br><small><i>group</i></small>|[roles/cloudasset.owner](https://cloud.google.com/iam/docs/understanding-roles#cloudasset.owner) <br>[roles/cloudsupport.admin](https://cloud.google.com/iam/docs/understanding-roles#cloudsupport.admin) <br>[roles/compute.osAdminLogin](https://cloud.google.com/iam/docs/understanding-roles#compute.osAdminLogin) <br>[roles/compute.osLoginExternalUser](https://cloud.google.com/iam/docs/understanding-roles#compute.osLoginExternalUser) <br>[roles/owner](https://cloud.google.com/iam/docs/understanding-roles#owner) <br>[roles/resourcemanager.folderAdmin](https://cloud.google.com/iam/docs/understanding-roles#resourcemanager.folderAdmin) <br>[roles/resourcemanager.organizationAdmin](https://cloud.google.com/iam/docs/understanding-roles#resourcemanager.organizationAdmin) <br>[roles/resourcemanager.projectCreator](https://cloud.google.com/iam/docs/understanding-roles#resourcemanager.projectCreator) <br>[roles/billing.admin](https://cloud.google.com/iam/docs/understanding-roles#billing.admin) <code>+</code><br>[roles/billing.costsManager](https://cloud.google.com/iam/docs/understanding-roles#billing.costsManager) <code>+</code><br>[roles/orgpolicy.policyAdmin](https://cloud.google.com/iam/docs/understanding-roles#orgpolicy.policyAdmin) <code>+</code>|
|<b>gcp-security-admins</b><br><small><i>group</i></small>|[roles/cloudasset.owner](https://cloud.google.com/iam/docs/understanding-roles#cloudasset.owner) <br>[roles/cloudsupport.techSupportEditor](https://cloud.google.com/iam/docs/understanding-roles#cloudsupport.techSupportEditor) <br>[roles/iam.securityReviewer](https://cloud.google.com/iam/docs/understanding-roles#iam.securityReviewer) <br>[roles/logging.admin](https://cloud.google.com/iam/docs/understanding-roles#logging.admin) <br>[roles/securitycenter.admin](https://cloud.google.com/iam/docs/understanding-roles#securitycenter.admin) <br>[roles/accesscontextmanager.policyAdmin](https://cloud.google.com/iam/docs/understanding-roles#accesscontextmanager.policyAdmin) <code>+</code><br>[roles/iam.organizationRoleAdmin](https://cloud.google.com/iam/docs/understanding-roles#iam.organizationRoleAdmin) <code>+</code><br>[roles/orgpolicy.policyAdmin](https://cloud.google.com/iam/docs/understanding-roles#orgpolicy.policyAdmin) <code>+</code>| |<b>gcp-security-admins</b><br><small><i>group</i></small>|[roles/cloudasset.owner](https://cloud.google.com/iam/docs/understanding-roles#cloudasset.owner) <br>[roles/cloudsupport.techSupportEditor](https://cloud.google.com/iam/docs/understanding-roles#cloudsupport.techSupportEditor) <br>[roles/iam.securityReviewer](https://cloud.google.com/iam/docs/understanding-roles#iam.securityReviewer) <br>[roles/logging.admin](https://cloud.google.com/iam/docs/understanding-roles#logging.admin) <br>[roles/securitycenter.admin](https://cloud.google.com/iam/docs/understanding-roles#securitycenter.admin) <br>[roles/accesscontextmanager.policyAdmin](https://cloud.google.com/iam/docs/understanding-roles#accesscontextmanager.policyAdmin) <code>+</code><br>[roles/iam.organizationRoleAdmin](https://cloud.google.com/iam/docs/understanding-roles#iam.organizationRoleAdmin) <code>+</code><br>[roles/orgpolicy.policyAdmin](https://cloud.google.com/iam/docs/understanding-roles#orgpolicy.policyAdmin) <code>+</code>|
|<b>gcp-support</b><br><small><i>group</i></small>|[roles/cloudsupport.techSupportEditor](https://cloud.google.com/iam/docs/understanding-roles#cloudsupport.techSupportEditor) <br>[roles/logging.viewer](https://cloud.google.com/iam/docs/understanding-roles#logging.viewer) <br>[roles/monitoring.viewer](https://cloud.google.com/iam/docs/understanding-roles#monitoring.viewer) |
|<b>prod-bootstrap-0</b><br><small><i>serviceAccount</i></small>|[roles/logging.admin](https://cloud.google.com/iam/docs/understanding-roles#logging.admin) <br>[roles/resourcemanager.organizationAdmin](https://cloud.google.com/iam/docs/understanding-roles#resourcemanager.organizationAdmin) <br>[roles/resourcemanager.projectCreator](https://cloud.google.com/iam/docs/understanding-roles#resourcemanager.projectCreator) <br>[roles/resourcemanager.projectMover](https://cloud.google.com/iam/docs/understanding-roles#resourcemanager.projectMover) <br>[roles/billing.admin](https://cloud.google.com/iam/docs/understanding-roles#billing.admin) <code>+</code><br>[roles/billing.costsManager](https://cloud.google.com/iam/docs/understanding-roles#billing.costsManager) <code>+</code><br>[roles/iam.organizationRoleAdmin](https://cloud.google.com/iam/docs/understanding-roles#iam.organizationRoleAdmin) <code>+</code>| |<b>prod-bootstrap-0</b><br><small><i>serviceAccount</i></small>|[roles/logging.admin](https://cloud.google.com/iam/docs/understanding-roles#logging.admin) <br>[roles/resourcemanager.organizationAdmin](https://cloud.google.com/iam/docs/understanding-roles#resourcemanager.organizationAdmin) <br>[roles/resourcemanager.projectCreator](https://cloud.google.com/iam/docs/understanding-roles#resourcemanager.projectCreator) <br>[roles/resourcemanager.projectMover](https://cloud.google.com/iam/docs/understanding-roles#resourcemanager.projectMover) <br>[roles/billing.admin](https://cloud.google.com/iam/docs/understanding-roles#billing.admin) <code>+</code><br>[roles/billing.costsManager](https://cloud.google.com/iam/docs/understanding-roles#billing.costsManager) <code>+</code><br>[roles/iam.organizationRoleAdmin](https://cloud.google.com/iam/docs/understanding-roles#iam.organizationRoleAdmin) <code>+</code>|
|<b>prod-resman-0</b><br><small><i>serviceAccount</i></small>|organizations/[org_id #0]/roles/organizationIamAdmin <code></code><br>[roles/resourcemanager.folderAdmin](https://cloud.google.com/iam/docs/understanding-roles#resourcemanager.folderAdmin) <br>[roles/resourcemanager.tagAdmin](https://cloud.google.com/iam/docs/understanding-roles#resourcemanager.tagAdmin) <br>[roles/resourcemanager.tagUser](https://cloud.google.com/iam/docs/understanding-roles#resourcemanager.tagUser) <br>[roles/billing.admin](https://cloud.google.com/iam/docs/understanding-roles#billing.admin) <code>+</code><br>[roles/billing.costsManager](https://cloud.google.com/iam/docs/understanding-roles#billing.costsManager) <code>+</code><br>[roles/orgpolicy.policyAdmin](https://cloud.google.com/iam/docs/understanding-roles#orgpolicy.policyAdmin) <code>+</code>| |<b>prod-resman-0</b><br><small><i>serviceAccount</i></small>|organizations/[org_id #0]/roles/organizationIamAdmin <code></code><br>[roles/resourcemanager.folderAdmin](https://cloud.google.com/iam/docs/understanding-roles#resourcemanager.folderAdmin) <br>[roles/resourcemanager.tagAdmin](https://cloud.google.com/iam/docs/understanding-roles#resourcemanager.tagAdmin) <br>[roles/resourcemanager.tagUser](https://cloud.google.com/iam/docs/understanding-roles#resourcemanager.tagUser) <br>[roles/billing.admin](https://cloud.google.com/iam/docs/understanding-roles#billing.admin) <code>+</code><br>[roles/billing.costsManager](https://cloud.google.com/iam/docs/understanding-roles#billing.costsManager) <code>+</code><br>[roles/orgpolicy.policyAdmin](https://cloud.google.com/iam/docs/understanding-roles#orgpolicy.policyAdmin) <code>+</code>|

View File

@ -41,12 +41,6 @@ Legend: <code>+</code> additive, <code>•</code> conditional.
|---|---| |---|---|
|<b>dev-resman-pf-0</b><br><small><i>serviceAccount</i></small>|organizations/[org_id #0]/roles/serviceProjectNetworkAdmin <br>[roles/logging.admin](https://cloud.google.com/iam/docs/understanding-roles#logging.admin) <br>[roles/owner](https://cloud.google.com/iam/docs/understanding-roles#owner) <br>[roles/resourcemanager.folderAdmin](https://cloud.google.com/iam/docs/understanding-roles#resourcemanager.folderAdmin) <br>[roles/resourcemanager.projectCreator](https://cloud.google.com/iam/docs/understanding-roles#resourcemanager.projectCreator) | |<b>dev-resman-pf-0</b><br><small><i>serviceAccount</i></small>|organizations/[org_id #0]/roles/serviceProjectNetworkAdmin <br>[roles/logging.admin](https://cloud.google.com/iam/docs/understanding-roles#logging.admin) <br>[roles/owner](https://cloud.google.com/iam/docs/understanding-roles#owner) <br>[roles/resourcemanager.folderAdmin](https://cloud.google.com/iam/docs/understanding-roles#resourcemanager.folderAdmin) <br>[roles/resourcemanager.projectCreator](https://cloud.google.com/iam/docs/understanding-roles#resourcemanager.projectCreator) |
## Folder <i>development [#4]</i>
| members | roles |
|---|---|
|<b>dev-resman-pf-0</b><br><small><i>serviceAccount</i></small>|organizations/[org_id #0]/roles/serviceProjectNetworkAdmin <br>[roles/logging.admin](https://cloud.google.com/iam/docs/understanding-roles#logging.admin) <br>[roles/owner](https://cloud.google.com/iam/docs/understanding-roles#owner) <br>[roles/resourcemanager.folderAdmin](https://cloud.google.com/iam/docs/understanding-roles#resourcemanager.folderAdmin) <br>[roles/resourcemanager.projectCreator](https://cloud.google.com/iam/docs/understanding-roles#resourcemanager.projectCreator) |
## Folder <i>networking</i> ## Folder <i>networking</i>
| members | roles | | members | roles |
@ -80,12 +74,6 @@ Legend: <code>+</code> additive, <code>•</code> conditional.
|---|---| |---|---|
|<b>prod-resman-pf-0</b><br><small><i>serviceAccount</i></small>|organizations/[org_id #0]/roles/serviceProjectNetworkAdmin <br>[roles/logging.admin](https://cloud.google.com/iam/docs/understanding-roles#logging.admin) <br>[roles/owner](https://cloud.google.com/iam/docs/understanding-roles#owner) <br>[roles/resourcemanager.folderAdmin](https://cloud.google.com/iam/docs/understanding-roles#resourcemanager.folderAdmin) <br>[roles/resourcemanager.projectCreator](https://cloud.google.com/iam/docs/understanding-roles#resourcemanager.projectCreator) | |<b>prod-resman-pf-0</b><br><small><i>serviceAccount</i></small>|organizations/[org_id #0]/roles/serviceProjectNetworkAdmin <br>[roles/logging.admin](https://cloud.google.com/iam/docs/understanding-roles#logging.admin) <br>[roles/owner](https://cloud.google.com/iam/docs/understanding-roles#owner) <br>[roles/resourcemanager.folderAdmin](https://cloud.google.com/iam/docs/understanding-roles#resourcemanager.folderAdmin) <br>[roles/resourcemanager.projectCreator](https://cloud.google.com/iam/docs/understanding-roles#resourcemanager.projectCreator) |
## Folder <i>production [#4]</i>
| members | roles |
|---|---|
|<b>prod-resman-pf-0</b><br><small><i>serviceAccount</i></small>|organizations/[org_id #0]/roles/serviceProjectNetworkAdmin <br>[roles/logging.admin](https://cloud.google.com/iam/docs/understanding-roles#logging.admin) <br>[roles/owner](https://cloud.google.com/iam/docs/understanding-roles#owner) <br>[roles/resourcemanager.folderAdmin](https://cloud.google.com/iam/docs/understanding-roles#resourcemanager.folderAdmin) <br>[roles/resourcemanager.projectCreator](https://cloud.google.com/iam/docs/understanding-roles#resourcemanager.projectCreator) |
## Folder <i>sandbox</i> ## Folder <i>sandbox</i>
| members | roles | | members | roles |
@ -99,30 +87,27 @@ Legend: <code>+</code> additive, <code>•</code> conditional.
|<b>gcp-security-admins</b><br><small><i>group</i></small>|[roles/viewer](https://cloud.google.com/iam/docs/understanding-roles#viewer) | |<b>gcp-security-admins</b><br><small><i>group</i></small>|[roles/viewer](https://cloud.google.com/iam/docs/understanding-roles#viewer) |
|<b>prod-resman-sec-0</b><br><small><i>serviceAccount</i></small>|[roles/logging.admin](https://cloud.google.com/iam/docs/understanding-roles#logging.admin) <br>[roles/owner](https://cloud.google.com/iam/docs/understanding-roles#owner) <br>[roles/resourcemanager.folderAdmin](https://cloud.google.com/iam/docs/understanding-roles#resourcemanager.folderAdmin) <br>[roles/resourcemanager.projectCreator](https://cloud.google.com/iam/docs/understanding-roles#resourcemanager.projectCreator) | |<b>prod-resman-sec-0</b><br><small><i>serviceAccount</i></small>|[roles/logging.admin](https://cloud.google.com/iam/docs/understanding-roles#logging.admin) <br>[roles/owner](https://cloud.google.com/iam/docs/understanding-roles#owner) <br>[roles/resourcemanager.folderAdmin](https://cloud.google.com/iam/docs/understanding-roles#resourcemanager.folderAdmin) <br>[roles/resourcemanager.projectCreator](https://cloud.google.com/iam/docs/understanding-roles#resourcemanager.projectCreator) |
## Folder <i>team a</i>
| members | roles |
|---|---|
|<b>team-a</b><br><small><i>group</i></small>|[roles/viewer](https://cloud.google.com/iam/docs/understanding-roles#viewer) |
|<b>prod-teams-team-a-0</b><br><small><i>serviceAccount</i></small>|[roles/compute.xpnAdmin](https://cloud.google.com/iam/docs/understanding-roles#compute.xpnAdmin) <br>[roles/logging.admin](https://cloud.google.com/iam/docs/understanding-roles#logging.admin) <br>[roles/owner](https://cloud.google.com/iam/docs/understanding-roles#owner) <br>[roles/resourcemanager.folderAdmin](https://cloud.google.com/iam/docs/understanding-roles#resourcemanager.folderAdmin) <br>[roles/resourcemanager.projectCreator](https://cloud.google.com/iam/docs/understanding-roles#resourcemanager.projectCreator) |
## Folder <i>team b</i>
| members | roles |
|---|---|
|<b>prod-teams-team-b-0</b><br><small><i>serviceAccount</i></small>|[roles/compute.xpnAdmin](https://cloud.google.com/iam/docs/understanding-roles#compute.xpnAdmin) <br>[roles/logging.admin](https://cloud.google.com/iam/docs/understanding-roles#logging.admin) <br>[roles/owner](https://cloud.google.com/iam/docs/understanding-roles#owner) <br>[roles/resourcemanager.folderAdmin](https://cloud.google.com/iam/docs/understanding-roles#resourcemanager.folderAdmin) <br>[roles/resourcemanager.projectCreator](https://cloud.google.com/iam/docs/understanding-roles#resourcemanager.projectCreator) |
## Folder <i>teams</i> ## Folder <i>teams</i>
| members | roles | | members | roles |
|---|---| |---|---|
|<b>prod-resman-teams-0</b><br><small><i>serviceAccount</i></small>|[roles/compute.xpnAdmin](https://cloud.google.com/iam/docs/understanding-roles#compute.xpnAdmin) <br>[roles/logging.admin](https://cloud.google.com/iam/docs/understanding-roles#logging.admin) <br>[roles/owner](https://cloud.google.com/iam/docs/understanding-roles#owner) <br>[roles/resourcemanager.folderAdmin](https://cloud.google.com/iam/docs/understanding-roles#resourcemanager.folderAdmin) <br>[roles/resourcemanager.projectCreator](https://cloud.google.com/iam/docs/understanding-roles#resourcemanager.projectCreator) | |<b>prod-resman-teams-0</b><br><small><i>serviceAccount</i></small>|[roles/compute.xpnAdmin](https://cloud.google.com/iam/docs/understanding-roles#compute.xpnAdmin) <br>[roles/logging.admin](https://cloud.google.com/iam/docs/understanding-roles#logging.admin) <br>[roles/owner](https://cloud.google.com/iam/docs/understanding-roles#owner) <br>[roles/resourcemanager.folderAdmin](https://cloud.google.com/iam/docs/understanding-roles#resourcemanager.folderAdmin) <br>[roles/resourcemanager.projectCreator](https://cloud.google.com/iam/docs/understanding-roles#resourcemanager.projectCreator) |
## Folder <i>teams test</i>
| members | roles |
|---|---|
|<b>prod-teams-teams-test-0</b><br><small><i>serviceAccount</i></small>|[roles/compute.xpnAdmin](https://cloud.google.com/iam/docs/understanding-roles#compute.xpnAdmin) <br>[roles/logging.admin](https://cloud.google.com/iam/docs/understanding-roles#logging.admin) <br>[roles/owner](https://cloud.google.com/iam/docs/understanding-roles#owner) <br>[roles/resourcemanager.folderAdmin](https://cloud.google.com/iam/docs/understanding-roles#resourcemanager.folderAdmin) <br>[roles/resourcemanager.projectCreator](https://cloud.google.com/iam/docs/understanding-roles#resourcemanager.projectCreator) |
## Project <i>prod-iac-core-0</i> ## Project <i>prod-iac-core-0</i>
| members | roles | | members | roles |
|---|---| |---|---|
|<b>dev-pf-resman-pf-1</b><br><small><i>serviceAccount</i></small>|[roles/logging.logWriter](https://cloud.google.com/iam/docs/understanding-roles#logging.logWriter) <code>+</code>|
|<b>dev-resman-dp-1</b><br><small><i>serviceAccount</i></small>|[roles/logging.logWriter](https://cloud.google.com/iam/docs/understanding-roles#logging.logWriter) <code>+</code>| |<b>dev-resman-dp-1</b><br><small><i>serviceAccount</i></small>|[roles/logging.logWriter](https://cloud.google.com/iam/docs/understanding-roles#logging.logWriter) <code>+</code>|
|<b>dev-resman-gke-1</b><br><small><i>serviceAccount</i></small>|[roles/logging.logWriter](https://cloud.google.com/iam/docs/understanding-roles#logging.logWriter) <code>+</code>| |<b>dev-resman-gke-1</b><br><small><i>serviceAccount</i></small>|[roles/logging.logWriter](https://cloud.google.com/iam/docs/understanding-roles#logging.logWriter) <code>+</code>|
|<b>prod-pf-resman-pf-1</b><br><small><i>serviceAccount</i></small>|[roles/logging.logWriter](https://cloud.google.com/iam/docs/understanding-roles#logging.logWriter) <code>+</code>|
|<b>prod-resman-dp-1</b><br><small><i>serviceAccount</i></small>|[roles/logging.logWriter](https://cloud.google.com/iam/docs/understanding-roles#logging.logWriter) <code>+</code>|
|<b>prod-resman-gke-1</b><br><small><i>serviceAccount</i></small>|[roles/logging.logWriter](https://cloud.google.com/iam/docs/understanding-roles#logging.logWriter) <code>+</code>| |<b>prod-resman-gke-1</b><br><small><i>serviceAccount</i></small>|[roles/logging.logWriter](https://cloud.google.com/iam/docs/understanding-roles#logging.logWriter) <code>+</code>|
|<b>prod-resman-net-1</b><br><small><i>serviceAccount</i></small>|[roles/logging.logWriter](https://cloud.google.com/iam/docs/understanding-roles#logging.logWriter) <code>+</code>| |<b>prod-resman-net-1</b><br><small><i>serviceAccount</i></small>|[roles/logging.logWriter](https://cloud.google.com/iam/docs/understanding-roles#logging.logWriter) <code>+</code>|
|<b>prod-resman-sec-1</b><br><small><i>serviceAccount</i></small>|[roles/logging.logWriter](https://cloud.google.com/iam/docs/understanding-roles#logging.logWriter) <code>+</code>|

View File

@ -16,23 +16,27 @@
# tfdoc:file:description Output files persistence to local filesystem. # tfdoc:file:description Output files persistence to local filesystem.
locals {
outputs_location = try(pathexpand(var.outputs_location), "")
}
resource "local_file" "providers" { resource "local_file" "providers" {
for_each = var.outputs_location == null ? {} : local.providers for_each = var.outputs_location == null ? {} : local.providers
file_permission = "0644" file_permission = "0644"
filename = "${pathexpand(var.outputs_location)}/providers/${each.key}-providers.tf" filename = "${local.outputs_location}/providers/${each.key}-providers.tf"
content = each.value content = try(each.value, null)
} }
resource "local_file" "tfvars" { resource "local_file" "tfvars" {
for_each = var.outputs_location == null ? {} : { 1 = 1 } for_each = var.outputs_location == null ? {} : { 1 = 1 }
file_permission = "0644" file_permission = "0644"
filename = "${pathexpand(var.outputs_location)}/tfvars/01-resman.auto.tfvars.json" filename = "${local.outputs_location}/tfvars/01-resman.auto.tfvars.json"
content = jsonencode(local.tfvars) content = jsonencode(local.tfvars)
} }
resource "local_file" "workflows" { resource "local_file" "workflows" {
for_each = var.outputs_location == null ? {} : local.cicd_workflows for_each = var.outputs_location == null ? {} : local.cicd_workflows
file_permission = "0644" file_permission = "0644"
filename = "${pathexpand(var.outputs_location)}/workflows/${replace(each.key, "_", "-")}-workflow.yaml" filename = "${local.outputs_location}/workflows/${replace(each.key, "_", "-")}-workflow.yaml"
content = each.value content = try(each.value, null)
} }

View File

@ -156,109 +156,85 @@ This allows configuring VPC SC in a fairly flexible and concise way, without rep
#### Dry-run vs. enforced #### Dry-run vs. enforced
The VPC SC configuration is set up by default in dry-run mode to allow easy experimentation, and detecting violations before enforcement. Once everything is set up correctly, switching to enforced mode needs to be done in code, by swapping the contents of the `spec` and `status` attributes for perimeters in the `vpc-sc.tf` file. The effort involved is minimal (2 lines of code per perimeter), and comments help identify the correct lines. The VPC SC configuration is set up by default in dry-run mode to allow easy experimentation, and detecting violations before enforcement. Once everything is set up correctly, switching to enforced mode needs to be done in code, by changing the `vpc_sc_explicit_dry_run_spec` local.
#### Perimeter resources
Projects are added to perimeters via the `vpc_sc_perimeter_projects`, and that's currently the only way of doing it without generating permadiffs or conflicts, because of the way the Terraform provider is implemented.
Once the Google Terraform Provider [implements support for dry-run mode in the additive resource](https://github.com/hashicorp/terraform-provider-google/issues/7270), it will be possible to concurrently manage perimeter resources both here and in subsequent Terraform configurations, for example to allow the Project Factory to add a project to a perimeter during the creation process.
Bridge perimeters are auto-populated with all projects configured for the connected regular perimeters.
An example of adding projects to perimeters using project numbers:
```hcl
# terraform.tfvars
vpc_sc_perimeter_projects = {
dev = ["projects/12345678", "projects/12345679"]
landing = ["projects/12345670"]
prod = ["projects/12345674", "projects/12345675"]
}
```
#### Access levels #### Access levels
Below an example for an access level that allows unconditional ingress from a set of IP CIDR ranges can be configured once, and enabled on selected perimeters: Access levels are defined via the `vpc_sc_access_levels` variable, and referenced by key in perimeter definitions:
```hcl ```hcl
# terraform.tfvars
vpc_sc_access_levels = { vpc_sc_access_levels = {
onprem = { onprem = {
combining_function = null,
conditions = [{ conditions = [{
ip_subnetworks = ["101.101.101.0/24"], ip_subnetworks = ["101.101.101.0/24"]
members = null, negate = null,
regions = null, required_access_levels = null
}] }]
} }
} }
vpc_sc_perimeter_access_levels = {
dev = null
landing = ["onprem"]
prod = ["onprem"]
}
``` ```
#### Ingress and Egress policies #### Ingress and Egress policies
The same applies to Ingress and Egress policies, as shown in the examples below referencing the automation service account for this stage. Ingress and egress policy are defined via the `vpc_sc_egress_policies` and `vpc_sc_ingress_policies`, and referenced by key in perimeter definitions:
Below you can find an ingress policy configuration that allows applying Terraform from outside the perimeter, useful when bringing up this stage to avoid generating violations:
```hcl ```hcl
# terraform.tfvars
vpc_sc_ingress_policies = {
iac = {
ingress_from = {
identities = [
"serviceAccount:xxx-prod-resman-security-0@xxx-prod-iac-core-0.iam.gserviceaccount.com"
]
source_access_levels = ["*"]
identity_type = null
source_resources = null
}
ingress_to = {
operations = [{ method_selectors = [], service_name = "*" }]
resources = ["*"]
}
}
}
vpc_sc_perimeter_ingress_policies = {
dev = ["iac"]
landing = ["iac"]
prod = ["iac"]
}
```
Below you can find an egress policy that allows writing Terraform state to the automation bucket, useful once Terraform starts running inside the perimeter in a pipeline:
```hcl
# terraform.tfvars
vpc_sc_egress_policies = { vpc_sc_egress_policies = {
iac-gcs = { iac-gcs = {
egress_from = { from = {
identity_type = null
identities = [ identities = [
"serviceAccount:xxx-prod-resman-security-0@xxx-prod-iac-core-0.iam.gserviceaccount.com" "serviceAccount:xxx-prod-resman-security-0@xxx-prod-iac-core-0.iam.gserviceaccount.com"
] ]
} }
egress_to = { to = {
operations = [{ operations = [{
method_selectors = ["*"], service_name = "storage.googleapis.com" method_selectors = ["*"]
service_name = "storage.googleapis.com"
}] }]
resources = ["projects/123456782"] resources = ["projects/123456782"]
} }
} }
} }
vpc_sc_perimeter_ingress_policies = { vpc_sc_ingress_policies = {
dev = ["iac-gcs"] iac = {
landing = ["iac-gcs"] from = {
prod = ["iac-gcs"] identities = [
"serviceAccount:xxx-prod-resman-security-0@xxx-prod-iac-core-0.iam.gserviceaccount.com"
]
access_levels = ["*"]
}
to = {
operations = [{ method_selectors = [], service_name = "*" }]
resources = ["*"]
}
}
}
```
#### Perimeters
Regular perimeters are defined via the the `vpc_sc_perimeters` variable, and bridge perimeters are automatically populated from that variable.
Support for independently adding projects to perimeters outside of this Terraform setup is pending resolution of [this Google Terraform Provider issue](https://github.com/hashicorp/terraform-provider-google/issues/7270), which implements support for dry-run mode in the additive resource.
Access levels and egress/ingress policies are referenced in perimeters via keys.
```hcl
vpc_sc_perimeters = {
dev = {
egress_policies = ["iac-gcs"]
ingress_policies = ["iac"]
resources = ["projects/1111111111"]
}
dev = {
egress_policies = ["iac-gcs"]
ingress_policies = ["iac"]
resources = ["projects/0000000000"]
}
dev = {
access_levels = ["onprem"]
egress_policies = ["iac-gcs"]
ingress_policies = ["iac"]
resources = ["projects/2222222222"]
}
} }
``` ```
@ -296,13 +272,10 @@ Some references that might be useful in setting up this stage:
| [kms_defaults](variables.tf#L57) | Defaults used for KMS keys. | <code title="object&#40;&#123;&#10; locations &#61; list&#40;string&#41;&#10; rotation_period &#61; string&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code title="&#123;&#10; locations &#61; &#91;&#34;europe&#34;, &#34;europe-west1&#34;, &#34;europe-west3&#34;, &#34;global&#34;&#93;&#10; rotation_period &#61; &#34;7776000s&#34;&#10;&#125;">&#123;&#8230;&#125;</code> | | | [kms_defaults](variables.tf#L57) | Defaults used for KMS keys. | <code title="object&#40;&#123;&#10; locations &#61; list&#40;string&#41;&#10; rotation_period &#61; string&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code title="&#123;&#10; locations &#61; &#91;&#34;europe&#34;, &#34;europe-west1&#34;, &#34;europe-west3&#34;, &#34;global&#34;&#93;&#10; rotation_period &#61; &#34;7776000s&#34;&#10;&#125;">&#123;&#8230;&#125;</code> | |
| [kms_keys](variables.tf#L69) | KMS keys to create, keyed by name. Null attributes will be interpolated with defaults. | <code title="map&#40;object&#40;&#123;&#10; iam &#61; map&#40;list&#40;string&#41;&#41;&#10; labels &#61; map&#40;string&#41;&#10; locations &#61; list&#40;string&#41;&#10; rotation_period &#61; string&#10;&#125;&#41;&#41;">map&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | | <code>&#123;&#125;</code> | | | [kms_keys](variables.tf#L69) | KMS keys to create, keyed by name. Null attributes will be interpolated with defaults. | <code title="map&#40;object&#40;&#123;&#10; iam &#61; map&#40;list&#40;string&#41;&#41;&#10; labels &#61; map&#40;string&#41;&#10; locations &#61; list&#40;string&#41;&#10; rotation_period &#61; string&#10;&#125;&#41;&#41;">map&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | | <code>&#123;&#125;</code> | |
| [outputs_location](variables.tf#L101) | Path where providers, tfvars files, and lists for the following stages are written. Leave empty to disable. | <code>string</code> | | <code>null</code> | | | [outputs_location](variables.tf#L101) | Path where providers, tfvars files, and lists for the following stages are written. Leave empty to disable. | <code>string</code> | | <code>null</code> | |
| [vpc_sc_access_levels](variables.tf#L118) | VPC SC access level definitions. | <code title="map&#40;object&#40;&#123;&#10; combining_function &#61; string&#10; conditions &#61; list&#40;object&#40;&#123;&#10; ip_subnetworks &#61; list&#40;string&#41;&#10; members &#61; list&#40;string&#41;&#10; negate &#61; bool&#10; regions &#61; list&#40;string&#41;&#10; required_access_levels &#61; list&#40;string&#41;&#10; &#125;&#41;&#41;&#10;&#125;&#41;&#41;">map&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | | <code>&#123;&#125;</code> | | | [vpc_sc_access_levels](variables.tf#L118) | VPC SC access level definitions. | <code title="map&#40;object&#40;&#123;&#10; combining_function &#61; optional&#40;string&#41;&#10; conditions &#61; optional&#40;list&#40;object&#40;&#123;&#10; device_policy &#61; optional&#40;object&#40;&#123;&#10; allowed_device_management_levels &#61; optional&#40;list&#40;string&#41;&#41;&#10; allowed_encryption_statuses &#61; optional&#40;list&#40;string&#41;&#41;&#10; require_admin_approval &#61; bool&#10; require_corp_owned &#61; bool&#10; require_screen_lock &#61; optional&#40;bool&#41;&#10; os_constraints &#61; optional&#40;list&#40;object&#40;&#123;&#10; os_type &#61; string&#10; minimum_version &#61; optional&#40;string&#41;&#10; require_verified_chrome_os &#61; optional&#40;bool&#41;&#10; &#125;&#41;&#41;&#41;&#10; &#125;&#41;&#41;&#10; ip_subnetworks &#61; optional&#40;list&#40;string&#41;, &#91;&#93;&#41;&#10; members &#61; optional&#40;list&#40;string&#41;, &#91;&#93;&#41;&#10; negate &#61; optional&#40;bool&#41;&#10; regions &#61; optional&#40;list&#40;string&#41;, &#91;&#93;&#41;&#10; required_access_levels &#61; optional&#40;list&#40;string&#41;, &#91;&#93;&#41;&#10; &#125;&#41;&#41;, &#91;&#93;&#41;&#10; description &#61; optional&#40;string&#41;&#10;&#125;&#41;&#41;">map&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | | <code>&#123;&#125;</code> | |
| [vpc_sc_egress_policies](variables.tf#L133) | VPC SC egress policy defnitions. | <code title="map&#40;object&#40;&#123;&#10; egress_from &#61; object&#40;&#123;&#10; identity_type &#61; string&#10; identities &#61; list&#40;string&#41;&#10; &#125;&#41;&#10; egress_to &#61; object&#40;&#123;&#10; operations &#61; list&#40;object&#40;&#123;&#10; method_selectors &#61; list&#40;string&#41;&#10; service_name &#61; string&#10; &#125;&#41;&#41;&#10; resources &#61; list&#40;string&#41;&#10; &#125;&#41;&#10;&#125;&#41;&#41;">map&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | | <code>&#123;&#125;</code> | | | [vpc_sc_egress_policies](variables.tf#L147) | VPC SC egress policy defnitions. | <code title="map&#40;object&#40;&#123;&#10; from &#61; object&#40;&#123;&#10; identity_type &#61; optional&#40;string, &#34;ANY_IDENTITY&#34;&#41;&#10; identities &#61; optional&#40;list&#40;string&#41;&#41;&#10; &#125;&#41;&#10; to &#61; object&#40;&#123;&#10; operations &#61; optional&#40;list&#40;object&#40;&#123;&#10; method_selectors &#61; optional&#40;list&#40;string&#41;&#41;&#10; service_name &#61; string&#10; &#125;&#41;&#41;, &#91;&#93;&#41;&#10; resources &#61; optional&#40;list&#40;string&#41;&#41;&#10; resource_type_external &#61; optional&#40;bool, false&#41;&#10; &#125;&#41;&#10;&#125;&#41;&#41;">map&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | | <code>&#123;&#125;</code> | |
| [vpc_sc_ingress_policies](variables.tf#L151) | VPC SC ingress policy defnitions. | <code title="map&#40;object&#40;&#123;&#10; ingress_from &#61; object&#40;&#123;&#10; identity_type &#61; string&#10; identities &#61; list&#40;string&#41;&#10; source_access_levels &#61; list&#40;string&#41;&#10; source_resources &#61; list&#40;string&#41;&#10; &#125;&#41;&#10; ingress_to &#61; object&#40;&#123;&#10; operations &#61; list&#40;object&#40;&#123;&#10; method_selectors &#61; list&#40;string&#41;&#10; service_name &#61; string&#10; &#125;&#41;&#41;&#10; resources &#61; list&#40;string&#41;&#10; &#125;&#41;&#10;&#125;&#41;&#41;">map&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | | <code>&#123;&#125;</code> | | | [vpc_sc_ingress_policies](variables.tf#L167) | VPC SC ingress policy defnitions. | <code title="map&#40;object&#40;&#123;&#10; from &#61; object&#40;&#123;&#10; access_levels &#61; optional&#40;list&#40;string&#41;, &#91;&#93;&#41;&#10; identity_type &#61; optional&#40;string&#41;&#10; identities &#61; optional&#40;list&#40;string&#41;&#41;&#10; resources &#61; optional&#40;list&#40;string&#41;, &#91;&#93;&#41;&#10; &#125;&#41;&#10; to &#61; object&#40;&#123;&#10; operations &#61; optional&#40;list&#40;object&#40;&#123;&#10; method_selectors &#61; optional&#40;list&#40;string&#41;&#41;&#10; service_name &#61; string&#10; &#125;&#41;&#41;, &#91;&#93;&#41;&#10; resources &#61; optional&#40;list&#40;string&#41;&#41;&#10; &#125;&#41;&#10;&#125;&#41;&#41;">map&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | | <code>&#123;&#125;</code> | |
| [vpc_sc_perimeter_access_levels](variables.tf#L171) | VPC SC perimeter access_levels. | <code title="object&#40;&#123;&#10; dev &#61; list&#40;string&#41;&#10; landing &#61; list&#40;string&#41;&#10; prod &#61; list&#40;string&#41;&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code>null</code> | | | [vpc_sc_perimeters](variables.tf#L188) | VPC SC regular perimeter definitions. | <code title="object&#40;&#123;&#10; dev &#61; optional&#40;object&#40;&#123;&#10; access_levels &#61; optional&#40;list&#40;string&#41;, &#91;&#93;&#41;&#10; egress_policies &#61; optional&#40;list&#40;string&#41;, &#91;&#93;&#41;&#10; ingress_policies &#61; optional&#40;list&#40;string&#41;, &#91;&#93;&#41;&#10; resources &#61; optional&#40;list&#40;string&#41;, &#91;&#93;&#41;&#10; &#125;&#41;, &#123;&#125;&#41;&#10; landing &#61; optional&#40;object&#40;&#123;&#10; access_levels &#61; optional&#40;list&#40;string&#41;, &#91;&#93;&#41;&#10; egress_policies &#61; optional&#40;list&#40;string&#41;, &#91;&#93;&#41;&#10; ingress_policies &#61; optional&#40;list&#40;string&#41;, &#91;&#93;&#41;&#10; resources &#61; optional&#40;list&#40;string&#41;, &#91;&#93;&#41;&#10; &#125;&#41;, &#123;&#125;&#41;&#10; prod &#61; optional&#40;object&#40;&#123;&#10; access_levels &#61; optional&#40;list&#40;string&#41;, &#91;&#93;&#41;&#10; egress_policies &#61; optional&#40;list&#40;string&#41;, &#91;&#93;&#41;&#10; ingress_policies &#61; optional&#40;list&#40;string&#41;, &#91;&#93;&#41;&#10; resources &#61; optional&#40;list&#40;string&#41;, &#91;&#93;&#41;&#10; &#125;&#41;, &#123;&#125;&#41;&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code>&#123;&#125;</code> | |
| [vpc_sc_perimeter_egress_policies](variables.tf#L181) | VPC SC egress policies per perimeter, values reference keys defined in the `vpc_sc_ingress_policies` variable. | <code title="object&#40;&#123;&#10; dev &#61; list&#40;string&#41;&#10; landing &#61; list&#40;string&#41;&#10; prod &#61; list&#40;string&#41;&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code>null</code> | |
| [vpc_sc_perimeter_ingress_policies](variables.tf#L191) | VPC SC ingress policies per perimeter, values reference keys defined in the `vpc_sc_ingress_policies` variable. | <code title="object&#40;&#123;&#10; dev &#61; list&#40;string&#41;&#10; landing &#61; list&#40;string&#41;&#10; prod &#61; list&#40;string&#41;&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code>null</code> | |
| [vpc_sc_perimeter_projects](variables.tf#L201) | VPC SC perimeter resources. | <code title="object&#40;&#123;&#10; dev &#61; list&#40;string&#41;&#10; landing &#61; list&#40;string&#41;&#10; prod &#61; list&#40;string&#41;&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code>null</code> | |
## Outputs ## Outputs

View File

@ -118,92 +118,95 @@ variable "prefix" {
variable "vpc_sc_access_levels" { variable "vpc_sc_access_levels" {
description = "VPC SC access level definitions." description = "VPC SC access level definitions."
type = map(object({ type = map(object({
combining_function = string combining_function = optional(string)
conditions = list(object({ conditions = optional(list(object({
ip_subnetworks = list(string) device_policy = optional(object({
members = list(string) allowed_device_management_levels = optional(list(string))
negate = bool allowed_encryption_statuses = optional(list(string))
regions = list(string) require_admin_approval = bool
required_access_levels = list(string) require_corp_owned = bool
})) require_screen_lock = optional(bool)
os_constraints = optional(list(object({
os_type = string
minimum_version = optional(string)
require_verified_chrome_os = optional(bool)
})))
}))
ip_subnetworks = optional(list(string), [])
members = optional(list(string), [])
negate = optional(bool)
regions = optional(list(string), [])
required_access_levels = optional(list(string), [])
})), [])
description = optional(string)
})) }))
default = {} default = {}
nullable = false
} }
variable "vpc_sc_egress_policies" { variable "vpc_sc_egress_policies" {
description = "VPC SC egress policy defnitions." description = "VPC SC egress policy defnitions."
type = map(object({ type = map(object({
egress_from = object({ from = object({
identity_type = string identity_type = optional(string, "ANY_IDENTITY")
identities = list(string) identities = optional(list(string))
}) })
egress_to = object({ to = object({
operations = list(object({ operations = optional(list(object({
method_selectors = list(string) method_selectors = optional(list(string))
service_name = string service_name = string
})) })), [])
resources = list(string) resources = optional(list(string))
resource_type_external = optional(bool, false)
}) })
})) }))
default = {} default = {}
nullable = false
} }
variable "vpc_sc_ingress_policies" { variable "vpc_sc_ingress_policies" {
description = "VPC SC ingress policy defnitions." description = "VPC SC ingress policy defnitions."
type = map(object({ type = map(object({
ingress_from = object({ from = object({
identity_type = string access_levels = optional(list(string), [])
identities = list(string) identity_type = optional(string)
source_access_levels = list(string) identities = optional(list(string))
source_resources = list(string) resources = optional(list(string), [])
}) })
ingress_to = object({ to = object({
operations = list(object({ operations = optional(list(object({
method_selectors = list(string) method_selectors = optional(list(string))
service_name = string service_name = string
})) })), [])
resources = list(string) resources = optional(list(string))
}) })
})) }))
default = {} default = {}
nullable = false
} }
variable "vpc_sc_perimeter_access_levels" { variable "vpc_sc_perimeters" {
description = "VPC SC perimeter access_levels." description = "VPC SC regular perimeter definitions."
type = object({ type = object({
dev = list(string) dev = optional(object({
landing = list(string) access_levels = optional(list(string), [])
prod = list(string) egress_policies = optional(list(string), [])
ingress_policies = optional(list(string), [])
resources = optional(list(string), [])
}), {})
landing = optional(object({
access_levels = optional(list(string), [])
egress_policies = optional(list(string), [])
ingress_policies = optional(list(string), [])
resources = optional(list(string), [])
}), {})
prod = optional(object({
access_levels = optional(list(string), [])
egress_policies = optional(list(string), [])
ingress_policies = optional(list(string), [])
resources = optional(list(string), [])
}), {})
}) })
default = null default = {}
} nullable = false
variable "vpc_sc_perimeter_egress_policies" {
description = "VPC SC egress policies per perimeter, values reference keys defined in the `vpc_sc_ingress_policies` variable."
type = object({
dev = list(string)
landing = list(string)
prod = list(string)
})
default = null
}
variable "vpc_sc_perimeter_ingress_policies" {
description = "VPC SC ingress policies per perimeter, values reference keys defined in the `vpc_sc_ingress_policies` variable."
type = object({
dev = list(string)
landing = list(string)
prod = list(string)
})
default = null
}
variable "vpc_sc_perimeter_projects" {
description = "VPC SC perimeter resources."
type = object({
dev = list(string)
landing = list(string)
prod = list(string)
})
default = null
} }

View File

@ -15,118 +15,34 @@
*/ */
locals { locals {
_perimeter_names = ["dev", "landing", "prod"] _vpc_sc_vpc_accessible_services = null
# dereference perimeter egress policy names to the actual objects _vpc_sc_restricted_services = yamldecode(
_vpc_sc_perimeter_egress_policies = { file("${path.module}/vpc-sc-restricted-services.yaml")
for k, v in coalesce(var.vpc_sc_perimeter_egress_policies, {}) : )
k => [
for i in coalesce(v, []) : var.vpc_sc_egress_policies[i]
if lookup(var.vpc_sc_egress_policies, i, null) != null
]
}
# dereference perimeter ingress policy names to the actual objects
_vpc_sc_perimeter_ingress_policies = {
for k, v in coalesce(var.vpc_sc_perimeter_ingress_policies, {}) :
k => [
for i in coalesce(v, []) : var.vpc_sc_ingress_policies[i]
if lookup(var.vpc_sc_ingress_policies, i, null) != null
]
}
# compute the number of projects in each perimeter to detect which to create # compute the number of projects in each perimeter to detect which to create
vpc_sc_counts = { vpc_sc_counts = {
for k in local._perimeter_names : k => length( for k, v in var.vpc_sc_perimeters : k => length(v.resources)
local.vpc_sc_perimeter_projects[k]
)
} }
# define dry run spec at file level for convenience # define dry run spec at file level for convenience
vpc_sc_explicit_dry_run_spec = true vpc_sc_explicit_dry_run_spec = true
# compute perimeter bridge resources (projects) # compute perimeter bridge resources (projects)
vpc_sc_p_bridge_resources = { vpc_sc_bridge_resources = {
landing_to_dev = concat( landing_to_dev = concat(
local.vpc_sc_perimeter_projects.landing, var.vpc_sc_perimeters.landing.resources,
local.vpc_sc_perimeter_projects.dev var.vpc_sc_perimeters.dev.resources
) )
landing_to_prod = concat( landing_to_prod = concat(
local.vpc_sc_perimeter_projects.landing, var.vpc_sc_perimeters.landing.resources,
local.vpc_sc_perimeter_projects.prod var.vpc_sc_perimeters.prod.resources
) )
} }
# computer perimeter regular specs / status # compute spec/status for each perimeter
vpc_sc_p_regular_specs = { vpc_sc_perimeters = {
dev = { dev = merge(var.vpc_sc_perimeters.dev, {
access_levels = coalesce( restricted_services = local._vpc_sc_restricted_services
try(var.vpc_sc_perimeter_access_levels.dev, null), [] vpc_accessible_services = local._vpc_sc_vpc_accessible_services
) })
resources = local.vpc_sc_perimeter_projects.dev
restricted_services = local.vpc_sc_restricted_services
egress_policies = try(
local._vpc_sc_perimeter_egress_policies.dev, null
)
ingress_policies = try(
local._vpc_sc_perimeter_ingress_policies.dev, null
)
vpc_accessible_services = null
# vpc_accessible_services = {
# allowed_services = ["RESTRICTED-SERVICES"]
# enable_restriction = true
# }
}
landing = {
access_levels = coalesce(
try(var.vpc_sc_perimeter_access_levels.landing, null), []
)
resources = local.vpc_sc_perimeter_projects.landing
restricted_services = local.vpc_sc_restricted_services
egress_policies = try(
local._vpc_sc_perimeter_egress_policies.landing, null
)
ingress_policies = try(
local._vpc_sc_perimeter_ingress_policies.landing, null
)
vpc_accessible_services = null
# vpc_accessible_services = {
# allowed_services = ["RESTRICTED-SERVICES"]
# enable_restriction = true
# }
}
prod = {
access_levels = coalesce(
try(var.vpc_sc_perimeter_access_levels.prod, null), []
)
# combine the security project, and any specified in the variable
resources = local.vpc_sc_perimeter_projects.prod
restricted_services = local.vpc_sc_restricted_services
egress_policies = try(
local._vpc_sc_perimeter_egress_policies.prod, null
)
ingress_policies = try(
local._vpc_sc_perimeter_ingress_policies.prod, null
)
vpc_accessible_services = null
# vpc_accessible_services = {
# allowed_services = ["RESTRICTED-SERVICES"]
# enable_restriction = true
# }
}
} }
# account for null values in variable
vpc_sc_perimeter_projects = (
var.vpc_sc_perimeter_projects == null ?
{
for k in local._perimeter_names : k => []
}
: {
for k in local._perimeter_names : k => (
var.vpc_sc_perimeter_projects[k] == null
? []
: var.vpc_sc_perimeter_projects[k]
)
}
)
# get the list of restricted services from the yaml file
vpc_sc_restricted_services = yamldecode(
file("${path.module}/vpc-sc-restricted-services.yaml")
)
} }
module "vpc-sc" { module "vpc-sc" {
@ -138,22 +54,39 @@ module "vpc-sc" {
parent = "organizations/${var.organization.id}" parent = "organizations/${var.organization.id}"
title = "default" title = "default"
} }
access_levels = coalesce(try(var.vpc_sc_access_levels, null), {}) access_levels = var.vpc_sc_access_levels
# bridge type perimeters egress_policies = var.vpc_sc_egress_policies
ingress_policies = var.vpc_sc_ingress_policies
service_perimeters_bridge = merge( service_perimeters_bridge = merge(
# landing to dev, only we have projects in landing and dev perimeters # landing to dev, only we have projects in landing and dev perimeters
local.vpc_sc_counts.landing * local.vpc_sc_counts.dev == 0 ? {} : { local.vpc_sc_counts.landing * local.vpc_sc_counts.dev == 0 ? {} : {
landing_to_dev = { landing_to_dev = {
spec_resources = local.vpc_sc_p_bridge_resources.landing_to_dev spec_resources = (
status_resources = null local.vpc_sc_explicit_dry_run_spec
? local.vpc_sc_bridge_resources.landing_to_dev
: null
)
status_resources = (
local.vpc_sc_explicit_dry_run_spec
? null
: local.vpc_sc_bridge_resources.landing_to_dev
)
use_explicit_dry_run_spec = local.vpc_sc_explicit_dry_run_spec use_explicit_dry_run_spec = local.vpc_sc_explicit_dry_run_spec
} }
}, },
# landing to prod, only we have projects in landing and prod perimeters # landing to prod, only we have projects in landing and prod perimeters
local.vpc_sc_counts.landing * local.vpc_sc_counts.prod == 0 ? {} : { local.vpc_sc_counts.landing * local.vpc_sc_counts.prod == 0 ? {} : {
landing_to_prod = { landing_to_prod = {
spec_resources = local.vpc_sc_p_bridge_resources.landing_to_prod spec_resources = (
status_resources = null local.vpc_sc_explicit_dry_run_spec
? local.vpc_sc_bridge_resources.landing_to_prod
: null
)
status_resources = (
local.vpc_sc_explicit_dry_run_spec
? null
: local.vpc_sc_bridge_resources.landing_to_prod
)
use_explicit_dry_run_spec = local.vpc_sc_explicit_dry_run_spec use_explicit_dry_run_spec = local.vpc_sc_explicit_dry_run_spec
} }
} }
@ -163,24 +96,48 @@ module "vpc-sc" {
# dev if we have projects in var.vpc_sc_perimeter_projects.dev # dev if we have projects in var.vpc_sc_perimeter_projects.dev
local.vpc_sc_counts.dev == 0 ? {} : { local.vpc_sc_counts.dev == 0 ? {} : {
dev = { dev = {
spec = local.vpc_sc_p_regular_specs.dev spec = (
status = null local.vpc_sc_explicit_dry_run_spec
? var.vpc_sc_perimeters.dev
: null
)
status = (
local.vpc_sc_explicit_dry_run_spec
? null
: var.vpc_sc_perimeters.dev
)
use_explicit_dry_run_spec = local.vpc_sc_explicit_dry_run_spec use_explicit_dry_run_spec = local.vpc_sc_explicit_dry_run_spec
} }
}, },
# landing if we have projects in var.vpc_sc_perimeter_projects.landing # landing if we have projects in var.vpc_sc_perimeter_projects.landing
local.vpc_sc_counts.landing == 0 ? {} : { local.vpc_sc_counts.landing == 0 ? {} : {
landing = { landing = {
spec = local.vpc_sc_p_regular_specs.landing spec = (
status = null local.vpc_sc_explicit_dry_run_spec
? var.vpc_sc_perimeters.landing
: null
)
status = (
local.vpc_sc_explicit_dry_run_spec
? null
: var.vpc_sc_perimeters.landing
)
use_explicit_dry_run_spec = local.vpc_sc_explicit_dry_run_spec use_explicit_dry_run_spec = local.vpc_sc_explicit_dry_run_spec
} }
}, },
# prod if we have projects in var.vpc_sc_perimeter_projects.prod # prod if we have projects in var.vpc_sc_perimeter_projects.prod
local.vpc_sc_counts.prod == 0 ? {} : { local.vpc_sc_counts.prod == 0 ? {} : {
prod = { prod = {
spec = local.vpc_sc_p_regular_specs.prod spec = (
status = null local.vpc_sc_explicit_dry_run_spec
? var.vpc_sc_perimeters.prod
: null
)
status = (
local.vpc_sc_explicit_dry_run_spec
? null
: var.vpc_sc_perimeters.prod
)
use_explicit_dry_run_spec = local.vpc_sc_explicit_dry_run_spec use_explicit_dry_run_spec = local.vpc_sc_explicit_dry_run_spec
} }
}, },

View File

@ -2,7 +2,7 @@
This module offers a unified interface to manage VPC Service Controls [Access Policy](https://cloud.google.com/access-context-manager/docs/create-access-policy), [Access Levels](https://cloud.google.com/access-context-manager/docs/manage-access-levels), and [Service Perimeters](https://cloud.google.com/vpc-service-controls/docs/service-perimeters). This module offers a unified interface to manage VPC Service Controls [Access Policy](https://cloud.google.com/access-context-manager/docs/create-access-policy), [Access Levels](https://cloud.google.com/access-context-manager/docs/manage-access-levels), and [Service Perimeters](https://cloud.google.com/vpc-service-controls/docs/service-perimeters).
Given the complexity of the underlying resources, the module intentionally mimics their interfaces to make it easier to map their documentation onto its variables, and reduce the internal complexity. The tradeoff is some verbosity, and a very complex type for the `service_perimeters_regular` variable (while [optional type attributes](https://www.terraform.io/language/expressions/type-constraints#experimental-optional-object-type-attributes) are still an experiment). Given the complexity of the underlying resources, the module intentionally mimics their interfaces to make it easier to map their documentation onto its variables, and reduce the internal complexity.
If you are using [Application Default Credentials](https://cloud.google.com/sdk/gcloud/reference/auth/application-default) with Terraform and run into permissions issues, make sure to check out the recommended provider configuration in the [VPC SC resources documentation](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/access_context_manager_access_level). If you are using [Application Default Credentials](https://cloud.google.com/sdk/gcloud/reference/auth/application-default) with Terraform and run into permissions issues, make sure to check out the recommended provider configuration in the [VPC SC resources documentation](https://registry.terraform.io/providers/hashicorp/google/latest/docs/resources/access_context_manager_access_level).
@ -44,21 +44,16 @@ module "test" {
access_policy = "12345678" access_policy = "12345678"
access_levels = { access_levels = {
a1 = { a1 = {
combining_function = null conditions = [
conditions = [{ { members = ["user:user1@example.com"] }
members = ["user:user1@example.com"], ip_subnetworks = null, ]
negate = null, regions = null, required_access_levels = null
}]
} }
a2 = { a2 = {
combining_function = "OR" combining_function = "OR"
conditions = [{ conditions = [
regions = ["IT", "FR"], ip_subnetworks = null, { regions = ["IT", "FR"] },
members = null, negate = null, required_access_levels = null { ip_subnetworks = ["101.101.101.0/24"] }
},{ ]
ip_subnetworks = ["101.101.101.0/24"], members = null,
negate = null, regions = null, required_access_levels = null
}]
} }
} }
} }
@ -85,12 +80,9 @@ module "test" {
access_policy = "12345678" access_policy = "12345678"
service_perimeters_bridge = { service_perimeters_bridge = {
b1 = { b1 = {
status_resources = ["projects/111110", "projects/111111"] status_resources = ["projects/111110", "projects/111111"]
spec_resources = null
use_explicit_dry_run_spec = false
} }
b2 = { b2 = {
status_resources = null
spec_resources = ["projects/222220", "projects/222221"] spec_resources = ["projects/222220", "projects/222221"]
use_explicit_dry_run_spec = true use_explicit_dry_run_spec = true
} }
@ -107,65 +99,61 @@ module "test" {
access_policy = "12345678" access_policy = "12345678"
access_levels = { access_levels = {
a1 = { a1 = {
combining_function = null conditions = [
conditions = [{ { members = ["user:user1@example.com"] }
members = ["user:user1@example.com"], ip_subnetworks = null, ]
negate = null, regions = null, required_access_levels = null
}]
} }
a2 = { a2 = {
combining_function = null conditions = [
conditions = [{ { members = ["user:user2@example.com"] }
members = ["user:user2@example.com"], ip_subnetworks = null, ]
negate = null, regions = null, required_access_levels = null }
}] }
egress_policies = {
# allow writing to external GCS bucket from a specific SA
gcs-sa-foo = {
from = {
identities = [
"serviceAccount:foo@myproject.iam.gserviceaccount.com"
]
}
to = {
operations = [{
method_selectors = ["*"]
service_name = "storage.googleapis.com"
}]
resources = ["projects/123456789"]
}
}
}
ingress_policies = {
# allow management from external automation SA
sa-tf-test = {
from = {
identities = [
"serviceAccount:test-tf@myproject.iam.gserviceaccount.com",
],
source_access_levels = ["*"]
}
to = {
operations = [{ service_name = "*" }]
resources = ["*"]
}
} }
} }
service_perimeters_regular = { service_perimeters_regular = {
r1 = { r1 = {
spec = null
status = { status = {
access_levels = [module.test.access_level_names["a1"], "a2"] access_levels = ["a1", "a2"]
resources = ["projects/11111", "projects/111111"] resources = ["projects/11111", "projects/111111"]
restricted_services = ["storage.googleapis.com"] restricted_services = ["storage.googleapis.com"]
# example: allow writing to external GCS bucket egress_policies = ["gcs-sa-foo"]
egress_policies = [ ingress_policies = ["sa-tf-test"]
{
egress_from = {
identity_type = null
identities = [
"serviceAccount:foo@myproject.iam.gserviceaccount.com"
]
}
egress_to = {
operations = [{
method_selectors = ["*"], service_name = "storage.googleapis.com"
}]
resources = ["projects/123456789"]
}
}
]
# example: allow management from external automation SA
ingress_policies = [
{
ingress_from = {
identities = [
"serviceAccount:test-tf@myproject.iam.gserviceaccount.com",
],
source_access_levels = ["*"], identity_type = null, source_resources = null
}
ingress_to = {
operations = [{ method_selectors = [], service_name = "*" }]
resources = ["*"]
}
}
]
vpc_accessible_services = { vpc_accessible_services = {
allowed_services = ["storage.googleapis.com"] allowed_services = ["storage.googleapis.com"]
enable_restriction = true enable_restriction = true
} }
} }
use_explicit_dry_run_spec = false
} }
} }
} }
@ -179,17 +167,33 @@ module "test" {
## TODO ## TODO
- [ ] implement support for the `google_access_context_manager_gcp_user_access_binding` resource - [ ] implement support for the `google_access_context_manager_gcp_user_access_binding` resource
<!-- TFDOC OPTS files:1 -->
<!-- BEGIN TFDOC --> <!-- BEGIN TFDOC -->
## Files
| name | description | resources |
|---|---|---|
| [access-levels.tf](./access-levels.tf) | Access level resources. | <code>google_access_context_manager_access_level</code> |
| [main.tf](./main.tf) | Module-level locals and resources. | <code>google_access_context_manager_access_policy</code> |
| [outputs.tf](./outputs.tf) | Module outputs. | |
| [service-perimeters-bridge.tf](./service-perimeters-bridge.tf) | Bridge service perimeter resources. | <code>google_access_context_manager_service_perimeter</code> |
| [service-perimeters-regular.tf](./service-perimeters-regular.tf) | Regular service perimeter resources. | <code>google_access_context_manager_service_perimeter</code> |
| [variables.tf](./variables.tf) | Module variables. | |
| [versions.tf](./versions.tf) | Version pins. | |
## Variables ## Variables
| name | description | type | required | default | | name | description | type | required | default |
|---|---|:---:|:---:|:---:| |---|---|:---:|:---:|:---:|
| [access_policy](variables.tf#L55) | Access Policy name, leave null to use auto-created one. | <code>string</code> | ✓ | | | [access_policy](variables.tf#L56) | Access Policy name, set to null if creating one. | <code>string</code> | ✓ | |
| [access_levels](variables.tf#L17) | Map of access levels in name => [conditions] format. | <code title="map&#40;object&#40;&#123;&#10; combining_function &#61; string&#10; conditions &#61; list&#40;object&#40;&#123;&#10; ip_subnetworks &#61; list&#40;string&#41;&#10; members &#61; list&#40;string&#41;&#10; negate &#61; bool&#10; regions &#61; list&#40;string&#41;&#10; required_access_levels &#61; list&#40;string&#41;&#10; &#125;&#41;&#41;&#10;&#125;&#41;&#41;">map&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | | <code>&#123;&#125;</code> | | [access_levels](variables.tf#L17) | Access level definitions. | <code title="map&#40;object&#40;&#123;&#10; combining_function &#61; optional&#40;string&#41;&#10; conditions &#61; optional&#40;list&#40;object&#40;&#123;&#10; device_policy &#61; optional&#40;object&#40;&#123;&#10; allowed_device_management_levels &#61; optional&#40;list&#40;string&#41;&#41;&#10; allowed_encryption_statuses &#61; optional&#40;list&#40;string&#41;&#41;&#10; require_admin_approval &#61; bool&#10; require_corp_owned &#61; bool&#10; require_screen_lock &#61; optional&#40;bool&#41;&#10; os_constraints &#61; optional&#40;list&#40;object&#40;&#123;&#10; os_type &#61; string&#10; minimum_version &#61; optional&#40;string&#41;&#10; require_verified_chrome_os &#61; optional&#40;bool&#41;&#10; &#125;&#41;&#41;&#41;&#10; &#125;&#41;&#41;&#10; ip_subnetworks &#61; optional&#40;list&#40;string&#41;, &#91;&#93;&#41;&#10; members &#61; optional&#40;list&#40;string&#41;, &#91;&#93;&#41;&#10; negate &#61; optional&#40;bool&#41;&#10; regions &#61; optional&#40;list&#40;string&#41;, &#91;&#93;&#41;&#10; required_access_levels &#61; optional&#40;list&#40;string&#41;, &#91;&#93;&#41;&#10; &#125;&#41;&#41;, &#91;&#93;&#41;&#10; description &#61; optional&#40;string&#41;&#10;&#125;&#41;&#41;">map&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | | <code>&#123;&#125;</code> |
| [access_policy_create](variables.tf#L60) | Access Policy configuration, fill in to create. Parent is in 'organizations/123456' format. | <code title="object&#40;&#123;&#10; parent &#61; string&#10; title &#61; string&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code>null</code> | | [access_policy_create](variables.tf#L61) | Access Policy configuration, fill in to create. Parent is in 'organizations/123456' format. | <code title="object&#40;&#123;&#10; parent &#61; string&#10; title &#61; string&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code>null</code> |
| [service_perimeters_bridge](variables.tf#L69) | Bridge service perimeters. | <code title="map&#40;object&#40;&#123;&#10; spec_resources &#61; list&#40;string&#41;&#10; status_resources &#61; list&#40;string&#41;&#10; use_explicit_dry_run_spec &#61; bool&#10;&#125;&#41;&#41;">map&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | | <code>&#123;&#125;</code> | | [egress_policies](variables.tf#L70) | Egress policy definitions that can be referenced in perimeters. | <code title="map&#40;object&#40;&#123;&#10; from &#61; object&#40;&#123;&#10; identity_type &#61; optional&#40;string, &#34;ANY_IDENTITY&#34;&#41;&#10; identities &#61; optional&#40;list&#40;string&#41;&#41;&#10; &#125;&#41;&#10; to &#61; object&#40;&#123;&#10; operations &#61; optional&#40;list&#40;object&#40;&#123;&#10; method_selectors &#61; optional&#40;list&#40;string&#41;&#41;&#10; service_name &#61; string&#10; &#125;&#41;&#41;, &#91;&#93;&#41;&#10; resources &#61; optional&#40;list&#40;string&#41;&#41;&#10; resource_type_external &#61; optional&#40;bool, false&#41;&#10; &#125;&#41;&#10;&#125;&#41;&#41;">map&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | | <code>&#123;&#125;</code> |
| [service_perimeters_regular](variables.tf#L79) | Regular service perimeters. | <code title="map&#40;object&#40;&#123;&#10; spec &#61; object&#40;&#123;&#10; access_levels &#61; list&#40;string&#41;&#10; resources &#61; list&#40;string&#41;&#10; restricted_services &#61; list&#40;string&#41;&#10; egress_policies &#61; list&#40;object&#40;&#123;&#10; egress_from &#61; object&#40;&#123;&#10; identity_type &#61; string&#10; identities &#61; list&#40;string&#41;&#10; &#125;&#41;&#10; egress_to &#61; object&#40;&#123;&#10; operations &#61; list&#40;object&#40;&#123;&#10; method_selectors &#61; list&#40;string&#41;&#10; service_name &#61; string&#10; &#125;&#41;&#41;&#10; resources &#61; list&#40;string&#41;&#10; &#125;&#41;&#10; &#125;&#41;&#41;&#10; ingress_policies &#61; list&#40;object&#40;&#123;&#10; ingress_from &#61; object&#40;&#123;&#10; identity_type &#61; string&#10; identities &#61; list&#40;string&#41;&#10; source_access_levels &#61; list&#40;string&#41;&#10; source_resources &#61; list&#40;string&#41;&#10; &#125;&#41;&#10; ingress_to &#61; object&#40;&#123;&#10; operations &#61; list&#40;object&#40;&#123;&#10; method_selectors &#61; list&#40;string&#41;&#10; service_name &#61; string&#10; &#125;&#41;&#41;&#10; resources &#61; list&#40;string&#41;&#10; &#125;&#41;&#10; &#125;&#41;&#41;&#10; vpc_accessible_services &#61; object&#40;&#123;&#10; allowed_services &#61; list&#40;string&#41;&#10; enable_restriction &#61; bool&#10; &#125;&#41;&#10; &#125;&#41;&#10; status &#61; object&#40;&#123;&#10; access_levels &#61; list&#40;string&#41;&#10; resources &#61; list&#40;string&#41;&#10; restricted_services &#61; list&#40;string&#41;&#10; egress_policies &#61; list&#40;object&#40;&#123;&#10; egress_from &#61; object&#40;&#123;&#10; identity_type &#61; string&#10; identities &#61; list&#40;string&#41;&#10; &#125;&#41;&#10; egress_to &#61; object&#40;&#123;&#10; operations &#61; list&#40;object&#40;&#123;&#10; method_selectors &#61; list&#40;string&#41;&#10; service_name &#61; string&#10; &#125;&#41;&#41;&#10; resources &#61; list&#40;string&#41;&#10; &#125;&#41;&#10; &#125;&#41;&#41;&#10; ingress_policies &#61; list&#40;object&#40;&#123;&#10; ingress_from &#61; object&#40;&#123;&#10; identity_type &#61; string&#10; identities &#61; list&#40;string&#41;&#10; source_access_levels &#61; list&#40;string&#41;&#10; source_resources &#61; list&#40;string&#41;&#10; &#125;&#41;&#10; ingress_to &#61; object&#40;&#123;&#10; operations &#61; list&#40;object&#40;&#123;&#10; method_selectors &#61; list&#40;string&#41;&#10; service_name &#61; string&#10; &#125;&#41;&#41;&#10; resources &#61; list&#40;string&#41;&#10; &#125;&#41;&#10; &#125;&#41;&#41;&#10; vpc_accessible_services &#61; object&#40;&#123;&#10; allowed_services &#61; list&#40;string&#41;&#10; enable_restriction &#61; bool&#10; &#125;&#41;&#10; &#125;&#41;&#10; use_explicit_dry_run_spec &#61; bool&#10;&#125;&#41;&#41;">map&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | | <code>&#123;&#125;</code> | | [ingress_policies](variables.tf#L99) | Ingress policy definitions that can be referenced in perimeters. | <code title="map&#40;object&#40;&#123;&#10; from &#61; object&#40;&#123;&#10; access_levels &#61; optional&#40;list&#40;string&#41;, &#91;&#93;&#41;&#10; identity_type &#61; optional&#40;string&#41;&#10; identities &#61; optional&#40;list&#40;string&#41;&#41;&#10; resources &#61; optional&#40;list&#40;string&#41;, &#91;&#93;&#41;&#10; &#125;&#41;&#10; to &#61; object&#40;&#123;&#10; operations &#61; optional&#40;list&#40;object&#40;&#123;&#10; method_selectors &#61; optional&#40;list&#40;string&#41;&#41;&#10; service_name &#61; string&#10; &#125;&#41;&#41;, &#91;&#93;&#41;&#10; resources &#61; optional&#40;list&#40;string&#41;&#41;&#10; &#125;&#41;&#10;&#125;&#41;&#41;">map&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | | <code>&#123;&#125;</code> |
| [service_perimeters_bridge](variables.tf#L130) | Bridge service perimeters. | <code title="map&#40;object&#40;&#123;&#10; spec_resources &#61; optional&#40;list&#40;string&#41;&#41;&#10; status_resources &#61; optional&#40;list&#40;string&#41;&#41;&#10; use_explicit_dry_run_spec &#61; optional&#40;bool, false&#41;&#10;&#125;&#41;&#41;">map&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | | <code>&#123;&#125;</code> |
| [service_perimeters_regular](variables.tf#L140) | Regular service perimeters. | <code title="map&#40;object&#40;&#123;&#10; spec &#61; optional&#40;object&#40;&#123;&#10; access_levels &#61; optional&#40;list&#40;string&#41;&#41;&#10; resources &#61; optional&#40;list&#40;string&#41;&#41;&#10; restricted_services &#61; optional&#40;list&#40;string&#41;&#41;&#10; egress_policies &#61; optional&#40;list&#40;string&#41;&#41;&#10; ingress_policies &#61; optional&#40;list&#40;string&#41;&#41;&#10; vpc_accessible_services &#61; optional&#40;object&#40;&#123;&#10; allowed_services &#61; list&#40;string&#41;&#10; enable_restriction &#61; bool&#10; &#125;&#41;&#41;&#10; &#125;&#41;, &#123;&#125;&#41;&#10; status &#61; optional&#40;object&#40;&#123;&#10; access_levels &#61; optional&#40;list&#40;string&#41;&#41;&#10; resources &#61; optional&#40;list&#40;string&#41;&#41;&#10; restricted_services &#61; optional&#40;list&#40;string&#41;&#41;&#10; egress_policies &#61; optional&#40;list&#40;string&#41;&#41;&#10; ingress_policies &#61; optional&#40;list&#40;string&#41;&#41;&#10; vpc_accessible_services &#61; optional&#40;object&#40;&#123;&#10; allowed_services &#61; list&#40;string&#41;&#10; enable_restriction &#61; bool&#10; &#125;&#41;&#41;&#10; &#125;&#41;, &#123;&#125;&#41;&#10; use_explicit_dry_run_spec &#61; optional&#40;bool, false&#41;&#10;&#125;&#41;&#41;">map&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | | <code>&#123;&#125;</code> |
## Outputs ## Outputs

View File

@ -14,66 +14,66 @@
* limitations under the License. * limitations under the License.
*/ */
# TODO(ludomagno): add a second variable and resource for custom access levels # tfdoc:file:description Access level resources.
# this code implements "additive" access levels, if "authoritative" # this code implements "additive" access levels, if "authoritative"
# access levels are needed, switch to the # access levels are needed, switch to the
# google_access_context_manager_access_levels resource # google_access_context_manager_access_levels resource
resource "google_access_context_manager_access_level" "basic" { resource "google_access_context_manager_access_level" "basic" {
for_each = var.access_levels == null ? {} : var.access_levels for_each = var.access_levels
parent = "accessPolicies/${local.access_policy}" parent = "accessPolicies/${local.access_policy}"
name = "accessPolicies/${local.access_policy}/accessLevels/${each.key}" name = "accessPolicies/${local.access_policy}/accessLevels/${each.key}"
title = each.key title = each.key
description = each.value.description
basic { basic {
combining_function = each.value.combining_function combining_function = each.value.combining_function
dynamic "conditions" { dynamic "conditions" {
for_each = toset( for_each = toset(each.value.conditions)
each.value.conditions == null ? [] : each.value.conditions iterator = c
)
iterator = condition
content { content {
# uncomment here and in the variable type to enable ip_subnetworks = c.value.ip_subnetworks
# dynamic "device_policy" { members = c.value.members
# for_each = toset( negate = c.value.negate
# condition.key.device_policy == null ? [] : [condition.key.device_policy] regions = c.value.regions
# ) required_access_levels = coalesce(c.value.required_access_levels, [])
# iterator = device_policy
# content { dynamic "device_policy" {
# dynamic "os_constraints" { for_each = c.value.device_policy == null ? [] : [c.value.device_policy]
# for_each = toset( iterator = dp
# device_policy.key.os_constraints == null ? [] : device_policy.key.os_constraints content {
# )
# iterator = os_constraint allowed_device_management_levels = (
# content { dp.value.allowed_device_management_levels
# minimum_version = os_constraint.key.minimum_version )
# os_type = os_constraint.key.os_type allowed_encryption_statuses = (
# require_verified_chrome_os = os_constraint.key.require_verified_chrome_os dp.value.allowed_encryption_statuses
# } )
# } require_admin_approval = dp.value.key.require_admin_approval
# allowed_encryption_statuses = device_policy.key.allowed_encryption_statuses require_corp_owned = dp.value.require_corp_owned
# allowed_device_management_levels = device_policy.key.allowed_device_management_levels require_screen_lock = dp.value.require_screen_lock
# require_admin_approval = device_policy.key.require_admin_approval
# require_corp_owned = device_policy.key.require_corp_owned dynamic "os_constraints" {
# require_screen_lock = device_policy.key.require_screen_lock for_each = toset(
# } dp.value.os_constraints == null
# } ? []
ip_subnetworks = ( : dp.value.os_constraints
condition.key.ip_subnetworks == null ? [] : condition.key.ip_subnetworks )
) iterator = oc
members = ( content {
condition.key.members == null ? [] : condition.key.members minimum_version = oc.value.minimum_version
) os_type = oc.value.os_type
negate = condition.key.negate require_verified_chrome_os = oc.value.require_verified_chrome_os
regions = ( }
condition.key.regions == null ? [] : condition.key.regions }
)
required_access_levels = ( }
condition.key.required_access_levels == null }
? []
: condition.key.required_access_levels
)
} }
} }
} }
} }

View File

@ -14,6 +14,8 @@
* limitations under the License. * limitations under the License.
*/ */
# tfdoc:file:description Bridge service perimeter resources.
# this code implements "additive" service perimeters, if "authoritative" # this code implements "additive" service perimeters, if "authoritative"
# service perimeters are needed, switch to the # service perimeters are needed, switch to the
# google_access_context_manager_service_perimeters resource # google_access_context_manager_service_perimeters resource

View File

@ -14,6 +14,8 @@
* limitations under the License. * limitations under the License.
*/ */
# tfdoc:file:description Regular service perimeter resources.
# this code implements "additive" service perimeters, if "authoritative" # this code implements "additive" service perimeters, if "authoritative"
# service perimeters are needed, switch to the # service perimeters are needed, switch to the
# google_access_context_manager_service_perimeters resource # google_access_context_manager_service_perimeters resource
@ -26,7 +28,7 @@ resource "google_access_context_manager_service_perimeter" "regular" {
perimeter_type = "PERIMETER_TYPE_REGULAR" perimeter_type = "PERIMETER_TYPE_REGULAR"
use_explicit_dry_run_spec = each.value.use_explicit_dry_run_spec use_explicit_dry_run_spec = each.value.use_explicit_dry_run_spec
dynamic "spec" { dynamic "spec" {
for_each = each.value.spec == null ? {} : { 1 = 1 } for_each = each.value.spec == null ? [] : [""]
content { content {
access_levels = ( access_levels = (
each.value.spec.access_levels == null ? null : [ each.value.spec.access_levels == null ? null : [
@ -36,43 +38,33 @@ resource "google_access_context_manager_service_perimeter" "regular" {
) )
resources = each.value.spec.resources resources = each.value.spec.resources
restricted_services = each.value.spec.restricted_services restricted_services = each.value.spec.restricted_services
# begin egress_policies
dynamic "egress_policies" { dynamic "egress_policies" {
for_each = toset( for_each = each.value.spec.egress_policies == null ? {} : {
each.value.spec.egress_policies == null for k in each.value.spec.egress_policies :
? [] k => lookup(var.egress_policies, k, null)
: each.value.spec.egress_policies if contains(keys(var.egress_policies), k)
) }
iterator = policy iterator = policy
content { content {
# begin egress_from
dynamic "egress_from" { dynamic "egress_from" {
for_each = policy.key.egress_from == null ? {} : { 1 = 1 } for_each = policy.value.from == null ? [] : [""]
content { content {
identity_type = policy.key.egress_from.identity_type identity_type = policy.value.from.identity_type
identities = policy.key.egress_from.identities identities = policy.value.from.identities
} }
} }
# end egress_from
# begin egress_to
dynamic "egress_to" { dynamic "egress_to" {
for_each = policy.key.egress_to == null ? {} : { 1 = 1 } for_each = policy.value.to == null ? [] : [""]
content { content {
resources = policy.key.egress_to.resources resources = policy.value.to.resources
dynamic "operations" { dynamic "operations" {
for_each = toset( for_each = toset(policy.value.to.operations)
policy.key.egress_to.operations == null iterator = o
? []
: policy.key.egress_to.operations
)
content { content {
service_name = operations.key.service_name service_name = o.value.service_name
dynamic "method_selectors" { dynamic "method_selectors" {
for_each = toset( for_each = toset(coalesce(o.value.method_selectors, []))
operations.key.method_selectors == null
? []
: operations.key.method_selectors
)
content { content {
method = method_selectors.key method = method_selectors.key
} }
@ -81,82 +73,61 @@ resource "google_access_context_manager_service_perimeter" "regular" {
} }
} }
} }
# end egress_to
} }
} }
# end egress_policies
# begin ingress_policies
dynamic "ingress_policies" { dynamic "ingress_policies" {
for_each = toset( for_each = each.value.spec.ingress_policies == null ? {} : {
each.value.spec.ingress_policies == null for k in each.value.spec.ingress_policies :
? [] k => lookup(var.ingress_policies, k, null)
: each.value.spec.ingress_policies if contains(keys(var.ingress_policies), k)
) }
iterator = policy iterator = policy
content { content {
# begin ingress_from
dynamic "ingress_from" { dynamic "ingress_from" {
for_each = policy.key.ingress_from == null ? {} : { 1 = 1 } for_each = policy.value.from == null ? [] : [""]
content { content {
identity_type = policy.key.ingress_from.identity_type identity_type = policy.value.from.identity_type
identities = policy.key.ingress_from.identities identities = policy.value.from.identities
# begin sources
dynamic "sources" { dynamic "sources" {
for_each = toset( for_each = toset(policy.value.from.access_levels)
policy.key.ingress_from.source_access_levels == null iterator = s
? []
: policy.key.ingress_from.source_access_levels
)
content { content {
access_level = sources.key access_level = try(
google_access_context_manager_access_level.basic[s.value].id, s.value
)
} }
} }
dynamic "sources" { dynamic "sources" {
for_each = toset( for_each = toset(policy.value.from.resources)
policy.key.ingress_from.source_resources == null
? []
: policy.key.ingress_from.source_resources
)
content { content {
resource = sources.key resource = sources.key
} }
} }
# end sources
} }
} }
# end ingress_from
# begin ingress_to
dynamic "ingress_to" { dynamic "ingress_to" {
for_each = policy.key.ingress_to == null ? {} : { 1 = 1 } for_each = policy.value.to == null ? [] : [""]
content { content {
resources = policy.key.ingress_to.resources resources = policy.value.to.resources
dynamic "operations" { dynamic "operations" {
for_each = toset( for_each = toset(policy.value.to.operations)
policy.key.ingress_to.operations == null iterator = o
? []
: policy.key.ingress_to.operations
)
content { content {
service_name = operations.key.service_name service_name = o.value.service_name
dynamic "method_selectors" { dynamic "method_selectors" {
for_each = toset( for_each = toset(coalesce(o.value.method_selectors, []))
operations.key.method_selectors == null
? []
: operations.key.method_selectors
)
content { content {
method = method_selectors.key method = method_selectors.value
} }
} }
} }
} }
} }
} }
# end ingress_to
} }
} }
# end ingress_policies
# begin vpc_accessible_services
dynamic "vpc_accessible_services" { dynamic "vpc_accessible_services" {
for_each = each.value.spec.vpc_accessible_services == null ? {} : { 1 = 1 } for_each = each.value.spec.vpc_accessible_services == null ? {} : { 1 = 1 }
content { content {
@ -164,7 +135,7 @@ resource "google_access_context_manager_service_perimeter" "regular" {
enable_restriction = each.value.spec.vpc_accessible_services.enable_restriction enable_restriction = each.value.spec.vpc_accessible_services.enable_restriction
} }
} }
# end vpc_accessible_services
} }
} }
dynamic "status" { dynamic "status" {
@ -178,43 +149,33 @@ resource "google_access_context_manager_service_perimeter" "regular" {
) )
resources = each.value.status.resources resources = each.value.status.resources
restricted_services = each.value.status.restricted_services restricted_services = each.value.status.restricted_services
# begin egress_policies
dynamic "egress_policies" { dynamic "egress_policies" {
for_each = toset( for_each = each.value.spec.egress_policies == null ? {} : {
each.value.status.egress_policies == null for k in each.value.spec.egress_policies :
? [] k => lookup(var.egress_policies, k, null)
: each.value.status.egress_policies if contains(keys(var.egress_policies), k)
) }
iterator = policy iterator = policy
content { content {
# begin egress_from
dynamic "egress_from" { dynamic "egress_from" {
for_each = policy.key.egress_from == null ? {} : { 1 = 1 } for_each = policy.value.from == null ? [] : [""]
content { content {
identity_type = policy.key.egress_from.identity_type identity_type = policy.value.from.identity_type
identities = policy.key.egress_from.identities identities = policy.value.from.identities
} }
} }
# end egress_from
# begin egress_to
dynamic "egress_to" { dynamic "egress_to" {
for_each = policy.key.egress_to == null ? {} : { 1 = 1 } for_each = policy.value.to == null ? [] : [""]
content { content {
resources = policy.key.egress_to.resources resources = policy.value.to.resources
dynamic "operations" { dynamic "operations" {
for_each = toset( for_each = toset(policy.value.to.operations)
policy.key.egress_to.operations == null iterator = o
? []
: policy.key.egress_to.operations
)
content { content {
service_name = operations.key.service_name service_name = o.value.service_name
dynamic "method_selectors" { dynamic "method_selectors" {
for_each = toset( for_each = toset(coalesce(o.value.method_selectors, []))
operations.key.method_selectors == null
? []
: operations.key.method_selectors
)
content { content {
method = method_selectors.key method = method_selectors.key
} }
@ -223,82 +184,61 @@ resource "google_access_context_manager_service_perimeter" "regular" {
} }
} }
} }
# end egress_to
} }
} }
# end egress_policies
# begin ingress_policies
dynamic "ingress_policies" { dynamic "ingress_policies" {
for_each = toset( for_each = each.value.spec.ingress_policies == null ? {} : {
each.value.status.ingress_policies == null for k in each.value.spec.ingress_policies :
? [] k => lookup(var.ingress_policies, k, null)
: each.value.status.ingress_policies if contains(keys(var.ingress_policies), k)
) }
iterator = policy iterator = policy
content { content {
# begin ingress_from
dynamic "ingress_from" { dynamic "ingress_from" {
for_each = policy.key.ingress_from == null ? {} : { 1 = 1 } for_each = policy.value.from == null ? [] : [""]
content { content {
identity_type = policy.key.ingress_from.identity_type identity_type = policy.value.from.identity_type
identities = policy.key.ingress_from.identities identities = policy.value.from.identities
# begin sources
dynamic "sources" { dynamic "sources" {
for_each = toset( for_each = toset(policy.value.from.access_levels)
policy.key.ingress_from.source_access_levels == null iterator = s
? []
: policy.key.ingress_from.source_access_levels
)
content { content {
access_level = sources.key access_level = try(
google_access_context_manager_access_level.basic[s.value].id, s.value
)
} }
} }
dynamic "sources" { dynamic "sources" {
for_each = toset( for_each = toset(policy.value.from.resources)
policy.key.ingress_from.source_resources == null
? []
: policy.key.ingress_from.source_resources
)
content { content {
resource = sources.key resource = sources.key
} }
} }
# end sources
} }
} }
# end ingress_from
# begin ingress_to
dynamic "ingress_to" { dynamic "ingress_to" {
for_each = policy.key.ingress_to == null ? {} : { 1 = 1 } for_each = policy.value.to == null ? [] : [""]
content { content {
resources = policy.key.ingress_to.resources resources = policy.value.to.resources
dynamic "operations" { dynamic "operations" {
for_each = toset( for_each = toset(policy.value.to.operations)
policy.key.ingress_to.operations == null iterator = o
? []
: policy.key.ingress_to.operations
)
content { content {
service_name = operations.key.service_name service_name = o.value.service_name
dynamic "method_selectors" { dynamic "method_selectors" {
for_each = toset( for_each = toset(coalesce(o.value.method_selectors, []))
operations.key.method_selectors == null
? []
: operations.key.method_selectors
)
content { content {
method = method_selectors.key method = method_selectors.value
} }
} }
} }
} }
} }
} }
# end ingress_to
} }
} }
# end ingress_policies
# begin vpc_accessible_services
dynamic "vpc_accessible_services" { dynamic "vpc_accessible_services" {
for_each = each.value.status.vpc_accessible_services == null ? {} : { 1 = 1 } for_each = each.value.status.vpc_accessible_services == null ? {} : { 1 = 1 }
content { content {
@ -306,7 +246,7 @@ resource "google_access_context_manager_service_perimeter" "regular" {
enable_restriction = each.value.status.vpc_accessible_services.enable_restriction enable_restriction = each.value.status.vpc_accessible_services.enable_restriction
} }
} }
# end vpc_accessible_services
} }
} }
# lifecycle { # lifecycle {

View File

@ -15,31 +15,32 @@
*/ */
variable "access_levels" { variable "access_levels" {
description = "Map of access levels in name => [conditions] format." description = "Access level definitions."
type = map(object({ type = map(object({
combining_function = string combining_function = optional(string)
conditions = list(object({ conditions = optional(list(object({
# disabled to reduce var surface, uncomment here and in resource to enable device_policy = optional(object({
# device_policy = object({ allowed_device_management_levels = optional(list(string))
# require_screen_lock = bool allowed_encryption_statuses = optional(list(string))
# allowed_encryption_statuses = list(string) require_admin_approval = bool
# allowed_device_management_levels = list(string) require_corp_owned = bool
# os_constraints = list(object({ require_screen_lock = optional(bool)
# minimum_version = string os_constraints = optional(list(object({
# os_type = string os_type = string
# require_verified_chrome_os = bool minimum_version = optional(string)
# })) require_verified_chrome_os = optional(bool)
# require_admin_approval = bool })))
# require_corp_owned = bool }))
# }) ip_subnetworks = optional(list(string), [])
ip_subnetworks = list(string) members = optional(list(string), [])
members = list(string) negate = optional(bool)
negate = bool regions = optional(list(string), [])
regions = list(string) required_access_levels = optional(list(string), [])
required_access_levels = list(string) })), [])
})) description = optional(string)
})) }))
default = {} default = {}
nullable = false
validation { validation {
condition = alltrue([ condition = alltrue([
for k, v in var.access_levels : ( for k, v in var.access_levels : (
@ -53,7 +54,7 @@ variable "access_levels" {
} }
variable "access_policy" { variable "access_policy" {
description = "Access Policy name, leave null to use auto-created one." description = "Access Policy name, set to null if creating one."
type = string type = string
} }
@ -66,12 +67,72 @@ variable "access_policy_create" {
default = null default = null
} }
variable "egress_policies" {
description = "Egress policy definitions that can be referenced in perimeters."
type = map(object({
from = object({
identity_type = optional(string, "ANY_IDENTITY")
identities = optional(list(string))
})
to = object({
operations = optional(list(object({
method_selectors = optional(list(string))
service_name = string
})), [])
resources = optional(list(string))
resource_type_external = optional(bool, false)
})
}))
default = {}
nullable = false
validation {
condition = alltrue([
for k, v in var.egress_policies : contains([
"IDENTITY_TYPE_UNSPECIFIED", "ANY_IDENTITY",
"ANY_USER", "ANY_SERVICE_ACCOUNT"
], v.from.identity_type)
])
error_message = "Invalid `from.identity_type` value in eress policy."
}
}
variable "ingress_policies" {
description = "Ingress policy definitions that can be referenced in perimeters."
type = map(object({
from = object({
access_levels = optional(list(string), [])
identity_type = optional(string)
identities = optional(list(string))
resources = optional(list(string), [])
})
to = object({
operations = optional(list(object({
method_selectors = optional(list(string))
service_name = string
})), [])
resources = optional(list(string))
})
}))
default = {}
nullable = false
validation {
condition = alltrue([
for k, v in var.ingress_policies :
v.from.identity_type == null || contains([
"IDENTITY_TYPE_UNSPECIFIED", "ANY_IDENTITY",
"ANY_USER", "ANY_SERVICE_ACCOUNT"
], coalesce(v.from.identity_type, "-"))
])
error_message = "Invalid `from.identity_type` value in eress policy."
}
}
variable "service_perimeters_bridge" { variable "service_perimeters_bridge" {
description = "Bridge service perimeters." description = "Bridge service perimeters."
type = map(object({ type = map(object({
spec_resources = list(string) spec_resources = optional(list(string))
status_resources = list(string) status_resources = optional(list(string))
use_explicit_dry_run_spec = bool use_explicit_dry_run_spec = optional(bool, false)
})) }))
default = {} default = {}
} }
@ -79,81 +140,30 @@ variable "service_perimeters_bridge" {
variable "service_perimeters_regular" { variable "service_perimeters_regular" {
description = "Regular service perimeters." description = "Regular service perimeters."
type = map(object({ type = map(object({
spec = object({ spec = optional(object({
access_levels = list(string) access_levels = optional(list(string))
resources = list(string) resources = optional(list(string))
restricted_services = list(string) restricted_services = optional(list(string))
egress_policies = list(object({ egress_policies = optional(list(string))
egress_from = object({ ingress_policies = optional(list(string))
identity_type = string vpc_accessible_services = optional(object({
identities = list(string)
})
egress_to = object({
operations = list(object({
method_selectors = list(string)
service_name = string
}))
resources = list(string)
})
}))
ingress_policies = list(object({
ingress_from = object({
identity_type = string
identities = list(string)
source_access_levels = list(string)
source_resources = list(string)
})
ingress_to = object({
operations = list(object({
method_selectors = list(string)
service_name = string
}))
resources = list(string)
})
}))
vpc_accessible_services = object({
allowed_services = list(string) allowed_services = list(string)
enable_restriction = bool enable_restriction = bool
})
})
status = object({
access_levels = list(string)
resources = list(string)
restricted_services = list(string)
egress_policies = list(object({
egress_from = object({
identity_type = string
identities = list(string)
})
egress_to = object({
operations = list(object({
method_selectors = list(string)
service_name = string
}))
resources = list(string)
})
})) }))
ingress_policies = list(object({ }), {})
ingress_from = object({ status = optional(object({
identity_type = string access_levels = optional(list(string))
identities = list(string) resources = optional(list(string))
source_access_levels = list(string) restricted_services = optional(list(string))
source_resources = list(string) egress_policies = optional(list(string))
}) ingress_policies = optional(list(string))
ingress_to = object({ vpc_accessible_services = optional(object({
operations = list(object({
method_selectors = list(string)
service_name = string
}))
resources = list(string)
})
}))
vpc_accessible_services = object({
allowed_services = list(string) allowed_services = list(string)
enable_restriction = bool enable_restriction = bool
}) }))
}) }), {})
use_explicit_dry_run_spec = bool use_explicit_dry_run_spec = optional(bool, false)
})) }))
default = {} default = {}
nullable = false
} }

View File

@ -49,68 +49,59 @@ module "stage" {
project-factory-dev = "foobar@iam.gserviceaccount.com" project-factory-dev = "foobar@iam.gserviceaccount.com"
project-factory-prod = "foobar@iam.gserviceaccount.com" project-factory-prod = "foobar@iam.gserviceaccount.com"
} }
vpc_sc_access_levels = {
onprem = {
conditions = [{
ip_subnetworks = ["101.101.101.0/24"]
}]
}
}
vpc_sc_egress_policies = {
iac-gcs = {
from = {
identities = [
"serviceAccount:xxx-prod-resman-security-0@xxx-prod-iac-core-0.iam.gserviceaccount.com"
]
}
to = {
operations = [{
method_selectors = ["*"]
service_name = "storage.googleapis.com"
}]
resources = ["projects/123456782"]
}
}
}
vpc_sc_ingress_policies = { vpc_sc_ingress_policies = {
iac = { iac = {
ingress_from = { from = {
identities = [ identities = [
"serviceAccount:fast-prod-resman-security-0@fast-prod-iac-core-0.iam.gserviceaccount.com" "serviceAccount:xxx-prod-resman-security-0@xxx-prod-iac-core-0.iam.gserviceaccount.com"
], ]
source_access_levels = ["*"], identity_type = null, source_resources = null access_levels = ["*"]
} }
ingress_to = { to = {
operations = [{ method_selectors = [], service_name = "*" }] operations = [{ method_selectors = [], service_name = "*" }]
resources = ["*"] resources = ["*"]
} }
} }
} }
vpc_sc_perimeter_ingress_policies = { vpc_sc_perimeters = {
dev = ["iac"] dev = {
landing = null egress_policies = ["iac-gcs"]
prod = ["iac"] ingress_policies = ["iac"]
} resources = ["projects/1111111111"]
vpc_sc_perimeter_projects = {
dev = [
"projects/345678912", # ludo-dev-sec-core-0
]
landing = []
prod = [
"projects/234567891", # ludo-prod-sec-core-0
]
}
vpc_sc_access_levels = {
all = {
combining_function = null
conditions = [{
members = [
"serviceAccount:quota-monitor@foobar.iam.gserviceaccount.com",
],
ip_subnetworks = null, negate = null, regions = null,
required_access_levels = null
}]
} }
} dev = {
egress_policies = ["iac-gcs"]
vpc_sc_perimeter_access_levels = { ingress_policies = ["iac"]
dev = ["all"] resources = ["projects/0000000000"]
landing = null }
prod = ["all"] dev = {
} access_levels = ["onprem"]
egress_policies = ["iac-gcs"]
vpc_sc_egress_policies = { ingress_policies = ["iac"]
iac-gcs = { resources = ["projects/2222222222"]
egress_from = {
identity_type = null
identities = [
"serviceAccount:fast-prod-resman-security-0@fast-prod-iac-core-0.iam.gserviceaccount.com"
]
}
egress_to = {
operations = [{
method_selectors = ["*"], service_name = "storage.googleapis.com"
}]
resources = ["projects/123456789"]
}
} }
} }
} }

View File

@ -14,133 +14,13 @@
* limitations under the License. * limitations under the License.
*/ */
variable "access_policy" {
description = "Access Policy name, leave null to use auto-created one."
type = string
default = null
}
variable "access_policy_create" {
description = "Access Policy configuration, fill in to create. Parent is in 'organizations/123456' format."
type = object({
parent = string
title = string
})
default = {
parent = "organizations/123456"
title = "vpcsc-policy"
}
}
module "test" { module "test" {
source = "../../../../modules/vpc-sc" source = "../../../../modules/vpc-sc"
access_policy = var.access_policy access_policy = var.access_policy
access_policy_create = var.access_policy_create access_policy_create = var.access_policy_create
access_levels = { access_levels = var.access_levels
a1 = { egress_policies = var.egress_policies
combining_function = null ingress_policies = var.ingress_policies
conditions = [ service_perimeters_bridge = var.service_perimeters_bridge
{ service_perimeters_regular = var.service_perimeters_regular
device_policy = null
ip_subnetworks = null
members = ["user:ludomagno@google.com"]
negate = null
regions = null
required_access_levels = null
}
]
}
a2 = {
combining_function = "OR"
conditions = [
{
device_policy = null
ip_subnetworks = null
members = null
negate = null
regions = ["IT", "FR"]
required_access_levels = null
},
{
device_policy = null
ip_subnetworks = null
members = null
negate = null
regions = ["US"]
required_access_levels = null
}
]
}
}
service_perimeters_bridge = {
b1 = {
status_resources = ["projects/111110", "projects/111111"]
spec_resources = null
use_explicit_dry_run_spec = false
}
b2 = {
status_resources = ["projects/111110", "projects/222220"]
spec_resources = ["projects/111110", "projects/222220"]
use_explicit_dry_run_spec = true
}
}
service_perimeters_regular = {
r1 = {
spec = null
status = {
access_levels = [module.test.access_level_names["a1"]]
resources = ["projects/11111", "projects/111111"]
restricted_services = ["storage.googleapis.com"]
egress_policies = null
ingress_policies = null
vpc_accessible_services = {
allowed_services = ["compute.googleapis.com"]
enable_restriction = true
}
}
use_explicit_dry_run_spec = false
}
r2 = {
spec = null
status = {
access_levels = [module.test.access_level_names["a1"], "a2"]
resources = ["projects/222220", "projects/222221"]
restricted_services = ["storage.googleapis.com"]
egress_policies = [
{
egress_from = {
identity_type = null
identities = ["user:foo@example.com"]
}
egress_to = {
operations = null
resources = ["projects/333330"]
}
}
]
ingress_policies = [
{
ingress_from = {
identity_type = null
identities = null
source_access_levels = [module.test.access_level_names["a2"]]
source_resources = ["projects/333330"]
}
ingress_to = {
operations = [{
method_selectors = null
service_name = "compute.googleapis.com"
}]
resources = ["projects/222220"]
}
}
]
vpc_accessible_services = {
allowed_services = ["compute.googleapis.com"]
enable_restriction = true
}
}
use_explicit_dry_run_spec = false
}
}
} }

View File

@ -0,0 +1,96 @@
access_levels = {
a1 = {
combining_function = null
conditions = [
{
device_policy = null
ip_subnetworks = null
members = ["user:ludomagno@google.com"]
negate = null
regions = null
required_access_levels = null
}
]
}
a2 = {
combining_function = "OR"
conditions = [
{
device_policy = null
ip_subnetworks = null
members = null
negate = null
regions = ["IT", "FR"]
required_access_levels = null
},
{
device_policy = null
ip_subnetworks = null
members = null
negate = null
regions = ["US"]
required_access_levels = null
}
]
}
}
egress_policies = {
foo = {
from = {
identities = ["user:foo@example.com"]
}
to = {
resources = ["projects/333330"]
}
}
}
ingress_policies = {
foo = {
from = {
source_access_levels = ["a2"]
source_resources = ["projects/333330"]
}
to = {
operations = [{
service_name = "compute.googleapis.com"
}]
resources = ["projects/222220"]
}
}
}
service_perimeters_bridge = {
b1 = {
status_resources = ["projects/111110", "projects/111111"]
}
b2 = {
status_resources = ["projects/111110", "projects/222220"]
spec_resources = ["projects/111110", "projects/222220"]
use_explicit_dry_run_spec = true
}
}
service_perimeters_regular = {
r1 = {
status = {
access_levels = ["a1"]
resources = ["projects/11111", "projects/111111"]
restricted_services = ["storage.googleapis.com"]
vpc_accessible_services = {
allowed_services = ["compute.googleapis.com"]
enable_restriction = true
}
}
}
r2 = {
status = {
access_levels = ["a1", "a2"]
resources = ["projects/222220", "projects/222221"]
restricted_services = ["storage.googleapis.com"]
egress_policies = ["foo"]
ingress_policies = ["foo"]
vpc_accessible_services = {
allowed_services = ["compute.googleapis.com"]
enable_restriction = true
}
}
}
}

View File

@ -0,0 +1,53 @@
/**
* Copyright 2022 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
variable "access_levels" {
type = any
default = {}
nullable = false
}
variable "access_policy" {
type = string
}
variable "access_policy_create" {
type = any
default = null
}
variable "egress_policies" {
type = any
default = {}
nullable = false
}
variable "ingress_policies" {
type = any
default = {}
nullable = false
}
variable "service_perimeters_bridge" {
type = any
default = {}
}
variable "service_perimeters_regular" {
type = any
default = {}
nullable = false
}

View File

@ -12,13 +12,19 @@
# See the License for the specific language governing permissions and # See the License for the specific language governing permissions and
# limitations under the License. # limitations under the License.
import collections
def test_create_policy(plan_runner): def test_create_policy(plan_runner):
"Test with auto-created policy." "Test with auto-created policy."
_, resources = plan_runner() access_policy_create = '''{
counts = {} parent = "organizations/123456"
for r in resources: title = "vpcsc-policy"
n = f'{r["type"]}.{r["name"]}' }'''
counts[n] = counts.get(n, 0) + 1 _, resources = plan_runner(tf_var_file='test.regular.tfvars',
access_policy='null',
access_policy_create=access_policy_create)
counts = collections.Counter(f'{r["type"]}.{r["name"]}' for r in resources)
assert counts == { assert counts == {
'google_access_context_manager_access_level.basic': 2, 'google_access_context_manager_access_level.basic': 2,
'google_access_context_manager_access_policy.default': 1, 'google_access_context_manager_access_policy.default': 1,
@ -29,12 +35,9 @@ def test_create_policy(plan_runner):
def test_use_policy(plan_runner): def test_use_policy(plan_runner):
"Test with existing policy." "Test with existing policy."
_, resources = plan_runner(access_policy_create="null", _, resources = plan_runner(tf_var_file='test.regular.tfvars',
access_policy="accessPolicies/foobar") access_policy="accessPolicies/foobar")
counts = {} counts = collections.Counter(f'{r["type"]}.{r["name"]}' for r in resources)
for r in resources:
n = f'{r["type"]}.{r["name"]}'
counts[n] = counts.get(n, 0) + 1
assert counts == { assert counts == {
'google_access_context_manager_access_level.basic': 2, 'google_access_context_manager_access_level.basic': 2,
'google_access_context_manager_service_perimeter.bridge': 2, 'google_access_context_manager_service_perimeter.bridge': 2,