Google Cloud Armor is a cloud-based Web Application Firewall provided by Google. Terraform is a tool provided by HashiCorp that automates the building of computer and network infrastructure. Combining them allows security policies to be declared as code for services behind Google Cloud load balancers, including serverless workloads such as Cloud Run. This article covers the architecture, Terraform setup, module usage, and operational patterns for Cloud Armor with Terraform using only the documented reference facts.
Cloud Armor fundamentals and Terraform scope
Cloud Armor helps protect Google Cloud deployments from multiple types of threats, including distributed denial-of-service attacks and application attacks like cross-site scripting and SQL injection. Cloud Armor features some automatic protections and some that you need to configure manually. This document provides a high-level overview of these features, several of which are only available for global external Application Load Balancers and classic Application Load Balancers.
Security policies can be configured manually, with configurable match conditions and actions in a security policy. Cloud Armor also features preconfigured security policies, which cover a variety of use cases. Cloud Armor lets you define prioritized rules with configurable match conditions and actions in a security policy. A rule takes effect, meaning that the configured action is applied, if the rule is the highest priority rule whose attributes match the attributes of the incoming request.
Terraform is used to automate the building of this protection. The reference implementation reviewed in the handbook uses the following structure:
.
|- main.tf
|- load_balancing.tf
|- cloud_run.tf
|- cloud_armor.tf
|- myCredentials.json
The file myCredentials.json contains the secret key for the service account used by Terraform. In this example, the service account used by Terraform has been granted the Cloud Run Admin role.
Since detailed operations require both google and google-beta providers, provider blocks are configured for each.
provider "google" {
credentials = file("myCredentials.json")
project = var.project_id
region = var.region
}
provider "google-beta" {
credentials = file("myCredentials.json")
project = var.project_id
region = var.region
}
Variables are used for data that is easier to handle when input at runtime:
variable "name" {}
variable "project_id" {}
variable "ssl" {}
variable "domain" {}
variable "region" {}
Outputs expose useful runtime values:
output "lb_ip" {
value = module.lb-http.external_ip
}
Because Cloud Armor attaches to Load Balancer backends, the Terraform project must first create the load balancing resources, the Cloud Run service, and then the security policy that binds to the backend.
Cloud Run behind a load balancer requirement
Cloud Run services are publicly accessible by default. While that is fine for development, production services need protection against common web attacks like SQL injection, cross-site scripting, and DDoS. Google Cloud Armor provides a Web Application Firewall that sits in front of your load balancer and filters malicious traffic before it reaches your service.
The catch is that Cloud Armor only works with external HTTP(S) load balancers, not with Cloud Run's default URL. So you need to put your Cloud Run service behind a load balancer first, then attach Cloud Armor policies to it.
Architecture Overview
Users --> Cloud Armor WAF Policy
Cloud Armor --> External HTTP(S) Load Balancer
Load Balancer --> Serverless NEG
Serverless NEG --> Cloud Run Service
Step 1 is to deploy the Cloud Run service itself. Step 2 is to create a Serverless Network Endpoint Group that points to the Cloud Run service. Step 3 is to create an external HTTP(S) load balancer that uses the NEG as backend. Step 4 is to create a Cloud Armor security policy and attach it to the backend service of the load balancer.
This ordering is required because Cloud Armor policies attach to backend services exposed by load balancer types, not directly to Cloud Run.
GoogleCloudPlatform Terraform module for Cloud Armor
The official module makes it easy to setup Cloud Armor Global Backend Security Policy with Security rules. You can attach the global Security policy to the backend services exposed by the following load balancer types:
- Global external Application Load Balancer HTTP/HTTPS
- Classic Application Load Balancer HTTP/HTTPS
- Global external proxy Network Load Balancer TCP/SSL
- Classic proxy Network Load Balancer TCP/SSL
There are five type of rules you can create in each policy:
- Pre-Configured Rules: These are based on pre-configured waf rules.
- Security Rules: Allow or Deny traffic from list of IP addresses or IP address ranges.
- Custom Rules: You can create your own rules using Common Expression Language CEL.
- Threat Intelligence Rules: Add Rules based on threat intelligence. Managed protection plus subscription is needed to use this feature.
- Automatically deploy Adaptive Protection Suggested Rules; When enable module will create a rule for automatically deploying the suggested rules that Adaptive Protection generates.
NOTE: For external passthrough Network Load Balancers, protocol forwarding and VMs with public IP addresses create network Edge Security policy using advanced network DDoS protection and network edge security policy sub-modules.
This module is meant for use with Terraform 1.3+ and tested using Terraform 1.3+. If you find incompatibilities using Terraform >=1.3, please open an issue. Current version is 2.X.
Module inputs and example configuration
Upgrade guides show usage as follows:
module security_policy {
source = "GoogleCloudPlatform/cloud-armor/google"
project_id = "my-project-id"
name = "my-test-ca-policy"
description = "Test Cloud Armor security policy with preconfigured rules, security rules and custom rules"
default_rule_action = "deny(403)"
type = "CLOUD_ARMOR"
layer_7_ddos_defense_enable = true
layer_7_ddos_defense_rule_visibility = "STANDARD"
recaptcha_redirect_site_key = google_recaptcha_enterprise_key.primary.name
json_parsing = "STANDARD"
log_level = "VERBOSE"
pre_configured_rules = {}
security_rules = {}
custom_rules = {}
threat_intelligence_rules = {}
adaptive_protection_auto_deploy = {}
}
Simple usage is as follows:
```
module "securitypolicy" {
source = "GoogleCloudPlatform/cloud-armor/google"
version = "~> 8.0"
projectid = var.projectid
name = "my-test-security-policy"
description = "Test Security Policy"
recaptcharedirectsitekey = googlerecaptchaenterprisekey.primary.name
defaultruleaction = "allow"
type = "CLOUDARMOR"
layer7ddosdefenseenable = true
layer7ddosdefenserule_visibility = "STANDARD"
preconfiguredrules = {
"sqlisensitivitylevel4" = {
action = "deny(502)"
priority = 1
targetruleset = "sqli-v33-stable"
sensitivitylevel = 4
description = "sqli-v33-stable Sensitivity Level 4 and 2
```
Rule details and Sample Code for each type of rule is available.
Rule types and operational modes
Pre-configured rules use Google maintained rule sets. Security rules allow or deny based on IP lists. Custom rules use CEL expressions. Threat Intelligence rules require Managed protection plus subscription. Adaptive Protection can auto deploy suggested rules.
Preview mode is used to validate rules without enforcement. In preview mode, rules log what they would do without actually blocking traffic. This lets you check for false positives before enabling enforcement:
rule {
action = "deny(403)"
priority = 1000
preview = true
match {
expr {
expression = "evaluatePreconfiguredWaf('sqli-v33-stable')"
}
}
}
Review the logs for a few days, tune the rules, then set preview = false to enforce.
Release history and breaking changes
The module tracks Terraform Google Provider changes. Selected releases:
| Version | Release Date | Notes |
|---|---|---|
| v8.1.0 | 2026-03-26 | add exceedredirectoptions to ratelimitoptions for all rule types |
| v8.0.0 | 2026-02-23 | BREAKING CHANGES TPG > 7.17: add labels and requestbodyinspection_size in global backend security policy |
| v7.0.0 | 2025-10-28 | BREAKING CHANGES added advancedoptionsconfig |
| v6.0.0 | 2025-09-11 | BREAKING CHANGES allowed max provider version to v7.X |
| v5.1.0 | 2025-05-06 | added reCAPTCHA actiontokensitekeys and sessiontokensitekeys |
| v5.0.0 | 2025-02-13 | BREAKING CHANGES TPG>=6.14: added layer7ddosdefensethreshold_configs |
These releases demonstrate the module's evolution with provider capabilities, including labels, request body inspection size, adaptive protection, and rate limit options.
File layout for a complete Cloud Run + Cloud Armor project
The handbook example separates concerns across files for maintainability.
- main.tf: Provider configuration, variables, outputs
- load_balancing.tf: External HTTP(S) load balancer, backend service, URL map, proxy, NEG
- cloud_run.tf: Cloud Run service definition and IAM
- cloud_armor.tf: Security policy definition and attachment to backend service
- myCredentials.json: Service account key
This separation allows independent testing of each layer and clearer diffs in version control.
Practical workflow
First, deploy the Cloud Run service itself with Terraform. Create the serverless NEG referencing the service. Create the external HTTP(S) load balancer with a backend service pointing to the NEG. Create a managed SSL certificate if HTTPS is required. Create the Cloud Armor security policy with desired rule types. Attach the policy to the backend service.
Protecting a Cloud Run service with Cloud Armor requires several pieces working together - the serverless NEG, the external load balancer, the SSL certificate, and the security policy itself. The Terraform configuration gives you OWASP protection, rate limiting, geo-blocking, and adaptive DDoS defense. Start with rules in preview mode, verify there are no false positives, and then enable enforcement for production traffic.
Supported load balancer and rule matrix
| Load Balancer Type | Cloud Armor Support |
|---|---|
| Global external Application Load Balancer HTTP/HTTPS | Global Backend Security Policy |
| Classic Application Load Balancer HTTP/HTTPS | Global Backend Security Policy |
| Global external proxy Network Load Balancer TCP/SSL | Global Backend Security Policy |
| Classic proxy Network Load Balancer TCP/SSL | Global Backend Security Policy |
| External passthrough Network Load Balancer | Network Edge Security Policy |
Rule categories available per policy:
- Pre-Configured Rules
- Security Rules
- Custom Rules
- Threat Intelligence Rules
- Adaptive Protection Auto Deploy
Conclusion
Terraform configuration of Google Cloud Armor for Cloud Run services requires understanding the dependency chain between Cloud Run, Serverless NEG, external HTTP(S) load balancer, and security policy attachment. The official GoogleCloudPlatform terraform-google-cloud-armor module provides a tested interface for creating global backend security policies with pre-configured WAF rules, IP based security rules, CEL custom rules, threat intelligence rules, and adaptive protection auto deployment. Provider blocks for google and google-beta are required for full feature coverage, and credentials are supplied via a service account JSON file with appropriate roles such as Cloud Run Admin.
Operational best practice is to start rules in preview mode to log evaluatePreconfiguredWaf matches without blocking, tune sensitivity and priority, then enable enforcement. Module versions carry breaking changes tied to Terraform Provider Google changes, so pinning version and reviewing release notes for labels, requestbodyinspectionsize, layer7ddosdefensethresholdconfigs, and advancedoptionsconfig is essential for stable upgrades. With the correct file separation and module usage, Terraform delivers repeatable, auditable Cloud Armor protection for serverless workloads behind Google Cloud load balancers.