Deploy on a VM (Hardened Org)
Overview
Section titled “Overview”Some GCP organizations enforce org policy constraints that a default (unrestricted) project does not have. scripts/single-node-vm/deploy.sh (see Deploy on a VM (GCE)) handles most of these automatically. This page covers which constraints are common, what the script does about each one, and the one manual step it cannot do for you.
Common constraints
Section titled “Common constraints”An organization with a hardening baseline commonly enforces some combination of:
constraints/compute.skipDefaultNetworkCreation— new projects do not get an auto-modedefaultVPC network (or itsdefault-allow-internalfirewall rule).constraints/compute.requireShieldedVm— GCE VMs must have Shielded VM features (Secure Boot, vTPM, integrity monitoring) enabled.- A disabled default Compute Engine service account — the
PROJECT_NUMBER-compute@developer.gserviceaccount.comaccount that GCE resources fall back to when no--service-accountis specified. constraints/iam.allowedPolicyMemberDomains— rejects IAM bindings for principals outside an allow-listed set of domains, including theallUsersprincipal.
What deploy.sh handles automatically
Section titled “What deploy.sh handles automatically”- Shielded VM. The hub VM is created with
--shielded-secure-boot(vTPM and integrity monitoring are already on by default for theubuntu-2204-ltsimage family this script uses). - The disabled default Compute Engine service account. The Cloud Run proxy is deployed with its own dedicated service account (
--service-account), created with no project IAM roles, instead of falling back to the project’s default Compute Engine service account. allUsers/ domain-restricted sharing. The Cloud Run IAP proxy is deployed in one step withgcloud beta run deploy ... --no-allow-unauthenticated --iap, so anallUsersIAM binding is never created.--iapgrants the IAP service agentroles/run.invokeron the service itself, butgcloudonly warns if that grant fails — it never fails the deploy. The script grants it explicitly as a safety net, so a failure there stops the deploy instead of leaving the proxy silently unreachable by IAP.- A dedicated proxy service account. Deploying the Cloud Run proxy with
--service-accountrequires the deployer to holdiam.serviceAccounts.actAson that service account. The Owner and Editor basic roles both include this permission; a deployer with a more narrowly-scoped role needsroles/iam.serviceAccountUsergranted on the SA specifically. - The scoped
tcp:8080ingress rule. Instead of depending on (or recreating) the broaddefault-allow-internalrule, the script creates its own rule scoped totcp:8080, sourced from the region’sdefaultsubnet CIDR, targeting only the hub VM’s network tag. You do not need to create any internal-allow firewall rule yourself — this replaces that need entirely, in every project, hardened or not. - An existing Cloud NAT. Before it creates the service account or any other resources, the script checks every Cloud Router in the region on network
defaultfor an existing NAT gateway. If one already covers thedefaultsubnet, the script reuses it and logs which router and NAT provide egress. If another NAT exists but does not coverdefault, the script creates its own NAT scoped to just that subnet (--nat-custom-subnet-ip-ranges=default) so the two gateways can coexist. The check requiresjqand fails closed if the NAT configuration cannot be read. - A missing
defaultnetwork. The script checks for thedefaultVPC network before creating anything else, and fails fast with a pointer to this page if it is missing. It does not create a VPC network on your behalf — see the one manual step below.
The one manual prerequisite: the default network
Section titled “The one manual prerequisite: the default network”If your organization enforces constraints/compute.skipDefaultNetworkCreation, create the auto-mode default VPC network once, before running deploy.sh:
gcloud compute networks create default \ --project=PROJECT_ID \ --subnet-mode=autoThat’s it. Do not also create a default-allow-internal firewall rule — deploy.sh creates its own narrowly-scoped tcp:8080 rule instead (see above), and a broad internal-allow rule is not needed for anything else this script sets up.
Verifying the prerequisite
Section titled “Verifying the prerequisite”gcloud compute networks describe default --project=PROJECT_IDIf this reports the network was not found, run the gcloud compute networks create command above before re-running deploy.sh.