GCP Terraform refactor (#46)

This is an updated version of the previous [GCP
terraform](https://github.com/fleetdm/fleet/tree/main/infrastructure/dogfood/terraform/gcp)
with more options and updated usage of modules and Cloud Run (v1 -> v2)
which means we were able to ditch the manged VPC connectors (extra cost)
and go with the VPC direct access.

I think now that I have cloud run jobs figured out we'll be able to add
a proper migration addon or module.
This commit is contained in:
Benjamin Edwards
2025-07-14 13:03:22 -04:00
committed by GitHub
parent b6b499063a
commit e82cec03eb
17 changed files with 1518 additions and 0 deletions
+223
View File
@@ -0,0 +1,223 @@
# Terraform Google Cloud Fleet Deployment
This Terraform project automates the deployment of Fleet Device Management (Fleet) on the Google Cloud Platform (GCP). It provisions a new GCP project (or uses an existing one if configured) and deploys all necessary infrastructure components, including:
* A new Google Cloud Project (via `terraform-google-modules/project-factory`)
* VPC Network and Subnets
* Cloud NAT for outbound internet access
* Cloud SQL for MySQL (database for Fleet)
* Memorystore for Redis (caching for Fleet)
* Google Cloud Storage (GCS) bucket (for Fleet software installers, S3-compatible access configured)
* Google Secret Manager (for storing sensitive data like database passwords and Fleet server private key)
* Cloud Run v2 Service (for the main Fleet application)
* Cloud Run v2 Job (for database migrations)
* External HTTP(S) Load Balancer with managed SSL certificate
* Cloud DNS for managing the Fleet endpoint
* Appropriate IAM service accounts and permissions
## Prerequisites
1. **Terraform:** Version `~> 1.11` (as specified in `main.tf`). Install from [terraform.io](https://www.terraform.io/downloads.html).
2. **Google Cloud SDK (`gcloud`):** Installed and configured. Install from [cloud.google.com/sdk](https://cloud.google.com/sdk/docs/install).
3. **GCP Account & Permissions:**
* A Google Cloud Organization ID.
* A Billing Account ID.
* Credentials configured for Terraform to interact with GCP. This typically means running `gcloud auth application-default login` with a user account that has:
* `Organization Administrator` or `Folder Creator` & `Project Creator` roles at the Organization/Folder level (for the `project-factory` module).
* `Billing Account User` role on the Billing Account.
* Once the project is created, subsequent applies for resources within the project will generally require `Owner` or `Editor` on the provisioned project.
* Alternatively, use a Service Account with appropriate permissions.
4. **Registered Domain Name:** You will need a domain name that you can manage DNS records for. This project will create a Cloud DNS Managed Zone for this domain (or a subdomain).
5. **(Optional) Fleet License Key:** If you have a premium Fleet license, you can provide it.
## Configuration
1. **Clone the repository (if applicable) or ensure you have all the files.**
2. **Create a `terraform.tfvars` file:**
Copy `terraform.tfvars.example` (you'll need to create this, see example below) to `terraform.tfvars` and populate it with your specific values.
**Example `terraform.tfvars.example`:**
```hcl
# Required: GCP Organization and Billing
org_id = "YOUR_ORGANIZATION_ID" # e.g., "123456789012"
billing_account_id = "YOUR_BILLING_ACCOUNT_ID" # e.g., "012345-6789AB-CDEF01"
# Required: DNS Configuration
# The public DNS zone name (e.g., "example.com." - note the trailing dot)
# This project will create/manage this zone in Cloud DNS.
dns_zone_name = "your-domain.com."
# The fully qualified domain name for your Fleet instance (e.g., "fleet.your-domain.com.")
dns_record_name = "fleet.your-domain.com."
# Optional: Project Configuration
project_name = "my-fleet-project" # Name for the new GCP project
random_project_id = true # Set to false if you want to use project_name as project_id (must be globally unique)
# Optional: Labels
labels = {
application = "fleet"
environment = "production"
owner = "devops-team"
}
# --- byo-project module specific variables (passed through from root) ---
# You can override defaults from byo-project/variables.tf here if needed,
# or set values for variables that don't have defaults.
# Example: Fleet Configuration including migration execution
fleet_config = {
# image_tag = "fleetdm/fleet:v4.x.y" # Specify your desired Fleet version
# license_key = "YOUR_FLEET_LICENSE_KEY"
# installers_bucket_name = "my-unique-fleet-installers" # Must be globally unique
# fleet_cpu = "1000m"
# fleet_memory = "4096Mi"
# debug_logging = false
# min_instance_count = 1
# max_instance_count = 3
# exec_migration = true # Set to true to run migrations on `terraform apply`
# (e.g., when changing image_tag)
}
```
**Key Variables to Set:**
* `org_id`: Your GCP Organization ID.
* `billing_account_id`: Your GCP Billing Account ID.
* `dns_zone_name`: The DNS zone that will be created/managed in Cloud DNS (e.g., `mydomain.com.`). **Must end with a dot.**
* `dns_record_name`: The specific DNS record for Fleet (e.g., `fleet.mydomain.com.`). **Must end with a dot.**
* `project_name`: A descriptive name for the project to be created.
* `fleet_config.image_tag`: The Docker image tag for the Fleet version you want to deploy (e.g., `fleetdm/fleet:v4.67.3`).
* `fleet_config.exec_migration`: Set to `true` when you are upgrading the `fleet_config.image_tag` to automatically run database migrations. Set to `false` if you want to manage migrations manually or if it's not an image upgrade.
* `fleet_config.license_key` (Optional, inside the `fleet_config` object): Your Fleet license key if you have one.
Review `variables.tf` and `byo-project/variables.tf` for all available options and their defaults.
## Deployment Steps
1. **Authenticate with GCP:**
Ensure your `gcloud` CLI is authenticated with the account having the necessary permissions.
```bash
gcloud auth application-default login
```
2. **Initialize Terraform:**
Navigate to the root directory of the project (where `main.tf` is located) and run:
```bash
terraform init
```
3. **Plan the Deployment:**
Review the changes Terraform will make:
```bash
terraform plan -out=tfplan
```
4. **Apply the Configuration:**
Provision the resources:
```bash
terraform apply tfplan
```
This process can take a significant amount of time (1020 minutes or more), especially for the initial project creation, API enablement, and Cloud SQL instance provisioning.
5. **Update DNS (if not using Cloud DNS for the parent zone):**
If `dns_zone_name` (e.g., `fleet.your-domain.com.`) is a new zone created by this Terraform, and your parent domain (e.g., `your-domain.com.`) is managed elsewhere (e.g., GoDaddy, Cloudflare), you need to delegate this new zone.
* Get the Name Servers (NS) records for the newly created `google_dns_managed_zone` (`${var.prefix}-zone`):
```bash
# First, get the project ID and zone name if you don't know them
PROJECT_ID=$(terraform output -raw project_id 2>/dev/null || gcloud projects list --filter="name~^my-fleet-project" --format="value(projectId)" | head -n 1)
DNS_ZONE_INTERNAL_NAME="fleet-zone" # Or as configured by 'prefix' variable
gcloud dns managed-zones describe ${DNS_ZONE_INTERNAL_NAME} \
--project=${PROJECT_ID} \
--format="value(nameServers)"
```
* Add these NS records to your parent domain's DNS settings at your domain registrar or DNS provider.
If `dns_zone_name` (e.g., `your-domain.com.`) was an *existing* zone you own and manage via Cloud DNS in this project, Terraform would add records to it directly. The current setup *creates* the zone, so delegation is key if the parent is external.
## Accessing Fleet
Once the deployment is complete, database migrations have run successfully, and DNS has propagated (which can take a few minutes):
* Open your web browser and navigate to the `dns_record_name` you configured (e.g., `https://fleet.your-domain.com`).
* You should be greeted by the Fleet setup page or login screen.
## Key Resources Created (within the `byo-project` module)
* **VPC Network (`fleet-network`):** Custom network for Fleet resources.
* **Subnet (`fleet-subnet`):** Subnetwork within the VPC.
* **Cloud Router & NAT (`fleet-cloud-router`, `fleet-vpc-nat`):** For outbound internet access from Cloud Run and other private resources.
* **Cloud SQL for MySQL (`fleet-mysql-xxxx`):** Managed MySQL database.
* **Memorystore for Redis (`fleet-cache`):** Managed Redis instance.
* **GCS Bucket (`fleet-installers-xxxx`):** For S3-compatible storage.
* **Secret Manager Secrets:**
* `fleet-db-password-xxxx`: Stores the generated MySQL user password.
* `fleet-private-key-xxxx`: Stores a generated private key for Fleet server.
* **Service Account (`fleet-run-sa`):** Used by Cloud Run services/jobs with necessary permissions.
* **Cloud Run Service (`fleet-service`):** Hosts the main Fleet application.
* **Cloud Run Job (`fleet-migration-job`):** For database migrations.
* **Network Endpoint Group (`fleet-neg`):** Connects the Load Balancer to the Cloud Run service.
* **Cloud DNS Managed Zone (`fleet-zone`):** Manages DNS records for `dns_zone_name`.
* **External HTTP(S) Load Balancer (`fleet-lb`):** Provides public access to Fleet via HTTPS.
* **DNS A Record:** Points `dns_record_name` to the Load Balancer's IP.
(Note: `xxxx` indicates a random suffix added by some modules/resources for uniqueness.)
## Cleaning Up
To destroy all resources created by this Terraform project:
1. Navigate to the root directory of the project.
2. Run the destroy command:
```bash
terraform destroy
```
3. Confirm by typing `yes` when prompted.
## Important Considerations
* **Permissions:** The user or service account running Terraform needs extensive permissions, especially for project creation. For ongoing management within the project, a role like `Project Editor` or more granular roles might suffice.
* **Fleet Configuration:** This Terraform setup provisions the infrastructure. Further Fleet application configuration (e.g., SSO, SMTP, agent options) will need to be done through the Fleet UI or API after deployment.
* **Security:**
* Review IAM permissions granted.
* The Load Balancer is configured for HTTPS and redirects HTTP.
* Cloud SQL and Memorystore are not publicly accessible and are connected via Private Service Access.
* **Scalability:** Cloud Run scaling parameters (`min_instance_count`, `max_instance_count`) and database/cache tier can be adjusted in `variables.tf` (via `fleet_config`, `database_config`, `cache_config`) to suit your load.
### Networking Diagram
```mermaid
graph TD
subgraph External
Internet[(Internet)]
Users[Web Console / fleetd agent]
GitHub[(GitHub - Vulnerability Resources)]
end
subgraph "Google Cloud Platform (GCP)"
subgraph VPC [VPC]
direction LR
subgraph PublicFacing [Public Zone]
LB[Global Load Balancer]
end
subgraph PrivateZone [Private Zone]
CloudRun[Cloud Run: Fleet App]
CloudSQL[Cloud SQL for MySQL]
Redis[Memorystore for Redis]
NAT[Cloud NAT]
end
CloudRun --> Redis
CloudRun --> CloudSQL
CloudRun --> NAT
end
end
Users -- "fleet.yourdomain.com" --> Internet
Internet -- "fleet.yourdomain.com" --> LB
LB --> CloudRun
NAT --> GitHub
```
+1
View File
@@ -0,0 +1 @@
header-from: .header.md
+284
View File
@@ -0,0 +1,284 @@
# Terraform Google Cloud Fleet Deployment
This Terraform project automates the deployment of Fleet Device Management (Fleet) on the Google Cloud Platform (GCP). It provisions a new GCP project (or uses an existing one if configured) and deploys all necessary infrastructure components, including:
* A new Google Cloud Project (via `terraform-google-modules/project-factory`)
* VPC Network and Subnets
* Cloud NAT for outbound internet access
* Cloud SQL for MySQL (database for Fleet)
* Memorystore for Redis (caching for Fleet)
* Google Cloud Storage (GCS) bucket (for Fleet software installers, S3-compatible access configured)
* Google Secret Manager (for storing sensitive data like database passwords and Fleet server private key)
* Cloud Run v2 Service (for the main Fleet application)
* Cloud Run v2 Job (for database migrations)
* External HTTP(S) Load Balancer with managed SSL certificate
* Cloud DNS for managing the Fleet endpoint
* Appropriate IAM service accounts and permissions
## Prerequisites
1. **Terraform:** Version `~> 1.11` (as specified in `main.tf`). Install from [terraform.io](https://www.terraform.io/downloads.html).
2. **Google Cloud SDK (`gcloud`):** Installed and configured. Install from [cloud.google.com/sdk](https://cloud.google.com/sdk/docs/install).
3. **GCP Account & Permissions:**
* A Google Cloud Organization ID.
* A Billing Account ID.
* Credentials configured for Terraform to interact with GCP. This typically means running `gcloud auth application-default login` with a user account that has:
* `Organization Administrator` or `Folder Creator` & `Project Creator` roles at the Organization/Folder level (for the `project-factory` module).
* `Billing Account User` role on the Billing Account.
* Once the project is created, subsequent applies for resources within the project will generally require `Owner` or `Editor` on the provisioned project.
* Alternatively, use a Service Account with appropriate permissions.
4. **Registered Domain Name:** You will need a domain name that you can manage DNS records for. This project will create a Cloud DNS Managed Zone for this domain (or a subdomain).
5. **(Optional) Fleet License Key:** If you have a premium Fleet license, you can provide it.
## Configuration
1. **Clone the repository (if applicable) or ensure you have all the files.**
2. **Create a `terraform.tfvars` file:**
Copy `terraform.tfvars.example` (you'll need to create this, see example below) to `terraform.tfvars` and populate it with your specific values.
**Example `terraform.tfvars.example`:**
```hcl
# Required: GCP Organization and Billing
org_id = "YOUR_ORGANIZATION_ID" # e.g., "123456789012"
billing_account_id = "YOUR_BILLING_ACCOUNT_ID" # e.g., "012345-6789AB-CDEF01"
# Required: DNS Configuration
# The public DNS zone name (e.g., "example.com." - note the trailing dot)
# This project will create/manage this zone in Cloud DNS.
dns_zone_name = "your-domain.com."
# The fully qualified domain name for your Fleet instance (e.g., "fleet.your-domain.com.")
dns_record_name = "fleet.your-domain.com."
# Optional: Project Configuration
project_name = "my-fleet-project" # Name for the new GCP project
random_project_id = true # Set to false if you want to use project_name as project_id (must be globally unique)
# Optional: Labels
labels = {
application = "fleet"
environment = "production"
owner = "devops-team"
}
# --- byo-project module specific variables (passed through from root) ---
# You can override defaults from byo-project/variables.tf here if needed,
# or set values for variables that don't have defaults.
# Example: Fleet Configuration including migration execution
fleet_config = {
# image_tag = "fleetdm/fleet:v4.x.y" # Specify your desired Fleet version
# license_key = "YOUR_FLEET_LICENSE_KEY"
# installers_bucket_name = "my-unique-fleet-installers" # Must be globally unique
# fleet_cpu = "1000m"
# fleet_memory = "4096Mi"
# debug_logging = false
# min_instance_count = 1
# max_instance_count = 3
# exec_migration = true # Set to true to run migrations on `terraform apply`
# (e.g., when changing image_tag)
}
```
**Key Variables to Set:**
* `org_id`: Your GCP Organization ID.
* `billing_account_id`: Your GCP Billing Account ID.
* `dns_zone_name`: The DNS zone that will be created/managed in Cloud DNS (e.g., `mydomain.com.`). **Must end with a dot.**
* `dns_record_name`: The specific DNS record for Fleet (e.g., `fleet.mydomain.com.`). **Must end with a dot.**
* `project_name`: A descriptive name for the project to be created.
* `fleet_config.image_tag`: The Docker image tag for the Fleet version you want to deploy (e.g., `fleetdm/fleet:v4.67.3`).
* `fleet_config.exec_migration`: Set to `true` when you are upgrading the `fleet_config.image_tag` to automatically run database migrations. Set to `false` if you want to manage migrations manually or if it's not an image upgrade.
* `fleet_config.license_key` (Optional, inside the `fleet_config` object): Your Fleet license key if you have one.
Review `variables.tf` and `byo-project/variables.tf` for all available options and their defaults.
## Deployment Steps
1. **Authenticate with GCP:**
Ensure your `gcloud` CLI is authenticated with the account having the necessary permissions.
```bash
gcloud auth application-default login
```
2. **Initialize Terraform:**
Navigate to the root directory of the project (where `main.tf` is located) and run:
```bash
terraform init
```
3. **Plan the Deployment:**
Review the changes Terraform will make:
```bash
terraform plan -out=tfplan
```
4. **Apply the Configuration:**
Provision the resources:
```bash
terraform apply tfplan
```
This process can take a significant amount of time (1020 minutes or more), especially for the initial project creation, API enablement, and Cloud SQL instance provisioning.
5. **Update DNS (if not using Cloud DNS for the parent zone):**
If `dns_zone_name` (e.g., `fleet.your-domain.com.`) is a new zone created by this Terraform, and your parent domain (e.g., `your-domain.com.`) is managed elsewhere (e.g., GoDaddy, Cloudflare), you need to delegate this new zone.
* Get the Name Servers (NS) records for the newly created `google_dns_managed_zone` (`${var.prefix}-zone`):
```bash
# First, get the project ID and zone name if you don't know them
PROJECT_ID=$(terraform output -raw project_id 2>/dev/null || gcloud projects list --filter="name~^my-fleet-project" --format="value(projectId)" | head -n 1)
DNS_ZONE_INTERNAL_NAME="fleet-zone" # Or as configured by 'prefix' variable
gcloud dns managed-zones describe ${DNS_ZONE_INTERNAL_NAME} \
--project=${PROJECT_ID} \
--format="value(nameServers)"
```
* Add these NS records to your parent domain's DNS settings at your domain registrar or DNS provider.
If `dns_zone_name` (e.g., `your-domain.com.`) was an *existing* zone you own and manage via Cloud DNS in this project, Terraform would add records to it directly. The current setup *creates* the zone, so delegation is key if the parent is external.
## Accessing Fleet
Once the deployment is complete, database migrations have run successfully, and DNS has propagated (which can take a few minutes):
* Open your web browser and navigate to the `dns_record_name` you configured (e.g., `https://fleet.your-domain.com`).
* You should be greeted by the Fleet setup page or login screen.
## Key Resources Created (within the `byo-project` module)
* **VPC Network (`fleet-network`):** Custom network for Fleet resources.
* **Subnet (`fleet-subnet`):** Subnetwork within the VPC.
* **Cloud Router & NAT (`fleet-cloud-router`, `fleet-vpc-nat`):** For outbound internet access from Cloud Run and other private resources.
* **Cloud SQL for MySQL (`fleet-mysql-xxxx`):** Managed MySQL database.
* **Memorystore for Redis (`fleet-cache`):** Managed Redis instance.
* **GCS Bucket (`fleet-installers-xxxx`):** For S3-compatible storage.
* **Secret Manager Secrets:**
* `fleet-db-password-xxxx`: Stores the generated MySQL user password.
* `fleet-private-key-xxxx`: Stores a generated private key for Fleet server.
* **Service Account (`fleet-run-sa`):** Used by Cloud Run services/jobs with necessary permissions.
* **Cloud Run Service (`fleet-service`):** Hosts the main Fleet application.
* **Cloud Run Job (`fleet-migration-job`):** For database migrations.
* **Network Endpoint Group (`fleet-neg`):** Connects the Load Balancer to the Cloud Run service.
* **Cloud DNS Managed Zone (`fleet-zone`):** Manages DNS records for `dns_zone_name`.
* **External HTTP(S) Load Balancer (`fleet-lb`):** Provides public access to Fleet via HTTPS.
* **DNS A Record:** Points `dns_record_name` to the Load Balancer's IP.
(Note: `xxxx` indicates a random suffix added by some modules/resources for uniqueness.)
## Cleaning Up
To destroy all resources created by this Terraform project:
1. Navigate to the root directory of the project.
2. Run the destroy command:
```bash
terraform destroy
```
3. Confirm by typing `yes` when prompted.
## Important Considerations
* **Permissions:** The user or service account running Terraform needs extensive permissions, especially for project creation. For ongoing management within the project, a role like `Project Editor` or more granular roles might suffice.
* **Fleet Configuration:** This Terraform setup provisions the infrastructure. Further Fleet application configuration (e.g., SSO, SMTP, agent options) will need to be done through the Fleet UI or API after deployment.
* **Security:**
* Review IAM permissions granted.
* The Load Balancer is configured for HTTPS and redirects HTTP.
* Cloud SQL and Memorystore are not publicly accessible and are connected via Private Service Access.
* **Scalability:** Cloud Run scaling parameters (`min_instance_count`, `max_instance_count`) and database/cache tier can be adjusted in `variables.tf` (via `fleet_config`, `database_config`, `cache_config`) to suit your load.
### Networking Diagram
```mermaid
graph TD
subgraph External
Internet[(Internet)]
Users[Web Console / fleetd agent]
GitHub[(GitHub - Vulnerability Resources)]
end
subgraph "Google Cloud Platform (GCP)"
subgraph VPC [VPC]
direction LR
subgraph PublicFacing [Public Zone]
LB[Global Load Balancer]
end
subgraph PrivateZone [Private Zone]
CloudRun[Cloud Run: Fleet App]
CloudSQL[Cloud SQL for MySQL]
Redis[Memorystore for Redis]
NAT[Cloud NAT]
end
CloudRun --> Redis
CloudRun --> CloudSQL
CloudRun --> NAT
end
end
Users -- "fleet.yourdomain.com" --> Internet
Internet -- "fleet.yourdomain.com" --> LB
LB --> CloudRun
NAT --> GitHub
```
## Requirements
| Name | Version |
|------|---------|
| <a name="requirement_terraform"></a> [terraform](#requirement\_terraform) | ~> 1.11 |
| <a name="requirement_google"></a> [google](#requirement\_google) | 6.35.0 |
## Providers
No providers.
## Modules
| Name | Source | Version |
|------|--------|---------|
| <a name="module_fleet"></a> [fleet](#module\_fleet) | ./byo-project | n/a |
| <a name="module_project_factory"></a> [project\_factory](#module\_project\_factory) | terraform-google-modules/project-factory/google | ~> 18.0.0 |
## Resources
No resources.
## Inputs
| Name | Description | Type | Default | Required |
|------|-------------|------|---------|:--------:|
| <a name="input_billing_account_id"></a> [billing\_account\_id](#input\_billing\_account\_id) | billing account id | `any` | n/a | yes |
| <a name="input_cache_config"></a> [cache\_config](#input\_cache\_config) | Configuration for the Memorystore (Redis) instance. | <pre>object({<br> name = string<br> tier = string<br> engine_version = string<br> connect_mode = string<br> memory_size = number<br> })</pre> | <pre>{<br> "connect_mode": "PRIVATE_SERVICE_ACCESS",<br> "engine_version": null,<br> "memory_size": 1,<br> "name": "fleet-cache",<br> "tier": "STANDARD_HA"<br>}</pre> | no |
| <a name="input_database_config"></a> [database\_config](#input\_database\_config) | Configuration for the Cloud SQL (MySQL) instance. | <pre>object({<br> name = string<br> database_name = string<br> database_user = string<br> collation = string<br> charset = string<br> deletion_protection = bool<br> database_version = string<br> tier = string<br> })</pre> | <pre>{<br> "charset": "utf8mb4",<br> "collation": "utf8mb4_unicode_ci",<br> "database_name": "fleet",<br> "database_user": "fleet",<br> "database_version": "MYSQL_8_0",<br> "deletion_protection": false,<br> "name": "fleet-mysql",<br> "tier": "db-n1-standard-1"<br>}</pre> | no |
| <a name="input_dns_record_name"></a> [dns\_record\_name](#input\_dns\_record\_name) | The DNS record for Fleet (e.g., 'fleet.my-fleet-infra.com.') | `string` | n/a | yes |
| <a name="input_dns_zone_name"></a> [dns\_zone\_name](#input\_dns\_zone\_name) | The DNS name of the managed zone (e.g., 'my-fleet-infra.com.') | `string` | n/a | yes |
| <a name="input_fleet_config"></a> [fleet\_config](#input\_fleet\_config) | Configuration for the Fleet application deployment. | <pre>object({<br> installers_bucket_name = string<br> image_tag = string<br> fleet_cpu = string<br> fleet_memory = string<br> debug_logging = bool<br> license_key = optional(string)<br> min_instance_count = number<br> max_instance_count = number<br> exec_migration = bool<br> extra_env_vars = optional(map(string))<br> extra_secret_env_vars = optional(map(object({<br> secret = string<br> version = string<br> })))<br> })</pre> | <pre>{<br> "debug_logging": false,<br> "exec_migration": true,<br> "extra_env_vars": {},<br> "extra_secret_env_vars": {},<br> "fleet_cpu": "1000m",<br> "fleet_memory": "4096Mi",<br> "image_tag": "fleetdm/fleet:v4.67.3",<br> "installers_bucket_name": "",<br> "max_instance_count": 5,<br> "min_instance_count": 1<br>}</pre> | no |
| <a name="input_fleet_image"></a> [fleet\_image](#input\_fleet\_image) | n/a | `string` | `"v4.67.3"` | no |
| <a name="input_labels"></a> [labels](#input\_labels) | resource labels | `map(string)` | <pre>{<br> "application": "fleet"<br>}</pre> | no |
| <a name="input_location"></a> [location](#input\_location) | The general location for resources, e.g., 'us' for GCS buckets. | `string` | `"us"` | no |
| <a name="input_org_id"></a> [org\_id](#input\_org\_id) | organization id | `any` | n/a | yes |
| <a name="input_prefix"></a> [prefix](#input\_prefix) | A prefix used for naming resources within the byo-project module. | `string` | `"fleet"` | no |
| <a name="input_project_name"></a> [project\_name](#input\_project\_name) | n/a | `string` | `"fleet"` | no |
| <a name="input_random_project_id"></a> [random\_project\_id](#input\_random\_project\_id) | n/a | `bool` | `true` | no |
| <a name="input_region"></a> [region](#input\_region) | The GCP region for regional resources. | `string` | `"us-central1"` | no |
| <a name="input_vpc_config"></a> [vpc\_config](#input\_vpc\_config) | Configuration for the VPC network and subnets. | <pre>object({<br> network_name = string<br> subnets = list(object({<br> subnet_name = string<br> subnet_ip = string<br> subnet_region = string<br> subnet_private_access = bool<br> }))<br> })</pre> | <pre>{<br> "network_name": "fleet-network",<br> "subnets": [<br> {<br> "subnet_ip": "10.10.10.0/24",<br> "subnet_name": "fleet-subnet",<br> "subnet_private_access": true,<br> "subnet_region": "us-central1"<br> }<br> ]<br>}</pre> | no |
## Outputs
| Name | Description |
|------|-------------|
| <a name="output_cloud_router_name"></a> [cloud\_router\_name](#output\_cloud\_router\_name) | The name of the Cloud Router created for NAT. |
| <a name="output_cloud_run_service_location"></a> [cloud\_run\_service\_location](#output\_cloud\_run\_service\_location) | The location of the deployed Fleet Cloud Run service. |
| <a name="output_cloud_run_service_name"></a> [cloud\_run\_service\_name](#output\_cloud\_run\_service\_name) | The name of the deployed Fleet Cloud Run service. |
| <a name="output_dns_managed_zone_name"></a> [dns\_managed\_zone\_name](#output\_dns\_managed\_zone\_name) | The name of the Cloud DNS managed zone created for Fleet. |
| <a name="output_dns_managed_zone_name_servers"></a> [dns\_managed\_zone\_name\_servers](#output\_dns\_managed\_zone\_name\_servers) | The authoritative name servers for the created Cloud DNS managed zone. Delegate your domain to these. |
| <a name="output_fleet_application_url"></a> [fleet\_application\_url](#output\_fleet\_application\_url) | The primary URL to access the Fleet application (via the Load Balancer). |
| <a name="output_fleet_service_account_email"></a> [fleet\_service\_account\_email](#output\_fleet\_service\_account\_email) | The email address of the service account used by the Fleet Cloud Run service. |
| <a name="output_load_balancer_ip_address"></a> [load\_balancer\_ip\_address](#output\_load\_balancer\_ip\_address) | The external IP address of the HTTP(S) Load Balancer. |
| <a name="output_mysql_instance_connection_name"></a> [mysql\_instance\_connection\_name](#output\_mysql\_instance\_connection\_name) | The connection name for the Cloud SQL instance (used by Cloud SQL Proxy). |
| <a name="output_mysql_instance_name"></a> [mysql\_instance\_name](#output\_mysql\_instance\_name) | The name of the Cloud SQL for MySQL instance. |
| <a name="output_redis_host"></a> [redis\_host](#output\_redis\_host) | The host IP address of the Memorystore for Redis instance. |
| <a name="output_redis_instance_name"></a> [redis\_instance\_name](#output\_redis\_instance\_name) | The name of the Memorystore for Redis instance. |
| <a name="output_redis_port"></a> [redis\_port](#output\_redis\_port) | The port number of the Memorystore for Redis instance. |
| <a name="output_software_installers_bucket_name"></a> [software\_installers\_bucket\_name](#output\_software\_installers\_bucket\_name) | The name of the GCS bucket for Fleet software installers. |
| <a name="output_software_installers_bucket_url"></a> [software\_installers\_bucket\_url](#output\_software\_installers\_bucket\_url) | The gsutil URL of the GCS bucket for Fleet software installers. |
| <a name="output_vpc_network_name"></a> [vpc\_network\_name](#output\_vpc\_network\_name) | The name of the VPC network created. |
| <a name="output_vpc_network_self_link"></a> [vpc\_network\_self\_link](#output\_vpc\_network\_self\_link) | The self-link of the VPC network created. |
| <a name="output_vpc_subnets_names"></a> [vpc\_subnets\_names](#output\_vpc\_subnets\_names) | List of subnet names created in the VPC. |
+18
View File
@@ -0,0 +1,18 @@
module "memstore" {
source = "terraform-google-modules/memorystore/google"
version = "~> 14.0"
name = var.cache_config.name
redis_version = var.cache_config.engine_version
tier = var.cache_config.tier
memory_size_gb = var.cache_config.memory_size
project_id = var.project_id
region = var.region
enable_apis = true
transit_encryption_mode = "DISABLED"
authorized_network = module.vpc.network_id
connect_mode = var.cache_config.connect_mode
depends_on = [module.private-service-access.peering_completed]
}
+225
View File
@@ -0,0 +1,225 @@
locals {
# --- Shared Container Configuration ---
fleet_image_tag = var.fleet_config.image_tag
fleet_resources_limits = {
cpu = var.fleet_config.fleet_cpu
memory = var.fleet_config.fleet_memory
}
fleet_secrets_env_vars = merge(var.fleet_config.extra_secret_env_vars, {
FLEET_MYSQL_PASSWORD = {
secret = google_secret_manager_secret.database_password.secret_id
version = "latest"
},
FLEET_SERVER_PRIVATE_KEY = {
secret = google_secret_manager_secret.private_key.secret_id
version = "latest"
}
})
fleet_env_vars = merge(var.fleet_config.extra_env_vars, {
FLEET_LICENSE_KEY = var.fleet_config.license_key
FLEET_SERVER_FORCE_H2C = "true"
FLEET_MYSQL_PROTOCOL = "tcp"
FLEET_MYSQL_ADDRESS = "${module.mysql.private_ip_address}:3306"
FLEET_MYSQL_USERNAME = var.database_config.database_user
FLEET_MYSQL_DATABASE = var.database_config.database_name
FLEET_REDIS_ADDRESS = "${module.memstore.host}:${module.memstore.port}"
FLEET_REDIS_USE_TLS = "false"
#FLEET_UPGRADES_ALLOW_MISSING_MIGRATIONS = "1"
FLEET_LOGGING_JSON = "true"
FLEET_LOGGING_DEBUG = var.fleet_config.debug_logging
FLEET_SERVER_TLS = "false"
FLEET_S3_SOFTWARE_INSTALLERS_BUCKET = google_storage_bucket.software_installers.id
FLEET_S3_SOFTWARE_INSTALLERS_ACCESS_KEY_ID = google_storage_hmac_key.key.access_id
FLEET_S3_SOFTWARE_INSTALLERS_SECRET_ACCESS_KEY = google_storage_hmac_key.key.secret
FLEET_S3_SOFTWARE_INSTALLERS_ENDPOINT_URL = "https://storage.googleapis.com"
FLEET_S3_SOFTWARE_INSTALLERS_FORCE_S3_PATH_STYLE = "true"
FLEET_S3_SOFTWARE_INSTALLERS_REGION = var.region
})
fleet_vpc_network_id = module.vpc.network_id
# Use the direct construction for the subnet ID key as discussed
fleet_vpc_subnet_id = "fleet-subnet"
}
module "fleet-service" {
source = "GoogleCloudPlatform/cloud-run/google//modules/v2"
version = "0.17.2"
service_name = "fleet-api"
project_id = var.project_id
location = var.region
create_service_account = false
service_account = google_service_account.fleet_run_sa.email
enable_prometheus_sidecar = false
cloud_run_deletion_protection = false
vpc_access = {
network_interfaces = {
network = local.fleet_vpc_network_id
subnetwork = local.fleet_vpc_subnet_id
}
egress = "ALL_TRAFFIC"
}
ingress = "INGRESS_TRAFFIC_INTERNAL_LOAD_BALANCER"
timeout = "300s"
service_scaling = {
min_instance_count = 0
}
template_scaling = {
min_instance_count = 0
max_instance_count = 0
}
containers = [
{
container_image = local.fleet_image_tag
ports = {
name = "h2c"
container_port = 8080
}
# container_command = ["/bin/sh"]
# container_args = [
# "-c",
# "fleet prepare --no-prompt=true db; exec fleet serve"
# ]
startup_probe = {
initial_delay_seconds = 30
timeout_seconds = 2
period_seconds = 60
failure_threshold = 3
tcp_socket = {
port = 8080
}
}
liveness_probe = {
initial_delay_seconds = 30
timeout_seconds = 2
failure_threshold = 3
period_seconds = 60
http_get = {
path = "/healthz"
http_headers = []
}
}
resources = {
limits = local.fleet_resources_limits
}
env_vars = local.fleet_env_vars
env_secret_vars = local.fleet_secrets_env_vars
}
]
}
# --- Cloud Run Job (Migrations) ---
resource "google_cloud_run_v2_job" "fleet_migration_job" {
name = "fleet-migration"
location = var.region
project = var.project_id
template {
template { # Double template for jobs
service_account = google_service_account.fleet_run_sa.email # Defined in iam.tf
# Define vpc_access block directly
vpc_access {
network_interfaces {
network = local.fleet_vpc_network_id
subnetwork = local.fleet_vpc_subnet_id
}
egress = "ALL_TRAFFIC"
}
timeout = "3600s"
containers {
image = local.fleet_image_tag
# Define resources block directly
resources {
limits = local.fleet_resources_limits
}
dynamic "env" {
for_each = local.fleet_env_vars
content {
name = env.key
value = env.value
}
}
dynamic "env" {
for_each = local.fleet_secrets_env_vars
content {
name = env.key
value_source {
secret_key_ref {
secret = env.value.secret
version = env.value.version
}
}
}
}
command = ["fleet"]
args = ["prepare", "db", "--no-prompt=true"]
}
}
}
depends_on = [
google_service_account.fleet_run_sa,
google_secret_manager_secret_version.database_password,
]
}
data "google_client_config" "default" {}
resource "terracurl_request" "exec" {
count = var.fleet_config.exec_migration ? 1 : 0
name = "exec-job"
url = "https://run.googleapis.com/v2/${google_cloud_run_v2_job.fleet_migration_job.id}:run"
method = "POST"
headers = {
Authorization = "Bearer ${data.google_client_config.default.access_token}"
Content-Type = "application/json",
}
response_codes = [200]
// no-op destroy
// we don't use terracurl_request data source as that will result in
// repeated job runs on every refresh
destroy_url = "https://run.googleapis.com/v2/${google_cloud_run_v2_job.fleet_migration_job.id}"
destroy_method = "GET"
destroy_response_codes = [200]
destroy_headers = {
Authorization = "Bearer ${data.google_client_config.default.access_token}"
Content-Type = "application/json",
}
}
resource "google_compute_region_network_endpoint_group" "neg" {
name = "${var.prefix}-neg"
region = var.region
project = var.project_id
network_endpoint_type = "SERVERLESS" # This type works for Cloud Run v2 services
cloud_run {
service = module.fleet-service.service_name
}
depends_on = [module.fleet-service]
}
data "google_project" "project" {
project_id = var.project_id
}
resource "google_cloud_run_v2_service_iam_member" "allow_lb_invoker" {
project = var.project_id
location = module.fleet-service.location
name = module.fleet-service.service_name
role = "roles/run.invoker"
member = "allUsers"
}
+43
View File
@@ -0,0 +1,43 @@
resource "random_id" "suffix" {
byte_length = 5
}
module "private-service-access" {
source = "terraform-google-modules/sql-db/google//modules/private_service_access"
version = "~> 25.0"
project_id = var.project_id
vpc_network = module.vpc.network_name
deletion_policy = "ABANDON"
}
module "mysql" {
source = "terraform-google-modules/sql-db/google//modules/mysql"
version = "~> 25.0"
name = var.database_config.name
project_id = var.project_id
deletion_protection = var.database_config.deletion_protection
database_version = var.database_config.database_version
tier = var.database_config.tier
region = var.region
random_instance_name = true
enable_default_user = true
enable_default_db = true
user_name = var.database_config.database_user
db_name = var.database_config.database_name
db_collation = var.database_config.collation
db_charset = var.database_config.charset
ip_configuration = {
ipv4_enabled = false
# We never set authorized networks, we need all connections via the
# public IP to be mediated by Cloud SQL.
authorized_networks = []
require_ssl = false
private_network = module.vpc.network_self_link
}
module_depends_on = [module.private-service-access.peering_completed]
}
+50
View File
@@ -0,0 +1,50 @@
# -------------------------------------
# Service Account for Cloud Run Service & Job
# -------------------------------------
resource "google_service_account" "fleet_run_sa" {
project = var.project_id
account_id = "${var.prefix}-run-sa"
display_name = "Service Account for Fleet Cloud Run Service and Jobs"
}
resource "google_project_iam_member" "fleet_run_sa_sql_instance_user" {
project = var.project_id
role = "roles/cloudsql.instanceUser"
member = "serviceAccount:${google_service_account.fleet_run_sa.email}"
}
# Recommended for Cloud Run standard logging
resource "google_project_iam_member" "fleet_run_sa_log_writer" {
project = var.project_id
role = "roles/logging.logWriter"
member = "serviceAccount:${google_service_account.fleet_run_sa.email}"
}
# Recommended for Cloud Run standard metrics
resource "google_project_iam_member" "fleet_run_sa_monitoring_writer" {
project = var.project_id
role = "roles/monitoring.metricWriter"
member = "serviceAccount:${google_service_account.fleet_run_sa.email}"
}
resource "google_secret_manager_secret_iam_member" "fleet_run_sa_db_secret_access" {
project = var.project_id
secret_id = google_secret_manager_secret.database_password.id
role = "roles/secretmanager.secretAccessor"
member = "serviceAccount:${google_service_account.fleet_run_sa.email}"
depends_on = [google_secret_manager_secret.database_password]
}
resource "google_secret_manager_secret_iam_member" "fleet_run_sa_private_key_secret_access" {
project = var.project_id
secret_id = google_secret_manager_secret.private_key.id
role = "roles/secretmanager.secretAccessor"
member = "serviceAccount:${google_service_account.fleet_run_sa.email}"
depends_on = [google_secret_manager_secret.private_key]
}
+65
View File
@@ -0,0 +1,65 @@
locals {
# Clean the DNS record name for use in managed SSL cert domains (remove trailing dot)
managed_ssl_domain = trim(var.dns_record_name, ".")
}
# Create/Manage the DNS Zone in Cloud DNS
resource "google_dns_managed_zone" "fleet_dns_zone" {
project = var.project_id
name = "${var.prefix}-zone"
dns_name = var.dns_zone_name
}
# Configure the External HTTP(S) Load Balancer
module "fleet_lb" {
source = "GoogleCloudPlatform/lb-http/google//modules/serverless_negs"
version = "~> 12.0"
project = var.project_id
name = "${var.prefix}-lb" # e.g., fleet-lb
# SSL Configuration
ssl = true
https_redirect = true # Enforce HTTPS
managed_ssl_certificate_domains = [local.managed_ssl_domain]
# Backend Configuration
backends = {
default = {
description = "Backend for Fleet Cloud Run service"
enable_cdn = false # Set to true if you want Cloud CDN
protocol = "HTTP"
groups = [
{
group = google_compute_region_network_endpoint_group.neg.id
}
]
log_config = {
enable = true
sample_rate = 1.0 # Log all requests
}
# IAP (Identity-Aware Proxy) - disabled by default
iap_config = {
enable = false
}
}
}
depends_on = [google_compute_region_network_endpoint_group.neg]
}
# Create the DNS A Record for the Load Balancer
resource "google_dns_record_set" "fleet_dns_record" {
project = var.project_id
managed_zone = google_dns_managed_zone.fleet_dns_zone.name
name = var.dns_record_name
type = "A"
ttl = 300 # Time-to-live in seconds
# Point to the external IP address of the created load balancer
rrdatas = [module.fleet_lb.external_ip]
depends_on = [module.fleet_lb]
}
+16
View File
@@ -0,0 +1,16 @@
terraform {
required_version = "~> 1.11"
required_providers {
google = {
source = "hashicorp/google"
version = "6.35.0"
}
terracurl = {
source = "devops-rob/terracurl"
version = "~> 1.0"
}
}
}
data "google_client_config" "current" {}
+89
View File
@@ -0,0 +1,89 @@
output "fleet_application_url" {
description = "The primary URL to access the Fleet application (via the Load Balancer)."
value = "https://${google_dns_record_set.fleet_dns_record.name}"
}
output "load_balancer_ip_address" {
description = "The external IP address of the HTTP(S) Load Balancer."
value = module.fleet_lb.external_ip
}
output "cloud_run_service_name" {
description = "The name of the deployed Fleet Cloud Run service."
value = module.fleet-service.service_name
}
output "cloud_run_service_location" {
description = "The location of the deployed Fleet Cloud Run service."
value = module.fleet-service.location // Check the actual output name
}
output "mysql_instance_name" {
description = "The name of the Cloud SQL for MySQL instance."
value = module.mysql.instance_name
}
output "mysql_instance_connection_name" {
description = "The connection name for the Cloud SQL instance (used by Cloud SQL Proxy)."
value = module.mysql.instance_connection_name
}
output "redis_instance_name" {
description = "The name of the Memorystore for Redis instance."
value = module.memstore.id
}
output "redis_host" {
description = "The host IP address of the Memorystore for Redis instance."
value = module.memstore.host
}
output "redis_port" {
description = "The port number of the Memorystore for Redis instance."
value = module.memstore.port
}
output "software_installers_bucket_name" {
description = "The name of the GCS bucket for Fleet software installers."
value = google_storage_bucket.software_installers.name
}
output "software_installers_bucket_url" {
description = "The gsutil URL of the GCS bucket for Fleet software installers."
value = google_storage_bucket.software_installers.url
}
output "fleet_service_account_email" {
description = "The email address of the service account used by the Fleet Cloud Run service."
value = google_service_account.fleet_run_sa.email
}
output "dns_managed_zone_name" {
description = "The name of the Cloud DNS managed zone created for Fleet."
value = google_dns_managed_zone.fleet_dns_zone.name
}
output "dns_managed_zone_name_servers" {
description = "The authoritative name servers for the created Cloud DNS managed zone. Delegate your domain to these."
value = google_dns_managed_zone.fleet_dns_zone.name_servers
}
output "vpc_network_name" {
description = "The name of the VPC network created."
value = module.vpc.network_name
}
output "vpc_network_self_link" {
description = "The self-link of the VPC network created."
value = module.vpc.network_self_link
}
output "vpc_subnets_names" {
description = "List of subnet names created in the VPC."
value = module.vpc.subnets_names
}
output "cloud_router_name" {
description = "The name of the Cloud Router created for NAT."
value = module.cloud_router.router.name
}
+33
View File
@@ -0,0 +1,33 @@
resource "random_pet" "suffix" {
length = 1
}
resource "random_password" "private_key" {
length = 32
}
resource "google_secret_manager_secret" "database_password" {
project = var.project_id
secret_id = "fleet-db-password-${random_pet.suffix.id}"
replication {
auto {}
}
}
resource "google_secret_manager_secret_version" "database_password" {
secret = google_secret_manager_secret.database_password.name
secret_data = module.mysql.generated_user_password
}
resource "google_secret_manager_secret" "private_key" {
project = var.project_id
secret_id = "fleet-private-key-${random_pet.suffix.id}"
replication {
auto {}
}
}
resource "google_secret_manager_secret_version" "private_key" {
secret = google_secret_manager_secret.private_key.name
secret_data = random_password.private_key.result
}
+19
View File
@@ -0,0 +1,19 @@
resource "google_storage_hmac_key" "key" {
project = var.project_id
service_account_email = google_service_account.fleet_run_sa.email
}
resource "google_storage_bucket" "software_installers" {
project = var.project_id
name = var.fleet_config.installers_bucket_name
location = var.location
force_destroy = true
uniform_bucket_level_access = true
}
resource "google_storage_bucket_iam_member" "hmac_sa_storage_admin" {
bucket = google_storage_bucket.software_installers.name
role = "roles/storage.objectAdmin"
member = "serviceAccount:${google_service_account.fleet_run_sa.email}"
}
+120
View File
@@ -0,0 +1,120 @@
variable "project_id" {
description = "GCP project ID"
}
variable "location" {
default = "us"
}
variable "region" {
default = "us-central1"
}
variable "prefix" {
default = "fleet"
}
variable "dns_zone_name" {
description = "The DNS name of the managed zone (e.g., 'my-fleet-infra.com.')"
type = string
}
variable "dns_record_name" {
description = "The DNS record for Fleet (e.g., 'fleet.my-fleet-infra.com.')"
type = string
}
variable "cache_config" {
type = object({
name = string
tier = string
engine_version = string
connect_mode = string
memory_size = number
})
default = {
name = "fleet-cache"
tier = "STANDARD_HA"
engine_version = null // defaults to version 7
connect_mode = "PRIVATE_SERVICE_ACCESS"
memory_size = 1
}
}
variable "database_config" {
type = object({
name = string
database_name = string
database_user = string
collation = string
charset = string
deletion_protection = bool
database_version = string
tier = string
})
default = {
name = "fleet-mysql"
database_name = "fleet"
database_user = "fleet"
collation = "utf8mb4_unicode_ci"
charset = "utf8mb4"
deletion_protection = false
database_version = "MYSQL_8_0"
tier = "db-n1-standard-1"
}
}
variable "vpc_config" {
type = object({
network_name = string
subnets = list(object({
subnet_name = string
subnet_ip = string
subnet_region = string
subnet_private_access = bool
}))
})
default = {
network_name = "fleet-network"
subnets = [
{
subnet_name = "fleet-subnet"
subnet_ip = "10.10.10.0/24"
subnet_region = "us-central1"
subnet_private_access = true
}
]
}
}
variable "fleet_config" {
type = object({
installers_bucket_name = string
image_tag = string
fleet_cpu = string
fleet_memory = string
debug_logging = bool
license_key = optional(string)
min_instance_count = number
max_instance_count = number
exec_migration = bool
extra_env_vars = optional(map(string))
extra_secret_env_vars = optional(map(object({
secret = string
version = string
})))
})
default = {
image_tag = "fleetdm/fleet:v4.67.3"
installers_bucket_name = ""
fleet_cpu = "1000m"
fleet_memory = "4096Mi"
debug_logging = false
min_instance_count = 1
max_instance_count = 5
exec_migration = true
extra_env_vars = {}
extra_secret_env_vars = {}
}
}
+30
View File
@@ -0,0 +1,30 @@
# Example from your vpc.tf (ensure this part is correct)
locals {
network_name = "${var.prefix}-network"
subnet_name = "${var.prefix}-subnet"
}
module "vpc" {
source = "terraform-google-modules/network/google"
version = "11.0.0"
project_id = var.project_id
network_name = var.vpc_config.network_name
subnets = var.vpc_config.subnets
}
module "cloud_router" {
source = "terraform-google-modules/cloud-router/google"
version = "7.0"
name = "${var.prefix}-cloud-router"
project = var.project_id
network = module.vpc.network_name
region = var.region
nats = [{
name = "${var.prefix}-vpc-nat"
}]
}
+63
View File
@@ -0,0 +1,63 @@
terraform {
required_version = "~> 1.11"
required_providers {
google = {
source = "hashicorp/google"
version = "6.35.0"
}
}
}
provider "google" {
# Credentials used here need Org/Folder level permissions
default_labels = var.labels
}
module "project_factory" {
source = "terraform-google-modules/project-factory/google"
version = "~> 18.0.0"
name = var.project_name
random_project_id = var.random_project_id
org_id = var.org_id
billing_account = var.billing_account_id
default_service_account = "delete"
# Enable baseline APIs needed by most projects + your app stack
activate_apis = [
"compute.googleapis.com",
"sqladmin.googleapis.com",
"redis.googleapis.com",
"run.googleapis.com",
"vpcaccess.googleapis.com",
"secretmanager.googleapis.com",
"storage.googleapis.com",
"dns.googleapis.com",
"iam.googleapis.com",
"cloudresourcemanager.googleapis.com",
"serviceusage.googleapis.com",
"servicenetworking.googleapis.com",
"logging.googleapis.com",
"monitoring.googleapis.com",
"memorystore.googleapis.com",
"serviceconsumermanagement.googleapis.com",
"networkconnectivity.googleapis.com"
]
labels = var.labels
}
module "fleet" {
source = "./byo-project"
project_id = module.project_factory.project_id
dns_record_name = var.dns_record_name
dns_zone_name = var.dns_zone_name
vpc_config = var.vpc_config
fleet_config = var.fleet_config
cache_config = var.cache_config
database_config = var.database_config
region = var.region
location = var.location
}
+89
View File
@@ -0,0 +1,89 @@
output "fleet_application_url" {
description = "The primary URL to access the Fleet application (via the Load Balancer)."
value = module.fleet.fleet_application_url
}
output "load_balancer_ip_address" {
description = "The external IP address of the HTTP(S) Load Balancer."
value = module.fleet.load_balancer_ip_address
}
output "cloud_run_service_name" {
description = "The name of the deployed Fleet Cloud Run service."
value = module.fleet.cloud_run_service_name
}
output "cloud_run_service_location" {
description = "The location of the deployed Fleet Cloud Run service."
value = module.fleet.cloud_run_service_location
}
output "mysql_instance_name" {
description = "The name of the Cloud SQL for MySQL instance."
value = module.fleet.mysql_instance_name
}
output "mysql_instance_connection_name" {
description = "The connection name for the Cloud SQL instance (used by Cloud SQL Proxy)."
value = module.fleet.mysql_instance_connection_name
}
output "redis_instance_name" {
description = "The name of the Memorystore for Redis instance."
value = module.fleet.redis_instance_name
}
output "redis_host" {
description = "The host IP address of the Memorystore for Redis instance."
value = module.fleet.redis_host
}
output "redis_port" {
description = "The port number of the Memorystore for Redis instance."
value = module.fleet.redis_port
}
output "software_installers_bucket_name" {
description = "The name of the GCS bucket for Fleet software installers."
value = module.fleet.software_installers_bucket_name
}
output "software_installers_bucket_url" {
description = "The gsutil URL of the GCS bucket for Fleet software installers."
value = module.fleet.software_installers_bucket_url
}
output "fleet_service_account_email" {
description = "The email address of the service account used by the Fleet Cloud Run service."
value = module.fleet.fleet_service_account_email
}
output "dns_managed_zone_name" {
description = "The name of the Cloud DNS managed zone created for Fleet."
value = module.fleet.dns_managed_zone_name
}
output "dns_managed_zone_name_servers" {
description = "The authoritative name servers for the created Cloud DNS managed zone. Delegate your domain to these."
value = module.fleet.dns_managed_zone_name_servers
}
output "vpc_network_name" {
description = "The name of the VPC network created."
value = module.fleet.vpc_network_name
}
output "vpc_network_self_link" {
description = "The self-link of the VPC network created."
value = module.fleet.vpc_network_self_link
}
output "vpc_subnets_names" {
description = "List of subnet names created in the VPC."
value = module.fleet.vpc_subnets_names
}
output "cloud_router_name" {
description = "The name of the Cloud Router created for NAT."
value = module.fleet.cloud_router_name
}
+150
View File
@@ -0,0 +1,150 @@
variable "project_name" {
default = "fleet"
}
variable "org_id" {
description = "organization id"
}
variable "billing_account_id" {
description = "billing account id"
}
variable "labels" {
description = "resource labels"
default = { application = "fleet" }
type = map(string)
}
variable "fleet_image" {
default = "v4.67.3"
}
variable "dns_zone_name" {
description = "The DNS name of the managed zone (e.g., 'my-fleet-infra.com.')"
type = string
}
variable "dns_record_name" {
description = "The DNS record for Fleet (e.g., 'fleet.my-fleet-infra.com.')"
type = string
}
variable "random_project_id" {
default = true
}
variable "location" {
description = "The general location for resources, e.g., 'us' for GCS buckets."
type = string
default = "us"
}
variable "region" {
description = "The GCP region for regional resources."
type = string
default = "us-central1"
}
variable "prefix" {
description = "A prefix used for naming resources within the byo-project module."
type = string
default = "fleet"
}
variable "cache_config" {
description = "Configuration for the Memorystore (Redis) instance."
type = object({
name = string
tier = string
engine_version = string
connect_mode = string
memory_size = number
})
default = {
name = "fleet-cache"
tier = "STANDARD_HA"
engine_version = null // defaults to version 7
connect_mode = "PRIVATE_SERVICE_ACCESS"
memory_size = 1
}
}
variable "database_config" {
description = "Configuration for the Cloud SQL (MySQL) instance."
type = object({
name = string
database_name = string
database_user = string
collation = string
charset = string
deletion_protection = bool
database_version = string
tier = string
})
default = {
name = "fleet-mysql"
database_name = "fleet"
database_user = "fleet"
collation = "utf8mb4_unicode_ci"
charset = "utf8mb4"
deletion_protection = false
database_version = "MYSQL_8_0"
tier = "db-n1-standard-1"
}
}
variable "vpc_config" {
description = "Configuration for the VPC network and subnets."
type = object({
network_name = string
subnets = list(object({
subnet_name = string
subnet_ip = string
subnet_region = string
subnet_private_access = bool
}))
})
default = {
network_name = "fleet-network"
subnets = [
{
subnet_name = "fleet-subnet"
subnet_ip = "10.10.10.0/24"
subnet_region = "us-central1"
subnet_private_access = true
}
]
}
}
variable "fleet_config" {
description = "Configuration for the Fleet application deployment."
type = object({
installers_bucket_name = string
image_tag = string
fleet_cpu = string
fleet_memory = string
debug_logging = bool
license_key = optional(string)
min_instance_count = number
max_instance_count = number
exec_migration = bool
extra_env_vars = optional(map(string))
extra_secret_env_vars = optional(map(object({
secret = string
version = string
})))
})
default = {
image_tag = "fleetdm/fleet:v4.67.3"
installers_bucket_name = "" # Bucket names must be globally unique
fleet_cpu = "1000m"
fleet_memory = "4096Mi"
debug_logging = false
min_instance_count = 1
max_instance_count = 5
exec_migration = true
extra_env_vars = {}
extra_secret_env_vars = {}
}
}