Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions examples/terraform/gcp/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# GCP Deployment using Terraform

This example shows how to deploy the complete platform stack inside GCP.

The platform services are deployed as kubernetes services to GKE.

All endpoints are protected by Cloud IAP.

Note that it is not possible to do the deployment in one
step.

1. Fill out the `terraform.tfvars`
2. `terraform apply -target=module.bootstrap`
3. Upload platform images to created Artifact Registry instance
4. `terraform apply`
5. Point your DNS entry towards the created public IP address
12 changes: 12 additions & 0 deletions examples/terraform/gcp/data.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# Uncomment to choose an explicit project and region.
# provider "google" {
# project = var.project_id
# region = var.region
# }

data "google_container_cluster" "platform_gke_cluster" {
name = module.bootstrap.cluster_name
location = module.bootstrap.cluster_location
}

data "google_client_config" "default" {}
142 changes: 142 additions & 0 deletions examples/terraform/gcp/iap.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@

locals {
tenzir_platform_domain_without_protocol = replace(var.tenzir_platform_domain, "/^(https?|wss?):\\/\\//", "")
tenzir_platform_api_endpoint_without_protocol = replace(var.tenzir_platform_api_endpoint, "/^(https?|wss?):\\/\\//", "")
tenzir_platform_nodes_endpoint_without_protocol = replace(var.tenzir_platform_control_endpoint, "/^(https?|wss?):\\/\\//", "")
}

resource "google_compute_global_address" "platform_ip" {
name = "platform-ingress-ip"
}

resource "kubernetes_manifest" "app_managed_certificate" {
manifest = {
apiVersion = "networking.gke.io/v1"
kind = "ManagedCertificate"
metadata = {
name = "app-managed-certificate"
namespace = "default"
}
spec = {
domains = [local.tenzir_platform_domain_without_protocol]
}
}
}

resource "kubernetes_manifest" "platform_managed_certificate" {
manifest = {
apiVersion = "networking.gke.io/v1"
kind = "ManagedCertificate"
metadata = {
name = "platform-managed-certificate"
namespace = "default"
}
spec = {
domains = [local.tenzir_platform_api_endpoint_without_protocol]
}
}
}


resource "kubernetes_manifest" "gateway_managed_certificate" {
manifest = {
apiVersion = "networking.gke.io/v1"
kind = "ManagedCertificate"
metadata = {
name = "gateway-managed-certificate"
namespace = "default"
}
spec = {
domains = [local.tenzir_platform_nodes_endpoint_without_protocol]
}
}
}

resource "kubernetes_ingress_v1" "platform_ingress_iap" {
metadata {
name = "platform-ingress-iap"
namespace = "default"
annotations = {
"kubernetes.io/ingress.class" = "gce"
"kubernetes.io/ingress.global-static-ip-name" = google_compute_global_address.platform_ip.name
"networking.gke.io/managed-certificates" = join(",", [
kubernetes_manifest.app_managed_certificate.manifest.metadata.name,
kubernetes_manifest.platform_managed_certificate.manifest.metadata.name,
kubernetes_manifest.gateway_managed_certificate.manifest.metadata.name,
])
}
}
spec {
rule {
host = local.tenzir_platform_domain_without_protocol
http {
path {
path = "/*"
backend {
service {
name = kubernetes_service.app_service.metadata[0].name
port {
number = 3000
}
}
}
}
}
}
rule {
host = local.tenzir_platform_api_endpoint_without_protocol
http {
path {
path = "/*"
backend {
service {
name = kubernetes_service.platform_service.metadata[0].name
port {
number = 5000
}
}
}
}
}
}
rule {
host = local.tenzir_platform_nodes_endpoint_without_protocol
http {
path {
path = "/*"
backend {
service {
name = kubernetes_service.websocket_gateway_service.metadata[0].name
port {
number = 5001
}
}
}
}
}
}
}

depends_on = [ kubernetes_manifest.app_backend_config ]
}

resource "google_compute_firewall" "allow_gke_traffic" {
name = "allow-iap-traffic-ingress"
network = module.bootstrap.network_id
description = "Allow inbound traffic to GKE cluster from specified CIDRs"

direction = "INGRESS"

# The inbound IP ranges for Google IAP, as provided in their
# documentation.
source_ranges = [
"130.211.0.0/22",
"35.191.0.0/16",
]

allow {
protocol = "tcp"
}
}


28 changes: 28 additions & 0 deletions examples/terraform/gcp/iap_custom_oauth_client.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# A project can only have a single brand, and the brand for a custom IAP oauth
# provider must be 'Internal'.

resource "google_iap_brand" "project_brand" {
# Note: This must be changed to a valid email address of
# a person with access to this project.
support_email = "[email protected]"
application_title = "Cloud IAP protected Application"
project = data.google_client_config.default.project
}
resource "google_iap_client" "project_client" {
display_name = "IAP OAuth Client"
brand = google_iap_brand.project_brand.name
}

resource "kubernetes_secret" "iap_oauth_secret" {
metadata {
name = "iap-oauth-credentials"
namespace = "default"
}

data = {
client_id = google_iap_client.project_client.client_id
client_secret = google_iap_client.project_client.secret
}

type = "Opaque"
}
96 changes: 96 additions & 0 deletions examples/terraform/gcp/inputs.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
variable "tenzir_platform_version" {
description = "The version tag for Tenzir platform images (e.g., 'latest')"
type = string
default = "latest"
}

variable "tenzir_platform_domain" {
description = "The public domain for the Tenzir platform"
type = string
}

variable "tenzir_platform_control_endpoint" {
description = "The public websocket gateway endpoint"
type = string
}

variable "tenzir_platform_api_endpoint" {
description = "The public API endpoint for the Tenzir platform"
type = string
}

variable "tenzir_platform_blobs_endpoint" {
description = "The public endpoint for blob storage"
type = string
}

variable "tenzir_platform_demo_node_image" {
description = "Image for Tenzir demo nodes"
type = string
default = "tenzir/tenzir-demo:latest"
}

variable "tenzir_platform_store_type" {
description = "The store type for Tenzir platform (e.g., 'postgres')"
type = string
default = "postgres"
}

variable "tenzir_platform_internal_bucket_name" {
description = "Internal bucket name for blob storage"
type = string
default = "tenzir-platform-bucket"
}

variable "tenzir_platform_postgres_db" {
description = "PostgreSQL database name"
type = string
}

variable "tenzir_platform_postgres_user" {
description = "PostgreSQL username"
type = string
}

variable "blob_storage_access_key_id" {
description = "Access key ID for blob storage"
type = string
}

# Sensitive variables - these should be provided securely (e.g., via .tfvars file not committed to VCS, or environment variables)

variable "private_oidc_provider_client_secret" {
description = "Client secret for OIDC provider"
type = string
sensitive = true
}

variable "tenzir_platform_internal_app_api_key" {
description = "Internal API key for the app"
type = string
sensitive = true
}

variable "tenzir_platform_internal_auth_secret" {
description = "Internal authentication secret"
type = string
sensitive = true
}

variable "tenzir_platform_postgres_password" {
description = "PostgreSQL password"
type = string
sensitive = true
}

variable "tenant_manager_tenant_token_encryption_key" {
description = "Encryption key for tenant tokens"
type = string
sensitive = true
}

variable "blob_storage_secret_access_key" {
description = "Secret access key for blob storage"
type = string
sensitive = true
}
28 changes: 28 additions & 0 deletions examples/terraform/gcp/kubernetes.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
module "bootstrap" {
source = "./modules/bootstrap"
}

provider "kubernetes" {
host = "https://${data.google_container_cluster.platform_gke_cluster.endpoint}"
token = data.google_client_config.default.access_token
cluster_ca_certificate = base64decode(data.google_container_cluster.platform_gke_cluster.master_auth[0].cluster_ca_certificate)
}

resource "kubernetes_secret" "tenzir_platform_secrets" {
metadata {
name = "tenzir-platform-secrets"
namespace = "default"
}
type = "Opaque"
data = {
"PRIVATE_OIDC_PROVIDER_CLIENT_SECRET" = var.private_oidc_provider_client_secret
"TENZIR_PLATFORM_INTERNAL_APP_API_KEY" = var.tenzir_platform_internal_app_api_key
"AUTH_SECRET" = var.tenzir_platform_internal_auth_secret
"TENZIR_PLATFORM_POSTGRES_USER" = var.tenzir_platform_postgres_user
"TENZIR_PLATFORM_POSTGRES_PASSWORD" = var.tenzir_platform_postgres_password
"TENANT_MANAGER_TENANT_TOKEN_ENCRYPTION_KEY" = var.tenant_manager_tenant_token_encryption_key
"BLOB_STORAGE_ACCESS_KEY_ID" = var.blob_storage_access_key_id
"BLOB_STORAGE_SECRET_ACCESS_KEY" = var.blob_storage_secret_access_key
}
}

Loading