Cloud Armor is a Google Cloud DDoS protection service to protect against multiple threats by enabling OWASP rules. Cloud Armor Security Policies help protect your application by providing Layer 7 filtering and scanning incoming requests for common web application attacks.
Each Security Policy is a combination of a set of rules that filter the traffic based on conditions such as an incoming request’s IP Address, IP Range, Geo-location, Request Headers, etc.
Terraform provides a declarative way to create, audit and attach Cloud Armor policies to backends behind Google Cloud load balancers. This article covers policy creation with Terraform, module usage, Cloud Run integration, releases and operational considerations.
Introduction to Cloud Armor and Terraform
Cloud Armor operates at the edge of Google’s network and inspects HTTP(S) traffic before it reaches backend services. Protection is applied through Security Policies that contain ordered rules with actions such as allow, deny(403), ratebasedban.
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.
A typical architecture is:
- Users -> Cloud Armor WAF Policy
- Cloud Armor -> External HTTP(S) Load Balancer
- Load Balancer -> Serverless NEG
- Serverless NEG -> Cloud Run Service
Terraform is already installed on your system and the service account executing terraform has compute admin role is a common assumption for these setups. The web server is running on Instance Groups, the front end is Layer 7 Load Balancer and the backend is already configured is the baseline for many Instance Group based deployments.
Prerequisites and Project Structure
A working Cloud Armor Terraform setup starts with tooling and project layout.
Prerequisites are:
- Google Cloud SDK installed and configured
- Terraform installed (version 1.0.0 or later)
- A GCP project with billing enabled
Project Structure recommended for modularity:
.
├── main.tf # Main Terraform configuration file
├── variables.tf # Variable definitions
├── outputs.tf # Output definitions
├── terraform.tfvars # Variable values
└── modules/
└── armor/
├── main.tf # Cloud Armor specific configurations
├── variables.tf # Module variables
├── policies.tf # Security policy configurations
└── outputs.tf # Module outputs
Provider configuration uses the Google provider.
terraform {
required_providers {
google = {
source = "hashicorp/google"
version = "~> 4.0"
}
}
}
provider "google" {
project = var.project_id
region = var.region
}
Variables commonly defined are:
variable "project_id" {
description = "The ID of the GCP project"
type = string
}
variable "region" {
description = "The region to deploy resources to"
type = string
default = "us-central1"
}
variable "policy_name" {
description = "Name of the Cloud Armor security policy"
type = string
}
Basic Security Policy with Terraform
Resource google_compute_security_policy is the core Terraform resource for Cloud Armor.
A basic security policy includes a default deny rule and an allow rule for internal ranges.
```
resource "googlecomputesecuritypolicy" "policy" {
name = var.policyname
description = "Cloud Armor security policy managed by Terraform"
Default rule (deny all)
rule {
action = "deny(403)"
priority = "2147483647"
match {
versionedexpr = "SRCIPSV1"
config {
srcip_ranges = ["*"]
}
}
description = "Default deny rule"
}
Allow specific IP ranges
rule {
action = "allow"
priority = "1000"
match {
versionedexpr = "SRCIPSV1"
config {
srcip_ranges = ["192.168.1.0/24", "10.0.0.0/8"]
}
}
description = "Allow internal IP ranges"
}
}
```
The agenda for a typical blog implementation is:
- Assumptions
- Creating policies using Terraform
- Attaching the policy to the Backends
- Auditing logs
- Remediation
- References
In this blog, we will use Terraform to create a security policy and add rules to it, other ways are using Google Cloud Console and gcloud commands. For the backend, we are going to use Instance Groups
Advanced Security Rules and WAF Protection
Advanced policies add OWASP preconfigured expressions and rate limiting.
```
resource "googlecomputesecuritypolicy" "advancedpolicy" {
name = "${var.policy_name}-advanced"
description = "Advanced Cloud Armor security policy"
OWASP Top 10 Protection
rule {
action = "deny(403)"
priority = "1000"
match {
expr {
expression = "evaluatePreconfiguredExpr('xss-stable')"
}
}
description = "Prevent XSS attacks"
}
rule {
action = "deny(403)"
priority = "1001"
match {
expr {
expression = "evaluatePreconfiguredExpr('sqli-stable')"
}
}
description = "Prevent SQL injection"
}
Rate limiting
rule {
action = "ratebasedban"
priority = "2000"
match {
versionedexpr = "SRCIPSV1"
config {
srcipranges = ["*"]
}
}
description = "Rate limiting rule"
ratelimitoptions {
ratelimitthreshold {
count = 100
intervalsec = 60
}
conformaction = "allow"
exceedaction =
```
Rate limiting uses rate_limit_options with rate_limit_threshold specifying count and interval_sec. Conform and exceed actions define allow vs ban behavior.
Cloud Armor with Cloud Run Behind Load Balancer
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 (WAF) that sits in front of your load balancer and filters malicious traffic before it reaches your service.
Step 1: Deploy the Cloud Run Service
The full setup with Terraform requires:
- Cloud Run service creation
- Serverless Network Endpoint Group
- External HTTP(S) Load Balancer with backend pointing to NEG
- Cloud Armor security policy attached to backend service
Architecture Overview
graph LR
Users["Users"] --> CloudArmor["Cloud Armor<br/>WAF Policy"]
CloudArmor --> LB["External HTTP(S)<br/>Load Balancer"]
LB --> NEG["Serverless NEG"]
NEG --> CloudRun["Cloud Run<br/>Service"]
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
Example module usage:
```
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"
Pre-configured WAF Rules
preconfiguredrules = {
"sqlisensitivitylevel4" = {
action = "deny(502)"
priority = 1
targetruleset = "sqli-v33-stable"
sensitivitylevel = 4
description = "sqli-v33-stable Sensitivity Level 4 and 2
```
Upgrade example:
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 = {}
}
Releases and Breaking Changes
The Terraform Google Cloud Armor module evolves with the Google provider.
Release history highlights:
| Version | Date | Notable Change |
| v8.1.0 | 2026-03-26 | add exceedredirectoptions to ratelimitoptions for all rule types |
| v8.0.0 | 2026-02-23 | BREAKING: add labels and requestbodyinspectionsize in global backend security policy |
| v7.0.0 | 2025-10-28 | BREAKING: added advancedoptionsconfig |
| v6.0.0 | 2025-09-11 | BREAKING: 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: TPG>=6.14: added layer7ddosdefensethresholdconfigs |
v8.1.0 Features:
add exceedredirectoptions to ratelimitoptions for all rule types (#192) (1e9d86a)
v8.0.0 BREAKING CHANGES
TPG > 7.17: add labels and requestbodyinspection_size in global backend security policy (#189)
Features
TPG > 7.17: add labels and requestbodyinspection_size in global backend security policy (#189) (7db91c0)
Bug Fixes
cloud-armor: allow adaptive protection block when L7 DDoS is disabled (#186)
regional-backend-security-policy Module requestbodyinspection_size variable description (#184)
Attaching Policies and Auditing
Creating policies using Terraform is only half the workflow. Attaching the policy to the Backends is required for enforcement.
For Instance Groups behind Layer 7 Load Balancer, the backend service resource is updated with security_policy attribute referencing the google_compute_security_policy.policy.name.
Auditing logs can be enabled via log_level set to VERBOSE on the policy module, and Cloud Armor logs can be exported to Cloud Logging for analysis.
Remediation steps typically involve:
- Reviewing denied requests in logs
- Adjusting rule priorities
- Updating allow lists
- Testing changes in staging before production apply
Operational Considerations
- Terraform is already installed on your system
- Service account executing terraform has compute admin role
- Provider version pinning matters because breaking changes in TPG > 7.17 and 6.14 affect module parameters
- Default rule should be deny with high priority 2147483647 to ensure safe fallback
- Rate limiting thresholds should be tuned per endpoint to avoid false positives
- Adaptive Protection auto deploy can be enabled via module input
adaptive_protection_auto_deploy
Conclusion
Terraform Cloud Armor management provides repeatable, auditable WAF policy definition for Google Cloud. Basic policies start with IP allow/deny rules and default deny, while advanced policies add OWASP preconfigured expressions for XSS and SQLi and rate based bans. Cloud Run protection requires an external HTTP(S) load balancer in front of the service, after which the Cloud Armor policy attaches to the backend service.
The official GoogleCloudPlatform/terraform-google-cloud-armor module abstracts five rule types - Pre-Configured, Security, Custom CEL, Threat Intelligence, and Adaptive Protection - and supports multiple load balancer types. Release history shows active development with breaking changes around labels, requestbodyinspectionsize, advancedoptionsconfig, and layer7ddosdefensethresholdconfigs, making version pinning and upgrade guides essential for production.
Depth of configuration is achieved through modular project layout with variables.tf, policies.tf, outputs.tf, and careful provider configuration. Attaching policies to backends, enabling verbose logging, and auditing denies completes the operational loop. With Terraform 1.3+ compatibility and provider constraints, teams can deliver consistent Cloud Armor WAF policies across Instance Groups and Cloud Run services behind load balancers.