From a7d7296068ddaecc352970f06e5574fd14552cdd Mon Sep 17 00:00:00 2001 From: Ryan Cartwright Date: Thu, 25 Sep 2025 14:51:31 +1000 Subject: [PATCH] fix: improve GCP user experience and documentation --- cdn/README.md | 64 +++++++++++++++++ cdn/manifest.yaml | 11 +++ cdn/module/main.tf | 13 ++-- cdn/module/providers.tf | 16 ++++- cdn/module/variables.tf | 21 ++++-- cloudrun/README.md | 82 ++++++++++++++++++++++ cloudrun/manifest.yaml | 5 +- cloudrun/module/main.tf | 17 +++-- cloudrun/module/providers.tf | 14 +++- cloudrun/module/variables.tf | 8 ++- serviceaccount/README.md | 106 +++++++++++++++++++++++++++++ serviceaccount/module/main.tf | 20 ++++++ serviceaccount/module/providers.tf | 9 ++- storage/README.md | 104 ++++++++++++++++++++++++++++ storage/module/providers.tf | 9 ++- 15 files changed, 472 insertions(+), 27 deletions(-) create mode 100644 cdn/README.md create mode 100644 cloudrun/README.md create mode 100644 serviceaccount/README.md create mode 100644 storage/README.md diff --git a/cdn/README.md b/cdn/README.md new file mode 100644 index 0000000..cd9fe6b --- /dev/null +++ b/cdn/README.md @@ -0,0 +1,64 @@ +# GCP CDN Plugin + +Creates a Google Cloud CDN distribution with load balancing, SSL certificates, and DNS configuration for global content delivery. + +## Overview + +This plugin provisions a complete CDN solution using Google Cloud CDN with: + +- Global load balancing with Cloud Load Balancer +- Automatic SSL certificate provisioning via Certificate Manager +- DNS record management in Cloud DNS +- Support for multiple origin types (Cloud Run, Cloud Storage, external) +- Path-based routing capabilities + +## Required Inputs + +| Parameter | Type | Description | +| --------------- | ------ | ----------------------------------------------------------------------------------------------------------------------------------------------- | +| `project_id` | string | Google Cloud Project ID (e.g. `my-project-123`) | +| `region` | string | Google Cloud region (e.g. `us-central1`) | +| `domain_name` | string | Domain name for the CDN (A records will be created) | +| `dns_zone_name` | string | Name of the existing [Cloud DNS](https://registry.terraform.io/providers/hashicorp/google/latest/docs/data-sources/dns_managed_zone#dns_name-2) | + +## Optional Inputs + +| Parameter | Type | Description | Default | +| ------------ | ------ | -------------------------------- | ------- | +| `domain_ttl` | number | TTL for DNS A records in seconds | `300` | + +## Prerequisites + +- Existing Cloud DNS zone in your project +- Enabled APIs: Certificate Manager, DNS, Compute Engine + +## Usage Example + +**Note:** This example shows platform file syntax. You can configure this plugin directly in the Suga Platform Builder UI without writing YAML. + +```yaml +entrypoints: + default: + plugin: "gcp-cdn" + properties: + project_id: "my-project-123" + region: "us-central1" + domain_name: "cdn.example.com" + dns_zone_name: "example-com-zone" + domain_ttl: 300 +``` + +## Features + +- **Global Distribution**: Leverages Google's global network for content delivery +- **Automatic SSL**: Provisions and manages SSL certificates automatically +- **Multi-Origin Support**: Routes traffic to Cloud Run services, Cloud Storage buckets, or external origins +- **DNS Integration**: Automatically configures DNS records in your existing zone +- **Load Balancing**: Built-in load balancing with health checking + +## References + +- [Cloud CDN Documentation](https://cloud.google.com/cdn/docs) +- [Cloud Load Balancing Documentation](https://cloud.google.com/load-balancing/docs) +- [Certificate Manager Documentation](https://cloud.google.com/certificate-manager/docs) +- [Cloud DNS Documentation](https://cloud.google.com/dns/docs) diff --git a/cdn/manifest.yaml b/cdn/manifest.yaml index 3e6a561..2f2e6ef 100644 --- a/cdn/manifest.yaml +++ b/cdn/manifest.yaml @@ -20,5 +20,16 @@ inputs: type: object required: true description: 'CDN domain configuration (e.g. `{"name": "cdn.example.com", "ssl": true}`)' + domain_name: + type: string + required: true + description: "New A records will be created in the hosted zone to establish this domain name for the CDN" + dns_zone_name: + type: string + required: true + description: "The name of the existing Cloud DNS zone that you would like your domain to be configured in" + domain_ttl: + type: number + description: "The time to live (TTL) for the A record created (in seconds). Defaults to 300 seconds" outputs: diff --git a/cdn/module/main.tf b/cdn/module/main.tf index a4fae4e..ea8c9c4 100644 --- a/cdn/module/main.tf +++ b/cdn/module/main.tf @@ -31,7 +31,6 @@ locals { } } - # Enable the required services resource "google_project_service" "required_services" { for_each = toset(local.required_services) @@ -233,7 +232,7 @@ resource "google_compute_url_map" "https_url_map" { # Lookup the Managed Zone for the CDN Domain data "google_dns_managed_zone" "cdn_zone" { - name = var.cdn_domain.zone_name + name = var.dns_zone_name project = var.project_id depends_on = [google_project_service.required_services] @@ -241,20 +240,20 @@ data "google_dns_managed_zone" "cdn_zone" { # Create DNS Records for the CDN resource "google_dns_record_set" "cdn_dns_record" { - name = endswith(var.cdn_domain.domain_name, ".") ? var.cdn_domain.domain_name : "${var.cdn_domain.domain_name}." + name = endswith(var.domain_name, ".") ? var.domain_name : "${var.domain_name}." managed_zone = data.google_dns_managed_zone.cdn_zone.name type = "A" rrdatas = [google_compute_global_address.cdn_ip.address] - ttl = var.cdn_domain.domain_ttl + ttl = var.domain_ttl project = var.project_id } resource "google_dns_record_set" "www_cdn_dns_record" { - name = endswith(var.cdn_domain.domain_name, ".") ? "www.${var.cdn_domain.domain_name}" : "www.${var.cdn_domain.domain_name}." + name = endswith(var.domain_name, ".") ? "www.${var.domain_name}" : "www.${var.domain_name}." managed_zone = data.google_dns_managed_zone.cdn_zone.name type = "A" rrdatas = [google_compute_global_address.cdn_ip.address] - ttl = var.cdn_domain.domain_ttl + ttl = var.domain_ttl project = var.project_id } @@ -265,7 +264,7 @@ resource "google_certificate_manager_certificate" "cdn_cert" { scope = "DEFAULT" managed { - domains = [var.cdn_domain.domain_name] + domains = [var.domain_name] } depends_on = [google_project_service.required_services] diff --git a/cdn/module/providers.tf b/cdn/module/providers.tf index 64a66dd..9182d61 100644 --- a/cdn/module/providers.tf +++ b/cdn/module/providers.tf @@ -1,13 +1,25 @@ terraform { + required_version = ">= 1.0" + required_providers { + google = { + source = "hashicorp/google" + version = "7.4.0" + } + google-beta = { source = "hashicorp/google-beta" - version = "~> 6.17.0" + version = "7.4.0" + } + + random = { + source = "hashicorp/random" + version = "~> 3.1" } corefunc = { source = "northwood-labs/corefunc" - version = "~> 1.4" + version = "2.1.0" } } } diff --git a/cdn/module/variables.tf b/cdn/module/variables.tf index 4502d82..c98586a 100644 --- a/cdn/module/variables.tf +++ b/cdn/module/variables.tf @@ -23,11 +23,18 @@ variable "region" { type = string } -variable "cdn_domain" { - description = "The CDN domain configuration." - type = object({ - domain_name = string - zone_name = string - domain_ttl = optional(number, 300) - }) +variable "domain_name" { + type = string + description = "New A records will be created in the hosted zone to establish this domain name for the CDN" +} + +variable "dns_zone_name" { + type = string + description = "The name of the existing Cloud DNS zone that you would like your domain to be configured in" } + +variable "domain_ttl" { + type = string + description = "The time to live (TTL) for the A record created (in seconds). Defaults to 300 seconds" + default = 300 +} \ No newline at end of file diff --git a/cloudrun/README.md b/cloudrun/README.md new file mode 100644 index 0000000..c3a5425 --- /dev/null +++ b/cloudrun/README.md @@ -0,0 +1,82 @@ +# GCP Cloud Run Plugin + +Deploys containerized applications to Google Cloud Run with automatic scaling, traffic management, and service account integration. + +## Overview + +This plugin provisions Cloud Run services with: + +- Automatic scaling based on traffic +- Configurable CPU, memory, and concurrency limits +- Environment variable management +- Service account integration for secure resource access +- Flexible ingress controls +- Request timeout configuration + +## Required Inputs + +| Parameter | Type | Description | +| ------------ | ------ | --------------------------------------------------------------- | +| `project_id` | string | Google Cloud Project ID (e.g. `my-project-123`) | +| `region` | string | Google Cloud region for service deployment (e.g. `us-central1`) | + +## Optional Inputs + +| Parameter | Type | Description | Default | +| ----------------------- | ----------- | ------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------- | +| `environment` | map(string) | Environment variables (e.g. `{"NODE_ENV": "production", "API_KEY": "secret"}`) | `{}` | +| `memory_mb` | number | Memory allocation in MB | `512` | +| `cpus` | number | CPU allocation | `1` | +| `gpus` | number | GPU allocation | `0` | +| `min_instances` | number | Minimum instances to keep running | `0` | +| `max_instances` | number | Maximum instances that can be created | `10` | +| `container_concurrency` | number | Maximum concurrent requests per instance | `80` | +| `timeout_seconds` | number | Maximum request timeout in seconds | `10` | +| `container_port` | number | Container port number | `9001` | +| `ingress` | string | Traffic ingress setting. Must be one of `INGRESS_TRAFFIC_ALL`, `INGRESS_TRAFFIC_INTERNAL_ONLY`, or `INGRESS_TRAFFIC_INTERNAL_LOAD_BALANCER` | `INGRESS_TRAFFIC_INTERNAL_LOAD_BALANCER` | + +## Prerequisites + +- Container image pushed to Google Container Registry or Artifact Registry +- Service account with appropriate IAM permissions (managed by `gcp-service-account` plugin) + +## Usage Example + +**Note:** This example shows platform file syntax. You can configure this plugin directly in the Suga Platform Builder UI without writing YAML. + +```yaml +services: + api: + plugin: "gcp-cloudrun" + identities: + - plugin: "gcp-service-account" + properties: + project_id: "my-project-123" + region: "us-central1" + environment: + NODE_ENV: "production" + DATABASE_URL: "postgresql://..." + memory_mb: 1024 + cpus: 2 + min_instances: 1 + max_instances: 100 + container_concurrency: 80 + timeout_seconds: 300 + container_port: 8080 + ingress: "INGRESS_TRAFFIC_ALL" +``` + +## Features + +- **Serverless**: Pay only for the resources you use +- **Auto-scaling**: Automatically scales from zero to handle traffic spikes +- **Security**: Integrated with GCP IAM and service accounts +- **Flexibility**: Support for various runtime configurations and constraints +- **Traffic Management**: Multiple ingress options for different security requirements + +## References + +- [Cloud Run Documentation](https://cloud.google.com/run/docs) +- [Cloud Run Pricing](https://cloud.google.com/run/pricing) +- [Cloud Run Quotas and Limits](https://cloud.google.com/run/quotas) +- [Container Runtime Contract](https://cloud.google.com/run/docs/container-contract) diff --git a/cloudrun/manifest.yaml b/cloudrun/manifest.yaml index 815d2de..2e8a58e 100644 --- a/cloudrun/manifest.yaml +++ b/cloudrun/manifest.yaml @@ -12,7 +12,7 @@ runtime: go_module: github.com/nitrictech/plugins/gcp/cloudrun inputs: - environment: + environment_variables: type: map(string) description: 'Cloud Run service environment variables (e.g. `{"NODE_ENV": "production", "API_KEY": "secret"}`)' memory_mb: @@ -47,5 +47,8 @@ inputs: container_port: type: number description: "Container port number (e.g. `9001`)" + ingress: + type: string + description: "The ingress for this Service. Possible values are INGRESS_TRAFFIC_ALL, INGRESS_TRAFFIC_INTERNAL_ONLY, or INGRESS_TRAFFIC_INTERNAL_LOAD_BALANCER" outputs: diff --git a/cloudrun/module/main.tf b/cloudrun/module/main.tf index b14b77a..ac55d5e 100644 --- a/cloudrun/module/main.tf +++ b/cloudrun/module/main.tf @@ -84,11 +84,13 @@ resource "google_cloud_run_v2_service" "service" { location = var.region project = var.project_id launch_stage = "GA" - ingress = "INGRESS_TRAFFIC_INTERNAL_LOAD_BALANCER" + ingress = var.ingress deletion_protection = false template { + max_instance_request_concurrency = var.container_concurrency + scaling { min_instance_count = var.min_instances max_instance_count = var.max_instances @@ -97,10 +99,13 @@ resource "google_cloud_run_v2_service" "service" { containers { image = "${local.service_image_url}@${docker_registry_image.push.sha256_digest}" resources { - limits = { - cpu = var.cpus - memory = "${var.memory_mb}Mi" - } + limits = merge( + { + cpu = var.cpus + memory = "${var.memory_mb}Mi" + }, + var.gpus > 0 ? { "nvidia.com/gpu" = var.gpus } : {} + ) } ports { @@ -130,7 +135,7 @@ resource "google_cloud_run_v2_service" "service" { } dynamic "env" { - for_each = merge(var.environment, var.suga.env) + for_each = merge(var.environment_variables, var.suga.env) content { name = env.key value = env.value diff --git a/cloudrun/module/providers.tf b/cloudrun/module/providers.tf index f67116c..4be8f28 100644 --- a/cloudrun/module/providers.tf +++ b/cloudrun/module/providers.tf @@ -1,8 +1,20 @@ terraform { + required_version = ">= 1.0" + required_providers { + google = { + source = "hashicorp/google" + version = "7.4.0" + } + + random = { + source = "hashicorp/random" + version = "~> 3.1" + } + docker = { source = "kreuzwerker/docker" - version = "3.6.0" + version = "3.6.2" } } } \ No newline at end of file diff --git a/cloudrun/module/variables.tf b/cloudrun/module/variables.tf index cf55c99..a411796 100644 --- a/cloudrun/module/variables.tf +++ b/cloudrun/module/variables.tf @@ -10,7 +10,7 @@ variable "suga" { }) } -variable "environment" { +variable "environment_variables" { type = map(string) description = "Environment variables to set on the lambda function" default = {} @@ -72,4 +72,10 @@ variable "container_port" { description = "The port to expose the CloudRun service to" type = number default = 9001 +} + +variable "ingress" { + description = "The ingress for this Service. Possible values are INGRESS_TRAFFIC_ALL, INGRESS_TRAFFIC_INTERNAL_ONLY, or INGRESS_TRAFFIC_INTERNAL_LOAD_BALANCER" + type = string + default = "INGRESS_TRAFFIC_INTERNAL_LOAD_BALANCER" } \ No newline at end of file diff --git a/serviceaccount/README.md b/serviceaccount/README.md new file mode 100644 index 0000000..3cb4056 --- /dev/null +++ b/serviceaccount/README.md @@ -0,0 +1,106 @@ +# GCP Service Account Plugin + +Creates and manages Google Cloud IAM service accounts with customizable permissions for secure resource access. + +## Overview + +This plugin provisions IAM service accounts with: + +- Fine-grained permission management via trusted actions +- Automatic key generation and rotation support +- Integration with other GCP services +- Principle of least privilege enforcement +- Support for custom IAM roles and predefined roles + +## Required Inputs + +| Parameter | Type | Description | +| ------------ | ------ | ----------------------- | +| `project_id` | string | Google Cloud Project ID | + +## Optional Inputs + +| Parameter | Type | Description | Default | +| ----------------- | ------------ | ------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------ | +| `trusted_actions` | list(string) | IAM actions/roles this service account can perform (e.g. `["storage.objects.get", "storage.objects.create", "cloudsql.instances.connect"]`) | `["monitoring.timeSeries.create", "resourcemanager.projects.get"]` | + +## Prerequisites + +- Sufficient IAM permissions to create service accounts and assign roles +- Enabled APIs for the services the account will access + +## Usage Examples + +**Note:** This example shows platform file syntax. You can configure this plugin directly in the Suga Platform Builder UI without writing YAML. + +### Basic Storage Access + +```yaml +services: + storage-worker: + plugin: "gcp-cloudrun" + identities: + - plugin: "gcp-service-account" + properties: + project_id: "my-project-123" + trusted_actions: + - "storage.objects.get" + - "storage.objects.create" + - "storage.objects.delete" +``` + +### Full Storage and Cloud SQL Access + +```yaml +services: + backend: + plugin: "gcp-cloudrun" + identities: + - plugin: "gcp-service-account" + properties: + project_id: "my-project-123" + trusted_actions: + - "roles/storage.admin" + - "roles/cloudsql.client" + - "secretmanager.versions.access" +``` + +### Read-Only Monitoring Access + +```yaml +services: + monitoring: + plugin: "gcp-cloudrun" + identities: + - plugin: "gcp-service-account" + properties: + project_id: "my-project-123" + trusted_actions: + - "roles/monitoring.viewer" + - "roles/logging.viewer" +``` + +## Common IAM Actions + +| Action/Role | Description | +| ------------------------------- | ------------------------------- | +| `storage.objects.get` | Read objects from Cloud Storage | +| `storage.objects.create` | Create objects in Cloud Storage | +| `roles/storage.admin` | Full Storage access | +| `roles/cloudsql.client` | Connect to Cloud SQL instances | +| `secretmanager.versions.access` | Access Secret Manager secrets | +| `roles/monitoring.viewer` | View monitoring data | + +## Security Best Practices + +- Use the minimum required permissions +- Regularly audit and rotate service account keys +- Use workload identity when possible for GKE workloads +- Avoid downloading service account keys when not necessary + +## References + +- [IAM Service Accounts Documentation](https://cloud.google.com/iam/docs/service-accounts) +- [Understanding Service Accounts](https://cloud.google.com/iam/docs/understanding-service-accounts) +- [IAM Permissions Reference](https://cloud.google.com/iam/docs/permissions-reference) +- [Service Account Best Practices](https://cloud.google.com/iam/docs/best-practices-service-accounts) diff --git a/serviceaccount/module/main.tf b/serviceaccount/module/main.tf index 18f72fa..b369623 100644 --- a/serviceaccount/module/main.tf +++ b/serviceaccount/module/main.tf @@ -28,4 +28,24 @@ resource "google_service_account" "service_account" { description = "Service account which runs the ${var.suga.name}" depends_on = [google_project_service.required_services] +} + +# Create custom role for trusted actions +resource "google_project_iam_custom_role" "trusted_actions_role" { + count = length(var.trusted_actions) > 0 ? 1 : 0 + + role_id = "${replace(local.service_account_name, "-", "_")}_trusted_actions" + title = "${var.suga.name} Trusted Actions Role" + description = "Custom role for trusted actions for ${var.suga.name}" + permissions = var.trusted_actions + project = var.project_id +} + +# Bind the custom role to the service account +resource "google_project_iam_member" "trusted_actions_binding" { + count = length(var.trusted_actions) > 0 ? 1 : 0 + + project = var.project_id + role = google_project_iam_custom_role.trusted_actions_role[0].name + member = "serviceAccount:${google_service_account.service_account.email}" } \ No newline at end of file diff --git a/serviceaccount/module/providers.tf b/serviceaccount/module/providers.tf index 1a895e0..31f09dd 100644 --- a/serviceaccount/module/providers.tf +++ b/serviceaccount/module/providers.tf @@ -1,8 +1,15 @@ terraform { + required_version = ">= 1.0" + required_providers { + google = { + source = "hashicorp/google" + version = "7.4.0" + } + corefunc = { source = "northwood-labs/corefunc" - version = "~> 1.4" + version = "2.1.0" } } } diff --git a/storage/README.md b/storage/README.md new file mode 100644 index 0000000..a908247 --- /dev/null +++ b/storage/README.md @@ -0,0 +1,104 @@ +# GCP Storage Plugin + +Provisions Google Cloud Storage buckets with configurable storage classes, access controls, and automatic file upload capabilities. + +## Overview + +This plugin creates Cloud Storage buckets with: + +- Multiple storage class options for cost optimization +- Configurable access controls and permissions +- Automatic file upload from local directories +- Integration with other GCP services +- Lifecycle management support +- Regional or multi-regional deployment options + +## Required Inputs + +| Parameter | Type | Description | +| ------------ | ------ | ----------------------------------------------------------- | +| `project_id` | string | Google Cloud Project ID (e.g. `my-project-123`) | +| `region` | string | Google Cloud region for storage bucket (e.g. `us-central1`) | + +## Optional Inputs + +| Parameter | Type | Description | Default | +| --------------- | ------ | -------------------------------------------------- | ---------- | +| `storage_class` | string | Performance and durability tier for bucket objects | `STANDARD` | + +## Storage Classes + +| Class | Use Case | Access Frequency | Min Storage Duration | +| ---------- | -------------------------- | ---------------- | -------------------- | +| `STANDARD` | Frequently accessed data | Daily/weekly | None | +| `NEARLINE` | Infrequently accessed data | Monthly | 30 days | +| `COLDLINE` | Rarely accessed data | Quarterly | 90 days | +| `ARCHIVE` | Long-term archival | Yearly | 365 days | + +## Prerequisites + +- Enabled Cloud Storage API +- Sufficient IAM permissions to create buckets + +## Usage Examples + +**Note:** This example shows platform file syntax. You can configure this plugin directly in the Suga Platform Builder UI without writing YAML. + +### Standard Storage for Frequent Access + +```yaml +buckets: + uploads: + plugin: "gcp-storage-bucket" + properties: + project_id: "my-project-123" + region: "us-central1" + storage_class: "STANDARD" +``` + +### Archive Storage for Long-term Backup + +```yaml +buckets: + backups: + plugin: "gcp-storage-bucket" + properties: + project_id: "my-project-123" + region: "us-central1" + storage_class: "ARCHIVE" +``` + +### Nearline Storage for Monthly Reports + +```yaml +buckets: + reports: + plugin: "gcp-storage-bucket" + properties: + project_id: "my-project-123" + region: "us-central1" + storage_class: "NEARLINE" +``` + +## Features + +- **Cost Optimization**: Choose the right storage class for your access patterns +- **Global Accessibility**: Access data from anywhere with internet connectivity +- **Automatic Upload**: Seamlessly upload files during deployment +- **Security**: Integration with GCP IAM for access control +- **Scalability**: Virtually unlimited storage capacity +- **Durability**: 99.999999999% (11 9's) annual durability + +## Storage Class Selection Guide + +- **STANDARD**: Web content, streaming videos, mobile apps +- **NEARLINE**: Monthly backups, data analytics workloads +- **COLDLINE**: Quarterly backups, disaster recovery +- **ARCHIVE**: Regulatory compliance, long-term preservation + +## References + +- [Cloud Storage Documentation](https://cloud.google.com/storage/docs) +- [Storage Classes and Pricing](https://cloud.google.com/storage/docs/storage-classes) +- [Bucket Locations](https://cloud.google.com/storage/docs/locations) +- [Cloud Storage Pricing](https://cloud.google.com/storage/pricing) diff --git a/storage/module/providers.tf b/storage/module/providers.tf index 1a895e0..31f09dd 100644 --- a/storage/module/providers.tf +++ b/storage/module/providers.tf @@ -1,8 +1,15 @@ terraform { + required_version = ">= 1.0" + required_providers { + google = { + source = "hashicorp/google" + version = "7.4.0" + } + corefunc = { source = "northwood-labs/corefunc" - version = "~> 1.4" + version = "2.1.0" } } }