I want to host my website on Cloud Run. The website is developed in React, and packaged using golang's "embed" package, which enables the React build assets to be served by the Go "wrapper" REST API.
I want to automatically create a DNS Zone and add the DNS records in there, then map the domain name to my Cloud Run based API/website. All in all, I want everything automated by Terraform.
I have written my main.tf
file like this:
# loal ariables
locals {
project_id = var.project_id
project_number = var.project_number
region = var.region
service_name = var.service_name
concurrency = var.concurrency
service_account = var.service_account
timeout = var.timeout
domain_name = var.domain_name
image_url = var.image_url
load_balancer_address_name = var.load_balancer_address_name
load_balancer_backend_name = var.load_balancer_backend_name
load_balancer_url_map_name = var.load_balancer_url_map_name
load_balancer_proxy_name = var.load_balancer_proxy_name
load_balancer_forwarding_rule_name = var.load_balancer_forwarding_rule_name
google_api_services = [
"cloudbilling.googleapis.com",
"serviceusage.googleapis.com",
"run.googleapis.com",
"cloudresourcemanager.googleapis.com",
"containerregistry.googleapis.com",
"iam.googleapis.com",
"dns.googleapis.com",
"compute.googleapis.com"
]
required_roles = [
"roles/run.admin",
"roles/run.invoker",
"roles/iam.serviceAccountUser",
"roles/logging.logWriter",
"roles/storage.admin",
"roles/storage.objectViewer",
"roles/owner",
"roles/resourcemanager.projectIamAdmin",
"roles/domains.admin"
]
}
# google provider
provider "google" {
project = local.project_id
region = local.region
}
# Enable Google Cloud APIs
resource "google_project_service" "enabled" {
for_each = toset(local.google_api_services)
service = each.key
disable_on_destroy = false
}
# Assign required roles/rights to the default service account
resource "google_project_iam_member" "default_service_account_roles" {
project = local.project_id
for_each = toset(local.required_roles)
role = each.key
member = "serviceAccount:${local.project_number}-compute@developer.gserviceaccount.com"
lifecycle {
prevent_destroy = true
}
}
# Create or Update Cloud Run service
resource "google_cloud_run_service" "mw_cloud_run_svc" {
name = local.service_name
location = local.region
template {
spec {
containers {
image = local.image_url
}
}
}
metadata {
annotations = {
"run.googleapis.com/ingress" = "all"
}
}
traffic {
percent = 100
latest_revision = true
}
}
# allow everyone on internet to access this cloud run service
resource "google_cloud_run_service_iam_member" "cloud_run_svc_invoker_policy_1" {
project = local.project_id
service = google_cloud_run_service.mw_cloud_run_svc.name
location = google_cloud_run_service.mw_cloud_run_svc.location
role = "roles/run.invoker"
member = "allUsers"
}
# DNS Managed Zone
resource "google_dns_managed_zone" "my_dns_zone" {
name = "my-dns-zone"
dns_name = "${local.domain_name}."
description = "Managed zone for ${local.domain_name}."
labels = {
environment = "production"
}
depends_on = [google_project_service.enabled]
}
# DNS Record Sets
resource "google_dns_record_set" "root_subdomain" {
name = "${local.domain_name}."
type = "A"
ttl = 300
managed_zone = google_dns_managed_zone.my_dns_zone.name
rrdatas = [
google_cloud_run_service.mw_cloud_run_svc.status[0].url
]
}
resource "google_dns_record_set" "www_subdomain" {
name = "www.${local.domain_name}."
type = "CNAME"
ttl = 300
managed_zone = google_dns_managed_zone.my_dns_zone.name
rrdatas = [
google_cloud_run_service.mw_cloud_run_svc.status[0].url
]
}
# use the authenticated (existing) project -> "my-website"
data "google_project" "my_website" {}
# map cloud run service to a custom domain name
resource "google_cloud_run_domain_mapping" "myweb" {
name = local.domain_name
location = google_cloud_run_service.mw_cloud_run_svc.location
metadata {
namespace = data.google_project.my_website.project_id
}
spec {
route_name = google_cloud_run_service.mw_cloud_run_svc.name
}
depends_on = [
google_dns_managed_zone.mahani_website_dns_zone,
google_dns_record_set.root_subdomain,
google_dns_record_set.www_subdomain
]
}
#===============================
output "url" {
value = google_cloud_run_service.mw_cloud_run_svc.status[0].url
}
output "domain_mapping" {
value = google_cloud_run_domain_mapping.myweb
}
I get this Error:
Error: Error creating DNS RecordSet: googleapi: Error 400: Invalid value for 'entity.change.additions[mywebxxx.com.][A].rrdata[0]': 'https://***-x4kxyob2wq-ue.a.run.app', invalid
│
│ with google_dns_record_set.root_subdomain,
│ on main.tf line 107, in resource "google_dns_record_set" "root_subdomain":
│ 107: resource "google_dns_record_set" "root_subdomain" ***
│
╵
╷
│ Error: Error creating DNS RecordSet: googleapi: Error 400: Invalid value for 'entity.change.additions[www.mywebxxx.com.][CNAME].rrdata[0]': 'https://***-x4kxyob2wq-ue.a.run.app', invalid
│
│ with google_dns_record_set.www_subdomain,
│ on main.tf line 118, in resource "google_dns_record_set" "www_subdomain":
│ 118: resource "google_dns_record_set" "www_subdomain" ***
NOTE: I had to first manually add some permissions to the service account that authenticates Terraform, by running the following commands:
gcloud projects add-iam-policy-binding <PROJECT_ID> --member='serviceAccount:<PROJECT_NUMBER>-compute@developer.gserviceaccount.com' --role='roles/iam.serviceAccountUser'
gcloud projects add-iam-policy-binding <PROJECT_ID> --member='serviceAccount:<PROJECT_NUMBER>-compute@developer.gserviceaccount.com' --role='roles/run.admin'
gcloud projects add-iam-policy-binding <PROJECT_ID> --member='serviceAccount:<PROJECT_NUMBER>-compute@developer.gserviceaccount.com' --role='roles/resourcemanager.projectIamAdmin'