Merge branch 'master' into fast/gke2
This commit is contained in:
commit
25955b158a
|
@ -31,3 +31,6 @@ fast/stages/**/globals.auto.tfvars.json
|
|||
cloud_sql_proxy
|
||||
examples/cloud-operations/binauthz/tenant-setup.yaml
|
||||
examples/cloud-operations/binauthz/app/app.yaml
|
||||
env/
|
||||
examples/cloud-operations/adfs/ansible/vars/vars.yaml
|
||||
examples/cloud-operations/adfs/ansible/gssh.sh
|
||||
|
|
|
@ -14,7 +14,7 @@ Contributors are the engine that keeps Fabric alive so if you were or are planni
|
|||
- [Design principles in action](#design-principles-in-action)
|
||||
- [FAST stage design](#fast-stage-design)
|
||||
- [Style guide reference](#style-guide-reference)
|
||||
- [Checks, tests and tools](#checks-tests-and-tools)
|
||||
- [Checks, tests and tools](#interacting-with-checks-tests-and-tools)
|
||||
|
||||
## I just found a bug / have a feature request
|
||||
|
||||
|
|
|
@ -29,7 +29,7 @@ The current list of modules supports most of the core foundational and networkin
|
|||
|
||||
Currently available modules:
|
||||
|
||||
- **foundational** - [folder](./modules/folder), [organization](./modules/organization), [project](./modules/project), [service accounts](./modules/iam-service-account), [logging bucket](./modules/logging-bucket), [billing budget](./modules/billing-budget), [naming convention](./modules/naming-convention), [projects-data-source](./modules/projects-data-source)
|
||||
- **foundational** - [folder](./modules/folder), [organization](./modules/organization), [project](./modules/project), [service accounts](./modules/iam-service-account), [logging bucket](./modules/logging-bucket), [billing budget](./modules/billing-budget), [naming convention](./modules/naming-convention), [projects-data-source](./modules/projects-data-source), [organization-policy](./modules/organization-policy)
|
||||
- **networking** - [VPC](./modules/net-vpc), [VPC firewall](./modules/net-vpc-firewall), [VPC peering](./modules/net-vpc-peering), [VPN static](./modules/net-vpn-static), [VPN dynamic](./modules/net-vpn-dynamic), [HA VPN](./modules/net-vpn-ha), [NAT](./modules/net-cloudnat), [address reservation](./modules/net-address), [DNS](./modules/dns), [L4 ILB](./modules/net-ilb), [L7 ILB](./modules/net-ilb-l7), [Service Directory](./modules/service-directory), [Cloud Endpoints](./modules/endpoints)
|
||||
- **compute** - [VM/VM group](./modules/compute-vm), [MIG](./modules/compute-mig), [GKE cluster](./modules/gke-cluster), [GKE nodepool](./modules/gke-nodepool), [GKE hub](./modules/gke-hub), [COS container](./modules/cloud-config-container/cos-generic-metadata/) (coredns, mysql, onprem, squid)
|
||||
- **data** - [GCS](./modules/gcs), [BigQuery dataset](./modules/bigquery-dataset), [Pub/Sub](./modules/pubsub), [Datafusion](./modules/datafusion), [Bigtable instance](./modules/bigtable-instance), [Cloud SQL instance](./modules/cloudsql-instance), [Data Catalog Policy Tag](./modules/data-catalog-policy-tag)
|
||||
|
|
|
@ -0,0 +1,76 @@
|
|||
# AD FS
|
||||
|
||||
This example does the following:
|
||||
|
||||
Terraform:
|
||||
|
||||
- (Optional) Creates a project.
|
||||
- (Optional) Creates a VPC.
|
||||
- Sets up managed AD
|
||||
- Creates a server where AD FS will be installed. This machine will also act as admin workstation for AD.
|
||||
- Exposes AD FS using GLB.
|
||||
|
||||
Ansible:
|
||||
|
||||
- Installs the required Windows features and joins the computer to the AD domain.
|
||||
- Provisions some tests users, groups and group memberships in AD. The data to provision is in the ifles directory of the ad-provisioning ansible role. There is script available in the scripts/ad-provisioning folder that you can use to generate an alternative users or memberships file.
|
||||
- Installs AD FS
|
||||
|
||||
In addition to this, we also include a Powershell script that facilitates the configuration required for Anthos when authenticating users with AD FS as IdP.
|
||||
|
||||
The diagram below depicts the architecture of the example:
|
||||
|
||||
![Architecture](architecture.png)
|
||||
|
||||
## Running the example
|
||||
|
||||
Clone this repository or [open it in cloud shell](https://ssh.cloud.google.com/cloudshell/editor?cloudshell_git_repo=https%3A%2F%2Fgithub.com%2Fterraform-google-modules%2Fcloud-foundation-fabric&cloudshell_print=cloud-shell-readme.txt&cloudshell_working_dir=examples%2Fcloud-operations%2Fadfs), then go through the following steps to create resources:
|
||||
|
||||
* `terraform init`
|
||||
* `terraform apply -var project_id=my-project-id -var ad_dns_domain_name=my-domain.org -var adfs_dns_domain_name=adfs.my-domain.org`
|
||||
|
||||
Once the resources have been created, do the following:
|
||||
|
||||
1. Create an A record to point the AD FS DNS domain name to the public IP address returned after the terraform configuration was applied.
|
||||
2. Run the ansible playbook
|
||||
|
||||
ansible-playbook playbook.yaml
|
||||
|
||||
# Testing the example
|
||||
|
||||
1. In your browser open the following URL:
|
||||
|
||||
https://adfs.my-domain.org/adfs/ls/IdpInitiatedSignOn.aspx
|
||||
|
||||
2. Enter the username and password of one of the users provisioned. The username has to be in the format: username@my-domain.org
|
||||
3. Verify that you have successfuly signed in.
|
||||
|
||||
Once done testing, you can clean up resources by running `terraform destroy`.
|
||||
<!-- BEGIN TFDOC -->
|
||||
|
||||
## Variables
|
||||
|
||||
| name | description | type | required | default |
|
||||
|---|---|:---:|:---:|:---:|
|
||||
| [ad_dns_domain_name](variables.tf#L44) | AD DNS domain name. | <code>string</code> | ✓ | |
|
||||
| [adfs_dns_domain_name](variables.tf#L49) | ADFS DNS domain name. | <code>string</code> | ✓ | |
|
||||
| [project_id](variables.tf#L24) | Host project ID. | <code>string</code> | ✓ | |
|
||||
| [ad_ip_cidr_block](variables.tf#L90) | Managed AD IP CIDR block. | <code>string</code> | | <code>"10.0.0.0/24"</code> |
|
||||
| [disk_size](variables.tf#L54) | Disk size. | <code>number</code> | | <code>50</code> |
|
||||
| [disk_type](variables.tf#L60) | Disk type. | <code>string</code> | | <code>"pd-ssd"</code> |
|
||||
| [image](variables.tf#L66) | Image. | <code>string</code> | | <code>"projects/windows-cloud/global/images/family/windows-2022"</code> |
|
||||
| [instance_type](variables.tf#L72) | Instance type. | <code>string</code> | | <code>"n1-standard-2"</code> |
|
||||
| [network_config](variables.tf#L35) | Network configuration | <code title="object({ network = string subnet = string })">object({…})</code> | | <code>null</code> |
|
||||
| [prefix](variables.tf#L29) | Prefix for the resources created. | <code>string</code> | | <code>null</code> |
|
||||
| [project_create](variables.tf#L15) | Parameters for the creation of the new project. | <code title="object({ billing_account_id = string parent = string })">object({…})</code> | | <code>null</code> |
|
||||
| [region](variables.tf#L78) | Region. | <code>string</code> | | <code>"europe-west1"</code> |
|
||||
| [subnet_ip_cidr_block](variables.tf#L96) | Subnet IP CIDR block. | <code>string</code> | | <code>"10.0.1.0/28"</code> |
|
||||
| [zone](variables.tf#L84) | Zone. | <code>string</code> | | <code>"europe-west1-c"</code> |
|
||||
|
||||
## Outputs
|
||||
|
||||
| name | description | sensitive |
|
||||
|---|---|:---:|
|
||||
| [ip_address](outputs.tf#L15) | IP address. | |
|
||||
|
||||
<!-- END TFDOC -->
|
|
@ -0,0 +1,8 @@
|
|||
[defaults]
|
||||
inventory = inventory/hosts.ini
|
||||
|
||||
[ssh_connection]
|
||||
pipelining = True
|
||||
ssh_executable = ./gssh.sh
|
||||
transfer_method = piped
|
||||
|
|
@ -0,0 +1 @@
|
|||
adfs ansible_connection=ssh ansible_shell_type=powershell
|
|
@ -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
|
||||
#
|
||||
# https://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.
|
||||
|
||||
- name: Prepare
|
||||
hosts: adfs
|
||||
gather_facts: yes
|
||||
vars_files:
|
||||
- vars/vars.yaml
|
||||
roles:
|
||||
- role: server-setup
|
||||
|
||||
- name: Provision organizational units users, groups and memberships
|
||||
hosts: adfs
|
||||
gather_facts: no
|
||||
vars_files:
|
||||
- vars/vars.yaml
|
||||
vars:
|
||||
ansible_become: yes
|
||||
ansible_become_method: runas
|
||||
ansible_become_user: "SetupAdmin@{{ ad_dns_domain_name }}"
|
||||
ansible_become_password: "{{ setupadmin_password }}"
|
||||
roles:
|
||||
- role: ad-provisioning
|
||||
|
||||
- name: Install AD FS
|
||||
hosts: adfs
|
||||
gather_facts: no
|
||||
vars_files:
|
||||
- vars/vars.yaml
|
||||
vars:
|
||||
ansible_become: yes
|
||||
ansible_become_method: runas
|
||||
adfssvc_password: "{{ lookup('ansible.builtin.password', '~/.adfssvc-password.txt chars=ascii_letters,digits') }}"
|
||||
roles:
|
||||
- role: adfs-prerequisites
|
||||
vars:
|
||||
ansible_become_user: "SetupAdmin@{{ ad_dns_domain_name }}"
|
||||
ansible_become_password: "{{ setupadmin_password }}"
|
||||
- role: adfs-installation
|
||||
vars:
|
||||
ansible_become_user: "adfssvc@{{ ad_dns_domain_name }}"
|
||||
ansible_become_password: "{{ adfssvc_password }}"
|
|
@ -0,0 +1,8 @@
|
|||
[
|
||||
"gcp-billing-admins",
|
||||
"gcp-devops",
|
||||
"gcp-network-admins",
|
||||
"gcp-organization-admins",
|
||||
"gcp-security-admins",
|
||||
"gcp-support"
|
||||
]
|
|
@ -0,0 +1,82 @@
|
|||
[
|
||||
{
|
||||
"group": "gcp-devops",
|
||||
"member": "pamela.reed"
|
||||
},
|
||||
{
|
||||
"group": "gcp-devops",
|
||||
"member": "joshua.banks"
|
||||
},
|
||||
{
|
||||
"group": "gcp-devops",
|
||||
"member": "clayton.espinoza"
|
||||
},
|
||||
{
|
||||
"group": "gcp-devops",
|
||||
"member": "maureen.morgan"
|
||||
},
|
||||
{
|
||||
"group": "gcp-network-admins",
|
||||
"member": "pamela.reed"
|
||||
},
|
||||
{
|
||||
"group": "gcp-network-admins",
|
||||
"member": "william.bowen"
|
||||
},
|
||||
{
|
||||
"group": "gcp-network-admins",
|
||||
"member": "clayton.espinoza"
|
||||
},
|
||||
{
|
||||
"group": "gcp-network-admins",
|
||||
"member": "stacy.holland"
|
||||
},
|
||||
{
|
||||
"group": "gcp-network-admins",
|
||||
"member": "joshua.banks"
|
||||
},
|
||||
{
|
||||
"group": "gcp-network-admins",
|
||||
"member": "charlene.mckenzie"
|
||||
},
|
||||
{
|
||||
"group": "gcp-network-admins",
|
||||
"member": "lisa.harris"
|
||||
},
|
||||
{
|
||||
"group": "gcp-organization-admins",
|
||||
"member": "maureen.morgan"
|
||||
},
|
||||
{
|
||||
"group": "gcp-organization-admins",
|
||||
"member": "pamela.reed"
|
||||
},
|
||||
{
|
||||
"group": "gcp-support",
|
||||
"member": "maureen.morgan"
|
||||
},
|
||||
{
|
||||
"group": "gcp-support",
|
||||
"member": "pamela.reed"
|
||||
},
|
||||
{
|
||||
"group": "gcp-support",
|
||||
"member": "lisa.harris"
|
||||
},
|
||||
{
|
||||
"group": "gcp-support",
|
||||
"member": "tina.ferguson"
|
||||
},
|
||||
{
|
||||
"group": "gcp-support",
|
||||
"member": "stacy.holland"
|
||||
},
|
||||
{
|
||||
"group": "gcp-support",
|
||||
"member": "william.bowen"
|
||||
},
|
||||
{
|
||||
"group": "gcp-support",
|
||||
"member": "clayton.espinoza"
|
||||
}
|
||||
]
|
|
@ -0,0 +1,56 @@
|
|||
[
|
||||
{
|
||||
"first_name": "Pamela",
|
||||
"last_name": "Reed",
|
||||
"username": "pamela.reed",
|
||||
"password": "Ig_17BbZVu"
|
||||
},
|
||||
{
|
||||
"first_name": "Charlene",
|
||||
"last_name": "Mckenzie",
|
||||
"username": "charlene.mckenzie",
|
||||
"password": "$y0IsMLPy5"
|
||||
},
|
||||
{
|
||||
"first_name": "William",
|
||||
"last_name": "Bowen",
|
||||
"username": "william.bowen",
|
||||
"password": "y882QxMHE@"
|
||||
},
|
||||
{
|
||||
"first_name": "Joshua",
|
||||
"last_name": "Banks",
|
||||
"username": "joshua.banks",
|
||||
"password": ")00+LN!r0$"
|
||||
},
|
||||
{
|
||||
"first_name": "Clayton",
|
||||
"last_name": "Espinoza",
|
||||
"username": "clayton.espinoza",
|
||||
"password": "gIf@52FqUY"
|
||||
},
|
||||
{
|
||||
"first_name": "Stacy",
|
||||
"last_name": "Holland",
|
||||
"username": "stacy.holland",
|
||||
"password": "da4PLSQDb^"
|
||||
},
|
||||
{
|
||||
"first_name": "Maureen",
|
||||
"last_name": "Morgan",
|
||||
"username": "maureen.morgan",
|
||||
"password": "V)c2Vfc%i#"
|
||||
},
|
||||
{
|
||||
"first_name": "Lisa",
|
||||
"last_name": "Harris",
|
||||
"username": "lisa.harris",
|
||||
"password": "0@1Oid71co"
|
||||
},
|
||||
{
|
||||
"first_name": "Tina",
|
||||
"last_name": "Ferguson",
|
||||
"username": "tina.ferguson",
|
||||
"password": "+f#0C#_oi6"
|
||||
}
|
||||
]
|
|
@ -0,0 +1,58 @@
|
|||
# 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
|
||||
#
|
||||
# https://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.
|
||||
|
||||
- name: Read files
|
||||
set_fact:
|
||||
ad_users: "{{ lookup('file','users.json') | from_json }}"
|
||||
ad_groups: "{{ lookup('file','groups.json') | from_json }}"
|
||||
ad_memberships: "{{ lookup('file','memberships.json') | from_json }}"
|
||||
|
||||
- name: Create organizational units
|
||||
community.windows.win_domain_ou:
|
||||
name: "{{ item }}"
|
||||
path: "{{ cloud_path }}"
|
||||
state: present
|
||||
protected: true
|
||||
with_items:
|
||||
- "Users"
|
||||
- "Groups"
|
||||
|
||||
- name: Create users
|
||||
community.windows.win_domain_user:
|
||||
name: "{{ item.username }}"
|
||||
firstname: "{{ item.first_name }}"
|
||||
surname: "{{ item.last_name }}"
|
||||
email: "{{ item.username }}@{{ ad_dns_domain_name }}"
|
||||
sam_account_name: "{{ item.username }}"
|
||||
upn: "{{ item.username }}@{{ ad_dns_domain_name }}"
|
||||
password: "{{ item.password }}"
|
||||
path: "OU=Users,{{ cloud_path }}"
|
||||
state: present
|
||||
with_items: "{{ ad_users }}"
|
||||
|
||||
- name: Create groups
|
||||
community.windows.win_domain_group:
|
||||
name: "{{ item }}"
|
||||
path: "OU=Groups,{{ cloud_path }}"
|
||||
scope: global
|
||||
state: present
|
||||
with_items: "{{ ad_groups }}"
|
||||
|
||||
- name: Create memberships
|
||||
community.windows.win_domain_group_membership:
|
||||
name: "{{ item.group }}"
|
||||
members:
|
||||
- "{{ item.member }}"
|
||||
state: present
|
||||
with_items: "{{ ad_memberships }}"
|
|
@ -0,0 +1,104 @@
|
|||
# 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
|
||||
#
|
||||
# https://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.
|
||||
|
||||
- name: Create server certificate
|
||||
ansible.windows.win_powershell:
|
||||
script: |
|
||||
$Certificate = Get-ChildItem -Path Cert:\LocalMachine\My | Where-Object {$_.Subject -eq "CN={{ adfs_dns_domain_name }}"}
|
||||
if(-not $Certificate) {
|
||||
$Certificate = New-SelfSignedCertificate `
|
||||
-Subject {{ adfs_dns_domain_name }} `
|
||||
-KeyAlgorithm RSA `
|
||||
-KeyLength 2048 `
|
||||
-KeyExportPolicy NonExportable `
|
||||
-KeyUsage DigitalSignature, KeyEncipherment `
|
||||
-Provider 'Microsoft Platform Crypto Provider' `
|
||||
-NotAfter (Get-Date).AddDays(365) `
|
||||
-Type SSLServerAuthentication `
|
||||
-CertStoreLocation 'Cert:\LocalMachine\My' `
|
||||
-DnsName {{ adfs_dns_domain_name }}
|
||||
}
|
||||
$Certificate.Thumbprint
|
||||
register: server_cert
|
||||
|
||||
- name: Create token signing certificate
|
||||
ansible.windows.win_powershell:
|
||||
script: |
|
||||
$Certificate = Get-ChildItem -Path Cert:\LocalMachine\My | Where-Object {$_.Subject -eq "CN=ADFS Signing"}
|
||||
if(-not $Certificate) {
|
||||
$Certificate = New-SelfSignedCertificate `
|
||||
-Subject "ADFS Signing" `
|
||||
-KeyAlgorithm RSA `
|
||||
-KeyLength 2048 `
|
||||
-KeyExportPolicy NonExportable `
|
||||
-KeyUsage DigitalSignature, KeyEncipherment `
|
||||
-Provider 'Microsoft RSA SChannel Cryptographic Provider' `
|
||||
-NotAfter (Get-Date).AddDays(365) `
|
||||
-DnsName {{ adfs_dns_domain_name }} `
|
||||
-CertStoreLocation 'Cert:\LocalMachine\My'
|
||||
}
|
||||
$Certificate.Thumbprint
|
||||
register: token_signing_cert
|
||||
|
||||
- name: Create AD FS DKM container
|
||||
ansible.windows.win_powershell:
|
||||
script: |
|
||||
$DkmContainer = Get-ADObject -LDAPFilter "(Objectclass=container)" -SearchBase "CN=ADFS Data,{{ cloud_path }}" -SearchScope 1
|
||||
if(-not $DkmContainer) {
|
||||
$DkmContainer.DistinguishedName
|
||||
$Name = (New-Guid).Guid
|
||||
$DkmContainer = New-ADObject `
|
||||
-Name $Name `
|
||||
-Type Container `
|
||||
-Path "CN=ADFS Data,{{ cloud_path }}" `
|
||||
-PassThru
|
||||
}
|
||||
$DkmContainer.DistinguishedName
|
||||
register: adfs_dkm_container
|
||||
|
||||
- name: Install ADFS
|
||||
ansible.windows.win_powershell:
|
||||
script: |
|
||||
try {
|
||||
$AdfsFarm = Get-AdfsFarmInformation
|
||||
} catch [System.ServiceModel.EndpointNotFoundException] {
|
||||
$AdfsCredential = New-Object `
|
||||
-TypeName System.Management.Automation.PSCredential `
|
||||
-ArgumentList "$env:userdomain\adfssvc", (ConvertTo-SecureString {{ adfssvc_password }} -AsPlainText -Force)
|
||||
Install-ADFSFarm `
|
||||
-CertificateThumbprint {{ server_cert.output[0] }} `
|
||||
-SigningCertificateThumbprint {{ token_signing_cert.output[0] }} `
|
||||
-DecryptionCertificateThumbprint {{ token_signing_cert.output[0] }}`
|
||||
-FederationServiceName {{ adfs_dns_domain_name }} `
|
||||
-ServiceAccountCredential $AdfsCredential `
|
||||
-OverwriteConfiguration `
|
||||
-AdminConfiguration @{"DKMContainerDn"="{{ adfs_dkm_container.output[0] }}"}
|
||||
}
|
||||
no_log: yes
|
||||
|
||||
- name: Configure TLS
|
||||
ansible.windows.win_powershell:
|
||||
script: |
|
||||
netsh http show sslcert ipport=0.0.0.0:443
|
||||
if($LastExitCode -gt 0) {
|
||||
netsh http add sslcert ipport=0.0.0.0:443 certhash={{ server_cert.output[0] }} appid="{5d89a20c-beab-4389-9447-324788eb944a}" certstorename=MY
|
||||
}
|
||||
|
||||
- name: Restart computer
|
||||
ansible.windows.win_reboot:
|
||||
|
||||
- name: Enable the Idp-Initiated Sign on page
|
||||
ansible.windows.win_powershell:
|
||||
script: |
|
||||
Set-AdfsProperties -EnableIdpInitiatedSignonPage $true
|
|
@ -0,0 +1,45 @@
|
|||
# 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
|
||||
#
|
||||
# https://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.
|
||||
|
||||
- name: Create AD FS service user
|
||||
community.windows.win_domain_user:
|
||||
name: "adfssvc"
|
||||
password: "{{ adfssvc_password }}"
|
||||
spn: "http/{{ adfs_dns_domain_name }}"
|
||||
path: "OU=Users,{{ cloud_path }}"
|
||||
state: present
|
||||
|
||||
- name: Add AD FS service user to local Administrators group
|
||||
ansible.windows.win_group_membership:
|
||||
name: Administrators
|
||||
members:
|
||||
- "adfssvc@{{ ad_dns_domain_name }}"
|
||||
state: present
|
||||
|
||||
- name: Create AD FS Data container
|
||||
ansible.windows.win_powershell:
|
||||
script: |
|
||||
try {
|
||||
Get-ADObject -Identity "CN=ADFS Data,{{ cloud_path }}"
|
||||
} catch [Microsoft.ActiveDirectory.Management.ADIdentityResolutionException] {
|
||||
New-ADObject `
|
||||
-Name "ADFS Data" `
|
||||
-Type Container `
|
||||
-Path "{{ cloud_path }}"
|
||||
}
|
||||
|
||||
- name: Grant the AD FS user full control on the container
|
||||
ansible.windows.win_powershell:
|
||||
script: |
|
||||
dsacls.exe "CN=ADFS Data,{{ cloud_path }}" /G $env:userdomain\adfssvc:GA /I:T
|
|
@ -0,0 +1,67 @@
|
|||
# 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
|
||||
#
|
||||
# https://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.
|
||||
|
||||
$ApplicationGroup = Get-AdfsApplicationGroup -Name Anthos
|
||||
|
||||
$ApplicationGroupName = "Anthos"
|
||||
$ApplicationGroupIdentifier = (New-Guid).Guid
|
||||
New-AdfsApplicationGroup -Name $ApplicationGroupName `
|
||||
-ApplicationGroupIdentifier $ApplicationGroupIdentifier
|
||||
|
||||
$ServerApplicationName = "$ApplicationGroupName Server App"
|
||||
$ServerApplicationIdentifier = (New-Guid).Guid
|
||||
$RelyingPartyTrustName = "Anthos"
|
||||
$RelyingPartyTrustIdentifier = (New-Guid).Guid
|
||||
$RedirectURI1 = "http://localhost:1025/callback"
|
||||
$RedirectURI2 = "https://console.cloud.google.com/kubernetes/oidc"
|
||||
|
||||
$ADFSApp = Add-AdfsServerApplication -Name $ServerApplicationName `
|
||||
-ApplicationGroupIdentifier $ApplicationGroupIdentifier `
|
||||
-RedirectUri $RedirectURI1,$RedirectURI2 `
|
||||
-Identifier $ServerApplicationIdentifier `
|
||||
-GenerateClientSecret
|
||||
|
||||
$IssuanceTransformRules = @'
|
||||
@RuleTemplate = "LdapClaims"
|
||||
@RuleName = "groups"
|
||||
c:[Type == "http://schemas.microsoft.com/ws/2008/06/identity/claims/windowsaccountname", Issuer == "AD AUTHORITY"]
|
||||
=> issue(store = "Active Directory", types = ("http://schemas.xmlsoap.org/claims/Group"), query = ";tokenGroups(domainQualifiedName);{0}", param = c.Value);
|
||||
'@
|
||||
|
||||
Add-AdfsRelyingPartyTrust -Name $RelyingPartyTrustName `
|
||||
-Identifier $RelyingPartyTrustIdentifier `
|
||||
-AccessControlPolicyName "Permit everyone" `
|
||||
-IssuanceTransformRules "$IssuanceTransformRules"
|
||||
|
||||
Grant-ADFSApplicationPermission -ClientRoleIdentifier $ServerApplicationIdentifier `
|
||||
-ServerRoleIdentifier $RelyingPartyTrustIdentifier `
|
||||
-ScopeName "allatclaims", "openid"
|
||||
|
||||
$ClientId = $ADFSApp.Identifier
|
||||
$ClientSecret = $ADFSApp.ClientSecret
|
||||
|
||||
@"
|
||||
authentication:
|
||||
oidc:
|
||||
clientID: $ADFSApp.Identifier
|
||||
clientSecret: $ADFSApp.ClientSecret
|
||||
extraParams: resource=$RelyingPartyTrustIdentifier
|
||||
group: groups
|
||||
groupPrefix: ""
|
||||
issuerURI: https://{{ adfs_dns_domain_name }}/adfs
|
||||
kubectlRedirectURL: $RedirectURI1
|
||||
scopes: openid
|
||||
username: upn
|
||||
usernamePrefix: ""
|
||||
"@
|
|
@ -0,0 +1,86 @@
|
|||
# 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
|
||||
#
|
||||
# https://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.
|
||||
|
||||
- name: Install Windows features
|
||||
ansible.windows.win_feature:
|
||||
name: "{{ item.feature }}"
|
||||
include_mamangement_tools: "{{ item.include_management_tools }}"
|
||||
state: present
|
||||
with_items:
|
||||
- { "feature": "RSAT-AD-Tools", "include_management_tools": false }
|
||||
- { "feature": "GPMC", "include_management_tools": false }
|
||||
- { "feature": "RSAT-DNS-Server", "include_management_tools": false }
|
||||
- { "feature": "ADFS-Federation", "include_management_tools": true }
|
||||
- { "feature": "RSAT-AD-PowerShell", "include_management_tools": false }
|
||||
- { "feature": "RSAT-ADDS-Tools", "include_management_tools": false }
|
||||
|
||||
- name: Check if SetupAdmin password has already been reset
|
||||
stat:
|
||||
path: ~/.setupadmin-password.txt
|
||||
register: setupadmin_password_file_check
|
||||
delegate_to: localhost
|
||||
|
||||
- name: Set AD SetupAdmin password fact
|
||||
set_fact:
|
||||
setupadmin_password: "{{ lookup('file', '~/.setupadmin-password.txt') }}"
|
||||
no_log: true
|
||||
when: setupadmin_password_file_check.stat.exists
|
||||
delegate_to: localhost
|
||||
|
||||
- name: Reset AD deletegated admin password
|
||||
shell: >
|
||||
gcloud active-directory domains reset-admin-password {{ ad_dns_domain_name }}
|
||||
--project={{ project_id }}
|
||||
--quiet
|
||||
--format "value(password)"
|
||||
register: setupadmin_password_reset
|
||||
no_log: yes
|
||||
when: not setupadmin_password_file_check.stat.exists
|
||||
delegate_to: localhost
|
||||
|
||||
- name: Set AD SetupAdmin password fact
|
||||
set_fact:
|
||||
setupadmin_password: "{{ setupadmin_password_reset.stdout }}"
|
||||
no_log: yes
|
||||
when: not setupadmin_password_file_check.stat.exists
|
||||
|
||||
- name: Creating a file setupadmin password
|
||||
copy:
|
||||
dest: ~/.setupadmin-password.txt
|
||||
content: "{{ setupadmin_password }}"
|
||||
when: not setupadmin_password_file_check.stat.exists
|
||||
delegate_to: localhost
|
||||
|
||||
- name: Add computer to domain
|
||||
ansible.windows.win_domain_membership:
|
||||
dns_domain_name: "{{ ad_dns_domain_name }}"
|
||||
domain_admin_user: "SetupAdmin@{{ ad_dns_domain_name }}"
|
||||
domain_admin_password: "{{ setupadmin_password }}"
|
||||
state: domain
|
||||
register: domain_state
|
||||
|
||||
- name: Restart computer
|
||||
ansible.windows.win_reboot:
|
||||
when: domain_state.reboot_required
|
||||
|
||||
- name: Get Domain info
|
||||
community.windows.win_domain_object_info:
|
||||
filter: ObjectClass -eq 'domain'
|
||||
domain_username: "SetupAdmin@{{ ad_dns_domain_name }}"
|
||||
domain_password: "{{ setupadmin_password }}"
|
||||
register: ad_domain
|
||||
|
||||
- name: Set facts
|
||||
set_fact:
|
||||
cloud_path: "OU=Cloud,{{ ad_domain.objects[0].DistinguishedName }}"
|
Binary file not shown.
After Width: | Height: | Size: 32 KiB |
|
@ -0,0 +1,191 @@
|
|||
# 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
|
||||
#
|
||||
# https://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
locals {
|
||||
prefix = (var.prefix == null || var.prefix == "") ? "" : "${var.prefix}-"
|
||||
}
|
||||
|
||||
module "project" {
|
||||
source = "../../../modules/project"
|
||||
billing_account = (
|
||||
var.project_create != null
|
||||
? var.project_create.billing_account_id
|
||||
: null
|
||||
)
|
||||
parent = (
|
||||
var.project_create != null
|
||||
? var.project_create.parent
|
||||
: null
|
||||
)
|
||||
prefix = var.project_create == null ? null : var.prefix
|
||||
name = var.project_id
|
||||
services = [
|
||||
"compute.googleapis.com",
|
||||
"dns.googleapis.com",
|
||||
"managedidentities.googleapis.com"
|
||||
]
|
||||
}
|
||||
|
||||
module "vpc" {
|
||||
count = var.network_config == null ? 1 : 0
|
||||
source = "../../../modules/net-vpc"
|
||||
project_id = module.project.project_id
|
||||
name = "${local.prefix}vpc"
|
||||
subnets = [
|
||||
{
|
||||
ip_cidr_range = var.subnet_ip_cidr_block
|
||||
name = "subnet"
|
||||
region = var.region
|
||||
secondary_ip_range = null
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
resource "google_active_directory_domain" "ad_domain" {
|
||||
project = module.project.project_id
|
||||
domain_name = var.ad_dns_domain_name
|
||||
locations = [var.region]
|
||||
authorized_networks = [module.vpc[0].network.id]
|
||||
reserved_ip_range = var.ad_ip_cidr_block
|
||||
}
|
||||
|
||||
module "server" {
|
||||
source = "../../../modules/compute-vm"
|
||||
project_id = module.project.project_id
|
||||
zone = var.zone
|
||||
name = "adfs"
|
||||
instance_type = var.instance_type
|
||||
network_interfaces = [{
|
||||
network = var.network_config == null ? module.vpc[0].self_link : var.network_config.network
|
||||
subnetwork = var.network_config == null ? module.vpc[0].subnet_self_links["${var.region}/subnet"] : var.network_config.subnet
|
||||
nat = false
|
||||
addresses = null
|
||||
}]
|
||||
metadata = {
|
||||
# Enables OpenSSH in the Windows instance
|
||||
sysprep-specialize-script-cmd = "googet -noconfirm=true update && googet -noconfirm=true install google-compute-engine-ssh"
|
||||
enable-windows-ssh = "TRUE"
|
||||
# Set the default OpenSSH shell to Powershell
|
||||
windows-startup-script-ps1 = <<EOT
|
||||
New-ItemProperty -Path "HKLM:\SOFTWARE\OpenSSH" `
|
||||
-Name DefaultShell `
|
||||
-Value "C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe" `
|
||||
-PropertyType String `
|
||||
-Force
|
||||
EOT
|
||||
}
|
||||
service_account_create = true
|
||||
boot_disk = {
|
||||
image = var.image
|
||||
type = var.disk_type
|
||||
size = var.disk_size
|
||||
}
|
||||
group = {
|
||||
named_ports = {
|
||||
http = 443
|
||||
}
|
||||
}
|
||||
tags = ["https-server"]
|
||||
}
|
||||
|
||||
module "glb" {
|
||||
source = "../../../modules/net-glb"
|
||||
name = "${local.prefix}glb"
|
||||
project_id = module.project.project_id
|
||||
|
||||
https = true
|
||||
reserve_ip_address = true
|
||||
|
||||
ssl_certificates_config = {
|
||||
adfs-domain = {
|
||||
domains = [
|
||||
"${var.adfs_dns_domain_name}"
|
||||
],
|
||||
unmanaged_config = null
|
||||
}
|
||||
}
|
||||
|
||||
target_proxy_https_config = {
|
||||
ssl_certificates = [
|
||||
"adfs-domain"
|
||||
]
|
||||
}
|
||||
|
||||
backend_services_config = {
|
||||
adfs-group-backend = {
|
||||
bucket_config = null
|
||||
enable_cdn = false
|
||||
cdn_config = null
|
||||
group_config = {
|
||||
backends = [
|
||||
{
|
||||
group = module.server.group.id
|
||||
options = null
|
||||
}
|
||||
],
|
||||
health_checks = ["hc"]
|
||||
log_config = {
|
||||
enable = true
|
||||
sample_rate = 1
|
||||
}
|
||||
options = {
|
||||
affinity_cookie_ttl_sec = null
|
||||
custom_request_headers = null
|
||||
custom_response_headers = null
|
||||
connection_draining_timeout_sec = null
|
||||
load_balancing_scheme = null
|
||||
locality_lb_policy = null
|
||||
port_name = null
|
||||
security_policy = null
|
||||
session_affinity = null
|
||||
timeout_sec = null
|
||||
circuits_breakers = null
|
||||
consistent_hash = null
|
||||
iap = null
|
||||
protocol = "HTTPS"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
health_checks_config = {
|
||||
hc = {
|
||||
type = "tcp"
|
||||
logging = true
|
||||
options = null
|
||||
check = {
|
||||
port_name = "http"
|
||||
port_specification = "USE_NAMED_PORT"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
resource "local_file" "vars_file" {
|
||||
content = templatefile("${path.module}/templates/vars.yaml.tpl", {
|
||||
project_id = var.project_id
|
||||
ad_dns_domain_name = var.ad_dns_domain_name
|
||||
adfs_dns_domain_name = var.adfs_dns_domain_name
|
||||
})
|
||||
filename = "${path.module}/ansible/vars/vars.yaml"
|
||||
file_permission = "0666"
|
||||
}
|
||||
|
||||
resource "local_file" "gssh_file" {
|
||||
content = templatefile("${path.module}/templates/gssh.sh.tpl", {
|
||||
zone = var.zone
|
||||
project_id = var.project_id
|
||||
})
|
||||
filename = "${path.module}/ansible/gssh.sh"
|
||||
file_permission = "0777"
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
# 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
|
||||
#
|
||||
# https://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
output "ip_address" {
|
||||
description = "IP address."
|
||||
value = module.glb.ip_address
|
||||
}
|
|
@ -0,0 +1,98 @@
|
|||
# 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
|
||||
#
|
||||
# https://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
from textwrap import indent
|
||||
import click
|
||||
import json
|
||||
import random
|
||||
from faker import Faker
|
||||
|
||||
ENCODING = 'UTF8'
|
||||
|
||||
FIELD_USER_FIRST_NAME = 'first_name'
|
||||
FIELD_USER_LAST_NAME = 'last_name'
|
||||
FIELD_USER_USERNAME = 'username'
|
||||
FIELD_USER_PASSWORD = 'password'
|
||||
|
||||
FIELD_MEMBERSHIP_GROUP = 'group'
|
||||
FIELD_MEMBERSHIP_MEMBER = 'member'
|
||||
|
||||
fake = Faker()
|
||||
|
||||
@ click.group()
|
||||
def cli():
|
||||
pass
|
||||
|
||||
@ cli.command()
|
||||
@ click.option(
|
||||
"--num-users",
|
||||
help="Number of users to create",
|
||||
default=10,
|
||||
)
|
||||
@click.option(
|
||||
"--output-file",
|
||||
help="Output file",
|
||||
default="users.json",
|
||||
)
|
||||
def create_users(num_users, output_file):
|
||||
rows = []
|
||||
for i in range(1, num_users):
|
||||
row = {}
|
||||
row[FIELD_USER_FIRST_NAME] = fake.first_name()
|
||||
row[FIELD_USER_LAST_NAME] = fake.last_name()
|
||||
row[FIELD_USER_USERNAME] = row[FIELD_USER_FIRST_NAME].lower() + "." + \
|
||||
row[FIELD_USER_LAST_NAME].lower()
|
||||
row[FIELD_USER_PASSWORD] = fake.password()
|
||||
rows.append(row)
|
||||
write_json(output_file, rows)
|
||||
|
||||
@cli.command()
|
||||
@click.option(
|
||||
"--users-file",
|
||||
help="Users file",
|
||||
default="users.json",
|
||||
)
|
||||
@click.option(
|
||||
"--groups-file",
|
||||
help="Groups file",
|
||||
default="groups.json",
|
||||
)
|
||||
@click.option(
|
||||
"--output-file",
|
||||
help="Output file",
|
||||
default="memberships.json",
|
||||
)
|
||||
def create_memberships(users_file, groups_file, output_file):
|
||||
users = read_json(users_file)
|
||||
groups = read_json(groups_file)
|
||||
rows = []
|
||||
for group in groups:
|
||||
members = random.sample(users, random.randint(0, len(users) - 1))
|
||||
for member in members:
|
||||
row = {}
|
||||
row[FIELD_MEMBERSHIP_GROUP] = group
|
||||
row[FIELD_MEMBERSHIP_MEMBER] = member[FIELD_USER_USERNAME]
|
||||
rows.append(row)
|
||||
write_json(output_file, rows)
|
||||
|
||||
def write_json(file, rows):
|
||||
with open(file, 'w') as f:
|
||||
json.dump(rows, f, indent=2)
|
||||
|
||||
def read_json(file):
|
||||
with open(file, 'r', encoding='UTF8') as f:
|
||||
return json.load(f)
|
||||
|
||||
if __name__ == "__main__":
|
||||
cli()
|
|
@ -0,0 +1,3 @@
|
|||
argparse==1.4.0
|
||||
Faker==13.3.2
|
||||
click==8.0.4
|
|
@ -0,0 +1,66 @@
|
|||
# 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
|
||||
#
|
||||
# https://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.
|
||||
|
||||
param($DnsName)
|
||||
|
||||
$ApplicationGroup = Get-AdfsApplicationGroup -Name Anthos
|
||||
|
||||
$ApplicationGroupName = "Anthos"
|
||||
$ApplicationGroupIdentifier = (New-Guid).Guid
|
||||
New-AdfsApplicationGroup -Name $ApplicationGroupName `
|
||||
-ApplicationGroupIdentifier $ApplicationGroupIdentifier
|
||||
|
||||
$ServerApplicationName = "$ApplicationGroupName Server App"
|
||||
$ServerApplicationIdentifier = (New-Guid).Guid
|
||||
$RelyingPartyTrustName = "Anthos"
|
||||
$RelyingPartyTrustIdentifier = (New-Guid).Guid
|
||||
$RedirectURI1 = "http://localhost:1025/callback"
|
||||
$RedirectURI2 = "https://console.cloud.google.com/kubernetes/oidc"
|
||||
|
||||
$ADFSApp = Add-AdfsServerApplication -Name $ServerApplicationName `
|
||||
-ApplicationGroupIdentifier $ApplicationGroupIdentifier `
|
||||
-RedirectUri $RedirectURI1,$RedirectURI2 `
|
||||
-Identifier $ServerApplicationIdentifier `
|
||||
-GenerateClientSecret
|
||||
|
||||
$IssuanceTransformRules = @'
|
||||
@RuleTemplate = "LdapClaims"
|
||||
@RuleName = "groups"
|
||||
c:[Type == "http://schemas.microsoft.com/ws/2008/06/identity/claims/windowsaccountname", Issuer == "AD AUTHORITY"]
|
||||
=> issue(store = "Active Directory", types = ("http://schemas.xmlsoap.org/claims/Group"), query = ";tokenGroups(domainQualifiedName);{0}", param = c.Value);
|
||||
'@
|
||||
|
||||
Add-AdfsRelyingPartyTrust -Name $RelyingPartyTrustName `
|
||||
-Identifier $RelyingPartyTrustIdentifier `
|
||||
-AccessControlPolicyName "Permit everyone" `
|
||||
-IssuanceTransformRules "$IssuanceTransformRules"
|
||||
|
||||
Grant-ADFSApplicationPermission -ClientRoleIdentifier $ServerApplicationIdentifier `
|
||||
-ServerRoleIdentifier $RelyingPartyTrustIdentifier `
|
||||
-ScopeName "allatclaims", "openid"
|
||||
|
||||
@"
|
||||
authentication:
|
||||
oidc:
|
||||
clientID: $($ADFSApp.Identifier)
|
||||
clientSecret: $($ADFSApp.ClientSecret)
|
||||
extraParams: resource=$RelyingPartyTrustIdentifier
|
||||
group: groups
|
||||
groupPrefix: ""
|
||||
issuerURI: https://$DnsName/adfs
|
||||
kubectlRedirectURL: $RedirectURI1
|
||||
scopes: openid
|
||||
username: upn
|
||||
usernamePrefix: ""
|
||||
"@
|
|
@ -0,0 +1,30 @@
|
|||
#!/bin/bash
|
||||
#
|
||||
# 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
|
||||
#
|
||||
# https://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.
|
||||
|
||||
host="$${@: -2: 1}"
|
||||
cmd="$${@: -1: 1}"
|
||||
|
||||
gcloud_args="
|
||||
--tunnel-through-iap
|
||||
--zone=${zone}
|
||||
--project=${project_id}
|
||||
--quiet
|
||||
--no-user-output-enabled
|
||||
--
|
||||
-C
|
||||
"
|
||||
|
||||
exec gcloud compute ssh "$host" $gcloud_args "$cmd"
|
|
@ -0,0 +1,17 @@
|
|||
# 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
|
||||
#
|
||||
# https://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.
|
||||
|
||||
project_id: ${project_id}
|
||||
ad_dns_domain_name: ${ad_dns_domain_name}
|
||||
adfs_dns_domain_name: ${adfs_dns_domain_name}
|
|
@ -0,0 +1,100 @@
|
|||
# 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
|
||||
#
|
||||
# https://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 "project_create" {
|
||||
description = "Parameters for the creation of the new project."
|
||||
type = object({
|
||||
billing_account_id = string
|
||||
parent = string
|
||||
})
|
||||
default = null
|
||||
}
|
||||
|
||||
variable "project_id" {
|
||||
description = "Host project ID."
|
||||
type = string
|
||||
}
|
||||
|
||||
variable "prefix" {
|
||||
description = "Prefix for the resources created."
|
||||
type = string
|
||||
default = null
|
||||
}
|
||||
|
||||
variable "network_config" {
|
||||
description = "Network configuration"
|
||||
type = object({
|
||||
network = string
|
||||
subnet = string
|
||||
})
|
||||
default = null
|
||||
}
|
||||
|
||||
variable "ad_dns_domain_name" {
|
||||
description = "AD DNS domain name."
|
||||
type = string
|
||||
}
|
||||
|
||||
variable "adfs_dns_domain_name" {
|
||||
description = "ADFS DNS domain name."
|
||||
type = string
|
||||
}
|
||||
|
||||
variable "disk_size" {
|
||||
description = "Disk size."
|
||||
type = number
|
||||
default = 50
|
||||
}
|
||||
|
||||
variable "disk_type" {
|
||||
description = "Disk type."
|
||||
type = string
|
||||
default = "pd-ssd"
|
||||
}
|
||||
|
||||
variable "image" {
|
||||
description = "Image."
|
||||
type = string
|
||||
default = "projects/windows-cloud/global/images/family/windows-2022"
|
||||
}
|
||||
|
||||
variable "instance_type" {
|
||||
description = "Instance type."
|
||||
type = string
|
||||
default = "n1-standard-2"
|
||||
}
|
||||
|
||||
variable "region" {
|
||||
description = "Region."
|
||||
type = string
|
||||
default = "europe-west1"
|
||||
}
|
||||
|
||||
variable "zone" {
|
||||
description = "Zone."
|
||||
type = string
|
||||
default = "europe-west1-c"
|
||||
}
|
||||
|
||||
variable "ad_ip_cidr_block" {
|
||||
description = "Managed AD IP CIDR block."
|
||||
type = string
|
||||
default = "10.0.0.0/24"
|
||||
}
|
||||
|
||||
variable "subnet_ip_cidr_block" {
|
||||
description = "Subnet IP CIDR block."
|
||||
type = string
|
||||
default = "10.0.1.0/28"
|
||||
}
|
|
@ -0,0 +1,32 @@
|
|||
# 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
|
||||
#
|
||||
# https://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
terraform {
|
||||
required_version = ">= 1.1.0"
|
||||
required_providers {
|
||||
local = {
|
||||
version = ">= 2.2.3"
|
||||
}
|
||||
google = {
|
||||
source = "hashicorp/google"
|
||||
version = ">= 4.17.0"
|
||||
}
|
||||
google-beta = {
|
||||
source = "hashicorp/google-beta"
|
||||
version = ">= 4.17.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -32,4 +32,10 @@ This [example](./data-platform-foundations/) implements SQL Server Always On Ava
|
|||
|
||||
<a href="./cloudsql-multiregion/" title="Cloud SQL instance with multi-region read replicas"><img src="./cloudsql-multiregion/diagram.png" align="left" width="280px"></a>
|
||||
This [example](./cloudsql-multiregion/) creates a [Cloud SQL instance](https://cloud.google.com/sql) with multi-region read replicas as described in the [Cloud SQL for PostgreSQL disaster recovery](https://cloud.google.com/architecture/cloud-sql-postgres-disaster-recovery-complete-failover-fallback) article.
|
||||
<br clear="left">
|
||||
|
||||
### Data Playground starter with Cloud Vertex AI Notebook and GCS
|
||||
|
||||
<a href="./data-playground/" title="Data Playground starter with Cloud Vertex AI Notebook and GCS"><img src="./data-playground/diagram.png" align="left" width="280px"></a>
|
||||
This [example](./data-playground/) creates a [Vertex AI Notebook](https://cloud.google.com/vertex-ai/docs/workbench/introduction) running under a VPC network and a starter GCS bucket to store inputs and outputs of data experiments.
|
||||
<br clear="left">
|
|
@ -0,0 +1,43 @@
|
|||
# Data Playground
|
||||
|
||||
This example creates a minimum viable template for a data experimentation project with the needed APIs enabled, basic VPC and Firewall set in place, GCS bucket and an AI notebook to get started.
|
||||
|
||||
This is the high level diagram:
|
||||
|
||||
![High-level diagram](diagram.png "High-level diagram")
|
||||
|
||||
## Managed resources and services
|
||||
|
||||
This sample creates several distinct groups of resources:
|
||||
|
||||
- projects
|
||||
- Service Project configured for GCE instances and GCS buckets
|
||||
- networking
|
||||
- VPC network
|
||||
- One default subnet
|
||||
- Firewall rules for [SSH access via IAP](https://cloud.google.com/iap/docs/using-tcp-forwarding) and open communication within the VPC
|
||||
- Vertex AI notebook
|
||||
- One Jupyter lab notebook instance with public access
|
||||
- GCS
|
||||
- One bucket initial bucket
|
||||
|
||||
|
||||
|
||||
## Variables
|
||||
| name | description | type | required | default |
|
||||
| ------------------------- | --------------------------------------------------------------------------------------------------------------------------------------- | ----------- | -------- | ------------ |
|
||||
| project\_id | Project id, references existing project if \`project\_create\` is null. | string | ✓ | |
|
||||
| location | The location where resources will be deployed | string | | europe |
|
||||
| region | The region where resources will be deployed. | string | | europe-west1 |
|
||||
| project\_create | Provide values if project creation is needed, uses existing project if null. Parent format: folders/folder\_id or organizations/org\_id | object({…}) | | null |
|
||||
| prefix | Unique prefix used for resource names. Not used for project if 'project\_create' is null. | string | | dp |
|
||||
| service\_encryption\_keys | Cloud KMS to use to encrypt different services. Key location should match service region. | object({…}) | | null |
|
||||
| vpc\_config | Parameters to create a simple VPC for the Data Playground | object({…}) | | {...} |
|
||||
|
||||
## Outputs
|
||||
| Name | Description |
|
||||
| ----------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------- |
|
||||
| bucket | GCS Bucket URL. |
|
||||
| project | Project id |
|
||||
| vpc | VPC Network name |
|
||||
| notebook | Vertex AI notebook name |
|
Binary file not shown.
After Width: | Height: | Size: 27 KiB |
|
@ -0,0 +1,113 @@
|
|||
# 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
|
||||
#
|
||||
# https://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.
|
||||
|
||||
###############################################################################
|
||||
# Project #
|
||||
###############################################################################
|
||||
locals {
|
||||
service_encryption_keys = var.service_encryption_keys
|
||||
}
|
||||
|
||||
module "project" {
|
||||
source = "../../../modules/project"
|
||||
name = var.project_id
|
||||
parent = try(var.project_create.parent, null)
|
||||
billing_account = try(var.project_create.billing_account_id, null)
|
||||
project_create = var.project_create != null
|
||||
prefix = var.project_create == null ? null : var.prefix
|
||||
services = [
|
||||
"stackdriver.googleapis.com",
|
||||
"compute.googleapis.com",
|
||||
"storage-component.googleapis.com",
|
||||
"storage.googleapis.com",
|
||||
"servicenetworking.googleapis.com",
|
||||
"bigquery.googleapis.com",
|
||||
"bigquerystorage.googleapis.com",
|
||||
"bigqueryreservation.googleapis.com",
|
||||
"dataflow.googleapis.com",
|
||||
"notebooks.googleapis.com",
|
||||
"composer.googleapis.com"
|
||||
]
|
||||
policy_boolean = {
|
||||
# "constraints/compute.requireOsLogin" = false
|
||||
# Example of applying a project wide policy, mainly useful for Composer
|
||||
}
|
||||
service_encryption_key_ids = {
|
||||
storage = [try(local.service_encryption_keys.storage, null)]
|
||||
}
|
||||
}
|
||||
|
||||
###############################################################################
|
||||
# Networking #
|
||||
###############################################################################
|
||||
|
||||
module "vpc" {
|
||||
source = "../../../modules/net-vpc"
|
||||
project_id = module.project.project_id
|
||||
name = var.vpc_config.vpc_name
|
||||
subnets = [
|
||||
{
|
||||
ip_cidr_range = var.vpc_config.ip_cidr_range
|
||||
name = var.vpc_config.subnet_name
|
||||
region = var.region
|
||||
secondary_ip_range = {}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
module "vpc-firewall" {
|
||||
source = "../../../modules/net-vpc-firewall"
|
||||
project_id = module.project.project_id
|
||||
network = module.vpc.name
|
||||
admin_ranges = [var.vpc_config.ip_cidr_range]
|
||||
}
|
||||
|
||||
###############################################################################
|
||||
# GCS #
|
||||
###############################################################################
|
||||
|
||||
module "base-gcs-bucket" {
|
||||
source = "../../../modules/gcs"
|
||||
project_id = module.project.project_id
|
||||
prefix = module.project.project_id
|
||||
name = "base"
|
||||
encryption_key = try(local.service_encryption_keys.storage, null) # Example assignment of an encryption key
|
||||
}
|
||||
|
||||
###############################################################################
|
||||
# Vertex AI Notebook #
|
||||
###############################################################################
|
||||
# TODO: Add encryption_key to Vertex AI notebooks as well
|
||||
# TODO: Add shared VPC support
|
||||
resource "google_notebooks_instance" "playground" {
|
||||
name = "data-play-notebook"
|
||||
location = format("%s-%s", var.region, "b")
|
||||
machine_type = "e2-medium"
|
||||
project = module.project.project_id
|
||||
|
||||
container_image {
|
||||
repository = "gcr.io/deeplearning-platform-release/base-cpu"
|
||||
tag = "latest"
|
||||
}
|
||||
|
||||
install_gpu_driver = true
|
||||
boot_disk_type = "PD_SSD"
|
||||
boot_disk_size_gb = 110
|
||||
|
||||
no_public_ip = false
|
||||
no_proxy_access = false
|
||||
|
||||
network = module.vpc.network.id
|
||||
subnet = module.vpc.subnets[format("%s/%s", var.region, var.vpc_config.subnet_name)].id
|
||||
}
|
|
@ -0,0 +1,33 @@
|
|||
# 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
|
||||
#
|
||||
# https://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
output "bucket" {
|
||||
description = "GCS Bucket URL."
|
||||
value = module.base-gcs-bucket.url
|
||||
}
|
||||
|
||||
output "notebook" {
|
||||
description = "Vertex AI notebook"
|
||||
value = resource.google_notebooks_instance.playground.name
|
||||
}
|
||||
|
||||
output "project" {
|
||||
description = "Project id"
|
||||
value = module.project.project_id
|
||||
}
|
||||
|
||||
output "vpc" {
|
||||
description = "VPC Network"
|
||||
value = module.vpc.name
|
||||
}
|
|
@ -0,0 +1,68 @@
|
|||
# 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
|
||||
#
|
||||
# https://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 "location" {
|
||||
description = "The location where resources will be deployed."
|
||||
type = string
|
||||
default = "europe"
|
||||
}
|
||||
|
||||
variable "project_id" {
|
||||
description = "Project id, references existing project if `project_create` is null."
|
||||
type = string
|
||||
}
|
||||
|
||||
variable "project_create" {
|
||||
description = "Provide values if project creation is needed, uses existing project if null. Parent format: folders/folder_id or organizations/org_id"
|
||||
type = object({
|
||||
billing_account_id = string
|
||||
parent = string
|
||||
})
|
||||
default = null
|
||||
}
|
||||
|
||||
variable "prefix" {
|
||||
description = "Unique prefix used for resource names. Not used for project if 'project_create' is null."
|
||||
type = string
|
||||
default = "dp"
|
||||
}
|
||||
|
||||
variable "region" {
|
||||
description = "The region where resources will be deployed."
|
||||
type = string
|
||||
default = "europe-west1"
|
||||
}
|
||||
|
||||
variable "service_encryption_keys" { # service encription key
|
||||
description = "Cloud KMS to use to encrypt different services. Key location should match service region."
|
||||
type = object({
|
||||
storage = string
|
||||
})
|
||||
default = null
|
||||
}
|
||||
|
||||
variable "vpc_config" {
|
||||
description = "Parameters to create a simple VPC for the Data Playground"
|
||||
type = object({
|
||||
ip_cidr_range = string
|
||||
subnet_name = string
|
||||
vpc_name = string
|
||||
})
|
||||
default = {
|
||||
ip_cidr_range = "10.0.0.0/20"
|
||||
subnet_name = "default-subnet"
|
||||
vpc_name = "data-playground-vpc"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
# Copyright 2022 Google LLC
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# https://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
terraform {
|
||||
required_version = ">= 1.1.0"
|
||||
required_providers {
|
||||
google = {
|
||||
source = "hashicorp/google"
|
||||
version = ">= 4.17.0"
|
||||
}
|
||||
google-beta = {
|
||||
source = "hashicorp/google-beta"
|
||||
version = ">= 4.17.0"
|
||||
}
|
||||
}
|
||||
}
|
|
@ -12,7 +12,7 @@ Nested folder structure for yaml configurations is optionally supported, which a
|
|||
|
||||
```hcl
|
||||
module "prod-firewall" {
|
||||
source = "./modules/net-vpc-firewall-yaml"
|
||||
source = "./examples/factories/net-vpc-firewall-yaml"
|
||||
|
||||
project_id = "my-prod-project"
|
||||
network = "my-prod-network"
|
||||
|
@ -27,7 +27,7 @@ module "prod-firewall" {
|
|||
}
|
||||
|
||||
module "dev-firewall" {
|
||||
source = "./modules/net-vpc-firewall-yaml"
|
||||
source = "./examples/factories/net-vpc-firewall-yaml"
|
||||
|
||||
project_id = "my-dev-project"
|
||||
network = "my-dev-network"
|
||||
|
|
|
@ -7,12 +7,13 @@ Legend: <code>+</code> additive, <code>•</code> conditional.
|
|||
| members | roles |
|
||||
|---|---|
|
||||
|<b>GCP organization domain</b><br><small><i>domain</i></small>|[roles/browser](https://cloud.google.com/iam/docs/understanding-roles#browser) <br>[roles/resourcemanager.organizationViewer](https://cloud.google.com/iam/docs/understanding-roles#resourcemanager.organizationViewer) |
|
||||
|<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-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/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-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/billing.admin](https://cloud.google.com/iam/docs/understanding-roles#billing.admin) <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/orgpolicy.policyAdmin](https://cloud.google.com/iam/docs/understanding-roles#orgpolicy.policyAdmin) <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/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>|
|
||||
|
||||
## Project <i>prod-audit-logs-0</i>
|
||||
|
||||
|
@ -31,7 +32,9 @@ Legend: <code>+</code> additive, <code>•</code> conditional.
|
|||
| members | roles |
|
||||
|---|---|
|
||||
|<b>gcp-devops</b><br><small><i>group</i></small>|[roles/iam.serviceAccountAdmin](https://cloud.google.com/iam/docs/understanding-roles#iam.serviceAccountAdmin) <br>[roles/iam.serviceAccountTokenCreator](https://cloud.google.com/iam/docs/understanding-roles#iam.serviceAccountTokenCreator) |
|
||||
|<b>gcp-organization-admins</b><br><small><i>group</i></small>|[roles/iam.serviceAccountTokenCreator](https://cloud.google.com/iam/docs/understanding-roles#iam.serviceAccountTokenCreator) |
|
||||
|<b>gcp-organization-admins</b><br><small><i>group</i></small>|[roles/iam.serviceAccountTokenCreator](https://cloud.google.com/iam/docs/understanding-roles#iam.serviceAccountTokenCreator) <br>[roles/iam.workloadIdentityPoolAdmin](https://cloud.google.com/iam/docs/understanding-roles#iam.workloadIdentityPoolAdmin) |
|
||||
|<b>SERVICE_IDENTITY_service-networking</b><br><small><i>serviceAccount</i></small>|[roles/servicenetworking.serviceAgent](https://cloud.google.com/iam/docs/understanding-roles#servicenetworking.serviceAgent) <code>+</code>|
|
||||
|<b>prod-bootstrap-0</b><br><small><i>serviceAccount</i></small>|[roles/owner](https://cloud.google.com/iam/docs/understanding-roles#owner) |
|
||||
|<b>prod-resman-0</b><br><small><i>serviceAccount</i></small>|[roles/iam.serviceAccountAdmin](https://cloud.google.com/iam/docs/understanding-roles#iam.serviceAccountAdmin) <br>[roles/iam.workloadIdentityPoolAdmin](https://cloud.google.com/iam/docs/understanding-roles#iam.workloadIdentityPoolAdmin) <br>[roles/storage.admin](https://cloud.google.com/iam/docs/understanding-roles#storage.admin) |
|
||||
|<b>prod-bootstrap-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-0</b><br><small><i>serviceAccount</i></small>|[roles/cloudbuild.builds.editor](https://cloud.google.com/iam/docs/understanding-roles#cloudbuild.builds.editor) <br>[roles/iam.serviceAccountAdmin](https://cloud.google.com/iam/docs/understanding-roles#iam.serviceAccountAdmin) <br>[roles/iam.workloadIdentityPoolAdmin](https://cloud.google.com/iam/docs/understanding-roles#iam.workloadIdentityPoolAdmin) <br>[roles/source.admin](https://cloud.google.com/iam/docs/understanding-roles#source.admin) <br>[roles/storage.admin](https://cloud.google.com/iam/docs/understanding-roles#storage.admin) |
|
||||
|<b>prod-resman-1</b><br><small><i>serviceAccount</i></small>|[roles/logging.logWriter](https://cloud.google.com/iam/docs/understanding-roles#logging.logWriter) <code>+</code>|
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
locals {
|
||||
# used here for convenience, in organization.tf members are explicit
|
||||
billing_ext_admins = [
|
||||
local.groups_iam.gcp-billing-admins,
|
||||
local.groups_iam.gcp-organization-admins,
|
||||
module.automation-tf-bootstrap-sa.iam_email,
|
||||
module.automation-tf-resman-sa.iam_email
|
||||
|
@ -103,3 +104,12 @@ resource "google_billing_account_iam_member" "billing_ext_admin" {
|
|||
role = "roles/billing.admin"
|
||||
member = each.key
|
||||
}
|
||||
|
||||
resource "google_billing_account_iam_member" "billing_ext_cost_manager" {
|
||||
for_each = toset(
|
||||
local.billing_ext ? local.billing_ext_admins : []
|
||||
)
|
||||
billing_account_id = var.billing_account.id
|
||||
role = "roles/billing.costsManager"
|
||||
member = each.key
|
||||
}
|
||||
|
|
|
@ -76,6 +76,13 @@ locals {
|
|||
},
|
||||
local.billing_org ? {
|
||||
"roles/billing.admin" = [
|
||||
local.groups_iam.gcp-billing-admins,
|
||||
local.groups_iam.gcp-organization-admins,
|
||||
module.automation-tf-bootstrap-sa.iam_email,
|
||||
module.automation-tf-resman-sa.iam_email
|
||||
],
|
||||
"roles/billing.costsManager" = [
|
||||
local.groups_iam.gcp-billing-admins,
|
||||
local.groups_iam.gcp-organization-admins,
|
||||
module.automation-tf-bootstrap-sa.iam_email,
|
||||
module.automation-tf-resman-sa.iam_email
|
||||
|
|
|
@ -42,7 +42,8 @@ module "billing-organization-ext" {
|
|||
count = local.billing_org_ext ? 1 : 0
|
||||
organization_id = "organizations/${var.billing_account.organization_id}"
|
||||
iam_additive = {
|
||||
"roles/billing.user" = local.billing_ext_users
|
||||
"roles/billing.user" = local.billing_ext_users
|
||||
"roles/billing.costsManager" = local.billing_ext_users
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -56,3 +57,12 @@ resource "google_billing_account_iam_member" "billing_ext_admin" {
|
|||
role = "roles/billing.user"
|
||||
member = each.key
|
||||
}
|
||||
|
||||
resource "google_billing_account_iam_member" "billing_ext_costsmanager" {
|
||||
for_each = toset(
|
||||
local.billing_ext ? local.billing_ext_users : []
|
||||
)
|
||||
billing_account_id = var.billing_account.id
|
||||
role = "roles/billing.costsManager"
|
||||
member = each.key
|
||||
}
|
||||
|
|
|
@ -122,10 +122,12 @@ module "branch-pf-dev-sa-cicd" {
|
|||
each.value.branch == null
|
||||
? format(
|
||||
local.identity_providers[each.value.identity_provider].principalset_tpl,
|
||||
var.automation.federated_identity_pool,
|
||||
each.value.name
|
||||
)
|
||||
: format(
|
||||
local.identity_providers[each.value.identity_provider].principal_tpl,
|
||||
var.automation.federated_identity_pool,
|
||||
each.value.name,
|
||||
each.value.branch
|
||||
)
|
||||
|
|
|
@ -38,10 +38,12 @@ module "landing-project" {
|
|||
service_projects = []
|
||||
}
|
||||
iam = {
|
||||
"roles/dns.admin" = [local.service_accounts.project-factory-prod]
|
||||
(local.custom_roles.service_project_network_admin) = [
|
||||
local.service_accounts.project-factory-prod
|
||||
]
|
||||
"roles/dns.admin" = compact([
|
||||
try(local.service_accounts.project-factory-prod, null)
|
||||
])
|
||||
(local.custom_roles.service_project_network_admin) = compact([
|
||||
try(local.service_accounts.project-factory-prod, null)
|
||||
])
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -25,7 +25,8 @@ locals {
|
|||
})]
|
||||
}
|
||||
service_accounts = {
|
||||
for k, v in coalesce(var.service_accounts, {}) : k => "serviceAccount:${v}"
|
||||
for k, v in coalesce(var.service_accounts, {}) :
|
||||
k => "serviceAccount:${v}" if v != null
|
||||
}
|
||||
stage3_sas_delegated_grants = [
|
||||
"roles/composer.sharedVpcAgent",
|
||||
|
|
|
@ -40,7 +40,9 @@ module "dev-spoke-project" {
|
|||
}
|
||||
metric_scopes = [module.landing-project.project_id]
|
||||
iam = {
|
||||
"roles/dns.admin" = compact([local.service_accounts.project-factory-dev])
|
||||
"roles/dns.admin" = compact([
|
||||
try(local.service_accounts.project-factory-dev, null)
|
||||
])
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -124,9 +126,9 @@ resource "google_project_iam_binding" "dev_spoke_project_iam_delegated" {
|
|||
project = module.dev-spoke-project.project_id
|
||||
role = "roles/resourcemanager.projectIamAdmin"
|
||||
members = compact([
|
||||
local.service_accounts.data-platform-dev,
|
||||
local.service_accounts.project-factory-dev,
|
||||
local.service_accounts.gke-dev,
|
||||
try(local.service_accounts.data-platform-dev, null),
|
||||
try(local.service_accounts.project-factory-dev, null),
|
||||
try(local.service_accounts.gke-dev, null),
|
||||
])
|
||||
condition {
|
||||
title = "dev_stage3_sa_delegated_grants"
|
||||
|
|
|
@ -40,7 +40,9 @@ module "prod-spoke-project" {
|
|||
}
|
||||
metric_scopes = [module.landing-project.project_id]
|
||||
iam = {
|
||||
"roles/dns.admin" = compact([local.service_accounts.project-factory-prod])
|
||||
"roles/dns.admin" = compact([
|
||||
try(local.service_accounts.project-factory-prod, null)
|
||||
])
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -124,9 +126,9 @@ resource "google_project_iam_binding" "prod_spoke_project_iam_delegated" {
|
|||
project = module.prod-spoke-project.project_id
|
||||
role = "roles/resourcemanager.projectIamAdmin"
|
||||
members = compact([
|
||||
local.service_accounts.data-platform-prod,
|
||||
local.service_accounts.project-factory-prod,
|
||||
local.service_accounts.gke-prod,
|
||||
try(local.service_accounts.data-platform-prod, null),
|
||||
try(local.service_accounts.project-factory-prod, null),
|
||||
try(local.service_accounts.gke-prod, null),
|
||||
])
|
||||
condition {
|
||||
title = "prod_stage3_sa_delegated_grants"
|
||||
|
|
|
@ -38,10 +38,12 @@ module "landing-project" {
|
|||
service_projects = []
|
||||
}
|
||||
iam = {
|
||||
"roles/dns.admin" = [local.service_accounts.project-factory-prod]
|
||||
(local.custom_roles.service_project_network_admin) = [
|
||||
local.service_accounts.project-factory-prod
|
||||
]
|
||||
"roles/dns.admin" = compact([
|
||||
try(local.service_accounts.project-factory-prod, null)
|
||||
])
|
||||
(local.custom_roles.service_project_network_admin) = compact([
|
||||
try(local.service_accounts.project-factory-prod, null)
|
||||
])
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -36,7 +36,8 @@ locals {
|
|||
"roles/vpcaccess.user",
|
||||
]
|
||||
service_accounts = {
|
||||
for k, v in coalesce(var.service_accounts, {}) : k => "serviceAccount:${v}"
|
||||
for k, v in coalesce(var.service_accounts, {}) :
|
||||
k => "serviceAccount:${v}" if v != null
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -41,7 +41,9 @@ module "dev-spoke-project" {
|
|||
}
|
||||
metric_scopes = [module.landing-project.project_id]
|
||||
iam = {
|
||||
"roles/dns.admin" = compact([local.service_accounts.project-factory-dev])
|
||||
"roles/dns.admin" = compact([
|
||||
try(local.service_accounts.project-factory-dev, null)
|
||||
])
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -101,9 +103,9 @@ resource "google_project_iam_binding" "dev_spoke_project_iam_delegated" {
|
|||
project = module.dev-spoke-project.project_id
|
||||
role = "roles/resourcemanager.projectIamAdmin"
|
||||
members = compact([
|
||||
local.service_accounts.data-platform-dev,
|
||||
local.service_accounts.project-factory-dev,
|
||||
local.service_accounts.gke-dev,
|
||||
try(local.service_accounts.data-platform-dev, null),
|
||||
try(local.service_accounts.project-factory-dev, null),
|
||||
try(local.service_accounts.gke-dev, null),
|
||||
])
|
||||
condition {
|
||||
title = "dev_stage3_sa_delegated_grants"
|
||||
|
|
|
@ -41,7 +41,9 @@ module "prod-spoke-project" {
|
|||
}
|
||||
metric_scopes = [module.landing-project.project_id]
|
||||
iam = {
|
||||
"roles/dns.admin" = compact([local.service_accounts.project-factory-prod])
|
||||
"roles/dns.admin" = compact([
|
||||
try(local.service_accounts.project-factory-prod, null)
|
||||
])
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -101,9 +103,9 @@ resource "google_project_iam_binding" "prod_spoke_project_iam_delegated" {
|
|||
project = module.prod-spoke-project.project_id
|
||||
role = "roles/resourcemanager.projectIamAdmin"
|
||||
members = compact([
|
||||
local.service_accounts.data-platform-prod,
|
||||
local.service_accounts.project-factory-prod,
|
||||
local.service_accounts.gke-prod,
|
||||
try(local.service_accounts.data-platform-prod, null),
|
||||
try(local.service_accounts.project-factory-prod, null),
|
||||
try(local.service_accounts.gke-prod, null),
|
||||
])
|
||||
condition {
|
||||
title = "prod_stage3_sa_delegated_grants"
|
||||
|
|
|
@ -38,10 +38,12 @@ module "landing-project" {
|
|||
service_projects = []
|
||||
}
|
||||
iam = {
|
||||
"roles/dns.admin" = [local.service_accounts.project-factory-prod]
|
||||
(local.custom_roles.service_project_network_admin) = [
|
||||
local.service_accounts.project-factory-prod
|
||||
]
|
||||
"roles/dns.admin" = compact([
|
||||
try(local.service_accounts.project-factory-prod, null)
|
||||
])
|
||||
(local.custom_roles.service_project_network_admin) = compact([
|
||||
try(local.service_accounts.project-factory-prod, null)
|
||||
])
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -36,7 +36,8 @@ locals {
|
|||
"roles/vpcaccess.user",
|
||||
]
|
||||
service_accounts = {
|
||||
for k, v in coalesce(var.service_accounts, {}) : k => "serviceAccount:${v}"
|
||||
for k, v in coalesce(var.service_accounts, {}) :
|
||||
k => "serviceAccount:${v}" if v != null
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -41,7 +41,9 @@ module "dev-spoke-project" {
|
|||
}
|
||||
metric_scopes = [module.landing-project.project_id]
|
||||
iam = {
|
||||
"roles/dns.admin" = compact([local.service_accounts.project-factory-dev])
|
||||
"roles/dns.admin" = compact([
|
||||
try(local.service_accounts.project-factory-dev, null)
|
||||
])
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -101,9 +103,9 @@ resource "google_project_iam_binding" "dev_spoke_project_iam_delegated" {
|
|||
project = module.dev-spoke-project.project_id
|
||||
role = "roles/resourcemanager.projectIamAdmin"
|
||||
members = compact([
|
||||
local.service_accounts.data-platform-dev,
|
||||
local.service_accounts.project-factory-dev,
|
||||
local.service_accounts.gke-dev,
|
||||
try(local.service_accounts.data-platform-dev, null),
|
||||
try(local.service_accounts.project-factory-dev, null),
|
||||
try(local.service_accounts.gke-dev, null)
|
||||
])
|
||||
condition {
|
||||
title = "dev_stage3_sa_delegated_grants"
|
||||
|
|
|
@ -41,7 +41,9 @@ module "prod-spoke-project" {
|
|||
}
|
||||
metric_scopes = [module.landing-project.project_id]
|
||||
iam = {
|
||||
"roles/dns.admin" = compact([local.service_accounts.project-factory-prod])
|
||||
"roles/dns.admin" = compact([
|
||||
try(local.service_accounts.project-factory-prod, null)
|
||||
])
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -101,9 +103,9 @@ resource "google_project_iam_binding" "prod_spoke_project_iam_delegated" {
|
|||
project = module.prod-spoke-project.project_id
|
||||
role = "roles/resourcemanager.projectIamAdmin"
|
||||
members = compact([
|
||||
local.service_accounts.data-platform-prod,
|
||||
local.service_accounts.project-factory-prod,
|
||||
local.service_accounts.gke-prod,
|
||||
try(local.service_accounts.data-platform-prod, null),
|
||||
try(local.service_accounts.project-factory-prod, null),
|
||||
tru(local.service_accounts.gke-prod, null),
|
||||
])
|
||||
condition {
|
||||
title = "prod_stage3_sa_delegated_grants"
|
||||
|
|
|
@ -37,6 +37,7 @@ These modules are used in the examples included in this repository. If you are u
|
|||
- [project](./project)
|
||||
- [projects-data-source](./projects-data-source)
|
||||
- [service account](./iam-service-account)
|
||||
- [organization policy](./organization-policy)
|
||||
|
||||
## Networking modules
|
||||
|
||||
|
|
|
@ -15,6 +15,21 @@
|
|||
*/
|
||||
|
||||
locals {
|
||||
annotations = merge(
|
||||
var.ingress_settings == null ? {} : {
|
||||
"run.googleapis.com/ingress" = var.ingress_settings
|
||||
},
|
||||
var.vpc_connector == null ? {} : {
|
||||
"run.googleapis.com/vpc-access-connector" = (
|
||||
try(var.vpc_connector.create, false)
|
||||
? google_vpc_access_connector.connector.0.id
|
||||
: var.vpc_connector.name
|
||||
)
|
||||
},
|
||||
try(var.vpc_connector.egress_settings, null) == null ? {} : {
|
||||
"run.googleapis.com/vpc-access-egress" = var.vpc_connector.egress_settings
|
||||
}
|
||||
)
|
||||
prefix = var.prefix == null ? "" : "${var.prefix}-"
|
||||
service_account_email = (
|
||||
var.service_account_create
|
||||
|
@ -25,21 +40,10 @@ locals {
|
|||
)
|
||||
: var.service_account
|
||||
)
|
||||
|
||||
annotations = merge(var.ingress_settings == null ? {} : { "run.googleapis.com/ingress" = var.ingress_settings },
|
||||
var.vpc_connector == null
|
||||
? {}
|
||||
: try(var.vpc_connector.create, false)
|
||||
? { "run.googleapis.com/vpc-access-connector" = var.vpc_connector.name }
|
||||
: { "run.googleapis.com/vpc-access-connector" = google_vpc_access_connector.connector.0.id }
|
||||
,
|
||||
try(var.vpc_connector.egress_settings, null) == null
|
||||
? {}
|
||||
: { "run.googleapis.com/vpc-access-egress" = var.vpc_connector.egress_settings })
|
||||
}
|
||||
|
||||
resource "google_vpc_access_connector" "connector" {
|
||||
count = try(var.vpc_connector.create, false) == false ? 0 : 1
|
||||
count = try(var.vpc_connector.create, false) ? 1 : 0
|
||||
project = var.project_id
|
||||
name = var.vpc_connector.name
|
||||
region = var.region
|
||||
|
@ -56,20 +60,30 @@ resource "google_cloud_run_service" "service" {
|
|||
template {
|
||||
spec {
|
||||
dynamic "containers" {
|
||||
for_each = var.containers == null ? {} : { for i, container in var.containers : i => container }
|
||||
for_each = var.containers == null ? {} : {
|
||||
for i, container in var.containers : i => container
|
||||
}
|
||||
content {
|
||||
image = containers.value["image"]
|
||||
command = try(containers.value["options"]["command"], null)
|
||||
args = try(containers.value["options"]["args"], null)
|
||||
dynamic "env" {
|
||||
for_each = try(containers.value["options"]["env"], null) == null ? {} : containers.value["options"]["env"]
|
||||
for_each = (
|
||||
try(containers.value["options"]["env"], null) == null
|
||||
? {}
|
||||
: containers.value["options"]["env"]
|
||||
)
|
||||
content {
|
||||
name = env.key
|
||||
value = env.value
|
||||
}
|
||||
}
|
||||
dynamic "env" {
|
||||
for_each = try(containers.value["options"]["env_from"], null) == null ? {} : containers.value["options"]["env_from"]
|
||||
for_each = (
|
||||
try(containers.value["options"]["env_from"], null) == null
|
||||
? {}
|
||||
: containers.value["options"]["env_from"]
|
||||
)
|
||||
content {
|
||||
name = env.key
|
||||
value_from {
|
||||
|
@ -81,7 +95,14 @@ resource "google_cloud_run_service" "service" {
|
|||
}
|
||||
}
|
||||
dynamic "ports" {
|
||||
for_each = containers.value["ports"] == null ? {} : { for port in containers.value["ports"] : "${port.name}-${port.container_port}" => port }
|
||||
for_each = (
|
||||
containers.value["ports"] == null
|
||||
? {}
|
||||
: {
|
||||
for port in containers.value["ports"] :
|
||||
"${port.name}-${port.container_port}" => port
|
||||
}
|
||||
)
|
||||
content {
|
||||
name = ports.value["name"]
|
||||
protocol = ports.value["protocol"]
|
||||
|
@ -96,7 +117,11 @@ resource "google_cloud_run_service" "service" {
|
|||
}
|
||||
}
|
||||
dynamic "volume_mounts" {
|
||||
for_each = containers.value["volume_mounts"] == null ? {} : containers.value["volume_mounts"]
|
||||
for_each = (
|
||||
containers.value["volume_mounts"] == null
|
||||
? {}
|
||||
: containers.value["volume_mounts"]
|
||||
)
|
||||
content {
|
||||
name = volume_mounts.key
|
||||
mount_path = volume_mounts.value
|
||||
|
@ -112,7 +137,11 @@ resource "google_cloud_run_service" "service" {
|
|||
secret {
|
||||
secret_name = volumes.value["secret_name"]
|
||||
dynamic "items" {
|
||||
for_each = volumes.value["items"] == null ? [] : volumes.value["items"]
|
||||
for_each = (
|
||||
volumes.value["items"] == null
|
||||
? []
|
||||
: volumes.value["items"]
|
||||
)
|
||||
content {
|
||||
key = items.value["key"]
|
||||
path = items.value["path"]
|
||||
|
@ -130,7 +159,6 @@ resource "google_cloud_run_service" "service" {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
metadata {
|
||||
annotations = local.annotations
|
||||
}
|
||||
|
@ -162,7 +190,10 @@ resource "google_service_account" "service_account" {
|
|||
}
|
||||
|
||||
resource "google_eventarc_trigger" "audit_log_triggers" {
|
||||
for_each = var.audit_log_triggers == null ? {} : { for trigger in var.audit_log_triggers : "${trigger.service_name}-${trigger.method_name}" => trigger }
|
||||
for_each = var.audit_log_triggers == null ? {} : {
|
||||
for trigger in var.audit_log_triggers :
|
||||
"${trigger.service_name}-${trigger.method_name}" => trigger
|
||||
}
|
||||
name = "${local.prefix}${each.key}-audit-log-trigger"
|
||||
location = google_cloud_run_service.service.location
|
||||
project = google_cloud_run_service.service.project
|
||||
|
@ -188,7 +219,11 @@ resource "google_eventarc_trigger" "audit_log_triggers" {
|
|||
|
||||
resource "google_eventarc_trigger" "pubsub_triggers" {
|
||||
for_each = var.pubsub_triggers == null ? [] : toset(var.pubsub_triggers)
|
||||
name = each.value == "" ? "${local.prefix}default-pubsub-trigger" : "${local.prefix}${each.value}-pubsub-trigger"
|
||||
name = (
|
||||
each.value == ""
|
||||
? "${local.prefix}default-pubsub-trigger"
|
||||
: "${local.prefix}${each.value}-pubsub-trigger"
|
||||
)
|
||||
location = google_cloud_run_service.service.location
|
||||
project = google_cloud_run_service.service.project
|
||||
matching_criteria {
|
||||
|
|
|
@ -531,11 +531,12 @@ Target proxies leverage [url-maps](url-map.tf): set of L7 rules, which create a
|
|||
| name | description | sensitive |
|
||||
|---|---|:---:|
|
||||
| [backend_services](outputs.tf#L22) | Backend service resources. | |
|
||||
| [global_forwarding_rule](outputs.tf#L57) | The global forwarding rule. | |
|
||||
| [global_forwarding_rule](outputs.tf#L62) | The global forwarding rule. | |
|
||||
| [health_checks](outputs.tf#L17) | Health-check resources. | |
|
||||
| [ip_address](outputs.tf#L44) | The reserved global IP address. | |
|
||||
| [ip_address_self_link](outputs.tf#L49) | The URI of the reserved global IP address. | |
|
||||
| [ssl_certificates](outputs.tf#L35) | The SSL certificate. | |
|
||||
| [target_proxy](outputs.tf#L49) | The target proxy. | |
|
||||
| [target_proxy](outputs.tf#L54) | The target proxy. | |
|
||||
| [url_map](outputs.tf#L30) | The url-map. | |
|
||||
|
||||
<!-- END TFDOC -->
|
||||
|
|
|
@ -43,6 +43,11 @@ output "ssl_certificates" {
|
|||
|
||||
output "ip_address" {
|
||||
description = "The reserved global IP address."
|
||||
value = try(google_compute_global_address.static_ip[0].address, null)
|
||||
}
|
||||
|
||||
output "ip_address_self_link" {
|
||||
description = "The URI of the reserved global IP address."
|
||||
value = google_compute_global_forwarding_rule.forwarding_rule.ip_address
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,166 @@
|
|||
# Google Cloud Organization Policy
|
||||
|
||||
This module allows creation and management of [GCP Organization Policies](https://cloud.google.com/resource-manager/docs/organization-policy/org-policy-constraints) by defining them in a well formatted `yaml` files or with HCL.
|
||||
|
||||
Yaml based factory can simplify centralized management of Org Policies for a DevSecOps team by providing a simple way to define/structure policies and exclusions.
|
||||
|
||||
> **_NOTE:_** This module uses experimental feature `module_variable_optional_attrs` which will be included into [terraform release 1.3](https://github.com/hashicorp/terraform/releases/tag/v1.3.0-alpha20220706).
|
||||
|
||||
## Example
|
||||
|
||||
### Terraform code
|
||||
|
||||
```hcl
|
||||
# using configuration provided in a set of yaml files
|
||||
module "org-policy-factory" {
|
||||
source = "./modules/organization-policy"
|
||||
|
||||
config_directory = "./policies"
|
||||
}
|
||||
|
||||
# using configuration provided in the module variable
|
||||
module "org-policy" {
|
||||
source = "./modules/organization-policy"
|
||||
|
||||
policies = {
|
||||
"folders/1234567890" = {
|
||||
# enforce boolean policy with no conditions
|
||||
"iam.disableServiceAccountKeyUpload" = {
|
||||
rules = [
|
||||
{
|
||||
enforce = true
|
||||
}
|
||||
]
|
||||
},
|
||||
# Deny All for compute.vmCanIpForward policy
|
||||
"compute.vmCanIpForward" = {
|
||||
inherit_from_parent = false
|
||||
rules = [
|
||||
deny = [] # stands for deny_all
|
||||
]
|
||||
}
|
||||
},
|
||||
"organizations/1234567890" = {
|
||||
# allow only internal ingress when match condition env=prod
|
||||
"run.allowedIngress" = {
|
||||
rules = [
|
||||
{
|
||||
allow = ["internal"]
|
||||
condition = {
|
||||
description= "allow ingress"
|
||||
expression = "resource.matchTag('123456789/environment', 'prod')"
|
||||
title = "allow-for-prod-org"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
# tftest skip
|
||||
```
|
||||
|
||||
## Org Policy definition format and structure
|
||||
|
||||
### Structure of `policies` variable
|
||||
|
||||
```hcl
|
||||
policies = {
|
||||
"parent_id" = { # parent id in format projects/project-id, folders/1234567890 or organizations/1234567890.
|
||||
"policy_name" = { # policy constraint id, for example compute.vmExternalIpAccess.
|
||||
inherit_from_parent = true|false # (Optional) Only for list constraints. Determines the inheritance behavior for this policy.
|
||||
reset = true|false # (Optional) Ignores policies set above this resource and restores the constraint_default enforcement behavior.
|
||||
rules = [ # Up to 10 PolicyRules are allowed.
|
||||
{
|
||||
allow = ["value1", "value2"] # (Optional) Only for list constraints. Stands for `allow_all` if set to empty list `[]` or to `values.allowed_values` if set to a list of values
|
||||
denyl = ["value3", "value4"] # (Optional) Only for list constraints. Stands for `deny_all` if set to empty list `[]` or to `values.denied_values` if set to a list of values
|
||||
enforce = true|false # (Optional) Only for boolean constraints. If true, then the Policy is enforced.
|
||||
condition = { # (Optional) A condition which determines whether this rule is used in the evaluation of the policy.
|
||||
description = "Condition description" # (Optional)
|
||||
expression = "Condition expression" # (Optional) For example "resource.matchTag('123456789/environment', 'prod')".
|
||||
location = "policy-error.log" # (Optional) String indicating the location of the expression for error reporting.
|
||||
title = "condition-title" # (Optional)
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
# tftest skip
|
||||
```
|
||||
|
||||
### Structure of configuration provided in a yaml file/s
|
||||
|
||||
Configuration should be placed in a set of yaml files in the config directory. Policy entry structure as follows:
|
||||
|
||||
```yaml
|
||||
parent_id: # parent id in format projects/project-id, folders/1234567890 or organizations/1234567890.
|
||||
policy_name1: # policy constraint id, for example compute.vmExternalIpAccess.
|
||||
inherit_from_parent: true|false # (Optional) Only for list constraints. Determines the inheritance behavior for this policy.
|
||||
reset: true|false # (Optional) Ignores policies set above this resource and restores the constraint_default enforcement behavior.
|
||||
rules:
|
||||
- allow: ["value1", "value2"] # (Optional) Only for list constraints. Stands for `allow_all` if set to empty list `[]` or to `values.allowed_values` if set to a list of values
|
||||
deny: ["value3", "value4"] # (Optional) Only for list constraints. Stands for `deny_all` if set to empty list `[]` or to `values.denied_values` if set to a list of values
|
||||
enforce: true|false # (Optional) Only for boolean constraints. If true, then the Policy is enforced.
|
||||
condition: # (Optional) A condition which determines whether this rule is used in the evaluation of the policy.
|
||||
description: Condition description # (Optional)
|
||||
expression: Condition expression # (Optional) For example resource.matchTag("123456789/environment", "prod")
|
||||
location: policy-error.log # (Optional) String indicating the location of the expression for error reporting.
|
||||
title: condition-title # (Optional)
|
||||
```
|
||||
|
||||
Module allows policies to be distributed into multiple yaml files for a better management and navigation.
|
||||
|
||||
```bash
|
||||
├── org-policies
|
||||
│ ├── baseline.yaml
|
||||
│ ├── image-import-projects.yaml
|
||||
│ └── exclusions.yaml
|
||||
```
|
||||
|
||||
Organization policies example yaml configuration
|
||||
|
||||
```bash
|
||||
cat ./policies/baseline.yaml
|
||||
organizations/1234567890:
|
||||
constraints/compute.vmExternalIpAccess:
|
||||
rules:
|
||||
- deny: [] # Stands for deny_all = true
|
||||
folders/1234567890:
|
||||
compute.vmCanIpForward:
|
||||
inherit_from_parent: false
|
||||
reset: false
|
||||
rules:
|
||||
- allow: [] # Stands for allow_all = true
|
||||
projects/my-project-id:
|
||||
run.allowedIngress:
|
||||
inherit_from_parent: true
|
||||
rules:
|
||||
- allow: ['internal'] # Stands for values.allowed_values
|
||||
condition:
|
||||
description: allow internal ingress
|
||||
expression: resource.matchTag("123456789/environment", "prod")
|
||||
location: test.log
|
||||
title: allow-for-prod
|
||||
iam.allowServiceAccountCredentialLifetimeExtension:
|
||||
rules:
|
||||
- deny: [] # Stands for deny_all = true
|
||||
compute.disableGlobalLoadBalancing:
|
||||
reset: true
|
||||
```
|
||||
<!-- BEGIN TFDOC -->
|
||||
|
||||
## Variables
|
||||
|
||||
| name | description | type | required | default |
|
||||
|---|---|:---:|:---:|:---:|
|
||||
| [config_directory](variables.tf#L17) | Paths to a folder where organization policy configs are stored in yaml format. Files suffix must be `.yaml`. | <code>string</code> | | <code>null</code> |
|
||||
| [policies](variables.tf#L23) | Organization policies keyed by parent in format `projects/project-id`, `folders/1234567890` or `organizations/1234567890`. | <code title="map(map(object({ inherit_from_parent = optional(bool) # List policy only. reset = optional(bool) rules = optional( list(object({ allow = optional(list(string)) # List policy only. Stands for `allow_all` if set to empty list `[]` or to `values.allowed_values` if set to a list of values deny = optional(list(string)) # List policy only. Stands for `deny_all` if set to empty list `[]` or to `values.denied_values` if set to a list of values enforce = optional(bool) # Boolean policy only. condition = optional( object({ description = optional(string) expression = optional(string) location = optional(string) title = optional(string) }) ) })) ) })))">map(map(object({…})))</code> | | <code>{}</code> |
|
||||
|
||||
## Outputs
|
||||
|
||||
| name | description | sensitive |
|
||||
|---|---|:---:|
|
||||
| [policies](outputs.tf#L17) | Organization policies. | |
|
||||
|
||||
<!-- END TFDOC -->
|
|
@ -0,0 +1,19 @@
|
|||
# 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
|
||||
#
|
||||
# https://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
|
||||
terraform {
|
||||
# TODO: Remove once Terraform 1.3 is released https://github.com/hashicorp/terraform/releases/tag/v1.3.0-alpha20220622
|
||||
experiments = [module_variable_optional_attrs]
|
||||
}
|
|
@ -0,0 +1,102 @@
|
|||
/**
|
||||
* 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.
|
||||
*/
|
||||
|
||||
|
||||
locals {
|
||||
policy_files = var.config_directory == null ? [] : concat(
|
||||
[
|
||||
for config_file in fileset("${path.root}/${var.config_directory}", "**/*.yaml") :
|
||||
"${path.root}/${var.config_directory}/${config_file}"
|
||||
]
|
||||
)
|
||||
|
||||
policies_raw = merge(
|
||||
merge(
|
||||
[
|
||||
for config_file in local.policy_files :
|
||||
try(yamldecode(file(config_file)), {})
|
||||
]...
|
||||
), var.policies)
|
||||
|
||||
policies_list = flatten([
|
||||
for parent, policies in local.policies_raw : [
|
||||
for policy_name, policy in policies : {
|
||||
parent = parent,
|
||||
policy_name = policy_name,
|
||||
inherit_from_parent = try(policy["inherit_from_parent"], null),
|
||||
reset = try(policy["reset"], null),
|
||||
rules = [
|
||||
for rule in try(policy["rules"], []) : {
|
||||
allow_all = try(length(rule["allow"]), -1) == 0 ? "TRUE" : null
|
||||
deny_all = try(length(rule["deny"]), -1) == 0 ? "TRUE" : null
|
||||
enforce = try(rule["enforce"], null) == true ? "TRUE" : try(
|
||||
rule["enforce"], null) == false ? "FALSE" : null,
|
||||
condition = try(rule["condition"], null) != null ? {
|
||||
description = try(rule["condition"]["description"], null),
|
||||
expression = try(rule["condition"]["expression"], null),
|
||||
location = try(rule["condition"]["location"], null),
|
||||
title = try(rule["condition"]["title"], null)
|
||||
} : null,
|
||||
values = try(length(rule["allow"]), 0) > 0 || try(length(rule["deny"]), 0) > 0 ? {
|
||||
allowed_values = try(length(rule["allow"]), 0) > 0 ? rule["allow"] : null
|
||||
denied_values = try(length(rule["deny"]), 0) > 0 ? rule["deny"] : null
|
||||
} : null
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
])
|
||||
|
||||
policies_map = {
|
||||
for item in local.policies_list :
|
||||
format("%s-%s", item["parent"], item["policy_name"]) => item
|
||||
}
|
||||
}
|
||||
|
||||
resource "google_org_policy_policy" "primary" {
|
||||
for_each = local.policies_map
|
||||
name = format("%s/policies/%s", each.value.parent, each.value.policy_name)
|
||||
parent = each.value.parent
|
||||
|
||||
spec {
|
||||
inherit_from_parent = each.value.inherit_from_parent
|
||||
reset = each.value.reset
|
||||
dynamic "rules" {
|
||||
for_each = each.value.rules
|
||||
content {
|
||||
allow_all = rules.value.allow_all
|
||||
deny_all = rules.value.deny_all
|
||||
enforce = rules.value.enforce
|
||||
dynamic "condition" {
|
||||
for_each = rules.value.condition != null ? [""] : []
|
||||
content {
|
||||
description = rules.value.condition.description
|
||||
expression = rules.value.condition.expression
|
||||
location = rules.value.condition.location
|
||||
title = rules.value.condition.title
|
||||
}
|
||||
}
|
||||
dynamic "values" {
|
||||
for_each = rules.value.values != null ? [""] : []
|
||||
content {
|
||||
allowed_values = rules.value.values.allowed_values
|
||||
denied_values = rules.value.values.denied_values
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
/**
|
||||
* Copyright 2022 Google LLC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
output "policies" {
|
||||
description = "Organization policies."
|
||||
value = google_org_policy_policy.primary
|
||||
}
|
|
@ -0,0 +1,45 @@
|
|||
/**
|
||||
* 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 "config_directory" {
|
||||
description = "Paths to a folder where organization policy configs are stored in yaml format. Files suffix must be `.yaml`."
|
||||
type = string
|
||||
default = null
|
||||
}
|
||||
|
||||
variable "policies" {
|
||||
description = "Organization policies keyed by parent in format `projects/project-id`, `folders/1234567890` or `organizations/1234567890`."
|
||||
type = map(map(object({
|
||||
inherit_from_parent = optional(bool) # List policy only.
|
||||
reset = optional(bool)
|
||||
rules = optional(
|
||||
list(object({
|
||||
allow = optional(list(string)) # List policy only. Stands for `allow_all` if set to empty list `[]` or to `values.allowed_values` if set to a list of values
|
||||
deny = optional(list(string)) # List policy only. Stands for `deny_all` if set to empty list `[]` or to `values.denied_values` if set to a list of values
|
||||
enforce = optional(bool) # Boolean policy only.
|
||||
condition = optional(
|
||||
object({
|
||||
description = optional(string)
|
||||
expression = optional(string)
|
||||
location = optional(string)
|
||||
title = optional(string)
|
||||
})
|
||||
)
|
||||
}))
|
||||
)
|
||||
})))
|
||||
default = {}
|
||||
}
|
|
@ -0,0 +1,29 @@
|
|||
# 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
|
||||
#
|
||||
# https://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
terraform {
|
||||
required_version = ">= 1.1.0"
|
||||
required_providers {
|
||||
google = {
|
||||
source = "hashicorp/google"
|
||||
version = ">= 4.20.0" # tftest
|
||||
}
|
||||
google-beta = {
|
||||
source = "hashicorp/google-beta"
|
||||
version = ">= 4.20.0" # tftest
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
/**
|
||||
* Copyright 2022 Google LLC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
module "test" {
|
||||
source = "../../../../../examples/cloud-operations/adfs"
|
||||
project_create = var.project_create
|
||||
project_id = var.project_id
|
||||
ad_dns_domain_name = var.ad_dns_domain_name
|
||||
adfs_dns_domain_name = var.adfs_dns_domain_name
|
||||
}
|
|
@ -0,0 +1,103 @@
|
|||
# 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
|
||||
#
|
||||
# https://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.
|
||||
|
||||
# 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
|
||||
#
|
||||
# https://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 "project_create" {
|
||||
type = object({
|
||||
billing_account_id = string
|
||||
parent = string
|
||||
})
|
||||
default = null
|
||||
}
|
||||
|
||||
variable "project_id" {
|
||||
type = string
|
||||
default = "my-project"
|
||||
}
|
||||
|
||||
variable "prefix" {
|
||||
type = string
|
||||
default = null
|
||||
}
|
||||
|
||||
variable "network_config" {
|
||||
type = object({
|
||||
network = string
|
||||
subnet = string
|
||||
})
|
||||
default = null
|
||||
}
|
||||
|
||||
variable "ad_dns_domain_name" {
|
||||
type = string
|
||||
default = "example.com"
|
||||
}
|
||||
|
||||
variable "adfs_dns_domain_name" {
|
||||
type = string
|
||||
default = "adfs.example.com"
|
||||
}
|
||||
|
||||
variable "disk_size" {
|
||||
type = number
|
||||
default = 50
|
||||
}
|
||||
|
||||
variable "disk_type" {
|
||||
type = string
|
||||
default = "pd-ssd"
|
||||
}
|
||||
|
||||
variable "image" {
|
||||
type = string
|
||||
default = "projects/windows-cloud/global/images/family/windows-2022"
|
||||
}
|
||||
|
||||
variable "instance_type" {
|
||||
type = string
|
||||
default = "n1-standard-2"
|
||||
}
|
||||
|
||||
variable "region" {
|
||||
type = string
|
||||
default = "europe-west1"
|
||||
}
|
||||
|
||||
variable "zone" {
|
||||
type = string
|
||||
default = "europe-west1-c"
|
||||
}
|
||||
|
||||
variable "ad_ip_cidr_block" {
|
||||
type = string
|
||||
default = "10.0.0.0/24"
|
||||
}
|
||||
|
||||
variable "subnet_ip_cidr_block" {
|
||||
type = string
|
||||
default = "10.0.1.0/28"
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
# Copyright 2022 Google LLC
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
def test_resources(e2e_plan_runner):
|
||||
"Test that plan works and the numbers of resources is as expected."
|
||||
modules, resources = e2e_plan_runner()
|
||||
assert len(modules) == 4
|
||||
assert len(resources) == 16
|
|
@ -0,0 +1,13 @@
|
|||
# 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.
|
|
@ -0,0 +1,24 @@
|
|||
/**
|
||||
* Copyright 2022 Google LLC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
module "test" {
|
||||
source = "../../../../../examples/data-solutions/data-playground/"
|
||||
project_id = "sampleproject"
|
||||
project_create = {
|
||||
billing_account_id = "123456-123456-123456",
|
||||
parent = "folders/467898377"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
# Copyright 2022 Google LLC
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
|
||||
import os
|
||||
import pytest
|
||||
|
||||
|
||||
FIXTURES_DIR = os.path.join(os.path.dirname(__file__), 'fixture')
|
||||
|
||||
def test_resources(e2e_plan_runner):
|
||||
"Test that plan works and the numbers of resources is as expected."
|
||||
modules, resources = e2e_plan_runner(FIXTURES_DIR)
|
||||
assert len(modules) == 4
|
||||
assert len(resources) == 23
|
|
@ -12,6 +12,16 @@
|
|||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
variable "vpc_connector" {
|
||||
type = any
|
||||
default = null
|
||||
}
|
||||
|
||||
variable "vpc_connector_config" {
|
||||
type = any
|
||||
default = null
|
||||
}
|
||||
|
||||
module "cloud_run" {
|
||||
source = "../../../../modules/cloud-run"
|
||||
project_id = "my-project"
|
||||
|
@ -37,4 +47,6 @@ module "cloud_run" {
|
|||
iam = {
|
||||
"roles/run.invoker" = ["allUsers"]
|
||||
}
|
||||
vpc_connector = var.vpc_connector
|
||||
vpc_connector_config = var.vpc_connector_config
|
||||
}
|
||||
|
|
|
@ -28,21 +28,54 @@ def test_resource_count(resources):
|
|||
|
||||
def test_iam(resources):
|
||||
"Test IAM binding resources."
|
||||
bindings = [r['values'] for r in resources if r['type']
|
||||
== 'google_cloud_run_service_iam_binding']
|
||||
bindings = [
|
||||
r['values']
|
||||
for r in resources
|
||||
if r['type'] == 'google_cloud_run_service_iam_binding'
|
||||
]
|
||||
assert len(bindings) == 1
|
||||
assert bindings[0]['role'] == 'roles/run.invoker'
|
||||
|
||||
|
||||
def test_audit_log_triggers(resources):
|
||||
"Test audit logs Eventarc trigger resources."
|
||||
audit_log_triggers = [r['values'] for r in resources if r['type']
|
||||
== 'google_eventarc_trigger' and r['name'] == 'audit_log_triggers']
|
||||
audit_log_triggers = [
|
||||
r['values']
|
||||
for r in resources
|
||||
if r['type'] == 'google_eventarc_trigger' and
|
||||
r['name'] == 'audit_log_triggers'
|
||||
]
|
||||
assert len(audit_log_triggers) == 1
|
||||
|
||||
|
||||
def test_pubsub_triggers(resources):
|
||||
"Test Pub/Sub Eventarc trigger resources."
|
||||
pubsub_triggers = [r['values'] for r in resources if r['type']
|
||||
== 'google_eventarc_trigger' and r['name'] == 'pubsub_triggers']
|
||||
pubsub_triggers = [
|
||||
r['values'] for r in resources if
|
||||
r['type'] == 'google_eventarc_trigger' and r['name'] == 'pubsub_triggers'
|
||||
]
|
||||
assert len(pubsub_triggers) == 2
|
||||
|
||||
|
||||
def test_vpc_connector_none(plan_runner):
|
||||
"Test VPC connector creation."
|
||||
_, resources = plan_runner()
|
||||
assert len(
|
||||
[r for r in resources if r['type'] == 'google_vpc_access_connector']) == 0
|
||||
|
||||
|
||||
def test_vpc_connector_nocreate(plan_runner):
|
||||
"Test VPC connector creation."
|
||||
_, resources = plan_runner(
|
||||
vpc_connector='{create=false, name="foo", egress_settings=null}')
|
||||
assert len(
|
||||
[r for r in resources if r['type'] == 'google_vpc_access_connector']) == 0
|
||||
|
||||
|
||||
def test_vpc_connector_create(plan_runner):
|
||||
"Test VPC connector creation."
|
||||
_, resources = plan_runner(
|
||||
vpc_connector='{create=true, name="foo", egress_settings=null}',
|
||||
vpc_connector_config='{ip_cidr_range="10.0.0.0/28", network="default"}')
|
||||
assert len(
|
||||
[r for r in resources if r['type'] == 'google_vpc_access_connector']) == 1
|
||||
|
|
|
@ -0,0 +1,13 @@
|
|||
# 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.
|
|
@ -0,0 +1,18 @@
|
|||
# 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
|
||||
#
|
||||
# https://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
terraform {
|
||||
# TODO: Remove once Terraform 1.3 is released https://github.com/hashicorp/terraform/releases/tag/v1.3.0-alpha20220622
|
||||
experiments = [module_variable_optional_attrs]
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
/**
|
||||
* Copyright 2022 Google LLC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
module "org-policy" {
|
||||
source = "../../../../modules/organization-policy"
|
||||
|
||||
config_directory = var.config_directory
|
||||
policies = var.policies
|
||||
}
|
|
@ -0,0 +1,40 @@
|
|||
# Copyright 2022 Google LLC
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
|
||||
organizations/1234567890:
|
||||
constraints/compute.vmExternalIpAccess:
|
||||
rules:
|
||||
- deny_all: true
|
||||
folders/1234567890:
|
||||
compute.vmCanIpForward:
|
||||
inherit_from_parent: false
|
||||
reset: false
|
||||
rules:
|
||||
- allow: []
|
||||
projects/my-project-id:
|
||||
run.allowedIngress:
|
||||
inherit_from_parent: true
|
||||
rules:
|
||||
- allow: ['internal']
|
||||
condition:
|
||||
description: allow internal ingress
|
||||
expression: resource.matchTag("123456789/environment", "prod")
|
||||
location: test.log
|
||||
title: allow-for-prod
|
||||
iam.allowServiceAccountCredentialLifetimeExtension:
|
||||
rules:
|
||||
- deny: []
|
||||
compute.disableGlobalLoadBalancing:
|
||||
reset: true
|
|
@ -0,0 +1,46 @@
|
|||
/**
|
||||
* Copyright 2022 Google LLC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
|
||||
variable "config_directory" {
|
||||
description = "Paths to a folder where organization policy configs are stored in yaml format. Files suffix must be `.yaml`."
|
||||
type = string
|
||||
default = null
|
||||
}
|
||||
|
||||
variable "policies" {
|
||||
description = "Organization policies keyed by parent in format `projects/project-id`, `folders/1234567890` or `organizations/1234567890`."
|
||||
type = map(map(object({
|
||||
inherit_from_parent = optional(bool) # List policy only.
|
||||
reset = optional(bool)
|
||||
rules = optional(
|
||||
list(object({
|
||||
allow = optional(list(string)) # List policy only. Stands for `allow_all` if set to empty list `[]` or to `values.allowed_values` if set to a list of values
|
||||
deny = optional(list(string)) # List policy only. Stands for `deny_all` if set to empty list `[]` or to `values.denied_values` if set to a list of values
|
||||
enforce = optional(bool) # Boolean policy only.
|
||||
condition = optional(
|
||||
object({
|
||||
description = optional(string)
|
||||
expression = optional(string)
|
||||
location = optional(string)
|
||||
title = optional(string)
|
||||
})
|
||||
)
|
||||
}))
|
||||
)
|
||||
})))
|
||||
default = {}
|
||||
}
|
|
@ -0,0 +1,89 @@
|
|||
# Copyright 2022 Google LLC
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
def test_org_policy_simple(plan_runner):
|
||||
"Test vpc with no extra options."
|
||||
org_policies = (
|
||||
'{'
|
||||
'"folders/1234567890" = {'
|
||||
' "constraints/iam.disableServiceAccountKeyUpload" = {'
|
||||
' rules = ['
|
||||
' {'
|
||||
' enforce = true,'
|
||||
' }'
|
||||
' ]'
|
||||
' }'
|
||||
' },'
|
||||
' "organizations/1234567890" = {'
|
||||
' "run.allowedIngress" = {'
|
||||
' rules = ['
|
||||
' {'
|
||||
' allow = ["internal"],'
|
||||
' condition = {'
|
||||
' description= "allow ingress",'
|
||||
' expression = "resource.matchTag(\'123456789/environment\', \'prod\')",'
|
||||
' title = "allow-for-prod-org"'
|
||||
' }'
|
||||
' }'
|
||||
' ]'
|
||||
' }'
|
||||
' }'
|
||||
'}'
|
||||
)
|
||||
_, resources = plan_runner(
|
||||
policies = org_policies
|
||||
)
|
||||
assert len(resources) == 2
|
||||
|
||||
org_policy = [r for r in resources if r["values"]
|
||||
["name"].endswith('iam.disableServiceAccountKeyUpload')][0]["values"]
|
||||
assert org_policy["parent"] == "folders/1234567890"
|
||||
assert org_policy["spec"][0]["rules"][0]["enforce"] == "TRUE"
|
||||
|
||||
|
||||
def test_org_policy_factory(plan_runner):
|
||||
"Test yaml based configuration"
|
||||
_, resources = plan_runner(
|
||||
config_directory="./policies",
|
||||
)
|
||||
assert len(resources) == 5
|
||||
|
||||
org_policy = [r for r in resources if r["values"]
|
||||
["name"].endswith('run.allowedIngress')][0]["values"]["spec"][0]
|
||||
assert org_policy["inherit_from_parent"] == True
|
||||
assert org_policy["rules"][0]["condition"][0]["title"] == "allow-for-prod"
|
||||
assert set(org_policy["rules"][0]["values"][0]["allowed_values"]) == set(["internal"])
|
||||
|
||||
|
||||
def test_combined_org_policy_config(plan_runner):
|
||||
"Test combined (yaml, hcl) policy configuration"
|
||||
org_policies = (
|
||||
'{'
|
||||
'"folders/3456789012" = {'
|
||||
' "constraints/iam.disableServiceAccountKeyUpload" = {'
|
||||
' rules = ['
|
||||
' {'
|
||||
' enforce = true'
|
||||
' }'
|
||||
' ]'
|
||||
' }'
|
||||
' }'
|
||||
'}'
|
||||
)
|
||||
_, resources = plan_runner(
|
||||
config_directory="./policies",
|
||||
policies = org_policies
|
||||
)
|
||||
|
||||
assert len(resources) == 6
|
Loading…
Reference in New Issue