fastly_service_waf_configuration

Defines a set of Web Application Firewall configuration options that can be used to populate a service WAF. This resource will configure rules, thresholds and other settings for a WAF.

Example Usage

Basic usage:

resource "fastly_service_vcl" "demo" {
  name = "demofastly"

  domain {
    name    = "example.com"
    comment = "demo"
  }

  backend {
    address = "127.0.0.1"
    name    = "origin1"
    port    = 80
  }

  condition {
    name      = "WAF_Prefetch"
    type      = "PREFETCH"
    statement = "req.backend.is_origin"
  }

  # This condition will always be false
  # adding it to the response object created below
  # prevents Fastly from returning a 403 on all of your traffic.
  condition {
    name      = "WAF_always_false"
    statement = "false"
    type      = "REQUEST"
  }

  response_object {
    name              = "WAF_Response"
    status            = "403"
    response          = "Forbidden"
    content_type      = "text/html"
    content           = "<html><body>Forbidden</body></html>"
    request_condition = "WAF_always_false"
  }

  waf {
    prefetch_condition = "WAF_Prefetch"
    response_object    = "WAF_Response"
  }

  force_destroy = true
}

resource "fastly_service_waf_configuration" "waf" {
  waf_id                         = fastly_service_vcl.demo.waf[0].waf_id
  http_violation_score_threshold = 100
}

Usage with rules:

resource "fastly_service_vcl" "demo" {
  name = "demofastly"

  domain {
    name    = "example.com"
    comment = "demo"
  }

  backend {
    address = "127.0.0.1"
    name    = "origin1"
    port    = 80
  }

  condition {
    name      = "WAF_Prefetch"
    type      = "PREFETCH"
    statement = "req.backend.is_origin"
  }

  # This condition will always be false
  # adding it to the response object created below
  # prevents Fastly from returning a 403 on all of your traffic.
  condition {
    name      = "WAF_always_false"
    statement = "false"
    type      = "REQUEST"
  }

  response_object {
    name              = "WAF_Response"
    status            = "403"
    response          = "Forbidden"
    content_type      = "text/html"
    content           = "<html><body>Forbidden</body></html>"
    request_condition = "WAF_always_false"
  }

  waf {
    prefetch_condition = "WAF_Prefetch"
    response_object    = "WAF_Response"
  }

  force_destroy = true
}

resource "fastly_service_waf_configuration" "waf" {
  waf_id                          = fastly_service_vcl.demo.waf[0].waf_id
  http_violation_score_threshold  = 100

  rule {
    modsec_rule_id = 1010090
    revision       = 1
    status         = "log"
  }
}

Usage with rule exclusions:

resource "fastly_service_vcl" "demo" {
  name = "demofastly"

  domain {
    name    = "example.com"
    comment = "demo"
  }

  backend {
    address = "127.0.0.1"
    name    = "origin1"
    port    = 80
  }

  condition {
    name      = "WAF_Prefetch"
    type      = "PREFETCH"
    statement = "req.backend.is_origin"
  }

  # This condition will always be false
  # adding it to the response object created below
  # prevents Fastly from returning a 403 on all of your traffic.
  condition {
    name      = "WAF_always_false"
    statement = "false"
    type      = "REQUEST"
  }

  response_object {
    name              = "WAF_Response"
    status            = "403"
    response          = "Forbidden"
    content_type      = "text/html"
    content           = "<html><body>Forbidden</body></html>"
    request_condition = "WAF_always_false"
  }

  waf {
    prefetch_condition = "WAF_Prefetch"
    response_object    = "WAF_Response"
  }

  force_destroy = true
}

resource "fastly_service_waf_configuration" "waf" {
  waf_id                         = fastly_service_vcl.demo.waf[0].waf_id
  http_violation_score_threshold = 100

  rule {
    modsec_rule_id = 2029718
    revision       = 1
    status         = "log"
  }

  rule_exclusion {
    name            = "index page"
    exclusion_type  = "rule"
    condition       = "req.url.basename == \"index.html\""
    modsec_rule_ids = [2029718]
  }
}

Usage with rules from data source:

variable "type_status" {
  type = map(string)
  default = {
    score     = "score"
    threshold = "log"
    strict    = "log"
  }
}

resource "fastly_service_vcl" "demo" {
  name = "demofastly"

  domain {
    name    = "example.com"
    comment = "demo"
  }

  backend {
    address = "127.0.0.1"
    name    = "origin1"
    port    = 80
  }

  condition {
    name      = "WAF_Prefetch"
    type      = "PREFETCH"
    statement = "req.backend.is_origin"
  }

  # This condition will always be false
  # adding it to the response object created below
  # prevents Fastly from returning a 403 on all of your traffic.
  condition {
    name      = "WAF_always_false"
    statement = "false"
    type      = "REQUEST"
  }

  response_object {
    name              = "WAF_Response"
    status            = "403"
    response          = "Forbidden"
    content_type      = "text/html"
    content           = "<html><body>Forbidden</body></html>"
    request_condition = "WAF_always_false"
  }
  waf {
    prefetch_condition = "WAF_Prefetch"
    response_object    = "WAF_Response"
  }

  force_destroy = true
}

data "fastly_waf_rules" "owasp" {
  publishers = ["owasp"]
}

resource "fastly_service_waf_configuration" "waf" {
  waf_id                          = fastly_service_vcl.demo.waf[0].waf_id
  http_violation_score_threshold  = 100

  dynamic "rule" {
    for_each = data.fastly_waf_rules.owasp.rules
    content {
      modsec_rule_id = rule.value.modsec_rule_id
      revision       = rule.value.latest_revision_number
      status         = lookup(var.type_status, rule.value.type, "log")
    }
  }
}

Usage with support for individual rule configuration (this is the suggested pattern):

# this variable is used for rule configuration in bulk
variable "type_status" {
  type    = map(string)
  default = {
    score     = "score"
    threshold = "log"
    strict    = "log"
  }
}
# this variable is used for individual rule configuration
variable "individual_rules" {
  type    = map(string)
  default = {
    1010020 = "block"
  }
}

resource "fastly_service_vcl" "demo" {
  name = "demofastly"

  domain {
    name    = "example.com"
    comment = "demo"
  }

  backend {
    address = "127.0.0.1"
    name    = "origin1"
    port    = 80
  }

  condition {
    name      = "WAF_Prefetch"
    type      = "PREFETCH"
    statement = "req.backend.is_origin"
  }

  # This condition will always be false
  # adding it to the response object created below
  # prevents Fastly from returning a 403 on all of your traffic.
  condition {
    name      = "WAF_always_false"
    statement = "false"
    type      = "REQUEST"
  }

  response_object {
    name              = "WAF_Response"
    status            = "403"
    response          = "Forbidden"
    content_type      = "text/html"
    content           = "<html><body>Forbidden</body></html>"
    request_condition = "WAF_always_false"
  }

  waf {
    prefetch_condition = "WAF_Prefetch"
    response_object    = "WAF_Response"
  }

  force_destroy = true
}

data "fastly_waf_rules" "owasp" {
  publishers = ["owasp"]
}

resource "fastly_service_waf_configuration" "waf" {
  waf_id                         = fastly_service_vcl.demo.waf[0].waf_id
  http_violation_score_threshold = 202

  dynamic "rule" {
    for_each = data.fastly_waf_rules.owasp.rules
    content {
      modsec_rule_id = rule.value.modsec_rule_id
      revision       = rule.value.latest_revision_number
      # Nested lookups in order to apply a combination of in bulk and individual rule configuration.
      status         = lookup(var.individual_rules, rule.value.modsec_rule_id, lookup(var.type_status, rule.value.type, "log"))
    }
  }
}

Usage with support for specific rule revision configuration:

# this variable is used for rule configuration in bulk
variable "type_status" {
  type    = map(string)
  default = {
    score     = "score"
    threshold = "log"
    strict    = "log"
  }
}

# This variable is used for individual rule revision configuration.
variable "specific_rule_revisions" {
  type    = map(string)
  default = {
    #  If the revision requested is not found, the server will return a 404 response code.
    1010020 = 1
  }
}

resource "fastly_service_vcl" "demo" {
  name = "demofastly"

  domain {
    name    = "example.com"
    comment = "demo"
  }

  backend {
    address = "127.0.0.1"
    name    = "origin1"
    port    = 80
  }

  condition {
    name      = "WAF_Prefetch"
    type      = "PREFETCH"
    statement = "req.backend.is_origin"
  }

  # This condition will always be false
  # adding it to the response object created below
  # prevents Fastly from returning a 403 on all of your traffic.
  condition {
    name      = "WAF_always_false"
    statement = "false"
    type      = "REQUEST"
  }

  response_object {
    name              = "WAF_Response"
    status            = "403"
    response          = "Forbidden"
    content_type      = "text/html"
    content           = "<html><body>Forbidden</body></html>"
    request_condition = "WAF_always_false"
  }

  waf {
    prefetch_condition = "WAF_Prefetch"
    response_object    = "WAF_Response"
  }

  force_destroy = true
}

data "fastly_waf_rules" "owasp" {
  publishers = ["owasp"]
}

resource "fastly_service_waf_configuration" "waf" {
  waf_id                         = fastly_service_vcl.demo.waf[0].waf_id
  http_violation_score_threshold = 202

  dynamic "rule" {
    for_each = data.fastly_waf_rules.owasp.rules
    content {
      modsec_rule_id = rule.value.modsec_rule_id
      revision       = lookup(var.specific_rule_revisions, rule.value.modsec_rule_id, rule.value.latest_revision_number)
      status         = lookup(var.type_status, rule.value.type, "log")
    }
  }
}

Usage omitting rule revision field. The first time Terraform is applied, the latest rule revisions are associated with the WAF. Any subsequent apply would not alter the rule revisions.

# This variable is used for rule configuration in bulk.
variable "type_status" {
  type    = map(string)
  default = {
    score     = "score"
    threshold = "log"
    strict    = "log"
  }
}
# This variable is used for individual rule configuration.
variable "individual_rules" {
  type    = map(string)
  default = {
    1010020 = "block"
  }
}

resource "fastly_service_vcl" "demo" {
  name = "demofastly"

  domain {
    name    = "example.com"
    comment = "demo"
  }

  backend {
    address = "127.0.0.1"
    name    = "origin1"
    port    = 80
  }

  condition {
    name      = "WAF_Prefetch"
    type      = "PREFETCH"
    statement = "req.backend.is_origin"
  }

  # This condition will always be false
  # adding it to the response object created below
  # prevents Fastly from returning a 403 on all of your traffic.
  condition {
    name      = "WAF_always_false"
    statement = "false"
    type      = "REQUEST"
  }

  response_object {
    name              = "WAF_Response"
    status            = "403"
    response          = "Forbidden"
    content_type      = "text/html"
    content           = "<html><body>Forbidden</body></html>"
    request_condition = "WAF_always_false"
  }

  waf {
    prefetch_condition = "WAF_Prefetch"
    response_object    = "WAF_Response"
  }

  force_destroy = true
}

data "fastly_waf_rules" "owasp" {
  publishers = ["owasp"]
}

resource "fastly_service_waf_configuration" "waf" {
  waf_id                         = fastly_service_vcl.demo.waf[0].waf_id
  http_violation_score_threshold = 202

  dynamic "rule" {
    for_each = data.fastly_waf_rules.owasp.rules
    content {
      modsec_rule_id = rule.value.modsec_rule_id
      # Rule revision field ommitted.
      status         = lookup(var.individual_rules, rule.value.modsec_rule_id, lookup(var.type_status, rule.value.type, "log"))
    }
  }
}
# This output contains the WAF's active rules set. This can be useful for identifying which revision is active for each WAF rule.
output "rules" {
  value = fastly_service_waf_configuration.waf.rule
}

Adding a WAF to an existing service

When adding a waf to an existing fastly_service_vcl and at the same time adding a fastly_service_waf_configuration resource with waf_id = fastly_service_vcl.demo.waf[0].waf_id might result with the in the following error:

fastly_service_vcl.demo.waf is empty list of object

For this scenario, it's recommended to split the changes into two distinct steps:

  1. Add the waf block to the fastly_service_vcl and apply the changes
  2. Add the fastly_service_waf_configuration to the HCL and apply the changes

Import

This is an example of the import command being applied to the resource named fastly_service_waf_configuration.waf The resource ID should be the WAF ID.

$ terraform import fastly_service_waf_configuration.waf xxxxxxxxxxxxxxxxxxxx

If Terraform is already managing a remote WAF configurations against a resource being imported then the user will be asked to remove it from the existing Terraform state. The following is an example of the Terraform state command to remove the resource named fastly_service_waf_configuration.waf from the Terraform state file.

$ terraform state rm fastly_service_waf_configuration.waf

Schema

Required

Optional

Read-Only

Nested Schema for rule

Required:

Optional:

Nested Schema for rule_exclusion

Required:

Optional:

Read-Only: