forked from GoogleCloudPlatform/cloud-foundation-fabric
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
New looker core module (GoogleCloudPlatform#2565)
* new looker core module --------- Co-authored-by: Wiktor Niesiobędzki <[email protected]>
- Loading branch information
1 parent
4bacbf5
commit 1f2bdd0
Showing
9 changed files
with
956 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,195 @@ | ||
# Looker Core module | ||
|
||
This module manages the creation of a [Looker Core instance](https://cloud.google.com/looker/docs/looker-core). | ||
|
||
This module accepts Oauth client ID and secret in the input variable `oauth_config` in case you have | ||
already [set up an oauth client and credentials](https://cloud.google.com/looker/docs/looker-core-create-oauth). | ||
If that is not the case it is possible to specify support_email in the same variable `oauth_config` for a default oauth | ||
client id and secret setup within the terraform script, be aware that **such an oauth client id is not suitable for | ||
authenticating end users**, and it is only used to provision the looker core instance. | ||
You'll still be forced to create a new oauth and update the looker core instance from the console (or gcloud) as there | ||
is no terraform support for these resources. | ||
|
||
|
||
> [!WARNING] | ||
> Please be aware that, at the time of this writing, deleting the looker core instance via terraform is not possible due | ||
> to https://github.com/hashicorp/terraform-provider-google/issues/19467. The work-around is to delete the instance from the | ||
> console (or gcloud with force option) and remove the corresponding resource from the terraform state. | ||
<!-- TOC --> | ||
|
||
* [Looker Core module](#looker-core-module) | ||
* [Examples](#examples) | ||
* [Simple example](#simple-example) | ||
* [Looker Core private instance with PSA](#looker-core-private-instance-with-psa) | ||
* [Looker Core full example](#looker-core-full-example) | ||
* [Variables](#variables) | ||
* [Outputs](#outputs) | ||
|
||
<!-- TOC --> | ||
|
||
## Examples | ||
|
||
### Simple example | ||
|
||
This example shows how to set up a public Looker Core instance. | ||
|
||
```hcl | ||
module "looker" { | ||
source = "./fabric/modules/looker-core" | ||
project_id = var.project_id | ||
region = var.region | ||
name = "looker" | ||
network_config = { | ||
public = true | ||
} | ||
oauth_config = { | ||
support_email = "[email protected]" | ||
} | ||
} | ||
# tftest modules=1 resources=3 inventory=simple.yaml | ||
``` | ||
|
||
### Looker Core private instance with PSA | ||
|
||
```hcl | ||
module "project" { | ||
source = "./fabric/modules/project" | ||
billing_account = var.billing_account_id | ||
parent = var.folder_id | ||
name = "looker" | ||
prefix = var.prefix | ||
services = [ | ||
"servicenetworking.googleapis.com", | ||
"looker.googleapis.com", | ||
] | ||
} | ||
module "vpc" { | ||
source = "./fabric/modules/net-vpc" | ||
project_id = module.project.project_id | ||
name = "my-network" | ||
psa_configs = [ | ||
{ | ||
ranges = { looker = "10.60.0.0/16" } | ||
} | ||
] | ||
} | ||
module "looker" { | ||
source = "./fabric/modules/looker-core" | ||
project_id = module.project.project_id | ||
region = var.region | ||
name = "looker" | ||
network_config = { | ||
psa_config = { | ||
network = module.vpc.id | ||
} | ||
} | ||
oauth_config = { | ||
support_email = "[email protected]" | ||
} | ||
platform_edition = "LOOKER_CORE_ENTERPRISE_ANNUAL" | ||
} | ||
# tftest modules=3 resources=16 inventory=psa.yaml | ||
``` | ||
|
||
### Looker Core full example | ||
|
||
```hcl | ||
module "project" { | ||
source = "./fabric/modules/project" | ||
billing_account = var.billing_account_id | ||
parent = var.folder_id | ||
name = "looker" | ||
prefix = var.prefix | ||
services = [ | ||
"cloudkms.googleapis.com", | ||
"iap.googleapis.com", | ||
"looker.googleapis.com", | ||
"servicenetworking.googleapis.com" | ||
] | ||
} | ||
module "vpc" { | ||
source = "./fabric/modules/net-vpc" | ||
project_id = module.project.project_id | ||
name = "my-network" | ||
psa_configs = [ | ||
{ | ||
ranges = { looker = "10.60.0.0/16" } | ||
} | ||
] | ||
} | ||
module "kms" { | ||
source = "./fabric/modules/kms" | ||
project_id = module.project.project_id | ||
keyring = { | ||
location = var.region | ||
name = "keyring" | ||
} | ||
keys = { | ||
"key-regional" = { | ||
} | ||
} | ||
iam = { | ||
"roles/cloudkms.cryptoKeyEncrypterDecrypter" = [ | ||
module.project.service_agents.looker.iam_email | ||
] | ||
} | ||
} | ||
module "looker" { | ||
source = "./fabric/modules/looker-core" | ||
project_id = module.project.project_id | ||
region = var.region | ||
name = "looker" | ||
admin_settings = { | ||
allowed_email_domains = ["google.com"] | ||
} | ||
encryption_config = { | ||
kms_key_name = module.kms.keys.key-regional.id | ||
} | ||
network_config = { | ||
psa_config = { | ||
network = module.vpc.id | ||
} | ||
} | ||
oauth_config = { | ||
client_id = "xxxxxxxxx" | ||
client_secret = "xxxxxxxx" | ||
} | ||
platform_edition = "LOOKER_CORE_ENTERPRISE_ANNUAL" | ||
} | ||
# tftest modules=4 resources=22 inventory=full.yaml | ||
``` | ||
<!-- BEGIN TFDOC --> | ||
## Variables | ||
|
||
| name | description | type | required | default | | ||
|---|---|:---:|:---:|:---:| | ||
| [name](variables.tf#L85) | Name of the looker core instance. | <code>string</code> | ✓ | | | ||
| [network_config](variables.tf#L90) | Network configuration for cluster and instance. Only one between psa_config and psc_config can be used. | <code title="object({ psa_config = optional(object({ network = string allocated_ip_range = optional(string) enable_public_ip = optional(bool, false) enable_private_ip = optional(bool, true) })) public = optional(bool, false) })">object({…})</code> | ✓ | | | ||
| [oauth_config](variables.tf#L108) | Looker Core Oauth config. Either client ID and secret (existing oauth client) or support email (temporary internal oauth client setup) must be specified. | <code title="object({ client_id = optional(string, null) client_secret = optional(string, null) support_email = optional(string, null) })">object({…})</code> | ✓ | | | ||
| [project_id](variables.tf#L141) | The ID of the project where this instances will be created. | <code>string</code> | ✓ | | | ||
| [region](variables.tf#L146) | Region for the Looker core instance. | <code>string</code> | ✓ | | | ||
| [admin_settings](variables.tf#L17) | Looker Core admins settings. | <code title="object({ allowed_email_domains = list(string) })">object({…})</code> | | <code>null</code> | | ||
| [encryption_config](variables.tf#L26) | Set encryption configuration. KMS name format: 'projects/[PROJECT]/locations/[REGION]/keyRings/[RING]/cryptoKeys/[KEY_NAME]'. | <code title="object({ kms_key_name = string })">object({…})</code> | | <code>null</code> | | ||
| [maintenance_config](variables.tf#L35) | Set maintenance window configuration and maintenance deny period (up to 90 days). Date format: 'yyyy-mm-dd'. | <code title="object({ maintenance_window = optional(object({ day = optional(string, "SUNDAY") start_time = optional(object({ hours = optional(number, 23) minutes = optional(number, 0) seconds = optional(number, 0) nanos = optional(number, 0) }), {}) }), null) deny_maintenance_period = optional(object({ start_date = object({ year = number month = number day = number }) end_date = object({ year = number month = number day = number }) start_time = optional(object({ hours = optional(number, 23) minutes = optional(number, 0) seconds = optional(number, 0) nanos = optional(number, 0) }), {}) }), null) })">object({…})</code> | | <code>{}</code> | | ||
| [platform_edition](variables.tf#L121) | Platform editions for a Looker instance. Each edition maps to a set of instance features, like its size. | <code>string</code> | | <code>"LOOKER_CORE_TRIAL"</code> | | ||
| [prefix](variables.tf#L131) | Optional prefix used to generate instance names. | <code>string</code> | | <code>null</code> | | ||
|
||
## Outputs | ||
|
||
| name | description | sensitive | | ||
|---|---|:---:| | ||
| [egress_public_ip](outputs.tf#L17) | Public IP address of Looker instance for egress. | | | ||
| [id](outputs.tf#L22) | Fully qualified primary instance id. | | | ||
| [ingress_private_ip](outputs.tf#L27) | Private IP address of Looker instance for ingress. | | | ||
| [ingress_public_ip](outputs.tf#L32) | Public IP address of Looker instance for ingress. | | | ||
| [instance](outputs.tf#L37) | Looker Core instance resource. | ✓ | | ||
| [instance_name](outputs.tf#L43) | Name of the looker instance. | | | ||
| [looker_uri](outputs.tf#L48) | Looker core URI. | | | ||
| [looker_version](outputs.tf#L53) | Looker core version. | | | ||
<!-- END TFDOC --> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,108 @@ | ||
/** | ||
* Copyright 2024 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 { | ||
bootstrap_oauth_client = var.oauth_config.client_secret == null || var.oauth_config.client_id == null | ||
looker_instance_name = "${local.prefix}${var.name}" | ||
oauth_client_id = local.bootstrap_oauth_client ? google_iap_client.looker_client[0].client_id : var.oauth_config.client_id | ||
oauth_client_secret = local.bootstrap_oauth_client ? google_iap_client.looker_client[0].secret : var.oauth_config.client_secret | ||
prefix = var.prefix == null ? "" : "${var.prefix}-" | ||
} | ||
|
||
resource "google_looker_instance" "looker" { | ||
project = var.project_id | ||
name = local.looker_instance_name | ||
consumer_network = try(var.network_config.psa_config.network, null) | ||
platform_edition = var.platform_edition | ||
private_ip_enabled = try(var.network_config.psa_config.enable_private_ip, null) | ||
public_ip_enabled = coalesce(var.network_config.public, false) || try(var.network_config.psa_config.enable_public_ip, false) | ||
region = var.region | ||
reserved_range = try(var.network_config.psa_config.allocated_ip_range, null) | ||
|
||
oauth_config { | ||
client_id = local.oauth_client_id | ||
client_secret = local.oauth_client_secret | ||
} | ||
|
||
dynamic "admin_settings" { | ||
for_each = var.admin_settings != null ? [""] : [] | ||
content { | ||
allowed_email_domains = var.admin_settings.allowed_email_domains | ||
} | ||
} | ||
dynamic "deny_maintenance_period" { | ||
for_each = var.maintenance_config.deny_maintenance_period != null ? [1] : [] | ||
content { | ||
start_date { | ||
year = var.maintenance_config.deny_maintenance_period.start_date.year | ||
month = var.maintenance_config.deny_maintenance_period.start_date.month | ||
day = var.maintenance_config.deny_maintenance_period.start_date.day | ||
} | ||
end_date { | ||
year = var.maintenance_config.deny_maintenance_period.start_date.year | ||
month = var.maintenance_config.deny_maintenance_period.start_date.month | ||
day = var.maintenance_config.deny_maintenance_period.start_date.day | ||
} | ||
time { | ||
hours = var.maintenance_config.deny_maintenance_period.start_times.hours | ||
minutes = var.maintenance_config.deny_maintenance_period.start_times.minutes | ||
seconds = var.maintenance_config.deny_maintenance_period.start_times.seconds | ||
nanos = var.maintenance_config.deny_maintenance_period.start_times.nanos | ||
} | ||
} | ||
} | ||
dynamic "encryption_config" { | ||
for_each = var.encryption_config != null ? [""] : [] | ||
content { | ||
kms_key_name = var.encryption_config.kms_key_name | ||
} | ||
} | ||
dynamic "maintenance_window" { | ||
for_each = var.maintenance_config.maintenance_window != null ? [""] : [] | ||
content { | ||
day_of_week = var.maintenance_config.maintenance_window.day | ||
start_time { | ||
hours = var.maintenance_config.maintenance_window.start_times.hours | ||
minutes = var.maintenance_config.maintenance_window.start_times.minutes | ||
seconds = var.maintenance_config.maintenance_window.start_times.seconds | ||
nanos = var.maintenance_config.maintenance_window.start_times.nanos | ||
} | ||
} | ||
} | ||
lifecycle { | ||
ignore_changes = [ | ||
oauth_config # do not replace target oauth client updated on the console with default one | ||
] | ||
} | ||
} | ||
|
||
|
||
# Only "Organization Internal" brands can be created programmatically via API. To convert it into an external brands please use the GCP Console. | ||
resource "google_iap_brand" "looker_brand" { | ||
count = local.bootstrap_oauth_client ? 1 : 0 | ||
support_email = var.oauth_config.support_email | ||
# application_title = "Looker Core Application" | ||
application_title = "Cloud IAP protected Application" | ||
project = var.project_id | ||
} | ||
|
||
# Only internal org clients can be created via declarative tools. External clients must be manually created via the GCP console. | ||
# This is a temporary IAP oauth client to be replaced after Looker Core is provisioned. | ||
resource "google_iap_client" "looker_client" { | ||
count = local.bootstrap_oauth_client ? 1 : 0 | ||
display_name = "Looker Core default oauth client." | ||
brand = google_iap_brand.looker_brand[0].name | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
/** | ||
* Copyright 2024 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 "egress_public_ip" { | ||
description = "Public IP address of Looker instance for egress." | ||
value = google_looker_instance.looker.egress_public_ip | ||
} | ||
|
||
output "id" { | ||
description = "Fully qualified primary instance id." | ||
value = google_looker_instance.looker.id | ||
} | ||
|
||
output "ingress_private_ip" { | ||
description = "Private IP address of Looker instance for ingress." | ||
value = google_looker_instance.looker.ingress_private_ip | ||
} | ||
|
||
output "ingress_public_ip" { | ||
description = "Public IP address of Looker instance for ingress." | ||
value = google_looker_instance.looker.ingress_public_ip | ||
} | ||
|
||
output "instance" { | ||
description = "Looker Core instance resource." | ||
value = google_looker_instance.looker.id | ||
sensitive = true | ||
} | ||
|
||
output "instance_name" { | ||
description = "Name of the looker instance." | ||
value = google_looker_instance.looker.name | ||
} | ||
|
||
output "looker_uri" { | ||
description = "Looker core URI." | ||
value = google_looker_instance.looker.looker_uri | ||
} | ||
|
||
output "looker_version" { | ||
description = "Looker core version." | ||
value = google_looker_instance.looker.looker_version | ||
} |
Oops, something went wrong.