App Common Shared Library
The App Common module is a collection of reusable, shared Terraform modules that serve as the foundation for the RAD Modules ecosystem. It provides the core infrastructure logic used by platform-specific modules like App CloudRun and App GKE.
1. Overview
Purpose: To enforce consistency, reduce code duplication, and standardize infrastructure patterns across different compute platforms (Serverless vs. Kubernetes).
Architecture:
- Layer 1 (App Common): Abstracted infrastructure components (Networking, Databases, Storage, IAM).
- Layer 2 (Platform):
App CloudRunandApp GKEcompose these components to build a complete deployment environment. - Layer 3 (Application): Wrappers (e.g.,
Cyclos CloudRun) instantiate the platform layer with specific app configurations.
2. Module Reference
The functional logic is encapsulated in submodules located in modules/App_Common/modules/. The top-level .tf files at modules/App_Common/ provide shared infrastructure resources consumed by the platform layer.
Top-Level Shared Resources
| File | Purpose |
|---|---|
buildappcontainer.tf | Creates local_file.app_dockerfile (when dockerfile_content is provided) and local_file.app_cloudbuild, then drives the application image build via terraform_data.build_and_push_application_image, which invokes scripts/build-container.sh. Replacement is triggered by hashes of the build script, Dockerfile, context directory, repository ID, tag, and build args. All three resources are gated on local.enable_custom_build. The cloudbuild.yaml template at the top level accepts PROJECT_ID, APP_NAME, IMAGE_REGION, IMAGE_NAME, IMAGE_VERSION, REPO_NAME, REPO_PROJECT_ID, REPO_LOCATION, DOCKERFILE, CONTEXT_PATH, and BUILD_ARGS — it does not include DOCKERFILE_CONTENT or CLOUDBUILD_SA (those variables are only used in the app_build submodule's template). |
sql.tf | Discovers an existing Cloud SQL instance via scripts/get-sqlserver-info.sh, computes the canonical ${instance}-root-password Secret Manager secret ID, and provisions the secret container plus a 32-char random_password version when the secret does not already exist in Secret Manager. Uses data.google_secret_manager_secret to detect whether Services GCP has already created it before deciding whether to create one. Note: the generated password is stored in Terraform state via random_password; callers can also retrieve it at runtime via gcloud secrets versions access. |
registry.tf | Discovers an existing Artifact Registry repository via scripts/discover_repo.sh (using data.external). The google_artifact_registry_repository data source is gated on `enable_custom_build |
network.tf | Delegates VPC network discovery to the app_networking submodule (modules/App_Common/modules/app_networking) and surfaces its outputs as locals (network_exists, discovered_regions, available_regions, subnet_names, subnet_cidrs, subnet_details, region_to_subnet). Note: network_tags is an output of the submodule but is not promoted to a top-level local here. |
nfs.tf | Runs get-nfsserver-info.sh and get-filestore-info.sh directly via data.external (when local.nfs_enabled = true) and exposes computed locals for nfs_internal_ip, nfs_instance_name, nfs_instance_zone, and nfs_server_exists. Filestore IP takes precedence over GCE IP. Does not expose nfs_instance_tags; that output exists only in the app_nfs_discovery submodule. |
storage.tf | Calls app_storage_enhanced directly (not app_storage_wrapper) to provision application buckets plus a fixed backup bucket (${resource_prefix}-backups). gcsfuse_unmount_wait is hardcoded to 60. The KMS key name is hardcoded to the well-known path projects/{project_id}/locations/{region}/keyRings/{project_id}-cmek-keyring/cryptoKeys/storage-key. Always unconditionally uploads a backup.sql seed object from ${path.module}/scripts/backup.sql to the backup bucket (no count gate). |
Core Integration Modules
These modules are directly used by App CloudRun and App GKE via gcp_integration.tf.
app_networking
- Path:
modules/app_networking - Description: Discovers and validates the existing VPC network topology. All discovery logic runs in a single inline
data "external"bash script. - Outputs:
network_exists,network_name,discovered_regions,available_regions,subnet_names,subnet_cidrs,subnet_details,region_to_subnet,subnet_count,network_tags. - Capabilities:
- Auto-discovery: When
network_nameis empty, queries all subnets with descriptionmanaged-by=services-gcpand selects the unique Services GCP-managed network; emits a stderr message and leavesNETWORK_NAMEempty (not a hard Terraform error) if zero or more than one is found. - Verifies existence of the target (or discovered) VPC network.
- Retrieves subnet names, CIDR ranges, and a
region_to_subnetmap for multi-region deployments. - Network tags: Discovers all ingress firewall-rule target tags on the network (filtered by
targetTags:*, not restricted to HTTP/HTTPS ports). Outputsnetwork_tags— used to tag Cloud Run services with Direct VPC Egress so the correct firewall rules apply. - Falls back to
fallback_regionwhen no subnets are discovered so downstreamavailable_regionsis never empty.
- Auto-discovery: When
app_sql_discovery
- Path:
modules/app_sql_discovery - Description: Discovers an existing Cloud SQL instance and exposes connection metadata. Does not create secrets or passwords — that is handled by
app_secrets. - Outputs:
sql_server_exists,db_instance_name,db_instance_region,db_instance_connection_name,database_version,db_internal_ip,db_root_password(sensitive),db_password_secret_name. - Capabilities:
- Calls
get-sqlserver-info.shwith an optionalsql_instance_namehint to locate the Cloud SQL instance. - Returns
sql_server_exists,db_instance_name,db_instance_region,db_instance_connection_name(for Auth Proxy),db_internal_ip(for direct access),database_version, anddb_root_password(sensitive; read from the script's JSON output, not generated here). - Derives
db_password_secret_nameas{instance}-{resource_prefix}-db-passwordfor use byapp_secretsandapp_iam.
- Calls
app_storage_wrapper
- Path:
modules/app_storage_wrapper - Description: Convenience wrapper that composes
app_cmek, GCS KMS IAM, andapp_storage_enhancedinto a single interface for standardized Cloud Storage provisioning. - Outputs:
bucket_names,backup_bucket_name,artifact_registry_key_id. - Capabilities:
- Invokes
app_cmekto provision a CMEK keyring whenmanage_storage_kms_iam = true. - Fetches the GCS project service account and grants it
roles/cloudkms.cryptoKeyEncrypterDecrypteron the storage key so encrypted buckets can be created without manual IAM setup. - Also runs
assert_gcs_kms_binding.shviadata "external"at plan time to re-assert the KMS binding idempotently — preventing plan/apply failures caused by binding drift even before the firstgoogle_kms_crypto_key_iam_memberis applied. - Delegates bucket creation to
app_storage_enhanced(versioning, lifecycle rules, KMS encryption, backup bucket). - Conditionally uploads a
backup.sqlseed object to the backup bucket whenbackup_sql_source_pathis set (count-gated). - Provides a simplified interface that hides the three-step composition from callers.
- Invokes
app_nfs_discovery
- Path:
modules/app_nfs_discovery - Description: Discovers NFS infrastructure — either a Cloud Filestore instance or a GCE-based NFS server — and outputs a unified set of connection details.
- Capabilities:
- Runs
get-nfsserver-info.sh(with optionalnfs_instance_namehint) andget-filestore-info.shin parallel whennfs_enabled = true. - Normalises residual
"null"strings returned byjq -ron JSON null values to""so all downstream comparisons use a single empty-string check. - Priority: Filestore IP takes precedence over GCE IP when both exist.
- Outputs
nfs_internal_ip,nfs_instance_name,nfs_instance_zone, andnfs_instance_tags(GCE network tags; empty for Filestore, which is a managed service). - Sets
nfs_server_exists = trueonly when a non-empty IP is discovered.
- Runs
app_registry_discovery
- Path:
modules/app_registry_discovery - Description: Artifact Registry repository discovery.
- Capabilities:
- Always runs
discover_repo.shviadata.externalto locate the shared Artifact Registry repository. - The
google_artifact_registry_repositorydata source (which validates the repo exists via the GCP API) is gated onenable_custom_build || enable_cicd_triggerand a non-empty discovered location — avoiding API errors when neither feature is active. - Outputs repository ID, project, location, and name for use by
app_buildand CI/CD resources.
- Always runs
app_build
- Path:
modules/app_build - Description: Container build orchestration using Cloud Build (Kaniko). Used by Foundation modules (
App CloudRun,App GKE). - Capabilities:
- Path handling: Detects whether
container_build_config.context_pathis absolute or relative. Absolute paths (e.g. an app's ownscripts/dir) set the build working directory to that path so all context files are included in the Cloud Build tarball; relative paths resolve againstscripts_diras before. - Inline Dockerfile: When
dockerfile_contentis provided it is written to disk vialocal_file; otherwise the existing file at{context_path}/{dockerfile_path}is used. Falls back to a clear error comment if neither is found. cloudbuild.yamlgeneration: Renderscloudbuild.yaml.tplinto the build working directory. Template now acceptsDOCKERFILE_CONTENTandCLOUDBUILD_SAso the Kaniko step can use an explicit service account and embed the Dockerfile inline when needed.core_scripts_dir: Optional override (var.core_scripts_dir) for the directory containing shared build scripts (build-container.sh,cloudbuild.yaml.tpl). Defaults tovar.scripts_dirwhen not set.- Rich trigger hashing:
null_resourcetriggers includescript_hash,context_hash(all files in the context dir, excludingcloudbuild.yaml, the Dockerfile, andDockerfile.placeholder),dockerfile_hash,build_args, andcloudbuild_hash— ensuring rebuilds fire on any meaningful change. - Tagging: Standardizes image tagging with
application_version.
- Path handling: Detects whether
Supporting Infrastructure Modules
These modules provide lower-level utilities or specific enhancements.
app_iam
- Path:
modules/app_iam - Description: Standardized IAM role bindings for Service Accounts (Workload, Cloud Build).
- Capabilities:
- Secret Manager: Grants
roles/secretmanager.secretAccessorto the workload service account for the database password secret, optionally the root password secret (grant_root_password_access), and all secrets listed insecret_environment_variables. - Storage Buckets: Grants
roles/storage.objectAdminandroles/storage.legacyBucketReaderto the workload service account for each bucket instorage_buckets(conditional oncreate_cloud_storage). - GitHub Token IAM: When
enable_github_token_iam = true, grantsroles/secretmanager.secretAccessoron the GitHub token secret to: the explicit Cloud Build service account (cloud_build_sa_email), the Cloud Build default service identity (obtained viagoogle_project_service_identity), and the Cloud Build service agent (service-{project_number}@gcp-sa-cloudbuild.iam.gserviceaccount.com). - Cloud Build deployment permissions: Gated on
enable_cicd_trigger. Grantsvar.deployment_role(e.g.roles/run.developerorroles/container.developer) androles/iam.serviceAccountUserto the Cloud Build service account so it can deploy on behalf of the workload identity. - Cloud Build service identity: When
enable_github_token_iam = true, enablescloudbuild.googleapis.com, waits 60s for propagation, and creates agoogle_project_service_identityfor Cloud Build so the service agent account exists before IAM bindings are applied.
- Secret Manager: Grants
app_secrets
- Path:
modules/app_secrets - Description: Full lifecycle management of secrets in Secret Manager, including generation, storage, validation, and automated rotation.
- Capabilities:
- Generates random database passwords of configurable length and stores them in Secret Manager. Configures a
secret_propagation_delayto ensure secrets are available before dependent resources start. - Explicit secret values: Accepts
explicit_secret_values(a map of key → sensitive string) for secrets whose values are provided directly by the caller. Keys in this map are excluded from the external existence check and fromdata.google_secret_manager_secret_versionreads. - Additional secrets: References user-provided secrets via
secret_environment_variables(key → Secret Manager secret ID). Non-explicit entries are validated bycheck-secret-exists.sh. - Non-fatal validation: A Terraform
checkblock (secret_environment_variables_exist) emits a plan-time warning when a referenced secret is absent, without hard-failing the plan. This keepsterraform destroysafe even when externally-managed secrets have already been deleted. read_additional_secret_versions: Whenfalse(default for Cloud Run), Terraform skips reading secret versions at plan time — Cloud Run resolves them at runtime. Whentrue(default for GKE, which embeds values into Kubernetes Secrets), versions are read during apply.- GitHub token: Stored with
deletion_policy = "ABANDON"so disablingenable_cicd_triggerleaves the secret version enabled in Secret Manager, preventing a "DESTROYED state" error on re-enable. - Secret Rotation: Creates a Pub/Sub topic and grants the Secret Manager service identity
roles/pubsub.publisherso rotation notifications are emitted at the configuredsecret_rotation_period. - Automatic Password Rotation (
enable_auto_password_rotation = true): Deploys a two-tier rotation architecture:- Cloud Run Job (
pw-rotator): Executes the zero-downtime dual-version pattern — generate new password →ALTER USERon Cloud SQL → add new Secret Manager version → sleeprotation_propagation_delay_sec(default 90s) → disable old version. - Dispatcher Cloud Run Service (
rot-dispatch): A minimal scale-to-zero HTTP service that bridges the Eventarc trigger to the Cloud Run Job (Eventarc does not yet support Jobs as a direct destination). - Eventarc trigger: Fires the dispatcher on each Pub/Sub rotation notification.
- A dedicated least-privilege service account is created for the rotator with only
roles/cloudsql.client,secretVersionAdder,secretVersionManager,secretAccessor,roles/run.developer, androles/eventarc.eventReceiver.
- Cloud Run Job (
- Optional VPC attachment (
rotation_vpc_network/rotation_vpc_subnet) for the rotator job to reach Cloud SQL via private IP.
- Generates random database passwords of configurable length and stores them in Secret Manager. Configures a
app_provider_auth
- Path:
modules/app_provider_auth - Description: Handles Service Account Impersonation logic to allow Terraform to provision resources with restricted privileges.
- Capabilities:
- Executes the external shell script
get-impersonation-token.sh(viadata.external) to obtain a GCP access token by callinggcloud auth print-access-token --impersonate-service-account. - Resolves the impersonation target: prefers
agent_service_account, falls back toresource_creator_identity; impersonation is disabled when neither is set. - Outputs
impersonation_token(sensitive) andimpersonation_enabledfor use by the caller to configure a Terraform Google provider with impersonated credentials. - Creates no GCP resources — purely an authentication helper.
- Executes the external shell script
app_monitoring
- Path:
modules/app_monitoring - Description: Shared logic for creating Cloud Monitoring Alert Policies and Uptime Checks.
- Capabilities:
- Notification channels: Creates one
google_monitoring_notification_channel(type: email) per address insupport_users; all alert policies notify these channels. - Default CPU alert: Creates a
google_monitoring_alert_policy({service_name}-cpu-utilization-alert) that fires when CPU utilization exceedscpu_threshold(default 90%) for 60s; re-notifies every 30 minutes. - Default memory alert: Creates a
google_monitoring_alert_policy({service_name}-memory-utilization-alert) that fires when memory utilization exceedsmemory_threshold(default 90%) for 60s; re-notifies every 30 minutes. - Custom alert policies: Accepts a
custom_alert_policiesmap; creates additionalgoogle_monitoring_alert_policyresources for each entry, each with configurable filter, threshold, duration, comparison, and aggregation period. - All resources are gated on
configure_monitoring = trueand a non-emptysupport_userslist.
- Notification channels: Creates one
app_cicd_base
- Path:
modules/app_cicd_base - Description: Sets up the GitHub repository and Cloud Build v2 connection required for trigger-based CI/CD pipelines.
- Capabilities:
- Repository initialization: Gated on
create_repository = true. Executesinit-cicd-repo.shvia anull_resourcelocal-exec provisioner (re-runs onrepo_name,service_name,container_image, or script hash changes) to create or reuse a GitHub repository, commit aREADME.md,.gitignore,cloudbuild.yaml, and optionally application source files fromAPP_SOURCE_DIR. Gated additionally ongithub_tokenbeing non-empty to avoid "unbound variable" errors. - Cloud Build v2 connection: Creates a
google_cloudbuildv2_connectionlinking Cloud Build to GitHub via an OAuth token (github_token_secret_version) and a GitHub App installation ID (github_app_installation_id). Waits 120s (driven byiam_dependenciestrigger) for IAM propagation before connecting. - Cloud Build v2 repository: Creates a
google_cloudbuildv2_repositorylinked to the connection. The remote URI defaults tohttps://github.com/{owner}/{repo}but can be overridden directly viagithub_repo_url. - All resources are gated on
enable_cicd = true.
- Repository initialization: Gated on
app_cloud_deploy
- Path:
modules/app_cloud_deploy - Description: Creates a Google Cloud Deploy delivery pipeline with per-stage targets, Skaffold configuration in GCS, and all required IAM bindings.
- Capabilities:
- Creates a
google_clouddeploy_delivery_pipelinewith an ordered list of stages (e.g.dev → staging → prod). Each stage maps to a named target with configurablerequire_approvalandauto_promoteflags, plus per-stageproject_idandregionoverrides for cross-project and cross-region topologies. - Auto-promote: For stages with
auto_promote = true(excluding the final stage), creates agoogle_clouddeploy_automationresource that advances the rollout to the next stage on success. - GCS Skaffold bucket: Creates a deterministically-named GCS bucket (
{project_id}-{md5(pipeline_name)[0:8]}-cd-configs) to store Skaffold YAML and per-stage deployment manifests. Cloud Build and the Cloud Deploy service agent are grantedroles/storage.objectVieweron this bucket. - Platform IAM: The Cloud Deploy service agent receives
roles/run.adminfor Cloud Run targets (notroles/run.developer, because the Skaffold after-hook that appliesallUsersIAM bindings requiresrun.services.setIamPolicy, present only inroles/run.admin) androles/container.developerfor GKE targets. - Initial release: When
create_initial_release = trueand acontainer_imageis provided, anull_resourcecreates a first Cloud Deploy release on apply so the pipeline is immediately visible in the dashboard. - Destroy cleanup: Per-stage
null_resourcedestroy-time provisioners delete the corresponding Cloud Run service or GKE Deployment/Service when the pipeline is torn down. - Enables
clouddeploy.googleapis.comand waits 120s for the Cloud Deploy service agent to be provisioned before applying IAM bindings.
- Creates a
Security Modules
app_cmek
- Path:
modules/app_cmek - Description: Provisions Cloud KMS infrastructure for Customer-Managed Encryption Keys (CMEK) for both Cloud Storage and Artifact Registry.
- Capabilities:
- Keyring discovery: Runs
discover_cmek_keyring.shat plan time to find any pre-existing keyring created byServices GCP(prefix{project_id}-cmek-). Reuses the discovered keyring; falls back to creating{project_id}-cmek-keyringwhen none exists. This ensures the two modules always converge on the same keyring without conflicts. - Storage CMEK (
enable_cmek = true): Idempotently creates the CMEK keyring and astorage-keyCryptoKey viagcloud kms keyrings create ... || true. Reads the keyring via a data source (deferred to apply time bydepends_on) and outputsstorage_key_idforapp_storage_enhanced. - Artifact Registry CMEK (
enable_artifact_registry_cmek = true): Idempotently creates anartifact-registry-keyin the same keyring and grants the AR service identity (service-{project_number}@gcp-sa-artifactregistry.iam.gserviceaccount.com)roles/cloudkms.cryptoKeyEncrypterDecrypter— without requiring thegoogle-betaprovider for the service identity lookup. - Set both flags to
falseto skip all provisioning.
- Keyring discovery: Runs
app_security
- Path:
modules/app_security - Description: Binary Authorization image signing and policy enforcement for container images.
- Capabilities:
- Creates a KMS keyring (
${project_id}-binauthz-keyring) and an asymmetric RSA signing key (binauthz-signer). The provisioner also restores and enables key version 1 if it is inDESTROY_SCHEDULEDstate, ensuring re-deploys after a partial destroy succeed without manual intervention. - Creates a Container Analysis note,
pipeline-attestorattestor, and Binary Authorization policy viacreate-binauthz-prerequisites.sh. The script is idempotent — it exits immediately when the attestor already exists, leavingServices GCP-provisioned environments untouched. - Signs the application container image via
sign-image.sh. Skips signing when the image is the placeholdergcr.io/cloudrun/hello(guard is inside the provisioner, notcount, because the image value may be unknown at plan time). - Optionally signs the
db-clientsimage whensql_server_exists = true. - Supports three enforcement modes via
binauthz_evaluation_mode:ALWAYS_ALLOW(default, permissive),REQUIRE_ATTESTATION(enforce signed images), andALWAYS_DENY(emergency lockdown). - Set
enable_binary_authorization = falseto skip all provisioning.
- Creates a KMS keyring (
app_vpc_sc
- Path:
modules/app_vpc_sc - Description: Configures VPC Service Controls to create an Access Context Manager perimeter around the project, restricting which identities and networks can access protected GCP services.
- Capabilities:
- Organization auto-discovery: Reads
org_idandfolder_idfrom the project resource. Emits a clearnull_resourcewarning (not a hard error) when: (a) the project is standalone with no GCP organization (VPC-SC permanently unavailable), (b) the project is nested under a folder and the org ID cannot be auto-discovered (caller must supplyorganization_idexplicitly), or (c)admin_ip_rangesis empty (would risk lockout). - VPC CIDR auto-discovery: When
vpc_cidr_rangesis empty andnetwork_nameis set, queries subnets of the named VPC and uses their CIDR ranges as the VPC access level. Falls back to10.0.0.0/8when no subnets are found. - Access Context Manager policy: Reuses an existing org-level ACM policy if one exists; creates one otherwise.
- Four access levels:
vpc_access(VPC subnet CIDRs),admin_access(admin_ip_ranges),iap_access(IAP service agent), andcicd_access(Cloud Build SA andresource_creator_identity). - Service perimeter: Restricts 15 GCP services (Cloud Run, GKE, Cloud SQL, Secret Manager, Storage, Artifact Registry, Cloud Build, Certificate Manager, IAP, Compute, KMS, Pub/Sub, Redis, Filestore, Firestore). Ingress allows all four access levels; egress policies permit outbound calls to Storage, Artifact Registry, Logging, Monitoring, Secret Manager, and Cloud SQL.
- Dry-run mode: Set
vpc_sc_dry_run = trueto enforce the perimeter in audit-only mode before going live. - Skipped entirely when no organization is present or
admin_ip_rangesis empty.
- Organization auto-discovery: Reads
Storage Modules
app_storage_enhanced
- Path:
modules/app_storage_enhanced - Description: Advanced Cloud Storage bucket management with fine-grained configuration. Used internally by
app_storage_wrapper. - Capabilities:
- Creates one or more application storage buckets from a
storage_bucketsmap with per-bucket settings. - Per-bucket configuration: storage class, versioning, uniform bucket-level access, public access prevention, soft-delete retention, and lifecycle rules (Delete, SetStorageClass with age, date, state, and version conditions).
- Optional dedicated backup bucket with configurable location, storage class, and
backup_retention_dayslifecycle auto-deletion policy. - Supports Customer-Managed Encryption Keys (
kms_key_name) or Google-managed encryption. - Destroy-time provisioners safely empty buckets before deletion, with a configurable
gcsfuse_unmount_waitto handle GCS Fuse unmount delays.
- Creates one or more application storage buckets from a
Database Utility Modules
app_db_clients
- Path:
modules/app_db_clients - Description: Builds and pushes a database client tools container image to Artifact Registry.
- Capabilities:
- Builds a container image containing PostgreSQL and MySQL clients via a Cloud Build job (
cloudbuild-db-clients.yaml). - Pushes the image to Artifact Registry tagged as
db-clients:latest. Outputs the full image URI (db_clients_image) consumed byapp_securityfor Binary Authorization signing. - Used by db-export CronJobs and db-cleanup destroy provisioners across platform modules.
- No
count:sql_server_existsandartifact_repo_idcan both be unknown at plan time, so a runtime guard inside the provisioner handles the skip logic rather than acountexpression. Analways_run = timestamp()trigger ensures the existence check runs on every apply, rebuilding only when the image is absent. - Triggers on
dockerfile_hash(Dockerfile.db-clients) andcloudbuild_hashso image changes are detected automatically.
- Builds a container image containing PostgreSQL and MySQL clients via a Cloud Build job (
Monitoring Modules
app_dashboard
- Path:
modules/app_dashboard - Description: Creates platform-specific Cloud Monitoring dashboards.
- Capabilities:
- Conditionally creates a dashboard when
configure_monitoring = true. - GKE dashboard: Displays CPU usage, memory usage, pod restart count, and network egress; filters by Kubernetes namespace.
- Cloud Run dashboard: Displays request count, request latency (p95), container instance count, and CPU utilization; filters by service name.
- The
platformvariable ("gke"or"cloudrun") selects which metric set is displayed. - Outputs
dashboard_id(empty string when not created).
- Conditionally creates a dashboard when
3. Implementation Pattern
Platform modules (App CloudRun, App GKE) consume App Common submodules directly. The pattern below shows a typical integration:
# Example from modules/App_CloudRun/gcp_integration.tf
module "network_discovery" {
source = "../App_Common/modules/app_networking"
project_id = local.project_id
network_name = "" # auto-discovers the Services_GCP network
fallback_region = "us-central1"
impersonation_service_account = local.impersonation_service_account
}
module "app_sql_discovery" {
source = "../App_Common/modules/app_sql_discovery"
project_id = local.project_id
database_client_type = local.database_client_type
impersonation_service_account = local.impersonation_service_account
scripts_dir = "${path.module}/../App_Common/scripts"
resource_prefix = local.resource_prefix
}
locals {
# Network outputs
region_to_subnet = module.network_discovery.region_to_subnet
network_tags = module.network_discovery.network_tags
# SQL outputs — used by app_secrets and app_iam
sql_server_exists = module.app_sql_discovery.sql_server_exists
db_connection_name = module.app_sql_discovery.db_instance_connection_name
db_password_secret = module.app_sql_discovery.db_password_secret_name
}
Any improvement to the shared discovery or management logic in App Common automatically benefits all consuming platform modules without changes to Application Modules.