How to Create Cloud Armor on GCP Using Terraform

Alandev
4 min readDec 3, 2023

--

👨‍💻 Introduction

In this article explains how to configure Google Cloud Armor security policies using Terraform on Google Cloud Platform (GCP).

Prerequisites

  • Google Cloud Platform (GCP) Account: Make sure you have a valid GCP account.
  • Install Terraform: If Terraform is not already installed, refer to the Terraform Installation Guide.
  • Basic Terraform Knowledge: Familiarize yourself with basic Terraform commands and concepts. The Terraform Getting Started Guide can be a helpful resource.

🎯 Setup

Step 1: Create provider.tf

Define the GCP Provider and the Terraform version required.

terraform {
required_providers {
google = {
source = "hashicorp/google"
version = "5.7.0"
}
}
}

provider "google" {
project = "your-project-id"
region = "asia-east1"
}

Step 2: Create main.tf

In this file, define the Google Cloud Armor security policy resources.

  • Security Policy
resource "google_compute_security_policy" "default" {
name = "my-security-policy"

rule {
action = "allow"
priority = 1000
match {
versioned_expr = "SRC_IPS_V1"
config {
src_ip_ranges = ["0.0.0.0/0"]
}
}
}
}

Parameter Introduction

  • name: The name of the security policy.
  • action: The action to take (e.g., ‘allow’ or ‘deny’).
  • priority: The priority of the rule.
  • versioned_expr: The version of the expression.
  • src_ip_ranges: Source IP ranges to match.

Step 3: Initialize Terraform

terraform init

Step 4: Execute Dry Run

terraform plan

Step 5: Deploy

terraform apply

Resource Deletion

To clean up resources after deployment:

terraform destroy

🛠️ Modularizing Terraform Configuration for Google Cloud Armor

Understanding Dynamic Blocks and Iterator

Before diving into the modularization of the Terraform configuration, let’s understand two advanced features of Terraform: dynamic blocks and iterator.

  • Dynamic Blocks: In Terraform, dynamic blocks are used to dynamically construct repeatable nested blocks within a resource based on a complex variable (like a list or map). This feature is particularly useful when you need to create multiple similar configurations within a resource but the exact number or composition of these blocks may vary.
  • Iterator: An iterator is an optional argument used within a dynamic block that allows you to customize the naming of the temporary variable for each item in the complex variable. By default, Terraform uses each as the iterator, but you can specify a different name to improve clarity or avoid conflicts with other variables.

1. Refactoring main.tf for Modularity

When refactoring main.tf for the Google Cloud Armor module, we'll use dynamic blocks and an iterator to efficiently create security rules from a list of objects.

  • Google Cloud Armor Module:
resource "google_compute_security_policy" "main" {
name = var.name

dynamic "rule" {
for_each = var.rules_src_ip_ranges
iterator = allow
content {
action = allow.value.action
priority = allow.value.priority
match {
versioned_expr = "SRC_IPS_V1"
config {
src_ip_ranges = allow.value.ranges
}
}
description = allow.value.description
}
}

dynamic "rule" {
for_each = var.rules_expression
iterator = deny
content {
action = deny.value.action
priority = deny.value.priority
match {
expr {
expression = deny.value.expression
}
}
description = deny.value.description
}
}
}

In this example:

  • We use two dynamic blocks to create different types of security rules: one for source IP range-based rules and another for expression-based rules.
  • Each dynamic block iterates over a list of objects (either rules_src_ip_ranges or rules_expression), creating a new rule for each object in the list.
  • The iterator (named allow for the first block and deny for the second) refers to each item in the list during each iteration.

2. Defining Variables in variables.tf

Define the necessary variables in a variables.tf file. These variables will be used to dynamically generate the security rules in the module.

  • Variable Definitions:
variable "security_policy_name" {
description = "The name for the security policy"
type = string
}

variable "rules_src_ip_ranges" {
description = "A list of security rules src ip ranges to be applied"
type = list(object({
action = string
priority = number
ranges = list(string)
description = string
}))
}

variable "rules_expression" {
description = "A list of security rules expression to be applied"
type = list(object({
action = string
priority = number
expression = string
description = string
}))
}

3. Creating the Google Cloud Armor Module

Create a directory modules/cloud_armor and add a cloud_armor.tf.

  • Module Definition (cloud_armor.tf):
module "google_cloud_armor" {
source = "../../cloud_armor"
name = "my-security-policy"

rules_src_ip_ranges = [{
action = "allow"
priority = 1
ranges = ["0.0.0.0/0"]
description = "allow all"
}
]

rules_expression = [{
action = "deny(403)"
priority = 2
expression = "request.headers['x-forwarded-for'].contains('127.0.0.1')"
description = "deny 127.0.0.1"
}
]
}
  • Here’s an example of how you might structure your folders and files for this project:
terraform-google-cloud-armor/

├── main.tf # Main Terraform configuration file
├── provider.tf # Provider configuration
├── variables.tf # Variables specific to the Cloud Armor module
|
├── modules/ # Directory containing all modular code
│ └── cloud_armor/ # Google Cloud Armor module
│ ├── main.tf # Main file for the Cloud Armor module

└── terraform.tfvars # (Optional) File to define values for your variables

4. Applying the Modular Configuration

Run the following Terraform commands in your project’s modules/cloud_armor directory:

  • Initialize Terraform:
terraform init
  • Execute a Dry Run:
terraform plan
  • Apply the Configuration:
terraform apply

This modular approach to configuring Google Cloud Armor using Terraform offers a scalable and manageable way to handle security policies.

📚References

--

--